add progress bar to data transfer

This commit is contained in:
marlon-erler
2026-09-30 18:57:09 +02:00
parent c78c5747cf
commit 397a5f76e4
25 changed files with 1125 additions and 1048 deletions
+63 -53
View File
@@ -436,9 +436,8 @@
sendButton: "Send",
sendAgainButton: "Send again",
///
filesSentCount: (count) => `Files sent: ${count}.`,
allFilesSent: "Done.",
filesReceivedCount: (count) => `Files processed: ${count}.`,
filesSentProgress: (progress, total) => `Sent ${progress} of ${total} files`,
filesReceivedProgress: (progress, total) => `Received ${progress} of ${total} files`,
//
exportKey: "Encryption key",
exportKeyConfirmation: "Confirm",
@@ -702,9 +701,8 @@
transferKeyHeadline: "Schl\xFCssel",
sendButton: "Senden",
sendAgainButton: "Erneut senden",
filesSentCount: (count) => `Dateien gesendet: ${count}.`,
allFilesSent: "Fertig.",
filesReceivedCount: (count) => `Dateien empfangen: ${count}.`,
filesSentProgress: (progress, total) => `${progress} von ${total} Dateien gesendet`,
filesReceivedProgress: (progress, total) => `${progress} von ${total} Dateien empfangen`,
exportKey: "Schl\xFCssel",
exportKeyConfirmation: "Schl\xFCssel best\xE4tigen",
downloadFileButton: "Herunterladen",
@@ -954,9 +952,8 @@
transferKeyHeadline: "Clave de encriptaci\xF3n de transferencia",
sendButton: "Enviar",
sendAgainButton: "Enviar otra vez",
filesSentCount: (count) => `Archivos enviados: ${count}.`,
allFilesSent: "Hecho.",
filesReceivedCount: (count) => `Archivos recibidos: ${count}.`,
filesSentProgress: (progress, total) => `${progress} de ${total} enviados`,
filesReceivedProgress: (progress, total) => `${progress} de ${total} recibidos`,
exportKey: "Clave de encriptaci\xF3n",
exportKeyConfirmation: "Confirmar clave",
downloadFileButton: "Descargar",
@@ -2290,27 +2287,13 @@
this.receivingTransferChannel = new State("");
this.receivingTransferKey = new State("");
this.filePathsSent = new ListState();
this.filesSentCount = createProxyState(
[this.filePathsSent],
() => this.filePathsSent.value.size
);
this.filesSentText = createProxyState(
[this.filesSentCount],
() => this.coreViewModel.translations.dataTransferModal.filesSentCount(
this.filesSentCount.value
)
);
this.fileTransferTotal = new State(0);
this.fileTransferProgress = new State(0);
this.filePathsReceived = new ListState();
this.filesReceivedCount = createProxyState(
[this.filePathsReceived],
() => this.filePathsReceived.value.size
);
this.filesReceivedText = createProxyState(
[this.filesReceivedCount],
() => this.coreViewModel.translations.dataTransferModal.filesReceivedCount(
this.filesReceivedCount.value
)
);
this.exportKey = new State("");
this.exportKeyConfirmation = new State("");
this.importedFile = new State(void 0);
@@ -2322,7 +2305,6 @@
[this.selectedPaths],
() => this.selectedPaths.value.size == 0
);
this.didNotFinishSending = new State(true);
this.cannotPrepareToReceive = createProxyState(
[this.receivingTransferChannel, this.receivingTransferKey],
() => this.receivingTransferChannel.value == "" || this.receivingTransferKey.value == ""
@@ -2346,6 +2328,7 @@
// handlers
this.handleReceivedFile = (path) => {
this.filePathsReceived.add(path);
this.fileTransferProgress.value = this.filePathsReceived.value.size;
};
// methods
this.getOptions = () => {
@@ -2454,7 +2437,6 @@
};
this.initiateTransfer = () => {
this.presentedModal.value = 3 /* TransferDisplay */;
this.didNotFinishSending.value = true;
this.filePathsSent.clear();
this.coreViewModel.fileTransferModel.sendFiles(
this.selectedPaths.value.values(),
@@ -2462,7 +2444,6 @@
this.filePathsSent.add(path);
}
);
this.didNotFinishSending.value = false;
};
this.showTransferDataInputModal = () => {
this.presentedModal.value = 4 /* TransferDataInput */;
@@ -2496,6 +2477,10 @@
this.handleContextClose = () => {
this.presentedModal.value = void 0;
};
this.coreViewModel.fileTransferModel.transferSizeHandlerManager.setHandler(
"file-transfer-view-model",
(count) => this.fileTransferTotal.value = count
);
this.coreViewModel.fileTransferModel.fileHandlerManager.setHandler(
"file-transfer-view-model",
this.handleReceivedFile
@@ -4742,7 +4727,6 @@
};
this.randomizeKey = () => {
this.encryptionKeyInput.value = random(24);
this.setEncryptionKey();
};
this.applyColor = () => {
this.chatViewModel.setColor(this.color.value);
@@ -8165,6 +8149,21 @@
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
function DataTransferModalWrapper(coreViewModel2, connectionViewModel2, fileTransferViewModel2) {
return /* @__PURE__ */ createElement("div", null, DirectionSelectionModal(
@@ -8320,20 +8319,8 @@
[fileTransferViewModel2.presentedModal],
() => 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(
"p",
{
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(
const progressLabel = createProxyState([fileTransferViewModel2.fileTransferProgress, fileTransferViewModel2.fileTransferTotal], () => coreViewModel2.translations.dataTransferModal.filesSentProgress(fileTransferViewModel2.fileTransferProgress.value, fileTransferViewModel2.fileTransferTotal.value));
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(
"b",
{
"subscribe:innerText": fileTransferViewModel2.transferChannel
@@ -8364,8 +8351,7 @@
"button",
{
class: "flex",
"on:click": fileTransferViewModel2.close,
"toggle:disabled": fileTransferViewModel2.didNotFinishSending
"on:click": fileTransferViewModel2.close
},
coreViewModel2.translations.general.closeButton,
/* @__PURE__ */ createElement("span", { class: "icon" }, "close")
@@ -8413,13 +8399,8 @@
[fileTransferViewModel2.presentedModal],
() => 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(
"p",
{
class: "secondary",
"subscribe:innerText": fileTransferViewModel2.filesReceivedText
}
), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement(
const progressLabel = createProxyState([fileTransferViewModel2.fileTransferProgress, fileTransferViewModel2.fileTransferTotal], () => coreViewModel2.translations.dataTransferModal.filesReceivedProgress(fileTransferViewModel2.fileTransferProgress.value, fileTransferViewModel2.fileTransferTotal.value));
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(
"div",
{
class: "tile flex-column align-start",
@@ -8641,6 +8622,7 @@
this.direction = 0 /* Send */;
// handler managers
this.fileHandlerManager = new HandlerManager();
this.transferSizeHandlerManager = new HandlerManager();
this.readyToSendHandlerManager = new HandlerManager();
// general
this.generateTransferData = () => {
@@ -8700,6 +8682,11 @@
};
this.handleDecryptedFile = (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(
parsed,
FileDataReference
@@ -8714,15 +8701,21 @@
};
// sending
this.sendFiles = (directoryPaths, fileCallback) => {
const filesToSend = /* @__PURE__ */ new Set();
for (const directoryPath of directoryPaths) {
this.storageModel.recurse(directoryPath, (filePath) => {
const stringifiedFileData = this.prepareFileForSending(filePath);
filesToSend.add([stringifiedFileData, filePath]);
});
}
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) => {
@@ -8739,10 +8732,26 @@
const blob = new Blob([encrypted], { type: "text/plain" });
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) => {
const fileContent = this.storageModel.read(filePath);
if (fileContent == null) return "";
const fileData = {
type: "file-data",
path: filePath,
body: fileContent
};
@@ -8771,6 +8780,7 @@
}
};
var FileDataReference = {
type: "file-data",
path: [""],
body: ""
};
Vendored
+1 -1
View File
@@ -1,4 +1,4 @@
const VERSION = "2610 Beta 21.12"
const VERSION = "2610 Beta 21.13"
function processUrl(event) {
const url = event.request.url;
+1 -1
View File
@@ -1 +1 @@
2610 Beta 21.12
2610 Beta 21.13
+40 -1
View File
@@ -24,6 +24,7 @@ export default class FileTransferModel {
// handler managers
readonly fileHandlerManager = new HandlerManager<string>();
readonly transferSizeHandlerManager = new HandlerManager<number>();
readonly readyToSendHandlerManager = new HandlerManager<boolean>();
// general
@@ -105,6 +106,12 @@ export default class FileTransferModel {
readonly handleDecryptedFile = (data: string): void => {
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(
parsed,
FileDataReference,
@@ -126,16 +133,22 @@ export default class FileTransferModel {
directoryPaths: IteratorObject<string[]>,
fileCallback: (path: string) => void,
): void => {
const filesToSend = new Set<[string, string[]]>();
for (const directoryPath of directoryPaths) {
this.storageModel.recurse(directoryPath, (filePath: string[]) => {
const stringifiedFileData =
this.prepareFileForSending(filePath);
filesToSend.add([stringifiedFileData, filePath]);
});
}
this.sendTransferSize(filesToSend.size);
for (const file of filesToSend) {
const [stringifiedFileData, filePath] = file;
this.sendFile(stringifiedFileData);
const pathString: string = StorageModel.pathComponentsToString(
...filePath,
);
fileCallback(pathString);
});
}
};
@@ -158,11 +171,30 @@ export default class FileTransferModel {
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 => {
const fileContent: string | null = this.storageModel.read(filePath);
if (fileContent == null) return "";
const fileData: FileData = {
type: "file-data",
path: filePath,
body: fileContent,
};
@@ -200,11 +232,18 @@ export interface TransferData {
}
export interface FileData {
type: "file-data";
path: string[];
body: string;
}
export interface TransferSize {
type: "transfer-size";
count: number;
}
export const FileDataReference: FileData = {
type: "file-data",
path: [""],
body: "",
};
+1 -1
View File
@@ -5,7 +5,7 @@ const ENCRYPTION_ALG = "AES-GCM";
// random
export function random(length: number): string {
return cryptoRandomString({length})
return cryptoRandomString({ length });
}
// encrypt
+5 -4
View File
@@ -140,11 +140,12 @@ export function SettingsPage(
<div class="flex-row width-input">
<button
class="width-50"
on:click={
settingsPageViewModel.randomizeKey
}
on:click={settingsPageViewModel.randomizeKey}
>
{coreViewModel.translations.chatPage.settings.randomizeKey}
{
coreViewModel.translations.chatPage.settings
.randomizeKey
}
<span class="icon">shuffle</span>
</button>
<button
+4 -4
View File
@@ -65,10 +65,10 @@ export function TaskPage(
class="no-outline"
bind:value={taskPageViewModel.boardQuery}
on:enter={taskPageViewModel.createBoard}
placeholder={
coreViewModel.translations.general
.filterOrCreateLabel(coreViewModel.translations.chatPage.task.typeBoard)
}
placeholder={coreViewModel.translations.general.filterOrCreateLabel(
coreViewModel.translations.chatPage.task
.typeBoard,
)}
></input>
</div>
</div>
+24
View File
@@ -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>
);
}
+36 -31
View File
@@ -1,6 +1,7 @@
import { ViewController } from "../viewController";
import { CommonKeys, getKeySymbol } from "../keystrokes";
import { StringToTextSpan } from "../Components/textSpan";
import { ProgressBar } from "../Components/progressBar";
import * as React from "../../react";
import FileTransferViewModel, {
FileTransferModals,
@@ -370,6 +371,18 @@ function TransferDisplayModal(
FileTransferModals.TransferDisplay,
);
const progressLabel = React.createProxyState(
[
fileTransferViewModel.fileTransferProgress,
fileTransferViewModel.fileTransferTotal,
],
() =>
coreViewModel.translations.dataTransferModal.filesSentProgress(
fileTransferViewModel.fileTransferProgress.value,
fileTransferViewModel.fileTransferTotal.value,
),
);
return (
<div class="modal" toggle:open={isPresented}>
<div>
@@ -381,24 +394,11 @@ function TransferDisplayModal(
}
</h2>
<p
class="secondary"
subscribe:innerText={
fileTransferViewModel.filesSentText
}
></p>
<p
class="secondary"
toggle:hidden={
fileTransferViewModel.didNotFinishSending
}
>
{
coreViewModel.translations.dataTransferModal
.allFilesSent
}
</p>
{ProgressBar(
progressLabel,
fileTransferViewModel.fileTransferProgress,
fileTransferViewModel.fileTransferTotal,
)}
<hr></hr>
@@ -460,13 +460,7 @@ function TransferDisplayModal(
}
<span class="icon">restart_alt</span>
</button>
<button
class="flex"
on:click={fileTransferViewModel.close}
toggle:disabled={
fileTransferViewModel.didNotFinishSending
}
>
<button class="flex" on:click={fileTransferViewModel.close}>
{coreViewModel.translations.general.closeButton}
<span class="icon">close</span>
</button>
@@ -588,6 +582,18 @@ function DataReceptionModal(
FileTransferModals.ReceptionDisplay,
);
const progressLabel = React.createProxyState(
[
fileTransferViewModel.fileTransferProgress,
fileTransferViewModel.fileTransferTotal,
],
() =>
coreViewModel.translations.dataTransferModal.filesReceivedProgress(
fileTransferViewModel.fileTransferProgress.value,
fileTransferViewModel.fileTransferTotal.value,
),
);
return (
<div class="modal" toggle:open={isPresented}>
<div>
@@ -599,12 +605,11 @@ function DataReceptionModal(
}
</h2>
<p
class="secondary"
subscribe:innerText={
fileTransferViewModel.filesReceivedText
}
></p>
{ProgressBar(
progressLabel,
fileTransferViewModel.fileTransferProgress,
fileTransferViewModel.fileTransferTotal,
)}
<hr></hr>
+21 -12
View File
@@ -1,5 +1,6 @@
import "./homePage.css";
import { StringToOption } from "./Components/option";
import { NewItemEntry } from "./Components/newItemEntry";
import { HomePageButton } from "./Components/homePageButton";
import { ChatViewModelToChatEntry } from "./Components/chatEntry";
import * as React from "../react";
@@ -9,7 +10,6 @@ import FileTransferViewModel from "../ViewModel/Global/fileTransferViewModel";
import CoreViewModel from "../ViewModel/Global/coreViewModel";
import ConnectionViewModel from "../ViewModel/Global/connectionViewModel";
import ChatListViewModel from "../ViewModel/Chat/chatListViewModel";
import {NewItemEntry} from "./Components/newItemEntry";
export function HomePage(
coreViewModel: CoreViewModel,
@@ -19,9 +19,18 @@ export function HomePage(
fileTransferViewModel: FileTransferViewModel,
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
const overviewSection = (
@@ -128,10 +137,9 @@ export function HomePage(
<div id="chat-section">
<div class="flex-row width-input">
<input
placeholder={
coreViewModel.translations.general
.filterOrCreateLabel(coreViewModel.translations.homePage.typeChat)
}
placeholder={coreViewModel.translations.general.filterOrCreateLabel(
coreViewModel.translations.homePage.typeChat,
)}
bind:value={chatListViewModel.chatQuery}
on:enter={chatListViewModel.createChat}
></input>
@@ -139,12 +147,13 @@ export function HomePage(
<hr></hr>
{NewItemEntry(coreViewModel, chatListViewModel.chatQuery, chatListViewModel.createChat)}
{NewItemEntry(
coreViewModel,
chatListViewModel.chatQuery,
chatListViewModel.createChat,
)}
<span
class="secondary"
toggle:hidden={isChatPlaceholderHidden}
>
<span class="secondary" toggle:hidden={isChatPlaceholderHidden}>
{coreViewModel.translations.homePage.noChatsPlaceholder}
</span>
+14 -13
View File
@@ -181,10 +181,10 @@ const englishTranslations = {
///
filesSentCount: (count: number) => `Files sent: ${count}.`,
allFilesSent: "Done.",
filesReceivedCount: (count: number) => `Files processed: ${count}.`,
filesSentProgress: (progress: number, total: number) =>
`Sent ${progress} of ${total} files`,
filesReceivedProgress: (progress: number, total: number) =>
`Received ${progress} of ${total} files`,
//
@@ -402,7 +402,8 @@ export const allTranslations: {
saveButton: "Speichern",
setButton: "OK",
filterOrCreateLabel: (type: string) => `${type} suchen oder erstellen`,
filterOrCreateLabel: (type: string) =>
`${type} suchen oder erstellen`,
createLabel: (query: string) => `"${query}" erstellen`,
reloadAppButton: "Neu laden",
@@ -550,10 +551,10 @@ export const allTranslations: {
sendButton: "Senden",
sendAgainButton: "Erneut senden",
filesSentCount: (count) => `Dateien gesendet: ${count}.`,
allFilesSent: "Fertig.",
filesReceivedCount: (count) => `Dateien empfangen: ${count}.`,
filesSentProgress: (progress: number, total: number) =>
`${progress} von ${total} Dateien gesendet`,
filesReceivedProgress: (progress: number, total: number) =>
`${progress} von ${total} Dateien empfangen`,
exportKey: "Schlüssel",
exportKeyConfirmation: "Schlüssel bestätigen",
@@ -894,10 +895,10 @@ export const allTranslations: {
sendButton: "Enviar",
sendAgainButton: "Enviar otra vez",
filesSentCount: (count) => `Archivos enviados: ${count}.`,
allFilesSent: "Hecho.",
filesReceivedCount: (count) => `Archivos recibidos: ${count}.`,
filesSentProgress: (progress: number, total: number) =>
`${progress} de ${total} enviados`,
filesReceivedProgress: (progress: number, total: number) =>
`${progress} de ${total} recibidos`,
exportKey: "Clave de encriptación",
exportKeyConfirmation: "Confirmar clave",
+7 -4
View File
@@ -35,9 +35,7 @@ export default class ChatListViewModel {
// methods
createChat = (): void => {
const chatModel: ChatModel =
this.coreViewModel.chatListModel.createChat(
this.chatQuery.value,
);
this.coreViewModel.chatListModel.createChat(this.chatQuery.value);
this.chatQuery.value = "";
const chatViewModel: ChatViewModel =
@@ -109,7 +107,12 @@ export default class ChatListViewModel {
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
+3 -2
View File
@@ -57,14 +57,15 @@ export default class CoreViewModel {
while (this.contexts.length > 0) {
const currentContext: Context | undefined = this.context;
if (!currentContext) break;
const response = currentContext.handleContextClose(fromHistoryEvent);
const response =
currentContext.handleContextClose(fromHistoryEvent);
if (response == false) break;
this.contextStack.delete(currentContext.contextId);
if (response == true) {
exitNext = true;
continue;
} else if (exitNext == true) {
break
break;
} else if (currentContext.contextId == contextId) {
break;
}
+9 -23
View File
@@ -11,7 +11,7 @@ import ChatModel from "../../Model/Chat/chatModel";
export default class FileTransferViewModel extends Context {
// state
exitReception: () => void | undefined = undefined;
exitReception: (() => void) | undefined = undefined;
presentedModal: React.State<FileTransferModals | undefined> =
new React.State<any>(undefined);
@@ -29,30 +29,14 @@ export default class FileTransferViewModel extends Context {
receivingTransferKey: React.State<string> = new React.State("");
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();
filesReceivedCount: React.State<number> = React.createProxyState(
[this.filePathsReceived],
() => 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("");
exportKeyConfirmation: React.State<string> = new React.State("");
@@ -69,7 +53,6 @@ export default class FileTransferViewModel extends Context {
[this.selectedPaths],
() => this.selectedPaths.value.size == 0,
);
didNotFinishSending: React.State<boolean> = new React.State(true);
cannotPrepareToReceive: React.State<boolean> = React.createProxyState(
[this.receivingTransferChannel, this.receivingTransferKey],
() =>
@@ -99,6 +82,7 @@ export default class FileTransferViewModel extends Context {
// handlers
handleReceivedFile = (path: string): void => {
this.filePathsReceived.add(path);
this.fileTransferProgress.value = this.filePathsReceived.value.size;
};
// methods
@@ -229,7 +213,6 @@ export default class FileTransferViewModel extends Context {
initiateTransfer = (): void => {
this.presentedModal.value = FileTransferModals.TransferDisplay;
this.didNotFinishSending.value = true;
this.filePathsSent.clear();
this.coreViewModel.fileTransferModel.sendFiles(
@@ -238,8 +221,6 @@ export default class FileTransferViewModel extends Context {
this.filePathsSent.add(path);
},
);
this.didNotFinishSending.value = false;
};
showTransferDataInputModal = (): void => {
@@ -287,6 +268,11 @@ export default class FileTransferViewModel extends Context {
constructor(public readonly coreViewModel: CoreViewModel) {
super("file-transfer");
this.coreViewModel.fileTransferModel.transferSizeHandlerManager.setHandler(
"file-transfer-view-model",
(count) => (this.fileTransferTotal.value = count),
);
this.coreViewModel.fileTransferModel.fileHandlerManager.setHandler(
"file-transfer-view-model",
this.handleReceivedFile,
+2 -2
View File
@@ -204,7 +204,7 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
resetPinchZoom = (): void => {
this.pinchToZoomData.value = { x: 0, y: 0, zoom: 1 };
}
};
// load
preloadData = (): void => {
@@ -318,7 +318,7 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
this.searchViewModel.appliedQuery.subscribeSilent(() => {
this.resetPinchZoom();
})
});
// handlers
boardsAndTasksModel.taskHandlerManager.setHandler(
+1 -1
View File
@@ -224,7 +224,7 @@ export default class CalendarPageViewModel extends TaskContainingPageViewModel {
handleContextClose = (fromHistoryEvent: boolean): void | boolean => {
if (!fromHistoryEvent) return;
return true;
}
};
// init
constructor(
+1 -2
View File
@@ -1,10 +1,9 @@
import * as React from "../../react";
import StorageViewModel from "../Global/storageViewModel";
import SettingsViewModel from "../Global/settingsViewModel";
import FileTransferViewModel from "../Global/fileTransferViewModel";
import CoreViewModel, { Context } from "../Global/coreViewModel";
import ConnectionViewModel from "../Global/connectionViewModel";
import * as React from "../../react";
import { CommonKeys } from "../../View/keystrokes";
export default class HomeViewModel extends Context {
+2 -2
View File
@@ -197,14 +197,14 @@ export default class MessagePageViewModel extends Context {
};
// context
handleContextClose = (fromHistoryEvent: boolean):boolean=> {
handleContextClose = (fromHistoryEvent: boolean): boolean | void => {
if (fromHistoryEvent) {
if (this.replyViewSelectedMessage.value == undefined) return true;
this.resetReplyView();
return false;
}
}
};
// init
constructor(
+2 -3
View File
@@ -76,8 +76,7 @@ export default class SettingsPageViewModel extends Context {
randomizeKey = (): void => {
this.encryptionKeyInput.value = random(24);
this.setEncryptionKey();
}
};
applyColor = (): void => {
this.chatViewModel.setColor(this.color.value);
@@ -125,7 +124,7 @@ export default class SettingsPageViewModel extends Context {
handleContextClose = (fromHistoryEvent: boolean): void | boolean => {
if (!fromHistoryEvent) return;
return true;
}
};
// init
constructor(
+1 -1
View File
@@ -170,7 +170,7 @@ export default class TaskPageViewModel extends ContextHost<string> {
handleContextClose = (fromHistoryEvent: boolean): void | boolean => {
if (!fromHistoryEvent) return;
return true;
}
};
// init
constructor(
+2 -2
View File
@@ -30,7 +30,7 @@ main {
z-index: -1;
pointer-events: none;
opacity: .3;
opacity: 0.3;
}
#background-wrapper > div > div {
@@ -143,6 +143,6 @@ body[theme="dark"] #background-wrapper #grass-2 {
}
body[keystroke-active] [ctkeystroke]::after {
opacity: .7;
opacity: 0.7;
transform: translateY(0px);
}