From 6ccbff01602fc9fef768ebbb4156292ecdbc6566 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Mon, 8 Jul 2024 22:07:14 -0300 Subject: [PATCH 01/54] feat: creating friends section --- src/locales/en/translation.json | 9 +- src/locales/pt/translation.json | 7 +- src/main/services/hydra-api.ts | 4 +- src/renderer/index.html | 2 +- src/renderer/src/hooks/use-user-details.ts | 5 + .../src/pages/user/user-add-friends-modal.tsx | 123 ++++++++++++++ src/renderer/src/pages/user/user-content.tsx | 160 +++++++++++++----- src/renderer/src/pages/user/user.css.ts | 32 +++- src/types/index.ts | 17 +- 9 files changed, 307 insertions(+), 52 deletions(-) create mode 100644 src/renderer/src/pages/user/user-add-friends-modal.tsx diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index e44dc7ea..2dadbcee 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -241,6 +241,13 @@ "successfully_signed_out": "Successfully signed out", "sign_out": "Sign out", "playing_for": "Playing for {{amount}}", - "sign_out_modal_text": "Your library is linked with your current account. When signing out, your library will not be visible anymore, and any progress will not be saved. Continue with sign out?" + "sign_out_modal_text": "Your library is linked with your current account. When signing out, your library will not be visible anymore, and any progress will not be saved. Continue with sign out?", + "add_friends": "Add Friends", + "friend_code": "Friend code", + "see_profile": "See profile", + "sending": "Sending", + "send": "Add friend", + "friend_request_sent": "Friend request sent", + "friends": "Friends" } } diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json index 02c0879f..9ff165f9 100644 --- a/src/locales/pt/translation.json +++ b/src/locales/pt/translation.json @@ -241,6 +241,11 @@ "sign_out": "Sair da conta", "sign_out_modal_title": "Tem certeza?", "playing_for": "Jogando por {{amount}}", - "sign_out_modal_text": "Sua biblioteca de jogos está associada com a sua conta atual. Ao sair, sua biblioteca não aparecerá mais no Hydra e qualquer progresso não será salvo. Deseja continuar?" + "sign_out_modal_text": "Sua biblioteca de jogos está associada com a sua conta atual. Ao sair, sua biblioteca não aparecerá mais no Hydra e qualquer progresso não será salvo. Deseja continuar?", + "add_friends": "Adicionar Amigos", + "friend_code": "Código de amigo", + "see_profile": "Ver perfil", + "friend_request_sent": "Pedido de amizade enviado", + "friends": "Amigos" } } diff --git a/src/main/services/hydra-api.ts b/src/main/services/hydra-api.ts index 98b783f3..1abae98c 100644 --- a/src/main/services/hydra-api.ts +++ b/src/main/services/hydra-api.ts @@ -98,9 +98,9 @@ export class HydraApi { logger.error(config.method, config.baseURL, config.url, config.headers); if (error.response) { - logger.error(error.response.status, error.response.data); + logger.error("Response", error.response.status, error.response.data); } else if (error.request) { - logger.error(error.request); + logger.error("Request", error.request); } else { logger.error("Error", error.message); } diff --git a/src/renderer/index.html b/src/renderer/index.html index 543b85a9..52276268 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -6,7 +6,7 @@ Hydra diff --git a/src/renderer/src/hooks/use-user-details.ts b/src/renderer/src/hooks/use-user-details.ts index e87f8ff6..6495ba27 100644 --- a/src/renderer/src/hooks/use-user-details.ts +++ b/src/renderer/src/hooks/use-user-details.ts @@ -78,6 +78,10 @@ export function useUserDetails() { [updateUserDetails] ); + const sendFriendRequest = useCallback(async (userId: string) => { + console.log("sending friend request to", userId); + }, []); + return { userDetails, fetchUserDetails, @@ -85,6 +89,7 @@ export function useUserDetails() { clearUserDetails, updateUserDetails, patchUser, + sendFriendRequest, profileBackground, }; } diff --git a/src/renderer/src/pages/user/user-add-friends-modal.tsx b/src/renderer/src/pages/user/user-add-friends-modal.tsx new file mode 100644 index 00000000..a872abcf --- /dev/null +++ b/src/renderer/src/pages/user/user-add-friends-modal.tsx @@ -0,0 +1,123 @@ +import { Button, Modal, TextField } from "@renderer/components"; +import { PendingFriendRequest } from "@types"; +import * as styles from "./user.css"; +import { SPACING_UNIT } from "@renderer/theme.css"; +import { useEffect, useState } from "react"; +import { useToast, useUserDetails } from "@renderer/hooks"; +import { useTranslation } from "react-i18next"; +import { useNavigate } from "react-router-dom"; + +export interface UserAddFriendsModalProps { + visible: boolean; + onClose: () => void; +} + +export const UserAddFriendsModal = ({ + visible, + onClose, +}: UserAddFriendsModalProps) => { + const { t } = useTranslation("user_profile"); + + const [friendCode, setFriendCode] = useState(""); + const [isAddingFriend, setIsAddingFriend] = useState(false); + const [pendingRequests, setPendingRequests] = useState< + PendingFriendRequest[] + >([]); + + const navigate = useNavigate(); + + const { sendFriendRequest } = useUserDetails(); + + const { showSuccessToast, showErrorToast } = useToast(); + + const handleAddFriend: React.FormEventHandler = async ( + event + ) => { + event.preventDefault(); + setIsAddingFriend(true); + sendFriendRequest(friendCode) + .then(() => { + showSuccessToast(t("friend_request_sent")); + }) + .catch(() => { + showErrorToast("falhaaaa"); + }) + .finally(() => { + setIsAddingFriend(false); + }); + }; + + useEffect(() => { + setPendingRequests([]); + }); + + const handleSeeProfileClick = () => { + navigate(`profile/${friendCode}`); + }; + + const resetModal = () => { + setFriendCode(""); + }; + + const cleanFormAndClose = () => { + resetModal(); + onClose(); + }; + + return ( + <> + +
+ setFriendCode(e.target.value)} + /> + + + + +
+ {pendingRequests.map((request) => { + return ( +

+ {request.AId} - {request.BId} +

+ ); + })} +
+
+ + ); +}; diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index 6f897238..8e973e7f 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -14,10 +14,16 @@ import { } from "@renderer/hooks"; import { useNavigate } from "react-router-dom"; import { buildGameDetailsPath, steamUrlBuilder } from "@renderer/helpers"; -import { PersonIcon, TelescopeIcon } from "@primer/octicons-react"; +import { + PersonAddIcon, + PersonIcon, + PlusCircleIcon, + TelescopeIcon, +} from "@primer/octicons-react"; import { Button, Link } from "@renderer/components"; import { UserEditProfileModal } from "./user-edit-modal"; import { UserSignOutModal } from "./user-signout-modal"; +import { UserAddFriendsModal } from "./user-add-friends-modal"; const MAX_MINUTES_TO_SHOW_IN_PLAYTIME = 120; @@ -37,6 +43,7 @@ export function UserContent({ const [showEditProfileModal, setShowEditProfileModal] = useState(false); const [showSignOutModal, setShowSignOutModal] = useState(false); + const [showAddFriendsModal, setShowAddFriendsModal] = useState(false); const { gameRunning } = useAppSelector((state) => state.gameRunning); @@ -103,6 +110,11 @@ export function UserContent({ onConfirm={handleConfirmSignout} /> + setShowAddFriendsModal(false)} + /> +

{t("activity")}

- {!userProfile.recentGames.length ? ( + {!userProfile.recentGames?.length ? (
@@ -259,54 +271,116 @@ export function UserContent({ )}
-
-
-

{t("library")}

+
+
+
+

{t("library")}

+ +
+

+ {userProfile.libraryGames?.length} +

+
+ {t("total_play_time", { amount: formatPlayTime() })} +
+ {userProfile.libraryGames?.map((game) => ( + + ))} +
+
+ +
+
+

{t("friends")}

+ +
+ +
-

- {userProfile.libraryGames.length} -

-
- {t("total_play_time", { amount: formatPlayTime() })} -
- {userProfile.libraryGames.map((game) => ( + > - ))} + + +
diff --git a/src/renderer/src/pages/user/user.css.ts b/src/renderer/src/pages/user/user.css.ts index 299aa393..0128ec90 100644 --- a/src/renderer/src/pages/user/user.css.ts +++ b/src/renderer/src/pages/user/user.css.ts @@ -86,7 +86,13 @@ export const profileContent = style({ export const profileGameSection = style({ width: "100%", - height: "100%", + display: "flex", + flexDirection: "column", + gap: `${SPACING_UNIT * 2}px`, +}); + +export const friendsSection = style({ + width: "100%", display: "flex", flexDirection: "column", gap: `${SPACING_UNIT * 2}px`, @@ -94,6 +100,9 @@ export const profileGameSection = style({ export const contentSidebar = style({ width: "100%", + display: "flex", + flexDirection: "column", + gap: `${SPACING_UNIT * 3}px`, "@media": { "(min-width: 768px)": { width: "100%", @@ -116,12 +125,17 @@ export const libraryGameIcon = style({ borderRadius: "4px", }); +export const friendProfileIcon = style({ + height: "100%", +}); + export const feedItem = style({ color: vars.color.body, display: "flex", flexDirection: "row", gap: `${SPACING_UNIT * 2}px`, width: "100%", + overflow: "hidden", height: "72px", transition: "all ease 0.2s", cursor: "pointer", @@ -143,6 +157,22 @@ export const gameListItem = style({ }, }); +export const friendListItem = style({ + color: vars.color.body, + display: "flex", + flexDirection: "row", + gap: `${SPACING_UNIT}px`, + width: "100%", + height: "48px", + transition: "all ease 0.2s", + cursor: "pointer", + zIndex: "1", + overflow: "hidden", + ":hover": { + backgroundColor: "rgba(255, 255, 255, 0.15)", + }, +}); + export const gameInformation = style({ display: "flex", flexDirection: "column", diff --git a/src/types/index.ts b/src/types/index.ts index 71071620..17925ac0 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -269,14 +269,25 @@ export interface UserDetails { profileImageUrl: string | null; } +export interface UserFriend { + id: string; + displayName: string; + profileImageUrl: string | null; +} + +export interface PendingFriendRequest { + AId: string; + BId: string; +} + export interface UserProfile { id: string; displayName: string; - username: string; profileImageUrl: string | null; totalPlayTimeInSeconds: number; - libraryGames: UserGame[]; - recentGames: UserGame[]; + libraryGames: UserGame[] | null; + recentGames: UserGame[] | null; + friends: UserFriend[] | null; } export interface DownloadSource { From 6cc8e8f5fe2d32046c718ae6540a251d6210f3f4 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Tue, 9 Jul 2024 16:14:47 -0300 Subject: [PATCH 02/54] feat: pending requests on modal --- .../src/pages/user/user-add-friends-modal.tsx | 167 +++++++++++++----- src/renderer/src/pages/user/user-content.tsx | 20 ++- .../user/user-friend-pending-request.tsx | 81 +++++++++ src/renderer/src/pages/user/user.css.ts | 27 +++ src/types/index.ts | 4 + 5 files changed, 244 insertions(+), 55 deletions(-) create mode 100644 src/renderer/src/pages/user/user-friend-pending-request.tsx diff --git a/src/renderer/src/pages/user/user-add-friends-modal.tsx b/src/renderer/src/pages/user/user-add-friends-modal.tsx index a872abcf..4306936d 100644 --- a/src/renderer/src/pages/user/user-add-friends-modal.tsx +++ b/src/renderer/src/pages/user/user-add-friends-modal.tsx @@ -1,11 +1,11 @@ import { Button, Modal, TextField } from "@renderer/components"; import { PendingFriendRequest } from "@types"; -import * as styles from "./user.css"; import { SPACING_UNIT } from "@renderer/theme.css"; import { useEffect, useState } from "react"; import { useToast, useUserDetails } from "@renderer/hooks"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; +import { UserFriendPendingRequest } from "./user-friend-pending-request"; export interface UserAddFriendsModalProps { visible: boolean; @@ -26,14 +26,11 @@ export const UserAddFriendsModal = ({ const navigate = useNavigate(); - const { sendFriendRequest } = useUserDetails(); + const { userDetails, sendFriendRequest } = useUserDetails(); const { showSuccessToast, showErrorToast } = useToast(); - const handleAddFriend: React.FormEventHandler = async ( - event - ) => { - event.preventDefault(); + const handleClickAddFriend = () => { setIsAddingFriend(true); sendFriendRequest(friendCode) .then(() => { @@ -47,12 +44,46 @@ export const UserAddFriendsModal = ({ }); }; - useEffect(() => { - setPendingRequests([]); - }); + const handleClickFriend = (userId: string) => { + navigate(userId); + }; - const handleSeeProfileClick = () => { - navigate(`profile/${friendCode}`); + useEffect(() => { + setPendingRequests([ + { + AId: "abcd1234", + ADisplayName: "Punheta Master 123", + AProfileImageUrl: + "https://cdn.discordapp.com/avatars/1239959140785455295/4aff4b901c7a9f5f814b4379b6cfd58a.webp", + BId: "BMmNRmP3", + BDisplayName: "Hydra", + BProfileImageUrl: null, + }, + { + AId: "BMmNRmP3", + ADisplayName: "Hydra", + AProfileImageUrl: null, + BId: "12345678", + BDisplayName: "Deyvis0n", + BProfileImageUrl: null, + }, + ]); + }, []); + + const handleClickSeeProfile = () => { + // navigate(`profile/${friendCode}`); + }; + + const handleClickCancelFriendRequest = (userId: string) => { + console.log(userId); + }; + + const handleClickAcceptFriendRequest = (userId: string) => { + console.log(userId); + }; + + const handleClickRefuseFriendRequest = (userId: string) => { + console.log(userId); }; const resetModal = () => { @@ -71,51 +102,89 @@ export const UserAddFriendsModal = ({ title={t("add_friends")} onClose={cleanFormAndClose} > -
- setFriendCode(e.target.value)} - /> - - - + setFriendCode(e.target.value)} + /> + + +
-
- {pendingRequests.map((request) => { - return ( -

- {request.AId} - {request.BId} -

- ); - })} +
+

Pendentes

+ {pendingRequests.map((request) => { + if (request.AId === userDetails?.id) { + return ( + + ); + } + + return ( + + ); + })} +
diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index 8e973e7f..9d9b9415 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -17,7 +17,6 @@ import { buildGameDetailsPath, steamUrlBuilder } from "@renderer/helpers"; import { PersonAddIcon, PersonIcon, - PlusCircleIcon, TelescopeIcon, } from "@primer/octicons-react"; import { Button, Link } from "@renderer/components"; @@ -79,6 +78,10 @@ export function UserContent({ setShowEditProfileModal(true); }; + const handleOnClickFriend = (userId: string) => { + console.log(userId); + }; + const handleConfirmSignout = async () => { await signOut(); @@ -359,6 +362,7 @@ export function UserContent({ >
diff --git a/src/renderer/src/pages/user/user-friend-pending-request.tsx b/src/renderer/src/pages/user/user-friend-pending-request.tsx new file mode 100644 index 00000000..40f4bf19 --- /dev/null +++ b/src/renderer/src/pages/user/user-friend-pending-request.tsx @@ -0,0 +1,81 @@ +import { CheckCircleIcon, XCircleIcon } from "@primer/octicons-react"; +import { SPACING_UNIT, vars } from "@renderer/theme.css"; +import * as styles from "./user.css"; +import cn from "classnames"; + +export interface UserFriendPendingRequestProps { + userId: string; + profileImageUrl: string | null; + displayName: string; + isRequestSent: boolean; + onClickCancelRequest: (userId: string) => void; + onClickAcceptRequest: (userId: string) => void; + onClickRefuseRequest: (userId: string) => void; + onClickRequest: (userId: string) => void; +} + +export const UserFriendPendingRequest = ({ + userId, + profileImageUrl, + displayName, + isRequestSent, + onClickCancelRequest, + onClickAcceptRequest, + onClickRefuseRequest, + onClickRequest, +}: UserFriendPendingRequestProps) => { + return ( + + ) : ( + <> + + + + )} + + ); +}; diff --git a/src/renderer/src/pages/user/user.css.ts b/src/renderer/src/pages/user/user.css.ts index 0128ec90..16a4d44f 100644 --- a/src/renderer/src/pages/user/user.css.ts +++ b/src/renderer/src/pages/user/user.css.ts @@ -245,3 +245,30 @@ export const profileBackground = style({ top: "0", borderRadius: "4px", }); + +export const friendRequestItem = style({ + color: vars.color.body, + ":hover": { + backgroundColor: "rgba(255, 255, 255, 0.15)", + }, +}); + +export const acceptRequestButton = style({ + cursor: "pointer", + color: vars.color.body, + width: "28px", + height: "28px", + ":hover": { + color: vars.color.success, + }, +}); + +export const cancelRequestButton = style({ + cursor: "pointer", + color: vars.color.body, + width: "28px", + height: "28px", + ":hover": { + color: vars.color.danger, + }, +}); diff --git a/src/types/index.ts b/src/types/index.ts index 17925ac0..396a3029 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -277,7 +277,11 @@ export interface UserFriend { export interface PendingFriendRequest { AId: string; + ADisplayName: string; + AProfileImageUrl: string | null; BId: string; + BDisplayName: string; + BProfileImageUrl: string | null; } export interface UserProfile { From 007da03837c52d25e77944084d558bd88ec083b1 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 10 Jul 2024 14:29:04 -0300 Subject: [PATCH 03/54] feat: finish ui for modal showing pending requests --- .../src/pages/user/user-add-friends-modal.tsx | 48 ++++++------------- .../user/user-friend-pending-request.tsx | 32 ++++++++----- src/renderer/src/pages/user/user.css.ts | 16 ++++++- src/types/index.ts | 10 ++-- 4 files changed, 52 insertions(+), 54 deletions(-) diff --git a/src/renderer/src/pages/user/user-add-friends-modal.tsx b/src/renderer/src/pages/user/user-add-friends-modal.tsx index 4306936d..cb990e1c 100644 --- a/src/renderer/src/pages/user/user-add-friends-modal.tsx +++ b/src/renderer/src/pages/user/user-add-friends-modal.tsx @@ -26,7 +26,7 @@ export const UserAddFriendsModal = ({ const navigate = useNavigate(); - const { userDetails, sendFriendRequest } = useUserDetails(); + const { sendFriendRequest } = useUserDetails(); const { showSuccessToast, showErrorToast } = useToast(); @@ -51,21 +51,17 @@ export const UserAddFriendsModal = ({ useEffect(() => { setPendingRequests([ { - AId: "abcd1234", - ADisplayName: "Punheta Master 123", - AProfileImageUrl: + userId: "abcd1234", + displayName: "Punheta Master 123", + profileImageUrl: "https://cdn.discordapp.com/avatars/1239959140785455295/4aff4b901c7a9f5f814b4379b6cfd58a.webp", - BId: "BMmNRmP3", - BDisplayName: "Hydra", - BProfileImageUrl: null, + type: "RECEIVED", }, { - AId: "BMmNRmP3", - ADisplayName: "Hydra", - AProfileImageUrl: null, - BId: "12345678", - BDisplayName: "Deyvis0n", - BProfileImageUrl: null, + userId: "12345678", + displayName: "Deyvis0n", + profileImageUrl: null, + type: "SENT", }, ]); }, []); @@ -154,29 +150,13 @@ export const UserAddFriendsModal = ({ >

Pendentes

{pendingRequests.map((request) => { - if (request.AId === userDetails?.id) { - return ( - - ); - } - return ( onClickRequest(userId)} style={{ - display: "flex", - flexDirection: "row", - gap: `${SPACING_UNIT}px`, - alignItems: "center", + padding: "8px", }} > - +
+ {profileImageUrl ? ( + {displayName} + ) : ( + + )} +
{isRequestSent ? ( ) : ( <> diff --git a/src/renderer/src/pages/user/user.css.ts b/src/renderer/src/pages/user/user.css.ts index 16a4d44f..1582af29 100644 --- a/src/renderer/src/pages/user/user.css.ts +++ b/src/renderer/src/pages/user/user.css.ts @@ -35,6 +35,20 @@ export const profileAvatarContainer = style({ zIndex: 1, }); +export const pendingFriendRequestAvatarContainer = style({ + width: "32px", + height: "32px", + borderRadius: "50%", + display: "flex", + justifyContent: "center", + alignItems: "center", + backgroundColor: vars.color.background, + overflow: "hidden", + border: `solid 1px ${vars.color.border}`, + boxShadow: "0px 0px 5px 0px rgba(0, 0, 0, 0.7)", + zIndex: 1, +}); + export const profileAvatarEditContainer = style({ width: "128px", height: "128px", @@ -53,8 +67,6 @@ export const profileAvatarEditContainer = style({ export const profileAvatar = style({ height: "100%", width: "100%", - borderRadius: "50%", - overflow: "hidden", objectFit: "cover", }); diff --git a/src/types/index.ts b/src/types/index.ts index 396a3029..46a556b3 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -276,12 +276,10 @@ export interface UserFriend { } export interface PendingFriendRequest { - AId: string; - ADisplayName: string; - AProfileImageUrl: string | null; - BId: string; - BDisplayName: string; - BProfileImageUrl: string | null; + userId: string; + displayName: string; + profileImageUrl: string | null; + type: "SENT" | "RECEIVED"; } export interface UserProfile { From 0f0a1e98a36306d1307af0efd40c952cc16d5b37 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 10 Jul 2024 17:32:32 -0300 Subject: [PATCH 04/54] feat: ui adjustments --- .../components/sidebar/sidebar-profile.tsx | 3 +- src/renderer/src/pages/user/user-content.tsx | 86 +++++++++++-------- .../user/user-friend-pending-request.tsx | 9 +- src/renderer/src/pages/user/user.css.ts | 24 ++++-- 4 files changed, 70 insertions(+), 52 deletions(-) diff --git a/src/renderer/src/components/sidebar/sidebar-profile.tsx b/src/renderer/src/components/sidebar/sidebar-profile.tsx index 914481b0..432ab9f0 100644 --- a/src/renderer/src/components/sidebar/sidebar-profile.tsx +++ b/src/renderer/src/components/sidebar/sidebar-profile.tsx @@ -40,12 +40,11 @@ export function SidebarProfile() {
{userDetails?.profileImageUrl ? ( {userDetails.displayName} ) : ( - + )}
diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index 9d9b9415..aca7cc60 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -1,4 +1,4 @@ -import { UserGame, UserProfile } from "@types"; +import { UserFriend, UserGame, UserProfile } from "@types"; import cn from "classnames"; import * as styles from "./user.css"; @@ -344,13 +344,15 @@ export function UserContent({ height: "1px", }} /> - + {isMe && ( + + )}
- + {( + [ + { + id: "", + displayName: "Punheta Master 123123123123123123", + profileImageUrl: + "https://cdn.discordapp.com/avatars/1239959140785455295/4aff4b901c7a9f5f814b4379b6cfd58a.webp", + }, + { + id: "", + displayName: "Hydra Launcher", + profileImageUrl: null, + }, + ] as UserFriend[] + ).map((friend) => { + return ( + +

+ {friend.displayName} +

+ + ); + })}
diff --git a/src/renderer/src/pages/user/user-friend-pending-request.tsx b/src/renderer/src/pages/user/user-friend-pending-request.tsx index c21f41aa..99fcb2f7 100644 --- a/src/renderer/src/pages/user/user-friend-pending-request.tsx +++ b/src/renderer/src/pages/user/user-friend-pending-request.tsx @@ -3,7 +3,6 @@ import { PersonIcon, XCircleIcon, } from "@primer/octicons-react"; -import { SPACING_UNIT } from "@renderer/theme.css"; import * as styles from "./user.css"; import cn from "classnames"; @@ -33,11 +32,8 @@ export const UserFriendPendingRequest = ({ type="button" className={cn(styles.friendListItem, styles.profileContentBox)} onClick={() => onClickRequest(userId)} - style={{ - padding: "8px", - }} > -
+
{profileImageUrl ? ( -

{displayName}

+

{displayName}

{isRequestSent ? "Pedido enviado" : "Pedido recebido"}
{isRequestSent ? ( diff --git a/src/renderer/src/pages/user/user.css.ts b/src/renderer/src/pages/user/user.css.ts index 1582af29..19da3caf 100644 --- a/src/renderer/src/pages/user/user.css.ts +++ b/src/renderer/src/pages/user/user.css.ts @@ -35,9 +35,10 @@ export const profileAvatarContainer = style({ zIndex: 1, }); -export const pendingFriendRequestAvatarContainer = style({ - width: "32px", - height: "32px", +export const friendAvatarContainer = style({ + width: "35px", + minWidth: "35px", + height: "35px", borderRadius: "50%", display: "flex", justifyContent: "center", @@ -46,7 +47,15 @@ export const pendingFriendRequestAvatarContainer = style({ overflow: "hidden", border: `solid 1px ${vars.color.border}`, boxShadow: "0px 0px 5px 0px rgba(0, 0, 0, 0.7)", - zIndex: 1, +}); + +export const friendListDisplayName = style({ + fontWeight: "bold", + fontSize: vars.size.body, + textAlign: "left", + overflow: "hidden", + textOverflow: "ellipsis", + whiteSpace: "nowrap", }); export const profileAvatarEditContainer = style({ @@ -173,13 +182,12 @@ export const friendListItem = style({ color: vars.color.body, display: "flex", flexDirection: "row", - gap: `${SPACING_UNIT}px`, + gap: `${SPACING_UNIT + SPACING_UNIT / 2}px`, width: "100%", - height: "48px", + height: "54px", + padding: "0 8px", transition: "all ease 0.2s", cursor: "pointer", - zIndex: "1", - overflow: "hidden", ":hover": { backgroundColor: "rgba(255, 255, 255, 0.15)", }, From 6ff48605da99f1539082b4b84c6e95725f8dfef2 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 10 Jul 2024 17:51:53 -0300 Subject: [PATCH 05/54] feat: show friends from response --- src/renderer/src/pages/user/user-content.tsx | 139 +++++++++---------- 1 file changed, 64 insertions(+), 75 deletions(-) diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index aca7cc60..5d0de1c5 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -1,4 +1,4 @@ -import { UserFriend, UserGame, UserProfile } from "@types"; +import { UserGame, UserProfile } from "@types"; import cn from "classnames"; import * as styles from "./user.css"; @@ -79,7 +79,7 @@ export function UserContent({ }; const handleOnClickFriend = (userId: string) => { - console.log(userId); + navigate(`/user/${userId}`); }; const handleConfirmSignout = async () => { @@ -326,86 +326,75 @@ export function UserContent({
-
-
-

{t("friends")}

+ {(isMe || + (userProfile.friends && userProfile.friends.length > 0)) && ( +
+
+

{t("friends")}

+ +
+ {isMe && ( + + )} +
- {isMe && ( - - )} -
- -
- {( - [ - { - id: "", - displayName: "Punheta Master 123123123123123123", - profileImageUrl: - "https://cdn.discordapp.com/avatars/1239959140785455295/4aff4b901c7a9f5f814b4379b6cfd58a.webp", - }, - { - id: "", - displayName: "Hydra Launcher", - profileImageUrl: null, - }, - ] as UserFriend[] - ).map((friend) => { - return ( - - ); - })} +

+ {friend.displayName} +

+ + ); + })} +
-
+ )}
From b3f87d56629403904e1276d20bf994dc7cccf503 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 10 Jul 2024 18:52:08 -0300 Subject: [PATCH 06/54] feat: get friends requests from api --- src/main/events/index.ts | 1 + .../events/profile/get-friend-requests.ts | 16 ++++++++++ src/preload/index.ts | 1 + src/renderer/src/declaration.d.ts | 2 ++ src/renderer/src/hooks/use-user-details.ts | 5 +++ .../src/pages/user/user-add-friends-modal.tsx | 32 ++++--------------- src/renderer/src/pages/user/user-content.tsx | 24 ++++++++++---- src/renderer/src/pages/user/user.tsx | 5 ++- 8 files changed, 54 insertions(+), 32 deletions(-) create mode 100644 src/main/events/profile/get-friend-requests.ts diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 1b500be9..64f95108 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -43,6 +43,7 @@ import "./auth/sign-out"; import "./auth/open-auth-window"; import "./auth/get-session-hash"; import "./user/get-user"; +import "./profile/get-friend-requests"; import "./profile/get-me"; import "./profile/update-profile"; diff --git a/src/main/events/profile/get-friend-requests.ts b/src/main/events/profile/get-friend-requests.ts new file mode 100644 index 00000000..ba2cf1b5 --- /dev/null +++ b/src/main/events/profile/get-friend-requests.ts @@ -0,0 +1,16 @@ +import { registerEvent } from "../register-event"; +import { HydraApi } from "@main/services"; +import { PendingFriendRequest } from "@types"; + +const getFriendRequests = async ( + _event: Electron.IpcMainInvokeEvent +): Promise => { + try { + const response = await HydraApi.get(`/profile/friend-requests`); + return response.data; + } catch (err) { + return null; + } +}; + +registerEvent("getFriendRequests", getFriendRequests); diff --git a/src/preload/index.ts b/src/preload/index.ts index 0cadbc03..759b0224 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -136,6 +136,7 @@ contextBridge.exposeInMainWorld("electron", { getMe: () => ipcRenderer.invoke("getMe"), updateProfile: (displayName: string, newProfileImagePath: string | null) => ipcRenderer.invoke("updateProfile", displayName, newProfileImagePath), + getFriendRequests: () => ipcRenderer.invoke("getFriendRequests"), /* User */ getUser: (userId: string) => ipcRenderer.invoke("getUser", userId), diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 48fa7aae..0ff9e876 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -14,6 +14,7 @@ import type { RealDebridUser, DownloadSource, UserProfile, + PendingFriendRequest, } from "@types"; import type { DiskSpace } from "check-disk-space"; @@ -132,6 +133,7 @@ declare global { displayName: string, newProfileImagePath: string | null ) => Promise; + getFriendRequests: () => Promise; } interface Window { diff --git a/src/renderer/src/hooks/use-user-details.ts b/src/renderer/src/hooks/use-user-details.ts index 6495ba27..d39c2578 100644 --- a/src/renderer/src/hooks/use-user-details.ts +++ b/src/renderer/src/hooks/use-user-details.ts @@ -82,6 +82,10 @@ export function useUserDetails() { console.log("sending friend request to", userId); }, []); + const fetchPendingRequests = useCallback(async () => { + return window.electron.getFriendRequests(); + }, []); + return { userDetails, fetchUserDetails, @@ -90,6 +94,7 @@ export function useUserDetails() { updateUserDetails, patchUser, sendFriendRequest, + fetchPendingRequests, profileBackground, }; } diff --git a/src/renderer/src/pages/user/user-add-friends-modal.tsx b/src/renderer/src/pages/user/user-add-friends-modal.tsx index cb990e1c..3037eadb 100644 --- a/src/renderer/src/pages/user/user-add-friends-modal.tsx +++ b/src/renderer/src/pages/user/user-add-friends-modal.tsx @@ -1,7 +1,7 @@ import { Button, Modal, TextField } from "@renderer/components"; import { PendingFriendRequest } from "@types"; import { SPACING_UNIT } from "@renderer/theme.css"; -import { useEffect, useState } from "react"; +import { useState } from "react"; import { useToast, useUserDetails } from "@renderer/hooks"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; @@ -10,19 +10,18 @@ import { UserFriendPendingRequest } from "./user-friend-pending-request"; export interface UserAddFriendsModalProps { visible: boolean; onClose: () => void; + pendingRequests: PendingFriendRequest[]; } export const UserAddFriendsModal = ({ visible, onClose, + pendingRequests, }: UserAddFriendsModalProps) => { const { t } = useTranslation("user_profile"); const [friendCode, setFriendCode] = useState(""); const [isAddingFriend, setIsAddingFriend] = useState(false); - const [pendingRequests, setPendingRequests] = useState< - PendingFriendRequest[] - >([]); const navigate = useNavigate(); @@ -37,7 +36,7 @@ export const UserAddFriendsModal = ({ showSuccessToast(t("friend_request_sent")); }) .catch(() => { - showErrorToast("falhaaaa"); + showErrorToast("Não foi possível enviar o pedido de amizade"); }) .finally(() => { setIsAddingFriend(false); @@ -45,29 +44,12 @@ export const UserAddFriendsModal = ({ }; const handleClickFriend = (userId: string) => { - navigate(userId); + navigate(`/user/${userId}`); }; - useEffect(() => { - setPendingRequests([ - { - userId: "abcd1234", - displayName: "Punheta Master 123", - profileImageUrl: - "https://cdn.discordapp.com/avatars/1239959140785455295/4aff4b901c7a9f5f814b4379b6cfd58a.webp", - type: "RECEIVED", - }, - { - userId: "12345678", - displayName: "Deyvis0n", - profileImageUrl: null, - type: "SENT", - }, - ]); - }, []); - const handleClickSeeProfile = () => { - // navigate(`profile/${friendCode}`); + onClose(); + navigate(`/user/${friendCode}`); }; const handleClickCancelFriendRequest = (userId: string) => { diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index 5d0de1c5..7944f0e5 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -1,9 +1,8 @@ -import { UserGame, UserProfile } from "@types"; +import { PendingFriendRequest, UserGame, UserProfile } from "@types"; import cn from "classnames"; - import * as styles from "./user.css"; import { SPACING_UNIT, vars } from "@renderer/theme.css"; -import { useMemo, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import SteamLogo from "@renderer/assets/steam-logo.svg?react"; import { @@ -90,8 +89,18 @@ export function UserContent({ navigate("/"); }; + const [pendingRequests, setPendingRequests] = useState< + PendingFriendRequest[] + >([]); + const isMe = userDetails?.id == userProfile.id; + useEffect(() => { + window.electron.getFriendRequests().then((friendsRequests) => { + setPendingRequests(friendsRequests ?? []); + }); + }, [isMe]); + const profileContentBoxBackground = useMemo(() => { if (profileBackground) return profileBackground; /* TODO: Render background colors for other users */ @@ -114,6 +123,7 @@ export function UserContent({ /> setShowAddFriendsModal(false)} /> @@ -231,9 +241,11 @@ export function UserContent({

{t("no_recent_activity_title")}

-

- {t("no_recent_activity_description")} -

+ {isMe && ( +

+ {t("no_recent_activity_description")} +

+ )} ) : (
{ const [userProfile, setUserProfile] = useState(); const navigate = useNavigate(); + const { showErrorToast } = useToast(); + const dispatch = useAppDispatch(); const getUserProfile = useCallback(() => { @@ -22,6 +24,7 @@ export const User = () => { dispatch(setHeaderTitle(userProfile.displayName)); setUserProfile(userProfile); } else { + showErrorToast("Usuário não encontrado"); navigate(-1); } }); From ef0699dbea91a7004c81a5eadfb834b224f9eb5d Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 10 Jul 2024 19:52:57 -0300 Subject: [PATCH 07/54] feat: refactor friends requests --- src/main/events/index.ts | 1 + .../events/profile/get-friend-requests.ts | 4 ++-- .../events/profile/update-friend-request.ts | 19 ++++++++++++++++++ src/preload/index.ts | 3 +++ src/renderer/src/declaration.d.ts | 9 +++++++-- .../src/features/user-details-slice.ts | 12 +++++++++-- src/renderer/src/hooks/use-user-details.ts | 20 ++++++++++++------- .../src/pages/user/user-add-friends-modal.tsx | 16 ++++++++------- src/renderer/src/pages/user/user-content.tsx | 14 ++++--------- ...ng-request.tsx => user-friend-request.tsx} | 6 +++--- src/types/index.ts | 4 +++- 11 files changed, 74 insertions(+), 34 deletions(-) create mode 100644 src/main/events/profile/update-friend-request.ts rename src/renderer/src/pages/user/{user-friend-pending-request.tsx => user-friend-request.tsx} (94%) diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 64f95108..ab82eefd 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -45,6 +45,7 @@ import "./auth/get-session-hash"; import "./user/get-user"; import "./profile/get-friend-requests"; import "./profile/get-me"; +import "./profile/update-friend-request"; import "./profile/update-profile"; ipcMain.handle("ping", () => "pong"); diff --git a/src/main/events/profile/get-friend-requests.ts b/src/main/events/profile/get-friend-requests.ts index ba2cf1b5..0e75efba 100644 --- a/src/main/events/profile/get-friend-requests.ts +++ b/src/main/events/profile/get-friend-requests.ts @@ -1,10 +1,10 @@ import { registerEvent } from "../register-event"; import { HydraApi } from "@main/services"; -import { PendingFriendRequest } from "@types"; +import { FriendRequest } from "@types"; const getFriendRequests = async ( _event: Electron.IpcMainInvokeEvent -): Promise => { +): Promise => { try { const response = await HydraApi.get(`/profile/friend-requests`); return response.data; diff --git a/src/main/events/profile/update-friend-request.ts b/src/main/events/profile/update-friend-request.ts new file mode 100644 index 00000000..24929544 --- /dev/null +++ b/src/main/events/profile/update-friend-request.ts @@ -0,0 +1,19 @@ +import { registerEvent } from "../register-event"; +import { HydraApi } from "@main/services"; +import { FriendRequestAction } from "@types"; + +const updateFriendRequest = async ( + _event: Electron.IpcMainInvokeEvent, + userId: string, + action: FriendRequestAction +) => { + if (action == "CANCEL") { + return HydraApi.delete(`/profile/friend-requests/${userId}`); + } + + return HydraApi.patch(`/profile/friend-requests/${userId}`, { + requestState: action, + }); +}; + +registerEvent("updateFriendRequest", updateFriendRequest); diff --git a/src/preload/index.ts b/src/preload/index.ts index 759b0224..26601ebd 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -9,6 +9,7 @@ import type { AppUpdaterEvent, StartGameDownloadPayload, GameRunning, + FriendRequestAction, } from "@types"; contextBridge.exposeInMainWorld("electron", { @@ -137,6 +138,8 @@ contextBridge.exposeInMainWorld("electron", { updateProfile: (displayName: string, newProfileImagePath: string | null) => ipcRenderer.invoke("updateProfile", displayName, newProfileImagePath), getFriendRequests: () => ipcRenderer.invoke("getFriendRequests"), + updateFriendRequest: (userId: string, action: FriendRequestAction) => + ipcRenderer.invoke("updateFriendRequest", userId, action), /* User */ getUser: (userId: string) => ipcRenderer.invoke("getUser", userId), diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 0ff9e876..747f8be4 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -14,7 +14,8 @@ import type { RealDebridUser, DownloadSource, UserProfile, - PendingFriendRequest, + FriendRequest, + FriendRequestAction, } from "@types"; import type { DiskSpace } from "check-disk-space"; @@ -133,7 +134,11 @@ declare global { displayName: string, newProfileImagePath: string | null ) => Promise; - getFriendRequests: () => Promise; + getFriendRequests: () => Promise; + updateFriendRequest: ( + userId: string, + action: FriendRequestAction + ) => Promise; } interface Window { diff --git a/src/renderer/src/features/user-details-slice.ts b/src/renderer/src/features/user-details-slice.ts index 0cc395b0..1912c810 100644 --- a/src/renderer/src/features/user-details-slice.ts +++ b/src/renderer/src/features/user-details-slice.ts @@ -1,14 +1,16 @@ import { PayloadAction, createSlice } from "@reduxjs/toolkit"; -import type { UserDetails } from "@types"; +import type { FriendRequest, UserDetails } from "@types"; export interface UserDetailsState { userDetails: UserDetails | null; profileBackground: null | string; + friendRequests: FriendRequest[] | null; } const initialState: UserDetailsState = { userDetails: null, profileBackground: null, + friendRequests: null, }; export const userDetailsSlice = createSlice({ @@ -21,8 +23,14 @@ export const userDetailsSlice = createSlice({ setProfileBackground: (state, action: PayloadAction) => { state.profileBackground = action.payload; }, + setFriendRequests: ( + state, + action: PayloadAction + ) => { + state.friendRequests = action.payload; + }, }, }); -export const { setUserDetails, setProfileBackground } = +export const { setUserDetails, setProfileBackground, setFriendRequests } = userDetailsSlice.actions; diff --git a/src/renderer/src/hooks/use-user-details.ts b/src/renderer/src/hooks/use-user-details.ts index d39c2578..bdbfcf2c 100644 --- a/src/renderer/src/hooks/use-user-details.ts +++ b/src/renderer/src/hooks/use-user-details.ts @@ -2,14 +2,18 @@ import { useCallback } from "react"; import { average } from "color.js"; import { useAppDispatch, useAppSelector } from "./redux"; -import { setProfileBackground, setUserDetails } from "@renderer/features"; +import { + setProfileBackground, + setUserDetails, + setFriendRequests, +} from "@renderer/features"; import { darkenColor } from "@renderer/helpers"; import { UserDetails } from "@types"; export function useUserDetails() { const dispatch = useAppDispatch(); - const { userDetails, profileBackground } = useAppSelector( + const { userDetails, profileBackground, friendRequests } = useAppSelector( (state) => state.userDetails ); @@ -82,19 +86,21 @@ export function useUserDetails() { console.log("sending friend request to", userId); }, []); - const fetchPendingRequests = useCallback(async () => { - return window.electron.getFriendRequests(); - }, []); + const updateFriendRequests = useCallback(async () => { + const friendRequests = await window.electron.getFriendRequests(); + dispatch(setFriendRequests(friendRequests)); + }, [dispatch]); return { userDetails, + profileBackground, + friendRequests, fetchUserDetails, signOut, clearUserDetails, updateUserDetails, patchUser, sendFriendRequest, - fetchPendingRequests, - profileBackground, + updateFriendRequests, }; } diff --git a/src/renderer/src/pages/user/user-add-friends-modal.tsx b/src/renderer/src/pages/user/user-add-friends-modal.tsx index 3037eadb..c5a2d9ec 100644 --- a/src/renderer/src/pages/user/user-add-friends-modal.tsx +++ b/src/renderer/src/pages/user/user-add-friends-modal.tsx @@ -1,22 +1,19 @@ import { Button, Modal, TextField } from "@renderer/components"; -import { PendingFriendRequest } from "@types"; import { SPACING_UNIT } from "@renderer/theme.css"; import { useState } from "react"; import { useToast, useUserDetails } from "@renderer/hooks"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; -import { UserFriendPendingRequest } from "./user-friend-pending-request"; +import { UserFriendRequest } from "./user-friend-request"; export interface UserAddFriendsModalProps { visible: boolean; onClose: () => void; - pendingRequests: PendingFriendRequest[]; } export const UserAddFriendsModal = ({ visible, onClose, - pendingRequests, }: UserAddFriendsModalProps) => { const { t } = useTranslation("user_profile"); @@ -25,7 +22,8 @@ export const UserAddFriendsModal = ({ const navigate = useNavigate(); - const { sendFriendRequest } = useUserDetails(); + const { sendFriendRequest, updateFriendRequests, friendRequests } = + useUserDetails(); const { showSuccessToast, showErrorToast } = useToast(); @@ -33,6 +31,7 @@ export const UserAddFriendsModal = ({ setIsAddingFriend(true); sendFriendRequest(friendCode) .then(() => { + updateFriendRequests(); showSuccessToast(t("friend_request_sent")); }) .catch(() => { @@ -54,14 +53,17 @@ export const UserAddFriendsModal = ({ const handleClickCancelFriendRequest = (userId: string) => { console.log(userId); + updateFriendRequests(); }; const handleClickAcceptFriendRequest = (userId: string) => { console.log(userId); + updateFriendRequests(); }; const handleClickRefuseFriendRequest = (userId: string) => { console.log(userId); + updateFriendRequests(); }; const resetModal = () => { @@ -131,9 +133,9 @@ export const UserAddFriendsModal = ({ }} >

Pendentes

- {pendingRequests.map((request) => { + {friendRequests?.map((request) => { return ( - ([]); - const isMe = userDetails?.id == userProfile.id; useEffect(() => { - window.electron.getFriendRequests().then((friendsRequests) => { - setPendingRequests(friendsRequests ?? []); - }); + if (isMe) updateFriendRequests(); }, [isMe]); const profileContentBoxBackground = useMemo(() => { @@ -123,7 +118,6 @@ export function UserContent({ /> setShowAddFriendsModal(false)} /> diff --git a/src/renderer/src/pages/user/user-friend-pending-request.tsx b/src/renderer/src/pages/user/user-friend-request.tsx similarity index 94% rename from src/renderer/src/pages/user/user-friend-pending-request.tsx rename to src/renderer/src/pages/user/user-friend-request.tsx index 99fcb2f7..1348cc2b 100644 --- a/src/renderer/src/pages/user/user-friend-pending-request.tsx +++ b/src/renderer/src/pages/user/user-friend-request.tsx @@ -6,7 +6,7 @@ import { import * as styles from "./user.css"; import cn from "classnames"; -export interface UserFriendPendingRequestProps { +export interface UserFriendRequestProps { userId: string; profileImageUrl: string | null; displayName: string; @@ -17,7 +17,7 @@ export interface UserFriendPendingRequestProps { onClickRequest: (userId: string) => void; } -export const UserFriendPendingRequest = ({ +export const UserFriendRequest = ({ userId, profileImageUrl, displayName, @@ -26,7 +26,7 @@ export const UserFriendPendingRequest = ({ onClickAcceptRequest, onClickRefuseRequest, onClickRequest, -}: UserFriendPendingRequestProps) => { +}: UserFriendRequestProps) => { return ( @@ -67,13 +67,13 @@ export const UserFriendRequest = ({ <> diff --git a/src/types/index.ts b/src/types/index.ts index 6768a840..3046a727 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -278,7 +278,7 @@ export interface UserFriend { } export interface FriendRequest { - userId: string; + id: string; displayName: string; profileImageUrl: string | null; type: "SENT" | "RECEIVED"; From 6f70b529a224510fab91064d029dd9b651ce1d30 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 10 Jul 2024 21:35:39 -0300 Subject: [PATCH 09/54] feat: refactor hydra api --- .../events/profile/get-friend-requests.ts | 7 +------ src/main/events/profile/get-me.ts | 4 +--- src/main/events/profile/update-profile.ts | 6 +++--- src/main/events/user/get-user.ts | 3 +-- src/main/services/hydra-api.ts | 5 +++++ src/main/services/library-sync/create-game.ts | 6 +----- .../library-sync/merge-with-remote-games.ts | 2 +- src/renderer/src/hooks/use-user-details.ts | 18 ++++++++++-------- .../src/pages/user/user-add-friends-modal.tsx | 19 ++++--------------- 9 files changed, 27 insertions(+), 43 deletions(-) diff --git a/src/main/events/profile/get-friend-requests.ts b/src/main/events/profile/get-friend-requests.ts index 0e75efba..2c04b865 100644 --- a/src/main/events/profile/get-friend-requests.ts +++ b/src/main/events/profile/get-friend-requests.ts @@ -5,12 +5,7 @@ import { FriendRequest } from "@types"; const getFriendRequests = async ( _event: Electron.IpcMainInvokeEvent ): Promise => { - try { - const response = await HydraApi.get(`/profile/friend-requests`); - return response.data; - } catch (err) { - return null; - } + return HydraApi.get(`/profile/friend-requests`).catch(() => null); }; registerEvent("getFriendRequests", getFriendRequests); diff --git a/src/main/events/profile/get-me.ts b/src/main/events/profile/get-me.ts index 83463680..6e451134 100644 --- a/src/main/events/profile/get-me.ts +++ b/src/main/events/profile/get-me.ts @@ -9,9 +9,7 @@ const getMe = async ( _event: Electron.IpcMainInvokeEvent ): Promise => { return HydraApi.get(`/profile/me`) - .then((response) => { - const me = response.data; - + .then((me) => { userAuthRepository.upsert( { id: 1, diff --git a/src/main/events/profile/update-profile.ts b/src/main/events/profile/update-profile.ts index fe79d345..e6671992 100644 --- a/src/main/events/profile/update-profile.ts +++ b/src/main/events/profile/update-profile.ts @@ -29,7 +29,7 @@ const updateProfile = async ( ) => { if (!newProfileImagePath) { return patchUserProfile(displayName).then( - (response) => response.data as UserProfile + (response) => response as UserProfile ); } @@ -42,7 +42,7 @@ const updateProfile = async ( imageLength: fileSizeInBytes, }) .then(async (preSignedResponse) => { - const { presignedUrl, profileImageUrl } = preSignedResponse.data; + const { presignedUrl, profileImageUrl } = preSignedResponse; const mimeType = await fileTypeFromFile(newProfileImagePath); @@ -56,7 +56,7 @@ const updateProfile = async ( .catch(() => undefined); return patchUserProfile(displayName, profileImageUrl).then( - (response) => response.data as UserProfile + (response) => response as UserProfile ); }; diff --git a/src/main/events/user/get-user.ts b/src/main/events/user/get-user.ts index 596df084..7b4c0aa8 100644 --- a/src/main/events/user/get-user.ts +++ b/src/main/events/user/get-user.ts @@ -10,8 +10,7 @@ const getUser = async ( userId: string ): Promise => { try { - const response = await HydraApi.get(`/user/${userId}`); - const profile = response.data; + const profile = await HydraApi.get(`/user/${userId}`); const recentGames = await Promise.all( profile.recentGames.map(async (game) => { diff --git a/src/main/services/hydra-api.ts b/src/main/services/hydra-api.ts index 1abae98c..f74403ce 100644 --- a/src/main/services/hydra-api.ts +++ b/src/main/services/hydra-api.ts @@ -190,6 +190,7 @@ export class HydraApi { await this.revalidateAccessTokenIfExpired(); return this.instance .get(url, this.getAxiosConfig()) + .then((response) => response.data) .catch(this.handleUnauthorizedError); } @@ -199,6 +200,7 @@ export class HydraApi { await this.revalidateAccessTokenIfExpired(); return this.instance .post(url, data, this.getAxiosConfig()) + .then((response) => response.data) .catch(this.handleUnauthorizedError); } @@ -208,6 +210,7 @@ export class HydraApi { await this.revalidateAccessTokenIfExpired(); return this.instance .put(url, data, this.getAxiosConfig()) + .then((response) => response.data) .catch(this.handleUnauthorizedError); } @@ -217,6 +220,7 @@ export class HydraApi { await this.revalidateAccessTokenIfExpired(); return this.instance .patch(url, data, this.getAxiosConfig()) + .then((response) => response.data) .catch(this.handleUnauthorizedError); } @@ -226,6 +230,7 @@ export class HydraApi { await this.revalidateAccessTokenIfExpired(); return this.instance .delete(url, this.getAxiosConfig()) + .then((response) => response.data) .catch(this.handleUnauthorizedError); } } diff --git a/src/main/services/library-sync/create-game.ts b/src/main/services/library-sync/create-game.ts index c0e8b1f8..b66a1897 100644 --- a/src/main/services/library-sync/create-game.ts +++ b/src/main/services/library-sync/create-game.ts @@ -10,11 +10,7 @@ export const createGame = async (game: Game) => { lastTimePlayed: game.lastTimePlayed, }) .then((response) => { - const { - id: remoteId, - playTimeInMilliseconds, - lastTimePlayed, - } = response.data; + const { id: remoteId, playTimeInMilliseconds, lastTimePlayed } = response; gameRepository.update( { objectID: game.objectID }, diff --git a/src/main/services/library-sync/merge-with-remote-games.ts b/src/main/services/library-sync/merge-with-remote-games.ts index 2162ea58..2a6b5bb5 100644 --- a/src/main/services/library-sync/merge-with-remote-games.ts +++ b/src/main/services/library-sync/merge-with-remote-games.ts @@ -6,7 +6,7 @@ import { getSteamAppAsset } from "@main/helpers"; export const mergeWithRemoteGames = async () => { return HydraApi.get("/games") .then(async (response) => { - for (const game of response.data) { + for (const game of response) { const localGame = await gameRepository.findOne({ where: { objectID: game.objectId, diff --git a/src/renderer/src/hooks/use-user-details.ts b/src/renderer/src/hooks/use-user-details.ts index ecf66276..ceb2df3f 100644 --- a/src/renderer/src/hooks/use-user-details.ts +++ b/src/renderer/src/hooks/use-user-details.ts @@ -82,23 +82,25 @@ export function useUserDetails() { [updateUserDetails] ); - const sendFriendRequest = useCallback(async (userId: string) => { - return window.electron.sendFriendRequest(userId); - }, []); - const updateFriendRequests = useCallback(async () => { const friendRequests = await window.electron.getFriendRequests(); dispatch(setFriendRequests(friendRequests)); }, [dispatch]); + const sendFriendRequest = useCallback( + async (userId: string) => { + return window.electron + .sendFriendRequest(userId) + .then(() => updateFriendRequests()); + }, + [updateFriendRequests] + ); + const updateFriendRequestState = useCallback( async (userId: string, action: FriendRequestAction) => { return window.electron .updateFriendRequest(userId, action) - .then(() => {}) - .catch(() => { - console.log("falha no updateFriendsRequestState"); - }); + .then(() => updateFriendRequests()); }, [updateFriendRequests] ); diff --git a/src/renderer/src/pages/user/user-add-friends-modal.tsx b/src/renderer/src/pages/user/user-add-friends-modal.tsx index a5462c5b..b61f293c 100644 --- a/src/renderer/src/pages/user/user-add-friends-modal.tsx +++ b/src/renderer/src/pages/user/user-add-friends-modal.tsx @@ -22,12 +22,8 @@ export const UserAddFriendsModal = ({ const navigate = useNavigate(); - const { - sendFriendRequest, - updateFriendRequests, - updateFriendRequestState, - friendRequests, - } = useUserDetails(); + const { sendFriendRequest, updateFriendRequestState, friendRequests } = + useUserDetails(); const { showSuccessToast, showErrorToast } = useToast(); @@ -35,7 +31,6 @@ export const UserAddFriendsModal = ({ setIsAddingFriend(true); sendFriendRequest(friendCode) .then(() => { - updateFriendRequests(); showSuccessToast(t("friend_request_sent")); }) .catch(() => { @@ -47,13 +42,11 @@ export const UserAddFriendsModal = ({ }; const handleClickFriend = (userId: string) => { - console.log("click friend"); - onClose(); - navigate(`/user/${userId}`); + //onClose(); + //navigate(`/user/${userId}`); }; const handleClickSeeProfile = () => { - console.log("click see profile"); onClose(); navigate(`/user/${friendCode}`); }; @@ -62,8 +55,6 @@ export const UserAddFriendsModal = ({ event: React.MouseEvent, userId: string ) => { - console.log("cancel"); - event.preventDefault(); updateFriendRequestState(userId, "CANCEL") .then(() => { console.log("sucesso"); @@ -77,8 +68,6 @@ export const UserAddFriendsModal = ({ event: React.MouseEvent, userId: string ) => { - console.log("accept friend request"); - event.preventDefault(); updateFriendRequestState(userId, "ACCEPTED").catch(() => { showErrorToast("Falha ao aceitar convite"); }); From 22b66149b37632f819ea7f41ebc3c66ed779fec9 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 10 Jul 2024 22:04:28 -0300 Subject: [PATCH 10/54] fix: buttons on friend request item --- .../src/pages/user/user-add-friends-modal.tsx | 172 ++++++++---------- src/renderer/src/pages/user/user-content.tsx | 2 +- .../src/pages/user/user-friend-request.tsx | 101 +++++----- src/renderer/src/pages/user/user.css.ts | 22 ++- 4 files changed, 143 insertions(+), 154 deletions(-) diff --git a/src/renderer/src/pages/user/user-add-friends-modal.tsx b/src/renderer/src/pages/user/user-add-friends-modal.tsx index b61f293c..d980df43 100644 --- a/src/renderer/src/pages/user/user-add-friends-modal.tsx +++ b/src/renderer/src/pages/user/user-add-friends-modal.tsx @@ -25,13 +25,13 @@ export const UserAddFriendsModal = ({ const { sendFriendRequest, updateFriendRequestState, friendRequests } = useUserDetails(); - const { showSuccessToast, showErrorToast } = useToast(); + const { showErrorToast } = useToast(); const handleClickAddFriend = () => { setIsAddingFriend(true); sendFriendRequest(friendCode) .then(() => { - showSuccessToast(t("friend_request_sent")); + setFriendCode(""); }) .catch(() => { showErrorToast("Não foi possível enviar o pedido de amizade"); @@ -41,133 +41,109 @@ export const UserAddFriendsModal = ({ }); }; - const handleClickFriend = (userId: string) => { - //onClose(); - //navigate(`/user/${userId}`); + const handleClickRequest = (userId: string) => { + resetAndClose(); + navigate(`/user/${userId}`); }; const handleClickSeeProfile = () => { - onClose(); + resetAndClose(); navigate(`/user/${friendCode}`); }; - const handleClickCancelFriendRequest = ( - event: React.MouseEvent, - userId: string - ) => { - updateFriendRequestState(userId, "CANCEL") - .then(() => { - console.log("sucesso"); - }) - .catch(() => { - showErrorToast("Falha ao cancelar convite"); - }); + const handleClickCancelFriendRequest = (userId: string) => { + updateFriendRequestState(userId, "CANCEL").catch(() => { + showErrorToast("Falha ao cancelar convite"); + }); }; - const handleClickAcceptFriendRequest = ( - event: React.MouseEvent, - userId: string - ) => { + const handleClickAcceptFriendRequest = (userId: string) => { updateFriendRequestState(userId, "ACCEPTED").catch(() => { showErrorToast("Falha ao aceitar convite"); }); }; - const handleClickRefuseFriendRequest = ( - event: React.MouseEvent, - userId: string - ) => { - event.preventDefault(); + const handleClickRefuseFriendRequest = (userId: string) => { updateFriendRequestState(userId, "REFUSED").catch(() => { showErrorToast("Falha ao recusar convite"); }); }; - const resetModal = () => { + const resetAndClose = () => { setFriendCode(""); - }; - - const cleanFormAndClose = () => { - resetModal(); onClose(); }; return ( - <> - +
+ setFriendCode(e.target.value)} + /> + + +
+ +
-
- setFriendCode(e.target.value)} - /> - - -
- -
-

Pendentes

- {friendRequests?.map((request) => { - return ( - - ); - })} -
+

Pendentes

+ {friendRequests?.map((request) => { + return ( + + ); + })}
- - +
+
); }; diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index 2915a82a..d8019159 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -375,7 +375,7 @@ export function UserContent({
- {isRequestSent ? ( - - ) : ( - <> - +

{displayName}

+ {isRequestSent ? "Pedido enviado" : "Pedido recebido"} + + + +
+ {isRequestSent ? ( - - )} - + ) : ( + <> + + + + )} +
+ ); }; diff --git a/src/renderer/src/pages/user/user.css.ts b/src/renderer/src/pages/user/user.css.ts index 19da3caf..8a56bd7d 100644 --- a/src/renderer/src/pages/user/user.css.ts +++ b/src/renderer/src/pages/user/user.css.ts @@ -178,21 +178,29 @@ export const gameListItem = style({ }, }); -export const friendListItem = style({ - color: vars.color.body, - display: "flex", - flexDirection: "row", - gap: `${SPACING_UNIT + SPACING_UNIT / 2}px`, +export const friendListContainer = style({ width: "100%", height: "54px", - padding: "0 8px", transition: "all ease 0.2s", - cursor: "pointer", + position: "relative", ":hover": { backgroundColor: "rgba(255, 255, 255, 0.15)", }, }); +export const friendListButton = style({ + display: "flex", + alignItems: "center", + position: "absolute", + cursor: "pointer", + height: "100%", + width: "100%", + flexDirection: "row", + color: vars.color.body, + gap: `${SPACING_UNIT + SPACING_UNIT / 2}px`, + padding: "0 8px", +}); + export const gameInformation = style({ display: "flex", flexDirection: "column", From cb93fbcb720db55d1daa23559ced76069d86bb8d Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 10 Jul 2024 22:13:42 -0300 Subject: [PATCH 11/54] feat: add buttons gap --- src/renderer/src/pages/user/user-friend-request.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/pages/user/user-friend-request.tsx b/src/renderer/src/pages/user/user-friend-request.tsx index aebff56e..e37004fc 100644 --- a/src/renderer/src/pages/user/user-friend-request.tsx +++ b/src/renderer/src/pages/user/user-friend-request.tsx @@ -5,6 +5,7 @@ import { } from "@primer/octicons-react"; import * as styles from "./user.css"; import cn from "classnames"; +import { SPACING_UNIT } from "@renderer/theme.css"; export interface UserFriendRequestProps { userId: string; @@ -59,7 +60,14 @@ export const UserFriendRequest = ({ -
+
{isRequestSent ? ( + + {userDetails && !gameRunning && ( +
+ +
+ )} +
); } diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index d8019159..503561a9 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -355,10 +355,11 @@ export function UserContent({ {isMe && ( )}
diff --git a/yarn.lock b/yarn.lock index 00172038..e6b91b9e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2433,6 +2433,13 @@ modern-ahocorasick "^1.0.0" picocolors "^1.0.0" +"@vanilla-extract/dynamic@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@vanilla-extract/dynamic/-/dynamic-2.1.1.tgz#bc93a577b127a7dcb6f254973d13a863029a7faf" + integrity sha512-iqf736036ujEIKsIq28UsBEMaLC2vR2DhwKyrG3NDb/fRy9qL9FKl1TqTtBV4daU30Uh3saeik4vRzN8bzQMbw== + dependencies: + "@vanilla-extract/private" "^1.0.5" + "@vanilla-extract/integration@^7.1.3": version "7.1.4" resolved "https://registry.npmjs.org/@vanilla-extract/integration/-/integration-7.1.4.tgz" @@ -2456,6 +2463,11 @@ resolved "https://registry.npmjs.org/@vanilla-extract/private/-/private-1.0.4.tgz" integrity sha512-8FGD6AejeC/nXcblgNCM5rnZb9KXa4WNkR03HCWtdJBpANjTgjHEglNLFnhuvdQ78tC6afaxBPI+g7F2NX3tgg== +"@vanilla-extract/private@^1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@vanilla-extract/private/-/private-1.0.5.tgz#8c08ac4851f4cc89a3dcdb858d8938e69b1481c4" + integrity sha512-6YXeOEKYTA3UV+RC8DeAjFk+/okoNz/h88R+McnzA2zpaVqTR/Ep+vszkWYlGBcMNO7vEkqbq5nT/JMMvhi+tw== + "@vanilla-extract/recipes@^0.5.2": version "0.5.2" resolved "https://registry.npmjs.org/@vanilla-extract/recipes/-/recipes-0.5.2.tgz" From 5aec973882b9120e4daf9785a29dec02b0a21832 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Tue, 16 Jul 2024 12:39:16 -0300 Subject: [PATCH 15/54] feat: show friend request modal when click on sidebar --- src/renderer/src/app.tsx | 19 +++++++- .../components/sidebar/sidebar-profile.css.ts | 2 +- .../components/sidebar/sidebar-profile.tsx | 15 +++--- .../src/features/user-details-slice.ts | 13 ++++- src/renderer/src/hooks/use-user-details.ts | 22 +++++++-- .../user-friend-request-modal.tsx} | 2 +- .../user-friend-request.tsx | 2 +- src/renderer/src/pages/user/user-content.tsx | 47 ++++++++----------- src/renderer/src/pages/user/user.css.ts | 11 +++++ 9 files changed, 91 insertions(+), 42 deletions(-) rename src/renderer/src/pages/{user/user-add-friends-modal.tsx => shared-modals/user-friend-request-modal.tsx} (99%) rename src/renderer/src/pages/{user => shared-modals}/user-friend-request.tsx (98%) diff --git a/src/renderer/src/app.tsx b/src/renderer/src/app.tsx index afce9622..a91e8881 100644 --- a/src/renderer/src/app.tsx +++ b/src/renderer/src/app.tsx @@ -25,6 +25,7 @@ import { setGameRunning, } from "@renderer/features"; import { useTranslation } from "react-i18next"; +import { UserFriendRequestModal } from "./pages/shared-modals/user-friend-request-modal"; export interface AppProps { children: React.ReactNode; @@ -38,6 +39,13 @@ export function App() { const { clearDownload, setLastPacket } = useDownload(); + const { + userDetails, + showFriendRequestsModal, + setShowFriendRequestModal, + updateFriendRequests, + } = useUserDetails(); + const { fetchUserDetails, updateUserDetails, clearUserDetails } = useUserDetails(); @@ -94,7 +102,10 @@ export function App() { } fetchUserDetails().then((response) => { - if (response) updateUserDetails(response); + if (response) { + updateUserDetails(response); + updateFriendRequests(); + } }); }, [fetchUserDetails, updateUserDetails, dispatch]); @@ -102,6 +113,7 @@ export function App() { fetchUserDetails().then((response) => { if (response) { updateUserDetails(response); + updateFriendRequests(); showSuccessToast(t("successfully_signed_in")); } }); @@ -206,6 +218,11 @@ export function App() { onClose={handleToastClose} /> + setShowFriendRequestModal(false)} + /> +
diff --git a/src/renderer/src/components/sidebar/sidebar-profile.css.ts b/src/renderer/src/components/sidebar/sidebar-profile.css.ts index 9fcc64c2..ba29c850 100644 --- a/src/renderer/src/components/sidebar/sidebar-profile.css.ts +++ b/src/renderer/src/components/sidebar/sidebar-profile.css.ts @@ -93,6 +93,6 @@ export const friendRequestButton = style({ width: "40px", height: "40px", ":hover": { - color: vars.color.body, + color: vars.color.muted, }, }); diff --git a/src/renderer/src/components/sidebar/sidebar-profile.tsx b/src/renderer/src/components/sidebar/sidebar-profile.tsx index d4414dec..33779c0f 100644 --- a/src/renderer/src/components/sidebar/sidebar-profile.tsx +++ b/src/renderer/src/components/sidebar/sidebar-profile.tsx @@ -12,7 +12,12 @@ export function SidebarProfile() { const { t } = useTranslation("sidebar"); - const { userDetails, profileBackground } = useUserDetails(); + const { + userDetails, + profileBackground, + friendRequests, + setShowFriendRequestModal, + } = useUserDetails(); const { gameRunning } = useAppSelector((state) => state.gameRunning); @@ -77,17 +82,15 @@ export function SidebarProfile() { )} - {userDetails && !gameRunning && ( + {userDetails && friendRequests?.length && !gameRunning && (
)} diff --git a/src/renderer/src/features/user-details-slice.ts b/src/renderer/src/features/user-details-slice.ts index 1912c810..04b716fd 100644 --- a/src/renderer/src/features/user-details-slice.ts +++ b/src/renderer/src/features/user-details-slice.ts @@ -5,12 +5,14 @@ export interface UserDetailsState { userDetails: UserDetails | null; profileBackground: null | string; friendRequests: FriendRequest[] | null; + showFriendRequestsModal: boolean; } const initialState: UserDetailsState = { userDetails: null, profileBackground: null, friendRequests: null, + showFriendRequestsModal: false, }; export const userDetailsSlice = createSlice({ @@ -29,8 +31,15 @@ export const userDetailsSlice = createSlice({ ) => { state.friendRequests = action.payload; }, + setShowFriendRequestsModal: (state, action: PayloadAction) => { + state.showFriendRequestsModal = action.payload; + }, }, }); -export const { setUserDetails, setProfileBackground, setFriendRequests } = - userDetailsSlice.actions; +export const { + setUserDetails, + setProfileBackground, + setFriendRequests, + setShowFriendRequestsModal, +} = userDetailsSlice.actions; diff --git a/src/renderer/src/hooks/use-user-details.ts b/src/renderer/src/hooks/use-user-details.ts index ceb2df3f..da4d51b5 100644 --- a/src/renderer/src/hooks/use-user-details.ts +++ b/src/renderer/src/hooks/use-user-details.ts @@ -6,6 +6,7 @@ import { setProfileBackground, setUserDetails, setFriendRequests, + setShowFriendRequestsModal, } from "@renderer/features"; import { darkenColor } from "@renderer/helpers"; import { FriendRequestAction, UserDetails } from "@types"; @@ -13,9 +14,12 @@ import { FriendRequestAction, UserDetails } from "@types"; export function useUserDetails() { const dispatch = useAppDispatch(); - const { userDetails, profileBackground, friendRequests } = useAppSelector( - (state) => state.userDetails - ); + const { + userDetails, + profileBackground, + friendRequests, + showFriendRequestsModal, + } = useAppSelector((state) => state.userDetails); const clearUserDetails = useCallback(async () => { dispatch(setUserDetails(null)); @@ -87,6 +91,16 @@ export function useUserDetails() { dispatch(setFriendRequests(friendRequests)); }, [dispatch]); + const setShowFriendRequestModal = useCallback( + (showModal: boolean) => { + dispatch(setShowFriendRequestsModal(showModal)); + if (showModal) { + updateFriendRequests(); + } + }, + [dispatch] + ); + const sendFriendRequest = useCallback( async (userId: string) => { return window.electron @@ -109,6 +123,7 @@ export function useUserDetails() { userDetails, profileBackground, friendRequests, + showFriendRequestsModal, fetchUserDetails, signOut, clearUserDetails, @@ -117,5 +132,6 @@ export function useUserDetails() { sendFriendRequest, updateFriendRequests, updateFriendRequestState, + setShowFriendRequestModal, }; } diff --git a/src/renderer/src/pages/user/user-add-friends-modal.tsx b/src/renderer/src/pages/shared-modals/user-friend-request-modal.tsx similarity index 99% rename from src/renderer/src/pages/user/user-add-friends-modal.tsx rename to src/renderer/src/pages/shared-modals/user-friend-request-modal.tsx index d980df43..9d8dbe6f 100644 --- a/src/renderer/src/pages/user/user-add-friends-modal.tsx +++ b/src/renderer/src/pages/shared-modals/user-friend-request-modal.tsx @@ -11,7 +11,7 @@ export interface UserAddFriendsModalProps { onClose: () => void; } -export const UserAddFriendsModal = ({ +export const UserFriendRequestModal = ({ visible, onClose, }: UserAddFriendsModalProps) => { diff --git a/src/renderer/src/pages/user/user-friend-request.tsx b/src/renderer/src/pages/shared-modals/user-friend-request.tsx similarity index 98% rename from src/renderer/src/pages/user/user-friend-request.tsx rename to src/renderer/src/pages/shared-modals/user-friend-request.tsx index e37004fc..e79a8369 100644 --- a/src/renderer/src/pages/user/user-friend-request.tsx +++ b/src/renderer/src/pages/shared-modals/user-friend-request.tsx @@ -3,7 +3,7 @@ import { PersonIcon, XCircleIcon, } from "@primer/octicons-react"; -import * as styles from "./user.css"; +import * as styles from "../user/user.css"; import cn from "classnames"; import { SPACING_UNIT } from "@renderer/theme.css"; diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index 503561a9..e1b0a8a2 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -16,12 +16,13 @@ import { buildGameDetailsPath, steamUrlBuilder } from "@renderer/helpers"; import { PersonAddIcon, PersonIcon, + PlusCircleIcon, + PlusIcon, TelescopeIcon, } from "@primer/octicons-react"; import { Button, Link } from "@renderer/components"; import { UserEditProfileModal } from "./user-edit-modal"; import { UserSignOutModal } from "./user-signout-modal"; -import { UserAddFriendsModal } from "./user-add-friends-modal"; const MAX_MINUTES_TO_SHOW_IN_PLAYTIME = 120; @@ -36,13 +37,17 @@ export function UserContent({ }: ProfileContentProps) { const { t, i18n } = useTranslation("user_profile"); - const { userDetails, profileBackground, signOut, updateFriendRequests } = - useUserDetails(); + const { + userDetails, + profileBackground, + signOut, + updateFriendRequests, + setShowFriendRequestModal, + } = useUserDetails(); const { showSuccessToast } = useToast(); const [showEditProfileModal, setShowEditProfileModal] = useState(false); const [showSignOutModal, setShowSignOutModal] = useState(false); - const [showAddFriendsModal, setShowAddFriendsModal] = useState(false); const { gameRunning } = useAppSelector((state) => state.gameRunning); @@ -117,11 +122,6 @@ export function UserContent({ onConfirm={handleConfirmSignout} /> - setShowAddFriendsModal(false)} - /> -
0)) && (
-
+

{t("friends")}

- {isMe && ( - - )} +

+ {userProfile.friends?.length || 0} +

); })} + +
)} diff --git a/src/renderer/src/pages/user/user.css.ts b/src/renderer/src/pages/user/user.css.ts index 8a56bd7d..629e8dc9 100644 --- a/src/renderer/src/pages/user/user.css.ts +++ b/src/renderer/src/pages/user/user.css.ts @@ -119,6 +119,17 @@ export const friendsSection = style({ gap: `${SPACING_UNIT * 2}px`, }); +export const friendsSectionHeader = style({ + cursor: "pointer", + display: "flex", + alignItems: "center", + justifyContent: "space-between", + gap: `${SPACING_UNIT * 2}px`, + ":hover": { + color: vars.color.muted, + }, +}); + export const contentSidebar = style({ width: "100%", display: "flex", From c6e99f8599ba18b5699a166d9cf0ecb28ea4c5d7 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Tue, 16 Jul 2024 12:56:59 -0300 Subject: [PATCH 16/54] feat: update i18n and texts --- src/locales/en/translation.json | 2 +- src/locales/pt/translation.json | 4 +++- .../src/pages/shared-modals/user-friend-request-modal.tsx | 4 ++-- src/renderer/src/pages/user/user-content.tsx | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 2dadbcee..99839304 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -243,10 +243,10 @@ "playing_for": "Playing for {{amount}}", "sign_out_modal_text": "Your library is linked with your current account. When signing out, your library will not be visible anymore, and any progress will not be saved. Continue with sign out?", "add_friends": "Add Friends", + "add": "Add", "friend_code": "Friend code", "see_profile": "See profile", "sending": "Sending", - "send": "Add friend", "friend_request_sent": "Friend request sent", "friends": "Friends" } diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json index 9ff165f9..19906e82 100644 --- a/src/locales/pt/translation.json +++ b/src/locales/pt/translation.json @@ -246,6 +246,8 @@ "friend_code": "Código de amigo", "see_profile": "Ver perfil", "friend_request_sent": "Pedido de amizade enviado", - "friends": "Amigos" + "friends": "Amigos", + "add": "Adicionar", + "sending": "Enviando" } } diff --git a/src/renderer/src/pages/shared-modals/user-friend-request-modal.tsx b/src/renderer/src/pages/shared-modals/user-friend-request-modal.tsx index 9d8dbe6f..6d72bba8 100644 --- a/src/renderer/src/pages/shared-modals/user-friend-request-modal.tsx +++ b/src/renderer/src/pages/shared-modals/user-friend-request-modal.tsx @@ -75,7 +75,7 @@ export const UserFriendRequestModal = ({ }; return ( - +
- {isAddingFriend ? t("sending") : t("send")} + {isAddingFriend ? t("sending") : t("add")}
From d0406282ceac1462337eb449d04ffe44a674350f Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Tue, 16 Jul 2024 13:44:52 -0300 Subject: [PATCH 17/54] feat: organize code --- src/renderer/src/app.tsx | 5 +- .../shared-modals/user-friend-modal.css.ts | 92 +++++++++++++++++++ ...equest-modal.tsx => user-friend-modal.tsx} | 2 +- .../shared-modals/user-friend-request.tsx | 2 +- src/renderer/src/pages/user/user-content.tsx | 22 ++--- src/renderer/src/pages/user/user.css.ts | 40 -------- 6 files changed, 105 insertions(+), 58 deletions(-) create mode 100644 src/renderer/src/pages/shared-modals/user-friend-modal.css.ts rename src/renderer/src/pages/shared-modals/{user-friend-request-modal.tsx => user-friend-modal.tsx} (99%) diff --git a/src/renderer/src/app.tsx b/src/renderer/src/app.tsx index a91e8881..09100d6b 100644 --- a/src/renderer/src/app.tsx +++ b/src/renderer/src/app.tsx @@ -25,7 +25,7 @@ import { setGameRunning, } from "@renderer/features"; import { useTranslation } from "react-i18next"; -import { UserFriendRequestModal } from "./pages/shared-modals/user-friend-request-modal"; +import { UserFriendModal } from "./pages/shared-modals/user-friend-modal"; export interface AppProps { children: React.ReactNode; @@ -40,7 +40,6 @@ export function App() { const { clearDownload, setLastPacket } = useDownload(); const { - userDetails, showFriendRequestsModal, setShowFriendRequestModal, updateFriendRequests, @@ -218,7 +217,7 @@ export function App() { onClose={handleToastClose} /> - setShowFriendRequestModal(false)} /> diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal.css.ts b/src/renderer/src/pages/shared-modals/user-friend-modal.css.ts new file mode 100644 index 00000000..717bc95f --- /dev/null +++ b/src/renderer/src/pages/shared-modals/user-friend-modal.css.ts @@ -0,0 +1,92 @@ +import { SPACING_UNIT, vars } from "../../theme.css"; +import { style } from "@vanilla-extract/css"; + +export const profileContentBox = style({ + display: "flex", + gap: `${SPACING_UNIT * 3}px`, + alignItems: "center", + borderRadius: "4px", + border: `solid 1px ${vars.color.border}`, + width: "100%", + boxShadow: "0px 0px 15px 0px rgba(0, 0, 0, 0.7)", + transition: "all ease 0.3s", +}); + +export const friendAvatarContainer = style({ + width: "35px", + minWidth: "35px", + height: "35px", + borderRadius: "50%", + display: "flex", + justifyContent: "center", + alignItems: "center", + backgroundColor: vars.color.background, + overflow: "hidden", + border: `solid 1px ${vars.color.border}`, + boxShadow: "0px 0px 5px 0px rgba(0, 0, 0, 0.7)", +}); + +export const friendListDisplayName = style({ + fontWeight: "bold", + fontSize: vars.size.body, + textAlign: "left", + overflow: "hidden", + textOverflow: "ellipsis", + whiteSpace: "nowrap", +}); + +export const profileAvatar = style({ + height: "100%", + width: "100%", + objectFit: "cover", +}); + +export const friendListContainer = style({ + width: "100%", + height: "54px", + transition: "all ease 0.2s", + position: "relative", + ":hover": { + backgroundColor: "rgba(255, 255, 255, 0.15)", + }, +}); + +export const friendListButton = style({ + display: "flex", + alignItems: "center", + position: "absolute", + cursor: "pointer", + height: "100%", + width: "100%", + flexDirection: "row", + color: vars.color.body, + gap: `${SPACING_UNIT + SPACING_UNIT / 2}px`, + padding: "0 8px", +}); + +export const friendRequestItem = style({ + color: vars.color.body, + ":hover": { + backgroundColor: "rgba(255, 255, 255, 0.15)", + }, +}); + +export const acceptRequestButton = style({ + cursor: "pointer", + color: vars.color.body, + width: "28px", + height: "28px", + ":hover": { + color: vars.color.success, + }, +}); + +export const cancelRequestButton = style({ + cursor: "pointer", + color: vars.color.body, + width: "28px", + height: "28px", + ":hover": { + color: vars.color.danger, + }, +}); diff --git a/src/renderer/src/pages/shared-modals/user-friend-request-modal.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal.tsx similarity index 99% rename from src/renderer/src/pages/shared-modals/user-friend-request-modal.tsx rename to src/renderer/src/pages/shared-modals/user-friend-modal.tsx index 6d72bba8..c8f870df 100644 --- a/src/renderer/src/pages/shared-modals/user-friend-request-modal.tsx +++ b/src/renderer/src/pages/shared-modals/user-friend-modal.tsx @@ -11,7 +11,7 @@ export interface UserAddFriendsModalProps { onClose: () => void; } -export const UserFriendRequestModal = ({ +export const UserFriendModal = ({ visible, onClose, }: UserAddFriendsModalProps) => { diff --git a/src/renderer/src/pages/shared-modals/user-friend-request.tsx b/src/renderer/src/pages/shared-modals/user-friend-request.tsx index e79a8369..022807d5 100644 --- a/src/renderer/src/pages/shared-modals/user-friend-request.tsx +++ b/src/renderer/src/pages/shared-modals/user-friend-request.tsx @@ -3,7 +3,7 @@ import { PersonIcon, XCircleIcon, } from "@primer/octicons-react"; -import * as styles from "../user/user.css"; +import * as styles from "./user-friend-modal.css"; import cn from "classnames"; import { SPACING_UNIT } from "@renderer/theme.css"; diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index de682922..8f1512e8 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -13,13 +13,7 @@ import { } from "@renderer/hooks"; import { useNavigate } from "react-router-dom"; import { buildGameDetailsPath, steamUrlBuilder } from "@renderer/helpers"; -import { - PersonAddIcon, - PersonIcon, - PlusCircleIcon, - PlusIcon, - TelescopeIcon, -} from "@primer/octicons-react"; +import { PersonIcon, PlusIcon, TelescopeIcon } from "@primer/octicons-react"; import { Button, Link } from "@renderer/components"; import { UserEditProfileModal } from "./user-edit-modal"; import { UserSignOutModal } from "./user-signout-modal"; @@ -386,12 +380,14 @@ export function UserContent({ ); })} - + {isMe && ( + + )}
)} diff --git a/src/renderer/src/pages/user/user.css.ts b/src/renderer/src/pages/user/user.css.ts index 629e8dc9..eceb06bc 100644 --- a/src/renderer/src/pages/user/user.css.ts +++ b/src/renderer/src/pages/user/user.css.ts @@ -199,19 +199,6 @@ export const friendListContainer = style({ }, }); -export const friendListButton = style({ - display: "flex", - alignItems: "center", - position: "absolute", - cursor: "pointer", - height: "100%", - width: "100%", - flexDirection: "row", - color: vars.color.body, - gap: `${SPACING_UNIT + SPACING_UNIT / 2}px`, - padding: "0 8px", -}); - export const gameInformation = style({ display: "flex", flexDirection: "column", @@ -284,30 +271,3 @@ export const profileBackground = style({ top: "0", borderRadius: "4px", }); - -export const friendRequestItem = style({ - color: vars.color.body, - ":hover": { - backgroundColor: "rgba(255, 255, 255, 0.15)", - }, -}); - -export const acceptRequestButton = style({ - cursor: "pointer", - color: vars.color.body, - width: "28px", - height: "28px", - ":hover": { - color: vars.color.success, - }, -}); - -export const cancelRequestButton = style({ - cursor: "pointer", - color: vars.color.body, - width: "28px", - height: "28px", - ":hover": { - color: vars.color.danger, - }, -}); From 7f3d7a56c31f2ff48f217ba7ca94ff1e130bf6ae Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Tue, 16 Jul 2024 14:52:39 -0300 Subject: [PATCH 18/54] feat: create tabs on user friend modal --- src/locales/en/translation.json | 3 +- src/locales/pt/translation.json | 3 +- .../pages/shared-modals/user-friend-modal.tsx | 149 ------------------ .../shared-modals/user-friend-modal/index.ts | 1 + .../user-friend-modal-add-friend.tsx | 138 ++++++++++++++++ .../user-friend-modal.css.ts | 2 +- .../user-friend-modal/user-friend-modal.tsx | 58 +++++++ .../user-friend-request.tsx | 0 8 files changed, 202 insertions(+), 152 deletions(-) delete mode 100644 src/renderer/src/pages/shared-modals/user-friend-modal.tsx create mode 100644 src/renderer/src/pages/shared-modals/user-friend-modal/index.ts create mode 100644 src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx rename src/renderer/src/pages/shared-modals/{ => user-friend-modal}/user-friend-modal.css.ts (97%) create mode 100644 src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx rename src/renderer/src/pages/shared-modals/{ => user-friend-modal}/user-friend-request.tsx (100%) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 99839304..917e5d75 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -248,6 +248,7 @@ "see_profile": "See profile", "sending": "Sending", "friend_request_sent": "Friend request sent", - "friends": "Friends" + "friends": "Friends", + "friends_list": "Friends list" } } diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json index 19906e82..46fdf522 100644 --- a/src/locales/pt/translation.json +++ b/src/locales/pt/translation.json @@ -248,6 +248,7 @@ "friend_request_sent": "Pedido de amizade enviado", "friends": "Amigos", "add": "Adicionar", - "sending": "Enviando" + "sending": "Enviando", + "friends_list": "Lista de amigos" } } diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal.tsx deleted file mode 100644 index c8f870df..00000000 --- a/src/renderer/src/pages/shared-modals/user-friend-modal.tsx +++ /dev/null @@ -1,149 +0,0 @@ -import { Button, Modal, TextField } from "@renderer/components"; -import { SPACING_UNIT } from "@renderer/theme.css"; -import { useState } from "react"; -import { useToast, useUserDetails } from "@renderer/hooks"; -import { useTranslation } from "react-i18next"; -import { useNavigate } from "react-router-dom"; -import { UserFriendRequest } from "./user-friend-request"; - -export interface UserAddFriendsModalProps { - visible: boolean; - onClose: () => void; -} - -export const UserFriendModal = ({ - visible, - onClose, -}: UserAddFriendsModalProps) => { - const { t } = useTranslation("user_profile"); - - const [friendCode, setFriendCode] = useState(""); - const [isAddingFriend, setIsAddingFriend] = useState(false); - - const navigate = useNavigate(); - - const { sendFriendRequest, updateFriendRequestState, friendRequests } = - useUserDetails(); - - const { showErrorToast } = useToast(); - - const handleClickAddFriend = () => { - setIsAddingFriend(true); - sendFriendRequest(friendCode) - .then(() => { - setFriendCode(""); - }) - .catch(() => { - showErrorToast("Não foi possível enviar o pedido de amizade"); - }) - .finally(() => { - setIsAddingFriend(false); - }); - }; - - const handleClickRequest = (userId: string) => { - resetAndClose(); - navigate(`/user/${userId}`); - }; - - const handleClickSeeProfile = () => { - resetAndClose(); - navigate(`/user/${friendCode}`); - }; - - const handleClickCancelFriendRequest = (userId: string) => { - updateFriendRequestState(userId, "CANCEL").catch(() => { - showErrorToast("Falha ao cancelar convite"); - }); - }; - - const handleClickAcceptFriendRequest = (userId: string) => { - updateFriendRequestState(userId, "ACCEPTED").catch(() => { - showErrorToast("Falha ao aceitar convite"); - }); - }; - - const handleClickRefuseFriendRequest = (userId: string) => { - updateFriendRequestState(userId, "REFUSED").catch(() => { - showErrorToast("Falha ao recusar convite"); - }); - }; - - const resetAndClose = () => { - setFriendCode(""); - onClose(); - }; - - return ( - -
-
- setFriendCode(e.target.value)} - /> - - -
- -
-

Pendentes

- {friendRequests?.map((request) => { - return ( - - ); - })} -
-
-
- ); -}; diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal/index.ts b/src/renderer/src/pages/shared-modals/user-friend-modal/index.ts new file mode 100644 index 00000000..c7484512 --- /dev/null +++ b/src/renderer/src/pages/shared-modals/user-friend-modal/index.ts @@ -0,0 +1 @@ +export * from "./user-friend-modal"; diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx new file mode 100644 index 00000000..45d3bc57 --- /dev/null +++ b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx @@ -0,0 +1,138 @@ +import { Button, TextField } from "@renderer/components"; +import { useToast, useUserDetails } from "@renderer/hooks"; +import { SPACING_UNIT } from "@renderer/theme.css"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useNavigate } from "react-router-dom"; +import { UserFriendRequest } from "./user-friend-request"; + +export interface UserFriendModalAddFriendProps { + closeModal: () => void; +} + +export const UserFriendModalAddFriend = ({ + closeModal, +}: UserFriendModalAddFriendProps) => { + const { t } = useTranslation("user_profile"); + + const [friendCode, setFriendCode] = useState(""); + const [isAddingFriend, setIsAddingFriend] = useState(false); + + const navigate = useNavigate(); + + const { sendFriendRequest, updateFriendRequestState, friendRequests } = + useUserDetails(); + + const { showErrorToast } = useToast(); + + const handleClickAddFriend = () => { + setIsAddingFriend(true); + sendFriendRequest(friendCode) + .then(() => { + setFriendCode(""); + }) + .catch(() => { + showErrorToast("Não foi possível enviar o pedido de amizade"); + }) + .finally(() => { + setIsAddingFriend(false); + }); + }; + + const resetAndClose = () => { + setFriendCode(""); + closeModal(); + }; + + const handleClickRequest = (userId: string) => { + resetAndClose(); + navigate(`/user/${userId}`); + }; + + const handleClickSeeProfile = () => { + resetAndClose(); + navigate(`/user/${friendCode}`); + }; + + const handleClickCancelFriendRequest = (userId: string) => { + updateFriendRequestState(userId, "CANCEL").catch(() => { + showErrorToast("Falha ao cancelar convite"); + }); + }; + + const handleClickAcceptFriendRequest = (userId: string) => { + updateFriendRequestState(userId, "ACCEPTED").catch(() => { + showErrorToast("Falha ao aceitar convite"); + }); + }; + + const handleClickRefuseFriendRequest = (userId: string) => { + updateFriendRequestState(userId, "REFUSED").catch(() => { + showErrorToast("Falha ao recusar convite"); + }); + }; + + return ( + <> +
+ setFriendCode(e.target.value)} + /> + + +
+ +
+

Pendentes

+ {friendRequests?.map((request) => { + return ( + + ); + })} +
+ + ); +}; diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal.css.ts b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.css.ts similarity index 97% rename from src/renderer/src/pages/shared-modals/user-friend-modal.css.ts rename to src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.css.ts index 717bc95f..b5b29963 100644 --- a/src/renderer/src/pages/shared-modals/user-friend-modal.css.ts +++ b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.css.ts @@ -1,4 +1,4 @@ -import { SPACING_UNIT, vars } from "../../theme.css"; +import { SPACING_UNIT, vars } from "../../../theme.css"; import { style } from "@vanilla-extract/css"; export const profileContentBox = style({ diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx new file mode 100644 index 00000000..0e66bc77 --- /dev/null +++ b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx @@ -0,0 +1,58 @@ +import { Button, Modal } from "@renderer/components"; +import { SPACING_UNIT } from "@renderer/theme.css"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { UserFriendModalAddFriend } from "./user-friend-modal-add-friend"; + +export interface UserAddFriendsModalProps { + visible: boolean; + onClose: () => void; +} + +export const UserFriendModal = ({ + visible, + onClose, +}: UserAddFriendsModalProps) => { + const { t } = useTranslation("user_profile"); + + const tabs = [t("add_friends"), t("friends_list")]; + + const [currentTabIndex, setCurrentTabIndex] = useState(0); + + const renderTab = () => { + if (currentTabIndex == 0) { + return ; + } + + return <>; + }; + + return ( + +
+
+ {tabs.map((tab, index) => { + return ( + + ); + })} +
+

{tabs[currentTabIndex]}

+ {renderTab()} +
+
+ ); +}; diff --git a/src/renderer/src/pages/shared-modals/user-friend-request.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-request.tsx similarity index 100% rename from src/renderer/src/pages/shared-modals/user-friend-request.tsx rename to src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-request.tsx From e55dc20c7db87bc1b1daf0018472ba75f45f420b Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:45:04 -0300 Subject: [PATCH 19/54] feat: open modal in correct tab --- src/renderer/src/app.tsx | 10 +++--- .../components/sidebar/sidebar-profile.tsx | 7 ++-- .../src/features/user-details-slice.ts | 20 ++++++++--- src/renderer/src/hooks/use-user-details.ts | 20 +++++++---- .../user-friend-modal/user-friend-modal.tsx | 33 +++++++++++++++---- src/renderer/src/pages/user/user-content.tsx | 16 ++++++--- src/renderer/src/pages/user/user.css.ts | 2 ++ 7 files changed, 79 insertions(+), 29 deletions(-) diff --git a/src/renderer/src/app.tsx b/src/renderer/src/app.tsx index 09100d6b..ddad1c51 100644 --- a/src/renderer/src/app.tsx +++ b/src/renderer/src/app.tsx @@ -40,8 +40,9 @@ export function App() { const { clearDownload, setLastPacket } = useDownload(); const { - showFriendRequestsModal, - setShowFriendRequestModal, + showFriendsModal, + friendRequetsModalTab, + setShowFriendsModal, updateFriendRequests, } = useUserDetails(); @@ -218,8 +219,9 @@ export function App() { /> setShowFriendRequestModal(false)} + visible={showFriendsModal} + initialTab={friendRequetsModalTab} + onClose={() => setShowFriendsModal(false, null)} />
diff --git a/src/renderer/src/components/sidebar/sidebar-profile.tsx b/src/renderer/src/components/sidebar/sidebar-profile.tsx index 33779c0f..aef8de3d 100644 --- a/src/renderer/src/components/sidebar/sidebar-profile.tsx +++ b/src/renderer/src/components/sidebar/sidebar-profile.tsx @@ -6,6 +6,7 @@ import { useAppSelector, useUserDetails } from "@renderer/hooks"; import { useMemo } from "react"; import { useTranslation } from "react-i18next"; import { profileContainerBackground } from "./sidebar-profile.css"; +import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal"; export function SidebarProfile() { const navigate = useNavigate(); @@ -16,7 +17,7 @@ export function SidebarProfile() { userDetails, profileBackground, friendRequests, - setShowFriendRequestModal, + setShowFriendsModal, } = useUserDetails(); const { gameRunning } = useAppSelector((state) => state.gameRunning); @@ -87,7 +88,9 @@ export function SidebarProfile() { ); })}
-

{tabs[currentTabIndex]}

+

{tabs[currentTab]}

{renderTab()} diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index 8f1512e8..e560a50b 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -17,6 +17,7 @@ import { PersonIcon, PlusIcon, TelescopeIcon } from "@primer/octicons-react"; import { Button, Link } from "@renderer/components"; import { UserEditProfileModal } from "./user-edit-modal"; import { UserSignOutModal } from "./user-signout-modal"; +import { UserFriendModalTab } from "../shared-modals/user-friend-modal"; const MAX_MINUTES_TO_SHOW_IN_PLAYTIME = 120; @@ -36,7 +37,7 @@ export function UserContent({ profileBackground, signOut, updateFriendRequests, - setShowFriendRequestModal, + setShowFriendsModal, } = useUserDetails(); const { showSuccessToast } = useToast(); @@ -329,7 +330,12 @@ export function UserContent({ {(isMe || (userProfile.friends && userProfile.friends.length > 0)) && (
-
+
setShowFriendRequestModal(true)} + onClick={() => + setShowFriendsModal(true, UserFriendModalTab.AddFriend) + } > {t("add")} diff --git a/src/renderer/src/pages/user/user.css.ts b/src/renderer/src/pages/user/user.css.ts index eceb06bc..34e7b670 100644 --- a/src/renderer/src/pages/user/user.css.ts +++ b/src/renderer/src/pages/user/user.css.ts @@ -120,6 +120,8 @@ export const friendsSection = style({ }); export const friendsSectionHeader = style({ + fontSize: vars.size.body, + color: vars.color.body, cursor: "pointer", display: "flex", alignItems: "center", From 004ccd0db57304634791ecf0442d09146af45e4c Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:51:42 -0300 Subject: [PATCH 20/54] feat: add comment --- .../user-friend-modal/user-friend-modal-add-friend.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx index 45d3bc57..f925ca65 100644 --- a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx +++ b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx @@ -29,6 +29,7 @@ export const UserFriendModalAddFriend = ({ setIsAddingFriend(true); sendFriendRequest(friendCode) .then(() => { + // TODO: add validation for this input? setFriendCode(""); }) .catch(() => { @@ -51,6 +52,7 @@ export const UserFriendModalAddFriend = ({ const handleClickSeeProfile = () => { resetAndClose(); + // TODO: add validation for this input? navigate(`/user/${friendCode}`); }; From d4902a5ab1cf2a81dd768019204a48344506b0e1 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Tue, 16 Jul 2024 22:02:17 -0300 Subject: [PATCH 21/54] feat: use empty list --- src/renderer/src/components/sidebar/sidebar-profile.tsx | 2 +- src/renderer/src/features/user-details-slice.ts | 9 +++------ src/renderer/src/hooks/use-user-details.ts | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/renderer/src/components/sidebar/sidebar-profile.tsx b/src/renderer/src/components/sidebar/sidebar-profile.tsx index aef8de3d..b5c2c539 100644 --- a/src/renderer/src/components/sidebar/sidebar-profile.tsx +++ b/src/renderer/src/components/sidebar/sidebar-profile.tsx @@ -83,7 +83,7 @@ export function SidebarProfile() { )}
- {userDetails && friendRequests?.length && !gameRunning && ( + {userDetails && friendRequests.length > 0 && !gameRunning && (
@@ -355,7 +355,7 @@ export function UserContent({ gap: `${SPACING_UNIT}px`, }} > - {userProfile.friends?.map((friend) => { + {userProfile.friends.map((friend) => { return (
{(isMe || - (userProfile.friends && userProfile.friends.length > 0)) && ( + (userProfile.friends && userProfile.friends.totalFriends > 0)) && (
@@ -369,7 +369,7 @@ export function UserContent({ gap: `${SPACING_UNIT}px`, }} > - {userProfile.friends.map((friend) => { + {userProfile.friends.friends.map((friend) => { return (
@@ -72,6 +77,7 @@ export const UserFriendRequest = ({ @@ -80,12 +86,14 @@ export const UserFriendRequest = ({ diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index c22aae4e..6d9401fb 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -17,7 +17,13 @@ import { profileBackgroundFromProfileImage, steamUrlBuilder, } from "@renderer/helpers"; -import { PersonIcon, PlusIcon, TelescopeIcon } from "@primer/octicons-react"; +import { + CheckCircleIcon, + PersonIcon, + PlusIcon, + TelescopeIcon, + XCircleIcon, +} from "@primer/octicons-react"; import { Button, Link } from "@renderer/components"; import { UserEditProfileModal } from "./user-edit-modal"; import { UserSignOutModal } from "./user-signout-modal"; @@ -39,6 +45,7 @@ export function UserContent({ const { userDetails, profileBackground, + friendRequests, signOut, updateFriendRequests, showFriendsModal, @@ -116,6 +123,71 @@ export function UserContent({ } }, [profileBackground, isMe]); + const getProfileActions = () => { + if (isMe) { + return ( + <> + + + + + ); + } + + const friendRequest = friendRequests.find( + (request) => request.id == userProfile.id + ); + + if (!friendRequest) { + return ( + <> + + + + + ); + } + + if (friendRequest.type === "RECEIVED") { + return ( + <> + + + + ); + } + + return ( + + ); + }; + return ( <> - {isMe && ( +
-
- <> - - - - -
+ {getProfileActions()}
- )} +
diff --git a/src/renderer/src/pages/user/user.css.ts b/src/renderer/src/pages/user/user.css.ts index 3df92ceb..f9b1b09a 100644 --- a/src/renderer/src/pages/user/user.css.ts +++ b/src/renderer/src/pages/user/user.css.ts @@ -279,3 +279,16 @@ export const profileBackground = style({ top: "0", borderRadius: "4px", }); + +export const cancelRequestButton = style({ + cursor: "pointer", + color: vars.color.body, + ":hover": { + color: vars.color.danger, + }, +}); + +export const acceptRequestButton = style({ + cursor: "pointer", + color: vars.color.success, +}); diff --git a/src/types/index.ts b/src/types/index.ts index 826c768f..1d91af62 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -293,6 +293,7 @@ export interface UserProfile { id: string; displayName: string; profileImageUrl: string | null; + profileVisibility: "PUBLIC" | "PRIVATE" | "FRIEND"; totalPlayTimeInSeconds: number; libraryGames: UserGame[]; recentGames: UserGame[]; From e642bf71b1741ca21a88e9b925ae70b90f286e0d Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sun, 21 Jul 2024 20:49:37 -0300 Subject: [PATCH 33/54] feat: rename functions --- src/renderer/src/app.tsx | 6 ++-- src/renderer/src/hooks/use-user-details.ts | 14 ++++----- .../user-friend-modal-add-friend.tsx | 12 +++---- src/renderer/src/pages/user/user-content.tsx | 31 +++++++++++++++---- 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/renderer/src/app.tsx b/src/renderer/src/app.tsx index 24c7bed6..689a338e 100644 --- a/src/renderer/src/app.tsx +++ b/src/renderer/src/app.tsx @@ -42,7 +42,7 @@ export function App() { const { isFriendsModalVisible, friendRequetsModalTab, - updateFriendRequests, + fetchFriendRequests, hideFriendsModal, } = useUserDetails(); @@ -104,7 +104,7 @@ export function App() { fetchUserDetails().then((response) => { if (response) { updateUserDetails(response); - updateFriendRequests(); + fetchFriendRequests(); } }); }, [fetchUserDetails, updateUserDetails, dispatch]); @@ -113,7 +113,7 @@ export function App() { fetchUserDetails().then((response) => { if (response) { updateUserDetails(response); - updateFriendRequests(); + fetchFriendRequests(); showSuccessToast(t("successfully_signed_in")); } }); diff --git a/src/renderer/src/hooks/use-user-details.ts b/src/renderer/src/hooks/use-user-details.ts index 80ef8b4c..8d53d996 100644 --- a/src/renderer/src/hooks/use-user-details.ts +++ b/src/renderer/src/hooks/use-user-details.ts @@ -84,7 +84,7 @@ export function useUserDetails() { [updateUserDetails] ); - const updateFriendRequests = useCallback(async () => { + const fetchFriendRequests = useCallback(async () => { const friendRequests = await window.electron.getFriendRequests(); dispatch(setFriendRequests(friendRequests)); }, [dispatch]); @@ -92,7 +92,7 @@ export function useUserDetails() { const showFriendsModal = useCallback( (tab: UserFriendModalTab) => { dispatch(setFriendsModalVisible(tab)); - updateFriendRequests(); + fetchFriendRequests(); }, [dispatch] ); @@ -105,18 +105,18 @@ export function useUserDetails() { async (userId: string) => { return window.electron .sendFriendRequest(userId) - .then(() => updateFriendRequests()); + .then(() => fetchFriendRequests()); }, - [updateFriendRequests] + [fetchFriendRequests] ); const updateFriendRequestState = useCallback( async (userId: string, action: FriendRequestAction) => { return window.electron .updateFriendRequest(userId, action) - .then(() => updateFriendRequests()); + .then(() => fetchFriendRequests()); }, - [updateFriendRequests] + [fetchFriendRequests] ); return { @@ -133,7 +133,7 @@ export function useUserDetails() { updateUserDetails, patchUser, sendFriendRequest, - updateFriendRequests, + fetchFriendRequests, updateFriendRequestState, }; } diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx index bf4879b2..62290095 100644 --- a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx +++ b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx @@ -56,19 +56,19 @@ export const UserFriendModalAddFriend = ({ navigate(`/user/${friendCode}`); }; - const handleClickCancelFriendRequest = (userId: string) => { + const handleCancelFriendRequest = (userId: string) => { updateFriendRequestState(userId, "CANCEL").catch(() => { showErrorToast("Falha ao cancelar convite"); }); }; - const handleClickAcceptFriendRequest = (userId: string) => { + const handleAcceptFriendRequest = (userId: string) => { updateFriendRequestState(userId, "ACCEPTED").catch(() => { showErrorToast("Falha ao aceitar convite"); }); }; - const handleClickRefuseFriendRequest = (userId: string) => { + const handleRefuseFriendRequest = (userId: string) => { updateFriendRequestState(userId, "REFUSED").catch(() => { showErrorToast("Falha ao recusar convite"); }); @@ -127,9 +127,9 @@ export const UserFriendModalAddFriend = ({ isRequestSent={request.type === "SENT"} profileImageUrl={request.profileImageUrl} userId={request.id} - onClickAcceptRequest={handleClickAcceptFriendRequest} - onClickCancelRequest={handleClickCancelFriendRequest} - onClickRefuseRequest={handleClickRefuseFriendRequest} + onClickAcceptRequest={handleAcceptFriendRequest} + onClickCancelRequest={handleCancelFriendRequest} + onClickRefuseRequest={handleRefuseFriendRequest} onClickRequest={handleClickRequest} /> ); diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index 6d9401fb..3ace2bd1 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -47,10 +47,11 @@ export function UserContent({ profileBackground, friendRequests, signOut, - updateFriendRequests, + fetchFriendRequests, showFriendsModal, + updateFriendRequestState, } = useUserDetails(); - const { showSuccessToast } = useToast(); + const { showSuccessToast, showErrorToast } = useToast(); const [profileContentBoxBackground, setProfileContentBoxBackground] = useState(); @@ -106,7 +107,7 @@ export function UserContent({ const isMe = userDetails?.id == userProfile.id; useEffect(() => { - if (isMe) updateFriendRequests(); + if (isMe) fetchFriendRequests(); }, [isMe]); useEffect(() => { @@ -123,6 +124,24 @@ export function UserContent({ } }, [profileBackground, isMe]); + const handleCancelFriendRequest = (userId: string) => { + updateFriendRequestState(userId, "CANCEL").catch(() => { + showErrorToast("Falha ao cancelar convite"); + }); + }; + + const handleAcceptFriendRequest = (userId: string) => { + updateFriendRequestState(userId, "ACCEPTED").catch(() => { + showErrorToast("Falha ao aceitar convite"); + }); + }; + + const handleRefuseFriendRequest = (userId: string) => { + updateFriendRequestState(userId, "REFUSED").catch(() => { + showErrorToast("Falha ao recusar convite"); + }); + }; + const getProfileActions = () => { if (isMe) { return ( @@ -162,14 +181,14 @@ export function UserContent({ @@ -181,7 +200,7 @@ export function UserContent({ From 11c29355e34339db63ba05810b35e9099c2ea5fa Mon Sep 17 00:00:00 2001 From: Lianela <140931995+Lianela@users.noreply.github.com> Date: Mon, 22 Jul 2024 19:38:34 -0600 Subject: [PATCH 34/54] Friend strings translated (ES) --- src/locales/es/translation.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/locales/es/translation.json b/src/locales/es/translation.json index 5e016d34..fcb2b099 100644 --- a/src/locales/es/translation.json +++ b/src/locales/es/translation.json @@ -241,6 +241,15 @@ "successfully_signed_out": "Sesión cerrada exitosamente", "sign_out": "Cerrar sesión", "playing_for": "Jugando por {{amount}}", - "sign_out_modal_text": "Tu biblioteca se ha vinculado con tu cuenta. Cuando cierres sesión, tú biblioteca ya no será visible y cualquier progreso no se guardará. ¿Continuar con el cierre de sesión?" + "sign_out_modal_text": "Tu biblioteca se ha vinculado con tu cuenta. Cuando cierres sesión, tú biblioteca ya no será visible y cualquier progreso no se guardará. ¿Continuar con el cierre de sesión?", + "add_friends": "Añadir amigos", + "add": "Añadir", + "friend_code": "Código de amigo", + "see_profile": "Ver perfil", + "sending": "Enviando", + "friend_request_sent": "Solicitud de amistad enviada", + "friends": "Amigos", + "friends_list": "Lista de amigos", + "user_not_found": "Usuario no encontrado" } } From 380143c780c585f2f9e83f0eb349b636976d99d8 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Tue, 23 Jul 2024 15:30:04 -0300 Subject: [PATCH 35/54] feat: correctly get own friends --- src/main/events/user/get-user-friends.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/events/user/get-user-friends.ts b/src/main/events/user/get-user-friends.ts index a82673eb..4505909a 100644 --- a/src/main/events/user/get-user-friends.ts +++ b/src/main/events/user/get-user-friends.ts @@ -1,3 +1,4 @@ +import { userAuthRepository } from "@main/repository"; import { registerEvent } from "../register-event"; import { HydraApi } from "@main/services"; import { UserFriends } from "@types"; @@ -7,9 +8,19 @@ export const getUserFriends = async ( take: number, skip: number ): Promise => { + const loggedUser = await userAuthRepository.findOne({ where: { id: 1 } }); + + if (loggedUser?.userId == userId) { + return HydraApi.get(`/profile/friends`, { take, skip }).catch( + (_err) => { + return { totalFriends: 0, friends: [] }; + } + ); + } + return HydraApi.get(`/user/${userId}/friends`, { take, skip }).catch( (_err) => { - return { totalFriends: 0, friends: [] } as UserFriends; + return { totalFriends: 0, friends: [] }; } ); }; From 010f07373d797534a3e6287aff0fcfc2603fa52a Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Tue, 23 Jul 2024 18:37:19 -0300 Subject: [PATCH 36/54] feat: add getUserFriends event --- src/main/events/index.ts | 1 + src/main/events/user/get-user-friends.ts | 20 +++++++++++--------- src/preload/index.ts | 1 + src/renderer/src/declaration.d.ts | 2 ++ 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/main/events/index.ts b/src/main/events/index.ts index dd5e3263..398235f0 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -43,6 +43,7 @@ import "./auth/sign-out"; import "./auth/open-auth-window"; import "./auth/get-session-hash"; import "./user/get-user"; +import "./user/get-user-friends"; import "./profile/get-friend-requests"; import "./profile/get-me"; import "./profile/update-friend-request"; diff --git a/src/main/events/user/get-user-friends.ts b/src/main/events/user/get-user-friends.ts index 4505909a..a9394d95 100644 --- a/src/main/events/user/get-user-friends.ts +++ b/src/main/events/user/get-user-friends.ts @@ -8,21 +8,23 @@ export const getUserFriends = async ( take: number, skip: number ): Promise => { - const loggedUser = await userAuthRepository.findOne({ where: { id: 1 } }); + try { + const loggedUser = await userAuthRepository.findOne({ where: { id: 1 } }); - if (loggedUser?.userId == userId) { - return HydraApi.get(`/profile/friends`, { take, skip }).catch( + if (loggedUser?.userId == userId) { + return HydraApi.get(`/profile/friends`, { take, skip }).catch((_err) => { + return { totalFriends: 0, friends: [] }; + }); + } + + return HydraApi.get(`/user/${userId}/friends`, { take, skip }).catch( (_err) => { return { totalFriends: 0, friends: [] }; } ); + } catch (err) { + return { totalFriends: 0, friends: [] }; } - - return HydraApi.get(`/user/${userId}/friends`, { take, skip }).catch( - (_err) => { - return { totalFriends: 0, friends: [] }; - } - ); }; const getUserFriendsEvent = async ( diff --git a/src/preload/index.ts b/src/preload/index.ts index 91722606..e6dafe0f 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -145,6 +145,7 @@ contextBridge.exposeInMainWorld("electron", { /* User */ getUser: (userId: string) => ipcRenderer.invoke("getUser", userId), + getUserFriends: (userId: string) => ipcRenderer.invoke("getUserFriends", userId), /* Auth */ signOut: () => ipcRenderer.invoke("signOut"), diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index bb89f84e..55fc1269 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -16,6 +16,7 @@ import type { UserProfile, FriendRequest, FriendRequestAction, + UserFriends, } from "@types"; import type { DiskSpace } from "check-disk-space"; @@ -127,6 +128,7 @@ declare global { /* User */ getUser: (userId: string) => Promise; + getUserFriends: (userId: string) => Promise; /* Profile */ getMe: () => Promise; From a196b91cb9a744b6bac644611de51ea60a7328b4 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Tue, 23 Jul 2024 20:27:38 -0300 Subject: [PATCH 37/54] feat: pass userId to modal --- src/preload/index.ts | 3 +- src/renderer/src/app.tsx | 16 +++++--- .../components/sidebar/sidebar-profile.tsx | 4 +- src/renderer/src/declaration.d.ts | 6 ++- .../src/features/user-details-slice.ts | 7 +++- src/renderer/src/hooks/use-user-details.ts | 6 ++- .../user-friend-modal-list.tsx | 40 ++++++++++++++++++ .../user-friend-modal/user-friend-modal.tsx | 41 +++++++++++-------- src/renderer/src/pages/user/user-content.tsx | 12 +++++- 9 files changed, 104 insertions(+), 31 deletions(-) create mode 100644 src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-list.tsx diff --git a/src/preload/index.ts b/src/preload/index.ts index e6dafe0f..cd3b9686 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -145,7 +145,8 @@ contextBridge.exposeInMainWorld("electron", { /* User */ getUser: (userId: string) => ipcRenderer.invoke("getUser", userId), - getUserFriends: (userId: string) => ipcRenderer.invoke("getUserFriends", userId), + getUserFriends: (userId: string, take: number, skip: number) => + ipcRenderer.invoke("getUserFriends", userId, take, skip), /* Auth */ signOut: () => ipcRenderer.invoke("signOut"), diff --git a/src/renderer/src/app.tsx b/src/renderer/src/app.tsx index 689a338e..8c6f7604 100644 --- a/src/renderer/src/app.tsx +++ b/src/renderer/src/app.tsx @@ -42,11 +42,12 @@ export function App() { const { isFriendsModalVisible, friendRequetsModalTab, + friendModalUserId, fetchFriendRequests, hideFriendsModal, } = useUserDetails(); - const { fetchUserDetails, updateUserDetails, clearUserDetails } = + const { userDetails, fetchUserDetails, updateUserDetails, clearUserDetails } = useUserDetails(); const dispatch = useAppDispatch(); @@ -218,11 +219,14 @@ export function App() { onClose={handleToastClose} /> - + {userDetails && ( + + )}
diff --git a/src/renderer/src/components/sidebar/sidebar-profile.tsx b/src/renderer/src/components/sidebar/sidebar-profile.tsx index 66c4d82d..3ff598f7 100644 --- a/src/renderer/src/components/sidebar/sidebar-profile.tsx +++ b/src/renderer/src/components/sidebar/sidebar-profile.tsx @@ -84,7 +84,9 @@ export function SidebarProfile() { - ); - })} - + {isMe && ( +
+ {tabs.map((tab, index) => { + return ( + + ); + })} +
+ )}

{tabs[currentTab]}

{renderTab()}
diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index 3ace2bd1..9fb1c8b5 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -424,7 +424,12 @@ export function UserContent({
+ ); + } + + if (type === "RECEIVED") { + return ( + <> + + + + ); + } + + return ( + + ); + }; + return (
@@ -73,32 +126,7 @@ export const UserFriendRequest = ({ gap: `${SPACING_UNIT}px`, }} > - {isRequestSent ? ( - - ) : ( - <> - - - - )} + {getRequestActions()}
); diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx index 62290095..f71d4790 100644 --- a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx +++ b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx @@ -4,7 +4,7 @@ import { SPACING_UNIT } from "@renderer/theme.css"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; -import { UserFriendRequest } from "./user-friend-request"; +import { UserFriendItem } from "./user-friend-item"; export interface UserFriendModalAddFriendProps { closeModal: () => void; @@ -121,16 +121,16 @@ export const UserFriendModalAddFriend = ({

Pendentes

{friendRequests.map((request) => { return ( - ); })} diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-list.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-list.tsx index 83d7a6a0..2c639847 100644 --- a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-list.tsx +++ b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-list.tsx @@ -1,13 +1,22 @@ +import { SPACING_UNIT } from "@renderer/theme.css"; import { UserFriend } from "@types"; import { useEffect, useState } from "react"; +import { UserFriendItem } from "./user-friend-item"; +import { useNavigate } from "react-router-dom"; export interface UserFriendModalListProps { userId: string; + closeModal: () => void; } const pageSize = 12; -export const UserFriendModalList = ({ userId }: UserFriendModalListProps) => { +export const UserFriendModalList = ({ + userId, + closeModal, +}: UserFriendModalListProps) => { + const navigate = useNavigate(); + const [page, setPage] = useState(0); const [maxPage, setMaxPage] = useState(0); const [friends, setFriends] = useState([]); @@ -34,7 +43,34 @@ export const UserFriendModalList = ({ userId }: UserFriendModalListProps) => { loadNextPage(); }, [userId]); - return friends.map((friend) => { - return

{friend.displayName}

; - }); + const handleClickFriend = (userId: string) => { + closeModal(); + navigate(`/user/${userId}`); + }; + + return ( +
+ {friends.map((friend) => { + return ( + {}} + onClickCancelRequest={() => {}} + onClickRefuseRequest={() => {}} + onClickItem={handleClickFriend} + type={"ACCEPTED"} + key={friend.id} + /> + ); + })} +
+ ); }; diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx index b0752de7..39a82ec4 100644 --- a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx +++ b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx @@ -43,7 +43,7 @@ export const UserFriendModal = ({ const renderTab = () => { if (currentTab == UserFriendModalTab.FriendsList) { - return ; + return ; } if (currentTab == UserFriendModalTab.AddFriend) { From 94bd691209c7cd42bbc3d6a5c6533c18d93f321a Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Thu, 25 Jul 2024 19:12:14 -0300 Subject: [PATCH 39/54] fet: show only received friends request on sidebar icon --- .../src/components/sidebar/sidebar-profile.tsx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/renderer/src/components/sidebar/sidebar-profile.tsx b/src/renderer/src/components/sidebar/sidebar-profile.tsx index 3ff598f7..b16c2c28 100644 --- a/src/renderer/src/components/sidebar/sidebar-profile.tsx +++ b/src/renderer/src/components/sidebar/sidebar-profile.tsx @@ -3,10 +3,11 @@ import { PersonAddIcon, PersonIcon } from "@primer/octicons-react"; import * as styles from "./sidebar-profile.css"; import { assignInlineVars } from "@vanilla-extract/dynamic"; import { useAppSelector, useUserDetails } from "@renderer/hooks"; -import { useMemo } from "react"; +import { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { profileContainerBackground } from "./sidebar-profile.css"; import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal"; +import { FriendRequest } from "@types"; export function SidebarProfile() { const navigate = useNavigate(); @@ -16,6 +17,14 @@ export function SidebarProfile() { const { userDetails, profileBackground, friendRequests, showFriendsModal } = useUserDetails(); + const [receivedRequests, setReceivedRequests] = useState([]); + + useEffect(() => { + setReceivedRequests( + receivedRequests.filter((request) => request.type === "RECEIVED") + ); + }, [friendRequests]); + const { gameRunning } = useAppSelector((state) => state.gameRunning); const handleButtonClick = () => { @@ -79,7 +88,7 @@ export function SidebarProfile() { )} - {userDetails && friendRequests.length > 0 && !gameRunning && ( + {userDetails && receivedRequests.length > 0 && !gameRunning && (
)} From 102299e42fad776f55d639cde932be5bd4843b04 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Thu, 25 Jul 2024 19:12:34 -0300 Subject: [PATCH 40/54] feat: remove tab title --- .../pages/shared-modals/user-friend-modal/user-friend-modal.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx index 39a82ec4..abc26270 100644 --- a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx +++ b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal.tsx @@ -78,7 +78,6 @@ export const UserFriendModal = ({ })} )} -

{tabs[currentTab]}

{renderTab()} From 304aa011ad6b6cb0bff01de3cb83535eb7185602 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Thu, 25 Jul 2024 19:27:03 -0300 Subject: [PATCH 41/54] feat: action buttons on user profile page --- src/locales/en/translation.json | 3 +- src/locales/pt/translation.json | 3 +- src/renderer/index.html | 2 +- .../components/sidebar/sidebar-profile.tsx | 2 +- src/renderer/src/hooks/use-user-details.ts | 10 +- .../user-friend-modal-add-friend.tsx | 16 ++- src/renderer/src/pages/user/user-content.tsx | 103 ++++++++++++------ src/types/index.ts | 9 ++ 8 files changed, 101 insertions(+), 47 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 24ba8f0f..dc1bb1d7 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -258,6 +258,7 @@ "accept_request": "Accept request", "ignore_request": "Ignore request", "cancel_request": "Cancel request", - "undo_friendship": "Undo friendship" + "undo_friendship": "Undo friendship", + "request_accepted": "Request accepted" } } diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json index ce4efd58..c95ae82f 100644 --- a/src/locales/pt/translation.json +++ b/src/locales/pt/translation.json @@ -258,6 +258,7 @@ "accept_request": "Aceitar pedido", "ignore_request": "Ignorar pedido", "cancel_request": "Cancelar pedido", - "undo_friendship": "Desfazer amizade" + "undo_friendship": "Desfazer amizade", + "request_accepted": "Pedido de amizade aceito" } } diff --git a/src/renderer/index.html b/src/renderer/index.html index 52276268..543b85a9 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -6,7 +6,7 @@ Hydra diff --git a/src/renderer/src/components/sidebar/sidebar-profile.tsx b/src/renderer/src/components/sidebar/sidebar-profile.tsx index b16c2c28..81736e37 100644 --- a/src/renderer/src/components/sidebar/sidebar-profile.tsx +++ b/src/renderer/src/components/sidebar/sidebar-profile.tsx @@ -21,7 +21,7 @@ export function SidebarProfile() { useEffect(() => { setReceivedRequests( - receivedRequests.filter((request) => request.type === "RECEIVED") + friendRequests.filter((request) => request.type === "RECEIVED") ); }, [friendRequests]); diff --git a/src/renderer/src/hooks/use-user-details.ts b/src/renderer/src/hooks/use-user-details.ts index 401f4684..38f6a8f3 100644 --- a/src/renderer/src/hooks/use-user-details.ts +++ b/src/renderer/src/hooks/use-user-details.ts @@ -85,9 +85,13 @@ export function useUserDetails() { [updateUserDetails] ); - const fetchFriendRequests = useCallback(async () => { - const friendRequests = await window.electron.getFriendRequests(); - dispatch(setFriendRequests(friendRequests)); + const fetchFriendRequests = useCallback(() => { + return window.electron + .getFriendRequests() + .then((friendRequests) => { + dispatch(setFriendRequests(friendRequests)); + }) + .catch(() => {}); }, [dispatch]); const showFriendsModal = useCallback( diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx index f71d4790..0725674e 100644 --- a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx +++ b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-add-friend.tsx @@ -23,7 +23,7 @@ export const UserFriendModalAddFriend = ({ const { sendFriendRequest, updateFriendRequestState, friendRequests } = useUserDetails(); - const { showErrorToast } = useToast(); + const { showSuccessToast, showErrorToast } = useToast(); const handleClickAddFriend = () => { setIsAddingFriend(true); @@ -58,19 +58,23 @@ export const UserFriendModalAddFriend = ({ const handleCancelFriendRequest = (userId: string) => { updateFriendRequestState(userId, "CANCEL").catch(() => { - showErrorToast("Falha ao cancelar convite"); + showErrorToast(t("try_again")); }); }; const handleAcceptFriendRequest = (userId: string) => { - updateFriendRequestState(userId, "ACCEPTED").catch(() => { - showErrorToast("Falha ao aceitar convite"); - }); + updateFriendRequestState(userId, "ACCEPTED") + .then(() => { + showSuccessToast(t("request_accepted")); + }) + .catch(() => { + showErrorToast(t("try_again")); + }); }; const handleRefuseFriendRequest = (userId: string) => { updateFriendRequestState(userId, "REFUSED").catch(() => { - showErrorToast("Falha ao recusar convite"); + showErrorToast(t("try_again")); }); }; diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index 9fb1c8b5..228fdbd9 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -1,4 +1,4 @@ -import { UserGame, UserProfile } from "@types"; +import { UserGame, UserProfile, UserRelation } from "@types"; import cn from "classnames"; import * as styles from "./user.css"; import { SPACING_UNIT, vars } from "@renderer/theme.css"; @@ -45,8 +45,8 @@ export function UserContent({ const { userDetails, profileBackground, - friendRequests, signOut, + sendFriendRequest, fetchFriendRequests, showFriendsModal, updateFriendRequestState, @@ -124,22 +124,47 @@ export function UserContent({ } }, [profileBackground, isMe]); + const handleUndoFriendship = (userRelation: UserRelation) => { + const userId = + userRelation.AId === userProfile.id ? userRelation.BId : userRelation.AId; + + updateFriendRequestState(userId, "CANCEL") + .then(updateUserProfile) + .catch(() => { + showErrorToast(t("try_again")); + }); + }; + + const handleSendFriendRequest = () => { + sendFriendRequest(userProfile.id) + .then(updateUserProfile) + .catch(() => { + showErrorToast(t("try_again")); + }); + }; + const handleCancelFriendRequest = (userId: string) => { - updateFriendRequestState(userId, "CANCEL").catch(() => { - showErrorToast("Falha ao cancelar convite"); - }); + updateFriendRequestState(userId, "CANCEL") + .then(updateUserProfile) + .catch(() => { + showErrorToast(t("try_again")); + }); }; const handleAcceptFriendRequest = (userId: string) => { - updateFriendRequestState(userId, "ACCEPTED").catch(() => { - showErrorToast("Falha ao aceitar convite"); - }); + updateFriendRequestState(userId, "ACCEPTED") + .then(updateUserProfile) + .catch(() => { + showErrorToast(t("try_again")); + }); }; const handleRefuseFriendRequest = (userId: string) => { - updateFriendRequestState(userId, "REFUSED").catch(() => { - showErrorToast("Falha ao recusar convite"); - }); + updateFriendRequestState(userId, "REFUSED") + .then(updateUserProfile) + .catch(() => { + showErrorToast(t("try_again")); + }); }; const getProfileActions = () => { @@ -157,14 +182,10 @@ export function UserContent({ ); } - const friendRequest = friendRequests.find( - (request) => request.id == userProfile.id - ); - - if (!friendRequest) { + if (userProfile.relation == null) { return ( <> - @@ -175,35 +196,49 @@ export function UserContent({ ); } - if (friendRequest.type === "RECEIVED") { + if (userProfile.relation.status === "ACCEPTED") { return ( <> - ); } + if (userProfile.relation.BId === userProfile.id) { + return ( + + ); + } + return ( - + <> + + + ); }; diff --git a/src/types/index.ts b/src/types/index.ts index 1d91af62..033739c4 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -289,6 +289,14 @@ export interface FriendRequest { type: "SENT" | "RECEIVED"; } +export interface UserRelation { + AId: string; + BId: string; + status: "ACCEPTED" | "PENDING"; + createdAt: string; + updatedAt: string; +} + export interface UserProfile { id: string; displayName: string; @@ -298,6 +306,7 @@ export interface UserProfile { libraryGames: UserGame[]; recentGames: UserGame[]; friends: UserFriends; + relation: UserRelation | null; } export interface DownloadSource { From edf920fed3228c2a3806e6bf8b21cba2ba81be10 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Thu, 25 Jul 2024 20:08:53 -0300 Subject: [PATCH 42/54] feat: block and unblock events --- src/locales/en/translation.json | 3 ++- src/locales/pt/translation.json | 3 ++- src/main/events/index.ts | 2 ++ src/main/events/user/block-user.ts | 11 +++++++++++ src/main/events/user/unblock-user.ts | 11 +++++++++++ src/preload/index.ts | 2 ++ src/renderer/src/declaration.d.ts | 2 ++ src/renderer/src/hooks/use-user-details.ts | 10 ++++++++++ src/renderer/src/pages/user/user-content.tsx | 14 +++++++++++++- 9 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 src/main/events/user/block-user.ts create mode 100644 src/main/events/user/unblock-user.ts diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index dc1bb1d7..3385feb7 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -259,6 +259,7 @@ "ignore_request": "Ignore request", "cancel_request": "Cancel request", "undo_friendship": "Undo friendship", - "request_accepted": "Request accepted" + "request_accepted": "Request accepted", + "user_blocked_successfully": "User blocked successfully" } } diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json index c95ae82f..5d58b62f 100644 --- a/src/locales/pt/translation.json +++ b/src/locales/pt/translation.json @@ -259,6 +259,7 @@ "ignore_request": "Ignorar pedido", "cancel_request": "Cancelar pedido", "undo_friendship": "Desfazer amizade", - "request_accepted": "Pedido de amizade aceito" + "request_accepted": "Pedido de amizade aceito", + "user_blocked_successfully": "Usuário bloqueado com sucesso" } } diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 398235f0..5ae0db46 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -43,6 +43,8 @@ import "./auth/sign-out"; import "./auth/open-auth-window"; import "./auth/get-session-hash"; import "./user/get-user"; +import "./user/block-user"; +import "./user/unblock-user"; import "./user/get-user-friends"; import "./profile/get-friend-requests"; import "./profile/get-me"; diff --git a/src/main/events/user/block-user.ts b/src/main/events/user/block-user.ts new file mode 100644 index 00000000..303a5315 --- /dev/null +++ b/src/main/events/user/block-user.ts @@ -0,0 +1,11 @@ +import { registerEvent } from "../register-event"; +import { HydraApi } from "@main/services"; + +const blockUser = async ( + _event: Electron.IpcMainInvokeEvent, + userId: string +): Promise => { + return HydraApi.get(`/user/${userId}/block`); +}; + +registerEvent("block", blockUser); diff --git a/src/main/events/user/unblock-user.ts b/src/main/events/user/unblock-user.ts new file mode 100644 index 00000000..c1c8112e --- /dev/null +++ b/src/main/events/user/unblock-user.ts @@ -0,0 +1,11 @@ +import { registerEvent } from "../register-event"; +import { HydraApi } from "@main/services"; + +const unblockUser = async ( + _event: Electron.IpcMainInvokeEvent, + userId: string +): Promise => { + return HydraApi.get(`/user/${userId}/unblock`); +}; + +registerEvent("unblockUser", unblockUser); diff --git a/src/preload/index.ts b/src/preload/index.ts index cd3b9686..b7c368fa 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -145,6 +145,8 @@ contextBridge.exposeInMainWorld("electron", { /* User */ getUser: (userId: string) => ipcRenderer.invoke("getUser", userId), + blockUser: (userId: string) => ipcRenderer.invoke("blockUser", userId), + unblockUser: (userId: string) => ipcRenderer.invoke("unblockUser", userId), getUserFriends: (userId: string, take: number, skip: number) => ipcRenderer.invoke("getUserFriends", userId, take, skip), diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index ad0b8953..564d7501 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -128,6 +128,8 @@ declare global { /* User */ getUser: (userId: string) => Promise; + blockUser: (userId: string) => Promise; + unblockUser: (userId: string) => Promise; getUserFriends: ( userId: string, take: number, diff --git a/src/renderer/src/hooks/use-user-details.ts b/src/renderer/src/hooks/use-user-details.ts index 38f6a8f3..4b4d5b45 100644 --- a/src/renderer/src/hooks/use-user-details.ts +++ b/src/renderer/src/hooks/use-user-details.ts @@ -124,6 +124,14 @@ export function useUserDetails() { [fetchFriendRequests] ); + const blockUser = (userId: string) => { + return window.electron.blockUser(userId); + }; + + const unblockUser = (userId: string) => { + return window.electron.unblockUser(userId); + }; + return { userDetails, profileBackground, @@ -141,5 +149,7 @@ export function useUserDetails() { sendFriendRequest, fetchFriendRequests, updateFriendRequestState, + blockUser, + unblockUser, }; } diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index 228fdbd9..8224122b 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -50,6 +50,7 @@ export function UserContent({ fetchFriendRequests, showFriendsModal, updateFriendRequestState, + blockUser, } = useUserDetails(); const { showSuccessToast, showErrorToast } = useToast(); @@ -143,6 +144,17 @@ export function UserContent({ }); }; + const handleBlockUser = () => { + blockUser(userProfile.id) + .then(() => { + showSuccessToast(t("user_blocked_successfully")); + navigate(-1); + }) + .catch(() => { + showErrorToast(t("try_again")); + }); + }; + const handleCancelFriendRequest = (userId: string) => { updateFriendRequestState(userId, "CANCEL") .then(updateUserProfile) @@ -189,7 +201,7 @@ export function UserContent({ {t("add_friend")} - From 00c46bc98155989859d6f0d7f3e7c3837ec88ce8 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Thu, 25 Jul 2024 23:03:11 -0300 Subject: [PATCH 43/54] feat: implement undo friendship --- src/main/events/index.ts | 1 + src/main/events/profile/undo-friendship.ts | 11 +++++++++++ src/preload/index.ts | 2 ++ src/renderer/src/declaration.d.ts | 1 + src/renderer/src/hooks/use-user-details.ts | 5 +++++ src/renderer/src/pages/user/user-content.tsx | 7 +++++-- 6 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 src/main/events/profile/undo-friendship.ts diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 5ae0db46..57daf51c 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -48,6 +48,7 @@ import "./user/unblock-user"; import "./user/get-user-friends"; import "./profile/get-friend-requests"; import "./profile/get-me"; +import "./profile/undo-friendship"; import "./profile/update-friend-request"; import "./profile/update-profile"; import "./profile/send-friend-request"; diff --git a/src/main/events/profile/undo-friendship.ts b/src/main/events/profile/undo-friendship.ts new file mode 100644 index 00000000..d6f858f1 --- /dev/null +++ b/src/main/events/profile/undo-friendship.ts @@ -0,0 +1,11 @@ +import { registerEvent } from "../register-event"; +import { HydraApi } from "@main/services"; + +const undoFriendship = async ( + _event: Electron.IpcMainInvokeEvent, + userId: string +): Promise => { + return HydraApi.delete(`/profile/friends/${userId}`); +}; + +registerEvent("undoFriendship", undoFriendship); diff --git a/src/preload/index.ts b/src/preload/index.ts index b7c368fa..3350a340 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -135,6 +135,8 @@ contextBridge.exposeInMainWorld("electron", { /* Profile */ getMe: () => ipcRenderer.invoke("getMe"), + undoFriendship: (userId: string) => + ipcRenderer.invoke("undoFriendship", userId), updateProfile: (displayName: string, newProfileImagePath: string | null) => ipcRenderer.invoke("updateProfile", displayName, newProfileImagePath), getFriendRequests: () => ipcRenderer.invoke("getFriendRequests"), diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 564d7501..e022cffe 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -138,6 +138,7 @@ declare global { /* Profile */ getMe: () => Promise; + undoFriendship: (userId: string) => Promise; updateProfile: ( displayName: string, newProfileImagePath: string | null diff --git a/src/renderer/src/hooks/use-user-details.ts b/src/renderer/src/hooks/use-user-details.ts index 4b4d5b45..21690e7e 100644 --- a/src/renderer/src/hooks/use-user-details.ts +++ b/src/renderer/src/hooks/use-user-details.ts @@ -124,6 +124,10 @@ export function useUserDetails() { [fetchFriendRequests] ); + const undoFriendship = (userId: string) => { + return window.electron.undoFriendship(userId); + }; + const blockUser = (userId: string) => { return window.electron.blockUser(userId); }; @@ -151,5 +155,6 @@ export function useUserDetails() { updateFriendRequestState, blockUser, unblockUser, + undoFriendship, }; } diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index 8224122b..ad591790 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -50,6 +50,7 @@ export function UserContent({ fetchFriendRequests, showFriendsModal, updateFriendRequestState, + undoFriendship, blockUser, } = useUserDetails(); const { showSuccessToast, showErrorToast } = useToast(); @@ -127,9 +128,11 @@ export function UserContent({ const handleUndoFriendship = (userRelation: UserRelation) => { const userId = - userRelation.AId === userProfile.id ? userRelation.BId : userRelation.AId; + userRelation.AId === userDetails?.id + ? userRelation.BId + : userRelation.AId; - updateFriendRequestState(userId, "CANCEL") + undoFriendship(userId) .then(updateUserProfile) .catch(() => { showErrorToast(t("try_again")); From 15269f3908cec1491641fb119f12418bf72760d3 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Fri, 26 Jul 2024 11:30:01 -0300 Subject: [PATCH 44/54] feat: show confirm modal for block action --- src/locales/en/translation.json | 3 +- src/locales/pt/translation.json | 3 +- src/main/events/user/block-user.ts | 4 +- src/main/events/user/unblock-user.ts | 2 +- .../src/pages/user/user-block-modal.tsx | 44 +++++++++++++++++++ src/renderer/src/pages/user/user-content.tsx | 12 ++++- .../src/pages/user/user-signout-modal.tsx | 4 +- 7 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 src/renderer/src/pages/user/user-block-modal.tsx diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 3385feb7..b24509d3 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -260,6 +260,7 @@ "cancel_request": "Cancel request", "undo_friendship": "Undo friendship", "request_accepted": "Request accepted", - "user_blocked_successfully": "User blocked successfully" + "user_blocked_successfully": "User blocked successfully", + "user_block_modal_text": "This will block {{displayName}}" } } diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json index 5d58b62f..ef94c31f 100644 --- a/src/locales/pt/translation.json +++ b/src/locales/pt/translation.json @@ -260,6 +260,7 @@ "cancel_request": "Cancelar pedido", "undo_friendship": "Desfazer amizade", "request_accepted": "Pedido de amizade aceito", - "user_blocked_successfully": "Usuário bloqueado com sucesso" + "user_blocked_successfully": "Usuário bloqueado com sucesso", + "user_block_modal_text": "Bloquear {{displayName}}" } } diff --git a/src/main/events/user/block-user.ts b/src/main/events/user/block-user.ts index 303a5315..041b6530 100644 --- a/src/main/events/user/block-user.ts +++ b/src/main/events/user/block-user.ts @@ -5,7 +5,7 @@ const blockUser = async ( _event: Electron.IpcMainInvokeEvent, userId: string ): Promise => { - return HydraApi.get(`/user/${userId}/block`); + return HydraApi.post(`/user/${userId}/block`); }; -registerEvent("block", blockUser); +registerEvent("blockUser", blockUser); diff --git a/src/main/events/user/unblock-user.ts b/src/main/events/user/unblock-user.ts index c1c8112e..57cdb236 100644 --- a/src/main/events/user/unblock-user.ts +++ b/src/main/events/user/unblock-user.ts @@ -5,7 +5,7 @@ const unblockUser = async ( _event: Electron.IpcMainInvokeEvent, userId: string ): Promise => { - return HydraApi.get(`/user/${userId}/unblock`); + return HydraApi.post(`/user/${userId}/unblock`); }; registerEvent("unblockUser", unblockUser); diff --git a/src/renderer/src/pages/user/user-block-modal.tsx b/src/renderer/src/pages/user/user-block-modal.tsx new file mode 100644 index 00000000..e179e4da --- /dev/null +++ b/src/renderer/src/pages/user/user-block-modal.tsx @@ -0,0 +1,44 @@ +import { Button, Modal } from "@renderer/components"; +import * as styles from "./user.css"; +import { useTranslation } from "react-i18next"; + +export interface UserBlockModalProps { + visible: boolean; + displayName: string; + onConfirm: () => void; + onClose: () => void; +} + +export const UserBlockModal = ({ + visible, + displayName, + onConfirm, + onClose, +}: UserBlockModalProps) => { + const { t } = useTranslation("user_profile"); + + return ( + <> + +
+

+ {t("user_block_modal_text", { displayName })} +

+
+ + + +
+
+
+ + ); +}; diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index ad591790..88d9892d 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -28,6 +28,7 @@ import { Button, Link } from "@renderer/components"; import { UserEditProfileModal } from "./user-edit-modal"; import { UserSignOutModal } from "./user-signout-modal"; import { UserFriendModalTab } from "../shared-modals/user-friend-modal"; +import { UserBlockModal } from "./user-block-modal"; const MAX_MINUTES_TO_SHOW_IN_PLAYTIME = 120; @@ -59,6 +60,7 @@ export function UserContent({ useState(); const [showEditProfileModal, setShowEditProfileModal] = useState(false); const [showSignOutModal, setShowSignOutModal] = useState(false); + const [showUserBlockModal, setShowUserBlockModal] = useState(false); const { gameRunning } = useAppSelector((state) => state.gameRunning); @@ -150,6 +152,7 @@ export function UserContent({ const handleBlockUser = () => { blockUser(userProfile.id) .then(() => { + setShowUserBlockModal(false); showSuccessToast(t("user_blocked_successfully")); navigate(-1); }) @@ -204,7 +207,7 @@ export function UserContent({ {t("add_friend")} - @@ -272,6 +275,13 @@ export function UserContent({ onConfirm={handleConfirmSignout} /> + setShowUserBlockModal(false)} + onConfirm={handleBlockUser} + displayName={userProfile.displayName} + /> +
void; onClose: () => void; @@ -12,7 +12,7 @@ export const UserSignOutModal = ({ visible, onConfirm, onClose, -}: UserEditProfileModalProps) => { +}: UserSignOutModalProps) => { const { t } = useTranslation("user_profile"); return ( From b188e93343581003a66036109a1fbeafb2ab4f0b Mon Sep 17 00:00:00 2001 From: Aniol Date: Sat, 27 Jul 2024 13:05:15 +0200 Subject: [PATCH 45/54] Update Catalan translation.json Update Catalan translation.json. --- src/locales/ca/translation.json | 121 ++++++++++++++++++++++++++++++-- 1 file changed, 114 insertions(+), 7 deletions(-) diff --git a/src/locales/ca/translation.json b/src/locales/ca/translation.json index c756745a..d3ac5dbe 100644 --- a/src/locales/ca/translation.json +++ b/src/locales/ca/translation.json @@ -1,4 +1,7 @@ { +"app": { + "successfully_signed_in": "Has entrat correctament" + }, "home": { "featured": "Destacats", "trending": "Populars", @@ -14,7 +17,10 @@ "paused": "{{title}} (Pausat)", "downloading": "{{title}} ({{percentage}} - S'està baixant…)", "filter": "Filtra la biblioteca", - "home": "Inici" + "home": "Inici", + "queued": "{{title}} (En espera)", + "game_has_no_executable": "El joc encara no té un executable seleccionat", + "sign_in": "Entra" }, "header": { "search": "Cerca jocs", @@ -29,7 +35,9 @@ "bottom_panel": { "no_downloads_in_progress": "Cap baixada en curs", "downloading_metadata": "S'estan baixant les metadades de: {{title}}…", - "downloading": "S'està baixant: {{title}}… ({{percentage}} complet) - Finalització: {{eta}} - {{speed}}" + "downloading": "S'està baixant: {{title}}… ({{percentage}} complet) - Finalització: {{eta}} - {{speed}}", + "calculating_eta": "Descarregant {{title}}… ({{percentage}} completat) - Calculant el temps restant…", + "checking_files": "Comprovant els fitxers de {{title}}… ({{percentage}} completat)" }, "catalogue": { "next_page": "Pàgina següent", @@ -47,12 +55,14 @@ "cancel": "Cancel·la", "remove": "Elimina", "space_left_on_disk": "{{space}} lliures al disc", - "eta": "Finalització: {{eta}}", + "eta": "Finalitza en: {{eta}}", + "calculating_eta": "Calculant temps estimat…", "downloading_metadata": "S'estan baixant les metadades…", "filter": "Filtra els reempaquetats", "requirements": "Requisits del sistema", "minimum": "Mínims", "recommended": "Recomanats", + "paused": "Paused", "release_date": "Publicat el {{date}}", "publisher": "Publicat per {{publisher}}", "hours": "hores", @@ -81,7 +91,29 @@ "previous_screenshot": "Captura anterior", "next_screenshot": "Captura següent", "screenshot": "Captura {{number}}", - "open_screenshot": "Obre la captura {{number}}" + "open_screenshot": "Obre la captura {{number}}", + "download_settings": "Configuració de descàrrega", + "downloader": "Descarregador", + "select_executable": "Selecciona", + "no_executable_selected": "No hi ha executable selccionat", + "open_folder": "Obre carpeta", + "open_download_location": "Visualitzar fitxers descarregats", + "create_shortcut": "Crear accés directe a l'escriptori", + "remove_files": "Elimina fitxers", + "remove_from_library_title": "Segur?", + "remove_from_library_description": "Això eliminarà el videojoc {{game}} del teu catàleg", + "options": "Opcions", + "executable_section_title": "Executable", + "executable_section_description": "Directori del fitxer des d'on s'executarà quan es cliqui a \"Executar\"", + "downloads_secion_title": "Descàrregues", + "downloads_section_description": "Comprova actualitzacions o altres versions del videojoc", + "danger_zone_section_title": "Zona de perill", + "danger_zone_section_description": "Elimina aquest videojoc del teu catàleg o els fitxers descarregats per Hydra", + "download_in_progress": "Descàrrega en progrés", + "download_paused": "Descàrrega en pausa", + "last_downloaded_option": "Opció de l'última descàrrega", + "create_shortcut_success": "Accés directe creat satisfactòriament", + "create_shortcut_error": "Error al crear l'accés directe" }, "activation": { "title": "Activa l'Hydra", @@ -98,6 +130,7 @@ "paused": "Pausada", "verifying": "S'està verificant…", "completed": "Completada", + "removed": "No descarregat", "cancel": "Cancel·la", "filter": "Filtra els jocs baixats", "remove": "Elimina", @@ -106,7 +139,14 @@ "delete": "Elimina l'instal·lador", "delete_modal_title": "N'estàs segur?", "delete_modal_description": "S'eliminaran de l'ordinador tots els fitxers d'instal·lació", - "install": "Instal·la" + "install": "Instal·la", + "download_in_progress": "En progrés", + "queued_downloads": "Descàrregues en espera", + "downloads_completed": "Completat", + "queued": "En espera", + "no_downloads_title": "Buit", + "no_downloads_description": "No has descarregat res amb Hydra encara, però mai és tard per començar a fer-ho.", + "checking_files": "Comprovant fitxers…" }, "settings": { "downloads_path": "Ruta de baixades", @@ -119,16 +159,49 @@ "launch_with_system": "Inicia l'Hydra quan s'iniciï el sistema", "general": "General", "behavior": "Comportament", + "download_sources": "Fonts de descàrrega", + "language": "Idioma", + "real_debrid_api_token": "Testimoni API", "enable_real_debrid": "Activa el Real Debrid", + "real_debrid_description": "Real-Debrid és un programa de descàrrega sense restriccions que us permet descarregar fitxers a l'instant i al màxim de la vostra velocitat d'Internet.", + "real_debrid_invalid_token": "Invalida el testimoni de l'API", "real_debrid_api_token_hint": "Pots obtenir la teva clau de l'API <0>aquí.", - "save_changes": "Desa els canvis" + "real_debrid_free_account_error": "L'usuari \"{{username}}\" és un compte gratuït. Si us plau subscriu-te a Real-Debrid", + "real_debrid_linked_message": "Compte \"{{username}}\" vinculat", + "save_changes": "Desa els canvis", + "changes_saved": "Els canvis s'han desat correctament", + "download_sources_description": "Hydra buscarà els enllaços de descàrrega d'aquestes fonts. L'URL d'origen ha de ser un enllaç directe a un fitxer .json que contingui els enllaços de descàrrega.", + "validate_download_source": "Valida", + "remove_download_source": "Elimina", + "add_download_source": "Afegeix font", + "download_count_zero": "No hi ha baixades a la llista", + "download_count_one": "{{countFormatted}} a la llista de baixades", + "download_count_other": "{{countFormatted}} baixades a la llista", + "download_options_zero": "No hi ha cap descàrrega disponible", + "download_options_one": "{{countFormatted}} descàrrega disponible", + "download_options_other": "{{countFormatted}} baixades disponibles", + "download_source_url": "Descarrega l'URL de la font", + "add_download_source_description": "Inseriu la URL que conté el fitxer .json", + "download_source_up_to_date": "Actualitzat", + "download_source_errored": "S'ha produït un error", + "sync_download_sources": "Sincronitza fonts", + "removed_download_source": "S'ha eliminat la font de descàrrega", + "added_download_source": "Added download source", + "download_sources_synced": "Totes les fonts de descàrrega estan sincronitzades", + "insert_valid_json_url": "Insereix una URL JSON vàlida", + "found_download_option_zero": "No s'ha trobat cap opció de descàrrega", + "found_download_option_one": "S'ha trobat l'opció de baixada de {{countFormatted}}", + "found_download_option_other": "S'han trobat {{countFormatted}} opcions de baixada", + "import": "Import" }, "notifications": { "download_complete": "La baixada ha finalitzat", "game_ready_to_install": "{{title}} ja es pot instal·lar", "repack_list_updated": "S'ha actualitzat la llista de reempaquetats", "repack_count_one": "S'ha afegit {{count}} reempaquetat", - "repack_count_other": "S'han afegit {{count}} reempaquetats" + "repack_count_other": "S'han afegit {{count}} reempaquetats", + "new_update_available": "Versió {{version}} disponible", + "restart_to_install_update": "Reinicieu Hydra per instal·lar l'actualització" }, "system_tray": { "open": "Obre l'Hydra", @@ -144,5 +217,39 @@ }, "modal": { "close": "Botó de tancar" + }, + "forms": { + "toggle_password_visibility": "Commuta la visibilitat de la contrasenya" + }, + "user_profile": { + "amount_hours": "{{amount}} hores", + "amount_minutes": "{{amount}} minuts", + "last_time_played": "Última partida {{period}}", + "activity": "Activitat recent", + "library": "Biblioteca", + "total_play_time": "Temps total de joc:{{amount}}", + "no_recent_activity_title": "Hmmm… encara no res", + "no_recent_activity_description": "No has jugat a cap joc recentment. És el moment de canviar-ho!", + "display_name": "Nom de visualització", + "saving": "Desant", + "save": "Desa", + "edit_profile": "Edita el Perfil", + "saved_successfully": "S'ha desat correctament", + "try_again": "Siusplau torna-ho a provar", + "sign_out_modal_title": "Segur?", + "cancel": "Cancel·la", + "successfully_signed_out": "S'ha tancat la sessió correctament", + "sign_out": "Tanca sessió", + "playing_for": "Jugant per {{amount}}", + "sign_out_modal_text": "La vostra biblioteca està enllaçada amb el vostre compte actual. Quan tanqueu la sessió, la vostra biblioteca ja no serà visible i cap progrés no es desarà. Voleu continuar amb tancar la sessió?", + "add_friends": "Afegeix amics", + "add": "Afegeix", + "friend_code": "Codi de l'amic", + "see_profile": "Veure Perfil", + "sending": "Enviant", + "friend_request_sent": "Sol·licitud d'amistat enviada", + "friends": "Amistats", + "friends_list": "Llista d'amistats", + "user_not_found": "Usuari no trobat" } } From d60242a62cb3a72bf7b847d7e4b045e6b9356af1 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Mon, 29 Jul 2024 19:10:53 -0300 Subject: [PATCH 46/54] feat: code review --- src/main/events/user/get-user.ts | 8 +- src/renderer/src/pages/user/user-content.tsx | 118 ++++++++---------- ...nout-modal.tsx => user-sign-out-modal.tsx} | 0 src/types/index.ts | 3 +- 4 files changed, 62 insertions(+), 67 deletions(-) rename src/renderer/src/pages/user/{user-signout-modal.tsx => user-sign-out-modal.tsx} (100%) diff --git a/src/main/events/user/get-user.ts b/src/main/events/user/get-user.ts index 98b98a77..96a93042 100644 --- a/src/main/events/user/get-user.ts +++ b/src/main/events/user/get-user.ts @@ -50,7 +50,13 @@ const getUser = async ( }) ); - return { ...profile, libraryGames, recentGames, friends }; + return { + ...profile, + libraryGames, + recentGames, + friends: friends.friends, + totalFriends: friends.totalFriends, + }; } catch (err) { return null; } diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index 88d9892d..81db119d 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -1,4 +1,4 @@ -import { UserGame, UserProfile, UserRelation } from "@types"; +import { FriendRequestAction, UserGame, UserProfile } from "@types"; import cn from "classnames"; import * as styles from "./user.css"; import { SPACING_UNIT, vars } from "@renderer/theme.css"; @@ -26,7 +26,7 @@ import { } from "@primer/octicons-react"; import { Button, Link } from "@renderer/components"; import { UserEditProfileModal } from "./user-edit-modal"; -import { UserSignOutModal } from "./user-signout-modal"; +import { UserSignOutModal } from "./user-sign-out-modal"; import { UserFriendModalTab } from "../shared-modals/user-friend-modal"; import { UserBlockModal } from "./user-block-modal"; @@ -37,6 +37,8 @@ export interface ProfileContentProps { updateUserProfile: () => Promise; } +type FriendAction = FriendRequestAction | ("BLOCK" | "UNDO" | "SEND"); + export function UserContent({ userProfile, updateUserProfile, @@ -128,62 +130,35 @@ export function UserContent({ } }, [profileBackground, isMe]); - const handleUndoFriendship = (userRelation: UserRelation) => { - const userId = - userRelation.AId === userDetails?.id - ? userRelation.BId - : userRelation.AId; + const handleFriendAction = (userId: string, action: FriendAction) => { + try { + if (action === "UNDO") { + undoFriendship(userId).then(updateUserProfile); + return; + } - undoFriendship(userId) - .then(updateUserProfile) - .catch(() => { - showErrorToast(t("try_again")); - }); + if (action === "BLOCK") { + blockUser(userId).then(() => { + setShowUserBlockModal(false); + showSuccessToast(t("user_blocked_successfully")); + navigate(-1); + }); + + return; + } + + if (action === "SEND") { + sendFriendRequest(userProfile.id).then(updateUserProfile); + return; + } + + updateFriendRequestState(userId, action).then(updateUserProfile); + } catch (err) { + showErrorToast(t("try_again")); + } }; - const handleSendFriendRequest = () => { - sendFriendRequest(userProfile.id) - .then(updateUserProfile) - .catch(() => { - showErrorToast(t("try_again")); - }); - }; - - const handleBlockUser = () => { - blockUser(userProfile.id) - .then(() => { - setShowUserBlockModal(false); - showSuccessToast(t("user_blocked_successfully")); - navigate(-1); - }) - .catch(() => { - showErrorToast(t("try_again")); - }); - }; - - const handleCancelFriendRequest = (userId: string) => { - updateFriendRequestState(userId, "CANCEL") - .then(updateUserProfile) - .catch(() => { - showErrorToast(t("try_again")); - }); - }; - - const handleAcceptFriendRequest = (userId: string) => { - updateFriendRequestState(userId, "ACCEPTED") - .then(updateUserProfile) - .catch(() => { - showErrorToast(t("try_again")); - }); - }; - - const handleRefuseFriendRequest = (userId: string) => { - updateFriendRequestState(userId, "REFUSED") - .then(updateUserProfile) - .catch(() => { - showErrorToast(t("try_again")); - }); - }; + const showFriends = isMe || userProfile.totalFriends > 0; const getProfileActions = () => { if (isMe) { @@ -203,7 +178,10 @@ export function UserContent({ if (userProfile.relation == null) { return ( <> - @@ -215,12 +193,17 @@ export function UserContent({ } if (userProfile.relation.status === "ACCEPTED") { + const userId = + userProfile.relation.AId === userDetails?.id + ? userProfile.relation.BId + : userProfile.relation.AId; + return ( <> @@ -233,7 +216,9 @@ export function UserContent({ @@ -245,14 +230,18 @@ export function UserContent({ @@ -278,7 +267,7 @@ export function UserContent({ setShowUserBlockModal(false)} - onConfirm={handleBlockUser} + onConfirm={() => handleFriendAction(userProfile.id, "BLOCK")} displayName={userProfile.displayName} /> @@ -479,8 +468,7 @@ export function UserContent({ - {(isMe || - (userProfile.friends && userProfile.friends.totalFriends > 0)) && ( + {showFriends && (
@@ -512,7 +500,7 @@ export function UserContent({ gap: `${SPACING_UNIT}px`, }} > - {userProfile.friends.friends.map((friend) => { + {userProfile.friends.map((friend) => { return ( - ); + if (type === "ACCEPTED") { + return ( + + ); + } + + return null; }; return ( diff --git a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-list.tsx b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-list.tsx index 2c639847..52e646e0 100644 --- a/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-list.tsx +++ b/src/renderer/src/pages/shared-modals/user-friend-modal/user-friend-modal-list.tsx @@ -3,6 +3,8 @@ import { UserFriend } from "@types"; import { useEffect, useState } from "react"; import { UserFriendItem } from "./user-friend-item"; import { useNavigate } from "react-router-dom"; +import { useToast, useUserDetails } from "@renderer/hooks"; +import { useTranslation } from "react-i18next"; export interface UserFriendModalListProps { userId: string; @@ -15,12 +17,17 @@ export const UserFriendModalList = ({ userId, closeModal, }: UserFriendModalListProps) => { + const { t } = useTranslation("user_profile"); + const { showErrorToast } = useToast(); const navigate = useNavigate(); const [page, setPage] = useState(0); const [maxPage, setMaxPage] = useState(0); const [friends, setFriends] = useState([]); + const { userDetails, undoFriendship } = useUserDetails(); + const isMe = userDetails?.id == userId; + const loadNextPage = () => { if (page > maxPage) return; window.electron @@ -36,11 +43,15 @@ export const UserFriendModalList = ({ .catch(() => {}); }; - useEffect(() => { + const reloadList = () => { setPage(0); setMaxPage(0); setFriends([]); loadNextPage(); + }; + + useEffect(() => { + reloadList(); }, [userId]); const handleClickFriend = (userId: string) => { @@ -48,6 +59,16 @@ export const UserFriendModalList = ({ navigate(`/user/${userId}`); }; + const handleUndoFriendship = (userId: string) => { + undoFriendship(userId) + .then(() => { + reloadList(); + }) + .catch(() => { + showErrorToast(t("try_again")); + }); + }; + return (
{}} - onClickCancelRequest={() => {}} - onClickRefuseRequest={() => {}} onClickItem={handleClickFriend} - type={"ACCEPTED"} + onClickUndoFriendship={handleUndoFriendship} + type={isMe ? "ACCEPTED" : null} key={friend.id} /> ); diff --git a/src/types/index.ts b/src/types/index.ts index 0945d410..ac352a91 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -301,7 +301,7 @@ export interface UserProfile { id: string; displayName: string; profileImageUrl: string | null; - profileVisibility: "PUBLIC" | "PRIVATE" | "FRIEND"; + profileVisibility: "PUBLIC" | "PRIVATE" | "FRIENDS"; totalPlayTimeInSeconds: number; libraryGames: UserGame[]; recentGames: UserGame[]; From ba8201dabac169f57dc09e33da2a0841843ba5d6 Mon Sep 17 00:00:00 2001 From: Bayu Wilanda Date: Sat, 3 Aug 2024 09:50:22 +0700 Subject: [PATCH 51/54] feat: improve Indonesian translations for better readability and naturalness --- src/locales/id/translation.json | 247 ++++++++++++++++++++++++-------- 1 file changed, 184 insertions(+), 63 deletions(-) diff --git a/src/locales/id/translation.json b/src/locales/id/translation.json index bb7f452b..bcf7c32a 100644 --- a/src/locales/id/translation.json +++ b/src/locales/id/translation.json @@ -1,134 +1,255 @@ { + "app": { + "successfully_signed_in": "Berhasil masuk" + }, "home": { "featured": "Unggulan", - "trending": "Trending", - "surprise_me": "Kejutkan Saya", - "no_results": "Tidak ada hasil" + "trending": "Sedang Tren", + "surprise_me": "Kejutkan saya", + "no_results": "Tidak ada hasil ditemukan" }, "sidebar": { "catalogue": "Katalog", "downloads": "Unduhan", "settings": "Pengaturan", - "my_library": "Koleksi saya", + "my_library": "Perpustakaan saya", "downloading_metadata": "{{title}} (Mengunduh metadata…)", - "paused": "{{title}} (Terhenti)", + "paused": "{{title}} (Dijeda)", "downloading": "{{title}} ({{percentage}} - Mengunduh…)", - "filter": "Filter koleksi", - "home": "Beranda" + "filter": "Filter perpustakaan", + "home": "Beranda", + "queued": "{{title}} (Antrian)", + "game_has_no_executable": "Game tidak punya file eksekusi yang dipilih", + "sign_in": "Masuk" }, "header": { - "search": "Pencarian", + "search": "Cari game", "home": "Beranda", "catalogue": "Katalog", "downloads": "Unduhan", "search_results": "Hasil pencarian", - "settings": "Pengaturan" + "settings": "Pengaturan", + "version_available_install": "Versi {{version}} tersedia. Klik di sini untuk restart dan instal.", + "version_available_download": "Versi {{version}} tersedia. Klik di sini untuk unduh." }, "bottom_panel": { - "no_downloads_in_progress": "Tidak ada unduhan berjalan", - "downloading_metadata": "Mengunduh metadata {{title}}...", - "downloading": "Mengunduh {{title}}… ({{percentage}} selesai) - Perkiraan {{eta}} - {{speed}}" + "no_downloads_in_progress": "Tidak ada unduhan yang sedang berjalan", + "downloading_metadata": "Mengunduh metadata {{title}}…", + "downloading": "Mengunduh {{title}}… ({{percentage}} selesai) - Estimasi selesai {{eta}} - {{speed}}", + "calculating_eta": "Mengunduh {{title}}… ({{percentage}} selesai) - Menghitung waktu yang tersisa…", + "checking_files": "Memeriksa file {{title}}… ({{percentage}} selesai)" }, "catalogue": { - "next_page": "Halaman berikutnya", - "previous_page": "Halaman sebelumnya" + "next_page": "Halaman Berikutnya", + "previous_page": "Halaman Sebelumnya" }, "game_details": { "open_download_options": "Buka opsi unduhan", "download_options_zero": "Tidak ada opsi unduhan", "download_options_one": "{{count}} opsi unduhan", "download_options_other": "{{count}} opsi unduhan", - "updated_at": "Diperbarui {{updated_at}}", - "install": "Install", + "updated_at": "Diperbarui pada {{updated_at}}", + "install": "Instal", "resume": "Lanjutkan", - "pause": "Hentikan sementara", - "cancel": "Batalkan", + "pause": "Jeda", + "cancel": "Batal", "remove": "Hapus", - "space_left_on_disk": "{{space}} tersisa pada disk", - "eta": "Perkiraan {{eta}}", + "space_left_on_disk": "{{space}} tersisa di disk", + "eta": "Estimasi {{eta}}", + "calculating_eta": "Menghitung waktu yang tersisa…", "downloading_metadata": "Mengunduh metadata…", - "filter": "Saring repacks", - "requirements": "Keperluan sistem", + "filter": "Filter repack", + "requirements": "Persyaratan sistem", "minimum": "Minimum", - "recommended": "Rekomendasi", + "recommended": "Dianjurkan", + "paused": "Dijeda", "release_date": "Dirilis pada {{date}}", - "publisher": "Dipublikasikan oleh {{publisher}}", + "publisher": "Diterbitkan oleh {{publisher}}", "hours": "jam", "minutes": "menit", "amount_hours": "{{amount}} jam", "amount_minutes": "{{amount}} menit", "accuracy": "{{accuracy}}% akurasi", - "add_to_library": "Tambahkan ke koleksi", - "remove_from_library": "Hapus dari koleksi", - "no_downloads": "Tidak ada unduhan tersedia", + "add_to_library": "Tambah ke perpustakaan", + "remove_from_library": "Hapus dari perpustakaan", + "no_downloads": "Tidak ada yang bisa diunduh", "play_time": "Dimainkan selama {{amount}}", "last_time_played": "Terakhir dimainkan {{period}}", "not_played_yet": "Kamu belum memainkan {{title}}", - "next_suggestion": "Rekomendasi berikutnya", - "play": "Mainkan", + "next_suggestion": "Saran berikutnya", + "play": "Main", "deleting": "Menghapus installer…", "close": "Tutup", - "playing_now": "Memainkan sekarang", + "playing_now": "Sedang dimainkan", "change": "Ubah", - "repacks_modal_description": "Pilih repack yang kamu ingin unduh", - "select_folder_hint": "Untuk merubah folder bawaan, akses melalui", - "download_now": "Unduh sekarang" + "repacks_modal_description": "Pilih repack yang ingin kamu unduh", + "select_folder_hint": "Untuk ganti folder default, buka <0>Pengaturan", + "download_now": "Unduh sekarang", + "no_shop_details": "Gagal mendapatkan detail toko.", + "download_options": "Opsi unduhan", + "download_path": "Path unduhan", + "previous_screenshot": "Screenshot sebelumnya", + "next_screenshot": "Screenshot berikutnya", + "screenshot": "Screenshot {{number}}", + "open_screenshot": "Buka screenshot {{number}}", + "download_settings": "Pengaturan unduhan", + "downloader": "Pengunduh", + "select_executable": "Pilih", + "no_executable_selected": "Tidak ada file eksekusi yang dipilih", + "open_folder": "Buka folder", + "open_download_location": "Lihat file yang diunduh", + "create_shortcut": "Buat pintasan desktop", + "remove_files": "Hapus file", + "remove_from_library_title": "Apa kamu yakin?", + "remove_from_library_description": "Ini akan menghapus {{game}} dari perpustakaan kamu", + "options": "Opsi", + "executable_section_title": "Eksekusi", + "executable_section_description": "Path file eksekusi saat \"Main\" diklik", + "downloads_secion_title": "Unduhan", + "downloads_section_description": "Cek update atau versi lain dari game ini", + "danger_zone_section_title": "Zona Berbahaya", + "danger_zone_section_description": "Hapus game ini dari perpustakaan kamu atau file yang diunduh oleh Hydra", + "download_in_progress": "Sedang mengunduh", + "download_paused": "Unduhan dijeda", + "last_downloaded_option": "Opsi terakhir diunduh", + "create_shortcut_success": "Pintasan berhasil dibuat", + "create_shortcut_error": "Gagal membuat pintasan" }, "activation": { - "title": "Aktivasi Hydra", - "installation_id": "ID instalasi:", - "enter_activation_code": "Masukkan kode aktivasi", - "message": "Jika kamu tidak tau dimana bertanya untuk ini, maka kamu tidak seharusnya memiliki ini.", + "title": "Aktifkan Hydra", + "installation_id": "ID Instalasi:", + "enter_activation_code": "Masukkan kode aktivasi kamu", + "message": "Kalau tidak tahu harus tanya ke siapa, berarti kamu tidak perlu ini.", "activate": "Aktifkan", "loading": "Memuat…" }, "downloads": { "resume": "Lanjutkan", - "pause": "Hentikan sementara", - "eta": "Perkiraan {{eta}}", - "paused": "Terhenti sementara", - "verifying": "Memeriksa…", + "pause": "Jeda", + "eta": "Estimasi {{eta}}", + "paused": "Dijeda", + "verifying": "Verifikasi…", "completed": "Selesai", - "cancel": "Batalkan", - "filter": "Saring game yang diunduh", + "removed": "Tidak diunduh", + "cancel": "Batal", + "filter": "Filter game yang diunduh", "remove": "Hapus", "downloading_metadata": "Mengunduh metadata…", - "deleting": "Menghapus file instalasi…", - "delete": "Hapus file instalasi", - "delete_modal_title": "Kamu yakin?", - "delete_modal_description": "Proses ini akan menghapus semua file instalasi dari komputer kamu", - "install": "Install" + "deleting": "Menghapus installer…", + "delete": "Hapus installer", + "delete_modal_title": "Apa kamu yakin?", + "delete_modal_description": "Ini akan menghapus semua file instalasi dari komputer kamu", + "install": "Instal", + "download_in_progress": "Sedang berlangsung", + "queued_downloads": "Unduhan dalam antrian", + "downloads_completed": "Selesai", + "queued": "Dalam antrian", + "no_downloads_title": "Kosong", + "no_downloads_description": "Kamu belum mengunduh apa pun dengan Hydra, tapi belum terlambat untuk mulai.", + "checking_files": "Memeriksa file…" }, "settings": { - "downloads_path": "Lokasi unduhan", - "change": "Perbarui", - "notifications": "Pengingat", + "downloads_path": "Path unduhan", + "change": "Ganti", + "notifications": "Notifikasi", "enable_download_notifications": "Saat unduhan selesai", - "enable_repack_list_notifications": "Saat repack terbaru ditambahkan", + "enable_repack_list_notifications": "Saat ada repack baru", + "real_debrid_api_token_label": "Token API Real-Debrid", + "quit_app_instead_hiding": "Jangan sembunyikan Hydra saat ditutup", + "launch_with_system": "Jalankan Hydra saat sistem dinyalakan", + "general": "Umum", "behavior": "Perilaku", - "quit_app_instead_hiding": "Tutup aplikasi alih-alih menyembunyikan aplikasi", - "launch_with_system": "Jalankan saat memulai sistem" + "download_sources": "Sumber unduhan", + "language": "Bahasa", + "real_debrid_api_token": "Token API", + "enable_real_debrid": "Aktifkan Real-Debrid", + "real_debrid_description": "Real-Debrid adalah downloader tanpa batas yang memungkinkan kamu untuk mengunduh file dengan cepat dan pada kecepatan terbaik dari Internet kamu.", + "real_debrid_invalid_token": "Token API tidak valid", + "real_debrid_api_token_hint": "Kamu bisa dapatkan token API di <0>sini", + "real_debrid_free_account_error": "Akun \"{{username}}\" adalah akun gratis. Silakan berlangganan Real-Debrid", + "real_debrid_linked_message": "Akun \"{{username}}\" terhubung", + "save_changes": "Simpan perubahan", + "changes_saved": "Perubahan disimpan berhasil", + "download_sources_description": "Hydra akan mencari link unduhan dari sini. URL harus menuju file .json dengan link unduhan.", + "validate_download_source": "Validasi", + "remove_download_source": "Hapus", + "add_download_source": "Tambahkan sumber", + "download_count_zero": "Tidak ada unduhan dalam daftar", + "download_count_one": "{{countFormatted}} unduhan dalam daftar", + "download_count_other": "{{countFormatted}} unduhan dalam daftar", + "download_options_zero": "Tidak ada unduhan tersedia", + "download_options_one": "{{countFormatted}} unduhan tersedia", + "download_options_other": "{{countFormatted}} unduhan tersedia", + "download_source_url": "URL sumber unduhan", + "add_download_source_description": "Masukkan URL yang berisi file .json", + "download_source_up_to_date": "Terkini", + "download_source_errored": "Terjadi kesalahan", + "sync_download_sources": "Sinkronkan sumber", + "removed_download_source": "Sumber unduhan dihapus", + "added_download_source": "Sumber unduhan ditambahkan", + "download_sources_synced": "Semua sumber unduhan disinkronkan", + "insert_valid_json_url": "Masukkan URL JSON yang valid", + "found_download_option_zero": "Tidak ada opsi unduhan ditemukan", + "found_download_option_one": "Ditemukan {{countFormatted}} opsi unduhan", + "found_download_option_other": "Ditemukan {{countFormatted}} opsi unduhan", + "import": "Impor" }, "notifications": { "download_complete": "Unduhan selesai", - "game_ready_to_install": "{{title}} sudah siap untuk instalasi", + "game_ready_to_install": "{{title}} siap untuk diinstal", "repack_list_updated": "Daftar repack diperbarui", "repack_count_one": "{{count}} repack ditambahkan", - "repack_count_other": "{{count}} repack ditambahkan" + "repack_count_other": "{{count}} repack ditambahkan", + "new_update_available": "Versi {{version}} tersedia", + "restart_to_install_update": "Restart Hydra untuk instal pembaruan" }, "system_tray": { "open": "Buka Hydra", - "quit": "Tutup" + "quit": "Keluar" }, "game_card": { - "no_downloads": "Tidak ada unduhan tersedia" + "no_downloads": "Tidak ada unduhan yang tersedia" }, "binary_not_found_modal": { - "title": "Program tidak terinstal", - "description": "Wine atau Lutris exe tidak ditemukan pada sistem kamu", - "instructions": "Periksa cara instalasi yang benar pada Linux distro-mu agar game dapat dimainkan dengan benar" + "title": "Program tidak terpasang", + "description": "Executable Wine atau Lutris tidak ditemukan di sistem kamu", + "instructions": "Cek cara instalasi yang benar di distro Linux kamu agar game bisa jalan normal" }, "modal": { - "close": "Tombol tutup" + "close": "Tutup" + }, + "forms": { + "toggle_password_visibility": "Tampilkan/Sembunyikan kata sandi" + }, + "user_profile": { + "amount_hours": "{{amount}} jam", + "amount_minutes": "{{amount}} menit", + "last_time_played": "Terakhir dimainkan {{period}}", + "activity": "Aktivitas terbaru", + "library": "Perpustakaan", + "total_play_time": "Total waktu bermain: {{amount}}", + "no_recent_activity_title": "Hmm… kosong di sini", + "no_recent_activity_description": "Kamu belum main game baru-baru ini. Yuk, mulai main!", + "display_name": "Nama tampilan", + "saving": "Menyimpan", + "save": "Simpan", + "edit_profile": "Edit Profil", + "saved_successfully": "Berhasil disimpan", + "try_again": "Coba lagi yuk", + "sign_out_modal_title": "Apa kamu yakin?", + "cancel": "Batal", + "successfully_signed_out": "Berhasil keluar", + "sign_out": "Keluar", + "playing_for": "Bermain selama {{amount}}", + "sign_out_modal_text": "Perpustakaan kamu terhubung dengan akun saat ini. Saat keluar, perpustakaan kamu tidak akan terlihat lagi, dan progres tidak akan disimpan. Lanjutkan keluar?", + "add_friends": "Tambah Teman", + "add": "Tambah", + "friend_code": "Kode teman", + "see_profile": "Lihat profil", + "sending": "Mengirim", + "friend_request_sent": "Permintaan teman terkirim", + "friends": "Teman", + "friends_list": "Daftar teman", + "user_not_found": "Pengguna tidak ditemukan" } -} +} \ No newline at end of file From 77280788a7147adb23cdc81f6b06edababe04213 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Mon, 5 Aug 2024 10:44:55 -0300 Subject: [PATCH 52/54] Format ca/translation.json --- src/locales/ca/translation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/ca/translation.json b/src/locales/ca/translation.json index d3ac5dbe..9124af79 100644 --- a/src/locales/ca/translation.json +++ b/src/locales/ca/translation.json @@ -1,5 +1,5 @@ { -"app": { + "app": { "successfully_signed_in": "Has entrat correctament" }, "home": { From e851600814db1cbc492b16b75d53b0198f7f427d Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Mon, 5 Aug 2024 11:07:44 -0300 Subject: [PATCH 53/54] Format id/translation.json --- src/locales/id/translation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/id/translation.json b/src/locales/id/translation.json index bcf7c32a..198aa568 100644 --- a/src/locales/id/translation.json +++ b/src/locales/id/translation.json @@ -252,4 +252,4 @@ "friends_list": "Daftar teman", "user_not_found": "Pengguna tidak ditemukan" } -} \ No newline at end of file +} From e44c15e9e1442d686507d402d0e510aff2b29bf3 Mon Sep 17 00:00:00 2001 From: Bayu Wilanda Date: Tue, 6 Aug 2024 11:11:05 +0700 Subject: [PATCH 54/54] fix: video won't load due to Content Security Policy restrictions --- src/renderer/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/index.html b/src/renderer/index.html index 543b85a9..d7abf3ad 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -6,7 +6,7 @@ Hydra