refactor: remove WrappedConfirmModal and integrate WrappedFullscreenModal in profile

This commit is contained in:
Moyasee
2025-12-12 17:29:44 +02:00
parent 0268829946
commit 5b4b258526
6 changed files with 101 additions and 78 deletions

View File

@@ -24,7 +24,6 @@ import { MAX_MINUTES_TO_SHOW_IN_PLAYTIME } from "@renderer/constants";
import { ProfileTabs, type ProfileTabType } from "./profile-tabs";
import { LibraryTab } from "./library-tab";
import { ReviewsTab } from "./reviews-tab";
import { WrappedConfirmModal } from "./wrapped-tab";
import { AnimatePresence } from "framer-motion";
import "./profile-content.scss";
@@ -105,7 +104,6 @@ export function ProfileContent() {
const [votingReviews, setVotingReviews] = useState<Set<string>>(new Set());
const [deleteModalVisible, setDeleteModalVisible] = useState(false);
const [reviewToDelete, setReviewToDelete] = useState<string | null>(null);
const [wrappedModalVisible, setWrappedModalVisible] = useState(false);
const dispatch = useAppDispatch();
@@ -388,7 +386,6 @@ export function ProfileContent() {
activeTab={activeTab}
reviewsTotalCount={reviewsTotalCount}
onTabChange={setActiveTab}
onWrappedClick={() => setWrappedModalVisible(true)}
/>
<div className="profile-content__tab-panels">
@@ -442,13 +439,6 @@ export function ProfileContent() {
onClose={handleDeleteCancel}
onConfirm={handleDeleteConfirm}
/>
<WrappedConfirmModal
userId={userProfile.id}
displayName={userProfile.displayName}
isOpen={wrappedModalVisible}
onClose={() => setWrappedModalVisible(false)}
/>
</section>
);
}, [
@@ -470,7 +460,6 @@ export function ProfileContent() {
isLoadingReviews,
votingReviews,
deleteModalVisible,
wrappedModalVisible,
]);
return (

View File

@@ -8,14 +8,12 @@ interface ProfileTabsProps {
activeTab: ProfileTabType;
reviewsTotalCount: number;
onTabChange: (tab: ProfileTabType) => void;
onWrappedClick: () => void;
}
export function ProfileTabs({
activeTab,
reviewsTotalCount,
onTabChange,
onWrappedClick,
}: Readonly<ProfileTabsProps>) {
const { t } = useTranslation("user_profile");
@@ -66,15 +64,6 @@ export function ProfileTabs({
/>
)}
</div>
<div className="profile-content__tab-wrapper">
<button
type="button"
className="profile-content__tab"
onClick={onWrappedClick}
>
{t("wrapped_2025")}
</button>
</div>
</div>
);
}

View File

@@ -58,11 +58,32 @@
}
&__content {
position: relative;
border-radius: 8px;
overflow: hidden;
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 8px 48px rgba(0, 0, 0, 0.5);
pointer-events: auto;
background: rgba(0, 0, 0, 0.5);
}
&__loader {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.5);
z-index: 1;
}
&__spinner {
width: 40px;
height: 40px;
border: 3px solid rgba(255, 255, 255, 0.2);
border-top-color: white;
border-radius: 50%;
animation: wrapped-spin 0.8s linear infinite;
}
&__iframe {
@@ -71,3 +92,9 @@
border: none;
}
}
@keyframes wrapped-spin {
to {
transform: rotate(360deg);
}
}

View File

