add method for filtering
This commit is contained in:
@@ -70,150 +70,150 @@ export default class FileModel {
|
|||||||
const didStore: boolean = this.storeFileContent(fileContent);
|
const didStore: boolean = this.storeFileContent(fileContent);
|
||||||
if (didStore == false) return;
|
if (didStore == false) return;
|
||||||
|
|
||||||
switch (fileContent.type) {
|
switch (fileContent.type) {
|
||||||
case "board-info":
|
case "board-info":
|
||||||
case "task":
|
case "task":
|
||||||
this.boardsAndTasksModel.handleFileContent(fileContent);
|
this.boardsAndTasksModel.handleFileContent(fileContent);
|
||||||
break;
|
break;
|
||||||
case "reaction":
|
case "reaction":
|
||||||
this.chatModel.handleReaction(fileContent);
|
this.chatModel.handleReaction(fileContent);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// methods
|
// methods
|
||||||
readonly addFileContentAndSend = (
|
readonly addFileContentAndSend = (
|
||||||
fileContent: FileContent<string>,
|
fileContent: FileContent<string>,
|
||||||
): void => {
|
): void => {
|
||||||
this.handleFileContent(fileContent);
|
this.handleFileContent(fileContent);
|
||||||
this.chatModel.sendMessage("", undefined, fileContent);
|
this.chatModel.sendMessage("", undefined, fileContent);
|
||||||
};
|
};
|
||||||
|
|
||||||
// storage
|
// storage
|
||||||
readonly storeFileContent = (fileContent: FileContent<string>): boolean => {
|
readonly storeFileContent = (fileContent: FileContent<string>): boolean => {
|
||||||
const fileContentPath: string[] = this.getFileContentPath(
|
const fileContentPath: string[] = this.getFileContentPath(
|
||||||
fileContent.fileId,
|
fileContent.fileId,
|
||||||
fileContent.fileContentId,
|
fileContent.fileContentId,
|
||||||
);
|
);
|
||||||
|
|
||||||
// check if fileContent already exists
|
// check if fileContent already exists
|
||||||
const existingFileContent: string | null =
|
const existingFileContent: string | null =
|
||||||
this.storageModel.read(fileContentPath);
|
this.storageModel.read(fileContentPath);
|
||||||
if (existingFileContent != null) return false;
|
if (existingFileContent != null) return false;
|
||||||
|
|
||||||
const stringifiedContent: string = stringify(fileContent);
|
const stringifiedContent: string = stringify(fileContent);
|
||||||
this.storageModel.write(fileContentPath, stringifiedContent);
|
this.storageModel.write(fileContentPath, stringifiedContent);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
readonly listFileIds = (): string[] => {
|
readonly listFileIds = (): string[] => {
|
||||||
return this.storageModel.list(this.basePath);
|
return this.storageModel.list(this.basePath);
|
||||||
};
|
};
|
||||||
|
|
||||||
readonly listFileContentIds = (fileId: string): string[] => {
|
readonly listFileContentIds = (fileId: string): string[] => {
|
||||||
const filePath: string[] = this.getFilePath(fileId);
|
const filePath: string[] = this.getFilePath(fileId);
|
||||||
return this.storageModel.list(filePath);
|
return this.storageModel.list(filePath);
|
||||||
};
|
};
|
||||||
|
|
||||||
readonly selectLatestFileContentId = (
|
readonly selectLatestFileContentId = (
|
||||||
fileContentIds: string[],
|
fileContentIds: string[],
|
||||||
): string | undefined => {
|
): string | undefined => {
|
||||||
return fileContentIds[fileContentIds.length - 1];
|
return fileContentIds[fileContentIds.length - 1];
|
||||||
};
|
};
|
||||||
|
|
||||||
readonly getFileContent = <T extends FileContent<string>>(
|
readonly getFileContent = <T extends FileContent<string>>(
|
||||||
fileId: string,
|
fileId: string,
|
||||||
fileContentName: string,
|
fileContentName: string,
|
||||||
reference: T,
|
reference: T,
|
||||||
): T | null => {
|
): T | null => {
|
||||||
const filePath: string[] = this.getFileContentPath(
|
const filePath: string[] = this.getFileContentPath(
|
||||||
fileId,
|
fileId,
|
||||||
fileContentName,
|
fileContentName,
|
||||||
);
|
);
|
||||||
const fileContentOrNull: T | null = this.storageModel.readStringifiable(
|
const fileContentOrNull: T | null = this.storageModel.readStringifiable(
|
||||||
filePath,
|
filePath,
|
||||||
reference,
|
reference,
|
||||||
);
|
);
|
||||||
return fileContentOrNull;
|
return fileContentOrNull;
|
||||||
};
|
};
|
||||||
|
|
||||||
readonly getLatestFileContent = <T extends FileContent<string>>(
|
readonly getLatestFileContent = <T extends FileContent<string>>(
|
||||||
fileId: string,
|
fileId: string,
|
||||||
reference: T,
|
reference: T,
|
||||||
): T | null => {
|
): T | null => {
|
||||||
const fileContentsIds: string[] = this.listFileContentIds(fileId);
|
const fileContentsIds: string[] = this.listFileContentIds(fileId);
|
||||||
const latestFileContentId: string | undefined =
|
const latestFileContentId: string | undefined =
|
||||||
this.selectLatestFileContentId(fileContentsIds);
|
this.selectLatestFileContentId(fileContentsIds);
|
||||||
if (latestFileContentId == undefined) return null;
|
if (latestFileContentId == undefined) return null;
|
||||||
|
|
||||||
const fileContent: T | null = this.getFileContent(
|
const fileContent: T | null = this.getFileContent(
|
||||||
fileId,
|
fileId,
|
||||||
latestFileContentId,
|
latestFileContentId,
|
||||||
reference,
|
reference,
|
||||||
);
|
);
|
||||||
return fileContent;
|
return fileContent;
|
||||||
};
|
};
|
||||||
|
|
||||||
// init
|
// init
|
||||||
constructor(
|
constructor(
|
||||||
storageModel: StorageModel,
|
storageModel: StorageModel,
|
||||||
settingsModel: SettingsModel,
|
settingsModel: SettingsModel,
|
||||||
chatModel: ChatModel,
|
chatModel: ChatModel,
|
||||||
) {
|
) {
|
||||||
this.chatModel = chatModel;
|
this.chatModel = chatModel;
|
||||||
this.settingsModel = settingsModel;
|
this.settingsModel = settingsModel;
|
||||||
this.storageModel = storageModel;
|
this.storageModel = storageModel;
|
||||||
|
|
||||||
this.boardsAndTasksModel = new BoardsAndTasksModel(
|
this.boardsAndTasksModel = new BoardsAndTasksModel(
|
||||||
this.storageModel,
|
this.storageModel,
|
||||||
this.settingsModel,
|
this.settingsModel,
|
||||||
chatModel,
|
chatModel,
|
||||||
this,
|
this,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// utility
|
// utility
|
||||||
static generateFileContentId = (creationDate: string): string => {
|
static generateFileContentId = (creationDate: string): string => {
|
||||||
return creationDate + v4();
|
return creationDate + v4();
|
||||||
};
|
};
|
||||||
|
|
||||||
static createFileContent = <T extends string>(
|
static createFileContent = <T extends string>(
|
||||||
fileId: string,
|
fileId: string,
|
||||||
type: T,
|
type: T,
|
||||||
): FileContent<T> => {
|
): FileContent<T> => {
|
||||||
const creationDate: string = createTimestamp();
|
const creationDate: string = createTimestamp();
|
||||||
const fileContentId: string =
|
const fileContentId: string =
|
||||||
FileModel.generateFileContentId(creationDate);
|
FileModel.generateFileContentId(creationDate);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dataVersion: DATA_VERSION,
|
dataVersion: DATA_VERSION,
|
||||||
|
|
||||||
fileId,
|
fileId,
|
||||||
fileContentId,
|
fileContentId,
|
||||||
creationDate,
|
creationDate,
|
||||||
type,
|
type,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// paths
|
// paths
|
||||||
export enum FileModelSubPath {
|
export enum FileModelSubPath {
|
||||||
Data = "data",
|
Data = "data",
|
||||||
Model = "model",
|
Model = "model",
|
||||||
ModelView = "view",
|
ModelView = "view",
|
||||||
ModelTask = "tasks",
|
ModelTask = "tasks",
|
||||||
ModelCalendar = "calendar",
|
ModelCalendar = "calendar",
|
||||||
}
|
}
|
||||||
|
|
||||||
// types
|
// types
|
||||||
export interface FileContent<T extends string>
|
export interface FileContent<T extends string>
|
||||||
extends ValidObject, StringEntryObject {
|
extends ValidObject, StringEntryObject {
|
||||||
readonly fileId: string;
|
readonly fileId: string;
|
||||||
readonly fileContentId: string;
|
readonly fileContentId: string;
|
||||||
readonly creationDate: string;
|
readonly creationDate: string;
|
||||||
|
|
||||||
readonly type: T;
|
readonly type: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
// references
|
// references
|
||||||
export const FileContentReference: FileContent<string> = {
|
export const FileContentReference: FileContent<string> = {
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export default class FileTransferModel {
|
|||||||
};
|
};
|
||||||
|
|
||||||
readonly prepareToSend = (): void => {
|
readonly prepareToSend = (): void => {
|
||||||
if (!this.transferData) return;
|
if (!this.transferData) return;
|
||||||
this.direction = TransferDirections.Send;
|
this.direction = TransferDirections.Send;
|
||||||
this.connectionModel.addChannel(this.transferData.channel);
|
this.connectionModel.addChannel(this.transferData.channel);
|
||||||
};
|
};
|
||||||
@@ -157,8 +157,7 @@ export default class FileTransferModel {
|
|||||||
};
|
};
|
||||||
|
|
||||||
readonly prepareFileForSending = (filePath: string[]): string => {
|
readonly prepareFileForSending = (filePath: string[]): string => {
|
||||||
const fileContent: string | null =
|
const fileContent: string | null = this.storageModel.read(filePath);
|
||||||
this.storageModel.read(filePath);
|
|
||||||
if (fileContent == null) return "";
|
if (fileContent == null) return "";
|
||||||
|
|
||||||
const fileData: FileData = {
|
const fileData: FileData = {
|
||||||
@@ -169,7 +168,7 @@ export default class FileTransferModel {
|
|||||||
};
|
};
|
||||||
|
|
||||||
readonly sendFile = async (stringifiedFileData: string): Promise<void> => {
|
readonly sendFile = async (stringifiedFileData: string): Promise<void> => {
|
||||||
if (!this.transferData) return;
|
if (!this.transferData) return;
|
||||||
|
|
||||||
const encryptedFileData: string = await encryptString(
|
const encryptedFileData: string = await encryptString(
|
||||||
stringifiedFileData,
|
stringifiedFileData,
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export default class SettingsModel {
|
|||||||
// load
|
// load
|
||||||
private readonly readSetting = (
|
private readonly readSetting = (
|
||||||
pathName: keyof typeof filePaths.settingsModel,
|
pathName: keyof typeof filePaths.settingsModel,
|
||||||
): string|null=> {
|
): string | null => {
|
||||||
const path = StorageModel.getPath(
|
const path = StorageModel.getPath(
|
||||||
StorageModelSubPaths.SettingsModel,
|
StorageModelSubPaths.SettingsModel,
|
||||||
filePaths.settingsModel[pathName],
|
filePaths.settingsModel[pathName],
|
||||||
|
|||||||
Binary file not shown.
+171
-149
@@ -1,5 +1,5 @@
|
|||||||
import { checkIsValidObject, checkMatchesObjectStructure } from "./typeSafety";
|
import { checkIsValidObject, checkMatchesObjectStructure } from "./typeSafety";
|
||||||
import * as Hammer from "hammerjs";
|
import * as React from "../../react";
|
||||||
|
|
||||||
export interface Stringifiable {
|
export interface Stringifiable {
|
||||||
toString(): string;
|
toString(): string;
|
||||||
@@ -18,7 +18,7 @@ export function createTimestamp(): string {
|
|||||||
return new Date().toISOString();
|
return new Date().toISOString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// filters etc
|
// filters
|
||||||
export type StringEntryObject = { [key: string]: Stringifiable | undefined };
|
export type StringEntryObject = { [key: string]: Stringifiable | undefined };
|
||||||
|
|
||||||
export function filterObjectsByStringEntries<T>(
|
export function filterObjectsByStringEntries<T>(
|
||||||
@@ -28,7 +28,7 @@ export function filterObjectsByStringEntries<T>(
|
|||||||
): Set<T> {
|
): Set<T> {
|
||||||
const matches: Set<T> = new Set();
|
const matches: Set<T> = new Set();
|
||||||
|
|
||||||
object_loop: for (const object of objects) {
|
for (const object of objects) {
|
||||||
const doesMatch: boolean = checkDoesObjectMatchReference(
|
const doesMatch: boolean = checkDoesObjectMatchReference(
|
||||||
reference,
|
reference,
|
||||||
converter(object),
|
converter(object),
|
||||||
@@ -39,79 +39,6 @@ export function filterObjectsByStringEntries<T>(
|
|||||||
return matches;
|
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>(
|
export function filterObjectsByWords<T>(
|
||||||
query: string,
|
query: string,
|
||||||
getStringsOfObject: (object: T) => string[],
|
getStringsOfObject: (object: T) => string[],
|
||||||
@@ -119,7 +46,7 @@ export function filterObjectsByWords<T>(
|
|||||||
): Set<T> {
|
): Set<T> {
|
||||||
const matches: Set<T> = new Set();
|
const matches: Set<T> = new Set();
|
||||||
|
|
||||||
object_loop: for (const object of objects) {
|
for (const object of objects) {
|
||||||
const doesMatch: boolean = checkDoesObjectMatchSearch(
|
const doesMatch: boolean = checkDoesObjectMatchSearch(
|
||||||
query,
|
query,
|
||||||
getStringsOfObject,
|
getStringsOfObject,
|
||||||
@@ -170,6 +97,26 @@ export function checkDoesObjectMatchSearch<T>(
|
|||||||
return true;
|
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
|
// handlers
|
||||||
export type Handler<T> = (item: T) => void;
|
export type Handler<T> = (item: T) => void;
|
||||||
|
|
||||||
@@ -313,99 +260,174 @@ export function implementPinchZoom(canvas: HTMLElement) {
|
|||||||
const MIN = 0.25;
|
const MIN = 0.25;
|
||||||
const MAX = 5;
|
const MAX = 5;
|
||||||
|
|
||||||
const element = (): HTMLElement|null=> canvas.querySelector(".zoom");
|
const element = (): HTMLElement | null => canvas.querySelector(".zoom");
|
||||||
const distance = (e: TouchEvent) =>
|
const distance = (e: TouchEvent) =>
|
||||||
Math.hypot(
|
Math.hypot(
|
||||||
e.touches[0].pageX - e.touches[1].pageX,
|
e.touches[0].pageX - e.touches[1].pageX,
|
||||||
e.touches[0].pageY - e.touches[1].pageY,
|
e.touches[0].pageY - e.touches[1].pageY,
|
||||||
);
|
);
|
||||||
const point = (e: TouchEvent, direction: "x" | "y", i: number) =>
|
const point = (e: TouchEvent, direction: "x" | "y", i: number) =>
|
||||||
e.touches[i][direction == "x" ? "clientX" : "clientY"];
|
e.touches[i][direction == "x" ? "clientX" : "clientY"];
|
||||||
const midpoint = (e: TouchEvent, direction: "x" | "y") =>
|
const midpoint = (e: TouchEvent, direction: "x" | "y") =>
|
||||||
(point(e, direction, 0) + point(e, direction, 1)) / 2;
|
(point(e, direction, 0) + point(e, direction, 1)) / 2;
|
||||||
|
|
||||||
function apply(factor: number, _offset?: [number, number]) {
|
function apply(factor: number, _offset?: [number, number]) {
|
||||||
const el = element();
|
const el = element();
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
if (factor < MIN) return apply(MIN, _offset);
|
if (factor < MIN) return apply(MIN, _offset);
|
||||||
if (factor > MAX) return apply(5, _offset);
|
if (factor > MAX) return apply(5, _offset);
|
||||||
|
|
||||||
let x: number, y: number;
|
let x: number, y: number;
|
||||||
if (_offset) [x, y] = _offset;
|
if (_offset) [x, y] = _offset;
|
||||||
else {
|
else {
|
||||||
x = currentX
|
x = currentX;
|
||||||
y = currentY
|
y = currentY;
|
||||||
}
|
}
|
||||||
el.style.transform = `scale(${factor.toString()}) translate(${x}px, ${y}px)`;
|
el.style.transform = `scale(${factor.toString()}) translate(${x}px, ${y}px)`;
|
||||||
currentX = x;
|
currentX = x;
|
||||||
currentY = y;
|
currentY = y;
|
||||||
currentZoom = factor;
|
currentZoom = factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.addEventListener("wheel", (event: WheelEvent) => {
|
canvas.addEventListener("wheel", (event: WheelEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
initialZoom = currentZoom;
|
initialZoom = currentZoom;
|
||||||
initialX = currentX;
|
initialX = currentX;
|
||||||
initialY = currentY;
|
initialY = currentY;
|
||||||
|
|
||||||
if (!event.shiftKey) {
|
if (!event.shiftKey) {
|
||||||
apply(currentZoom, [
|
apply(currentZoom, [
|
||||||
currentX - event.deltaX,
|
currentX - event.deltaX,
|
||||||
currentY - event.deltaY,
|
currentY - event.deltaY,
|
||||||
]);
|
]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
apply(currentZoom - event.deltaY * 0.01);
|
apply(currentZoom - event.deltaY * 0.01);
|
||||||
});
|
});
|
||||||
canvas.addEventListener("scroll", (event: Event) => {
|
canvas.addEventListener("scroll", (event: Event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
canvas.addEventListener("touchstart", (event: TouchEvent) => {
|
canvas.addEventListener("touchstart", (event: TouchEvent) => {
|
||||||
(document.activeElement as HTMLElement).blur();
|
(document.activeElement as HTMLElement).blur();
|
||||||
|
|
||||||
const el = element();
|
const el = element();
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
if (lastElement != undefined && lastElement != el) reset();
|
if (lastElement != undefined && lastElement != el) reset();
|
||||||
lastElement = el;
|
lastElement = el;
|
||||||
|
|
||||||
initialZoom = currentZoom;
|
initialZoom = currentZoom;
|
||||||
initialX = currentX;
|
initialX = currentX;
|
||||||
initialY = currentY;
|
initialY = currentY;
|
||||||
|
|
||||||
if (event.touches.length != 2) {
|
if (event.touches.length != 2) {
|
||||||
pinching = false;
|
pinching = false;
|
||||||
initialTouchX = event.touches[0].clientX;
|
initialTouchX = event.touches[0].clientX;
|
||||||
initialTouchY = event.touches[0].clientY;
|
initialTouchY = event.touches[0].clientY;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pinching = true;
|
pinching = true;
|
||||||
initialDistance = distance(event);
|
initialDistance = distance(event);
|
||||||
initialTouchX = midpoint(event, "x");
|
initialTouchX = midpoint(event, "x");
|
||||||
initialTouchY = midpoint(event, "y");
|
initialTouchY = midpoint(event, "y");
|
||||||
});
|
});
|
||||||
canvas.addEventListener("touchmove", (event: TouchEvent) => {
|
canvas.addEventListener("touchmove", (event: TouchEvent) => {
|
||||||
if (!pinching) {
|
if (!pinching) {
|
||||||
apply(currentZoom, [
|
apply(currentZoom, [
|
||||||
initialX +
|
initialX +
|
||||||
(event.touches[0].clientX - initialTouchX) / initialZoom,
|
(event.touches[0].clientX - initialTouchX) / initialZoom,
|
||||||
initialY +
|
initialY +
|
||||||
(event.touches[0].clientY - initialTouchY) / initialZoom,
|
(event.touches[0].clientY - initialTouchY) / initialZoom,
|
||||||
]);
|
]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event.touches.length < 2) return;
|
if (event.touches.length < 2) return;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const currentDistance = distance(event);
|
const currentDistance = distance(event);
|
||||||
const midX = midpoint(event, "x");
|
const midX = midpoint(event, "x");
|
||||||
const midY = midpoint(event, "y");
|
const midY = midpoint(event, "y");
|
||||||
const ratio = currentDistance / initialDistance;
|
const ratio = currentDistance / initialDistance;
|
||||||
const difference = currentDistance - initialDistance;
|
const difference = currentDistance - initialDistance;
|
||||||
apply(initialZoom * ratio, [
|
apply(initialZoom * ratio, [
|
||||||
initialX + (midX - difference - initialTouchX) / initialZoom,
|
initialX + (midX - difference - initialTouchX) / initialZoom,
|
||||||
initialY + (midY - difference - initialTouchY) / initialZoom,
|
initialY + (midY - difference - initialTouchY) / initialZoom,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 { ViewController } from "../viewController";
|
||||||
import { TaskViewModelToEntry } from "../Components/taskEntry";
|
import { TaskViewModelToEntry } from "../Components/taskEntry";
|
||||||
import {
|
import {
|
||||||
@@ -6,6 +5,7 @@ import {
|
|||||||
createPropertyValueIndexState,
|
createPropertyValueIndexState,
|
||||||
} from "../Components/propertyValueList";
|
} from "../Components/propertyValueList";
|
||||||
import { FilteredList } from "../Components/filteredList";
|
import { FilteredList } from "../Components/filteredList";
|
||||||
|
import * as React from "../../react";
|
||||||
import { TaskCategoryBulkChangeViewModel } from "../../ViewModel/Utility/taskPropertyBulkChangeViewModel";
|
import { TaskCategoryBulkChangeViewModel } from "../../ViewModel/Utility/taskPropertyBulkChangeViewModel";
|
||||||
import TaskViewModel from "../../ViewModel/Pages/taskViewModel";
|
import TaskViewModel from "../../ViewModel/Pages/taskViewModel";
|
||||||
import BoardViewModel from "../../ViewModel/Pages/boardViewModel";
|
import BoardViewModel from "../../ViewModel/Pages/boardViewModel";
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import { BoardStatusGridPage } from "./boardStatusGridPage";
|
import { BoardStatusGridPage } from "./boardStatusGridPage";
|
||||||
import { BoardKanbanPage } from "./boardKanbanPage";
|
import { BoardKanbanPage } from "./boardKanbanPage";
|
||||||
import { ViewController } from "../viewController";
|
import { ViewController } from "../viewController";
|
||||||
@@ -7,6 +6,7 @@ import { SearchModal } from "../Modals/searchModal";
|
|||||||
import { BoardSettingsModal } from "../Modals/boardSettingsModal";
|
import { BoardSettingsModal } from "../Modals/boardSettingsModal";
|
||||||
import { TaskViewModelToEntry } from "../Components/taskEntry";
|
import { TaskViewModelToEntry } from "../Components/taskEntry";
|
||||||
import { BoardViewToggleButton } from "../Components/boardViewToggleButton";
|
import { BoardViewToggleButton } from "../Components/boardViewToggleButton";
|
||||||
|
import * as React from "../../react";
|
||||||
import BoardViewModel, {
|
import BoardViewModel, {
|
||||||
BoardPageTypes,
|
BoardPageTypes,
|
||||||
} from "../../ViewModel/Pages/boardViewModel";
|
} from "../../ViewModel/Pages/boardViewModel";
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import { ViewController } from "../viewController";
|
import { ViewController } from "../viewController";
|
||||||
import { TaskViewModelToEntry } from "../Components/taskEntry";
|
import { TaskViewModelToEntry } from "../Components/taskEntry";
|
||||||
import {
|
import {
|
||||||
@@ -8,6 +7,7 @@ import {
|
|||||||
createSortedPropertyValueState,
|
createSortedPropertyValueState,
|
||||||
} from "../Components/propertyValueList";
|
} from "../Components/propertyValueList";
|
||||||
import { FilteredList } from "../Components/filteredList";
|
import { FilteredList } from "../Components/filteredList";
|
||||||
|
import * as React from "../../react";
|
||||||
import {
|
import {
|
||||||
TaskCategoryBulkChangeViewModel,
|
TaskCategoryBulkChangeViewModel,
|
||||||
TaskStatusBulkChangeViewModel,
|
TaskStatusBulkChangeViewModel,
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import "./calendarPage.css";
|
import "./calendarPage.css";
|
||||||
import { ViewController } from "../viewController";
|
import { ViewController } from "../viewController";
|
||||||
import { TaskSettingsModal } from "../Modals/taskSettingsModal";
|
import { TaskSettingsModal } from "../Modals/taskSettingsModal";
|
||||||
import { TaskViewModelToEntry } from "../Components/taskEntry";
|
import { TaskViewModelToEntry } from "../Components/taskEntry";
|
||||||
import { PlaceholderView } from "../Components/placeholderView";
|
import { PlaceholderView } from "../Components/placeholderView";
|
||||||
import { MonthGrid } from "../Components/monthGrid";
|
import { MonthGrid } from "../Components/monthGrid";
|
||||||
|
import * as React from "../../react";
|
||||||
import CalendarPageViewModel from "../../ViewModel/Pages/calendarPageViewModel";
|
import CalendarPageViewModel from "../../ViewModel/Pages/calendarPageViewModel";
|
||||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import "./messagePage.css";
|
import "./messagePage.css";
|
||||||
import { ViewController } from "../viewController";
|
import { ViewController } from "../viewController";
|
||||||
import { MessageFilterModal } from "../Modals/messageFilterModal";
|
import { MessageFilterModal } from "../Modals/messageFilterModal";
|
||||||
import { ReplyPreview } from "../Components/replyPreview";
|
import { ReplyPreview } from "../Components/replyPreview";
|
||||||
import { ChatMessage } from "../Components/chatMessage";
|
import { ChatMessage } from "../Components/chatMessage";
|
||||||
|
import * as React from "../../react";
|
||||||
import MessagePageViewModel from "../../ViewModel/Pages/messagePageViewModel";
|
import MessagePageViewModel from "../../ViewModel/Pages/messagePageViewModel";
|
||||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||||
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import { DeletableListItem } from "../Components/deletableListItem";
|
import { DeletableListItem } from "../Components/deletableListItem";
|
||||||
import { DangerousActionButton } from "../Components/dangerousActionButton";
|
import { DangerousActionButton } from "../Components/dangerousActionButton";
|
||||||
import { ColorPicker } from "../Components/colorPicker";
|
import { ColorPicker } from "../Components/colorPicker";
|
||||||
|
import * as React from "../../react";
|
||||||
import SettingsPageViewModel from "../../ViewModel/Pages/settingsPageViewModel";
|
import SettingsPageViewModel from "../../ViewModel/Pages/settingsPageViewModel";
|
||||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import "./taskPage.css";
|
import "./taskPage.css";
|
||||||
import { BoardPage } from "./boardPage";
|
import { BoardPage } from "./boardPage";
|
||||||
import { ViewController } from "../viewController";
|
import { ViewController } from "../viewController";
|
||||||
import { PlaceholderView } from "../Components/placeholderView";
|
import { PlaceholderView } from "../Components/placeholderView";
|
||||||
import { BoardViewModelToEntry } from "../Components/boardEntry";
|
import { BoardViewModelToEntry } from "../Components/boardEntry";
|
||||||
|
import * as React from "../../react";
|
||||||
import TaskPageViewModel from "../../ViewModel/Pages/taskPageViewModel";
|
import TaskPageViewModel from "../../ViewModel/Pages/taskPageViewModel";
|
||||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ export function TaskPage(
|
|||||||
<div class="flex-row width-input">
|
<div class="flex-row width-input">
|
||||||
<input
|
<input
|
||||||
class="no-outline"
|
class="no-outline"
|
||||||
bind:value={taskPageViewModel.newBoardNameInput}
|
bind:value={taskPageViewModel.boardQuery}
|
||||||
on:enter={taskPageViewModel.createBoard}
|
on:enter={taskPageViewModel.createBoard}
|
||||||
placeholder={
|
placeholder={
|
||||||
coreViewModel.translations.chatPage.task
|
coreViewModel.translations.chatPage.task
|
||||||
@@ -89,7 +89,7 @@ export function TaskPage(
|
|||||||
class="grid gap"
|
class="grid gap"
|
||||||
style="grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr))"
|
style="grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr))"
|
||||||
children:append={[
|
children:append={[
|
||||||
taskPageViewModel.boardViewModels,
|
taskPageViewModel.boardMatches,
|
||||||
BoardViewModelToEntry,
|
BoardViewModelToEntry,
|
||||||
]}
|
]}
|
||||||
></div>
|
></div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import { ViewController } from "../viewController";
|
import { ViewController } from "../viewController";
|
||||||
|
import * as React from "../../react";
|
||||||
import BoardViewModel from "../../ViewModel/Pages/boardViewModel";
|
import BoardViewModel from "../../ViewModel/Pages/boardViewModel";
|
||||||
|
|
||||||
export function BoardEntry(boardViewModel: BoardViewModel) {
|
export function BoardEntry(boardViewModel: BoardViewModel) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import { RibbonButton } from "./ribbonButton";
|
import { RibbonButton } from "./ribbonButton";
|
||||||
|
import * as React from "../../react";
|
||||||
import BoardViewModel, {
|
import BoardViewModel, {
|
||||||
BoardPageTypes,
|
BoardPageTypes,
|
||||||
} from "../../ViewModel/Pages/boardViewModel";
|
} from "../../ViewModel/Pages/boardViewModel";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import { MessageReactionButtonRow } from "./messageReactionButtonRow";
|
import { MessageReactionButtonRow } from "./messageReactionButtonRow";
|
||||||
import { InlineReply } from "./inlineReply";
|
import { InlineReply } from "./inlineReply";
|
||||||
import { ChatMessageInfoModal } from "../Modals/chatMessageInfoModal";
|
import { ChatMessageInfoModal } from "../Modals/chatMessageInfoModal";
|
||||||
|
import * as React from "../../react";
|
||||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||||
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
||||||
import { ChatMessageStatuses } from "../../Model/Chat/chatModel";
|
import { ChatMessageStatuses } from "../../Model/Chat/chatModel";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import { RibbonButton } from "./ribbonButton";
|
import { RibbonButton } from "./ribbonButton";
|
||||||
|
import * as React from "../../react";
|
||||||
import ChatViewModel, {
|
import ChatViewModel, {
|
||||||
ChatPageTypes,
|
ChatPageTypes,
|
||||||
} from "../../ViewModel/Chat/chatViewModel";
|
} from "../../ViewModel/Chat/chatViewModel";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import { ViewController } from "../viewController";
|
import { ViewController } from "../viewController";
|
||||||
|
import * as React from "../../react";
|
||||||
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
||||||
|
|
||||||
export function InlineReply(chatMessageViewModel: ChatMessageViewModel) {
|
export function InlineReply(chatMessageViewModel: ChatMessageViewModel) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import { MessageReactionButton } from "./messageReactionButton";
|
import { MessageReactionButton } from "./messageReactionButton";
|
||||||
|
import * as React from "../../react";
|
||||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||||
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
||||||
import { ReactionSymbols } from "../../Model/Chat/chatModel";
|
import { ReactionSymbols } from "../../Model/Chat/chatModel";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import "./monthGrid.css";
|
import "./monthGrid.css";
|
||||||
import { ViewController } from "../viewController";
|
import { ViewController } from "../viewController";
|
||||||
|
import * as React from "../../react";
|
||||||
import TaskViewModel from "../../ViewModel/Pages/taskViewModel";
|
import TaskViewModel from "../../ViewModel/Pages/taskViewModel";
|
||||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||||
import { localeCompare } from "../../Model/Utility/utility";
|
import { localeCompare } from "../../Model/Utility/utility";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import { OptionButton } from "./optionButton";
|
import { OptionButton } from "./optionButton";
|
||||||
|
import * as React from "../../react";
|
||||||
|
|
||||||
export function OptionButtonList<T>(
|
export function OptionButtonList<T>(
|
||||||
options: React.ListState<[string, T]>,
|
options: React.ListState<[string, T]>,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import { ViewController } from "../viewController";
|
import { ViewController } from "../viewController";
|
||||||
import { DangerousActionButton } from "../Components/dangerousActionButton";
|
import { DangerousActionButton } from "../Components/dangerousActionButton";
|
||||||
import { ColorPicker } from "../Components/colorPicker";
|
import { ColorPicker } from "../Components/colorPicker";
|
||||||
|
import * as React from "../../react";
|
||||||
import BoardViewModel from "../../ViewModel/Pages/boardViewModel";
|
import BoardViewModel from "../../ViewModel/Pages/boardViewModel";
|
||||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import { MessageReactionEntry } from "../Components/messageReactionEntry";
|
import { MessageReactionEntry } from "../Components/messageReactionEntry";
|
||||||
import { MessageReactionButtonRow } from "../Components/messageReactionButtonRow";
|
import { MessageReactionButtonRow } from "../Components/messageReactionButtonRow";
|
||||||
import { InfoTile } from "../Components/infoTile";
|
import { InfoTile } from "../Components/infoTile";
|
||||||
|
import * as React from "../../react";
|
||||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||||
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import { DeletableListItem } from "../Components/deletableListItem";
|
import { DeletableListItem } from "../Components/deletableListItem";
|
||||||
|
import * as React from "../../react";
|
||||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||||
import ConnectionViewModel from "../../ViewModel/Global/connectionViewModel";
|
import ConnectionViewModel from "../../ViewModel/Global/connectionViewModel";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import { ViewController } from "../viewController";
|
import { ViewController } from "../viewController";
|
||||||
import { StringToTextSpan } from "../Components/textSpan";
|
import { StringToTextSpan } from "../Components/textSpan";
|
||||||
|
import * as React from "../../react";
|
||||||
import FileTransferViewModel, {
|
import FileTransferViewModel, {
|
||||||
FileTransferModals,
|
FileTransferModals,
|
||||||
FileTransferOption,
|
FileTransferOption,
|
||||||
@@ -758,7 +758,9 @@ function ExportModal(
|
|||||||
</span>
|
</span>
|
||||||
<input
|
<input
|
||||||
bind:value={fileTransferViewModel.exportKey}
|
bind:value={fileTransferViewModel.exportKey}
|
||||||
on:enter={fileTransferViewModel.downloadFile}
|
on:enter={
|
||||||
|
fileTransferViewModel.downloadFile
|
||||||
|
}
|
||||||
type="password"
|
type="password"
|
||||||
></input>
|
></input>
|
||||||
</div>
|
</div>
|
||||||
@@ -777,36 +779,38 @@ function ExportModal(
|
|||||||
bind:value={
|
bind:value={
|
||||||
fileTransferViewModel.exportKeyConfirmation
|
fileTransferViewModel.exportKeyConfirmation
|
||||||
}
|
}
|
||||||
on:enter={fileTransferViewModel.downloadFile}
|
on:enter={
|
||||||
type="password"
|
fileTransferViewModel.downloadFile
|
||||||
></input>
|
}
|
||||||
</div>
|
type="password"
|
||||||
</label>
|
></input>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</label>
|
||||||
<div class="flex-row width-100">
|
</div>
|
||||||
<button
|
</main>
|
||||||
class="flex"
|
<div class="flex-row width-100">
|
||||||
on:click={
|
<button
|
||||||
fileTransferViewModel.showExportSelectionModal
|
class="flex"
|
||||||
}
|
on:click={
|
||||||
>
|
fileTransferViewModel.showExportSelectionModal
|
||||||
{coreViewModel.translations.general.backButton}
|
}
|
||||||
</button>
|
>
|
||||||
<button
|
{coreViewModel.translations.general.backButton}
|
||||||
class="primary flex"
|
</button>
|
||||||
on:click={fileTransferViewModel.downloadFile}
|
<button
|
||||||
toggle:disabled={fileTransferViewModel.cannotExport}
|
class="primary flex"
|
||||||
>
|
on:click={fileTransferViewModel.downloadFile}
|
||||||
{
|
toggle:disabled={fileTransferViewModel.cannotExport}
|
||||||
coreViewModel.translations.dataTransferModal
|
>
|
||||||
.downloadFileButton
|
{
|
||||||
}
|
coreViewModel.translations.dataTransferModal
|
||||||
<span class="icon">download</span>
|
.downloadFileButton
|
||||||
</button>
|
}
|
||||||
</div>
|
<span class="icon">download</span>
|
||||||
</div>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -815,57 +819,57 @@ function ImportModal(
|
|||||||
fileTransferViewModel: FileTransferViewModel,
|
fileTransferViewModel: FileTransferViewModel,
|
||||||
) {
|
) {
|
||||||
const isPresented = React.createProxyState(
|
const isPresented = React.createProxyState(
|
||||||
[fileTransferViewModel.presentedModal],
|
[fileTransferViewModel.presentedModal],
|
||||||
() =>
|
() =>
|
||||||
fileTransferViewModel.presentedModal.value ==
|
fileTransferViewModel.presentedModal.value ==
|
||||||
FileTransferModals.ImportSelection,
|
FileTransferModals.ImportSelection,
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="modal" toggle:open={isPresented}>
|
<div class="modal" toggle:open={isPresented}>
|
||||||
<div>
|
<div>
|
||||||
<main>
|
<main>
|
||||||
<h2>
|
<h2>
|
||||||
{
|
{
|
||||||
coreViewModel.translations.dataTransferModal
|
coreViewModel.translations.dataTransferModal
|
||||||
.importHeadline
|
.importHeadline
|
||||||
}
|
}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div class="flex-column gap content-margin-bottom">
|
<div class="flex-column gap content-margin-bottom">
|
||||||
<input
|
<input
|
||||||
id="file-transfer-input"
|
id="file-transfer-input"
|
||||||
type="file"
|
type="file"
|
||||||
on:change={
|
on:change={
|
||||||
fileTransferViewModel.updateImportSelection
|
fileTransferViewModel.updateImportSelection
|
||||||
}
|
}
|
||||||
></input>
|
></input>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div class="flex-row width-100">
|
<div class="flex-row width-100">
|
||||||
<button
|
<button
|
||||||
class="flex"
|
class="flex"
|
||||||
on:click={
|
on:click={
|
||||||
fileTransferViewModel.showDirectionSelectionModal
|
fileTransferViewModel.showDirectionSelectionModal
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{coreViewModel.translations.general.backButton}
|
{coreViewModel.translations.general.backButton}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="primary flex"
|
class="primary flex"
|
||||||
on:click={fileTransferViewModel.importFile}
|
on:click={fileTransferViewModel.importFile}
|
||||||
toggle:disabled={fileTransferViewModel.cannotImport}
|
toggle:disabled={fileTransferViewModel.cannotImport}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
coreViewModel.translations.dataTransferModal
|
coreViewModel.translations.dataTransferModal
|
||||||
.importFileButton
|
.importFileButton
|
||||||
}
|
}
|
||||||
<span class="icon">arrow_forward</span>
|
<span class="icon">arrow_forward</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -874,74 +878,76 @@ function ImportDecryptionDataModal(
|
|||||||
fileTransferViewModel: FileTransferViewModel,
|
fileTransferViewModel: FileTransferViewModel,
|
||||||
) {
|
) {
|
||||||
const isPresented = React.createProxyState(
|
const isPresented = React.createProxyState(
|
||||||
[fileTransferViewModel.presentedModal],
|
[fileTransferViewModel.presentedModal],
|
||||||
() =>
|
() =>
|
||||||
fileTransferViewModel.presentedModal.value ==
|
fileTransferViewModel.presentedModal.value ==
|
||||||
FileTransferModals.ImportDecryptData,
|
FileTransferModals.ImportDecryptData,
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="modal" toggle:open={isPresented}>
|
<div class="modal" toggle:open={isPresented}>
|
||||||
<div>
|
<div>
|
||||||
<main>
|
<main>
|
||||||
<h2>
|
<h2>
|
||||||
{
|
{
|
||||||
coreViewModel.translations.dataTransferModal
|
coreViewModel.translations.dataTransferModal
|
||||||
.importHeadline
|
.importHeadline
|
||||||
}
|
}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div class="flex-column gap content-margin-bottom">
|
<div class="flex-column gap content-margin-bottom">
|
||||||
<label class="tile">
|
<label class="tile">
|
||||||
<span class="icon">key</span>
|
<span class="icon">key</span>
|
||||||
<div>
|
<div>
|
||||||
<span class="secondary">
|
<span class="secondary">
|
||||||
{
|
{
|
||||||
coreViewModel.translations
|
coreViewModel.translations
|
||||||
.dataTransferModal.exportKey
|
.dataTransferModal.exportKey
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
<input
|
<input
|
||||||
bind:value={fileTransferViewModel.importKey}
|
bind:value={fileTransferViewModel.importKey}
|
||||||
on:enter={fileTransferViewModel.decryptImport}
|
on:enter={
|
||||||
type="password"
|
fileTransferViewModel.decryptImport
|
||||||
></input>
|
}
|
||||||
</div>
|
type="password"
|
||||||
</label>
|
></input>
|
||||||
</div>
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<p
|
<p
|
||||||
class="error"
|
class="error"
|
||||||
toggle:hidden={fileTransferViewModel.isImportKeyCorrect}
|
toggle:hidden={fileTransferViewModel.isImportKeyCorrect}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
coreViewModel.translations.dataTransferModal
|
coreViewModel.translations.dataTransferModal
|
||||||
.incorrectPassphraseError
|
.incorrectPassphraseError
|
||||||
}
|
}
|
||||||
</p>
|
</p>
|
||||||
</main>
|
</main>
|
||||||
<div class="flex-row width-100">
|
<div class="flex-row width-100">
|
||||||
<button
|
<button
|
||||||
class="flex"
|
class="flex"
|
||||||
on:click={fileTransferViewModel.showImportModal}
|
on:click={fileTransferViewModel.showImportModal}
|
||||||
>
|
>
|
||||||
{coreViewModel.translations.general.backButton}
|
{coreViewModel.translations.general.backButton}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="primary flex"
|
class="primary flex"
|
||||||
on:click={fileTransferViewModel.decryptImport}
|
on:click={fileTransferViewModel.decryptImport}
|
||||||
toggle:disabled={
|
toggle:disabled={
|
||||||
fileTransferViewModel.importDecryptSuccessful
|
fileTransferViewModel.importDecryptSuccessful
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
coreViewModel.translations.dataTransferModal
|
coreViewModel.translations.dataTransferModal
|
||||||
.decryptImportButton
|
.decryptImportButton
|
||||||
}
|
}
|
||||||
<span class="icon">arrow_forward</span>
|
<span class="icon">arrow_forward</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import { ViewController } from "../viewController";
|
import { ViewController } from "../viewController";
|
||||||
import { MessageReactionFilterButton } from "../Components/messageReactionFilterButton";
|
import { MessageReactionFilterButton } from "../Components/messageReactionFilterButton";
|
||||||
|
import * as React from "../../react";
|
||||||
import MessagePageViewModel from "../../ViewModel/Pages/messagePageViewModel";
|
import MessagePageViewModel from "../../ViewModel/Pages/messagePageViewModel";
|
||||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||||
import { ReactionSymbols } from "../../Model/Chat/chatModel";
|
import { ReactionSymbols } from "../../Model/Chat/chatModel";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
import * as React from "../../react";
|
|
||||||
import { ViewController } from "../viewController";
|
import { ViewController } from "../viewController";
|
||||||
import { StringToOption } from "../Components/option";
|
import { StringToOption } from "../Components/option";
|
||||||
|
import * as React from "../../react";
|
||||||
import SearchViewModel from "../../ViewModel/Utility/searchViewModel";
|
import SearchViewModel from "../../ViewModel/Utility/searchViewModel";
|
||||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||||
|
|
||||||
@@ -29,8 +29,8 @@ export function SearchModal<T>(
|
|||||||
<h2>{headline}</h2>
|
<h2>{headline}</h2>
|
||||||
<div class="flex-row">
|
<div class="flex-row">
|
||||||
<input
|
<input
|
||||||
id="focused"
|
id="focused"
|
||||||
style="max-width: unset"
|
style="max-width: unset"
|
||||||
placeholder={
|
placeholder={
|
||||||
coreViewModel.translations.general.searchLabel
|
coreViewModel.translations.general.searchLabel
|
||||||
}
|
}
|
||||||
@@ -70,55 +70,73 @@ export function SearchModal<T>(
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div toggle:hidden={searchViewModel.hasNoSuggestions}>
|
||||||
toggle:hidden={searchViewModel.hasNoSuggestions}>
|
<hr></hr>
|
||||||
<hr></hr>
|
<h3>
|
||||||
<h3>{coreViewModel.translations.general.searchSuggestionsLabel}</h3>
|
{
|
||||||
<div
|
coreViewModel.translations.general
|
||||||
class="flex-column gap"
|
.searchSuggestionsLabel
|
||||||
children:append={[
|
}
|
||||||
searchViewModel.suggestions,
|
</h3>
|
||||||
(suggestion: string) => SuggestionView(suggestion, searchViewModel, coreViewModel),
|
<div
|
||||||
]}
|
class="flex-column gap"
|
||||||
></div>
|
children:append={[
|
||||||
</div>
|
searchViewModel.suggestions,
|
||||||
</main>
|
(suggestion: string) =>
|
||||||
<button on:click={close}>
|
SuggestionView(
|
||||||
{coreViewModel.translations.general.closeButton}
|
suggestion,
|
||||||
<span class="icon">close</span>
|
searchViewModel,
|
||||||
</button>
|
coreViewModel,
|
||||||
</div>
|
),
|
||||||
</div>
|
]}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<button on:click={close}>
|
||||||
|
{coreViewModel.translations.general.closeButton}
|
||||||
|
<span class="icon">close</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SuggestionView<T>(suggestion: string, searchViewModel: SearchViewModel<T>, coreViewModel: CoreViewModel) {
|
function SuggestionView<T>(
|
||||||
const isApplied = React.createProxyState([searchViewModel.appliedQuery], ()=>searchViewModel.appliedQuery.value == suggestion);
|
suggestion: string,
|
||||||
|
searchViewModel: SearchViewModel<T>,
|
||||||
|
coreViewModel: CoreViewModel,
|
||||||
|
) {
|
||||||
|
const isApplied = React.createProxyState(
|
||||||
|
[searchViewModel.appliedQuery],
|
||||||
|
() => searchViewModel.appliedQuery.value == suggestion,
|
||||||
|
);
|
||||||
|
|
||||||
function deleteSuggestion() {
|
function deleteSuggestion() {
|
||||||
searchViewModel.deleteSuggestion(suggestion);
|
searchViewModel.deleteSuggestion(suggestion);
|
||||||
}
|
}
|
||||||
|
|
||||||
function applySuggestion() {
|
function applySuggestion() {
|
||||||
searchViewModel.search(suggestion);
|
searchViewModel.search(suggestion);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div class="flex-row surface align-center">
|
return (
|
||||||
<span class="width-100 flex-1 padding-h">{suggestion}</span>
|
<div class="flex-row surface align-center">
|
||||||
<button
|
<span class="width-100 flex-1 padding-h">{suggestion}</span>
|
||||||
class="danger"
|
<button
|
||||||
aria-label={coreViewModel.translations.general.deleteButton}
|
class="danger"
|
||||||
on:click={deleteSuggestion}
|
aria-label={coreViewModel.translations.general.deleteButton}
|
||||||
>
|
on:click={deleteSuggestion}
|
||||||
<span class="icon">delete</span>
|
>
|
||||||
</button>
|
<span class="icon">delete</span>
|
||||||
<button
|
</button>
|
||||||
class="primary"
|
<button
|
||||||
aria-label={coreViewModel.translations.general.applyButton}
|
class="primary"
|
||||||
on:click={applySuggestion}
|
aria-label={coreViewModel.translations.general.applyButton}
|
||||||
toggle:disabled={isApplied}
|
on:click={applySuggestion}
|
||||||
>
|
toggle:disabled={isApplied}
|
||||||
<span class="icon">check</span>
|
>
|
||||||
</button>
|
<span class="icon">check</span>
|
||||||
</div>
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import { SplitModal } from "./splitModal";
|
import { SplitModal } from "./splitModal";
|
||||||
import { languageNames } from "../translations";
|
import { languageNames } from "../translations";
|
||||||
import { PlaceholderView } from "../Components/placeholderView";
|
import { PlaceholderView } from "../Components/placeholderView";
|
||||||
import { OptionButtonList } from "../Components/optionButtonList";
|
import { OptionButtonList } from "../Components/optionButtonList";
|
||||||
import { NavigationButton } from "../Components/navigationButton";
|
import { NavigationButton } from "../Components/navigationButton";
|
||||||
import { InfoTile } from "../Components/infoTile";
|
import { InfoTile } from "../Components/infoTile";
|
||||||
|
import * as React from "../../react";
|
||||||
import SettingsViewModel, {
|
import SettingsViewModel, {
|
||||||
SettingsModalPages,
|
SettingsModalPages,
|
||||||
} from "../../ViewModel/Global/settingsViewModel";
|
} from "../../ViewModel/Global/settingsViewModel";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import "./splitModal.css";
|
import "./splitModal.css";
|
||||||
|
import * as React from "../../react";
|
||||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||||
|
|
||||||
export function SplitModal<T>(
|
export function SplitModal<T>(
|
||||||
@@ -10,7 +10,7 @@ export function SplitModal<T>(
|
|||||||
navigationState?: React.State<T | undefined>,
|
navigationState?: React.State<T | undefined>,
|
||||||
) {
|
) {
|
||||||
function closePage() {
|
function closePage() {
|
||||||
if (!navigationState) return;
|
if (!navigationState) return;
|
||||||
navigationState.value = undefined;
|
navigationState.value = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import { SplitModal } from "./splitModal";
|
import { SplitModal } from "./splitModal";
|
||||||
import { DirectoryItemList } from "../Components/directoryItemList";
|
import { DirectoryItemList } from "../Components/directoryItemList";
|
||||||
import { DangerousActionButton } from "../Components/dangerousActionButton";
|
import { DangerousActionButton } from "../Components/dangerousActionButton";
|
||||||
|
import * as React from "../../react";
|
||||||
import StorageViewModel from "../../ViewModel/Global/storageViewModel";
|
import StorageViewModel from "../../ViewModel/Global/storageViewModel";
|
||||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||||
import { PATH_COMPONENT_SEPARATOR } from "../../Model/Global/storageModel";
|
import { PATH_COMPONENT_SEPARATOR } from "../../Model/Global/storageModel";
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
import * as React from "../../react";
|
|
||||||
import {
|
import {
|
||||||
Entry,
|
Entry,
|
||||||
Option,
|
Option,
|
||||||
@@ -7,6 +6,7 @@ import {
|
|||||||
VersionIdToOption,
|
VersionIdToOption,
|
||||||
} from "../Components/option";
|
} from "../Components/option";
|
||||||
import { DangerousActionButton } from "../Components/dangerousActionButton";
|
import { DangerousActionButton } from "../Components/dangerousActionButton";
|
||||||
|
import * as React from "../../react";
|
||||||
import TaskViewModel from "../../ViewModel/Pages/taskViewModel";
|
import TaskViewModel from "../../ViewModel/Pages/taskViewModel";
|
||||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||||
|
|
||||||
@@ -18,264 +18,269 @@ export function TaskSettingsModal(
|
|||||||
const statusSuggestionId = v4();
|
const statusSuggestionId = v4();
|
||||||
|
|
||||||
const BoardOptionConverter: React.StateItemConverter<Entry> = (
|
const BoardOptionConverter: React.StateItemConverter<Entry> = (
|
||||||
entry: Entry,
|
entry: Entry,
|
||||||
) => {
|
) => {
|
||||||
const isSelected = entry[0] == taskViewModel.task.boardId;
|
const isSelected = entry[0] == taskViewModel.task.boardId;
|
||||||
return Option(entry[1], entry[0], isSelected);
|
return Option(entry[1], entry[0], isSelected);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="modal task-settings" open toggle:full-description={taskViewModel.isPresentingFullScreenDescription}>
|
<div
|
||||||
<div>
|
class="modal task-settings"
|
||||||
<main id="standard-main">
|
open
|
||||||
<h2>
|
toggle:full-description={
|
||||||
{
|
taskViewModel.isPresentingFullScreenDescription
|
||||||
coreViewModel.translations.chatPage.task
|
}
|
||||||
.taskSettingsHeadline
|
>
|
||||||
}
|
<div>
|
||||||
</h2>
|
<main id="standard-main">
|
||||||
|
<h2>
|
||||||
|
{
|
||||||
|
coreViewModel.translations.chatPage.task
|
||||||
|
.taskSettingsHeadline
|
||||||
|
}
|
||||||
|
</h2>
|
||||||
|
|
||||||
<div class="column-wrapper">
|
<div class="column-wrapper">
|
||||||
<div class="flex-column">
|
<div class="flex-column">
|
||||||
<label class="tile flex-no">
|
<label class="tile flex-no">
|
||||||
<span class="icon">label</span>
|
<span class="icon">label</span>
|
||||||
<div>
|
<div>
|
||||||
<span>
|
<span>
|
||||||
{
|
{
|
||||||
coreViewModel.translations.chatPage.task
|
coreViewModel.translations.chatPage
|
||||||
.taskNameLabel
|
.task.taskNameLabel
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
<input
|
<input
|
||||||
bind:value={taskViewModel.name}
|
bind:value={taskViewModel.name}
|
||||||
></input>
|
></input>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="tile flex-no">
|
<label class="tile flex-no">
|
||||||
<span class="icon">category</span>
|
<span class="icon">category</span>
|
||||||
<div>
|
<div>
|
||||||
<span>
|
<span>
|
||||||
{
|
{
|
||||||
coreViewModel.translations.chatPage.task
|
coreViewModel.translations.chatPage
|
||||||
.taskBoardLabel
|
.task.taskBoardLabel
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
<select
|
<select
|
||||||
bind:value={taskViewModel.boardId}
|
bind:value={taskViewModel.boardId}
|
||||||
children:append={[
|
children:append={[
|
||||||
taskViewModel.chatViewModel
|
taskViewModel.chatViewModel
|
||||||
.taskBoardSuggestions,
|
.taskBoardSuggestions,
|
||||||
BoardOptionConverter,
|
BoardOptionConverter,
|
||||||
]}
|
]}
|
||||||
></select>
|
></select>
|
||||||
<span class="icon">arrow_drop_down</span>
|
<span class="icon">arrow_drop_down</span>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="tile flex-no">
|
<label class="tile flex-no">
|
||||||
<span class="icon">description</span>
|
<span class="icon">description</span>
|
||||||
<div>
|
<div>
|
||||||
<span>
|
<span>
|
||||||
{
|
{
|
||||||
coreViewModel.translations.chatPage.task
|
coreViewModel.translations.chatPage
|
||||||
.taskDescriptionLabel
|
.task.taskDescriptionLabel
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
<textarea
|
<textarea
|
||||||
rows="10"
|
rows="10"
|
||||||
bind:value={taskViewModel.description}
|
bind:value={taskViewModel.description}
|
||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<div class="flex-row justify-end">
|
<div class="flex-row justify-end">
|
||||||
<button
|
<button
|
||||||
class="width-50"
|
class="width-50"
|
||||||
on:click={taskViewModel.openFullscreenDecription}
|
on:click={
|
||||||
>
|
taskViewModel.openFullscreenDecription
|
||||||
{
|
}
|
||||||
coreViewModel.translations.general.fullscreenButton
|
>
|
||||||
}
|
{
|
||||||
|
coreViewModel.translations.general
|
||||||
|
.fullscreenButton
|
||||||
|
}
|
||||||
|
|
||||||
<span class="icon">fullscreen</span>
|
<span class="icon">fullscreen</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr class="mobile-only"></hr>
|
<hr class="mobile-only"></hr>
|
||||||
|
|
||||||
<div class="flex-column">
|
<div class="flex-column">
|
||||||
<label class="tile flex-no">
|
<label class="tile flex-no">
|
||||||
<span class="icon">category</span>
|
<span class="icon">category</span>
|
||||||
<div>
|
<div>
|
||||||
<span>
|
<span>
|
||||||
{
|
{
|
||||||
coreViewModel.translations.chatPage.task
|
coreViewModel.translations.chatPage
|
||||||
.taskCategoryLabel
|
.task.taskCategoryLabel
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
<input
|
<input
|
||||||
bind:value={taskViewModel.category}
|
bind:value={taskViewModel.category}
|
||||||
list={categorySuggestionId}
|
list={categorySuggestionId}
|
||||||
></input>
|
></input>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<datalist
|
<datalist
|
||||||
hidden
|
hidden
|
||||||
id={categorySuggestionId}
|
id={categorySuggestionId}
|
||||||
children:append={[
|
children:append={[
|
||||||
taskViewModel.containingViewModel.taskCategorySuggestions,
|
taskViewModel.containingViewModel
|
||||||
StringToOption,
|
.taskCategorySuggestions,
|
||||||
]}
|
StringToOption,
|
||||||
></datalist>
|
]}
|
||||||
|
></datalist>
|
||||||
|
|
||||||
<label class="tile flex-no">
|
<label class="tile flex-no">
|
||||||
<span class="icon">clock_loader_40</span>
|
<span class="icon">clock_loader_40</span>
|
||||||
<div>
|
<div>
|
||||||
<span>
|
<span>
|
||||||
{
|
{
|
||||||
coreViewModel.translations.chatPage.task
|
coreViewModel.translations.chatPage
|
||||||
.taskStatusLabel
|
.task.taskStatusLabel
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
<input
|
<input
|
||||||
bind:value={taskViewModel.status}
|
bind:value={taskViewModel.status}
|
||||||
list={statusSuggestionId}
|
list={statusSuggestionId}
|
||||||
></input>
|
></input>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<datalist
|
<datalist
|
||||||
hidden
|
hidden
|
||||||
id={statusSuggestionId}
|
id={statusSuggestionId}
|
||||||
children:append={[
|
children:append={[
|
||||||
taskViewModel.containingViewModel.taskStatusSuggestions,
|
taskViewModel.containingViewModel
|
||||||
StringToOption,
|
.taskStatusSuggestions,
|
||||||
]}
|
StringToOption,
|
||||||
></datalist>
|
]}
|
||||||
|
></datalist>
|
||||||
|
|
||||||
<label class="tile flex-no">
|
<label class="tile flex-no">
|
||||||
<span class="icon">priority_high</span>
|
<span class="icon">priority_high</span>
|
||||||
<div>
|
<div>
|
||||||
<span>
|
<span>
|
||||||
{
|
{
|
||||||
coreViewModel.translations.chatPage.task
|
coreViewModel.translations.chatPage
|
||||||
.taskPriorityLabel
|
.task.taskPriorityLabel
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
bind:value={taskViewModel.priority}
|
bind:value={taskViewModel.priority}
|
||||||
></input>
|
></input>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<hr></hr>
|
<hr></hr>
|
||||||
|
|
||||||
<label class="tile flex-no">
|
<label class="tile flex-no">
|
||||||
<span class="icon">calendar_month</span>
|
<span class="icon">calendar_month</span>
|
||||||
<div>
|
<div>
|
||||||
<span>
|
<span>
|
||||||
{
|
{
|
||||||
coreViewModel.translations.chatPage.task
|
coreViewModel.translations.chatPage
|
||||||
.taskDateLabel
|
.task.taskDateLabel
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
bind:value={taskViewModel.date}
|
bind:value={taskViewModel.date}
|
||||||
></input>
|
></input>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="tile flex-no">
|
<label class="tile flex-no">
|
||||||
<span class="icon">schedule</span>
|
<span class="icon">schedule</span>
|
||||||
<div>
|
<div>
|
||||||
<span>
|
<span>
|
||||||
{
|
{
|
||||||
coreViewModel.translations.chatPage.task
|
coreViewModel.translations.chatPage
|
||||||
.taskTimeLabel
|
.task.taskTimeLabel
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
<input
|
<input
|
||||||
type="time"
|
type="time"
|
||||||
bind:value={taskViewModel.time}
|
bind:value={taskViewModel.time}
|
||||||
></input>
|
></input>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr></hr>
|
<hr></hr>
|
||||||
|
|
||||||
<label class="tile flex-no">
|
<label class="tile flex-no">
|
||||||
<span class="icon">history</span>
|
<span class="icon">history</span>
|
||||||
<div>
|
<div>
|
||||||
<span>
|
<span>
|
||||||
{
|
{
|
||||||
coreViewModel.translations.general
|
coreViewModel.translations.general
|
||||||
.fileVersionLabel
|
.fileVersionLabel
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
<select
|
<select
|
||||||
bind:value={taskViewModel.selectedVersionId}
|
bind:value={taskViewModel.selectedVersionId}
|
||||||
children:append={[
|
children:append={[
|
||||||
taskViewModel.versionIds,
|
taskViewModel.versionIds,
|
||||||
VersionIdToOption,
|
VersionIdToOption,
|
||||||
]}
|
]}
|
||||||
></select>
|
></select>
|
||||||
<span class="icon">arrow_drop_down</span>
|
<span class="icon">arrow_drop_down</span>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<hr></hr>
|
<hr></hr>
|
||||||
|
|
||||||
<div class="width-input">
|
<div class="width-input">
|
||||||
{DangerousActionButton(
|
{DangerousActionButton(
|
||||||
coreViewModel,
|
coreViewModel,
|
||||||
coreViewModel.translations.chatPage.task
|
coreViewModel.translations.chatPage.task
|
||||||
.deleteTaskButton,
|
.deleteTaskButton,
|
||||||
"delete_forever",
|
"delete_forever",
|
||||||
taskViewModel.deleteTask,
|
taskViewModel.deleteTask,
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<main id="fullscreen-main">
|
<main id="fullscreen-main">
|
||||||
<textarea
|
<textarea
|
||||||
style="height: 100%; width: 100%; max-width: unset"
|
style="height: 100%; width: 100%; max-width: unset"
|
||||||
bind:value={taskViewModel.description}
|
bind:value={taskViewModel.description}
|
||||||
></textarea>
|
></textarea>
|
||||||
</main>
|
</main>
|
||||||
<div
|
<div class="flex-row width-100" id="fullscreen-exit">
|
||||||
class="flex-row width-100"
|
<button
|
||||||
id="fullscreen-exit"
|
class="flex"
|
||||||
>
|
on:click={taskViewModel.closeFullscreenDecription}
|
||||||
<button
|
>
|
||||||
class="flex"
|
{coreViewModel.translations.general.exitButton}
|
||||||
on:click={taskViewModel.closeFullscreenDecription}
|
<span class="icon">fullscreen_exit</span>
|
||||||
>
|
</button>
|
||||||
{coreViewModel.translations.general.exitButton}
|
</div>
|
||||||
<span class="icon">fullscreen_exit</span>
|
<div class="flex-row width-100" id="controls">
|
||||||
</button>
|
<button
|
||||||
</div>
|
class="flex"
|
||||||
<div
|
on:click={taskViewModel.closeAndDiscard}
|
||||||
class="flex-row width-100"
|
>
|
||||||
id="controls"
|
{coreViewModel.translations.general.closeButton}
|
||||||
>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="flex"
|
class="flex primary"
|
||||||
on:click={taskViewModel.closeAndDiscard}
|
on:click={taskViewModel.closeAndSave}
|
||||||
>
|
>
|
||||||
{coreViewModel.translations.general.closeButton}
|
{coreViewModel.translations.general.saveButton}
|
||||||
</button>
|
<span class="icon">save</span>
|
||||||
<button
|
</button>
|
||||||
class="flex primary"
|
</div>
|
||||||
on:click={taskViewModel.closeAndSave}
|
</div>
|
||||||
>
|
</div>
|
||||||
{coreViewModel.translations.general.saveButton}
|
);
|
||||||
<span class="icon">save</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
+47
-46
@@ -65,9 +65,9 @@ body[showing-chat] #chat-page-wrapper {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (pointer: coarse) and (max-width: 480px){
|
@media (pointer: coarse) and (max-width: 480px) {
|
||||||
#chat-page > div > #main > div > .pane-wrapper > .pane {
|
#chat-page > div > #main > div > .pane-wrapper > .pane {
|
||||||
flex-direction: column-reverse;
|
flex-direction: column-reverse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,13 +78,13 @@ body[showing-chat] #chat-page-wrapper {
|
|||||||
|
|
||||||
@media (min-width: 1024px) {
|
@media (min-width: 1024px) {
|
||||||
#chat-page > div > #main > div > .pane-wrapper {
|
#chat-page > div > #main > div > .pane-wrapper {
|
||||||
min-width: unset;
|
min-width: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat-page > div > #main > div > .pane-wrapper.side {
|
#chat-page > div > #main > div > .pane-wrapper.side {
|
||||||
width: 18rem;
|
width: 18rem;
|
||||||
min-width: 18rem;
|
min-width: 18rem;
|
||||||
max-width: 18rem;
|
max-width: 18rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,21 +114,21 @@ body[showing-chat] #chat-page-wrapper {
|
|||||||
|
|
||||||
#chat-page > div > #ribbon > span,
|
#chat-page > div > #ribbon > span,
|
||||||
#chat-page
|
#chat-page
|
||||||
> div
|
> div
|
||||||
> #main
|
> #main
|
||||||
> div
|
> div
|
||||||
> .pane-wrapper
|
> .pane-wrapper
|
||||||
> .pane
|
> .pane
|
||||||
> .toolbar
|
> .toolbar
|
||||||
> span.ribbon {
|
> span.ribbon {
|
||||||
gap: var(--gap-between-tiles);
|
gap: var(--gap-between-tiles);
|
||||||
padding: var(--gap-between-tiles);
|
padding: var(--gap-between-tiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (pointer: coarse) and (max-width: 480px){
|
@media (pointer: coarse) and (max-width: 480px) {
|
||||||
#chat-page > div > #ribbon #close-button {
|
#chat-page > div > #ribbon #close-button {
|
||||||
order: 1;
|
order: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat-page > div > #main > div > .pane-wrapper > .pane > .toolbar {
|
#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 > #ribbon button.ribbon-button,
|
||||||
#chat-page
|
#chat-page
|
||||||
> div
|
> div
|
||||||
> #main
|
> #main
|
||||||
> div
|
> div
|
||||||
> .pane-wrapper
|
> .pane-wrapper
|
||||||
> .pane
|
> .pane
|
||||||
> .toolbar
|
> .toolbar
|
||||||
button.ribbon-button {
|
button.ribbon-button {
|
||||||
min-width: calc(var(--control-height) - 2 * var(--gap-between-tiles));
|
min-width: calc(var(--control-height) - 2 * var(--gap-between-tiles));
|
||||||
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));
|
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 > #ribbon button > .icon,
|
||||||
#chat-page
|
#chat-page
|
||||||
> div
|
> div
|
||||||
> #main
|
> #main
|
||||||
> div
|
> div
|
||||||
> .pane-wrapper
|
> .pane-wrapper
|
||||||
> .pane
|
> .pane
|
||||||
> .toolbar
|
> .toolbar
|
||||||
button
|
button
|
||||||
> .icon {
|
> .icon {
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,13 +244,13 @@ button
|
|||||||
}
|
}
|
||||||
|
|
||||||
#chat-page
|
#chat-page
|
||||||
> div
|
> div
|
||||||
> #main
|
> #main
|
||||||
> div
|
> div
|
||||||
> .pane-wrapper
|
> .pane-wrapper
|
||||||
> .pane
|
> .pane
|
||||||
> .toolbar
|
> .toolbar
|
||||||
:is(input, select, button) {
|
:is(input, select, button) {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
@@ -262,18 +262,19 @@ button
|
|||||||
|
|
||||||
@media (min-width: 640px) {
|
@media (min-width: 640px) {
|
||||||
.modal.task-settings .column-wrapper {
|
.modal.task-settings .column-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: var(--content-padding);
|
gap: var(--content-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal.task-settings .column-wrapper > .flex-column{
|
.modal.task-settings .column-wrapper > .flex-column {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
flex: 1;
|
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;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+44
-43
@@ -1,4 +1,3 @@
|
|||||||
import * as React from "../react";
|
|
||||||
import { ViewController } from "./viewController";
|
import { ViewController } from "./viewController";
|
||||||
import "./homePage.css";
|
import "./homePage.css";
|
||||||
import { ChatViewToggleButton } from "./Components/chatViewToggleButton";
|
import { ChatViewToggleButton } from "./Components/chatViewToggleButton";
|
||||||
@@ -6,6 +5,7 @@ import { TaskPage } from "./ChatPages/taskPage";
|
|||||||
import { SettingsPage } from "./ChatPages/settingsPage";
|
import { SettingsPage } from "./ChatPages/settingsPage";
|
||||||
import { MessagePage } from "./ChatPages/messagePage";
|
import { MessagePage } from "./ChatPages/messagePage";
|
||||||
import { CalendarPage } from "./ChatPages/calendarPage";
|
import { CalendarPage } from "./ChatPages/calendarPage";
|
||||||
|
import * as React from "../react";
|
||||||
import CoreViewModel from "../ViewModel/Global/coreViewModel";
|
import CoreViewModel from "../ViewModel/Global/coreViewModel";
|
||||||
import ChatViewModel, { ChatPageTypes } from "../ViewModel/Chat/chatViewModel";
|
import ChatViewModel, { ChatPageTypes } from "../ViewModel/Chat/chatViewModel";
|
||||||
|
|
||||||
@@ -73,8 +73,8 @@ export function ChatPage(
|
|||||||
<div>
|
<div>
|
||||||
<div id="ribbon">
|
<div id="ribbon">
|
||||||
<button
|
<button
|
||||||
class="ghost"
|
class="ghost"
|
||||||
id="close-button"
|
id="close-button"
|
||||||
aria-label={
|
aria-label={
|
||||||
coreViewModel.translations.chatPage
|
coreViewModel.translations.chatPage
|
||||||
.closeChatAudioLabe
|
.closeChatAudioLabe
|
||||||
@@ -95,47 +95,48 @@ export function ChatPage(
|
|||||||
toggle:hidden={
|
toggle:hidden={
|
||||||
chatViewModel.connectionViewModel.isConnected
|
chatViewModel.connectionViewModel.isConnected
|
||||||
}
|
}
|
||||||
toggle:disabled={
|
toggle:disabled={
|
||||||
chatViewModel.connectionViewModel.hasNoPreviousConnections
|
chatViewModel.connectionViewModel
|
||||||
}
|
.hasNoPreviousConnections
|
||||||
>
|
}
|
||||||
<span class="icon">signal_disconnected</span>
|
>
|
||||||
</button>
|
<span class="icon">signal_disconnected</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
<span class="marquee" children:set={marqueeContent}></span>
|
<span class="marquee" children:set={marqueeContent}></span>
|
||||||
|
|
||||||
<span class="navigation-buttons">
|
<span class="navigation-buttons">
|
||||||
{ChatViewToggleButton(
|
{ChatViewToggleButton(
|
||||||
coreViewModel.translations.chatPage.pages.calendar,
|
coreViewModel.translations.chatPage.pages.calendar,
|
||||||
"calendar_month",
|
"calendar_month",
|
||||||
ChatPageTypes.Calendar,
|
ChatPageTypes.Calendar,
|
||||||
chatViewModel,
|
chatViewModel,
|
||||||
)}
|
)}
|
||||||
{ChatViewToggleButton(
|
{ChatViewToggleButton(
|
||||||
coreViewModel.translations.chatPage.pages.tasks,
|
coreViewModel.translations.chatPage.pages.tasks,
|
||||||
"task_alt",
|
"task_alt",
|
||||||
ChatPageTypes.Tasks,
|
ChatPageTypes.Tasks,
|
||||||
chatViewModel,
|
chatViewModel,
|
||||||
)}
|
)}
|
||||||
{ChatViewToggleButton(
|
{ChatViewToggleButton(
|
||||||
coreViewModel.translations.chatPage.pages.messages,
|
coreViewModel.translations.chatPage.pages.messages,
|
||||||
"forum",
|
"forum",
|
||||||
ChatPageTypes.Messages,
|
ChatPageTypes.Messages,
|
||||||
chatViewModel,
|
chatViewModel,
|
||||||
)}
|
)}
|
||||||
{ChatViewToggleButton(
|
{ChatViewToggleButton(
|
||||||
coreViewModel.translations.chatPage.pages.settings,
|
coreViewModel.translations.chatPage.pages.settings,
|
||||||
"settings",
|
"settings",
|
||||||
ChatPageTypes.Settings,
|
ChatPageTypes.Settings,
|
||||||
chatViewModel,
|
chatViewModel,
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
id="main"
|
id="main"
|
||||||
children:set={ViewController.chatPages.state}
|
children:set={ViewController.chatPages.state}
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as React from "../react";
|
|
||||||
import "./chatPage.css";
|
import "./chatPage.css";
|
||||||
import { ChatPage } from "./chatPage";
|
import { ChatPage } from "./chatPage";
|
||||||
|
import * as React from "../react";
|
||||||
import CoreViewModel from "../ViewModel/Global/coreViewModel";
|
import CoreViewModel from "../ViewModel/Global/coreViewModel";
|
||||||
import ChatListViewModel from "../ViewModel/Chat/chatListViewModel";
|
import ChatListViewModel from "../ViewModel/Chat/chatListViewModel";
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import * as React from "../react";
|
|
||||||
import "./homePage.css";
|
import "./homePage.css";
|
||||||
import { StringToOption } from "./Components/option";
|
import { StringToOption } from "./Components/option";
|
||||||
import { HomePageButton } from "./Components/homePageButton";
|
import { HomePageButton } from "./Components/homePageButton";
|
||||||
import { ChatViewModelToChatEntry } from "./Components/chatEntry";
|
import { ChatViewModelToChatEntry } from "./Components/chatEntry";
|
||||||
|
import * as React from "../react";
|
||||||
import StorageViewModel from "../ViewModel/Global/storageViewModel";
|
import StorageViewModel from "../ViewModel/Global/storageViewModel";
|
||||||
import SettingsViewModel from "../ViewModel/Global/settingsViewModel";
|
import SettingsViewModel from "../ViewModel/Global/settingsViewModel";
|
||||||
import FileTransferViewModel from "../ViewModel/Global/fileTransferViewModel";
|
import FileTransferViewModel from "../ViewModel/Global/fileTransferViewModel";
|
||||||
|
|||||||
+536
-536
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ class Tracker<T> {
|
|||||||
class TrackerMap<T> {
|
class TrackerMap<T> {
|
||||||
trackers = new Map<string, Tracker<T>>();
|
trackers = new Map<string, Tracker<T>>();
|
||||||
|
|
||||||
private register = (key: string): Tracker<T>=> {
|
private register = (key: string): Tracker<T> => {
|
||||||
if (this.trackers.has(key)) {
|
if (this.trackers.has(key)) {
|
||||||
return this.trackers.get(key)!;
|
return this.trackers.get(key)!;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import ChatViewModel from "./chatViewModel";
|
import ChatViewModel from "./chatViewModel";
|
||||||
import SettingsViewModel from "../Global/settingsViewModel";
|
import SettingsViewModel from "../Global/settingsViewModel";
|
||||||
import NotificationViewModel from "../Global/notificationViewModel";
|
import NotificationViewModel from "../Global/notificationViewModel";
|
||||||
import CoreViewModel from "../Global/coreViewModel";
|
import CoreViewModel from "../Global/coreViewModel";
|
||||||
import ConnectionViewModel from "../Global/connectionViewModel";
|
import ConnectionViewModel from "../Global/connectionViewModel";
|
||||||
|
import * as React from "../../react";
|
||||||
import { IndexManager } from "../../Model/Utility/utility";
|
import { IndexManager } from "../../Model/Utility/utility";
|
||||||
import ChatModel from "../../Model/Chat/chatModel";
|
import ChatModel from "../../Model/Chat/chatModel";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import MessagePageViewModel from "../Pages/messagePageViewModel";
|
import MessagePageViewModel from "../Pages/messagePageViewModel";
|
||||||
import CoreViewModel from "../Global/coreViewModel";
|
import CoreViewModel from "../Global/coreViewModel";
|
||||||
|
import * as React from "../../react";
|
||||||
import {
|
import {
|
||||||
ChatMessage,
|
ChatMessage,
|
||||||
ChatMessageReaction,
|
ChatMessageReaction,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import ChatListViewModel from "./chatListViewModel";
|
import ChatListViewModel from "./chatListViewModel";
|
||||||
import TaskPageViewModel from "../Pages/taskPageViewModel";
|
import TaskPageViewModel from "../Pages/taskPageViewModel";
|
||||||
import SettingsPageViewModel from "../Pages/settingsPageViewModel";
|
import SettingsPageViewModel from "../Pages/settingsPageViewModel";
|
||||||
@@ -10,6 +9,7 @@ import SettingsViewModel from "../Global/settingsViewModel";
|
|||||||
import NotificationViewModel from "../Global/notificationViewModel";
|
import NotificationViewModel from "../Global/notificationViewModel";
|
||||||
import CoreViewModel, { Context, ContextHost } from "../Global/coreViewModel";
|
import CoreViewModel, { Context, ContextHost } from "../Global/coreViewModel";
|
||||||
import ConnectionViewModel from "../Global/connectionViewModel";
|
import ConnectionViewModel from "../Global/connectionViewModel";
|
||||||
|
import * as React from "../../react";
|
||||||
import { Colors } from "../../colors";
|
import { Colors } from "../../colors";
|
||||||
import { CommonKeys } from "../../View/keystrokes";
|
import { CommonKeys } from "../../View/keystrokes";
|
||||||
import { Entry } from "../../View/Components/option";
|
import { Entry } from "../../View/Components/option";
|
||||||
|
|||||||
Binary file not shown.
@@ -1,5 +1,5 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import CoreViewModel from "./coreViewModel";
|
import CoreViewModel from "./coreViewModel";
|
||||||
|
import * as React from "../../react";
|
||||||
|
|
||||||
export default class ConnectionViewModel {
|
export default class ConnectionViewModel {
|
||||||
// state
|
// state
|
||||||
@@ -48,7 +48,7 @@ export default class ConnectionViewModel {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.hideConnectionModal();
|
this.hideConnectionModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
// methods
|
// methods
|
||||||
@@ -69,8 +69,8 @@ export default class ConnectionViewModel {
|
|||||||
this.coreViewModel.connectionModel.removeAddress(address);
|
this.coreViewModel.connectionModel.removeAddress(address);
|
||||||
this.updatePreviousAddresses();
|
this.updatePreviousAddresses();
|
||||||
|
|
||||||
if (this.previousAddresses.value.size > 0) return;
|
if (this.previousAddresses.value.size > 0) return;
|
||||||
this.hideConnectionModal();
|
this.hideConnectionModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
// view
|
// view
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ export default class CoreViewModel {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
while (this.contexts.length > 0) {
|
while (this.contexts.length > 0) {
|
||||||
const currentContext: Context|undefined= this.context;
|
const currentContext: Context | undefined = this.context;
|
||||||
if (!currentContext) return;
|
if (!currentContext) return;
|
||||||
currentContext.handleContextClose(fromHistoryEvent);
|
currentContext.handleContextClose(fromHistoryEvent);
|
||||||
this.contextStack.delete(currentContext.contextId);
|
this.contextStack.delete(currentContext.contextId);
|
||||||
if (currentContext.contextId == contextId) break;
|
if (currentContext.contextId == contextId) break;
|
||||||
@@ -49,9 +49,10 @@ export default class CoreViewModel {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handleKeyDown = (e: KeyboardEvent): void => {
|
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;
|
if (CoreViewModel.checkIsKeystroke(e) == false) return;
|
||||||
console.log(e.key);
|
console.log(e.key);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const contexts: Context[] = this.contexts;
|
const contexts: Context[] = this.contexts;
|
||||||
while (contexts.length > 0) {
|
while (contexts.length > 0) {
|
||||||
@@ -97,7 +98,7 @@ export default class CoreViewModel {
|
|||||||
document.body.addEventListener("keydown", this.handleKeyDown);
|
document.body.addEventListener("keydown", this.handleKeyDown);
|
||||||
|
|
||||||
window.onpopstate = () => {
|
window.onpopstate = () => {
|
||||||
if (!this.context) return;
|
if (!this.context) return;
|
||||||
this.closeContext(this.context.contextId, true);
|
this.closeContext(this.context.contextId, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -164,11 +165,10 @@ export class ContextHost<T> extends Context {
|
|||||||
updateContexts = (): void => {
|
updateContexts = (): void => {
|
||||||
if (this.isOpen == false) return;
|
if (this.isOpen == false) return;
|
||||||
|
|
||||||
const selection = this.contextSelection;
|
const selection = this.contextSelection;
|
||||||
if (!selection) return;
|
if (!selection) return;
|
||||||
const selectedContext: Context | undefined = this.contexts.get(
|
const selectedContext: Context | undefined =
|
||||||
selection
|
this.contexts.get(selection);
|
||||||
);
|
|
||||||
if (!selectedContext) return;
|
if (!selectedContext) return;
|
||||||
if (selectedContext != this.currentContext.value) {
|
if (selectedContext != this.currentContext.value) {
|
||||||
this.closeCurrentContext();
|
this.closeCurrentContext();
|
||||||
@@ -178,7 +178,10 @@ export class ContextHost<T> extends Context {
|
|||||||
this.currentContext.value = selectedContext;
|
this.currentContext.value = selectedContext;
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(contextDebugDescription: string, public coreViewModel: CoreViewModel) {
|
constructor(
|
||||||
super(contextDebugDescription);
|
contextDebugDescription: string,
|
||||||
|
public coreViewModel: CoreViewModel,
|
||||||
|
) {
|
||||||
|
super(contextDebugDescription);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import CoreViewModel, { Context } from "./coreViewModel";
|
import CoreViewModel, { Context } from "./coreViewModel";
|
||||||
|
import * as React from "../../react";
|
||||||
import { CommonKeys } from "../../View/keystrokes";
|
import { CommonKeys } from "../../View/keystrokes";
|
||||||
import StorageModel, {
|
import StorageModel, {
|
||||||
StorageModelSubPaths,
|
StorageModelSubPaths,
|
||||||
@@ -54,7 +54,9 @@ export default class FileTransferViewModel extends Context {
|
|||||||
exportKey: React.State<string> = new React.State("");
|
exportKey: React.State<string> = new React.State("");
|
||||||
exportKeyConfirmation: 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("");
|
importedFileString: React.State<string> = new React.State("");
|
||||||
importKey: React.State<string> = new React.State("");
|
importKey: React.State<string> = new React.State("");
|
||||||
isImportKeyCorrect: React.State<boolean> = new React.State(true);
|
isImportKeyCorrect: React.State<boolean> = new React.State(true);
|
||||||
@@ -139,7 +141,7 @@ export default class FileTransferViewModel extends Context {
|
|||||||
};
|
};
|
||||||
|
|
||||||
downloadFile = async (): Promise<void> => {
|
downloadFile = async (): Promise<void> => {
|
||||||
if (this.cannotExport.value == true) return;
|
if (this.cannotExport.value == true) return;
|
||||||
const date = new Date().toISOString().split("T")[0];
|
const date = new Date().toISOString().split("T")[0];
|
||||||
|
|
||||||
const backup: Blob =
|
const backup: Blob =
|
||||||
@@ -158,7 +160,7 @@ export default class FileTransferViewModel extends Context {
|
|||||||
if (!input) return;
|
if (!input) return;
|
||||||
if (!(input instanceof HTMLInputElement)) return;
|
if (!(input instanceof HTMLInputElement)) return;
|
||||||
|
|
||||||
if (!input.files) return;
|
if (!input.files) return;
|
||||||
const file = input.files[0];
|
const file = input.files[0];
|
||||||
this.importedFile.value = file;
|
this.importedFile.value = file;
|
||||||
};
|
};
|
||||||
@@ -171,7 +173,7 @@ export default class FileTransferViewModel extends Context {
|
|||||||
reader.readAsText(file, "utf8");
|
reader.readAsText(file, "utf8");
|
||||||
reader.onload = () => {
|
reader.onload = () => {
|
||||||
const result = reader.result;
|
const result = reader.result;
|
||||||
if (!result) return;
|
if (!result) return;
|
||||||
this.importedFileString.value = result.toString();
|
this.importedFileString.value = result.toString();
|
||||||
this.showImportDecryptDataModal();
|
this.showImportDecryptDataModal();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import { ChatPageTypes } from "../Chat/chatViewModel";
|
import { ChatPageTypes } from "../Chat/chatViewModel";
|
||||||
import ChatListViewModel from "../Chat/chatListViewModel";
|
import ChatListViewModel from "../Chat/chatListViewModel";
|
||||||
|
import * as React from "../../react";
|
||||||
import ChatModel, { ChatMessage } from "../../Model/Chat/chatModel";
|
import ChatModel, { ChatMessage } from "../../Model/Chat/chatModel";
|
||||||
|
|
||||||
export default class NotificationViewModel {
|
export default class NotificationViewModel {
|
||||||
@@ -35,7 +35,7 @@ export default class NotificationViewModel {
|
|||||||
};
|
};
|
||||||
|
|
||||||
openNotification = () => {
|
openNotification = () => {
|
||||||
const notification: Notification|undefined = this.marquee.value;
|
const notification: Notification | undefined = this.marquee.value;
|
||||||
if (notification == undefined) return;
|
if (notification == undefined) return;
|
||||||
|
|
||||||
const chat = [
|
const chat = [
|
||||||
@@ -44,7 +44,7 @@ export default class NotificationViewModel {
|
|||||||
(chat) =>
|
(chat) =>
|
||||||
chat.chatModel.unwrappedPrimaryChannel == notification.chat,
|
chat.chatModel.unwrappedPrimaryChannel == notification.chat,
|
||||||
);
|
);
|
||||||
if (!chat) return;
|
if (!chat) return;
|
||||||
chat.open();
|
chat.open();
|
||||||
chat.openPage(ChatPageTypes.Messages);
|
chat.openPage(ChatPageTypes.Messages);
|
||||||
};
|
};
|
||||||
@@ -56,8 +56,9 @@ export default class NotificationViewModel {
|
|||||||
return this.stopLoop();
|
return this.stopLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
const notification: Notification|undefined = this.messagesInMarquee.shift();
|
const notification: Notification | undefined =
|
||||||
if (!notification) return;
|
this.messagesInMarquee.shift();
|
||||||
|
if (!notification) return;
|
||||||
this.seenMessageIds.delete(notification.messageId);
|
this.seenMessageIds.delete(notification.messageId);
|
||||||
this.marquee.value = notification;
|
this.marquee.value = notification;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import CoreViewModel, { Context } from "./coreViewModel";
|
import CoreViewModel, { Context } from "./coreViewModel";
|
||||||
|
import * as React from "../../react";
|
||||||
import { CommonKeys } from "../../View/keystrokes";
|
import { CommonKeys } from "../../View/keystrokes";
|
||||||
import { Languages, ThemeSettings } from "../../Model/Global/settingsModel";
|
import { Languages, ThemeSettings } from "../../Model/Global/settingsModel";
|
||||||
|
|
||||||
@@ -13,12 +13,12 @@ export default class SettingsViewModel extends Context {
|
|||||||
requiresReload = new React.State<boolean>(false);
|
requiresReload = new React.State<boolean>(false);
|
||||||
|
|
||||||
firstDayOfWeek: React.State<string> = new React.State("0");
|
firstDayOfWeek: React.State<string> = new React.State("0");
|
||||||
language: React.State<Languages | string> = new React.State<Languages | string>(
|
language: React.State<Languages | string> = new React.State<
|
||||||
Languages.English,
|
Languages | string
|
||||||
);
|
>(Languages.English);
|
||||||
theme: React.State<ThemeSettings | string> = new React.State<ThemeSettings | string>(
|
theme: React.State<ThemeSettings | string> = new React.State<
|
||||||
ThemeSettings.System,
|
ThemeSettings | string
|
||||||
);
|
>(ThemeSettings.System);
|
||||||
|
|
||||||
// guards
|
// guards
|
||||||
cannotSetName: React.State<boolean> = React.createProxyState(
|
cannotSetName: React.State<boolean> = React.createProxyState(
|
||||||
@@ -48,121 +48,132 @@ export default class SettingsViewModel extends Context {
|
|||||||
};
|
};
|
||||||
|
|
||||||
showModalPage = (page: SettingsModalPages): void => {
|
showModalPage = (page: SettingsModalPages): void => {
|
||||||
this.selectedModalPage.value = page;
|
this.selectedModalPage.value = page;
|
||||||
};
|
};
|
||||||
|
|
||||||
// view
|
// view
|
||||||
applyTheme = (): void => {
|
applyTheme = (): void => {
|
||||||
let theme: string = this.theme.value;
|
let theme: string = this.theme.value;
|
||||||
|
|
||||||
if (theme == ThemeSettings.Dynamic) {
|
if (theme == ThemeSettings.Dynamic) {
|
||||||
const scene = this.selectDynamicScene();
|
const scene = this.selectDynamicScene();
|
||||||
this.setScene(scene);
|
this.setScene(scene);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theme == ThemeSettings.System) {
|
if (theme == ThemeSettings.System) {
|
||||||
theme = SettingsViewModel.getSystemTheme();
|
theme = SettingsViewModel.getSystemTheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theme == ThemeSettings.Dark) {
|
if (theme == ThemeSettings.Dark) {
|
||||||
this.setScene(DynamicSceneNight);
|
this.setScene(DynamicSceneNight);
|
||||||
} else {
|
} else {
|
||||||
this.setScene(DynamicSceneDay);
|
this.setScene(DynamicSceneDay);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
selectDynamicScene = (): DynamicScene => {
|
selectDynamicScene = (): DynamicScene => {
|
||||||
let hour = new Date().getHours();
|
let hour = new Date().getHours();
|
||||||
|
|
||||||
if (hour >= 21 || hour <= 6) {
|
if (hour >= 21 || hour <= 6) {
|
||||||
return DynamicSceneNight;
|
return DynamicSceneNight;
|
||||||
} else if (hour > 6 && hour < 10) {
|
} else if (hour > 6 && hour < 10) {
|
||||||
return DynamicSceneSunrise;
|
return DynamicSceneSunrise;
|
||||||
} else if (hour < 14) {
|
} else if (hour < 14) {
|
||||||
return DynamicSceneDay;
|
return DynamicSceneDay;
|
||||||
} else if (hour < 17) {
|
} else if (hour < 17) {
|
||||||
return DynamicSceneAfternoon
|
return DynamicSceneAfternoon;
|
||||||
} else {
|
} else {
|
||||||
return DynamicSceneSunset
|
return DynamicSceneSunset;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
setScene = (scene: DynamicScene): void => {
|
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) {
|
function setSkyColor(
|
||||||
document.body.style.setProperty(`--sky-${tone}`, `hsl(${hue}, ${saturation}%, ${luma}%)`);
|
tone: 1 | 2,
|
||||||
}
|
hue: number,
|
||||||
setSkyColor(1, scene.hue1, scene.saturation, scene.luma);
|
saturation: number,
|
||||||
setSkyColor(2, scene.hue2, scene.saturation - 10, scene.luma-10);
|
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);
|
||||||
|
|
||||||
document.body.setAttribute("theme", scene.baseTheme);
|
document.body.setAttribute("theme", scene.baseTheme);
|
||||||
}
|
};
|
||||||
|
|
||||||
// exit
|
// exit
|
||||||
close = (): void => {
|
close = (): void => {
|
||||||
this.coreViewModel.closeContext(this.contextId);
|
this.coreViewModel.closeContext(this.contextId);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleContextClose = (): void => {
|
handleContextClose = (): void => {
|
||||||
this.isShowingSettingsModal.value = false;
|
this.isShowingSettingsModal.value = false;
|
||||||
if (this.requiresReload.value == true) {
|
if (this.requiresReload.value == true) {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// init
|
// init
|
||||||
constructor(public readonly coreViewModel: CoreViewModel) {
|
constructor(public readonly coreViewModel: CoreViewModel) {
|
||||||
super("settings");
|
super("settings");
|
||||||
|
|
||||||
this.username.value = coreViewModel.settingsModel.username;
|
this.username.value = coreViewModel.settingsModel.username;
|
||||||
this.usernameInput.value = coreViewModel.settingsModel.username;
|
this.usernameInput.value = coreViewModel.settingsModel.username;
|
||||||
this.firstDayOfWeek.value = coreViewModel.settingsModel.firstDayOfWeek;
|
this.firstDayOfWeek.value = coreViewModel.settingsModel.firstDayOfWeek;
|
||||||
this.language.value = coreViewModel.settingsModel.language;
|
this.language.value = coreViewModel.settingsModel.language;
|
||||||
this.theme.value = coreViewModel.settingsModel.theme;
|
this.theme.value = coreViewModel.settingsModel.theme;
|
||||||
|
|
||||||
// subscriptions
|
// subscriptions
|
||||||
this.firstDayOfWeek.subscribe(this.setFirstDayofWeek);
|
this.firstDayOfWeek.subscribe(this.setFirstDayofWeek);
|
||||||
this.language.subscribeSilent((newValue) => {
|
this.language.subscribeSilent((newValue) => {
|
||||||
this.coreViewModel.settingsModel.setLanguage(newValue);
|
this.coreViewModel.settingsModel.setLanguage(newValue);
|
||||||
this.requiresReload.value = true;
|
this.requiresReload.value = true;
|
||||||
});
|
});
|
||||||
this.theme.subscribeSilent((newValue) => {
|
this.theme.subscribeSilent((newValue) => {
|
||||||
this.coreViewModel.settingsModel.setTheme(newValue);
|
this.coreViewModel.settingsModel.setTheme(newValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
// set theme
|
// set theme
|
||||||
this.theme.subscribe(() => {
|
this.theme.subscribe(() => {
|
||||||
this.applyTheme();
|
this.applyTheme();
|
||||||
});
|
});
|
||||||
SettingsViewModel.generateThemeMedia().addEventListener("change", () =>
|
SettingsViewModel.generateThemeMedia().addEventListener("change", () =>
|
||||||
this.applyTheme(),
|
this.applyTheme(),
|
||||||
);
|
);
|
||||||
const seconds = new Date().getSeconds();
|
const seconds = new Date().getSeconds();
|
||||||
const secondsUntilNewMinute = 60-seconds;
|
const secondsUntilNewMinute = 60 - seconds;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setInterval(this.applyTheme, 1000*60);
|
setInterval(this.applyTheme, 1000 * 60);
|
||||||
}, secondsUntilNewMinute);
|
}, secondsUntilNewMinute);
|
||||||
|
|
||||||
// keystrokes
|
// keystrokes
|
||||||
this.registerKeyStroke(CommonKeys.CloseOrCancel, this.close);
|
this.registerKeyStroke(CommonKeys.CloseOrCancel, this.close);
|
||||||
}
|
}
|
||||||
|
|
||||||
static generateThemeMedia(): MediaQueryList {
|
static generateThemeMedia(): MediaQueryList {
|
||||||
return window.matchMedia("(prefers-color-scheme: dark)");
|
return window.matchMedia("(prefers-color-scheme: dark)");
|
||||||
}
|
}
|
||||||
|
|
||||||
static getSystemTheme(): string {
|
static getSystemTheme(): string {
|
||||||
const media = SettingsViewModel.generateThemeMedia();
|
const media = SettingsViewModel.generateThemeMedia();
|
||||||
return media.matches == true ? ThemeSettings.Dark : ThemeSettings.Light;
|
return media.matches == true ? ThemeSettings.Dark : ThemeSettings.Light;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SettingsModalPages {
|
export enum SettingsModalPages {
|
||||||
Appearance,
|
Appearance,
|
||||||
Regional,
|
Regional,
|
||||||
Info,
|
Info,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DynamicScene {
|
export interface DynamicScene {
|
||||||
@@ -176,40 +187,40 @@ export interface DynamicScene {
|
|||||||
export const DynamicSceneNight: DynamicScene = {
|
export const DynamicSceneNight: DynamicScene = {
|
||||||
hue1: 215,
|
hue1: 215,
|
||||||
hue2: 220,
|
hue2: 220,
|
||||||
saturation : 50,
|
saturation: 50,
|
||||||
luma: 25,
|
luma: 25,
|
||||||
brightness: 0.4,
|
brightness: 0.4,
|
||||||
baseTheme: ThemeSettings.Dark,
|
baseTheme: ThemeSettings.Dark,
|
||||||
}
|
};
|
||||||
export const DynamicSceneSunrise: DynamicScene = {
|
export const DynamicSceneSunrise: DynamicScene = {
|
||||||
hue1: 45,
|
hue1: 45,
|
||||||
hue2: 190,
|
hue2: 190,
|
||||||
saturation : 100,
|
saturation: 100,
|
||||||
luma: 50,
|
luma: 50,
|
||||||
brightness: 1.2,
|
brightness: 1.2,
|
||||||
baseTheme: ThemeSettings.Light,
|
baseTheme: ThemeSettings.Light,
|
||||||
}
|
};
|
||||||
export const DynamicSceneDay: DynamicScene = {
|
export const DynamicSceneDay: DynamicScene = {
|
||||||
hue1: 190,
|
hue1: 190,
|
||||||
hue2: 180,
|
hue2: 180,
|
||||||
saturation : 100,
|
saturation: 100,
|
||||||
luma: 70,
|
luma: 70,
|
||||||
brightness: 1.4,
|
brightness: 1.4,
|
||||||
baseTheme: ThemeSettings.Light,
|
baseTheme: ThemeSettings.Light,
|
||||||
}
|
};
|
||||||
export const DynamicSceneAfternoon: DynamicScene = {
|
export const DynamicSceneAfternoon: DynamicScene = {
|
||||||
hue1: 195,
|
hue1: 195,
|
||||||
hue2: 220,
|
hue2: 220,
|
||||||
saturation : 100,
|
saturation: 100,
|
||||||
luma: 50,
|
luma: 50,
|
||||||
brightness: 0.8,
|
brightness: 0.8,
|
||||||
baseTheme: ThemeSettings.Light,
|
baseTheme: ThemeSettings.Light,
|
||||||
}
|
};
|
||||||
export const DynamicSceneSunset: DynamicScene = {
|
export const DynamicSceneSunset: DynamicScene = {
|
||||||
hue1: 20,
|
hue1: 20,
|
||||||
hue2: 260,
|
hue2: 260,
|
||||||
saturation : 100,
|
saturation: 100,
|
||||||
luma: 50,
|
luma: 50,
|
||||||
brightness: 0.4,
|
brightness: 0.4,
|
||||||
baseTheme: ThemeSettings.Dark,
|
baseTheme: ThemeSettings.Dark,
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import CoreViewModel, { Context } from "./coreViewModel";
|
import CoreViewModel, { Context } from "./coreViewModel";
|
||||||
|
import * as React from "../../react";
|
||||||
import { CommonKeys } from "../../View/keystrokes";
|
import { CommonKeys } from "../../View/keystrokes";
|
||||||
import StorageModel, {
|
import StorageModel, {
|
||||||
PATH_COMPONENT_SEPARATOR,
|
PATH_COMPONENT_SEPARATOR,
|
||||||
|
|||||||
Binary file not shown.
@@ -1,10 +1,10 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import TaskViewModel from "./taskViewModel";
|
import TaskViewModel from "./taskViewModel";
|
||||||
import TaskPageViewModel from "./taskPageViewModel";
|
import TaskPageViewModel from "./taskPageViewModel";
|
||||||
import TaskContainingPageViewModel from "./taskContainingPageViewModel";
|
import TaskContainingPageViewModel from "./taskContainingPageViewModel";
|
||||||
import SearchViewModel from "../Utility/searchViewModel";
|
import SearchViewModel from "../Utility/searchViewModel";
|
||||||
import CoreViewModel from "../Global/coreViewModel";
|
import CoreViewModel from "../Global/coreViewModel";
|
||||||
import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel";
|
import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel";
|
||||||
|
import * as React from "../../react";
|
||||||
import { Colors } from "../../colors";
|
import { Colors } from "../../colors";
|
||||||
import { CommonKeys } from "../../View/keystrokes";
|
import { CommonKeys } from "../../View/keystrokes";
|
||||||
import BoardsAndTasksModel, {
|
import BoardsAndTasksModel, {
|
||||||
@@ -115,11 +115,7 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
|||||||
searchTerm,
|
searchTerm,
|
||||||
];
|
];
|
||||||
this.coreViewModel.storageModel.write(suggestionPath, "");
|
this.coreViewModel.storageModel.write(suggestionPath, "");
|
||||||
if (
|
if (!this.searchSuggestions.value.has(searchTerm) && searchTerm != "") {
|
||||||
!this.searchSuggestions.value.has(
|
|
||||||
searchTerm,
|
|
||||||
) && searchTerm != ""
|
|
||||||
) {
|
|
||||||
this.searchSuggestions.add(searchTerm);
|
this.searchSuggestions.add(searchTerm);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +129,7 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
|||||||
searchTerm,
|
searchTerm,
|
||||||
];
|
];
|
||||||
this.coreViewModel.storageModel.remove(suggestionPath);
|
this.coreViewModel.storageModel.remove(suggestionPath);
|
||||||
}
|
};
|
||||||
|
|
||||||
// view
|
// view
|
||||||
showTask = (taskFileContent: TaskFileContent): void => {
|
showTask = (taskFileContent: TaskFileContent): void => {
|
||||||
@@ -154,7 +150,7 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
|||||||
this,
|
this,
|
||||||
taskFileContent,
|
taskFileContent,
|
||||||
);
|
);
|
||||||
console.log("STILL HERE", taskFileContent.fileId);
|
console.log("STILL HERE", taskFileContent.fileId);
|
||||||
this.taskViewModels.set(taskFileContent.fileId, taskViewModel);
|
this.taskViewModels.set(taskFileContent.fileId, taskViewModel);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -234,7 +230,7 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
|||||||
const dirPath: string[] = this.getPreviousSearchesPath();
|
const dirPath: string[] = this.getPreviousSearchesPath();
|
||||||
const searches: string[] =
|
const searches: string[] =
|
||||||
this.coreViewModel.storageModel.list(dirPath);
|
this.coreViewModel.storageModel.list(dirPath);
|
||||||
this.searchSuggestions.add(...searches.filter(x => x != ""));
|
this.searchSuggestions.add(...searches.filter((x) => x != ""));
|
||||||
};
|
};
|
||||||
|
|
||||||
restoreSearch = (): void => {
|
restoreSearch = (): void => {
|
||||||
@@ -307,13 +303,13 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
|||||||
this.taskViewModels,
|
this.taskViewModels,
|
||||||
this.filteredTaskViewModels,
|
this.filteredTaskViewModels,
|
||||||
TaskViewModel.getStringsForFilter,
|
TaskViewModel.getStringsForFilter,
|
||||||
this.searchSuggestions,
|
this.searchSuggestions,
|
||||||
);
|
);
|
||||||
this.searchViewModel.appliedQuery.subscribeSilent((newQuery) => {
|
this.searchViewModel.appliedQuery.subscribeSilent((newQuery) => {
|
||||||
this.handleNewSearch(newQuery);
|
this.handleNewSearch(newQuery);
|
||||||
});
|
});
|
||||||
this.restoreSearch();
|
this.restoreSearch();
|
||||||
this.searchSuggestions.handleRemovals(this.handleSearchRemoved);
|
this.searchSuggestions.handleRemovals(this.handleSearchRemoved);
|
||||||
|
|
||||||
this.isFilterActive = React.createProxyState(
|
this.isFilterActive = React.createProxyState(
|
||||||
[this.searchViewModel.appliedQuery],
|
[this.searchViewModel.appliedQuery],
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import TaskViewModel from "./taskViewModel";
|
import TaskViewModel from "./taskViewModel";
|
||||||
import TaskContainingPageViewModel from "./taskContainingPageViewModel";
|
import TaskContainingPageViewModel from "./taskContainingPageViewModel";
|
||||||
import CoreViewModel from "../Global/coreViewModel";
|
import CoreViewModel from "../Global/coreViewModel";
|
||||||
import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel";
|
import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel";
|
||||||
|
import * as React from "../../react";
|
||||||
import { CommonKeys } from "../../View/keystrokes";
|
import { CommonKeys } from "../../View/keystrokes";
|
||||||
import CalendarModel, { MonthGrid } from "../../Model/Files/calendarModel";
|
import CalendarModel, { MonthGrid } from "../../Model/Files/calendarModel";
|
||||||
import BoardsAndTasksModel, {
|
import BoardsAndTasksModel, {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import SearchViewModel from "../Utility/searchViewModel";
|
import SearchViewModel from "../Utility/searchViewModel";
|
||||||
import CoreViewModel, { Context } from "../Global/coreViewModel";
|
import CoreViewModel, { Context } from "../Global/coreViewModel";
|
||||||
import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel";
|
import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel";
|
||||||
import ChatMessageViewModel from "../Chat/chatMessageViewModel";
|
import ChatMessageViewModel from "../Chat/chatMessageViewModel";
|
||||||
|
import * as React from "../../react";
|
||||||
import { CommonKeys } from "../../View/keystrokes";
|
import { CommonKeys } from "../../View/keystrokes";
|
||||||
import {
|
import {
|
||||||
ChatMessage,
|
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.replyingMessage.value = chatMessageViewModel;
|
||||||
this.setFocus();
|
this.setFocus();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import CoreViewModel, { Context } from "../Global/coreViewModel";
|
import CoreViewModel, { Context } from "../Global/coreViewModel";
|
||||||
import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel";
|
import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel";
|
||||||
|
import * as React from "../../react";
|
||||||
import { Colors } from "../../colors";
|
import { Colors } from "../../colors";
|
||||||
|
|
||||||
export default class SettingsPageViewModel extends Context {
|
export default class SettingsPageViewModel extends Context {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import TaskViewModel from "./taskViewModel";
|
import TaskViewModel from "./taskViewModel";
|
||||||
import CoreViewModel, { Context } from "../Global/coreViewModel";
|
import CoreViewModel, { Context } from "../Global/coreViewModel";
|
||||||
import ChatViewModel from "../Chat/chatViewModel";
|
import ChatViewModel from "../Chat/chatViewModel";
|
||||||
|
import * as React from "../../react";
|
||||||
import { IndexManager } from "../../Model/Utility/utility";
|
import { IndexManager } from "../../Model/Utility/utility";
|
||||||
import BoardsAndTasksModel, {
|
import BoardsAndTasksModel, {
|
||||||
TaskFileContent,
|
TaskFileContent,
|
||||||
@@ -21,7 +21,6 @@ export default class TaskContainingPageViewModel extends Context {
|
|||||||
taskCategorySuggestions: React.ListState<string> = new React.ListState();
|
taskCategorySuggestions: React.ListState<string> = new React.ListState();
|
||||||
taskStatusSuggestions: React.ListState<string> = new React.ListState();
|
taskStatusSuggestions: React.ListState<string> = new React.ListState();
|
||||||
|
|
||||||
|
|
||||||
// methods
|
// methods
|
||||||
createTaskFromBoardId = (boardId: string): void => {
|
createTaskFromBoardId = (boardId: string): void => {
|
||||||
const taskFileContent: TaskFileContent =
|
const taskFileContent: TaskFileContent =
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import BoardViewModel from "./boardViewModel";
|
import BoardViewModel from "./boardViewModel";
|
||||||
import CoreViewModel, { ContextHost } from "../Global/coreViewModel";
|
import CoreViewModel, { ContextHost } from "../Global/coreViewModel";
|
||||||
import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel";
|
import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel";
|
||||||
|
import * as React from "../../react";
|
||||||
import { CommonKeys } from "../../View/keystrokes";
|
import { CommonKeys } from "../../View/keystrokes";
|
||||||
import { IndexManager } from "../../Model/Utility/utility";
|
import implementFilter, { IndexManager } from "../../Model/Utility/utility";
|
||||||
import BoardsAndTasksModel, {
|
import BoardsAndTasksModel, {
|
||||||
BoardInfoFileContent,
|
BoardInfoFileContent,
|
||||||
} from "../../Model/Files/boardsAndTasksModel";
|
} from "../../Model/Files/boardsAndTasksModel";
|
||||||
@@ -26,8 +26,9 @@ export default class TaskPageViewModel extends ContextHost<string> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// state
|
// state
|
||||||
newBoardNameInput: React.State<string> = new React.State("");
|
boardQuery: React.State<string> = new React.State("");
|
||||||
boardViewModels: React.MapState<BoardViewModel> = new React.MapState();
|
boardViewModels: React.MapState<BoardViewModel> = new React.MapState();
|
||||||
|
boardMatches: React.ListState<BoardViewModel> = new React.ListState();
|
||||||
isShowingBoadList: React.State<boolean> = new React.State(true);
|
isShowingBoadList: React.State<boolean> = new React.State(true);
|
||||||
selectedBoardId: React.State<string | undefined> = new React.State<any>(
|
selectedBoardId: React.State<string | undefined> = new React.State<any>(
|
||||||
undefined,
|
undefined,
|
||||||
@@ -47,8 +48,8 @@ export default class TaskPageViewModel extends ContextHost<string> {
|
|||||||
|
|
||||||
// guards
|
// guards
|
||||||
cannotCreateBoard: React.State<boolean> = React.createProxyState(
|
cannotCreateBoard: React.State<boolean> = React.createProxyState(
|
||||||
[this.newBoardNameInput],
|
[this.boardQuery],
|
||||||
() => this.newBoardNameInput.value == "",
|
() => this.boardQuery.value == "",
|
||||||
);
|
);
|
||||||
|
|
||||||
// methods
|
// methods
|
||||||
@@ -56,8 +57,8 @@ export default class TaskPageViewModel extends ContextHost<string> {
|
|||||||
if (this.cannotCreateBoard.value == true) return;
|
if (this.cannotCreateBoard.value == true) return;
|
||||||
|
|
||||||
const boardInfoFileContent: BoardInfoFileContent =
|
const boardInfoFileContent: BoardInfoFileContent =
|
||||||
this.boardsAndTasksModel.createBoard(this.newBoardNameInput.value);
|
this.boardsAndTasksModel.createBoard(this.boardQuery.value);
|
||||||
this.newBoardNameInput.value = "";
|
this.boardQuery.value = "";
|
||||||
|
|
||||||
this.showBoardInList(boardInfoFileContent);
|
this.showBoardInList(boardInfoFileContent);
|
||||||
this.boardsAndTasksModel.updateBoardAndSend(boardInfoFileContent);
|
this.boardsAndTasksModel.updateBoardAndSend(boardInfoFileContent);
|
||||||
@@ -177,6 +178,9 @@ export default class TaskPageViewModel extends ContextHost<string> {
|
|||||||
|
|
||||||
this.chatViewModel = chatViewModel;
|
this.chatViewModel = chatViewModel;
|
||||||
|
|
||||||
|
// filter
|
||||||
|
implementFilter(this.boardViewModels, this.boardMatches, this.boardQuery, (board) => board.name.value);
|
||||||
|
|
||||||
// context
|
// context
|
||||||
this.chatViewModel.registerContext(ChatPageTypes.Tasks, this);
|
this.chatViewModel.registerContext(ChatPageTypes.Tasks, this);
|
||||||
this.selectedBoardId.subscribeSilent(this.updateContexts);
|
this.selectedBoardId.subscribeSilent(this.updateContexts);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import TaskContainingPageViewModel from "./taskContainingPageViewModel";
|
import TaskContainingPageViewModel from "./taskContainingPageViewModel";
|
||||||
import CoreViewModel, { Context } from "../Global/coreViewModel";
|
import CoreViewModel, { Context } from "../Global/coreViewModel";
|
||||||
import ChatViewModel from "../Chat/chatViewModel";
|
import ChatViewModel from "../Chat/chatViewModel";
|
||||||
|
import * as React from "../../react";
|
||||||
import { ViewController } from "../../View/viewController";
|
import { ViewController } from "../../View/viewController";
|
||||||
import { CommonKeys } from "../../View/keystrokes";
|
import { CommonKeys } from "../../View/keystrokes";
|
||||||
import { localeCompare, padZero } from "../../Model/Utility/utility";
|
import { localeCompare, padZero } from "../../Model/Utility/utility";
|
||||||
@@ -58,7 +58,9 @@ export default class TaskViewModel extends Context {
|
|||||||
selectedVersionId: React.State<string> = new React.State("");
|
selectedVersionId: React.State<string> = new React.State("");
|
||||||
versionIds: React.ListState<string> = new React.ListState();
|
versionIds: React.ListState<string> = new React.ListState();
|
||||||
|
|
||||||
isPresentingFullScreenDescription: React.State<boolean> = new React.State(false);
|
isPresentingFullScreenDescription: React.State<boolean> = new React.State(
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
// methods
|
// methods
|
||||||
dragStart = (event: DragEvent): void => {
|
dragStart = (event: DragEvent): void => {
|
||||||
@@ -99,12 +101,12 @@ export default class TaskViewModel extends Context {
|
|||||||
};
|
};
|
||||||
|
|
||||||
openFullscreenDecription = (): void => {
|
openFullscreenDecription = (): void => {
|
||||||
this.isPresentingFullScreenDescription.value = true;
|
this.isPresentingFullScreenDescription.value = true;
|
||||||
}
|
};
|
||||||
|
|
||||||
closeFullscreenDecription = (): void => {
|
closeFullscreenDecription = (): void => {
|
||||||
this.isPresentingFullScreenDescription.value = false;
|
this.isPresentingFullScreenDescription.value = false;
|
||||||
}
|
};
|
||||||
|
|
||||||
updateIndex = (): void => {
|
updateIndex = (): void => {
|
||||||
const index: number =
|
const index: number =
|
||||||
@@ -118,7 +120,9 @@ export default class TaskViewModel extends Context {
|
|||||||
this.category.value,
|
this.category.value,
|
||||||
) == false
|
) == false
|
||||||
) {
|
) {
|
||||||
this.containingViewModel.taskCategorySuggestions.add(this.category.value);
|
this.containingViewModel.taskCategorySuggestions.add(
|
||||||
|
this.category.value,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -126,7 +130,9 @@ export default class TaskViewModel extends Context {
|
|||||||
this.status.value,
|
this.status.value,
|
||||||
) == false
|
) == 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],
|
||||||
() => this.searchInput.value == "",
|
() => 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
|
// methods
|
||||||
search = (searchTerm: string): void => {
|
search = (searchTerm: string): void => {
|
||||||
@@ -36,12 +39,12 @@ export default class SearchViewModel<T> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
clear = (): void => {
|
clear = (): void => {
|
||||||
this.searchInput.value = "";
|
this.searchInput.value = "";
|
||||||
}
|
};
|
||||||
|
|
||||||
deleteSuggestion = (suggestion: string): void => {
|
deleteSuggestion = (suggestion: string): void => {
|
||||||
this.suggestions.remove(suggestion);
|
this.suggestions.remove(suggestion);
|
||||||
}
|
};
|
||||||
|
|
||||||
// init
|
// init
|
||||||
constructor(
|
constructor(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
import TaskViewModel from "../Pages/taskViewModel";
|
import TaskViewModel from "../Pages/taskViewModel";
|
||||||
|
import * as React from "../../react";
|
||||||
|
|
||||||
export default class TaskPropertyBulkChangeViewModel {
|
export default class TaskPropertyBulkChangeViewModel {
|
||||||
taskViewModels: React.ListState<TaskViewModel>;
|
taskViewModels: React.ListState<TaskViewModel>;
|
||||||
|
|||||||
+5
-3
@@ -44,7 +44,9 @@ main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
transition: --sky-1 1s, --sky-2 5s;
|
transition:
|
||||||
|
--sky-1 1s,
|
||||||
|
--sky-2 5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
#background-wrapper #grass-1 {
|
#background-wrapper #grass-1 {
|
||||||
@@ -52,7 +54,7 @@ body {
|
|||||||
--color-2: hsl(150, 60%, 25%);
|
--color-2: hsl(150, 60%, 25%);
|
||||||
|
|
||||||
filter: var(--backdrop-grass-filter);
|
filter: var(--backdrop-grass-filter);
|
||||||
opacity: .7;
|
opacity: 0.7;
|
||||||
|
|
||||||
background-image: linear-gradient(
|
background-image: linear-gradient(
|
||||||
350deg,
|
350deg,
|
||||||
@@ -67,7 +69,7 @@ body {
|
|||||||
--color-2: hsl(110, 60%, 35%);
|
--color-2: hsl(110, 60%, 35%);
|
||||||
|
|
||||||
filter: var(--backdrop-grass-filter);
|
filter: var(--backdrop-grass-filter);
|
||||||
opacity: .7;
|
opacity: 0.7;
|
||||||
|
|
||||||
background-image: linear-gradient(
|
background-image: linear-gradient(
|
||||||
9deg,
|
9deg,
|
||||||
|
|||||||
+136
-136
@@ -85,7 +85,7 @@ export class ListState<T> extends State<Set<T>> {
|
|||||||
items.forEach((item) => {
|
items.forEach((item) => {
|
||||||
this.value.delete(item);
|
this.value.delete(item);
|
||||||
|
|
||||||
this.genericRemovalHandlers.forEach(handler => handler(item));
|
this.genericRemovalHandlers.forEach((handler) => handler(item));
|
||||||
if (!this.removalHandlers.has(item)) return;
|
if (!this.removalHandlers.has(item)) return;
|
||||||
this.removalHandlers.get(item)!.forEach((handler) => handler(item));
|
this.removalHandlers.get(item)!.forEach((handler) => handler(item));
|
||||||
this.removalHandlers.delete(item);
|
this.removalHandlers.delete(item);
|
||||||
@@ -146,73 +146,73 @@ export class MapState<T> extends State<Map<string, T>> {
|
|||||||
this.value.delete(key);
|
this.value.delete(key);
|
||||||
this.callSubscriptions();
|
this.callSubscriptions();
|
||||||
|
|
||||||
this.genericRemovalHandlers.forEach(handler => handler(item));
|
this.genericRemovalHandlers.forEach((handler) => handler(item));
|
||||||
if (!this.removalHandlers.has(item)) return;
|
if (!this.removalHandlers.has(item)) return;
|
||||||
this.removalHandlers.get(item)!.forEach((handler) => handler(item));
|
this.removalHandlers.get(item)!.forEach((handler) => handler(item));
|
||||||
this.removalHandlers.delete(item);
|
this.removalHandlers.delete(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
[...this.value.keys()].forEach((key) => this.remove(key));
|
[...this.value.keys()].forEach((key) => this.remove(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
// handlers
|
// handlers
|
||||||
handleAddition(handler: AdditionSubscription<T>): void {
|
handleAddition(handler: AdditionSubscription<T>): void {
|
||||||
this.additionHandlers.add(handler);
|
this.additionHandlers.add(handler);
|
||||||
[...this.value.values()].forEach(handler);
|
[...this.value.values()].forEach(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRemoval(item: T, handler: RemovalSubscription<T>): void {
|
handleRemoval(item: T, handler: RemovalSubscription<T>): void {
|
||||||
if (!this.removalHandlers.has(item))
|
if (!this.removalHandlers.has(item))
|
||||||
this.removalHandlers.set(item, new Set());
|
this.removalHandlers.set(item, new Set());
|
||||||
this.removalHandlers.get(item)!.add(handler);
|
this.removalHandlers.get(item)!.add(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRemovals(handler: RemovalSubscription<T>): void {
|
handleRemovals(handler: RemovalSubscription<T>): void {
|
||||||
this.genericRemovalHandlers.add(handler);
|
this.genericRemovalHandlers.add(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
// stringification
|
// stringification
|
||||||
toString(): string {
|
toString(): string {
|
||||||
const array = [...this.value.entries()];
|
const array = [...this.value.entries()];
|
||||||
const json = JSON.stringify(array);
|
const json = JSON.stringify(array);
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UTILITY
|
// UTILITY
|
||||||
export function createProxyState<T>(
|
export function createProxyState<T>(
|
||||||
statesToSubscibe: State<any>[],
|
statesToSubscibe: State<any>[],
|
||||||
fn: () => T
|
fn: () => T,
|
||||||
): State<T> {
|
): State<T> {
|
||||||
const proxyState = new State<T>(fn());
|
const proxyState = new State<T>(fn());
|
||||||
statesToSubscibe.forEach((state) =>
|
statesToSubscibe.forEach((state) =>
|
||||||
state.subscribeSilent(() => (proxyState.value = fn()))
|
state.subscribeSilent(() => (proxyState.value = fn())),
|
||||||
);
|
);
|
||||||
return proxyState;
|
return proxyState;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bulkSubscribe(
|
export function bulkSubscribe(
|
||||||
statesToSubscibe: State<any>[],
|
statesToSubscibe: State<any>[],
|
||||||
fn: () => void
|
fn: () => void,
|
||||||
): void {
|
): void {
|
||||||
statesToSubscibe.forEach((state) => state.subscribeSilent(fn));
|
statesToSubscibe.forEach((state) => state.subscribeSilent(fn));
|
||||||
}
|
}
|
||||||
|
|
||||||
function persistState(localStorageKey: string, state: State<any>) {
|
function persistState(localStorageKey: string, state: State<any>) {
|
||||||
state.subscribe(() => {
|
state.subscribe(() => {
|
||||||
const stringifiedValue = state.toString();
|
const stringifiedValue = state.toString();
|
||||||
localStorage.setItem(localStorageKey, stringifiedValue);
|
localStorage.setItem(localStorageKey, stringifiedValue);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function restoreState<T>(
|
export function restoreState<T>(
|
||||||
localStorageKey: string,
|
localStorageKey: string,
|
||||||
initialStateValue: T
|
initialStateValue: T,
|
||||||
): State<T> {
|
): State<T> {
|
||||||
const storedString =
|
const storedString =
|
||||||
localStorage.getItem(localStorageKey) ??
|
localStorage.getItem(localStorageKey) ??
|
||||||
JSON.stringify(initialStateValue);
|
JSON.stringify(initialStateValue);
|
||||||
const convertedValue = JSON.parse(storedString);
|
const convertedValue = JSON.parse(storedString);
|
||||||
|
|
||||||
const state = new State(convertedValue);
|
const state = new State(convertedValue);
|
||||||
@@ -223,14 +223,14 @@ export function restoreState<T>(
|
|||||||
|
|
||||||
export function restoreListState<T>(
|
export function restoreListState<T>(
|
||||||
localStorageKey: string,
|
localStorageKey: string,
|
||||||
initialItems: any[] = []
|
initialItems: any[] = [],
|
||||||
): ListState<T> {
|
): ListState<T> {
|
||||||
const storedString = localStorage.getItem(localStorageKey) ?? "";
|
const storedString = localStorage.getItem(localStorageKey) ?? "";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const array = JSON.parse(storedString);
|
const array = JSON.parse(storedString);
|
||||||
if (!Array.isArray(array)) throw "";
|
if (!Array.isArray(array)) throw "";
|
||||||
initialItems = array;
|
initialItems = array;
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
const state = new ListState<T>(initialItems);
|
const state = new ListState<T>(initialItems);
|
||||||
@@ -241,14 +241,14 @@ export function restoreListState<T>(
|
|||||||
|
|
||||||
export function restoreMapState<T>(
|
export function restoreMapState<T>(
|
||||||
localStorageKey: string,
|
localStorageKey: string,
|
||||||
initialItems: [string, any][] = []
|
initialItems: [string, any][] = [],
|
||||||
): MapState<T> {
|
): MapState<T> {
|
||||||
const storedString = localStorage.getItem(localStorageKey) ?? "";
|
const storedString = localStorage.getItem(localStorageKey) ?? "";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const array = JSON.parse(storedString);
|
const array = JSON.parse(storedString);
|
||||||
if (!Array.isArray(array)) throw "";
|
if (!Array.isArray(array)) throw "";
|
||||||
initialItems = array;
|
initialItems = array;
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
const state = new MapState<T>(initialItems);
|
const state = new MapState<T>(initialItems);
|
||||||
@@ -271,115 +271,115 @@ export function createElement(
|
|||||||
const element = document.createElement(tagName);
|
const element = document.createElement(tagName);
|
||||||
|
|
||||||
if (attributes != null)
|
if (attributes != null)
|
||||||
Object.entries(attributes).forEach((entry) => {
|
Object.entries(attributes).forEach((entry) => {
|
||||||
const [attributename, value] = entry;
|
const [attributename, value] = entry;
|
||||||
const [directiveKey, directiveValue] = attributename.split(":");
|
const [directiveKey, directiveValue] = attributename.split(":");
|
||||||
|
|
||||||
switch (directiveKey) {
|
switch (directiveKey) {
|
||||||
case "on": {
|
case "on": {
|
||||||
switch (directiveValue) {
|
switch (directiveValue) {
|
||||||
case "enter": {
|
case "enter": {
|
||||||
element.addEventListener("keydown", (e: Event) => {
|
element.addEventListener("keydown", (e: Event) => {
|
||||||
if (!(e instanceof KeyboardEvent)) return;
|
if (!(e instanceof KeyboardEvent)) return;
|
||||||
if (e.key != "Enter") return;
|
if (e.key != "Enter") return;
|
||||||
value(e);
|
value(e);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
element.addEventListener(directiveValue, value);
|
element.addEventListener(directiveValue, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "keystroke": {
|
case "keystroke": {
|
||||||
element.addEventListener("keydown", (e: Event) => {
|
element.addEventListener("keydown", (e: Event) => {
|
||||||
if (!(e instanceof KeyboardEvent)) return;
|
if (!(e instanceof KeyboardEvent)) return;
|
||||||
if (e.metaKey == false && e.ctrlKey == false) return;
|
if (e.metaKey == false && e.ctrlKey == false) return;
|
||||||
if (e.key != directiveValue) return;
|
if (e.key != directiveValue) return;
|
||||||
value(e);
|
value(e);
|
||||||
})
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "subscribe": {
|
case "subscribe": {
|
||||||
const state = value as State<any>;
|
const state = value as State<any>;
|
||||||
state.subscribe(
|
state.subscribe(
|
||||||
(newValue) => (element[directiveValue] = newValue)
|
(newValue) => (element[directiveValue] = newValue),
|
||||||
);
|
);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "bind": {
|
case "bind": {
|
||||||
const state = value as State<any>;
|
const state = value as State<any>;
|
||||||
state.subscribe(
|
state.subscribe(
|
||||||
(newValue) => (element[directiveValue] = newValue)
|
(newValue) => (element[directiveValue] = newValue),
|
||||||
);
|
);
|
||||||
element.addEventListener(
|
element.addEventListener(
|
||||||
"input",
|
"input",
|
||||||
() => (state.value = (element as any)[directiveValue])
|
() => (state.value = (element as any)[directiveValue]),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "toggle": {
|
case "toggle": {
|
||||||
if (value.subscribe) {
|
if (value.subscribe) {
|
||||||
const state = value as State<any>;
|
const state = value as State<any>;
|
||||||
state.subscribe((newValue) =>
|
state.subscribe((newValue) =>
|
||||||
element.toggleAttribute(directiveValue, newValue)
|
element.toggleAttribute(directiveValue, newValue),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
element.toggleAttribute(directiveValue, value);
|
element.toggleAttribute(directiveValue, value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "set": {
|
case "set": {
|
||||||
const state = value as State<any>;
|
const state = value as State<any>;
|
||||||
state.subscribe((newValue) =>
|
state.subscribe((newValue) =>
|
||||||
element.setAttribute(directiveValue, newValue)
|
element.setAttribute(directiveValue, newValue),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "children": {
|
case "children": {
|
||||||
switch (directiveValue) {
|
switch (directiveValue) {
|
||||||
case "set": {
|
case "set": {
|
||||||
const state = value as State<Node | Node[]>;
|
const state = value as State<Node | Node[]>;
|
||||||
state.subscribe((newValue) => {
|
state.subscribe((newValue) => {
|
||||||
element.innerHTML = "";
|
element.innerHTML = "";
|
||||||
element.append(...[newValue].flat());
|
element.append(...[newValue].flat());
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "append":
|
case "append":
|
||||||
case "prepend": {
|
case "prepend": {
|
||||||
try {
|
try {
|
||||||
const [listState, toElement] = value as [
|
const [listState, toElement] = value as [
|
||||||
listState: ListState<any>,
|
listState: ListState<any>,
|
||||||
StateItemConverter<any>
|
StateItemConverter<any>,
|
||||||
];
|
];
|
||||||
|
|
||||||
listState.handleAddition((newItem) => {
|
listState.handleAddition((newItem) => {
|
||||||
const child = toElement(newItem);
|
const child = toElement(newItem);
|
||||||
listState.handleRemoval(newItem, () =>
|
listState.handleRemoval(newItem, () =>
|
||||||
child.remove()
|
child.remove(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (directiveValue == "append") {
|
if (directiveValue == "append") {
|
||||||
element.append(child);
|
element.append(child);
|
||||||
} else if (directiveValue == "prepend") {
|
} else if (directiveValue == "prepend") {
|
||||||
element.prepend(child);
|
element.prepend(child);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
throw `error: cannot process subscribe:children directive. \n Usage: "children:append={[list, converter]}"; you can find a more detailed example in the documentation.`;
|
throw `error: cannot process subscribe:children directive. \n Usage: "children:append={[list, converter]}"; you can find a more detailed example in the documentation.`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
element.setAttribute(attributename, value);
|
element.setAttribute(attributename, value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
children.filter((x) => x).forEach((child) => element.append(child));
|
children.filter((x) => x).forEach((child) => element.append(child));
|
||||||
|
|
||||||
|
|||||||
Vendored
+1
-2
@@ -1,2 +1 @@
|
|||||||
declare module "*.css" {
|
declare module "*.css" {}
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user