mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-03-09 15:40:26 +00:00
feat: adding modal to edit profile
This commit is contained in:
parent
79ca354da1
commit
af69509c61
9 changed files with 219 additions and 7 deletions
4
src/renderer/src/declaration.d.ts
vendored
4
src/renderer/src/declaration.d.ts
vendored
|
@ -121,6 +121,10 @@ declare global {
|
|||
|
||||
/* Profile */
|
||||
getMe: () => Promise<UserProfile | null>;
|
||||
updateProfile: (
|
||||
displayName: string,
|
||||
newProfileImagePath: string | null
|
||||
) => Promise<void>;
|
||||
}
|
||||
|
||||
interface Window {
|
||||
|
|
|
@ -3,7 +3,7 @@ import cn from "classnames";
|
|||
|
||||
import * as styles from "./user.css";
|
||||
import { SPACING_UNIT, vars } from "@renderer/theme.css";
|
||||
import { useMemo } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import SteamLogo from "@renderer/assets/steam-logo.svg?react";
|
||||
import { useDate, useUserDetails } from "@renderer/hooks";
|
||||
|
@ -11,6 +11,7 @@ import { useNavigate } from "react-router-dom";
|
|||
import { buildGameDetailsPath } from "@renderer/helpers";
|
||||
import { PersonIcon } from "@primer/octicons-react";
|
||||
import { Button } from "@renderer/components";
|
||||
import { UserEditProfileModal } from "./user-edit-modal";
|
||||
|
||||
const MAX_MINUTES_TO_SHOW_IN_PLAYTIME = 120;
|
||||
|
||||
|
@ -23,6 +24,8 @@ export function UserContent({ userProfile }: ProfileContentProps) {
|
|||
|
||||
const { userDetails, profileBackground, signOut } = useUserDetails();
|
||||
|
||||
const [showEditProfileModal, setShowEditProfileModal] = useState(false);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const numberFormatter = useMemo(() => {
|
||||
|
@ -54,6 +57,10 @@ export function UserContent({ userProfile }: ProfileContentProps) {
|
|||
navigate(buildGameDetailsPath(game));
|
||||
};
|
||||
|
||||
const handleEditProfile = () => {
|
||||
setShowEditProfileModal(true);
|
||||
};
|
||||
|
||||
const handleSignout = async () => {
|
||||
await signOut();
|
||||
navigate("/");
|
||||
|
@ -69,10 +76,17 @@ export function UserContent({ userProfile }: ProfileContentProps) {
|
|||
|
||||
return (
|
||||
<>
|
||||
<UserEditProfileModal
|
||||
visible={showEditProfileModal}
|
||||
onClose={() => setShowEditProfileModal(false)}
|
||||
userProfile={userProfile}
|
||||
/>
|
||||
|
||||
<section
|
||||
className={styles.profileContentBox}
|
||||
style={{
|
||||
background: profileContentBoxBackground,
|
||||
padding: `${SPACING_UNIT * 4}px ${SPACING_UNIT * 2}px`,
|
||||
}}
|
||||
>
|
||||
<div className={styles.profileAvatarContainer}>
|
||||
|
@ -93,9 +107,23 @@ export function UserContent({ userProfile }: ProfileContentProps) {
|
|||
|
||||
{isMe && (
|
||||
<div style={{ flex: 1, display: "flex", justifyContent: "end" }}>
|
||||
<Button theme="danger" onClick={handleSignout}>
|
||||
{t("sign_out")}
|
||||
</Button>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: `${SPACING_UNIT}px`,
|
||||
}}
|
||||
>
|
||||
<>
|
||||
<Button theme="outline" onClick={handleEditProfile}>
|
||||
Editar perfil
|
||||
</Button>
|
||||
|
||||
<Button theme="danger" onClick={handleSignout}>
|
||||
{t("sign_out")}
|
||||
</Button>
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
|
90
src/renderer/src/pages/user/user-edit-modal.tsx
Normal file
90
src/renderer/src/pages/user/user-edit-modal.tsx
Normal file
|
@ -0,0 +1,90 @@
|
|||
import { Button, Modal, TextField } from "@renderer/components";
|
||||
import { UserProfile } from "@types";
|
||||
import * as styles from "./user.css";
|
||||
import { PersonIcon } from "@primer/octicons-react";
|
||||
import { SPACING_UNIT } from "@renderer/theme.css";
|
||||
import { useState } from "react";
|
||||
|
||||
export interface UserEditProfileModalProps {
|
||||
userProfile: UserProfile;
|
||||
visible: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const UserEditProfileModal = ({
|
||||
userProfile,
|
||||
visible,
|
||||
onClose,
|
||||
}: UserEditProfileModalProps) => {
|
||||
const [displayName, setDisplayName] = useState(userProfile.displayName);
|
||||
const [newImagePath, setNewImagePath] = useState<string | null>(null);
|
||||
|
||||
const handleChangeProfileAvatar = async () => {
|
||||
const { filePaths } = await window.electron.showOpenDialog({
|
||||
properties: ["openFile"],
|
||||
filters: [
|
||||
{
|
||||
name: "Profile avatar",
|
||||
extensions: ["jpg", "png", "gif"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const path = filePaths[0];
|
||||
console.log(path);
|
||||
|
||||
setNewImagePath(path);
|
||||
};
|
||||
|
||||
const handleSaveProfile = async () => {
|
||||
await window.electron
|
||||
.updateProfile(displayName, newImagePath)
|
||||
.catch((err) => {
|
||||
console.log("errro", err);
|
||||
});
|
||||
setNewImagePath(null);
|
||||
onClose();
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<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",
|
||||
alignItems: "center",
|
||||
gap: `${SPACING_UNIT * 3}px`,
|
||||
width: "300px",
|
||||
}}
|
||||
>
|
||||
<button
|
||||
className={styles.profileAvatarEditContainer}
|
||||
onClick={handleChangeProfileAvatar}
|
||||
>
|
||||
{userProfile.profileImageUrl ? (
|
||||
<img
|
||||
className={styles.profileAvatar}
|
||||
alt={userProfile.displayName}
|
||||
src={newImagePath ?? userProfile.profileImageUrl}
|
||||
/>
|
||||
) : (
|
||||
<PersonIcon size={72} />
|
||||
)}
|
||||
</button>
|
||||
|
||||
<TextField
|
||||
label="Nome de exibição"
|
||||
value={displayName}
|
||||
containerProps={{ style: { width: "100%" } }}
|
||||
onChange={(e) => setDisplayName(e.target.value)}
|
||||
/>
|
||||
<Button style={{ alignSelf: "end" }} onClick={handleSaveProfile}>
|
||||
Salvar{" "}
|
||||
</Button>
|
||||
</section>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -12,7 +12,6 @@ export const wrapper = style({
|
|||
export const profileContentBox = style({
|
||||
display: "flex",
|
||||
gap: `${SPACING_UNIT * 3}px`,
|
||||
padding: `${SPACING_UNIT * 4}px ${SPACING_UNIT * 2}px`,
|
||||
alignItems: "center",
|
||||
borderRadius: "4px",
|
||||
border: `solid 1px ${vars.color.border}`,
|
||||
|
@ -36,12 +35,35 @@ export const profileAvatarContainer = style({
|
|||
boxShadow: "0px 0px 5px 0px rgba(0, 0, 0, 0.7)",
|
||||
});
|
||||
|
||||
export const profileAvatarEditContainer = style({
|
||||
width: "128px",
|
||||
height: "128px",
|
||||
borderRadius: "50%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
backgroundColor: vars.color.background,
|
||||
position: "relative",
|
||||
overflow: "hidden",
|
||||
border: `solid 1px ${vars.color.border}`,
|
||||
boxShadow: "0px 0px 5px 0px rgba(0, 0, 0, 0.7)",
|
||||
});
|
||||
|
||||
export const profileAvatar = style({
|
||||
width: "96px",
|
||||
height: "96px",
|
||||
height: "100%",
|
||||
objectFit: "cover",
|
||||
});
|
||||
|
||||
export const profileAvatarEditOverlay = style({
|
||||
position: "absolute",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
backgroundColor: "#00000055",
|
||||
color: vars.color.muted,
|
||||
zIndex: 1,
|
||||
cursor: "pointer",
|
||||
});
|
||||
|
||||
export const profileInformation = style({
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue