From 8bd14b1fea436f1083abb5656e741be8b9b6b5cc Mon Sep 17 00:00:00 2001 From: Ragne Date: Tue, 17 Dec 2024 18:38:26 +0200 Subject: [PATCH 01/33] wip Signed-off-by: Ragne --- .../detailed/leaderboard/WaveLeaderboard.tsx | 7 +- .../leaderboard/WaveLeaderboardWinner.tsx | 247 ++++++++++++++++++ .../WaveLeaderboardWinnerTrophy.tsx | 43 +++ .../drops/WaveLeaderboardWinnerDrop.tsx | 68 +++++ .../waves/detailed/winner/WaveWinner.tsx | 98 +++++++ 5 files changed, 461 insertions(+), 2 deletions(-) create mode 100644 components/waves/detailed/leaderboard/WaveLeaderboardWinner.tsx create mode 100644 components/waves/detailed/leaderboard/WaveLeaderboardWinnerTrophy.tsx create mode 100644 components/waves/detailed/leaderboard/drops/WaveLeaderboardWinnerDrop.tsx create mode 100644 components/waves/detailed/winner/WaveWinner.tsx diff --git a/components/waves/detailed/leaderboard/WaveLeaderboard.tsx b/components/waves/detailed/leaderboard/WaveLeaderboard.tsx index c6cf7cf3b..d35ba512d 100644 --- a/components/waves/detailed/leaderboard/WaveLeaderboard.tsx +++ b/components/waves/detailed/leaderboard/WaveLeaderboard.tsx @@ -12,6 +12,7 @@ import WaveLeaderboardRightSidebar from "./sidebar/WaveLeaderboardRightSidebar"; import { WaveDropCreate } from "./create/WaveDropCreate"; import { AnimatePresence, motion } from "framer-motion"; import useCapacitor from "../../../../hooks/useCapacitor"; +import { WaveLeaderboardWinner } from "./WaveLeaderboardWinner"; interface WaveLeaderboardProps { readonly wave: ApiWave; @@ -67,7 +68,9 @@ export const WaveLeaderboard: React.FC = ({ > {children} - + + + {/* = ({ showMyDrops={showMyDrops} setActiveDrop={setActiveDrop} onCreateDrop={() => setIsCreatingDrop(true)} - /> + /> */} diff --git a/components/waves/detailed/leaderboard/WaveLeaderboardWinner.tsx b/components/waves/detailed/leaderboard/WaveLeaderboardWinner.tsx new file mode 100644 index 000000000..46c36dd54 --- /dev/null +++ b/components/waves/detailed/leaderboard/WaveLeaderboardWinner.tsx @@ -0,0 +1,247 @@ +import React from "react"; +import { motion } from "framer-motion"; +import { DropTrophyIcon } from "../../utils/DropThrophyIcon"; +import UserCICAndLevel from "../../../user/utils/UserCICAndLevel"; +import { UserCICAndLevelSize } from "../../../user/utils/UserCICAndLevel"; +import { cicToType } from "../../../../helpers/Helpers"; + +interface WaveLeaderboardWinnerProps { + readonly wave: any; + readonly onDropClick: (drop: any) => void; +} + +export const WaveLeaderboardWinner: React.FC = ({ + wave, + onDropClick, +}) => { + return ( +
+ {/* Title */} +
+

+ Wave Winners +

+

Lorem ipsum dolor sit amet consectetur

+
+ + {/* Podium Section */} +
+
+ {/* Second Place */} +
+
+
+ +
+ cryptowhale +
+
+
+
+
+
+ 2nd +
+
+ +
+
+
+ + 45,678 + + TDH total +
+
+ 234 voters +
+
+
+
+
+ + {/* First Place */} +
+
+
+ +
+ nftmaster +
+
+
+
+
+
+ WINNER{" "} + +
+
+ +
+
+
+ + 89,432 + + TDH total +
+
+ 567 voters +
+
+
+
+
+ + {/* Third Place */} +
+
+
+ +
+ artcollector +
+
+
+
+
+
+ 3rd +
+
+ +
+
+
+ + 23,456 + + TDH total +
+
+ 123 voters +
+
+
+
+
+
+
+ + {/* Posts */} +
+ {/* Winner Post */} +
+
+
+ {/* Header */} +
+
+
+
+ + + #1 + +
+
+ User avatar +
+ +
+
+
+
+
+ + nftmaster + +
+ + 2 days ago + +
+
+ +
+
+ + 89,432 + + TDH total +
+
+
+
+
+
+
+ + 567 voters + +
+
+
+ + {/* Content */} +
+
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Optio + doloremque laboriosam iusto. +
+
+ post image +
+
+
+
+
+
+
+ ); +}; diff --git a/components/waves/detailed/leaderboard/WaveLeaderboardWinnerTrophy.tsx b/components/waves/detailed/leaderboard/WaveLeaderboardWinnerTrophy.tsx new file mode 100644 index 000000000..ee8dd8ec7 --- /dev/null +++ b/components/waves/detailed/leaderboard/WaveLeaderboardWinnerTrophy.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import { motion } from "framer-motion"; + +interface WaveLeaderboardWinnerTrophyProps { + rank: 1 | 2 | 3; +} + +const trophyGradients = { + 1: "tw-from-[#E8D48A] tw-via-[#D9A962] tw-to-[#E8D48A]", + 2: "tw-from-[#DDDDDD] tw-via-[#C0C0C0] tw-to-[#DDDDDD]", + 3: "tw-from-[#CD7F32] tw-via-[#B87333] tw-to-[#CD7F32]", +}; + +export const WaveLeaderboardWinnerTrophy: React.FC = ({ + rank +}) => { + return ( + +
+ + + + + ); +}; \ No newline at end of file diff --git a/components/waves/detailed/leaderboard/drops/WaveLeaderboardWinnerDrop.tsx b/components/waves/detailed/leaderboard/drops/WaveLeaderboardWinnerDrop.tsx new file mode 100644 index 000000000..14697c305 --- /dev/null +++ b/components/waves/detailed/leaderboard/drops/WaveLeaderboardWinnerDrop.tsx @@ -0,0 +1,68 @@ +import React from "react"; +import { ExtendedDrop } from "../../../../../helpers/waves/drop.helpers"; +import { WaveLeaderboardDropRankIndicator } from "./WaveLeaderboardDropRankIndicator"; +import { WaveLeaderboardDropHeader } from "./header/WaveLeaderboardDropHeader"; +import { WaveLeaderboardDropContent } from "../content/WaveLeaderboardDropContent"; +import { WaveLeaderboardDropFooter } from "./footer/WaveLeaderboardDropFooter"; +import { ApiWave } from "../../../../../generated/models/ObjectSerializer"; +import { WaveLeaderboardDropRaters } from "./header/WaveleaderboardDropRaters"; + +interface WaveLeaderboardWinnerDropProps { + readonly drop: ExtendedDrop; + readonly wave: ApiWave; + readonly onDropClick: (drop: ExtendedDrop) => void; +} + +export const WaveLeaderboardWinnerDrop: React.FC = ({ + drop, + wave, + onDropClick, +}) => { + const gradientClass = "tw-from-[#E8D48A]/20 tw-via-[#D9A962]/20 tw-to-[#E8D48A]/20 desktop-hover:hover:tw-from-[#E8D48A]/30 desktop-hover:hover:tw-via-[#D9A962]/30 desktop-hover:hover:tw-to-[#E8D48A]/30"; + + return ( +
onDropClick(drop)} + className={`tw-group tw-cursor-pointer tw-rounded-xl tw-bg-gradient-to-b ${gradientClass} tw-p-[1px] tw-transition tw-duration-300 tw-ease-out`} + > +
+
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+ +
+ +
+
+ +
+
+ +
+ +
+
+
+
+ ); +}; \ No newline at end of file diff --git a/components/waves/detailed/winner/WaveWinner.tsx b/components/waves/detailed/winner/WaveWinner.tsx new file mode 100644 index 000000000..0ef150364 --- /dev/null +++ b/components/waves/detailed/winner/WaveWinner.tsx @@ -0,0 +1,98 @@ +import React from "react"; +import { ApiWave } from "../../../../generated/models/ApiWave"; +import { ExtendedDrop } from "../../../../helpers/waves/drop.helpers"; +import { getScaledImageUri, ImageScale } from "../../../../helpers/image.helpers"; + +interface WaveWinnerProps { + readonly wave: ApiWave; + readonly onDropClick: (drop: ExtendedDrop) => void; +} + +export const WaveWinner: React.FC = ({ wave, onDropClick }) => { + return ( +
+ {/* Winner Card */} +
+
+

+ Wave Winner +

+
+ +
+
+
+ {`${wave.author.handle}'s +
+ +
+
+

+ {wave.author.handle} +

+
+ + 1234 votes + +
+
+ +
+

+ This is a hardcoded winner submission content. We'll replace this with real data later. +

+
+ + + + Posted 2 days ago + +
+
+
+
+
+
+ + {/* Results Summary */} +
+
+

+ Wave Summary +

+
+ +
+
+
+ + {wave.metrics.drops_count} + participants contributed to "{wave.wave.name}" +
+
+ + +
+
+
+
+
+ ); +}; \ No newline at end of file From b324bb18d73f120d5cc9d86b6cb09da7b45af426 Mon Sep 17 00:00:00 2001 From: Simo Date: Wed, 18 Dec 2024 14:26:49 +0200 Subject: [PATCH 02/33] wip Signed-off-by: Simo --- components/auth/Auth.tsx | 3 +- .../detailed/leaderboard/WaveLeaderboard.tsx | 4 +- .../leaderboard/WaveLeaderboardWinner.tsx | 247 ------------------ .../WaveLeaderboardWinnerTrophy.tsx | 43 --- .../waves/detailed/winner/WaveWinner.tsx | 98 ------- .../waves/detailed/winners/WaveWinners.tsx | 22 ++ .../detailed/winners/WaveWinnersHeader.tsx | 18 ++ .../winners/drops/WaveWinnersDrop.tsx | 25 ++ .../winners/drops/WaveWinnersDropContent.tsx | 26 ++ .../winners/drops/WaveWinnersDrops.tsx | 49 ++++ .../drops/header/WaveWinnersDropHeader.tsx | 45 ++++ .../WaveWinnersDropHeaderAuthorHandle.tsx | 21 ++ .../header/WaveWinnersDropHeaderAuthorPfp.tsx | 37 +++ .../header/WaveWinnersDropHeaderCreated.tsx | 16 ++ .../header/WaveWinnersDropHeaderRank.tsx | 28 ++ .../WaveWinnersDropHeaderTotalVotes.tsx | 27 ++ .../header/WaveWinnersDropHeaderVoter.tsx | 53 ++++ .../header/WaveWinnersDropHeaderVoters.tsx | 30 +++ .../winners/podium/WaveWinnersPodium.tsx | 114 ++++++++ .../winners/podium/WaveWinnersPodiumFirst.tsx | 83 ++++++ .../podium/WaveWinnersPodiumSecond.tsx | 69 +++++ .../winners/podium/WaveWinnersPodiumThird.tsx | 70 +++++ hooks/useWaveDropsLeaderboard.ts | 14 +- 23 files changed, 745 insertions(+), 397 deletions(-) delete mode 100644 components/waves/detailed/leaderboard/WaveLeaderboardWinner.tsx delete mode 100644 components/waves/detailed/leaderboard/WaveLeaderboardWinnerTrophy.tsx delete mode 100644 components/waves/detailed/winner/WaveWinner.tsx create mode 100644 components/waves/detailed/winners/WaveWinners.tsx create mode 100644 components/waves/detailed/winners/WaveWinnersHeader.tsx create mode 100644 components/waves/detailed/winners/drops/WaveWinnersDrop.tsx create mode 100644 components/waves/detailed/winners/drops/WaveWinnersDropContent.tsx create mode 100644 components/waves/detailed/winners/drops/WaveWinnersDrops.tsx create mode 100644 components/waves/detailed/winners/drops/header/WaveWinnersDropHeader.tsx create mode 100644 components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorHandle.tsx create mode 100644 components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorPfp.tsx create mode 100644 components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderCreated.tsx create mode 100644 components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx create mode 100644 components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderTotalVotes.tsx create mode 100644 components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoter.tsx create mode 100644 components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoters.tsx create mode 100644 components/waves/detailed/winners/podium/WaveWinnersPodium.tsx create mode 100644 components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx create mode 100644 components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx create mode 100644 components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx diff --git a/components/auth/Auth.tsx b/components/auth/Auth.tsx index f52a08fa2..b98d02138 100644 --- a/components/auth/Auth.tsx +++ b/components/auth/Auth.tsx @@ -66,7 +66,8 @@ type AuthContextType = { readonly title: string; }; -export const WAVES_MIN_ACCESS_LEVEL = 10; +// TODO: change it back to 10 +export const WAVES_MIN_ACCESS_LEVEL = 0; const DEFAULT_TITLE = "6529 SEIZE"; export const AuthContext = createContext({ diff --git a/components/waves/detailed/leaderboard/WaveLeaderboard.tsx b/components/waves/detailed/leaderboard/WaveLeaderboard.tsx index d35ba512d..de6b474a5 100644 --- a/components/waves/detailed/leaderboard/WaveLeaderboard.tsx +++ b/components/waves/detailed/leaderboard/WaveLeaderboard.tsx @@ -12,7 +12,7 @@ import WaveLeaderboardRightSidebar from "./sidebar/WaveLeaderboardRightSidebar"; import { WaveDropCreate } from "./create/WaveDropCreate"; import { AnimatePresence, motion } from "framer-motion"; import useCapacitor from "../../../../hooks/useCapacitor"; -import { WaveLeaderboardWinner } from "./WaveLeaderboardWinner"; +import { WaveWinners } from "../winners/WaveWinners"; interface WaveLeaderboardProps { readonly wave: ApiWave; @@ -68,7 +68,7 @@ export const WaveLeaderboard: React.FC = ({ > {children} - + {/* void; -} - -export const WaveLeaderboardWinner: React.FC = ({ - wave, - onDropClick, -}) => { - return ( -
- {/* Title */} -
-

- Wave Winners -

-

Lorem ipsum dolor sit amet consectetur

-
- - {/* Podium Section */} -
-
- {/* Second Place */} -
-
-
- -
- cryptowhale -
-
-
-
-
-
- 2nd -
-
- -
-
-
- - 45,678 - - TDH total -
-
- 234 voters -
-
-
-
-
- - {/* First Place */} -
-
-
- -
- nftmaster -
-
-
-
-
-
- WINNER{" "} - -
-
- -
-
-
- - 89,432 - - TDH total -
-
- 567 voters -
-
-
-
-
- - {/* Third Place */} -
-
-
- -
- artcollector -
-
-
-
-
-
- 3rd -
-
- -
-
-
- - 23,456 - - TDH total -
-
- 123 voters -
-
-
-
-
-
-
- - {/* Posts */} -
- {/* Winner Post */} -
-
-
- {/* Header */} -
-
-
-
- - - #1 - -
-
- User avatar -
- -
-
-
-
-
- - nftmaster - -
- - 2 days ago - -
-
- -
-
- - 89,432 - - TDH total -
-
-
-
-
-
-
- - 567 voters - -
-
-
- - {/* Content */} -
-
- Lorem ipsum dolor sit amet consectetur adipisicing elit. Optio - doloremque laboriosam iusto. -
-
- post image -
-
-
-
-
-
-
- ); -}; diff --git a/components/waves/detailed/leaderboard/WaveLeaderboardWinnerTrophy.tsx b/components/waves/detailed/leaderboard/WaveLeaderboardWinnerTrophy.tsx deleted file mode 100644 index ee8dd8ec7..000000000 --- a/components/waves/detailed/leaderboard/WaveLeaderboardWinnerTrophy.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React from "react"; -import { motion } from "framer-motion"; - -interface WaveLeaderboardWinnerTrophyProps { - rank: 1 | 2 | 3; -} - -const trophyGradients = { - 1: "tw-from-[#E8D48A] tw-via-[#D9A962] tw-to-[#E8D48A]", - 2: "tw-from-[#DDDDDD] tw-via-[#C0C0C0] tw-to-[#DDDDDD]", - 3: "tw-from-[#CD7F32] tw-via-[#B87333] tw-to-[#CD7F32]", -}; - -export const WaveLeaderboardWinnerTrophy: React.FC = ({ - rank -}) => { - return ( - -
- - - - - ); -}; \ No newline at end of file diff --git a/components/waves/detailed/winner/WaveWinner.tsx b/components/waves/detailed/winner/WaveWinner.tsx deleted file mode 100644 index 0ef150364..000000000 --- a/components/waves/detailed/winner/WaveWinner.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import React from "react"; -import { ApiWave } from "../../../../generated/models/ApiWave"; -import { ExtendedDrop } from "../../../../helpers/waves/drop.helpers"; -import { getScaledImageUri, ImageScale } from "../../../../helpers/image.helpers"; - -interface WaveWinnerProps { - readonly wave: ApiWave; - readonly onDropClick: (drop: ExtendedDrop) => void; -} - -export const WaveWinner: React.FC = ({ wave, onDropClick }) => { - return ( -
- {/* Winner Card */} -
-
-

- Wave Winner -

-
- -
-
-
- {`${wave.author.handle}'s -
- -
-
-

- {wave.author.handle} -

-
- - 1234 votes - -
-
- -
-

- This is a hardcoded winner submission content. We'll replace this with real data later. -

-
- - - - Posted 2 days ago - -
-
-
-
-
-
- - {/* Results Summary */} -
-
-

- Wave Summary -

-
- -
-
-
- - {wave.metrics.drops_count} - participants contributed to "{wave.wave.name}" -
-
- - -
-
-
-
-
- ); -}; \ No newline at end of file diff --git a/components/waves/detailed/winners/WaveWinners.tsx b/components/waves/detailed/winners/WaveWinners.tsx new file mode 100644 index 000000000..15b092a36 --- /dev/null +++ b/components/waves/detailed/winners/WaveWinners.tsx @@ -0,0 +1,22 @@ +import React from "react"; +import { WaveWinnersHeader } from "./WaveWinnersHeader"; +import { WaveWinnersPodium } from "./podium/WaveWinnersPodium"; +import { WaveWinnersDrops } from "./drops/WaveWinnersDrops"; + +interface WaveWinnersProps { + readonly wave: any; + readonly onDropClick: (drop: any) => void; +} + +export const WaveWinners: React.FC = ({ + wave, + onDropClick, +}) => { + return ( +
+ + + +
+ ); +}; diff --git a/components/waves/detailed/winners/WaveWinnersHeader.tsx b/components/waves/detailed/winners/WaveWinnersHeader.tsx new file mode 100644 index 000000000..25783479b --- /dev/null +++ b/components/waves/detailed/winners/WaveWinnersHeader.tsx @@ -0,0 +1,18 @@ +import React from "react"; + +interface WaveWinnersHeaderProps { + readonly wave: any; +} + +export const WaveWinnersHeader: React.FC = ({ + wave, +}) => { + return ( +
+

+ Wave Winners +

+

Lorem ipsum dolor sit amet consectetur

+
+ ); +}; diff --git a/components/waves/detailed/winners/drops/WaveWinnersDrop.tsx b/components/waves/detailed/winners/drops/WaveWinnersDrop.tsx new file mode 100644 index 000000000..209ff24cf --- /dev/null +++ b/components/waves/detailed/winners/drops/WaveWinnersDrop.tsx @@ -0,0 +1,25 @@ +import React from "react"; +import { ExtendedDrop } from "../../../../../helpers/waves/drop.helpers"; +import { WaveWinnersDropHeader } from "./header/WaveWinnersDropHeader"; +import { WaveWinnersDropContent } from "./WaveWinnersDropContent"; + +interface WaveWinnersDropProps { + readonly drop: ExtendedDrop; + readonly onDropClick: (drop: ExtendedDrop) => void; +} + +export const WaveWinnersDrop: React.FC = ({ + drop, + onDropClick, +}) => { + return ( +
+
+
+ + +
+
+
+ ); +}; diff --git a/components/waves/detailed/winners/drops/WaveWinnersDropContent.tsx b/components/waves/detailed/winners/drops/WaveWinnersDropContent.tsx new file mode 100644 index 000000000..e9c0d6f2d --- /dev/null +++ b/components/waves/detailed/winners/drops/WaveWinnersDropContent.tsx @@ -0,0 +1,26 @@ +import React, { useState } from "react"; +import { ExtendedDrop } from "../../../../../helpers/waves/drop.helpers"; +import WaveDetailedDropContent from "../../drops/WaveDetailedDropContent"; + +interface WaveWinnersDropContentProps { + readonly drop: ExtendedDrop; +} + +export const WaveWinnersDropContent: React.FC = ({ + drop, +}) => { + const [activePartIndex, setActivePartIndex] = useState(0); + return ( +
+ {}} + onDropClick={() => {}} + onQuoteClick={() => {}} + setLongPressTriggered={() => {}} + /> +
+ ); +}; diff --git a/components/waves/detailed/winners/drops/WaveWinnersDrops.tsx b/components/waves/detailed/winners/drops/WaveWinnersDrops.tsx new file mode 100644 index 000000000..87a84512a --- /dev/null +++ b/components/waves/detailed/winners/drops/WaveWinnersDrops.tsx @@ -0,0 +1,49 @@ +import React, { useContext, useMemo } from "react"; +import { WaveWinnersDrop } from "./WaveWinnersDrop"; +import { WaveDropsLeaderboardSortDirection } from "../../../../../hooks/useWaveDropsLeaderboard"; +import { WaveDropsLeaderboardSortBy } from "../../../../../hooks/useWaveDropsLeaderboard"; +import { AuthContext } from "../../../../auth/Auth"; +import { useWaveDropsLeaderboard } from "../../../../../hooks/useWaveDropsLeaderboard"; +import { useIntersectionObserver } from "../../../../../hooks/useIntersectionObserver"; + +interface WaveWinnersDropsProps { + readonly wave: any; + readonly onDropClick: (drop: any) => void; +} + +export const WaveWinnersDrops: React.FC = ({ + wave, + onDropClick, +}) => { + const { connectedProfile } = useContext(AuthContext); + const { drops, fetchNextPage, hasNextPage, isFetching, isFetchingNextPage } = + useWaveDropsLeaderboard({ + waveId: wave.id, + connectedProfileHandle: connectedProfile?.profile?.handle, + reverse: false, + dropsSortBy: WaveDropsLeaderboardSortBy.RANK, + sortDirection: WaveDropsLeaderboardSortDirection.ASC, + pollingEnabled: false, + }); + + const memoizedDrops = useMemo(() => drops, [drops]); + + const intersectionElementRef = useIntersectionObserver(() => { + if (hasNextPage && !isFetching && !isFetchingNextPage) { + fetchNextPage(); + } + }); + return ( +
+ {memoizedDrops.map((drop) => ( + + ))} + {isFetchingNextPage && ( +
+
+
+ )} +
+
+ ); +}; diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeader.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeader.tsx new file mode 100644 index 000000000..2b81a2c95 --- /dev/null +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeader.tsx @@ -0,0 +1,45 @@ +import React from "react"; +import { ExtendedDrop } from "../../../../../../helpers/waves/drop.helpers"; +import { UserCICAndLevelSize } from "../../../../../user/utils/UserCICAndLevel"; +import { + cicToType, + formatNumber, + formatNumberWithCommas, + getTimeAgoShort, +} from "../../../../../../helpers/Helpers"; +import UserCICAndLevel from "../../../../../user/utils/UserCICAndLevel"; +import Link from "next/link"; +import WaveWinnersDropHeaderTotalVotes from "./WaveWinnersDropHeaderTotalVotes"; +import WaveWinnersDropHeaderVoters from "./WaveWinnersDropHeaderVoters"; +import WaveWinnersDropHeaderAuthorPfp from "./WaveWinnersDropHeaderAuthorPfp"; +import WaveWinnersDropHeaderRank from "./WaveWinnersDropHeaderRank"; +import WaveWinnersDropHeaderAuthorHandle from "./WaveWinnersDropHeaderAuthorHandle"; +import WaveWinnersDropHeaderCreated from "./WaveWinnersDropHeaderCreated"; + +interface WaveWinnersDropHeaderProps { + readonly drop: ExtendedDrop; +} + +export const WaveWinnersDropHeader: React.FC = ({ + drop, +}) => { + return ( +
+
+
+ + +
+
+ + +
+
+ +
+ + +
+
+ ); +}; diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorHandle.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorHandle.tsx new file mode 100644 index 000000000..36a09e7a1 --- /dev/null +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorHandle.tsx @@ -0,0 +1,21 @@ +import Link from "next/link"; +import { ExtendedDrop } from "../../../../../../helpers/waves/drop.helpers"; + +interface WaveWinnersDropHeaderAuthorHandleProps { + readonly drop: ExtendedDrop; +} + +export default function WaveWinnersDropHeaderAuthorHandle({ + drop, +}: WaveWinnersDropHeaderAuthorHandleProps) { + return ( +
+ + {drop.author.handle} + +
+ ); +} diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorPfp.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorPfp.tsx new file mode 100644 index 000000000..13371d61b --- /dev/null +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorPfp.tsx @@ -0,0 +1,37 @@ +import { ExtendedDrop } from "../../../../../../helpers/waves/drop.helpers"; +import { cicToType } from "../../../../../../helpers/Helpers"; +import UserCICAndLevel from "../../../../../user/utils/UserCICAndLevel"; +import { UserCICAndLevelSize } from "../../../../../user/utils/UserCICAndLevel"; +import { + getScaledImageUri, + ImageScale, +} from "../../../../../../helpers/image.helpers"; + +interface WaveWinnersDropHeaderAuthorPfpProps { + readonly drop: ExtendedDrop; +} + +export default function WaveWinnersDropHeaderAuthorPfp({ + drop, +}: WaveWinnersDropHeaderAuthorPfpProps) { + return ( +
+ {drop.author.pfp ? ( + User avatar + ) : ( +
+ )} +
+ +
+
+ ); +} diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderCreated.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderCreated.tsx new file mode 100644 index 000000000..ff69f6f78 --- /dev/null +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderCreated.tsx @@ -0,0 +1,16 @@ +import { getTimeAgoShort } from "../../../../../../helpers/Helpers"; +import { ExtendedDrop } from "../../../../../../helpers/waves/drop.helpers"; + +interface WaveWinnersDropHeaderCreatedProps { + readonly drop: ExtendedDrop; +} + +export default function WaveWinnersDropHeaderCreated({ + drop, +}: WaveWinnersDropHeaderCreatedProps) { + return ( + + {getTimeAgoShort(drop.created_at)} + + ); +} diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx new file mode 100644 index 000000000..dafe6093c --- /dev/null +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx @@ -0,0 +1,28 @@ +import { ExtendedDrop } from "../../../../../../helpers/waves/drop.helpers"; + +interface WaveWinnersDropHeaderRankProps { + readonly drop: ExtendedDrop; +} + +export default function WaveWinnersDropHeaderRank({ + drop, +}: WaveWinnersDropHeaderRankProps) { + return ( +
+ + + #{drop.rank} + +
+ ); +} diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderTotalVotes.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderTotalVotes.tsx new file mode 100644 index 000000000..7a7ce8bc0 --- /dev/null +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderTotalVotes.tsx @@ -0,0 +1,27 @@ +import { ApiDrop } from "../../../../../../generated/models/ApiDrop"; +import { formatNumberWithCommas } from "../../../../../../helpers/Helpers"; + +interface WaveWinnersDropHeaderTotalVotesProps { + readonly drop: ApiDrop; +} + +export default function WaveWinnersDropHeaderTotalVotes({ + drop, +}: WaveWinnersDropHeaderTotalVotesProps) { + return ( +
+ = 0 + ? "tw-from-emerald-400 tw-to-emerald-500" + : "tw-from-red tw-to-red" + } tw-font-semibold tw-bg-gradient-to-r tw-bg-clip-text tw-text-transparent`} + > + {formatNumberWithCommas(drop.rating)} + + + {drop.wave.voting_credit_type} total + +
+ ); +} diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoter.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoter.tsx new file mode 100644 index 000000000..38cd8afbc --- /dev/null +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoter.tsx @@ -0,0 +1,53 @@ +import Tippy from "@tippyjs/react"; +import { getScaledImageUri } from "../../../../../../helpers/image.helpers"; +import { ApiDropRater } from "../../../../../../generated/models/ApiDropRater"; +import { ExtendedDrop } from "../../../../../../helpers/waves/drop.helpers"; +import { formatNumberWithCommas } from "../../../../../../helpers/Helpers"; +import Link from "next/link"; +import { ImageScale } from "../../../../../../helpers/image.helpers"; + +interface WaveWinnersDropHeaderVoterProps { + readonly voter: ApiDropRater; + readonly drop: ExtendedDrop; + readonly index: number; +} + +export default function WaveWinnersDropHeaderVoter({ + voter, + drop, + index, +}: WaveWinnersDropHeaderVoterProps) { + return ( + + {voter.profile.handle} • {formatNumberWithCommas(voter.rating)}{" "} + {drop.wave.voting_credit_type} + + } + interactive={true} + delay={[0, 0]} + hideOnClick={false} + appendTo={() => document.body} + zIndex={1000} + > +
+ + {voter.profile.pfp ? ( + {`${voter.profile.handle}'s + ) : ( +
+ )} + +
+
+ ); +} diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoters.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoters.tsx new file mode 100644 index 000000000..20d227539 --- /dev/null +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoters.tsx @@ -0,0 +1,30 @@ +import { formatNumberWithCommas } from "../../../../../../helpers/Helpers"; +import { ExtendedDrop } from "../../../../../../helpers/waves/drop.helpers"; +import WaveWinnersDropHeaderVoter from "./WaveWinnersDropHeaderVoter"; + +interface WaveWinnersDropHeaderVotersProps { + readonly drop: ExtendedDrop; +} + +export default function WaveWinnersDropHeaderVoters({ + drop, +}: WaveWinnersDropHeaderVotersProps) { + return ( +
+
+ {drop.top_raters.map((voter, index) => ( + + ))} +
+ + {formatNumberWithCommas(drop.raters_count)}{" "} + {drop.raters_count === 1 ? "voter" : "voters"} + +
+ ); +} diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx new file mode 100644 index 000000000..737bad300 --- /dev/null +++ b/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx @@ -0,0 +1,114 @@ +import React, { useContext } from "react"; +import { ApiWave } from "../../../../../generated/models/ApiWave"; +import { ExtendedDrop } from "../../../../../helpers/waves/drop.helpers"; +import { + useWaveDropsLeaderboard, + WaveDropsLeaderboardSortDirection, + WaveDropsLeaderboardSortBy, +} from "../../../../../hooks/useWaveDropsLeaderboard"; +import { AuthContext } from "../../../../auth/Auth"; +import { WaveWinnersPodiumFirst } from "./WaveWinnersPodiumFirst"; +import { WaveWinnersPodiumSecond } from "./WaveWinnersPodiumSecond"; +import { WaveWinnersPodiumThird } from "./WaveWinnersPodiumThird"; + +interface WaveWinnersPodiumProps { + readonly wave: ApiWave; + readonly onDropClick: (drop: ExtendedDrop) => void; +} + +const PodiumPlaceholderCard = ({ height }: { height: string }) => ( +
+
+
+
+
+); + +export const WaveWinnersPodium: React.FC = ({ + wave, + onDropClick, +}) => { + const { connectedProfile } = useContext(AuthContext); + const { drops, isFetching } = useWaveDropsLeaderboard({ + waveId: wave.id, + connectedProfileHandle: connectedProfile?.profile?.handle, + reverse: false, + dropsSortBy: WaveDropsLeaderboardSortBy.RANK, + sortDirection: WaveDropsLeaderboardSortDirection.ASC, + pollingEnabled: false, + }); + + const firstPlaceDrop = drops[0] ?? null; + const secondPlaceDrop = drops[1] ?? null; + const thirdPlaceDrop = drops[2] ?? null; + + if (isFetching && !drops.length) { + return ( +
+
+ + + +
+
+ ); + } + + if (!drops.length) { + return ( +
+
+
+ + + +
+
+ No Submissions +
+

+ This wave ended without any submissions +

+
+
+ ); + } + + return ( +
+
+ {secondPlaceDrop && ( + + )} + {firstPlaceDrop && ( + + )} + {thirdPlaceDrop && ( + + )} +
+
+ ); +}; diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx new file mode 100644 index 000000000..5f70906d8 --- /dev/null +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx @@ -0,0 +1,83 @@ +import React from "react"; +import Link from "next/link"; +import { ExtendedDrop } from "../../../../../helpers/waves/drop.helpers"; +import { formatNumberWithCommas } from "../../../../../helpers/Helpers"; +import { ImageScale } from "../../../../../helpers/image.helpers"; +import { getScaledImageUri } from "../../../../../helpers/image.helpers"; + +interface WaveWinnersPodiumFirstProps { + readonly drop: ExtendedDrop; + readonly onDropClick: (drop: ExtendedDrop) => void; +} + +export const WaveWinnersPodiumFirst: React.FC = ({ + drop, + onDropClick, +}) => { + return ( +
+
+ +
+ {drop.author.pfp ? ( + + ) : ( +
+ )} +
+ {drop.author.handle} +
+
+ +
+
+
+
+ WINNER{" "} + +
+
+ +
+
+
+ = 0 ? "tw-text-emerald-400" : "tw-text-red" + } tw-font-semibold tw-text-xl`} + > + {formatNumberWithCommas(drop.rating)} + + + {drop.wave.voting_credit_type} total + +
+
+ {formatNumberWithCommas(drop.raters_count)}{" "} + + {drop.raters_count === 1 ? "voter" : "voters"} + +
+
+
+
+
+ ); +}; diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx new file mode 100644 index 000000000..3de98590c --- /dev/null +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx @@ -0,0 +1,69 @@ +import React from "react"; +import { ExtendedDrop } from "../../../../../helpers/waves/drop.helpers"; +import Link from "next/link"; +import { formatNumberWithCommas } from "../../../../../helpers/Helpers"; +import { ImageScale } from "../../../../../helpers/image.helpers"; +import { getScaledImageUri } from "../../../../../helpers/image.helpers"; + +interface WaveWinnersPodiumSecondProps { + readonly drop: ExtendedDrop; + readonly onDropClick: (drop: ExtendedDrop) => void; +} + +export const WaveWinnersPodiumSecond: React.FC< + WaveWinnersPodiumSecondProps +> = ({ drop, onDropClick }) => { + return ( +
+
+ + {drop.author.pfp ? ( + + ) : ( +
+ )} +
+ {drop.author.handle} +
+ +
+
+
+
+ 2nd +
+
+ +
+
+
+ = 0 ? "tw-text-emerald-400" : "tw-text-red" + } tw-font-medium`} + > + {formatNumberWithCommas(drop.rating)} + + + {drop.wave.voting_credit_type} total + +
+
+ {formatNumberWithCommas(drop.raters_count)}{" "} + + {drop.raters_count === 1 ? "voter" : "voters"} + +
+
+
+
+
+ ); +}; diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx new file mode 100644 index 000000000..b181fb6e1 --- /dev/null +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx @@ -0,0 +1,70 @@ +import React from "react"; +import { ExtendedDrop } from "../../../../../helpers/waves/drop.helpers"; +import Link from "next/link"; +import { formatNumberWithCommas } from "../../../../../helpers/Helpers"; +import { ImageScale } from "../../../../../helpers/image.helpers"; +import { getScaledImageUri } from "../../../../../helpers/image.helpers"; + +interface WaveWinnersPodiumThirdProps { + readonly drop: ExtendedDrop; + readonly onDropClick: (drop: ExtendedDrop) => void; +} + +export const WaveWinnersPodiumThird: React.FC = ({ + drop, + onDropClick, +}) => { + return ( +
+
+ + {drop.author.pfp ? ( + + ) : ( +
+ )} +
+ {drop.author.handle} +
+ +
+
+
+
+ 3rd +
+
+ +
+
+
+ = 0 ? "tw-text-emerald-400" : "tw-text-red" + } tw-font-medium`} + > + {formatNumberWithCommas(drop.rating)} + + + {drop.wave.voting_credit_type} total + +
+
+ {formatNumberWithCommas(drop.raters_count)}{" "} + + {drop.raters_count === 1 ? "voter" : "voters"} + +
+
+
+
+
+ ); +}; diff --git a/hooks/useWaveDropsLeaderboard.ts b/hooks/useWaveDropsLeaderboard.ts index 3f81ab6d5..a13862c4c 100644 --- a/hooks/useWaveDropsLeaderboard.ts +++ b/hooks/useWaveDropsLeaderboard.ts @@ -34,6 +34,7 @@ interface UseWaveDropsLeaderboardProps { readonly dropsSortBy: WaveDropsLeaderboardSortBy; readonly sortDirection: WaveDropsLeaderboardSortDirection; readonly handle?: string; + readonly pollingEnabled?: boolean; } const POLLING_DELAY = 3000; @@ -60,6 +61,7 @@ export function useWaveDropsLeaderboard({ dropsSortBy, sortDirection, handle, + pollingEnabled = true, }: UseWaveDropsLeaderboardProps) { const queryClient = useQueryClient(); @@ -187,14 +189,14 @@ export function useWaveDropsLeaderboard({ params, }); }, - enabled: !haveNewDrops && canPoll, - refetchInterval: isTabVisible + enabled: !haveNewDrops && canPoll && pollingEnabled, + refetchInterval: isTabVisible && pollingEnabled ? ACTIVE_POLLING_INTERVAL : INACTIVE_POLLING_INTERVAL, - refetchOnWindowFocus: true, - refetchOnMount: true, - refetchOnReconnect: true, - refetchIntervalInBackground: true, + refetchOnWindowFocus: pollingEnabled, + refetchOnMount: pollingEnabled, + refetchOnReconnect: pollingEnabled, + refetchIntervalInBackground: pollingEnabled, }); useEffect(() => { From 13123dff8098ffb15ff51840e3f7338160aafbd6 Mon Sep 17 00:00:00 2001 From: Ragne Date: Thu, 19 Dec 2024 11:44:13 +0200 Subject: [PATCH 03/33] wip Signed-off-by: Ragne --- .../waves/detailed/winners/WaveWinners.tsx | 8 +++++--- .../detailed/winners/WaveWinnersHeader.tsx | 15 ++++++++++----- .../winners/podium/WaveWinnersPodiumFirst.tsx | 6 ++++-- .../winners/podium/WaveWinnersPodiumSecond.tsx | 17 +++++++++++++++-- .../winners/podium/WaveWinnersPodiumThird.tsx | 15 +++++++++++++-- 5 files changed, 47 insertions(+), 14 deletions(-) diff --git a/components/waves/detailed/winners/WaveWinners.tsx b/components/waves/detailed/winners/WaveWinners.tsx index 15b092a36..a1cf5c022 100644 --- a/components/waves/detailed/winners/WaveWinners.tsx +++ b/components/waves/detailed/winners/WaveWinners.tsx @@ -13,9 +13,11 @@ export const WaveWinners: React.FC = ({ onDropClick, }) => { return ( -
- - +
+
+ + +
); diff --git a/components/waves/detailed/winners/WaveWinnersHeader.tsx b/components/waves/detailed/winners/WaveWinnersHeader.tsx index 25783479b..76f094790 100644 --- a/components/waves/detailed/winners/WaveWinnersHeader.tsx +++ b/components/waves/detailed/winners/WaveWinnersHeader.tsx @@ -9,10 +9,15 @@ export const WaveWinnersHeader: React.FC = ({ }) => { return (
-

- Wave Winners -

-

Lorem ipsum dolor sit amet consectetur

-
+
+
+

+ Wave Winners +

+
+

+ Celebrating the most impactful contributors in this wave +

+
); }; diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx index 5f70906d8..fe2a259a7 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx @@ -39,8 +39,7 @@ export const WaveWinnersPodiumFirst: React.FC = ({
-
- WINNER{" "} +
+ + 1st +
diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx index 3de98590c..4ce25f415 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx @@ -36,8 +36,21 @@ export const WaveWinnersPodiumSecond: React.FC<
-
- 2nd +
+ + + 2nd +
diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx index b181fb6e1..1ae8007f2 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx @@ -37,8 +37,19 @@ export const WaveWinnersPodiumThird: React.FC = ({
-
- 3rd +
+ + 3rd
From 00386fc5099537c74dcee86cf977c6759dbc5675 Mon Sep 17 00:00:00 2001 From: Ragne Date: Thu, 19 Dec 2024 13:42:24 +0200 Subject: [PATCH 04/33] wip Signed-off-by: Ragne --- .../waves/detailed/winners/WaveWinners.tsx | 9 +- .../podium/WavePodiumItemContentOutcomes.tsx | 278 ++++++++++++++++++ .../winners/podium/WaveWinnersPodium.tsx | 42 +-- .../winners/podium/WaveWinnersPodiumFirst.tsx | 98 +++--- .../podium/WaveWinnersPodiumSecond.tsx | 78 +++-- .../winners/podium/WaveWinnersPodiumThird.tsx | 73 +++-- 6 files changed, 462 insertions(+), 116 deletions(-) create mode 100644 components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx diff --git a/components/waves/detailed/winners/WaveWinners.tsx b/components/waves/detailed/winners/WaveWinners.tsx index a1cf5c022..6d48d9673 100644 --- a/components/waves/detailed/winners/WaveWinners.tsx +++ b/components/waves/detailed/winners/WaveWinners.tsx @@ -13,11 +13,10 @@ export const WaveWinners: React.FC = ({ onDropClick, }) => { return ( -
-
- - -
+
+ {/* */} + +
); diff --git a/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx b/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx new file mode 100644 index 000000000..5945dd2e6 --- /dev/null +++ b/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx @@ -0,0 +1,278 @@ +import React, { useState, useEffect } from "react"; +import Tippy from "@tippyjs/react"; +import { ApiWave } from "../../../../../generated/models/ApiWave"; +import { ApiWaveOutcomeCredit } from "../../../../../generated/models/ApiWaveOutcomeCredit"; +import { ApiWaveOutcomeType } from "../../../../../generated/models/ApiWaveOutcomeType"; +import { ApiDrop } from "../../../../../generated/models/ApiDrop"; + +interface WavePodiumItemContentOutcomesProps { + readonly drop: ApiDrop; + readonly wave: ApiWave; + readonly isMobile?: boolean; +} + +interface OutcomeSummary { + nicTotal: number; + repTotal: number; + manualOutcomes: string[]; +} + +const calculateNIC = ({ + drop, + wave, +}: { + drop: ApiDrop; + wave: ApiWave; +}): number => { + const rank = drop.rank; + if (!rank) return 0; + const outcomes = wave.outcomes; + const nicOutcomes = outcomes.filter( + (outcome) => outcome.credit === ApiWaveOutcomeCredit.Cic + ); + const nic = nicOutcomes.reduce((acc, outcome) => { + return acc + (outcome.distribution?.[rank - 1]?.amount ?? 0); + }, 0); + return nic; +}; + +const calculateRep = ({ + drop, + wave, +}: { + drop: ApiDrop; + wave: ApiWave; +}): number => { + const rank = drop.rank; + if (!rank) return 0; + const outcomes = wave.outcomes; + const repOutcomes = outcomes.filter( + (outcome) => outcome.credit === ApiWaveOutcomeCredit.Rep + ); + const rep = repOutcomes.reduce((acc, outcome) => { + return acc + (outcome.distribution?.[rank - 1]?.amount ?? 0); + }, 0); + return rep; +}; + +const calculateManualOutcomes = ({ + drop, + wave, +}: { + drop: ApiDrop; + wave: ApiWave; +}): string[] => { + const rank = drop.rank; + if (!rank) return []; + const outcomes = wave.outcomes; + const manualOutcomes = outcomes.filter( + (outcome) => outcome.type === ApiWaveOutcomeType.Manual + ); + return manualOutcomes + .filter((outcome) => !!outcome.distribution?.[rank - 1]?.amount) + .map((outcome) => outcome.distribution?.[rank - 1]?.description ?? ""); +}; + +const calculateOutcomeSummary = ({ + drop, + wave, +}: { + drop: ApiDrop; + wave: ApiWave; +}): OutcomeSummary => { + return { + nicTotal: calculateNIC({ drop, wave }), + repTotal: calculateRep({ drop, wave }), + manualOutcomes: calculateManualOutcomes({ drop, wave }), + }; +}; + +export const WavePodiumItemContentOutcomes: React.FC = ({ + drop, + wave, + isMobile = false, +}) => { + const [isTouch, setIsTouch] = useState(false); + const [isOpen, setIsOpen] = useState(false); + + useEffect(() => { + setIsTouch('ontouchstart' in window); + }, []); + + const handleClick = (e: React.MouseEvent) => { + if (isTouch) { + e.stopPropagation(); + setIsOpen(!isOpen); + } + }; + + const { nicTotal, repTotal, manualOutcomes } = calculateOutcomeSummary({ + drop, + wave, + }); + const totalOutcomes = + (nicTotal ? 1 : 0) + (repTotal ? 1 : 0) + manualOutcomes.length; + + if (totalOutcomes === 0) { + return null; + } + + const tooltipContent = ( +
+
+ + Outcome + +
+ {!!nicTotal && ( +
+
+ + + NIC + +
+ + {nicTotal} + +
+ )} + {!!repTotal && ( +
+
+ + + Rep + +
+ + {repTotal} + +
+ )} + {manualOutcomes.map((outcome) => ( +
+
+ + + {outcome} + +
+
+ ))} +
+
+
+ ); + + return ( + setIsOpen(false)} + > + + + ); +}; diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx index 737bad300..529361ea9 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx @@ -88,26 +88,28 @@ export const WaveWinnersPodium: React.FC = ({ } return ( -
-
- {secondPlaceDrop && ( - - )} - {firstPlaceDrop && ( - - )} - {thirdPlaceDrop && ( - - )} +
+
+
+ {secondPlaceDrop && ( + + )} + {firstPlaceDrop && ( + + )} + {thirdPlaceDrop && ( + + )} +
); diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx index fe2a259a7..a2eb2151d 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx @@ -2,8 +2,13 @@ import React from "react"; import Link from "next/link"; import { ExtendedDrop } from "../../../../../helpers/waves/drop.helpers"; import { formatNumberWithCommas } from "../../../../../helpers/Helpers"; -import { ImageScale } from "../../../../../helpers/image.helpers"; -import { getScaledImageUri } from "../../../../../helpers/image.helpers"; +import { + getScaledImageUri, + ImageScale, +} from "../../../../../helpers/image.helpers"; +import { WavePodiumItemContentOutcomes } from "./WavePodiumItemContentOutcomes"; +import { ApiDrop } from "../../../../../generated/models/ApiDrop"; +import { ApiWave } from "../../../../../generated/models/ApiWave"; interface WaveWinnersPodiumFirstProps { readonly drop: ExtendedDrop; @@ -16,32 +21,27 @@ export const WaveWinnersPodiumFirst: React.FC = ({ }) => { return (
-
+
+
-
- {drop.author.pfp ? ( - - ) : ( -
- )} -
- {drop.author.handle} -
-
+ {drop.author.pfp ? ( + + ) : ( +
+ )} -
-
-
-
+ +
+
- + 1st
+
+
+
+
+
+
+
+
+
+
+ + +
+ {drop.author.handle} +
+ -
-
-
+
+
= 0 ? "tw-text-emerald-400" : "tw-text-red" - } tw-font-semibold tw-text-xl`} + drop.rating >= 0 ? "tw-text-[#E8D48A]" : "tw-text-[#ff4466]" + } tw-font-semibold tw-text-2xl`} > {formatNumberWithCommas(drop.rating)} - - {drop.wave.voting_credit_type} total + + {drop.wave.voting_credit_type}
-
- {formatNumberWithCommas(drop.raters_count)}{" "} - - {drop.raters_count === 1 ? "voter" : "voters"} - + +
+
+ + {formatNumberWithCommas(drop.raters_count)} + + + {drop.raters_count === 1 ? "voter" : "voters"} + +
+ + +
diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx index 4ce25f415..4bafd66e7 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx @@ -10,35 +10,34 @@ interface WaveWinnersPodiumSecondProps { readonly onDropClick: (drop: ExtendedDrop) => void; } -export const WaveWinnersPodiumSecond: React.FC< - WaveWinnersPodiumSecondProps -> = ({ drop, onDropClick }) => { +export const WaveWinnersPodiumSecond: React.FC = ({ + drop, + onDropClick, +}) => { return ( -
-
+
+
+
+ {drop.author.pfp ? ( ) : ( -
+
)} -
- {drop.author.handle} -
-
-
-
-
+ +
+
- + 2nd
+
-
-
-
+
+
+ +
+ {drop.author.handle} +
+ + +
+
= 0 ? "tw-text-emerald-400" : "tw-text-red" - } tw-font-medium`} + drop.rating >= 0 ? "tw-text-[#DDDDDD]" : "tw-text-[#ff4466]" + } tw-font-semibold tw-text-base`} > {formatNumberWithCommas(drop.rating)} - - {drop.wave.voting_credit_type} total + + {drop.wave.voting_credit_type}
-
- {formatNumberWithCommas(drop.raters_count)}{" "} - - {drop.raters_count === 1 ? "voter" : "voters"} - + +
+
+ + {formatNumberWithCommas(drop.raters_count)} + + + {drop.raters_count === 1 ? "voter" : "voters"} + +
+ +
+ x + outcome +
diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx index 1ae8007f2..a9197971f 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx @@ -15,31 +15,29 @@ export const WaveWinnersPodiumThird: React.FC = ({ onDropClick, }) => { return ( -
-
+
+
+
+ {drop.author.pfp ? ( ) : ( -
+
)} -
- {drop.author.handle} -
-
-
-
-
+ +
+
- 3rd + + 3rd +
+
-
-
-
+
+
+ +
+ {drop.author.handle} +
+ + +
+
= 0 ? "tw-text-emerald-400" : "tw-text-red" - } tw-font-medium`} + drop.rating >= 0 ? "tw-text-[#CD7F32]" : "tw-text-[#ff4466]" + } tw-font-semibold tw-text-base`} > {formatNumberWithCommas(drop.rating)} - - {drop.wave.voting_credit_type} total + + {drop.wave.voting_credit_type}
-
- {formatNumberWithCommas(drop.raters_count)}{" "} - - {drop.raters_count === 1 ? "voter" : "voters"} - + +
+
+ + {formatNumberWithCommas(drop.raters_count)} + + + {drop.raters_count === 1 ? "voter" : "voters"} + +
+ +
+ x + outcome +
From ac01dc071712afdc887d9ea6e0b4530b046e19e3 Mon Sep 17 00:00:00 2001 From: Ragne Date: Thu, 19 Dec 2024 14:53:39 +0200 Subject: [PATCH 05/33] wip Signed-off-by: Ragne --- .../podium/WavePodiumItemContentOutcomes.tsx | 4 +- .../winners/podium/WaveWinnersPodium.tsx | 68 ++++++-- .../winners/podium/WaveWinnersPodiumFirst.tsx | 146 +++++++++--------- .../podium/WaveWinnersPodiumSecond.tsx | 141 +++++++++-------- .../winners/podium/WaveWinnersPodiumThird.tsx | 135 ++++++++-------- 5 files changed, 279 insertions(+), 215 deletions(-) diff --git a/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx b/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx index 5945dd2e6..1fc22efeb 100644 --- a/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx +++ b/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx @@ -215,9 +215,9 @@ export const WavePodiumItemContentOutcomes: React.FC
); +const podiumVariants = { + hidden: { + opacity: 0, + y: 20, + scale: 0.98 + }, + visible: (i: number) => ({ + opacity: 1, + y: 0, + scale: 1, + transition: { + delay: i * 0.08, + duration: 0.4, + ease: [0.2, 0.9, 0.3, 1], + opacity: { duration: 0.25 } + } + }) +}; + export const WaveWinnersPodium: React.FC = ({ wave, onDropClick, @@ -92,22 +112,46 @@ export const WaveWinnersPodium: React.FC = ({
{secondPlaceDrop && ( - + + + )} {firstPlaceDrop && ( - + + + )} {thirdPlaceDrop && ( - + + + )}
diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx index a2eb2151d..0cc1c33dd 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx @@ -7,101 +7,109 @@ import { ImageScale, } from "../../../../../helpers/image.helpers"; import { WavePodiumItemContentOutcomes } from "./WavePodiumItemContentOutcomes"; -import { ApiDrop } from "../../../../../generated/models/ApiDrop"; import { ApiWave } from "../../../../../generated/models/ApiWave"; interface WaveWinnersPodiumFirstProps { readonly drop: ExtendedDrop; + readonly wave: ApiWave; readonly onDropClick: (drop: ExtendedDrop) => void; } export const WaveWinnersPodiumFirst: React.FC = ({ drop, + wave, onDropClick, }) => { return ( -
-
-
- - {drop.author.pfp ? ( - - ) : ( -
- )} - - -
-
- - - 1st - -
-
-
-
-
-
-
-
-
-
-
-
- +
onDropClick(drop)} + className="tw-cursor-pointer tw-group" + > +
+
+
e.stopPropagation()} + className="tw-transform hover:tw-scale-105 tw-transition-all tw-duration-300" > -
- {drop.author.handle} -
+ {drop.author.pfp ? ( + + ) : ( +
+ )} -
-
- = 0 ? "tw-text-[#E8D48A]" : "tw-text-[#ff4466]" - } tw-font-semibold tw-text-2xl`} +
+
+ - {drop.wave.voting_credit_type} + + + + 1st
+
+
+ +
+
+
+
+
+
+
+
+
-
-
- - {formatNumberWithCommas(drop.raters_count)} + e.stopPropagation()} + className="tw-transition-all tw-no-underline tw-mb-4 tw-relative" + > +
+ {drop.author.handle} +
+ + +
+
+ = 0 ? "tw-text-[#E8D48A]" : "tw-text-[#ff4466]" + } tw-font-semibold tw-text-2xl`} + > + {formatNumberWithCommas(drop.rating)} - {drop.raters_count === 1 ? "voter" : "voters"} + {drop.wave.voting_credit_type}
- - +
+
+ + {formatNumberWithCommas(drop.raters_count)} + + + {drop.raters_count === 1 ? "voter" : "voters"} + +
+ + +
diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx index 4bafd66e7..d9d8d3257 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx @@ -2,96 +2,101 @@ import React from "react"; import { ExtendedDrop } from "../../../../../helpers/waves/drop.helpers"; import Link from "next/link"; import { formatNumberWithCommas } from "../../../../../helpers/Helpers"; -import { ImageScale } from "../../../../../helpers/image.helpers"; -import { getScaledImageUri } from "../../../../../helpers/image.helpers"; +import { + ImageScale, + getScaledImageUri, +} from "../../../../../helpers/image.helpers"; +import { WavePodiumItemContentOutcomes } from "./WavePodiumItemContentOutcomes"; +import { ApiWave } from "../../../../../generated/models/ApiWave"; interface WaveWinnersPodiumSecondProps { readonly drop: ExtendedDrop; + readonly wave: ApiWave; readonly onDropClick: (drop: ExtendedDrop) => void; } -export const WaveWinnersPodiumSecond: React.FC = ({ - drop, - onDropClick, -}) => { +export const WaveWinnersPodiumSecond: React.FC< + WaveWinnersPodiumSecondProps +> = ({ drop, wave, onDropClick }) => { return ( -
-
-
+
onDropClick(drop)} className="tw-cursor-pointer tw-group"> +
+
+
- - {drop.author.pfp ? ( - - ) : ( -
- )} - - -
-
- - - 2nd - -
-
-
- -
-
e.stopPropagation()} + className="tw-transform hover:tw-scale-105 tw-transition-all tw-duration-300" > -
- {drop.author.handle} -
+ {drop.author.pfp ? ( + + ) : ( +
+ )} -
-
- = 0 ? "tw-text-[#DDDDDD]" : "tw-text-[#ff4466]" - } tw-font-semibold tw-text-base`} +
+
+ - {drop.wave.voting_credit_type} + + + + 2nd
+
+
-
-
- - {formatNumberWithCommas(drop.raters_count)} +
+
+ e.stopPropagation()} + className="tw-transition-all tw-no-underline tw-mb-3" + > +
+ {drop.author.handle} +
+ + +
+
+ = 0 ? "tw-text-[#DDDDDD]" : "tw-text-[#ff4466]" + } tw-font-semibold tw-text-base`} + > + {formatNumberWithCommas(drop.rating)} - {drop.raters_count === 1 ? "voter" : "voters"} + {drop.wave.voting_credit_type}
-
- x - outcome +
+
+ + {formatNumberWithCommas(drop.raters_count)} + + + {drop.raters_count === 1 ? "voter" : "voters"} + +
+ +
diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx index a9197971f..cd10da222 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx @@ -2,96 +2,103 @@ import React from "react"; import { ExtendedDrop } from "../../../../../helpers/waves/drop.helpers"; import Link from "next/link"; import { formatNumberWithCommas } from "../../../../../helpers/Helpers"; -import { ImageScale } from "../../../../../helpers/image.helpers"; -import { getScaledImageUri } from "../../../../../helpers/image.helpers"; +import { + getScaledImageUri, + ImageScale, +} from "../../../../../helpers/image.helpers"; +import { WavePodiumItemContentOutcomes } from "./WavePodiumItemContentOutcomes"; +import { ApiWave } from "../../../../../generated/models/ApiWave"; interface WaveWinnersPodiumThirdProps { readonly drop: ExtendedDrop; + readonly wave: ApiWave; readonly onDropClick: (drop: ExtendedDrop) => void; } export const WaveWinnersPodiumThird: React.FC = ({ drop, + wave, onDropClick, }) => { return ( -
-
-
+
onDropClick(drop)} className="tw-cursor-pointer tw-group"> +
+
+
- - {drop.author.pfp ? ( - - ) : ( -
- )} - - -
-
- - - 3rd - -
-
-
- -
-
e.stopPropagation()} + className="tw-transform hover:tw-scale-105 tw-transition-all tw-duration-300" > -
- {drop.author.handle} -
+ {drop.author.pfp ? ( + + ) : ( +
+ )} -
-
- = 0 ? "tw-text-[#CD7F32]" : "tw-text-[#ff4466]" - } tw-font-semibold tw-text-base`} +
+
+ - {drop.wave.voting_credit_type} + + + + 3rd
+
+
-
-
- - {formatNumberWithCommas(drop.raters_count)} +
+
+ e.stopPropagation()} + className="tw-transition-all tw-no-underline tw-mb-3" + > +
+ {drop.author.handle} +
+ + +
+
+ = 0 ? "tw-text-[#CD7F32]" : "tw-text-[#ff4466]" + } tw-font-semibold tw-text-base`} + > + {formatNumberWithCommas(drop.rating)} - {drop.raters_count === 1 ? "voter" : "voters"} + {drop.wave.voting_credit_type}
-
- x - outcome +
+
+ + {formatNumberWithCommas(drop.raters_count)} + + + {drop.raters_count === 1 ? "voter" : "voters"} + +
+ +
From 906853deca1a71d13ba93d145af175012311795c Mon Sep 17 00:00:00 2001 From: Ragne Date: Thu, 19 Dec 2024 15:04:40 +0200 Subject: [PATCH 06/33] wip Signed-off-by: Ragne --- .../podium/WavePodiumItemContentOutcomes.tsx | 127 +++++++++--------- .../winners/podium/WaveWinnersPodium.tsx | 57 ++++---- .../winners/podium/WaveWinnersPodiumFirst.tsx | 2 +- .../podium/WaveWinnersPodiumSecond.tsx | 2 +- .../winners/podium/WaveWinnersPodiumThird.tsx | 7 +- 5 files changed, 101 insertions(+), 94 deletions(-) diff --git a/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx b/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx index 1fc22efeb..b3f5ebfad 100644 --- a/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx +++ b/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx @@ -118,14 +118,14 @@ export const WavePodiumItemContentOutcomes: React.FC -
+
+
- Outcome + Outcome Details -
+
{!!nicTotal && ( -
+
- + NIC
- + {nicTotal}
)} {!!repTotal && ( -
+
- + Rep
- + {repTotal}
@@ -177,7 +177,7 @@ export const WavePodiumItemContentOutcomes: React.FC (
- + {outcome}
@@ -208,68 +208,73 @@ export const WavePodiumItemContentOutcomes: React.FC setIsOpen(false)} + arrow={false} + duration={200} + className="!tw-backdrop-blur-xl" > diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx index f76c20f52..af73f2e4a 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx @@ -28,10 +28,10 @@ const PodiumPlaceholderCard = ({ height }: { height: string }) => ( ); const podiumVariants = { - hidden: { - opacity: 0, + hidden: { + opacity: 0, y: 20, - scale: 0.98 + scale: 0.98, }, visible: (i: number) => ({ opacity: 1, @@ -41,9 +41,9 @@ const podiumVariants = { delay: i * 0.08, duration: 0.4, ease: [0.2, 0.9, 0.3, 1], - opacity: { duration: 0.25 } - } - }) + opacity: { duration: 0.25 }, + }, + }), }; export const WaveWinnersPodium: React.FC = ({ @@ -78,30 +78,29 @@ export const WaveWinnersPodium: React.FC = ({ if (!drops.length) { return ( -
-
-
- - - -
-
- No Submissions +
+
+
+
+ +
+
+ No Submissions Yet +
+

+ This wave ended without any submissions +

-

- This wave ended without any submissions -

); diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx index 0cc1c33dd..717b7c3f3 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx @@ -100,7 +100,7 @@ export const WaveWinnersPodiumFirst: React.FC = ({
- + {formatNumberWithCommas(drop.raters_count)} diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx index d9d8d3257..0a926eb25 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx @@ -88,7 +88,7 @@ export const WaveWinnersPodiumSecond: React.FC<
- + {formatNumberWithCommas(drop.raters_count)} diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx index cd10da222..c9f6e5820 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx @@ -21,7 +21,10 @@ export const WaveWinnersPodiumThird: React.FC = ({ onDropClick, }) => { return ( -
onDropClick(drop)} className="tw-cursor-pointer tw-group"> +
onDropClick(drop)} + className="tw-cursor-pointer tw-group" + >
@@ -90,7 +93,7 @@ export const WaveWinnersPodiumThird: React.FC = ({
- + {formatNumberWithCommas(drop.raters_count)} From 9f11199e705779095b46325634ee1f018f4836bc Mon Sep 17 00:00:00 2001 From: Ragne Date: Thu, 19 Dec 2024 15:18:20 +0200 Subject: [PATCH 07/33] wip Signed-off-by: Ragne --- .../winners/podium/WaveWinnersPodium.tsx | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx index af73f2e4a..2edac74c9 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx @@ -19,11 +19,11 @@ interface WaveWinnersPodiumProps { const PodiumPlaceholderCard = ({ height }: { height: string }) => (
-
-
-
+
+
+
); @@ -64,13 +64,13 @@ export const WaveWinnersPodium: React.FC = ({ const secondPlaceDrop = drops[1] ?? null; const thirdPlaceDrop = drops[2] ?? null; - if (isFetching && !drops.length) { + if (!isFetching && !drops.length) { return ( -
-
- - - +
+
+ + +
); @@ -107,6 +107,7 @@ export const WaveWinnersPodium: React.FC = ({ } return ( +
From 3e5bbd589588137d2e535a89f7e3a92e9cd7cf4d Mon Sep 17 00:00:00 2001 From: Ragne Date: Mon, 23 Dec 2024 15:14:09 +0200 Subject: [PATCH 08/33] wip Signed-off-by: Ragne --- .../winners/drops/WaveWinnersDrop.tsx | 52 +++- .../winners/drops/WaveWinnersDrops.tsx | 6 +- .../drops/header/WaveWinnersDropHeader.tsx | 11 +- .../WaveWinnersDropHeaderAuthorHandle.tsx | 16 +- .../header/WaveWinnersDropHeaderAuthorPfp.tsx | 23 +- .../header/WaveWinnersDropHeaderRank.tsx | 81 ++++- .../header/WaveWinnersDropHeaderVoters.tsx | 2 +- .../drops/header/WaveWinnersDropOutcome.tsx | 292 ++++++++++++++++++ 8 files changed, 432 insertions(+), 51 deletions(-) create mode 100644 components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx diff --git a/components/waves/detailed/winners/drops/WaveWinnersDrop.tsx b/components/waves/detailed/winners/drops/WaveWinnersDrop.tsx index 209ff24cf..24aabd922 100644 --- a/components/waves/detailed/winners/drops/WaveWinnersDrop.tsx +++ b/components/waves/detailed/winners/drops/WaveWinnersDrop.tsx @@ -2,22 +2,66 @@ import React from "react"; import { ExtendedDrop } from "../../../../../helpers/waves/drop.helpers"; import { WaveWinnersDropHeader } from "./header/WaveWinnersDropHeader"; import { WaveWinnersDropContent } from "./WaveWinnersDropContent"; +import WaveWinnersDropOutcome from "./header/WaveWinnersDropOutcome"; +import { ApiWave } from "../../../../../generated/models/ApiWave"; interface WaveWinnersDropProps { - readonly drop: ExtendedDrop; + readonly drop: ExtendedDrop & { wave: ApiWave }; readonly onDropClick: (drop: ExtendedDrop) => void; } +const getColorClasses = (rank: number | null) => { + const rankStyles = { + 1: { + base: "tw-border tw-border-solid tw-border-amber-400/10 tw-bg-[linear-gradient(180deg,rgba(31,31,37,0.98)_0%,rgba(51,46,41,0.95)_100%)] tw-shadow-[inset_0_0_16px_rgba(251,191,36,0.01)]", + hover: + "desktop-hover:hover:tw-shadow-[inset_0_0_20px_rgba(251,191,36,0.02)] desktop-hover:hover:tw-border-amber-400/15 desktop-hover:hover:tw-bg-[linear-gradient(180deg,rgba(35,35,41,0.98)_0%,rgba(56,51,46,0.95)_100%)]", + }, + 2: { + base: "tw-border tw-border-solid tw-border-slate-400/10 tw-bg-[linear-gradient(180deg,rgba(31,31,37,0.98)_0%,rgba(46,46,51,0.95)_100%)] tw-shadow-[inset_0_0_16px_rgba(226,232,240,0.01)]", + hover: + "desktop-hover:hover:tw-shadow-[inset_0_0_20px_rgba(226,232,240,0.02)] desktop-hover:hover:tw-border-slate-400/15 desktop-hover:hover:tw-bg-[linear-gradient(180deg,rgba(35,35,41,0.98)_0%,rgba(51,51,56,0.95)_100%)]", + }, + 3: { + base: "tw-border tw-border-solid tw-border-[#CD7F32]/10 tw-bg-[linear-gradient(180deg,rgba(31,31,37,0.98)_0%,rgba(46,41,36,0.95)_100%)] tw-shadow-[inset_0_0_16px_rgba(205,127,50,0.01)]", + hover: + "desktop-hover:hover:tw-shadow-[inset_0_0_20px_rgba(205,127,50,0.02)] desktop-hover:hover:tw-border-[#CD7F32]/15 desktop-hover:hover:tw-bg-[linear-gradient(180deg,rgba(35,35,41,0.98)_0%,rgba(51,46,41,0.95)_100%)]", + }, + default: { + base: "tw-border tw-border-solid tw-border-iron-600/40 tw-bg-[linear-gradient(90deg,rgba(31,31,37,0.95)_0%,rgba(35,35,40,0.98)_100%)] tw-shadow-[inset_0_0_16px_rgba(255,255,255,0.03)]", + hover: + "desktop-hover:hover:tw-shadow-[inset_0_0_20px_rgba(255,255,255,0.05)] desktop-hover:hover:tw-border-iron-500/40", + }, + }; + + const style = + rankStyles[rank as keyof typeof rankStyles] ?? rankStyles.default; + return `${style.base} ${style.hover}`; +}; + export const WaveWinnersDrop: React.FC = ({ drop, onDropClick, }) => { + const colorClasses = getColorClasses(drop.rank); + return ( -
-
-
+
onDropClick(drop)} + role="button" + className={`tw-cursor-pointer tw-relative tw-w-full tw-rounded-xl tw-py-5 tw-px-4 ${colorClasses} tw-overflow-hidden tw-backdrop-blur-sm + tw-transition-all tw-duration-300 tw-ease-out + tw-shadow-lg tw-shadow-black/5 + desktop-hover:hover:tw-shadow-xl desktop-hover:hover:tw-shadow-black/10 + tw-group`} + > +
+
+
+ +
diff --git a/components/waves/detailed/winners/drops/WaveWinnersDrops.tsx b/components/waves/detailed/winners/drops/WaveWinnersDrops.tsx index 87a84512a..d9efc15b7 100644 --- a/components/waves/detailed/winners/drops/WaveWinnersDrops.tsx +++ b/components/waves/detailed/winners/drops/WaveWinnersDrops.tsx @@ -36,7 +36,11 @@ export const WaveWinnersDrops: React.FC = ({ return (
{memoizedDrops.map((drop) => ( - + ))} {isFetchingNextPage && (
diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeader.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeader.tsx index 2b81a2c95..43a4409c9 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeader.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeader.tsx @@ -1,14 +1,5 @@ import React from "react"; import { ExtendedDrop } from "../../../../../../helpers/waves/drop.helpers"; -import { UserCICAndLevelSize } from "../../../../../user/utils/UserCICAndLevel"; -import { - cicToType, - formatNumber, - formatNumberWithCommas, - getTimeAgoShort, -} from "../../../../../../helpers/Helpers"; -import UserCICAndLevel from "../../../../../user/utils/UserCICAndLevel"; -import Link from "next/link"; import WaveWinnersDropHeaderTotalVotes from "./WaveWinnersDropHeaderTotalVotes"; import WaveWinnersDropHeaderVoters from "./WaveWinnersDropHeaderVoters"; import WaveWinnersDropHeaderAuthorPfp from "./WaveWinnersDropHeaderAuthorPfp"; @@ -25,7 +16,7 @@ export const WaveWinnersDropHeader: React.FC = ({ }) => { return (
-
+
e.stopPropagation()}>
diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorHandle.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorHandle.tsx index 36a09e7a1..10d2f2a10 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorHandle.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorHandle.tsx @@ -1,3 +1,4 @@ +import React from "react"; import Link from "next/link"; import { ExtendedDrop } from "../../../../../../helpers/waves/drop.helpers"; @@ -9,13 +10,12 @@ export default function WaveWinnersDropHeaderAuthorHandle({ drop, }: WaveWinnersDropHeaderAuthorHandleProps) { return ( -
- - {drop.author.handle} - -
+ e.stopPropagation()} + className="tw-text-base tw-no-underline tw-font-semibold tw-text-iron-50 desktop-hover:hover:tw-text-iron-400 tw-transition-all tw-duration-300 tw-ease-out" + > + {drop.author.handle} + ); } diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorPfp.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorPfp.tsx index 13371d61b..f940bbf63 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorPfp.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorPfp.tsx @@ -1,3 +1,5 @@ +import React from "react"; +import Link from "next/link"; import { ExtendedDrop } from "../../../../../../helpers/waves/drop.helpers"; import { cicToType } from "../../../../../../helpers/Helpers"; import UserCICAndLevel from "../../../../../user/utils/UserCICAndLevel"; @@ -15,23 +17,20 @@ export default function WaveWinnersDropHeaderAuthorPfp({ drop, }: WaveWinnersDropHeaderAuthorPfpProps) { return ( -
+ e.stopPropagation()} + className="tw-transform hover:tw-scale-105 tw-transition-all tw-duration-300" + > {drop.author.pfp ? ( User avatar ) : ( -
+
)} -
- -
-
+ ); } diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx index dafe6093c..3f3be03c2 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx @@ -1,5 +1,25 @@ import { ExtendedDrop } from "../../../../../../helpers/waves/drop.helpers"; +interface TrophyIconProps { + readonly color: string; +} + +export function TrophyIcon({ color }: TrophyIconProps) { + return ( + + ); +} + interface WaveWinnersDropHeaderRankProps { readonly drop: ExtendedDrop; } @@ -7,22 +27,53 @@ interface WaveWinnersDropHeaderRankProps { export default function WaveWinnersDropHeaderRank({ drop, }: WaveWinnersDropHeaderRankProps) { + if (!drop.rank) { + return ( +
+ + + +
+ ); + } + + if (drop.rank === 1) { + return ( +
+ + #{drop.rank} +
+ ); + } + + if (drop.rank === 2) { + return ( +
+ + #{drop.rank} +
+ ); + } + + if (drop.rank === 3) { + return ( +
+ + #{drop.rank} +
+ ); + } + return ( -
- - - #{drop.rank} - +
+ #{drop.rank}
); } diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoters.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoters.tsx index 20d227539..90f8d0f33 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoters.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoters.tsx @@ -11,7 +11,7 @@ export default function WaveWinnersDropHeaderVoters({ }: WaveWinnersDropHeaderVotersProps) { return (
-
+
{drop.top_raters.map((voter, index) => ( { + const rank = drop.rank; + if (!rank || !wave?.outcomes) return 0; + const outcomes = wave.outcomes; + const nicOutcomes = outcomes.filter( + (outcome) => outcome.credit === ApiWaveOutcomeCredit.Cic + ); + const nic = nicOutcomes.reduce((acc, outcome) => { + return acc + (outcome.distribution?.[rank - 1]?.amount ?? 0); + }, 0); + return nic; +}; + +const calculateRep = ({ + drop, + wave, +}: { + drop: ExtendedDrop; + wave: ApiWave; +}): number => { + const rank = drop.rank; + if (!rank || !wave?.outcomes) return 0; + const outcomes = wave.outcomes; + const repOutcomes = outcomes.filter( + (outcome) => outcome.credit === ApiWaveOutcomeCredit.Rep + ); + const rep = repOutcomes.reduce((acc, outcome) => { + return acc + (outcome.distribution?.[rank - 1]?.amount ?? 0); + }, 0); + return rep; +}; + +const calculateManualOutcomes = ({ + drop, + wave, +}: { + drop: ExtendedDrop; + wave: ApiWave; +}): string[] => { + const rank = drop.rank; + if (!rank || !wave?.outcomes) return []; + const outcomes = wave.outcomes; + const manualOutcomes = outcomes.filter( + (outcome) => outcome.type === ApiWaveOutcomeType.Manual + ); + return manualOutcomes + .filter((outcome) => !!outcome.distribution?.[rank - 1]?.amount) + .map((outcome) => outcome.distribution?.[rank - 1]?.description ?? ""); +}; + +const calculateOutcomeSummary = ({ + drop, + wave, +}: { + drop: ExtendedDrop; + wave: ApiWave; +}): OutcomeSummary => { + return { + nicTotal: calculateNIC({ drop, wave }), + repTotal: calculateRep({ drop, wave }), + manualOutcomes: calculateManualOutcomes({ drop, wave }), + }; +}; + +export default function WaveWinnersDropOutcome({ + drop, + wave, + isMobile = false, +}: WaveWinnersDropOutcomeProps) { + const [isTouch, setIsTouch] = useState(false); + const [isOpen, setIsOpen] = useState(false); + + useEffect(() => { + setIsTouch('ontouchstart' in window); + }, []); + + const handleClick = (e: React.MouseEvent) => { + if (isTouch) { + e.stopPropagation(); + setIsOpen(!isOpen); + } + }; + + console.log('Wave:', wave); + console.log('Wave outcomes:', wave?.outcomes); + + const { nicTotal, repTotal, manualOutcomes } = calculateOutcomeSummary({ + drop, + wave, + }); + + console.log('Calculated outcomes:', { nicTotal, repTotal, manualOutcomes }); + + const totalOutcomes = + (nicTotal ? 1 : 0) + (repTotal ? 1 : 0) + manualOutcomes.length; + + console.log('Total outcomes:', totalOutcomes); + + if (totalOutcomes === 0) { + return null; + } + + const tooltipContent = ( +
+
+ + Outcome Details + +
+ {!!nicTotal && ( +
+
+ + + NIC + +
+ + {nicTotal} + +
+ )} + {!!repTotal && ( +
+
+ + + Rep + +
+ + {repTotal} + +
+ )} + {manualOutcomes.map((outcome) => ( +
+
+ + + {outcome} + +
+
+ ))} +
+
+
+ ); + + return ( + setIsOpen(false)} + arrow={false} + duration={200} + > + + + ); +} \ No newline at end of file From a0001b17d482db2acd00b8d0d830d1e0723d72f9 Mon Sep 17 00:00:00 2001 From: Ragne Date: Wed, 25 Dec 2024 23:04:05 +0200 Subject: [PATCH 09/33] wip Signed-off-by: Ragne --- .../waves/detailed/winners/WaveWinners.tsx | 2 - .../detailed/winners/WaveWinnersHeader.tsx | 23 ---- .../header/WaveWinnersDropHeaderRank.tsx | 22 ++-- .../winners/podium/WaveWinnersPodium.tsx | 119 ++++++++++-------- .../winners/podium/WaveWinnersPodiumFirst.tsx | 6 +- .../podium/WaveWinnersPodiumPlaceholder.tsx | 32 +++++ .../podium/WaveWinnersPodiumSecond.tsx | 42 ++++--- .../winners/podium/WaveWinnersPodiumThird.tsx | 31 +++-- 8 files changed, 161 insertions(+), 116 deletions(-) delete mode 100644 components/waves/detailed/winners/WaveWinnersHeader.tsx create mode 100644 components/waves/detailed/winners/podium/WaveWinnersPodiumPlaceholder.tsx diff --git a/components/waves/detailed/winners/WaveWinners.tsx b/components/waves/detailed/winners/WaveWinners.tsx index 6d48d9673..7ad620370 100644 --- a/components/waves/detailed/winners/WaveWinners.tsx +++ b/components/waves/detailed/winners/WaveWinners.tsx @@ -1,5 +1,4 @@ import React from "react"; -import { WaveWinnersHeader } from "./WaveWinnersHeader"; import { WaveWinnersPodium } from "./podium/WaveWinnersPodium"; import { WaveWinnersDrops } from "./drops/WaveWinnersDrops"; @@ -14,7 +13,6 @@ export const WaveWinners: React.FC = ({ }) => { return (
- {/* */} diff --git a/components/waves/detailed/winners/WaveWinnersHeader.tsx b/components/waves/detailed/winners/WaveWinnersHeader.tsx deleted file mode 100644 index 76f094790..000000000 --- a/components/waves/detailed/winners/WaveWinnersHeader.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from "react"; - -interface WaveWinnersHeaderProps { - readonly wave: any; -} - -export const WaveWinnersHeader: React.FC = ({ - wave, -}) => { - return ( -
-
-
-

- Wave Winners -

-
-

- Celebrating the most impactful contributors in this wave -

-
- ); -}; diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx index 3f3be03c2..8d1eeaa27 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx @@ -29,13 +29,13 @@ export default function WaveWinnersDropHeaderRank({ }: WaveWinnersDropHeaderRankProps) { if (!drop.rank) { return ( -
+
@@ -46,27 +46,33 @@ export default function WaveWinnersDropHeaderRank({ if (drop.rank === 1) { return ( -
+
- #{drop.rank} + + #{drop.rank} +
); } if (drop.rank === 2) { return ( -
+
- #{drop.rank} + + #{drop.rank} +
); } if (drop.rank === 3) { return ( -
+
- #{drop.rank} + + #{drop.rank} +
); } diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx index 2edac74c9..4b43fe522 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx @@ -11,6 +11,7 @@ import { WaveWinnersPodiumFirst } from "./WaveWinnersPodiumFirst"; import { WaveWinnersPodiumSecond } from "./WaveWinnersPodiumSecond"; import { WaveWinnersPodiumThird } from "./WaveWinnersPodiumThird"; import { motion } from "framer-motion"; +import { WaveWinnersPodiumPlaceholder } from "./WaveWinnersPodiumPlaceholder"; interface WaveWinnersPodiumProps { readonly wave: ApiWave; @@ -18,12 +19,17 @@ interface WaveWinnersPodiumProps { } const PodiumPlaceholderCard = ({ height }: { height: string }) => ( -
-
-
-
+
+
+
+
+
+
+
+
+
); @@ -66,7 +72,7 @@ export const WaveWinnersPodium: React.FC = ({ if (!isFetching && !drops.length) { return ( -
+
@@ -94,7 +100,7 @@ export const WaveWinnersPodium: React.FC = ({ />
-
+
No Submissions Yet

@@ -107,52 +113,63 @@ export const WaveWinnersPodium: React.FC = ({ } return ( -

- {secondPlaceDrop && ( - - - - )} - {firstPlaceDrop && ( - - - - )} - {thirdPlaceDrop && ( - - - - )} +
+ {secondPlaceDrop ? ( + + + + ) : ( + + )} +
+
+ {firstPlaceDrop ? ( + + + + ) : ( + + )} +
+
+ {thirdPlaceDrop ? ( + + + + ) : ( + + )} +
diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx index 717b7c3f3..515f5dc25 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx @@ -26,7 +26,7 @@ export const WaveWinnersPodiumFirst: React.FC = ({ className="tw-cursor-pointer tw-group" >
-
+
= ({ e.stopPropagation()} - className="tw-transition-all tw-no-underline tw-mb-4 tw-relative" + className="tw-transition-all tw-no-underline tw-mb-2 tw-mt-4 tw-relative" >
{drop.author.handle}
-
+
= ({ + height, + position, +}) => { + const positionColors = { + first: "tw-border-yellow-900/20", + second: "tw-border-iron-700/20", + third: "tw-border-amber-900/20", + }; + + return ( +
+
+
+
+
+
+
+
+
+
+ ); +}; \ No newline at end of file diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx index 0a926eb25..3c5e95411 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx @@ -15,14 +15,16 @@ interface WaveWinnersPodiumSecondProps { readonly onDropClick: (drop: ExtendedDrop) => void; } -export const WaveWinnersPodiumSecond: React.FC< - WaveWinnersPodiumSecondProps -> = ({ drop, wave, onDropClick }) => { +export const WaveWinnersPodiumSecond: React.FC = ({ + drop, + wave, + onDropClick, +}) => { return (
onDropClick(drop)} className="tw-cursor-pointer tw-group">
-
-
+
+
) : ( -
+
)}
- + 2nd
@@ -61,22 +63,30 @@ export const WaveWinnersPodiumSecond: React.FC<
-
+
+
+
+
+
+
+
+
+ e.stopPropagation()} - className="tw-transition-all tw-no-underline tw-mb-3" + className="tw-transition-all tw-no-underline tw-mb-2 tw-mt-4 tw-relative" > -
+
{drop.author.handle}
-
+
= 0 ? "tw-text-[#DDDDDD]" : "tw-text-[#ff4466]" + drop.rating >= 0 ? "tw-text-[#94A3B8]" : "tw-text-[#ff4466]" } tw-font-semibold tw-text-base`} > {formatNumberWithCommas(drop.rating)} @@ -86,8 +96,8 @@ export const WaveWinnersPodiumSecond: React.FC<
-
-
+
+
{formatNumberWithCommas(drop.raters_count)} diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx index c9f6e5820..a782e01f3 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx @@ -3,8 +3,8 @@ import { ExtendedDrop } from "../../../../../helpers/waves/drop.helpers"; import Link from "next/link"; import { formatNumberWithCommas } from "../../../../../helpers/Helpers"; import { - getScaledImageUri, ImageScale, + getScaledImageUri, } from "../../../../../helpers/image.helpers"; import { WavePodiumItemContentOutcomes } from "./WavePodiumItemContentOutcomes"; import { ApiWave } from "../../../../../generated/models/ApiWave"; @@ -21,12 +21,9 @@ export const WaveWinnersPodiumThird: React.FC = ({ onDropClick, }) => { return ( -
onDropClick(drop)} - className="tw-cursor-pointer tw-group" - > +
onDropClick(drop)} className="tw-cursor-pointer tw-group">
-
+
= ({ ) : ( -
+
)} @@ -66,18 +63,26 @@ export const WaveWinnersPodiumThird: React.FC = ({
-
+
+
+
+
+
+
+
+
+ e.stopPropagation()} - className="tw-transition-all tw-no-underline tw-mb-3" + className="tw-transition-all tw-no-underline tw-mb-2 tw-mt-4 tw-relative" >
{drop.author.handle}
-
+
= ({
-
-
+
+
{formatNumberWithCommas(drop.raters_count)} From 920370db8689d9c1ea3efce71ffeeda142ba96da Mon Sep 17 00:00:00 2001 From: Ragne Date: Thu, 26 Dec 2024 10:25:44 +0200 Subject: [PATCH 10/33] wip Signed-off-by: Ragne --- .../winners/drops/header/WaveWinnersDropHeaderVoter.tsx | 6 +++--- .../winners/drops/header/WaveWinnersDropOutcome.tsx | 2 +- .../waves/detailed/winners/podium/WaveWinnersPodium.tsx | 8 ++++---- .../detailed/winners/podium/WaveWinnersPodiumFirst.tsx | 6 +++--- .../detailed/winners/podium/WaveWinnersPodiumSecond.tsx | 8 ++++---- .../detailed/winners/podium/WaveWinnersPodiumThird.tsx | 8 ++++---- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoter.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoter.tsx index 38cd8afbc..00336b76d 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoter.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoter.tsx @@ -40,11 +40,11 @@ export default function WaveWinnersDropHeaderVoter({ {voter.profile.pfp ? ( {`${voter.profile.handle}'s ) : ( -
+
)}
diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx index 4f34a9b7b..7088ba5dc 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx @@ -227,7 +227,7 @@ export default function WaveWinnersDropOutcome({ onClick={handleClick} tabIndex={0} aria-label="View outcome details" - className="tw-transition-all tw-duration-200 tw-flex tw-items-center tw-gap-2 tw-px-3 tw-py-1.5 tw-rounded-full tw-bg-iron-800/40 tw-backdrop-blur-sm tw-border tw-border-solid tw-border-iron-700/20 desktop-hover:hover:tw-bg-iron-800/60 desktop-hover:hover:tw-border-iron-700/40 desktop-hover:hover:tw-shadow-lg" + className="tw-transition-all tw-duration-200 tw-flex tw-items-center tw-gap-2 tw-px-3 tw-py-1.5 tw-rounded-full tw-bg-iron-700 tw-backdrop-blur-sm tw-border tw-border-solid tw-border-iron-700/20 desktop-hover:hover:tw-bg-iron-800/60 desktop-hover:hover:tw-border-iron-700/40 desktop-hover:hover:tw-shadow-lg" > Outcome diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx index 4b43fe522..21d06b0d1 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx @@ -24,7 +24,7 @@ const PodiumPlaceholderCard = ({ height }: { height: string }) => (
@@ -72,8 +72,8 @@ export const WaveWinnersPodium: React.FC = ({ if (!isFetching && !drops.length) { return ( -
-
+
+
@@ -115,7 +115,7 @@ export const WaveWinnersPodium: React.FC = ({ return (
-
+
{secondPlaceDrop ? ( = ({ onClick={() => onDropClick(drop)} className="tw-cursor-pointer tw-group" > -
+
= ({
-
+
@@ -99,7 +99,7 @@ export const WaveWinnersPodiumFirst: React.FC = ({
-
+
{formatNumberWithCommas(drop.raters_count)} diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx index 3c5e95411..3ad11d53a 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx @@ -22,7 +22,7 @@ export const WaveWinnersPodiumSecond: React.FC = ( }) => { return (
onDropClick(drop)} className="tw-cursor-pointer tw-group"> -
+
@@ -42,7 +42,7 @@ export const WaveWinnersPodiumSecond: React.FC = ( )} -
+
= (
-
+
@@ -97,7 +97,7 @@ export const WaveWinnersPodiumSecond: React.FC = (
-
+
{formatNumberWithCommas(drop.raters_count)} diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx index a782e01f3..6003bfbf3 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx @@ -22,7 +22,7 @@ export const WaveWinnersPodiumThird: React.FC = ({ }) => { return (
onDropClick(drop)} className="tw-cursor-pointer tw-group"> -
+
@@ -42,7 +42,7 @@ export const WaveWinnersPodiumThird: React.FC = ({ )} -
+
= ({
-
+
@@ -97,7 +97,7 @@ export const WaveWinnersPodiumThird: React.FC = ({
-
+
{formatNumberWithCommas(drop.raters_count)} From 7b948c5266e24045c51782ab423e233e78bd6171 Mon Sep 17 00:00:00 2001 From: Ragne Date: Thu, 26 Dec 2024 11:00:48 +0200 Subject: [PATCH 11/33] wip Signed-off-by: Ragne --- .../WaveDetailedLeaderboardItemOutcomes.tsx | 4 ++-- .../winners/drops/header/WaveWinnersDropHeaderVoter.tsx | 6 ++++-- .../waves/detailed/winners/podium/WaveWinnersPodium.tsx | 6 +++--- .../detailed/winners/podium/WaveWinnersPodiumFirst.tsx | 2 +- .../detailed/winners/podium/WaveWinnersPodiumSecond.tsx | 2 +- .../detailed/winners/podium/WaveWinnersPodiumThird.tsx | 2 +- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/components/waves/detailed/small-leaderboard/WaveDetailedLeaderboardItemOutcomes.tsx b/components/waves/detailed/small-leaderboard/WaveDetailedLeaderboardItemOutcomes.tsx index fc9b30629..dc193dfdc 100644 --- a/components/waves/detailed/small-leaderboard/WaveDetailedLeaderboardItemOutcomes.tsx +++ b/components/waves/detailed/small-leaderboard/WaveDetailedLeaderboardItemOutcomes.tsx @@ -214,9 +214,9 @@ export const WaveDetailedLeaderboardItemOutcomes: React.FC< > - - ); -} \ No newline at end of file +} diff --git a/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx b/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx index b3f5ebfad..0201bddd3 100644 --- a/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx +++ b/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx @@ -208,17 +208,17 @@ export const WavePodiumItemContentOutcomes: React.FC setIsOpen(false)} arrow={false} duration={200} - className="!tw-backdrop-blur-xl" + className="tw-backdrop-blur-xl" > )} {isDropWave && ( diff --git a/components/waves/detailed/chat/WaveChat.tsx b/components/waves/detailed/chat/WaveChat.tsx index 3fbb04274..d21ef23fa 100644 --- a/components/waves/detailed/chat/WaveChat.tsx +++ b/components/waves/detailed/chat/WaveChat.tsx @@ -35,7 +35,9 @@ const calculateHeight = (isCapacitor: boolean, isNotChatWave: boolean) => { return "tw-h-[calc(100vh-14.7rem)]"; } return `tw-h-[calc(100vh-8.8rem)] ${ - isNotChatWave ? "lg:tw-h-[calc(100vh-9.4rem)]" : "lg:tw-h-[calc(100vh-6.5rem)]" + isNotChatWave + ? "lg:tw-h-[calc(100vh-9.4rem)]" + : "lg:tw-h-[calc(100vh-6.5rem)]" }`; }; @@ -94,7 +96,10 @@ export const WaveChat: React.FC = ({ }; const containerClassName = useMemo(() => { - return `tw-w-full tw-flex tw-flex-col ${calculateHeight(capacitor.isCapacitor, isNotChatWave)}`; + return `tw-w-full tw-flex tw-flex-col ${calculateHeight( + capacitor.isCapacitor, + isNotChatWave + )}`; }, [capacitor.isCapacitor, isNotChatWave]); if (!searchParamsDone) { @@ -105,7 +110,9 @@ export const WaveChat: React.FC = ({ <>
= ({ {isNotChatWave && (
diff --git a/hooks/useWaveState.ts b/hooks/useWaveState.ts new file mode 100644 index 000000000..265ba7a7f --- /dev/null +++ b/hooks/useWaveState.ts @@ -0,0 +1,108 @@ +import { ApiWave } from "../generated/models/ApiWave"; +import { useState, useEffect } from "react"; + +export enum WaveVotingState { + NOT_STARTED = "NOT_STARTED", + ONGOING = "ONGOING", + ENDED = "ENDED" +} + +interface UseWaveStateReturn { + readonly votingState: WaveVotingState; +} + +const calculateVotingState = ( + now: number, + minTime: number | null, + maxTime: number | null +): WaveVotingState => { + // No timestamps set - always ongoing + if (!minTime && !maxTime) { + return WaveVotingState.ONGOING; + } + + // Only max time set - either ongoing or ended + if (!minTime && maxTime) { + return now <= maxTime ? WaveVotingState.ONGOING : WaveVotingState.ENDED; + } + + // Only min time set - either not started or ongoing + if (minTime && !maxTime) { + return now < minTime ? WaveVotingState.NOT_STARTED : WaveVotingState.ONGOING; + } + + // Both times set - all states possible + if (minTime && maxTime) { + if (now < minTime) { + return WaveVotingState.NOT_STARTED; + } + if (now <= maxTime) { + return WaveVotingState.ONGOING; + } + return WaveVotingState.ENDED; + } + + return WaveVotingState.ONGOING; +}; + +const calculateNextStateChangeTime = ( + now: number, + minTime: number | null, + maxTime: number | null, + currentState: WaveVotingState +): number | null => { + // If no timestamps, or already ended, no next change + if ((!minTime && !maxTime) || currentState === WaveVotingState.ENDED) { + return null; + } + + // If not started, next change is at minTime + if (currentState === WaveVotingState.NOT_STARTED && minTime) { + return minTime; + } + + // If ongoing, next change is at maxTime if it exists + if (currentState === WaveVotingState.ONGOING && maxTime) { + return maxTime; + } + + return null; +}; + +export function useWaveState(wave: ApiWave): UseWaveStateReturn { + const [votingState, setVotingState] = useState(() => { + const minTime = wave.voting.period?.min ? new Date(wave.voting.period.min).getTime() : null; + const maxTime = wave.voting.period?.max ? new Date(wave.voting.period.max).getTime() : null; + return calculateVotingState(Date.now(), minTime, maxTime); + }); + + useEffect(() => { + const minTime = wave.voting.period?.min ? new Date(wave.voting.period.min).getTime() : null; + const maxTime = wave.voting.period?.max ? new Date(wave.voting.period.max).getTime() : null; + const now = Date.now(); + + // Update current state + const currentState = calculateVotingState(now, minTime, maxTime); + setVotingState(currentState); + + // Calculate when the next state change should occur + const nextChangeTime = calculateNextStateChangeTime(now, minTime, maxTime, currentState); + + // If there's a next state change, set up a timeout + if (nextChangeTime) { + const timeUntilChange = nextChangeTime - now; + if (timeUntilChange > 0) { + const timeout = setTimeout(() => { + const newState = calculateVotingState(Date.now(), minTime, maxTime); + setVotingState(newState); + }, timeUntilChange); + + return () => clearTimeout(timeout); + } + } + }, [wave.voting.period?.min, wave.voting.period?.max]); + + return { + votingState + }; +} From a09a03f1ff92a0eea7a175a91e174a13047d1451 Mon Sep 17 00:00:00 2001 From: Ragne Date: Thu, 26 Dec 2024 12:39:03 +0200 Subject: [PATCH 16/33] wip Signed-off-by: Ragne --- .../winners/drops/header/WaveWinnersDropHeaderRank.tsx | 6 +++--- .../winners/drops/header/WaveWinnersDropHeaderVoter.tsx | 4 ++-- .../winners/drops/header/WaveWinnersDropOutcome.tsx | 8 ++++---- .../detailed/winners/podium/WaveWinnersPodiumFirst.tsx | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx index 8d1eeaa27..144387040 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx @@ -49,7 +49,7 @@ export default function WaveWinnersDropHeaderRank({
- #{drop.rank} + {drop.rank}st
); @@ -60,7 +60,7 @@ export default function WaveWinnersDropHeaderRank({
- #{drop.rank} + {drop.rank}nd
); @@ -71,7 +71,7 @@ export default function WaveWinnersDropHeaderRank({
- #{drop.rank} + {drop.rank}rd
); diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoter.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoter.tsx index a9348b314..46088f974 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoter.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoter.tsx @@ -43,10 +43,10 @@ export default function WaveWinnersDropHeaderVoter({ {`${voter.profile.handle}'s ) : ( -
+
)}
diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx index e1ee8b47c..d44b279af 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx @@ -123,7 +123,7 @@ export default function WaveWinnersDropOutcome({ strokeLinejoin="round" /> - + NIC @@ -145,7 +145,7 @@ export default function WaveWinnersDropOutcome({ strokeWidth="1.5" /> - + Rep @@ -159,7 +159,7 @@ export default function WaveWinnersDropOutcome({ className="tw-flex tw-items-center tw-gap-2 tw-px-3 tw-py-1.5 tw-rounded-xl tw-bg-iron-700 tw-backdrop-blur-sm tw-border tw-border-iron-700/20" > - + {outcome}
diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx index 01fbc6128..ac15082d1 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx @@ -77,11 +77,11 @@ export const WaveWinnersPodiumFirst: React.FC = ({ e.stopPropagation()} - className="tw-transition-all tw-no-underline tw-mb-2 tw-mt-4 tw-relative" + className="tw-transition-all tw-no-underline tw-mb-2 tw-mt-4 tw-relative tw-text-center" > -
+ {drop.author.handle} -
+
From 38ea7ad6aca4a626e223206d74595e7c195bba56 Mon Sep 17 00:00:00 2001 From: Simo Date: Thu, 26 Dec 2024 12:39:21 +0200 Subject: [PATCH 17/33] wip Signed-off-by: Simo --- hooks/drops/useDropOutcomes.tsx | 79 +++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 hooks/drops/useDropOutcomes.tsx diff --git a/hooks/drops/useDropOutcomes.tsx b/hooks/drops/useDropOutcomes.tsx new file mode 100644 index 000000000..77ae3b66b --- /dev/null +++ b/hooks/drops/useDropOutcomes.tsx @@ -0,0 +1,79 @@ +import { ApiDrop } from "../../generated/models/ApiDrop"; +import { ApiWave } from "../../generated/models/ApiWave"; +import { ApiWaveOutcomeCredit } from "../../generated/models/ApiWaveOutcomeCredit"; +import { ApiWaveOutcomeType } from "../../generated/models/ApiWaveOutcomeType"; + +export enum OutcomeType { + NIC = 'NIC', + REP = 'REP', + MANUAL = 'MANUAL' +} + +interface NICOutcome { + type: OutcomeType.NIC; + value: number; +} + +interface REPOutcome { + type: OutcomeType.REP; + value: number; + category: string; +} + +interface ManualOutcome { + type: OutcomeType.MANUAL; + description: string; +} + +interface DropOutcomes { + nicOutcomes: NICOutcome[]; + repOutcomes: REPOutcome[]; + manualOutcomes: ManualOutcome[]; +} + +export interface DropOutcomesInput { + readonly drop: ApiDrop; + readonly wave: ApiWave; +} + +export function useDropOutcomes({ drop, wave }: DropOutcomesInput): DropOutcomes { + const rank = drop.rank; + if (!rank || !wave?.outcomes) { + return { + nicOutcomes: [], + repOutcomes: [], + manualOutcomes: [] + }; + } + + const nicOutcomes = wave.outcomes + .filter(outcome => outcome.credit === ApiWaveOutcomeCredit.Cic) + .map(outcome => ({ + type: OutcomeType.NIC as const, + value: outcome.distribution?.[rank - 1]?.amount ?? 0 + })) + .filter(outcome => outcome.value > 0); + + const repOutcomes = wave.outcomes + .filter(outcome => outcome.credit === ApiWaveOutcomeCredit.Rep) + .map(outcome => ({ + type: OutcomeType.REP as const, + value: outcome.distribution?.[rank - 1]?.amount ?? 0, + category: outcome.rep_category ?? '' + })) + .filter(outcome => outcome.value > 0); + + const manualOutcomes = wave.outcomes + .filter(outcome => outcome.type === ApiWaveOutcomeType.Manual) + .map(outcome => ({ + type: OutcomeType.MANUAL as const, + description: outcome.distribution?.[rank - 1]?.description ?? '' + })) + .filter(outcome => outcome.description !== ''); + + return { + nicOutcomes, + repOutcomes, + manualOutcomes + }; +} From 13cb1ea90fa4fdc1d70a988b6fc4c6583ddf1671 Mon Sep 17 00:00:00 2001 From: Ragne Date: Thu, 26 Dec 2024 13:11:40 +0200 Subject: [PATCH 18/33] wip Signed-off-by: Ragne --- .../detailed/leaderboard/WaveLeaderboard.tsx | 6 +- .../header/WaveleaderboardDropRaters.tsx | 92 +++++++++++++------ .../drops/header/WaveWinnersDropHeader.tsx | 2 +- .../WaveWinnersDropHeaderAuthorHandle.tsx | 2 +- .../header/WaveWinnersDropHeaderCreated.tsx | 2 +- .../WaveWinnersDropHeaderTotalVotes.tsx | 20 ++-- 6 files changed, 84 insertions(+), 40 deletions(-) diff --git a/components/waves/detailed/leaderboard/WaveLeaderboard.tsx b/components/waves/detailed/leaderboard/WaveLeaderboard.tsx index de6b474a5..080d49201 100644 --- a/components/waves/detailed/leaderboard/WaveLeaderboard.tsx +++ b/components/waves/detailed/leaderboard/WaveLeaderboard.tsx @@ -68,9 +68,9 @@ export const WaveLeaderboard: React.FC = ({ > {children} - + - {/* + = ({ showMyDrops={showMyDrops} setActiveDrop={setActiveDrop} onCreateDrop={() => setIsCreatingDrop(true)} - /> */} + />
diff --git a/components/waves/detailed/leaderboard/drops/header/WaveleaderboardDropRaters.tsx b/components/waves/detailed/leaderboard/drops/header/WaveleaderboardDropRaters.tsx index 262cd9e72..1b2b1b2f2 100644 --- a/components/waves/detailed/leaderboard/drops/header/WaveleaderboardDropRaters.tsx +++ b/components/waves/detailed/leaderboard/drops/header/WaveleaderboardDropRaters.tsx @@ -1,6 +1,9 @@ import React from "react"; import { ExtendedDrop } from "../../../../../../helpers/waves/drop.helpers"; import { formatNumberWithCommas } from "../../../../../../helpers/Helpers"; +import Tippy from "@tippyjs/react"; +import Link from "next/link"; +import { getScaledImageUri, ImageScale } from "../../../../../../helpers/image.helpers"; interface WaveLeaderboardDropRatersProps { readonly drop: ExtendedDrop; @@ -14,47 +17,82 @@ export const WaveLeaderboardDropRaters: React.FC< const isNegativeVote = userVote < 0; const topThreeRankStyles: { [key: number]: string } = { - 1: "tw-text-[#D9A962]", - 2: "tw-text-[#C0C0C0]", - 3: "tw-text-[#B87333]", + 1: "tw-text-[#E8D48A]", + 2: "tw-text-[#DDDDDD]", + 3: "tw-text-[#CD7F32]", }; const rankStyle = drop.rank && drop.rank <= 3 ? topThreeRankStyles[drop.rank] - : "tw-text-iron-300"; + : isNegativeVote + ? "tw-bg-gradient-to-r tw-from-red tw-to-red tw-bg-clip-text tw-text-transparent" + : "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent"; + const hasUserVoted = drop.context_profile_context?.rating !== undefined && drop.context_profile_context?.rating !== 0; return ( -
-
-
- - {formatNumberWithCommas(drop.raters_count)} {votersCountLabel} - -
- - {formatNumberWithCommas(drop.rating)}{" "} - - {drop.wave.voting_credit_type} - - +
+
+ + {formatNumberWithCommas(drop.rating)} + + + {drop.wave.voting_credit_type} total + +
+ +
+
+ {drop.top_raters.map((voter, index) => ( + + {voter.profile.handle} • {formatNumberWithCommas(voter.rating)}{" "} + {drop.wave.voting_credit_type} + + } + interactive={true} + delay={[0, 0]} + hideOnClick={false} + appendTo={() => document.body} + zIndex={1000} + > +
+ + {voter.profile.pfp ? ( + {`${voter.profile.handle}'s + ) : ( +
+ )} + +
+ + ))}
+ + {formatNumberWithCommas(drop.raters_count)} {votersCountLabel} +
+ {hasUserVoted && ( -
-
- - Your vote: - - {formatNumberWithCommas(userVote)} {drop.wave.voting_credit_type} - +
+ + Your vote: + + {formatNumberWithCommas(userVote)} {drop.wave.voting_credit_type} -
+
)}
diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeader.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeader.tsx index 43a4409c9..3b8fe20ad 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeader.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeader.tsx @@ -21,7 +21,7 @@ export const WaveWinnersDropHeader: React.FC = ({
-
+
diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorHandle.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorHandle.tsx index 10d2f2a10..ba007c041 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorHandle.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorHandle.tsx @@ -13,7 +13,7 @@ export default function WaveWinnersDropHeaderAuthorHandle({ e.stopPropagation()} - className="tw-text-base tw-no-underline tw-font-semibold tw-text-iron-50 desktop-hover:hover:tw-text-iron-400 tw-transition-all tw-duration-300 tw-ease-out" + className="tw-text-base md:tw-text-lg tw-font-semibold tw-text-iron-100 tw-leading-none group-hover:tw-text-iron-50 tw-no-underline desktop-hover:hover:tw-text-iron-400 tw-transition-all tw-duration-300 tw-ease-out" > {drop.author.handle} diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderCreated.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderCreated.tsx index ff69f6f78..29433bcb2 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderCreated.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderCreated.tsx @@ -9,7 +9,7 @@ export default function WaveWinnersDropHeaderCreated({ drop, }: WaveWinnersDropHeaderCreatedProps) { return ( - + {getTimeAgoShort(drop.created_at)} ); diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderTotalVotes.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderTotalVotes.tsx index 7a7ce8bc0..b01d58181 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderTotalVotes.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderTotalVotes.tsx @@ -8,15 +8,21 @@ interface WaveWinnersDropHeaderTotalVotesProps { export default function WaveWinnersDropHeaderTotalVotes({ drop, }: WaveWinnersDropHeaderTotalVotesProps) { + const topThreeRankStyles: { [key: number]: string } = { + 1: "tw-text-[#E8D48A]", + 2: "tw-text-[#DDDDDD]", + 3: "tw-text-[#CD7F32]", + }; + + const style = drop.rank && drop.rank <= 3 + ? topThreeRankStyles[drop.rank] + : drop.rating >= 0 + ? "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent" + : "tw-bg-gradient-to-r tw-from-red tw-to-red tw-bg-clip-text tw-text-transparent"; + return (
- = 0 - ? "tw-from-emerald-400 tw-to-emerald-500" - : "tw-from-red tw-to-red" - } tw-font-semibold tw-bg-gradient-to-r tw-bg-clip-text tw-text-transparent`} - > + {formatNumberWithCommas(drop.rating)} From bd2998afaa3cd2cd58820f229fa35254a82b20bc Mon Sep 17 00:00:00 2001 From: Simo Date: Thu, 26 Dec 2024 13:19:44 +0200 Subject: [PATCH 19/33] wip Signed-off-by: Simo --- .../drops/header/WaveWinnersDropOutcome.tsx | 119 ++++----------- .../podium/WavePodiumItemContentOutcomes.tsx | 141 +++++------------- hooks/drops/useDropOutcomes.tsx | 57 +++---- 3 files changed, 95 insertions(+), 222 deletions(-) diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx index d44b279af..0b541275c 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx @@ -3,101 +3,27 @@ import { ExtendedDrop } from "../../../../../../helpers/waves/drop.helpers"; import { ApiWaveOutcomeCredit } from "../../../../../../generated/models/ApiWaveOutcomeCredit"; import { ApiWaveOutcomeType } from "../../../../../../generated/models/ApiWaveOutcomeType"; import { ApiWave } from "../../../../../../generated/models/ApiWave"; +import { useDropOutcomes } from "../../../../../../hooks/drops/useDropOutcomes"; +import { formatNumberWithCommas } from "../../../../../../helpers/Helpers"; interface WaveWinnersDropOutcomeProps { readonly drop: ExtendedDrop; readonly wave: ApiWave; } -interface OutcomeSummary { - nicTotal: number; - repTotal: number; - manualOutcomes: string[]; -} - -const calculateNIC = ({ - drop, - wave, -}: { - drop: ExtendedDrop; - wave: ApiWave; -}): number => { - const rank = drop.rank; - if (!rank || !wave?.outcomes) return 0; - const outcomes = wave.outcomes; - const nicOutcomes = outcomes.filter( - (outcome) => outcome.credit === ApiWaveOutcomeCredit.Cic - ); - const nic = nicOutcomes.reduce((acc, outcome) => { - return acc + (outcome.distribution?.[rank - 1]?.amount ?? 0); - }, 0); - return nic; -}; - -const calculateRep = ({ - drop, - wave, -}: { - drop: ExtendedDrop; - wave: ApiWave; -}): number => { - const rank = drop.rank; - if (!rank || !wave?.outcomes) return 0; - const outcomes = wave.outcomes; - const repOutcomes = outcomes.filter( - (outcome) => outcome.credit === ApiWaveOutcomeCredit.Rep - ); - const rep = repOutcomes.reduce((acc, outcome) => { - return acc + (outcome.distribution?.[rank - 1]?.amount ?? 0); - }, 0); - return rep; -}; - -const calculateManualOutcomes = ({ - drop, - wave, -}: { - drop: ExtendedDrop; - wave: ApiWave; -}): string[] => { - const rank = drop.rank; - if (!rank || !wave?.outcomes) return []; - const outcomes = wave.outcomes; - const manualOutcomes = outcomes.filter( - (outcome) => outcome.type === ApiWaveOutcomeType.Manual - ); - return manualOutcomes - .filter((outcome) => !!outcome.distribution?.[rank - 1]?.amount) - .map((outcome) => outcome.distribution?.[rank - 1]?.description ?? ""); -}; - -const calculateOutcomeSummary = ({ - drop, - wave, -}: { - drop: ExtendedDrop; - wave: ApiWave; -}): OutcomeSummary => { - return { - nicTotal: calculateNIC({ drop, wave }), - repTotal: calculateRep({ drop, wave }), - manualOutcomes: calculateManualOutcomes({ drop, wave }), - }; -}; - export default function WaveWinnersDropOutcome({ drop, wave, }: WaveWinnersDropOutcomeProps) { - const { nicTotal, repTotal, manualOutcomes } = calculateOutcomeSummary({ + const { + outcomes: { nicOutcomes, repOutcomes, manualOutcomes }, + haveOutcomes, + } = useDropOutcomes({ drop, wave, }); - const totalOutcomes = - (nicTotal ? 1 : 0) + (repTotal ? 1 : 0) + manualOutcomes.length; - - if (totalOutcomes === 0) { + if (!haveOutcomes) { return null; } @@ -107,8 +33,11 @@ export default function WaveWinnersDropOutcome({ Outcome:
- {!!nicTotal && ( -
+ {nicOutcomes.map((nicOutcome, i) => ( +
- {nicTotal} + {formatNumberWithCommas(nicOutcome.value)}
- )} - {!!repTotal && ( -
+ ))} + {repOutcomes.map((repOutcome, i) => ( +
- {repTotal} + {formatNumberWithCommas(repOutcome.value)} + + + {repOutcome.category}
- )} - {manualOutcomes.map((outcome) => ( + ))} + {manualOutcomes.map((outcome, i) => (
- {outcome} + {outcome.description}
))} diff --git a/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx b/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx index 0201bddd3..1262b394f 100644 --- a/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx +++ b/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx @@ -1,102 +1,23 @@ import React, { useState, useEffect } from "react"; import Tippy from "@tippyjs/react"; import { ApiWave } from "../../../../../generated/models/ApiWave"; -import { ApiWaveOutcomeCredit } from "../../../../../generated/models/ApiWaveOutcomeCredit"; -import { ApiWaveOutcomeType } from "../../../../../generated/models/ApiWaveOutcomeType"; import { ApiDrop } from "../../../../../generated/models/ApiDrop"; +import { useDropOutcomes } from "../../../../../hooks/drops/useDropOutcomes"; +import { formatNumberWithCommas } from "../../../../../helpers/Helpers"; interface WavePodiumItemContentOutcomesProps { readonly drop: ApiDrop; readonly wave: ApiWave; - readonly isMobile?: boolean; } -interface OutcomeSummary { - nicTotal: number; - repTotal: number; - manualOutcomes: string[]; -} - -const calculateNIC = ({ - drop, - wave, -}: { - drop: ApiDrop; - wave: ApiWave; -}): number => { - const rank = drop.rank; - if (!rank) return 0; - const outcomes = wave.outcomes; - const nicOutcomes = outcomes.filter( - (outcome) => outcome.credit === ApiWaveOutcomeCredit.Cic - ); - const nic = nicOutcomes.reduce((acc, outcome) => { - return acc + (outcome.distribution?.[rank - 1]?.amount ?? 0); - }, 0); - return nic; -}; - -const calculateRep = ({ - drop, - wave, -}: { - drop: ApiDrop; - wave: ApiWave; -}): number => { - const rank = drop.rank; - if (!rank) return 0; - const outcomes = wave.outcomes; - const repOutcomes = outcomes.filter( - (outcome) => outcome.credit === ApiWaveOutcomeCredit.Rep - ); - const rep = repOutcomes.reduce((acc, outcome) => { - return acc + (outcome.distribution?.[rank - 1]?.amount ?? 0); - }, 0); - return rep; -}; - -const calculateManualOutcomes = ({ - drop, - wave, -}: { - drop: ApiDrop; - wave: ApiWave; -}): string[] => { - const rank = drop.rank; - if (!rank) return []; - const outcomes = wave.outcomes; - const manualOutcomes = outcomes.filter( - (outcome) => outcome.type === ApiWaveOutcomeType.Manual - ); - return manualOutcomes - .filter((outcome) => !!outcome.distribution?.[rank - 1]?.amount) - .map((outcome) => outcome.distribution?.[rank - 1]?.description ?? ""); -}; - -const calculateOutcomeSummary = ({ - drop, - wave, -}: { - drop: ApiDrop; - wave: ApiWave; -}): OutcomeSummary => { - return { - nicTotal: calculateNIC({ drop, wave }), - repTotal: calculateRep({ drop, wave }), - manualOutcomes: calculateManualOutcomes({ drop, wave }), - }; -}; - -export const WavePodiumItemContentOutcomes: React.FC = ({ - drop, - wave, - isMobile = false, -}) => { +export const WavePodiumItemContentOutcomes: React.FC< + WavePodiumItemContentOutcomesProps +> = ({ drop, wave }) => { const [isTouch, setIsTouch] = useState(false); const [isOpen, setIsOpen] = useState(false); useEffect(() => { - setIsTouch('ontouchstart' in window); + setIsTouch("ontouchstart" in window); }, []); const handleClick = (e: React.MouseEvent) => { @@ -106,14 +27,15 @@ export const WavePodiumItemContentOutcomes: React.FC
- {!!nicTotal && ( -
+ {nicOutcomes.map((nicOutcome, i) => ( +
- {nicTotal} + {formatNumberWithCommas(nicOutcome.value)}
- )} - {!!repTotal && ( -
+ ))} + {repOutcomes.map((repOutcome, i) => ( +
- {repTotal} + {formatNumberWithCommas(repOutcome.value)} + + + {repOutcome.category}
- )} - {manualOutcomes.map((outcome) => ( + ))} + {manualOutcomes.map((outcome, i) => (
@@ -195,7 +126,7 @@ export const WavePodiumItemContentOutcomes: React.FC - {outcome} + {outcome.description}
@@ -206,8 +137,8 @@ export const WavePodiumItemContentOutcomes: React.FC
- {!!nicTotal && ( + {!!nicOutcomes.length && (
)} - {!!repTotal && ( + {!!repOutcomes.length && (
)} - {manualOutcomes.length > 0 && ( + {!!manualOutcomes.length && (
outcome.credit === ApiWaveOutcomeCredit.Cic) - .map(outcome => ({ + .filter((outcome) => outcome.credit === ApiWaveOutcomeCredit.Cic) + .map((outcome) => ({ type: OutcomeType.NIC as const, - value: outcome.distribution?.[rank - 1]?.amount ?? 0 + value: outcome.distribution?.[rank - 1]?.amount ?? 0, })) - .filter(outcome => outcome.value > 0); + .filter((outcome) => outcome.value > 0); const repOutcomes = wave.outcomes - .filter(outcome => outcome.credit === ApiWaveOutcomeCredit.Rep) - .map(outcome => ({ + .filter((outcome) => outcome.credit === ApiWaveOutcomeCredit.Rep) + .map((outcome) => ({ type: OutcomeType.REP as const, value: outcome.distribution?.[rank - 1]?.amount ?? 0, - category: outcome.rep_category ?? '' + category: outcome.rep_category ?? "", })) - .filter(outcome => outcome.value > 0); + .filter((outcome) => outcome.value > 0); const manualOutcomes = wave.outcomes - .filter(outcome => outcome.type === ApiWaveOutcomeType.Manual) - .map(outcome => ({ + .filter((outcome) => outcome.type === ApiWaveOutcomeType.Manual) + .map((outcome) => ({ type: OutcomeType.MANUAL as const, - description: outcome.distribution?.[rank - 1]?.description ?? '' + description: outcome.distribution?.[rank - 1]?.description ?? "", })) - .filter(outcome => outcome.description !== ''); + .filter((outcome) => outcome.description !== ""); + const outcomes = { nicOutcomes, repOutcomes, manualOutcomes }; + const haveOutcomes = + !!nicOutcomes.length || !!repOutcomes.length || !!manualOutcomes.length; return { - nicOutcomes, - repOutcomes, - manualOutcomes + outcomes, + haveOutcomes, }; } From 45c776cca536d6f54cef62db6fe7c7f782a2b3fb Mon Sep 17 00:00:00 2001 From: Simo Date: Thu, 26 Dec 2024 13:34:27 +0200 Subject: [PATCH 20/33] wip Signed-off-by: Simo --- components/waves/detailed/winners/WaveWinners.tsx | 6 ++++-- .../detailed/winners/drops/WaveWinnersDrop.tsx | 7 +++++-- .../detailed/winners/drops/WaveWinnersDrops.tsx | 15 +++++++++------ 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/components/waves/detailed/winners/WaveWinners.tsx b/components/waves/detailed/winners/WaveWinners.tsx index 7ad620370..f4055d5e8 100644 --- a/components/waves/detailed/winners/WaveWinners.tsx +++ b/components/waves/detailed/winners/WaveWinners.tsx @@ -1,10 +1,12 @@ import React from "react"; import { WaveWinnersPodium } from "./podium/WaveWinnersPodium"; import { WaveWinnersDrops } from "./drops/WaveWinnersDrops"; +import { ApiWave } from "../../../../generated/models/ApiWave"; +import { ExtendedDrop } from "../../../../helpers/waves/drop.helpers"; interface WaveWinnersProps { - readonly wave: any; - readonly onDropClick: (drop: any) => void; + readonly wave: ApiWave; + readonly onDropClick: (drop: ExtendedDrop) => void; } export const WaveWinners: React.FC = ({ diff --git a/components/waves/detailed/winners/drops/WaveWinnersDrop.tsx b/components/waves/detailed/winners/drops/WaveWinnersDrop.tsx index 2fd0d2190..5eef63537 100644 --- a/components/waves/detailed/winners/drops/WaveWinnersDrop.tsx +++ b/components/waves/detailed/winners/drops/WaveWinnersDrop.tsx @@ -6,7 +6,8 @@ import WaveWinnersDropOutcome from "./header/WaveWinnersDropOutcome"; import { ApiWave } from "../../../../../generated/models/ApiWave"; interface WaveWinnersDropProps { - readonly drop: ExtendedDrop & { wave: ApiWave }; + readonly drop: ExtendedDrop; + readonly wave: ApiWave; readonly onDropClick: (drop: ExtendedDrop) => void; } @@ -41,6 +42,7 @@ const getColorClasses = (rank: number | null) => { export const WaveWinnersDrop: React.FC = ({ drop, + wave, onDropClick, }) => { const colorClasses = getColorClasses(drop.rank); @@ -60,8 +62,9 @@ export const WaveWinnersDrop: React.FC = ({
- +
+
diff --git a/components/waves/detailed/winners/drops/WaveWinnersDrops.tsx b/components/waves/detailed/winners/drops/WaveWinnersDrops.tsx index d9efc15b7..7918ef7c0 100644 --- a/components/waves/detailed/winners/drops/WaveWinnersDrops.tsx +++ b/components/waves/detailed/winners/drops/WaveWinnersDrops.tsx @@ -5,10 +5,12 @@ import { WaveDropsLeaderboardSortBy } from "../../../../../hooks/useWaveDropsLea import { AuthContext } from "../../../../auth/Auth"; import { useWaveDropsLeaderboard } from "../../../../../hooks/useWaveDropsLeaderboard"; import { useIntersectionObserver } from "../../../../../hooks/useIntersectionObserver"; +import { ApiWave } from "../../../../../generated/models/ApiWave"; +import { ExtendedDrop } from "../../../../../helpers/waves/drop.helpers"; interface WaveWinnersDropsProps { - readonly wave: any; - readonly onDropClick: (drop: any) => void; + readonly wave: ApiWave; + readonly onDropClick: (drop: ExtendedDrop) => void; } export const WaveWinnersDrops: React.FC = ({ @@ -36,10 +38,11 @@ export const WaveWinnersDrops: React.FC = ({ return (
{memoizedDrops.map((drop) => ( - ))} {isFetchingNextPage && ( From 3e1d229bb26558e33d91c2b9fcab1d2e7e5e06e3 Mon Sep 17 00:00:00 2001 From: Ragne Date: Fri, 27 Dec 2024 10:42:05 +0200 Subject: [PATCH 21/33] wip Signed-off-by: Ragne --- .../WaveLeaderboardDropRankIndicator.tsx | 110 +++++++++--------- .../header/WaveleaderboardDropRaters.tsx | 2 +- .../WaveDetailedLeaderboardTopThreeDrop.tsx | 10 +- .../winners/drops/WaveWinnersDrop.tsx | 59 +++------- .../winners/drops/WaveWinnersDropContent.tsx | 2 +- .../header/WaveWinnersDropHeaderRank.tsx | 18 +-- .../header/WaveWinnersDropHeaderVoters.tsx | 33 +++++- .../drops/header/WaveWinnersDropOutcome.tsx | 10 +- .../winners/podium/WaveWinnersPodiumFirst.tsx | 10 +- .../podium/WaveWinnersPodiumSecond.tsx | 28 ++--- .../winners/podium/WaveWinnersPodiumThird.tsx | 16 +-- 11 files changed, 152 insertions(+), 146 deletions(-) diff --git a/components/waves/detailed/leaderboard/drops/WaveLeaderboardDropRankIndicator.tsx b/components/waves/detailed/leaderboard/drops/WaveLeaderboardDropRankIndicator.tsx index 0832f67a1..4045ed884 100644 --- a/components/waves/detailed/leaderboard/drops/WaveLeaderboardDropRankIndicator.tsx +++ b/components/waves/detailed/leaderboard/drops/WaveLeaderboardDropRankIndicator.tsx @@ -5,78 +5,76 @@ interface WaveLeaderboardDropRankIndicatorProps { readonly drop: ExtendedDrop; } +const TrophyIcon = ({ color }: { color: string }) => ( + +); + export const WaveLeaderboardDropRankIndicator: React.FC< WaveLeaderboardDropRankIndicatorProps > = ({ drop }) => { - if (drop.rank && drop.rank <= 3) { - const rankStyles: { - [key: number]: { container: string; rankColor: string }; - } = { - 1: { - container: "tw-bg-[#D9A962]/10", - rankColor: "tw-text-[#D9A962]", - }, - 2: { - container: "tw-bg-[#C0C0C0]/10", - rankColor: "tw-text-[#C0C0C0]", - }, - 3: { - container: "tw-bg-[#B87333]/10", - rankColor: "tw-text-[#B87333]", - }, - }; - - const containerStyles = `tw-flex tw-items-center tw-gap-1 tw-px-2 tw-py-1.5 md:tw-py-1 tw-rounded-lg ${ - rankStyles[drop.rank].container - } tw-ring-1 tw-ring-inset tw-ring-white/5`; - - const rankColor = rankStyles[drop.rank].rankColor; - + if (!drop.rank) { return ( -
+
- +
+ ); + } + + if (drop.rank === 1) { + return ( +
+ + #{drop.rank}
); } - return ( -
- {drop.rank ? ( - + if (drop.rank === 2) { + return ( +
+ + #{drop.rank} - ) : ( - - )} +
+ ); + } + + if (drop.rank === 3) { + return ( +
+ + + #{drop.rank} + +
+ ); + } + + return ( +
+ #{drop.rank}
); }; diff --git a/components/waves/detailed/leaderboard/drops/header/WaveleaderboardDropRaters.tsx b/components/waves/detailed/leaderboard/drops/header/WaveleaderboardDropRaters.tsx index 1b2b1b2f2..84951136c 100644 --- a/components/waves/detailed/leaderboard/drops/header/WaveleaderboardDropRaters.tsx +++ b/components/waves/detailed/leaderboard/drops/header/WaveleaderboardDropRaters.tsx @@ -13,7 +13,7 @@ export const WaveLeaderboardDropRaters: React.FC< WaveLeaderboardDropRatersProps > = ({ drop }) => { const votersCountLabel = drop.raters_count === 1 ? "voter" : "voters"; - const userVote = drop.context_profile_context?.rating || 0; + const userVote = drop.context_profile_context?.rating ?? 0; const isNegativeVote = userVote < 0; const topThreeRankStyles: { [key: number]: string } = { diff --git a/components/waves/detailed/small-leaderboard/WaveDetailedLeaderboardTopThreeDrop.tsx b/components/waves/detailed/small-leaderboard/WaveDetailedLeaderboardTopThreeDrop.tsx index 207a76a01..877a6397c 100644 --- a/components/waves/detailed/small-leaderboard/WaveDetailedLeaderboardTopThreeDrop.tsx +++ b/components/waves/detailed/small-leaderboard/WaveDetailedLeaderboardTopThreeDrop.tsx @@ -21,14 +21,14 @@ export const WaveDetailedLeaderboardTopThreeDrop: React.FC< const getPFPColor = (rank: number | null): string | null => { if (rank === 1) return "tw-ring-[#E8D48A]/80"; if (rank === 2) return "tw-ring-[#DDDDDD]/80"; - if (rank === 3) return "tw-ring-[#D9A962]/80"; + if (rank === 3) return "tw-ring-[#CD7F32]/80"; return null; }; const getRankTextColor = (rank: number | null): string | null => { if (rank === 1) return "tw-text-[#E8D48A]"; if (rank === 2) return "tw-text-[#DDDDDD]"; - if (rank === 3) return "tw-text-[#D9A962]"; + if (rank === 3) return "tw-text-[#CD7F32]"; return null; }; @@ -54,7 +54,7 @@ export const WaveDetailedLeaderboardTopThreeDrop: React.FC< const thropyIcon = (rank: number | null) => { if (rank === 1) { return ( -
+
+
+
void; } -const getColorClasses = (rank: number | null) => { - const rankStyles = { - 1: { - base: "tw-border tw-border-solid tw-border-amber-400/10 tw-bg-[linear-gradient(180deg,rgba(31,31,37,0.98)_0%,rgba(51,46,41,0.95)_100%)] tw-shadow-[inset_0_0_16px_rgba(251,191,36,0.01)]", - hover: - "desktop-hover:hover:tw-shadow-[inset_0_0_20px_rgba(251,191,36,0.02)] desktop-hover:hover:tw-border-amber-400/15 desktop-hover:hover:tw-bg-[linear-gradient(180deg,rgba(35,35,41,0.98)_0%,rgba(56,51,46,0.95)_100%)]", - }, - 2: { - base: "tw-border tw-border-solid tw-border-slate-400/10 tw-bg-[linear-gradient(180deg,rgba(31,31,37,0.98)_0%,rgba(46,46,51,0.95)_100%)] tw-shadow-[inset_0_0_16px_rgba(226,232,240,0.01)]", - hover: - "desktop-hover:hover:tw-shadow-[inset_0_0_20px_rgba(226,232,240,0.02)] desktop-hover:hover:tw-border-slate-400/15 desktop-hover:hover:tw-bg-[linear-gradient(180deg,rgba(35,35,41,0.98)_0%,rgba(51,51,56,0.95)_100%)]", - }, - 3: { - base: "tw-border tw-border-solid tw-border-[#CD7F32]/10 tw-bg-[linear-gradient(180deg,rgba(31,31,37,0.98)_0%,rgba(46,41,36,0.95)_100%)] tw-shadow-[inset_0_0_16px_rgba(205,127,50,0.01)]", - hover: - "desktop-hover:hover:tw-shadow-[inset_0_0_20px_rgba(205,127,50,0.02)] desktop-hover:hover:tw-border-[#CD7F32]/15 desktop-hover:hover:tw-bg-[linear-gradient(180deg,rgba(35,35,41,0.98)_0%,rgba(51,46,41,0.95)_100%)]", - }, - default: { - base: "tw-border tw-border-solid tw-border-iron-600/40 tw-bg-[linear-gradient(90deg,rgba(31,31,37,0.95)_0%,rgba(35,35,40,0.98)_100%)] tw-shadow-[inset_0_0_16px_rgba(255,255,255,0.03)]", - hover: - "desktop-hover:hover:tw-shadow-[inset_0_0_20px_rgba(255,255,255,0.05)] desktop-hover:hover:tw-border-iron-500/40", - }, - }; - - const style = - rankStyles[rank as keyof typeof rankStyles] ?? rankStyles.default; - return `${style.base} ${style.hover}`; +const rankGradients: Record = { + 1: "tw-from-[#E8D48A]/30 tw-via-[#D9A962]/30 tw-to-[#E8D48A]/30 desktop-hover:hover:tw-from-[#E8D48A]/40 desktop-hover:hover:tw-via-[#D9A962]/40 desktop-hover:hover:tw-to-[#E8D48A]/40 tw-shadow-[0_0_32px_rgba(232,212,138,0.1)] desktop-hover:hover:tw-shadow-[0_0_48px_rgba(232,212,138,0.15)]", + 2: "tw-from-[#DDDDDD]/30 tw-via-[#C0C0C0]/30 tw-to-[#DDDDDD]/30 desktop-hover:hover:tw-from-[#DDDDDD]/40 desktop-hover:hover:tw-via-[#C0C0C0]/40 desktop-hover:hover:tw-to-[#DDDDDD]/40 tw-shadow-[0_0_32px_rgba(221,221,221,0.1)] desktop-hover:hover:tw-shadow-[0_0_48px_rgba(221,221,221,0.15)]", + 3: "tw-from-[#CD7F32]/30 tw-via-[#B87333]/30 tw-to-[#CD7F32]/30 desktop-hover:hover:tw-from-[#CD7F32]/40 desktop-hover:hover:tw-via-[#B87333]/40 desktop-hover:hover:tw-to-[#CD7F32]/40 tw-shadow-[0_0_32px_rgba(205,127,50,0.1)] desktop-hover:hover:tw-shadow-[0_0_48px_rgba(205,127,50,0.15)]", + default: "tw-from-iron-800/50 tw-via-iron-700/30 tw-to-iron-800/50 hover:tw-from-iron-700/60 hover:tw-via-iron-600/40 hover:tw-to-iron-800/60", }; export const WaveWinnersDrop: React.FC = ({ @@ -45,26 +23,25 @@ export const WaveWinnersDrop: React.FC = ({ wave, onDropClick, }) => { - const colorClasses = getColorClasses(drop.rank); + const gradientClass = + drop.rank && drop.rank <= 3 + ? rankGradients[drop.rank] + : rankGradients.default; return (
onDropClick(drop)} - role="button" - className={`tw-cursor-pointer tw-relative tw-w-full tw-rounded-xl tw-py-5 tw-px-4 ${colorClasses} tw-overflow-hidden tw-backdrop-blur-sm - tw-transition-all tw-duration-300 tw-ease-out - tw-shadow-lg tw-shadow-black/5 - desktop-hover:hover:tw-shadow-xl desktop-hover:hover:tw-shadow-black/10 - tw-group`} + className={`tw-group tw-cursor-pointer tw-rounded-xl tw-bg-gradient-to-b ${gradientClass} tw-p-[1px] tw-transition tw-duration-300 tw-ease-out`} > -
-
- - -
- +
+
+
+ + +
+ +
-
diff --git a/components/waves/detailed/winners/drops/WaveWinnersDropContent.tsx b/components/waves/detailed/winners/drops/WaveWinnersDropContent.tsx index e9c0d6f2d..ccd250f87 100644 --- a/components/waves/detailed/winners/drops/WaveWinnersDropContent.tsx +++ b/components/waves/detailed/winners/drops/WaveWinnersDropContent.tsx @@ -11,7 +11,7 @@ export const WaveWinnersDropContent: React.FC = ({ }) => { const [activePartIndex, setActivePartIndex] = useState(0); return ( -
+
+
+
- {drop.rank}st + #{drop.rank}
); @@ -57,10 +57,10 @@ export default function WaveWinnersDropHeaderRank({ if (drop.rank === 2) { return ( -
+
- {drop.rank}nd + #{drop.rank}
); @@ -68,18 +68,18 @@ export default function WaveWinnersDropHeaderRank({ if (drop.rank === 3) { return ( -
+
- {drop.rank}rd + #{drop.rank}
); } return ( -
- #{drop.rank} +
+ #{drop.rank}
); } diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoters.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoters.tsx index 90f8d0f33..3d0903dbd 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoters.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoters.tsx @@ -9,8 +9,28 @@ interface WaveWinnersDropHeaderVotersProps { export default function WaveWinnersDropHeaderVoters({ drop, }: WaveWinnersDropHeaderVotersProps) { + const hasUserVoted = + drop.context_profile_context?.rating !== undefined && + drop.context_profile_context?.rating !== 0; + + const userVote = drop.context_profile_context?.rating ?? 0; + const isNegativeVote = userVote < 0; + + const topThreeRankStyles: { [key: number]: string } = { + 1: "tw-text-[#E8D48A]", + 2: "tw-text-[#DDDDDD]", + 3: "tw-text-[#CD7F32]", + }; + + const rankStyle = + drop.rank && drop.rank <= 3 + ? topThreeRankStyles[drop.rank] + : isNegativeVote + ? "tw-bg-gradient-to-r tw-from-red tw-to-red tw-bg-clip-text tw-text-transparent" + : "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent"; + return ( -
+
{drop.top_raters.map((voter, index) => ( + + {hasUserVoted && ( +
+ + Your vote: + + {formatNumberWithCommas(userVote)} {drop.wave.voting_credit_type} + + +
+ )}
); } diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx index 0b541275c..75e844d17 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx @@ -36,7 +36,7 @@ export default function WaveWinnersDropOutcome({ {nicOutcomes.map((nicOutcome, i) => (
- + NIC @@ -63,7 +63,7 @@ export default function WaveWinnersDropOutcome({ {repOutcomes.map((repOutcome, i) => (
- + Rep @@ -91,7 +91,7 @@ export default function WaveWinnersDropOutcome({ {manualOutcomes.map((outcome, i) => (
= ({
-
+
-
+
-
-
-
+
+
+
= ( return (
onDropClick(drop)} className="tw-cursor-pointer tw-group">
-
-
+
+
= ( ) : ( -
+
)}
- + 2nd
@@ -63,13 +63,13 @@ export const WaveWinnersPodiumSecond: React.FC = (
-
+
-
-
-
-
-
+
+
+
+
+
= ( onClick={(e) => e.stopPropagation()} className="tw-transition-all tw-no-underline tw-mb-2 tw-mt-4 tw-relative" > -
+
{drop.author.handle}
@@ -86,7 +86,7 @@ export const WaveWinnersPodiumSecond: React.FC = (
= 0 ? "tw-text-[#94A3B8]" : "tw-text-[#ff4466]" + drop.rating >= 0 ? "tw-text-[#dddddd]" : "tw-text-[#ff4466]" } tw-font-semibold tw-text-base`} > {formatNumberWithCommas(drop.rating)} diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx index 2cf78d0b0..482a16893 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx @@ -23,7 +23,7 @@ export const WaveWinnersPodiumThird: React.FC = ({ return (
onDropClick(drop)} className="tw-cursor-pointer tw-group">
-
+
= ({ ) : ( -
+
)} @@ -63,13 +63,13 @@ export const WaveWinnersPodiumThird: React.FC = ({
-
+
-
+
-
-
-
+
+
+
Date: Fri, 27 Dec 2024 12:56:05 +0200 Subject: [PATCH 22/33] wip Signed-off-by: Ragne --- .../header/WaveleaderboardDropRaters.tsx | 4 +-- .../waves/detailed/winners/WaveWinners.tsx | 1 - .../winners/drops/WaveWinnersDrop.tsx | 2 +- .../winners/drops/WaveWinnersDropContent.tsx | 2 +- .../drops/header/WaveWinnersDropHeader.tsx | 28 +++++++++++-------- .../header/WaveWinnersDropHeaderAuthorPfp.tsx | 7 ++--- .../WaveWinnersDropHeaderTotalVotes.tsx | 4 +-- .../header/WaveWinnersDropHeaderVoter.tsx | 4 +-- .../header/WaveWinnersDropHeaderVoters.tsx | 4 +-- .../drops/header/WaveWinnersDropOutcome.tsx | 2 +- .../podium/WavePodiumItemContentOutcomes.tsx | 4 +-- .../winners/podium/WaveWinnersPodium.tsx | 6 ++-- .../winners/podium/WaveWinnersPodiumFirst.tsx | 24 ++++++++-------- .../podium/WaveWinnersPodiumSecond.tsx | 22 +++++++-------- .../winners/podium/WaveWinnersPodiumThird.tsx | 20 ++++++------- 15 files changed, 67 insertions(+), 67 deletions(-) diff --git a/components/waves/detailed/leaderboard/drops/header/WaveleaderboardDropRaters.tsx b/components/waves/detailed/leaderboard/drops/header/WaveleaderboardDropRaters.tsx index 84951136c..9b9522b1a 100644 --- a/components/waves/detailed/leaderboard/drops/header/WaveleaderboardDropRaters.tsx +++ b/components/waves/detailed/leaderboard/drops/header/WaveleaderboardDropRaters.tsx @@ -70,10 +70,10 @@ export const WaveLeaderboardDropRaters: React.FC< {`${voter.profile.handle}'s ) : ( -
+
)}
diff --git a/components/waves/detailed/winners/WaveWinners.tsx b/components/waves/detailed/winners/WaveWinners.tsx index f4055d5e8..4d0696690 100644 --- a/components/waves/detailed/winners/WaveWinners.tsx +++ b/components/waves/detailed/winners/WaveWinners.tsx @@ -16,7 +16,6 @@ export const WaveWinners: React.FC = ({ return (
-
); diff --git a/components/waves/detailed/winners/drops/WaveWinnersDrop.tsx b/components/waves/detailed/winners/drops/WaveWinnersDrop.tsx index 2d6714c58..f13b72d37 100644 --- a/components/waves/detailed/winners/drops/WaveWinnersDrop.tsx +++ b/components/waves/detailed/winners/drops/WaveWinnersDrop.tsx @@ -38,7 +38,7 @@ export const WaveWinnersDrop: React.FC = ({
-
+
diff --git a/components/waves/detailed/winners/drops/WaveWinnersDropContent.tsx b/components/waves/detailed/winners/drops/WaveWinnersDropContent.tsx index ccd250f87..ae0e4c16c 100644 --- a/components/waves/detailed/winners/drops/WaveWinnersDropContent.tsx +++ b/components/waves/detailed/winners/drops/WaveWinnersDropContent.tsx @@ -11,7 +11,7 @@ export const WaveWinnersDropContent: React.FC = ({ }) => { const [activePartIndex, setActivePartIndex] = useState(0); return ( -
+
= ({ drop, }) => { return ( -
-
e.stopPropagation()}> -
- +
e.stopPropagation()} + className="tw-flex tw-flex-wrap sm:tw-flex-nowrap tw-items-center tw-gap-3" + > + + +
+
+
+ + +
-
- - -
-
-
- - +
+ + +
); diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorPfp.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorPfp.tsx index f940bbf63..d78335084 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorPfp.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderAuthorPfp.tsx @@ -1,9 +1,6 @@ import React from "react"; import Link from "next/link"; import { ExtendedDrop } from "../../../../../../helpers/waves/drop.helpers"; -import { cicToType } from "../../../../../../helpers/Helpers"; -import UserCICAndLevel from "../../../../../user/utils/UserCICAndLevel"; -import { UserCICAndLevelSize } from "../../../../../user/utils/UserCICAndLevel"; import { getScaledImageUri, ImageScale, @@ -26,10 +23,10 @@ export default function WaveWinnersDropHeaderAuthorPfp({ ) : ( -
+
)} ); diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderTotalVotes.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderTotalVotes.tsx index b01d58181..396e6a51b 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderTotalVotes.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderTotalVotes.tsx @@ -18,11 +18,11 @@ export default function WaveWinnersDropHeaderTotalVotes({ ? topThreeRankStyles[drop.rank] : drop.rating >= 0 ? "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent" - : "tw-bg-gradient-to-r tw-from-red tw-to-red tw-bg-clip-text tw-text-transparent"; + : "tw-bg-gradient-to-r tw-from-rose-400 tw-to-rose-500 tw-bg-clip-text tw-text-transparent"; return (
- + {formatNumberWithCommas(drop.rating)} diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoter.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoter.tsx index 46088f974..cba16ea26 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoter.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoter.tsx @@ -43,10 +43,10 @@ export default function WaveWinnersDropHeaderVoter({ {`${voter.profile.handle}'s ) : ( -
+
)}
diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoters.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoters.tsx index 3d0903dbd..d1bac79c3 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoters.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoters.tsx @@ -30,7 +30,7 @@ export default function WaveWinnersDropHeaderVoters({ : "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent"; return ( -
+
{drop.top_raters.map((voter, index) => ( {hasUserVoted && ( -
+
Your vote: diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx index 75e844d17..850b25730 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx @@ -28,7 +28,7 @@ export default function WaveWinnersDropOutcome({ } return ( -
+
Outcome:
diff --git a/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx b/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx index 1262b394f..17dd34606 100644 --- a/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx +++ b/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx @@ -40,7 +40,7 @@ export const WavePodiumItemContentOutcomes: React.FC< } const tooltipContent = ( -
+
Outcome Details @@ -151,7 +151,7 @@ export const WavePodiumItemContentOutcomes: React.FC< onClick={handleClick} className="tw-transition-all tw-duration-200 tw-flex tw-items-center tw-gap-2 tw-px-3 tw-py-1.5 tw-rounded-xl tw-bg-iron-800/40 tw-backdrop-blur-sm tw-border tw-border-solid tw-border-iron-700/20 hover:tw-bg-iron-800/60 hover:tw-border-iron-700/40 hover:tw-shadow-lg" > - + Outcome
diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx index 2fd4ea8b1..4e2ce991e 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodium.tsx @@ -113,9 +113,9 @@ export const WaveWinnersPodium: React.FC = ({ } return ( -
-
-
+
+
+
{secondPlaceDrop ? ( = ({ ) : ( -
+
)}
- + 1st
@@ -77,33 +77,33 @@ export const WaveWinnersPodiumFirst: React.FC = ({ e.stopPropagation()} - className="tw-transition-all tw-no-underline tw-mb-2 tw-mt-4 tw-relative tw-text-center" + className="tw-transition-all tw-no-underline tw-mb-2 tw-mt-2 sm:tw-mt-4 tw-relative tw-text-center" > - + {drop.author.handle}
-
+
= 0 ? "tw-text-[#E8D48A]" : "tw-text-[#ff4466]" - } tw-font-semibold tw-text-2xl`} + } tw-font-semibold tw-text-sm sm:tw-text-base md:tw-text-2xl`} > {formatNumberWithCommas(drop.rating)} - + {drop.wave.voting_credit_type}
-
- +
+ {formatNumberWithCommas(drop.raters_count)} - + {drop.raters_count === 1 ? "voter" : "voters"}
diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx index 959bfe581..464effd78 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx @@ -35,17 +35,17 @@ export const WaveWinnersPodiumSecond: React.FC = ( ) : ( -
+
)}
- + 2nd
@@ -77,31 +77,31 @@ export const WaveWinnersPodiumSecond: React.FC = ( onClick={(e) => e.stopPropagation()} className="tw-transition-all tw-no-underline tw-mb-2 tw-mt-4 tw-relative" > -
+
{drop.author.handle}
-
+
= 0 ? "tw-text-[#dddddd]" : "tw-text-[#ff4466]" - } tw-font-semibold tw-text-base`} + } tw-font-semibold tw-text-sm sm:tw-text-base`} > {formatNumberWithCommas(drop.rating)} - + {drop.wave.voting_credit_type}
-
- +
+ {formatNumberWithCommas(drop.raters_count)} - + {drop.raters_count === 1 ? "voter" : "voters"}
diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx index 482a16893..7091d11e4 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx @@ -35,17 +35,17 @@ export const WaveWinnersPodiumThird: React.FC = ({ ) : ( -
+
)}
- + 3rd
@@ -77,13 +77,13 @@ export const WaveWinnersPodiumThird: React.FC = ({ onClick={(e) => e.stopPropagation()} className="tw-transition-all tw-no-underline tw-mb-2 tw-mt-4 tw-relative" > -
+
{drop.author.handle}
-
+
= 0 ? "tw-text-[#CD7F32]" : "tw-text-[#ff4466]" @@ -91,17 +91,17 @@ export const WaveWinnersPodiumThird: React.FC = ({ > {formatNumberWithCommas(drop.rating)} - + {drop.wave.voting_credit_type}
-
- +
+ {formatNumberWithCommas(drop.raters_count)} - + {drop.raters_count === 1 ? "voter" : "voters"}
From 7c6d0334e72d9db10feb8ca1cec8a077e8d7bcdb Mon Sep 17 00:00:00 2001 From: Ragne Date: Mon, 30 Dec 2024 11:05:24 +0200 Subject: [PATCH 23/33] wip Signed-off-by: Ragne --- .../detailed/leaderboard/WaveLeaderboard.tsx | 80 ++++++++++--------- .../WaveDetailedSmallLeaderboard.tsx | 2 +- .../drops/header/WaveWinnersDropHeader.tsx | 2 +- 3 files changed, 45 insertions(+), 39 deletions(-) diff --git a/components/waves/detailed/leaderboard/WaveLeaderboard.tsx b/components/waves/detailed/leaderboard/WaveLeaderboard.tsx index 080d49201..9ddd46056 100644 --- a/components/waves/detailed/leaderboard/WaveLeaderboard.tsx +++ b/components/waves/detailed/leaderboard/WaveLeaderboard.tsx @@ -13,6 +13,7 @@ import { WaveDropCreate } from "./create/WaveDropCreate"; import { AnimatePresence, motion } from "framer-motion"; import useCapacitor from "../../../../hooks/useCapacitor"; import { WaveWinners } from "../winners/WaveWinners"; +import { useWaveState, WaveVotingState } from "../../../../hooks/useWaveState"; interface WaveLeaderboardProps { readonly wave: ApiWave; @@ -31,6 +32,7 @@ export const WaveLeaderboard: React.FC = ({ setActiveDrop, }) => { const capacitor = useCapacitor(); + const { votingState } = useWaveState(wave); const [sort, setSort] = useState( WaveLeaderboardSortType.RANK ); @@ -68,44 +70,48 @@ export const WaveLeaderboard: React.FC = ({ > {children} - + {votingState === WaveVotingState.ENDED ? ( + + ) : ( + <> + + setIsCreatingDrop(true)} + /> - - setIsCreatingDrop(true)} - /> - - - {isCreatingDrop && ( - - setIsCreatingDrop(false)} - onSuccess={() => { - setIsCreatingDrop(false); - }} - /> - - )} - - setIsCreatingDrop(true)} - /> + + {isCreatingDrop && ( + + setIsCreatingDrop(false)} + onSuccess={() => { + setIsCreatingDrop(false); + }} + /> + + )} + + setIsCreatingDrop(true)} + /> + + )}
diff --git a/components/waves/detailed/small-leaderboard/WaveDetailedSmallLeaderboard.tsx b/components/waves/detailed/small-leaderboard/WaveDetailedSmallLeaderboard.tsx index f98497a76..109912ad8 100644 --- a/components/waves/detailed/small-leaderboard/WaveDetailedSmallLeaderboard.tsx +++ b/components/waves/detailed/small-leaderboard/WaveDetailedSmallLeaderboard.tsx @@ -37,7 +37,7 @@ export const WaveDetailedSmallLeaderboard: React.FC< }); return ( -
+
{memoizedDrops.length === 0 && !isFetching ? (
diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeader.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeader.tsx index a682ab8e0..098008678 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeader.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeader.tsx @@ -17,7 +17,7 @@ export const WaveWinnersDropHeader: React.FC = ({ return (
e.stopPropagation()} - className="tw-flex tw-flex-wrap sm:tw-flex-nowrap tw-items-center tw-gap-3" + className="tw-flex tw-flex-wrap sm:tw-flex-nowrap tw-items-center tw-gap-x-4 tw-gap-y-4" > From 20e839fe05c7ff98ab834c5a3b42c649df965d91 Mon Sep 17 00:00:00 2001 From: Simo Date: Mon, 30 Dec 2024 11:05:45 +0200 Subject: [PATCH 24/33] wip Signed-off-by: Simo --- components/brain/my-stream/MyStreamWave.tsx | 105 +++++------------- .../brain/my-stream/MyStreamWaveChat.tsx | 104 +++++++++++++++++ .../brain/my-stream/MyStreamWaveTabs.tsx | 43 +++++++ .../brain/my-stream/MyStreamWaveViews.tsx | 38 +++++++ hooks/useWaveState.ts | 36 ++++-- 5 files changed, 237 insertions(+), 89 deletions(-) create mode 100644 components/brain/my-stream/MyStreamWaveChat.tsx create mode 100644 components/brain/my-stream/MyStreamWaveTabs.tsx create mode 100644 components/brain/my-stream/MyStreamWaveViews.tsx diff --git a/components/brain/my-stream/MyStreamWave.tsx b/components/brain/my-stream/MyStreamWave.tsx index 5cdf577e6..080732339 100644 --- a/components/brain/my-stream/MyStreamWave.tsx +++ b/components/brain/my-stream/MyStreamWave.tsx @@ -1,96 +1,41 @@ -import React, { useMemo, useState } from "react"; -import WaveDropsAll from "../../waves/detailed/drops/WaveDropsAll"; -import { CreateDropWaveWrapper } from "../../waves/detailed/CreateDropWaveWrapper"; -import PrivilegedDropCreator, { - DropMode, -} from "../../waves/detailed/PrivilegedDropCreator"; -import { useWaveData } from "../../../hooks/useWaveData"; -import useCapacitor from "../../../hooks/useCapacitor"; -import { ActiveDropAction, ActiveDropState } from "../../waves/detailed/chat/WaveChat"; -import { ApiDrop } from "../../../generated/models/ObjectSerializer"; +import React, { useState } from "react"; import { ExtendedDrop } from "../../../helpers/waves/drop.helpers"; +import MyStreamWaveChat from "./MyStreamWaveChat"; +import MyStreamWaveTabs, { MyStreamWaveTab } from "./MyStreamWaveTabs"; +import { useWaveData } from "../../../hooks/useWaveData"; +import MyStreamWaveViews from "./MyStreamWaveViews"; +import { useWaveState } from "../../../hooks/useWaveState"; +import { ApiWaveType } from "../../../generated/models/ObjectSerializer"; interface MyStreamWaveProps { readonly waveId: string; readonly onDropClick: (drop: ExtendedDrop) => void; } -const calculateHeight = (isCapacitor: boolean) => { - if (isCapacitor) { - return "tw-h-[calc(100vh-18.75rem)]"; - } - return `tw-h-[calc(100vh-13rem)] lg:tw-h-[calc(100vh-10rem)]`; -}; - const MyStreamWave: React.FC = ({ waveId, onDropClick }) => { + const [activeTab, setActiveTab] = useState( + MyStreamWaveTab.CHAT + ); const { data: wave } = useWaveData(waveId); - const capacitor = useCapacitor(); - - const containerClassName = useMemo(() => { - return `tw-w-full tw-flex tw-flex-col tw-rounded-t-xl tw-overflow-hidden ${calculateHeight( - capacitor.isCapacitor - )}`; - }, [capacitor.isCapacitor]); - - const [activeDrop, setActiveDrop] = useState(null); - const onReply = (drop: ApiDrop, partId: number) => { - setActiveDrop({ - action: ActiveDropAction.REPLY, - drop, - partId, - }); - }; - - const onQuote = (drop: ApiDrop, partId: number) => { - setActiveDrop({ - action: ActiveDropAction.QUOTE, - drop, - partId, - }); - }; - - const handleReply = ({ drop, partId }: { drop: ApiDrop; partId: number }) => { - onReply(drop, partId); - }; - - const handleQuote = ({ drop, partId }: { drop: ApiDrop; partId: number }) => { - onQuote(drop, partId); - }; - - const onCancelReplyQuote = () => { - setActiveDrop(null); - }; - + const isDropsWave = wave?.wave.type !== ApiWaveType.Chat; if (!wave) { return null; } + return ( -
-
-
- -
- - - -
-
-
+
+ {isDropsWave && ( + + )} +
); }; diff --git a/components/brain/my-stream/MyStreamWaveChat.tsx b/components/brain/my-stream/MyStreamWaveChat.tsx new file mode 100644 index 000000000..17140e97b --- /dev/null +++ b/components/brain/my-stream/MyStreamWaveChat.tsx @@ -0,0 +1,104 @@ +import React, { useMemo, useState } from "react"; +import { ExtendedDrop } from "../../../helpers/waves/drop.helpers"; +import { useWaveData } from "../../../hooks/useWaveData"; +import useCapacitor from "../../../hooks/useCapacitor"; +import { + ActiveDropAction, + ActiveDropState, +} from "../../waves/detailed/chat/WaveChat"; +import { ApiDrop } from "../../../generated/models/ApiDrop"; +import WaveDropsAll from "../../waves/detailed/drops/WaveDropsAll"; +import { CreateDropWaveWrapper } from "../../waves/detailed/CreateDropWaveWrapper"; +import PrivilegedDropCreator, { + DropMode, +} from "../../waves/detailed/PrivilegedDropCreator"; +import { ApiWave } from "../../../generated/models/ApiWave"; + +interface MyStreamWaveChatProps { + readonly wave: ApiWave; + readonly onDropClick: (drop: ExtendedDrop) => void; +} + +const calculateHeight = (isCapacitor: boolean) => { + if (isCapacitor) { + return "tw-h-[calc(100vh-18.75rem)]"; + } + return `tw-h-[calc(100vh-13rem)] lg:tw-h-[calc(100vh-10rem)]`; +}; + +const MyStreamWaveChat: React.FC = ({ + wave, + onDropClick, +}) => { + const capacitor = useCapacitor(); + + const containerClassName = useMemo(() => { + return `tw-w-full tw-flex tw-flex-col tw-rounded-t-xl tw-overflow-hidden ${calculateHeight( + capacitor.isCapacitor + )}`; + }, [capacitor.isCapacitor]); + + const [activeDrop, setActiveDrop] = useState(null); + const onReply = (drop: ApiDrop, partId: number) => { + setActiveDrop({ + action: ActiveDropAction.REPLY, + drop, + partId, + }); + }; + + const onQuote = (drop: ApiDrop, partId: number) => { + setActiveDrop({ + action: ActiveDropAction.QUOTE, + drop, + partId, + }); + }; + + const handleReply = ({ drop, partId }: { drop: ApiDrop; partId: number }) => { + onReply(drop, partId); + }; + + const handleQuote = ({ drop, partId }: { drop: ApiDrop; partId: number }) => { + onQuote(drop, partId); + }; + + const onCancelReplyQuote = () => { + setActiveDrop(null); + }; + + if (!wave) { + return null; + } + return ( +
+
+
+ +
+ + + +
+
+
+
+ ); +}; + +export default MyStreamWaveChat; diff --git a/components/brain/my-stream/MyStreamWaveTabs.tsx b/components/brain/my-stream/MyStreamWaveTabs.tsx new file mode 100644 index 000000000..b3ab17334 --- /dev/null +++ b/components/brain/my-stream/MyStreamWaveTabs.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import { ApiWave } from "../../../generated/models/ApiWave"; +import { useWaveState, WaveVotingState } from "../../../hooks/useWaveState"; +import { TabToggle } from "../../common/TabToggle"; + +interface MyStreamWaveTabsProps { + readonly wave: ApiWave; + readonly activeTab: MyStreamWaveTab; + readonly setActiveTab: (tab: MyStreamWaveTab) => void; +} + +export enum MyStreamWaveTab { + CHAT = "CHAT", + LEADERBOARD = "LEADERBOARD", + OUTCOME = "OUTCOME", +} + +export const MyStreamWaveTabs: React.FC = ({ + activeTab, + setActiveTab, + wave, +}) => { + const { votingState } = useWaveState(wave); + const options = [ + { key: MyStreamWaveTab.CHAT, label: "Chat" }, + { + key: MyStreamWaveTab.LEADERBOARD, + label: votingState === WaveVotingState.ENDED ? "Winners" : "Leaderboard", + }, + { key: MyStreamWaveTab.OUTCOME, label: "Outcome" }, + ] as const; + return ( +
+ setActiveTab(key as MyStreamWaveTab)} + /> +
+ ); +}; + +export default MyStreamWaveTabs; diff --git a/components/brain/my-stream/MyStreamWaveViews.tsx b/components/brain/my-stream/MyStreamWaveViews.tsx new file mode 100644 index 000000000..63aea07ec --- /dev/null +++ b/components/brain/my-stream/MyStreamWaveViews.tsx @@ -0,0 +1,38 @@ +import React from "react"; +import { ApiWave } from "../../../generated/models/ApiWave"; +import { MyStreamWaveTab } from "./MyStreamWaveTabs"; +import { ExtendedDrop } from "../../../helpers/waves/drop.helpers"; +import MyStreamWaveChat from "./MyStreamWaveChat"; +import { WaveLeaderboard } from "../../waves/detailed/leaderboard/WaveLeaderboard"; +import { WaveOutcome } from "../../waves/detailed/outcome/WaveOutcome"; + +interface MyStreamWaveViewsProps { + readonly wave: ApiWave; + readonly activeTab: MyStreamWaveTab; + readonly onDropClick: (drop: ExtendedDrop) => void; +} + +const MyStreamWaveViews: React.FC = ({ + wave, + activeTab, + onDropClick, +}) => { + const components: Record = { + [MyStreamWaveTab.CHAT]: ( + + ), + [MyStreamWaveTab.LEADERBOARD]: ( + +
+
+ ), + [MyStreamWaveTab.OUTCOME]: ( + +
+
+ ), + }; + return components[activeTab]; +}; + +export default MyStreamWaveViews; diff --git a/hooks/useWaveState.ts b/hooks/useWaveState.ts index 265ba7a7f..69e1d3577 100644 --- a/hooks/useWaveState.ts +++ b/hooks/useWaveState.ts @@ -1,14 +1,16 @@ import { ApiWave } from "../generated/models/ApiWave"; import { useState, useEffect } from "react"; +import { ApiWaveType } from "../generated/models/ApiWaveType"; export enum WaveVotingState { NOT_STARTED = "NOT_STARTED", ONGOING = "ONGOING", - ENDED = "ENDED" + ENDED = "ENDED", } interface UseWaveStateReturn { readonly votingState: WaveVotingState; + readonly isDropsWave: boolean; } const calculateVotingState = ( @@ -28,7 +30,9 @@ const calculateVotingState = ( // Only min time set - either not started or ongoing if (minTime && !maxTime) { - return now < minTime ? WaveVotingState.NOT_STARTED : WaveVotingState.ONGOING; + return now < minTime + ? WaveVotingState.NOT_STARTED + : WaveVotingState.ONGOING; } // Both times set - all states possible @@ -71,14 +75,22 @@ const calculateNextStateChangeTime = ( export function useWaveState(wave: ApiWave): UseWaveStateReturn { const [votingState, setVotingState] = useState(() => { - const minTime = wave.voting.period?.min ? new Date(wave.voting.period.min).getTime() : null; - const maxTime = wave.voting.period?.max ? new Date(wave.voting.period.max).getTime() : null; + const minTime = wave.voting.period?.min + ? new Date(wave.voting.period.min).getTime() + : null; + const maxTime = wave.voting.period?.max + ? new Date(wave.voting.period.max).getTime() + : null; return calculateVotingState(Date.now(), minTime, maxTime); }); useEffect(() => { - const minTime = wave.voting.period?.min ? new Date(wave.voting.period.min).getTime() : null; - const maxTime = wave.voting.period?.max ? new Date(wave.voting.period.max).getTime() : null; + const minTime = wave.voting.period?.min + ? new Date(wave.voting.period.min).getTime() + : null; + const maxTime = wave.voting.period?.max + ? new Date(wave.voting.period.max).getTime() + : null; const now = Date.now(); // Update current state @@ -86,7 +98,12 @@ export function useWaveState(wave: ApiWave): UseWaveStateReturn { setVotingState(currentState); // Calculate when the next state change should occur - const nextChangeTime = calculateNextStateChangeTime(now, minTime, maxTime, currentState); + const nextChangeTime = calculateNextStateChangeTime( + now, + minTime, + maxTime, + currentState + ); // If there's a next state change, set up a timeout if (nextChangeTime) { @@ -103,6 +120,7 @@ export function useWaveState(wave: ApiWave): UseWaveStateReturn { }, [wave.voting.period?.min, wave.voting.period?.max]); return { - votingState + votingState, + isDropsWave: wave.wave.type !== ApiWaveType.Chat, }; -} +} From bf5b01dea4dda49206796959c46ab098e86716f8 Mon Sep 17 00:00:00 2001 From: Simo Date: Mon, 30 Dec 2024 11:11:02 +0200 Subject: [PATCH 25/33] wip Signed-off-by: Simo --- components/brain/my-stream/MyStreamWave.tsx | 49 ++++++++++--------- .../brain/my-stream/MyStreamWaveChat.tsx | 8 +-- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/components/brain/my-stream/MyStreamWave.tsx b/components/brain/my-stream/MyStreamWave.tsx index 080732339..3a5abda0f 100644 --- a/components/brain/my-stream/MyStreamWave.tsx +++ b/components/brain/my-stream/MyStreamWave.tsx @@ -13,31 +13,32 @@ interface MyStreamWaveProps { } const MyStreamWave: React.FC = ({ waveId, onDropClick }) => { - const [activeTab, setActiveTab] = useState( - MyStreamWaveTab.CHAT - ); - const { data: wave } = useWaveData(waveId); - const isDropsWave = wave?.wave.type !== ApiWaveType.Chat; - if (!wave) { - return null; - } + return ; + // const [activeTab, setActiveTab] = useState( + // MyStreamWaveTab.CHAT + // ); + // const { data: wave } = useWaveData(waveId); + // const isDropsWave = wave?.wave.type !== ApiWaveType.Chat; + // if (!wave) { + // return null; + // } - return ( -
- {isDropsWave && ( - - )} - -
- ); + // return ( + //
+ // {isDropsWave && ( + // + // )} + // + //
+ // ); }; export default MyStreamWave; diff --git a/components/brain/my-stream/MyStreamWaveChat.tsx b/components/brain/my-stream/MyStreamWaveChat.tsx index 17140e97b..287586ec1 100644 --- a/components/brain/my-stream/MyStreamWaveChat.tsx +++ b/components/brain/my-stream/MyStreamWaveChat.tsx @@ -12,10 +12,9 @@ import { CreateDropWaveWrapper } from "../../waves/detailed/CreateDropWaveWrappe import PrivilegedDropCreator, { DropMode, } from "../../waves/detailed/PrivilegedDropCreator"; -import { ApiWave } from "../../../generated/models/ApiWave"; interface MyStreamWaveChatProps { - readonly wave: ApiWave; + readonly waveId: string; readonly onDropClick: (drop: ExtendedDrop) => void; } @@ -27,9 +26,10 @@ const calculateHeight = (isCapacitor: boolean) => { }; const MyStreamWaveChat: React.FC = ({ - wave, + waveId, onDropClick, }) => { + const { data: wave } = useWaveData(waveId); const capacitor = useCapacitor(); const containerClassName = useMemo(() => { @@ -75,7 +75,7 @@ const MyStreamWaveChat: React.FC = ({
Date: Mon, 30 Dec 2024 13:31:54 +0200 Subject: [PATCH 26/33] wip Signed-off-by: Ragne --- .../brain/right-sidebar/WaveContent.tsx | 12 +- .../detailed/WaveDetailedRightSidebar.tsx | 9 +- .../detailed/winners/WaveWinnersSmall.tsx | 311 ++++++++++++++++++ .../header/WaveWinnersDropHeaderRank.tsx | 6 +- .../drops/header/WaveWinnersDropOutcome.tsx | 15 +- .../winners/podium/WaveWinnersPodiumFirst.tsx | 23 +- .../podium/WaveWinnersPodiumSecond.tsx | 21 +- .../winners/podium/WaveWinnersPodiumThird.tsx | 21 +- 8 files changed, 396 insertions(+), 22 deletions(-) create mode 100644 components/waves/detailed/winners/WaveWinnersSmall.tsx diff --git a/components/brain/right-sidebar/WaveContent.tsx b/components/brain/right-sidebar/WaveContent.tsx index 5ad9e0b58..ff0834d10 100644 --- a/components/brain/right-sidebar/WaveContent.tsx +++ b/components/brain/right-sidebar/WaveContent.tsx @@ -6,6 +6,7 @@ import WaveHeader, { WaveHeaderPinnedSide, } from "../../waves/detailed/header/WaveHeader"; import { WaveDetailedSmallLeaderboard } from "../../waves/detailed/small-leaderboard/WaveDetailedSmallLeaderboard"; +import { WaveWinnersSmall } from "../../waves/detailed/winners/WaveWinnersSmall"; import BrainRightSidebarContent from "./BrainRightSidebarContent"; import BrainRightSidebarFollowers from "./BrainRightSidebarFollowers"; import { Mode, SidebarTab } from "./BrainRightSidebar"; @@ -33,10 +34,11 @@ export const WaveContent: React.FC = ({ const isRankWave = wave.wave.type === ApiWaveType.Rank; const { votingState } = useWaveState(wave); + const hasVotingEnded = votingState === WaveVotingState.ENDED; const options = [ { key: SidebarTab.ABOUT, label: "About" }, - { key: SidebarTab.LEADERBOARD, label: votingState === WaveVotingState.ENDED ? "Winners" : "Leaderboard", }, + { key: SidebarTab.LEADERBOARD, label: hasVotingEnded ? "Winners" : "Leaderboard" }, ] as const; if (!isRankWave) { @@ -89,7 +91,13 @@ export const WaveContent: React.FC = ({ )}
) : ( - +
+ {hasVotingEnded ? ( + + ) : ( + + )} +
)} ); diff --git a/components/waves/detailed/WaveDetailedRightSidebar.tsx b/components/waves/detailed/WaveDetailedRightSidebar.tsx index f6c8ecdac..ec139fc89 100644 --- a/components/waves/detailed/WaveDetailedRightSidebar.tsx +++ b/components/waves/detailed/WaveDetailedRightSidebar.tsx @@ -4,6 +4,8 @@ import { ApiWave } from "../../../generated/models/ObjectSerializer"; import { WaveDetailedSmallLeaderboard } from "./small-leaderboard/WaveDetailedSmallLeaderboard"; import { ExtendedDrop } from "../../../helpers/waves/drop.helpers"; import WaveDetailedRightSidebarToggle from "./WaveDetailedRightSidebarToggle"; +import { useWaveState, WaveVotingState } from "../../../hooks/useWaveState"; +import { WaveWinnersSmall } from "./winners/WaveWinnersSmall"; interface WaveDetailedRightSidebarProps { readonly wave: ApiWave; @@ -19,6 +21,7 @@ const WaveDetailedRightSidebar: React.FC = ({ onDropClick, }) => { const capacitor = useCapacitor(); + const { votingState } = useWaveState(wave); const containerClassName = `${ capacitor.isCapacitor ? "tw-pb-20" : "" @@ -43,7 +46,11 @@ const WaveDetailedRightSidebar: React.FC = ({ } tw-text-iron-500 tw-text-sm tw-overflow-y-auto no-scrollbar lg:tw-scrollbar-thin tw-scrollbar-thin tw-scrollbar-thumb-iron-500 tw-scrollbar-track-iron-800 hover:tw-scrollbar-thumb-iron-300 tw-h-full`} > - + {votingState === WaveVotingState.ENDED ? ( + + ) : ( + + )}
); diff --git a/components/waves/detailed/winners/WaveWinnersSmall.tsx b/components/waves/detailed/winners/WaveWinnersSmall.tsx new file mode 100644 index 000000000..f60e2c850 --- /dev/null +++ b/components/waves/detailed/winners/WaveWinnersSmall.tsx @@ -0,0 +1,311 @@ +import React, { useContext, useState } from "react"; +import { ApiWave } from "../../../../generated/models/ApiWave"; +import { ExtendedDrop } from "../../../../helpers/waves/drop.helpers"; +import { + useWaveDropsLeaderboard, + WaveDropsLeaderboardSortBy, + WaveDropsLeaderboardSortDirection, +} from "../../../../hooks/useWaveDropsLeaderboard"; +import { AuthContext } from "../../../auth/Auth"; +import Link from "next/link"; +import { + formatNumberWithCommas, + getTimeAgoShort, +} from "../../../../helpers/Helpers"; +import { + ImageScale, + getScaledImageUri, +} from "../../../../helpers/image.helpers"; +import WaveDetailedDropContent from "../drops/WaveDetailedDropContent"; + +interface WaveWinnersSmallProps { + readonly wave: ApiWave; + readonly onDropClick: (drop: ExtendedDrop) => void; +} + +const rankColors = { + 1: { + text: "tw-text-[#E8D48A]", + bg: "tw-bg-[#E8D48A]/20", + ring: "tw-ring-[#E8D48A]/40", + shadow: "tw-shadow-[0_4px_12px_rgba(232,212,138,0.2)]", + hover: + "desktop-hover:hover:tw-from-[#E8D48A]/40 desktop-hover:hover:tw-ring-[#E8D48A]/50", + dropShadow: "tw-drop-shadow-[0_2px_3px_rgba(232,212,138,0.4)]", + }, + 2: { + text: "tw-text-[#DDDDDD]", + bg: "tw-bg-[#dddddd]/20", + ring: "tw-ring-[#dddddd]/40", + shadow: "tw-shadow-[0_4px_12px_rgba(221,221,221,0.15)]", + hover: + "desktop-hover:hover:tw-from-[#dddddd]/35 desktop-hover:hover:tw-ring-[#dddddd]/50", + dropShadow: "tw-drop-shadow-[0_2px_3px_rgba(221,221,221,0.4)]", + }, + 3: { + text: "tw-text-[#CD7F32]", + bg: "tw-bg-[#B87333]/20", + ring: "tw-ring-[#CD7F32]/40", + shadow: "tw-shadow-[0_4px_12px_rgba(205,127,50,0.15)]", + hover: + "desktop-hover:hover:tw-from-[#CD7F32]/35 desktop-hover:hover:tw-ring-[#CD7F32]/50", + dropShadow: "tw-drop-shadow-[0_2px_3px_rgba(205,127,50,0.4)]", + }, +} as const; + +const rankGradients: Record = { + 1: "tw-from-[#E8D48A]/30 tw-via-[#D9A962]/30 tw-to-[#E8D48A]/30 desktop-hover:hover:tw-from-[#E8D48A]/40 desktop-hover:hover:tw-via-[#D9A962]/40 desktop-hover:hover:tw-to-[#E8D48A]/40 desktop-hover:hover:tw-shadow-[0_0_48px_rgba(232,212,138,0.15)]", + 2: "tw-from-[#DDDDDD]/30 tw-via-[#C0C0C0]/30 tw-to-[#DDDDDD]/30 desktop-hover:hover:tw-from-[#DDDDDD]/40 desktop-hover:hover:tw-via-[#C0C0C0]/40 desktop-hover:hover:tw-to-[#DDDDDD]/40 desktop-hover:hover:tw-shadow-[0_0_48px_rgba(221,221,221,0.15)]", + 3: "tw-from-[#CD7F32]/30 tw-via-[#B87333]/30 tw-to-[#CD7F32]/30 desktop-hover:hover:tw-from-[#CD7F32]/40 desktop-hover:hover:tw-via-[#B87333]/40 desktop-hover:hover:tw-to-[#CD7F32]/40 desktop-hover:hover:tw-shadow-[0_0_48px_rgba(205,127,50,0.15)]", + default: + "tw-from-iron-800 tw-via-iron-800 tw-to-iron-800 desktop-hover:hover:tw-from-iron-700 desktop-hover:hover:tw-via-iron-700 desktop-hover:hover:tw-to-iron-700", +}; + +const TrophyIcon = () => ( + +); + +export const WaveWinnersSmall: React.FC = ({ + wave, + onDropClick, +}) => { + const { connectedProfile } = useContext(AuthContext); + const { drops, isFetching } = useWaveDropsLeaderboard({ + waveId: wave.id, + connectedProfileHandle: connectedProfile?.profile?.handle, + reverse: false, + dropsSortBy: WaveDropsLeaderboardSortBy.RANK, + sortDirection: WaveDropsLeaderboardSortDirection.ASC, + pollingEnabled: false, + }); + + if (isFetching && !drops.length) { + return ( +
+
+
+ +
+
+ Loading Winners... +
+
+
+ ); + } + + if (!drops.length) { + return ( +
+
+
+ +
+
+ No Winners to Display +
+

+ This wave ended without any submissions +

+
+
+ ); + } + + const DropContent = ({ drop }: { drop: ExtendedDrop }) => { + const [activePartIndex, setActivePartIndex] = useState(0); + return ( +
+ {}} + onDropClick={() => onDropClick(drop)} + onQuoteClick={() => {}} + setLongPressTriggered={() => {}} + /> +
+ + ); + }; + + return ( +
+
+

+ Winners +

+ + {drops.length} submissions + +
+ +
+ {drops.map((drop) => { + const gradientClass = + drop.rank && drop.rank <= 3 + ? rankGradients[drop.rank] + : rankGradients.default; + const rankStyle = + drop.rank && drop.rank <= 3 + ? rankColors[drop.rank as keyof typeof rankColors] + : null; + const hasUserVoted = + drop.context_profile_context?.rating !== undefined && + drop.context_profile_context?.rating !== 0; + const userVote = drop.context_profile_context?.rating ?? 0; + const isNegativeVote = userVote < 0; + + const ratingStyle = rankStyle + ? rankStyle.text + : drop.rating >= 0 + ? "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent" + : "tw-bg-gradient-to-r tw-from-rose-400 tw-to-rose-500 tw-bg-clip-text tw-text-transparent"; + + return ( +
onDropClick(drop)} + className={`tw-group tw-cursor-pointer tw-rounded-2xl tw-bg-gradient-to-b ${gradientClass} tw-p-[1.5px] tw-transition tw-duration-300 tw-ease-out desktop-hover:hover:tw-scale-[1.01]`} + > +
+
+
+
+ {drop.rank && ( +
+ {rankStyle ? ( +
+ + + #{drop.rank} + +
+ ) : ( +
+ #{drop.rank} +
+ )} +
+ )} +
+ +
+
+ + {formatNumberWithCommas(drop.rating)} + + + {drop.wave.voting_credit_type} + +
+
+ + {formatNumberWithCommas(drop.raters_count)} + + + {drop.raters_count === 1 ? "voter" : "voters"} + +
+ + {hasUserVoted && ( +
+ + You: + + + {formatNumberWithCommas(userVote)}{" "} + {drop.wave.voting_credit_type} + +
+ )} +
+
+ +
+ e.stopPropagation()} + className="tw-block tw-flex-shrink-0 desktop-hover:group-hover:tw-opacity-90 tw-transition-opacity" + > + {drop.author.pfp ? ( + + ) : ( +
+ )} + + +
+ e.stopPropagation()} + className="tw-no-underline tw-truncate" + > + + {drop.author.handle} + + + + {getTimeAgoShort(drop.created_at)} + +
+
+ + +
+
+
+ ); + })} +
+
+ ); +}; diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx index ed950f2be..2dcf24a32 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx @@ -46,7 +46,7 @@ export default function WaveWinnersDropHeaderRank({ if (drop.rank === 1) { return ( -
+
#{drop.rank} @@ -57,7 +57,7 @@ export default function WaveWinnersDropHeaderRank({ if (drop.rank === 2) { return ( -
+
#{drop.rank} @@ -68,7 +68,7 @@ export default function WaveWinnersDropHeaderRank({ if (drop.rank === 3) { return ( -
+
#{drop.rank} diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx index 850b25730..f68b0eac0 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx @@ -80,12 +80,15 @@ export default function WaveWinnersDropOutcome({ Rep - - {formatNumberWithCommas(repOutcome.value)} - - - {repOutcome.category} - +
+ + {formatNumberWithCommas(repOutcome.value)} + + + + {repOutcome.category} + +
))} {manualOutcomes.map((outcome, i) => ( diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx index df59ad4d8..63b7b28e8 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumFirst.tsx @@ -21,7 +21,7 @@ export const WaveWinnersPodiumFirst: React.FC = ({ onDropClick, }) => { return ( -
onDropClick(drop)} className="tw-cursor-pointer tw-group" > @@ -77,10 +77,25 @@ export const WaveWinnersPodiumFirst: React.FC = ({ e.stopPropagation()} - className="tw-transition-all tw-no-underline tw-mb-2 tw-mt-2 sm:tw-mt-4 tw-relative tw-text-center" + className="tw-transition-all tw-no-underline tw-mb-2 tw-mt-2 sm:tw-mt-4 tw-relative tw-text-center desktop-hover:hover:tw-text-[#E8D48A] tw-group/link" > - + {drop.author.handle} + @@ -108,7 +123,7 @@ export const WaveWinnersPodiumFirst: React.FC = ({
- +
diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx index 464effd78..1a7319630 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumSecond.tsx @@ -75,11 +75,26 @@ export const WaveWinnersPodiumSecond: React.FC = ( e.stopPropagation()} - className="tw-transition-all tw-no-underline tw-mb-2 tw-mt-4 tw-relative" + className="tw-transition-all tw-no-underline tw-mb-2 tw-mt-2 sm:tw-mt-4 tw-relative tw-text-center desktop-hover:hover:tw-text-[#dddddd] tw-group/link" > -
+ {drop.author.handle} -
+ +
diff --git a/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx b/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx index 7091d11e4..d2159897b 100644 --- a/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx +++ b/components/waves/detailed/winners/podium/WaveWinnersPodiumThird.tsx @@ -75,11 +75,26 @@ export const WaveWinnersPodiumThird: React.FC = ({ e.stopPropagation()} - className="tw-transition-all tw-no-underline tw-mb-2 tw-mt-4 tw-relative" + className="tw-transition-all tw-no-underline tw-mb-2 tw-mt-2 sm:tw-mt-4 tw-relative tw-text-center desktop-hover:hover:tw-text-[#CD7F32] tw-group/link" > -
+ {drop.author.handle} -
+ +
From 1cef2af5fd8742d257c03431d1c5ce06980da406 Mon Sep 17 00:00:00 2001 From: Simo Date: Mon, 30 Dec 2024 15:26:30 +0200 Subject: [PATCH 27/33] wip Signed-off-by: Simo --- components/brain/BrainMobile.tsx | 6 ++-- .../mobile/BrainMobileLeaderboardWrapper.tsx | 23 ++++++++++++ components/brain/mobile/BrainMobileTabs.tsx | 31 +++++++--------- .../my-stream/MyStreamWaveTabsLeaderboard.tsx | 36 +++++++++++++++++++ 4 files changed, 73 insertions(+), 23 deletions(-) create mode 100644 components/brain/mobile/BrainMobileLeaderboardWrapper.tsx create mode 100644 components/brain/my-stream/MyStreamWaveTabsLeaderboard.tsx diff --git a/components/brain/BrainMobile.tsx b/components/brain/BrainMobile.tsx index c1efeefe3..b582427e2 100644 --- a/components/brain/BrainMobile.tsx +++ b/components/brain/BrainMobile.tsx @@ -12,6 +12,7 @@ import BrainMobileAbout from "./mobile/BrainMobileAbout"; import { ExtendedDrop } from "../../helpers/waves/drop.helpers"; import BrainMobileLeaderboard from "./mobile/BrainMobileLeaderboard"; import { useWaveData } from "../../hooks/useWaveData"; +import BrainMobileLeaderboardWrapper from "./mobile/BrainMobileLeaderboardWrapper"; export enum BrainView { DEFAULT = "DEFAULT", @@ -76,10 +77,7 @@ const BrainMobile: React.FC = ({ children }) => { [BrainView.DEFAULT]: children, [BrainView.LEADERBOARD]: isRankWave && !!wave ? ( - + ) : null, }; diff --git a/components/brain/mobile/BrainMobileLeaderboardWrapper.tsx b/components/brain/mobile/BrainMobileLeaderboardWrapper.tsx new file mode 100644 index 000000000..caaffc04b --- /dev/null +++ b/components/brain/mobile/BrainMobileLeaderboardWrapper.tsx @@ -0,0 +1,23 @@ +import React from "react"; +import { ApiWave } from "../../../generated/models/ApiWave"; +import { ExtendedDrop } from "../../../helpers/waves/drop.helpers"; +import BrainMobileLeaderboard from "./BrainMobileLeaderboard"; +import { useWaveState, WaveVotingState } from "../../../hooks/useWaveState"; +import { WaveWinners } from "../../waves/detailed/winners/WaveWinners"; + +interface BrainMobileLeaderboardWrapperProps { + readonly wave: ApiWave; + readonly onDropClick: (drop: ExtendedDrop) => void; +} + +const BrainMobileLeaderboardWrapper: React.FC< + BrainMobileLeaderboardWrapperProps +> = ({ wave, onDropClick }) => { + const { votingState } = useWaveState(wave); + if (votingState === WaveVotingState.ENDED) { + return ; + } + return ; +}; + +export default BrainMobileLeaderboardWrapper; diff --git a/components/brain/mobile/BrainMobileTabs.tsx b/components/brain/mobile/BrainMobileTabs.tsx index b9efccc0a..6c9af2dab 100644 --- a/components/brain/mobile/BrainMobileTabs.tsx +++ b/components/brain/mobile/BrainMobileTabs.tsx @@ -6,6 +6,7 @@ import { useUnreadNotifications } from "../../../hooks/useUnreadNotifications"; import { BrainView } from "../BrainMobile"; import { ApiWaveType } from "../../../generated/models/ApiWaveType"; import { ApiWave } from "../../../generated/models/ApiWave"; +import MyStreamWaveTabsLeaderboard from "../my-stream/MyStreamWaveTabsLeaderboard"; interface BrainMobileTabsProps { readonly activeView: BrainView; @@ -39,8 +40,11 @@ const BrainMobileTabs: React.FC = ({ }; }, [router.events, onViewChange]); - const isLinkActive = router.pathname === "/my-stream" && activeView === BrainView.DEFAULT; - const isNotificationsActive = router.pathname === "/my-stream/notifications" && activeView === BrainView.DEFAULT; + const isLinkActive = + router.pathname === "/my-stream" && activeView === BrainView.DEFAULT; + const isNotificationsActive = + router.pathname === "/my-stream/notifications" && + activeView === BrainView.DEFAULT; const wavesButtonClasses = `tw-border-none tw-no-underline tw-flex tw-justify-center tw-items-center tw-px-3 tw-py-2 tw-gap-2 tw-flex-1 tw-h-9 tw-rounded-lg ${ activeView === BrainView.WAVES ? "tw-bg-iron-800" : "tw-bg-iron-950" @@ -58,14 +62,6 @@ const BrainMobileTabs: React.FC = ({ activeView === BrainView.ABOUT ? "tw-text-iron-300" : "tw-text-iron-400" }`; - const leaderboardButtonClasses = `tw-border-none tw-no-underline tw-flex tw-justify-center tw-items-center tw-px-3 tw-py-2 tw-gap-2 tw-flex-1 tw-h-9 tw-rounded-lg ${ - activeView === BrainView.LEADERBOARD ? "tw-bg-iron-800" : "tw-bg-iron-950" - }`; - - const leaderboardButtonTextClasses = `tw-font-semibold tw-text-xs sm:tw-text-sm tw-whitespace-nowrap ${ - activeView === BrainView.LEADERBOARD ? "tw-text-iron-300" : "tw-text-iron-400" - }`; - const myStreamLinkClasses = `tw-no-underline tw-flex tw-justify-center tw-items-center tw-px-3 tw-py-2 tw-gap-2 tw-flex-1 tw-h-9 tw-rounded-md ${ isLinkActive ? "tw-bg-iron-800" : "tw-bg-iron-950" }`; @@ -130,21 +126,18 @@ const BrainMobileTabs: React.FC = ({ My Stream {isWave() && isRankWave && ( - + )} - - Notifications - + Notifications {haveUnreadNotifications && ( )} diff --git a/components/brain/my-stream/MyStreamWaveTabsLeaderboard.tsx b/components/brain/my-stream/MyStreamWaveTabsLeaderboard.tsx new file mode 100644 index 000000000..888a15a70 --- /dev/null +++ b/components/brain/my-stream/MyStreamWaveTabsLeaderboard.tsx @@ -0,0 +1,36 @@ +import React from "react"; +import { ApiWave } from "../../../generated/models/ApiWave"; +import { BrainView } from "../BrainMobile"; +import { useWaveState, WaveVotingState } from "../../../hooks/useWaveState"; + +interface MyStreamWaveTabsLeaderboardProps { + readonly wave: ApiWave; + readonly activeView: BrainView; + readonly onViewChange: (view: BrainView) => void; +} + +export const MyStreamWaveTabsLeaderboard: React.FC = ({ + wave, + activeView, + onViewChange, +}) => { + const { votingState } = useWaveState(wave); + const leaderboardButtonClasses = `tw-border-none tw-no-underline tw-flex tw-justify-center tw-items-center tw-px-3 tw-py-2 tw-gap-2 tw-flex-1 tw-h-9 tw-rounded-lg ${ + activeView === BrainView.LEADERBOARD ? "tw-bg-iron-800" : "tw-bg-iron-950" + }`; + const leaderboardButtonTextClasses = `tw-font-semibold tw-text-xs sm:tw-text-sm tw-whitespace-nowrap ${ + activeView === BrainView.LEADERBOARD ? "tw-text-iron-300" : "tw-text-iron-400" + }`; + return ( + + ); +}; + +export default MyStreamWaveTabsLeaderboard; From 93373483de361cd528f5ea3b03b80bbf28e933f6 Mon Sep 17 00:00:00 2001 From: Ragne Date: Mon, 30 Dec 2024 19:54:53 +0200 Subject: [PATCH 28/33] wip Signed-off-by: Ragne --- .../WaveDetailedLeaderboardItemOutcomes.tsx | 2 +- .../detailed/winners/WaveWinnersSmall.tsx | 22 +- .../winners/WaveWinnersSmallOutcome.tsx | 211 ++++++++++++++++++ .../podium/WavePodiumItemContentOutcomes.tsx | 40 ++-- 4 files changed, 247 insertions(+), 28 deletions(-) create mode 100644 components/waves/detailed/winners/WaveWinnersSmallOutcome.tsx diff --git a/components/waves/detailed/small-leaderboard/WaveDetailedLeaderboardItemOutcomes.tsx b/components/waves/detailed/small-leaderboard/WaveDetailedLeaderboardItemOutcomes.tsx index bf061593d..3f4c24828 100644 --- a/components/waves/detailed/small-leaderboard/WaveDetailedLeaderboardItemOutcomes.tsx +++ b/components/waves/detailed/small-leaderboard/WaveDetailedLeaderboardItemOutcomes.tsx @@ -214,7 +214,7 @@ export const WaveDetailedLeaderboardItemOutcomes: React.FC< >
- ); }; @@ -195,9 +195,9 @@ export const WaveWinnersSmall: React.FC = ({
onDropClick(drop)} - className={`tw-group tw-cursor-pointer tw-rounded-2xl tw-bg-gradient-to-b ${gradientClass} tw-p-[1.5px] tw-transition tw-duration-300 tw-ease-out desktop-hover:hover:tw-scale-[1.01]`} + className={`tw-group tw-cursor-pointer tw-rounded-2xl tw-bg-gradient-to-b ${gradientClass} tw-p-[1px] tw-transition tw-duration-300 tw-ease-out desktop-hover:hover:tw-scale-[1.01]`} > -
+
@@ -283,7 +283,7 @@ export const WaveWinnersSmall: React.FC = ({ )} -
+
e.stopPropagation()} @@ -293,13 +293,19 @@ export const WaveWinnersSmall: React.FC = ({ {drop.author.handle} + {getTimeAgoShort(drop.created_at)}
- +
+ +
+ +
+
diff --git a/components/waves/detailed/winners/WaveWinnersSmallOutcome.tsx b/components/waves/detailed/winners/WaveWinnersSmallOutcome.tsx new file mode 100644 index 000000000..4b548b4f0 --- /dev/null +++ b/components/waves/detailed/winners/WaveWinnersSmallOutcome.tsx @@ -0,0 +1,211 @@ +import React, { useState, useEffect } from "react"; +import Tippy from "@tippyjs/react"; +import { ApiWave } from "../../../../generated/models/ApiWave"; +import { ExtendedDrop } from "../../../../helpers/waves/drop.helpers"; +import { useDropOutcomes } from "../../../../hooks/drops/useDropOutcomes"; +import { formatNumberWithCommas } from "../../../../helpers/Helpers"; + +interface WaveWinnersSmallOutcomeProps { + readonly drop: ExtendedDrop; + readonly wave: ApiWave; +} + +export const WaveWinnersSmallOutcome: React.FC = ({ + drop, + wave, +}) => { + const [isTouch, setIsTouch] = useState(false); + const [isOpen, setIsOpen] = useState(false); + + useEffect(() => { + setIsTouch("ontouchstart" in window); + }, []); + + const handleClick = (e: React.MouseEvent) => { + if (isTouch) { + e.stopPropagation(); + setIsOpen(!isOpen); + } + }; + + const { + outcomes: { nicOutcomes, repOutcomes, manualOutcomes }, + haveOutcomes, + } = useDropOutcomes({ + drop, + wave, + }); + + if (!haveOutcomes) { + return null; + } + + const tooltipContent = ( +
+
+ + Outcome Details + +
+ {nicOutcomes.map((nicOutcome, i) => ( +
+
+ + + NIC + +
+ + {formatNumberWithCommas(nicOutcome.value)} + +
+ ))} + {repOutcomes.map((repOutcome, i) => ( +
+
+
+ + + Rep + +
+ + {formatNumberWithCommas(repOutcome.value)} + +
+ + {repOutcome.category} + +
+ ))} + {manualOutcomes.map((outcome, i) => ( +
+
+ + + {outcome.description} + +
+
+ ))} +
+
+
+ ); + + return ( + setIsOpen(false)} + > + + + ); +}; \ No newline at end of file diff --git a/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx b/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx index 17dd34606..19347fa3c 100644 --- a/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx +++ b/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx @@ -78,28 +78,30 @@ export const WavePodiumItemContentOutcomes: React.FC< {repOutcomes.map((repOutcome, i) => (
-
- - - Rep +
+
+ + + Rep + +
+ + {formatNumberWithCommas(repOutcome.value)}
- - {formatNumberWithCommas(repOutcome.value)} - {repOutcome.category} From ad4b670486ce2b1b3611ababb855ecbdd33e3aa8 Mon Sep 17 00:00:00 2001 From: Ragne Date: Wed, 1 Jan 2025 15:25:19 +0200 Subject: [PATCH 29/33] wip Signed-off-by: Ragne --- .../mobile/BrainMobileLeaderboardWrapper.tsx | 13 +- .../detailed/leaderboard/WaveLeaderboard.tsx | 4 +- .../waves/detailed/winners/WaveWinners.tsx | 2 +- .../detailed/winners/WaveWinnersSmall.tsx | 225 ++++++++++-------- .../header/WaveWinnersDropHeaderRank.tsx | 69 +++--- .../podium/WavePodiumItemContentOutcomes.tsx | 6 +- .../winners/podium/WaveWinnersPodiumFirst.tsx | 4 +- components/waves/utils/DropThrophyIcon.tsx | 116 ++++----- 8 files changed, 247 insertions(+), 192 deletions(-) diff --git a/components/brain/mobile/BrainMobileLeaderboardWrapper.tsx b/components/brain/mobile/BrainMobileLeaderboardWrapper.tsx index caaffc04b..a34800655 100644 --- a/components/brain/mobile/BrainMobileLeaderboardWrapper.tsx +++ b/components/brain/mobile/BrainMobileLeaderboardWrapper.tsx @@ -4,6 +4,7 @@ import { ExtendedDrop } from "../../../helpers/waves/drop.helpers"; import BrainMobileLeaderboard from "./BrainMobileLeaderboard"; import { useWaveState, WaveVotingState } from "../../../hooks/useWaveState"; import { WaveWinners } from "../../waves/detailed/winners/WaveWinners"; +import useCapacitor from "../../../hooks/useCapacitor"; interface BrainMobileLeaderboardWrapperProps { readonly wave: ApiWave; @@ -14,8 +15,18 @@ const BrainMobileLeaderboardWrapper: React.FC< BrainMobileLeaderboardWrapperProps > = ({ wave, onDropClick }) => { const { votingState } = useWaveState(wave); + const capacitor = useCapacitor(); + + const contentHeight = capacitor.isCapacitor + ? "tw-h-[calc(100vh-20rem)]" + : "tw-h-[calc(100vh-10rem)]"; + if (votingState === WaveVotingState.ENDED) { - return ; + return ( +
+ +
+ ); } return ; }; diff --git a/components/waves/detailed/leaderboard/WaveLeaderboard.tsx b/components/waves/detailed/leaderboard/WaveLeaderboard.tsx index 9ddd46056..0964a3e31 100644 --- a/components/waves/detailed/leaderboard/WaveLeaderboard.tsx +++ b/components/waves/detailed/leaderboard/WaveLeaderboard.tsx @@ -71,7 +71,9 @@ export const WaveLeaderboard: React.FC = ({ {children} {votingState === WaveVotingState.ENDED ? ( - +
+ +
) : ( <> diff --git a/components/waves/detailed/winners/WaveWinners.tsx b/components/waves/detailed/winners/WaveWinners.tsx index 4d0696690..6c97f45b6 100644 --- a/components/waves/detailed/winners/WaveWinners.tsx +++ b/components/waves/detailed/winners/WaveWinners.tsx @@ -14,7 +14,7 @@ export const WaveWinners: React.FC = ({ onDropClick, }) => { return ( -
+
diff --git a/components/waves/detailed/winners/WaveWinnersSmall.tsx b/components/waves/detailed/winners/WaveWinnersSmall.tsx index 9b82be722..9caf08064 100644 --- a/components/waves/detailed/winners/WaveWinnersSmall.tsx +++ b/components/waves/detailed/winners/WaveWinnersSmall.tsx @@ -24,6 +24,10 @@ interface WaveWinnersSmallProps { readonly onDropClick: (drop: ExtendedDrop) => void; } +interface DropContentProps { + readonly drop: ExtendedDrop; +} + const rankColors = { 1: { text: "tw-text-[#E8D48A]", @@ -54,82 +58,72 @@ const rankColors = { }, } as const; -const rankGradients: Record = { - 1: "tw-from-[#E8D48A]/30 tw-via-[#D9A962]/30 tw-to-[#E8D48A]/30 desktop-hover:hover:tw-from-[#E8D48A]/40 desktop-hover:hover:tw-via-[#D9A962]/40 desktop-hover:hover:tw-to-[#E8D48A]/40", - 2: "tw-from-[#DDDDDD]/30 tw-via-[#C0C0C0]/30 tw-to-[#DDDDDD]/30 desktop-hover:hover:tw-from-[#DDDDDD]/40 desktop-hover:hover:tw-via-[#C0C0C0]/40 desktop-hover:hover:tw-to-[#DDDDDD]/40", - 3: "tw-from-[#CD7F32]/30 tw-via-[#B87333]/30 tw-to-[#CD7F32]/30 desktop-hover:hover:tw-from-[#CD7F32]/40 desktop-hover:hover:tw-via-[#B87333]/40 desktop-hover:hover:tw-to-[#CD7F32]/40", +const rankGradients = { + 1: "tw-from-[#E8D48A]/30 tw-via-[#D9A962]/30 tw-to-[#E8D48A]/30 desktop-hover:hover:tw-from-[#E8D48A]/40 desktop-hover:hover:tw-via-[#D9A962]/40 desktop-hover:hover:tw-to-[#E8D48A]/40 desktop-hover:hover:tw-shadow-[0_0_48px_rgba(232,212,138,0.15)]", + 2: "tw-from-[#DDDDDD]/30 tw-via-[#C0C0C0]/30 tw-to-[#DDDDDD]/30 desktop-hover:hover:tw-from-[#DDDDDD]/40 desktop-hover:hover:tw-via-[#C0C0C0]/40 desktop-hover:hover:tw-to-[#DDDDDD]/40 desktop-hover:hover:tw-shadow-[0_0_48px_rgba(221,221,221,0.15)]", + 3: "tw-from-[#CD7F32]/30 tw-via-[#B87333]/30 tw-to-[#CD7F32]/30 desktop-hover:hover:tw-from-[#CD7F32]/40 desktop-hover:hover:tw-via-[#B87333]/40 desktop-hover:hover:tw-to-[#CD7F32]/40 desktop-hover:hover:tw-shadow-[0_0_48px_rgba(205,127,50,0.15)]", default: - "tw-from-iron-800 tw-via-iron-800 tw-to-iron-800 desktop-hover:hover:tw-from-iron-700 desktop-hover:hover:tw-via-iron-700 desktop-hover:hover:tw-to-iron-700", -}; + "tw-from-iron-800/50 tw-via-iron-800/50 tw-to-iron-800/50 hover:tw-from-iron-700/60 hover:tw-via-iron-700/60 hover:tw-to-iron-700/60", +} as const; const TrophyIcon = () => ( ); +const getRankGradientClasses = (rank: number) => { + switch (rank) { + case 1: + return { + gradient: "tw-from-[#E8D48A]/[0.02] tw-via-[#E8D48A]/[0.01] tw-to-transparent", + borderTop: "tw-via-[#E8D48A]/25 tw-to-transparent", + borderSide: "tw-from-[#E8D48A]/30 tw-via-[#E8D48A]/20 tw-to-transparent", + }; + case 2: + return { + gradient: "tw-from-[#DDDDDD]/[0.02] tw-via-[#DDDDDD]/[0.01] tw-to-transparent", + borderTop: "tw-via-[#DDDDDD]/25 tw-to-transparent", + borderSide: "tw-from-[#DDDDDD]/30 tw-via-[#DDDDDD]/20 tw-to-transparent", + }; + case 3: + return { + gradient: "tw-from-[#CD7F32]/[0.02] tw-via-[#CD7F32]/[0.01] tw-to-transparent", + borderTop: "tw-via-[#CD7F32]/25 tw-to-transparent", + borderSide: "tw-from-[#CD7F32]/30 tw-via-[#CD7F32]/20 tw-to-transparent", + }; + default: + return { + gradient: "", + borderTop: "", + borderSide: "", + }; + } +}; + export const WaveWinnersSmall: React.FC = ({ wave, onDropClick, }) => { const { connectedProfile } = useContext(AuthContext); - const { drops, isFetching } = useWaveDropsLeaderboard({ + const { drops } = useWaveDropsLeaderboard({ waveId: wave.id, connectedProfileHandle: connectedProfile?.profile?.handle, - reverse: false, dropsSortBy: WaveDropsLeaderboardSortBy.RANK, sortDirection: WaveDropsLeaderboardSortDirection.ASC, - pollingEnabled: false, + reverse: false, }); - if (isFetching && !drops.length) { - return ( -
-
-
- -
-
- Loading Winners... -
-
-
- ); - } - - if (!drops.length) { + if (!drops?.length) { return ( -
-
-
- -
+
+
No Winners to Display
@@ -141,7 +135,7 @@ export const WaveWinnersSmall: React.FC = ({ ); } - const DropContent = ({ drop }: { drop: ExtendedDrop }) => { + const DropContent: React.FC = ({ drop }) => { const [activePartIndex, setActivePartIndex] = useState(0); return (
@@ -171,17 +165,15 @@ export const WaveWinnersSmall: React.FC = ({
{drops.map((drop) => { - const gradientClass = - drop.rank && drop.rank <= 3 - ? rankGradients[drop.rank] - : rankGradients.default; const rankStyle = drop.rank && drop.rank <= 3 ? rankColors[drop.rank as keyof typeof rankColors] : null; + const hasUserVoted = drop.context_profile_context?.rating !== undefined && drop.context_profile_context?.rating !== 0; + const userVote = drop.context_profile_context?.rating ?? 0; const isNegativeVote = userVote < 0; @@ -191,21 +183,54 @@ export const WaveWinnersSmall: React.FC = ({ ? "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent" : "tw-bg-gradient-to-r tw-from-rose-400 tw-to-rose-500 tw-bg-clip-text tw-text-transparent"; + const userVoteStyle = drop.rank && drop.rank <= 3 && rankStyle + ? rankStyle.text + : isNegativeVote + ? "tw-bg-gradient-to-r tw-from-rose-400 tw-to-rose-500 tw-bg-clip-text tw-text-transparent" + : "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent"; + return (
onDropClick(drop)} - className={`tw-group tw-cursor-pointer tw-rounded-2xl tw-bg-gradient-to-b ${gradientClass} tw-p-[1px] tw-transition tw-duration-300 tw-ease-out desktop-hover:hover:tw-scale-[1.01]`} + className="tw-cursor-pointer tw-group tw-rounded-xl tw-overflow-hidden" > -
-
+
+ {drop.rank && drop.rank <= 3 && rankStyle && ( +
+ {(() => { + const classes = getRankGradientClasses(drop.rank); + return ( + <> +
+
+
+
+
+
+ + ); + })()} +
+ )} +
{drop.rank && (
{rankStyle ? (
= ({
) : ( -
+
#{drop.rank}
)} @@ -249,11 +274,7 @@ export const WaveWinnersSmall: React.FC = ({ You: {formatNumberWithCommas(userVote)}{" "} {drop.wave.voting_credit_type} @@ -262,49 +283,49 @@ export const WaveWinnersSmall: React.FC = ({ )}
+
-
+
+ e.stopPropagation()} + className="tw-block tw-flex-shrink-0 desktop-hover:group-hover:tw-opacity-90 tw-transition-opacity" + > + {drop.author.pfp ? ( + Picture + ) : ( +
+ )} + + +
e.stopPropagation()} - className="tw-block tw-flex-shrink-0 desktop-hover:group-hover:tw-opacity-90 tw-transition-opacity" + className="tw-no-underline tw-truncate" > - {drop.author.pfp ? ( - - ) : ( -
- )} - - -
- e.stopPropagation()} - className="tw-no-underline tw-truncate" - > - - {drop.author.handle} - - - - - {getTimeAgoShort(drop.created_at)} + + {drop.author.handle} -
+ + + + {getTimeAgoShort(drop.created_at)} +
+
-
- -
- -
+
+ +
+
diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx index 2dcf24a32..568451a64 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderRank.tsx @@ -1,5 +1,35 @@ import { ExtendedDrop } from "../../../../../../helpers/waves/drop.helpers"; +const rankColors = { + 1: { + text: "tw-text-[#E8D48A]", + bg: "tw-bg-[#E8D48A]/20", + ring: "tw-ring-[#E8D48A]/40", + shadow: "tw-shadow-[0_4px_12px_rgba(232,212,138,0.2)]", + hover: "desktop-hover:hover:tw-from-[#E8D48A]/40 desktop-hover:hover:tw-ring-[#E8D48A]/50", + dropShadow: "tw-drop-shadow-[0_2px_3px_rgba(232,212,138,0.4)]", + color: "#E8D48A", + }, + 2: { + text: "tw-text-[#DDDDDD]", + bg: "tw-bg-[#dddddd]/20", + ring: "tw-ring-[#dddddd]/40", + shadow: "tw-shadow-[0_4px_12px_rgba(221,221,221,0.15)]", + hover: "desktop-hover:hover:tw-from-[#dddddd]/35 desktop-hover:hover:tw-ring-[#dddddd]/50", + dropShadow: "tw-drop-shadow-[0_2px_3px_rgba(221,221,221,0.4)]", + color: "#DDDDDD", + }, + 3: { + text: "tw-text-[#CD7F32]", + bg: "tw-bg-[#B87333]/20", + ring: "tw-ring-[#CD7F32]/40", + shadow: "tw-shadow-[0_4px_12px_rgba(205,127,50,0.15)]", + hover: "desktop-hover:hover:tw-from-[#CD7F32]/35 desktop-hover:hover:tw-ring-[#CD7F32]/50", + dropShadow: "tw-drop-shadow-[0_2px_3px_rgba(205,127,50,0.4)]", + color: "#CD7F32", + }, +} as const; + interface TrophyIconProps { readonly color: string; } @@ -7,7 +37,7 @@ interface TrophyIconProps { export function TrophyIcon({ color }: TrophyIconProps) { return (
- - - #{drop.rank} - -
- ); - } - - if (drop.rank === 2) { - return ( -
- - - #{drop.rank} - -
- ); - } - - if (drop.rank === 3) { + if (drop.rank && drop.rank <= 3) { + const rankStyle = rankColors[drop.rank as keyof typeof rankColors]; return ( -
- - +
+ + #{drop.rank}
@@ -78,8 +87,8 @@ export default function WaveWinnersDropHeaderRank({ } return ( -
- #{drop.rank} +
+ #{drop.rank}
); } diff --git a/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx b/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx index 19347fa3c..c265c7ecc 100644 --- a/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx +++ b/components/waves/detailed/winners/podium/WavePodiumItemContentOutcomes.tsx @@ -150,7 +150,11 @@ export const WavePodiumItemContentOutcomes: React.FC< className="tw-backdrop-blur-xl" >
- +
e.stopPropagation()}> + +
diff --git a/components/waves/utils/DropThrophyIcon.tsx b/components/waves/utils/DropThrophyIcon.tsx index a7be2704c..418bc4469 100644 --- a/components/waves/utils/DropThrophyIcon.tsx +++ b/components/waves/utils/DropThrophyIcon.tsx @@ -1,6 +1,56 @@ import React from "react"; import { formatNumberWithCommas } from "../../../helpers/Helpers"; +const rankColors = { + 1: { + text: "tw-text-[#E8D48A]", + bg: "tw-bg-[#E8D48A]/20", + ring: "tw-ring-[#E8D48A]/40", + shadow: "tw-shadow-[0_4px_12px_rgba(232,212,138,0.2)]", + hover: "desktop-hover:hover:tw-from-[#E8D48A]/40 desktop-hover:hover:tw-ring-[#E8D48A]/50", + dropShadow: "tw-drop-shadow-[0_2px_3px_rgba(232,212,138,0.4)]", + color: "#E8D48A", + }, + 2: { + text: "tw-text-[#DDDDDD]", + bg: "tw-bg-[#dddddd]/20", + ring: "tw-ring-[#dddddd]/40", + shadow: "tw-shadow-[0_4px_12px_rgba(221,221,221,0.15)]", + hover: "desktop-hover:hover:tw-from-[#dddddd]/35 desktop-hover:hover:tw-ring-[#dddddd]/50", + dropShadow: "tw-drop-shadow-[0_2px_3px_rgba(221,221,221,0.4)]", + color: "#DDDDDD", + }, + 3: { + text: "tw-text-[#CD7F32]", + bg: "tw-bg-[#B87333]/20", + ring: "tw-ring-[#CD7F32]/40", + shadow: "tw-shadow-[0_4px_12px_rgba(205,127,50,0.15)]", + hover: "desktop-hover:hover:tw-from-[#CD7F32]/35 desktop-hover:hover:tw-ring-[#CD7F32]/50", + dropShadow: "tw-drop-shadow-[0_2px_3px_rgba(205,127,50,0.4)]", + color: "#CD7F32", + }, +} as const; + +interface TrophyIconProps { + readonly color: string; +} + +function TrophyIcon({ color }: TrophyIconProps) { + return ( + + ); +} + interface DropTrophyIconProps { readonly rank: number | null; } @@ -8,13 +58,13 @@ interface DropTrophyIconProps { export const DropTrophyIcon: React.FC = ({ rank }) => { if (!rank) { return ( -
+
@@ -22,66 +72,22 @@ export const DropTrophyIcon: React.FC = ({ rank }) => {
); } - if (rank === 1) { - return ( -
- - {rank} -
- ); - } - - if (rank === 2) { - return ( -
- - {rank} -
- ); - } - if (rank === 3) { + if (rank && rank <= 3) { + const rankStyle = rankColors[rank as keyof typeof rankColors]; return ( -
- - {rank} +
+ + + #{rank} +
); } return ( -
- #{formatNumberWithCommas(rank)} +
+ #{formatNumberWithCommas(rank)}
); }; From cf3eba5799dec0c85e8491b30e1d5f7a6582af26 Mon Sep 17 00:00:00 2001 From: Ragne Date: Wed, 1 Jan 2025 15:28:01 +0200 Subject: [PATCH 30/33] wip Signed-off-by: Ragne --- .../detailed/winners/WaveWinnersSmall.tsx | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/components/waves/detailed/winners/WaveWinnersSmall.tsx b/components/waves/detailed/winners/WaveWinnersSmall.tsx index 9caf08064..a87128a68 100644 --- a/components/waves/detailed/winners/WaveWinnersSmall.tsx +++ b/components/waves/detailed/winners/WaveWinnersSmall.tsx @@ -82,21 +82,27 @@ const getRankGradientClasses = (rank: number) => { switch (rank) { case 1: return { - gradient: "tw-from-[#E8D48A]/[0.02] tw-via-[#E8D48A]/[0.01] tw-to-transparent", + gradient: + "tw-from-[#E8D48A]/[0.02] tw-via-[#E8D48A]/[0.01] tw-to-transparent", borderTop: "tw-via-[#E8D48A]/25 tw-to-transparent", - borderSide: "tw-from-[#E8D48A]/30 tw-via-[#E8D48A]/20 tw-to-transparent", + borderSide: + "tw-from-[#E8D48A]/30 tw-via-[#E8D48A]/20 tw-to-transparent", }; case 2: return { - gradient: "tw-from-[#DDDDDD]/[0.02] tw-via-[#DDDDDD]/[0.01] tw-to-transparent", + gradient: + "tw-from-[#DDDDDD]/[0.02] tw-via-[#DDDDDD]/[0.01] tw-to-transparent", borderTop: "tw-via-[#DDDDDD]/25 tw-to-transparent", - borderSide: "tw-from-[#DDDDDD]/30 tw-via-[#DDDDDD]/20 tw-to-transparent", + borderSide: + "tw-from-[#DDDDDD]/30 tw-via-[#DDDDDD]/20 tw-to-transparent", }; case 3: return { - gradient: "tw-from-[#CD7F32]/[0.02] tw-via-[#CD7F32]/[0.01] tw-to-transparent", + gradient: + "tw-from-[#CD7F32]/[0.02] tw-via-[#CD7F32]/[0.01] tw-to-transparent", borderTop: "tw-via-[#CD7F32]/25 tw-to-transparent", - borderSide: "tw-from-[#CD7F32]/30 tw-via-[#CD7F32]/20 tw-to-transparent", + borderSide: + "tw-from-[#CD7F32]/30 tw-via-[#CD7F32]/20 tw-to-transparent", }; default: return { @@ -183,19 +189,20 @@ export const WaveWinnersSmall: React.FC = ({ ? "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent" : "tw-bg-gradient-to-r tw-from-rose-400 tw-to-rose-500 tw-bg-clip-text tw-text-transparent"; - const userVoteStyle = drop.rank && drop.rank <= 3 && rankStyle - ? rankStyle.text - : isNegativeVote - ? "tw-bg-gradient-to-r tw-from-rose-400 tw-to-rose-500 tw-bg-clip-text tw-text-transparent" - : "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent"; + const userVoteStyle = + drop.rank && drop.rank <= 3 && rankStyle + ? rankStyle.text + : isNegativeVote + ? "tw-bg-gradient-to-r tw-from-rose-400 tw-to-rose-500 tw-bg-clip-text tw-text-transparent" + : "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent"; return (
onDropClick(drop)} - className="tw-cursor-pointer tw-group tw-rounded-xl tw-overflow-hidden" + className="tw-cursor-pointer tw-group tw-rounded-xl tw-overflow-hidden desktop-hover:hover:tw-scale-[1.01] tw-transform tw-transition-all tw-duration-300 tw-ease-out" > -
+
{drop.rank && drop.rank <= 3 && rankStyle && (
{(() => { @@ -218,7 +225,7 @@ export const WaveWinnersSmall: React.FC = ({ className={`tw-absolute tw-inset-y-0 tw-left-0 tw-w-px tw-bg-gradient-to-b tw-from-transparent tw-via-${classes.borderSide} tw-to-transparent`} />
- + ); })()}
From 61d84b046fee8a16cd3e124bb963fc3534a0b471 Mon Sep 17 00:00:00 2001 From: Ragne Date: Wed, 1 Jan 2025 16:00:35 +0200 Subject: [PATCH 31/33] wip Signed-off-by: Ragne --- .../WaveDetailedLeaderboardDefaultDrop.tsx | 2 +- .../WaveDetailedLeaderboardTopThreeDrop.tsx | 8 ++++---- components/waves/detailed/winners/WaveWinnersSmall.tsx | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/waves/detailed/small-leaderboard/WaveDetailedLeaderboardDefaultDrop.tsx b/components/waves/detailed/small-leaderboard/WaveDetailedLeaderboardDefaultDrop.tsx index 89fe8e87a..acf5ebb52 100644 --- a/components/waves/detailed/small-leaderboard/WaveDetailedLeaderboardDefaultDrop.tsx +++ b/components/waves/detailed/small-leaderboard/WaveDetailedLeaderboardDefaultDrop.tsx @@ -43,7 +43,7 @@ export const WaveDetailedLeaderboardDefaultDrop: React.FC<
-
+
{drop.rank ? ( #{drop.rank} diff --git a/components/waves/detailed/small-leaderboard/WaveDetailedLeaderboardTopThreeDrop.tsx b/components/waves/detailed/small-leaderboard/WaveDetailedLeaderboardTopThreeDrop.tsx index 877a6397c..fe4e9238b 100644 --- a/components/waves/detailed/small-leaderboard/WaveDetailedLeaderboardTopThreeDrop.tsx +++ b/components/waves/detailed/small-leaderboard/WaveDetailedLeaderboardTopThreeDrop.tsx @@ -54,7 +54,7 @@ export const WaveDetailedLeaderboardTopThreeDrop: React.FC< const thropyIcon = (rank: number | null) => { if (rank === 1) { return ( -
+
+
+
) : (
= ({
-
+
From f1694fad1c6df12d98a1bc409ffddb8f8aa3bac0 Mon Sep 17 00:00:00 2001 From: Simo Date: Thu, 2 Jan 2025 07:40:19 +0200 Subject: [PATCH 32/33] wip Signed-off-by: Simo --- .../brain/right-sidebar/WaveContent.tsx | 11 +- .../waves/detailed/WaveDetailedMobile.tsx | 83 ++-- .../detailed/winners/WaveWinnersSmall.tsx | 442 +++++++++--------- 3 files changed, 268 insertions(+), 268 deletions(-) diff --git a/components/brain/right-sidebar/WaveContent.tsx b/components/brain/right-sidebar/WaveContent.tsx index ff0834d10..11ccd114d 100644 --- a/components/brain/right-sidebar/WaveContent.tsx +++ b/components/brain/right-sidebar/WaveContent.tsx @@ -11,6 +11,7 @@ import BrainRightSidebarContent from "./BrainRightSidebarContent"; import BrainRightSidebarFollowers from "./BrainRightSidebarFollowers"; import { Mode, SidebarTab } from "./BrainRightSidebar"; import { useWaveState, WaveVotingState } from "../../../hooks/useWaveState"; +import { useEffect } from "react"; interface WaveContentProps { readonly wave: ApiWave; @@ -38,7 +39,10 @@ export const WaveContent: React.FC = ({ const options = [ { key: SidebarTab.ABOUT, label: "About" }, - { key: SidebarTab.LEADERBOARD, label: hasVotingEnded ? "Winners" : "Leaderboard" }, + { + key: SidebarTab.LEADERBOARD, + label: hasVotingEnded ? "Winners" : "Leaderboard", + }, ] as const; if (!isRankWave) { @@ -95,7 +99,10 @@ export const WaveContent: React.FC = ({ {hasVotingEnded ? ( ) : ( - + )}
)} diff --git a/components/waves/detailed/WaveDetailedMobile.tsx b/components/waves/detailed/WaveDetailedMobile.tsx index 6b5342336..28694fb74 100644 --- a/components/waves/detailed/WaveDetailedMobile.tsx +++ b/components/waves/detailed/WaveDetailedMobile.tsx @@ -1,5 +1,5 @@ import { ApiWave } from "../../../generated/models/ApiWave"; -import { useContext, useEffect, useState } from "react"; +import { useCallback, useContext, useEffect, useMemo, useState } from "react"; import { AuthContext } from "../../auth/Auth"; import { WaveDetailedView } from "./WaveDetailed"; import WaveDetailedMobileAbout from "./WaveDetailedMobileAbout"; @@ -12,7 +12,6 @@ import { ApiWaveType } from "../../../generated/models/ApiWaveType"; import { WaveOutcome } from "./outcome/WaveOutcome"; import { useWaveState, WaveVotingState } from "../../../hooks/useWaveState"; - interface WaveDetailedMobileProps { readonly wave: ApiWave; readonly setView: (view: WaveDetailedView) => void; @@ -47,47 +46,27 @@ const WaveDetailedMobile: React.FC = ({ WaveDetailedMobileView.CHAT ); - const [forceRender, setForceRender] = useState(0); - - useEffect(() => { - if (activeDrop) { - setForceRender((prev) => prev + 1); - } - }, [activeDrop]); - - const getIsAuthorAndNotProxy = () => + const getIsAuthorAndNotProxy = useCallback(() => connectedProfile?.profile?.handle === wave.author.handle && - !activeProfileProxy; - - const [isAuthorAndNotProxy, setIsAuthorAndNotProxy] = useState( - getIsAuthorAndNotProxy() + !activeProfileProxy, [connectedProfile?.profile?.handle, wave.author.handle, activeProfileProxy] ); - useEffect( - () => setIsAuthorAndNotProxy(getIsAuthorAndNotProxy()), - [connectedProfile, wave] + const isAuthorAndNotProxy = useMemo(() => + getIsAuthorAndNotProxy(), + [getIsAuthorAndNotProxy] ); - const getShowRequiredMetadata = () => - isAuthorAndNotProxy || !!wave.participation.required_metadata.length; - - const [showRequiredMetadata, setShowRequiredMetadata] = useState( - getShowRequiredMetadata() + const showRequiredMetadata = useMemo(() => + isAuthorAndNotProxy || !!wave.participation.required_metadata.length, + [isAuthorAndNotProxy, wave.participation.required_metadata.length] ); - const getShowRequiredTypes = () => - isAuthorAndNotProxy || !!wave.participation.required_media.length; - - const [showRequiredTypes, setShowRequiredTypes] = useState( - getShowRequiredTypes() + const showRequiredTypes = useMemo(() => + isAuthorAndNotProxy || !!wave.participation.required_media.length, + [isAuthorAndNotProxy, wave.participation.required_media.length] ); - useEffect(() => { - setShowRequiredMetadata(getShowRequiredMetadata()); - setShowRequiredTypes(getShowRequiredTypes()); - }, [wave, isAuthorAndNotProxy]); - - const handleWaveChange = (newWave: ApiWave) => { + const handleWaveChange = useCallback((newWave: ApiWave) => { setIsLoading(true); onWaveChange(newWave); setActiveView(WaveDetailedMobileView.CHAT); @@ -95,9 +74,9 @@ const WaveDetailedMobile: React.FC = ({ setTimeout(() => { setIsLoading(false); }, 300); - }; + }, [onWaveChange, setView, setIsLoading]); - const components: Record = { + const components = useMemo(() => ({ [WaveDetailedMobileView.CHAT]: ( = ({ /> ), [WaveDetailedMobileView.OUTCOME]: , - }; + }), [wave, setActiveDrop, showRequiredMetadata, showRequiredTypes, setView, handleWaveChange, setIsLoading]); if (!showWaves) { return null; @@ -134,6 +113,21 @@ const WaveDetailedMobile: React.FC = ({ className="tailwind-scope tw-bg-black tw-relative" style={{ paddingBottom: "env(safe-area-inset-bottom)" }} > + + {activeDrop && ( + + setActiveDrop(null)} /> + + )} +
); }; diff --git a/components/waves/detailed/winners/WaveWinnersSmall.tsx b/components/waves/detailed/winners/WaveWinnersSmall.tsx index 200042952..e89819cd8 100644 --- a/components/waves/detailed/winners/WaveWinnersSmall.tsx +++ b/components/waves/detailed/winners/WaveWinnersSmall.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useState } from "react"; +import React, { useContext, useState, memo, useCallback } from "react"; import { ApiWave } from "../../../../generated/models/ApiWave"; import { ExtendedDrop } from "../../../../helpers/waves/drop.helpers"; import { @@ -26,6 +26,7 @@ interface WaveWinnersSmallProps { interface DropContentProps { readonly drop: ExtendedDrop; + readonly onDropClick: (drop: ExtendedDrop) => void; } const rankColors = { @@ -66,7 +67,7 @@ const rankGradients = { "tw-from-iron-800/50 tw-via-iron-800/50 tw-to-iron-800/50 hover:tw-from-iron-700/60 hover:tw-via-iron-700/60 hover:tw-to-iron-700/60", } as const; -const TrophyIcon = () => ( +const TrophyIcon = memo(() => ( -); +)); +TrophyIcon.displayName = "TrophyIcon"; const getRankGradientClasses = (rank: number) => { switch (rank) { @@ -113,233 +115,245 @@ const getRankGradientClasses = (rank: number) => { } }; -export const WaveWinnersSmall: React.FC = ({ - wave, - onDropClick, -}) => { - const { connectedProfile } = useContext(AuthContext); - const { drops } = useWaveDropsLeaderboard({ - waveId: wave.id, - connectedProfileHandle: connectedProfile?.profile?.handle, - dropsSortBy: WaveDropsLeaderboardSortBy.RANK, - sortDirection: WaveDropsLeaderboardSortDirection.ASC, - reverse: false, - }); - - if (!drops?.length) { - return ( -
-
-
- No Winners to Display -
-

- This wave ended without any submissions -

-
-
- ); - } +const DropContent = memo(({ drop, onDropClick }) => { + const [activePartIndex, setActivePartIndex] = useState(0); - const DropContent: React.FC = ({ drop }) => { - const [activePartIndex, setActivePartIndex] = useState(0); - return ( -
- {}} - onDropClick={() => onDropClick(drop)} - onQuoteClick={() => {}} - setLongPressTriggered={() => {}} - /> -
- ); - }; + const handleDropClick = useCallback(() => { + onDropClick(drop); + }, [drop, onDropClick]); return ( -
-
-

- Winners -

- - {drops.length} submissions - -
- -
- {drops.map((drop) => { - const rankStyle = - drop.rank && drop.rank <= 3 - ? rankColors[drop.rank as keyof typeof rankColors] - : null; - - const hasUserVoted = - drop.context_profile_context?.rating !== undefined && - drop.context_profile_context?.rating !== 0; +
+ {}} + onDropClick={handleDropClick} + onQuoteClick={() => {}} + setLongPressTriggered={() => {}} + /> +
+ ); +}); +DropContent.displayName = "DropContent"; - const userVote = drop.context_profile_context?.rating ?? 0; - const isNegativeVote = userVote < 0; +const WaveWinnerItem = memo<{ + drop: ExtendedDrop; + onDropClick: (drop: ExtendedDrop) => void; + wave: ApiWave; +}>(({ drop, onDropClick, wave }) => { + const rankStyle = + drop.rank && drop.rank <= 3 + ? rankColors[drop.rank as keyof typeof rankColors] + : null; - const ratingStyle = rankStyle - ? rankStyle.text - : drop.rating >= 0 - ? "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent" - : "tw-bg-gradient-to-r tw-from-rose-400 tw-to-rose-500 tw-bg-clip-text tw-text-transparent"; + const hasUserVoted = + drop.context_profile_context?.rating !== undefined && + drop.context_profile_context?.rating !== 0; - const userVoteStyle = - drop.rank && drop.rank <= 3 && rankStyle - ? rankStyle.text - : isNegativeVote - ? "tw-bg-gradient-to-r tw-from-rose-400 tw-to-rose-500 tw-bg-clip-text tw-text-transparent" - : "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent"; + const userVote = drop.context_profile_context?.rating ?? 0; + const isNegativeVote = userVote < 0; - return ( -
onDropClick(drop)} - className="tw-cursor-pointer tw-group tw-rounded-xl tw-overflow-hidden desktop-hover:hover:tw-scale-[1.01] tw-transform tw-transition-all tw-duration-300 tw-ease-out" - > -
- {drop.rank && drop.rank <= 3 && rankStyle && ( -
- {(() => { - const classes = getRankGradientClasses(drop.rank); - return ( - <> -
-
-
-
-
-
- - ); - })()} -
- )} -
-
-
- {drop.rank && ( -
- {rankStyle ? ( -
- - - #{drop.rank} - -
- ) : ( -
- #{drop.rank} -
- )} -
- )} -
+ const ratingStyle = rankStyle + ? rankStyle.text + : drop.rating >= 0 + ? "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent" + : "tw-bg-gradient-to-r tw-from-rose-400 tw-to-rose-500 tw-bg-clip-text tw-text-transparent"; -
-
- - {formatNumberWithCommas(drop.rating)} - - - {drop.wave.voting_credit_type} - -
-
- - {formatNumberWithCommas(drop.raters_count)} - - - {drop.raters_count === 1 ? "voter" : "voters"} - -
+ const userVoteStyle = + drop.rank && drop.rank <= 3 && rankStyle + ? rankStyle.text + : isNegativeVote + ? "tw-bg-gradient-to-r tw-from-rose-400 tw-to-rose-500 tw-bg-clip-text tw-text-transparent" + : "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent"; - {hasUserVoted && ( -
- - You: - - - {formatNumberWithCommas(userVote)}{" "} - {drop.wave.voting_credit_type} - -
- )} -
-
-
+ const handleDropClick = useCallback(() => { + onDropClick(drop); + }, [drop, onDropClick]); -
- e.stopPropagation()} - className="tw-block tw-flex-shrink-0 desktop-hover:group-hover:tw-opacity-90 tw-transition-opacity" - > - {drop.author.pfp ? ( - Picture - ) : ( -
- )} - - -
- e.stopPropagation()} - className="tw-no-underline tw-truncate" + return ( +
+
+ {drop.rank && drop.rank <= 3 && rankStyle && ( +
+ {(() => { + const classes = getRankGradientClasses(drop.rank); + return ( + <> +
+
+
+
+
+
+ + ); + })()} +
+ )} +
+
+
+ {drop.rank && ( +
+ {rankStyle ? ( +
- - {drop.author.handle} + + + #{drop.rank} - - - - {getTimeAgoShort(drop.created_at)} - -
+
+ ) : ( +
+ #{drop.rank} +
+ )}
+ )} +
-
- -
- -
-
+
+
+ + {formatNumberWithCommas(drop.rating)} + + + {drop.wave.voting_credit_type} +
+
+ + {formatNumberWithCommas(drop.raters_count)} + + + {drop.raters_count === 1 ? "voter" : "voters"} + +
+ + {hasUserVoted && ( +
+ You: + + {formatNumberWithCommas(userVote)}{" "} + {drop.wave.voting_credit_type} + +
+ )}
- ); - })} +
+
+ +
+ e.stopPropagation()} + className="tw-block tw-flex-shrink-0 desktop-hover:group-hover:tw-opacity-90 tw-transition-opacity" + > + {drop.author.pfp ? ( + Picture + ) : ( +
+ )} + + +
+ e.stopPropagation()} + className="tw-no-underline tw-truncate" + > + + {drop.author.handle} + + + + + {getTimeAgoShort(drop.created_at)} + +
+
+ +
+ +
+ +
+
); -}; +}); +WaveWinnerItem.displayName = "WaveWinnerItem"; + +export const WaveWinnersSmall = memo( + ({ wave, onDropClick }) => { + const { connectedProfile } = useContext(AuthContext); + const { drops } = useWaveDropsLeaderboard({ + waveId: wave.id, + connectedProfileHandle: connectedProfile?.profile?.handle, + dropsSortBy: WaveDropsLeaderboardSortBy.RANK, + sortDirection: WaveDropsLeaderboardSortDirection.ASC, + reverse: false, + }); + + if (!drops?.length) { + return ( +
+
+
+ No Winners to Display +
+

+ This wave ended without any submissions +

+
+
+ ); + } + + return ( +
+
+

+ Winners +

+
+ +
+ {drops.map((drop) => ( + + ))} +
+
+ ); + } +); +WaveWinnersSmall.displayName = "WaveWinnersSmall"; From 0ada04c09a95123a9886bcfd62b377971a2c579f Mon Sep 17 00:00:00 2001 From: Simo Date: Thu, 2 Jan 2025 08:02:31 +0200 Subject: [PATCH 33/33] wip Signed-off-by: Simo --- components/auth/Auth.tsx | 3 +- components/brain/BrainMobile.tsx | 1 - components/brain/my-stream/MyStreamWave.tsx | 40 +------ .../brain/my-stream/MyStreamWaveViews.tsx | 2 +- .../brain/right-sidebar/WaveContent.tsx | 1 - .../waves/detailed/WaveDetailedMobile.tsx | 108 ++++++++++-------- .../header/WaveleaderboardDropRaters.tsx | 17 ++- .../detailed/winners/WaveWinnersSmall.tsx | 37 ++++-- .../winners/drops/WaveWinnersDrops.tsx | 8 +- .../WaveWinnersDropHeaderTotalVotes.tsx | 18 ++- .../header/WaveWinnersDropHeaderVoters.tsx | 19 ++- .../drops/header/WaveWinnersDropOutcome.tsx | 2 - 12 files changed, 133 insertions(+), 123 deletions(-) diff --git a/components/auth/Auth.tsx b/components/auth/Auth.tsx index b801142dc..8012824f0 100644 --- a/components/auth/Auth.tsx +++ b/components/auth/Auth.tsx @@ -66,8 +66,7 @@ type AuthContextType = { readonly title: string; }; -// TODO: change it back to 10 -export const WAVES_MIN_ACCESS_LEVEL = 0; +export const WAVES_MIN_ACCESS_LEVEL = 10; const DEFAULT_TITLE = "6529 SEIZE"; diff --git a/components/brain/BrainMobile.tsx b/components/brain/BrainMobile.tsx index b582427e2..4edda852c 100644 --- a/components/brain/BrainMobile.tsx +++ b/components/brain/BrainMobile.tsx @@ -10,7 +10,6 @@ import { commonApiFetch } from "../../services/api/common-api"; import BrainDesktopDrop from "./BrainDesktopDrop"; import BrainMobileAbout from "./mobile/BrainMobileAbout"; import { ExtendedDrop } from "../../helpers/waves/drop.helpers"; -import BrainMobileLeaderboard from "./mobile/BrainMobileLeaderboard"; import { useWaveData } from "../../hooks/useWaveData"; import BrainMobileLeaderboardWrapper from "./mobile/BrainMobileLeaderboardWrapper"; diff --git a/components/brain/my-stream/MyStreamWave.tsx b/components/brain/my-stream/MyStreamWave.tsx index 5ab3e8941..6e8fe4a36 100644 --- a/components/brain/my-stream/MyStreamWave.tsx +++ b/components/brain/my-stream/MyStreamWave.tsx @@ -1,19 +1,6 @@ -import React, { useMemo, useState } from "react"; -import WaveDropsAll from "../../waves/detailed/drops/WaveDropsAll"; -import { CreateDropWaveWrapper, CreateDropWaveWrapperContext } from "../../waves/detailed/CreateDropWaveWrapper"; -import PrivilegedDropCreator, { - DropMode, -} from "../../waves/detailed/PrivilegedDropCreator"; -import useCapacitor from "../../../hooks/useCapacitor"; -import { ActiveDropAction, ActiveDropState } from "../../waves/detailed/chat/WaveChat"; -import { ApiDrop } from "../../../generated/models/ObjectSerializer"; +import React from "react"; import { ExtendedDrop } from "../../../helpers/waves/drop.helpers"; import MyStreamWaveChat from "./MyStreamWaveChat"; -import MyStreamWaveTabs, { MyStreamWaveTab } from "./MyStreamWaveTabs"; -import { useWaveData } from "../../../hooks/useWaveData"; -import MyStreamWaveViews from "./MyStreamWaveViews"; -import { useWaveState } from "../../../hooks/useWaveState"; -import { ApiWaveType } from "../../../generated/models/ObjectSerializer"; interface MyStreamWaveProps { readonly waveId: string; @@ -22,31 +9,6 @@ interface MyStreamWaveProps { const MyStreamWave: React.FC = ({ waveId, onDropClick }) => { return ; - // const [activeTab, setActiveTab] = useState( - // MyStreamWaveTab.CHAT - // ); - // const { data: wave } = useWaveData(waveId); - // const isDropsWave = wave?.wave.type !== ApiWaveType.Chat; - // if (!wave) { - // return null; - // } - - // return ( - //
- // {isDropsWave && ( - // - // )} - // - //
- // ); }; export default MyStreamWave; diff --git a/components/brain/my-stream/MyStreamWaveViews.tsx b/components/brain/my-stream/MyStreamWaveViews.tsx index 63aea07ec..1a254bdac 100644 --- a/components/brain/my-stream/MyStreamWaveViews.tsx +++ b/components/brain/my-stream/MyStreamWaveViews.tsx @@ -19,7 +19,7 @@ const MyStreamWaveViews: React.FC = ({ }) => { const components: Record = { [MyStreamWaveTab.CHAT]: ( - + ), [MyStreamWaveTab.LEADERBOARD]: ( diff --git a/components/brain/right-sidebar/WaveContent.tsx b/components/brain/right-sidebar/WaveContent.tsx index 11ccd114d..a00773cb4 100644 --- a/components/brain/right-sidebar/WaveContent.tsx +++ b/components/brain/right-sidebar/WaveContent.tsx @@ -11,7 +11,6 @@ import BrainRightSidebarContent from "./BrainRightSidebarContent"; import BrainRightSidebarFollowers from "./BrainRightSidebarFollowers"; import { Mode, SidebarTab } from "./BrainRightSidebar"; import { useWaveState, WaveVotingState } from "../../../hooks/useWaveState"; -import { useEffect } from "react"; interface WaveContentProps { readonly wave: ApiWave; diff --git a/components/waves/detailed/WaveDetailedMobile.tsx b/components/waves/detailed/WaveDetailedMobile.tsx index 28694fb74..6f6a740d3 100644 --- a/components/waves/detailed/WaveDetailedMobile.tsx +++ b/components/waves/detailed/WaveDetailedMobile.tsx @@ -1,5 +1,5 @@ import { ApiWave } from "../../../generated/models/ApiWave"; -import { useCallback, useContext, useEffect, useMemo, useState } from "react"; +import { useCallback, useContext, useMemo, useState } from "react"; import { AuthContext } from "../../auth/Auth"; import { WaveDetailedView } from "./WaveDetailed"; import WaveDetailedMobileAbout from "./WaveDetailedMobileAbout"; @@ -46,63 +46,79 @@ const WaveDetailedMobile: React.FC = ({ WaveDetailedMobileView.CHAT ); - const getIsAuthorAndNotProxy = useCallback(() => - connectedProfile?.profile?.handle === wave.author.handle && - !activeProfileProxy, [connectedProfile?.profile?.handle, wave.author.handle, activeProfileProxy] + const getIsAuthorAndNotProxy = useCallback( + () => + connectedProfile?.profile?.handle === wave.author.handle && + !activeProfileProxy, + [connectedProfile?.profile?.handle, wave.author.handle, activeProfileProxy] ); - const isAuthorAndNotProxy = useMemo(() => - getIsAuthorAndNotProxy(), + const isAuthorAndNotProxy = useMemo( + () => getIsAuthorAndNotProxy(), [getIsAuthorAndNotProxy] ); - const showRequiredMetadata = useMemo(() => - isAuthorAndNotProxy || !!wave.participation.required_metadata.length, + const showRequiredMetadata = useMemo( + () => isAuthorAndNotProxy || !!wave.participation.required_metadata.length, [isAuthorAndNotProxy, wave.participation.required_metadata.length] ); - const showRequiredTypes = useMemo(() => - isAuthorAndNotProxy || !!wave.participation.required_media.length, + const showRequiredTypes = useMemo( + () => isAuthorAndNotProxy || !!wave.participation.required_media.length, [isAuthorAndNotProxy, wave.participation.required_media.length] ); - const handleWaveChange = useCallback((newWave: ApiWave) => { - setIsLoading(true); - onWaveChange(newWave); - setActiveView(WaveDetailedMobileView.CHAT); - setView(WaveDetailedView.CHAT); - setTimeout(() => { - setIsLoading(false); - }, 300); - }, [onWaveChange, setView, setIsLoading]); + const handleWaveChange = useCallback( + (newWave: ApiWave) => { + setIsLoading(true); + onWaveChange(newWave); + setActiveView(WaveDetailedMobileView.CHAT); + setView(WaveDetailedView.CHAT); + setTimeout(() => { + setIsLoading(false); + }, 300); + }, + [onWaveChange, setView, setIsLoading] + ); - const components = useMemo(() => ({ - [WaveDetailedMobileView.CHAT]: ( - {}} - onDropClick={setActiveDrop} - /> - ), - [WaveDetailedMobileView.LEADERBOARD]: ( - -
-
- ), - [WaveDetailedMobileView.ABOUT]: ( - - ), - [WaveDetailedMobileView.OUTCOME]: , - }), [wave, setActiveDrop, showRequiredMetadata, showRequiredTypes, setView, handleWaveChange, setIsLoading]); + const components = useMemo( + () => ({ + [WaveDetailedMobileView.CHAT]: ( + {}} + onDropClick={setActiveDrop} + /> + ), + [WaveDetailedMobileView.LEADERBOARD]: ( + +
+
+ ), + [WaveDetailedMobileView.ABOUT]: ( + + ), + [WaveDetailedMobileView.OUTCOME]: , + }), + [ + wave, + setActiveDrop, + showRequiredMetadata, + showRequiredTypes, + setView, + handleWaveChange, + setIsLoading, + ] + ); if (!showWaves) { return null; diff --git a/components/waves/detailed/leaderboard/drops/header/WaveleaderboardDropRaters.tsx b/components/waves/detailed/leaderboard/drops/header/WaveleaderboardDropRaters.tsx index 9b9522b1a..8f41e79d9 100644 --- a/components/waves/detailed/leaderboard/drops/header/WaveleaderboardDropRaters.tsx +++ b/components/waves/detailed/leaderboard/drops/header/WaveleaderboardDropRaters.tsx @@ -22,12 +22,17 @@ export const WaveLeaderboardDropRaters: React.FC< 3: "tw-text-[#CD7F32]", }; - const rankStyle = - drop.rank && drop.rank <= 3 - ? topThreeRankStyles[drop.rank] - : isNegativeVote - ? "tw-bg-gradient-to-r tw-from-red tw-to-red tw-bg-clip-text tw-text-transparent" - : "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent"; + const getRankStyle = () => { + if (drop.rank && drop.rank <= 3) { + return topThreeRankStyles[drop.rank]; + } + if (isNegativeVote) { + return "tw-bg-gradient-to-r tw-from-red tw-to-red tw-bg-clip-text tw-text-transparent"; + } + return "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent"; + }; + + const rankStyle = getRankStyle(); const hasUserVoted = drop.context_profile_context?.rating !== undefined && diff --git a/components/waves/detailed/winners/WaveWinnersSmall.tsx b/components/waves/detailed/winners/WaveWinnersSmall.tsx index e89819cd8..c529b4f63 100644 --- a/components/waves/detailed/winners/WaveWinnersSmall.tsx +++ b/components/waves/detailed/winners/WaveWinnersSmall.tsx @@ -155,18 +155,33 @@ const WaveWinnerItem = memo<{ const userVote = drop.context_profile_context?.rating ?? 0; const isNegativeVote = userVote < 0; - const ratingStyle = rankStyle - ? rankStyle.text - : drop.rating >= 0 - ? "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent" - : "tw-bg-gradient-to-r tw-from-rose-400 tw-to-rose-500 tw-bg-clip-text tw-text-transparent"; + const getRatingStyle = () => { + if (rankStyle) { + return rankStyle.text; + } + + if (drop.rating >= 0) { + return "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent"; + } + + return "tw-bg-gradient-to-r tw-from-rose-400 tw-to-rose-500 tw-bg-clip-text tw-text-transparent"; + }; + + const ratingStyle = getRatingStyle(); + + const getUserVoteStyle = () => { + if (drop.rank && drop.rank <= 3 && rankStyle) { + return rankStyle.text; + } + + if (isNegativeVote) { + return "tw-bg-gradient-to-r tw-from-rose-400 tw-to-rose-500 tw-bg-clip-text tw-text-transparent"; + } + + return "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent"; + }; - const userVoteStyle = - drop.rank && drop.rank <= 3 && rankStyle - ? rankStyle.text - : isNegativeVote - ? "tw-bg-gradient-to-r tw-from-rose-400 tw-to-rose-500 tw-bg-clip-text tw-text-transparent" - : "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent"; + const userVoteStyle = getUserVoteStyle(); const handleDropClick = useCallback(() => { onDropClick(drop); diff --git a/components/waves/detailed/winners/drops/WaveWinnersDrops.tsx b/components/waves/detailed/winners/drops/WaveWinnersDrops.tsx index 7918ef7c0..d07fdfe42 100644 --- a/components/waves/detailed/winners/drops/WaveWinnersDrops.tsx +++ b/components/waves/detailed/winners/drops/WaveWinnersDrops.tsx @@ -1,9 +1,11 @@ import React, { useContext, useMemo } from "react"; import { WaveWinnersDrop } from "./WaveWinnersDrop"; -import { WaveDropsLeaderboardSortDirection } from "../../../../../hooks/useWaveDropsLeaderboard"; -import { WaveDropsLeaderboardSortBy } from "../../../../../hooks/useWaveDropsLeaderboard"; +import { + useWaveDropsLeaderboard, + WaveDropsLeaderboardSortBy, + WaveDropsLeaderboardSortDirection, +} from "../../../../../hooks/useWaveDropsLeaderboard"; import { AuthContext } from "../../../../auth/Auth"; -import { useWaveDropsLeaderboard } from "../../../../../hooks/useWaveDropsLeaderboard"; import { useIntersectionObserver } from "../../../../../hooks/useIntersectionObserver"; import { ApiWave } from "../../../../../generated/models/ApiWave"; import { ExtendedDrop } from "../../../../../helpers/waves/drop.helpers"; diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderTotalVotes.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderTotalVotes.tsx index 396e6a51b..82c38fbe8 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderTotalVotes.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderTotalVotes.tsx @@ -14,11 +14,19 @@ export default function WaveWinnersDropHeaderTotalVotes({ 3: "tw-text-[#CD7F32]", }; - const style = drop.rank && drop.rank <= 3 - ? topThreeRankStyles[drop.rank] - : drop.rating >= 0 - ? "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent" - : "tw-bg-gradient-to-r tw-from-rose-400 tw-to-rose-500 tw-bg-clip-text tw-text-transparent"; + const getVoteStyle = (rank: number | null, rating: number) => { + if (rank && rank <= 3) { + return topThreeRankStyles[rank]; + } + + if (rating >= 0) { + return "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent"; + } + + return "tw-bg-gradient-to-r tw-from-rose-400 tw-to-rose-500 tw-bg-clip-text tw-text-transparent"; + }; + + const style = getVoteStyle(drop.rank, drop.rating); return (
diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoters.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoters.tsx index d1bac79c3..c423ff079 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoters.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropHeaderVoters.tsx @@ -22,12 +22,19 @@ export default function WaveWinnersDropHeaderVoters({ 3: "tw-text-[#CD7F32]", }; - const rankStyle = - drop.rank && drop.rank <= 3 - ? topThreeRankStyles[drop.rank] - : isNegativeVote - ? "tw-bg-gradient-to-r tw-from-red tw-to-red tw-bg-clip-text tw-text-transparent" - : "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent"; + const getVoteStyle = (rank: number | null, isNegative: boolean) => { + if (rank && rank <= 3) { + return topThreeRankStyles[rank]; + } + + if (isNegative) { + return "tw-bg-gradient-to-r tw-from-red tw-to-red tw-bg-clip-text tw-text-transparent"; + } + + return "tw-bg-gradient-to-r tw-from-emerald-400 tw-to-emerald-500 tw-bg-clip-text tw-text-transparent"; + }; + + const rankStyle = getVoteStyle(drop.rank, isNegativeVote); return (
diff --git a/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx b/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx index f68b0eac0..354744b20 100644 --- a/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx +++ b/components/waves/detailed/winners/drops/header/WaveWinnersDropOutcome.tsx @@ -1,7 +1,5 @@ import React from "react"; import { ExtendedDrop } from "../../../../../../helpers/waves/drop.helpers"; -import { ApiWaveOutcomeCredit } from "../../../../../../generated/models/ApiWaveOutcomeCredit"; -import { ApiWaveOutcomeType } from "../../../../../../generated/models/ApiWaveOutcomeType"; import { ApiWave } from "../../../../../../generated/models/ApiWave"; import { useDropOutcomes } from "../../../../../../hooks/drops/useDropOutcomes"; import { formatNumberWithCommas } from "../../../../../../helpers/Helpers";