mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-03-09 15:40:26 +00:00
feat: replace context with slice
This commit is contained in:
parent
1f72bb6138
commit
1bf2c8faf9
12 changed files with 93 additions and 110 deletions
|
@ -1,17 +1,12 @@
|
||||||
import { userAuthRepository } from "@main/repository";
|
import { userAuthRepository } from "@main/repository";
|
||||||
import { registerEvent } from "../register-event";
|
import { registerEvent } from "../register-event";
|
||||||
import { HydraApi } from "@main/services/hydra-api";
|
import { HydraApi } from "@main/services/hydra-api";
|
||||||
import { WindowManager } from "@main/services";
|
|
||||||
|
|
||||||
const signout = async (_event: Electron.IpcMainInvokeEvent): Promise<void> => {
|
const signout = async (_event: Electron.IpcMainInvokeEvent): Promise<void> => {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
userAuthRepository.delete({ id: 1 }),
|
userAuthRepository.delete({ id: 1 }),
|
||||||
HydraApi.post("/auth/logout"),
|
HydraApi.post("/auth/logout"),
|
||||||
]).finally(() => {
|
]);
|
||||||
if (WindowManager.mainWindow) {
|
|
||||||
WindowManager.mainWindow.webContents.send("on-signout");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
registerEvent("signout", signout);
|
registerEvent("signout", signout);
|
||||||
|
|
|
@ -19,6 +19,7 @@ import {
|
||||||
toggleDraggingDisabled,
|
toggleDraggingDisabled,
|
||||||
closeToast,
|
closeToast,
|
||||||
} from "@renderer/features";
|
} from "@renderer/features";
|
||||||
|
import { useUserAuth } from "./hooks/use-user-auth";
|
||||||
|
|
||||||
export interface AppProps {
|
export interface AppProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
@ -67,6 +68,23 @@ export function App() {
|
||||||
};
|
};
|
||||||
}, [clearDownload, setLastPacket, updateLibrary]);
|
}, [clearDownload, setLastPacket, updateLibrary]);
|
||||||
|
|
||||||
|
const { updateUserAuth, clearUserAuth } = useUserAuth();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const listeners = [
|
||||||
|
window.electron.onSignIn(() => {
|
||||||
|
updateUserAuth();
|
||||||
|
}),
|
||||||
|
window.electron.onSignOut(() => {
|
||||||
|
clearUserAuth();
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
listeners.forEach((unsubscribe) => unsubscribe());
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleSearch = useCallback(
|
const handleSearch = useCallback(
|
||||||
(query: string) => {
|
(query: string) => {
|
||||||
dispatch(setSearch(query));
|
dispatch(setSearch(query));
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
import { useContext } from "react";
|
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { PersonIcon } from "@primer/octicons-react";
|
import { PersonIcon } from "@primer/octicons-react";
|
||||||
import { userAuthContext } from "@renderer/context/user-auth/user-auth.context";
|
|
||||||
import * as styles from "./sidebar.css";
|
import * as styles from "./sidebar.css";
|
||||||
|
import { useUserAuth } from "@renderer/hooks/use-user-auth";
|
||||||
|
|
||||||
export function SidebarProfile() {
|
export function SidebarProfile() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const { userAuth, isLoading } = useContext(userAuthContext);
|
const { userAuth, isLoading } = useUserAuth();
|
||||||
|
|
||||||
const handleClickProfile = () => {
|
const handleClickProfile = () => {
|
||||||
navigate(`/user/${userAuth!.id}`);
|
navigate(`/user/${userAuth!.id}`);
|
||||||
|
|
|
@ -14,7 +14,6 @@ import { buildGameDetailsPath } from "@renderer/helpers";
|
||||||
|
|
||||||
import SteamLogo from "@renderer/assets/steam-logo.svg?react";
|
import SteamLogo from "@renderer/assets/steam-logo.svg?react";
|
||||||
import { SidebarProfile } from "./sidebar-profile";
|
import { SidebarProfile } from "./sidebar-profile";
|
||||||
import { UserAuthContextProvider } from "@renderer/context/user-auth/user-auth.context";
|
|
||||||
|
|
||||||
const SIDEBAR_MIN_WIDTH = 200;
|
const SIDEBAR_MIN_WIDTH = 200;
|
||||||
const SIDEBAR_INITIAL_WIDTH = 250;
|
const SIDEBAR_INITIAL_WIDTH = 250;
|
||||||
|
@ -155,9 +154,7 @@ export function Sidebar() {
|
||||||
maxWidth: sidebarWidth,
|
maxWidth: sidebarWidth,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<UserAuthContextProvider>
|
|
||||||
<SidebarProfile />
|
<SidebarProfile />
|
||||||
</UserAuthContextProvider>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={styles.content({
|
className={styles.content({
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
import { createContext, useEffect, useState } from "react";
|
|
||||||
import { UserAuthContext } from "./user-auth.context.types";
|
|
||||||
import { UserAuth } from "@types";
|
|
||||||
|
|
||||||
export const userAuthContext = createContext<UserAuthContext>({
|
|
||||||
userAuth: null,
|
|
||||||
isLoading: false,
|
|
||||||
signout: async () => {},
|
|
||||||
updateMe: async () => {},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { Provider } = userAuthContext;
|
|
||||||
export const { Consumer: UserAuthContextConsumer } = userAuthContext;
|
|
||||||
|
|
||||||
export interface UserAuthContextProps {
|
|
||||||
children: React.ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function UserAuthContextProvider({ children }: UserAuthContextProps) {
|
|
||||||
const [userAuth, setUserAuth] = useState<UserAuth | null>(null);
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
|
||||||
|
|
||||||
const updateMe = () => {
|
|
||||||
setIsLoading(true);
|
|
||||||
|
|
||||||
return window.electron
|
|
||||||
.getMe()
|
|
||||||
.then((user) => {
|
|
||||||
setUserAuth(user);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setIsLoading(false);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateMe();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const listeners = [
|
|
||||||
window.electron.onSignIn(() => {
|
|
||||||
updateMe();
|
|
||||||
}),
|
|
||||||
window.electron.onSignOut(() => {
|
|
||||||
setUserAuth(null);
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
listeners.forEach((unsubscribe) => unsubscribe());
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const signout = () => {
|
|
||||||
return window.electron.signout().finally(() => {
|
|
||||||
setUserAuth(null);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Provider
|
|
||||||
value={{
|
|
||||||
userAuth,
|
|
||||||
signout,
|
|
||||||
updateMe,
|
|
||||||
isLoading,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</Provider>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
import { UserAuth } from "@types";
|
|
||||||
|
|
||||||
export interface UserAuthContext {
|
|
||||||
userAuth: UserAuth | null;
|
|
||||||
isLoading: boolean;
|
|
||||||
updateMe: () => Promise<void>;
|
|
||||||
signout: () => Promise<void>;
|
|
||||||
}
|
|
|
@ -4,3 +4,4 @@ export * from "./use-preferences-slice";
|
||||||
export * from "./download-slice";
|
export * from "./download-slice";
|
||||||
export * from "./window-slice";
|
export * from "./window-slice";
|
||||||
export * from "./toast-slice";
|
export * from "./toast-slice";
|
||||||
|
export * from "./user-auth-slice";
|
||||||
|
|
22
src/renderer/src/features/user-auth-slice.ts
Normal file
22
src/renderer/src/features/user-auth-slice.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
|
||||||
|
import type { UserAuth } from "@types";
|
||||||
|
|
||||||
|
export interface UserAuthState {
|
||||||
|
userAuth: UserAuth | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: UserAuthState = {
|
||||||
|
userAuth: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const userAuthSlice = createSlice({
|
||||||
|
name: "user-auth",
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
setUserAuth: (state, userAuth: PayloadAction<UserAuth | null>) => {
|
||||||
|
state.userAuth = userAuth.payload;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const { setUserAuth } = userAuthSlice.actions;
|
33
src/renderer/src/hooks/use-user-auth.ts
Normal file
33
src/renderer/src/hooks/use-user-auth.ts
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import { useAppDispatch, useAppSelector } from "./redux";
|
||||||
|
import { setUserAuth } from "@renderer/features";
|
||||||
|
|
||||||
|
export function useUserAuth() {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
|
const { userAuth } = useAppSelector((state) => state.userAuth);
|
||||||
|
|
||||||
|
const signOut = useCallback(async () => {
|
||||||
|
dispatch(setUserAuth(null));
|
||||||
|
return window.electron.signout();
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
const updateUserAuth = useCallback(async () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
|
return window.electron
|
||||||
|
.getMe()
|
||||||
|
.then((userAuth) => dispatch(setUserAuth(userAuth)))
|
||||||
|
.finally(() => {
|
||||||
|
setIsLoading(false);
|
||||||
|
});
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
const clearUserAuth = useCallback(async () => {
|
||||||
|
dispatch(setUserAuth(null));
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
return { userAuth, isLoading, updateUserAuth, signOut, clearUserAuth };
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ import { ProfileGame, UserProfile } from "@types";
|
||||||
import cn from "classnames";
|
import cn from "classnames";
|
||||||
import * as styles from "./user.css";
|
import * as styles from "./user.css";
|
||||||
import { SPACING_UNIT, vars } from "@renderer/theme.css";
|
import { SPACING_UNIT, vars } from "@renderer/theme.css";
|
||||||
import { useContext, useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import SteamLogo from "@renderer/assets/steam-logo.svg?react";
|
import SteamLogo from "@renderer/assets/steam-logo.svg?react";
|
||||||
import { useDate } from "@renderer/hooks";
|
import { useDate } from "@renderer/hooks";
|
||||||
|
@ -10,7 +10,7 @@ import { useNavigate } from "react-router-dom";
|
||||||
import { buildGameDetailsPath } from "@renderer/helpers";
|
import { buildGameDetailsPath } from "@renderer/helpers";
|
||||||
import { PersonIcon } from "@primer/octicons-react";
|
import { PersonIcon } from "@primer/octicons-react";
|
||||||
import { Button } from "@renderer/components";
|
import { Button } from "@renderer/components";
|
||||||
import { userAuthContext } from "@renderer/context/user-auth/user-auth.context";
|
import { useUserAuth } from "@renderer/hooks/use-user-auth";
|
||||||
|
|
||||||
const MAX_MINUTES_TO_SHOW_IN_PLAYTIME = 120;
|
const MAX_MINUTES_TO_SHOW_IN_PLAYTIME = 120;
|
||||||
export interface ProfileContentProps {
|
export interface ProfileContentProps {
|
||||||
|
@ -20,7 +20,7 @@ export interface ProfileContentProps {
|
||||||
export const UserContent = ({ userProfile }: ProfileContentProps) => {
|
export const UserContent = ({ userProfile }: ProfileContentProps) => {
|
||||||
const { t, i18n } = useTranslation("user_profile");
|
const { t, i18n } = useTranslation("user_profile");
|
||||||
|
|
||||||
const { userAuth, signout } = useContext(userAuthContext);
|
const { userAuth, signOut } = useUserAuth();
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ export const UserContent = ({ userProfile }: ProfileContentProps) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSignout = async () => {
|
const handleSignout = async () => {
|
||||||
await signout();
|
await signOut();
|
||||||
navigate("/");
|
navigate("/");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ import { UserContent } from "./user-content";
|
||||||
import { SkeletonTheme } from "react-loading-skeleton";
|
import { SkeletonTheme } from "react-loading-skeleton";
|
||||||
import { vars } from "@renderer/theme.css";
|
import { vars } from "@renderer/theme.css";
|
||||||
import * as styles from "./user.css";
|
import * as styles from "./user.css";
|
||||||
import { UserAuthContextProvider } from "@renderer/context/user-auth/user-auth.context";
|
|
||||||
|
|
||||||
export const User = () => {
|
export const User = () => {
|
||||||
const { username } = useParams();
|
const { username } = useParams();
|
||||||
|
@ -26,7 +25,6 @@ export const User = () => {
|
||||||
}, [dispatch, username]);
|
}, [dispatch, username]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<UserAuthContextProvider>
|
|
||||||
<SkeletonTheme baseColor={vars.color.background} highlightColor="#444">
|
<SkeletonTheme baseColor={vars.color.background} highlightColor="#444">
|
||||||
<div className={styles.wrapper}>
|
<div className={styles.wrapper}>
|
||||||
{userProfile ? (
|
{userProfile ? (
|
||||||
|
@ -36,6 +34,5 @@ export const User = () => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</SkeletonTheme>
|
</SkeletonTheme>
|
||||||
</UserAuthContextProvider>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
searchSlice,
|
searchSlice,
|
||||||
userPreferencesSlice,
|
userPreferencesSlice,
|
||||||
toastSlice,
|
toastSlice,
|
||||||
|
userAuthSlice,
|
||||||
} from "@renderer/features";
|
} from "@renderer/features";
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
|
@ -16,6 +17,7 @@ export const store = configureStore({
|
||||||
userPreferences: userPreferencesSlice.reducer,
|
userPreferences: userPreferencesSlice.reducer,
|
||||||
download: downloadSlice.reducer,
|
download: downloadSlice.reducer,
|
||||||
toast: toastSlice.reducer,
|
toast: toastSlice.reducer,
|
||||||
|
userAuth: userAuthSlice.reducer,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue