feat(web): move from react-router to @tanstack/router (#1338)

* fix(auth): invalid cookie handling and wrongful basic auth invalidation

* fix(auth): fix test to reflect new HTTP status code

* fix(auth/web): do not throw on error

* fix(http): replace http codes in middleware to prevent basic auth invalidation
fix typo in comment

* fix test

* fix(web): api client handle 403

* refactor(http): auth_test use testify.assert

* refactor(http): set session opts after valid login

* refactor(http): send more client headers

* fix(http): test

* refactor(web): move router to tanstack/router

* refactor(web): use route loaders and suspense

* refactor(web): useSuspense for settings

* refactor(web): invalidate cookie in middleware

* fix: loclfile

* fix: load filter/id

* fix(web): login, onboard, types, imports

* fix(web): filter load

* fix(web): build errors

* fix(web): ts-expect-error

* fix(tests): filter_test.go

* fix(filters): tests

* refactor: remove duplicate spinner components
refactor: ReleaseTable.tsx loading animation
refactor: remove dedicated `pendingComponent` for `settingsRoute`

* fix: refactor missed SectionLoader to RingResizeSpinner

* fix: substitute divides with borders to account for unloaded elements

* fix(api): action status URL param

* revert: action status URL param
add comment

* fix(routing): notfound handling and split files

* fix(filters): notfound get params

* fix(queries): colon

* fix(queries): comments ts-ignore

* fix(queries): extract queryKeys

* fix(queries): remove err

* fix(routes): move zob schema inline

* fix(auth): middleware and redirect to login

* fix(auth): failing test

* fix(logs): invalidate correct key

* fix(logs): invalidate correct key

* fix(logs): invalidate correct key

* fix: JSX element stealing focus from searchbar

* reimplement empty release table state text

* fix(context): use deep-copy

* fix(releases): empty state and filter input warnings

* fix(releases): empty states

* fix(auth): onboarding

* fix(cache): invalidate queries

---------

Co-authored-by: ze0s <43699394+zze0s@users.noreply.github.com>
This commit is contained in:
martylukyy 2024-02-12 13:07:00 +01:00 committed by GitHub
parent cc9656cd41
commit 1a23b69bcf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
64 changed files with 2543 additions and 2091 deletions

View file

@ -3,13 +3,8 @@
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import { newRidgeState } from "react-ridge-state";
import type { StateWithValue } from "react-ridge-state";
interface AuthInfo {
username: string;
isLoggedIn: boolean;
}
import { newRidgeState } from "react-ridge-state";
interface SettingsType {
debug: boolean;
@ -26,11 +21,16 @@ export type FilterListState = {
status: string;
};
// interface AuthInfo {
// username: string;
// isLoggedIn: boolean;
// }
// Default values
const AuthContextDefaults: AuthInfo = {
username: "",
isLoggedIn: false
};
// const AuthContextDefaults: AuthInfo = {
// username: "",
// isLoggedIn: false
// };
const SettingsContextDefaults: SettingsType = {
debug: false,
@ -53,7 +53,7 @@ function ContextMerger<T extends {}>(
defaults: T,
ctxState: StateWithValue<T>
) {
let values = defaults;
let values = structuredClone(defaults);
const storage = localStorage.getItem(key);
if (storage) {
@ -62,25 +62,28 @@ function ContextMerger<T extends {}>(
if (json === null) {
console.warn(`JSON localStorage value for '${key}' context state is null`);
} else {
values = { ...defaults, ...json };
values = { ...values, ...json };
}
} catch (e) {
console.error(`Failed to merge ${key} context state: ${e}`);
}
}
ctxState.set(values);
}
const SettingsKey = "autobrr_settings";
const FilterListKey = "autobrr_filter_list";
export const InitializeGlobalContext = () => {
ContextMerger<AuthInfo>("auth", AuthContextDefaults, AuthContext);
// ContextMerger<AuthInfo>(localStorageUserKey, AuthContextDefaults, AuthContextt);
ContextMerger<SettingsType>(
"settings",
SettingsKey,
SettingsContextDefaults,
SettingsContext
);
ContextMerger<FilterListState>(
"filterList",
FilterListKey,
FilterListContextDefaults,
FilterListContext
);
@ -98,16 +101,16 @@ function DefaultSetter<T>(name: string, newState: T, prevState: T) {
}
}
export const AuthContext = newRidgeState<AuthInfo>(AuthContextDefaults, {
onSet: (newState, prevState) => DefaultSetter("auth", newState, prevState)
});
// export const AuthContextt = newRidgeState<AuthInfo>(AuthContextDefaults, {
// onSet: (newState, prevState) => DefaultSetter(localStorageUserKey, newState, prevState)
// });
export const SettingsContext = newRidgeState<SettingsType>(
SettingsContextDefaults,
{
onSet: (newState, prevState) => {
document.documentElement.classList.toggle("dark", newState.darkTheme);
DefaultSetter("settings", newState, prevState);
DefaultSetter(SettingsKey, newState, prevState);
}
}
);
@ -115,6 +118,32 @@ export const SettingsContext = newRidgeState<SettingsType>(
export const FilterListContext = newRidgeState<FilterListState>(
FilterListContextDefaults,
{
onSet: (newState, prevState) => DefaultSetter("filterList", newState, prevState)
onSet: (newState, prevState) => DefaultSetter(FilterListKey, newState, prevState)
}
);
export type AuthCtx = {
isLoggedIn: boolean
username?: string
login: (username: string) => void
logout: () => void
}
export const localStorageUserKey = "autobrr_user_auth"
export const AuthContext: AuthCtx = {
isLoggedIn: false,
username: undefined,
login: (username: string) => {
AuthContext.isLoggedIn = true
AuthContext.username = username
localStorage.setItem(localStorageUserKey, JSON.stringify(AuthContext));
},
logout: () => {
AuthContext.isLoggedIn = false
AuthContext.username = undefined
localStorage.removeItem(localStorageUserKey);
},
}