Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions sql/auth/user_preferences.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
-- Create user_preferences table to store user settings
CREATE TABLE IF NOT EXISTS user_preferences (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
hide_from_leaderboard BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(user_id)
);

-- Create RLS policies for user_preferences table
ALTER TABLE user_preferences ENABLE ROW LEVEL SECURITY;

-- Users can read their own preferences
CREATE POLICY "Users can read their own preferences" ON user_preferences FOR
SELECT USING (auth.uid() = user_id);

-- Users can insert their own preferences
CREATE POLICY "Users can insert their own preferences" ON user_preferences FOR
INSERT WITH CHECK (auth.uid() = user_id);

-- Users can update their own preferences
CREATE POLICY "Users can update their own preferences" ON user_preferences FOR
UPDATE USING (auth.uid() = user_id);

-- Create function to automatically create user preferences on new user creation
CREATE OR REPLACE FUNCTION public.handle_new_user_preferences() RETURNS TRIGGER AS $$
BEGIN
INSERT INTO public.user_preferences (user_id, hide_from_leaderboard)
VALUES (NEW.id, false);
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- Create trigger to call the function when a new user is created
DROP TRIGGER IF EXISTS on_auth_user_created_preferences ON auth.users;
CREATE TRIGGER on_auth_user_created_preferences
AFTER INSERT ON auth.users
FOR EACH ROW EXECUTE FUNCTION public.handle_new_user_preferences();
7 changes: 6 additions & 1 deletion sql/init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,26 @@
-- 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 -- Problem-related tables and functions
\ 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 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;
3 changes: 3 additions & 0 deletions sql/leaderboard/leaderboard_functions.sql
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ CREATE OR REPLACE FUNCTION get_leaderboard() RETURNS TABLE (
) AS earliest_solves_sum
FROM auth.users u
LEFT JOIN user_solved_problems usp ON u.id = usp.user_id
LEFT JOIN user_preferences up ON u.id = up.user_id
WHERE COALESCE(up.hide_from_leaderboard, false) = false
GROUP BY u.id,
u.raw_user_meta_data
HAVING COUNT(usp.problem_id) > 0
Expand All @@ -45,6 +47,7 @@ FROM user_stats
ORDER BY user_stats.problems_solved DESC,
user_stats.earliest_solves_sum ASC;
$$ LANGUAGE SQL SECURITY DEFINER;

