Skip to content
This repository has been archived by the owner on Jan 14, 2024. It is now read-only.

Commit

Permalink
feat: add gaming votes
Browse files Browse the repository at this point in the history
  • Loading branch information
sunxyw committed Nov 25, 2023
1 parent 54d4b29 commit dca5a17
Show file tree
Hide file tree
Showing 18 changed files with 317 additions and 73 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,13 @@
"supabase": "^1.110.1",
"svelte": "^4.2.3",
"svelte-check": "^3.6.0",
"svelte-french-toast": "^1.2.0",
"svelte-legos": "^0.2.2",
"svelte-meta-tags": "^3.1.0",
"svelte-modals": "^1.3.0",
"svelte-sequential-preprocessor": "^2.0.1",
"sveltekit-embed": "^0.0.12",
"sveltekit-flash-message": "^2.2.2",
"sveltekit-sitemap": "^1.0.21",
"sveltekit-superforms": "^1.10.1",
"tailwindcss": "^3.3.5",
Expand Down
33 changes: 33 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion src/app.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ declare global {
supabase: SupabaseBrowserClient;
getSession: () => Promise<Session | null>;
}
// interface PageData {}
interface PageData {
flash?: { type: 'success' | 'error'; message: string };
}
// interface Platform {}
}

Expand Down
2 changes: 2 additions & 0 deletions src/hooks.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { sequence } from '@sveltejs/kit/hooks';
import { sitemapHook } from 'sveltekit-sitemap';
import { createTRPCHandle } from 'trpc-sveltekit';

