autobrr/web/src/utils/Context.ts
martylukyy 8120c33f6b
fix(auth): cookie expiry and renewal (#1527)
* fix(auth/web): logout when expired/invalid/no cookie is present

* fix(auth/web): specify error message in invalid cookie

* fix(auth/web): reset error boundary on login

* fix(auth/web): fix onboarding

* chore: code cleanup

* fix(web): revert tanstack/router to 1.31.0

* refactor(web): remove react-error-boundary

* feat(auth): refresh cookie when close to expiry

* enhancement(web): specify defaultError message in HttpClient

* fix(web): use absolute paths for router links (#1530)

* chore(web): bump `@tanstack/react-router` to `1.31.6`

* fix(web): settings routes

* fix(web): filter routes

* fix(web): remove unused ReleasesIndexRoute

* chore(web): add documentation for HttpClient

* chore(lint): remove unnecessary whitespace
2024-05-08 10:38:02 +02:00

127 lines
3 KiB
TypeScript

/*
* Copyright (c) 2021 - 2024, Ludvig Lundgren and the autobrr contributors.
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import type { StateWithValue } from "react-ridge-state";
import { newRidgeState } from "react-ridge-state";
interface SettingsType {
debug: boolean;
checkForUpdates: boolean;
darkTheme: boolean;
scrollOnNewLog: boolean;
indentLogLines: boolean;
hideWrappedText: boolean;
}
export type FilterListState = {
indexerFilter: string[];
sortOrder: string;
status: string;
};
interface AuthInfo {
username: string;
isLoggedIn: boolean;
}
// Default values
const AuthContextDefaults: AuthInfo = {
username: "",
isLoggedIn: false
};
const SettingsContextDefaults: SettingsType = {
debug: false,
checkForUpdates: true,
darkTheme: window.matchMedia('(prefers-color-scheme: dark)').matches,
scrollOnNewLog: false,
indentLogLines: false,
hideWrappedText: false
};
const FilterListContextDefaults: FilterListState = {
indexerFilter: [],
sortOrder: "",
status: ""
};
// eslint-disable-next-line
function ContextMerger<T extends {}>(
key: string,
defaults: T,
ctxState: StateWithValue<T>
) {
let values = structuredClone(defaults);
const storage = localStorage.getItem(key);
if (storage) {
try {
const json = JSON.parse(storage);
if (json === null) {
console.warn(`JSON localStorage value for '${key}' context state is null`);
} else {
values = { ...values, ...json };
}
} catch (e) {
console.error(`Failed to merge ${key} context state: ${e}`);
}
}
ctxState.set(values);
}
const AuthKey = "autobrr_user_auth";
const SettingsKey = "autobrr_settings";
const FilterListKey = "autobrr_filter_list";
export const InitializeGlobalContext = () => {
ContextMerger<AuthInfo>(AuthKey, AuthContextDefaults, AuthContext);
ContextMerger<SettingsType>(
SettingsKey,
SettingsContextDefaults,
SettingsContext
);
ContextMerger<FilterListState>(
FilterListKey,
FilterListContextDefaults,
FilterListContext
);
};
function DefaultSetter<T>(name: string, newState: T, prevState: T) {
try {
localStorage.setItem(name, JSON.stringify(newState));
} catch (e) {
console.error(
`An error occurred while trying to modify '${name}' context state: ${e}`
);
console.warn(` --> prevState: ${prevState}`);
console.warn(` --> newState: ${newState}`);
}
}
export const AuthContext = newRidgeState<AuthInfo>(
AuthContextDefaults,
{
onSet: (newState, prevState) => DefaultSetter(AuthKey, newState, prevState)
}
);
export const SettingsContext = newRidgeState<SettingsType>(
SettingsContextDefaults,
{
onSet: (newState, prevState) => {
document.documentElement.classList.toggle("dark", newState.darkTheme);
DefaultSetter(SettingsKey, newState, prevState);
}
}
);
export const FilterListContext = newRidgeState<FilterListState>(
FilterListContextDefaults,
{
onSet: (newState, prevState) => DefaultSetter(FilterListKey, newState, prevState)
}
);