From 22acf7f7d8997484851a64427c655e4649367165 Mon Sep 17 00:00:00 2001 From: marlon-erler Date: Mon, 28 Sep 2026 15:21:50 +0200 Subject: [PATCH] move name to settings modal --- dist/index.js | 325 ++++++++++++-------- dist/sw.js | 2 +- dist/version.txt | 2 +- src/Model/Chat/chatListModel.ts | 8 +- src/Model/Chat/chatModel.ts | 26 +- src/Model/Global/connectionModel.ts | 10 +- src/Model/Global/contactListModel.ts | 40 ++- src/Model/Global/settingsModel.ts | 88 +++--- src/Model/Global/storageModel.ts | 110 +++---- src/View/Components/chatMessage.tsx | 8 +- src/View/Components/inlineReply.tsx | 6 +- src/View/Components/replyPreview.tsx | 41 +-- src/View/Modals/settingsModal.tsx | 72 ++++- src/View/homePage.tsx | 33 -- src/View/translations.ts | 75 +++-- src/ViewModel/Chat/chatListViewModel.ts | 6 +- src/ViewModel/Chat/chatMessageViewModel.ts | 4 +- src/ViewModel/Chat/chatViewModel.ts | 6 +- src/ViewModel/Global/contactViewModel.ts | 73 +++-- src/ViewModel/Global/settingsViewModel.ts | 207 +++++++------ src/ViewModel/Pages/messagePageViewModel.ts | 181 +++++------ src/index.tsx | 9 +- 22 files changed, 743 insertions(+), 589 deletions(-) diff --git a/dist/index.js b/dist/index.js index 134aa93e..432757a0 100644 --- a/dist/index.js +++ b/dist/index.js @@ -351,9 +351,6 @@ connectAudioLabel: "connect to server", disconnectAudioLabel: "disconnect from server", manageConnectionsAudioLabel: "manage connections", - yourNameLabel: "Your name", - yourNamePlaceholder: "Jane Doe", - setNameButtonAudioLabel: "set name", settingsButton: "Settings", manageStorageButton: "Manage storage", transferDataButton: "Transfer or export data", @@ -369,6 +366,7 @@ settings: { pages: { appearance: "Appearance", + account: "Account", regional: "Language & Region", info: "About CT" }, @@ -379,10 +377,19 @@ system: "Device theme", black: "Black" }, - version: "Version", - language: "Language", - firstDayOfWeekLabel: "First day of week", - checkUpdatesButton: "Check for updates" + account: { + yourNameLabel: "Your name", + yourNamePlaceholder: "Jane Doe", + setNameButtonAudioLabel: "set name" + }, + regional: { + language: "Language", + firstDayOfWeekLabel: "First day of week" + }, + about: { + version: "Version", + checkUpdatesButton: "Check for updates" + } }, connectionModal: { connectionModalHeadline: "Manage Connections", @@ -600,9 +607,6 @@ connectAudioLabel: "mit Server verbinden", disconnectAudioLabel: "vom Server trennen", manageConnectionsAudioLabel: "Verbindungen verwalten", - yourNameLabel: "Dein Name", - yourNamePlaceholder: "Max Mustermann", - setNameButtonAudioLabel: "Name speichern", settingsButton: "Einstellungen", manageStorageButton: "Daten verwalten", transferDataButton: "Daten \xFCbertragen", @@ -617,6 +621,7 @@ settings: { pages: { appearance: "Erscheinungsbild", + account: "Konto", regional: "Sprache & Region", info: "\xDCber CT" }, @@ -627,10 +632,19 @@ system: "Ger\xE4teeinstellung", black: "Schwarz" }, - version: "Version", - language: "Sprache", - firstDayOfWeekLabel: "Erster Wochentag", - checkUpdatesButton: "Nach updates suchen" + account: { + yourNameLabel: "Dein Name", + yourNamePlaceholder: "Max Mustermann", + setNameButtonAudioLabel: "Namen festlegen" + }, + regional: { + language: "Sprache", + firstDayOfWeekLabel: "Erster Wochentag" + }, + about: { + version: "Version", + checkUpdatesButton: "Nach Updates suchen" + } }, connectionModal: { connectionModalHeadline: "Verbindungen verwalten", @@ -829,9 +843,6 @@ connectAudioLabel: "conectar al servidor", disconnectAudioLabel: "desconectar del servidor", manageConnectionsAudioLabel: "gestionar conexiones", - yourNameLabel: "Tu nombre", - yourNamePlaceholder: "Juan P\xE9rez", - setNameButtonAudioLabel: "establecer nombre", settingsButton: "Ajustes", manageStorageButton: "Gestionar almacenamiento", transferDataButton: "Enviar o exportar archivos", @@ -846,6 +857,7 @@ settings: { pages: { appearance: "Aspecto", + account: "Cuenta", regional: "Idioma y Regi\xF3n", info: "Sobre CT" }, @@ -856,10 +868,19 @@ system: "Seg\xFAn dispositivo", black: "Negro" }, - version: "Versi\xF3n", - language: "Idioma", - firstDayOfWeekLabel: "Primer d\xEDa de la semana", - checkUpdatesButton: "Buscar actualizaciones" + account: { + yourNameLabel: "Tu nombre", + yourNamePlaceholder: "Juan P\xE9rez", + setNameButtonAudioLabel: "establecer nombre" + }, + regional: { + language: "Idioma", + firstDayOfWeekLabel: "Primer d\xEDa de la semana" + }, + about: { + version: "Versi\xF3n", + checkUpdatesButton: "Buscar actualisaciones" + } }, connectionModal: { connectionModalHeadline: "Gestionar Conexiones", @@ -1709,7 +1730,7 @@ this.printTree = () => { return stringify(this.storageEntryTree); }; - // upgrade + // upgrade this.upgrade = () => { }; this.initializeTree(); @@ -1862,9 +1883,8 @@ // src/Model/Global/settingsModel.ts var SettingsModel = class _SettingsModel { // init - constructor(storageModel2, contactListModel2) { + constructor(storageModel2) { this.storageModel = storageModel2; - this.contactListModel = contactListModel2; // data this.username = ""; this.userid = ""; @@ -1886,7 +1906,6 @@ this.setName = (newValue) => { this.username = newValue; this.storeSetting("username", newValue); - this.contactListModel.storeContact(this.userid, newValue); }; this.setFirstDayOfWeek = (newValue) => { this.firstDayOfWeek = newValue; @@ -1976,7 +1995,10 @@ () => this.usernameInput.value == "" || this.usernameInput.value == this.coreViewModel.settingsModel.username ); // methods - this.setName = () => { + this.setName = (name) => { + if (typeof name == "string") { + this.usernameInput.value = name; + } this.coreViewModel.settingsModel.setName(this.usernameInput.value); this.username.value = this.coreViewModel.settingsModel.username; this.usernameInput.callSubscriptions(); @@ -2362,6 +2384,58 @@ } }; + // src/ViewModel/Global/contactViewModel.ts + var ContactListViewModel = class { + // init + constructor(contactListModel2, settingsViewModel2) { + this.contactListModel = contactListModel2; + this.settingsViewModel = settingsViewModel2; + // state + this.contacts = /* @__PURE__ */ new Map(); + // main + this.updateContact = (id, name) => { + if (id == this.settingsViewModel.settingsModel.userid) { + this.settingsViewModel.setName(name); + } + const match = this.contacts.get(id); + if (match) { + match.name.value = name; + return match; + } + const vm = new ContactViewModel(id, name); + this.contacts.set(id, vm); + return vm; + }; + this.handleContact = (contact) => { + this.updateContact(...contact); + }; + this.unwrapContact = (id, knownName) => { + if (this.contacts.has(id)) + return this.contacts.get(id); + return this.updateContact(id, knownName); + }; + // load + this.loadData = () => { + const contacts = this.contactListModel.loadContacts(); + for (const contact of contacts) { + const vm = new ContactViewModel(...contact); + this.contacts.set(vm.id, vm); + } + }; + this.loadData(); + this.contactListModel.contactHandlerManager.setHandler("contact-list-view-model", this.handleContact); + } + }; + var ContactViewModel = class { + // init + constructor(id, name) { + this.id = id; + // state + this.name = new State(""); + this.name.value = name; + } + }; + // src/ViewModel/Global/connectionViewModel.ts var ConnectionViewModel = class { // init @@ -2952,9 +3026,14 @@ // messaging this.addMessage = async (chatMessage) => { await this.decryptMessage(chatMessage); - this.contactListModel.storeContact(chatMessage.senderId, chatMessage.senderName); + this.contactListModel.storeContact( + chatMessage.senderId, + chatMessage.senderName + ); if (chatMessage.body != "") { - const messagePath = this.getMessagePath(chatMessage.fileId); + const messagePath = this.getMessagePath( + chatMessage.fileId + ); this.storageModel.writeStringifiable(messagePath, chatMessage); this.chatMessageHandlerManager.trigger(chatMessage); } @@ -5196,23 +5275,6 @@ "toggle:disabled": connectionViewModel2.cannotConnect }, /* @__PURE__ */ createElement("span", { class: "icon" }, "link") - )), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "account_circle"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, coreViewModel2.translations.homePage.yourNameLabel), /* @__PURE__ */ createElement( - "input", - { - placeholder: coreViewModel2.translations.homePage.yourNamePlaceholder, - "bind:value": settingsViewModel2.usernameInput, - "on:enter": settingsViewModel2.setName - } - ))), /* @__PURE__ */ createElement("div", { class: "flex-row justify-end" }, /* @__PURE__ */ createElement( - "button", - { - class: "width-50", - "on:click": settingsViewModel2.setName, - "toggle:disabled": settingsViewModel2.cannotSetName, - "aria-label": coreViewModel2.translations.homePage.setNameButtonAudioLabel - }, - coreViewModel2.translations.general.setButton, - /* @__PURE__ */ createElement("span", { class: "icon" }, "check") )), /* @__PURE__ */ createElement("hr", null), HomePageButton( settingsViewModel2.showSettingsModal, coreViewModel2.translations.homePage.settingsButton, @@ -6476,7 +6538,13 @@ // src/View/Components/replyPreview.tsx function ReplyPreview(coreViewModel2, chatMessageViewModel) { - return /* @__PURE__ */ createElement("div", { class: "reply-preview" }, /* @__PURE__ */ createElement("div", { class: "surface blur" }, /* @__PURE__ */ createElement("span", { class: "secondary", "subscribe:innerText": chatMessageViewModel.contact.name }), /* @__PURE__ */ createElement( + return /* @__PURE__ */ createElement("div", { class: "reply-preview" }, /* @__PURE__ */ createElement("div", { class: "surface blur" }, /* @__PURE__ */ createElement( + "span", + { + class: "secondary", + "subscribe:innerText": chatMessageViewModel.contact.name + } + ), /* @__PURE__ */ createElement( "b", { class: "ellipsis", @@ -7430,12 +7498,17 @@ coreViewModel2, settingsViewModel2 ); - case 1 /* Regional */: + case 1 /* Account */: + return SettingsAccountPane( + coreViewModel2, + settingsViewModel2 + ); + case 2 /* Regional */: return SettingsRegionalPane( coreViewModel2, settingsViewModel2 ); - case 2 /* Info */: + case 3 /* Info */: return SettingsInfoPane(coreViewModel2, settingsViewModel2); } } @@ -7466,12 +7539,17 @@ ), SettingsPaneButton( coreViewModel2, settingsViewModel2, - 1 /* Regional */, + 1 /* Account */, + coreViewModel2.translations.settings.pages.account + ), SettingsPaneButton( + coreViewModel2, + settingsViewModel2, + 2 /* Regional */, coreViewModel2.translations.settings.pages.regional ), SettingsPaneButton( coreViewModel2, settingsViewModel2, - 2 /* Info */, + 3 /* Info */, coreViewModel2.translations.settings.pages.info )); } @@ -7487,7 +7565,7 @@ function SettingsInfoPane(coreViewModel2, settingsViewModel2) { return /* @__PURE__ */ createElement("div", { class: "slide-up" }, /* @__PURE__ */ createElement("h2", null, coreViewModel2.translations.homePage.appName), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-column gap" }, InfoTile( "build", - coreViewModel2.translations.settings.version, + coreViewModel2.translations.settings.about.version, settingsViewModel2.coreViewModel.version ), /* @__PURE__ */ createElement( "button", @@ -7498,15 +7576,15 @@ }, /* @__PURE__ */ createElement("span", { "subscribe:innerText": coreViewModel2.updateText }), /* @__PURE__ */ createElement("span", { class: "icon" }, "update") - ), /* @__PURE__ */ createElement("button", { class: "standard", "on:click": coreViewModel2.checkUpdates }, coreViewModel2.translations.settings.checkUpdatesButton))); + ), /* @__PURE__ */ createElement("button", { class: "standard", "on:click": coreViewModel2.checkUpdates }, coreViewModel2.translations.settings.about.checkUpdatesButton))); } function SettingsRegionalPane(coreViewModel2, settingsViewModel2) { - return /* @__PURE__ */ createElement("div", { class: "slide-up" }, /* @__PURE__ */ createElement("h2", null, coreViewModel2.translations.settings.pages.regional), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("h3", null, coreViewModel2.translations.settings.language), OptionButtonList( + return /* @__PURE__ */ createElement("div", { class: "slide-up" }, /* @__PURE__ */ createElement("h2", null, coreViewModel2.translations.settings.pages.regional), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("h3", null, coreViewModel2.translations.settings.regional.language), OptionButtonList( new ListState( Object.values(Languages).map((x) => [languageNames[x], x]) ), settingsViewModel2.language - ), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("h3", null, coreViewModel2.translations.settings.firstDayOfWeekLabel), OptionButtonList( + ), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("h3", null, coreViewModel2.translations.settings.regional.firstDayOfWeekLabel), OptionButtonList( new ListState( coreViewModel2.translations.regional.weekdays.full.map( (x, i) => [x, i.toString()] @@ -7515,6 +7593,26 @@ settingsViewModel2.firstDayOfWeek )); } + function SettingsAccountPane(coreViewModel2, settingsViewModel2) { + return /* @__PURE__ */ createElement("div", { class: "slide-up" }, /* @__PURE__ */ createElement("h2", null, coreViewModel2.translations.settings.pages.account), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "account_circle"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, coreViewModel2.translations.settings.account.yourNameLabel), /* @__PURE__ */ createElement( + "input", + { + placeholder: coreViewModel2.translations.settings.account.yourNamePlaceholder, + "bind:value": settingsViewModel2.usernameInput, + "on:enter": settingsViewModel2.setName + } + ))), /* @__PURE__ */ createElement("div", { class: "flex-row justify-end width-input" }, /* @__PURE__ */ createElement( + "button", + { + class: "width-50", + "on:click": settingsViewModel2.setName, + "toggle:disabled": settingsViewModel2.cannotSetName, + "aria-label": coreViewModel2.translations.settings.account.setNameButtonAudioLabel + }, + coreViewModel2.translations.general.setButton, + /* @__PURE__ */ createElement("span", { class: "icon" }, "check") + ))); + } function SettingsAppearancePane(coreViewModel2, settingsViewModel2) { return /* @__PURE__ */ createElement("div", { class: "slide-up" }, /* @__PURE__ */ createElement("h2", null, coreViewModel2.translations.settings.pages.appearance), /* @__PURE__ */ createElement("hr", null), OptionButtonList( new ListState([ @@ -8150,6 +8248,37 @@ body: "" }; + // src/Model/Global/contactListModel.ts + var ContactListModel = class { + constructor(storageModel2, settingsModel2) { + this.storageModel = storageModel2; + this.settingsModel = settingsModel2; + this.contactHandlerManager = new HandlerManager(); + this.getContactPath = (contactId) => { + return StorageModel.getPath("contacts" /* ContactsModel */, [ + contactId + ]); + }; + this.loadContacts = () => { + const contactIds = this.storageModel.list([ + "contacts" /* ContactsModel */ + ]); + const contacts = contactIds.map((id) => [ + id, + this.storageModel.read(this.getContactPath(id)) ?? "?" + ]); + contacts.push([this.settingsModel.userid, this.settingsModel.username]); + return contacts; + }; + this.storeContact = (id, name) => { + this.contactHandlerManager.trigger([id, name]); + if (id == this.settingsModel.userid) return; + const path = this.getContactPath(id); + this.storageModel.write(path, name); + }; + } + }; + // node_modules/udn-frontend/index.ts var UDNFrontend = class { ws; @@ -8370,11 +8499,17 @@ return chatMessages; }; this.addToOutbox = (chatMessage) => { - const messagePath = [...this.getOutboxPath(), chatMessage.fileId]; + const messagePath = [ + ...this.getOutboxPath(), + chatMessage.fileId + ]; this.storageModel.writeStringifiable(messagePath, chatMessage); }; this.removeFromOutbox = (chatMessage) => { - const messagePath = [...this.getOutboxPath(), chatMessage.fileId]; + const messagePath = [ + ...this.getOutboxPath(), + chatMessage.fileId + ]; this.storageModel.remove(messagePath); }; this.sendMessagesInOutbox = () => { @@ -8564,79 +8699,10 @@ } }; - // src/Model/Global/contactListModel.ts - var ContactListModel = class { - constructor(storageModel2) { - this.storageModel = storageModel2; - this.contactHandlerManager = new HandlerManager(); - this.getContactPath = (contactId) => { - return StorageModel.getPath("contacts" /* ContactsModel */, [contactId]); - }; - this.loadContacts = () => { - const contactIds = this.storageModel.list(["contacts" /* ContactsModel */]); - const contacts = contactIds.map((id) => [id, this.storageModel.read(this.getContactPath(id)) ?? "?"]); - return contacts; - }; - this.storeContact = (id, name) => { - const path = this.getContactPath(id); - this.storageModel.write(path, name); - this.contactHandlerManager.trigger([id, name]); - }; - } - }; - - // src/ViewModel/Global/contactViewModel.ts - var ContactListViewModel = class { - // init - constructor(contactListModel2) { - this.contactListModel = contactListModel2; - // state - this.contacts = /* @__PURE__ */ new Map(); - // main - this.updateContact = (id, name) => { - const match = this.contacts.get(id); - if (match) { - match.name.value = name; - return match; - } - const vm = new ContactViewModel(id, name); - this.contacts.set(id, vm); - return vm; - }; - this.handleContact = (contact) => { - this.updateContact(...contact); - }; - this.unwrapContact = (id, knownName) => { - if (this.contacts.has(id)) - return this.contacts.get(id); - return this.updateContact(id, knownName); - }; - // load - this.loadData = () => { - const contacts = this.contactListModel.loadContacts(); - for (const contact of contacts) { - const vm = new ContactViewModel(...contact); - this.contacts.set(vm.id, vm); - } - }; - this.loadData(); - this.contactListModel.contactHandlerManager.setHandler("contact-list-view-model", this.handleContact); - } - }; - var ContactViewModel = class { - // init - constructor(id, name) { - this.id = id; - // state - this.name = new State(""); - this.name.value = name; - } - }; - // src/index.tsx var storageModel = new StorageModel(); - var contactListModel = new ContactListModel(storageModel); - var settingsModel = new SettingsModel(storageModel, contactListModel); + var settingsModel = new SettingsModel(storageModel); + var contactListModel = new ContactListModel(storageModel, settingsModel); var connectionModel = new ConnectionModel(storageModel); var chatListModel = new ChatListModel( storageModel, @@ -8653,8 +8719,11 @@ fileTransferModel ); var storageViewModel = new StorageViewModel(coreViewModel); - var contactListViewModel = new ContactListViewModel(contactListModel); var settingsViewModel = new SettingsViewModel(coreViewModel, settingsModel); + var contactListViewModel = new ContactListViewModel( + contactListModel, + settingsViewModel + ); var connectionViewModel = new ConnectionViewModel(coreViewModel); var chatListViewModel = new ChatListViewModel( coreViewModel, diff --git a/dist/sw.js b/dist/sw.js index e36842af..364f3e90 100644 --- a/dist/sw.js +++ b/dist/sw.js @@ -1,4 +1,4 @@ -const VERSION = "2610 Beta 14" +const VERSION = "2610 Beta 16" function processUrl(event) { const url = event.request.url; diff --git a/dist/version.txt b/dist/version.txt index f57140b9..28920a99 100644 --- a/dist/version.txt +++ b/dist/version.txt @@ -1 +1 @@ -2610 Beta 14 +2610 Beta 16 diff --git a/src/Model/Chat/chatListModel.ts b/src/Model/Chat/chatListModel.ts index e7e6de74..98707d4f 100644 --- a/src/Model/Chat/chatListModel.ts +++ b/src/Model/Chat/chatListModel.ts @@ -8,8 +8,8 @@ import StorageModel, { filePaths, } from "../Global/storageModel"; import SettingsModel from "../Global/settingsModel"; -import ConnectionModel from "../Global/connectionModel"; import ContactListModel from "../Global/contactListModel"; +import ConnectionModel from "../Global/connectionModel"; export default class ChatListModel { // data @@ -28,7 +28,7 @@ export default class ChatListModel { this.connectionModel, this.settingsModel, this, - this.contactListModel, + this.contactListModel, id, ); chatModel.setName(primaryChannel); @@ -90,7 +90,7 @@ export default class ChatListModel { this.connectionModel, this.settingsModel, this, - this.contactListModel, + this.contactListModel, chatId, ); this.addChatModel(chatModel); @@ -102,7 +102,7 @@ export default class ChatListModel { public storageModel: StorageModel, public settingsModel: SettingsModel, public connectionModel: ConnectionModel, - public contactListModel: ContactListModel + public contactListModel: ContactListModel, ) { this.loadChats(); diff --git a/src/Model/Chat/chatModel.ts b/src/Model/Chat/chatModel.ts index b78d39be..f04165fa 100644 --- a/src/Model/Chat/chatModel.ts +++ b/src/Model/Chat/chatModel.ts @@ -18,10 +18,10 @@ import StorageModel, { filePaths, } from "../Global/storageModel"; import SettingsModel from "../Global/settingsModel"; +import ContactListModel from "../Global/contactListModel"; import ConnectionModel from "../Global/connectionModel"; import FileModel, { FileContent } from "../Files/fileModel"; import { Colors } from "../../colors"; -import ContactListModel from "../Global/contactListModel"; export default class ChatModel { // models @@ -162,11 +162,16 @@ export default class ChatModel { readonly addMessage = async (chatMessage: ChatMessage): Promise => { await this.decryptMessage(chatMessage); - this.contactListModel.storeContact(chatMessage.senderId, chatMessage.senderName); + this.contactListModel.storeContact( + chatMessage.senderId, + chatMessage.senderName, + ); // message if (chatMessage.body != "") { - const messagePath: string[] = this.getMessagePath(chatMessage.fileId); + const messagePath: string[] = this.getMessagePath( + chatMessage.fileId, + ); this.storageModel.writeStringifiable(messagePath, chatMessage); this.chatMessageHandlerManager.trigger(chatMessage); } @@ -238,7 +243,7 @@ export default class ChatModel { const reaction = ChatModel.createMessageReaction( messageId, senderId, - senderName, + senderName, content, isDeleting, ); @@ -350,7 +355,7 @@ export default class ChatModel { public connectionModel: ConnectionModel, public settingsModel: SettingsModel, public chatListModel: ChatListModel, - public contactListModel: ContactListModel, + public contactListModel: ContactListModel, chatId: string, ) { this.id = chatId; @@ -397,10 +402,10 @@ export default class ChatModel { inlineReplyId?: string, fileContent?: FileContent, ): Promise => { - const messageFileContent = FileModel.createFileContent(v4(), "message"); + const messageFileContent = FileModel.createFileContent(v4(), "message"); const chatMessage: ChatMessage = { - ...messageFileContent, + ...messageFileContent, channel, senderName, @@ -446,7 +451,7 @@ export default class ChatModel { messageId, senderId, - senderName, + senderName, content, isDeleting, }; @@ -501,8 +506,7 @@ export interface ChatMessage extends FileContent<"message"> { stringifiedFile: string; } -export interface ChatMessageReaction - extends FileContent<"reaction"> { +export interface ChatMessageReaction extends FileContent<"reaction"> { readonly messageId: string; readonly senderId: string; readonly senderName: string; @@ -531,7 +535,7 @@ export const ChatInfoReference: ChatInfoFileContent = { export const ChatMessageReference: ChatMessage = { dataVersion: DATA_VERSION, - + fileId: "", fileContentId: "", creationDate: "", diff --git a/src/Model/Global/connectionModel.ts b/src/Model/Global/connectionModel.ts index f4283d54..42911d4e 100644 --- a/src/Model/Global/connectionModel.ts +++ b/src/Model/Global/connectionModel.ts @@ -149,12 +149,18 @@ export default class ConnectionModel { }; readonly addToOutbox = (chatMessage: ChatMessage): void => { - const messagePath: string[] = [...this.getOutboxPath(), chatMessage.fileId]; + const messagePath: string[] = [ + ...this.getOutboxPath(), + chatMessage.fileId, + ]; this.storageModel.writeStringifiable(messagePath, chatMessage); }; readonly removeFromOutbox = (chatMessage: ChatMessage): void => { - const messagePath: string[] = [...this.getOutboxPath(), chatMessage.fileId]; + const messagePath: string[] = [ + ...this.getOutboxPath(), + chatMessage.fileId, + ]; this.storageModel.remove(messagePath); }; diff --git a/src/Model/Global/contactListModel.ts b/src/Model/Global/contactListModel.ts index d33ac7dd..b602b48b 100644 --- a/src/Model/Global/contactListModel.ts +++ b/src/Model/Global/contactListModel.ts @@ -1,28 +1,38 @@ -import {HandlerManager} from "../Utility/utility"; +import StorageModel, { StorageModelSubPaths } from "./storageModel"; import SettingsModel from "./settingsModel"; -import StorageModel, {StorageModelSubPaths} from "./storageModel"; +import { HandlerManager } from "../Utility/utility"; export default class ContactListModel { contactHandlerManager = new HandlerManager<[string, string]>(); private getContactPath = (contactId: string): string[] => { - return StorageModel.getPath(StorageModelSubPaths.ContactsModel, [contactId]); - } + return StorageModel.getPath(StorageModelSubPaths.ContactsModel, [ + contactId, + ]); + }; loadContacts = (): [string, string][] => { - const contactIds: string[] = this.storageModel.list([StorageModelSubPaths.ContactsModel]); - const contacts: [string, string][] = contactIds.map(id => [id, this.storageModel.read(this.getContactPath(id)) ?? "?"]); - contacts.push([this.settingsModel.userid, this.settingsModel.username]); - return contacts; - } + const contactIds: string[] = this.storageModel.list([ + StorageModelSubPaths.ContactsModel, + ]); + const contacts: [string, string][] = contactIds.map((id) => [ + id, + this.storageModel.read(this.getContactPath(id)) ?? "?", + ]); + contacts.push([this.settingsModel.userid, this.settingsModel.username]); + return contacts; + }; storeContact = (id: string, name: string) => { - this.contactHandlerManager.trigger([id, name]); + this.contactHandlerManager.trigger([id, name]); - if (id == this.settingsModel.userid) return; - const path = this.getContactPath(id); - this.storageModel.write(path, name) - } + if (id == this.settingsModel.userid) return; + const path = this.getContactPath(id); + this.storageModel.write(path, name); + }; - constructor(public storageModel: StorageModel, public settingsModel: SettingsModel) {} + constructor( + public storageModel: StorageModel, + public settingsModel: SettingsModel, + ) {} } diff --git a/src/Model/Global/settingsModel.ts b/src/Model/Global/settingsModel.ts index e1a904f0..f64c0573 100644 --- a/src/Model/Global/settingsModel.ts +++ b/src/Model/Global/settingsModel.ts @@ -28,93 +28,93 @@ export default class SettingsModel { readonly setName = (newValue: string): void => { this.username = newValue; - this.storeSetting("username", newValue); + this.storeSetting("username", newValue); }; readonly setFirstDayOfWeek = (newValue: string): void => { - this.firstDayOfWeek = newValue; - this.storeSetting("firstDayOfWeek", newValue); + this.firstDayOfWeek = newValue; + this.storeSetting("firstDayOfWeek", newValue); }; readonly setLanguage = (newValue: string): void => { - this.language = newValue; - this.storeSetting("language", newValue); + this.language = newValue; + this.storeSetting("language", newValue); }; readonly setTheme = (newValue: string): void => { - this.theme = newValue; - this.storeSetting("theme", newValue); + this.theme = newValue; + this.storeSetting("theme", newValue); }; // load private readonly readSetting = ( - pathName: keyof typeof filePaths.settingsModel, + pathName: keyof typeof filePaths.settingsModel, ): string | null => { - const path = StorageModel.getPath( - StorageModelSubPaths.SettingsModel, - filePaths.settingsModel[pathName], - ); - return this.storageModel.read(path); + const path = StorageModel.getPath( + StorageModelSubPaths.SettingsModel, + filePaths.settingsModel[pathName], + ); + return this.storageModel.read(path); }; readonly loadId = (): void => { - const content: string | null = this.readSetting("userid"); - const id = content ?? UUID(); - if (content) this.userid = content; - else this.setId(id); + const content: string | null = this.readSetting("userid"); + const id = content ?? UUID(); + if (content) this.userid = content; + else this.setId(id); }; readonly loadUsername = (): void => { - const content: string | null = this.readSetting("username"); - this.username = content ?? ""; + const content: string | null = this.readSetting("username"); + this.username = content ?? ""; }; readonly loadFirstDayofWeek = (): void => { - const content: string | null = this.readSetting("firstDayOfWeek"); - this.firstDayOfWeek = content ?? "0"; + const content: string | null = this.readSetting("firstDayOfWeek"); + this.firstDayOfWeek = content ?? "0"; }; readonly loadLanguage = (): void => { - const content: string | null = this.readSetting("language"); - this.language = content ?? SettingsModel.getSystemLanguage(); + const content: string | null = this.readSetting("language"); + this.language = content ?? SettingsModel.getSystemLanguage(); }; readonly loadTheme = (): void => { - const content: string | null = this.readSetting("theme"); - this.theme = content ?? ThemeSettings.System; + const content: string | null = this.readSetting("theme"); + this.theme = content ?? ThemeSettings.System; }; // init constructor(public storageModel: StorageModel) { - this.loadId(); - this.loadUsername(); - this.loadFirstDayofWeek(); - this.loadLanguage(); - this.loadTheme(); + this.loadId(); + this.loadUsername(); + this.loadFirstDayofWeek(); + this.loadLanguage(); + this.loadTheme(); } static getSystemLanguage(): Languages { - switch (navigator.language.substring(0, 2)) { - case "de": - return Languages.German; - case "es": - return Languages.Spanish; - default: - return Languages.English; - } + switch (navigator.language.substring(0, 2)) { + case "de": + return Languages.German; + case "es": + return Languages.Spanish; + default: + return Languages.English; + } } } export enum Languages { English = "en", - German = "de", - Spanish = "es", + German = "de", + Spanish = "es", } export enum ThemeSettings { Dark = "dark", - Light = "light", - System = "system", - Dynamic = "dynamic", - Black = "black", + Light = "light", + System = "system", + Dynamic = "dynamic", + Black = "black", } diff --git a/src/Model/Global/storageModel.ts b/src/Model/Global/storageModel.ts index 49cda2bc..5ec25c29 100644 --- a/src/Model/Global/storageModel.ts +++ b/src/Model/Global/storageModel.ts @@ -216,52 +216,52 @@ export default class StorageModel { return stringify(this.storageEntryTree); }; - // upgrade + // upgrade readonly upgrade = (): void => { - // function used to upgrade data versions - } + // function used to upgrade data versions + }; // init constructor() { - this.initializeTree(); - this.upgrade(); + this.initializeTree(); + this.upgrade(); } // utility static getFileName = (pathComponents: string[]): string => { - return pathComponents[pathComponents.length - 1] || "\\"; + return pathComponents[pathComponents.length - 1] || "\\"; }; static getFileNameFromString = (pathString: string): string => { - const pathComponents: string[] = - this.stringToPathComponents(pathString); - return pathComponents[pathComponents.length - 1] || "\\"; + const pathComponents: string[] = + this.stringToPathComponents(pathString); + return pathComponents[pathComponents.length - 1] || "\\"; }; static pathComponentsToString = (...pathComponents: string[]): string => { - return pathComponents - .filter((x) => x != "") - .join(PATH_COMPONENT_SEPARATOR); + return pathComponents + .filter((x) => x != "") + .join(PATH_COMPONENT_SEPARATOR); }; static stringToPathComponents = (string: string): string[] => { - return string.split(PATH_COMPONENT_SEPARATOR).filter((x) => x != ""); + return string.split(PATH_COMPONENT_SEPARATOR).filter((x) => x != ""); }; static join = (...items: string[]): string => { - let allComponents: string[] = []; - for (const item of items) { - const parts = this.stringToPathComponents(item); - allComponents.push(...parts); - } - return StorageModel.pathComponentsToString(...allComponents); + let allComponents: string[] = []; + for (const item of items) { + const parts = this.stringToPathComponents(item); + allComponents.push(...parts); + } + return StorageModel.pathComponentsToString(...allComponents); }; static getPath( - locationName: StorageModelSubPaths, - filePath: string[], + locationName: StorageModelSubPaths, + filePath: string[], ): string[] { - return [DATA_VERSION, locationName, ...filePath]; + return [DATA_VERSION, locationName, ...filePath]; } } @@ -272,52 +272,52 @@ export type StorageEntry = { [key: string]: StorageEntry }; export enum StorageModelSubPaths { Chat = "chat", - ConnectionModel = "connection", - NotificationModel = "notifications", - SettingsModel = "settings", - ContactsModel = "contacts", + ConnectionModel = "connection", + NotificationModel = "notifications", + SettingsModel = "settings", + ContactsModel = "contacts", } export const filePaths = { connectionModel: { - socketAddress: ["socket-address"], - reconnectAddress: ["reconnect-address"], - outbox: ["outbox"], - mailboxes: ["mailboxes"], + socketAddress: ["socket-address"], + reconnectAddress: ["reconnect-address"], + outbox: ["outbox"], + mailboxes: ["mailboxes"], - previousAddresses: ["previous-addresses"], + previousAddresses: ["previous-addresses"], }, chat: { - base: [], - chatBase: (id: string) => [id], - info: (id: string) => [...filePaths.chat.chatBase(id), "info"], - color: (id: string) => [...filePaths.chat.chatBase(id), "color"], - messages: (id: string) => [...filePaths.chat.chatBase(id), "messages"], - reactions: (id: string) => [ - ...filePaths.chat.chatBase(id), - "reactions", - ], - previousFilter: (id: string) => [ - ...filePaths.chat.chatBase(id), - "previous-filter", - ], - lastUsedPage: (id: string) => [ - ...filePaths.chat.chatBase(id), - "last-used-page", - ], - files: (id: string) => [...filePaths.chat.chatBase(id), "files"], + base: [], + chatBase: (id: string) => [id], + info: (id: string) => [...filePaths.chat.chatBase(id), "info"], + color: (id: string) => [...filePaths.chat.chatBase(id), "color"], + messages: (id: string) => [...filePaths.chat.chatBase(id), "messages"], + reactions: (id: string) => [ + ...filePaths.chat.chatBase(id), + "reactions", + ], + previousFilter: (id: string) => [ + ...filePaths.chat.chatBase(id), + "previous-filter", + ], + lastUsedPage: (id: string) => [ + ...filePaths.chat.chatBase(id), + "last-used-page", + ], + files: (id: string) => [...filePaths.chat.chatBase(id), "files"], }, notificationModel: { - base: [], + base: [], }, settingsModel: { - username: ["user-name"], - userid: ["user-id"], - firstDayOfWeek: ["first-day-of-week"], - language: ["language"], - theme: ["theme"], + username: ["user-name"], + userid: ["user-id"], + firstDayOfWeek: ["first-day-of-week"], + language: ["language"], + theme: ["theme"], }, }; diff --git a/src/View/Components/chatMessage.tsx b/src/View/Components/chatMessage.tsx index 81a890e0..783a6901 100644 --- a/src/View/Components/chatMessage.tsx +++ b/src/View/Components/chatMessage.tsx @@ -37,10 +37,10 @@ export function ChatMessage( {InlineReply(chatMessageViewModel)}
- +
; @@ -13,7 +13,7 @@ export function InlineReply(chatMessageViewModel: ChatMessageViewModel ) { return (
- +
); diff --git a/src/View/Components/replyPreview.tsx b/src/View/Components/replyPreview.tsx index e2b90496..9c20b5a3 100644 --- a/src/View/Components/replyPreview.tsx +++ b/src/View/Components/replyPreview.tsx @@ -7,24 +7,27 @@ export function ReplyPreview( chatMessageViewModel: ChatMessageViewModel, ) { return ( -
-
- - -
- -
+
+
+ + +
+ +
); } diff --git a/src/View/Modals/settingsModal.tsx b/src/View/Modals/settingsModal.tsx index 72e7f84b..952cde35 100644 --- a/src/View/Modals/settingsModal.tsx +++ b/src/View/Modals/settingsModal.tsx @@ -29,6 +29,11 @@ export function SettingsModal( coreViewModel, settingsViewModel, ); + case SettingsModalPages.Account: + return SettingsAccountPane( + coreViewModel, + settingsViewModel, + ); case SettingsModalPages.Regional: return SettingsRegionalPane( coreViewModel, @@ -79,6 +84,12 @@ function SettingsLeftPane( SettingsModalPages.Appearance, coreViewModel.translations.settings.pages.appearance, )} + {SettingsPaneButton( + coreViewModel, + settingsViewModel, + SettingsModalPages.Account, + coreViewModel.translations.settings.pages.account, + )} {SettingsPaneButton( coreViewModel, settingsViewModel, @@ -121,7 +132,7 @@ function SettingsInfoPane(
{InfoTile( "build", - coreViewModel.translations.settings.version, + coreViewModel.translations.settings.about.version, settingsViewModel.coreViewModel.version, )} @@ -135,7 +146,10 @@ function SettingsInfoPane(
@@ -151,7 +165,7 @@ function SettingsRegionalPane(

{coreViewModel.translations.settings.pages.regional}


-

{coreViewModel.translations.settings.language}

+

{coreViewModel.translations.settings.regional.language}

{OptionButtonList( new React.ListState<[string, string]>( Object.values(Languages).map((x) => [languageNames[x], x]), @@ -160,7 +174,12 @@ function SettingsRegionalPane( )}
-

{coreViewModel.translations.settings.firstDayOfWeekLabel}

+

+ { + coreViewModel.translations.settings.regional + .firstDayOfWeekLabel + } +

{OptionButtonList( new React.ListState<[string, string]>( coreViewModel.translations.regional.weekdays.full.map( @@ -173,6 +192,51 @@ function SettingsRegionalPane( ); } +function SettingsAccountPane( + coreViewModel: CoreViewModel, + settingsViewModel: SettingsViewModel, +) { + return ( +
+

{coreViewModel.translations.settings.pages.account}

+
+ +
+ +
+
+ ); +} + function SettingsAppearancePane( coreViewModel: CoreViewModel, settingsViewModel: SettingsViewModel, diff --git a/src/View/homePage.tsx b/src/View/homePage.tsx index 0d08e24c..a7ba9dca 100644 --- a/src/View/homePage.tsx +++ b/src/View/homePage.tsx @@ -87,39 +87,6 @@ export function HomePage(
- -
- -
- -
- {HomePageButton( settingsViewModel.showSettingsModal, coreViewModel.translations.homePage.settingsButton, diff --git a/src/View/translations.ts b/src/View/translations.ts index fa6ded66..b45ab375 100644 --- a/src/View/translations.ts +++ b/src/View/translations.ts @@ -65,10 +65,6 @@ const englishTranslations = { disconnectAudioLabel: "disconnect from server", manageConnectionsAudioLabel: "manage connections", - yourNameLabel: "Your name", - yourNamePlaceholder: "Jane Doe", - setNameButtonAudioLabel: "set name", - settingsButton: "Settings", manageStorageButton: "Manage storage", transferDataButton: "Transfer or export data", @@ -90,6 +86,7 @@ const englishTranslations = { settings: { pages: { appearance: "Appearance", + account: "Account", regional: "Language & Region", info: "About CT", }, @@ -100,13 +97,19 @@ const englishTranslations = { system: "Device theme", black: "Black", }, - - version: "Version", - language: "Language", - - firstDayOfWeekLabel: "First day of week", - - checkUpdatesButton: "Check for updates", + account: { + yourNameLabel: "Your name", + yourNamePlaceholder: "Jane Doe", + setNameButtonAudioLabel: "set name", + }, + regional: { + language: "Language", + firstDayOfWeekLabel: "First day of week", + }, + about: { + version: "Version", + checkUpdatesButton: "Check for updates", + }, }, connectionModal: { @@ -414,10 +417,6 @@ export const allTranslations: { disconnectAudioLabel: "vom Server trennen", manageConnectionsAudioLabel: "Verbindungen verwalten", - yourNameLabel: "Dein Name", - yourNamePlaceholder: "Max Mustermann", - setNameButtonAudioLabel: "Name speichern", - settingsButton: "Einstellungen", manageStorageButton: "Daten verwalten", transferDataButton: "Daten übertragen", @@ -437,6 +436,7 @@ export const allTranslations: { settings: { pages: { appearance: "Erscheinungsbild", + account: "Konto", regional: "Sprache & Region", info: "Über CT", }, @@ -447,13 +447,19 @@ export const allTranslations: { system: "Geräteeinstellung", black: "Schwarz", }, - - version: "Version", - language: "Sprache", - - firstDayOfWeekLabel: "Erster Wochentag", - - checkUpdatesButton: "Nach updates suchen", + account: { + yourNameLabel: "Dein Name", + yourNamePlaceholder: "Max Mustermann", + setNameButtonAudioLabel: "Namen festlegen", + }, + regional: { + language: "Sprache", + firstDayOfWeekLabel: "Erster Wochentag", + }, + about: { + version: "Version", + checkUpdatesButton: "Nach Updates suchen", + }, }, connectionModal: { @@ -729,10 +735,6 @@ export const allTranslations: { disconnectAudioLabel: "desconectar del servidor", manageConnectionsAudioLabel: "gestionar conexiones", - yourNameLabel: "Tu nombre", - yourNamePlaceholder: "Juan Pérez", - setNameButtonAudioLabel: "establecer nombre", - settingsButton: "Ajustes", manageStorageButton: "Gestionar almacenamiento", transferDataButton: "Enviar o exportar archivos", @@ -752,6 +754,7 @@ export const allTranslations: { settings: { pages: { appearance: "Aspecto", + account: "Cuenta", regional: "Idioma y Región", info: "Sobre CT", }, @@ -762,13 +765,19 @@ export const allTranslations: { system: "Según dispositivo", black: "Negro", }, - - version: "Versión", - language: "Idioma", - - firstDayOfWeekLabel: "Primer día de la semana", - - checkUpdatesButton: "Buscar actualizaciones", + account: { + yourNameLabel: "Tu nombre", + yourNamePlaceholder: "Juan Pérez", + setNameButtonAudioLabel: "establecer nombre", + }, + regional: { + language: "Idioma", + firstDayOfWeekLabel: "Primer día de la semana", + }, + about: { + version: "Versión", + checkUpdatesButton: "Buscar actualisaciones", + }, }, connectionModal: { connectionModalHeadline: "Gestionar Conexiones", diff --git a/src/ViewModel/Chat/chatListViewModel.ts b/src/ViewModel/Chat/chatListViewModel.ts index 1f6465a6..9e2a7b5a 100644 --- a/src/ViewModel/Chat/chatListViewModel.ts +++ b/src/ViewModel/Chat/chatListViewModel.ts @@ -2,11 +2,11 @@ import ChatViewModel from "./chatViewModel"; import SettingsViewModel from "../Global/settingsViewModel"; import NotificationViewModel from "../Global/notificationViewModel"; import CoreViewModel from "../Global/coreViewModel"; +import ContactListViewModel from "../Global/contactViewModel"; import ConnectionViewModel from "../Global/connectionViewModel"; import * as React from "../../react"; import { IndexManager } from "../../Model/Utility/utility"; import ChatModel from "../../Model/Chat/chatModel"; -import ContactListViewModel from "../Global/contactViewModel"; export default class ChatListViewModel { notificationViewModel: NotificationViewModel; @@ -62,7 +62,7 @@ export default class ChatListViewModel { this.notificationViewModel, this.connectionViewModel, this, - this.contactListViewModel, + this.contactListViewModel, ); }; @@ -99,7 +99,7 @@ export default class ChatListViewModel { public readonly coreViewModel: CoreViewModel, public readonly settingsViewModel: SettingsViewModel, public readonly connectionViewModel: ConnectionViewModel, - public readonly contactListViewModel: ContactListViewModel, + public readonly contactListViewModel: ContactListViewModel, ) { this.notificationViewModel = new NotificationViewModel(this); diff --git a/src/ViewModel/Chat/chatMessageViewModel.ts b/src/ViewModel/Chat/chatMessageViewModel.ts index 124f1165..dcee3ef8 100644 --- a/src/ViewModel/Chat/chatMessageViewModel.ts +++ b/src/ViewModel/Chat/chatMessageViewModel.ts @@ -1,5 +1,6 @@ import MessagePageViewModel from "../Pages/messagePageViewModel"; import CoreViewModel from "../Global/coreViewModel"; +import { ContactViewModel } from "../Global/contactViewModel"; import * as React from "../../react"; import { ChatMessage, @@ -7,7 +8,6 @@ import { ChatMessageStatuses, ReactionSymbols, } from "../../Model/Chat/chatModel"; -import {ContactViewModel} from "../Global/contactViewModel"; export default class ChatMessageViewModel { // data @@ -203,7 +203,7 @@ export default class ChatMessageViewModel { public readonly coreViewModel: CoreViewModel, public readonly messagePageViewModel: MessagePageViewModel, chatMessage: ChatMessage, - public contact: ContactViewModel, + public contact: ContactViewModel, sentByUser: boolean, ) { this.chatMessage = chatMessage; diff --git a/src/ViewModel/Chat/chatViewModel.ts b/src/ViewModel/Chat/chatViewModel.ts index c2eb43c0..f37b0122 100644 --- a/src/ViewModel/Chat/chatViewModel.ts +++ b/src/ViewModel/Chat/chatViewModel.ts @@ -8,6 +8,7 @@ import CalendarPageViewModel, { import SettingsViewModel from "../Global/settingsViewModel"; import NotificationViewModel from "../Global/notificationViewModel"; import CoreViewModel, { Context, ContextHost } from "../Global/coreViewModel"; +import ContactListViewModel from "../Global/contactViewModel"; import ConnectionViewModel from "../Global/connectionViewModel"; import * as React from "../../react"; import { Colors } from "../../colors"; @@ -21,7 +22,6 @@ import ChatModel, { ChatMessage, ChatMessageReaction, } from "../../Model/Chat/chatModel"; -import ContactListViewModel from "../Global/contactViewModel"; export default class ChatViewModel extends ContextHost { calendarViewModel: CalendarPageViewModel; @@ -150,7 +150,7 @@ export default class ChatViewModel extends ContextHost { public readonly notificationViewModel: NotificationViewModel, public readonly connectionViewModel: ConnectionViewModel, public readonly chatListViewModel: ChatListViewModel, - public readonly contactListViewModel: ContactListViewModel, + public readonly contactListViewModel: ContactListViewModel, ) { super("chat", coreViewModel); @@ -169,7 +169,7 @@ export default class ChatViewModel extends ContextHost { this.messagePageViewModel = new MessagePageViewModel( this.coreViewModel, this, - this.contactListViewModel, + this.contactListViewModel, ); this.settingsPageViewModel = new SettingsPageViewModel( this.coreViewModel, diff --git a/src/ViewModel/Global/contactViewModel.ts b/src/ViewModel/Global/contactViewModel.ts index 86259061..ea88e8b5 100644 --- a/src/ViewModel/Global/contactViewModel.ts +++ b/src/ViewModel/Global/contactViewModel.ts @@ -1,6 +1,6 @@ -import ContactListModel from "../../Model/Global/contactListModel"; -import * as React from "../../react"; import SettingsViewModel from "./settingsViewModel"; +import * as React from "../../react"; +import ContactListModel from "../../Model/Global/contactListModel"; export default class ContactListViewModel { // state @@ -8,43 +8,49 @@ export default class ContactListViewModel { // main updateContact = (id: string, name: string): ContactViewModel => { - if (id == this.settingsViewModel.settingsModel.userid) { - this.settingsViewModel.setName(name); - } + if (id == this.settingsViewModel.settingsModel.userid) { + this.settingsViewModel.setName(name); + } - const match = this.contacts.get(id); - if (match) { - match.name.value = name; - return match; - } - const vm = new ContactViewModel(id, name); - this.contacts.set(id, vm); - return vm; - } + const match = this.contacts.get(id); + if (match) { + match.name.value = name; + return match; + } + const vm = new ContactViewModel(id, name); + this.contacts.set(id, vm); + return vm; + }; handleContact = (contact: [string, string]): void => { - this.updateContact(...contact); - } + this.updateContact(...contact); + }; unwrapContact = (id: string, knownName: string): ContactViewModel => { - if (this.contacts.has(id)) - return this.contacts.get(id)! - return this.updateContact(id, knownName); - } + if (this.contacts.has(id)) return this.contacts.get(id)!; + return this.updateContact(id, knownName); + }; // load loadData = (): void => { - const contacts: [string, string][] = this.contactListModel.loadContacts(); - for (const contact of contacts) { - const vm = new ContactViewModel(...contact); - this.contacts.set(vm.id, vm); - } - } - + const contacts: [string, string][] = + this.contactListModel.loadContacts(); + for (const contact of contacts) { + const vm = new ContactViewModel(...contact); + this.contacts.set(vm.id, vm); + } + }; + // init - constructor(public contactListModel: ContactListModel, public settingsViewModel: SettingsViewModel) { - this.loadData(); - this.contactListModel.contactHandlerManager.setHandler("contact-list-view-model", this.handleContact); + constructor( + public contactListModel: ContactListModel, + public settingsViewModel: SettingsViewModel, + ) { + this.loadData(); + this.contactListModel.contactHandlerManager.setHandler( + "contact-list-view-model", + this.handleContact, + ); } } @@ -53,7 +59,10 @@ export class ContactViewModel { name: React.State = new React.State(""); // init - constructor(public id: string, name: string) { - this.name.value = name; + constructor( + public id: string, + name: string, + ) { + this.name.value = name; } } diff --git a/src/ViewModel/Global/settingsViewModel.ts b/src/ViewModel/Global/settingsViewModel.ts index 1afb0dcd..9c8530c4 100644 --- a/src/ViewModel/Global/settingsViewModel.ts +++ b/src/ViewModel/Global/settingsViewModel.ts @@ -1,7 +1,10 @@ import CoreViewModel, { Context } from "./coreViewModel"; import * as React from "../../react"; import { CommonKeys } from "../../View/keystrokes"; -import SettingsModel, { Languages, ThemeSettings } from "../../Model/Global/settingsModel"; +import SettingsModel, { + Languages, + ThemeSettings, +} from "../../Model/Global/settingsModel"; export default class SettingsViewModel extends Context { // state @@ -31,154 +34,158 @@ export default class SettingsViewModel extends Context { // methods setName = (name?: string): void => { - if (typeof name == "string") { - this.usernameInput.value = name; - } - this.coreViewModel.settingsModel.setName(this.usernameInput.value); - this.username.value = this.coreViewModel.settingsModel.username; - this.usernameInput.callSubscriptions(); + if (typeof name == "string") { + this.usernameInput.value = name; + } + this.coreViewModel.settingsModel.setName(this.usernameInput.value); + this.username.value = this.coreViewModel.settingsModel.username; + this.usernameInput.callSubscriptions(); }; setFirstDayofWeek = (): void => { - this.coreViewModel.settingsModel.setFirstDayOfWeek( - this.firstDayOfWeek.value, - ); + this.coreViewModel.settingsModel.setFirstDayOfWeek( + this.firstDayOfWeek.value, + ); }; showSettingsModal = (): void => { - this.coreViewModel.context = this; - this.isShowingSettingsModal.value = true; + this.coreViewModel.context = this; + this.isShowingSettingsModal.value = true; }; showModalPage = (page: SettingsModalPages): void => { - this.selectedModalPage.value = page; + this.selectedModalPage.value = page; }; // view applyTheme = (): void => { - let theme: string = this.theme.value; + let theme: string = this.theme.value; - if (theme == ThemeSettings.System) - theme = SettingsViewModel.getSystemTheme(); + if (theme == ThemeSettings.System) + theme = SettingsViewModel.getSystemTheme(); - switch (theme) { - case ThemeSettings.Dynamic: - const scene = this.selectDynamicScene(); - this.setScene(scene); - break; - case ThemeSettings.Dark: - this.setScene(DynamicSceneNight); - break; - case ThemeSettings.Black: - this.setScene(DynamicSceneBlack); - break; - default: - this.setScene(DynamicSceneDay); - } + switch (theme) { + case ThemeSettings.Dynamic: + const scene = this.selectDynamicScene(); + this.setScene(scene); + break; + case ThemeSettings.Dark: + this.setScene(DynamicSceneNight); + break; + case ThemeSettings.Black: + this.setScene(DynamicSceneBlack); + break; + default: + this.setScene(DynamicSceneDay); + } }; selectDynamicScene = (): DynamicScene => { - let hour = new Date().getHours(); + let hour = new Date().getHours(); - if (hour >= 21 || hour <= 6) { - return DynamicSceneNight; - } else if (hour > 6 && hour < 10) { - return DynamicSceneSunrise; - } else if (hour < 14) { - return DynamicSceneDay; - } else if (hour < 17) { - return DynamicSceneAfternoon; - } else { - return DynamicSceneSunset; - } + if (hour >= 21 || hour <= 6) { + return DynamicSceneNight; + } else if (hour > 6 && hour < 10) { + return DynamicSceneSunrise; + } else if (hour < 14) { + return DynamicSceneDay; + } else if (hour < 17) { + return DynamicSceneAfternoon; + } else { + return DynamicSceneSunset; + } }; setScene = (scene: DynamicScene): void => { - document.body.style.setProperty( - "--backdrop-grass-filter", - `brightness(${scene.brightness})`, - ); + document.body.style.setProperty( + "--backdrop-grass-filter", + `brightness(${scene.brightness})`, + ); - function setSkyColor( - tone: 1 | 2, - hue: number, - saturation: number, - luma: number, - ) { - document.body.style.setProperty( - `--sky-${tone}`, - `hsl(${hue}, ${saturation}%, ${luma}%)`, - ); - } - setSkyColor(1, scene.hue1, scene.saturation, scene.luma); - setSkyColor(2, scene.hue2, scene.saturation - 10, scene.luma - 10); + function setSkyColor( + tone: 1 | 2, + hue: number, + saturation: number, + luma: number, + ) { + document.body.style.setProperty( + `--sky-${tone}`, + `hsl(${hue}, ${saturation}%, ${luma}%)`, + ); + } + setSkyColor(1, scene.hue1, scene.saturation, scene.luma); + setSkyColor(2, scene.hue2, scene.saturation - 10, scene.luma - 10); - document.body.setAttribute("theme", scene.baseTheme); + document.body.setAttribute("theme", scene.baseTheme); }; // exit close = (): void => { - this.coreViewModel.closeContext(this.contextId); + this.coreViewModel.closeContext(this.contextId); }; handleContextClose = (): void => { - this.isShowingSettingsModal.value = false; - if (this.requiresReload.value == true) { - window.location.reload(); - } + this.isShowingSettingsModal.value = false; + if (this.requiresReload.value == true) { + window.location.reload(); + } }; // init - constructor(public readonly coreViewModel: CoreViewModel, public readonly settingsModel: SettingsModel) { - super("settings"); + constructor( + public readonly coreViewModel: CoreViewModel, + public readonly settingsModel: SettingsModel, + ) { + super("settings"); - this.username.value = coreViewModel.settingsModel.username; - this.usernameInput.value = coreViewModel.settingsModel.username; - this.firstDayOfWeek.value = coreViewModel.settingsModel.firstDayOfWeek; - this.language.value = coreViewModel.settingsModel.language; - this.theme.value = coreViewModel.settingsModel.theme; + this.username.value = coreViewModel.settingsModel.username; + this.usernameInput.value = coreViewModel.settingsModel.username; + this.firstDayOfWeek.value = coreViewModel.settingsModel.firstDayOfWeek; + this.language.value = coreViewModel.settingsModel.language; + this.theme.value = coreViewModel.settingsModel.theme; - // subscriptions - this.firstDayOfWeek.subscribe(this.setFirstDayofWeek); - this.language.subscribeSilent((newValue) => { - this.coreViewModel.settingsModel.setLanguage(newValue); - this.requiresReload.value = true; - }); - this.theme.subscribeSilent((newValue) => { - this.coreViewModel.settingsModel.setTheme(newValue); - }); + // subscriptions + this.firstDayOfWeek.subscribe(this.setFirstDayofWeek); + this.language.subscribeSilent((newValue) => { + this.coreViewModel.settingsModel.setLanguage(newValue); + this.requiresReload.value = true; + }); + this.theme.subscribeSilent((newValue) => { + this.coreViewModel.settingsModel.setTheme(newValue); + }); - // set theme - this.theme.subscribe(() => { - this.applyTheme(); - }); - SettingsViewModel.generateThemeMedia().addEventListener("change", () => - this.applyTheme(), - ); - const seconds = new Date().getSeconds(); - const secondsUntilNewMinute = 60 - seconds; - setTimeout(() => { - setInterval(this.applyTheme, 1000 * 60); - }, secondsUntilNewMinute); + // set theme + this.theme.subscribe(() => { + this.applyTheme(); + }); + SettingsViewModel.generateThemeMedia().addEventListener("change", () => + this.applyTheme(), + ); + const seconds = new Date().getSeconds(); + const secondsUntilNewMinute = 60 - seconds; + setTimeout(() => { + setInterval(this.applyTheme, 1000 * 60); + }, secondsUntilNewMinute); - // keystrokes - this.registerKeyStroke(CommonKeys.CloseOrCancel, this.close); + // keystrokes + this.registerKeyStroke(CommonKeys.CloseOrCancel, this.close); } static generateThemeMedia(): MediaQueryList { - return window.matchMedia("(prefers-color-scheme: dark)"); + return window.matchMedia("(prefers-color-scheme: dark)"); } static getSystemTheme(): string { - const media = SettingsViewModel.generateThemeMedia(); - return media.matches == true ? ThemeSettings.Dark : ThemeSettings.Light; + const media = SettingsViewModel.generateThemeMedia(); + return media.matches == true ? ThemeSettings.Dark : ThemeSettings.Light; } } export enum SettingsModalPages { Appearance, - Regional, - Info, + Account, + Regional, + Info, } export interface DynamicScene { diff --git a/src/ViewModel/Pages/messagePageViewModel.ts b/src/ViewModel/Pages/messagePageViewModel.ts index 5865a323..5ebf3569 100644 --- a/src/ViewModel/Pages/messagePageViewModel.ts +++ b/src/ViewModel/Pages/messagePageViewModel.ts @@ -1,5 +1,6 @@ import SearchViewModel from "../Utility/searchViewModel"; import CoreViewModel, { Context } from "../Global/coreViewModel"; +import ContactListViewModel from "../Global/contactViewModel"; import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel"; import ChatMessageViewModel from "../Chat/chatMessageViewModel"; import * as React from "../../react"; @@ -9,7 +10,6 @@ import { ChatMessageReaction, ReactionSymbols, } from "../../Model/Chat/chatModel"; -import ContactListViewModel from "../Global/contactViewModel"; export default class MessagePageViewModel extends Context { // state @@ -107,138 +107,141 @@ export default class MessagePageViewModel extends Context { // view showChatMessage = (chatMessage: ChatMessage): void => { - const contact = this.contactListViewModel.unwrapContact(chatMessage.senderId, chatMessage.senderName); - const chatMessageViewModel = new ChatMessageViewModel( - this.coreViewModel, - this, - chatMessage, - contact, - chatMessage.senderId == - this.chatViewModel.settingsViewModel.settingsModel.userid - ); + const contact = this.contactListViewModel.unwrapContact( + chatMessage.senderId, + chatMessage.senderName, + ); + const chatMessageViewModel = new ChatMessageViewModel( + this.coreViewModel, + this, + chatMessage, + contact, + chatMessage.senderId == + this.chatViewModel.settingsViewModel.settingsModel.userid, + ); - const existingChatMessageViewModel: ChatMessageViewModel | undefined = - this.chatMessageViewModels.value.get(chatMessage.fileId); - if (existingChatMessageViewModel != undefined) { - existingChatMessageViewModel.body.value = chatMessage.body; - existingChatMessageViewModel.status.value = chatMessage.status; - } else { - this.chatMessageViewModels.set( - chatMessage.fileId, - chatMessageViewModel, - ); - } + const existingChatMessageViewModel: ChatMessageViewModel | undefined = + this.chatMessageViewModels.value.get(chatMessage.fileId); + if (existingChatMessageViewModel != undefined) { + existingChatMessageViewModel.body.value = chatMessage.body; + existingChatMessageViewModel.status.value = chatMessage.status; + } else { + this.chatMessageViewModels.set( + chatMessage.fileId, + chatMessageViewModel, + ); + } }; handleReaction = (reaction: ChatMessageReaction): void => { - const messageViewModel: ChatMessageViewModel | undefined = - this.chatMessageViewModels.value.get(reaction.messageId); - if (messageViewModel == undefined) return; - messageViewModel.handleReaction(reaction); + const messageViewModel: ChatMessageViewModel | undefined = + this.chatMessageViewModels.value.get(reaction.messageId); + if (messageViewModel == undefined) return; + messageViewModel.handleReaction(reaction); }; showFilterModal = (): void => { - this.isFilterModalOpen.value = true; + this.isFilterModalOpen.value = true; }; hideFilterModal = (): void => { - this.isFilterModalOpen.value = false; + this.isFilterModalOpen.value = false; }; revokeReactionFilter = (persist: boolean = true): void => { - this.reactionFilter.value = undefined; - if (persist) this.chatViewModel.chatModel.storeFilter(""); + this.reactionFilter.value = undefined; + if (persist) this.chatViewModel.chatModel.storeFilter(""); }; setReactionFilter = (content: ReactionSymbols): void => { - this.reactionFilter.value = content; - this.chatViewModel.chatModel.storeFilter(content); + this.reactionFilter.value = content; + this.chatViewModel.chatModel.storeFilter(content); }; resetFilter = (): void => { - this.revokeReactionFilter(); - this.searchViewModel.search(""); + this.revokeReactionFilter(); + this.searchViewModel.search(""); }; setReplyView = (message: ChatMessageViewModel): void => { - this.replyViewSelectedMessage.value = message; - this.revokeReactionFilter(false); - this.setReply(message, false); + this.replyViewSelectedMessage.value = message; + this.revokeReactionFilter(false); + this.setReply(message, false); }; resetReplyView = (): void => { - this.replyViewSelectedMessage.value = undefined; - this.resetReply(false); - this.restoreFilter(); + this.replyViewSelectedMessage.value = undefined; + this.resetReply(false); + this.restoreFilter(); }; setFocus = (): void => { - this.focusSetter.callSubscriptions(); + this.focusSetter.callSubscriptions(); }; // load loadData = (): void => { - this.chatMessageViewModels.clear(); - for (const chatMessage of this.chatViewModel.chatModel.messages) { - this.showChatMessage(chatMessage); - } - for (const reaction of this.chatViewModel.chatModel.reactions) { - this.handleReaction(reaction); - } + this.chatMessageViewModels.clear(); + for (const chatMessage of this.chatViewModel.chatModel.messages) { + this.showChatMessage(chatMessage); + } + for (const reaction of this.chatViewModel.chatModel.reactions) { + this.handleReaction(reaction); + } }; restoreFilter = (): void => { - const previousFilter = this.chatViewModel.chatModel.getFilter() as any; - if (Object.values(ReactionSymbols).includes(previousFilter)) - this.setReactionFilter(previousFilter); + const previousFilter = this.chatViewModel.chatModel.getFilter() as any; + if (Object.values(ReactionSymbols).includes(previousFilter)) + this.setReactionFilter(previousFilter); }; // init constructor( - public readonly coreViewModel: CoreViewModel, - public readonly chatViewModel: ChatViewModel, - public readonly contactListViewModel: ContactListViewModel, + public readonly coreViewModel: CoreViewModel, + public readonly chatViewModel: ChatViewModel, + public readonly contactListViewModel: ContactListViewModel, ) { - super("message-page"); - this.restoreFilter(); + super("message-page"); + this.restoreFilter(); - // states - this.cannotSendMessage = React.createProxyState( - [ - this.chatViewModel.settingsViewModel.username, - this.composingMessage, - ], - () => - this.chatViewModel.settingsViewModel.username.value == "" || - this.composingMessage.value == "", - ); + // states + this.cannotSendMessage = React.createProxyState( + [ + this.chatViewModel.settingsViewModel.username, + this.composingMessage, + ], + () => + this.chatViewModel.settingsViewModel.username.value == "" || + this.composingMessage.value == "", + ); - this.searchViewModel = new SearchViewModel( - this.chatMessageViewModels, - this.filteredMessageViewModels, - (chatMessageViewModel) => [chatMessageViewModel.body.value], - new React.ListState(), - ); + this.searchViewModel = new SearchViewModel( + this.chatMessageViewModels, + this.filteredMessageViewModels, + (chatMessageViewModel) => [chatMessageViewModel.body.value], + new React.ListState(), + ); - this.isFilterActive = React.createProxyState( - [this.searchViewModel.appliedQuery, this.reactionFilter], - () => - this.searchViewModel.appliedQuery.value != "" || - this.reactionFilter.value != undefined, - ); + this.isFilterActive = React.createProxyState( + [this.searchViewModel.appliedQuery, this.reactionFilter], + () => + this.searchViewModel.appliedQuery.value != "" || + this.reactionFilter.value != undefined, + ); - // keystrokes - this.registerKeyStroke(CommonKeys.Filter, this.showFilterModal); - this.registerKeyStroke(CommonKeys.CloseOrCancel, () => { - if (this.isFilterModalOpen.value == true) { - this.hideFilterModal(); - } else { - this.replyingMessage.value = undefined; - } - }); - this.registerKeyStroke(CommonKeys.Reset, this.resetFilter); - this.registerKeyStroke(CommonKeys.Create, this.setFocus); + // keystrokes + this.registerKeyStroke(CommonKeys.Filter, this.showFilterModal); + this.registerKeyStroke(CommonKeys.CloseOrCancel, () => { + if (this.isFilterModalOpen.value == true) { + this.hideFilterModal(); + } else { + this.replyingMessage.value = undefined; + } + }); + this.registerKeyStroke(CommonKeys.Reset, this.resetFilter); + this.registerKeyStroke(CommonKeys.Create, this.setFocus); - this.chatViewModel.registerContext(ChatPageTypes.Messages, this); + this.chatViewModel.registerContext(ChatPageTypes.Messages, this); } } diff --git a/src/index.tsx b/src/index.tsx index 01d8a1f4..3b3b7da0 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -7,6 +7,7 @@ import StorageViewModel from "./ViewModel/Global/storageViewModel"; import SettingsViewModel from "./ViewModel/Global/settingsViewModel"; import FileTransferViewModel from "./ViewModel/Global/fileTransferViewModel"; import CoreViewModel from "./ViewModel/Global/coreViewModel"; +import ContactListViewModel from "./ViewModel/Global/contactViewModel"; import ConnectionViewModel from "./ViewModel/Global/connectionViewModel"; import ChatListViewModel from "./ViewModel/Chat/chatListViewModel"; import { HomePage } from "./View/homePage"; @@ -18,10 +19,9 @@ import { ConnectionModal } from "./View/Modals/connectionModal"; import StorageModel from "./Model/Global/storageModel"; import SettingsModel from "./Model/Global/settingsModel"; import FileTransferModel from "./Model/Global/fileTransferModel"; +import ContactListModel from "./Model/Global/contactListModel"; import ConnectionModel from "./Model/Global/connectionModel"; import ChatListModel from "./Model/Chat/chatListModel"; -import ContactListModel from "./Model/Global/contactListModel"; -import ContactListViewModel from "./ViewModel/Global/contactViewModel"; // models const storageModel = new StorageModel(); @@ -47,7 +47,10 @@ const coreViewModel = new CoreViewModel( const storageViewModel = new StorageViewModel(coreViewModel); const settingsViewModel = new SettingsViewModel(coreViewModel, settingsModel); -const contactListViewModel = new ContactListViewModel(contactListModel, settingsViewModel); +const contactListViewModel = new ContactListViewModel( + contactListModel, + settingsViewModel, +); const connectionViewModel = new ConnectionViewModel(coreViewModel); const chatListViewModel = new ChatListViewModel( coreViewModel,