mirror of
https://github.com/binwiederhier/ntfy.git
synced 2024-12-23 02:02:33 +01:00
Merge branch 'pwa' of github.com:nimbleghost/ntfy into pwa
This commit is contained in:
commit
cf050cc289
19 changed files with 56 additions and 89 deletions
|
@ -367,7 +367,7 @@ class AccountApi {
|
|||
} catch (e) {
|
||||
console.log(`[AccountApi] Error fetching account`, e);
|
||||
if (e instanceof UnauthorizedError) {
|
||||
session.resetAndRedirect(routes.login);
|
||||
await session.resetAndRedirect(routes.login);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
|
|
@ -132,7 +132,6 @@ class Api {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
async deleteWebPush(pushSubscription) {
|
||||
const user = await userManager.get(config.base_url);
|
||||
const url = accountWebPushUrl(config.base_url);
|
||||
|
@ -141,7 +140,7 @@ class Api {
|
|||
method: "DELETE",
|
||||
headers: maybeWithAuth({}, user),
|
||||
body: JSON.stringify({
|
||||
endpoint: pushSubscription.endpoint
|
||||
endpoint: pushSubscription.endpoint,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import db from "./db";
|
||||
|
||||
class Prefs {
|
||||
constructor(db) {
|
||||
this.db = db;
|
||||
constructor(dbImpl) {
|
||||
this.db = dbImpl;
|
||||
}
|
||||
|
||||
async setSound(sound) {
|
||||
|
|
|
@ -1,29 +1,36 @@
|
|||
import sessionReplica from "./SessionReplica";
|
||||
import Dexie from "dexie";
|
||||
|
||||
/**
|
||||
* Manages the logged-in user's session and access token.
|
||||
* The session replica is stored in IndexedDB so that the service worker can access it.
|
||||
*/
|
||||
class Session {
|
||||
constructor(replica) {
|
||||
this.replica = replica;
|
||||
constructor() {
|
||||
const db = new Dexie("session-replica");
|
||||
db.version(1).stores({
|
||||
kv: "&key",
|
||||
});
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
store(username, token) {
|
||||
async store(username, token) {
|
||||
await this.db.kv.bulkPut([
|
||||
{ key: "user", value: username },
|
||||
{ key: "token", value: token },
|
||||
]);
|
||||
localStorage.setItem("user", username);
|
||||
localStorage.setItem("token", token);
|
||||
this.replica.store(username, token);
|
||||
}
|
||||
|
||||
reset() {
|
||||
async resetAndRedirect(url) {
|
||||
await this.db.delete();
|
||||
localStorage.removeItem("user");
|
||||
localStorage.removeItem("token");
|
||||
this.replica.reset();
|
||||
window.location.href = url;
|
||||
}
|
||||
|
||||
resetAndRedirect(url) {
|
||||
this.reset();
|
||||
window.location.href = url;
|
||||
async usernameAsync() {
|
||||
return (await this.db.kv.get({ key: "user" }))?.value;
|
||||
}
|
||||
|
||||
exists() {
|
||||
|
@ -39,5 +46,5 @@ class Session {
|
|||
}
|
||||
}
|
||||
|
||||
const session = new Session(sessionReplica);
|
||||
const session = new Session();
|
||||
export default session;
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
import Dexie from "dexie";
|
||||
|
||||
/**
|
||||
* Replica of the session in IndexedDB. This is used by the service
|
||||
* worker to access the session. This is a bit of a hack.
|
||||
*/
|
||||
class SessionReplica {
|
||||
constructor() {
|
||||
const db = new Dexie("session-replica");
|
||||
db.version(1).stores({
|
||||
kv: "&key",
|
||||
});
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
async store(username, token) {
|
||||
try {
|
||||
await this.db.kv.bulkPut([
|
||||
{ key: "user", value: username },
|
||||
{ key: "token", value: token },
|
||||
]);
|
||||
} catch (e) {
|
||||
console.error("[Session] Error replicating session to IndexedDB", e);
|
||||
}
|
||||
}
|
||||
|
||||
async reset() {
|
||||
try {
|
||||
await this.db.delete();
|
||||
} catch (e) {
|
||||
console.error("[Session] Error resetting session on IndexedDB", e);
|
||||
}
|
||||
}
|
||||
|
||||
async username() {
|
||||
return (await this.db.kv.get({ key: "user" }))?.value;
|
||||
}
|
||||
}
|
||||
|
||||
const sessionReplica = new SessionReplica();
|
||||
export default sessionReplica;
|
|
@ -5,8 +5,8 @@ import db from "./db";
|
|||
import { topicUrl } from "./utils";
|
||||
|
||||
class SubscriptionManager {
|
||||
constructor(db) {
|
||||
this.db = db;
|
||||
constructor(dbImpl) {
|
||||
this.db = dbImpl;
|
||||
}
|
||||
|
||||
/** All subscriptions, including "new count"; this is a JOIN, see https://dexie.org/docs/API-Reference#joining */
|
||||
|
@ -124,7 +124,6 @@ class SubscriptionManager {
|
|||
} else {
|
||||
await api.deleteWebPush(browserSubscription);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async updateState(subscriptionId, state) {
|
||||
|
|
|
@ -2,8 +2,8 @@ import db from "./db";
|
|||
import session from "./Session";
|
||||
|
||||
class UserManager {
|
||||
constructor(db) {
|
||||
this.db = db;
|
||||
constructor(dbImpl) {
|
||||
this.db = dbImpl;
|
||||
}
|
||||
|
||||
async all() {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import Dexie from "dexie";
|
||||
import session from "./Session";
|
||||
import sessionReplica from "./SessionReplica";
|
||||
|
||||
// Uses Dexie.js
|
||||
// https://dexie.org/docs/API-Reference#quick-reference
|
||||
|
@ -23,10 +22,10 @@ const createDatabase = (username) => {
|
|||
};
|
||||
|
||||
export const dbAsync = async () => {
|
||||
const username = await sessionReplica.username();
|
||||
const username = await session.usernameAsync();
|
||||
return createDatabase(username);
|
||||
};
|
||||
|
||||
export const db = () => createDatabase(session.username());
|
||||
const db = () => createDatabase(session.username());
|
||||
|
||||
export default db;
|
||||
|
|
|
@ -164,7 +164,7 @@ const ChangePasswordDialog = (props) => {
|
|||
if (e instanceof IncorrectPasswordError) {
|
||||
setError(t("account_basics_password_dialog_current_password_incorrect"));
|
||||
} else if (e instanceof UnauthorizedError) {
|
||||
session.resetAndRedirect(routes.login);
|
||||
await session.resetAndRedirect(routes.login);
|
||||
} else {
|
||||
setError(e.message);
|
||||
}
|
||||
|
@ -245,7 +245,7 @@ const AccountType = () => {
|
|||
} catch (e) {
|
||||
console.log(`[Account] Error opening billing portal`, e);
|
||||
if (e instanceof UnauthorizedError) {
|
||||
session.resetAndRedirect(routes.login);
|
||||
await session.resetAndRedirect(routes.login);
|
||||
} else {
|
||||
setShowPortalError(true);
|
||||
}
|
||||
|
@ -371,7 +371,7 @@ const PhoneNumbers = () => {
|
|||
} catch (e) {
|
||||
console.log(`[Account] Error deleting phone number`, e);
|
||||
if (e instanceof UnauthorizedError) {
|
||||
session.resetAndRedirect(routes.login);
|
||||
await session.resetAndRedirect(routes.login);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -447,7 +447,7 @@ const AddPhoneNumberDialog = (props) => {
|
|||
} catch (e) {
|
||||
console.log(`[Account] Error sending verification`, e);
|
||||
if (e instanceof UnauthorizedError) {
|
||||
session.resetAndRedirect(routes.login);
|
||||
await session.resetAndRedirect(routes.login);
|
||||
} else {
|
||||
setError(e.message);
|
||||
}
|
||||
|
@ -464,7 +464,7 @@ const AddPhoneNumberDialog = (props) => {
|
|||
} catch (e) {
|
||||
console.log(`[Account] Error confirming verification`, e);
|
||||
if (e instanceof UnauthorizedError) {
|
||||
session.resetAndRedirect(routes.login);
|
||||
await session.resetAndRedirect(routes.login);
|
||||
} else {
|
||||
setError(e.message);
|
||||
}
|
||||
|
@ -946,7 +946,7 @@ const TokenDialog = (props) => {
|
|||
} catch (e) {
|
||||
console.log(`[Account] Error creating token`, e);
|
||||
if (e instanceof UnauthorizedError) {
|
||||
session.resetAndRedirect(routes.login);
|
||||
await session.resetAndRedirect(routes.login);
|
||||
} else {
|
||||
setError(e.message);
|
||||
}
|
||||
|
@ -1003,7 +1003,7 @@ const TokenDeleteDialog = (props) => {
|
|||
} catch (e) {
|
||||
console.log(`[Account] Error deleting token`, e);
|
||||
if (e instanceof UnauthorizedError) {
|
||||
session.resetAndRedirect(routes.login);
|
||||
await session.resetAndRedirect(routes.login);
|
||||
} else {
|
||||
setError(e.message);
|
||||
}
|
||||
|
@ -1080,13 +1080,13 @@ const DeleteAccountDialog = (props) => {
|
|||
await accountApi.delete(password);
|
||||
await db().delete();
|
||||
console.debug(`[Account] Account deleted`);
|
||||
session.resetAndRedirect(routes.app);
|
||||
await session.resetAndRedirect(routes.app);
|
||||
} catch (e) {
|
||||
console.log(`[Account] Error deleting account`, e);
|
||||
if (e instanceof IncorrectPasswordError) {
|
||||
setError(t("account_basics_password_dialog_current_password_incorrect"));
|
||||
} else if (e instanceof UnauthorizedError) {
|
||||
session.resetAndRedirect(routes.login);
|
||||
await session.resetAndRedirect(routes.login);
|
||||
} else {
|
||||
setError(e.message);
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ const ProfileIcon = () => {
|
|||
await accountApi.logout();
|
||||
await db().delete();
|
||||
} finally {
|
||||
session.resetAndRedirect(routes.app);
|
||||
await session.resetAndRedirect(routes.app);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ const Login = () => {
|
|||
try {
|
||||
const token = await accountApi.login(user);
|
||||
console.log(`[Login] User auth for user ${user.username} successful, token is ${token}`);
|
||||
session.store(user.username, token);
|
||||
await session.store(user.username, token);
|
||||
window.location.href = routes.app;
|
||||
} catch (e) {
|
||||
console.log(`[Login] User auth for user ${user.username} failed`, e);
|
||||
|
|
|
@ -59,7 +59,7 @@ const maybeUpdateAccountSettings = async (payload) => {
|
|||
} catch (e) {
|
||||
console.log(`[Preferences] Error updating account settings`, e);
|
||||
if (e instanceof UnauthorizedError) {
|
||||
session.resetAndRedirect(routes.login);
|
||||
await session.resetAndRedirect(routes.login);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -247,7 +247,11 @@ const WebPushEnabled = () => {
|
|||
}
|
||||
|
||||
return (
|
||||
<Pref labelId={labelId} title={t("prefs_notifications_web_push_title")} description={enabled ? t("prefs_notifications_web_push_enabled_description") : t("prefs_notifications_web_push_disabled_description")}>
|
||||
<Pref
|
||||
labelId={labelId}
|
||||
title={t("prefs_notifications_web_push_title")}
|
||||
description={enabled ? t("prefs_notifications_web_push_enabled_description") : t("prefs_notifications_web_push_disabled_description")}
|
||||
>
|
||||
<FormControl fullWidth variant="standard" sx={{ m: 1 }}>
|
||||
<Select value={enabled ?? false} onChange={handleChange} aria-labelledby={labelId}>
|
||||
<MenuItem value>{t("prefs_notifications_web_push_enabled")}</MenuItem>
|
||||
|
|
|
@ -211,7 +211,7 @@ const PublishDialog = (props) => {
|
|||
} catch (e) {
|
||||
console.log(`[PublishDialog] Retrieving attachment limits failed`, e);
|
||||
if (e instanceof UnauthorizedError) {
|
||||
session.resetAndRedirect(routes.login);
|
||||
await session.resetAndRedirect(routes.login);
|
||||
} else {
|
||||
setAttachFileError(""); // Reset error (rely on server-side checking)
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ export const ReserveAddDialog = (props) => {
|
|||
} catch (e) {
|
||||
console.log(`[ReserveAddDialog] Error adding topic reservation.`, e);
|
||||
if (e instanceof UnauthorizedError) {
|
||||
session.resetAndRedirect(routes.login);
|
||||
await session.resetAndRedirect(routes.login);
|
||||
} else if (e instanceof TopicReservedError) {
|
||||
setError(t("subscribe_dialog_error_topic_already_reserved"));
|
||||
return;
|
||||
|
@ -99,7 +99,7 @@ export const ReserveEditDialog = (props) => {
|
|||
} catch (e) {
|
||||
console.log(`[ReserveEditDialog] Error updating topic reservation.`, e);
|
||||
if (e instanceof UnauthorizedError) {
|
||||
session.resetAndRedirect(routes.login);
|
||||
await session.resetAndRedirect(routes.login);
|
||||
} else {
|
||||
setError(e.message);
|
||||
return;
|
||||
|
@ -136,7 +136,7 @@ export const ReserveDeleteDialog = (props) => {
|
|||
} catch (e) {
|
||||
console.log(`[ReserveDeleteDialog] Error deleting topic reservation.`, e);
|
||||
if (e instanceof UnauthorizedError) {
|
||||
session.resetAndRedirect(routes.login);
|
||||
await session.resetAndRedirect(routes.login);
|
||||
} else {
|
||||
setError(e.message);
|
||||
return;
|
||||
|
|
|
@ -27,7 +27,7 @@ const Signup = () => {
|
|||
await accountApi.create(user.username, user.password);
|
||||
const token = await accountApi.login(user);
|
||||
console.log(`[Signup] User signup for user ${user.username} successful, token is ${token}`);
|
||||
session.store(user.username, token);
|
||||
await session.store(user.username, token);
|
||||
window.location.href = routes.app;
|
||||
} catch (e) {
|
||||
console.log(`[Signup] Signup for user ${user.username} failed`, e);
|
||||
|
|
|
@ -39,7 +39,7 @@ export const subscribeTopic = async (baseUrl, topic, opts) => {
|
|||
} catch (e) {
|
||||
console.log(`[SubscribeDialog] Subscribing to topic ${topic} failed`, e);
|
||||
if (e instanceof UnauthorizedError) {
|
||||
session.resetAndRedirect(routes.login);
|
||||
await session.resetAndRedirect(routes.login);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ const SubscribePage = (props) => {
|
|||
} catch (e) {
|
||||
console.log(`[SubscribeDialog] Error reserving topic`, e);
|
||||
if (e instanceof UnauthorizedError) {
|
||||
session.resetAndRedirect(routes.login);
|
||||
await session.resetAndRedirect(routes.login);
|
||||
} else if (e instanceof TopicReservedError) {
|
||||
setError(t("subscribe_dialog_error_topic_already_reserved"));
|
||||
return;
|
||||
|
|
|
@ -155,7 +155,7 @@ export const SubscriptionPopup = (props) => {
|
|||
} catch (e) {
|
||||
console.log(`[SubscriptionPopup] Error unsubscribing`, e);
|
||||
if (e instanceof UnauthorizedError) {
|
||||
session.resetAndRedirect(routes.login);
|
||||
await session.resetAndRedirect(routes.login);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -298,7 +298,7 @@ const DisplayNameDialog = (props) => {
|
|||
} catch (e) {
|
||||
console.log(`[SubscriptionSettingsDialog] Error updating subscription`, e);
|
||||
if (e instanceof UnauthorizedError) {
|
||||
session.resetAndRedirect(routes.login);
|
||||
await session.resetAndRedirect(routes.login);
|
||||
} else {
|
||||
setError(e.message);
|
||||
return;
|
||||
|
|
|
@ -140,7 +140,7 @@ const UpgradeDialog = (props) => {
|
|||
} catch (e) {
|
||||
console.log(`[UpgradeDialog] Error changing billing subscription`, e);
|
||||
if (e instanceof UnauthorizedError) {
|
||||
session.resetAndRedirect(routes.login);
|
||||
await session.resetAndRedirect(routes.login);
|
||||
} else {
|
||||
setError(e.message);
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ export const useAutoSubscribe = (subscriptions, selected) => {
|
|||
} catch (e) {
|
||||
console.log(`[Hooks] Auto-subscribing failed`, e);
|
||||
if (e instanceof UnauthorizedError) {
|
||||
session.resetAndRedirect(routes.login);
|
||||
await session.resetAndRedirect(routes.login);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue