mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-01-23 21:44:55 +03:00
feat: adding user local storage cache
This commit is contained in:
parent
eea19d43c2
commit
a8624058e4
@ -19,6 +19,8 @@ import {
|
||||
setUserPreferences,
|
||||
toggleDraggingDisabled,
|
||||
closeToast,
|
||||
setUserDetails,
|
||||
setProfileBackground,
|
||||
} from "@renderer/features";
|
||||
|
||||
export interface AppProps {
|
||||
@ -31,7 +33,8 @@ export function App() {
|
||||
|
||||
const { clearDownload, setLastPacket } = useDownload();
|
||||
|
||||
const { updateUser, clearUser } = useUserDetails();
|
||||
const { fetchUserDetails, updateUserDetails, clearUserDetails } =
|
||||
useUserDetails();
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
@ -73,23 +76,38 @@ export function App() {
|
||||
}, [clearDownload, setLastPacket, updateLibrary]);
|
||||
|
||||
useEffect(() => {
|
||||
updateUser();
|
||||
}, [updateUser]);
|
||||
const cachedUserDetails = window.localStorage.getItem("userDetails");
|
||||
|
||||
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(() => {
|
||||
const listeners = [
|
||||
window.electron.onSignIn(() => {
|
||||
updateUser();
|
||||
fetchUserDetails().then((response) => {
|
||||
if (response) updateUserDetails(response);
|
||||
});
|
||||
}),
|
||||
window.electron.onSignOut(() => {
|
||||
clearUser();
|
||||
clearUserDetails();
|
||||
}),
|
||||
];
|
||||
|
||||
return () => {
|
||||
listeners.forEach((unsubscribe) => unsubscribe());
|
||||
};
|
||||
}, [updateUser, clearUser]);
|
||||
}, [fetchUserDetails, updateUserDetails, clearUserDetails]);
|
||||
|
||||
const handleSearch = useCallback(
|
||||
(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 { PersonIcon } from "@primer/octicons-react";
|
||||
import * as styles from "./sidebar.css";
|
||||
import * as styles from "./sidebar-profile.css";
|
||||
|
||||
import { useUserDetails } from "@renderer/hooks";
|
||||
import { useMemo } from "react";
|
||||
|
||||
@ -9,12 +10,13 @@ export function SidebarProfile() {
|
||||
|
||||
const { userDetails, profileBackground } = useUserDetails();
|
||||
|
||||
const handleClickProfile = () => {
|
||||
navigate(`/user/${userDetails!.id}`);
|
||||
};
|
||||
const handleButtonClick = () => {
|
||||
if (userDetails === null) {
|
||||
window.electron.openExternal("https://auth.hydra.losbroxas.org");
|
||||
return;
|
||||
}
|
||||
|
||||
const handleClickLogin = () => {
|
||||
window.electron.openExternal("https://auth.hydra.losbroxas.org");
|
||||
navigate(`/user/${userDetails!.id}`);
|
||||
};
|
||||
|
||||
const profileButtonBackground = useMemo(() => {
|
||||
@ -22,36 +24,16 @@ export function SidebarProfile() {
|
||||
return undefined;
|
||||
}, [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 (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.profileButton}
|
||||
style={{ background: profileButtonBackground }}
|
||||
onClick={handleClickProfile}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.profileButton}
|
||||
style={{ background: profileButtonBackground }}
|
||||
onClick={handleButtonClick}
|
||||
>
|
||||
<div className={styles.profileButtonContent}>
|
||||
<div className={styles.profileAvatar}>
|
||||
{userDetails.profileImageUrl ? (
|
||||
{userDetails?.profileImageUrl ? (
|
||||
<img
|
||||
className={styles.profileAvatar}
|
||||
src={userDetails.profileImageUrl}
|
||||
@ -63,9 +45,11 @@ export function SidebarProfile() {
|
||||
</div>
|
||||
|
||||
<div className={styles.profileButtonInformation}>
|
||||
<p style={{ fontWeight: "bold" }}>{userDetails.displayName}</p>
|
||||
<p className={styles.profileButtonTitle}>
|
||||
{userDetails ? userDetails.displayName : "Sign in"}
|
||||
</p>
|
||||
</div>
|
||||
</button>
|
||||
</>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
@ -125,48 +125,3 @@ export const section = style({
|
||||
flexDirection: "column",
|
||||
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: (
|
||||
displayName: string,
|
||||
newProfileImagePath: string | null
|
||||
) => Promise<UserProfile | null>;
|
||||
) => Promise<UserProfile>;
|
||||
}
|
||||
|
||||
interface Window {
|
||||
|
@ -15,18 +15,14 @@ export const userDetailsSlice = createSlice({
|
||||
name: "user-details",
|
||||
initialState,
|
||||
reducers: {
|
||||
setUserDetails: (state, action: PayloadAction<UserDetails>) => {
|
||||
setUserDetails: (state, action: PayloadAction<UserDetails | null>) => {
|
||||
state.userDetails = action.payload;
|
||||
},
|
||||
setProfileBackground: (state, action: PayloadAction<string>) => {
|
||||
setProfileBackground: (state, action: PayloadAction<string | null>) => {
|
||||
state.profileBackground = action.payload;
|
||||
},
|
||||
clearUserDetails: (state) => {
|
||||
state.userDetails = null;
|
||||
state.profileBackground = null;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { setUserDetails, setProfileBackground, clearUserDetails } =
|
||||
export const { setUserDetails, setProfileBackground } =
|
||||
userDetailsSlice.actions;
|
||||
|
@ -2,12 +2,9 @@ import { useCallback } from "react";
|
||||
import { average } from "color.js";
|
||||
|
||||
import { useAppDispatch, useAppSelector } from "./redux";
|
||||
import {
|
||||
clearUserDetails,
|
||||
setProfileBackground,
|
||||
setUserDetails,
|
||||
} from "@renderer/features";
|
||||
import { setProfileBackground, setUserDetails } from "@renderer/features";
|
||||
import { darkenColor } from "@renderer/helpers";
|
||||
import { UserDetails } from "@types";
|
||||
|
||||
export function useUserDetails() {
|
||||
const dispatch = useAppDispatch();
|
||||
@ -16,68 +13,64 @@ export function useUserDetails() {
|
||||
(state) => state.userDetails
|
||||
);
|
||||
|
||||
const clearUser = useCallback(async () => {
|
||||
dispatch(clearUserDetails());
|
||||
const clearUserDetails = useCallback(async () => {
|
||||
dispatch(setUserDetails(null));
|
||||
dispatch(setProfileBackground(null));
|
||||
|
||||
window.localStorage.removeItem("userDetails");
|
||||
}, [dispatch]);
|
||||
|
||||
const signOut = useCallback(async () => {
|
||||
clearUser();
|
||||
clearUserDetails();
|
||||
|
||||
return window.electron.signOut();
|
||||
}, [clearUser]);
|
||||
}, [clearUserDetails]);
|
||||
|
||||
const updateUser = useCallback(async () => {
|
||||
return window.electron.getMe().then(async (userDetails) => {
|
||||
if (userDetails) {
|
||||
dispatch(setUserDetails(userDetails));
|
||||
const updateUserDetails = useCallback(
|
||||
async (userDetails: 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)})`
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [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)})`
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
if (userDetails.profileImageUrl) {
|
||||
const output = await average(userDetails.profileImageUrl, {
|
||||
amount: 1,
|
||||
format: "hex",
|
||||
});
|
||||
|
||||
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]
|
||||
);
|
||||
|
||||
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 {
|
||||
userDetails,
|
||||
updateUser,
|
||||
fetchUserDetails,
|
||||
signOut,
|
||||
clearUser,
|
||||
clearUserDetails,
|
||||
updateUserDetails,
|
||||
patchUser,
|
||||
profileBackground,
|
||||
};
|
||||
|
@ -71,7 +71,6 @@ export const UserEditProfileModal = ({
|
||||
<Modal visible={visible} title="Editar Perfil" onClose={onClose}>
|
||||
<section
|
||||
style={{
|
||||
padding: `${SPACING_UNIT * 2}px ${SPACING_UNIT * 2}px`,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
|
Loading…
Reference in New Issue
Block a user