move name to settings modal

This commit is contained in:
marlon-erler
2026-09-28 15:21:50 +02:00
parent 0afbfc2d0b
commit 22acf7f7d8
22 changed files with 743 additions and 589 deletions
+197 -128
View File
@@ -351,9 +351,6 @@
connectAudioLabel: "connect to server", connectAudioLabel: "connect to server",
disconnectAudioLabel: "disconnect from server", disconnectAudioLabel: "disconnect from server",
manageConnectionsAudioLabel: "manage connections", manageConnectionsAudioLabel: "manage connections",
yourNameLabel: "Your name",
yourNamePlaceholder: "Jane Doe",
setNameButtonAudioLabel: "set name",
settingsButton: "Settings", settingsButton: "Settings",
manageStorageButton: "Manage storage", manageStorageButton: "Manage storage",
transferDataButton: "Transfer or export data", transferDataButton: "Transfer or export data",
@@ -369,6 +366,7 @@
settings: { settings: {
pages: { pages: {
appearance: "Appearance", appearance: "Appearance",
account: "Account",
regional: "Language & Region", regional: "Language & Region",
info: "About CT" info: "About CT"
}, },
@@ -379,10 +377,19 @@
system: "Device theme", system: "Device theme",
black: "Black" black: "Black"
}, },
version: "Version", account: {
language: "Language", yourNameLabel: "Your name",
firstDayOfWeekLabel: "First day of week", yourNamePlaceholder: "Jane Doe",
checkUpdatesButton: "Check for updates" setNameButtonAudioLabel: "set name"
},
regional: {
language: "Language",
firstDayOfWeekLabel: "First day of week"
},
about: {
version: "Version",
checkUpdatesButton: "Check for updates"
}
}, },
connectionModal: { connectionModal: {
connectionModalHeadline: "Manage Connections", connectionModalHeadline: "Manage Connections",
@@ -600,9 +607,6 @@
connectAudioLabel: "mit Server verbinden", connectAudioLabel: "mit Server verbinden",
disconnectAudioLabel: "vom Server trennen", disconnectAudioLabel: "vom Server trennen",
manageConnectionsAudioLabel: "Verbindungen verwalten", manageConnectionsAudioLabel: "Verbindungen verwalten",
yourNameLabel: "Dein Name",
yourNamePlaceholder: "Max Mustermann",
setNameButtonAudioLabel: "Name speichern",
settingsButton: "Einstellungen", settingsButton: "Einstellungen",
manageStorageButton: "Daten verwalten", manageStorageButton: "Daten verwalten",
transferDataButton: "Daten \xFCbertragen", transferDataButton: "Daten \xFCbertragen",
@@ -617,6 +621,7 @@
settings: { settings: {
pages: { pages: {
appearance: "Erscheinungsbild", appearance: "Erscheinungsbild",
account: "Konto",
regional: "Sprache & Region", regional: "Sprache & Region",
info: "\xDCber CT" info: "\xDCber CT"
}, },
@@ -627,10 +632,19 @@
system: "Ger\xE4teeinstellung", system: "Ger\xE4teeinstellung",
black: "Schwarz" black: "Schwarz"
}, },
version: "Version", account: {
language: "Sprache", yourNameLabel: "Dein Name",
firstDayOfWeekLabel: "Erster Wochentag", yourNamePlaceholder: "Max Mustermann",
checkUpdatesButton: "Nach updates suchen" setNameButtonAudioLabel: "Namen festlegen"
},
regional: {
language: "Sprache",
firstDayOfWeekLabel: "Erster Wochentag"
},
about: {
version: "Version",
checkUpdatesButton: "Nach Updates suchen"
}
}, },
connectionModal: { connectionModal: {
connectionModalHeadline: "Verbindungen verwalten", connectionModalHeadline: "Verbindungen verwalten",
@@ -829,9 +843,6 @@
connectAudioLabel: "conectar al servidor", connectAudioLabel: "conectar al servidor",
disconnectAudioLabel: "desconectar del servidor", disconnectAudioLabel: "desconectar del servidor",
manageConnectionsAudioLabel: "gestionar conexiones", manageConnectionsAudioLabel: "gestionar conexiones",
yourNameLabel: "Tu nombre",
yourNamePlaceholder: "Juan P\xE9rez",
setNameButtonAudioLabel: "establecer nombre",
settingsButton: "Ajustes", settingsButton: "Ajustes",
manageStorageButton: "Gestionar almacenamiento", manageStorageButton: "Gestionar almacenamiento",
transferDataButton: "Enviar o exportar archivos", transferDataButton: "Enviar o exportar archivos",
@@ -846,6 +857,7 @@
settings: { settings: {
pages: { pages: {
appearance: "Aspecto", appearance: "Aspecto",
account: "Cuenta",
regional: "Idioma y Regi\xF3n", regional: "Idioma y Regi\xF3n",
info: "Sobre CT" info: "Sobre CT"
}, },
@@ -856,10 +868,19 @@
system: "Seg\xFAn dispositivo", system: "Seg\xFAn dispositivo",
black: "Negro" black: "Negro"
}, },
version: "Versi\xF3n", account: {
language: "Idioma", yourNameLabel: "Tu nombre",
firstDayOfWeekLabel: "Primer d\xEDa de la semana", yourNamePlaceholder: "Juan P\xE9rez",
checkUpdatesButton: "Buscar actualizaciones" setNameButtonAudioLabel: "establecer nombre"
},
regional: {
language: "Idioma",
firstDayOfWeekLabel: "Primer d\xEDa de la semana"
},
about: {
version: "Versi\xF3n",
checkUpdatesButton: "Buscar actualisaciones"
}
}, },
connectionModal: { connectionModal: {
connectionModalHeadline: "Gestionar Conexiones", connectionModalHeadline: "Gestionar Conexiones",
@@ -1709,7 +1730,7 @@
this.printTree = () => { this.printTree = () => {
return stringify(this.storageEntryTree); return stringify(this.storageEntryTree);
}; };
// upgrade // upgrade
this.upgrade = () => { this.upgrade = () => {
}; };
this.initializeTree(); this.initializeTree();
@@ -1862,9 +1883,8 @@
// src/Model/Global/settingsModel.ts // src/Model/Global/settingsModel.ts
var SettingsModel = class _SettingsModel { var SettingsModel = class _SettingsModel {
// init // init
constructor(storageModel2, contactListModel2) { constructor(storageModel2) {
this.storageModel = storageModel2; this.storageModel = storageModel2;
this.contactListModel = contactListModel2;
// data // data
this.username = ""; this.username = "";
this.userid = ""; this.userid = "";
@@ -1886,7 +1906,6 @@
this.setName = (newValue) => { this.setName = (newValue) => {
this.username = newValue; this.username = newValue;
this.storeSetting("username", newValue); this.storeSetting("username", newValue);
this.contactListModel.storeContact(this.userid, newValue);
}; };
this.setFirstDayOfWeek = (newValue) => { this.setFirstDayOfWeek = (newValue) => {
this.firstDayOfWeek = newValue; this.firstDayOfWeek = newValue;
@@ -1976,7 +1995,10 @@
() => this.usernameInput.value == "" || this.usernameInput.value == this.coreViewModel.settingsModel.username () => this.usernameInput.value == "" || this.usernameInput.value == this.coreViewModel.settingsModel.username
); );
// methods // methods
this.setName = () => { this.setName = (name) => {
if (typeof name == "string") {
this.usernameInput.value = name;
}
this.coreViewModel.settingsModel.setName(this.usernameInput.value); this.coreViewModel.settingsModel.setName(this.usernameInput.value);
this.username.value = this.coreViewModel.settingsModel.username; this.username.value = this.coreViewModel.settingsModel.username;
this.usernameInput.callSubscriptions(); 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 // src/ViewModel/Global/connectionViewModel.ts
var ConnectionViewModel = class { var ConnectionViewModel = class {
// init // init
@@ -2952,9 +3026,14 @@
// messaging // messaging
this.addMessage = async (chatMessage) => { this.addMessage = async (chatMessage) => {
await this.decryptMessage(chatMessage); await this.decryptMessage(chatMessage);
this.contactListModel.storeContact(chatMessage.senderId, chatMessage.senderName); this.contactListModel.storeContact(
chatMessage.senderId,
chatMessage.senderName
);
if (chatMessage.body != "") { if (chatMessage.body != "") {
const messagePath = this.getMessagePath(chatMessage.fileId); const messagePath = this.getMessagePath(
chatMessage.fileId
);
this.storageModel.writeStringifiable(messagePath, chatMessage); this.storageModel.writeStringifiable(messagePath, chatMessage);
this.chatMessageHandlerManager.trigger(chatMessage); this.chatMessageHandlerManager.trigger(chatMessage);
} }
@@ -5196,23 +5275,6 @@
"toggle:disabled": connectionViewModel2.cannotConnect "toggle:disabled": connectionViewModel2.cannotConnect
}, },
/* @__PURE__ */ createElement("span", { class: "icon" }, "link") /* @__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( )), /* @__PURE__ */ createElement("hr", null), HomePageButton(
settingsViewModel2.showSettingsModal, settingsViewModel2.showSettingsModal,
coreViewModel2.translations.homePage.settingsButton, coreViewModel2.translations.homePage.settingsButton,
@@ -6476,7 +6538,13 @@
// src/View/Components/replyPreview.tsx // src/View/Components/replyPreview.tsx
function ReplyPreview(coreViewModel2, chatMessageViewModel) { 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", "b",
{ {
class: "ellipsis", class: "ellipsis",
@@ -7430,12 +7498,17 @@
coreViewModel2, coreViewModel2,
settingsViewModel2 settingsViewModel2
); );
case 1 /* Regional */: case 1 /* Account */:
return SettingsAccountPane(
coreViewModel2,
settingsViewModel2
);
case 2 /* Regional */:
return SettingsRegionalPane( return SettingsRegionalPane(
coreViewModel2, coreViewModel2,
settingsViewModel2 settingsViewModel2
); );
case 2 /* Info */: case 3 /* Info */:
return SettingsInfoPane(coreViewModel2, settingsViewModel2); return SettingsInfoPane(coreViewModel2, settingsViewModel2);
} }
} }
@@ -7466,12 +7539,17 @@
), SettingsPaneButton( ), SettingsPaneButton(
coreViewModel2, coreViewModel2,
settingsViewModel2, settingsViewModel2,
1 /* Regional */, 1 /* Account */,
coreViewModel2.translations.settings.pages.account
), SettingsPaneButton(
coreViewModel2,
settingsViewModel2,
2 /* Regional */,
coreViewModel2.translations.settings.pages.regional coreViewModel2.translations.settings.pages.regional
), SettingsPaneButton( ), SettingsPaneButton(
coreViewModel2, coreViewModel2,
settingsViewModel2, settingsViewModel2,
2 /* Info */, 3 /* Info */,
coreViewModel2.translations.settings.pages.info coreViewModel2.translations.settings.pages.info
)); ));
} }
@@ -7487,7 +7565,7 @@
function SettingsInfoPane(coreViewModel2, settingsViewModel2) { 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( 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", "build",
coreViewModel2.translations.settings.version, coreViewModel2.translations.settings.about.version,
settingsViewModel2.coreViewModel.version settingsViewModel2.coreViewModel.version
), /* @__PURE__ */ createElement( ), /* @__PURE__ */ createElement(
"button", "button",
@@ -7498,15 +7576,15 @@
}, },
/* @__PURE__ */ createElement("span", { "subscribe:innerText": coreViewModel2.updateText }), /* @__PURE__ */ createElement("span", { "subscribe:innerText": coreViewModel2.updateText }),
/* @__PURE__ */ createElement("span", { class: "icon" }, "update") /* @__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) { 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( new ListState(
Object.values(Languages).map((x) => [languageNames[x], x]) Object.values(Languages).map((x) => [languageNames[x], x])
), ),
settingsViewModel2.language 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( new ListState(
coreViewModel2.translations.regional.weekdays.full.map( coreViewModel2.translations.regional.weekdays.full.map(
(x, i) => [x, i.toString()] (x, i) => [x, i.toString()]
@@ -7515,6 +7593,26 @@
settingsViewModel2.firstDayOfWeek 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) { 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( return /* @__PURE__ */ createElement("div", { class: "slide-up" }, /* @__PURE__ */ createElement("h2", null, coreViewModel2.translations.settings.pages.appearance), /* @__PURE__ */ createElement("hr", null), OptionButtonList(
new ListState([ new ListState([
@@ -8150,6 +8248,37 @@
body: "" 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 // node_modules/udn-frontend/index.ts
var UDNFrontend = class { var UDNFrontend = class {
ws; ws;
@@ -8370,11 +8499,17 @@
return chatMessages; return chatMessages;
}; };
this.addToOutbox = (chatMessage) => { this.addToOutbox = (chatMessage) => {
const messagePath = [...this.getOutboxPath(), chatMessage.fileId]; const messagePath = [
...this.getOutboxPath(),
chatMessage.fileId
];
this.storageModel.writeStringifiable(messagePath, chatMessage); this.storageModel.writeStringifiable(messagePath, chatMessage);
}; };
this.removeFromOutbox = (chatMessage) => { this.removeFromOutbox = (chatMessage) => {
const messagePath = [...this.getOutboxPath(), chatMessage.fileId]; const messagePath = [
...this.getOutboxPath(),
chatMessage.fileId
];
this.storageModel.remove(messagePath); this.storageModel.remove(messagePath);
}; };
this.sendMessagesInOutbox = () => { 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 // src/index.tsx
var storageModel = new StorageModel(); var storageModel = new StorageModel();
var contactListModel = new ContactListModel(storageModel); var settingsModel = new SettingsModel(storageModel);
var settingsModel = new SettingsModel(storageModel, contactListModel); var contactListModel = new ContactListModel(storageModel, settingsModel);
var connectionModel = new ConnectionModel(storageModel); var connectionModel = new ConnectionModel(storageModel);
var chatListModel = new ChatListModel( var chatListModel = new ChatListModel(
storageModel, storageModel,
@@ -8653,8 +8719,11 @@
fileTransferModel fileTransferModel
); );
var storageViewModel = new StorageViewModel(coreViewModel); var storageViewModel = new StorageViewModel(coreViewModel);
var contactListViewModel = new ContactListViewModel(contactListModel);
var settingsViewModel = new SettingsViewModel(coreViewModel, settingsModel); var settingsViewModel = new SettingsViewModel(coreViewModel, settingsModel);
var contactListViewModel = new ContactListViewModel(
contactListModel,
settingsViewModel
);
var connectionViewModel = new ConnectionViewModel(coreViewModel); var connectionViewModel = new ConnectionViewModel(coreViewModel);
var chatListViewModel = new ChatListViewModel( var chatListViewModel = new ChatListViewModel(
coreViewModel, coreViewModel,
Vendored
+1 -1
View File
@@ -1,4 +1,4 @@
const VERSION = "2610 Beta 14" const VERSION = "2610 Beta 16"
function processUrl(event) { function processUrl(event) {
const url = event.request.url; const url = event.request.url;
+1 -1
View File
@@ -1 +1 @@
2610 Beta 14 2610 Beta 16
+4 -4
View File
@@ -8,8 +8,8 @@ import StorageModel, {
filePaths, filePaths,
} from "../Global/storageModel"; } from "../Global/storageModel";
import SettingsModel from "../Global/settingsModel"; import SettingsModel from "../Global/settingsModel";
import ConnectionModel from "../Global/connectionModel";
import ContactListModel from "../Global/contactListModel"; import ContactListModel from "../Global/contactListModel";
import ConnectionModel from "../Global/connectionModel";
export default class ChatListModel { export default class ChatListModel {
// data // data
@@ -28,7 +28,7 @@ export default class ChatListModel {
this.connectionModel, this.connectionModel,
this.settingsModel, this.settingsModel,
this, this,
this.contactListModel, this.contactListModel,
id, id,
); );
chatModel.setName(primaryChannel); chatModel.setName(primaryChannel);
@@ -90,7 +90,7 @@ export default class ChatListModel {
this.connectionModel, this.connectionModel,
this.settingsModel, this.settingsModel,
this, this,
this.contactListModel, this.contactListModel,
chatId, chatId,
); );
this.addChatModel(chatModel); this.addChatModel(chatModel);
@@ -102,7 +102,7 @@ export default class ChatListModel {
public storageModel: StorageModel, public storageModel: StorageModel,
public settingsModel: SettingsModel, public settingsModel: SettingsModel,
public connectionModel: ConnectionModel, public connectionModel: ConnectionModel,
public contactListModel: ContactListModel public contactListModel: ContactListModel,
) { ) {
this.loadChats(); this.loadChats();
+15 -11
View File
@@ -18,10 +18,10 @@ import StorageModel, {
filePaths, filePaths,
} from "../Global/storageModel"; } from "../Global/storageModel";
import SettingsModel from "../Global/settingsModel"; import SettingsModel from "../Global/settingsModel";
import ContactListModel from "../Global/contactListModel";
import ConnectionModel from "../Global/connectionModel"; import ConnectionModel from "../Global/connectionModel";
import FileModel, { FileContent } from "../Files/fileModel"; import FileModel, { FileContent } from "../Files/fileModel";
import { Colors } from "../../colors"; import { Colors } from "../../colors";
import ContactListModel from "../Global/contactListModel";
export default class ChatModel { export default class ChatModel {
// models // models
@@ -162,11 +162,16 @@ export default class ChatModel {
readonly addMessage = async (chatMessage: ChatMessage): Promise<void> => { readonly addMessage = async (chatMessage: ChatMessage): Promise<void> => {
await this.decryptMessage(chatMessage); await this.decryptMessage(chatMessage);
this.contactListModel.storeContact(chatMessage.senderId, chatMessage.senderName); this.contactListModel.storeContact(
chatMessage.senderId,
chatMessage.senderName,
);
// message // message
if (chatMessage.body != "") { if (chatMessage.body != "") {
const messagePath: string[] = this.getMessagePath(chatMessage.fileId); const messagePath: string[] = this.getMessagePath(
chatMessage.fileId,
);
this.storageModel.writeStringifiable(messagePath, chatMessage); this.storageModel.writeStringifiable(messagePath, chatMessage);
this.chatMessageHandlerManager.trigger(chatMessage); this.chatMessageHandlerManager.trigger(chatMessage);
} }
@@ -238,7 +243,7 @@ export default class ChatModel {
const reaction = ChatModel.createMessageReaction( const reaction = ChatModel.createMessageReaction(
messageId, messageId,
senderId, senderId,
senderName, senderName,
content, content,
isDeleting, isDeleting,
); );
@@ -350,7 +355,7 @@ export default class ChatModel {
public connectionModel: ConnectionModel, public connectionModel: ConnectionModel,
public settingsModel: SettingsModel, public settingsModel: SettingsModel,
public chatListModel: ChatListModel, public chatListModel: ChatListModel,
public contactListModel: ContactListModel, public contactListModel: ContactListModel,
chatId: string, chatId: string,
) { ) {
this.id = chatId; this.id = chatId;
@@ -397,10 +402,10 @@ export default class ChatModel {
inlineReplyId?: string, inlineReplyId?: string,
fileContent?: FileContent<string>, fileContent?: FileContent<string>,
): Promise<ChatMessage> => { ): Promise<ChatMessage> => {
const messageFileContent = FileModel.createFileContent(v4(), "message"); const messageFileContent = FileModel.createFileContent(v4(), "message");
const chatMessage: ChatMessage = { const chatMessage: ChatMessage = {
...messageFileContent, ...messageFileContent,
channel, channel,
senderName, senderName,
@@ -446,7 +451,7 @@ export default class ChatModel {
messageId, messageId,
senderId, senderId,
senderName, senderName,
content, content,
isDeleting, isDeleting,
}; };
@@ -501,8 +506,7 @@ export interface ChatMessage extends FileContent<"message"> {
stringifiedFile: string; stringifiedFile: string;
} }
export interface ChatMessageReaction export interface ChatMessageReaction extends FileContent<"reaction"> {
extends FileContent<"reaction"> {
readonly messageId: string; readonly messageId: string;
readonly senderId: string; readonly senderId: string;
readonly senderName: string; readonly senderName: string;
@@ -531,7 +535,7 @@ export const ChatInfoReference: ChatInfoFileContent = {
export const ChatMessageReference: ChatMessage = { export const ChatMessageReference: ChatMessage = {
dataVersion: DATA_VERSION, dataVersion: DATA_VERSION,
fileId: "", fileId: "",
fileContentId: "", fileContentId: "",
creationDate: "", creationDate: "",
+8 -2
View File
@@ -149,12 +149,18 @@ export default class ConnectionModel {
}; };
readonly addToOutbox = (chatMessage: ChatMessage): void => { readonly addToOutbox = (chatMessage: ChatMessage): void => {
const messagePath: string[] = [...this.getOutboxPath(), chatMessage.fileId]; const messagePath: string[] = [
...this.getOutboxPath(),
chatMessage.fileId,
];
this.storageModel.writeStringifiable(messagePath, chatMessage); this.storageModel.writeStringifiable(messagePath, chatMessage);
}; };
readonly removeFromOutbox = (chatMessage: ChatMessage): void => { readonly removeFromOutbox = (chatMessage: ChatMessage): void => {
const messagePath: string[] = [...this.getOutboxPath(), chatMessage.fileId]; const messagePath: string[] = [
...this.getOutboxPath(),
chatMessage.fileId,
];
this.storageModel.remove(messagePath); this.storageModel.remove(messagePath);
}; };
+25 -15
View File
@@ -1,28 +1,38 @@
import {HandlerManager} from "../Utility/utility"; import StorageModel, { StorageModelSubPaths } from "./storageModel";
import SettingsModel from "./settingsModel"; import SettingsModel from "./settingsModel";
import StorageModel, {StorageModelSubPaths} from "./storageModel"; import { HandlerManager } from "../Utility/utility";
export default class ContactListModel { export default class ContactListModel {
contactHandlerManager = new HandlerManager<[string, string]>(); contactHandlerManager = new HandlerManager<[string, string]>();
private getContactPath = (contactId: string): string[] => { private getContactPath = (contactId: string): string[] => {
return StorageModel.getPath(StorageModelSubPaths.ContactsModel, [contactId]); return StorageModel.getPath(StorageModelSubPaths.ContactsModel, [
} contactId,
]);
};
loadContacts = (): [string, string][] => { loadContacts = (): [string, string][] => {
const contactIds: string[] = this.storageModel.list([StorageModelSubPaths.ContactsModel]); const contactIds: string[] = this.storageModel.list([
const contacts: [string, string][] = contactIds.map(id => [id, this.storageModel.read(this.getContactPath(id)) ?? "?"]); StorageModelSubPaths.ContactsModel,
contacts.push([this.settingsModel.userid, this.settingsModel.username]); ]);
return contacts; 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) => { storeContact = (id: string, name: string) => {
this.contactHandlerManager.trigger([id, name]); this.contactHandlerManager.trigger([id, name]);
if (id == this.settingsModel.userid) return; if (id == this.settingsModel.userid) return;
const path = this.getContactPath(id); const path = this.getContactPath(id);
this.storageModel.write(path, name) this.storageModel.write(path, name);
} };
constructor(public storageModel: StorageModel, public settingsModel: SettingsModel) {} constructor(
public storageModel: StorageModel,
public settingsModel: SettingsModel,
) {}
} }
+44 -44
View File
@@ -28,93 +28,93 @@ export default class SettingsModel {
readonly setName = (newValue: string): void => { readonly setName = (newValue: string): void => {
this.username = newValue; this.username = newValue;
this.storeSetting("username", newValue); this.storeSetting("username", newValue);
}; };
readonly setFirstDayOfWeek = (newValue: string): void => { readonly setFirstDayOfWeek = (newValue: string): void => {
this.firstDayOfWeek = newValue; this.firstDayOfWeek = newValue;
this.storeSetting("firstDayOfWeek", newValue); this.storeSetting("firstDayOfWeek", newValue);
}; };
readonly setLanguage = (newValue: string): void => { readonly setLanguage = (newValue: string): void => {
this.language = newValue; this.language = newValue;
this.storeSetting("language", newValue); this.storeSetting("language", newValue);
}; };
readonly setTheme = (newValue: string): void => { readonly setTheme = (newValue: string): void => {
this.theme = newValue; this.theme = newValue;
this.storeSetting("theme", newValue); this.storeSetting("theme", newValue);
}; };
// load // load
private readonly readSetting = ( private readonly readSetting = (
pathName: keyof typeof filePaths.settingsModel, pathName: keyof typeof filePaths.settingsModel,
): string | null => { ): string | null => {
const path = StorageModel.getPath( const path = StorageModel.getPath(
StorageModelSubPaths.SettingsModel, StorageModelSubPaths.SettingsModel,
filePaths.settingsModel[pathName], filePaths.settingsModel[pathName],
); );
return this.storageModel.read(path); return this.storageModel.read(path);
}; };
readonly loadId = (): void => { readonly loadId = (): void => {
const content: string | null = this.readSetting("userid"); const content: string | null = this.readSetting("userid");
const id = content ?? UUID(); const id = content ?? UUID();
if (content) this.userid = content; if (content) this.userid = content;
else this.setId(id); else this.setId(id);
}; };
readonly loadUsername = (): void => { readonly loadUsername = (): void => {
const content: string | null = this.readSetting("username"); const content: string | null = this.readSetting("username");
this.username = content ?? ""; this.username = content ?? "";
}; };
readonly loadFirstDayofWeek = (): void => { readonly loadFirstDayofWeek = (): void => {
const content: string | null = this.readSetting("firstDayOfWeek"); const content: string | null = this.readSetting("firstDayOfWeek");
this.firstDayOfWeek = content ?? "0"; this.firstDayOfWeek = content ?? "0";
}; };
readonly loadLanguage = (): void => { readonly loadLanguage = (): void => {
const content: string | null = this.readSetting("language"); const content: string | null = this.readSetting("language");
this.language = content ?? SettingsModel.getSystemLanguage(); this.language = content ?? SettingsModel.getSystemLanguage();
}; };
readonly loadTheme = (): void => { readonly loadTheme = (): void => {
const content: string | null = this.readSetting("theme"); const content: string | null = this.readSetting("theme");
this.theme = content ?? ThemeSettings.System; this.theme = content ?? ThemeSettings.System;
}; };
// init // init
constructor(public storageModel: StorageModel) { constructor(public storageModel: StorageModel) {
this.loadId(); this.loadId();
this.loadUsername(); this.loadUsername();
this.loadFirstDayofWeek(); this.loadFirstDayofWeek();
this.loadLanguage(); this.loadLanguage();
this.loadTheme(); this.loadTheme();
} }
static getSystemLanguage(): Languages { static getSystemLanguage(): Languages {
switch (navigator.language.substring(0, 2)) { switch (navigator.language.substring(0, 2)) {
case "de": case "de":
return Languages.German; return Languages.German;
case "es": case "es":
return Languages.Spanish; return Languages.Spanish;
default: default:
return Languages.English; return Languages.English;
} }
} }
} }
export enum Languages { export enum Languages {
English = "en", English = "en",
German = "de", German = "de",
Spanish = "es", Spanish = "es",
} }
export enum ThemeSettings { export enum ThemeSettings {
Dark = "dark", Dark = "dark",
Light = "light", Light = "light",
System = "system", System = "system",
Dynamic = "dynamic", Dynamic = "dynamic",
Black = "black", Black = "black",
} }
+55 -55
View File
@@ -216,52 +216,52 @@ export default class StorageModel {
return stringify(this.storageEntryTree); return stringify(this.storageEntryTree);
}; };
// upgrade // upgrade
readonly upgrade = (): void => { readonly upgrade = (): void => {
// function used to upgrade data versions // function used to upgrade data versions
} };
// init // init
constructor() { constructor() {
this.initializeTree(); this.initializeTree();
this.upgrade(); this.upgrade();
} }
// utility // utility
static getFileName = (pathComponents: string[]): string => { static getFileName = (pathComponents: string[]): string => {
return pathComponents[pathComponents.length - 1] || "\\"; return pathComponents[pathComponents.length - 1] || "\\";
}; };
static getFileNameFromString = (pathString: string): string => { static getFileNameFromString = (pathString: string): string => {
const pathComponents: string[] = const pathComponents: string[] =
this.stringToPathComponents(pathString); this.stringToPathComponents(pathString);
return pathComponents[pathComponents.length - 1] || "\\"; return pathComponents[pathComponents.length - 1] || "\\";
}; };
static pathComponentsToString = (...pathComponents: string[]): string => { static pathComponentsToString = (...pathComponents: string[]): string => {
return pathComponents return pathComponents
.filter((x) => x != "") .filter((x) => x != "")
.join(PATH_COMPONENT_SEPARATOR); .join(PATH_COMPONENT_SEPARATOR);
}; };
static stringToPathComponents = (string: string): string[] => { 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 => { static join = (...items: string[]): string => {
let allComponents: string[] = []; let allComponents: string[] = [];
for (const item of items) { for (const item of items) {
const parts = this.stringToPathComponents(item); const parts = this.stringToPathComponents(item);
allComponents.push(...parts); allComponents.push(...parts);
} }
return StorageModel.pathComponentsToString(...allComponents); return StorageModel.pathComponentsToString(...allComponents);
}; };
static getPath( static getPath(
locationName: StorageModelSubPaths, locationName: StorageModelSubPaths,
filePath: string[], filePath: string[],
): 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 { export enum StorageModelSubPaths {
Chat = "chat", Chat = "chat",
ConnectionModel = "connection", ConnectionModel = "connection",
NotificationModel = "notifications", NotificationModel = "notifications",
SettingsModel = "settings", SettingsModel = "settings",
ContactsModel = "contacts", ContactsModel = "contacts",
} }
export const filePaths = { export const filePaths = {
connectionModel: { connectionModel: {
socketAddress: ["socket-address"], socketAddress: ["socket-address"],
reconnectAddress: ["reconnect-address"], reconnectAddress: ["reconnect-address"],
outbox: ["outbox"], outbox: ["outbox"],
mailboxes: ["mailboxes"], mailboxes: ["mailboxes"],
previousAddresses: ["previous-addresses"], previousAddresses: ["previous-addresses"],
}, },
chat: { chat: {
base: [], base: [],
chatBase: (id: string) => [id], chatBase: (id: string) => [id],
info: (id: string) => [...filePaths.chat.chatBase(id), "info"], info: (id: string) => [...filePaths.chat.chatBase(id), "info"],
color: (id: string) => [...filePaths.chat.chatBase(id), "color"], color: (id: string) => [...filePaths.chat.chatBase(id), "color"],
messages: (id: string) => [...filePaths.chat.chatBase(id), "messages"], messages: (id: string) => [...filePaths.chat.chatBase(id), "messages"],
reactions: (id: string) => [ reactions: (id: string) => [
...filePaths.chat.chatBase(id), ...filePaths.chat.chatBase(id),
"reactions", "reactions",
], ],
previousFilter: (id: string) => [ previousFilter: (id: string) => [
...filePaths.chat.chatBase(id), ...filePaths.chat.chatBase(id),
"previous-filter", "previous-filter",
], ],
lastUsedPage: (id: string) => [ lastUsedPage: (id: string) => [
...filePaths.chat.chatBase(id), ...filePaths.chat.chatBase(id),
"last-used-page", "last-used-page",
], ],
files: (id: string) => [...filePaths.chat.chatBase(id), "files"], files: (id: string) => [...filePaths.chat.chatBase(id), "files"],
}, },
notificationModel: { notificationModel: {
base: [], base: [],
}, },
settingsModel: { settingsModel: {
username: ["user-name"], username: ["user-name"],
userid: ["user-id"], userid: ["user-id"],
firstDayOfWeek: ["first-day-of-week"], firstDayOfWeek: ["first-day-of-week"],
language: ["language"], language: ["language"],
theme: ["theme"], theme: ["theme"],
}, },
}; };
+4 -4
View File
@@ -37,10 +37,10 @@ export function ChatMessage(
{InlineReply(chatMessageViewModel)} {InlineReply(chatMessageViewModel)}
<div class="main tile"> <div class="main tile">
<div class="text-container"> <div class="text-container">
<span <span
class="sender-name ellipsis" class="sender-name ellipsis"
subscribe:innerText={chatMessageViewModel.contact.name} subscribe:innerText={chatMessageViewModel.contact.name}
></span> ></span>
<span <span
class="body" class="body"
subscribe:innerText={chatMessageViewModel.body} subscribe:innerText={chatMessageViewModel.body}
+3 -3
View File
@@ -1,9 +1,9 @@
import { ViewController } from "../viewController"; import { ViewController } from "../viewController";
import * as React from "../../react"; import * as React from "../../react";
import { ContactViewModel } from "../../ViewModel/Global/contactViewModel";
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel"; import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
import {ContactViewModel} from "../../ViewModel/Global/contactViewModel";
export function InlineReply(chatMessageViewModel: ChatMessageViewModel ) { export function InlineReply(chatMessageViewModel: ChatMessageViewModel) {
const reply = chatMessageViewModel.inlineReply; const reply = chatMessageViewModel.inlineReply;
if (reply == undefined) return <div></div>; if (reply == undefined) return <div></div>;
@@ -13,7 +13,7 @@ export function InlineReply(chatMessageViewModel: ChatMessageViewModel ) {
return ( return (
<div class="inline-reply" on:click={scroll}> <div class="inline-reply" on:click={scroll}>
<span subscribe:innerText={reply.contact.name}></span> <span subscribe:innerText={reply.contact.name}></span>
<b class="ellipsis" subscribe:innerText={reply.body}></b> <b class="ellipsis" subscribe:innerText={reply.body}></b>
</div> </div>
); );
+22 -19
View File
@@ -7,24 +7,27 @@ export function ReplyPreview(
chatMessageViewModel: ChatMessageViewModel, chatMessageViewModel: ChatMessageViewModel,
) { ) {
return ( return (
<div class="reply-preview"> <div class="reply-preview">
<div class="surface blur"> <div class="surface blur">
<span class="secondary" subscribe:innerText={chatMessageViewModel.contact.name}></span> <span
<b class="secondary"
class="ellipsis" subscribe:innerText={chatMessageViewModel.contact.name}
subscribe:innerText={chatMessageViewModel.body} ></span>
></b> <b
</div> class="ellipsis"
<button subscribe:innerText={chatMessageViewModel.body}
class="standard square blur" ></b>
on:click={chatMessageViewModel.cancelReply} </div>
aria-label={ <button
coreViewModel.translations.chatPage.message class="standard square blur"
.cancelReplyAudioLabel on:click={chatMessageViewModel.cancelReply}
} aria-label={
> coreViewModel.translations.chatPage.message
<span class="icon">close</span> .cancelReplyAudioLabel
</button> }
</div> >
<span class="icon">close</span>
</button>
</div>
); );
} }
+68 -4
View File
@@ -29,6 +29,11 @@ export function SettingsModal(
coreViewModel, coreViewModel,
settingsViewModel, settingsViewModel,
); );
case SettingsModalPages.Account:
return SettingsAccountPane(
coreViewModel,
settingsViewModel,
);
case SettingsModalPages.Regional: case SettingsModalPages.Regional:
return SettingsRegionalPane( return SettingsRegionalPane(
coreViewModel, coreViewModel,
@@ -79,6 +84,12 @@ function SettingsLeftPane(
SettingsModalPages.Appearance, SettingsModalPages.Appearance,
coreViewModel.translations.settings.pages.appearance, coreViewModel.translations.settings.pages.appearance,
)} )}
{SettingsPaneButton(
coreViewModel,
settingsViewModel,
SettingsModalPages.Account,
coreViewModel.translations.settings.pages.account,
)}
{SettingsPaneButton( {SettingsPaneButton(
coreViewModel, coreViewModel,
settingsViewModel, settingsViewModel,
@@ -121,7 +132,7 @@ function SettingsInfoPane(
<div class="flex-column gap"> <div class="flex-column gap">
{InfoTile( {InfoTile(
"build", "build",
coreViewModel.translations.settings.version, coreViewModel.translations.settings.about.version,
settingsViewModel.coreViewModel.version, settingsViewModel.coreViewModel.version,
)} )}
@@ -135,7 +146,10 @@ function SettingsInfoPane(
</button> </button>
<button class="standard" on:click={coreViewModel.checkUpdates}> <button class="standard" on:click={coreViewModel.checkUpdates}>
{coreViewModel.translations.settings.checkUpdatesButton} {
coreViewModel.translations.settings.about
.checkUpdatesButton
}
</button> </button>
</div> </div>
</div> </div>
@@ -151,7 +165,7 @@ function SettingsRegionalPane(
<h2>{coreViewModel.translations.settings.pages.regional}</h2> <h2>{coreViewModel.translations.settings.pages.regional}</h2>
<hr></hr> <hr></hr>
<h3>{coreViewModel.translations.settings.language}</h3> <h3>{coreViewModel.translations.settings.regional.language}</h3>
{OptionButtonList( {OptionButtonList(
new React.ListState<[string, string]>( new React.ListState<[string, string]>(
Object.values(Languages).map((x) => [languageNames[x], x]), Object.values(Languages).map((x) => [languageNames[x], x]),
@@ -160,7 +174,12 @@ function SettingsRegionalPane(
)} )}
<hr></hr> <hr></hr>
<h3>{coreViewModel.translations.settings.firstDayOfWeekLabel}</h3> <h3>
{
coreViewModel.translations.settings.regional
.firstDayOfWeekLabel
}
</h3>
{OptionButtonList( {OptionButtonList(
new React.ListState<[string, string]>( new React.ListState<[string, string]>(
coreViewModel.translations.regional.weekdays.full.map( coreViewModel.translations.regional.weekdays.full.map(
@@ -173,6 +192,51 @@ function SettingsRegionalPane(
); );
} }
function SettingsAccountPane(
coreViewModel: CoreViewModel,
settingsViewModel: SettingsViewModel,
) {
return (
<div class="slide-up">
<h2>{coreViewModel.translations.settings.pages.account}</h2>
<hr></hr>
<label class="tile flex-no">
<span class="icon">account_circle</span>
<div>
<span>
{
coreViewModel.translations.settings.account
.yourNameLabel
}
</span>
<input
placeholder={
coreViewModel.translations.settings.account
.yourNamePlaceholder
}
bind:value={settingsViewModel.usernameInput}
on:enter={settingsViewModel.setName}
></input>
</div>
</label>
<div class="flex-row justify-end width-input">
<button
class="width-50"
on:click={settingsViewModel.setName}
toggle:disabled={settingsViewModel.cannotSetName}
aria-label={
coreViewModel.translations.settings.account
.setNameButtonAudioLabel
}
>
{coreViewModel.translations.general.setButton}
<span class="icon">check</span>
</button>
</div>
</div>
);
}
function SettingsAppearancePane( function SettingsAppearancePane(
coreViewModel: CoreViewModel, coreViewModel: CoreViewModel,
settingsViewModel: SettingsViewModel, settingsViewModel: SettingsViewModel,
-33
View File
@@ -87,39 +87,6 @@ export function HomePage(
<hr></hr> <hr></hr>
<label class="tile flex-no">
<span class="icon">account_circle</span>
<div>
<span>
{coreViewModel.translations.homePage.yourNameLabel}
</span>
<input
placeholder={
coreViewModel.translations.homePage
.yourNamePlaceholder
}
bind:value={settingsViewModel.usernameInput}
on:enter={settingsViewModel.setName}
></input>
</div>
</label>
<div class="flex-row justify-end">
<button
class="width-50"
on:click={settingsViewModel.setName}
toggle:disabled={settingsViewModel.cannotSetName}
aria-label={
coreViewModel.translations.homePage
.setNameButtonAudioLabel
}
>
{coreViewModel.translations.general.setButton}
<span class="icon">check</span>
</button>
</div>
<hr></hr>
{HomePageButton( {HomePageButton(
settingsViewModel.showSettingsModal, settingsViewModel.showSettingsModal,
coreViewModel.translations.homePage.settingsButton, coreViewModel.translations.homePage.settingsButton,
+42 -33
View File
@@ -65,10 +65,6 @@ const englishTranslations = {
disconnectAudioLabel: "disconnect from server", disconnectAudioLabel: "disconnect from server",
manageConnectionsAudioLabel: "manage connections", manageConnectionsAudioLabel: "manage connections",
yourNameLabel: "Your name",
yourNamePlaceholder: "Jane Doe",
setNameButtonAudioLabel: "set name",
settingsButton: "Settings", settingsButton: "Settings",
manageStorageButton: "Manage storage", manageStorageButton: "Manage storage",
transferDataButton: "Transfer or export data", transferDataButton: "Transfer or export data",
@@ -90,6 +86,7 @@ const englishTranslations = {
settings: { settings: {
pages: { pages: {
appearance: "Appearance", appearance: "Appearance",
account: "Account",
regional: "Language & Region", regional: "Language & Region",
info: "About CT", info: "About CT",
}, },
@@ -100,13 +97,19 @@ const englishTranslations = {
system: "Device theme", system: "Device theme",
black: "Black", black: "Black",
}, },
account: {
version: "Version", yourNameLabel: "Your name",
language: "Language", yourNamePlaceholder: "Jane Doe",
setNameButtonAudioLabel: "set name",
firstDayOfWeekLabel: "First day of week", },
regional: {
checkUpdatesButton: "Check for updates", language: "Language",
firstDayOfWeekLabel: "First day of week",
},
about: {
version: "Version",
checkUpdatesButton: "Check for updates",
},
}, },
connectionModal: { connectionModal: {
@@ -414,10 +417,6 @@ export const allTranslations: {
disconnectAudioLabel: "vom Server trennen", disconnectAudioLabel: "vom Server trennen",
manageConnectionsAudioLabel: "Verbindungen verwalten", manageConnectionsAudioLabel: "Verbindungen verwalten",
yourNameLabel: "Dein Name",
yourNamePlaceholder: "Max Mustermann",
setNameButtonAudioLabel: "Name speichern",
settingsButton: "Einstellungen", settingsButton: "Einstellungen",
manageStorageButton: "Daten verwalten", manageStorageButton: "Daten verwalten",
transferDataButton: "Daten übertragen", transferDataButton: "Daten übertragen",
@@ -437,6 +436,7 @@ export const allTranslations: {
settings: { settings: {
pages: { pages: {
appearance: "Erscheinungsbild", appearance: "Erscheinungsbild",
account: "Konto",
regional: "Sprache & Region", regional: "Sprache & Region",
info: "Über CT", info: "Über CT",
}, },
@@ -447,13 +447,19 @@ export const allTranslations: {
system: "Geräteeinstellung", system: "Geräteeinstellung",
black: "Schwarz", black: "Schwarz",
}, },
account: {
version: "Version", yourNameLabel: "Dein Name",
language: "Sprache", yourNamePlaceholder: "Max Mustermann",
setNameButtonAudioLabel: "Namen festlegen",
firstDayOfWeekLabel: "Erster Wochentag", },
regional: {
checkUpdatesButton: "Nach updates suchen", language: "Sprache",
firstDayOfWeekLabel: "Erster Wochentag",
},
about: {
version: "Version",
checkUpdatesButton: "Nach Updates suchen",
},
}, },
connectionModal: { connectionModal: {
@@ -729,10 +735,6 @@ export const allTranslations: {
disconnectAudioLabel: "desconectar del servidor", disconnectAudioLabel: "desconectar del servidor",
manageConnectionsAudioLabel: "gestionar conexiones", manageConnectionsAudioLabel: "gestionar conexiones",
yourNameLabel: "Tu nombre",
yourNamePlaceholder: "Juan Pérez",
setNameButtonAudioLabel: "establecer nombre",
settingsButton: "Ajustes", settingsButton: "Ajustes",
manageStorageButton: "Gestionar almacenamiento", manageStorageButton: "Gestionar almacenamiento",
transferDataButton: "Enviar o exportar archivos", transferDataButton: "Enviar o exportar archivos",
@@ -752,6 +754,7 @@ export const allTranslations: {
settings: { settings: {
pages: { pages: {
appearance: "Aspecto", appearance: "Aspecto",
account: "Cuenta",
regional: "Idioma y Región", regional: "Idioma y Región",
info: "Sobre CT", info: "Sobre CT",
}, },
@@ -762,13 +765,19 @@ export const allTranslations: {
system: "Según dispositivo", system: "Según dispositivo",
black: "Negro", black: "Negro",
}, },
account: {
version: "Versión", yourNameLabel: "Tu nombre",
language: "Idioma", yourNamePlaceholder: "Juan Pérez",
setNameButtonAudioLabel: "establecer nombre",
firstDayOfWeekLabel: "Primer día de la semana", },
regional: {
checkUpdatesButton: "Buscar actualizaciones", language: "Idioma",
firstDayOfWeekLabel: "Primer día de la semana",
},
about: {
version: "Versión",
checkUpdatesButton: "Buscar actualisaciones",
},
}, },
connectionModal: { connectionModal: {
connectionModalHeadline: "Gestionar Conexiones", connectionModalHeadline: "Gestionar Conexiones",
+3 -3
View File
@@ -2,11 +2,11 @@ import ChatViewModel from "./chatViewModel";
import SettingsViewModel from "../Global/settingsViewModel"; import SettingsViewModel from "../Global/settingsViewModel";
import NotificationViewModel from "../Global/notificationViewModel"; import NotificationViewModel from "../Global/notificationViewModel";
import CoreViewModel from "../Global/coreViewModel"; import CoreViewModel from "../Global/coreViewModel";
import ContactListViewModel from "../Global/contactViewModel";
import ConnectionViewModel from "../Global/connectionViewModel"; import ConnectionViewModel from "../Global/connectionViewModel";
import * as React from "../../react"; import * as React from "../../react";
import { IndexManager } from "../../Model/Utility/utility"; import { IndexManager } from "../../Model/Utility/utility";
import ChatModel from "../../Model/Chat/chatModel"; import ChatModel from "../../Model/Chat/chatModel";
import ContactListViewModel from "../Global/contactViewModel";
export default class ChatListViewModel { export default class ChatListViewModel {
notificationViewModel: NotificationViewModel; notificationViewModel: NotificationViewModel;
@@ -62,7 +62,7 @@ export default class ChatListViewModel {
this.notificationViewModel, this.notificationViewModel,
this.connectionViewModel, this.connectionViewModel,
this, this,
this.contactListViewModel, this.contactListViewModel,
); );
}; };
@@ -99,7 +99,7 @@ export default class ChatListViewModel {
public readonly coreViewModel: CoreViewModel, public readonly coreViewModel: CoreViewModel,
public readonly settingsViewModel: SettingsViewModel, public readonly settingsViewModel: SettingsViewModel,
public readonly connectionViewModel: ConnectionViewModel, public readonly connectionViewModel: ConnectionViewModel,
public readonly contactListViewModel: ContactListViewModel, public readonly contactListViewModel: ContactListViewModel,
) { ) {
this.notificationViewModel = new NotificationViewModel(this); this.notificationViewModel = new NotificationViewModel(this);
+2 -2
View File
@@ -1,5 +1,6 @@
import MessagePageViewModel from "../Pages/messagePageViewModel"; import MessagePageViewModel from "../Pages/messagePageViewModel";
import CoreViewModel from "../Global/coreViewModel"; import CoreViewModel from "../Global/coreViewModel";
import { ContactViewModel } from "../Global/contactViewModel";
import * as React from "../../react"; import * as React from "../../react";
import { import {
ChatMessage, ChatMessage,
@@ -7,7 +8,6 @@ import {
ChatMessageStatuses, ChatMessageStatuses,
ReactionSymbols, ReactionSymbols,
} from "../../Model/Chat/chatModel"; } from "../../Model/Chat/chatModel";
import {ContactViewModel} from "../Global/contactViewModel";
export default class ChatMessageViewModel { export default class ChatMessageViewModel {
// data // data
@@ -203,7 +203,7 @@ export default class ChatMessageViewModel {
public readonly coreViewModel: CoreViewModel, public readonly coreViewModel: CoreViewModel,
public readonly messagePageViewModel: MessagePageViewModel, public readonly messagePageViewModel: MessagePageViewModel,
chatMessage: ChatMessage, chatMessage: ChatMessage,
public contact: ContactViewModel, public contact: ContactViewModel,
sentByUser: boolean, sentByUser: boolean,
) { ) {
this.chatMessage = chatMessage; this.chatMessage = chatMessage;
+3 -3
View File
@@ -8,6 +8,7 @@ import CalendarPageViewModel, {
import SettingsViewModel from "../Global/settingsViewModel"; import SettingsViewModel from "../Global/settingsViewModel";
import NotificationViewModel from "../Global/notificationViewModel"; import NotificationViewModel from "../Global/notificationViewModel";
import CoreViewModel, { Context, ContextHost } from "../Global/coreViewModel"; import CoreViewModel, { Context, ContextHost } from "../Global/coreViewModel";
import ContactListViewModel from "../Global/contactViewModel";
import ConnectionViewModel from "../Global/connectionViewModel"; import ConnectionViewModel from "../Global/connectionViewModel";
import * as React from "../../react"; import * as React from "../../react";
import { Colors } from "../../colors"; import { Colors } from "../../colors";
@@ -21,7 +22,6 @@ import ChatModel, {
ChatMessage, ChatMessage,
ChatMessageReaction, ChatMessageReaction,
} from "../../Model/Chat/chatModel"; } from "../../Model/Chat/chatModel";
import ContactListViewModel from "../Global/contactViewModel";
export default class ChatViewModel extends ContextHost<ChatPageTypes> { export default class ChatViewModel extends ContextHost<ChatPageTypes> {
calendarViewModel: CalendarPageViewModel; calendarViewModel: CalendarPageViewModel;
@@ -150,7 +150,7 @@ export default class ChatViewModel extends ContextHost<ChatPageTypes> {
public readonly notificationViewModel: NotificationViewModel, public readonly notificationViewModel: NotificationViewModel,
public readonly connectionViewModel: ConnectionViewModel, public readonly connectionViewModel: ConnectionViewModel,
public readonly chatListViewModel: ChatListViewModel, public readonly chatListViewModel: ChatListViewModel,
public readonly contactListViewModel: ContactListViewModel, public readonly contactListViewModel: ContactListViewModel,
) { ) {
super("chat", coreViewModel); super("chat", coreViewModel);
@@ -169,7 +169,7 @@ export default class ChatViewModel extends ContextHost<ChatPageTypes> {
this.messagePageViewModel = new MessagePageViewModel( this.messagePageViewModel = new MessagePageViewModel(
this.coreViewModel, this.coreViewModel,
this, this,
this.contactListViewModel, this.contactListViewModel,
); );
this.settingsPageViewModel = new SettingsPageViewModel( this.settingsPageViewModel = new SettingsPageViewModel(
this.coreViewModel, this.coreViewModel,
+41 -32
View File
@@ -1,6 +1,6 @@
import ContactListModel from "../../Model/Global/contactListModel";
import * as React from "../../react";
import SettingsViewModel from "./settingsViewModel"; import SettingsViewModel from "./settingsViewModel";
import * as React from "../../react";
import ContactListModel from "../../Model/Global/contactListModel";
export default class ContactListViewModel { export default class ContactListViewModel {
// state // state
@@ -8,43 +8,49 @@ export default class ContactListViewModel {
// main // main
updateContact = (id: string, name: string): ContactViewModel => { updateContact = (id: string, name: string): ContactViewModel => {
if (id == this.settingsViewModel.settingsModel.userid) { if (id == this.settingsViewModel.settingsModel.userid) {
this.settingsViewModel.setName(name); this.settingsViewModel.setName(name);
} }
const match = this.contacts.get(id); const match = this.contacts.get(id);
if (match) { if (match) {
match.name.value = name; match.name.value = name;
return match; return match;
} }
const vm = new ContactViewModel(id, name); const vm = new ContactViewModel(id, name);
this.contacts.set(id, vm); this.contacts.set(id, vm);
return vm; return vm;
} };
handleContact = (contact: [string, string]): void => { handleContact = (contact: [string, string]): void => {
this.updateContact(...contact); this.updateContact(...contact);
} };
unwrapContact = (id: string, knownName: string): ContactViewModel => { unwrapContact = (id: string, knownName: string): ContactViewModel => {
if (this.contacts.has(id)) if (this.contacts.has(id)) return this.contacts.get(id)!;
return this.contacts.get(id)! return this.updateContact(id, knownName);
return this.updateContact(id, knownName); };
}
// load // load
loadData = (): void => { loadData = (): void => {
const contacts: [string, string][] = this.contactListModel.loadContacts(); const contacts: [string, string][] =
for (const contact of contacts) { this.contactListModel.loadContacts();
const vm = new ContactViewModel(...contact); for (const contact of contacts) {
this.contacts.set(vm.id, vm); const vm = new ContactViewModel(...contact);
} this.contacts.set(vm.id, vm);
} }
};
// init // init
constructor(public contactListModel: ContactListModel, public settingsViewModel: SettingsViewModel) { constructor(
this.loadData(); public contactListModel: ContactListModel,
this.contactListModel.contactHandlerManager.setHandler("contact-list-view-model", this.handleContact); 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<string> = new React.State(""); name: React.State<string> = new React.State("");
// init // init
constructor(public id: string, name: string) { constructor(
this.name.value = name; public id: string,
name: string,
) {
this.name.value = name;
} }
} }
+107 -100
View File
@@ -1,7 +1,10 @@
import CoreViewModel, { Context } from "./coreViewModel"; import CoreViewModel, { Context } from "./coreViewModel";
import * as React from "../../react"; import * as React from "../../react";
import { CommonKeys } from "../../View/keystrokes"; 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 { export default class SettingsViewModel extends Context {
// state // state
@@ -31,154 +34,158 @@ export default class SettingsViewModel extends Context {
// methods // methods
setName = (name?: string): void => { setName = (name?: string): void => {
if (typeof name == "string") { if (typeof name == "string") {
this.usernameInput.value = name; this.usernameInput.value = name;
} }
this.coreViewModel.settingsModel.setName(this.usernameInput.value); this.coreViewModel.settingsModel.setName(this.usernameInput.value);
this.username.value = this.coreViewModel.settingsModel.username; this.username.value = this.coreViewModel.settingsModel.username;
this.usernameInput.callSubscriptions(); this.usernameInput.callSubscriptions();
}; };
setFirstDayofWeek = (): void => { setFirstDayofWeek = (): void => {
this.coreViewModel.settingsModel.setFirstDayOfWeek( this.coreViewModel.settingsModel.setFirstDayOfWeek(
this.firstDayOfWeek.value, this.firstDayOfWeek.value,
); );
}; };
showSettingsModal = (): void => { showSettingsModal = (): void => {
this.coreViewModel.context = this; this.coreViewModel.context = this;
this.isShowingSettingsModal.value = true; this.isShowingSettingsModal.value = true;
}; };
showModalPage = (page: SettingsModalPages): void => { showModalPage = (page: SettingsModalPages): void => {
this.selectedModalPage.value = page; this.selectedModalPage.value = page;
}; };
// view // view
applyTheme = (): void => { applyTheme = (): void => {
let theme: string = this.theme.value; let theme: string = this.theme.value;
if (theme == ThemeSettings.System) if (theme == ThemeSettings.System)
theme = SettingsViewModel.getSystemTheme(); theme = SettingsViewModel.getSystemTheme();
switch (theme) { switch (theme) {
case ThemeSettings.Dynamic: case ThemeSettings.Dynamic:
const scene = this.selectDynamicScene(); const scene = this.selectDynamicScene();
this.setScene(scene); this.setScene(scene);
break; break;
case ThemeSettings.Dark: case ThemeSettings.Dark:
this.setScene(DynamicSceneNight); this.setScene(DynamicSceneNight);
break; break;
case ThemeSettings.Black: case ThemeSettings.Black:
this.setScene(DynamicSceneBlack); this.setScene(DynamicSceneBlack);
break; break;
default: default:
this.setScene(DynamicSceneDay); this.setScene(DynamicSceneDay);
} }
}; };
selectDynamicScene = (): DynamicScene => { selectDynamicScene = (): DynamicScene => {
let hour = new Date().getHours(); let hour = new Date().getHours();
if (hour >= 21 || hour <= 6) { if (hour >= 21 || hour <= 6) {
return DynamicSceneNight; return DynamicSceneNight;
} else if (hour > 6 && hour < 10) { } else if (hour > 6 && hour < 10) {
return DynamicSceneSunrise; return DynamicSceneSunrise;
} else if (hour < 14) { } else if (hour < 14) {
return DynamicSceneDay; return DynamicSceneDay;
} else if (hour < 17) { } else if (hour < 17) {
return DynamicSceneAfternoon; return DynamicSceneAfternoon;
} else { } else {
return DynamicSceneSunset; return DynamicSceneSunset;
} }
}; };
setScene = (scene: DynamicScene): void => { setScene = (scene: DynamicScene): void => {
document.body.style.setProperty( document.body.style.setProperty(
"--backdrop-grass-filter", "--backdrop-grass-filter",
`brightness(${scene.brightness})`, `brightness(${scene.brightness})`,
); );
function setSkyColor( function setSkyColor(
tone: 1 | 2, tone: 1 | 2,
hue: number, hue: number,
saturation: number, saturation: number,
luma: number, luma: number,
) { ) {
document.body.style.setProperty( document.body.style.setProperty(
`--sky-${tone}`, `--sky-${tone}`,
`hsl(${hue}, ${saturation}%, ${luma}%)`, `hsl(${hue}, ${saturation}%, ${luma}%)`,
); );
} }
setSkyColor(1, scene.hue1, scene.saturation, scene.luma); setSkyColor(1, scene.hue1, scene.saturation, scene.luma);
setSkyColor(2, scene.hue2, scene.saturation - 10, scene.luma - 10); setSkyColor(2, scene.hue2, scene.saturation - 10, scene.luma - 10);
document.body.setAttribute("theme", scene.baseTheme); document.body.setAttribute("theme", scene.baseTheme);
}; };
// exit // exit
close = (): void => { close = (): void => {
this.coreViewModel.closeContext(this.contextId); this.coreViewModel.closeContext(this.contextId);
}; };
handleContextClose = (): void => { handleContextClose = (): void => {
this.isShowingSettingsModal.value = false; this.isShowingSettingsModal.value = false;
if (this.requiresReload.value == true) { if (this.requiresReload.value == true) {
window.location.reload(); window.location.reload();
} }
}; };
// init // init
constructor(public readonly coreViewModel: CoreViewModel, public readonly settingsModel: SettingsModel) { constructor(
super("settings"); public readonly coreViewModel: CoreViewModel,
public readonly settingsModel: SettingsModel,
) {
super("settings");
this.username.value = coreViewModel.settingsModel.username; this.username.value = coreViewModel.settingsModel.username;
this.usernameInput.value = coreViewModel.settingsModel.username; this.usernameInput.value = coreViewModel.settingsModel.username;
this.firstDayOfWeek.value = coreViewModel.settingsModel.firstDayOfWeek; this.firstDayOfWeek.value = coreViewModel.settingsModel.firstDayOfWeek;
this.language.value = coreViewModel.settingsModel.language; this.language.value = coreViewModel.settingsModel.language;
this.theme.value = coreViewModel.settingsModel.theme; this.theme.value = coreViewModel.settingsModel.theme;
// subscriptions // subscriptions
this.firstDayOfWeek.subscribe(this.setFirstDayofWeek); this.firstDayOfWeek.subscribe(this.setFirstDayofWeek);
this.language.subscribeSilent((newValue) => { this.language.subscribeSilent((newValue) => {
this.coreViewModel.settingsModel.setLanguage(newValue); this.coreViewModel.settingsModel.setLanguage(newValue);
this.requiresReload.value = true; this.requiresReload.value = true;
}); });
this.theme.subscribeSilent((newValue) => { this.theme.subscribeSilent((newValue) => {
this.coreViewModel.settingsModel.setTheme(newValue); this.coreViewModel.settingsModel.setTheme(newValue);
}); });
// set theme // set theme
this.theme.subscribe(() => { this.theme.subscribe(() => {
this.applyTheme(); this.applyTheme();
}); });
SettingsViewModel.generateThemeMedia().addEventListener("change", () => SettingsViewModel.generateThemeMedia().addEventListener("change", () =>
this.applyTheme(), this.applyTheme(),
); );
const seconds = new Date().getSeconds(); const seconds = new Date().getSeconds();
const secondsUntilNewMinute = 60 - seconds; const secondsUntilNewMinute = 60 - seconds;
setTimeout(() => { setTimeout(() => {
setInterval(this.applyTheme, 1000 * 60); setInterval(this.applyTheme, 1000 * 60);
}, secondsUntilNewMinute); }, secondsUntilNewMinute);
// keystrokes // keystrokes
this.registerKeyStroke(CommonKeys.CloseOrCancel, this.close); this.registerKeyStroke(CommonKeys.CloseOrCancel, this.close);
} }
static generateThemeMedia(): MediaQueryList { static generateThemeMedia(): MediaQueryList {
return window.matchMedia("(prefers-color-scheme: dark)"); return window.matchMedia("(prefers-color-scheme: dark)");
} }
static getSystemTheme(): string { static getSystemTheme(): string {
const media = SettingsViewModel.generateThemeMedia(); const media = SettingsViewModel.generateThemeMedia();
return media.matches == true ? ThemeSettings.Dark : ThemeSettings.Light; return media.matches == true ? ThemeSettings.Dark : ThemeSettings.Light;
} }
} }
export enum SettingsModalPages { export enum SettingsModalPages {
Appearance, Appearance,
Regional, Account,
Info, Regional,
Info,
} }
export interface DynamicScene { export interface DynamicScene {
+92 -89
View File
@@ -1,5 +1,6 @@
import SearchViewModel from "../Utility/searchViewModel"; import SearchViewModel from "../Utility/searchViewModel";
import CoreViewModel, { Context } from "../Global/coreViewModel"; import CoreViewModel, { Context } from "../Global/coreViewModel";
import ContactListViewModel from "../Global/contactViewModel";
import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel"; import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel";
import ChatMessageViewModel from "../Chat/chatMessageViewModel"; import ChatMessageViewModel from "../Chat/chatMessageViewModel";
import * as React from "../../react"; import * as React from "../../react";
@@ -9,7 +10,6 @@ import {
ChatMessageReaction, ChatMessageReaction,
ReactionSymbols, ReactionSymbols,
} from "../../Model/Chat/chatModel"; } from "../../Model/Chat/chatModel";
import ContactListViewModel from "../Global/contactViewModel";
export default class MessagePageViewModel extends Context { export default class MessagePageViewModel extends Context {
// state // state
@@ -107,138 +107,141 @@ export default class MessagePageViewModel extends Context {
// view // view
showChatMessage = (chatMessage: ChatMessage): void => { showChatMessage = (chatMessage: ChatMessage): void => {
const contact = this.contactListViewModel.unwrapContact(chatMessage.senderId, chatMessage.senderName); const contact = this.contactListViewModel.unwrapContact(
const chatMessageViewModel = new ChatMessageViewModel( chatMessage.senderId,
this.coreViewModel, chatMessage.senderName,
this, );
chatMessage, const chatMessageViewModel = new ChatMessageViewModel(
contact, this.coreViewModel,
chatMessage.senderId == this,
this.chatViewModel.settingsViewModel.settingsModel.userid chatMessage,
); contact,
chatMessage.senderId ==
this.chatViewModel.settingsViewModel.settingsModel.userid,
);
const existingChatMessageViewModel: ChatMessageViewModel | undefined = const existingChatMessageViewModel: ChatMessageViewModel | undefined =
this.chatMessageViewModels.value.get(chatMessage.fileId); this.chatMessageViewModels.value.get(chatMessage.fileId);
if (existingChatMessageViewModel != undefined) { if (existingChatMessageViewModel != undefined) {
existingChatMessageViewModel.body.value = chatMessage.body; existingChatMessageViewModel.body.value = chatMessage.body;
existingChatMessageViewModel.status.value = chatMessage.status; existingChatMessageViewModel.status.value = chatMessage.status;
} else { } else {
this.chatMessageViewModels.set( this.chatMessageViewModels.set(
chatMessage.fileId, chatMessage.fileId,
chatMessageViewModel, chatMessageViewModel,
); );
} }
}; };
handleReaction = (reaction: ChatMessageReaction): void => { handleReaction = (reaction: ChatMessageReaction): void => {
const messageViewModel: ChatMessageViewModel | undefined = const messageViewModel: ChatMessageViewModel | undefined =
this.chatMessageViewModels.value.get(reaction.messageId); this.chatMessageViewModels.value.get(reaction.messageId);
if (messageViewModel == undefined) return; if (messageViewModel == undefined) return;
messageViewModel.handleReaction(reaction); messageViewModel.handleReaction(reaction);
}; };
showFilterModal = (): void => { showFilterModal = (): void => {
this.isFilterModalOpen.value = true; this.isFilterModalOpen.value = true;
}; };
hideFilterModal = (): void => { hideFilterModal = (): void => {
this.isFilterModalOpen.value = false; this.isFilterModalOpen.value = false;
}; };
revokeReactionFilter = (persist: boolean = true): void => { revokeReactionFilter = (persist: boolean = true): void => {
this.reactionFilter.value = undefined; this.reactionFilter.value = undefined;
if (persist) this.chatViewModel.chatModel.storeFilter(""); if (persist) this.chatViewModel.chatModel.storeFilter("");
}; };
setReactionFilter = (content: ReactionSymbols): void => { setReactionFilter = (content: ReactionSymbols): void => {
this.reactionFilter.value = content; this.reactionFilter.value = content;
this.chatViewModel.chatModel.storeFilter(content); this.chatViewModel.chatModel.storeFilter(content);
}; };
resetFilter = (): void => { resetFilter = (): void => {
this.revokeReactionFilter(); this.revokeReactionFilter();
this.searchViewModel.search(""); this.searchViewModel.search("");
}; };
setReplyView = (message: ChatMessageViewModel): void => { setReplyView = (message: ChatMessageViewModel): void => {
this.replyViewSelectedMessage.value = message; this.replyViewSelectedMessage.value = message;
this.revokeReactionFilter(false); this.revokeReactionFilter(false);
this.setReply(message, false); this.setReply(message, false);
}; };
resetReplyView = (): void => { resetReplyView = (): void => {
this.replyViewSelectedMessage.value = undefined; this.replyViewSelectedMessage.value = undefined;
this.resetReply(false); this.resetReply(false);
this.restoreFilter(); this.restoreFilter();
}; };
setFocus = (): void => { setFocus = (): void => {
this.focusSetter.callSubscriptions(); this.focusSetter.callSubscriptions();
}; };
// load // load
loadData = (): void => { loadData = (): void => {
this.chatMessageViewModels.clear(); this.chatMessageViewModels.clear();
for (const chatMessage of this.chatViewModel.chatModel.messages) { for (const chatMessage of this.chatViewModel.chatModel.messages) {
this.showChatMessage(chatMessage); this.showChatMessage(chatMessage);
} }
for (const reaction of this.chatViewModel.chatModel.reactions) { for (const reaction of this.chatViewModel.chatModel.reactions) {
this.handleReaction(reaction); this.handleReaction(reaction);
} }
}; };
restoreFilter = (): void => { restoreFilter = (): void => {
const previousFilter = this.chatViewModel.chatModel.getFilter() as any; const previousFilter = this.chatViewModel.chatModel.getFilter() as any;
if (Object.values(ReactionSymbols).includes(previousFilter)) if (Object.values(ReactionSymbols).includes(previousFilter))
this.setReactionFilter(previousFilter); this.setReactionFilter(previousFilter);
}; };
// init // init
constructor( constructor(
public readonly coreViewModel: CoreViewModel, public readonly coreViewModel: CoreViewModel,
public readonly chatViewModel: ChatViewModel, public readonly chatViewModel: ChatViewModel,
public readonly contactListViewModel: ContactListViewModel, public readonly contactListViewModel: ContactListViewModel,
) { ) {
super("message-page"); super("message-page");
this.restoreFilter(); this.restoreFilter();
// states // states
this.cannotSendMessage = React.createProxyState( this.cannotSendMessage = React.createProxyState(
[ [
this.chatViewModel.settingsViewModel.username, this.chatViewModel.settingsViewModel.username,
this.composingMessage, this.composingMessage,
], ],
() => () =>
this.chatViewModel.settingsViewModel.username.value == "" || this.chatViewModel.settingsViewModel.username.value == "" ||
this.composingMessage.value == "", this.composingMessage.value == "",
); );
this.searchViewModel = new SearchViewModel( this.searchViewModel = new SearchViewModel(
this.chatMessageViewModels, this.chatMessageViewModels,
this.filteredMessageViewModels, this.filteredMessageViewModels,
(chatMessageViewModel) => [chatMessageViewModel.body.value], (chatMessageViewModel) => [chatMessageViewModel.body.value],
new React.ListState(), new React.ListState(),
); );
this.isFilterActive = React.createProxyState( this.isFilterActive = React.createProxyState(
[this.searchViewModel.appliedQuery, this.reactionFilter], [this.searchViewModel.appliedQuery, this.reactionFilter],
() => () =>
this.searchViewModel.appliedQuery.value != "" || this.searchViewModel.appliedQuery.value != "" ||
this.reactionFilter.value != undefined, this.reactionFilter.value != undefined,
); );
// keystrokes // keystrokes
this.registerKeyStroke(CommonKeys.Filter, this.showFilterModal); this.registerKeyStroke(CommonKeys.Filter, this.showFilterModal);
this.registerKeyStroke(CommonKeys.CloseOrCancel, () => { this.registerKeyStroke(CommonKeys.CloseOrCancel, () => {
if (this.isFilterModalOpen.value == true) { if (this.isFilterModalOpen.value == true) {
this.hideFilterModal(); this.hideFilterModal();
} else { } else {
this.replyingMessage.value = undefined; this.replyingMessage.value = undefined;
} }
}); });
this.registerKeyStroke(CommonKeys.Reset, this.resetFilter); this.registerKeyStroke(CommonKeys.Reset, this.resetFilter);
this.registerKeyStroke(CommonKeys.Create, this.setFocus); this.registerKeyStroke(CommonKeys.Create, this.setFocus);
this.chatViewModel.registerContext(ChatPageTypes.Messages, this); this.chatViewModel.registerContext(ChatPageTypes.Messages, this);
} }
} }
+6 -3
View File
@@ -7,6 +7,7 @@ import StorageViewModel from "./ViewModel/Global/storageViewModel";
import SettingsViewModel from "./ViewModel/Global/settingsViewModel"; import SettingsViewModel from "./ViewModel/Global/settingsViewModel";
import FileTransferViewModel from "./ViewModel/Global/fileTransferViewModel"; import FileTransferViewModel from "./ViewModel/Global/fileTransferViewModel";
import CoreViewModel from "./ViewModel/Global/coreViewModel"; import CoreViewModel from "./ViewModel/Global/coreViewModel";
import ContactListViewModel from "./ViewModel/Global/contactViewModel";
import ConnectionViewModel from "./ViewModel/Global/connectionViewModel"; import ConnectionViewModel from "./ViewModel/Global/connectionViewModel";
import ChatListViewModel from "./ViewModel/Chat/chatListViewModel"; import ChatListViewModel from "./ViewModel/Chat/chatListViewModel";
import { HomePage } from "./View/homePage"; import { HomePage } from "./View/homePage";
@@ -18,10 +19,9 @@ import { ConnectionModal } from "./View/Modals/connectionModal";
import StorageModel from "./Model/Global/storageModel"; import StorageModel from "./Model/Global/storageModel";
import SettingsModel from "./Model/Global/settingsModel"; import SettingsModel from "./Model/Global/settingsModel";
import FileTransferModel from "./Model/Global/fileTransferModel"; import FileTransferModel from "./Model/Global/fileTransferModel";
import ContactListModel from "./Model/Global/contactListModel";
import ConnectionModel from "./Model/Global/connectionModel"; import ConnectionModel from "./Model/Global/connectionModel";
import ChatListModel from "./Model/Chat/chatListModel"; import ChatListModel from "./Model/Chat/chatListModel";
import ContactListModel from "./Model/Global/contactListModel";
import ContactListViewModel from "./ViewModel/Global/contactViewModel";
// models // models
const storageModel = new StorageModel(); const storageModel = new StorageModel();
@@ -47,7 +47,10 @@ const coreViewModel = new CoreViewModel(
const storageViewModel = new StorageViewModel(coreViewModel); const storageViewModel = new StorageViewModel(coreViewModel);
const settingsViewModel = new SettingsViewModel(coreViewModel, settingsModel); const settingsViewModel = new SettingsViewModel(coreViewModel, settingsModel);
const contactListViewModel = new ContactListViewModel(contactListModel, settingsViewModel); const contactListViewModel = new ContactListViewModel(
contactListModel,
settingsViewModel,
);
const connectionViewModel = new ConnectionViewModel(coreViewModel); const connectionViewModel = new ConnectionViewModel(coreViewModel);
const chatListViewModel = new ChatListViewModel( const chatListViewModel = new ChatListViewModel(
coreViewModel, coreViewModel,