From cabcdb16193476907b3db47fc2d1d3a7d8ef6ad0 Mon Sep 17 00:00:00 2001 From: Cho Young-Hwi Date: Thu, 19 Mar 2026 13:23:14 +0000 Subject: [PATCH] [#356] Add Donation History on writer dashboard with Load more Show received donations below each storyline card on the writer dashboard. Each row shows donor address, date, formatted amount with Basescan tx link. Uses same Load more pattern as #351 (10 per batch). Hidden when no donations exist. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/app/dashboard/writer/page.tsx | 90 ++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 2 deletions(-) diff --git a/src/app/dashboard/writer/page.tsx b/src/app/dashboard/writer/page.tsx index 02cd0570..88b7c385 100644 --- a/src/app/dashboard/writer/page.tsx +++ b/src/app/dashboard/writer/page.tsx @@ -4,15 +4,17 @@ import { useState } from "react"; import { useAccount, useSignMessage } from "wagmi"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { formatUnits } from "viem"; -import { supabase, type Storyline } from "../../../../lib/supabase"; +import { supabase, type Storyline, type Donation } from "../../../../lib/supabase"; import { getTokenTVL } from "../../../../lib/price"; -import { RESERVE_LABEL, STORY_FACTORY } from "../../../../lib/contracts/constants"; +import { RESERVE_LABEL, STORY_FACTORY, EXPLORER_URL } from "../../../../lib/contracts/constants"; import { GENRES, LANGUAGES } from "../../../../lib/genres"; import { DeadlineCountdown } from "../../../components/DeadlineCountdown"; import { ClaimRoyalties } from "../../../components/ClaimRoyalties"; import { WriterTradingStats } from "../../../components/WriterTradingStats"; import { WriterIdentityClient } from "../../../components/WriterIdentityClient"; import { DropdownSelect } from "../../../components/DropdownSelect"; +import { truncateAddress } from "../../../../lib/utils"; +import { formatPrice } from "../../../../lib/format"; import Link from "next/link"; import { ConnectWallet } from "../../../components/ConnectWallet"; import { type Address } from "viem"; @@ -177,6 +179,90 @@ function StorylineDetail({ storyline, writerAddress }: { storyline: Storyline; w /> )} + + + + ); +} + +const DONATION_PAGE_SIZE = 10; + +function WriterDonationHistory({ storylineId }: { storylineId: number }) { + const [limit, setLimit] = useState(DONATION_PAGE_SIZE); + + const { data } = useQuery({ + queryKey: ["writer-donations", storylineId, limit], + queryFn: async () => { + if (!supabase) return { rows: [], totalCount: 0 }; + const { data: rows, count } = await supabase + .from("donations") + .select("*", { count: "exact" }) + .eq("storyline_id", storylineId) + .eq("contract_address", STORY_FACTORY.toLowerCase()) + .order("block_timestamp", { ascending: false }) + .range(0, limit - 1) + .returns(); + return { rows: rows ?? [], totalCount: count ?? 0 }; + }, + }); + + const donations = data?.rows ?? []; + const totalCount = data?.totalCount ?? 0; + const hasMore = donations.length < totalCount; + + if (donations.length === 0) return null; + + return ( +
+ + Donation History + +
+ {donations.map((d) => ( +
+
+ + {truncateAddress(d.donor_address)} + + {d.block_timestamp && ( + + )} +
+
+ + {formatPrice(formatUnits(BigInt(d.amount), 18))} {RESERVE_LABEL} + + {d.tx_hash && ( + + ↗ + + )} +
+
+ ))} +
+ {hasMore && ( + + )}
); }