From 0bf2d9cb077e69287770713148bb0ed59e51eaed Mon Sep 17 00:00:00 2001 From: Cameron Custer Date: Sun, 13 Apr 2025 22:19:59 -0700 Subject: [PATCH 1/7] settings page --- sql/auth/user_preferences.sql | 39 +++++ sql/init.sql | 1 + sql/leaderboard/leaderboard_functions.sql | 2 + src/lib/components/Header.svelte | 75 +++++++-- src/lib/services/user.ts | 156 ++++++++++++++++++ src/routes/settings/+page.svelte | 187 ++++++++++++++++++++++ 6 files changed, 446 insertions(+), 14 deletions(-) create mode 100644 sql/auth/user_preferences.sql create mode 100644 src/lib/services/user.ts create mode 100644 src/routes/settings/+page.svelte diff --git a/sql/auth/user_preferences.sql b/sql/auth/user_preferences.sql new file mode 100644 index 0000000..b16b5ab --- /dev/null +++ b/sql/auth/user_preferences.sql @@ -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(); diff --git a/sql/init.sql b/sql/init.sql index b80c45c..da55ef8 100644 --- a/sql/init.sql +++ b/sql/init.sql @@ -10,6 +10,7 @@ CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- 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 diff --git a/sql/leaderboard/leaderboard_functions.sql b/sql/leaderboard/leaderboard_functions.sql index 118607b..94948a6 100644 --- a/sql/leaderboard/leaderboard_functions.sql +++ b/sql/leaderboard/leaderboard_functions.sql @@ -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 diff --git a/src/lib/components/Header.svelte b/src/lib/components/Header.svelte index 701a4ff..3c19d88 100644 --- a/src/lib/components/Header.svelte +++ b/src/lib/components/Header.svelte @@ -229,20 +229,45 @@ $: if ($page) { style="will-change: opacity;" > {#if $user} - - @{username} - - +
+ + @{username} + + + + + +
{:else} + + + {#if error} +
+

{error}

+
+ {/if} + + {#if success} +
+

{success}

+
+ {/if} + + {/if} + + + From 111509a00cca43b159b11db8681d1fececbfd088 Mon Sep 17 00:00:00 2001 From: Cameron Custer Date: Sun, 13 Apr 2025 22:42:56 -0700 Subject: [PATCH 2/7] settings page --- src/lib/services/user.ts | 65 ++++-------- src/routes/settings/+page.svelte | 170 ++++++++++++------------------- 2 files changed, 86 insertions(+), 149 deletions(-) diff --git a/src/lib/services/user.ts b/src/lib/services/user.ts index 5389506..299d5e5 100644 --- a/src/lib/services/user.ts +++ b/src/lib/services/user.ts @@ -42,7 +42,6 @@ export async function fetchUserPreferences(): Promise { .single(); if (error) { - console.error('Error fetching user preferences:', error); return null; } @@ -55,7 +54,6 @@ export async function fetchUserPreferences(): Promise { hideFromLeaderboard: record.hide_from_leaderboard }; } catch (err) { - console.error('Failed to fetch user preferences:', err); return null; } } @@ -69,49 +67,44 @@ export async function updateUserPreferences(preferences: UserPreferences): Promi const currentUser = get(user); if (!currentUser) { - console.error('No current user found'); return false; } - console.log('Current user:', currentUser.id); - console.log('Updating preferences with:', { - user_id: currentUser.id, - hide_from_leaderboard: preferences.hideFromLeaderboard - }); - try { - // Try to update first (most likely scenario) - let result = await supabase + // First check if a record exists + const { data: existingData } = await supabase .from('user_preferences') - .update({ - hide_from_leaderboard: preferences.hideFromLeaderboard, - updated_at: new Date().toISOString() - }) - .eq('user_id', currentUser.id); + .select('id') + .eq('user_id', currentUser.id) + .single(); - console.log('Update result:', result); + let result; - // If no rows were affected, try to insert - if (result.error || result.count === 0) { - console.log('No rows updated, trying insert'); - - // Try to insert a new record + if (existingData) { + // Update existing record + result = await supabase + .from('user_preferences') + .update({ + hide_from_leaderboard: preferences.hideFromLeaderboard, + updated_at: new Date().toISOString() + }) + .eq('user_id', currentUser.id); + } else { + // Insert new record result = await supabase .from('user_preferences') .insert({ user_id: currentUser.id, - hide_from_leaderboard: preferences.hideFromLeaderboard + hide_from_leaderboard: preferences.hideFromLeaderboard, + created_at: new Date().toISOString(), + updated_at: new Date().toISOString() }); - - console.log('Insert result:', result); } if (result.error) { // If we still have an error and it's a duplicate key error, // try one more time with an update if (result.error.code === '23505') { - console.log('Duplicate key error, trying update one more time'); - // Wait a moment before retrying await new Promise(resolve => setTimeout(resolve, 100)); @@ -123,34 +116,16 @@ export async function updateUserPreferences(preferences: UserPreferences): Promi }) .eq('user_id', currentUser.id); - console.log('Final update result:', result); - if (result.error) { - console.error('Error in final update attempt:', result.error); return false; } } else { - console.error('Error updating user preferences:', result.error); return false; } } - // Verify the update by fetching the current value - const { data: verifyData, error: verifyError } = await supabase - .from('user_preferences') - .select('*') - .eq('user_id', currentUser.id) - .single(); - - console.log('Verification fetch:', { data: verifyData, error: verifyError }); - - if (verifyError) { - console.error('Error verifying update:', verifyError); - } - return true; } catch (err) { - console.error('Failed to update user preferences:', err); return false; } } diff --git a/src/routes/settings/+page.svelte b/src/routes/settings/+page.svelte index 0a4b9e0..6988b2c 100644 --- a/src/routes/settings/+page.svelte +++ b/src/routes/settings/+page.svelte @@ -1,10 +1,10 @@ AlgoHub | Settings - + -
-

Settings

- +
{#if loading} -
-
- - - - -

Loading...

-
-
- {:else if !isAuthenticated} -
-

- You need to be signed in to access settings. -

+
+
{:else} -
-
-
-

Hide from Leaderboard

-

- When enabled, your solved problems will not be displayed on the leaderboard. -

-
- - -
- +
+ {#if success} +
{success}
+ {/if} {#if error} -
-

{error}

-
+
{error}
{/if} - - {#if success} -
-

{success}

+
+ +
+
+
+ + + + + + + Privacy
- {/if} +
+ +
+
+
+

Hide from leaderboard

+
+ +
+ +
+
+
{/if}
- - From 2eff04aec92aedfe82e76e9399eb8dbd6d6f6cd6 Mon Sep 17 00:00:00 2001 From: Cameron Custer Date: Sun, 13 Apr 2025 22:43:59 -0700 Subject: [PATCH 3/7] lint --- src/lib/services/user.ts | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/lib/services/user.ts b/src/lib/services/user.ts index 299d5e5..9317b27 100644 --- a/src/lib/services/user.ts +++ b/src/lib/services/user.ts @@ -77,9 +77,9 @@ export async function updateUserPreferences(preferences: UserPreferences): Promi .select('id') .eq('user_id', currentUser.id) .single(); - + let result; - + if (existingData) { // Update existing record result = await supabase @@ -91,14 +91,12 @@ export async function updateUserPreferences(preferences: UserPreferences): Promi .eq('user_id', currentUser.id); } else { // Insert new record - result = await supabase - .from('user_preferences') - .insert({ - user_id: currentUser.id, - hide_from_leaderboard: preferences.hideFromLeaderboard, - created_at: new Date().toISOString(), - updated_at: new Date().toISOString() - }); + result = await supabase.from('user_preferences').insert({ + user_id: currentUser.id, + hide_from_leaderboard: preferences.hideFromLeaderboard, + created_at: new Date().toISOString(), + updated_at: new Date().toISOString() + }); } if (result.error) { @@ -106,8 +104,8 @@ export async function updateUserPreferences(preferences: UserPreferences): Promi // try one more time with an update if (result.error.code === '23505') { // Wait a moment before retrying - await new Promise(resolve => setTimeout(resolve, 100)); - + await new Promise((resolve) => setTimeout(resolve, 100)); + result = await supabase .from('user_preferences') .update({ @@ -115,7 +113,7 @@ export async function updateUserPreferences(preferences: UserPreferences): Promi updated_at: new Date().toISOString() }) .eq('user_id', currentUser.id); - + if (result.error) { return false; } From 5c7d8ed242cf857f3ad8454f1e8090d5ac7ef465 Mon Sep 17 00:00:00 2001 From: Cameron Custer Date: Sat, 19 Apr 2025 12:52:27 -0700 Subject: [PATCH 4/7] update mobile header with settings --- src/lib/components/Header.svelte | 61 +++++++++++++++++--------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/src/lib/components/Header.svelte b/src/lib/components/Header.svelte index b4f0b5b..ff1ea03 100644 --- a/src/lib/components/Header.svelte +++ b/src/lib/components/Header.svelte @@ -331,36 +331,39 @@ $: if ($page) { style="will-change: opacity;" > {#if $user} - - @{username} - - - - Settings - + @{username} + + + + +
diff --git a/src/routes/submit/+page.svelte b/src/routes/submit/+page.svelte index 9ec229b..93a8b3f 100644 --- a/src/routes/submit/+page.svelte +++ b/src/routes/submit/+page.svelte @@ -51,7 +51,7 @@ onMount(() => { - gitgud | Submit + Submit
diff --git a/src/routes/submit/codeforces/+page.svelte b/src/routes/submit/codeforces/+page.svelte index 10dc156..15a2566 100644 --- a/src/routes/submit/codeforces/+page.svelte +++ b/src/routes/submit/codeforces/+page.svelte @@ -275,7 +275,7 @@ async function processUrls() { - gitgud | Submit Codeforces + Submit | Codeforces
diff --git a/src/routes/submit/kattis/+page.svelte b/src/routes/submit/kattis/+page.svelte index 4f55866..f016a8a 100644 --- a/src/routes/submit/kattis/+page.svelte +++ b/src/routes/submit/kattis/+page.svelte @@ -9,7 +9,7 @@ import { - gitgud | Submit Kattis + Submit | Kattis Date: Sat, 19 Apr 2025 13:08:30 -0700 Subject: [PATCH 6/7] fix leaderboard toggle bugs --- src/app.d.ts | 4 - src/hooks.server.ts | 23 ----- src/lib/services/user.ts | 70 ++++++++++++++- src/routes/settings/+page.server.ts | 18 ---- src/routes/settings/+page.svelte | 127 +++++++++++++++++++++++++--- 5 files changed, 184 insertions(+), 58 deletions(-) delete mode 100644 src/hooks.server.ts delete mode 100644 src/routes/settings/+page.server.ts diff --git a/src/app.d.ts b/src/app.d.ts index a11c4de..8774480 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -1,13 +1,9 @@ // See https://kit.svelte.dev/docs/types#app // for information about these interfaces // and what to do when importing types -import type { SupabaseClient, Session } from '@supabase/supabase-js'; - declare namespace App { interface Locals { userid: string; - supabase: SupabaseClient; - session: Session | null; } // interface Platform {} diff --git a/src/hooks.server.ts b/src/hooks.server.ts deleted file mode 100644 index 8ceb493..0000000 --- a/src/hooks.server.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { createClient } from '@supabase/supabase-js'; -import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public'; -import type { Handle } from '@sveltejs/kit'; - -export const handle: Handle = async ({ event, resolve }) => { - event.locals.supabase = createClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, { - auth: { - autoRefreshToken: true, - persistSession: true, - detectSessionInUrl: true - } - }); - - // Get the session from the cookies - const { - data: { session } - } = await event.locals.supabase.auth.getSession(); - - // Add the session to the locals object - event.locals.session = session; - - return resolve(event); -}; diff --git a/src/lib/services/user.ts b/src/lib/services/user.ts index 9317b27..750a781 100644 --- a/src/lib/services/user.ts +++ b/src/lib/services/user.ts @@ -28,13 +28,30 @@ export type UserPreferencesRecord = { * @returns User preferences or null if not found */ export async function fetchUserPreferences(): Promise { - const currentUser = get(user); + // First try to get the user from the store + let currentUser = get(user); + // If no user in the store, try to get it directly from Supabase if (!currentUser) { + console.log('fetchUserPreferences: No user in store, checking session directly'); + try { + const { data } = await supabase.auth.getSession(); + if (data.session?.user) { + currentUser = data.session.user; + console.log('fetchUserPreferences: Got user from session', currentUser.id); + } + } catch (err) { + console.error('fetchUserPreferences: Error getting session', err); + } + } + + if (!currentUser) { + console.log('fetchUserPreferences: No current user after all checks'); return null; } try { + console.log('fetchUserPreferences: Fetching preferences for user', currentUser.id); const { data, error } = await supabase .from('user_preferences') .select('*') @@ -42,18 +59,37 @@ export async function fetchUserPreferences(): Promise { .single(); if (error) { + console.log('fetchUserPreferences: Error fetching preferences', error); + // If the error is that no rows were returned, create default preferences + if (error.code === 'PGRST116') { + console.log('fetchUserPreferences: No preferences found, creating default'); + // Create default preferences + const result = await updateUserPreferences({ + hideFromLeaderboard: false + }); + + if (result) { + console.log('fetchUserPreferences: Default preferences created'); + return { + hideFromLeaderboard: false + }; + } + } return null; } if (!data) { + console.log('fetchUserPreferences: No data returned'); return null; } + console.log('fetchUserPreferences: Preferences found', data); const record = data as UserPreferencesRecord; return { hideFromLeaderboard: record.hide_from_leaderboard }; } catch (err) { + console.error('fetchUserPreferences: Exception', err); return null; } } @@ -64,23 +100,45 @@ export async function fetchUserPreferences(): Promise { * @returns True if successful, false otherwise */ export async function updateUserPreferences(preferences: UserPreferences): Promise { - const currentUser = get(user); + // First try to get the user from the store + let currentUser = get(user); + // If no user in the store, try to get it directly from Supabase if (!currentUser) { + console.log('updateUserPreferences: No user in store, checking session directly'); + try { + const { data } = await supabase.auth.getSession(); + if (data.session?.user) { + currentUser = data.session.user; + console.log('updateUserPreferences: Got user from session', currentUser.id); + } + } catch (err) { + console.error('updateUserPreferences: Error getting session', err); + } + } + + if (!currentUser) { + console.log('updateUserPreferences: No current user after all checks'); return false; } try { + console.log('updateUserPreferences: Checking if preferences exist for user', currentUser.id); // First check if a record exists - const { data: existingData } = await supabase + const { data: existingData, error: checkError } = await supabase .from('user_preferences') .select('id') .eq('user_id', currentUser.id) .single(); + if (checkError && checkError.code !== 'PGRST116') { + console.error('updateUserPreferences: Error checking if preferences exist', checkError); + } + let result; if (existingData) { + console.log('updateUserPreferences: Updating existing preferences', existingData.id); // Update existing record result = await supabase .from('user_preferences') @@ -90,6 +148,7 @@ export async function updateUserPreferences(preferences: UserPreferences): Promi }) .eq('user_id', currentUser.id); } else { + console.log('updateUserPreferences: Creating new preferences record'); // Insert new record result = await supabase.from('user_preferences').insert({ user_id: currentUser.id, @@ -100,9 +159,11 @@ export async function updateUserPreferences(preferences: UserPreferences): Promi } if (result.error) { + console.error('updateUserPreferences: Error updating/inserting preferences', result.error); // If we still have an error and it's a duplicate key error, // try one more time with an update if (result.error.code === '23505') { + console.log('updateUserPreferences: Duplicate key error, retrying with update'); // Wait a moment before retrying await new Promise((resolve) => setTimeout(resolve, 100)); @@ -115,6 +176,7 @@ export async function updateUserPreferences(preferences: UserPreferences): Promi .eq('user_id', currentUser.id); if (result.error) { + console.error('updateUserPreferences: Error on retry', result.error); return false; } } else { @@ -122,8 +184,10 @@ export async function updateUserPreferences(preferences: UserPreferences): Promi } } + console.log('updateUserPreferences: Preferences updated successfully'); return true; } catch (err) { + console.error('updateUserPreferences: Exception', err); return false; } } diff --git a/src/routes/settings/+page.server.ts b/src/routes/settings/+page.server.ts deleted file mode 100644 index 195b3aa..0000000 --- a/src/routes/settings/+page.server.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { redirect } from '@sveltejs/kit'; -import type { PageServerLoad } from './$types'; - -export const load: PageServerLoad = async ({ locals }) => { - // Get the session from the locals object - const { supabase } = locals; - const { data } = await supabase.auth.getSession(); - - // If there's no session, redirect to the home page - if (!data.session) { - throw redirect(303, '/'); - } - - // Return the session data to the page - return { - session: data.session - }; -}; diff --git a/src/routes/settings/+page.svelte b/src/routes/settings/+page.svelte index 1db52de..1d4dac9 100644 --- a/src/routes/settings/+page.svelte +++ b/src/routes/settings/+page.svelte @@ -5,10 +5,7 @@ import { user } from '$lib/services/auth'; import { fetchUserPreferences, updateUserPreferences } from '$lib/services/user'; import type { UserPreferences } from '$lib/services/user'; import type { Unsubscriber } from 'svelte/store'; - -// Get the session data from the server -export let data; -// We don't directly use data in the template, but it's required for the server-side check +import { supabase } from '$lib/services/database'; let preferences: UserPreferences = { hideFromLeaderboard: false @@ -26,11 +23,30 @@ async function loadPreferences(): Promise { error = null; try { + console.log('loadPreferences: Fetching user preferences'); const userPrefs = await fetchUserPreferences(); if (userPrefs) { + console.log('loadPreferences: User preferences loaded', userPrefs); preferences = userPrefs; + } else { + console.log('loadPreferences: No preferences returned, using defaults'); + // If no preferences were returned, try to create them + const result = await updateUserPreferences({ + hideFromLeaderboard: false + }); + + if (result) { + console.log('loadPreferences: Default preferences created'); + // Set the default preferences in the UI + preferences = { + hideFromLeaderboard: false + }; + } else { + console.log('loadPreferences: Failed to create default preferences'); + } } } catch (err) { + console.error('loadPreferences: Error loading preferences', err); error = 'Failed to load preferences'; } finally { loading = false; @@ -44,16 +60,20 @@ async function savePreferences(): Promise { success = null; try { + console.log('savePreferences: Saving preferences', preferences); const result = await updateUserPreferences(preferences); if (result) { + console.log('savePreferences: Preferences saved successfully'); success = 'Saved'; setTimeout(() => { success = null; }, 2000); } else { + console.error('savePreferences: Failed to save preferences'); error = 'Failed to save'; } } catch (err) { + console.error('savePreferences: Error saving preferences', err); error = 'Failed to save'; } finally { saving = false; @@ -68,15 +88,99 @@ function toggleHideFromLeaderboard(): void { // Initialize auth state and load preferences onMount(() => { - // Load preferences immediately since we know the user is authenticated - // (the server-side load function already checked this) - loadPreferences(); + // Create a flag to track if we've already checked auth + let authChecked = false; + let loadingTimeout: ReturnType | null = null; + + // First, directly check the session + const checkSession = async () => { + try { + const { data } = await supabase.auth.getSession(); + if (!data.session) { + // No session, redirect to home + goto('/'); + return false; + } + return true; + } catch (err) { + console.error('Error checking session:', err); + return false; + } + }; + + // Function to load preferences with a delay + const loadPreferencesWithDelay = () => { + // Clear any existing timeout + if (loadingTimeout) { + clearTimeout(loadingTimeout); + } + + // Set a timeout to ensure user state is fully initialized + loadingTimeout = setTimeout(async () => { + console.log('Loading preferences after delay'); + + // Try to directly check the toggle state from the database + try { + const { data } = await supabase.auth.getSession(); + if (data.session?.user) { + const userId = data.session.user.id; + console.log('Directly checking preferences for user', userId); - // Set up a subscription to handle logout events - userUnsubscribe = user.subscribe((value) => { - if (value === null) { + const { data: prefData, error } = await supabase + .from('user_preferences') + .select('hide_from_leaderboard') + .eq('user_id', userId) + .single(); + + if (prefData && !error) { + console.log('Direct preference check result:', prefData); + preferences = { + hideFromLeaderboard: prefData.hide_from_leaderboard + }; + loading = false; + return; + } else { + console.log('No direct preferences found, falling back to normal load'); + } + } + } catch (err) { + console.error('Error in direct preference check:', err); + } + + // Fall back to normal loading if direct check fails + loadPreferences(); + }, 500); // 500ms delay + }; + + // Check session and load preferences if authenticated + checkSession().then((isAuthenticated) => { + if (isAuthenticated) { + authChecked = true; + loadPreferencesWithDelay(); + } + }); + + // Also set up a subscription to handle auth state changes + userUnsubscribe = user.subscribe(async (value) => { + console.log('User state changed:', value ? 'logged in' : 'logged out'); + + // If we haven't checked auth yet, do it now + if (!authChecked && value === null) { + // Double-check with the API directly + const isAuthenticated = await checkSession(); + if (!isAuthenticated) { + goto('/'); + return; + } + } else if (value === null) { // User logged out, redirect to home goto('/'); + return; + } else if (!authChecked || value) { + // User is authenticated and we haven't loaded preferences yet + // OR user state just changed to logged in + authChecked = true; + loadPreferencesWithDelay(); } }); @@ -85,6 +189,9 @@ onMount(() => { if (userUnsubscribe) { userUnsubscribe(); } + if (loadingTimeout) { + clearTimeout(loadingTimeout); + } }; }); From 2ad84861b5cae8380bb68b5feb4f978a1ceb75b2 Mon Sep 17 00:00:00 2001 From: Cameron Custer Date: Sat, 19 Apr 2025 13:18:35 -0700 Subject: [PATCH 7/7] Fix header bug --- src/lib/components/Header.svelte | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/lib/components/Header.svelte b/src/lib/components/Header.svelte index ff1ea03..bf7cbd1 100644 --- a/src/lib/components/Header.svelte +++ b/src/lib/components/Header.svelte @@ -129,16 +129,18 @@ $: if ($page) { gitgud Logo - gitgud.cc + + gitgud +
-