Update store/routes

This commit is contained in:
jeffvli
2022-10-24 22:20:35 -07:00
parent f8e7d02daf
commit 8973571147
10 changed files with 418 additions and 293 deletions
@@ -1,15 +1,14 @@
/* eslint-disable sort-keys-fix/sort-keys-fix */
import { Routes, Route } from 'react-router-dom';
import { LibraryAlbumsRoute } from '@/renderer/features/library/routes/LibraryAlbumsRoute';
import { LibraryArtistsRoute } from '@/renderer/features/library/routes/LibraryArtistsRoute';
import { LibraryRoute } from '@/renderer/features/library/routes/LibraryRoute';
import { AuthOutlet } from '@/renderer/router/auth-outlet';
import { PrivateOutlet } from '@/renderer/router/private-outlet';
import { LoginRoute } from '../features/auth';
import { DashboardRoute } from '../features/dashboard';
import { LibraryAlbumsRoute } from '../features/library/routes/LibraryAlbumsRoute';
import { LibraryArtistsRoute } from '../features/library/routes/LibraryArtistsRoute';
import { LibraryRoute } from '../features/library/routes/LibraryRoute';
import { ServersRoute } from '../features/servers';
import { AuthLayout, DefaultLayout } from '../layouts';
import { AuthOutlet } from './outlets/AuthOutlet';
import { PrivateOutlet } from './outlets/PrivateOutlet';
import { AppRoute } from './utils/routes';
import { AppRoute } from './routes';
export const AppRouter = () => {
return (
@@ -25,7 +24,6 @@ export const AppRouter = () => {
>
<Route element={<DefaultLayout />}>
<Route element={<DashboardRoute />} path={AppRoute.HOME} />
<Route element={<ServersRoute />} path={AppRoute.SERVERS} />
<Route element={<></>} path={AppRoute.SEARCH} />
<Route element={<LibraryRoute />} path={AppRoute.LIBRARY} />
@@ -1,5 +1,5 @@
import { Navigate, Outlet, useLocation } from 'react-router-dom';
import { useAuthStore } from '../../store';
import { useAuthStore } from '../store';
interface AuthOutletProps {
redirectTo: string;
@@ -7,7 +7,7 @@ interface AuthOutletProps {
export const AuthOutlet = ({ redirectTo }: AuthOutletProps) => {
const location = useLocation();
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
const isAuthenticated = useAuthStore((state) => !!state.accessToken);
if (isAuthenticated) {
return <Navigate replace state={{ from: location }} to={redirectTo} />;
@@ -1,5 +1,5 @@
import { Navigate, Outlet, useLocation } from 'react-router-dom';
import { useAuthStore } from '../../store';
import { useAuthStore } from '@/renderer/store';
interface PrivateOutletProps {
redirectTo: string;
@@ -7,7 +7,7 @@ interface PrivateOutletProps {
export const PrivateOutlet = ({ redirectTo }: PrivateOutletProps) => {
const location = useLocation();
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
const isAuthenticated = useAuthStore((state) => !!state.accessToken);
if (isAuthenticated) {
return <Outlet />;
@@ -1,6 +1,6 @@
import create from 'zustand';
import { devtools } from 'zustand/middleware';
import { Platform } from '../../types';
import { Platform } from '@/types';
export interface AppState {
currentPage: {
+124
View File
@@ -0,0 +1,124 @@
import create from 'zustand';
import { devtools, persist } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
import { Server } from '@/renderer/api/types';
export interface AuthState {
accessToken: string;
currentServer?: Server;
permissions: {
isAdmin: boolean;
};
refreshToken: string;
serverCredentials: {
enabled: boolean;
id: string;
serverId: string;
token: string;
username: string;
}[];
serverKey: string;
serverUrl: string;
}
export interface AuthSlice extends AuthState {
addServerCredential: (options: {
enabled: boolean;
id: string;
serverId: string;
token: string;
username: string;
}) => void;
deleteServerCredential: (options: { id: string }) => void;
disableServerCredential: (options: { id: string }) => void;
enableServerCredential: (options: { id: string }) => void;
login: (auth: Partial<AuthState>) => void;
logout: () => void;
setCurrentServer: (server: Server) => void;
}
const persistedState = JSON.parse(
localStorage.getItem('authentication') || '{}'
);
export const useAuthStore = create<AuthSlice>()(
persist(
devtools(
immer((set) => ({
accessToken: '',
addServerCredential: (options) => {
set((state) => {
state.serverCredentials = state.serverCredentials.filter(
(c) => c.username !== options.username
);
state.serverCredentials.push(options);
});
},
currentServer: undefined,
deleteServerCredential: (options) => {
set((state) => {
state.serverCredentials = state.serverCredentials.filter(
(credential) => credential.id !== options.id
);
});
},
disableServerCredential: (options) => {
set((state) => {
state.serverCredentials = state.serverCredentials.map(
(credential) => {
if (credential.id === options.id) {
credential.enabled = false;
}
return credential;
}
);
});
},
enableServerCredential: (options) => {
set((state) => {
state.serverCredentials = state.serverCredentials.map(
(credential) => {
if (credential.id === options.id) {
credential.enabled = true;
}
return credential;
}
);
});
},
login: (auth: Partial<AuthState>) => {
return set({ ...auth });
},
logout: () => {
return set({
accessToken: undefined,
permissions: { isAdmin: false },
refreshToken: undefined,
});
},
permissions: {
isAdmin: false,
},
refreshToken: '',
serverCredentials: [],
serverKey: '',
serverPermissions: '',
serverUrl: '',
setCurrentServer: (server: Server) => {
const prev = JSON.parse(
localStorage.getItem('authentication') || '{}'
);
localStorage.setItem(
'authentication',
JSON.stringify({
...prev,
state: { ...prev.state, currentServer: server },
})
);
return set({ currentServer: server });
},
}))
),
{ name: 'authentication' }
)
);
+3 -2
View File
@@ -1,2 +1,3 @@
export * from './useAuthStore';
export * from './usePlayerStore';
export * from './auth.store';
export * from './player.store';
export * from './app.store';
+280
View File
@@ -0,0 +1,280 @@
/* eslint-disable prefer-destructuring */
/* eslint-disable @typescript-eslint/no-unused-vars */
import produce from 'immer';
import map from 'lodash/map';
import { nanoid } from 'nanoid/non-secure';
import create from 'zustand';
import { devtools, persist } from 'zustand/middleware';
import { Song } from '@/renderer/api/types';
import {
Play,
CrossfadeStyle,
PlaybackStyle,
PlaybackType,
PlayerRepeat,
PlayerStatus,
UniqueId,
} from '../../types';
type QueueSong = Song & UniqueId;
export interface PlayerState {
current: {
index: number;
player: 1 | 2;
song: QueueSong;
status: PlayerStatus;
time: number;
};
queue: {
default: QueueSong[];
previousNode: QueueSong;
shuffled: QueueSong[];
sorted: QueueSong[];
};
settings: {
crossfadeDuration: number;
crossfadeStyle: CrossfadeStyle;
muted: boolean;
repeat: PlayerRepeat;
shuffle: boolean;
style: PlaybackStyle;
type: PlaybackType;
volume: number;
};
}
export interface PlayerData {
current: {
index: number;
player: 1 | 2;
song: QueueSong;
status: PlayerStatus;
};
player1: QueueSong;
player2: QueueSong;
queue: QueueData;
}
export interface QueueData {
current: QueueSong;
next: QueueSong;
previous: QueueSong;
}
export interface PlayerSlice extends PlayerState {
addToQueue: (songs: Song[], type: Play) => PlayerData;
autoNext: () => PlayerData;
getPlayerData: () => PlayerData;
getQueueData: () => QueueData;
next: () => PlayerData;
pause: () => void;
play: () => void;
player1: () => QueueSong;
player2: () => QueueSong;
prev: () => PlayerData;
setCurrentIndex: (index: number) => PlayerData;
setCurrentTime: (time: number) => void;
setSettings: (settings: Partial<PlayerState['settings']>) => void;
}
export const usePlayerStore = create<PlayerSlice>()(
persist(
devtools((set, get) => ({
addToQueue: (songs, type) => {
const queueSongs = map(songs, (song) => ({
...song,
uniqueId: nanoid(),
}));
if (type === Play.NOW) {
set(
produce((state) => {
state.queue.default = queueSongs;
state.current.time = 0;
state.current.player = 1;
state.current.index = 0;
state.current.song = queueSongs[0];
})
);
} else if (type === Play.LAST) {
set(
produce((state) => {
state.queue.default = [...get().queue.default, ...queueSongs];
})
);
} else if (type === Play.NEXT) {
const queue = get().queue.default;
const currentIndex = get().current.index;
set(
produce((state) => {
state.queue.default = [
...queue.slice(0, currentIndex + 1),
...queueSongs,
...queue.slice(currentIndex + 1),
];
})
);
}
return get().getPlayerData();
},
autoNext: () => {
set(
produce((state) => {
state.current.time = 0;
state.current.index += 1;
state.current.player = state.current.player === 1 ? 2 : 1;
state.current.song = state.queue.default[state.current.index];
state.queue.previousNode = get().current.song;
})
);
return get().getPlayerData();
},
current: {
index: 0,
player: 1,
song: {} as QueueSong,
status: PlayerStatus.PAUSED,
time: 0,
},
getPlayerData: () => {
const queue = get().queue.default;
const currentPlayer = get().current.player;
const player1 =
currentPlayer === 1
? queue[get().current.index]
: queue[get().current.index + 1];
const player2 =
currentPlayer === 1
? queue[get().current.index + 1]
: queue[get().current.index];
return {
current: {
index: get().current.index,
player: get().current.player,
song: get().current.song,
status: get().current.status,
},
player1,
player2,
queue: {
current: queue[get().current.index],
next: queue[get().current.index + 1],
previous: queue[get().current.index - 1],
},
};
},
getQueueData: () => {
const queue = get().queue.default;
return {
current: queue[get().current.index],
next: queue[get().current.index + 1],
previous: queue[get().current.index - 1],
};
},
next: () => {
set(
produce((state) => {
state.current.time = 0;
state.current.index += 1;
state.current.player = 1;
state.current.song = state.queue.default[state.current.index];
state.queue.previousNode = get().current.song;
})
);
return get().getPlayerData();
},
pause: () => {
set(
produce((state) => {
state.current.status = PlayerStatus.PAUSED;
})
);
},
play: () => {
set(
produce((state) => {
state.current.status = PlayerStatus.PLAYING;
})
);
},
player1: () => {
return get().getPlayerData().player1;
},
player2: () => {
return get().getPlayerData().player2;
},
prev: () => {
set(
produce((state) => {
state.current.time = 0;
state.current.index =
state.current.index - 1 < 0 ? 0 : state.current.index - 1;
state.current.player = 1;
state.current.song = state.queue.default[state.current.index];
state.queue.previousNode = get().current.song;
})
);
return get().getPlayerData();
},
queue: {
default: [],
previousNode: {} as QueueSong,
shuffled: [],
sorted: [],
},
setCurrentIndex: (index) => {
set(
produce((state) => {
state.current.time = 0;
state.current.index = index;
state.current.player = 1;
state.current.song = state.queue.default[index];
state.queue.previousNode = get().current.song;
})
);
return get().getPlayerData();
},
setCurrentTime: (time) => {
set(
produce((state) => {
state.current.time = time;
})
);
},
setSettings: (settings) => {
set(
produce((state) => {
state.settings = { ...get().settings, ...settings };
})
);
// try {
// setLocalStorageSettings('player', get().settings);
// } catch (err) {
// console.log('none');
// }
},
settings: {
crossfadeDuration: 5,
crossfadeStyle: CrossfadeStyle.EQUALPOWER,
muted: false,
repeat: PlayerRepeat.NONE,
shuffle: false,
style: PlaybackStyle.GAPLESS,
type: PlaybackType.LOCAL,
volume: 50,
},
})),
{ name: 'player' }
)
);
-38
View File
@@ -1,38 +0,0 @@
import create from 'zustand';
import { devtools } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
export interface AuthState {
accessToken: string;
isAuthenticated: boolean;
key: string;
refreshToken: string;
serverUrl: string;
}
export interface AuthSlice extends AuthState {
login: (auth: Partial<AuthState>) => void;
logout: () => void;
}
const persistedAuthState = JSON.parse(
localStorage.getItem('authentication') || '{}'
);
export const useAuthStore = create<AuthSlice>()(
devtools(
immer((set) => ({
accessToken: persistedAuthState.accessToken,
isAuthenticated: persistedAuthState.isAuthenticated,
key: persistedAuthState.key,
login: (auth: Partial<AuthState>) => {
return set({ isAuthenticated: true, ...auth });
},
logout: () => {
return set({ isAuthenticated: false });
},
refreshToken: persistedAuthState.refreshToken,
serverUrl: persistedAuthState.serverUrl,
}))
)
);
-240
View File
@@ -1,240 +0,0 @@
/* eslint-disable prefer-destructuring */
/* eslint-disable @typescript-eslint/no-unused-vars */
import produce from 'immer';
import create from 'zustand';
import { devtools } from 'zustand/middleware';
import {
Play,
CrossfadeStyle,
PlaybackStyle,
PlaybackType,
PlayerRepeat,
PlayerStatus,
Song,
} from '../../types';
import { setLocalStorageSettings } from '../utils';
export interface PlayerState {
current: {
index: number;
player: 1 | 2;
song: Song;
status: PlayerStatus;
time: number;
};
queue: {
default: Song[];
shuffled: Song[];
sorted: Song[];
};
settings: {
crossfadeDuration: number;
crossfadeStyle: CrossfadeStyle;
muted: boolean;
repeat: PlayerRepeat;
shuffle: boolean;
style: PlaybackStyle;
type: PlaybackType;
volume: number;
};
}
export interface PlayerData {
current: {
index: number;
player: 1 | 2;
song: Song;
status: PlayerStatus;
time: number;
};
player1: Song;
player2: Song;
queue: {
current: Song;
next: Song;
previous: Song;
};
}
export interface PlayerSlice extends PlayerState {
addToQueue: (songs: Song[], type: Play) => PlayerData;
autoNext: () => PlayerData;
getPlayerData: () => PlayerData;
next: () => PlayerData;
pause: () => void;
play: () => void;
player1: () => Song;
player2: () => Song;
prev: () => PlayerData;
setCurrentTime: (time: number) => void;
setSettings: (settings: Partial<PlayerState['settings']>) => void;
}
export const usePlayerStore = create<PlayerSlice>()(
devtools((set, get) => ({
addToQueue: (songs, type) => {
if (type === Play.NOW) {
set(
produce((state) => {
state.queue.default = songs;
state.current.time = 0;
state.current.player = 1;
state.current.index = 0;
state.current.song = songs[0];
})
);
} else if (type === Play.LAST) {
set(
produce((state) => {
state.queue.default = [...get().queue.default, ...songs];
})
);
} else if (type === Play.NEXT) {
const queue = get().queue.default;
const currentIndex = get().current.index;
set(
produce((state) => {
state.queue.default = [
...queue.slice(0, currentIndex + 1),
...songs,
...queue.slice(currentIndex + 1),
];
})
);
}
return get().getPlayerData();
},
autoNext: () => {
set(
produce((state) => {
state.current.time = 0;
state.current.index += 1;
state.current.player = state.current.player === 1 ? 2 : 1;
state.current.song = state.queue.default[state.current.index];
})
);
return get().getPlayerData();
},
current: {
index: 0,
player: 1,
song: {} as Song,
status: PlayerStatus.PAUSED,
time: 0,
},
getPlayerData: () => {
const queue = get().queue.default;
const currentPlayer = get().current.player;
const player1 =
currentPlayer === 1
? queue[get().current.index]
: queue[get().current.index + 1];
const player2 =
currentPlayer === 1
? queue[get().current.index + 1]
: queue[get().current.index];
return {
current: {
index: get().current.index,
player: get().current.player,
song: get().current.song,
status: get().current.status,
time: get().current.time,
},
player1,
player2,
queue: {
current: queue[get().current.index],
next: queue[get().current.index + 1],
previous: queue[get().current.index - 1],
},
};
},
next: () => {
set(
produce((state) => {
state.current.time = 0;
state.current.index += 1;
state.current.player = 1;
state.current.song = state.queue.default[state.current.index];
})
);
return get().getPlayerData();
},
pause: () => {
set(
produce((state) => {
state.current.status = PlayerStatus.PAUSED;
})
);
},
play: () => {
set(
produce((state) => {
state.current.status = PlayerStatus.PLAYING;
})
);
},
player1: () => {
return get().getPlayerData().player1;
},
player2: () => {
return get().getPlayerData().player2;
},
prev: () => {
set(
produce((state) => {
state.current.time = 0;
state.current.index =
state.current.index - 1 < 0 ? 0 : state.current.index - 1;
state.current.player = 1;
state.current.song = state.queue.default[state.current.index];
})
);
return get().getPlayerData();
},
queue: {
default: [],
shuffled: [],
sorted: [],
},
setCurrentTime: (time) => {
set(
produce((state) => {
state.current.time = time;
})
);
},
setSettings: (settings) => {
set(
produce((state) => {
state.settings = { ...get().settings, ...settings };
})
);
try {
setLocalStorageSettings('player', get().settings);
} catch (err) {
console.log('none');
}
},
settings: {
crossfadeDuration: 5,
crossfadeStyle: CrossfadeStyle.EQUALPOWER,
muted: false,
repeat: PlayerRepeat.NONE,
shuffle: false,
style: PlaybackStyle.GAPLESS,
type: PlaybackType.LOCAL,
volume: 50,
},
}))
);