mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-11 13:56:16 +00:00
feat: added pin/unpin to the game card in profile
This commit is contained in:
@@ -14,6 +14,7 @@ export interface UserProfileContext {
|
||||
isMe: boolean;
|
||||
userStats: UserStats | null;
|
||||
getUserProfile: () => Promise<void>;
|
||||
getUserLibraryGames: () => Promise<void>;
|
||||
setSelectedBackgroundImage: React.Dispatch<React.SetStateAction<string>>;
|
||||
backgroundImage: string;
|
||||
badges: Badge[];
|
||||
@@ -29,6 +30,7 @@ export const userProfileContext = createContext<UserProfileContext>({
|
||||
isMe: false,
|
||||
userStats: null,
|
||||
getUserProfile: async () => {},
|
||||
getUserLibraryGames: async () => {},
|
||||
setSelectedBackgroundImage: () => {},
|
||||
backgroundImage: "",
|
||||
badges: [],
|
||||
@@ -149,6 +151,7 @@ export function UserProfileContextProvider({
|
||||
heroBackground,
|
||||
isMe,
|
||||
getUserProfile,
|
||||
getUserLibraryGames,
|
||||
setSelectedBackgroundImage,
|
||||
backgroundImage: getBackgroundImageUrl(),
|
||||
userStats,
|
||||
|
||||
@@ -84,7 +84,8 @@ export function ProfileContent() {
|
||||
const hasPinnedGames = pinnedGames.length > 0;
|
||||
const hasAnyGames = hasGames || hasPinnedGames;
|
||||
|
||||
const shouldShowRightContent = hasAnyGames || userProfile.friends.length > 0;
|
||||
const shouldShowRightContent =
|
||||
hasAnyGames || userProfile.friends.length > 0;
|
||||
|
||||
return (
|
||||
<section className="profile-content__section">
|
||||
@@ -127,7 +128,9 @@ export function ProfileContent() {
|
||||
<div className="profile-content__section-header">
|
||||
<h2>{t("library")}</h2>
|
||||
{userStats && (
|
||||
<span>{numberFormatter.format(userStats.libraryCount)}</span>
|
||||
<span>
|
||||
{numberFormatter.format(userStats.libraryCount)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -65,18 +65,45 @@
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
&__favorite-icon {
|
||||
&__actions-container {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
color: #ff6b6b;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&__favorite-icon {
|
||||
color: white;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
border-radius: 50%;
|
||||
padding: 4px;
|
||||
padding: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&__pin-button {
|
||||
color: white;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
padding: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
&__playtime {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { UserGame } from "@types";
|
||||
import HydraIcon from "@renderer/assets/icons/hydra.svg?react";
|
||||
import { useFormat } from "@renderer/hooks";
|
||||
import { useFormat, useToast } from "@renderer/hooks";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useCallback, useContext, useState } from "react";
|
||||
import {
|
||||
@@ -14,6 +14,8 @@ import {
|
||||
TrophyIcon,
|
||||
AlertFillIcon,
|
||||
HeartFillIcon,
|
||||
PinIcon,
|
||||
PinSlashIcon,
|
||||
} from "@primer/octicons-react";
|
||||
import { MAX_MINUTES_TO_SHOW_IN_PLAYTIME } from "@renderer/constants";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
@@ -33,11 +35,14 @@ export function UserLibraryGameCard({
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
}: UserLibraryGameCardProps) {
|
||||
const { userProfile } = useContext(userProfileContext);
|
||||
const { userProfile, isMe, getUserLibraryGames } = useContext(userProfileContext);
|
||||
const { t } = useTranslation("user_profile");
|
||||
const { t: tGame } = useTranslation("game_details");
|
||||
const { numberFormatter } = useFormat();
|
||||
const { showSuccessToast } = useToast();
|
||||
const navigate = useNavigate();
|
||||
const [isTooltipHovered, setIsTooltipHovered] = useState(false);
|
||||
const [isPinning, setIsPinning] = useState(false);
|
||||
|
||||
const getStatsItemCount = useCallback(() => {
|
||||
let statsCount = 1;
|
||||
@@ -89,6 +94,30 @@ export function UserLibraryGameCard({
|
||||
[numberFormatter, t]
|
||||
);
|
||||
|
||||
const toggleGamePinned = async () => {
|
||||
setIsPinning(true);
|
||||
|
||||
try {
|
||||
if (game.isPinned) {
|
||||
await window.electron.removeGameFromPinned(game.shop, game.objectId).then(() => {
|
||||
showSuccessToast(tGame("game_removed_from_pinned"));
|
||||
});
|
||||
} else {
|
||||
await window.electron.addGameToPinned(game.shop, game.objectId).then(() => {
|
||||
showSuccessToast(tGame("game_added_to_pinned"));
|
||||
});
|
||||
}
|
||||
|
||||
// Add a small delay to allow server synchronization before refreshing
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// Refresh the library games to update the UI
|
||||
await getUserLibraryGames();
|
||||
} finally {
|
||||
setIsPinning(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<li
|
||||
@@ -103,9 +132,26 @@ export function UserLibraryGameCard({
|
||||
onClick={() => navigate(buildUserGameDetailsPath(game))}
|
||||
>
|
||||
<div className="user-library-game__overlay">
|
||||
{game.isFavorite && (
|
||||
<div className="user-library-game__favorite-icon">
|
||||
<HeartFillIcon size={14} />
|
||||
{(game.isFavorite || isMe) && (
|
||||
<div className="user-library-game__actions-container">
|
||||
{game.isFavorite && (
|
||||
<div className="user-library-game__favorite-icon">
|
||||
<HeartFillIcon size={12} />
|
||||
</div>
|
||||
)}
|
||||
{isMe && (
|
||||
<button
|
||||
type="button"
|
||||
className="user-library-game__pin-button"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
toggleGamePinned();
|
||||
}}
|
||||
disabled={isPinning}
|
||||
>
|
||||
{game.isPinned ? <PinSlashIcon size={12} /> : <PinIcon size={12} />}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<small
|
||||
|
||||
Reference in New Issue
Block a user