diff --git a/lib/supabase.ts b/lib/supabase.ts index ed1a4811..8a7e7d62 100644 --- a/lib/supabase.ts +++ b/lib/supabase.ts @@ -357,6 +357,7 @@ export interface Database { tx_hash: string; log_index: number; contract_address: string; + user_address: string | null; }; Insert: { id?: never; @@ -371,6 +372,7 @@ export interface Database { tx_hash: string; log_index: number; contract_address: string; + user_address?: string | null; }; Update: { id?: never; @@ -385,6 +387,7 @@ export interface Database { tx_hash?: string; log_index?: number; contract_address?: string; + user_address?: string | null; }; Relationships: []; }; @@ -413,3 +416,4 @@ export type Plot = Database["public"]["Tables"]["plots"]["Row"]; export type Donation = Database["public"]["Tables"]["donations"]["Row"]; export type Rating = Database["public"]["Tables"]["ratings"]["Row"]; export type Comment = Database["public"]["Tables"]["comments"]["Row"]; +export type TradeHistory = Database["public"]["Tables"]["trade_history"]["Row"]; diff --git a/src/app/api/cron/trade-history/route.ts b/src/app/api/cron/trade-history/route.ts index 402b6877..7516bc44 100644 --- a/src/app/api/cron/trade-history/route.ts +++ b/src/app/api/cron/trade-history/route.ts @@ -215,6 +215,7 @@ async function processTradeEvent( tx_hash: log.transactionHash!.toLowerCase(), log_index: log.logIndex!, contract_address: MCV2_BOND.toLowerCase(), + user_address: args.user.toLowerCase(), }; const { error } = await supabase diff --git a/src/app/api/index/trade/route.ts b/src/app/api/index/trade/route.ts index 011e5d19..a211d446 100644 --- a/src/app/api/index/trade/route.ts +++ b/src/app/api/index/trade/route.ts @@ -65,6 +65,7 @@ export async function POST(req: Request) { const args = decoded.args as { token: `0x${string}`; + user: `0x${string}`; amountMinted?: bigint; amountBurned?: bigint; reserveAmount?: bigint; @@ -107,6 +108,7 @@ export async function POST(req: Request) { tx_hash: txHash.toLowerCase(), log_index: log.logIndex!, contract_address: MCV2_BOND.toLowerCase(), + user_address: args.user.toLowerCase(), }; const { error: dbError } = await supabase diff --git a/src/app/dashboard/reader/page.tsx b/src/app/dashboard/reader/page.tsx index a76f4a83..69cea98c 100644 --- a/src/app/dashboard/reader/page.tsx +++ b/src/app/dashboard/reader/page.tsx @@ -3,8 +3,10 @@ import { useEffect, useState } from "react"; import { useAccount } from "wagmi"; import { useQuery } from "@tanstack/react-query"; -import { supabase, type Donation } from "../../../../lib/supabase"; +import { supabase, type Donation, type TradeHistory } from "../../../../lib/supabase"; +import { formatPrice } from "../../../../lib/format"; import { ReaderPortfolio } from "../../../components/ReaderPortfolio"; +import Link from "next/link"; import { WriterIdentityClient } from "../../../components/WriterIdentityClient"; import { formatUnits } from "viem"; import { ConnectWallet } from "../../../components/ConnectWallet"; @@ -106,6 +108,9 @@ export default function ReaderDashboard() { + {/* --- Trading History --- */} + + {/* --- Donation History --- */}

@@ -188,3 +193,108 @@ function DonationRow({ donation, decimals }: { donation: Donation; decimals: num ); } + +const TRADE_PAGE_SIZE = 10; + +function TradingHistory({ address }: { address: string }) { + const [limit, setLimit] = useState(TRADE_PAGE_SIZE); + const [prevAddress, setPrevAddress] = useState(address); + if (address !== prevAddress) { + setPrevAddress(address); + setLimit(TRADE_PAGE_SIZE); + } + + const { data, isLoading } = useQuery({ + queryKey: ["reader-trades", address, limit], + queryFn: async () => { + if (!supabase) return { rows: [], totalCount: 0 }; + const { data: rows, count } = await supabase + .from("trade_history") + .select("*", { count: "exact" }) + .eq("user_address", address.toLowerCase()) + .order("block_timestamp", { ascending: false }) + .range(0, limit - 1) + .returns(); + return { rows: rows ?? [], totalCount: count ?? 0 }; + }, + }); + + const trades = data?.rows ?? []; + const totalCount = data?.totalCount ?? 0; + const hasMore = trades.length < totalCount; + + return ( +
+

Trading History

+

+ {totalCount} {totalCount === 1 ? "trade" : "trades"} +

+ + {isLoading &&

Loading...

} + +
+ {trades.map((t) => ( +
+
+ + {t.event_type === "mint" ? "Buy" : "Sell"} + + + Story #{t.storyline_id} + + {t.block_timestamp && ( + + )} +
+
+ {t.price_per_token > 0 && ( + + {formatPrice(t.reserve_amount / t.price_per_token)} tokens + + )} + + {formatPrice(t.reserve_amount)} {RESERVE_LABEL} + + {t.tx_hash && ( + + ↗ + + )} +
+
+ ))} + {!isLoading && trades.length === 0 && ( +

+ No trades yet. +

+ )} +
+ + {hasMore && ( + + )} +
+ ); +} diff --git a/supabase/migrations/00019_trade_history_user_address.sql b/supabase/migrations/00019_trade_history_user_address.sql new file mode 100644 index 00000000..eeb234ea --- /dev/null +++ b/supabase/migrations/00019_trade_history_user_address.sql @@ -0,0 +1,6 @@ +-- Add user_address column to trade_history for filtering by trader +ALTER TABLE trade_history ADD COLUMN IF NOT EXISTS user_address text; + +-- Index for reader dashboard queries +CREATE INDEX IF NOT EXISTS idx_trade_history_user_address + ON trade_history (user_address, block_timestamp DESC);