From 161618b342069c9532f165a012e87e12db3a0553 Mon Sep 17 00:00:00 2001 From: Cameron Custer Date: Sun, 20 Apr 2025 13:34:09 -0700 Subject: [PATCH 1/2] show user solves from the leaderboard --- sql/init.sql | 7 +- sql/problems/get_user_solved_problems.sql | 16 + src/lib/components/LeaderboardTable.svelte | 46 +- src/lib/components/ProblemDisplay.svelte | 541 +++++++++++++++++++++ src/lib/services/problem.ts | 28 ++ src/routes/+page.svelte | 502 +------------------ src/routes/user/[userId]/+page.svelte | 7 + src/routes/user/[userId]/+page.ts | 7 + 8 files changed, 637 insertions(+), 517 deletions(-) create mode 100644 sql/problems/get_user_solved_problems.sql create mode 100644 src/lib/components/ProblemDisplay.svelte create mode 100644 src/routes/user/[userId]/+page.svelte create mode 100644 src/routes/user/[userId]/+page.ts diff --git a/sql/init.sql b/sql/init.sql index d1c3fdc..6131d54 100644 --- a/sql/init.sql +++ b/sql/init.sql @@ -2,26 +2,21 @@ -- This file includes all the necessary SQL files to set up the database schema -- Enable UUID extension if not already enabled CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; - -- Common utility functions \ i common / utility_functions.sql -- Authentication and user management \ i auth / user_roles.sql \ i auth / user_triggers.sql \ i auth / user_preferences.sql -- Problem-related tables and functions -\ i problems / problems.sql \ i problems / user_problem_feedback.sql \ i problems / user_solved_problems.sql \ i problems / problem_functions.sql -- Contest-related tables and functions +\ i problems / problems.sql \ i problems / user_problem_feedback.sql \ i problems / user_solved_problems.sql \ i problems / problem_functions.sql \ i problems / get_user_solved_problems.sql -- Contest-related tables and functions \ i contests / contests.sql \ i contests / user_contest_participation.sql \ i contests / user_contest_feedback.sql \ i contests / contest_functions.sql -- Leaderboard functions \ i leaderboard / leaderboard_functions.sql -- Grant necessary permissions - GRANT USAGE ON SCHEMA public TO anon, authenticated, service_role; - GRANT ALL ON ALL TABLES IN SCHEMA public TO anon, authenticated, service_role; - GRANT ALL ON ALL FUNCTIONS IN SCHEMA public TO anon, authenticated, service_role; - GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO anon, authenticated, service_role; \ No newline at end of file diff --git a/sql/problems/get_user_solved_problems.sql b/sql/problems/get_user_solved_problems.sql new file mode 100644 index 0000000..c1dfaf2 --- /dev/null +++ b/sql/problems/get_user_solved_problems.sql @@ -0,0 +1,16 @@ +-- Function to get solved problems for a specific user +CREATE OR REPLACE FUNCTION get_user_solved_problems(p_user_id UUID) +RETURNS TABLE (problem_id UUID) AS $$ +BEGIN + RETURN QUERY + SELECT usp.problem_id + FROM user_solved_problems usp + JOIN auth.users u ON usp.user_id = u.id + LEFT JOIN user_preferences up ON u.id = up.user_id + WHERE usp.user_id = p_user_id + AND COALESCE(up.hide_from_leaderboard, false) = false; +END; +$$ LANGUAGE plpgsql SECURITY DEFINER; + +-- Grant permissions +GRANT EXECUTE ON FUNCTION get_user_solved_problems TO authenticated, anon; diff --git a/src/lib/components/LeaderboardTable.svelte b/src/lib/components/LeaderboardTable.svelte index a7695d5..43dacc5 100644 --- a/src/lib/components/LeaderboardTable.svelte +++ b/src/lib/components/LeaderboardTable.svelte @@ -111,15 +111,39 @@ function getRankTierName(rank: number): string { {entry.username.substring(0, 2).toUpperCase()} {/if} - - @{entry.username} - +
+ + @{entry.username} + + + + + + + + +
@@ -182,11 +206,11 @@ function getRankTierName(rank: number): string { } /* Ensure username is always purple */ -a[href*='github.com'] { +a[href^='/user/'] { color: var(--color-username) !important; } -a[href*='github.com']:hover { +a[href^='/user/']:hover { color: color-mix(in oklab, var(--color-username) 80%, white) !important; } diff --git a/src/lib/components/ProblemDisplay.svelte b/src/lib/components/ProblemDisplay.svelte new file mode 100644 index 0000000..6790162 --- /dev/null +++ b/src/lib/components/ProblemDisplay.svelte @@ -0,0 +1,541 @@ + + + + {targetUser ? `${targetUser.username}'s Solved Problems` : pageTitle} + + + +
+ {#if loading} +
+
+ + + + +

Loading problems...

+
+
+ {:else if error} +
+

{error}

+ +
+ {:else} +
+ + + + +
+
+
+ +
+
+
+
+ {/if} +
+ + diff --git a/src/lib/services/problem.ts b/src/lib/services/problem.ts index 77e1579..38dffc5 100644 --- a/src/lib/services/problem.ts +++ b/src/lib/services/problem.ts @@ -260,6 +260,34 @@ export async function fetchUserSolvedProblems(): Promise> { } } +/** + * Fetches solved problems for a specific user + * @param userId - User ID to fetch solved problems for + * @returns Set of solved problem IDs + */ +export async function fetchUserSolvedProblemsByUserId(userId: string): Promise> { + if (!userId) { + return new Set(); + } + + try { + // Use the RPC function to get solved problems for a user who isn't hidden from the leaderboard + const { data, error } = await supabase.rpc('get_user_solved_problems', { + p_user_id: userId + }); + + if (error) { + console.error(`Error fetching solved problems for user ${userId}:`, error); + return new Set(); + } + + return new Set(data.map((item: { problem_id: string }) => item.problem_id)); + } catch (err) { + console.error(`Failed to fetch solved problems for user ${userId}:`, err); + return new Set(); + } +} + /** * Marks a problem as solved or unsolved by the current user * @param problemId - Problem ID diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index af81d1e..a441197 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,503 +1,5 @@ - - Problems - - - -
- {#if loading} -
-
- - - - -

Loading problems...

-
-
- {:else if error} -
-

{error}

- -
- {:else} -
- - - - -
-
-
- -
-
-
-
- {/if} -
- - + diff --git a/src/routes/user/[userId]/+page.svelte b/src/routes/user/[userId]/+page.svelte new file mode 100644 index 0000000..d72fe5f --- /dev/null +++ b/src/routes/user/[userId]/+page.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/routes/user/[userId]/+page.ts b/src/routes/user/[userId]/+page.ts new file mode 100644 index 0000000..4b82490 --- /dev/null +++ b/src/routes/user/[userId]/+page.ts @@ -0,0 +1,7 @@ +import type { PageLoad } from './$types'; + +export const load: PageLoad = ({ params }) => { + return { + userId: params.userId + }; +}; From d4969e65258e6fd9ee6ea6142a57dff04fb892ae Mon Sep 17 00:00:00 2001 From: Cameron Custer Date: Sun, 20 Apr 2025 13:38:23 -0700 Subject: [PATCH 2/2] minor formatting nit --- src/lib/components/ProblemDisplay.svelte | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/lib/components/ProblemDisplay.svelte b/src/lib/components/ProblemDisplay.svelte index 6790162..acc9fb0 100644 --- a/src/lib/components/ProblemDisplay.svelte +++ b/src/lib/components/ProblemDisplay.svelte @@ -17,7 +17,7 @@ import ProblemTable from '$lib/components/ProblemTable.svelte'; import TopicSidebar from '$lib/components/TopicSidebar.svelte'; // Props -export let pageTitle = "Problems"; +export let pageTitle = 'Problems'; export let targetUserId: string | null = null; export let defaultSolvedFilterState: 'all' | 'solved' | 'unsolved' = 'all'; @@ -416,7 +416,7 @@ onMount(() => { const pathParts = window.location.pathname.split('/'); targetUserId = pathParts[pathParts.length - 1]; } - + loadProblems(); // Subscribe to auth state changes @@ -478,12 +478,8 @@ onMount(() => { {:else if error} -
-

{error}

- +
+

{error}

{:else}