From fbe270dff9e77308c03a346c5b95389443754a31 Mon Sep 17 00:00:00 2001 From: Jacob Hurwitz Date: Sat, 14 Feb 2026 19:53:44 -0800 Subject: [PATCH] show own stats at top of friends stats page Co-Authored-By: Claude Opus 4.6 --- src/components/FriendProfileCardsScreen.tsx | 41 +++++++++++++++-- src/types.ts | 7 +++ worker/routes/friends.ts | 51 +++++++++++++++++++-- 3 files changed, 90 insertions(+), 9 deletions(-) diff --git a/src/components/FriendProfileCardsScreen.tsx b/src/components/FriendProfileCardsScreen.tsx index d25fd28..0c934d5 100644 --- a/src/components/FriendProfileCardsScreen.tsx +++ b/src/components/FriendProfileCardsScreen.tsx @@ -7,7 +7,7 @@ import { onMount, Show, } from "solid-js"; -import type { FriendProfile } from "../types"; +import type { FriendProfile, SelfProfile } from "../types"; import { formatTime } from "../utils"; import "./ButtonStyles.css"; import "./FriendProfileCardsScreen.css"; @@ -34,6 +34,7 @@ const reportReasons = [ export function FriendProfileCardsScreen(props: FriendProfileCardsScreenProps) { const [searchParams] = useSearchParams(); + const [selfProfile, setSelfProfile] = createSignal(null); const [profiles, setProfiles] = createSignal([]); const [loading, setLoading] = createSignal(true); const [error, setError] = createSignal(null); @@ -75,9 +76,11 @@ export function FriendProfileCardsScreen(props: FriendProfileCardsScreenProps) { setLoading(true); setError(null); try { - const response = await props.api<{ profiles: FriendProfile[] }>( - "/api/friends/profiles", - ); + const response = await props.api<{ + self: SelfProfile | null; + profiles: FriendProfile[]; + }>("/api/friends/profiles"); + setSelfProfile(response.self); setProfiles(response.profiles); setNicknameDrafts( Object.fromEntries( @@ -269,6 +272,36 @@ export function FriendProfileCardsScreen(props: FriendProfileCardsScreenProps) {
+ + {(self) => ( +
+
+
+
+
+

{self().username}

+
+
+
+ +
    +
  • + Friend Count + {self().friendCount} +
  • +
  • + Lifetime Oys Sent + {self().lifetimeOysSent} +
  • +
  • + Lifetime Oys Received + {self().lifetimeOysReceived} +
  • +
+
+
+ )} +
{(profile) => (
( - ` + const [profilesResult, selfResult] = await Promise.all([ + c.get("db").query( + ` SELECT u.id, u.username, @@ -219,11 +220,51 @@ export function registerFriendRoutes(app: App) { AND b1.blocker_user_id IS NULL AND b2.blocker_user_id IS NULL ORDER BY loi.last_oy_created_at DESC NULLS LAST, u.username - `, - [user.id], - ); + `, + [user.id], + ), + c.get("db").query<{ + username: string; + friend_count: number; + lifetime_oys_sent: number; + lifetime_oys_received: number; + }>( + ` + SELECT + u.username, + ( + SELECT COUNT(*) + FROM friendships f_count + WHERE f_count.user_id = u.id + )::INTEGER AS friend_count, + ( + SELECT COUNT(*) + FROM oys sent + WHERE sent.from_user_id = u.id + )::INTEGER AS lifetime_oys_sent, + ( + SELECT COUNT(*) + FROM oys received + WHERE received.to_user_id = u.id + )::INTEGER AS lifetime_oys_received + FROM users u + WHERE u.id = $1 + `, + [user.id], + ), + ]); + + const selfRow = selfResult.rows[0]; return c.json({ + self: selfRow + ? { + username: selfRow.username, + friendCount: Number(selfRow.friend_count), + lifetimeOysSent: Number(selfRow.lifetime_oys_sent), + lifetimeOysReceived: Number(selfRow.lifetime_oys_received), + } + : null, profiles: profilesResult.rows.map((row) => ({ id: row.id, username: row.username,