@@ -1,12 +1,9 @@
import { useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { useEffect, useState } from "react";
import { XIcon } from "@primer/octicons-react";
import { ConfirmationModal } from "@renderer/components";
import "./wrapped-tab.scss";
interface WrappedModalProps {
interface WrappedFullscreenModalProps {
userId: string;
displayName: string;
isOpen: boolean;
onClose: () => void;
}
@@ -29,18 +26,16 @@ const getScaleConfigForHeight = (height: number): ScaleConfig => {
return SCALE_CONFIGS[0.25];
};
export function WrappedConfirmModal({
export function WrappedFullscreenModal({
userId,
displayName,
isOpen,
onClose,
}: Readonly<WrappedModalProps>) {
const { t } = useTranslation("user_profile");
const [showFullscreen, setShowFullscreen] = useState(false);
}: Readonly<WrappedFullscreenModalProps>) {
const [config, setConfig] = useState<ScaleConfig>(SCALE_CONFIGS[0.5]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
if (!showFullscreen) return;
if (!isOpen) return;
const updateConfig = () => {
setConfig(getScaleConfigForHeight(window.innerHeight));
@@ -49,56 +44,51 @@ export function WrappedConfirmModal({
updateConfig();
window.addEventListener("resize", updateConfig);
return () => window.removeEventListener("resize", updateConfig);
}, [showFullscreen]);
}, [isOpen]);
const handleConfirm = () => {
onClose();
setShowFullscreen(true);
};
useEffect(() => {
if (isOpen) {
setIsLoading(true);
}
}, [isOpen]);
if (!isOpen) return null;
return (
<>
<ConfirmationModal
visible={isOpen}
title={t("wrapped_2025")}
descriptionText={t("view_wrapped_title", { displayName })}
confirmButtonLabel={t("view_wrapped_yes")}
cancelButtonLabel={t("view_wrapped_no")}
onConfirm={handleConfirm}
onClose={onClose}
<dialog className="wrapped-fullscreen-modal" aria-modal="true" open>
<button
type="button"
className="wrapped-fullscreen-modal__backdrop"
onClick={onClose}
aria-label="Close wrapped"
/>
<div className="wrapped-fullscreen-modal__container">
<button
type="button"
className="wrapped-fullscreen-modal__close-button"
onClick={onClose}
aria-label="Close wrapped"
>
<XIcon size={24} />
</button>
{showFullscreen && (
<dialog className="wrapped-fullscreen-modal" aria-modal="true" open>
<button
type="button"
className="wrapped-fullscreen-modal__backdrop"
onClick={() => setShowFullscreen(false)}
aria-label="Close wrapped"
/>
<div className="wrapped-fullscreen-modal__container">
<button
type="button"
className="wrapped-fullscreen-modal__close-button"
onClick={() => setShowFullscreen(false)}
aria-label="Close wrapped"
>
<XIcon size={24} />
</button>
<div
className="wrapped-fullscreen-modal__content"
style={{ width: config.width, height: config.height }}
>
<iframe
src={`https://hydrawrapped.com/embed/${userId}?scale=${config.scale}`}
className="wrapped-fullscreen-modal__iframe"
title="Wrapped 2025"
/>
<div
className="wrapped-fullscreen-modal__content"
style={{ width: config.width, height: config.height }}
>
{isLoading && (
<div className="wrapped-fullscreen-modal__loader">
<div className="wrapped-fullscreen-modal__spinner" />
</div>
</div>
</dialog>
)}
</>
)}
<iframe
src={`https://hydrawrapped.com/embed/${userId}?scale=${config.scale}`}
className="wrapped-fullscreen-modal__iframe"
title="Wrapped 2025"
onLoad={() => setIsLoading(false)}
/>
</div>
</div>
</dialog>
);
}

View File

@@ -120,6 +120,11 @@
}
}
&__left-actions {
display: flex;
gap: globals.$spacing-unit;
}
&__actions {
display: flex;
gap: globals.$spacing-unit;

View File

@@ -6,6 +6,7 @@ import {
PencilIcon,
PersonAddIcon,
SignOutIcon,
TrophyIcon,
XCircleFillIcon,
} from "@primer/octicons-react";
import { buildGameDetailsPath } from "@renderer/helpers";
@@ -22,6 +23,7 @@ import { useNavigate } from "react-router-dom";
import type { FriendRequestAction } from "@types";
import { EditProfileModal } from "../edit-profile-modal/edit-profile-modal";
import { WrappedFullscreenModal } from "../profile-content/wrapped-tab";
import Skeleton from "react-loading-skeleton";
import { UploadBackgroundImageButton } from "../upload-background-image-button/upload-background-image-button";
import { Tooltip } from "react-tooltip";
@@ -33,6 +35,7 @@ type FriendAction =
export function ProfileHero() {
const [showEditProfileModal, setShowEditProfileModal] = useState(false);
const [showWrappedModal, setShowWrappedModal] = useState(false);
const [isPerformingAction, setIsPerformingAction] = useState(false);
const {
@@ -272,6 +275,14 @@ export function ProfileHero() {
onClose={() => setShowEditProfileModal(false)}
/>
{userProfile && (
<WrappedFullscreenModal
userId={userProfile.id}
isOpen={showWrappedModal}
onClose={() => setShowWrappedModal(false)}
/>
)}
<section
className="profile-hero__content-box"
style={{ background: !backgroundImage ? heroBackground : undefined }}
@@ -378,6 +389,18 @@ export function ProfileHero() {
background: !backgroundImage ? heroBackground : undefined,
}}
>
{userProfile && (
<div className="profile-hero__left-actions">
<Button
theme="outline"
onClick={() => setShowWrappedModal(true)}
className="profile-hero__button--outline"
>
<TrophyIcon />
{t("wrapped_2025")}
</Button>
</div>
)}
<div className="profile-hero__actions">{profileActions}</div>
</div>
</section>