import authHook from '$lib/auth/hook.server';
import { localePreference, resolveFirstAvailableLocale } from '$lib/shared/i18n';
import * as seoSites from '$lib/shared/seo/sites';
import supabaseHook from '$lib/shared/supabase/hook.server';
Expand Down Expand Up @@ -33,6 +34,7 @@ export const handle: Handle = sequence(
});
},
supabaseHook,
authHook,
createTRPCHandle({ router, createContext }),
sitemapHook(sitemap, {
getRobots: seoSites.getRobots,
Expand Down
1 change: 1 addition & 0 deletions src/lib/auth/authenticated_routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const authenticatedRoutes: string[] = ['/games/proposals'];
24 changes: 24 additions & 0 deletions src/lib/auth/hook.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { redirect } from 'sveltekit-flash-message/server';

import { authenticatedRoutes } from './authenticated_routes';

import type { Handle } from '@sveltejs/kit';

export default (async ({ event, resolve }) => {
// check url against list of routes that require authentication
if (authenticatedRoutes.includes(event.url.pathname)) {
// check if user is logged in
const session = await event.locals.getSession();
if (!session) {
// user is not logged in, redirect to login page
throw redirect(
303,
'/auth',
{ type: 'error', message: 'You must be logged in to view this page.' },
event
);
}
}

return resolve(event);
}) satisfies Handle;
5 changes: 5 additions & 0 deletions src/lib/shared/shared/components/SimpleHeroSection.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,10 @@
<p class="mt-6 text-lg leading-8 text-muted-foreground">
<slot />
</p>
{#if $$slots.cta}
<div class="mt-8">
<slot name="cta" />
</div>
{/if}
</div>
</div>
67 changes: 63 additions & 4 deletions src/lib/shared/supabase/database.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,49 @@ export interface Database {
}
];
};
game_votes: {
Row: {
created_at: string;
game_id: number;
is_upvote: boolean;
voter_id: string;
};
Insert: {
created_at?: string;
game_id: number;
is_upvote: boolean;
voter_id: string;
};
Update: {
created_at?: string;
game_id?: number;
is_upvote?: boolean;
voter_id?: string;
};
Relationships: [
{
foreignKeyName: 'game_votes_game_id_fkey';
columns: ['game_id'];
isOneToOne: false;
referencedRelation: 'games';
referencedColumns: ['id'];
},
{
foreignKeyName: 'game_votes_game_id_fkey';
columns: ['game_id'];
isOneToOne: false;
referencedRelation: 'games_with_votes';
referencedColumns: ['id'];
},
{
foreignKeyName: 'game_votes_voter_id_fkey';
columns: ['voter_id'];
isOneToOne: false;
referencedRelation: 'profiles';
referencedColumns: ['id'];
}
];
};
games: {
Row: {
created_at: string;
Expand All @@ -105,7 +148,7 @@ export interface Database {
image_url: string;
name: string;
on_sale: boolean;
provider: number;
provider: Database['public']['Enums']['game_providers'];
provider_identifier: string;
};
Insert: {
Expand All @@ -116,7 +159,7 @@ export interface Database {
image_url: string;
name: string;
on_sale?: boolean;
provider: number;
provider: Database['public']['Enums']['game_providers'];
provider_identifier: string;
};
Update: {
Expand All @@ -127,7 +170,7 @@ export interface Database {
image_url?: string;
name?: string;
on_sale?: boolean;
provider?: number;
provider?: Database['public']['Enums']['game_providers'];
provider_identifier?: string;
};
Relationships: [];
Expand Down Expand Up @@ -184,7 +227,22 @@ export interface Database {
};
};
Views: {
[_ in never]: never;
games_with_votes: {
Row: {
created_at: string | null;
description: string | null;
downvote_count: number | null;
formatted_price: number | null;
id: number | null;
image_url: string | null;
name: string | null;
on_sale: boolean | null;
provider: Database['public']['Enums']['game_providers'] | null;
provider_identifier: string | null;
upvote_count: number | null;
};
Relationships: [];
};
};
Functions: {
get_random_phrases: {
Expand All @@ -199,6 +257,7 @@ export interface Database {
};
Enums: {
activity_status: 'upcoming' | 'finished' | 'ongoing';
game_providers: 'steam' | 'xbox' | 'other';
};
CompositeTypes: {
[_ in never]: never;
Expand Down
2 changes: 1 addition & 1 deletion src/lib/trpc/routes/game_store_resolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default {
return {
name: data.name,
provider: 'steam',
provider_identifier: data.steam_appid,
provider_identifier: data.steam_appid.toString(),
image_url: data.header_image,
description: data.short_description
};
Expand Down
6 changes: 4 additions & 2 deletions src/routes/+layout.server.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { loadFlash } from 'sveltekit-flash-message/server';

import type { LayoutServerLoad } from './$types';

export const load: LayoutServerLoad = async ({ locals: { getSession } }) => {
export const load: LayoutServerLoad = loadFlash(async ({ locals: { getSession } }) => {
return {
session: await getSession()
};
};
});
22 changes: 22 additions & 0 deletions src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import { QueryClientProvider } from '@tanstack/svelte-query';
import { onMount } from 'svelte';
import toast, { Toaster } from 'svelte-french-toast';
import { getFlash } from 'sveltekit-flash-message';
import { webVitals } from '$lib/shared/analytics/vitals';
import ParaglideJsSvelteKitProviderCsr from '$lib/shared/i18n/ParaglideJsSvelteKitProviderCsr.svelte';
import Footer from '$lib/shared/layout/Footer.svelte';
Expand Down Expand Up @@ -42,6 +45,23 @@
}
}
}
const flash = getFlash(page);
flash.subscribe((v) => {
if (!v) {
return;
}
if (v.type === 'success') {
toast.success(v.message);
} else if (v.type === 'error') {
toast.error(v.message);
} else {
toast(v.message);
}
flash.set(undefined);
});
</script>

<ParaglideJsSvelteKitProviderCsr>
Expand All @@ -56,5 +76,7 @@
<slot />

<Footer />

<Toaster />
</QueryClientProvider>
</ParaglideJsSvelteKitProviderCsr>
42 changes: 37 additions & 5 deletions src/routes/games/+page.server.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,44 @@
import { fail } from '@sveltejs/kit';
import { setFlash } from 'sveltekit-flash-message/server';

import { superValidate } from 'sveltekit-superforms/server';

import { z } from 'zod';

const schema = z.object({
name: z.string().default('John')
import type { Actions } from './$types';

const formSchema = z.object({
game_id: z.number(),
vote: z.enum(['upvote', 'downvote'])
});

export const load = async () => {
const form = await superValidate(schema);
export const actions: Actions = {
default: async (event) => {
const form = await superValidate(event, formSchema);
if (!form.valid) {
return fail(400, { form });
}

const session = await event.locals.getSession();
if (!session) {
setFlash({ type: 'error', message: 'You must be logged in to vote.' }, event);
return fail(401);
}
const {
user: { id: user_id }
} = session;

const { error } = await event.locals.supabase.from('game_votes').upsert({
game_id: form.data.game_id,
voter_id: user_id,
is_upvote: form.data.vote === 'upvote'
});

if (error) {
setFlash({ type: 'error', message: 'There was an error saving your vote.' }, event);
return fail(400, { form });
}

return { form };
setFlash({ type: 'success', message: 'Your vote has been noted.' }, event);
}
};
Loading

0 comments on commit dca5a17

Please sign in to comment.