mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-11 13:56:16 +00:00
refactor: remove WrappedConfirmModal and integrate WrappedFullscreenModal in profile
This commit is contained in:
@@ -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 (
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -120,6 +120,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__left-actions {
|
||||
display: flex;
|
||||
gap: globals.$spacing-unit;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
display: flex;
|
||||
gap: globals.$spacing-unit;
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user