add progress bar to data transfer
This commit is contained in:
Vendored
+67
-57
@@ -436,9 +436,8 @@
|
|||||||
sendButton: "Send",
|
sendButton: "Send",
|
||||||
sendAgainButton: "Send again",
|
sendAgainButton: "Send again",
|
||||||
///
|
///
|
||||||
filesSentCount: (count) => `Files sent: ${count}.`,
|
filesSentProgress: (progress, total) => `Sent ${progress} of ${total} files`,
|
||||||
allFilesSent: "Done.",
|
filesReceivedProgress: (progress, total) => `Received ${progress} of ${total} files`,
|
||||||
filesReceivedCount: (count) => `Files processed: ${count}.`,
|
|
||||||
//
|
//
|
||||||
exportKey: "Encryption key",
|
exportKey: "Encryption key",
|
||||||
exportKeyConfirmation: "Confirm",
|
exportKeyConfirmation: "Confirm",
|
||||||
@@ -702,9 +701,8 @@
|
|||||||
transferKeyHeadline: "Schl\xFCssel",
|
transferKeyHeadline: "Schl\xFCssel",
|
||||||
sendButton: "Senden",
|
sendButton: "Senden",
|
||||||
sendAgainButton: "Erneut senden",
|
sendAgainButton: "Erneut senden",
|
||||||
filesSentCount: (count) => `Dateien gesendet: ${count}.`,
|
filesSentProgress: (progress, total) => `${progress} von ${total} Dateien gesendet`,
|
||||||
allFilesSent: "Fertig.",
|
filesReceivedProgress: (progress, total) => `${progress} von ${total} Dateien empfangen`,
|
||||||
filesReceivedCount: (count) => `Dateien empfangen: ${count}.`,
|
|
||||||
exportKey: "Schl\xFCssel",
|
exportKey: "Schl\xFCssel",
|
||||||
exportKeyConfirmation: "Schl\xFCssel best\xE4tigen",
|
exportKeyConfirmation: "Schl\xFCssel best\xE4tigen",
|
||||||
downloadFileButton: "Herunterladen",
|
downloadFileButton: "Herunterladen",
|
||||||
@@ -954,9 +952,8 @@
|
|||||||
transferKeyHeadline: "Clave de encriptaci\xF3n de transferencia",
|
transferKeyHeadline: "Clave de encriptaci\xF3n de transferencia",
|
||||||
sendButton: "Enviar",
|
sendButton: "Enviar",
|
||||||
sendAgainButton: "Enviar otra vez",
|
sendAgainButton: "Enviar otra vez",
|
||||||
filesSentCount: (count) => `Archivos enviados: ${count}.`,
|
filesSentProgress: (progress, total) => `${progress} de ${total} enviados`,
|
||||||
allFilesSent: "Hecho.",
|
filesReceivedProgress: (progress, total) => `${progress} de ${total} recibidos`,
|
||||||
filesReceivedCount: (count) => `Archivos recibidos: ${count}.`,
|
|
||||||
exportKey: "Clave de encriptaci\xF3n",
|
exportKey: "Clave de encriptaci\xF3n",
|
||||||
exportKeyConfirmation: "Confirmar clave",
|
exportKeyConfirmation: "Confirmar clave",
|
||||||
downloadFileButton: "Descargar",
|
downloadFileButton: "Descargar",
|
||||||
@@ -2290,27 +2287,13 @@
|
|||||||
this.receivingTransferChannel = new State("");
|
this.receivingTransferChannel = new State("");
|
||||||
this.receivingTransferKey = new State("");
|
this.receivingTransferKey = new State("");
|
||||||
this.filePathsSent = new ListState();
|
this.filePathsSent = new ListState();
|
||||||
this.filesSentCount = createProxyState(
|
this.fileTransferTotal = new State(0);
|
||||||
[this.filePathsSent],
|
this.fileTransferProgress = new State(0);
|
||||||
() => this.filePathsSent.value.size
|
|
||||||
);
|
|
||||||
this.filesSentText = createProxyState(
|
|
||||||
[this.filesSentCount],
|
|
||||||
() => this.coreViewModel.translations.dataTransferModal.filesSentCount(
|
|
||||||
this.filesSentCount.value
|
|
||||||
)
|
|
||||||
);
|
|
||||||
this.filePathsReceived = new ListState();
|
this.filePathsReceived = new ListState();
|
||||||
this.filesReceivedCount = createProxyState(
|
this.filesReceivedCount = createProxyState(
|
||||||
[this.filePathsReceived],
|
[this.filePathsReceived],
|
||||||
() => this.filePathsReceived.value.size
|
() => this.filePathsReceived.value.size
|
||||||
);
|
);
|
||||||
this.filesReceivedText = createProxyState(
|
|
||||||
[this.filesReceivedCount],
|
|
||||||
() => this.coreViewModel.translations.dataTransferModal.filesReceivedCount(
|
|
||||||
this.filesReceivedCount.value
|
|
||||||
)
|
|
||||||
);
|
|
||||||
this.exportKey = new State("");
|
this.exportKey = new State("");
|
||||||
this.exportKeyConfirmation = new State("");
|
this.exportKeyConfirmation = new State("");
|
||||||
this.importedFile = new State(void 0);
|
this.importedFile = new State(void 0);
|
||||||
@@ -2322,7 +2305,6 @@
|
|||||||
[this.selectedPaths],
|
[this.selectedPaths],
|
||||||
() => this.selectedPaths.value.size == 0
|
() => this.selectedPaths.value.size == 0
|
||||||
);
|
);
|
||||||
this.didNotFinishSending = new State(true);
|
|
||||||
this.cannotPrepareToReceive = createProxyState(
|
this.cannotPrepareToReceive = createProxyState(
|
||||||
[this.receivingTransferChannel, this.receivingTransferKey],
|
[this.receivingTransferChannel, this.receivingTransferKey],
|
||||||
() => this.receivingTransferChannel.value == "" || this.receivingTransferKey.value == ""
|
() => this.receivingTransferChannel.value == "" || this.receivingTransferKey.value == ""
|
||||||
@@ -2346,6 +2328,7 @@
|
|||||||
// handlers
|
// handlers
|
||||||
this.handleReceivedFile = (path) => {
|
this.handleReceivedFile = (path) => {
|
||||||
this.filePathsReceived.add(path);
|
this.filePathsReceived.add(path);
|
||||||
|
this.fileTransferProgress.value = this.filePathsReceived.value.size;
|
||||||
};
|
};
|
||||||
// methods
|
// methods
|
||||||
this.getOptions = () => {
|
this.getOptions = () => {
|
||||||
@@ -2454,7 +2437,6 @@
|
|||||||
};
|
};
|
||||||
this.initiateTransfer = () => {
|
this.initiateTransfer = () => {
|
||||||
this.presentedModal.value = 3 /* TransferDisplay */;
|
this.presentedModal.value = 3 /* TransferDisplay */;
|
||||||
this.didNotFinishSending.value = true;
|
|
||||||
this.filePathsSent.clear();
|
this.filePathsSent.clear();
|
||||||
this.coreViewModel.fileTransferModel.sendFiles(
|
this.coreViewModel.fileTransferModel.sendFiles(
|
||||||
this.selectedPaths.value.values(),
|
this.selectedPaths.value.values(),
|
||||||
@@ -2462,7 +2444,6 @@
|
|||||||
this.filePathsSent.add(path);
|
this.filePathsSent.add(path);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
this.didNotFinishSending.value = false;
|
|
||||||
};
|
};
|
||||||
this.showTransferDataInputModal = () => {
|
this.showTransferDataInputModal = () => {
|
||||||
this.presentedModal.value = 4 /* TransferDataInput */;
|
this.presentedModal.value = 4 /* TransferDataInput */;
|
||||||
@@ -2496,6 +2477,10 @@
|
|||||||
this.handleContextClose = () => {
|
this.handleContextClose = () => {
|
||||||
this.presentedModal.value = void 0;
|
this.presentedModal.value = void 0;
|
||||||
};
|
};
|
||||||
|
this.coreViewModel.fileTransferModel.transferSizeHandlerManager.setHandler(
|
||||||
|
"file-transfer-view-model",
|
||||||
|
(count) => this.fileTransferTotal.value = count
|
||||||
|
);
|
||||||
this.coreViewModel.fileTransferModel.fileHandlerManager.setHandler(
|
this.coreViewModel.fileTransferModel.fileHandlerManager.setHandler(
|
||||||
"file-transfer-view-model",
|
"file-transfer-view-model",
|
||||||
this.handleReceivedFile
|
this.handleReceivedFile
|
||||||
@@ -4742,7 +4727,6 @@
|
|||||||
};
|
};
|
||||||
this.randomizeKey = () => {
|
this.randomizeKey = () => {
|
||||||
this.encryptionKeyInput.value = random(24);
|
this.encryptionKeyInput.value = random(24);
|
||||||
this.setEncryptionKey();
|
|
||||||
};
|
};
|
||||||
this.applyColor = () => {
|
this.applyColor = () => {
|
||||||
this.chatViewModel.setColor(this.color.value);
|
this.chatViewModel.setColor(this.color.value);
|
||||||
@@ -8165,6 +8149,21 @@
|
|||||||
return /* @__PURE__ */ createElement("span", { class: "ellipsis" }, string);
|
return /* @__PURE__ */ createElement("span", { class: "ellipsis" }, string);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// src/View/Components/progressBar.tsx
|
||||||
|
function ProgressBar(valueLabel, value, maximum) {
|
||||||
|
const style = createProxyState(
|
||||||
|
[value, maximum],
|
||||||
|
() => `width: ${100 * (value.value / maximum.value)}%`
|
||||||
|
);
|
||||||
|
return /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("div", { class: "width-100 surface-alt", style: "height: 18px" }, /* @__PURE__ */ createElement(
|
||||||
|
"div",
|
||||||
|
{
|
||||||
|
class: "height-100 background-primary",
|
||||||
|
"set:style": style
|
||||||
|
}
|
||||||
|
)), /* @__PURE__ */ createElement("span", { class: "secondary", "subscribe:innerText": valueLabel }));
|
||||||
|
}
|
||||||
|
|
||||||
// src/View/Modals/dataTransferModal.tsx
|
// src/View/Modals/dataTransferModal.tsx
|
||||||
function DataTransferModalWrapper(coreViewModel2, connectionViewModel2, fileTransferViewModel2) {
|
function DataTransferModalWrapper(coreViewModel2, connectionViewModel2, fileTransferViewModel2) {
|
||||||
return /* @__PURE__ */ createElement("div", null, DirectionSelectionModal(
|
return /* @__PURE__ */ createElement("div", null, DirectionSelectionModal(
|
||||||
@@ -8320,20 +8319,8 @@
|
|||||||
[fileTransferViewModel2.presentedModal],
|
[fileTransferViewModel2.presentedModal],
|
||||||
() => fileTransferViewModel2.presentedModal.value == 3 /* TransferDisplay */
|
() => fileTransferViewModel2.presentedModal.value == 3 /* TransferDisplay */
|
||||||
);
|
);
|
||||||
return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": isPresented }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, coreViewModel2.translations.dataTransferModal.transferDataHeadline), /* @__PURE__ */ createElement(
|
const progressLabel = createProxyState([fileTransferViewModel2.fileTransferProgress, fileTransferViewModel2.fileTransferTotal], () => coreViewModel2.translations.dataTransferModal.filesSentProgress(fileTransferViewModel2.fileTransferProgress.value, fileTransferViewModel2.fileTransferTotal.value));
|
||||||
"p",
|
return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": isPresented }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, coreViewModel2.translations.dataTransferModal.transferDataHeadline), ProgressBar(progressLabel, fileTransferViewModel2.fileTransferProgress, fileTransferViewModel2.fileTransferTotal), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-column gap content-margin-bottom" }, /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "forum"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", { class: "secondary" }, coreViewModel2.translations.dataTransferModal.transferChannelHeadline), /* @__PURE__ */ createElement(
|
||||||
{
|
|
||||||
class: "secondary",
|
|
||||||
"subscribe:innerText": fileTransferViewModel2.filesSentText
|
|
||||||
}
|
|
||||||
), /* @__PURE__ */ createElement(
|
|
||||||
"p",
|
|
||||||
{
|
|
||||||
class: "secondary",
|
|
||||||
"toggle:hidden": fileTransferViewModel2.didNotFinishSending
|
|
||||||
},
|
|
||||||
coreViewModel2.translations.dataTransferModal.allFilesSent
|
|
||||||
), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-column gap content-margin-bottom" }, /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "forum"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", { class: "secondary" }, coreViewModel2.translations.dataTransferModal.transferChannelHeadline), /* @__PURE__ */ createElement(
|
|
||||||
"b",
|
"b",
|
||||||
{
|
{
|
||||||
"subscribe:innerText": fileTransferViewModel2.transferChannel
|
"subscribe:innerText": fileTransferViewModel2.transferChannel
|
||||||
@@ -8364,8 +8351,7 @@
|
|||||||
"button",
|
"button",
|
||||||
{
|
{
|
||||||
class: "flex",
|
class: "flex",
|
||||||
"on:click": fileTransferViewModel2.close,
|
"on:click": fileTransferViewModel2.close
|
||||||
"toggle:disabled": fileTransferViewModel2.didNotFinishSending
|
|
||||||
},
|
},
|
||||||
coreViewModel2.translations.general.closeButton,
|
coreViewModel2.translations.general.closeButton,
|
||||||
/* @__PURE__ */ createElement("span", { class: "icon" }, "close")
|
/* @__PURE__ */ createElement("span", { class: "icon" }, "close")
|
||||||
@@ -8413,13 +8399,8 @@
|
|||||||
[fileTransferViewModel2.presentedModal],
|
[fileTransferViewModel2.presentedModal],
|
||||||
() => fileTransferViewModel2.presentedModal.value == 5 /* ReceptionDisplay */
|
() => fileTransferViewModel2.presentedModal.value == 5 /* ReceptionDisplay */
|
||||||
);
|
);
|
||||||
return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": isPresented }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, coreViewModel2.translations.dataTransferModal.receiveHeadline), /* @__PURE__ */ createElement(
|
const progressLabel = createProxyState([fileTransferViewModel2.fileTransferProgress, fileTransferViewModel2.fileTransferTotal], () => coreViewModel2.translations.dataTransferModal.filesReceivedProgress(fileTransferViewModel2.fileTransferProgress.value, fileTransferViewModel2.fileTransferTotal.value));
|
||||||
"p",
|
return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": isPresented }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, coreViewModel2.translations.dataTransferModal.receiveHeadline), ProgressBar(progressLabel, fileTransferViewModel2.fileTransferProgress, fileTransferViewModel2.fileTransferTotal), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement(
|
||||||
{
|
|
||||||
class: "secondary",
|
|
||||||
"subscribe:innerText": fileTransferViewModel2.filesReceivedText
|
|
||||||
}
|
|
||||||
), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement(
|
|
||||||
"div",
|
"div",
|
||||||
{
|
{
|
||||||
class: "tile flex-column align-start",
|
class: "tile flex-column align-start",
|
||||||
@@ -8641,6 +8622,7 @@
|
|||||||
this.direction = 0 /* Send */;
|
this.direction = 0 /* Send */;
|
||||||
// handler managers
|
// handler managers
|
||||||
this.fileHandlerManager = new HandlerManager();
|
this.fileHandlerManager = new HandlerManager();
|
||||||
|
this.transferSizeHandlerManager = new HandlerManager();
|
||||||
this.readyToSendHandlerManager = new HandlerManager();
|
this.readyToSendHandlerManager = new HandlerManager();
|
||||||
// general
|
// general
|
||||||
this.generateTransferData = () => {
|
this.generateTransferData = () => {
|
||||||
@@ -8700,6 +8682,11 @@
|
|||||||
};
|
};
|
||||||
this.handleDecryptedFile = (data) => {
|
this.handleDecryptedFile = (data) => {
|
||||||
const parsed = parse(data);
|
const parsed = parse(data);
|
||||||
|
if (!parsed.type) return;
|
||||||
|
if (parsed.type == "transfer-size" && typeof parsed.count == "number") {
|
||||||
|
this.transferSizeHandlerManager.trigger(parsed.count);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const isFileData = checkMatchesObjectStructure(
|
const isFileData = checkMatchesObjectStructure(
|
||||||
parsed,
|
parsed,
|
||||||
FileDataReference
|
FileDataReference
|
||||||
@@ -8714,16 +8701,22 @@
|
|||||||
};
|
};
|
||||||
// sending
|
// sending
|
||||||
this.sendFiles = (directoryPaths, fileCallback) => {
|
this.sendFiles = (directoryPaths, fileCallback) => {
|
||||||
|
const filesToSend = /* @__PURE__ */ new Set();
|
||||||
for (const directoryPath of directoryPaths) {
|
for (const directoryPath of directoryPaths) {
|
||||||
this.storageModel.recurse(directoryPath, (filePath) => {
|
this.storageModel.recurse(directoryPath, (filePath) => {
|
||||||
const stringifiedFileData = this.prepareFileForSending(filePath);
|
const stringifiedFileData = this.prepareFileForSending(filePath);
|
||||||
this.sendFile(stringifiedFileData);
|
filesToSend.add([stringifiedFileData, filePath]);
|
||||||
const pathString = StorageModel.pathComponentsToString(
|
|
||||||
...filePath
|
|
||||||
);
|
|
||||||
fileCallback(pathString);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
this.sendTransferSize(filesToSend.size);
|
||||||
|
for (const file of filesToSend) {
|
||||||
|
const [stringifiedFileData, filePath] = file;
|
||||||
|
this.sendFile(stringifiedFileData);
|
||||||
|
const pathString = StorageModel.pathComponentsToString(
|
||||||
|
...filePath
|
||||||
|
);
|
||||||
|
fileCallback(pathString);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
this.generateBackup = async (directoryPaths, passphrase) => {
|
this.generateBackup = async (directoryPaths, passphrase) => {
|
||||||
const files = [];
|
const files = [];
|
||||||
@@ -8739,10 +8732,26 @@
|
|||||||
const blob = new Blob([encrypted], { type: "text/plain" });
|
const blob = new Blob([encrypted], { type: "text/plain" });
|
||||||
return blob;
|
return blob;
|
||||||
};
|
};
|
||||||
|
this.sendTransferSize = async (count) => {
|
||||||
|
if (!this.transferData) return;
|
||||||
|
const data = {
|
||||||
|
type: "transfer-size",
|
||||||
|
count
|
||||||
|
};
|
||||||
|
const encryptedData = await encryptString(
|
||||||
|
stringify(data),
|
||||||
|
this.transferData.key
|
||||||
|
);
|
||||||
|
this.connectionModel.sendPlainMessage(
|
||||||
|
this.transferData.channel,
|
||||||
|
encryptedData
|
||||||
|
);
|
||||||
|
};
|
||||||
this.prepareFileForSending = (filePath) => {
|
this.prepareFileForSending = (filePath) => {
|
||||||
const fileContent = this.storageModel.read(filePath);
|
const fileContent = this.storageModel.read(filePath);
|
||||||
if (fileContent == null) return "";
|
if (fileContent == null) return "";
|
||||||
const fileData = {
|
const fileData = {
|
||||||
|
type: "file-data",
|
||||||
path: filePath,
|
path: filePath,
|
||||||
body: fileContent
|
body: fileContent
|
||||||
};
|
};
|
||||||
@@ -8771,6 +8780,7 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
var FileDataReference = {
|
var FileDataReference = {
|
||||||
|
type: "file-data",
|
||||||
path: [""],
|
path: [""],
|
||||||
body: ""
|
body: ""
|
||||||
};
|
};
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
const VERSION = "2610 Beta 21.12"
|
const VERSION = "2610 Beta 21.13"
|
||||||
|
|
||||||
function processUrl(event) {
|
function processUrl(event) {
|
||||||
const url = event.request.url;
|
const url = event.request.url;
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1 +1 @@
|
|||||||
2610 Beta 21.12
|
2610 Beta 21.13
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export default class FileTransferModel {
|
|||||||
|
|
||||||
// handler managers
|
// handler managers
|
||||||
readonly fileHandlerManager = new HandlerManager<string>();
|
readonly fileHandlerManager = new HandlerManager<string>();
|
||||||
|
readonly transferSizeHandlerManager = new HandlerManager<number>();
|
||||||
readonly readyToSendHandlerManager = new HandlerManager<boolean>();
|
readonly readyToSendHandlerManager = new HandlerManager<boolean>();
|
||||||
|
|
||||||
// general
|
// general
|
||||||
@@ -105,6 +106,12 @@ export default class FileTransferModel {
|
|||||||
readonly handleDecryptedFile = (data: string): void => {
|
readonly handleDecryptedFile = (data: string): void => {
|
||||||
const parsed: any = parse(data);
|
const parsed: any = parse(data);
|
||||||
|
|
||||||
|
if (!parsed.type) return;
|
||||||
|
if (parsed.type == "transfer-size" && typeof parsed.count == "number") {
|
||||||
|
this.transferSizeHandlerManager.trigger(parsed.count);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const isFileData: boolean = checkMatchesObjectStructure(
|
const isFileData: boolean = checkMatchesObjectStructure(
|
||||||
parsed,
|
parsed,
|
||||||
FileDataReference,
|
FileDataReference,
|
||||||
@@ -126,17 +133,23 @@ export default class FileTransferModel {
|
|||||||
directoryPaths: IteratorObject<string[]>,
|
directoryPaths: IteratorObject<string[]>,
|
||||||
fileCallback: (path: string) => void,
|
fileCallback: (path: string) => void,
|
||||||
): void => {
|
): void => {
|
||||||
|
const filesToSend = new Set<[string, string[]]>();
|
||||||
for (const directoryPath of directoryPaths) {
|
for (const directoryPath of directoryPaths) {
|
||||||
this.storageModel.recurse(directoryPath, (filePath: string[]) => {
|
this.storageModel.recurse(directoryPath, (filePath: string[]) => {
|
||||||
const stringifiedFileData =
|
const stringifiedFileData =
|
||||||
this.prepareFileForSending(filePath);
|
this.prepareFileForSending(filePath);
|
||||||
this.sendFile(stringifiedFileData);
|
filesToSend.add([stringifiedFileData, filePath]);
|
||||||
const pathString: string = StorageModel.pathComponentsToString(
|
|
||||||
...filePath,
|
|
||||||
);
|
|
||||||
fileCallback(pathString);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
this.sendTransferSize(filesToSend.size);
|
||||||
|
for (const file of filesToSend) {
|
||||||
|
const [stringifiedFileData, filePath] = file;
|
||||||
|
this.sendFile(stringifiedFileData);
|
||||||
|
const pathString: string = StorageModel.pathComponentsToString(
|
||||||
|
...filePath,
|
||||||
|
);
|
||||||
|
fileCallback(pathString);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
readonly generateBackup = async (
|
readonly generateBackup = async (
|
||||||
@@ -158,11 +171,30 @@ export default class FileTransferModel {
|
|||||||
return blob;
|
return blob;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
readonly sendTransferSize = async (count: number): Promise<void> => {
|
||||||
|
if (!this.transferData) return;
|
||||||
|
|
||||||
|
const data: TransferSize = {
|
||||||
|
type: "transfer-size",
|
||||||
|
count,
|
||||||
|
};
|
||||||
|
|
||||||
|
const encryptedData: string = await encryptString(
|
||||||
|
stringify(data),
|
||||||
|
this.transferData.key,
|
||||||
|
);
|
||||||
|
this.connectionModel.sendPlainMessage(
|
||||||
|
this.transferData.channel,
|
||||||
|
encryptedData,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
readonly prepareFileForSending = (filePath: string[]): string => {
|
readonly prepareFileForSending = (filePath: string[]): string => {
|
||||||
const fileContent: string | null = this.storageModel.read(filePath);
|
const fileContent: string | null = this.storageModel.read(filePath);
|
||||||
if (fileContent == null) return "";
|
if (fileContent == null) return "";
|
||||||
|
|
||||||
const fileData: FileData = {
|
const fileData: FileData = {
|
||||||
|
type: "file-data",
|
||||||
path: filePath,
|
path: filePath,
|
||||||
body: fileContent,
|
body: fileContent,
|
||||||
};
|
};
|
||||||
@@ -200,11 +232,18 @@ export interface TransferData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface FileData {
|
export interface FileData {
|
||||||
|
type: "file-data";
|
||||||
path: string[];
|
path: string[];
|
||||||
body: string;
|
body: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TransferSize {
|
||||||
|
type: "transfer-size";
|
||||||
|
count: number;
|
||||||
|
}
|
||||||
|
|
||||||
export const FileDataReference: FileData = {
|
export const FileDataReference: FileData = {
|
||||||
|
type: "file-data",
|
||||||
path: [""],
|
path: [""],
|
||||||
body: "",
|
body: "",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const ENCRYPTION_ALG = "AES-GCM";
|
|||||||
|
|
||||||
// random
|
// random
|
||||||
export function random(length: number): string {
|
export function random(length: number): string {
|
||||||
return cryptoRandomString({length})
|
return cryptoRandomString({ length });
|
||||||
}
|
}
|
||||||
|
|
||||||
// encrypt
|
// encrypt
|
||||||
|
|||||||
@@ -140,11 +140,12 @@ export function SettingsPage(
|
|||||||
<div class="flex-row width-input">
|
<div class="flex-row width-input">
|
||||||
<button
|
<button
|
||||||
class="width-50"
|
class="width-50"
|
||||||
on:click={
|
on:click={settingsPageViewModel.randomizeKey}
|
||||||
settingsPageViewModel.randomizeKey
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{coreViewModel.translations.chatPage.settings.randomizeKey}
|
{
|
||||||
|
coreViewModel.translations.chatPage.settings
|
||||||
|
.randomizeKey
|
||||||
|
}
|
||||||
<span class="icon">shuffle</span>
|
<span class="icon">shuffle</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
@@ -176,7 +177,7 @@ export function SettingsPage(
|
|||||||
coreViewModel.translations.chatPage.settings
|
coreViewModel.translations.chatPage.settings
|
||||||
.showEncryptionKey
|
.showEncryptionKey
|
||||||
}
|
}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<hr></hr>
|
<hr></hr>
|
||||||
|
|
||||||
|
|||||||
@@ -65,10 +65,10 @@ export function TaskPage(
|
|||||||
class="no-outline"
|
class="no-outline"
|
||||||
bind:value={taskPageViewModel.boardQuery}
|
bind:value={taskPageViewModel.boardQuery}
|
||||||
on:enter={taskPageViewModel.createBoard}
|
on:enter={taskPageViewModel.createBoard}
|
||||||
placeholder={
|
placeholder={coreViewModel.translations.general.filterOrCreateLabel(
|
||||||
coreViewModel.translations.general
|
coreViewModel.translations.chatPage.task
|
||||||
.filterOrCreateLabel(coreViewModel.translations.chatPage.task.typeBoard)
|
.typeBoard,
|
||||||
}
|
)}
|
||||||
></input>
|
></input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ export function BoardViewToggleButton(
|
|||||||
key: string,
|
key: string,
|
||||||
) {
|
) {
|
||||||
function select() {
|
function select() {
|
||||||
boardViewModel.selectedPage.value = page;
|
boardViewModel.selectedPage.value = page;
|
||||||
boardViewModel.resetPinchZoom();
|
boardViewModel.resetPinchZoom();
|
||||||
}
|
}
|
||||||
|
|
||||||
const isSelected = React.createProxyState(
|
const isSelected = React.createProxyState(
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import * as React from "../../react";
|
||||||
|
|
||||||
|
export function ProgressBar(
|
||||||
|
valueLabel: React.State<string>,
|
||||||
|
value: React.State<number>,
|
||||||
|
maximum: React.State<number>,
|
||||||
|
) {
|
||||||
|
const style = React.createProxyState(
|
||||||
|
[value, maximum],
|
||||||
|
() => `width: ${100 * (value.value / maximum.value)}%`,
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div class="width-100 surface-alt" style="height: 18px">
|
||||||
|
<div
|
||||||
|
class="height-100 background-primary"
|
||||||
|
set:style={style}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<span class="secondary" subscribe:innerText={valueLabel}></span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { ViewController } from "../viewController";
|
import { ViewController } from "../viewController";
|
||||||
import { CommonKeys, getKeySymbol } from "../keystrokes";
|
import { CommonKeys, getKeySymbol } from "../keystrokes";
|
||||||
import { StringToTextSpan } from "../Components/textSpan";
|
import { StringToTextSpan } from "../Components/textSpan";
|
||||||
|
import { ProgressBar } from "../Components/progressBar";
|
||||||
import * as React from "../../react";
|
import * as React from "../../react";
|
||||||
import FileTransferViewModel, {
|
import FileTransferViewModel, {
|
||||||
FileTransferModals,
|
FileTransferModals,
|
||||||
@@ -370,6 +371,18 @@ function TransferDisplayModal(
|
|||||||
FileTransferModals.TransferDisplay,
|
FileTransferModals.TransferDisplay,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const progressLabel = React.createProxyState(
|
||||||
|
[
|
||||||
|
fileTransferViewModel.fileTransferProgress,
|
||||||
|
fileTransferViewModel.fileTransferTotal,
|
||||||
|
],
|
||||||
|
() =>
|
||||||
|
coreViewModel.translations.dataTransferModal.filesSentProgress(
|
||||||
|
fileTransferViewModel.fileTransferProgress.value,
|
||||||
|
fileTransferViewModel.fileTransferTotal.value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="modal" toggle:open={isPresented}>
|
<div class="modal" toggle:open={isPresented}>
|
||||||
<div>
|
<div>
|
||||||
@@ -381,24 +394,11 @@ function TransferDisplayModal(
|
|||||||
}
|
}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<p
|
{ProgressBar(
|
||||||
class="secondary"
|
progressLabel,
|
||||||
subscribe:innerText={
|
fileTransferViewModel.fileTransferProgress,
|
||||||
fileTransferViewModel.filesSentText
|
fileTransferViewModel.fileTransferTotal,
|
||||||
}
|
)}
|
||||||
></p>
|
|
||||||
|
|
||||||
<p
|
|
||||||
class="secondary"
|
|
||||||
toggle:hidden={
|
|
||||||
fileTransferViewModel.didNotFinishSending
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{
|
|
||||||
coreViewModel.translations.dataTransferModal
|
|
||||||
.allFilesSent
|
|
||||||
}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<hr></hr>
|
<hr></hr>
|
||||||
|
|
||||||
@@ -460,13 +460,7 @@ function TransferDisplayModal(
|
|||||||
}
|
}
|
||||||
<span class="icon">restart_alt</span>
|
<span class="icon">restart_alt</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button class="flex" on:click={fileTransferViewModel.close}>
|
||||||
class="flex"
|
|
||||||
on:click={fileTransferViewModel.close}
|
|
||||||
toggle:disabled={
|
|
||||||
fileTransferViewModel.didNotFinishSending
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{coreViewModel.translations.general.closeButton}
|
{coreViewModel.translations.general.closeButton}
|
||||||
<span class="icon">close</span>
|
<span class="icon">close</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -588,6 +582,18 @@ function DataReceptionModal(
|
|||||||
FileTransferModals.ReceptionDisplay,
|
FileTransferModals.ReceptionDisplay,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const progressLabel = React.createProxyState(
|
||||||
|
[
|
||||||
|
fileTransferViewModel.fileTransferProgress,
|
||||||
|
fileTransferViewModel.fileTransferTotal,
|
||||||
|
],
|
||||||
|
() =>
|
||||||
|
coreViewModel.translations.dataTransferModal.filesReceivedProgress(
|
||||||
|
fileTransferViewModel.fileTransferProgress.value,
|
||||||
|
fileTransferViewModel.fileTransferTotal.value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="modal" toggle:open={isPresented}>
|
<div class="modal" toggle:open={isPresented}>
|
||||||
<div>
|
<div>
|
||||||
@@ -599,12 +605,11 @@ function DataReceptionModal(
|
|||||||
}
|
}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<p
|
{ProgressBar(
|
||||||
class="secondary"
|
progressLabel,
|
||||||
subscribe:innerText={
|
fileTransferViewModel.fileTransferProgress,
|
||||||
fileTransferViewModel.filesReceivedText
|
fileTransferViewModel.fileTransferTotal,
|
||||||
}
|
)}
|
||||||
></p>
|
|
||||||
|
|
||||||
<hr></hr>
|
<hr></hr>
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
|
|
||||||
@media (min-width: 640px) {
|
@media (min-width: 640px) {
|
||||||
#home-page > div {
|
#home-page > div {
|
||||||
gap: var(--content-padding);
|
gap: var(--content-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
#home-page #overview-section {
|
#home-page #overview-section {
|
||||||
@@ -66,11 +66,11 @@
|
|||||||
|
|
||||||
@media (min-width: 1000px) {
|
@media (min-width: 1000px) {
|
||||||
#home-page > div {
|
#home-page > div {
|
||||||
gap: var(--large-gap);
|
gap: var(--large-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
#home-page > h1 {
|
#home-page > h1 {
|
||||||
padding-top: 3rem;
|
padding-top: 3rem;
|
||||||
padding-bottom: 1rem;
|
padding-bottom: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-20
@@ -1,5 +1,6 @@
|
|||||||
import "./homePage.css";
|
import "./homePage.css";
|
||||||
import { StringToOption } from "./Components/option";
|
import { StringToOption } from "./Components/option";
|
||||||
|
import { NewItemEntry } from "./Components/newItemEntry";
|
||||||
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 * as React from "../react";
|
||||||
@@ -9,7 +10,6 @@ import FileTransferViewModel from "../ViewModel/Global/fileTransferViewModel";
|
|||||||
import CoreViewModel from "../ViewModel/Global/coreViewModel";
|
import CoreViewModel from "../ViewModel/Global/coreViewModel";
|
||||||
import ConnectionViewModel from "../ViewModel/Global/connectionViewModel";
|
import ConnectionViewModel from "../ViewModel/Global/connectionViewModel";
|
||||||
import ChatListViewModel from "../ViewModel/Chat/chatListViewModel";
|
import ChatListViewModel from "../ViewModel/Chat/chatListViewModel";
|
||||||
import {NewItemEntry} from "./Components/newItemEntry";
|
|
||||||
|
|
||||||
export function HomePage(
|
export function HomePage(
|
||||||
coreViewModel: CoreViewModel,
|
coreViewModel: CoreViewModel,
|
||||||
@@ -19,9 +19,18 @@ export function HomePage(
|
|||||||
fileTransferViewModel: FileTransferViewModel,
|
fileTransferViewModel: FileTransferViewModel,
|
||||||
chatListViewModel: ChatListViewModel,
|
chatListViewModel: ChatListViewModel,
|
||||||
) {
|
) {
|
||||||
const greeting = React.createProxyState([settingsViewModel.username], ()=>coreViewModel.translations.homePage.greeting(settingsViewModel.username.value));
|
const greeting = React.createProxyState([settingsViewModel.username], () =>
|
||||||
|
coreViewModel.translations.homePage.greeting(
|
||||||
|
settingsViewModel.username.value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
const isChatPlaceholderHidden = React.createProxyState([chatListViewModel.chatMatches, chatListViewModel.chatQuery], ()=>chatListViewModel.chatMatches.value.size > 0 || chatListViewModel.chatQuery.value != "");
|
const isChatPlaceholderHidden = React.createProxyState(
|
||||||
|
[chatListViewModel.chatMatches, chatListViewModel.chatQuery],
|
||||||
|
() =>
|
||||||
|
chatListViewModel.chatMatches.value.size > 0 ||
|
||||||
|
chatListViewModel.chatQuery.value != "",
|
||||||
|
);
|
||||||
|
|
||||||
// sections
|
// sections
|
||||||
const overviewSection = (
|
const overviewSection = (
|
||||||
@@ -120,7 +129,7 @@ export function HomePage(
|
|||||||
<span class="icon">update</span>
|
<span class="icon">update</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<hr class="mobile-only"></hr>
|
<hr class="mobile-only"></hr>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -128,25 +137,25 @@ export function HomePage(
|
|||||||
<div id="chat-section">
|
<div id="chat-section">
|
||||||
<div class="flex-row width-input">
|
<div class="flex-row width-input">
|
||||||
<input
|
<input
|
||||||
placeholder={
|
placeholder={coreViewModel.translations.general.filterOrCreateLabel(
|
||||||
coreViewModel.translations.general
|
coreViewModel.translations.homePage.typeChat,
|
||||||
.filterOrCreateLabel(coreViewModel.translations.homePage.typeChat)
|
)}
|
||||||
}
|
|
||||||
bind:value={chatListViewModel.chatQuery}
|
bind:value={chatListViewModel.chatQuery}
|
||||||
on:enter={chatListViewModel.createChat}
|
on:enter={chatListViewModel.createChat}
|
||||||
></input>
|
></input>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr></hr>
|
<hr></hr>
|
||||||
|
|
||||||
{NewItemEntry(coreViewModel, chatListViewModel.chatQuery, chatListViewModel.createChat)}
|
{NewItemEntry(
|
||||||
|
coreViewModel,
|
||||||
|
chatListViewModel.chatQuery,
|
||||||
|
chatListViewModel.createChat,
|
||||||
|
)}
|
||||||
|
|
||||||
<span
|
<span class="secondary" toggle:hidden={isChatPlaceholderHidden}>
|
||||||
class="secondary"
|
{coreViewModel.translations.homePage.noChatsPlaceholder}
|
||||||
toggle:hidden={isChatPlaceholderHidden}
|
</span>
|
||||||
>
|
|
||||||
{coreViewModel.translations.homePage.noChatsPlaceholder}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
id="chat-grid"
|
id="chat-grid"
|
||||||
@@ -154,14 +163,14 @@ export function HomePage(
|
|||||||
chatListViewModel.chatMatches,
|
chatListViewModel.chatMatches,
|
||||||
ChatViewModelToChatEntry,
|
ChatViewModelToChatEntry,
|
||||||
]}
|
]}
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
// final
|
// final
|
||||||
return (
|
return (
|
||||||
<article id="home-page">
|
<article id="home-page">
|
||||||
<h1 subscribe:innerText={greeting}></h1>
|
<h1 subscribe:innerText={greeting}></h1>
|
||||||
<div>
|
<div>
|
||||||
{overviewSection}
|
{overviewSection}
|
||||||
{chatSection}
|
{chatSection}
|
||||||
|
|||||||
+567
-566
File diff suppressed because it is too large
Load Diff
@@ -35,9 +35,7 @@ export default class ChatListViewModel {
|
|||||||
// methods
|
// methods
|
||||||
createChat = (): void => {
|
createChat = (): void => {
|
||||||
const chatModel: ChatModel =
|
const chatModel: ChatModel =
|
||||||
this.coreViewModel.chatListModel.createChat(
|
this.coreViewModel.chatListModel.createChat(this.chatQuery.value);
|
||||||
this.chatQuery.value,
|
|
||||||
);
|
|
||||||
this.chatQuery.value = "";
|
this.chatQuery.value = "";
|
||||||
|
|
||||||
const chatViewModel: ChatViewModel =
|
const chatViewModel: ChatViewModel =
|
||||||
@@ -109,7 +107,12 @@ export default class ChatListViewModel {
|
|||||||
|
|
||||||
this.loadChats();
|
this.loadChats();
|
||||||
|
|
||||||
implementFilter(this.chatViewModels, this.chatMatches, this.chatQuery, chat => chat.chatModel.info.name)
|
implementFilter(
|
||||||
|
this.chatViewModels,
|
||||||
|
this.chatMatches,
|
||||||
|
this.chatQuery,
|
||||||
|
(chat) => chat.chatModel.info.name,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// utility
|
// utility
|
||||||
|
|||||||
@@ -52,22 +52,23 @@ export default class CoreViewModel {
|
|||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let exitNext: boolean = false;
|
let exitNext: boolean = false;
|
||||||
|
|
||||||
while (this.contexts.length > 0) {
|
while (this.contexts.length > 0) {
|
||||||
const currentContext: Context | undefined = this.context;
|
const currentContext: Context | undefined = this.context;
|
||||||
if (!currentContext) break;
|
if (!currentContext) break;
|
||||||
const response = currentContext.handleContextClose(fromHistoryEvent);
|
const response =
|
||||||
if (response == false) break;
|
currentContext.handleContextClose(fromHistoryEvent);
|
||||||
|
if (response == false) break;
|
||||||
this.contextStack.delete(currentContext.contextId);
|
this.contextStack.delete(currentContext.contextId);
|
||||||
if (response == true) {
|
if (response == true) {
|
||||||
exitNext = true;
|
exitNext = true;
|
||||||
continue;
|
continue;
|
||||||
} else if (exitNext == true) {
|
} else if (exitNext == true) {
|
||||||
break
|
break;
|
||||||
} else if (currentContext.contextId == contextId) {
|
} else if (currentContext.contextId == contextId) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -206,7 +207,7 @@ export class Context {
|
|||||||
};
|
};
|
||||||
|
|
||||||
close = (): void => {};
|
close = (): void => {};
|
||||||
handleContextClose = (fromHistoryEvent: boolean): void|boolean=> {};
|
handleContextClose = (fromHistoryEvent: boolean): void | boolean => {};
|
||||||
|
|
||||||
registerKeyStroke = (key: string, fn: () => void): void => {
|
registerKeyStroke = (key: string, fn: () => void): void => {
|
||||||
this.keystrokes.set(key, fn);
|
this.keystrokes.set(key, fn);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import ChatModel from "../../Model/Chat/chatModel";
|
|||||||
|
|
||||||
export default class FileTransferViewModel extends Context {
|
export default class FileTransferViewModel extends Context {
|
||||||
// state
|
// state
|
||||||
exitReception: () => void | undefined = undefined;
|
exitReception: (() => void) | undefined = undefined;
|
||||||
|
|
||||||
presentedModal: React.State<FileTransferModals | undefined> =
|
presentedModal: React.State<FileTransferModals | undefined> =
|
||||||
new React.State<any>(undefined);
|
new React.State<any>(undefined);
|
||||||
@@ -29,30 +29,14 @@ export default class FileTransferViewModel extends Context {
|
|||||||
receivingTransferKey: React.State<string> = new React.State("");
|
receivingTransferKey: React.State<string> = new React.State("");
|
||||||
|
|
||||||
filePathsSent: React.ListState<string> = new React.ListState();
|
filePathsSent: React.ListState<string> = new React.ListState();
|
||||||
filesSentCount: React.State<number> = React.createProxyState(
|
|
||||||
[this.filePathsSent],
|
|
||||||
() => this.filePathsSent.value.size,
|
|
||||||
);
|
|
||||||
filesSentText: React.State<string> = React.createProxyState(
|
|
||||||
[this.filesSentCount],
|
|
||||||
() =>
|
|
||||||
this.coreViewModel.translations.dataTransferModal.filesSentCount(
|
|
||||||
this.filesSentCount.value,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
|
fileTransferTotal: React.State<number> = new React.State(0);
|
||||||
|
fileTransferProgress: React.State<number> = new React.State(0);
|
||||||
filePathsReceived: React.ListState<string> = new React.ListState();
|
filePathsReceived: React.ListState<string> = new React.ListState();
|
||||||
filesReceivedCount: React.State<number> = React.createProxyState(
|
filesReceivedCount: React.State<number> = React.createProxyState(
|
||||||
[this.filePathsReceived],
|
[this.filePathsReceived],
|
||||||
() => this.filePathsReceived.value.size,
|
() => this.filePathsReceived.value.size,
|
||||||
);
|
);
|
||||||
filesReceivedText: React.State<string> = React.createProxyState(
|
|
||||||
[this.filesReceivedCount],
|
|
||||||
() =>
|
|
||||||
this.coreViewModel.translations.dataTransferModal.filesReceivedCount(
|
|
||||||
this.filesReceivedCount.value,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
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("");
|
||||||
@@ -69,7 +53,6 @@ export default class FileTransferViewModel extends Context {
|
|||||||
[this.selectedPaths],
|
[this.selectedPaths],
|
||||||
() => this.selectedPaths.value.size == 0,
|
() => this.selectedPaths.value.size == 0,
|
||||||
);
|
);
|
||||||
didNotFinishSending: React.State<boolean> = new React.State(true);
|
|
||||||
cannotPrepareToReceive: React.State<boolean> = React.createProxyState(
|
cannotPrepareToReceive: React.State<boolean> = React.createProxyState(
|
||||||
[this.receivingTransferChannel, this.receivingTransferKey],
|
[this.receivingTransferChannel, this.receivingTransferKey],
|
||||||
() =>
|
() =>
|
||||||
@@ -99,6 +82,7 @@ export default class FileTransferViewModel extends Context {
|
|||||||
// handlers
|
// handlers
|
||||||
handleReceivedFile = (path: string): void => {
|
handleReceivedFile = (path: string): void => {
|
||||||
this.filePathsReceived.add(path);
|
this.filePathsReceived.add(path);
|
||||||
|
this.fileTransferProgress.value = this.filePathsReceived.value.size;
|
||||||
};
|
};
|
||||||
|
|
||||||
// methods
|
// methods
|
||||||
@@ -229,7 +213,6 @@ export default class FileTransferViewModel extends Context {
|
|||||||
|
|
||||||
initiateTransfer = (): void => {
|
initiateTransfer = (): void => {
|
||||||
this.presentedModal.value = FileTransferModals.TransferDisplay;
|
this.presentedModal.value = FileTransferModals.TransferDisplay;
|
||||||
this.didNotFinishSending.value = true;
|
|
||||||
this.filePathsSent.clear();
|
this.filePathsSent.clear();
|
||||||
|
|
||||||
this.coreViewModel.fileTransferModel.sendFiles(
|
this.coreViewModel.fileTransferModel.sendFiles(
|
||||||
@@ -238,8 +221,6 @@ export default class FileTransferViewModel extends Context {
|
|||||||
this.filePathsSent.add(path);
|
this.filePathsSent.add(path);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
this.didNotFinishSending.value = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
showTransferDataInputModal = (): void => {
|
showTransferDataInputModal = (): void => {
|
||||||
@@ -287,6 +268,11 @@ export default class FileTransferViewModel extends Context {
|
|||||||
constructor(public readonly coreViewModel: CoreViewModel) {
|
constructor(public readonly coreViewModel: CoreViewModel) {
|
||||||
super("file-transfer");
|
super("file-transfer");
|
||||||
|
|
||||||
|
this.coreViewModel.fileTransferModel.transferSizeHandlerManager.setHandler(
|
||||||
|
"file-transfer-view-model",
|
||||||
|
(count) => (this.fileTransferTotal.value = count),
|
||||||
|
);
|
||||||
|
|
||||||
this.coreViewModel.fileTransferModel.fileHandlerManager.setHandler(
|
this.coreViewModel.fileTransferModel.fileHandlerManager.setHandler(
|
||||||
"file-transfer-view-model",
|
"file-transfer-view-model",
|
||||||
this.handleReceivedFile,
|
this.handleReceivedFile,
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
|||||||
index: React.State<number> = new React.State(0);
|
index: React.State<number> = new React.State(0);
|
||||||
|
|
||||||
selectedPage: React.State<BoardPageTypes> = new React.State<any>(
|
selectedPage: React.State<BoardPageTypes> = new React.State<any>(
|
||||||
BoardPageTypes.List,
|
BoardPageTypes.List,
|
||||||
);
|
);
|
||||||
|
|
||||||
isSelected: React.State<boolean>;
|
isSelected: React.State<boolean>;
|
||||||
@@ -31,339 +31,339 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
|||||||
searchViewModel: SearchViewModel<TaskViewModel>;
|
searchViewModel: SearchViewModel<TaskViewModel>;
|
||||||
searchSuggestions = new React.ListState<string>();
|
searchSuggestions = new React.ListState<string>();
|
||||||
filteredTaskViewModels: React.ListState<TaskViewModel> =
|
filteredTaskViewModels: React.ListState<TaskViewModel> =
|
||||||
new React.ListState();
|
new React.ListState();
|
||||||
isFilterActive: React.State<boolean>;
|
isFilterActive: React.State<boolean>;
|
||||||
|
|
||||||
pinchToZoomData: React.State<PinchToZoomData> = new React.State({
|
pinchToZoomData: React.State<PinchToZoomData> = new React.State({
|
||||||
zoom: 1,
|
zoom: 1,
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
// paths
|
// paths
|
||||||
getBasePath = (): string[] => {
|
getBasePath = (): string[] => {
|
||||||
return [
|
return [
|
||||||
...this.taskPageViewModel.getBoardViewPath(this.boardInfo.fileId),
|
...this.taskPageViewModel.getBoardViewPath(this.boardInfo.fileId),
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
getLastUsedBoardPath = (): string[] => {
|
getLastUsedBoardPath = (): string[] => {
|
||||||
return [...this.getBasePath(), BoardViewModelSubPaths.LastUsedView];
|
return [...this.getBasePath(), BoardViewModelSubPaths.LastUsedView];
|
||||||
};
|
};
|
||||||
|
|
||||||
getPreviousSearchesPath = (): string[] => {
|
getPreviousSearchesPath = (): string[] => {
|
||||||
return [...this.getBasePath(), BoardViewModelSubPaths.PreviousSearches];
|
return [...this.getBasePath(), BoardViewModelSubPaths.PreviousSearches];
|
||||||
};
|
};
|
||||||
|
|
||||||
getLastSearchPath = (): string[] => {
|
getLastSearchPath = (): string[] => {
|
||||||
return [...this.getBasePath(), BoardViewModelSubPaths.LastSearch];
|
return [...this.getBasePath(), BoardViewModelSubPaths.LastSearch];
|
||||||
};
|
};
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
saveSettings = (): void => {
|
saveSettings = (): void => {
|
||||||
const newBoardInfoFileContent: BoardInfoFileContent =
|
const newBoardInfoFileContent: BoardInfoFileContent =
|
||||||
BoardsAndTasksModel.createBoardInfoFileContent(
|
BoardsAndTasksModel.createBoardInfoFileContent(
|
||||||
this.boardInfo.fileId,
|
this.boardInfo.fileId,
|
||||||
this.name.value,
|
this.name.value,
|
||||||
this.color.value,
|
this.color.value,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.taskPageViewModel.updateBoard(newBoardInfoFileContent);
|
this.taskPageViewModel.updateBoard(newBoardInfoFileContent);
|
||||||
};
|
};
|
||||||
|
|
||||||
applyColor = (): void => {
|
applyColor = (): void => {
|
||||||
this.taskPageViewModel.chatViewModel.setDisplayedColor(
|
this.taskPageViewModel.chatViewModel.setDisplayedColor(
|
||||||
this.color.value,
|
this.color.value,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
deleteBoard = (): void => {
|
deleteBoard = (): void => {
|
||||||
this.taskPageViewModel.deleteBoard(this.boardInfo);
|
this.taskPageViewModel.deleteBoard(this.boardInfo);
|
||||||
this.chatViewModel.taskBoardSuggestions.remove(this.boardInfo.fileId);
|
this.chatViewModel.taskBoardSuggestions.remove(this.boardInfo.fileId);
|
||||||
this.close();
|
this.close();
|
||||||
};
|
};
|
||||||
|
|
||||||
// methods
|
// methods
|
||||||
createTask = (): void => {
|
createTask = (): void => {
|
||||||
this.createTaskFromBoardId(this.boardInfo.fileId);
|
this.createTaskFromBoardId(this.boardInfo.fileId);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleDropWithinBoard = (category?: string, status?: string): void => {
|
handleDropWithinBoard = (category?: string, status?: string): void => {
|
||||||
const draggedObject: any = this.coreViewModel.draggedObject.value;
|
const draggedObject: any = this.coreViewModel.draggedObject.value;
|
||||||
if (draggedObject instanceof TaskViewModel == false) return;
|
if (draggedObject instanceof TaskViewModel == false) return;
|
||||||
draggedObject.setCategoryAndStatus(category, status);
|
draggedObject.setCategoryAndStatus(category, status);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleDropBetweenBoards = (): void => {
|
handleDropBetweenBoards = (): void => {
|
||||||
const draggedObject: any = this.coreViewModel.draggedObject.value;
|
const draggedObject: any = this.coreViewModel.draggedObject.value;
|
||||||
if (draggedObject instanceof TaskViewModel == false) return;
|
if (draggedObject instanceof TaskViewModel == false) return;
|
||||||
draggedObject.setBoardId(this.boardInfo.fileId);
|
draggedObject.setBoardId(this.boardInfo.fileId);
|
||||||
};
|
};
|
||||||
|
|
||||||
// storage
|
// storage
|
||||||
storeLastUsedView = (): void => {
|
storeLastUsedView = (): void => {
|
||||||
const path: string[] = this.getLastUsedBoardPath();
|
const path: string[] = this.getLastUsedBoardPath();
|
||||||
const lastUsedView: string = this.selectedPage.value;
|
const lastUsedView: string = this.selectedPage.value;
|
||||||
this.coreViewModel.storageModel.write(path, lastUsedView);
|
this.coreViewModel.storageModel.write(path, lastUsedView);
|
||||||
};
|
};
|
||||||
|
|
||||||
restoreLastUsedView = (): void => {
|
restoreLastUsedView = (): void => {
|
||||||
const path: string[] = this.getLastUsedBoardPath();
|
const path: string[] = this.getLastUsedBoardPath();
|
||||||
const lastUsedView: string | null =
|
const lastUsedView: string | null =
|
||||||
this.coreViewModel.storageModel.read(path);
|
this.coreViewModel.storageModel.read(path);
|
||||||
if (lastUsedView == null) return;
|
if (lastUsedView == null) return;
|
||||||
|
|
||||||
this.selectedPage.value = lastUsedView as BoardPageTypes;
|
this.selectedPage.value = lastUsedView as BoardPageTypes;
|
||||||
};
|
};
|
||||||
|
|
||||||
handleNewSearch = (searchTerm: string): void => {
|
handleNewSearch = (searchTerm: string): void => {
|
||||||
const suggestionPath: string[] = [
|
const suggestionPath: string[] = [
|
||||||
...this.getPreviousSearchesPath(),
|
...this.getPreviousSearchesPath(),
|
||||||
searchTerm,
|
searchTerm,
|
||||||
];
|
];
|
||||||
this.coreViewModel.storageModel.write(suggestionPath, "");
|
this.coreViewModel.storageModel.write(suggestionPath, "");
|
||||||
if (!this.searchSuggestions.value.has(searchTerm) && searchTerm != "") {
|
if (!this.searchSuggestions.value.has(searchTerm) && searchTerm != "") {
|
||||||
this.searchSuggestions.add(searchTerm);
|
this.searchSuggestions.add(searchTerm);
|
||||||
}
|
}
|
||||||
|
|
||||||
const lastSearchPath: string[] = this.getLastSearchPath();
|
const lastSearchPath: string[] = this.getLastSearchPath();
|
||||||
this.coreViewModel.storageModel.write(lastSearchPath, searchTerm);
|
this.coreViewModel.storageModel.write(lastSearchPath, searchTerm);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSearchRemoved = (searchTerm: string): void => {
|
handleSearchRemoved = (searchTerm: string): void => {
|
||||||
const suggestionPath: string[] = [
|
const suggestionPath: string[] = [
|
||||||
...this.getPreviousSearchesPath(),
|
...this.getPreviousSearchesPath(),
|
||||||
searchTerm,
|
searchTerm,
|
||||||
];
|
];
|
||||||
this.coreViewModel.storageModel.remove(suggestionPath);
|
this.coreViewModel.storageModel.remove(suggestionPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
// view
|
// view
|
||||||
showTask = (taskFileContent: TaskFileContent): void => {
|
showTask = (taskFileContent: TaskFileContent): void => {
|
||||||
if (taskFileContent.boardId != this.boardInfo.fileId) {
|
if (taskFileContent.boardId != this.boardInfo.fileId) {
|
||||||
// remove task that was moved to different board
|
// remove task that was moved to different board
|
||||||
this.boardsAndTasksModel.deleteTaskReference(
|
this.boardsAndTasksModel.deleteTaskReference(
|
||||||
this.boardInfo.fileId,
|
this.boardInfo.fileId,
|
||||||
taskFileContent.fileId,
|
taskFileContent.fileId,
|
||||||
);
|
);
|
||||||
this.removeTaskFromView(taskFileContent);
|
this.removeTaskFromView(taskFileContent);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const taskViewModel: TaskViewModel = new TaskViewModel(
|
const taskViewModel: TaskViewModel = new TaskViewModel(
|
||||||
this.coreViewModel,
|
this.coreViewModel,
|
||||||
this.chatViewModel,
|
this.chatViewModel,
|
||||||
this.boardsAndTasksModel,
|
this.boardsAndTasksModel,
|
||||||
this,
|
this,
|
||||||
taskFileContent,
|
taskFileContent,
|
||||||
);
|
);
|
||||||
this.taskViewModels.set(taskFileContent.fileId, taskViewModel);
|
this.taskViewModels.set(taskFileContent.fileId, taskViewModel);
|
||||||
};
|
};
|
||||||
|
|
||||||
removeTaskFromView = (taskFileContent: TaskFileContent): void => {
|
removeTaskFromView = (taskFileContent: TaskFileContent): void => {
|
||||||
this.taskViewModels.remove(taskFileContent.fileId);
|
this.taskViewModels.remove(taskFileContent.fileId);
|
||||||
this.updateIndex();
|
this.updateIndex();
|
||||||
};
|
};
|
||||||
|
|
||||||
select = (): void => {
|
select = (): void => {
|
||||||
this.taskPageViewModel.selectBoard(this);
|
this.taskPageViewModel.selectBoard(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
close = (): void => {
|
close = (): void => {
|
||||||
this.taskPageViewModel.closeBoard();
|
this.taskPageViewModel.closeBoard();
|
||||||
this.taskPageViewModel.storeLastUsedBoard();
|
this.taskPageViewModel.storeLastUsedBoard();
|
||||||
this.taskViewModels.clear();
|
this.taskViewModels.clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
showSettings = (): void => {
|
showSettings = (): void => {
|
||||||
this.isPresentingSettingsModal.value = true;
|
this.isPresentingSettingsModal.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
hideSettings = (): void => {
|
hideSettings = (): void => {
|
||||||
this.saveSettings();
|
this.saveSettings();
|
||||||
this.isPresentingSettingsModal.value = false;
|
this.isPresentingSettingsModal.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
showFilterModal = (): void => {
|
showFilterModal = (): void => {
|
||||||
this.isPresentingFilterModal.value = true;
|
this.isPresentingFilterModal.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
hideFilterModal = (): void => {
|
hideFilterModal = (): void => {
|
||||||
this.isPresentingFilterModal.value = false;
|
this.isPresentingFilterModal.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
resetFilter = (): void => {
|
resetFilter = (): void => {
|
||||||
this.searchViewModel.search("");
|
this.searchViewModel.search("");
|
||||||
};
|
};
|
||||||
|
|
||||||
updateIndex = (): void => {
|
updateIndex = (): void => {
|
||||||
const index: number =
|
const index: number =
|
||||||
this.taskPageViewModel.boardIndexManager.getIndex(this);
|
this.taskPageViewModel.boardIndexManager.getIndex(this);
|
||||||
this.index.value = index;
|
this.index.value = index;
|
||||||
};
|
};
|
||||||
|
|
||||||
resetPinchZoom = (): void => {
|
resetPinchZoom = (): void => {
|
||||||
this.pinchToZoomData.value = {x: 0, y: 0, zoom: 1};
|
this.pinchToZoomData.value = { x: 0, y: 0, zoom: 1 };
|
||||||
}
|
};
|
||||||
|
|
||||||
// load
|
// load
|
||||||
preloadData = (): void => {
|
preloadData = (): void => {
|
||||||
this.name.value = this.boardInfo.name;
|
this.name.value = this.boardInfo.name;
|
||||||
this.color.value = this.boardInfo.color;
|
this.color.value = this.boardInfo.color;
|
||||||
};
|
};
|
||||||
|
|
||||||
loadTasks = (): void => {
|
loadTasks = (): void => {
|
||||||
const taskIds: string[] = this.boardsAndTasksModel.listTaskIds(
|
const taskIds: string[] = this.boardsAndTasksModel.listTaskIds(
|
||||||
this.boardInfo.fileId,
|
this.boardInfo.fileId,
|
||||||
);
|
);
|
||||||
for (const taskId of taskIds) {
|
for (const taskId of taskIds) {
|
||||||
if (this.taskViewModels.value.has(taskId)) continue;
|
if (this.taskViewModels.value.has(taskId)) continue;
|
||||||
|
|
||||||
const taskFileContent: TaskFileContent | null =
|
const taskFileContent: TaskFileContent | null =
|
||||||
this.boardsAndTasksModel.getLatestTaskFileContent(taskId);
|
this.boardsAndTasksModel.getLatestTaskFileContent(taskId);
|
||||||
if (taskFileContent == null) continue;
|
if (taskFileContent == null) continue;
|
||||||
|
|
||||||
const taskViewModel: TaskViewModel = new TaskViewModel(
|
const taskViewModel: TaskViewModel = new TaskViewModel(
|
||||||
this.coreViewModel,
|
this.coreViewModel,
|
||||||
this.chatViewModel,
|
this.chatViewModel,
|
||||||
this.boardsAndTasksModel,
|
this.boardsAndTasksModel,
|
||||||
this,
|
this,
|
||||||
taskFileContent,
|
taskFileContent,
|
||||||
);
|
);
|
||||||
this.taskViewModels.set(taskFileContent.fileId, taskViewModel);
|
this.taskViewModels.set(taskFileContent.fileId, taskViewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateTaskIndices();
|
this.updateTaskIndices();
|
||||||
};
|
};
|
||||||
|
|
||||||
loadSearchSuggestions = (): void => {
|
loadSearchSuggestions = (): void => {
|
||||||
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 => {
|
||||||
const lastSearchPath: string[] = this.getLastSearchPath();
|
const lastSearchPath: string[] = this.getLastSearchPath();
|
||||||
const lastSearch: string | null =
|
const lastSearch: string | null =
|
||||||
this.coreViewModel.storageModel.read(lastSearchPath);
|
this.coreViewModel.storageModel.read(lastSearchPath);
|
||||||
if (lastSearch != null) {
|
if (lastSearch != null) {
|
||||||
this.searchViewModel.search(lastSearch);
|
this.searchViewModel.search(lastSearch);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
loadData = (): void => {
|
loadData = (): void => {
|
||||||
this.restoreLastUsedView();
|
this.restoreLastUsedView();
|
||||||
this.loadTasks();
|
this.loadTasks();
|
||||||
this.loadSearchSuggestions();
|
this.loadSearchSuggestions();
|
||||||
};
|
};
|
||||||
|
|
||||||
// exit
|
// exit
|
||||||
handleContextClose = (fromHistoryEvent: boolean): void => {
|
handleContextClose = (fromHistoryEvent: boolean): void => {
|
||||||
this.taskPageViewModel.handleBoardClosed(this);
|
this.taskPageViewModel.handleBoardClosed(this);
|
||||||
|
|
||||||
if (!fromHistoryEvent) return;
|
if (!fromHistoryEvent) return;
|
||||||
this.taskPageViewModel.storeLastUsedBoard();
|
this.taskPageViewModel.storeLastUsedBoard();
|
||||||
};
|
};
|
||||||
|
|
||||||
// init
|
// init
|
||||||
constructor(
|
constructor(
|
||||||
public readonly coreViewModel: CoreViewModel,
|
public readonly coreViewModel: CoreViewModel,
|
||||||
public readonly chatViewModel: ChatViewModel,
|
public readonly chatViewModel: ChatViewModel,
|
||||||
public readonly boardsAndTasksModel: BoardsAndTasksModel,
|
public readonly boardsAndTasksModel: BoardsAndTasksModel,
|
||||||
public readonly taskPageViewModel: TaskPageViewModel,
|
public readonly taskPageViewModel: TaskPageViewModel,
|
||||||
public readonly boardInfo: BoardInfoFileContent,
|
public readonly boardInfo: BoardInfoFileContent,
|
||||||
) {
|
) {
|
||||||
super(coreViewModel, chatViewModel, boardsAndTasksModel, "board");
|
super(coreViewModel, chatViewModel, boardsAndTasksModel, "board");
|
||||||
|
|
||||||
// load
|
// load
|
||||||
this.preloadData();
|
this.preloadData();
|
||||||
|
|
||||||
// search
|
// search
|
||||||
this.searchViewModel = new SearchViewModel(
|
this.searchViewModel = new SearchViewModel(
|
||||||
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],
|
||||||
() => this.searchViewModel.appliedQuery.value != "",
|
() => this.searchViewModel.appliedQuery.value != "",
|
||||||
);
|
);
|
||||||
|
|
||||||
// subscriptions
|
// subscriptions
|
||||||
this.isSelected = React.createProxyState(
|
this.isSelected = React.createProxyState(
|
||||||
[this.taskPageViewModel.selectedBoardId],
|
[this.taskPageViewModel.selectedBoardId],
|
||||||
() =>
|
() =>
|
||||||
this.taskPageViewModel.selectedBoardId.value ==
|
this.taskPageViewModel.selectedBoardId.value ==
|
||||||
this.boardInfo.fileId,
|
this.boardInfo.fileId,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.color.subscribe(() => {
|
this.color.subscribe(() => {
|
||||||
if (this.isSelected.value == false) return;
|
if (this.isSelected.value == false) return;
|
||||||
if (this.chatViewModel.selectedPage.value != ChatPageTypes.Tasks)
|
if (this.chatViewModel.selectedPage.value != ChatPageTypes.Tasks)
|
||||||
return;
|
return;
|
||||||
this.applyColor();
|
this.applyColor();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.selectedPage.subscribeSilent(() => {
|
this.selectedPage.subscribeSilent(() => {
|
||||||
this.storeLastUsedView();
|
this.storeLastUsedView();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.searchViewModel.appliedQuery.subscribeSilent(()=> {
|
this.searchViewModel.appliedQuery.subscribeSilent(() => {
|
||||||
this.resetPinchZoom();
|
this.resetPinchZoom();
|
||||||
})
|
});
|
||||||
|
|
||||||
// handlers
|
// handlers
|
||||||
boardsAndTasksModel.taskHandlerManager.setHandler(
|
boardsAndTasksModel.taskHandlerManager.setHandler(
|
||||||
this.boardInfo.fileId,
|
this.boardInfo.fileId,
|
||||||
(taskFileContent: TaskFileContent) => {
|
(taskFileContent: TaskFileContent) => {
|
||||||
if (taskFileContent.boardId != this.boardInfo.fileId) return;
|
if (taskFileContent.boardId != this.boardInfo.fileId) return;
|
||||||
this.showTask(taskFileContent);
|
this.showTask(taskFileContent);
|
||||||
this.updateTaskIndices();
|
this.updateTaskIndices();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// keystrokes
|
// keystrokes
|
||||||
this.registerKeyStroke(CommonKeys.Filter, this.showFilterModal);
|
this.registerKeyStroke(CommonKeys.Filter, this.showFilterModal);
|
||||||
this.registerKeyStroke(CommonKeys.Reset, this.resetFilter); //
|
this.registerKeyStroke(CommonKeys.Reset, this.resetFilter); //
|
||||||
this.registerKeyStroke(CommonKeys.CloseOrCancel, this.hideFilterModal);
|
this.registerKeyStroke(CommonKeys.CloseOrCancel, this.hideFilterModal);
|
||||||
this.registerKeyStroke(CommonKeys.Settings, this.showSettings);
|
this.registerKeyStroke(CommonKeys.Settings, this.showSettings);
|
||||||
this.registerKeyStroke(CommonKeys.Apply, this.hideSettings);
|
this.registerKeyStroke(CommonKeys.Apply, this.hideSettings);
|
||||||
this.registerKeyStroke(CommonKeys.Create, this.createTask);
|
this.registerKeyStroke(CommonKeys.Create, this.createTask);
|
||||||
|
|
||||||
this.registerKeyStroke(
|
this.registerKeyStroke(
|
||||||
"j",
|
"j",
|
||||||
() => (this.selectedPage.value = BoardPageTypes.List),
|
() => (this.selectedPage.value = BoardPageTypes.List),
|
||||||
);
|
);
|
||||||
this.registerKeyStroke(
|
this.registerKeyStroke(
|
||||||
"k",
|
"k",
|
||||||
() => (this.selectedPage.value = BoardPageTypes.Kanban),
|
() => (this.selectedPage.value = BoardPageTypes.Kanban),
|
||||||
);
|
);
|
||||||
this.registerKeyStroke(
|
this.registerKeyStroke(
|
||||||
"l",
|
"l",
|
||||||
() => (this.selectedPage.value = BoardPageTypes.StatusGrid),
|
() => (this.selectedPage.value = BoardPageTypes.StatusGrid),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.taskPageViewModel.registerContext(this.boardInfo.fileId, this);
|
this.taskPageViewModel.registerContext(this.boardInfo.fileId, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum BoardViewModelSubPaths {
|
export enum BoardViewModelSubPaths {
|
||||||
LastUsedView = "last-used-view",
|
LastUsedView = "last-used-view",
|
||||||
PreviousSearches = "previous-searches",
|
PreviousSearches = "previous-searches",
|
||||||
LastSearch = "last-search",
|
LastSearch = "last-search",
|
||||||
}
|
}
|
||||||
|
|
||||||
// types
|
// types
|
||||||
export enum BoardPageTypes {
|
export enum BoardPageTypes {
|
||||||
List = "list",
|
List = "list",
|
||||||
Kanban = "kanban",
|
Kanban = "kanban",
|
||||||
StatusGrid = "status-grid",
|
StatusGrid = "status-grid",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -222,9 +222,9 @@ export default class CalendarPageViewModel extends TaskContainingPageViewModel {
|
|||||||
|
|
||||||
// context
|
// context
|
||||||
handleContextClose = (fromHistoryEvent: boolean): void | boolean => {
|
handleContextClose = (fromHistoryEvent: boolean): void | boolean => {
|
||||||
if (!fromHistoryEvent) return;
|
if (!fromHistoryEvent) return;
|
||||||
return true;
|
return true;
|
||||||
}
|
};
|
||||||
|
|
||||||
// init
|
// init
|
||||||
constructor(
|
constructor(
|
||||||
|
|||||||
@@ -1,34 +1,33 @@
|
|||||||
import * as React from "../../react";
|
|
||||||
|
|
||||||
import StorageViewModel from "../Global/storageViewModel";
|
import StorageViewModel from "../Global/storageViewModel";
|
||||||
import SettingsViewModel from "../Global/settingsViewModel";
|
import SettingsViewModel from "../Global/settingsViewModel";
|
||||||
import FileTransferViewModel from "../Global/fileTransferViewModel";
|
import FileTransferViewModel from "../Global/fileTransferViewModel";
|
||||||
import CoreViewModel, { Context } from "../Global/coreViewModel";
|
import CoreViewModel, { Context } from "../Global/coreViewModel";
|
||||||
import ConnectionViewModel from "../Global/connectionViewModel";
|
import ConnectionViewModel from "../Global/connectionViewModel";
|
||||||
|
import * as React from "../../react";
|
||||||
import { CommonKeys } from "../../View/keystrokes";
|
import { CommonKeys } from "../../View/keystrokes";
|
||||||
|
|
||||||
export default class HomeViewModel extends Context {
|
export default class HomeViewModel extends Context {
|
||||||
constructor(
|
constructor(
|
||||||
public readonly coreViewModel: CoreViewModel,
|
public readonly coreViewModel: CoreViewModel,
|
||||||
public readonly settingsViewModel: SettingsViewModel,
|
public readonly settingsViewModel: SettingsViewModel,
|
||||||
public readonly fileTransferViewModel: FileTransferViewModel,
|
public readonly fileTransferViewModel: FileTransferViewModel,
|
||||||
public readonly storageViewModel: StorageViewModel,
|
public readonly storageViewModel: StorageViewModel,
|
||||||
public readonly connectionViewModel: ConnectionViewModel,
|
public readonly connectionViewModel: ConnectionViewModel,
|
||||||
) {
|
) {
|
||||||
super("home");
|
super("home");
|
||||||
|
|
||||||
this.coreViewModel.context = this;
|
this.coreViewModel.context = this;
|
||||||
|
|
||||||
this.registerKeyStroke(
|
this.registerKeyStroke(
|
||||||
CommonKeys.Settings,
|
CommonKeys.Settings,
|
||||||
this.settingsViewModel.showSettingsModal,
|
this.settingsViewModel.showSettingsModal,
|
||||||
);
|
);
|
||||||
this.registerKeyStroke(
|
this.registerKeyStroke(
|
||||||
"t",
|
"t",
|
||||||
this.fileTransferViewModel.showDirectionSelectionModal,
|
this.fileTransferViewModel.showDirectionSelectionModal,
|
||||||
);
|
);
|
||||||
this.registerKeyStroke("e", this.storageViewModel.showStorageModal);
|
this.registerKeyStroke("e", this.storageViewModel.showStorageModal);
|
||||||
this.registerKeyStroke("x", this.connectionViewModel.disconnect);
|
this.registerKeyStroke("x", this.connectionViewModel.disconnect);
|
||||||
this.registerKeyStroke("c", this.connectionViewModel.connect);
|
this.registerKeyStroke("c", this.connectionViewModel.connect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -197,61 +197,61 @@ export default class MessagePageViewModel extends Context {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// context
|
// context
|
||||||
handleContextClose = (fromHistoryEvent: boolean):boolean=> {
|
handleContextClose = (fromHistoryEvent: boolean): boolean | void => {
|
||||||
if (fromHistoryEvent) {
|
if (fromHistoryEvent) {
|
||||||
if (this.replyViewSelectedMessage.value == undefined) return true;
|
if (this.replyViewSelectedMessage.value == undefined) return true;
|
||||||
|
|
||||||
this.resetReplyView();
|
this.resetReplyView();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// init
|
// init
|
||||||
constructor(
|
constructor(
|
||||||
public readonly coreViewModel: CoreViewModel,
|
public readonly coreViewModel: CoreViewModel,
|
||||||
public readonly chatViewModel: ChatViewModel,
|
public readonly chatViewModel: ChatViewModel,
|
||||||
public readonly contactListViewModel: ContactListViewModel,
|
public readonly contactListViewModel: ContactListViewModel,
|
||||||
) {
|
) {
|
||||||
super("message-page");
|
super("message-page");
|
||||||
this.restoreFilter();
|
this.restoreFilter();
|
||||||
|
|
||||||
// states
|
// states
|
||||||
this.cannotSendMessage = React.createProxyState(
|
this.cannotSendMessage = React.createProxyState(
|
||||||
[
|
[
|
||||||
this.chatViewModel.settingsViewModel.username,
|
this.chatViewModel.settingsViewModel.username,
|
||||||
this.composingMessage,
|
this.composingMessage,
|
||||||
],
|
],
|
||||||
() =>
|
() =>
|
||||||
this.chatViewModel.settingsViewModel.username.value == "" ||
|
this.chatViewModel.settingsViewModel.username.value == "" ||
|
||||||
this.composingMessage.value == "",
|
this.composingMessage.value == "",
|
||||||
);
|
);
|
||||||
|
|
||||||
this.searchViewModel = new SearchViewModel(
|
this.searchViewModel = new SearchViewModel(
|
||||||
this.chatMessageViewModels,
|
this.chatMessageViewModels,
|
||||||
this.filteredMessageViewModels,
|
this.filteredMessageViewModels,
|
||||||
(chatMessageViewModel) => [chatMessageViewModel.body.value],
|
(chatMessageViewModel) => [chatMessageViewModel.body.value],
|
||||||
new React.ListState(),
|
new React.ListState(),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.isFilterActive = React.createProxyState(
|
this.isFilterActive = React.createProxyState(
|
||||||
[this.searchViewModel.appliedQuery, this.reactionFilter],
|
[this.searchViewModel.appliedQuery, this.reactionFilter],
|
||||||
() =>
|
() =>
|
||||||
this.searchViewModel.appliedQuery.value != "" ||
|
this.searchViewModel.appliedQuery.value != "" ||
|
||||||
this.reactionFilter.value != undefined,
|
this.reactionFilter.value != undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
// keystrokes
|
// keystrokes
|
||||||
this.registerKeyStroke(CommonKeys.Filter, this.showFilterModal);
|
this.registerKeyStroke(CommonKeys.Filter, this.showFilterModal);
|
||||||
this.registerKeyStroke(CommonKeys.CloseOrCancel, () => {
|
this.registerKeyStroke(CommonKeys.CloseOrCancel, () => {
|
||||||
if (this.isFilterModalOpen.value == true) {
|
if (this.isFilterModalOpen.value == true) {
|
||||||
this.hideFilterModal();
|
this.hideFilterModal();
|
||||||
} else {
|
} else {
|
||||||
this.replyingMessage.value = undefined;
|
this.replyingMessage.value = undefined;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.registerKeyStroke(CommonKeys.Reset, this.resetFilter);
|
this.registerKeyStroke(CommonKeys.Reset, this.resetFilter);
|
||||||
this.registerKeyStroke(CommonKeys.Create, this.setFocus);
|
this.registerKeyStroke(CommonKeys.Create, this.setFocus);
|
||||||
|
|
||||||
this.chatViewModel.registerContext(ChatPageTypes.Messages, this);
|
this.chatViewModel.registerContext(ChatPageTypes.Messages, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import CoreViewModel, { Context } from "../Global/coreViewModel";
|
|||||||
import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel";
|
import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel";
|
||||||
import * as React from "../../react";
|
import * as React from "../../react";
|
||||||
import { Colors } from "../../colors";
|
import { Colors } from "../../colors";
|
||||||
import {random} from "../../Model/Utility/crypto";
|
import { random } from "../../Model/Utility/crypto";
|
||||||
|
|
||||||
export default class SettingsPageViewModel extends Context {
|
export default class SettingsPageViewModel extends Context {
|
||||||
// state
|
// state
|
||||||
@@ -75,9 +75,8 @@ export default class SettingsPageViewModel extends Context {
|
|||||||
};
|
};
|
||||||
|
|
||||||
randomizeKey = (): void => {
|
randomizeKey = (): void => {
|
||||||
this.encryptionKeyInput.value = random(24);
|
this.encryptionKeyInput.value = random(24);
|
||||||
this.setEncryptionKey();
|
};
|
||||||
}
|
|
||||||
|
|
||||||
applyColor = (): void => {
|
applyColor = (): void => {
|
||||||
this.chatViewModel.setColor(this.color.value);
|
this.chatViewModel.setColor(this.color.value);
|
||||||
@@ -123,9 +122,9 @@ export default class SettingsPageViewModel extends Context {
|
|||||||
|
|
||||||
// context
|
// context
|
||||||
handleContextClose = (fromHistoryEvent: boolean): void | boolean => {
|
handleContextClose = (fromHistoryEvent: boolean): void | boolean => {
|
||||||
if (!fromHistoryEvent) return;
|
if (!fromHistoryEvent) return;
|
||||||
return true;
|
return true;
|
||||||
}
|
};
|
||||||
|
|
||||||
// init
|
// init
|
||||||
constructor(
|
constructor(
|
||||||
|
|||||||
@@ -168,9 +168,9 @@ export default class TaskPageViewModel extends ContextHost<string> {
|
|||||||
|
|
||||||
// context
|
// context
|
||||||
handleContextClose = (fromHistoryEvent: boolean): void | boolean => {
|
handleContextClose = (fromHistoryEvent: boolean): void | boolean => {
|
||||||
if (!fromHistoryEvent) return;
|
if (!fromHistoryEvent) return;
|
||||||
return true;
|
return true;
|
||||||
}
|
};
|
||||||
|
|
||||||
// init
|
// init
|
||||||
constructor(
|
constructor(
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export default class SearchViewModel<T> {
|
|||||||
|
|
||||||
clear = (): void => {
|
clear = (): void => {
|
||||||
this.searchInput.value = "";
|
this.searchInput.value = "";
|
||||||
this.applySearch();
|
this.applySearch();
|
||||||
};
|
};
|
||||||
|
|
||||||
deleteSuggestion = (suggestion: string): void => {
|
deleteSuggestion = (suggestion: string): void => {
|
||||||
|
|||||||
+2
-2
@@ -30,7 +30,7 @@ main {
|
|||||||
z-index: -1;
|
z-index: -1;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
||||||
opacity: .3;
|
opacity: 0.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
#background-wrapper > div > div {
|
#background-wrapper > div > div {
|
||||||
@@ -143,6 +143,6 @@ body[theme="dark"] #background-wrapper #grass-2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body[keystroke-active] [ctkeystroke]::after {
|
body[keystroke-active] [ctkeystroke]::after {
|
||||||
opacity: .7;
|
opacity: 0.7;
|
||||||
transform: translateY(0px);
|
transform: translateY(0px);
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-23
@@ -85,32 +85,32 @@ chatListViewModel.selectedChat.subscribe(() => {
|
|||||||
|
|
||||||
document.body.append(
|
document.body.append(
|
||||||
<div id="background-wrapper">
|
<div id="background-wrapper">
|
||||||
<div>
|
<div>
|
||||||
<div id="sky"></div>
|
<div id="sky"></div>
|
||||||
<div id="grass-1"></div>
|
<div id="grass-1"></div>
|
||||||
<div id="grass-2"></div>
|
<div id="grass-2"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>,
|
</div>,
|
||||||
);
|
);
|
||||||
document
|
document
|
||||||
.querySelector("main")!
|
.querySelector("main")!
|
||||||
.append(
|
.append(
|
||||||
HomePage(
|
HomePage(
|
||||||
coreViewModel,
|
coreViewModel,
|
||||||
storageViewModel,
|
storageViewModel,
|
||||||
settingsViewModel,
|
settingsViewModel,
|
||||||
connectionViewModel,
|
connectionViewModel,
|
||||||
fileTransferViewModel,
|
fileTransferViewModel,
|
||||||
chatListViewModel,
|
chatListViewModel,
|
||||||
),
|
),
|
||||||
ChatPageWrapper(coreViewModel, chatListViewModel),
|
ChatPageWrapper(coreViewModel, chatListViewModel),
|
||||||
OnboardingModalWrapper(coreViewModel, onboardingViewModel),
|
OnboardingModalWrapper(coreViewModel, onboardingViewModel),
|
||||||
ConnectionModal(coreViewModel, connectionViewModel),
|
ConnectionModal(coreViewModel, connectionViewModel),
|
||||||
DataTransferModalWrapper(
|
DataTransferModalWrapper(
|
||||||
coreViewModel,
|
coreViewModel,
|
||||||
connectionViewModel,
|
connectionViewModel,
|
||||||
fileTransferViewModel,
|
fileTransferViewModel,
|
||||||
),
|
),
|
||||||
StorageModal(coreViewModel, storageViewModel),
|
StorageModal(coreViewModel, storageViewModel),
|
||||||
SettingsModal(coreViewModel, settingsViewModel),
|
SettingsModal(coreViewModel, settingsViewModel),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user