-- Grant permissions
GRANT EXECUTE ON FUNCTION get_leaderboard() TO authenticated,
anon;
120 changes: 86 additions & 34 deletions src/lib/components/Header.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -129,16 +129,18 @@ $: if ($page) {
<a
href="/"
aria-label="Home"
class="flex items-center gap-2 text-xl font-bold text-[var(--color-heading)] no-underline"
class="flex items-center gap-2 pr-2 text-xl font-bold text-[var(--color-heading)] no-underline lg:pr-4"
>
<img src="/favicon.png" alt="gitgud Logo" class="h-12 w-12 object-contain" />
<span>gitgud.cc</span>
<span class="flex">
<span>gitgud</span><span class="hidden sm:inline">.cc</span>
</span>
</a>
</div>

<!-- Mobile menu button -->
<button
class="flex items-center rounded-md border border-[var(--color-border)] px-2 py-1 text-[var(--color-text)] md:hidden"
class="flex items-center rounded-md border border-[var(--color-border)] px-2 py-1 text-[var(--color-text)] lg:hidden"
aria-label={mobileMenuOpen ? 'Close menu' : 'Open menu'}
on:click={toggleMobileMenu}
>
Expand All @@ -162,16 +164,16 @@ $: if ($page) {
</button>

<!-- Desktop navigation -->
<nav class="hidden items-center gap-6 md:flex md:gap-4">
<ul class="m-0 flex list-none gap-6 p-0 md:gap-4">
<nav class="hidden items-center gap-6 lg:flex lg:gap-4">
<ul class="m-0 flex list-none gap-2 p-0 lg:gap-3 xl:gap-4">
<li
class="relative {$page.url.pathname === '/'
? "after:absolute after:-bottom-2 after:left-0 after:h-0.5 after:w-full after:rounded-sm after:bg-[var(--color-accent)] after:content-['']"
: ''}"
>
<a
href="/"
class="block py-2 text-base font-semibold text-[var(--color-heading)] no-underline transition-colors duration-200 hover:text-[var(--color-accent)]"
class="block py-2 text-sm font-semibold text-[var(--color-heading)] no-underline transition-colors duration-200 hover:text-[var(--color-accent)] lg:text-base"
>Problems</a
>
</li>
Expand All @@ -182,7 +184,7 @@ $: if ($page) {
>
<a
href="/contests"
class="block py-2 text-base font-semibold text-[var(--color-heading)] no-underline transition-colors duration-200 hover:text-[var(--color-accent)]"
class="block py-2 text-sm font-semibold text-[var(--color-heading)] no-underline transition-colors duration-200 hover:text-[var(--color-accent)] lg:text-base"
>Contests</a
>
</li>
Expand All @@ -193,7 +195,7 @@ $: if ($page) {
>
<a
href="/leaderboard"
class="block py-2 text-base font-semibold text-[var(--color-heading)] no-underline transition-colors duration-200 hover:text-[var(--color-accent)]"
class="block py-2 text-sm font-semibold text-[var(--color-heading)] no-underline transition-colors duration-200 hover:text-[var(--color-accent)] lg:text-base"
>Leaderboard</a
>
</li>
Expand All @@ -204,7 +206,7 @@ $: if ($page) {
>
<a
href="/about"
class="block py-2 text-base font-semibold text-[var(--color-heading)] no-underline transition-colors duration-200 hover:text-[var(--color-accent)]"
class="block py-2 text-sm font-semibold text-[var(--color-heading)] no-underline transition-colors duration-200 hover:text-[var(--color-accent)] lg:text-base"
>About</a
>
</li>
Expand All @@ -216,7 +218,7 @@ $: if ($page) {
>
<a
href="/submit"
class="block py-2 text-base font-semibold text-[var(--color-heading)] no-underline transition-colors duration-200 hover:text-[var(--color-accent)]"
class="block py-2 text-sm font-semibold text-[var(--color-heading)] no-underline transition-colors duration-200 hover:text-[var(--color-accent)] lg:text-base"
>Submit</a
>
</li>
Expand All @@ -229,20 +231,45 @@ $: if ($page) {
style="will-change: opacity;"
>
{#if $user}
<a
href={githubUrl}
target="_blank"
rel="noopener noreferrer"
class="text-sm font-medium text-[var(--color-username)] transition-colors duration-200 hover:text-[color-mix(in_oklab,var(--color-username)_80%,white)]"
>
@{username}
</a>
<button
class="cursor-pointer rounded border border-[var(--color-border)] bg-transparent px-3 py-1.5 text-sm font-semibold text-[var(--color-text)] transition-all duration-200 hover:bg-[color-mix(in_oklab,black_5%,transparent)]"
on:click={handleLogout}
>
Logout
</button>
<div class="flex items-center gap-3">
<a
href={githubUrl}
target="_blank"
rel="noopener noreferrer"
class="text-sm font-medium text-[var(--color-username)] transition-colors duration-200 hover:text-[color-mix(in_oklab,var(--color-username)_80%,white)]"
>
@{username}
</a>
<a
href="/settings"
class="flex items-center justify-center rounded-full p-1.5 text-[var(--color-text)] transition-colors hover:bg-[var(--color-tertiary)] hover:text-[var(--color-accent)]"
title="Settings"
aria-label="Settings"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="h-5 w-5"
aria-hidden="true"
>
<path
d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"
></path>
<circle cx="12" cy="12" r="3"></circle>
</svg>
</a>
<button
class="cursor-pointer rounded border border-[var(--color-border)] bg-transparent px-3 py-1.5 text-sm font-semibold text-[var(--color-text)] transition-all duration-200 hover:bg-[color-mix(in_oklab,black_5%,transparent)]"
on:click={handleLogout}
>
Logout
</button>
</div>
{:else}
<button
class="cursor-pointer rounded border border-[#4285f4] bg-[#4285f4] px-3 py-1.5 text-sm font-semibold text-white shadow-sm transition-all duration-200 hover:border-[#3367d6] hover:bg-[#3367d6] hover:shadow"
Expand All @@ -259,7 +286,7 @@ $: if ($page) {
<!-- Mobile menu -->
{#if mobileMenuOpen}
<div
class="mt-3 border-t border-[var(--color-border)] bg-[var(--color-secondary)] px-4 py-4 shadow-md md:hidden"
class="mt-3 border-t border-[var(--color-border)] bg-[var(--color-secondary)] px-4 py-4 shadow-md lg:hidden"
>
<nav class="flex flex-col gap-4">
<ul class="m-0 flex list-none flex-col gap-4 p-0">
Expand Down Expand Up @@ -306,14 +333,39 @@ $: if ($page) {
style="will-change: opacity;"
>
{#if $user}
<a
href={githubUrl}
target="_blank"
rel="noopener noreferrer"
class="text-sm font-medium text-[var(--color-username)] transition-colors duration-200 hover:text-[color-mix(in_oklab,var(--color-username)_80%,white)]"
>
@{username}
</a>
<div class="flex items-center gap-3">
<a
href={githubUrl}
target="_blank"
rel="noopener noreferrer"
class="text-sm font-medium text-[var(--color-username)] transition-colors duration-200 hover:text-[color-mix(in_oklab,var(--color-username)_80%,white)]"
>
@{username}
</a>
<a
href="/settings"
class="flex items-center justify-center rounded-full p-1.5 text-[var(--color-text)] transition-colors hover:bg-[var(--color-tertiary)] hover:text-[var(--color-accent)]"
title="Settings"
aria-label="Settings"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="h-5 w-5"
aria-hidden="true"
>
<path
d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"
></path>
<circle cx="12" cy="12" r="3"></circle>
</svg>
</a>
</div>
<button
class="cursor-pointer rounded border border-[var(--color-border)] bg-transparent px-3 py-1.5 text-sm font-semibold text-[var(--color-text)] transition-all duration-200 hover:bg-[color-mix(in_oklab,black_5%,transparent)]"
on:click={handleLogout}
Expand Down Expand Up @@ -348,7 +400,7 @@ $: if ($page) {
}
}

div.md\:hidden {
div.lg\:hidden {
animation: slideDown 0.2s ease-out;
}

Expand Down
Loading