add method for filtering
This commit is contained in:
@@ -213,7 +213,7 @@ export interface FileContent<T extends string>
|
||||
readonly creationDate: string;
|
||||
|
||||
readonly type: T;
|
||||
}
|
||||
}
|
||||
|
||||
// references
|
||||
export const FileContentReference: FileContent<string> = {
|
||||
|
||||
@@ -157,8 +157,7 @@ export default class FileTransferModel {
|
||||
};
|
||||
|
||||
readonly prepareFileForSending = (filePath: string[]): string => {
|
||||
const fileContent: string | null =
|
||||
this.storageModel.read(filePath);
|
||||
const fileContent: string | null = this.storageModel.read(filePath);
|
||||
if (fileContent == null) return "";
|
||||
|
||||
const fileData: FileData = {
|
||||
|
||||
@@ -46,7 +46,7 @@ export default class SettingsModel {
|
||||
// load
|
||||
private readonly readSetting = (
|
||||
pathName: keyof typeof filePaths.settingsModel,
|
||||
): string|null=> {
|
||||
): string | null => {
|
||||
const path = StorageModel.getPath(
|
||||
StorageModelSubPaths.SettingsModel,
|
||||
filePaths.settingsModel[pathName],
|
||||
|
||||
Binary file not shown.
+102
-80
@@ -1,5 +1,5 @@
|
||||
import { checkIsValidObject, checkMatchesObjectStructure } from "./typeSafety";
|
||||
import * as Hammer from "hammerjs";
|
||||
import * as React from "../../react";
|
||||
|
||||
export interface Stringifiable {
|
||||
toString(): string;
|
||||
@@ -18,7 +18,7 @@ export function createTimestamp(): string {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
|
||||
// filters etc
|
||||
// filters
|
||||
export type StringEntryObject = { [key: string]: Stringifiable | undefined };
|
||||
|
||||
export function filterObjectsByStringEntries<T>(
|
||||
@@ -28,7 +28,7 @@ export function filterObjectsByStringEntries<T>(
|
||||
): Set<T> {
|
||||
const matches: Set<T> = new Set();
|
||||
|
||||
object_loop: for (const object of objects) {
|
||||
for (const object of objects) {
|
||||
const doesMatch: boolean = checkDoesObjectMatchReference(
|
||||
reference,
|
||||
converter(object),
|
||||
@@ -39,79 +39,6 @@ export function filterObjectsByStringEntries<T>(
|
||||
return matches;
|
||||
}
|
||||
|
||||
export function checkDoesObjectMatchReference(
|
||||
reference: StringEntryObject,
|
||||
stringEntryObject: StringEntryObject,
|
||||
explicitEmptyValue: boolean = false,
|
||||
): boolean {
|
||||
reference_entry_loop: for (const referenceEntry of Object.entries(
|
||||
reference,
|
||||
)) {
|
||||
const [referenceKey, referenceValue] = referenceEntry;
|
||||
const stringEntryObjectValue: Stringifiable | undefined =
|
||||
stringEntryObject[referenceKey];
|
||||
|
||||
if (referenceValue == undefined) return false;
|
||||
|
||||
if (referenceValue[0] == "-") {
|
||||
const strippedReferenceValue: string = referenceValue
|
||||
.toString()
|
||||
.substring(1);
|
||||
// property may not exist
|
||||
if (
|
||||
strippedReferenceValue == "" &&
|
||||
stringEntryObjectValue != undefined &&
|
||||
stringEntryObjectValue != ""
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// property may not match
|
||||
if (stringEntryObjectValue == strippedReferenceValue) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (explicitEmptyValue == false) {
|
||||
// property must exist but be anything
|
||||
if (
|
||||
referenceValue == "" &&
|
||||
(stringEntryObjectValue == undefined ||
|
||||
stringEntryObjectValue == "")
|
||||
) {
|
||||
return false;
|
||||
} else if (referenceValue == "") {
|
||||
continue reference_entry_loop;
|
||||
}
|
||||
}
|
||||
|
||||
// property must match
|
||||
if (stringEntryObjectValue != referenceValue) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function collectObjectValuesForKey<T>(
|
||||
key: string,
|
||||
converter: (object: T) => StringEntryObject,
|
||||
objects: T[],
|
||||
): string[] {
|
||||
const values: Set<string> = new Set();
|
||||
|
||||
for (const object of objects) {
|
||||
const stringEntryObject: StringEntryObject = converter(object);
|
||||
const stringEntryObjectValue: Stringifiable | undefined =
|
||||
stringEntryObject[key];
|
||||
if (stringEntryObjectValue == undefined) continue;
|
||||
|
||||
values.add(stringEntryObjectValue.toString());
|
||||
}
|
||||
|
||||
return [...values.values()];
|
||||
}
|
||||
|
||||
export function filterObjectsByWords<T>(
|
||||
query: string,
|
||||
getStringsOfObject: (object: T) => string[],
|
||||
@@ -119,7 +46,7 @@ export function filterObjectsByWords<T>(
|
||||
): Set<T> {
|
||||
const matches: Set<T> = new Set();
|
||||
|
||||
object_loop: for (const object of objects) {
|
||||
for (const object of objects) {
|
||||
const doesMatch: boolean = checkDoesObjectMatchSearch(
|
||||
query,
|
||||
getStringsOfObject,
|
||||
@@ -170,6 +97,26 @@ export function checkDoesObjectMatchSearch<T>(
|
||||
return true;
|
||||
}
|
||||
|
||||
export default function implementFilter<T>(
|
||||
allItems: React.ListState<T>|React.MapState<T>,
|
||||
matches: React.ListState<T>,
|
||||
query: React.State<string>,
|
||||
itemToString: (item: T) => string,
|
||||
) {
|
||||
function update() {
|
||||
matches.clear();
|
||||
allItems.value.forEach((item: T) => {
|
||||
if (!itemToString(item).includes(query.value)) return;
|
||||
matches.add(item);
|
||||
allItems.handleRemoval(item, () => {
|
||||
matches.remove(item);
|
||||
});
|
||||
});
|
||||
}
|
||||
query.subscribe(update);
|
||||
allItems.handleAddition(update);
|
||||
}
|
||||
|
||||
// handlers
|
||||
export type Handler<T> = (item: T) => void;
|
||||
|
||||
@@ -313,7 +260,7 @@ export function implementPinchZoom(canvas: HTMLElement) {
|
||||
const MIN = 0.25;
|
||||
const MAX = 5;
|
||||
|
||||
const element = (): HTMLElement|null=> canvas.querySelector(".zoom");
|
||||
const element = (): HTMLElement | null => canvas.querySelector(".zoom");
|
||||
const distance = (e: TouchEvent) =>
|
||||
Math.hypot(
|
||||
e.touches[0].pageX - e.touches[1].pageX,
|
||||
@@ -333,8 +280,8 @@ export function implementPinchZoom(canvas: HTMLElement) {
|
||||
let x: number, y: number;
|
||||
if (_offset) [x, y] = _offset;
|
||||
else {
|
||||
x = currentX
|
||||
y = currentY
|
||||
x = currentX;
|
||||
y = currentY;
|
||||
}
|
||||
el.style.transform = `scale(${factor.toString()}) translate(${x}px, ${y}px)`;
|
||||
currentX = x;
|
||||
@@ -409,3 +356,78 @@ export function implementPinchZoom(canvas: HTMLElement) {
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
// objecs
|
||||
export function collectObjectValuesForKey<T>(
|
||||
key: string,
|
||||
converter: (object: T) => StringEntryObject,
|
||||
objects: T[],
|
||||
): string[] {
|
||||
const values: Set<string> = new Set();
|
||||
|
||||
for (const object of objects) {
|
||||
const stringEntryObject: StringEntryObject = converter(object);
|
||||
const stringEntryObjectValue: Stringifiable | undefined =
|
||||
stringEntryObject[key];
|
||||
if (stringEntryObjectValue == undefined) continue;
|
||||
|
||||
values.add(stringEntryObjectValue.toString());
|
||||
}
|
||||
|
||||
return [...values.values()];
|
||||
}
|
||||
|
||||
// type safety
|
||||
export function checkDoesObjectMatchReference(
|
||||
reference: StringEntryObject,
|
||||
stringEntryObject: StringEntryObject,
|
||||
explicitEmptyValue: boolean = false,
|
||||
): boolean {
|
||||
reference_entry_loop: for (const referenceEntry of Object.entries(
|
||||
reference,
|
||||
)) {
|
||||
const [referenceKey, referenceValue] = referenceEntry;
|
||||
const stringEntryObjectValue: Stringifiable | undefined =
|
||||
stringEntryObject[referenceKey];
|
||||
|
||||
if (referenceValue == undefined) return false;
|
||||
|
||||
if (referenceValue[0] == "-") {
|
||||
const strippedReferenceValue: string = referenceValue
|
||||
.toString()
|
||||
.substring(1);
|
||||
// property may not exist
|
||||
if (
|
||||
strippedReferenceValue == "" &&
|
||||
stringEntryObjectValue != undefined &&
|
||||
stringEntryObjectValue != ""
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// property may not match
|
||||
if (stringEntryObjectValue == strippedReferenceValue) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (explicitEmptyValue == false) {
|
||||
// property must exist but be anything
|
||||
if (
|
||||
referenceValue == "" &&
|
||||
(stringEntryObjectValue == undefined ||
|
||||
stringEntryObjectValue == "")
|
||||
) {
|
||||
return false;
|
||||
} else if (referenceValue == "") {
|
||||
continue reference_entry_loop;
|
||||
}
|
||||
}
|
||||
|
||||
// property must match
|
||||
if (stringEntryObjectValue != referenceValue) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -1,4 +1,3 @@
|
||||
import * as React from "../../react";
|
||||
import { ViewController } from "../viewController";
|
||||
import { TaskViewModelToEntry } from "../Components/taskEntry";
|
||||
import {
|
||||
@@ -6,6 +5,7 @@ import {
|
||||
createPropertyValueIndexState,
|
||||
} from "../Components/propertyValueList";
|
||||
import { FilteredList } from "../Components/filteredList";
|
||||
import * as React from "../../react";
|
||||
import { TaskCategoryBulkChangeViewModel } from "../../ViewModel/Utility/taskPropertyBulkChangeViewModel";
|
||||
import TaskViewModel from "../../ViewModel/Pages/taskViewModel";
|
||||
import BoardViewModel from "../../ViewModel/Pages/boardViewModel";
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as React from "../../react";
|
||||
import { BoardStatusGridPage } from "./boardStatusGridPage";
|
||||
import { BoardKanbanPage } from "./boardKanbanPage";
|
||||
import { ViewController } from "../viewController";
|
||||
@@ -7,6 +6,7 @@ import { SearchModal } from "../Modals/searchModal";
|
||||
import { BoardSettingsModal } from "../Modals/boardSettingsModal";
|
||||
import { TaskViewModelToEntry } from "../Components/taskEntry";
|
||||
import { BoardViewToggleButton } from "../Components/boardViewToggleButton";
|
||||
import * as React from "../../react";
|
||||
import BoardViewModel, {
|
||||
BoardPageTypes,
|
||||
} from "../../ViewModel/Pages/boardViewModel";
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as React from "../../react";
|
||||
import { ViewController } from "../viewController";
|
||||
import { TaskViewModelToEntry } from "../Components/taskEntry";
|
||||
import {
|
||||
@@ -8,6 +7,7 @@ import {
|
||||
createSortedPropertyValueState,
|
||||
} from "../Components/propertyValueList";
|
||||
import { FilteredList } from "../Components/filteredList";
|
||||
import * as React from "../../react";
|
||||
import {
|
||||
TaskCategoryBulkChangeViewModel,
|
||||
TaskStatusBulkChangeViewModel,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import * as React from "../../react";
|
||||
import "./calendarPage.css";
|
||||
import { ViewController } from "../viewController";
|
||||
import { TaskSettingsModal } from "../Modals/taskSettingsModal";
|
||||
import { TaskViewModelToEntry } from "../Components/taskEntry";
|
||||
import { PlaceholderView } from "../Components/placeholderView";
|
||||
import { MonthGrid } from "../Components/monthGrid";
|
||||
import * as React from "../../react";
|
||||
import CalendarPageViewModel from "../../ViewModel/Pages/calendarPageViewModel";
|
||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as React from "../../react";
|
||||
import "./messagePage.css";
|
||||
import { ViewController } from "../viewController";
|
||||
import { MessageFilterModal } from "../Modals/messageFilterModal";
|
||||
import { ReplyPreview } from "../Components/replyPreview";
|
||||
import { ChatMessage } from "../Components/chatMessage";
|
||||
import * as React from "../../react";
|
||||
import MessagePageViewModel from "../../ViewModel/Pages/messagePageViewModel";
|
||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from "../../react";
|
||||
import { DeletableListItem } from "../Components/deletableListItem";
|
||||
import { DangerousActionButton } from "../Components/dangerousActionButton";
|
||||
import { ColorPicker } from "../Components/colorPicker";
|
||||
import * as React from "../../react";
|
||||
import SettingsPageViewModel from "../../ViewModel/Pages/settingsPageViewModel";
|
||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as React from "../../react";
|
||||
import "./taskPage.css";
|
||||
import { BoardPage } from "./boardPage";
|
||||
import { ViewController } from "../viewController";
|
||||
import { PlaceholderView } from "../Components/placeholderView";
|
||||
import { BoardViewModelToEntry } from "../Components/boardEntry";
|
||||
import * as React from "../../react";
|
||||
import TaskPageViewModel from "../../ViewModel/Pages/taskPageViewModel";
|
||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||
|
||||
@@ -62,7 +62,7 @@ export function TaskPage(
|
||||
<div class="flex-row width-input">
|
||||
<input
|
||||
class="no-outline"
|
||||
bind:value={taskPageViewModel.newBoardNameInput}
|
||||
bind:value={taskPageViewModel.boardQuery}
|
||||
on:enter={taskPageViewModel.createBoard}
|
||||
placeholder={
|
||||
coreViewModel.translations.chatPage.task
|
||||
@@ -89,7 +89,7 @@ export function TaskPage(
|
||||
class="grid gap"
|
||||
style="grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr))"
|
||||
children:append={[
|
||||
taskPageViewModel.boardViewModels,
|
||||
taskPageViewModel.boardMatches,
|
||||
BoardViewModelToEntry,
|
||||
]}
|
||||
></div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from "../../react";
|
||||
import { ViewController } from "../viewController";
|
||||
import * as React from "../../react";
|
||||
import BoardViewModel from "../../ViewModel/Pages/boardViewModel";
|
||||
|
||||
export function BoardEntry(boardViewModel: BoardViewModel) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from "../../react";
|
||||
import { RibbonButton } from "./ribbonButton";
|
||||
import * as React from "../../react";
|
||||
import BoardViewModel, {
|
||||
BoardPageTypes,
|
||||
} from "../../ViewModel/Pages/boardViewModel";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from "../../react";
|
||||
import { MessageReactionButtonRow } from "./messageReactionButtonRow";
|
||||
import { InlineReply } from "./inlineReply";
|
||||
import { ChatMessageInfoModal } from "../Modals/chatMessageInfoModal";
|
||||
import * as React from "../../react";
|
||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
||||
import { ChatMessageStatuses } from "../../Model/Chat/chatModel";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from "../../react";
|
||||
import { RibbonButton } from "./ribbonButton";
|
||||
import * as React from "../../react";
|
||||
import ChatViewModel, {
|
||||
ChatPageTypes,
|
||||
} from "../../ViewModel/Chat/chatViewModel";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from "../../react";
|
||||
import { ViewController } from "../viewController";
|
||||
import * as React from "../../react";
|
||||
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
||||
|
||||
export function InlineReply(chatMessageViewModel: ChatMessageViewModel) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from "../../react";
|
||||
import { MessageReactionButton } from "./messageReactionButton";
|
||||
import * as React from "../../react";
|
||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
||||
import { ReactionSymbols } from "../../Model/Chat/chatModel";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from "../../react";
|
||||
import "./monthGrid.css";
|
||||
import { ViewController } from "../viewController";
|
||||
import * as React from "../../react";
|
||||
import TaskViewModel from "../../ViewModel/Pages/taskViewModel";
|
||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||
import { localeCompare } from "../../Model/Utility/utility";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from "../../react";
|
||||
import { OptionButton } from "./optionButton";
|
||||
import * as React from "../../react";
|
||||
|
||||
export function OptionButtonList<T>(
|
||||
options: React.ListState<[string, T]>,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from "../../react";
|
||||
import { ViewController } from "../viewController";
|
||||
import { DangerousActionButton } from "../Components/dangerousActionButton";
|
||||
import { ColorPicker } from "../Components/colorPicker";
|
||||
import * as React from "../../react";
|
||||
import BoardViewModel from "../../ViewModel/Pages/boardViewModel";
|
||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from "../../react";
|
||||
import { MessageReactionEntry } from "../Components/messageReactionEntry";
|
||||
import { MessageReactionButtonRow } from "../Components/messageReactionButtonRow";
|
||||
import { InfoTile } from "../Components/infoTile";
|
||||
import * as React from "../../react";
|
||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from "../../react";
|
||||
import { DeletableListItem } from "../Components/deletableListItem";
|
||||
import * as React from "../../react";
|
||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||
import ConnectionViewModel from "../../ViewModel/Global/connectionViewModel";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from "../../react";
|
||||
import { ViewController } from "../viewController";
|
||||
import { StringToTextSpan } from "../Components/textSpan";
|
||||
import * as React from "../../react";
|
||||
import FileTransferViewModel, {
|
||||
FileTransferModals,
|
||||
FileTransferOption,
|
||||
@@ -758,7 +758,9 @@ function ExportModal(
|
||||
</span>
|
||||
<input
|
||||
bind:value={fileTransferViewModel.exportKey}
|
||||
on:enter={fileTransferViewModel.downloadFile}
|
||||
on:enter={
|
||||
fileTransferViewModel.downloadFile
|
||||
}
|
||||
type="password"
|
||||
></input>
|
||||
</div>
|
||||
@@ -777,7 +779,9 @@ function ExportModal(
|
||||
bind:value={
|
||||
fileTransferViewModel.exportKeyConfirmation
|
||||
}
|
||||
on:enter={fileTransferViewModel.downloadFile}
|
||||
on:enter={
|
||||
fileTransferViewModel.downloadFile
|
||||
}
|
||||
type="password"
|
||||
></input>
|
||||
</div>
|
||||
@@ -903,7 +907,9 @@ function ImportDecryptionDataModal(
|
||||
</span>
|
||||
<input
|
||||
bind:value={fileTransferViewModel.importKey}
|
||||
on:enter={fileTransferViewModel.decryptImport}
|
||||
on:enter={
|
||||
fileTransferViewModel.decryptImport
|
||||
}
|
||||
type="password"
|
||||
></input>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from "../../react";
|
||||
import { ViewController } from "../viewController";
|
||||
import { MessageReactionFilterButton } from "../Components/messageReactionFilterButton";
|
||||
import * as React from "../../react";
|
||||
import MessagePageViewModel from "../../ViewModel/Pages/messagePageViewModel";
|
||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||
import { ReactionSymbols } from "../../Model/Chat/chatModel";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { v4 } from "uuid";
|
||||
import * as React from "../../react";
|
||||
import { ViewController } from "../viewController";
|
||||
import { StringToOption } from "../Components/option";
|
||||
import * as React from "../../react";
|
||||
import SearchViewModel from "../../ViewModel/Utility/searchViewModel";
|
||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||
|
||||
@@ -70,15 +70,24 @@ export function SearchModal<T>(
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
toggle:hidden={searchViewModel.hasNoSuggestions}>
|
||||
<div toggle:hidden={searchViewModel.hasNoSuggestions}>
|
||||
<hr></hr>
|
||||
<h3>{coreViewModel.translations.general.searchSuggestionsLabel}</h3>
|
||||
<h3>
|
||||
{
|
||||
coreViewModel.translations.general
|
||||
.searchSuggestionsLabel
|
||||
}
|
||||
</h3>
|
||||
<div
|
||||
class="flex-column gap"
|
||||
children:append={[
|
||||
searchViewModel.suggestions,
|
||||
(suggestion: string) => SuggestionView(suggestion, searchViewModel, coreViewModel),
|
||||
(suggestion: string) =>
|
||||
SuggestionView(
|
||||
suggestion,
|
||||
searchViewModel,
|
||||
coreViewModel,
|
||||
),
|
||||
]}
|
||||
></div>
|
||||
</div>
|
||||
@@ -92,8 +101,15 @@ export function SearchModal<T>(
|
||||
);
|
||||
}
|
||||
|
||||
function SuggestionView<T>(suggestion: string, searchViewModel: SearchViewModel<T>, coreViewModel: CoreViewModel) {
|
||||
const isApplied = React.createProxyState([searchViewModel.appliedQuery], ()=>searchViewModel.appliedQuery.value == suggestion);
|
||||
function SuggestionView<T>(
|
||||
suggestion: string,
|
||||
searchViewModel: SearchViewModel<T>,
|
||||
coreViewModel: CoreViewModel,
|
||||
) {
|
||||
const isApplied = React.createProxyState(
|
||||
[searchViewModel.appliedQuery],
|
||||
() => searchViewModel.appliedQuery.value == suggestion,
|
||||
);
|
||||
|
||||
function deleteSuggestion() {
|
||||
searchViewModel.deleteSuggestion(suggestion);
|
||||
@@ -103,7 +119,8 @@ function SuggestionView<T>(suggestion: string, searchViewModel: SearchViewModel<
|
||||
searchViewModel.search(suggestion);
|
||||
}
|
||||
|
||||
return <div class="flex-row surface align-center">
|
||||
return (
|
||||
<div class="flex-row surface align-center">
|
||||
<span class="width-100 flex-1 padding-h">{suggestion}</span>
|
||||
<button
|
||||
class="danger"
|
||||
@@ -121,4 +138,5 @@ function SuggestionView<T>(suggestion: string, searchViewModel: SearchViewModel<
|
||||
<span class="icon">check</span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import * as React from "../../react";
|
||||
import { SplitModal } from "./splitModal";
|
||||
import { languageNames } from "../translations";
|
||||
import { PlaceholderView } from "../Components/placeholderView";
|
||||
import { OptionButtonList } from "../Components/optionButtonList";
|
||||
import { NavigationButton } from "../Components/navigationButton";
|
||||
import { InfoTile } from "../Components/infoTile";
|
||||
import * as React from "../../react";
|
||||
import SettingsViewModel, {
|
||||
SettingsModalPages,
|
||||
} from "../../ViewModel/Global/settingsViewModel";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from "../../react";
|
||||
import "./splitModal.css";
|
||||
import * as React from "../../react";
|
||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||
|
||||
export function SplitModal<T>(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from "../../react";
|
||||
import { SplitModal } from "./splitModal";
|
||||
import { DirectoryItemList } from "../Components/directoryItemList";
|
||||
import { DangerousActionButton } from "../Components/dangerousActionButton";
|
||||
import * as React from "../../react";
|
||||
import StorageViewModel from "../../ViewModel/Global/storageViewModel";
|
||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||
import { PATH_COMPONENT_SEPARATOR } from "../../Model/Global/storageModel";
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { v4 } from "uuid";
|
||||
import * as React from "../../react";
|
||||
import {
|
||||
Entry,
|
||||
Option,
|
||||
@@ -7,6 +6,7 @@ import {
|
||||
VersionIdToOption,
|
||||
} from "../Components/option";
|
||||
import { DangerousActionButton } from "../Components/dangerousActionButton";
|
||||
import * as React from "../../react";
|
||||
import TaskViewModel from "../../ViewModel/Pages/taskViewModel";
|
||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||
|
||||
@@ -25,7 +25,13 @@ export function TaskSettingsModal(
|
||||
};
|
||||
|
||||
return (
|
||||
<div class="modal task-settings" open toggle:full-description={taskViewModel.isPresentingFullScreenDescription}>
|
||||
<div
|
||||
class="modal task-settings"
|
||||
open
|
||||
toggle:full-description={
|
||||
taskViewModel.isPresentingFullScreenDescription
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<main id="standard-main">
|
||||
<h2>
|
||||
@@ -42,8 +48,8 @@ export function TaskSettingsModal(
|
||||
<div>
|
||||
<span>
|
||||
{
|
||||
coreViewModel.translations.chatPage.task
|
||||
.taskNameLabel
|
||||
coreViewModel.translations.chatPage
|
||||
.task.taskNameLabel
|
||||
}
|
||||
</span>
|
||||
<input
|
||||
@@ -57,8 +63,8 @@ export function TaskSettingsModal(
|
||||
<div>
|
||||
<span>
|
||||
{
|
||||
coreViewModel.translations.chatPage.task
|
||||
.taskBoardLabel
|
||||
coreViewModel.translations.chatPage
|
||||
.task.taskBoardLabel
|
||||
}
|
||||
</span>
|
||||
<select
|
||||
@@ -78,8 +84,8 @@ export function TaskSettingsModal(
|
||||
<div>
|
||||
<span>
|
||||
{
|
||||
coreViewModel.translations.chatPage.task
|
||||
.taskDescriptionLabel
|
||||
coreViewModel.translations.chatPage
|
||||
.task.taskDescriptionLabel
|
||||
}
|
||||
</span>
|
||||
<textarea
|
||||
@@ -91,10 +97,13 @@ export function TaskSettingsModal(
|
||||
<div class="flex-row justify-end">
|
||||
<button
|
||||
class="width-50"
|
||||
on:click={taskViewModel.openFullscreenDecription}
|
||||
on:click={
|
||||
taskViewModel.openFullscreenDecription
|
||||
}
|
||||
>
|
||||
{
|
||||
coreViewModel.translations.general.fullscreenButton
|
||||
coreViewModel.translations.general
|
||||
.fullscreenButton
|
||||
}
|
||||
|
||||
<span class="icon">fullscreen</span>
|
||||
@@ -110,8 +119,8 @@ export function TaskSettingsModal(
|
||||
<div>
|
||||
<span>
|
||||
{
|
||||
coreViewModel.translations.chatPage.task
|
||||
.taskCategoryLabel
|
||||
coreViewModel.translations.chatPage
|
||||
.task.taskCategoryLabel
|
||||
}
|
||||
</span>
|
||||
<input
|
||||
@@ -124,7 +133,8 @@ export function TaskSettingsModal(
|
||||
hidden
|
||||
id={categorySuggestionId}
|
||||
children:append={[
|
||||
taskViewModel.containingViewModel.taskCategorySuggestions,
|
||||
taskViewModel.containingViewModel
|
||||
.taskCategorySuggestions,
|
||||
StringToOption,
|
||||
]}
|
||||
></datalist>
|
||||
@@ -134,8 +144,8 @@ export function TaskSettingsModal(
|
||||
<div>
|
||||
<span>
|
||||
{
|
||||
coreViewModel.translations.chatPage.task
|
||||
.taskStatusLabel
|
||||
coreViewModel.translations.chatPage
|
||||
.task.taskStatusLabel
|
||||
}
|
||||
</span>
|
||||
<input
|
||||
@@ -148,7 +158,8 @@ export function TaskSettingsModal(
|
||||
hidden
|
||||
id={statusSuggestionId}
|
||||
children:append={[
|
||||
taskViewModel.containingViewModel.taskStatusSuggestions,
|
||||
taskViewModel.containingViewModel
|
||||
.taskStatusSuggestions,
|
||||
StringToOption,
|
||||
]}
|
||||
></datalist>
|
||||
@@ -158,8 +169,8 @@ export function TaskSettingsModal(
|
||||
<div>
|
||||
<span>
|
||||
{
|
||||
coreViewModel.translations.chatPage.task
|
||||
.taskPriorityLabel
|
||||
coreViewModel.translations.chatPage
|
||||
.task.taskPriorityLabel
|
||||
}
|
||||
</span>
|
||||
<input
|
||||
@@ -176,8 +187,8 @@ export function TaskSettingsModal(
|
||||
<div>
|
||||
<span>
|
||||
{
|
||||
coreViewModel.translations.chatPage.task
|
||||
.taskDateLabel
|
||||
coreViewModel.translations.chatPage
|
||||
.task.taskDateLabel
|
||||
}
|
||||
</span>
|
||||
<input
|
||||
@@ -192,8 +203,8 @@ export function TaskSettingsModal(
|
||||
<div>
|
||||
<span>
|
||||
{
|
||||
coreViewModel.translations.chatPage.task
|
||||
.taskTimeLabel
|
||||
coreViewModel.translations.chatPage
|
||||
.task.taskTimeLabel
|
||||
}
|
||||
</span>
|
||||
<input
|
||||
@@ -245,10 +256,7 @@ export function TaskSettingsModal(
|
||||
bind:value={taskViewModel.description}
|
||||
></textarea>
|
||||
</main>
|
||||
<div
|
||||
class="flex-row width-100"
|
||||
id="fullscreen-exit"
|
||||
>
|
||||
<div class="flex-row width-100" id="fullscreen-exit">
|
||||
<button
|
||||
class="flex"
|
||||
on:click={taskViewModel.closeFullscreenDecription}
|
||||
@@ -257,10 +265,7 @@ export function TaskSettingsModal(
|
||||
<span class="icon">fullscreen_exit</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="flex-row width-100"
|
||||
id="controls"
|
||||
>
|
||||
<div class="flex-row width-100" id="controls">
|
||||
<button
|
||||
class="flex"
|
||||
on:click={taskViewModel.closeAndDiscard}
|
||||
@@ -276,6 +281,6 @@ export function TaskSettingsModal(
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+36
-35
@@ -65,7 +65,7 @@ body[showing-chat] #chat-page-wrapper {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@media (pointer: coarse) and (max-width: 480px){
|
||||
@media (pointer: coarse) and (max-width: 480px) {
|
||||
#chat-page > div > #main > div > .pane-wrapper > .pane {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
@@ -114,21 +114,21 @@ body[showing-chat] #chat-page-wrapper {
|
||||
|
||||
#chat-page > div > #ribbon > span,
|
||||
#chat-page
|
||||
> div
|
||||
> #main
|
||||
> div
|
||||
> .pane-wrapper
|
||||
> .pane
|
||||
> .toolbar
|
||||
> span.ribbon {
|
||||
> div
|
||||
> #main
|
||||
> div
|
||||
> .pane-wrapper
|
||||
> .pane
|
||||
> .toolbar
|
||||
> span.ribbon {
|
||||
gap: var(--gap-between-tiles);
|
||||
padding: var(--gap-between-tiles);
|
||||
}
|
||||
|
||||
@media (pointer: coarse) and (max-width: 480px){
|
||||
#chat-page > div > #ribbon #close-button {
|
||||
@media (pointer: coarse) and (max-width: 480px) {
|
||||
#chat-page > div > #ribbon #close-button {
|
||||
order: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#chat-page > div > #main > div > .pane-wrapper > .pane > .toolbar {
|
||||
@@ -192,13 +192,13 @@ body[showing-chat] #chat-page-wrapper {
|
||||
|
||||
#chat-page > div > #ribbon button.ribbon-button,
|
||||
#chat-page
|
||||
> div
|
||||
> #main
|
||||
> div
|
||||
> .pane-wrapper
|
||||
> .pane
|
||||
> .toolbar
|
||||
button.ribbon-button {
|
||||
> div
|
||||
> #main
|
||||
> div
|
||||
> .pane-wrapper
|
||||
> .pane
|
||||
> .toolbar
|
||||
button.ribbon-button {
|
||||
min-width: calc(var(--control-height) - 2 * var(--gap-between-tiles));
|
||||
width: calc(var(--control-height) - 2 * var(--gap-between-tiles));
|
||||
max-width: calc(var(--control-height) - 2 * var(--gap-between-tiles));
|
||||
@@ -211,14 +211,14 @@ button.ribbon-button {
|
||||
|
||||
#chat-page > div > #ribbon button > .icon,
|
||||
#chat-page
|
||||
> div
|
||||
> #main
|
||||
> div
|
||||
> .pane-wrapper
|
||||
> .pane
|
||||
> .toolbar
|
||||
button
|
||||
> .icon {
|
||||
> div
|
||||
> #main
|
||||
> div
|
||||
> .pane-wrapper
|
||||
> .pane
|
||||
> .toolbar
|
||||
button
|
||||
> .icon {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
@@ -244,13 +244,13 @@ button
|
||||
}
|
||||
|
||||
#chat-page
|
||||
> div
|
||||
> #main
|
||||
> div
|
||||
> .pane-wrapper
|
||||
> .pane
|
||||
> .toolbar
|
||||
:is(input, select, button) {
|
||||
> div
|
||||
> #main
|
||||
> div
|
||||
> .pane-wrapper
|
||||
> .pane
|
||||
> .toolbar
|
||||
:is(input, select, button) {
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
@@ -267,13 +267,14 @@ button
|
||||
gap: var(--content-padding);
|
||||
}
|
||||
|
||||
.modal.task-settings .column-wrapper > .flex-column{
|
||||
.modal.task-settings .column-wrapper > .flex-column {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.modal.task-settings:not([full-description]) :is(#fullscreen-exit, #fullscreen-main) {
|
||||
.modal.task-settings:not([full-description])
|
||||
:is(#fullscreen-exit, #fullscreen-main) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as React from "../react";
|
||||
import { ViewController } from "./viewController";
|
||||
import "./homePage.css";
|
||||
import { ChatViewToggleButton } from "./Components/chatViewToggleButton";
|
||||
@@ -6,6 +5,7 @@ import { TaskPage } from "./ChatPages/taskPage";
|
||||
import { SettingsPage } from "./ChatPages/settingsPage";
|
||||
import { MessagePage } from "./ChatPages/messagePage";
|
||||
import { CalendarPage } from "./ChatPages/calendarPage";
|
||||
import * as React from "../react";
|
||||
import CoreViewModel from "../ViewModel/Global/coreViewModel";
|
||||
import ChatViewModel, { ChatPageTypes } from "../ViewModel/Chat/chatViewModel";
|
||||
|
||||
@@ -96,7 +96,8 @@ export function ChatPage(
|
||||
chatViewModel.connectionViewModel.isConnected
|
||||
}
|
||||
toggle:disabled={
|
||||
chatViewModel.connectionViewModel.hasNoPreviousConnections
|
||||
chatViewModel.connectionViewModel
|
||||
.hasNoPreviousConnections
|
||||
}
|
||||
>
|
||||
<span class="icon">signal_disconnected</span>
|
||||
@@ -129,7 +130,7 @@ export function ChatPage(
|
||||
ChatPageTypes.Settings,
|
||||
chatViewModel,
|
||||
)}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
id="main"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from "../react";
|
||||
import "./chatPage.css";
|
||||
import { ChatPage } from "./chatPage";
|
||||
import * as React from "../react";
|
||||
import CoreViewModel from "../ViewModel/Global/coreViewModel";
|
||||
import ChatListViewModel from "../ViewModel/Chat/chatListViewModel";
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as React from "../react";
|
||||
import "./homePage.css";
|
||||
import { StringToOption } from "./Components/option";
|
||||
import { HomePageButton } from "./Components/homePageButton";
|
||||
import { ChatViewModelToChatEntry } from "./Components/chatEntry";
|
||||
import * as React from "../react";
|
||||
import StorageViewModel from "../ViewModel/Global/storageViewModel";
|
||||
import SettingsViewModel from "../ViewModel/Global/settingsViewModel";
|
||||
import FileTransferViewModel from "../ViewModel/Global/fileTransferViewModel";
|
||||
|
||||
@@ -484,7 +484,7 @@ export const allTranslations: {
|
||||
downloadFileButton: "Herunterladen",
|
||||
importFileButton: "Importieren",
|
||||
decryptImportButton: "Entschlüsseln",
|
||||
incorrectPassphraseError: "Der Schlüssel ist nicht korrekt"
|
||||
incorrectPassphraseError: "Der Schlüssel ist nicht korrekt",
|
||||
},
|
||||
|
||||
storage: {
|
||||
@@ -785,7 +785,7 @@ export const allTranslations: {
|
||||
downloadFileButton: "Descargar",
|
||||
importFileButton: "Importar",
|
||||
decryptImportButton: "Descifrar",
|
||||
incorrectPassphraseError: "La clave no es correcta"
|
||||
incorrectPassphraseError: "La clave no es correcta",
|
||||
},
|
||||
|
||||
storage: {
|
||||
|
||||
@@ -7,7 +7,7 @@ class Tracker<T> {
|
||||
class TrackerMap<T> {
|
||||
trackers = new Map<string, Tracker<T>>();
|
||||
|
||||
private register = (key: string): Tracker<T>=> {
|
||||
private register = (key: string): Tracker<T> => {
|
||||
if (this.trackers.has(key)) {
|
||||
return this.trackers.get(key)!;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as React from "../../react";
|
||||
import ChatViewModel from "./chatViewModel";
|
||||
import SettingsViewModel from "../Global/settingsViewModel";
|
||||
import NotificationViewModel from "../Global/notificationViewModel";
|
||||
import CoreViewModel from "../Global/coreViewModel";
|
||||
import ConnectionViewModel from "../Global/connectionViewModel";
|
||||
import * as React from "../../react";
|
||||
import { IndexManager } from "../../Model/Utility/utility";
|
||||
import ChatModel from "../../Model/Chat/chatModel";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from "../../react";
|
||||
import MessagePageViewModel from "../Pages/messagePageViewModel";
|
||||
import CoreViewModel from "../Global/coreViewModel";
|
||||
import * as React from "../../react";
|
||||
import {
|
||||
ChatMessage,
|
||||
ChatMessageReaction,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as React from "../../react";
|
||||
import ChatListViewModel from "./chatListViewModel";
|
||||
import TaskPageViewModel from "../Pages/taskPageViewModel";
|
||||
import SettingsPageViewModel from "../Pages/settingsPageViewModel";
|
||||
@@ -10,6 +9,7 @@ import SettingsViewModel from "../Global/settingsViewModel";
|
||||
import NotificationViewModel from "../Global/notificationViewModel";
|
||||
import CoreViewModel, { Context, ContextHost } from "../Global/coreViewModel";
|
||||
import ConnectionViewModel from "../Global/connectionViewModel";
|
||||
import * as React from "../../react";
|
||||
import { Colors } from "../../colors";
|
||||
import { CommonKeys } from "../../View/keystrokes";
|
||||
import { Entry } from "../../View/Components/option";
|
||||
|
||||
Binary file not shown.
@@ -1,5 +1,5 @@
|
||||
import * as React from "../../react";
|
||||
import CoreViewModel from "./coreViewModel";
|
||||
import * as React from "../../react";
|
||||
|
||||
export default class ConnectionViewModel {
|
||||
// state
|
||||
|
||||
@@ -40,7 +40,7 @@ export default class CoreViewModel {
|
||||
return;
|
||||
|
||||
while (this.contexts.length > 0) {
|
||||
const currentContext: Context|undefined= this.context;
|
||||
const currentContext: Context | undefined = this.context;
|
||||
if (!currentContext) return;
|
||||
currentContext.handleContextClose(fromHistoryEvent);
|
||||
this.contextStack.delete(currentContext.contextId);
|
||||
@@ -49,7 +49,8 @@ export default class CoreViewModel {
|
||||
};
|
||||
|
||||
handleKeyDown = (e: KeyboardEvent): void => {
|
||||
if (!(e instanceof KeyboardEvent)) return console.trace("NOT A KEY EVENT");
|
||||
if (!(e instanceof KeyboardEvent))
|
||||
return console.trace("NOT A KEY EVENT");
|
||||
if (CoreViewModel.checkIsKeystroke(e) == false) return;
|
||||
console.log(e.key);
|
||||
e.preventDefault();
|
||||
@@ -166,9 +167,8 @@ export class ContextHost<T> extends Context {
|
||||
|
||||
const selection = this.contextSelection;
|
||||
if (!selection) return;
|
||||
const selectedContext: Context | undefined = this.contexts.get(
|
||||
selection
|
||||
);
|
||||
const selectedContext: Context | undefined =
|
||||
this.contexts.get(selection);
|
||||
if (!selectedContext) return;
|
||||
if (selectedContext != this.currentContext.value) {
|
||||
this.closeCurrentContext();
|
||||
@@ -178,7 +178,10 @@ export class ContextHost<T> extends Context {
|
||||
this.currentContext.value = selectedContext;
|
||||
};
|
||||
|
||||
constructor(contextDebugDescription: string, public coreViewModel: CoreViewModel) {
|
||||
constructor(
|
||||
contextDebugDescription: string,
|
||||
public coreViewModel: CoreViewModel,
|
||||
) {
|
||||
super(contextDebugDescription);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from "../../react";
|
||||
import CoreViewModel, { Context } from "./coreViewModel";
|
||||
import * as React from "../../react";
|
||||
import { CommonKeys } from "../../View/keystrokes";
|
||||
import StorageModel, {
|
||||
StorageModelSubPaths,
|
||||
@@ -54,7 +54,9 @@ export default class FileTransferViewModel extends Context {
|
||||
exportKey: React.State<string> = new React.State("");
|
||||
exportKeyConfirmation: React.State<string> = new React.State("");
|
||||
|
||||
importedFile: React.State<File | undefined> = new React.State<File | undefined>(undefined);
|
||||
importedFile: React.State<File | undefined> = new React.State<
|
||||
File | undefined
|
||||
>(undefined);
|
||||
importedFileString: React.State<string> = new React.State("");
|
||||
importKey: React.State<string> = new React.State("");
|
||||
isImportKeyCorrect: React.State<boolean> = new React.State(true);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from "../../react";
|
||||
import { ChatPageTypes } from "../Chat/chatViewModel";
|
||||
import ChatListViewModel from "../Chat/chatListViewModel";
|
||||
import * as React from "../../react";
|
||||
import ChatModel, { ChatMessage } from "../../Model/Chat/chatModel";
|
||||
|
||||
export default class NotificationViewModel {
|
||||
@@ -35,7 +35,7 @@ export default class NotificationViewModel {
|
||||
};
|
||||
|
||||
openNotification = () => {
|
||||
const notification: Notification|undefined = this.marquee.value;
|
||||
const notification: Notification | undefined = this.marquee.value;
|
||||
if (notification == undefined) return;
|
||||
|
||||
const chat = [
|
||||
@@ -56,7 +56,8 @@ export default class NotificationViewModel {
|
||||
return this.stopLoop();
|
||||
}
|
||||
|
||||
const notification: Notification|undefined = this.messagesInMarquee.shift();
|
||||
const notification: Notification | undefined =
|
||||
this.messagesInMarquee.shift();
|
||||
if (!notification) return;
|
||||
this.seenMessageIds.delete(notification.messageId);
|
||||
this.marquee.value = notification;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from "../../react";
|
||||
import CoreViewModel, { Context } from "./coreViewModel";
|
||||
import * as React from "../../react";
|
||||
import { CommonKeys } from "../../View/keystrokes";
|
||||
import { Languages, ThemeSettings } from "../../Model/Global/settingsModel";
|
||||
|
||||
@@ -13,12 +13,12 @@ export default class SettingsViewModel extends Context {
|
||||
requiresReload = new React.State<boolean>(false);
|
||||
|
||||
firstDayOfWeek: React.State<string> = new React.State("0");
|
||||
language: React.State<Languages | string> = new React.State<Languages | string>(
|
||||
Languages.English,
|
||||
);
|
||||
theme: React.State<ThemeSettings | string> = new React.State<ThemeSettings | string>(
|
||||
ThemeSettings.System,
|
||||
);
|
||||
language: React.State<Languages | string> = new React.State<
|
||||
Languages | string
|
||||
>(Languages.English);
|
||||
theme: React.State<ThemeSettings | string> = new React.State<
|
||||
ThemeSettings | string
|
||||
>(ThemeSettings.System);
|
||||
|
||||
// guards
|
||||
cannotSetName: React.State<boolean> = React.createProxyState(
|
||||
@@ -82,23 +82,34 @@ export default class SettingsViewModel extends Context {
|
||||
} else if (hour < 14) {
|
||||
return DynamicSceneDay;
|
||||
} else if (hour < 17) {
|
||||
return DynamicSceneAfternoon
|
||||
return DynamicSceneAfternoon;
|
||||
} else {
|
||||
return DynamicSceneSunset
|
||||
}
|
||||
return DynamicSceneSunset;
|
||||
}
|
||||
};
|
||||
|
||||
setScene = (scene: DynamicScene): void => {
|
||||
document.body.style.setProperty("--backdrop-grass-filter", `brightness(${scene.brightness})`);
|
||||
document.body.style.setProperty(
|
||||
"--backdrop-grass-filter",
|
||||
`brightness(${scene.brightness})`,
|
||||
);
|
||||
|
||||
function setSkyColor(tone: 1|2, hue: number, saturation: number, luma: number) {
|
||||
document.body.style.setProperty(`--sky-${tone}`, `hsl(${hue}, ${saturation}%, ${luma}%)`);
|
||||
function setSkyColor(
|
||||
tone: 1 | 2,
|
||||
hue: number,
|
||||
saturation: number,
|
||||
luma: number,
|
||||
) {
|
||||
document.body.style.setProperty(
|
||||
`--sky-${tone}`,
|
||||
`hsl(${hue}, ${saturation}%, ${luma}%)`,
|
||||
);
|
||||
}
|
||||
setSkyColor(1, scene.hue1, scene.saturation, scene.luma);
|
||||
setSkyColor(2, scene.hue2, scene.saturation - 10, scene.luma-10);
|
||||
setSkyColor(2, scene.hue2, scene.saturation - 10, scene.luma - 10);
|
||||
|
||||
document.body.setAttribute("theme", scene.baseTheme);
|
||||
}
|
||||
};
|
||||
|
||||
// exit
|
||||
close = (): void => {
|
||||
@@ -140,9 +151,9 @@ export default class SettingsViewModel extends Context {
|
||||
this.applyTheme(),
|
||||
);
|
||||
const seconds = new Date().getSeconds();
|
||||
const secondsUntilNewMinute = 60-seconds;
|
||||
const secondsUntilNewMinute = 60 - seconds;
|
||||
setTimeout(() => {
|
||||
setInterval(this.applyTheme, 1000*60);
|
||||
setInterval(this.applyTheme, 1000 * 60);
|
||||
}, secondsUntilNewMinute);
|
||||
|
||||
// keystrokes
|
||||
@@ -176,40 +187,40 @@ export interface DynamicScene {
|
||||
export const DynamicSceneNight: DynamicScene = {
|
||||
hue1: 215,
|
||||
hue2: 220,
|
||||
saturation : 50,
|
||||
saturation: 50,
|
||||
luma: 25,
|
||||
brightness: 0.4,
|
||||
baseTheme: ThemeSettings.Dark,
|
||||
}
|
||||
};
|
||||
export const DynamicSceneSunrise: DynamicScene = {
|
||||
hue1: 45,
|
||||
hue2: 190,
|
||||
saturation : 100,
|
||||
saturation: 100,
|
||||
luma: 50,
|
||||
brightness: 1.2,
|
||||
baseTheme: ThemeSettings.Light,
|
||||
}
|
||||
};
|
||||
export const DynamicSceneDay: DynamicScene = {
|
||||
hue1: 190,
|
||||
hue2: 180,
|
||||
saturation : 100,
|
||||
saturation: 100,
|
||||
luma: 70,
|
||||
brightness: 1.4,
|
||||
baseTheme: ThemeSettings.Light,
|
||||
}
|
||||
};
|
||||
export const DynamicSceneAfternoon: DynamicScene = {
|
||||
hue1: 195,
|
||||
hue2: 220,
|
||||
saturation : 100,
|
||||
saturation: 100,
|
||||
luma: 50,
|
||||
brightness: 0.8,
|
||||
baseTheme: ThemeSettings.Light,
|
||||
}
|
||||
};
|
||||
export const DynamicSceneSunset: DynamicScene = {
|
||||
hue1: 20,
|
||||
hue2: 260,
|
||||
saturation : 100,
|
||||
saturation: 100,
|
||||
luma: 50,
|
||||
brightness: 0.4,
|
||||
baseTheme: ThemeSettings.Dark,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from "../../react";
|
||||
import CoreViewModel, { Context } from "./coreViewModel";
|
||||
import * as React from "../../react";
|
||||
import { CommonKeys } from "../../View/keystrokes";
|
||||
import StorageModel, {
|
||||
PATH_COMPONENT_SEPARATOR,
|
||||
|
||||
Binary file not shown.
@@ -1,10 +1,10 @@
|
||||
import * as React from "../../react";
|
||||
import TaskViewModel from "./taskViewModel";
|
||||
import TaskPageViewModel from "./taskPageViewModel";
|
||||
import TaskContainingPageViewModel from "./taskContainingPageViewModel";
|
||||
import SearchViewModel from "../Utility/searchViewModel";
|
||||
import CoreViewModel from "../Global/coreViewModel";
|
||||
import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel";
|
||||
import * as React from "../../react";
|
||||
import { Colors } from "../../colors";
|
||||
import { CommonKeys } from "../../View/keystrokes";
|
||||
import BoardsAndTasksModel, {
|
||||
@@ -115,11 +115,7 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
||||
searchTerm,
|
||||
];
|
||||
this.coreViewModel.storageModel.write(suggestionPath, "");
|
||||
if (
|
||||
!this.searchSuggestions.value.has(
|
||||
searchTerm,
|
||||
) && searchTerm != ""
|
||||
) {
|
||||
if (!this.searchSuggestions.value.has(searchTerm) && searchTerm != "") {
|
||||
this.searchSuggestions.add(searchTerm);
|
||||
}
|
||||
|
||||
@@ -133,7 +129,7 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
||||
searchTerm,
|
||||
];
|
||||
this.coreViewModel.storageModel.remove(suggestionPath);
|
||||
}
|
||||
};
|
||||
|
||||
// view
|
||||
showTask = (taskFileContent: TaskFileContent): void => {
|
||||
@@ -234,7 +230,7 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
||||
const dirPath: string[] = this.getPreviousSearchesPath();
|
||||
const searches: string[] =
|
||||
this.coreViewModel.storageModel.list(dirPath);
|
||||
this.searchSuggestions.add(...searches.filter(x => x != ""));
|
||||
this.searchSuggestions.add(...searches.filter((x) => x != ""));
|
||||
};
|
||||
|
||||
restoreSearch = (): void => {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as React from "../../react";
|
||||
import TaskViewModel from "./taskViewModel";
|
||||
import TaskContainingPageViewModel from "./taskContainingPageViewModel";
|
||||
import CoreViewModel from "../Global/coreViewModel";
|
||||
import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel";
|
||||
import * as React from "../../react";
|
||||
import { CommonKeys } from "../../View/keystrokes";
|
||||
import CalendarModel, { MonthGrid } from "../../Model/Files/calendarModel";
|
||||
import BoardsAndTasksModel, {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as React from "../../react";
|
||||
import SearchViewModel from "../Utility/searchViewModel";
|
||||
import CoreViewModel, { Context } from "../Global/coreViewModel";
|
||||
import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel";
|
||||
import ChatMessageViewModel from "../Chat/chatMessageViewModel";
|
||||
import * as React from "../../react";
|
||||
import { CommonKeys } from "../../View/keystrokes";
|
||||
import {
|
||||
ChatMessage,
|
||||
@@ -68,7 +68,9 @@ export default class MessagePageViewModel extends Context {
|
||||
);
|
||||
};
|
||||
|
||||
setReply = (chatMessageViewModel: ChatMessageViewModel|undefined): void => {
|
||||
setReply = (
|
||||
chatMessageViewModel: ChatMessageViewModel | undefined,
|
||||
): void => {
|
||||
this.replyingMessage.value = chatMessageViewModel;
|
||||
this.setFocus();
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from "../../react";
|
||||
import CoreViewModel, { Context } from "../Global/coreViewModel";
|
||||
import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel";
|
||||
import * as React from "../../react";
|
||||
import { Colors } from "../../colors";
|
||||
|
||||
export default class SettingsPageViewModel extends Context {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from "../../react";
|
||||
import TaskViewModel from "./taskViewModel";
|
||||
import CoreViewModel, { Context } from "../Global/coreViewModel";
|
||||
import ChatViewModel from "../Chat/chatViewModel";
|
||||
import * as React from "../../react";
|
||||
import { IndexManager } from "../../Model/Utility/utility";
|
||||
import BoardsAndTasksModel, {
|
||||
TaskFileContent,
|
||||
@@ -21,7 +21,6 @@ export default class TaskContainingPageViewModel extends Context {
|
||||
taskCategorySuggestions: React.ListState<string> = new React.ListState();
|
||||
taskStatusSuggestions: React.ListState<string> = new React.ListState();
|
||||
|
||||
|
||||
// methods
|
||||
createTaskFromBoardId = (boardId: string): void => {
|
||||
const taskFileContent: TaskFileContent =
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as React from "../../react";
|
||||
import BoardViewModel from "./boardViewModel";
|
||||
import CoreViewModel, { ContextHost } from "../Global/coreViewModel";
|
||||
import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel";
|
||||
import * as React from "../../react";
|
||||
import { CommonKeys } from "../../View/keystrokes";
|
||||
import { IndexManager } from "../../Model/Utility/utility";
|
||||
import implementFilter, { IndexManager } from "../../Model/Utility/utility";
|
||||
import BoardsAndTasksModel, {
|
||||
BoardInfoFileContent,
|
||||
} from "../../Model/Files/boardsAndTasksModel";
|
||||
@@ -26,8 +26,9 @@ export default class TaskPageViewModel extends ContextHost<string> {
|
||||
};
|
||||
|
||||
// state
|
||||
newBoardNameInput: React.State<string> = new React.State("");
|
||||
boardQuery: React.State<string> = new React.State("");
|
||||
boardViewModels: React.MapState<BoardViewModel> = new React.MapState();
|
||||
boardMatches: React.ListState<BoardViewModel> = new React.ListState();
|
||||
isShowingBoadList: React.State<boolean> = new React.State(true);
|
||||
selectedBoardId: React.State<string | undefined> = new React.State<any>(
|
||||
undefined,
|
||||
@@ -47,8 +48,8 @@ export default class TaskPageViewModel extends ContextHost<string> {
|
||||
|
||||
// guards
|
||||
cannotCreateBoard: React.State<boolean> = React.createProxyState(
|
||||
[this.newBoardNameInput],
|
||||
() => this.newBoardNameInput.value == "",
|
||||
[this.boardQuery],
|
||||
() => this.boardQuery.value == "",
|
||||
);
|
||||
|
||||
// methods
|
||||
@@ -56,8 +57,8 @@ export default class TaskPageViewModel extends ContextHost<string> {
|
||||
if (this.cannotCreateBoard.value == true) return;
|
||||
|
||||
const boardInfoFileContent: BoardInfoFileContent =
|
||||
this.boardsAndTasksModel.createBoard(this.newBoardNameInput.value);
|
||||
this.newBoardNameInput.value = "";
|
||||
this.boardsAndTasksModel.createBoard(this.boardQuery.value);
|
||||
this.boardQuery.value = "";
|
||||
|
||||
this.showBoardInList(boardInfoFileContent);
|
||||
this.boardsAndTasksModel.updateBoardAndSend(boardInfoFileContent);
|
||||
@@ -177,6 +178,9 @@ export default class TaskPageViewModel extends ContextHost<string> {
|
||||
|
||||
this.chatViewModel = chatViewModel;
|
||||
|
||||
// filter
|
||||
implementFilter(this.boardViewModels, this.boardMatches, this.boardQuery, (board) => board.name.value);
|
||||
|
||||
// context
|
||||
this.chatViewModel.registerContext(ChatPageTypes.Tasks, this);
|
||||
this.selectedBoardId.subscribeSilent(this.updateContexts);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from "../../react";
|
||||
import TaskContainingPageViewModel from "./taskContainingPageViewModel";
|
||||
import CoreViewModel, { Context } from "../Global/coreViewModel";
|
||||
import ChatViewModel from "../Chat/chatViewModel";
|
||||
import * as React from "../../react";
|
||||
import { ViewController } from "../../View/viewController";
|
||||
import { CommonKeys } from "../../View/keystrokes";
|
||||
import { localeCompare, padZero } from "../../Model/Utility/utility";
|
||||
@@ -58,7 +58,9 @@ export default class TaskViewModel extends Context {
|
||||
selectedVersionId: React.State<string> = new React.State("");
|
||||
versionIds: React.ListState<string> = new React.ListState();
|
||||
|
||||
isPresentingFullScreenDescription: React.State<boolean> = new React.State(false);
|
||||
isPresentingFullScreenDescription: React.State<boolean> = new React.State(
|
||||
false,
|
||||
);
|
||||
|
||||
// methods
|
||||
dragStart = (event: DragEvent): void => {
|
||||
@@ -100,11 +102,11 @@ export default class TaskViewModel extends Context {
|
||||
|
||||
openFullscreenDecription = (): void => {
|
||||
this.isPresentingFullScreenDescription.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
closeFullscreenDecription = (): void => {
|
||||
this.isPresentingFullScreenDescription.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
updateIndex = (): void => {
|
||||
const index: number =
|
||||
@@ -118,7 +120,9 @@ export default class TaskViewModel extends Context {
|
||||
this.category.value,
|
||||
) == false
|
||||
) {
|
||||
this.containingViewModel.taskCategorySuggestions.add(this.category.value);
|
||||
this.containingViewModel.taskCategorySuggestions.add(
|
||||
this.category.value,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -126,7 +130,9 @@ export default class TaskViewModel extends Context {
|
||||
this.status.value,
|
||||
) == false
|
||||
) {
|
||||
this.containingViewModel.taskStatusSuggestions.add(this.status.value);
|
||||
this.containingViewModel.taskStatusSuggestions.add(
|
||||
this.status.value,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -15,7 +15,10 @@ export default class SearchViewModel<T> {
|
||||
[this.searchInput],
|
||||
() => this.searchInput.value == "",
|
||||
);
|
||||
hasNoSuggestions: React.State<boolean> = React.createProxyState([this.suggestions], ()=>this.suggestions.value.size == 0);
|
||||
hasNoSuggestions: React.State<boolean> = React.createProxyState(
|
||||
[this.suggestions],
|
||||
() => this.suggestions.value.size == 0,
|
||||
);
|
||||
|
||||
// methods
|
||||
search = (searchTerm: string): void => {
|
||||
@@ -37,11 +40,11 @@ export default class SearchViewModel<T> {
|
||||
|
||||
clear = (): void => {
|
||||
this.searchInput.value = "";
|
||||
}
|
||||
};
|
||||
|
||||
deleteSuggestion = (suggestion: string): void => {
|
||||
this.suggestions.remove(suggestion);
|
||||
}
|
||||
};
|
||||
|
||||
// init
|
||||
constructor(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from "../../react";
|
||||
import TaskViewModel from "../Pages/taskViewModel";
|
||||
import * as React from "../../react";
|
||||
|
||||
export default class TaskPropertyBulkChangeViewModel {
|
||||
taskViewModels: React.ListState<TaskViewModel>;
|
||||
|
||||
+5
-3
@@ -44,7 +44,9 @@ main {
|
||||
}
|
||||
|
||||
body {
|
||||
transition: --sky-1 1s, --sky-2 5s;
|
||||
transition:
|
||||
--sky-1 1s,
|
||||
--sky-2 5s;
|
||||
}
|
||||
|
||||
#background-wrapper #grass-1 {
|
||||
@@ -52,7 +54,7 @@ body {
|
||||
--color-2: hsl(150, 60%, 25%);
|
||||
|
||||
filter: var(--backdrop-grass-filter);
|
||||
opacity: .7;
|
||||
opacity: 0.7;
|
||||
|
||||
background-image: linear-gradient(
|
||||
350deg,
|
||||
@@ -67,7 +69,7 @@ body {
|
||||
--color-2: hsl(110, 60%, 35%);
|
||||
|
||||
filter: var(--backdrop-grass-filter);
|
||||
opacity: .7;
|
||||
opacity: 0.7;
|
||||
|
||||
background-image: linear-gradient(
|
||||
9deg,
|
||||
|
||||
+16
-16
@@ -85,7 +85,7 @@ export class ListState<T> extends State<Set<T>> {
|
||||
items.forEach((item) => {
|
||||
this.value.delete(item);
|
||||
|
||||
this.genericRemovalHandlers.forEach(handler => handler(item));
|
||||
this.genericRemovalHandlers.forEach((handler) => handler(item));
|
||||
if (!this.removalHandlers.has(item)) return;
|
||||
this.removalHandlers.get(item)!.forEach((handler) => handler(item));
|
||||
this.removalHandlers.delete(item);
|
||||
@@ -146,7 +146,7 @@ export class MapState<T> extends State<Map<string, T>> {
|
||||
this.value.delete(key);
|
||||
this.callSubscriptions();
|
||||
|
||||
this.genericRemovalHandlers.forEach(handler => handler(item));
|
||||
this.genericRemovalHandlers.forEach((handler) => handler(item));
|
||||
if (!this.removalHandlers.has(item)) return;
|
||||
this.removalHandlers.get(item)!.forEach((handler) => handler(item));
|
||||
this.removalHandlers.delete(item);
|
||||
@@ -183,18 +183,18 @@ export class MapState<T> extends State<Map<string, T>> {
|
||||
// UTILITY
|
||||
export function createProxyState<T>(
|
||||
statesToSubscibe: State<any>[],
|
||||
fn: () => T
|
||||
fn: () => T,
|
||||
): State<T> {
|
||||
const proxyState = new State<T>(fn());
|
||||
statesToSubscibe.forEach((state) =>
|
||||
state.subscribeSilent(() => (proxyState.value = fn()))
|
||||
state.subscribeSilent(() => (proxyState.value = fn())),
|
||||
);
|
||||
return proxyState;
|
||||
}
|
||||
|
||||
export function bulkSubscribe(
|
||||
statesToSubscibe: State<any>[],
|
||||
fn: () => void
|
||||
fn: () => void,
|
||||
): void {
|
||||
statesToSubscibe.forEach((state) => state.subscribeSilent(fn));
|
||||
}
|
||||
@@ -208,7 +208,7 @@ function persistState(localStorageKey: string, state: State<any>) {
|
||||
|
||||
export function restoreState<T>(
|
||||
localStorageKey: string,
|
||||
initialStateValue: T
|
||||
initialStateValue: T,
|
||||
): State<T> {
|
||||
const storedString =
|
||||
localStorage.getItem(localStorageKey) ??
|
||||
@@ -223,7 +223,7 @@ export function restoreState<T>(
|
||||
|
||||
export function restoreListState<T>(
|
||||
localStorageKey: string,
|
||||
initialItems: any[] = []
|
||||
initialItems: any[] = [],
|
||||
): ListState<T> {
|
||||
const storedString = localStorage.getItem(localStorageKey) ?? "";
|
||||
|
||||
@@ -241,7 +241,7 @@ export function restoreListState<T>(
|
||||
|
||||
export function restoreMapState<T>(
|
||||
localStorageKey: string,
|
||||
initialItems: [string, any][] = []
|
||||
initialItems: [string, any][] = [],
|
||||
): MapState<T> {
|
||||
const storedString = localStorage.getItem(localStorageKey) ?? "";
|
||||
|
||||
@@ -298,13 +298,13 @@ export function createElement(
|
||||
if (e.metaKey == false && e.ctrlKey == false) return;
|
||||
if (e.key != directiveValue) return;
|
||||
value(e);
|
||||
})
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "subscribe": {
|
||||
const state = value as State<any>;
|
||||
state.subscribe(
|
||||
(newValue) => (element[directiveValue] = newValue)
|
||||
(newValue) => (element[directiveValue] = newValue),
|
||||
);
|
||||
|
||||
break;
|
||||
@@ -312,11 +312,11 @@ export function createElement(
|
||||
case "bind": {
|
||||
const state = value as State<any>;
|
||||
state.subscribe(
|
||||
(newValue) => (element[directiveValue] = newValue)
|
||||
(newValue) => (element[directiveValue] = newValue),
|
||||
);
|
||||
element.addEventListener(
|
||||
"input",
|
||||
() => (state.value = (element as any)[directiveValue])
|
||||
() => (state.value = (element as any)[directiveValue]),
|
||||
);
|
||||
break;
|
||||
}
|
||||
@@ -324,7 +324,7 @@ export function createElement(
|
||||
if (value.subscribe) {
|
||||
const state = value as State<any>;
|
||||
state.subscribe((newValue) =>
|
||||
element.toggleAttribute(directiveValue, newValue)
|
||||
element.toggleAttribute(directiveValue, newValue),
|
||||
);
|
||||
} else {
|
||||
element.toggleAttribute(directiveValue, value);
|
||||
@@ -334,7 +334,7 @@ export function createElement(
|
||||
case "set": {
|
||||
const state = value as State<any>;
|
||||
state.subscribe((newValue) =>
|
||||
element.setAttribute(directiveValue, newValue)
|
||||
element.setAttribute(directiveValue, newValue),
|
||||
);
|
||||
break;
|
||||
}
|
||||
@@ -353,13 +353,13 @@ export function createElement(
|
||||
try {
|
||||
const [listState, toElement] = value as [
|
||||
listState: ListState<any>,
|
||||
StateItemConverter<any>
|
||||
StateItemConverter<any>,
|
||||
];
|
||||
|
||||
listState.handleAddition((newItem) => {
|
||||
const child = toElement(newItem);
|
||||
listState.handleRemoval(newItem, () =>
|
||||
child.remove()
|
||||
child.remove(),
|
||||
);
|
||||
|
||||
if (directiveValue == "append") {
|
||||
|
||||
Vendored
+1
-2
@@ -1,2 +1 @@
|
||||
declare module "*.css" {
|
||||
}
|
||||
declare module "*.css" {}
|
||||
|
||||
Reference in New Issue
Block a user