Skip to content

Commit 619d2c5

Browse files
committed
frontend: add persistent storage to keep browsers from clearing the cache
1 parent 88da52b commit 619d2c5

File tree

3 files changed

+98
-2
lines changed

3 files changed

+98
-2
lines changed

frontend/src/index.tsx

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ import Tab from "react-bootstrap/Tab";
3232
import { ErrorAlert } from './components/Alert.js';
3333
import { isDebugMode, refresh_access_token } from './utils';
3434
import { AppState, loggedIn } from './utils.js';
35-
import { Col, Spinner } from 'react-bootstrap';
35+
import { Col, Form, Spinner } from 'react-bootstrap';
36+
import Modal from "react-bootstrap/Modal";
37+
import Button from "react-bootstrap/Button";
3638
import { Recovery } from './pages/Recovery.js';
3739
import { Trans, useTranslation } from "react-i18next";
3840
import Median from "median-js-bridge";
@@ -44,7 +46,7 @@ import { startVersionChecking } from './versionChecker';
4446

4547
import "./styles/main.scss";
4648
import { docs } from "links";
47-
import { useEffect } from "preact/hooks";
49+
import { useEffect, useState } from "preact/hooks";
4850

4951
if (isDebugMode.value) {
5052
addEventListener("unhandledrejection", (event) => {
@@ -79,6 +81,52 @@ const Frame = lazy(() => import('./pages/Frame.js').then(m => m.Frame));
7981
export function App() {
8082
const {t} = useTranslation("", {useSuspense: false});
8183

84+
const [modalConfig, setModalConfig] = useState({ show: false, rememberChoice: false });
85+
86+
console.log(window.navigator.userAgent);
87+
if (window.navigator.userAgent.indexOf("Firefox") !== -1 && localStorage.getItem("persistedChoice") !== "true") {
88+
useEffect(() => {
89+
(async () => {
90+
try {
91+
const persisted = await navigator.storage.persisted();
92+
console.log(`Storage persisted: ${persisted}`);
93+
if (!persisted) {
94+
setModalConfig({...modalConfig, show: true });
95+
}
96+
} catch (e) {
97+
console.warn("Failed to check storage persistence", e);
98+
}
99+
})();
100+
}, []);
101+
} else {
102+
useEffect(() => {
103+
(async () => {
104+
try {
105+
const granted = await navigator.storage.persist();
106+
console.log(`Storage persistence granted: ${granted}`);
107+
if (!granted) {
108+
console.warn("Storage persistence not granted. You may be logged out from time to time.");
109+
}
110+
} catch (e) {
111+
console.warn("Failed to request storage persistence", e);
112+
}
113+
})();
114+
}, []);
115+
}
116+
117+
const requestPersistence = async () => {
118+
try {
119+
const granted = await navigator.storage.persist();
120+
if (!granted) {
121+
console.warn("Storage persistence not granted. You may be logged out from time to time.");
122+
}
123+
} catch (e) {
124+
console.warn("Failed to request storage persistence", e);
125+
} finally {
126+
setModalConfig({...modalConfig, show: false });
127+
}
128+
};
129+
82130
useEffect(() => {
83131
if (!connected.value) {
84132
document.title = t("app_name");
@@ -119,6 +167,39 @@ export function App() {
119167
}
120168
}, []);
121169

170+
// Prepare persistence explanation modal to render alongside any app state
171+
const persistModal = (
172+
<Modal show={modalConfig.show} onHide={() => setModalConfig({...modalConfig, show: false})} backdrop="static" keyboard={false} centered>
173+
<Modal.Header closeButton>
174+
<Modal.Title>{t("storage_persistence.title", { defaultValue: "Keep you signed in" })}</Modal.Title>
175+
</Modal.Header>
176+
<Modal.Body>
177+
{t("storage_persistence.body")}
178+
<div className="mt-3"
179+
onClick={() => setModalConfig({...modalConfig, rememberChoice: !modalConfig.rememberChoice})}>
180+
<Form.Check
181+
type="checkbox"
182+
label={t("storage_persistence.remember_choice")}
183+
checked={modalConfig.rememberChoice}
184+
/>
185+
</div>
186+
</Modal.Body>
187+
<Modal.Footer>
188+
<Button variant="secondary" onClick={() => {
189+
setModalConfig({...modalConfig, show: false});
190+
if (modalConfig.rememberChoice) {
191+
localStorage.setItem("persistedChoice", "true");
192+
}
193+
}}>
194+
{t("not_now")}
195+
</Button>
196+
<Button variant="primary" onClick={requestPersistence}>
197+
{t("allow")}
198+
</Button>
199+
</Modal.Footer>
200+
</Modal>
201+
);
202+
122203
if (!window.ServiceWorker) {
123204
return <Row fluid className="align-content-center justify-content-center vh-100">
124205
{t("no_service_worker")}
@@ -187,6 +268,7 @@ export function App() {
187268
</LocationProvider>
188269
</Col>
189270
{ Median.isNativeApp() ? <></> : <Footer /> }
271+
{persistModal}
190272
</>
191273
);
192274
// we need an extra recovery state, otherwise we would show the login/register page.

frontend/src/locales/de.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ export const de ={
66
"alert_default_success": "Erfolg!",
77
description,
88
"no_service_worker": "Dein Browser benötigt Unterstützung für ServiceWorker um den Fernzugriff nutzen zu können.",
9+
"not_now": "Jetzt nicht",
10+
"allow": "Erlauben",
11+
"storage_persistence": {
12+
"title": "Angemeldet bleiben",
13+
"body": "Wir verwenden den Speicher deines Browsers, um dich angemeldet zu halten. Einige Browser löschen diese Daten automatisch (z. B. bei wenig Speicherplatz oder nach einiger Zeit). Das Zulassen von dauerhaftem Speicher hilft unerwartete Abmeldungen zu vermeiden.",
14+
"remember_choice": "Nicht mehr fragen"
15+
},
916
"user": {
1017
"account_actions": "Kontoaktionen",
1118
"profile_information": "Kontoinformationen",

frontend/src/locales/en.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ export const en = {
66
"alert_default_success": "Success!",
77
description,
88
"no_service_worker": "Your browser needs support for ServiceWorkers to be able to run the Remote Access.",
9+
"not_now": "Not now",
10+
"allow": "Allow",
11+
"storage_persistence": {
12+
"title": "Keep you signed in",
13+
"body": "We use your browser's storage to keep you signed in. Some browsers clear this data automatically (e.g., on low storage or after some time). Allowing persistent storage helps prevent unexpected logouts.",
14+
"remember_choice": "Don't ask again"
15+
},
916
"user": {
1017
"account_actions": "Account actions",
1118
"profile_information": "Account information",

0 commit comments

Comments
 (0)