fix: using local state for game running

This commit is contained in:
Chubby Granny Chaser 2024-09-14 02:27:58 +01:00
parent 6368bd66d3
commit d15ca88714
No known key found for this signature in database
9 changed files with 112 additions and 37 deletions

View file

@ -293,6 +293,7 @@
"image_process_failure": "Falha ao processar a imagem", "image_process_failure": "Falha ao processar a imagem",
"required_field": "Este campo é obrigatório", "required_field": "Este campo é obrigatório",
"displayname_min_length": "Nome de exibição deve ter pelo menos 3 caracteres", "displayname_min_length": "Nome de exibição deve ter pelo menos 3 caracteres",
"displayname_max_length": "Nome de exibição deve ter no máximo 50 caracteres" "displayname_max_length": "Nome de exibição deve ter no máximo 50 caracteres",
"locked_profile": "Este perfil é privado"
} }
} }

View file

@ -1,7 +1,7 @@
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { PeopleIcon, PersonIcon } from "@primer/octicons-react"; import { PeopleIcon, PersonIcon } from "@primer/octicons-react";
import * as styles from "./sidebar-profile.css"; import * as styles from "./sidebar-profile.css";
import { useUserDetails } from "@renderer/hooks"; import { useAppSelector, useUserDetails } from "@renderer/hooks";
import { useMemo } from "react"; import { useMemo } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal"; import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal";
@ -13,6 +13,8 @@ export function SidebarProfile() {
const { userDetails, friendRequests, showFriendsModal } = useUserDetails(); const { userDetails, friendRequests, showFriendsModal } = useUserDetails();
const { gameRunning } = useAppSelector((state) => state.gameRunning);
const receivedRequests = useMemo(() => { const receivedRequests = useMemo(() => {
return friendRequests.filter((request) => request.type === "RECEIVED"); return friendRequests.filter((request) => request.type === "RECEIVED");
}, [friendRequests]); }, [friendRequests]);
@ -74,19 +76,19 @@ export function SidebarProfile() {
{userDetails ? userDetails.displayName : t("sign_in")} {userDetails ? userDetails.displayName : t("sign_in")}
</p> </p>
{userDetails && userDetails.currentGame && ( {userDetails && gameRunning && (
<div> <div>
<small>{userDetails.currentGame.title}</small> <small>{gameRunning.title}</small>
</div> </div>
)} )}
</div> </div>
{userDetails && userDetails.currentGame && ( {userDetails && gameRunning && (
<img <img
alt={userDetails.currentGame.title} alt={gameRunning.title}
width={24} width={24}
style={{ borderRadius: 4 }} style={{ borderRadius: 4 }}
src={userDetails.currentGame.iconUrl!} src={gameRunning.iconUrl!}
/> />
)} )}
</div> </div>

View file

@ -23,6 +23,8 @@ const SIDEBAR_MAX_WIDTH = 450;
const initialSidebarWidth = window.localStorage.getItem("sidebarWidth"); const initialSidebarWidth = window.localStorage.getItem("sidebarWidth");
export function Sidebar() { export function Sidebar() {
const filterRef = useRef<HTMLInputElement>(null);
const { t } = useTranslation("sidebar"); const { t } = useTranslation("sidebar");
const { library, updateLibrary } = useLibrary(); const { library, updateLibrary } = useLibrary();
const navigate = useNavigate(); const navigate = useNavigate();
@ -78,6 +80,10 @@ export function Sidebar() {
useEffect(() => { useEffect(() => {
setFilteredLibrary(sortedLibrary); setFilteredLibrary(sortedLibrary);
if (filterRef.current) {
filterRef.current.value = "";
}
}, [sortedLibrary]); }, [sortedLibrary]);
useEffect(() => { useEffect(() => {
@ -139,7 +145,7 @@ export function Sidebar() {
navigate(path); navigate(path);
} }
if (event.detail == 2) { if (event.detail === 2) {
if (game.executablePath) { if (game.executablePath) {
window.electron.openGame(game.id, game.executablePath); window.electron.openGame(game.id, game.executablePath);
} else { } else {
@ -190,6 +196,7 @@ export function Sidebar() {
<small className={styles.sectionTitle}>{t("my_library")}</small> <small className={styles.sectionTitle}>{t("my_library")}</small>
<TextField <TextField
ref={filterRef}
placeholder={t("filter")} placeholder={t("filter")}
onChange={handleFilter} onChange={handleFilter}
theme="dark" theme="dark"

View file

@ -110,6 +110,7 @@ export function GameDetails() {
}; };
const handleNSFWContentRefuse = () => { const handleNSFWContentRefuse = () => {
setHasNSFWContentBlocked(false);
navigate(-1); navigate(-1);
}; };

View file

@ -7,10 +7,6 @@ export const contentSidebar = style({
width: "100%", width: "100%",
height: "100%", height: "100%",
"@media": { "@media": {
"(min-width: 768px)": {
width: "100%",
maxWidth: "200px",
},
"(min-width: 1024px)": { "(min-width: 1024px)": {
maxWidth: "300px", maxWidth: "300px",
width: "100%", width: "100%",
@ -90,6 +86,14 @@ export const statsSection = style({
gap: `${SPACING_UNIT * 2}px`, gap: `${SPACING_UNIT * 2}px`,
padding: `${SPACING_UNIT * 2}px`, padding: `${SPACING_UNIT * 2}px`,
justifyContent: "space-between", justifyContent: "space-between",
"@media": {
"(min-width: 1024px)": {
flexDirection: "column",
},
"(min-width: 1280px)": {
flexDirection: "row",
},
},
}); });
export const statsCategoryTitle = style({ export const statsCategoryTitle = style({
@ -104,7 +108,6 @@ export const statsCategory = style({
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
gap: `${SPACING_UNIT / 2}px`, gap: `${SPACING_UNIT / 2}px`,
alignItems: "flex-end",
}); });
globalStyle(`${requirementsDetails} a`, { globalStyle(`${requirementsDetails} a`, {

View file

@ -124,3 +124,24 @@ export const gamesGrid = style({
}, },
}, },
}); });
export const telescopeIcon = style({
width: "60px",
height: "60px",
borderRadius: "50%",
backgroundColor: "rgba(255, 255, 255, 0.06)",
display: "flex",
alignItems: "center",
justifyContent: "center",
marginBottom: `${SPACING_UNIT * 2}px`,
});
export const noGames = style({
display: "flex",
width: "100%",
height: "100%",
justifyContent: "center",
alignItems: "center",
flexDirection: "column",
gap: `${SPACING_UNIT}px`,
});

View file

@ -29,11 +29,6 @@ export function ProfileContent() {
} }
}, [userProfile, dispatch]); }, [userProfile, dispatch]);
const truncatedGamesList = useMemo(() => {
if (!userProfile) return [];
return userProfile?.libraryGames.slice(0, 12);
}, [userProfile]);
const { numberFormatter } = useFormat(); const { numberFormatter } = useFormat();
const navigate = useNavigate(); const navigate = useNavigate();
@ -91,8 +86,16 @@ export function ProfileContent() {
<h3>{numberFormatter.format(userProfile.libraryGames.length)}</h3> <h3>{numberFormatter.format(userProfile.libraryGames.length)}</h3>
</div> </div>
{/* <div className={styles.noGames}>
<div className={styles.telescopeIcon}>
<TelescopeIcon size={24} />
</div>
<h2>{t("no_recent_activity_title")}</h2>
{isMe && <p>{t("no_recent_activity_description")}</p>}
</div> */}
<ul className={styles.gamesGrid}> <ul className={styles.gamesGrid}>
{truncatedGamesList.map((game) => ( {userProfile?.libraryGames?.map((game) => (
<li <li
key={game.objectId} key={game.objectId}
style={{ style={{
@ -128,7 +131,7 @@ export function ProfileContent() {
<div className={styles.rightContent}> <div className={styles.rightContent}>
<div> <div>
<div className={styles.sectionHeader}> <div className={styles.sectionHeader}>
<h2>Played recently</h2> <h2>{t("activity")}</h2>
</div> </div>
<div className={styles.box}> <div className={styles.box}>
@ -216,7 +219,6 @@ export function ProfileContent() {
formatPlayTime, formatPlayTime,
numberFormatter, numberFormatter,
t, t,
truncatedGamesList,
usersAreFriends, usersAreFriends,
isMe, isMe,
navigate, navigate,

View file

@ -4,8 +4,10 @@ import * as styles from "./profile-hero.css";
import { useCallback, useContext, useMemo, useState } from "react"; import { useCallback, useContext, useMemo, useState } from "react";
import { userProfileContext } from "@renderer/context"; import { userProfileContext } from "@renderer/context";
import { import {
BlockedIcon,
CheckCircleFillIcon, CheckCircleFillIcon,
PencilIcon, PencilIcon,
PersonAddIcon,
PersonIcon, PersonIcon,
SignOutIcon, SignOutIcon,
XCircleFillIcon, XCircleFillIcon,
@ -13,7 +15,12 @@ import {
import { buildGameDetailsPath } from "@renderer/helpers"; import { buildGameDetailsPath } from "@renderer/helpers";
import { Button, Link } from "@renderer/components"; import { Button, Link } from "@renderer/components";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useDate, useToast, useUserDetails } from "@renderer/hooks"; import {
useAppSelector,
useDate,
useToast,
useUserDetails,
} from "@renderer/hooks";
import { addSeconds } from "date-fns"; import { addSeconds } from "date-fns";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
@ -37,10 +44,11 @@ export function ProfileHero() {
blockUser, blockUser,
} = useUserDetails(); } = useUserDetails();
const { gameRunning } = useAppSelector((state) => state.gameRunning);
const { isMe, heroBackground, getUserProfile } = context; const { isMe, heroBackground, getUserProfile } = context;
const userProfile = context.userProfile!; const userProfile = context.userProfile!;
const { currentGame } = userProfile;
const { t } = useTranslation("user_profile"); const { t } = useTranslation("user_profile");
const { formatDistance } = useDate(); const { formatDistance } = useDate();
@ -63,17 +71,17 @@ export function ProfileHero() {
}, [navigate, signOut, showSuccessToast, t]); }, [navigate, signOut, showSuccessToast, t]);
const handleFriendAction = useCallback( const handleFriendAction = useCallback(
(userId: string, action: FriendAction) => { async (userId: string, action: FriendAction) => {
setIsPerformingAction(true); setIsPerformingAction(true);
try { try {
if (action === "UNDO_FRIENDSHIP") { if (action === "UNDO_FRIENDSHIP") {
undoFriendship(userId).then(getUserProfile); await undoFriendship(userId).then(getUserProfile);
return; return;
} }
if (action === "BLOCK") { if (action === "BLOCK") {
blockUser(userId).then(() => { await blockUser(userId).then(() => {
showSuccessToast(t("user_blocked_successfully")); showSuccessToast(t("user_blocked_successfully"));
navigate(-1); navigate(-1);
}); });
@ -82,11 +90,11 @@ export function ProfileHero() {
} }
if (action === "SEND") { if (action === "SEND") {
sendFriendRequest(userProfile.id).then(getUserProfile); await sendFriendRequest(userProfile.id).then(getUserProfile);
return; return;
} }
updateFriendRequestState(userId, action).then(getUserProfile); await updateFriendRequestState(userId, action).then(getUserProfile);
} catch (err) { } catch (err) {
showErrorToast(t("try_again")); showErrorToast(t("try_again"));
} finally { } finally {
@ -140,6 +148,7 @@ export function ProfileHero() {
onClick={() => handleFriendAction(userProfile.id, "SEND")} onClick={() => handleFriendAction(userProfile.id, "SEND")}
disabled={isPerformingAction} disabled={isPerformingAction}
> >
<PersonAddIcon />
{t("add_friend")} {t("add_friend")}
</Button> </Button>
@ -148,6 +157,7 @@ export function ProfileHero() {
onClick={() => handleFriendAction(userProfile.id, "BLOCK")} onClick={() => handleFriendAction(userProfile.id, "BLOCK")}
disabled={isPerformingAction} disabled={isPerformingAction}
> >
<BlockedIcon />
{t("block_user")} {t("block_user")}
</Button> </Button>
</> </>
@ -218,6 +228,20 @@ export function ProfileHero() {
} }
}, [isMe]); }, [isMe]);
const currentGame = useMemo(() => {
if (isMe) {
if (gameRunning)
return {
...gameRunning,
objectId: gameRunning.objectID,
sessionDurationInSeconds: gameRunning.sessionDurationInMillis / 1000,
};
return null;
}
return userProfile.currentGame;
}, [isMe, userProfile, gameRunning]);
return ( return (
<> <>
{/* <ConfirmationModal {/* <ConfirmationModal
@ -289,8 +313,14 @@ export function ProfileHero() {
</div> </div>
<div className={styles.heroPanel}> <div className={styles.heroPanel}>
<div></div> <div
<div style={{ display: "flex", gap: `${SPACING_UNIT}px` }}> style={{
display: "flex",
gap: `${SPACING_UNIT}px`,
justifyContent: "flex-end",
flex: 1,
}}
>
{profileActions} {profileActions}
</div> </div>
</div> </div>

View file

@ -12,17 +12,25 @@ import {
SettingsContextProvider, SettingsContextProvider,
} from "@renderer/context"; } from "@renderer/context";
import { SettingsPrivacy } from "./settings-privacy"; import { SettingsPrivacy } from "./settings-privacy";
import { useUserDetails } from "@renderer/hooks";
import { useMemo } from "react";
export function Settings() { export function Settings() {
const { t } = useTranslation("settings"); const { t } = useTranslation("settings");
const categories = [ const { userDetails } = useUserDetails();
t("general"),
t("behavior"), const categories = useMemo(() => {
t("download_sources"), const categories = [
"Real-Debrid", t("general"),
t("privacy"), t("behavior"),
]; t("download_sources"),
"Real-Debrid",
];
if (userDetails) return [...categories, t("privacy")];
return categories;
}, [userDetails, t]);
return ( return (
<SettingsContextProvider> <SettingsContextProvider>