implement onboarding

This commit is contained in:
marlon-erler
2026-09-29 21:15:11 +02:00
parent fbc40b5e3e
commit 7a532bbf5c
13 changed files with 687 additions and 281 deletions
+272 -1
View File
@@ -391,6 +391,24 @@
checkUpdatesButton: "Check for updates" checkUpdatesButton: "Check for updates"
} }
}, },
onboarding: {
connectionHeadline: "Connect to UDN",
connectionDescription: "To transfer files and send messages, connect to the Universal Decentralized Network (UDN).",
connectButton: "Connect",
connectionNextButton: (isConnected) => isConnected ? "Continue" : "Set up without UDN",
///
transferOptionsHeadline: "Copy Your Data",
transferOptionsDescription: "If you already use the Coordination Tool on another device, copy your identity here. You can use both devices at the same time.",
transferOptionTransfer: "Copy my data here",
transferOptionNew: "Set up as new device",
//
nameHeadline: "Your Name",
nameDescription: "This is what others see when you send messages. You can always change your name in settings.",
//
transferHeadline: "Data Transfer",
transferDescription: "On your other device, prepare to transfer your identity and personal data alongside any chats you wish to copy. Then, enter the displayed data here. You can find the data transfer on the home screen of the other device.",
transferButton: "Copy data"
},
connectionModal: { connectionModal: {
connectionModalHeadline: "Manage Connections", connectionModalHeadline: "Manage Connections",
/// ///
@@ -646,6 +664,24 @@
checkUpdatesButton: "Nach Updates suchen" checkUpdatesButton: "Nach Updates suchen"
} }
}, },
onboarding: {
connectionHeadline: "Mit UDN verbinden",
connectionDescription: "Um Daten und Nachichten zu verschicken, verbinde dich mit dem Universellen Dezentralen Netzwerk (UDN).",
connectButton: "Verbinden",
connectionNextButton: (isConnected) => isConnected ? "Weiter" : "Ohne UDN einrichten",
///
transferOptionsHeadline: "Deine Daten kopieren",
transferOptionsDescription: "Wenn du Coordination Tool bereits auf einem anderen Ger\xE4t verwendest, kopiere deine Kontodaten. Du kannst beide Ger\xE4te gleichzeitig verwenden.",
transferOptionTransfer: "Daten \xFCbertragen",
transferOptionNew: "Als neues Ger\xE4t enrichten",
//
nameHeadline: "Dein Name",
nameDescription: "Diesen Namen sehen andere, wenn du Nachrichten versendest. Du kannst deinen Namen jederzeit in den Einstellungen \xE4ndern.",
//
transferHeadline: "Daten\xFCbertragung",
transferDescription: "Verwende die Funktion zur Daten\xFCbertragung auf deinem anderen Get\xE4t, um deine Identit\xE4t und pers\xF6nlichen Daten zu kopieren. Gib die dazu angezeigten Daten unten ein. Du findest die Daten\xFCbertragung auf der Startseite des anderen Ger\xE4ts.",
transferButton: "Daten kopieren"
},
connectionModal: { connectionModal: {
connectionModalHeadline: "Verbindungen verwalten", connectionModalHeadline: "Verbindungen verwalten",
connectButtonAudioLabel: "verbinden" connectButtonAudioLabel: "verbinden"
@@ -882,6 +918,24 @@
checkUpdatesButton: "Buscar actualisaciones" checkUpdatesButton: "Buscar actualisaciones"
} }
}, },
onboarding: {
connectionHeadline: "Conectar al UDN",
connectionDescription: "Para transferir datos y enviar mensajes, con\xE9ctate a la Red Descentralizada Universal (UDN).",
connectButton: "Conectar",
connectionNextButton: (isConnected) => isConnected ? "Continuar" : "Configurar sin UDN",
///
transferOptionsHeadline: "Copiar tus datos",
transferOptionsDescription: "Si ya usas el Coordination Tool en otro disposistivo, copia tu idantidad aqu\xED. Puedes usar ambos dispositivos al mismo tiempo.",
transferOptionTransfer: "Copiar mis datos aqu\xED",
transferOptionNew: "Configurar como dispositivo nuevo",
//
nameHeadline: "Tu nombre",
nameDescription: "Esto es lo que ven otros cuando env\xEDas menajes. Siempre lo puedes cambiar en las ajustes.",
//
transferHeadline: "Transferencia de datos",
transferDescription: "En el otro dispositivo, prepara la transferencia de tu identidad y datos personales. Ingresa los datos que se muestran a continuaci\xF3n. La opci\xF3n de la transferencia de datos se encuentra en la pantalla inicial en el otro dispositivo.",
transferButton: "Copiar datos"
},
connectionModal: { connectionModal: {
connectionModalHeadline: "Gestionar Conexiones", connectionModalHeadline: "Gestionar Conexiones",
connectButtonAudioLabel: "conectar" connectButtonAudioLabel: "conectar"
@@ -2165,6 +2219,7 @@
super("file-transfer"); super("file-transfer");
this.coreViewModel = coreViewModel2; this.coreViewModel = coreViewModel2;
// state // state
this.exitReception = void 0;
this.presentedModal = new State(void 0); this.presentedModal = new State(void 0);
this.generalFileOptions = new ListState(); this.generalFileOptions = new ListState();
this.chatFileOptions = new ListState(); this.chatFileOptions = new ListState();
@@ -2328,6 +2383,14 @@
this.getTransferData(); this.getTransferData();
this.coreViewModel.fileTransferModel.prepareToSend(); this.coreViewModel.fileTransferModel.prepareToSend();
}; };
this.correctTransferData = () => {
if (this.exitReception != void 0) {
this.presentedModal.value = void 0;
this.exitReception();
return;
}
this.showTransferDataInputModal();
};
this.initiateTransfer = () => { this.initiateTransfer = () => {
this.presentedModal.value = 3 /* TransferDisplay */; this.presentedModal.value = 3 /* TransferDisplay */;
this.didNotFinishSending.value = true; this.didNotFinishSending.value = true;
@@ -7913,7 +7976,7 @@
"button", "button",
{ {
class: "flex", class: "flex",
"on:click": fileTransferViewModel2.showTransferDataInputModal, "on:click": fileTransferViewModel2.correctTransferData,
"toggle:disabled": fileTransferViewModel2.cannotExitReception "toggle:disabled": fileTransferViewModel2.cannotExitReception
}, },
coreViewModel2.translations.general.backButton coreViewModel2.translations.general.backButton
@@ -8707,6 +8770,212 @@
} }
}; };
// src/ViewModel/Global/onboardingViewModel.ts
var OnboardingViewModel = class {
// init
constructor(connectionViewmodel, fileTransferViewModel2, settingsViewModel2) {
this.connectionViewmodel = connectionViewmodel;
this.fileTransferViewModel = fileTransferViewModel2;
this.settingsViewModel = settingsViewModel2;
// state
this.presentedModal = new State(void 0);
// guards
this.cannotTransfer = createProxyState([this.connectionViewmodel.isConnected], () => !this.connectionViewmodel.isConnected.value);
// navigation
this.open = () => {
this.presentedModal.value = 0 /* Connection */;
};
this.showTransferOption = () => {
this.presentedModal.value = 1 /* TransferOrNew */;
};
this.setupNew = () => {
this.presentedModal.value = 2 /* Name */;
};
this.showTransferData = () => {
this.presentedModal.value = 3 /* Transfer */;
};
// methods
this.transferData = () => {
this.fileTransferViewModel.exitReception = () => this.showTransferData();
this.fileTransferViewModel.prepareReceivingData();
this.presentedModal.value = void 0;
};
this.finish = () => {
this.settingsViewModel.setName();
this.presentedModal.value = void 0;
};
if (this.settingsViewModel.settingsModel.username == "") this.open();
}
};
// src/View/Modals/onboardingModal.tsx
function OnboardingModalWrapper(coreViewModel2, onboardingViewModel2) {
return /* @__PURE__ */ createElement("div", null, ConnectionModal2(coreViewModel2, onboardingViewModel2), TransferModal(coreViewModel2, onboardingViewModel2), NameModal(coreViewModel2, onboardingViewModel2), TransferDataModal(coreViewModel2, onboardingViewModel2));
}
function ConnectionModal2(coreViewModel2, onboardingViewModel2) {
const isPresented = createProxyState(
[onboardingViewModel2.presentedModal],
() => onboardingViewModel2.presentedModal.value == 0 /* Connection */
);
const connectionViewModel2 = onboardingViewModel2.connectionViewmodel;
const nextButton = createProxyState([connectionViewModel2.isConnected], () => coreViewModel2.translations.onboarding.connectionNextButton(connectionViewModel2.isConnected.value));
return /* @__PURE__ */ createElement(
"div",
{
class: "modal",
"toggle:open": isPresented
},
/* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, coreViewModel2.translations.onboarding.connectionHeadline), /* @__PURE__ */ createElement("p", { class: "secondary width-input" }, coreViewModel2.translations.onboarding.connectionDescription), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "cell_tower"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, coreViewModel2.translations.homePage.serverAddress), /* @__PURE__ */ createElement(
"input",
{
placeholder: coreViewModel2.translations.homePage.serverAddressPlaceholder,
"bind:value": connectionViewModel2.serverAddressInput,
"on:enter": connectionViewModel2.connect
}
))), /* @__PURE__ */ createElement("div", { class: "flex-row width-input justify-end" }, /* @__PURE__ */ createElement(
"button",
{
class: "standard width-50",
"on:click": connectionViewModel2.connect,
"toggle:disabled": connectionViewModel2.cannotConnect
},
coreViewModel2.translations.onboarding.connectButton
))), /* @__PURE__ */ createElement("div", { class: "flex-row justify-end" }, /* @__PURE__ */ createElement(
"button",
{
class: "primary width-50",
"on:click": onboardingViewModel2.showTransferOption
},
/* @__PURE__ */ createElement(
"span",
{
"subscribe:innerText": nextButton
}
),
/* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_forward")
)))
);
}
function TransferModal(coreViewModel2, onboardingViewModel2) {
const isPresented = createProxyState(
[onboardingViewModel2.presentedModal],
() => onboardingViewModel2.presentedModal.value == 1 /* TransferOrNew */
);
return /* @__PURE__ */ createElement(
"div",
{
class: "modal",
"toggle:open": isPresented
},
/* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, coreViewModel2.translations.onboarding.transferOptionsHeadline), /* @__PURE__ */ createElement("p", { class: "secondary width-input" }, coreViewModel2.translations.onboarding.transferOptionsDescription), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-column gap" }, /* @__PURE__ */ createElement(
"button",
{
class: "tile",
"on:click": onboardingViewModel2.showTransferData,
"toggle:disabled": onboardingViewModel2.cannotTransfer
},
/* @__PURE__ */ createElement("div", null, coreViewModel2.translations.onboarding.transferOptionTransfer),
/* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_forward")
), /* @__PURE__ */ createElement(
"button",
{
class: "tile",
"on:click": onboardingViewModel2.setupNew
},
/* @__PURE__ */ createElement("div", null, coreViewModel2.translations.onboarding.transferOptionNew),
/* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_forward")
))), /* @__PURE__ */ createElement("div", { class: "flex-row" }, /* @__PURE__ */ createElement(
"button",
{
class: "standard width-50",
"on:click": onboardingViewModel2.open
},
coreViewModel2.translations.general.backButton
)))
);
}
function NameModal(coreViewModel2, onboardingViewModel2) {
const isPresented = createProxyState(
[onboardingViewModel2.presentedModal],
() => onboardingViewModel2.presentedModal.value == 2 /* Name */
);
const settingsViewModel2 = onboardingViewModel2.settingsViewModel;
return /* @__PURE__ */ createElement(
"div",
{
class: "modal",
"toggle:open": isPresented
},
/* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, coreViewModel2.translations.onboarding.nameHeadline), /* @__PURE__ */ createElement("p", { class: "secondary width-input" }, coreViewModel2.translations.onboarding.nameDescription), /* @__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
}
)))), /* @__PURE__ */ createElement("div", { class: "flex-row" }, /* @__PURE__ */ createElement(
"button",
{
class: "standard width-50",
"on:click": onboardingViewModel2.showTransferOption
},
coreViewModel2.translations.general.backButton
), /* @__PURE__ */ createElement(
"button",
{
class: "primary width-50",
"on:click": onboardingViewModel2.finish
},
coreViewModel2.translations.general.setButton,
/* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_forward")
)))
);
}
function TransferDataModal(coreViewModel2, onboardingViewModel2) {
const isPresented = createProxyState(
[onboardingViewModel2.presentedModal],
() => onboardingViewModel2.presentedModal.value == 3 /* Transfer */
);
const fileTransferViewModel2 = onboardingViewModel2.fileTransferViewModel;
return /* @__PURE__ */ createElement(
"div",
{
class: "modal",
"toggle:open": isPresented
},
/* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, coreViewModel2.translations.onboarding.transferHeadline), /* @__PURE__ */ createElement("p", { class: "secondary width-input" }, coreViewModel2.translations.onboarding.transferDescription), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-column gap content-margin-bottom" }, /* @__PURE__ */ createElement("label", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "forum"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", { class: "secondary" }, coreViewModel2.translations.dataTransferModal.transferChannelHeadline), /* @__PURE__ */ createElement(
"input",
{
type: "number",
"on:enter": fileTransferViewModel2.prepareReceivingData,
"bind:value": fileTransferViewModel2.receivingTransferChannel
}
))), /* @__PURE__ */ createElement("label", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "key"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", { class: "secondary" }, coreViewModel2.translations.dataTransferModal.transferKeyHeadline), /* @__PURE__ */ createElement(
"input",
{
type: "number",
"on:enter": fileTransferViewModel2.prepareReceivingData,
"bind:value": fileTransferViewModel2.receivingTransferKey
}
))))), /* @__PURE__ */ createElement("div", { class: "flex-row" }, /* @__PURE__ */ createElement(
"button",
{
class: "standard width-50",
"on:click": onboardingViewModel2.showTransferOption,
"toggle:disabled": fileTransferViewModel2.cannotExitReception
},
coreViewModel2.translations.general.backButton
), /* @__PURE__ */ createElement(
"button",
{
class: "primary width-50",
"on:click": onboardingViewModel2.transferData
},
coreViewModel2.translations.onboarding.transferButton,
/* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_forward")
)))
);
}
// src/index.tsx // src/index.tsx
var storageModel = new StorageModel(); var storageModel = new StorageModel();
var settingsModel = new SettingsModel(storageModel); var settingsModel = new SettingsModel(storageModel);
@@ -8740,6 +9009,7 @@
contactListViewModel contactListViewModel
); );
var fileTransferViewModel = new FileTransferViewModel(coreViewModel); var fileTransferViewModel = new FileTransferViewModel(coreViewModel);
var onboardingViewModel = new OnboardingViewModel(connectionViewModel, fileTransferViewModel, settingsViewModel);
var homeViewModel = new HomeViewModel( var homeViewModel = new HomeViewModel(
coreViewModel, coreViewModel,
settingsViewModel, settingsViewModel,
@@ -8766,6 +9036,7 @@
chatListViewModel chatListViewModel
), ),
ChatPageWrapper(coreViewModel, chatListViewModel), ChatPageWrapper(coreViewModel, chatListViewModel),
OnboardingModalWrapper(coreViewModel, onboardingViewModel),
ConnectionModal(coreViewModel, connectionViewModel), ConnectionModal(coreViewModel, connectionViewModel),
DataTransferModalWrapper( DataTransferModalWrapper(
coreViewModel, coreViewModel,
Vendored
+1 -1
View File
@@ -1,4 +1,4 @@
const VERSION = "2610 Beta 18" const VERSION = "2610 Beta 19"
function processUrl(event) { function processUrl(event) {
const url = event.request.url; const url = event.request.url;
+1 -1
View File
@@ -1 +1 @@
2610 Beta 18 2610 Beta 19
+1 -1
View File
@@ -111,7 +111,7 @@ export default class ChatModel {
this.addMessage(chatMessage); this.addMessage(chatMessage);
if (chatMessage.stringifiedFile) return; if (chatMessage.stringifiedFile) return;
if (chatMessage.senderId == this.settingsModel.userid) return; if (chatMessage.senderId == this.settingsModel.userid) return;
this.setReadStatus(true); this.setReadStatus(true);
}; };
+1 -3
View File
@@ -615,9 +615,7 @@ function DataReceptionModal(
<div class="flex-row width-100"> <div class="flex-row width-100">
<button <button
class="flex" class="flex"
on:click={ on:click={fileTransferViewModel.correctTransferData}
fileTransferViewModel.correctTransferData
}
toggle:disabled={ toggle:disabled={
fileTransferViewModel.cannotExitReception fileTransferViewModel.cannotExitReception
} }
+262 -208
View File
@@ -1,222 +1,275 @@
import { ViewController } from "../viewController";
import * as React from "../../react"; import * as React from "../../react";
import OnboardingViewModel, {
OnboardingModals,
} from "../../ViewModel/Global/onboardingViewModel";
import CoreViewModel from "../../ViewModel/Global/coreViewModel"; import CoreViewModel from "../../ViewModel/Global/coreViewModel";
import OnboardingViewModel, {OnboardingModals} from "../../ViewModel/Global/onboardingViewModel";
import {ViewController} from "../viewController";
// MAIN // MAIN
export function OnboardingModalWrapper(coreViewModel: CoreViewModel, onboardingViewModel: OnboardingViewModel) { export function OnboardingModalWrapper(
return <div> coreViewModel: CoreViewModel,
{ConnectionModal(coreViewModel, onboardingViewModel)} onboardingViewModel: OnboardingViewModel,
{TransferModal(coreViewModel, onboardingViewModel)} ) {
{NameModal(coreViewModel, onboardingViewModel)} return (
{TransferDataModal(coreViewModel, onboardingViewModel)} <div>
</div> {ConnectionModal(coreViewModel, onboardingViewModel)}
{TransferModal(coreViewModel, onboardingViewModel)}
{NameModal(coreViewModel, onboardingViewModel)}
{TransferDataModal(coreViewModel, onboardingViewModel)}
</div>
);
} }
function ConnectionModal(coreViewModel: CoreViewModel, onboardingViewModel: OnboardingViewModel) { function ConnectionModal(
coreViewModel: CoreViewModel,
onboardingViewModel: OnboardingViewModel,
) {
const isPresented = React.createProxyState( const isPresented = React.createProxyState(
[onboardingViewModel.presentedModal], [onboardingViewModel.presentedModal],
() => () =>
onboardingViewModel.presentedModal.value == onboardingViewModel.presentedModal.value ==
OnboardingModals.Connection OnboardingModals.Connection,
); );
const connectionViewModel = onboardingViewModel.connectionViewmodel; const connectionViewModel = onboardingViewModel.connectionViewmodel;
const nextButton = React.createProxyState([connectionViewModel.isConnected], ()=>coreViewModel.translations.onboarding.connectionNextButton(connectionViewModel.isConnected.value)); const nextButton = React.createProxyState(
[connectionViewModel.isConnected],
return <div () =>
class="modal" coreViewModel.translations.onboarding.connectionNextButton(
toggle:open={isPresented} connectionViewModel.isConnected.value,
> ),
<div>
<main>
<h2>{coreViewModel.translations.onboarding.connectionHeadline}</h2>
<p class="secondary width-input">{coreViewModel.translations.onboarding.connectionDescription}</p>
<hr></hr>
<label class="tile flex-no">
<span class="icon">cell_tower</span>
<div>
<span>
{coreViewModel.translations.homePage.serverAddress}
</span>
<input
placeholder={
coreViewModel.translations.homePage
.serverAddressPlaceholder
}
bind:value={connectionViewModel.serverAddressInput}
on:enter={connectionViewModel.connect}
></input>
</div>
</label>
<div class="flex-row width-input justify-end">
<button
class="standard width-50"
on:click={connectionViewModel.connect}
toggle:disabled={connectionViewModel.cannotConnect}
>
{coreViewModel.translations.onboarding.connectButton}
</button>
</div>
</main>
<div class="flex-row justify-end">
<button
class="primary width-50"
on:click={onboardingViewModel.showTransferOption}
>
<span
subscribe:innerText={nextButton}
></span>
<span class="icon">
arrow_forward
</span>
</button>
</div>
</div>
</div>
}
function TransferModal(coreViewModel: CoreViewModel, onboardingViewModel: OnboardingViewModel) {
const isPresented = React.createProxyState(
[onboardingViewModel.presentedModal],
() =>
onboardingViewModel.presentedModal.value ==
OnboardingModals.TransferOrNew
); );
return <div return (
class="modal" <div class="modal" toggle:open={isPresented}>
toggle:open={isPresented} <div>
> <main>
<div> <h2>
<main> {
<h2>{coreViewModel.translations.onboarding.transferOptionsHeadline}</h2> coreViewModel.translations.onboarding
<p class="secondary width-input">{coreViewModel.translations.onboarding.transferOptionsDescription}</p> .connectionHeadline
}
</h2>
<p class="secondary width-input">
{
coreViewModel.translations.onboarding
.connectionDescription
}
</p>
<hr></hr> <hr></hr>
<div class="flex-column gap"> <label class="tile flex-no">
<button <span class="icon">cell_tower</span>
class="tile" <div>
on:click={onboardingViewModel.showTransferData} <span>
toggle:disabled={onboardingViewModel.cannotTransfer} {
> coreViewModel.translations.homePage
<div> .serverAddress
{coreViewModel.translations.onboarding.transferOptionTransfer} }
</div> </span>
<span class="icon"> <input
arrow_forward placeholder={
</span> coreViewModel.translations.homePage
</button> .serverAddressPlaceholder
<button }
class="tile" bind:value={
on:click={onboardingViewModel.setupNew} connectionViewModel.serverAddressInput
> }
<div> on:enter={connectionViewModel.connect}
{coreViewModel.translations.onboarding.transferOptionNew} ></input>
</div> </div>
<span class="icon"> </label>
arrow_forward <div class="flex-row width-input justify-end">
</span> <button
</button> class="standard width-50"
</div> on:click={connectionViewModel.connect}
</main> toggle:disabled={connectionViewModel.cannotConnect}
<div class="flex-row"> >
<button {
class="standard width-50" coreViewModel.translations.onboarding
on:click={onboardingViewModel.open} .connectButton
> }
{coreViewModel.translations.general.backButton} </button>
</button> </div>
</div> </main>
</div> <div class="flex-row justify-end">
</div> <button
class="primary width-50"
on:click={onboardingViewModel.showTransferOption}
>
<span subscribe:innerText={nextButton}></span>
<span class="icon">arrow_forward</span>
</button>
</div>
</div>
</div>
);
} }
function NameModal(coreViewModel: CoreViewModel, onboardingViewModel: OnboardingViewModel) { function TransferModal(
coreViewModel: CoreViewModel,
onboardingViewModel: OnboardingViewModel,
) {
const isPresented = React.createProxyState( const isPresented = React.createProxyState(
[onboardingViewModel.presentedModal], [onboardingViewModel.presentedModal],
() => () =>
onboardingViewModel.presentedModal.value == onboardingViewModel.presentedModal.value ==
OnboardingModals.Name OnboardingModals.TransferOrNew,
);
return (
<div class="modal" toggle:open={isPresented}>
<div>
<main>
<h2>
{
coreViewModel.translations.onboarding
.transferOptionsHeadline
}
</h2>
<p class="secondary width-input">
{
coreViewModel.translations.onboarding
.transferOptionsDescription
}
</p>
<hr></hr>
<div class="flex-column gap">
<button
class="tile"
on:click={onboardingViewModel.showTransferData}
toggle:disabled={onboardingViewModel.cannotTransfer}
>
<div>
{
coreViewModel.translations.onboarding
.transferOptionTransfer
}
</div>
<span class="icon">arrow_forward</span>
</button>
<button
class="tile"
on:click={onboardingViewModel.setupNew}
>
<div>
{
coreViewModel.translations.onboarding
.transferOptionNew
}
</div>
<span class="icon">arrow_forward</span>
</button>
</div>
</main>
<div class="flex-row">
<button
class="standard width-50"
on:click={onboardingViewModel.open}
>
{coreViewModel.translations.general.backButton}
</button>
</div>
</div>
</div>
);
}
function NameModal(
coreViewModel: CoreViewModel,
onboardingViewModel: OnboardingViewModel,
) {
const isPresented = React.createProxyState(
[onboardingViewModel.presentedModal],
() => onboardingViewModel.presentedModal.value == OnboardingModals.Name,
); );
const settingsViewModel = onboardingViewModel.settingsViewModel; const settingsViewModel = onboardingViewModel.settingsViewModel;
return <div return (
class="modal" <div class="modal" toggle:open={isPresented}>
toggle:open={isPresented} <div>
> <main>
<div> <h2>
<main> {coreViewModel.translations.onboarding.nameHeadline}
<h2>{coreViewModel.translations.onboarding.nameHeadline}</h2> </h2>
<p class="secondary width-input">{coreViewModel.translations.onboarding.nameDescription}</p> <p class="secondary width-input">
{coreViewModel.translations.onboarding.nameDescription}
</p>
<hr></hr> <hr></hr>
<label class="tile flex-no"> <label class="tile flex-no">
<span class="icon">account_circle</span> <span class="icon">account_circle</span>
<div> <div>
<span> <span>
{ {
coreViewModel.translations.settings.account coreViewModel.translations.settings.account
.yourNameLabel .yourNameLabel
} }
</span> </span>
<input <input
placeholder={ placeholder={
coreViewModel.translations.settings.account coreViewModel.translations.settings.account
.yourNamePlaceholder .yourNamePlaceholder
} }
bind:value={settingsViewModel.usernameInput} bind:value={settingsViewModel.usernameInput}
></input> ></input>
</div> </div>
</label> </label>
</main> </main>
<div class="flex-row"> <div class="flex-row">
<button <button
class="standard width-50" class="standard width-50"
on:click={onboardingViewModel.showTransferOption} on:click={onboardingViewModel.showTransferOption}
> >
{coreViewModel.translations.general.backButton} {coreViewModel.translations.general.backButton}
</button> </button>
<button <button
class="primary width-50" class="primary width-50"
on:click={onboardingViewModel.finish} on:click={onboardingViewModel.finish}
> >
{coreViewModel.translations.general.setButton} {coreViewModel.translations.general.setButton}
<span class="icon"> <span class="icon">arrow_forward</span>
arrow_forward </button>
</span> </div>
</button> </div>
</div> </div>
</div> );
</div>
} }
function TransferDataModal(coreViewModel: CoreViewModel, onboardingViewModel: OnboardingViewModel) { function TransferDataModal(
coreViewModel: CoreViewModel,
onboardingViewModel: OnboardingViewModel,
) {
const isPresented = React.createProxyState( const isPresented = React.createProxyState(
[onboardingViewModel.presentedModal], [onboardingViewModel.presentedModal],
() => () =>
onboardingViewModel.presentedModal.value == onboardingViewModel.presentedModal.value ==
OnboardingModals.Transfer OnboardingModals.Transfer,
); );
const fileTransferViewModel = onboardingViewModel.fileTransferViewModel; const fileTransferViewModel = onboardingViewModel.fileTransferViewModel;
return <div return (
class="modal" <div class="modal" toggle:open={isPresented}>
toggle:open={isPresented} <div>
> <main>
<div> <h2>
<main> {coreViewModel.translations.onboarding.transferHeadline}
<h2>{coreViewModel.translations.onboarding.transferHeadline}</h2> </h2>
<p class="secondary width-input">{coreViewModel.translations.onboarding.transferDescription}</p> <p class="secondary width-input">
{
coreViewModel.translations.onboarding
.transferDescription
}
</p>
<hr></hr> <hr></hr>
<div class="flex-column gap content-margin-bottom"> <div class="flex-column gap content-margin-bottom">
<label class="tile"> <label class="tile">
@@ -262,25 +315,26 @@ function TransferDataModal(coreViewModel: CoreViewModel, onboardingViewModel: On
</div> </div>
</label> </label>
</div> </div>
</main> </main>
<div class="flex-row"> <div class="flex-row">
<button <button
class="standard width-50" class="standard width-50"
on:click={onboardingViewModel.showTransferOption} on:click={onboardingViewModel.showTransferOption}
toggle:disabled={fileTransferViewModel.cannotExitReception} toggle:disabled={
> fileTransferViewModel.cannotExitReception
{coreViewModel.translations.general.backButton} }
</button> >
<button {coreViewModel.translations.general.backButton}
class="primary width-50" </button>
on:click={onboardingViewModel.transferData} <button
> class="primary width-50"
{coreViewModel.translations.onboarding.transferButton} on:click={onboardingViewModel.transferData}
<span class="icon"> >
check {coreViewModel.translations.onboarding.transferButton}
</span> <span class="icon">arrow_forward</span>
</button> </button>
</div> </div>
</div> </div>
</div> </div>
);
} }
+85 -19
View File
@@ -113,28 +113,33 @@ const englishTranslations = {
}, },
onboarding: { onboarding: {
connectionHeadline: "Connect to UDN", connectionHeadline: "Connect to UDN",
connectionDescription: "To transfer files and send messages, connect to the Universal Decentralized Network (UDN).", connectionDescription:
connectButton: "Connect", "To transfer files and send messages, connect to the Universal Decentralized Network (UDN).",
connectionNextButton: (isConnected: boolean) => isConnected ? "Continue" : "Continue without UDN", connectButton: "Connect",
connectionNextButton: (isConnected: boolean): string =>
isConnected ? "Continue" : "Set up without UDN",
/// ///
transferOptionsHeadline: "Copy Your Data",
transferOptionsDescription: "If you already use the Coordination Tool on another device, copy your identity here. You can use both devices at the same time.",
transferOptionTransfer: "Copy here",
transferOptionNew: "Set up as new device",
// transferOptionsHeadline: "Copy Your Data",
transferOptionsDescription:
nameHeadline: "Your Name", "If you already use the Coordination Tool on another device, copy your identity here. You can use both devices at the same time.",
nameDescription: "This is what others see when you send messages. You can always change your name in settings.", transferOptionTransfer: "Copy my data here",
transferOptionNew: "Set up as new device",
// //
transferHeadline: "Data Transfer", nameHeadline: "Your Name",
transferDescription: 'On your other device, prepare to transfer your identity and personal data alongside any chats you wish to copy. Then, enter the displayed data here.', nameDescription:
transferButton: "Copy data", "This is what others see when you send messages. You can always change your name in settings.",
//
transferHeadline: "Data Transfer",
transferDescription:
"On your other device, prepare to transfer your identity and personal data alongside any chats you wish to copy. Then, enter the displayed data here. You can find the data transfer on the home screen of the other device.",
transferButton: "Copy data",
}, },
connectionModal: { connectionModal: {
@@ -487,6 +492,36 @@ export const allTranslations: {
}, },
}, },
onboarding: {
connectionHeadline: "Mit UDN verbinden",
connectionDescription:
"Um Daten und Nachichten zu verschicken, verbinde dich mit dem Universellen Dezentralen Netzwerk (UDN).",
connectButton: "Verbinden",
connectionNextButton: (isConnected: boolean) =>
isConnected ? "Weiter" : "Ohne UDN einrichten",
///
transferOptionsHeadline: "Deine Daten kopieren",
transferOptionsDescription:
"Wenn du Coordination Tool bereits auf einem anderen Gerät verwendest, kopiere deine Kontodaten. Du kannst beide Geräte gleichzeitig verwenden.",
transferOptionTransfer: "Daten übertragen",
transferOptionNew: "Als neues Gerät enrichten",
//
nameHeadline: "Dein Name",
nameDescription:
"Diesen Namen sehen andere, wenn du Nachrichten versendest. Du kannst deinen Namen jederzeit in den Einstellungen ändern.",
//
transferHeadline: "Datenübertragung",
transferDescription:
"Verwende die Funktion zur Datenübertragung auf deinem anderen Getät, um deine Identität und persönlichen Daten zu kopieren. Gib die dazu angezeigten Daten unten ein. Du findest die Datenübertragung auf der Startseite des anderen Geräts.",
transferButton: "Daten kopieren",
},
connectionModal: { connectionModal: {
connectionModalHeadline: "Verbindungen verwalten", connectionModalHeadline: "Verbindungen verwalten",
@@ -804,6 +839,37 @@ export const allTranslations: {
checkUpdatesButton: "Buscar actualisaciones", checkUpdatesButton: "Buscar actualisaciones",
}, },
}, },
onboarding: {
connectionHeadline: "Conectar al UDN",
connectionDescription:
"Para transferir datos y enviar mensajes, conéctate a la Red Descentralizada Universal (UDN).",
connectButton: "Conectar",
connectionNextButton: (isConnected: boolean): string =>
isConnected ? "Continuar" : "Configurar sin UDN",
///
transferOptionsHeadline: "Copiar tus datos",
transferOptionsDescription:
"Si ya usas el Coordination Tool en otro disposistivo, copia tu idantidad aquí. Puedes usar ambos dispositivos al mismo tiempo.",
transferOptionTransfer: "Copiar mis datos aquí",
transferOptionNew: "Configurar como dispositivo nuevo",
//
nameHeadline: "Tu nombre",
nameDescription:
"Esto es lo que ven otros cuando envías menajes. Siempre lo puedes cambiar en las ajustes.",
//
transferHeadline: "Transferencia de datos",
transferDescription:
"En el otro dispositivo, prepara la transferencia de tu identidad y datos personales. Ingresa los datos que se muestran a continuación. La opción de la transferencia de datos se encuentra en la pantalla inicial en el otro dispositivo.",
transferButton: "Copiar datos",
},
connectionModal: { connectionModal: {
connectionModalHeadline: "Gestionar Conexiones", connectionModalHeadline: "Gestionar Conexiones",
+4 -1
View File
@@ -101,7 +101,10 @@ export default class ChatListViewModel {
public readonly connectionViewModel: ConnectionViewModel, public readonly connectionViewModel: ConnectionViewModel,
public readonly contactListViewModel: ContactListViewModel, public readonly contactListViewModel: ContactListViewModel,
) { ) {
this.notificationViewModel = new NotificationViewModel(this, this.settingsViewModel); this.notificationViewModel = new NotificationViewModel(
this,
this.settingsViewModel,
);
this.loadChats(); this.loadChats();
} }
+2 -1
View File
@@ -150,7 +150,8 @@ export default class ChatMessageViewModel {
hideForReplyView = false; hideForReplyView = false;
else if ( else if (
this.inlineReply != undefined && this.inlineReply != undefined &&
selectedMessage.chatMessage.fileId == this.inlineReply.chatMessage.fileId selectedMessage.chatMessage.fileId ==
this.inlineReply.chatMessage.fileId
) )
hideForReplyView = false; hideForReplyView = false;
else hideForReplyView = true; else hideForReplyView = true;
@@ -11,8 +11,8 @@ import ChatModel from "../../Model/Chat/chatModel";
export default class FileTransferViewModel extends Context { export default class FileTransferViewModel extends Context {
// state // state
exitReception: ()=>void|undefined = undefined; exitReception: () => void | undefined = undefined;
presentedModal: React.State<FileTransferModals | undefined> = presentedModal: React.State<FileTransferModals | undefined> =
new React.State<any>(undefined); new React.State<any>(undefined);
@@ -219,13 +219,13 @@ export default class FileTransferViewModel extends Context {
}; };
correctTransferData = (): void => { correctTransferData = (): void => {
if (this.exitReception != undefined) { if (this.exitReception != undefined) {
this.presentedModal.value = undefined; this.presentedModal.value = undefined;
this.exitReception(); this.exitReception();
return; return;
} }
this.showTransferDataInputModal() this.showTransferDataInputModal();
} };
initiateTransfer = (): void => { initiateTransfer = (): void => {
this.presentedModal.value = FileTransferModals.TransferDisplay; this.presentedModal.value = FileTransferModals.TransferDisplay;
@@ -1,8 +1,8 @@
import SettingsViewModel from "./settingsViewModel";
import { ChatPageTypes } from "../Chat/chatViewModel"; import { ChatPageTypes } from "../Chat/chatViewModel";
import ChatListViewModel from "../Chat/chatListViewModel"; import ChatListViewModel from "../Chat/chatListViewModel";
import * as React from "../../react"; import * as React from "../../react";
import ChatModel, { ChatMessage } from "../../Model/Chat/chatModel"; import ChatModel, { ChatMessage } from "../../Model/Chat/chatModel";
import SettingsViewModel from "./settingsViewModel";
export default class NotificationViewModel { export default class NotificationViewModel {
// data // data
@@ -16,7 +16,8 @@ export default class NotificationViewModel {
showNotification = (message: ChatMessage): void => { showNotification = (message: ChatMessage): void => {
if (this.seenMessageIds.has(message.fileId)) return; if (this.seenMessageIds.has(message.fileId)) return;
if (this.chatListViewModel.selectedChat.value == undefined) return; if (this.chatListViewModel.selectedChat.value == undefined) return;
if (message.senderId == this.settingsViewModel.settingsModel.userid) return; if (message.senderId == this.settingsViewModel.settingsModel.userid)
return;
const notification: Notification = this.createNotification(message); const notification: Notification = this.createNotification(message);
const currentChat = const currentChat =
@@ -79,7 +80,10 @@ export default class NotificationViewModel {
}; };
// init // init
constructor(public chatListViewModel: ChatListViewModel, public settingsViewModel: SettingsViewModel) {} constructor(
public chatListViewModel: ChatListViewModel,
public settingsViewModel: SettingsViewModel,
) {}
// util // util
createNotification(message: ChatMessage): Notification { createNotification(message: ChatMessage): Notification {
+32 -27
View File
@@ -1,52 +1,57 @@
import CoreViewModel from "./coreViewModel";
import * as React from "../../react";
import FileTransferViewModel from "./fileTransferViewModel";
import ConnectionViewModel from "./connectionViewModel";
import SettingsViewModel from "./settingsViewModel"; import SettingsViewModel from "./settingsViewModel";
import FileTransferViewModel from "./fileTransferViewModel";
import CoreViewModel from "./coreViewModel";
import ConnectionViewModel from "./connectionViewModel";
import * as React from "../../react";
export default class OnboardingViewModel { export default class OnboardingViewModel {
// state // state
presentedModal: React.State<OnboardingModals | undefined> = new React.State<any>(undefined); presentedModal: React.State<OnboardingModals | undefined> =
new React.State<any>(undefined);
// guards // guards
cannotTransfer: React.State<boolean> = React.createProxyState([this.connectionViewmodel.isConnected], ()=>!this.connectionViewmodel.isConnected.value) cannotTransfer: React.State<boolean> = React.createProxyState(
[this.connectionViewmodel.isConnected],
() => !this.connectionViewmodel.isConnected.value,
);
// navigation // navigation
open = (): void => { open = (): void => {
this.presentedModal.value = OnboardingModals.Connection; this.presentedModal.value = OnboardingModals.Connection;
} };
showTransferOption = (): void => { showTransferOption = (): void => {
this.presentedModal.value = OnboardingModals.TransferOrNew; this.presentedModal.value = OnboardingModals.TransferOrNew;
} };
setupNew = (): void => { setupNew = (): void => {
this.presentedModal.value = OnboardingModals.Name; this.presentedModal.value = OnboardingModals.Name;
} };
showTransferData = (): void => { showTransferData = (): void => {
this.presentedModal.value = OnboardingModals.Transfer; this.presentedModal.value = OnboardingModals.Transfer;
} };
// methods // methods
transferData = (): void => { transferData = (): void => {
this.fileTransferViewModel.exitReception = ()=>this.showTransferData(); this.fileTransferViewModel.exitReception = () =>
this.fileTransferViewModel.prepareReceivingData(); this.showTransferData();
this.presentedModal.value = undefined; this.fileTransferViewModel.prepareReceivingData();
} this.presentedModal.value = undefined;
};
finish = (): void => { finish = (): void => {
this.settingsViewModel.setName(); this.settingsViewModel.setName();
this.presentedModal.value = undefined; this.presentedModal.value = undefined;
} };
// init // init
constructor( constructor(
public connectionViewmodel: ConnectionViewModel, public connectionViewmodel: ConnectionViewModel,
public fileTransferViewModel: FileTransferViewModel, public fileTransferViewModel: FileTransferViewModel,
public settingsViewModel: SettingsViewModel, public settingsViewModel: SettingsViewModel,
) { ) {
if (this.settingsViewModel.settingsModel.username == "") this.open(); if (this.settingsViewModel.settingsModel.username == "") this.open();
} }
} }
+10 -6
View File
@@ -5,6 +5,7 @@ import "./base.css";
import HomeViewModel from "./ViewModel/Pages/homeViewModel"; import HomeViewModel from "./ViewModel/Pages/homeViewModel";
import StorageViewModel from "./ViewModel/Global/storageViewModel"; import StorageViewModel from "./ViewModel/Global/storageViewModel";
import SettingsViewModel from "./ViewModel/Global/settingsViewModel"; import SettingsViewModel from "./ViewModel/Global/settingsViewModel";
import OnboardingViewModel from "./ViewModel/Global/onboardingViewModel";
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 ContactListViewModel from "./ViewModel/Global/contactViewModel";
@@ -14,6 +15,7 @@ import { HomePage } from "./View/homePage";
import { ChatPageWrapper } from "./View/chatPageWrapper"; import { ChatPageWrapper } from "./View/chatPageWrapper";
import { StorageModal } from "./View/Modals/storageModal"; import { StorageModal } from "./View/Modals/storageModal";
import { SettingsModal } from "./View/Modals/settingsModal"; import { SettingsModal } from "./View/Modals/settingsModal";
import { OnboardingModalWrapper } from "./View/Modals/onboardingModal";
import { DataTransferModalWrapper } from "./View/Modals/dataTransferModal"; import { DataTransferModalWrapper } from "./View/Modals/dataTransferModal";
import { ConnectionModal } from "./View/Modals/connectionModal"; import { ConnectionModal } from "./View/Modals/connectionModal";
import StorageModel from "./Model/Global/storageModel"; import StorageModel from "./Model/Global/storageModel";
@@ -22,8 +24,6 @@ import FileTransferModel from "./Model/Global/fileTransferModel";
import ContactListModel from "./Model/Global/contactListModel"; 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 OnboardingViewModel from "./ViewModel/Global/onboardingViewModel";
import {OnboardingModalWrapper} from "./View/Modals/onboardingModal";
// models // models
const storageModel = new StorageModel(); const storageModel = new StorageModel();
@@ -61,7 +61,11 @@ const chatListViewModel = new ChatListViewModel(
contactListViewModel, contactListViewModel,
); );
const fileTransferViewModel = new FileTransferViewModel(coreViewModel); const fileTransferViewModel = new FileTransferViewModel(coreViewModel);
const onboardingViewModel = new OnboardingViewModel(connectionViewModel, fileTransferViewModel, settingsViewModel); const onboardingViewModel = new OnboardingViewModel(
connectionViewModel,
fileTransferViewModel,
settingsViewModel,
);
const homeViewModel = new HomeViewModel( const homeViewModel = new HomeViewModel(
coreViewModel, coreViewModel,
@@ -97,8 +101,8 @@ document
fileTransferViewModel, fileTransferViewModel,
chatListViewModel, chatListViewModel,
), ),
ChatPageWrapper(coreViewModel, chatListViewModel), ChatPageWrapper(coreViewModel, chatListViewModel),
OnboardingModalWrapper(coreViewModel, onboardingViewModel), OnboardingModalWrapper(coreViewModel, onboardingViewModel),
ConnectionModal(coreViewModel, connectionViewModel), ConnectionModal(coreViewModel, connectionViewModel),
DataTransferModalWrapper( DataTransferModalWrapper(
coreViewModel, coreViewModel,
@@ -106,5 +110,5 @@ document
fileTransferViewModel, fileTransferViewModel,
), ),
StorageModal(coreViewModel, storageViewModel), StorageModal(coreViewModel, storageViewModel),
SettingsModal(coreViewModel, settingsViewModel), SettingsModal(coreViewModel, settingsViewModel),
); );