mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-10 04:30:25 +02:00
Add session refresh handler
This commit is contained in:
@@ -5,7 +5,7 @@ import { useFocusTrap } from '@mantine/hooks';
|
|||||||
import { jellyfinApi } from '/@/api/jellyfin.api';
|
import { jellyfinApi } from '/@/api/jellyfin.api';
|
||||||
import { navidromeApi } from '/@/api/navidrome.api';
|
import { navidromeApi } from '/@/api/navidrome.api';
|
||||||
import { subsonicApi } from '/@/api/subsonic.api';
|
import { subsonicApi } from '/@/api/subsonic.api';
|
||||||
import type { AuthResponse } from '/@/api/types';
|
import type { AuthenticationResponse } from '/@/api/types';
|
||||||
import { ServerType } from '/@/types';
|
import { ServerType } from '/@/types';
|
||||||
import { Button, PasswordInput, TextInput, SegmentedControl, toast } from '/@/components';
|
import { Button, PasswordInput, TextInput, SegmentedControl, toast } from '/@/components';
|
||||||
import { useAuthStoreActions } from '/@/store';
|
import { useAuthStoreActions } from '/@/store';
|
||||||
@@ -56,10 +56,9 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const data: AuthResponse = await authFunction({
|
const data: AuthenticationResponse = await authFunction(values.url, {
|
||||||
legacy: values.legacyAuth,
|
legacy: values.legacyAuth,
|
||||||
password: values.password,
|
password: values.password,
|
||||||
url: values.url,
|
|
||||||
username: values.username,
|
username: values.username,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -70,6 +69,7 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => {
|
|||||||
ndCredential: data.ndCredential,
|
ndCredential: data.ndCredential,
|
||||||
type: values.type,
|
type: values.type,
|
||||||
url: values.url,
|
url: values.url,
|
||||||
|
userId: data.userId,
|
||||||
username: data.username,
|
username: data.username,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Checkbox, Stack, Group } from '@mantine/core';
|
import { Checkbox, Stack, Group } from '@mantine/core';
|
||||||
import { useForm } from '@mantine/form';
|
import { useForm } from '@mantine/form';
|
||||||
|
import { closeAllModals } from '@mantine/modals';
|
||||||
import { nanoid } from 'nanoid/non-secure';
|
import { nanoid } from 'nanoid/non-secure';
|
||||||
import { RiInformationLine } from 'react-icons/ri';
|
import { RiInformationLine } from 'react-icons/ri';
|
||||||
import { ServerType } from '/@/types';
|
import { ServerType } from '/@/types';
|
||||||
@@ -10,9 +11,10 @@ import { useAuthStoreActions } from '/@/store';
|
|||||||
import { jellyfinApi } from '/@/api/jellyfin.api';
|
import { jellyfinApi } from '/@/api/jellyfin.api';
|
||||||
import { navidromeApi } from '/@/api/navidrome.api';
|
import { navidromeApi } from '/@/api/navidrome.api';
|
||||||
import { subsonicApi } from '/@/api/subsonic.api';
|
import { subsonicApi } from '/@/api/subsonic.api';
|
||||||
import type { AuthResponse } from '/@/api/types';
|
import type { AuthenticationResponse } from '/@/api/types';
|
||||||
|
|
||||||
interface EditServerFormProps {
|
interface EditServerFormProps {
|
||||||
|
isUpdate?: boolean;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
server: ServerListItem;
|
server: ServerListItem;
|
||||||
}
|
}
|
||||||
@@ -23,8 +25,8 @@ const AUTH_FUNCTIONS = {
|
|||||||
[ServerType.SUBSONIC]: subsonicApi.authenticate,
|
[ServerType.SUBSONIC]: subsonicApi.authenticate,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const EditServerForm = ({ server, onCancel }: EditServerFormProps) => {
|
export const EditServerForm = ({ isUpdate, server, onCancel }: EditServerFormProps) => {
|
||||||
const { updateServer } = useAuthStoreActions();
|
const { updateServer, setCurrentServer } = useAuthStoreActions();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
@@ -52,14 +54,13 @@ export const EditServerForm = ({ server, onCancel }: EditServerFormProps) => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const data: AuthResponse = await authFunction({
|
const data: AuthenticationResponse = await authFunction(values.url, {
|
||||||
legacy: values.legacyAuth,
|
legacy: values.legacyAuth,
|
||||||
password: values.password,
|
password: values.password,
|
||||||
url: values.url,
|
|
||||||
username: values.username,
|
username: values.username,
|
||||||
});
|
});
|
||||||
|
|
||||||
updateServer(server.id, {
|
const serverItem = {
|
||||||
credential: data.credential,
|
credential: data.credential,
|
||||||
id: nanoid(),
|
id: nanoid(),
|
||||||
name: values.name,
|
name: values.name,
|
||||||
@@ -67,7 +68,10 @@ export const EditServerForm = ({ server, onCancel }: EditServerFormProps) => {
|
|||||||
type: values.type,
|
type: values.type,
|
||||||
url: values.url,
|
url: values.url,
|
||||||
username: data.username,
|
username: data.username,
|
||||||
});
|
};
|
||||||
|
|
||||||
|
updateServer(server.id, serverItem);
|
||||||
|
setCurrentServer(serverItem);
|
||||||
|
|
||||||
toast.success({ message: 'Server updated' });
|
toast.success({ message: 'Server updated' });
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
@@ -76,6 +80,7 @@ export const EditServerForm = ({ server, onCancel }: EditServerFormProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
if (isUpdate) closeAllModals();
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,46 +1,41 @@
|
|||||||
import { Group } from '@mantine/core';
|
import { Group } from '@mantine/core';
|
||||||
import { openModal } from '@mantine/modals';
|
import { openModal, closeAllModals } from '@mantine/modals';
|
||||||
import {
|
import {
|
||||||
RiMenu3Fill,
|
RiMenu3Fill,
|
||||||
RiSearch2Line,
|
RiSearch2Line,
|
||||||
RiSettings2Fill,
|
RiSettings2Fill,
|
||||||
RiSettings2Line,
|
RiSettings2Line,
|
||||||
RiEdit2Line,
|
RiEdit2Line,
|
||||||
|
RiLockLine,
|
||||||
} from 'react-icons/ri';
|
} from 'react-icons/ri';
|
||||||
import { JFAlbumListSort } from '/@/api/jellyfin.types';
|
|
||||||
import { NDAlbumListSort } from '/@/api/navidrome.types';
|
|
||||||
import { SortOrder } from '/@/api/types';
|
|
||||||
import { Button, DropdownMenu, Text } from '/@/components';
|
import { Button, DropdownMenu, Text } from '/@/components';
|
||||||
import { ServerList } from '/@/features/servers';
|
import { ServerList } from '/@/features/servers';
|
||||||
import { Settings } from '/@/features/settings';
|
import { Settings } from '/@/features/settings';
|
||||||
import type { ServerListItem } from '/@/store';
|
import type { ServerListItem } from '/@/store';
|
||||||
import { useAppStore } from '/@/store';
|
|
||||||
import { useAppStoreActions } from '/@/store';
|
|
||||||
import { useAuthStoreActions, useCurrentServer, useServerList } from '/@/store';
|
import { useAuthStoreActions, useCurrentServer, useServerList } from '/@/store';
|
||||||
import { ServerType } from '/@/types';
|
import { ServerType } from '/@/types';
|
||||||
|
import { EditServerForm } from '/@/features/servers/components/edit-server-form';
|
||||||
|
|
||||||
export const AppMenu = () => {
|
export const AppMenu = () => {
|
||||||
const currentServer = useCurrentServer();
|
const currentServer = useCurrentServer();
|
||||||
const serverList = useServerList();
|
const serverList = useServerList();
|
||||||
const { setCurrentServer } = useAuthStoreActions();
|
const { setCurrentServer } = useAuthStoreActions();
|
||||||
const { setPage } = useAppStoreActions();
|
|
||||||
|
|
||||||
const handleSetCurrentServer = (server: ServerListItem) => {
|
const handleSetCurrentServer = (server: ServerListItem) => {
|
||||||
setCurrentServer(server);
|
setCurrentServer(server);
|
||||||
|
};
|
||||||
|
|
||||||
const sortBy =
|
const handleCredentialsModal = (server: ServerListItem) => {
|
||||||
server.type === ServerType.NAVIDROME ? NDAlbumListSort.NAME : JFAlbumListSort.NAME;
|
openModal({
|
||||||
|
children: server && (
|
||||||
// Reset filter when switching servers
|
<EditServerForm
|
||||||
setPage('albums', {
|
isUpdate
|
||||||
list: {
|
server={server}
|
||||||
...useAppStore.getState().albums.list,
|
onCancel={closeAllModals}
|
||||||
filter: {
|
/>
|
||||||
...useAppStore.getState().albums.list.filter,
|
),
|
||||||
sortBy,
|
size: 'sm',
|
||||||
sortOrder: SortOrder.ASC,
|
title: `Update session for "${server.name}"`,
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -86,11 +81,26 @@ export const AppMenu = () => {
|
|||||||
<DropdownMenu.Dropdown>
|
<DropdownMenu.Dropdown>
|
||||||
<DropdownMenu.Label>Select a server</DropdownMenu.Label>
|
<DropdownMenu.Label>Select a server</DropdownMenu.Label>
|
||||||
{serverList.map((s) => {
|
{serverList.map((s) => {
|
||||||
|
const isNavidromeExpired = s.type === ServerType.NAVIDROME && !s.ndCredential;
|
||||||
|
const isJellyfinExpired = false;
|
||||||
|
const isSessionExpired = isNavidromeExpired || isJellyfinExpired;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
key={`server-${s.id}`}
|
key={`server-${s.id}`}
|
||||||
$isActive={s.id === currentServer?.id}
|
$isActive={s.id === currentServer?.id}
|
||||||
onClick={() => handleSetCurrentServer(s)}
|
icon={
|
||||||
|
isSessionExpired && (
|
||||||
|
<RiLockLine
|
||||||
|
color="var(--danger-color)"
|
||||||
|
size={12}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onClick={() => {
|
||||||
|
if (!isSessionExpired) return handleSetCurrentServer(s);
|
||||||
|
handleCredentialsModal(s);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Group>{s.name}</Group>
|
<Group>{s.name}</Group>
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
|
|||||||
@@ -3,7 +3,11 @@ import { nanoid } from 'nanoid/non-secure';
|
|||||||
import create from 'zustand';
|
import create from 'zustand';
|
||||||
import { devtools, persist } from 'zustand/middleware';
|
import { devtools, persist } from 'zustand/middleware';
|
||||||
import { immer } from 'zustand/middleware/immer';
|
import { immer } from 'zustand/middleware/immer';
|
||||||
import type { ServerType } from '../types';
|
import { JFAlbumListSort } from '/@/api/jellyfin.types';
|
||||||
|
import { NDAlbumListSort } from '/@/api/navidrome.types';
|
||||||
|
import { SortOrder } from '/@/api/types';
|
||||||
|
import { useAppStore } from '/@/store/app.store';
|
||||||
|
import { ServerType } from '/@/types';
|
||||||
|
|
||||||
export type ServerListItem = {
|
export type ServerListItem = {
|
||||||
credential: string;
|
credential: string;
|
||||||
@@ -12,6 +16,7 @@ export type ServerListItem = {
|
|||||||
ndCredential?: string;
|
ndCredential?: string;
|
||||||
type: ServerType;
|
type: ServerType;
|
||||||
url: string;
|
url: string;
|
||||||
|
userId: string | null;
|
||||||
username: string;
|
username: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -25,7 +30,7 @@ export interface AuthSlice extends AuthState {
|
|||||||
actions: {
|
actions: {
|
||||||
addServer: (args: ServerListItem) => void;
|
addServer: (args: ServerListItem) => void;
|
||||||
deleteServer: (id: string) => void;
|
deleteServer: (id: string) => void;
|
||||||
setCurrentServer: (server: ServerListItem) => void;
|
setCurrentServer: (server: ServerListItem | null) => void;
|
||||||
updateServer: (id: string, args: Partial<ServerListItem>) => void;
|
updateServer: (id: string, args: Partial<ServerListItem>) => void;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -48,12 +53,32 @@ export const useAuthStore = create<AuthSlice>()(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
setCurrentServer: (server) => set({ currentServer: server }),
|
setCurrentServer: (server) => {
|
||||||
|
set((state) => {
|
||||||
|
state.currentServer = server;
|
||||||
|
|
||||||
|
if (server) {
|
||||||
|
useAppStore.getState().actions.setPage('albums', {
|
||||||
|
list: {
|
||||||
|
...useAppStore.getState().albums.list,
|
||||||
|
filter: {
|
||||||
|
...useAppStore.getState().albums.list.filter,
|
||||||
|
sortBy:
|
||||||
|
server.type === ServerType.NAVIDROME
|
||||||
|
? NDAlbumListSort.NAME
|
||||||
|
: JFAlbumListSort.NAME,
|
||||||
|
sortOrder: SortOrder.ASC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
updateServer: (id: string, args: Partial<ServerListItem>) => {
|
updateServer: (id: string, args: Partial<ServerListItem>) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const server = state.serverList.find((server) => server.id === id);
|
const server = state.serverList.find((server) => server.id === id);
|
||||||
if (server) {
|
if (server) {
|
||||||
Object.assign(server, args);
|
Object.assign(server, { ...server, ...args });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user