implement update prompt, clean service worker handling
This commit is contained in:
Vendored
-1
@@ -15,7 +15,6 @@
|
|||||||
<link rel="stylesheet" href="index.css" />
|
<link rel="stylesheet" href="index.css" />
|
||||||
<link rel="manifest" href="manifest.json" />
|
<link rel="manifest" href="manifest.json" />
|
||||||
<link rel="icon" href="icon.png" />
|
<link rel="icon" href="icon.png" />
|
||||||
<script src="support.js" defer></script>
|
|
||||||
|
|
||||||
<script src="index.js" defer></script>
|
<script src="index.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
Vendored
+59
-2
@@ -353,6 +353,7 @@
|
|||||||
settingsButton: "Settings",
|
settingsButton: "Settings",
|
||||||
manageStorageButton: "Manage storage",
|
manageStorageButton: "Manage storage",
|
||||||
transferDataButton: "Transfer or export data",
|
transferDataButton: "Transfer or export data",
|
||||||
|
updateButton: (version) => `Update to ${version}`,
|
||||||
scrollToChatButton: "Chats",
|
scrollToChatButton: "Chats",
|
||||||
///
|
///
|
||||||
backToOverviewAudioLabel: "go back to overview",
|
backToOverviewAudioLabel: "go back to overview",
|
||||||
@@ -596,6 +597,7 @@
|
|||||||
settingsButton: "Einstellungen",
|
settingsButton: "Einstellungen",
|
||||||
manageStorageButton: "Daten verwalten",
|
manageStorageButton: "Daten verwalten",
|
||||||
transferDataButton: "Daten \xFCbertragen",
|
transferDataButton: "Daten \xFCbertragen",
|
||||||
|
updateButton: (version) => `Aktualisieren: ${version}`,
|
||||||
scrollToChatButton: "Chats",
|
scrollToChatButton: "Chats",
|
||||||
backToOverviewAudioLabel: "zur\xFCck zur \xFCbersicht",
|
backToOverviewAudioLabel: "zur\xFCck zur \xFCbersicht",
|
||||||
chatsHeadline: "Chats",
|
chatsHeadline: "Chats",
|
||||||
@@ -820,6 +822,7 @@
|
|||||||
settingsButton: "Ajustes",
|
settingsButton: "Ajustes",
|
||||||
manageStorageButton: "Gestionar almacenamiento",
|
manageStorageButton: "Gestionar almacenamiento",
|
||||||
transferDataButton: "Enviar o exportar archivos",
|
transferDataButton: "Enviar o exportar archivos",
|
||||||
|
updateButton: (version) => `Actualizar a ${version}`,
|
||||||
scrollToChatButton: "Chats",
|
scrollToChatButton: "Chats",
|
||||||
backToOverviewAudioLabel: "volver al resumen",
|
backToOverviewAudioLabel: "volver al resumen",
|
||||||
chatsHeadline: "Chats",
|
chatsHeadline: "Chats",
|
||||||
@@ -1314,7 +1317,12 @@
|
|||||||
this.connectionModel = connectionModel2;
|
this.connectionModel = connectionModel2;
|
||||||
this.chatListModel = chatListModel2;
|
this.chatListModel = chatListModel2;
|
||||||
this.fileTransferModel = fileTransferModel2;
|
this.fileTransferModel = fileTransferModel2;
|
||||||
this.BUILD = "Build 26.09.24.D";
|
this.swRegistration = void 0;
|
||||||
|
this.version = new State("");
|
||||||
|
this.latestVersion = new State("");
|
||||||
|
this.noUpdateAvailable = createProxyState([this.latestVersion], () => {
|
||||||
|
return this.latestVersion.value == "" || this.latestVersion.value == this.version.value;
|
||||||
|
});
|
||||||
// CONTEXT
|
// CONTEXT
|
||||||
this.contextStack = /* @__PURE__ */ new Map();
|
this.contextStack = /* @__PURE__ */ new Map();
|
||||||
this.closeContext = (contextId, fromHistoryEvent = false) => {
|
this.closeContext = (contextId, fromHistoryEvent = false) => {
|
||||||
@@ -1356,12 +1364,46 @@
|
|||||||
};
|
};
|
||||||
// DRAG & DROP
|
// DRAG & DROP
|
||||||
this.draggedObject = new State(void 0);
|
this.draggedObject = new State(void 0);
|
||||||
|
// OFFLINE MODE & UPDATE
|
||||||
|
this.configureServiceWorker = async () => {
|
||||||
|
if ("serviceWorker" in navigator) {
|
||||||
|
try {
|
||||||
|
this.swRegistration = await navigator.serviceWorker.register(
|
||||||
|
"sw.js",
|
||||||
|
{
|
||||||
|
scope: "/"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (this.swRegistration.active) {
|
||||||
|
console.log("Service worker active");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Could not install service worker: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fetch("/version").then(async (response) => {
|
||||||
|
this.version.value = await response.text();
|
||||||
|
});
|
||||||
|
fetch("/latestVersion").then(async (response) => {
|
||||||
|
this.latestVersion.value = (await response.text()).replace("\n", "");
|
||||||
|
console.log("LATEST", this.latestVersion.value);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
this.update = async () => {
|
||||||
|
if (!this.swRegistration) return;
|
||||||
|
await this.swRegistration.update();
|
||||||
|
window.location.reload();
|
||||||
|
};
|
||||||
this.translations = allTranslations[settingsModel2.language] || allTranslations.en;
|
this.translations = allTranslations[settingsModel2.language] || allTranslations.en;
|
||||||
document.body.addEventListener("keydown", this.handleKeyDown);
|
document.body.addEventListener("keydown", this.handleKeyDown);
|
||||||
window.onpopstate = () => {
|
window.onpopstate = () => {
|
||||||
if (!this.context) return;
|
if (!this.context) return;
|
||||||
this.closeContext(this.context.contextId, true);
|
this.closeContext(this.context.contextId, true);
|
||||||
};
|
};
|
||||||
|
this.configureServiceWorker();
|
||||||
|
this.noUpdateAvailable.subscribe(() => {
|
||||||
|
console.log(this.noUpdateAvailable.value, this.latestVersion.value, this.version.value);
|
||||||
|
});
|
||||||
this.startChron();
|
this.startChron();
|
||||||
this.chronHandlerManager.setHandler(
|
this.chronHandlerManager.setHandler(
|
||||||
"core-view-model",
|
"core-view-model",
|
||||||
@@ -4955,6 +4997,7 @@
|
|||||||
|
|
||||||
// src/View/homePage.tsx
|
// src/View/homePage.tsx
|
||||||
function HomePage(coreViewModel2, storageViewModel2, settingsViewModel2, connectionViewModel2, fileTransferViewModel2, chatListViewModel2) {
|
function HomePage(coreViewModel2, storageViewModel2, settingsViewModel2, connectionViewModel2, fileTransferViewModel2, chatListViewModel2) {
|
||||||
|
const updateText = createProxyState([coreViewModel2.latestVersion], () => coreViewModel2.translations.homePage.updateButton(coreViewModel2.latestVersion.value));
|
||||||
const overviewSection = /* @__PURE__ */ createElement("div", { id: "overview-section" }, /* @__PURE__ */ createElement("h2", null, coreViewModel2.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, coreViewModel2.translations.homePage.serverAddress), /* @__PURE__ */ createElement(
|
const overviewSection = /* @__PURE__ */ createElement("div", { id: "overview-section" }, /* @__PURE__ */ createElement("h2", null, coreViewModel2.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, coreViewModel2.translations.homePage.serverAddress), /* @__PURE__ */ createElement(
|
||||||
"input",
|
"input",
|
||||||
{
|
{
|
||||||
@@ -5029,6 +5072,20 @@
|
|||||||
storageViewModel2.showStorageModal,
|
storageViewModel2.showStorageModal,
|
||||||
coreViewModel2.translations.homePage.manageStorageButton,
|
coreViewModel2.translations.homePage.manageStorageButton,
|
||||||
"hard_drive"
|
"hard_drive"
|
||||||
|
), /* @__PURE__ */ createElement(
|
||||||
|
"button",
|
||||||
|
{
|
||||||
|
class: "primary",
|
||||||
|
"on:click": coreViewModel2.update,
|
||||||
|
"toggle:hidden": coreViewModel2.noUpdateAvailable
|
||||||
|
},
|
||||||
|
/* @__PURE__ */ createElement(
|
||||||
|
"span",
|
||||||
|
{
|
||||||
|
"subscribe:innerText": updateText
|
||||||
|
}
|
||||||
|
),
|
||||||
|
/* @__PURE__ */ createElement("span", { class: "icon" }, "update")
|
||||||
), /* @__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 }, coreViewModel2.translations.homePage.scrollToChatButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_forward")))));
|
), /* @__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 }, coreViewModel2.translations.homePage.scrollToChatButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_forward")))));
|
||||||
const chatSection = /* @__PURE__ */ createElement("div", { id: "chat-section" }, /* @__PURE__ */ createElement("h2", null, coreViewModel2.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, coreViewModel2.translations.homePage.chatsHeadline), /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement(
|
||||||
"input",
|
"input",
|
||||||
@@ -7221,7 +7278,7 @@
|
|||||||
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.version,
|
||||||
settingsViewModel2.coreViewModel.BUILD
|
settingsViewModel2.coreViewModel.version
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
function SettingsRegionalPane(coreViewModel2, settingsViewModel2) {
|
function SettingsRegionalPane(coreViewModel2, settingsViewModel2) {
|
||||||
|
|||||||
Vendored
-35
@@ -1,35 +0,0 @@
|
|||||||
// TABS
|
|
||||||
document.querySelector("main").addEventListener("scroll", () => {
|
|
||||||
const links = document.querySelectorAll(".tab-link");
|
|
||||||
links.forEach((element) => element.removeAttribute("active"));
|
|
||||||
|
|
||||||
const pageWidth = document.querySelector("main > article").offsetWidth;
|
|
||||||
const scroll = document.querySelector("main").scrollLeft;
|
|
||||||
const currentPage = Math.round(scroll / pageWidth);
|
|
||||||
|
|
||||||
if (links[currentPage]) links[currentPage].setAttribute("active", "");
|
|
||||||
});
|
|
||||||
|
|
||||||
// SERVICE WORKER
|
|
||||||
async function registerServiceWorker() {
|
|
||||||
if ("serviceWorker" in navigator) {
|
|
||||||
try {
|
|
||||||
const registration = await navigator.serviceWorker.register(
|
|
||||||
"/sw.js",
|
|
||||||
{
|
|
||||||
scope: "/",
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (registration.installing) {
|
|
||||||
setTimeout(() => window.location.reload(), 500);
|
|
||||||
} else if (registration.active) {
|
|
||||||
console.log("Service worker active");
|
|
||||||
registration.update();
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Could not install service worker: ${error}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
registerServiceWorker();
|
|
||||||
Vendored
+35
-14
@@ -1,32 +1,53 @@
|
|||||||
const CACHE_VERSION = "3";
|
const VERSION = "2610 Beta 1"
|
||||||
|
|
||||||
self.addEventListener("fetch", (event) => {
|
function processUrl(event) {
|
||||||
event.respondWith(handleRequest(event));
|
const url = event.request.url;
|
||||||
|
const fileName = url.split("/").pop();
|
||||||
|
return {url, fileName};
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addEventListener("install", () => {
|
||||||
|
self.skipWaiting();
|
||||||
});
|
});
|
||||||
|
|
||||||
async function handleRequest(event) {
|
self.addEventListener("fetch", async (event) => {
|
||||||
const response = await getFromCache(event);
|
console.log(VERSION);
|
||||||
if (response) {
|
const {url, fileName}=processUrl(event);
|
||||||
fetchAndCache(event); // Call this but don't await, allowing the cache to update in the background
|
event.respondWith(handleRequest(event, url, fileName));
|
||||||
return response;
|
});
|
||||||
|
|
||||||
|
async function handleRequest(event, url, fileName) {
|
||||||
|
switch (fileName) {
|
||||||
|
case "version":
|
||||||
|
const response = new Response(VERSION);
|
||||||
|
return response;
|
||||||
|
case "latestVersion":
|
||||||
|
return await fetch("/version.txt", {cache: "no-cache"})
|
||||||
}
|
}
|
||||||
return fetchAndCache(event);
|
const response = await getFromCache(event);
|
||||||
|
|
||||||
|
if (response) {
|
||||||
|
fetchAndCache(event, fileName); // Call this but don't await, allowing the cache to update in the background
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
return fetchAndCache(event, fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getFromCache(event) {
|
async function getFromCache(event) {
|
||||||
const request = event.request;
|
const request = event.request;
|
||||||
const cache = await caches.open(CACHE_VERSION);
|
const cache = await caches.open(VERSION);
|
||||||
const response = await cache.match(request);
|
const response = await cache.match(request);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchAndCache(event) {
|
async function fetchAndCache(event, fileName) {
|
||||||
const request = event.request;
|
const request = event.request;
|
||||||
|
|
||||||
const response = await fetch(request, { cache: "no-cache" });
|
const response = await fetch(request, { cache: "no-cache" });
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
// Only cache successful responses
|
// Only cache successful responses
|
||||||
const cache = await caches.open(CACHE_VERSION);
|
const cache = await caches.open(VERSION);
|
||||||
cache.put(request, response.clone());
|
await cache.put(request, response.clone());
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
2610 Beta 1
|
||||||
@@ -122,7 +122,7 @@ function SettingsInfoPane(
|
|||||||
{InfoTile(
|
{InfoTile(
|
||||||
"build",
|
"build",
|
||||||
coreViewModel.translations.settings.version,
|
coreViewModel.translations.settings.version,
|
||||||
settingsViewModel.coreViewModel.BUILD,
|
settingsViewModel.coreViewModel.version,
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+179
-166
@@ -19,185 +19,198 @@ export function HomePage(
|
|||||||
chatListViewModel: ChatListViewModel,
|
chatListViewModel: ChatListViewModel,
|
||||||
) {
|
) {
|
||||||
// sections
|
// sections
|
||||||
|
const updateText = React.createProxyState([coreViewModel.latestVersion], ()=>coreViewModel.translations.homePage.updateButton(coreViewModel.latestVersion.value));
|
||||||
|
|
||||||
const overviewSection = (
|
const overviewSection = (
|
||||||
<div id="overview-section">
|
<div id="overview-section">
|
||||||
<h2>{coreViewModel.translations.homePage.overviewHeadline}</h2>
|
<h2>{coreViewModel.translations.homePage.overviewHeadline}</h2>
|
||||||
|
|
||||||
<label class="tile flex-no">
|
<label class="tile flex-no">
|
||||||
<span class="icon">cell_tower</span>
|
<span class="icon">cell_tower</span>
|
||||||
<div>
|
<div>
|
||||||
<span>
|
<span>
|
||||||
{coreViewModel.translations.homePage.serverAddress}
|
{coreViewModel.translations.homePage.serverAddress}
|
||||||
</span>
|
</span>
|
||||||
<input
|
<input
|
||||||
list="previous-connection-list"
|
list="previous-connection-list"
|
||||||
placeholder={
|
placeholder={
|
||||||
coreViewModel.translations.homePage
|
coreViewModel.translations.homePage
|
||||||
.serverAddressPlaceholder
|
.serverAddressPlaceholder
|
||||||
}
|
}
|
||||||
bind:value={connectionViewModel.serverAddressInput}
|
bind:value={connectionViewModel.serverAddressInput}
|
||||||
on:enter={connectionViewModel.connect}
|
on:enter={connectionViewModel.connect}
|
||||||
></input>
|
></input>
|
||||||
<datalist
|
<datalist
|
||||||
hidden
|
hidden
|
||||||
id="previous-connection-list"
|
id="previous-connection-list"
|
||||||
children:append={[
|
children:append={[
|
||||||
connectionViewModel.previousAddresses,
|
connectionViewModel.previousAddresses,
|
||||||
StringToOption,
|
StringToOption,
|
||||||
]}
|
]}
|
||||||
></datalist>
|
></datalist>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="flex-row">
|
<div class="flex-row">
|
||||||
<button
|
<button
|
||||||
class="danger flex justify-center"
|
class="danger flex justify-center"
|
||||||
aria-label={
|
aria-label={
|
||||||
coreViewModel.translations.homePage.disconnectAudioLabel
|
coreViewModel.translations.homePage.disconnectAudioLabel
|
||||||
}
|
}
|
||||||
on:click={connectionViewModel.disconnect}
|
on:click={connectionViewModel.disconnect}
|
||||||
toggle:disabled={connectionViewModel.cannotDisonnect}
|
toggle:disabled={connectionViewModel.cannotDisonnect}
|
||||||
>
|
>
|
||||||
<span class="icon">link_off</span>
|
<span class="icon">link_off</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="flex justify-center"
|
class="flex justify-center"
|
||||||
aria-label={
|
aria-label={
|
||||||
coreViewModel.translations.homePage
|
coreViewModel.translations.homePage
|
||||||
.manageConnectionsAudioLabel
|
.manageConnectionsAudioLabel
|
||||||
}
|
}
|
||||||
on:click={connectionViewModel.showConnectionModal}
|
on:click={connectionViewModel.showConnectionModal}
|
||||||
toggle:disabled={
|
toggle:disabled={
|
||||||
connectionViewModel.hasNoPreviousConnections
|
connectionViewModel.hasNoPreviousConnections
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<span class="icon">history</span>
|
<span class="icon">history</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="primary flex justify-center"
|
class="primary flex justify-center"
|
||||||
aria-label={
|
aria-label={
|
||||||
coreViewModel.translations.homePage.connectAudioLabel
|
coreViewModel.translations.homePage.connectAudioLabel
|
||||||
}
|
}
|
||||||
on:click={connectionViewModel.connect}
|
on:click={connectionViewModel.connect}
|
||||||
toggle:disabled={connectionViewModel.cannotConnect}
|
toggle:disabled={connectionViewModel.cannotConnect}
|
||||||
>
|
>
|
||||||
<span class="icon">link</span>
|
<span class="icon">link</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<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.homePage.yourNameLabel}
|
{coreViewModel.translations.homePage.yourNameLabel}
|
||||||
</span>
|
</span>
|
||||||
<input
|
<input
|
||||||
placeholder={
|
placeholder={
|
||||||
coreViewModel.translations.homePage
|
coreViewModel.translations.homePage
|
||||||
.yourNamePlaceholder
|
.yourNamePlaceholder
|
||||||
}
|
}
|
||||||
bind:value={settingsViewModel.usernameInput}
|
bind:value={settingsViewModel.usernameInput}
|
||||||
on:enter={settingsViewModel.setName}
|
on:enter={settingsViewModel.setName}
|
||||||
></input>
|
></input>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<div class="flex-row justify-end">
|
<div class="flex-row justify-end">
|
||||||
<button
|
<button
|
||||||
class="width-50"
|
class="width-50"
|
||||||
on:click={settingsViewModel.setName}
|
on:click={settingsViewModel.setName}
|
||||||
toggle:disabled={settingsViewModel.cannotSetName}
|
toggle:disabled={settingsViewModel.cannotSetName}
|
||||||
aria-label={
|
aria-label={
|
||||||
coreViewModel.translations.homePage
|
coreViewModel.translations.homePage
|
||||||
.setNameButtonAudioLabel
|
.setNameButtonAudioLabel
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{coreViewModel.translations.general.setButton}
|
{coreViewModel.translations.general.setButton}
|
||||||
<span class="icon">check</span>
|
<span class="icon">check</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr></hr>
|
<hr></hr>
|
||||||
|
|
||||||
{HomePageButton(
|
{HomePageButton(
|
||||||
settingsViewModel.showSettingsModal,
|
settingsViewModel.showSettingsModal,
|
||||||
coreViewModel.translations.homePage.settingsButton,
|
coreViewModel.translations.homePage.settingsButton,
|
||||||
"settings",
|
"settings",
|
||||||
)}
|
)}
|
||||||
{HomePageButton(
|
{HomePageButton(
|
||||||
fileTransferViewModel.showDirectionSelectionModal,
|
fileTransferViewModel.showDirectionSelectionModal,
|
||||||
coreViewModel.translations.homePage.transferDataButton,
|
coreViewModel.translations.homePage.transferDataButton,
|
||||||
"sync_alt",
|
"sync_alt",
|
||||||
)}
|
)}
|
||||||
{HomePageButton(
|
{HomePageButton(
|
||||||
storageViewModel.showStorageModal,
|
storageViewModel.showStorageModal,
|
||||||
coreViewModel.translations.homePage.manageStorageButton,
|
coreViewModel.translations.homePage.manageStorageButton,
|
||||||
"hard_drive",
|
"hard_drive",
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div class="mobile-only">
|
<button
|
||||||
<hr></hr>
|
class="primary"
|
||||||
|
on:click={coreViewModel.update}
|
||||||
|
toggle:hidden={coreViewModel.noUpdateAvailable}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
subscribe:innerText={updateText}
|
||||||
|
></span>
|
||||||
|
<span class="icon">update</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
<div class="flex-row justify-end">
|
<div class="mobile-only">
|
||||||
<button class="ghost width-50" on:click={scrollToChat}>
|
<hr></hr>
|
||||||
{coreViewModel.translations.homePage.scrollToChatButton}
|
|
||||||
<span class="icon">arrow_forward</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const chatSection = (
|
<div class="flex-row justify-end">
|
||||||
<div id="chat-section">
|
<button class="ghost width-50" on:click={scrollToChat}>
|
||||||
<h2>{coreViewModel.translations.homePage.chatsHeadline}</h2>
|
{coreViewModel.translations.homePage.scrollToChatButton}
|
||||||
|
<span class="icon">arrow_forward</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
<div class="flex-row width-input">
|
const chatSection = (
|
||||||
<input
|
<div id="chat-section">
|
||||||
placeholder={
|
<h2>{coreViewModel.translations.homePage.chatsHeadline}</h2>
|
||||||
coreViewModel.translations.homePage.addChatPlaceholder
|
|
||||||
}
|
|
||||||
aria-label={
|
|
||||||
coreViewModel.translations.homePage.addChatAudioLabel
|
|
||||||
}
|
|
||||||
bind:value={chatListViewModel.newChatPrimaryChannel}
|
|
||||||
on:enter={chatListViewModel.createChat}
|
|
||||||
></input>
|
|
||||||
<button
|
|
||||||
class="primary"
|
|
||||||
aria-label={
|
|
||||||
coreViewModel.translations.homePage.addChatButton
|
|
||||||
}
|
|
||||||
on:click={chatListViewModel.createChat}
|
|
||||||
toggle:disabled={chatListViewModel.cannotCreateChat}
|
|
||||||
>
|
|
||||||
<span class="icon">add</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
<div class="flex-row width-input">
|
||||||
id="chat-grid"
|
<input
|
||||||
children:append={[
|
placeholder={
|
||||||
chatListViewModel.chatViewModels,
|
coreViewModel.translations.homePage.addChatPlaceholder
|
||||||
ChatViewModelToChatEntry,
|
}
|
||||||
]}
|
aria-label={
|
||||||
></div>
|
coreViewModel.translations.homePage.addChatAudioLabel
|
||||||
</div>
|
}
|
||||||
);
|
bind:value={chatListViewModel.newChatPrimaryChannel}
|
||||||
|
on:enter={chatListViewModel.createChat}
|
||||||
|
></input>
|
||||||
|
<button
|
||||||
|
class="primary"
|
||||||
|
aria-label={
|
||||||
|
coreViewModel.translations.homePage.addChatButton
|
||||||
|
}
|
||||||
|
on:click={chatListViewModel.createChat}
|
||||||
|
toggle:disabled={chatListViewModel.cannotCreateChat}
|
||||||
|
>
|
||||||
|
<span class="icon">add</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
// methods
|
<div
|
||||||
function scrollToChat() {
|
id="chat-grid"
|
||||||
chatSection.scrollIntoView();
|
children:append={[
|
||||||
}
|
chatListViewModel.chatViewModels,
|
||||||
|
ChatViewModelToChatEntry,
|
||||||
|
]}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
// final
|
// methods
|
||||||
return (
|
function scrollToChat() {
|
||||||
<article id="home-page">
|
chatSection.scrollIntoView();
|
||||||
<div>
|
}
|
||||||
{overviewSection}
|
|
||||||
{chatSection}
|
// final
|
||||||
</div>
|
return (
|
||||||
</article>
|
<article id="home-page">
|
||||||
|
<div>
|
||||||
|
{overviewSection}
|
||||||
|
{chatSection}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ const englishTranslations = {
|
|||||||
manageStorageButton: "Manage storage",
|
manageStorageButton: "Manage storage",
|
||||||
transferDataButton: "Transfer or export data",
|
transferDataButton: "Transfer or export data",
|
||||||
|
|
||||||
|
updateButton: (version: string) => `Update to ${version}`,
|
||||||
|
|
||||||
scrollToChatButton: "Chats",
|
scrollToChatButton: "Chats",
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -411,6 +413,8 @@ export const allTranslations: {
|
|||||||
settingsButton: "Einstellungen",
|
settingsButton: "Einstellungen",
|
||||||
manageStorageButton: "Daten verwalten",
|
manageStorageButton: "Daten verwalten",
|
||||||
transferDataButton: "Daten übertragen",
|
transferDataButton: "Daten übertragen",
|
||||||
|
|
||||||
|
updateButton: (version: string) => `Aktualisieren: ${version}`,
|
||||||
|
|
||||||
scrollToChatButton: "Chats",
|
scrollToChatButton: "Chats",
|
||||||
|
|
||||||
@@ -716,6 +720,8 @@ export const allTranslations: {
|
|||||||
settingsButton: "Ajustes",
|
settingsButton: "Ajustes",
|
||||||
manageStorageButton: "Gestionar almacenamiento",
|
manageStorageButton: "Gestionar almacenamiento",
|
||||||
transferDataButton: "Enviar o exportar archivos",
|
transferDataButton: "Enviar o exportar archivos",
|
||||||
|
|
||||||
|
updateButton: (version: string) => `Actualizar a ${version}`,
|
||||||
|
|
||||||
scrollToChatButton: "Chats",
|
scrollToChatButton: "Chats",
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,13 @@ import ConnectionModel from "../../Model/Global/connectionModel";
|
|||||||
import ChatListModel from "../../Model/Chat/chatListModel";
|
import ChatListModel from "../../Model/Chat/chatListModel";
|
||||||
|
|
||||||
export default class CoreViewModel {
|
export default class CoreViewModel {
|
||||||
readonly BUILD = "Build 26.09.24.D";
|
swRegistration: ServiceWorkerRegistration|undefined = undefined;
|
||||||
|
|
||||||
|
version: React.State<string> = new React.State("");
|
||||||
|
latestVersion: React.State<string> = new React.State("");
|
||||||
|
noUpdateAvailable: React.State<boolean> = React.createProxyState([this.latestVersion], () => {
|
||||||
|
return (this.latestVersion.value == "" || this.latestVersion.value == this.version.value)
|
||||||
|
})
|
||||||
|
|
||||||
translations: Translations;
|
translations: Translations;
|
||||||
|
|
||||||
@@ -84,34 +90,72 @@ export default class CoreViewModel {
|
|||||||
// DRAG & DROP
|
// DRAG & DROP
|
||||||
draggedObject: React.State<any> = new React.State<any>(undefined);
|
draggedObject: React.State<any> = new React.State<any>(undefined);
|
||||||
|
|
||||||
|
// OFFLINE MODE & UPDATE
|
||||||
|
configureServiceWorker = async (): Promise<void> => {
|
||||||
|
if ("serviceWorker" in navigator) {
|
||||||
|
try {
|
||||||
|
this.swRegistration = await navigator.serviceWorker.register(
|
||||||
|
"sw.js",
|
||||||
|
{
|
||||||
|
scope: "/",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (this.swRegistration.active) {
|
||||||
|
console.log("Service worker active");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Could not install service worker: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch("/version").then(async response => {
|
||||||
|
this.version.value = await response.text();
|
||||||
|
});
|
||||||
|
fetch("/latestVersion").then(async response => {
|
||||||
|
this.latestVersion.value = (await response.text()).replace("\n", "");
|
||||||
|
console.log("LATEST", this.latestVersion.value);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
update = async (): Promise<void> => {
|
||||||
|
if (!this.swRegistration) return;
|
||||||
|
await this.swRegistration.update();
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
constructor(
|
constructor(
|
||||||
public readonly storageModel: StorageModel,
|
public readonly storageModel: StorageModel,
|
||||||
public readonly settingsModel: SettingsModel,
|
public readonly settingsModel: SettingsModel,
|
||||||
public readonly connectionModel: ConnectionModel,
|
public readonly connectionModel: ConnectionModel,
|
||||||
public readonly chatListModel: ChatListModel,
|
public readonly chatListModel: ChatListModel,
|
||||||
public readonly fileTransferModel: FileTransferModel,
|
public readonly fileTransferModel: FileTransferModel,
|
||||||
) {
|
) {
|
||||||
this.translations =
|
this.translations =
|
||||||
allTranslations[settingsModel.language] || allTranslations.en;
|
allTranslations[settingsModel.language] || allTranslations.en;
|
||||||
|
|
||||||
document.body.addEventListener("keydown", this.handleKeyDown);
|
document.body.addEventListener("keydown", this.handleKeyDown);
|
||||||
|
|
||||||
window.onpopstate = () => {
|
window.onpopstate = () => {
|
||||||
if (!this.context) return;
|
if (!this.context) return;
|
||||||
this.closeContext(this.context.contextId, true);
|
this.closeContext(this.context.contextId, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.startChron();
|
this.configureServiceWorker();
|
||||||
this.chronHandlerManager.setHandler(
|
this.noUpdateAvailable.subscribe(() => {
|
||||||
"core-view-model",
|
console.log(this.noUpdateAvailable.value, this.latestVersion.value, this.version.value);
|
||||||
this.handleChron,
|
})
|
||||||
);
|
|
||||||
|
this.startChron();
|
||||||
|
this.chronHandlerManager.setHandler(
|
||||||
|
"core-view-model",
|
||||||
|
this.handleChron,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// util
|
// util
|
||||||
static checkIsKeystroke(e: KeyboardEvent): boolean {
|
static checkIsKeystroke(e: KeyboardEvent): boolean {
|
||||||
return (e.metaKey || e.altKey) && e.ctrlKey;
|
return (e.metaKey || e.altKey) && e.ctrlKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,19 +164,19 @@ export class Context {
|
|||||||
keystrokes = new Map<string, () => void>();
|
keystrokes = new Map<string, () => void>();
|
||||||
|
|
||||||
handleKeystroke = (e: KeyboardEvent): boolean => {
|
handleKeystroke = (e: KeyboardEvent): boolean => {
|
||||||
const fn: (() => void) | undefined = this.keystrokes.get(
|
const fn: (() => void) | undefined = this.keystrokes.get(
|
||||||
e.key.toLowerCase(),
|
e.key.toLowerCase(),
|
||||||
);
|
);
|
||||||
if (!fn) return false;
|
if (!fn) return false;
|
||||||
fn();
|
fn();
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
close = (): void => {};
|
close = (): void => {};
|
||||||
handleContextClose = (fromHistoryEvent: boolean): void => {};
|
handleContextClose = (fromHistoryEvent: boolean): void => {};
|
||||||
|
|
||||||
registerKeyStroke = (key: string, fn: () => void): void => {
|
registerKeyStroke = (key: string, fn: () => void): void => {
|
||||||
this.keystrokes.set(key, fn);
|
this.keystrokes.set(key, fn);
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(public contextDebugDescription: string) {}
|
constructor(public contextDebugDescription: string) {}
|
||||||
@@ -143,45 +187,45 @@ export class ContextHost<T> extends Context {
|
|||||||
currentContext = new React.State<Context | undefined>(undefined);
|
currentContext = new React.State<Context | undefined>(undefined);
|
||||||
|
|
||||||
get isOpen(): boolean {
|
get isOpen(): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
get contextSelection(): T | undefined {
|
get contextSelection(): T | undefined {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerContext = (key: T, context: Context): void => {
|
registerContext = (key: T, context: Context): void => {
|
||||||
this.contexts.set(key, context);
|
this.contexts.set(key, context);
|
||||||
};
|
};
|
||||||
|
|
||||||
closeCurrentContext = (): void => {
|
closeCurrentContext = (): void => {
|
||||||
if (this.currentContext.value) {
|
if (this.currentContext.value) {
|
||||||
this.coreViewModel.closeContext(
|
this.coreViewModel.closeContext(
|
||||||
this.currentContext.value.contextId,
|
this.currentContext.value.contextId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.currentContext.value = undefined;
|
this.currentContext.value = undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
updateContexts = (): void => {
|
updateContexts = (): void => {
|
||||||
if (this.isOpen == false) return;
|
if (this.isOpen == false) return;
|
||||||
|
|
||||||
const selection = this.contextSelection;
|
const selection = this.contextSelection;
|
||||||
if (!selection) return;
|
if (!selection) return;
|
||||||
const selectedContext: Context | undefined =
|
const selectedContext: Context | undefined =
|
||||||
this.contexts.get(selection);
|
this.contexts.get(selection);
|
||||||
if (!selectedContext) return;
|
if (!selectedContext) return;
|
||||||
if (selectedContext != this.currentContext.value) {
|
if (selectedContext != this.currentContext.value) {
|
||||||
this.closeCurrentContext();
|
this.closeCurrentContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.coreViewModel.context = selectedContext;
|
this.coreViewModel.context = selectedContext;
|
||||||
this.currentContext.value = selectedContext;
|
this.currentContext.value = selectedContext;
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
contextDebugDescription: string,
|
contextDebugDescription: string,
|
||||||
public coreViewModel: CoreViewModel,
|
public coreViewModel: CoreViewModel,
|
||||||
) {
|
) {
|
||||||
super(contextDebugDescription);
|
super(contextDebugDescription);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user