mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-01-24 05:54:55 +03:00
feat: adding user local storage cache
This commit is contained in:
parent
eea19d43c2
commit
a8624058e4
@ -19,6 +19,8 @@ import {
|
|||||||
setUserPreferences,
|
setUserPreferences,
|
||||||
toggleDraggingDisabled,
|
toggleDraggingDisabled,
|
||||||
closeToast,
|
closeToast,
|
||||||
|
setUserDetails,
|
||||||
|
setProfileBackground,
|
||||||
} from "@renderer/features";
|
} from "@renderer/features";
|
||||||
|
|
||||||
export interface AppProps {
|
export interface AppProps {
|
||||||
@ -31,7 +33,8 @@ export function App() {
|
|||||||
|
|
||||||
const { clearDownload, setLastPacket } = useDownload();
|
const { clearDownload, setLastPacket } = useDownload();
|
||||||
|
|
||||||
const { updateUser, clearUser } = useUserDetails();
|
const { fetchUserDetails, updateUserDetails, clearUserDetails } =
|
||||||
|
useUserDetails();
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
@ -73,23 +76,38 @@ export function App() {
|
|||||||
}, [clearDownload, setLastPacket, updateLibrary]);
|
}, [clearDownload, setLastPacket, updateLibrary]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
updateUser();
|
const cachedUserDetails = window.localStorage.getItem("userDetails");
|
||||||
}, [updateUser]);
|
|
||||||
|
if (cachedUserDetails) {
|
||||||
|
const { profileBackground, ...userDetails } =
|
||||||
|
JSON.parse(cachedUserDetails);
|
||||||
|
|
||||||
|
dispatch(setUserDetails(userDetails));
|
||||||
|
dispatch(setProfileBackground(profileBackground));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Check if user is logged in before calling this */
|
||||||
|
fetchUserDetails().then((response) => {
|
||||||
|
if (response) setUserDetails(response);
|
||||||
|
});
|
||||||
|
}, [dispatch, fetchUserDetails]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const listeners = [
|
const listeners = [
|
||||||
window.electron.onSignIn(() => {
|
window.electron.onSignIn(() => {
|
||||||
updateUser();
|
fetchUserDetails().then((response) => {
|
||||||
|
if (response) updateUserDetails(response);
|
||||||
|
});
|
||||||
}),
|
}),
|
||||||
window.electron.onSignOut(() => {
|
window.electron.onSignOut(() => {
|
||||||
clearUser();
|
clearUserDetails();
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
listeners.forEach((unsubscribe) => unsubscribe());
|
listeners.forEach((unsubscribe) => unsubscribe());
|
||||||
};
|
};
|
||||||
}, [updateUser, clearUser]);
|
}, [fetchUserDetails, updateUserDetails, clearUserDetails]);
|
||||||
|
|
||||||
const handleSearch = useCallback(
|
const handleSearch = useCallback(
|
||||||
(query: string) => {
|
(query: string) => {
|
||||||
|
58
src/renderer/src/components/sidebar/sidebar-profile.css.ts
Normal file
58
src/renderer/src/components/sidebar/sidebar-profile.css.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { style } from "@vanilla-extract/css";
|
||||||
|
|
||||||
|
import { SPACING_UNIT, vars } from "../../theme.css";
|
||||||
|
|
||||||
|
export const profileButton = style({
|
||||||
|
display: "flex",
|
||||||
|
cursor: "pointer",
|
||||||
|
transition: "all ease 0.1s",
|
||||||
|
padding: `${SPACING_UNIT * 2}px ${SPACING_UNIT * 2}px`,
|
||||||
|
color: vars.color.muted,
|
||||||
|
borderBottom: `solid 1px ${vars.color.border}`,
|
||||||
|
boxShadow: "0px 0px 15px 0px #000000",
|
||||||
|
":hover": {
|
||||||
|
backgroundColor: "rgba(255, 255, 255, 0.15)",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const profileButtonContent = style({
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: `${SPACING_UNIT + SPACING_UNIT / 2}px`,
|
||||||
|
height: "40px",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const profileAvatar = style({
|
||||||
|
width: "35px",
|
||||||
|
height: "35px",
|
||||||
|
borderRadius: "50%",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
backgroundColor: vars.color.background,
|
||||||
|
border: `solid 1px ${vars.color.border}`,
|
||||||
|
position: "relative",
|
||||||
|
objectFit: "cover",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const profileButtonInformation = style({
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const statusBadge = style({
|
||||||
|
width: "9px",
|
||||||
|
height: "9px",
|
||||||
|
borderRadius: "50%",
|
||||||
|
backgroundColor: vars.color.danger,
|
||||||
|
position: "absolute",
|
||||||
|
bottom: "-2px",
|
||||||
|
right: "-3px",
|
||||||
|
zIndex: "1",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const profileButtonTitle = style({
|
||||||
|
fontWeight: "bold",
|
||||||
|
fontSize: vars.size.body,
|
||||||
|
});
|
@ -1,6 +1,7 @@
|
|||||||
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 * as styles from "./sidebar.css";
|
import * as styles from "./sidebar-profile.css";
|
||||||
|
|
||||||
import { useUserDetails } from "@renderer/hooks";
|
import { useUserDetails } from "@renderer/hooks";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|
||||||
@ -9,12 +10,13 @@ export function SidebarProfile() {
|
|||||||
|
|
||||||
const { userDetails, profileBackground } = useUserDetails();
|
const { userDetails, profileBackground } = useUserDetails();
|
||||||
|
|
||||||
const handleClickProfile = () => {
|
const handleButtonClick = () => {
|
||||||
navigate(`/user/${userDetails!.id}`);
|
if (userDetails === null) {
|
||||||
};
|
window.electron.openExternal("https://auth.hydra.losbroxas.org");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const handleClickLogin = () => {
|
navigate(`/user/${userDetails!.id}`);
|
||||||
window.electron.openExternal("https://auth.hydra.losbroxas.org");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const profileButtonBackground = useMemo(() => {
|
const profileButtonBackground = useMemo(() => {
|
||||||
@ -22,36 +24,16 @@ export function SidebarProfile() {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}, [profileBackground]);
|
}, [profileBackground]);
|
||||||
|
|
||||||
if (userDetails == null) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={styles.profileButton}
|
|
||||||
onClick={handleClickLogin}
|
|
||||||
>
|
|
||||||
<div className={styles.profileAvatar}>
|
|
||||||
<PersonIcon />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.profileButtonInformation}>
|
|
||||||
<p style={{ fontWeight: "bold" }}>Fazer login</p>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<button
|
||||||
<button
|
type="button"
|
||||||
type="button"
|
className={styles.profileButton}
|
||||||
className={styles.profileButton}
|
style={{ background: profileButtonBackground }}
|
||||||
style={{ background: profileButtonBackground }}
|
onClick={handleButtonClick}
|
||||||
onClick={handleClickProfile}
|
>
|
||||||
>
|
<div className={styles.profileButtonContent}>
|
||||||
<div className={styles.profileAvatar}>
|
<div className={styles.profileAvatar}>
|
||||||
{userDetails.profileImageUrl ? (
|
{userDetails?.profileImageUrl ? (
|
||||||
<img
|
<img
|
||||||
className={styles.profileAvatar}
|
className={styles.profileAvatar}
|
||||||
src={userDetails.profileImageUrl}
|
src={userDetails.profileImageUrl}
|
||||||
@ -63,9 +45,11 @@ export function SidebarProfile() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.profileButtonInformation}>
|
<div className={styles.profileButtonInformation}>
|
||||||
<p style={{ fontWeight: "bold" }}>{userDetails.displayName}</p>
|
<p className={styles.profileButtonTitle}>
|
||||||
|
{userDetails ? userDetails.displayName : "Sign in"}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</div>
|
||||||
</>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -125,48 +125,3 @@ export const section = style({
|
|||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
paddingBottom: `${SPACING_UNIT}px`,
|
paddingBottom: `${SPACING_UNIT}px`,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const profileButton = style({
|
|
||||||
display: "flex",
|
|
||||||
cursor: "pointer",
|
|
||||||
transition: "all ease 0.1s",
|
|
||||||
gap: `${SPACING_UNIT + SPACING_UNIT / 2}px`,
|
|
||||||
alignItems: "center",
|
|
||||||
padding: `${SPACING_UNIT * 2}px ${SPACING_UNIT * 2}px`,
|
|
||||||
color: vars.color.muted,
|
|
||||||
borderBottom: `solid 1px ${vars.color.border}`,
|
|
||||||
boxShadow: "0px 0px 15px 0px #000000",
|
|
||||||
":hover": {
|
|
||||||
backgroundColor: "rgba(255, 255, 255, 0.15)",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const profileAvatar = style({
|
|
||||||
width: "30px",
|
|
||||||
height: "30px",
|
|
||||||
borderRadius: "50%",
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
backgroundColor: vars.color.background,
|
|
||||||
border: `solid 1px ${vars.color.border}`,
|
|
||||||
position: "relative",
|
|
||||||
objectFit: "cover",
|
|
||||||
});
|
|
||||||
|
|
||||||
export const profileButtonInformation = style({
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
alignItems: "flex-start",
|
|
||||||
});
|
|
||||||
|
|
||||||
export const statusBadge = style({
|
|
||||||
width: "9px",
|
|
||||||
height: "9px",
|
|
||||||
borderRadius: "50%",
|
|
||||||
backgroundColor: vars.color.danger,
|
|
||||||
position: "absolute",
|
|
||||||
bottom: "-2px",
|
|
||||||
right: "-3px",
|
|
||||||
zIndex: "1",
|
|
||||||
});
|
|
||||||
|
2
src/renderer/src/declaration.d.ts
vendored
2
src/renderer/src/declaration.d.ts
vendored
@ -125,7 +125,7 @@ declare global {
|
|||||||
updateProfile: (
|
updateProfile: (
|
||||||
displayName: string,
|
displayName: string,
|
||||||
newProfileImagePath: string | null
|
newProfileImagePath: string | null
|
||||||
) => Promise<UserProfile | null>;
|
) => Promise<UserProfile>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Window {
|
interface Window {
|
||||||
|
@ -15,18 +15,14 @@ export const userDetailsSlice = createSlice({
|
|||||||
name: "user-details",
|
name: "user-details",
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
setUserDetails: (state, action: PayloadAction<UserDetails>) => {
|
setUserDetails: (state, action: PayloadAction<UserDetails | null>) => {
|
||||||
state.userDetails = action.payload;
|
state.userDetails = action.payload;
|
||||||
},
|
},
|
||||||
setProfileBackground: (state, action: PayloadAction<string>) => {
|
setProfileBackground: (state, action: PayloadAction<string | null>) => {
|
||||||
state.profileBackground = action.payload;
|
state.profileBackground = action.payload;
|
||||||
},
|
},
|
||||||
clearUserDetails: (state) => {
|
|
||||||
state.userDetails = null;
|
|
||||||
state.profileBackground = null;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { setUserDetails, setProfileBackground, clearUserDetails } =
|
export const { setUserDetails, setProfileBackground } =
|
||||||
userDetailsSlice.actions;
|
userDetailsSlice.actions;
|
||||||
|
@ -2,12 +2,9 @@ import { useCallback } from "react";
|
|||||||
import { average } from "color.js";
|
import { average } from "color.js";
|
||||||
|
|
||||||
import { useAppDispatch, useAppSelector } from "./redux";
|
import { useAppDispatch, useAppSelector } from "./redux";
|
||||||
import {
|
import { setProfileBackground, setUserDetails } from "@renderer/features";
|
||||||
clearUserDetails,
|
|
||||||
setProfileBackground,
|
|
||||||
setUserDetails,
|
|
||||||
} from "@renderer/features";
|
|
||||||
import { darkenColor } from "@renderer/helpers";
|
import { darkenColor } from "@renderer/helpers";
|
||||||
|
import { UserDetails } from "@types";
|
||||||
|
|
||||||
export function useUserDetails() {
|
export function useUserDetails() {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@ -16,68 +13,64 @@ export function useUserDetails() {
|
|||||||
(state) => state.userDetails
|
(state) => state.userDetails
|
||||||
);
|
);
|
||||||
|
|
||||||
const clearUser = useCallback(async () => {
|
const clearUserDetails = useCallback(async () => {
|
||||||
dispatch(clearUserDetails());
|
dispatch(setUserDetails(null));
|
||||||
|
dispatch(setProfileBackground(null));
|
||||||
|
|
||||||
|
window.localStorage.removeItem("userDetails");
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
const signOut = useCallback(async () => {
|
const signOut = useCallback(async () => {
|
||||||
clearUser();
|
clearUserDetails();
|
||||||
|
|
||||||
return window.electron.signOut();
|
return window.electron.signOut();
|
||||||
}, [clearUser]);
|
}, [clearUserDetails]);
|
||||||
|
|
||||||
const updateUser = useCallback(async () => {
|
const updateUserDetails = useCallback(
|
||||||
return window.electron.getMe().then(async (userDetails) => {
|
async (userDetails: UserDetails) => {
|
||||||
if (userDetails) {
|
dispatch(setUserDetails(userDetails));
|
||||||
dispatch(setUserDetails(userDetails));
|
|
||||||
|
|
||||||
if (userDetails.profileImageUrl) {
|
if (userDetails.profileImageUrl) {
|
||||||
const output = await average(userDetails.profileImageUrl, {
|
const output = await average(userDetails.profileImageUrl, {
|
||||||
amount: 1,
|
amount: 1,
|
||||||
format: "hex",
|
format: "hex",
|
||||||
});
|
|
||||||
|
|
||||||
dispatch(
|
|
||||||
setProfileBackground(
|
|
||||||
`linear-gradient(135deg, ${darkenColor(output as string, 0.6)}, ${darkenColor(output as string, 0.7)})`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, [dispatch]);
|
|
||||||
|
|
||||||
const patchUser = useCallback(
|
|
||||||
async (displayName: string, imageProfileUrl: string | null) => {
|
|
||||||
return window.electron
|
|
||||||
.updateProfile(displayName, imageProfileUrl)
|
|
||||||
.then(async (userDetails) => {
|
|
||||||
if (userDetails) {
|
|
||||||
dispatch(setUserDetails(userDetails));
|
|
||||||
|
|
||||||
if (userDetails.profileImageUrl) {
|
|
||||||
const output = await average(userDetails.profileImageUrl, {
|
|
||||||
amount: 1,
|
|
||||||
format: "hex",
|
|
||||||
});
|
|
||||||
|
|
||||||
dispatch(
|
|
||||||
setProfileBackground(
|
|
||||||
`linear-gradient(135deg, ${darkenColor(output as string, 0.6)}, ${darkenColor(output as string, 0.7)})`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const profileBackground = `linear-gradient(135deg, ${darkenColor(output as string, 0.6)}, ${darkenColor(output as string, 0.8)})`;
|
||||||
|
|
||||||
|
dispatch(setProfileBackground(profileBackground));
|
||||||
|
|
||||||
|
window.localStorage.setItem(
|
||||||
|
"userDetails",
|
||||||
|
JSON.stringify({ ...userDetails, profileBackground })
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const fetchUserDetails = useCallback(async () => {
|
||||||
|
return window.electron.getMe();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const patchUser = useCallback(
|
||||||
|
async (displayName: string, imageProfileUrl: string | null) => {
|
||||||
|
const response = await window.electron.updateProfile(
|
||||||
|
displayName,
|
||||||
|
imageProfileUrl
|
||||||
|
);
|
||||||
|
|
||||||
|
return updateUserDetails(response);
|
||||||
|
},
|
||||||
|
[updateUserDetails]
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
userDetails,
|
userDetails,
|
||||||
updateUser,
|
fetchUserDetails,
|
||||||
signOut,
|
signOut,
|
||||||
clearUser,
|
clearUserDetails,
|
||||||
|
updateUserDetails,
|
||||||
patchUser,
|
patchUser,
|
||||||
profileBackground,
|
profileBackground,
|
||||||
};
|
};
|
||||||
|
@ -71,7 +71,6 @@ export const UserEditProfileModal = ({
|
|||||||
<Modal visible={visible} title="Editar Perfil" onClose={onClose}>
|
<Modal visible={visible} title="Editar Perfil" onClose={onClose}>
|
||||||
<section
|
<section
|
||||||
style={{
|
style={{
|
||||||
padding: `${SPACING_UNIT * 2}px ${SPACING_UNIT * 2}px`,
|
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
|
Loading…
Reference in New Issue
Block a user