From 883cfe20f2a772a3634e3a05a418fd9e6ea533b8 Mon Sep 17 00:00:00 2001 From: ragnep Date: Thu, 11 Dec 2025 14:07:45 +0200 Subject: [PATCH 1/6] wip Signed-off-by: ragnep --- .../memes/drops/MemesLeaderboardDrop.tsx | 2 +- .../drops/MemesLeaderboardDropArtistInfo.tsx | 23 ++- .../MemeDropArtistInfo.tsx | 21 ++- components/utils/button/ActionButton.tsx | 36 ++++ components/utils/button/SecondaryButton.tsx | 8 +- .../waves/drop/SingleWaveDropAuthor.tsx | 103 ++++++++--- components/waves/drops/ArtistPreviewModal.tsx | 6 +- components/waves/drops/DropItemChat.tsx | 2 +- components/waves/drops/WaveDropHeader.tsx | 32 +--- components/waves/list/WaveItem.tsx | 168 +++++++++--------- components/waves/list/WaveItemDropped.tsx | 4 +- components/waves/list/WaveItemFollow.tsx | 86 +++++---- .../waves/list/header/WavesListHeader.tsx | 68 +++---- 13 files changed, 310 insertions(+), 249 deletions(-) create mode 100644 components/utils/button/ActionButton.tsx diff --git a/components/memes/drops/MemesLeaderboardDrop.tsx b/components/memes/drops/MemesLeaderboardDrop.tsx index 697edf2764..675203ea4e 100644 --- a/components/memes/drops/MemesLeaderboardDrop.tsx +++ b/components/memes/drops/MemesLeaderboardDrop.tsx @@ -113,7 +113,7 @@ export const MemesLeaderboardDrop: React.FC = ({ )} {/* Footer Section: Traits + Vote Summary + Vote Button */} -
+
diff --git a/components/memes/drops/MemesLeaderboardDropArtistInfo.tsx b/components/memes/drops/MemesLeaderboardDropArtistInfo.tsx index 3ad0355103..efe4467086 100644 --- a/components/memes/drops/MemesLeaderboardDropArtistInfo.tsx +++ b/components/memes/drops/MemesLeaderboardDropArtistInfo.tsx @@ -13,6 +13,7 @@ import WaveDropTime from "@/components/waves/drops/time/WaveDropTime"; import UserProfileTooltipWrapper from "@/components/utils/tooltip/UserProfileTooltipWrapper"; import { ArtistSubmissionBadge } from "@/components/waves/drops/ArtistSubmissionBadge"; import { ArtistPreviewModal } from "@/components/waves/drops/ArtistPreviewModal"; +import { ProfileWinnerBadge } from "@/components/waves/drops/ProfileWinnerBadge"; interface MemesLeaderboardDropArtistInfoProps { readonly drop: ExtendedDrop; @@ -22,11 +23,17 @@ const MemesLeaderboardDropArtistInfo: React.FC< MemesLeaderboardDropArtistInfoProps > = ({ drop }) => { const [isModalOpen, setIsModalOpen] = useState(false); - + const [modalInitialTab, setModalInitialTab] = useState<"active" | "winners">("active"); + const submissionCount = drop.author.active_main_stage_submission_ids?.length || 0; const hasSubmissions = submissionCount > 0; - const handleSubmissionBadgeClick = () => { + // Check if this drop author has any main stage winner drop IDs + const winnerCount = drop.author.winner_main_stage_drop_ids?.length || 0; + const isWinner = winnerCount > 0; + + const handleBadgeClick = (tab: "active" | "winners") => { + setModalInitialTab(tab); setIsModalOpen(true); }; @@ -71,10 +78,17 @@ const MemesLeaderboardDropArtistInfo: React.FC< )} + {isWinner && ( + handleBadgeClick("winners")} + tooltipId={`leaderboard-winner-badge-${drop.id}`} + /> + )} {hasSubmissions && ( handleBadgeClick("active")} tooltipId={`leaderboard-badge-${drop.id}`} /> )} @@ -95,11 +109,12 @@ const MemesLeaderboardDropArtistInfo: React.FC<
- {/* Artist Submission Preview Modal */} + {/* Artist Preview Modal */}
); diff --git a/components/memes/drops/meme-participation-drop/MemeDropArtistInfo.tsx b/components/memes/drops/meme-participation-drop/MemeDropArtistInfo.tsx index 9109caed14..a7c0b27031 100644 --- a/components/memes/drops/meme-participation-drop/MemeDropArtistInfo.tsx +++ b/components/memes/drops/meme-participation-drop/MemeDropArtistInfo.tsx @@ -21,15 +21,17 @@ interface MemeDropArtistInfoProps { export default function MemeDropArtistInfo({ drop }: MemeDropArtistInfoProps) { const [isModalOpen, setIsModalOpen] = useState(false); - + const [modalInitialTab, setModalInitialTab] = useState<"active" | "winners">("active"); + const submissionCount = drop.author.active_main_stage_submission_ids?.length || 0; const hasSubmissions = submissionCount > 0; // Check if this drop author has any main stage winner drop IDs - const isWinner = drop.author.winner_main_stage_drop_ids && - drop.author.winner_main_stage_drop_ids.length > 0; + const winnerCount = drop.author.winner_main_stage_drop_ids?.length || 0; + const isWinner = winnerCount > 0; - const handleSubmissionBadgeClick = () => { + const handleBadgeClick = (tab: "active" | "winners") => { + setModalInitialTab(tab); setIsModalOpen(true); }; @@ -67,14 +69,16 @@ export default function MemeDropArtistInfo({ drop }: MemeDropArtistInfoProps) { )} {isWinner && ( - handleBadgeClick("winners")} + tooltipId={`meme-winner-badge-${drop.id}`} /> )} {hasSubmissions && ( handleBadgeClick("active")} tooltipId={`meme-badge-${drop.id}`} /> )} @@ -98,11 +102,12 @@ export default function MemeDropArtistInfo({ drop }: MemeDropArtistInfoProps) { )} - {/* Artist Submission Preview Modal */} + {/* Artist Preview Modal */} ); diff --git a/components/utils/button/ActionButton.tsx b/components/utils/button/ActionButton.tsx new file mode 100644 index 0000000000..264068eae0 --- /dev/null +++ b/components/utils/button/ActionButton.tsx @@ -0,0 +1,36 @@ +import CircleLoader from "@/components/distribution-plan-tool/common/CircleLoader"; + +interface ActionButtonProps { + readonly onClicked: () => void; + readonly children: React.ReactNode; + readonly size?: "default" | "sm"; + readonly disabled?: boolean; + readonly loading?: boolean; +} + +export default function ActionButton({ + onClicked, + children, + size = "default", + disabled = false, + loading = false, +}: ActionButtonProps) { + const sizeClasses = + size === "sm" + ? "tw-px-2.5 tw-py-1.5" + : "tw-px-3.5 tw-py-2.5"; + + return ( + + ); +} diff --git a/components/utils/button/SecondaryButton.tsx b/components/utils/button/SecondaryButton.tsx index 980bca4011..1c7295f838 100644 --- a/components/utils/button/SecondaryButton.tsx +++ b/components/utils/button/SecondaryButton.tsx @@ -1,5 +1,3 @@ -import React from "react"; - import CircleLoader from "@/components/distribution-plan-tool/common/CircleLoader"; interface SecondaryButtonProps { @@ -21,8 +19,8 @@ export default function SecondaryButton({ }: SecondaryButtonProps) { const sizeClasses = size === "sm" - ? "tw-px-2.5 tw-py-1.5 tw-text-xs" - : "tw-px-3.5 tw-py-2.5 tw-text-sm"; + ? "tw-px-2.5 tw-py-1.5" + : "tw-px-3.5 tw-py-2.5"; return ( ); } else { authorSection = ( -
-
{authorAvatar}
+
+
{authorAvatar}
{author?.handle ?? userPlaceholder} - {authorLevelBadge} +
{authorLevelBadge}
); } @@ -326,7 +322,7 @@ export default function WaveItem({ onClick={handleCardClick} onKeyDown={handleCardKeyDown} > -
+
)} -
+
-
+
- + {wave?.name ?? titlePlaceholder}
@@ -362,68 +352,70 @@ export default function WaveItem({
-
- {authorSection} -
+
+
+ {authorSection} + +
+
+
-
-
-
-
- {waveHref && followersTooltipId ? ( - - {followersContent} - - ) : ( -
- {followersContent} -
+ Joined + )}
- {wave && followersTooltipId && ( - - Joined - - )} -
+
-
-
- {wave && ( -
- -
- )} -
+
+
+ {wave && ( +
+ +
+ )} +
-
- {wave && ( -
- -
- )} +
+ {wave && ( +
+ +
+ )} +
diff --git a/components/waves/list/WaveItemDropped.tsx b/components/waves/list/WaveItemDropped.tsx index 6d22d6ad38..5b11401fd6 100644 --- a/components/waves/list/WaveItemDropped.tsx +++ b/components/waves/list/WaveItemDropped.tsx @@ -100,10 +100,10 @@ export default function WaveItemDropped({ wave }: { readonly wave: ApiWave }) { })}
- + {numberWithCommas(wave.metrics.drops_count)} - + {wave.metrics.drops_count === 1 ? "Drop" : "Drops"}
diff --git a/components/waves/list/WaveItemFollow.tsx b/components/waves/list/WaveItemFollow.tsx index ad8c7a24dc..1ca5b5a250 100644 --- a/components/waves/list/WaveItemFollow.tsx +++ b/components/waves/list/WaveItemFollow.tsx @@ -10,8 +10,9 @@ import { commonApiPost, } from "@/services/api/common-api"; import { ApiWaveSubscriptionActions } from "@/generated/models/ApiWaveSubscriptionActions"; -import CircleLoader from "@/components/distribution-plan-tool/common/CircleLoader"; import { WAVE_DEFAULT_SUBSCRIPTION_ACTIONS } from "@/components/react-query-wrapper/utils/query-utils"; +import SecondaryButton from "@/components/utils/button/SecondaryButton"; +import ActionButton from "@/components/utils/button/ActionButton"; enum WaveItemFollowState { FOLLOWING = "FOLLOWING", @@ -51,15 +52,6 @@ export default function WaveItemFollow({ wave }: { readonly wave: ApiWave }) { const [state, setState] = useState(getState()); useEffect(() => setState(getState()), [isSubscribed, canSubscribe]); - const CLASSES: Record = { - [WaveItemFollowState.FOLLOWING]: - "tw-bg-iron-800 tw-text-iron-300 hover:tw-bg-iron-700 hover:tw-border-iron-700 focus-visible:tw-outline-iron-700 tw-border-iron-800", - [WaveItemFollowState.FOLLOW]: - "tw-text-white hover:tw-bg-primary-600 hover:tw-border-primary-600 focus-visible:tw-outline-primary-600 tw-bg-primary-500 tw-border-primary-500", - [WaveItemFollowState.CANT_FOLLOW]: - "tw-opacity-50 tw-cursor-not-allowed tw-bg-primary-500 tw-ring-primary-500 tw-text-white", - }; - const subscribeMutation = useMutation({ mutationFn: async () => { await commonApiPost< @@ -118,10 +110,7 @@ export default function WaveItemFollow({ wave }: { readonly wave: ApiWave }) { }, }); - const onSubscribe = async ( - e: React.MouseEvent - ): Promise => { - e.preventDefault(); + const onSubscribe = async (): Promise => { setMutating(true); const { success } = await requestAuth(); if (!success) { @@ -135,21 +124,15 @@ export default function WaveItemFollow({ wave }: { readonly wave: ApiWave }) { await subscribeMutation.mutateAsync(); }; - return ( -
- -
+ + ); + } + + return ( + + {!mutating && ( + + )} + {!mutating && label} + ); } diff --git a/components/waves/list/header/WavesListHeader.tsx b/components/waves/list/header/WavesListHeader.tsx index d3f0f8f465..7babd450c2 100644 --- a/components/waves/list/header/WavesListHeader.tsx +++ b/components/waves/list/header/WavesListHeader.tsx @@ -6,6 +6,9 @@ import WavesListSearch from "./WavesListSearch"; import { AuthContext } from "@/components/auth/Auth"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faPaperPlane } from "@fortawesome/free-solid-svg-icons"; +import { PlusIcon } from "@heroicons/react/24/outline"; +import PrimaryButton from "@/components/utils/button/PrimaryButton"; +import SecondaryButton from "@/components/utils/button/SecondaryButton"; export default function WavesListHeader({ title = "Waves", @@ -48,52 +51,35 @@ export default function WavesListHeader({ />
{showMyWavesButton && ( - + )} {showCreateNewButton && ( <> -
- -
-
- -
+ + + Create Wave + + + + Create DM + )}
From a6c83aaf49b5a7e43b38bd036e8e34da39bb2935 Mon Sep 17 00:00:00 2001 From: ragnep Date: Thu, 11 Dec 2025 14:18:16 +0200 Subject: [PATCH 2/6] wip Signed-off-by: ragnep --- .../drops/MemesLeaderboardDropArtistInfo.tsx | 17 ++++--------- .../MemeDropArtistInfo.tsx | 17 ++++--------- components/waves/drops/ArtistPreviewModal.tsx | 6 ++--- components/waves/drops/WaveDropHeader.tsx | 22 ++++++----------- hooks/useArtistPreviewModal.ts | 24 +++++++++++++++++++ 5 files changed, 42 insertions(+), 44 deletions(-) create mode 100644 hooks/useArtistPreviewModal.ts diff --git a/components/memes/drops/MemesLeaderboardDropArtistInfo.tsx b/components/memes/drops/MemesLeaderboardDropArtistInfo.tsx index efe4467086..de9c6f0a5b 100644 --- a/components/memes/drops/MemesLeaderboardDropArtistInfo.tsx +++ b/components/memes/drops/MemesLeaderboardDropArtistInfo.tsx @@ -1,6 +1,6 @@ "use client" -import React, { useState } from "react"; +import React from "react"; import Link from "next/link"; import { cicToType } from "@/helpers/Helpers"; import UserCICAndLevel, { @@ -14,6 +14,7 @@ import UserProfileTooltipWrapper from "@/components/utils/tooltip/UserProfileToo import { ArtistSubmissionBadge } from "@/components/waves/drops/ArtistSubmissionBadge"; import { ArtistPreviewModal } from "@/components/waves/drops/ArtistPreviewModal"; import { ProfileWinnerBadge } from "@/components/waves/drops/ProfileWinnerBadge"; +import { useArtistPreviewModal } from "@/hooks/useArtistPreviewModal"; interface MemesLeaderboardDropArtistInfoProps { readonly drop: ExtendedDrop; @@ -22,25 +23,15 @@ interface MemesLeaderboardDropArtistInfoProps { const MemesLeaderboardDropArtistInfo: React.FC< MemesLeaderboardDropArtistInfoProps > = ({ drop }) => { - const [isModalOpen, setIsModalOpen] = useState(false); - const [modalInitialTab, setModalInitialTab] = useState<"active" | "winners">("active"); + const { isModalOpen, modalInitialTab, handleBadgeClick, handleModalClose } = + useArtistPreviewModal(); const submissionCount = drop.author.active_main_stage_submission_ids?.length || 0; const hasSubmissions = submissionCount > 0; - // Check if this drop author has any main stage winner drop IDs const winnerCount = drop.author.winner_main_stage_drop_ids?.length || 0; const isWinner = winnerCount > 0; - const handleBadgeClick = (tab: "active" | "winners") => { - setModalInitialTab(tab); - setIsModalOpen(true); - }; - - const handleModalClose = () => { - setIsModalOpen(false); - }; - return (
diff --git a/components/memes/drops/meme-participation-drop/MemeDropArtistInfo.tsx b/components/memes/drops/meme-participation-drop/MemeDropArtistInfo.tsx index a7c0b27031..f08c82a91e 100644 --- a/components/memes/drops/meme-participation-drop/MemeDropArtistInfo.tsx +++ b/components/memes/drops/meme-participation-drop/MemeDropArtistInfo.tsx @@ -1,6 +1,6 @@ "use client" -import React, { useState } from "react"; +import React from "react"; import Link from "next/link"; import { cicToType } from "@/helpers/Helpers"; import UserCICAndLevel, { @@ -14,31 +14,22 @@ import UserProfileTooltipWrapper from "@/components/utils/tooltip/UserProfileToo import { ArtistSubmissionBadge } from "@/components/waves/drops/ArtistSubmissionBadge"; import { ArtistPreviewModal } from "@/components/waves/drops/ArtistPreviewModal"; import { ProfileWinnerBadge } from "@/components/waves/drops/ProfileWinnerBadge"; +import { useArtistPreviewModal } from "@/hooks/useArtistPreviewModal"; interface MemeDropArtistInfoProps { readonly drop: ExtendedDrop; } export default function MemeDropArtistInfo({ drop }: MemeDropArtistInfoProps) { - const [isModalOpen, setIsModalOpen] = useState(false); - const [modalInitialTab, setModalInitialTab] = useState<"active" | "winners">("active"); + const { isModalOpen, modalInitialTab, handleBadgeClick, handleModalClose } = + useArtistPreviewModal(); const submissionCount = drop.author.active_main_stage_submission_ids?.length || 0; const hasSubmissions = submissionCount > 0; - // Check if this drop author has any main stage winner drop IDs const winnerCount = drop.author.winner_main_stage_drop_ids?.length || 0; const isWinner = winnerCount > 0; - const handleBadgeClick = (tab: "active" | "winners") => { - setModalInitialTab(tab); - setIsModalOpen(true); - }; - - const handleModalClose = () => { - setIsModalOpen(false); - }; - return (
diff --git a/components/waves/drops/ArtistPreviewModal.tsx b/components/waves/drops/ArtistPreviewModal.tsx index b26e84fcbb..08a3403395 100644 --- a/components/waves/drops/ArtistPreviewModal.tsx +++ b/components/waves/drops/ArtistPreviewModal.tsx @@ -80,7 +80,7 @@ export const ArtistPreviewModal: React.FC< return createPortal( - {}}> + {/* Backdrop */} -
{ e.stopPropagation(); onClose(); }} /> + {/* Desktop modal */} -
{ e.stopPropagation(); onClose(); }}> +
= ({ badge, }) => { const router = useRouter(); - const [isModalOpen, setIsModalOpen] = useState(false); - const [modalInitialTab, setModalInitialTab] = useState<"active" | "winners">("active"); + const { isModalOpen, modalInitialTab, handleBadgeClick, handleModalClose } = + useArtistPreviewModal(); // Memoize expensive computations const cicType = useMemo(() => cicToType(drop.author.cic), [drop.author.cic]); - - const submissionCount = useMemo(() => + + const submissionCount = useMemo(() => drop.author.active_main_stage_submission_ids?.length || 0, [drop.author.active_main_stage_submission_ids] ); - + const hasSubmissions = submissionCount > 0; // Check if this drop author has any main stage winner drop IDs @@ -60,15 +61,6 @@ const WaveDropHeader: React.FC = ({ router.push(path); }, [router]); - const handleBadgeClick = useCallback((tab: "active" | "winners") => { - setModalInitialTab(tab); - setIsModalOpen(true); - }, []); - - const handleModalClose = useCallback(() => { - setIsModalOpen(false); - }, []); - return ( <>
diff --git a/hooks/useArtistPreviewModal.ts b/hooks/useArtistPreviewModal.ts new file mode 100644 index 0000000000..6cc9d15f16 --- /dev/null +++ b/hooks/useArtistPreviewModal.ts @@ -0,0 +1,24 @@ +import { useState, useCallback } from "react"; + +export type ArtistPreviewTab = "active" | "winners"; + +export function useArtistPreviewModal() { + const [isModalOpen, setIsModalOpen] = useState(false); + const [modalInitialTab, setModalInitialTab] = useState("active"); + + const handleBadgeClick = useCallback((tab: ArtistPreviewTab) => { + setModalInitialTab(tab); + setIsModalOpen(true); + }, []); + + const handleModalClose = useCallback(() => { + setIsModalOpen(false); + }, []); + + return { + isModalOpen, + modalInitialTab, + handleBadgeClick, + handleModalClose, + }; +} From fbab55e47c33247eec6722f5ac37abd0dd7e21ee Mon Sep 17 00:00:00 2001 From: ragnep Date: Thu, 11 Dec 2025 14:36:03 +0200 Subject: [PATCH 3/6] wip Signed-off-by: ragnep --- components/waves/drops/ArtistPreviewModal.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/waves/drops/ArtistPreviewModal.tsx b/components/waves/drops/ArtistPreviewModal.tsx index 08a3403395..b26e84fcbb 100644 --- a/components/waves/drops/ArtistPreviewModal.tsx +++ b/components/waves/drops/ArtistPreviewModal.tsx @@ -80,7 +80,7 @@ export const ArtistPreviewModal: React.FC< return createPortal( - + {}}> {/* Backdrop */} - +
{ e.stopPropagation(); onClose(); }} /> {/* Desktop modal */} -
+
{ e.stopPropagation(); onClose(); }}>
Date: Thu, 11 Dec 2025 14:42:20 +0200 Subject: [PATCH 4/6] wip Signed-off-by: ragnep --- .../drops/MemesLeaderboardDropArtistInfo.tsx | 7 ++-- components/waves/drops/ArtistPreviewModal.tsx | 37 +++++++------------ components/waves/drops/WaveDropHeader.tsx | 9 +++-- 3 files changed, 22 insertions(+), 31 deletions(-) diff --git a/components/memes/drops/MemesLeaderboardDropArtistInfo.tsx b/components/memes/drops/MemesLeaderboardDropArtistInfo.tsx index de9c6f0a5b..99c9ccd1d3 100644 --- a/components/memes/drops/MemesLeaderboardDropArtistInfo.tsx +++ b/components/memes/drops/MemesLeaderboardDropArtistInfo.tsx @@ -1,6 +1,5 @@ "use client" -import React from "react"; import Link from "next/link"; import { cicToType } from "@/helpers/Helpers"; import UserCICAndLevel, { @@ -20,9 +19,9 @@ interface MemesLeaderboardDropArtistInfoProps { readonly drop: ExtendedDrop; } -const MemesLeaderboardDropArtistInfo: React.FC< - MemesLeaderboardDropArtistInfoProps -> = ({ drop }) => { +const MemesLeaderboardDropArtistInfo = ({ + drop +}: MemesLeaderboardDropArtistInfoProps) => { const { isModalOpen, modalInitialTab, handleBadgeClick, handleModalClose } = useArtistPreviewModal(); diff --git a/components/waves/drops/ArtistPreviewModal.tsx b/components/waves/drops/ArtistPreviewModal.tsx index b26e84fcbb..18d89974f2 100644 --- a/components/waves/drops/ArtistPreviewModal.tsx +++ b/components/waves/drops/ArtistPreviewModal.tsx @@ -3,12 +3,13 @@ import { ApiProfileMin } from "@/generated/models/ApiProfileMin"; import useDeviceInfo from "@/hooks/useDeviceInfo"; import { Dialog, Transition } from "@headlessui/react"; -import React, { Fragment, useEffect, useRef, useState } from "react"; +import { Fragment, useEffect, useState } from "react"; import { createPortal } from "react-dom"; import ArtistPreviewAppWrapper from "./ArtistPreviewAppWrapper"; import { ArtistPreviewModalContent } from "./ArtistPreviewModalContent"; +import { ArtistPreviewTab as ModalTab } from "@/hooks/useArtistPreviewModal"; -export type ModalTab = "active" | "winners"; +export type { ModalTab }; interface ArtistPreviewModalProps { readonly isOpen: boolean; @@ -17,18 +18,19 @@ interface ArtistPreviewModalProps { readonly initialTab?: ModalTab; } -export const ArtistPreviewModal: React.FC< - ArtistPreviewModalProps -> = ({ isOpen, onClose, user, initialTab = "active" }) => { +export const ArtistPreviewModal = ({ + isOpen, + onClose, + user, + initialTab = "active" +}: ArtistPreviewModalProps) => { const { isApp } = useDeviceInfo(); - const modalRef = useRef(null); - const previousFocusRef = useRef(null); const [activeTab, setActiveTab] = useState(initialTab); - + // Check if user has winning artworks - const hasWinningArtworks = user.winner_main_stage_drop_ids && + const hasWinningArtworks = user.winner_main_stage_drop_ids && user.winner_main_stage_drop_ids.length > 0; - + // Reset tab when modal opens with different initial tab useEffect(() => { if (isOpen) { @@ -36,24 +38,13 @@ export const ArtistPreviewModal: React.FC< } }, [isOpen, initialTab]); - // Cleanup body overflow and manage focus + // Cleanup body overflow useEffect(() => { if (isOpen && !isApp) { - // Store current focus - previousFocusRef.current = document.activeElement as HTMLElement; - - // Set body overflow document.body.style.overflow = 'hidden'; - - // Focus the modal after animation - setTimeout(() => { - modalRef.current?.focus(); - }, 100); - + return () => { document.body.style.overflow = 'unset'; - // Restore previous focus - previousFocusRef.current?.focus(); }; } }, [isOpen, isApp]); diff --git a/components/waves/drops/WaveDropHeader.tsx b/components/waves/drops/WaveDropHeader.tsx index ea957f5b6e..65bb1b9f15 100644 --- a/components/waves/drops/WaveDropHeader.tsx +++ b/components/waves/drops/WaveDropHeader.tsx @@ -48,12 +48,13 @@ const WaveDropHeader: React.FC = ({ const hasSubmissions = submissionCount > 0; // Check if this drop author has any main stage winner drop IDs - const isWinner = useMemo(() => - drop.author.winner_main_stage_drop_ids && - drop.author.winner_main_stage_drop_ids.length > 0, + const winnerCount = useMemo(() => + drop.author.winner_main_stage_drop_ids?.length || 0, [drop.author.winner_main_stage_drop_ids] ); + const isWinner = winnerCount > 0; + // Memoize event handlers to prevent unnecessary re-renders const handleNavigation = useCallback((e: React.MouseEvent, path: string) => { e.preventDefault(); @@ -94,7 +95,7 @@ const WaveDropHeader: React.FC = ({ )} {isWinner && ( handleBadgeClick("winners")} tooltipId={`winner-badge-${drop.id}`} /> From e3f8e660fb280886de367dc342ea7e098202dbf5 Mon Sep 17 00:00:00 2001 From: ragnep Date: Thu, 11 Dec 2025 14:52:49 +0200 Subject: [PATCH 5/6] wip Signed-off-by: ragnep --- components/memes/drops/MemesLeaderboardDropArtistInfo.tsx | 2 +- components/waves/drops/ArtistPreviewModal.tsx | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/components/memes/drops/MemesLeaderboardDropArtistInfo.tsx b/components/memes/drops/MemesLeaderboardDropArtistInfo.tsx index 99c9ccd1d3..a04160b1c3 100644 --- a/components/memes/drops/MemesLeaderboardDropArtistInfo.tsx +++ b/components/memes/drops/MemesLeaderboardDropArtistInfo.tsx @@ -37,7 +37,7 @@ const MemesLeaderboardDropArtistInfo = ({
{/* Top row: Handle + Artist badge + Timestamp */}
- {drop.author?.level && ( + {!!drop.author?.level && ( { if (isOpen && !isApp) { + const originalOverflow = document.body.style.overflow; document.body.style.overflow = 'hidden'; return () => { - document.body.style.overflow = 'unset'; + document.body.style.overflow = originalOverflow; }; } }, [isOpen, isApp]); From b6c04cde0c056ab83dd437d82b6ff1ddbad3ac92 Mon Sep 17 00:00:00 2001 From: ragnep Date: Thu, 11 Dec 2025 15:01:32 +0200 Subject: [PATCH 6/6] wip Signed-off-by: ragnep --- components/waves/drops/ArtistPreviewModal.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/components/waves/drops/ArtistPreviewModal.tsx b/components/waves/drops/ArtistPreviewModal.tsx index 4ac38381d3..7505cb2628 100644 --- a/components/waves/drops/ArtistPreviewModal.tsx +++ b/components/waves/drops/ArtistPreviewModal.tsx @@ -7,13 +7,15 @@ import { Fragment, useEffect, useState } from "react"; import { createPortal } from "react-dom"; import ArtistPreviewAppWrapper from "./ArtistPreviewAppWrapper"; import { ArtistPreviewModalContent } from "./ArtistPreviewModalContent"; -export type { ArtistPreviewTab as ModalTab } from "@/hooks/useArtistPreviewModal"; +import { ArtistPreviewTab } from "@/hooks/useArtistPreviewModal"; + +export type { ArtistPreviewTab as ModalTab }; interface ArtistPreviewModalProps { readonly isOpen: boolean; readonly onClose: () => void; readonly user: ApiProfileMin; - readonly initialTab?: ModalTab; + readonly initialTab?: ArtistPreviewTab; } export const ArtistPreviewModal = ({ @@ -23,7 +25,7 @@ export const ArtistPreviewModal = ({ initialTab = "active" }: ArtistPreviewModalProps) => { const { isApp } = useDeviceInfo(); - const [activeTab, setActiveTab] = useState(initialTab); + const [activeTab, setActiveTab] = useState(initialTab); // Check if user has winning artworks const hasWinningArtworks = user.winner_main_stage_drop_ids &&