add settingsViewModel, implement name and first weekday

This commit is contained in:
marlon-erler
2024-07-20 02:00:00 +02:00
parent 7ceb77e032
commit 6d7d9e5250
6 changed files with 214 additions and 15 deletions
+133 -3
View File
@@ -163,6 +163,45 @@
}; };
// node_modules/bloatless-react/index.ts // node_modules/bloatless-react/index.ts
var State = class {
_value;
_bindings = /* @__PURE__ */ new Set();
// init
constructor(initialValue) {
this._value = initialValue;
}
// value
get value() {
return this._value;
}
set value(newValue) {
if (this._value == newValue) return;
this._value = newValue;
this.callSubscriptions();
}
// subscriptions
callSubscriptions() {
this._bindings.forEach((fn) => fn(this._value));
}
subscribe(fn) {
this._bindings.add(fn);
fn(this._value);
}
subscribeSilent(fn) {
this._bindings.add(fn);
}
// stringify
toString() {
return JSON.stringify(this._value);
}
};
function createProxyState(statesToSubscibe, fn) {
const proxyState = new State(fn());
statesToSubscibe.forEach(
(state) => state.subscribe(() => proxyState.value = fn())
);
return proxyState;
}
function createElement(tagName, attributes = {}, ...children) { function createElement(tagName, attributes = {}, ...children) {
const element = document.createElement(tagName); const element = document.createElement(tagName);
if (attributes != null) if (attributes != null)
@@ -263,6 +302,11 @@
return element; return element;
} }
// src/View/Components/option.tsx
function Option(text, value, selectedOnCreate) {
return /* @__PURE__ */ createElement("option", { value, "toggle:selected": selectedOnCreate }, text);
}
// src/View/translations.ts // src/View/translations.ts
var englishTranslations = { var englishTranslations = {
general: { general: {
@@ -326,7 +370,7 @@
var translations = allTranslations[language] || allTranslations.en; var translations = allTranslations[language] || allTranslations.en;
// src/View/homePage.tsx // src/View/homePage.tsx
function HomePage() { function HomePage(settingsViewModel2) {
const overviewSection = /* @__PURE__ */ createElement("div", { id: "overview-section" }, /* @__PURE__ */ createElement("h2", null, translations.homePage.overviewHeadline), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "cell_tower"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.homePage.serverAddress), /* @__PURE__ */ createElement("input", null))), /* @__PURE__ */ createElement("div", { class: "flex-row" }, /* @__PURE__ */ createElement( const overviewSection = /* @__PURE__ */ createElement("div", { id: "overview-section" }, /* @__PURE__ */ createElement("h2", null, translations.homePage.overviewHeadline), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "cell_tower"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.homePage.serverAddress), /* @__PURE__ */ createElement("input", null))), /* @__PURE__ */ createElement("div", { class: "flex-row" }, /* @__PURE__ */ createElement(
"button", "button",
{ {
@@ -348,7 +392,28 @@
"aria-label": translations.homePage.connectAudioLabel "aria-label": translations.homePage.connectAudioLabel
}, },
/* @__PURE__ */ createElement("span", { class: "icon" }, "link") /* @__PURE__ */ createElement("span", { class: "icon" }, "link")
)), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "inbox"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", null, translations.homePage.mailboxHeadline), /* @__PURE__ */ createElement("span", { class: "error" }, translations.homePage.mailboxDisabled))), /* @__PURE__ */ createElement("div", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "outbox"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", null, translations.homePage.outboxHeadline), /* @__PURE__ */ createElement("span", { class: "success" }, translations.homePage.outboxAllItemsSent))), /* @__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, translations.homePage.yourNameLabel), /* @__PURE__ */ createElement("input", null))), /* @__PURE__ */ createElement("div", { class: "flex-row justify-end" }, /* @__PURE__ */ createElement("button", { class: "width-50" }, translations.homePage.setNameButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "check"))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "calendar_month"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.homePage.firstDayOfWeekLabel), /* @__PURE__ */ createElement("select", null))), /* @__PURE__ */ createElement("div", { class: "mobile-only" }, /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-row justify-end" }, /* @__PURE__ */ createElement("button", { class: "ghost width-50", "on:click": scrollToChat }, translations.homePage.scrollToChatButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_forward"))))); )), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "inbox"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", null, translations.homePage.mailboxHeadline), /* @__PURE__ */ createElement("span", { class: "error" }, translations.homePage.mailboxDisabled))), /* @__PURE__ */ createElement("div", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "outbox"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", null, translations.homePage.outboxHeadline), /* @__PURE__ */ createElement("span", { class: "success" }, translations.homePage.outboxAllItemsSent))), /* @__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, translations.homePage.yourNameLabel), /* @__PURE__ */ createElement(
"input",
{
"bind:value": settingsViewModel2.nameInput,
"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
},
translations.homePage.setNameButton,
/* @__PURE__ */ createElement("span", { class: "icon" }, "check")
)), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "calendar_month"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.homePage.firstDayOfWeekLabel), /* @__PURE__ */ createElement("select", { "bind:value": settingsViewModel2.firstDayOfWeekInput }, ...translations.regional.weekdays.full.map(
(weekdayName, i) => Option(
weekdayName,
i.toString(),
i == settingsViewModel2.firstDayOfWeekInput.value
)
)), /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_drop_down"))), /* @__PURE__ */ createElement("div", { class: "mobile-only" }, /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-row justify-end" }, /* @__PURE__ */ createElement("button", { class: "ghost width-50", "on:click": scrollToChat }, translations.homePage.scrollToChatButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_forward")))));
const chatSection = /* @__PURE__ */ createElement("div", { id: "chat-section" }, /* @__PURE__ */ createElement("h2", null, translations.homePage.chatsHeadline), /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement( const chatSection = /* @__PURE__ */ createElement("div", { id: "chat-section" }, /* @__PURE__ */ createElement("h2", null, translations.homePage.chatsHeadline), /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement(
"input", "input",
{ {
@@ -457,13 +522,78 @@
chatOutbox: (id) => `${DATA_VERSION}/chat/${id}/outbox` chatOutbox: (id) => `${DATA_VERSION}/chat/${id}/outbox`
}; };
// src/Model/settingsModel.ts
var SettingsModel = class {
storageModel;
username;
firstDayOfWeek;
// set
setName(newValue) {
this.username = newValue;
const path = storageKeys.username;
this.storageModel.store(path, newValue);
}
setFirstDayOfWeek(newValue) {
this.firstDayOfWeek = newValue;
const path = storageKeys.firstDayOfWeek;
this.storageModel.storeStringifiable(path, newValue);
}
// restore
restoreUsername() {
const path = storageKeys.username;
const content = this.storageModel.restore(path);
this.username = content ?? "";
}
restoreFirstDayofWeek() {
const path = storageKeys.firstDayOfWeek;
const content = this.storageModel.restoreStringifiable(path);
this.firstDayOfWeek = content ?? 0;
}
// init
constructor(storageModel2) {
this.storageModel = storageModel2;
this.restoreUsername();
this.restoreFirstDayofWeek();
}
};
// src/ViewModel/settingsViewModel.ts
var SettingsViewModel = class {
settingsModel;
// state
nameInput = new State("");
firstDayOfWeekInput = new State(0);
// toggles
cannotSetName = createProxyState(
[this.nameInput],
() => this.nameInput.value == "" || this.nameInput.value == this.settingsModel.username
);
// set
setName = () => {
this.settingsModel.setName(this.nameInput.value);
this.nameInput.callSubscriptions();
};
setFirstDayofWeek = () => {
this.settingsModel.setFirstDayOfWeek(this.firstDayOfWeekInput.value);
};
// init
constructor(settingsModel2) {
this.settingsModel = settingsModel2;
this.nameInput.value = settingsModel2.username;
this.firstDayOfWeekInput.value = settingsModel2.firstDayOfWeek;
this.firstDayOfWeekInput.subscribe(this.setFirstDayofWeek);
}
};
// src/index.tsx // src/index.tsx
var storageModel = new StorageModel(); var storageModel = new StorageModel();
var settingsModel = new SettingsModel(storageModel);
var connectionModel = new ConnectionModel({ var connectionModel = new ConnectionModel({
connectionChangeHandler() { connectionChangeHandler() {
}, },
messageHandler(data) { messageHandler(data) {
} }
}); });
document.querySelector("main").append(HomePage()); var settingsViewModel = new SettingsViewModel(settingsModel);
document.querySelector("main").append(HomePage(settingsViewModel));
})(); })();
+2 -2
View File
@@ -25,13 +25,13 @@ export default class SettingsModel {
restoreUsername(): void { restoreUsername(): void {
const path = storageKeys.username; const path = storageKeys.username;
const content = this.storageModel.restore(path); const content = this.storageModel.restore(path);
this.username == content ?? ""; this.username = content ?? "";
} }
restoreFirstDayofWeek(): void { restoreFirstDayofWeek(): void {
const path = storageKeys.firstDayOfWeek; const path = storageKeys.firstDayOfWeek;
const content = this.storageModel.restoreStringifiable(path); const content = this.storageModel.restoreStringifiable(path);
this.firstDayOfWeek == content ?? 0; this.firstDayOfWeek = content ?? 0;
} }
// init // init
+9
View File
@@ -0,0 +1,9 @@
import * as React from "bloatless-react";
export function Option(text: string, value: string, selectedOnCreate: boolean) {
return (
<option value={value} toggle:selected={selectedOnCreate}>
{text}
</option>
);
}
+23 -7
View File
@@ -2,9 +2,11 @@ import "./homePage.css";
import * as React from "bloatless-react"; import * as React from "bloatless-react";
import { Option } from "./Components/option";
import SettingsViewModel from "../ViewModel/settingsViewModel";
import { translations } from "./translations"; import { translations } from "./translations";
export function HomePage() { export function HomePage(settingsViewModel: SettingsViewModel) {
// sections // sections
const overviewSection = ( const overviewSection = (
<div id="overview-section"> <div id="overview-section">
@@ -45,9 +47,7 @@ export function HomePage() {
<span class="icon">inbox</span> <span class="icon">inbox</span>
<div> <div>
<b>{translations.homePage.mailboxHeadline}</b> <b>{translations.homePage.mailboxHeadline}</b>
<span class="error"> <span class="error">{translations.homePage.mailboxDisabled}</span>
{translations.homePage.mailboxDisabled}
</span>
</div> </div>
</div> </div>
@@ -67,11 +67,18 @@ export function HomePage() {
<span class="icon">account_circle</span> <span class="icon">account_circle</span>
<div> <div>
<span>{translations.homePage.yourNameLabel}</span> <span>{translations.homePage.yourNameLabel}</span>
<input></input> <input
bind:value={settingsViewModel.nameInput}
on:enter={settingsViewModel.setName}
></input>
</div> </div>
</label> </label>
<div class="flex-row justify-end"> <div class="flex-row justify-end">
<button class="width-50"> <button
class="width-50"
on:click={settingsViewModel.setName}
toggle:disabled={settingsViewModel.cannotSetName}
>
{translations.homePage.setNameButton} {translations.homePage.setNameButton}
<span class="icon">check</span> <span class="icon">check</span>
</button> </button>
@@ -83,7 +90,16 @@ export function HomePage() {
<span class="icon">calendar_month</span> <span class="icon">calendar_month</span>
<div> <div>
<span>{translations.homePage.firstDayOfWeekLabel}</span> <span>{translations.homePage.firstDayOfWeekLabel}</span>
<select></select> <select bind:value={settingsViewModel.firstDayOfWeekInput}>
{...translations.regional.weekdays.full.map((weekdayName, i) =>
Option(
weekdayName,
i.toString(),
i == settingsViewModel.firstDayOfWeekInput.value
)
)}
</select>
<span class="icon">arrow_drop_down</span>
</div> </div>
</label> </label>
+40
View File
@@ -0,0 +1,40 @@
import * as React from "bloatless-react";
import SettingsModel from "../Model/settingsModel";
export default class SettingsViewModel {
settingsModel: SettingsModel;
// state
nameInput: React.State<string> = new React.State("");
firstDayOfWeekInput: React.State<number> = new React.State(0);
// toggles
cannotSetName = React.createProxyState(
[this.nameInput],
() =>
this.nameInput.value == "" ||
this.nameInput.value == this.settingsModel.username
);
// set
setName = (): void => {
this.settingsModel.setName(this.nameInput.value);
this.nameInput.callSubscriptions();
};
setFirstDayofWeek = (): void => {
this.settingsModel.setFirstDayOfWeek(this.firstDayOfWeekInput.value);
};
// init
constructor(settingsModel: SettingsModel) {
this.settingsModel = settingsModel;
this.nameInput.value = settingsModel.username;
this.firstDayOfWeekInput.value = settingsModel.firstDayOfWeek;
// subscriptions
this.firstDayOfWeekInput.subscribe(this.setFirstDayofWeek);
}
}
+7 -3
View File
@@ -1,16 +1,20 @@
import ConnectionModel from "./Model/connectionModel"; import ConnectionModel from "./Model/connectionModel";
import { HomePage } from "./View/homePage"; import { HomePage } from "./View/homePage";
import { MainPage } from "./View/mainPage"; import { MainPage } from "./View/mainPage";
import SettingsModel from "./Model/settingsModel";
import SettingsViewModel from "./ViewModel/settingsViewModel";
import StorageModel from "./Model/storageModel"; import StorageModel from "./Model/storageModel";
// models // models
const storageModel = new StorageModel(); const storageModel = new StorageModel();
const settingsModel = new SettingsModel(storageModel);
const connectionModel = new ConnectionModel({ const connectionModel = new ConnectionModel({
connectionChangeHandler() {}, connectionChangeHandler() {},
messageHandler(data) {}, messageHandler(data) {},
}); });
// viewModels
const settingsViewModel = new SettingsViewModel(settingsModel);
// view // view
document document.querySelector("main")!.append(HomePage(settingsViewModel));
.querySelector("main")!
.append(HomePage());