108 lines
3.4 KiB
TypeScript
108 lines
3.4 KiB
TypeScript
import { db } from '$lib/server/db';
|
|
import { game, pokemonGameEntry, userEntry, userGame } from '$lib/server/db/schema';
|
|
import { eq, inArray } from 'drizzle-orm';
|
|
import type { Actions, PageServerLoad } from './$types';
|
|
import { availabilityValues } from '$lib/availability';
|
|
import { fail } from '@sveltejs/kit';
|
|
export type WireEntry = [id: number, gameId: number, availability: number, note?: string];
|
|
|
|
export const load: PageServerLoad = async ({ locals }) => {
|
|
const pokemonRows = await db.query.pokemon.findMany({
|
|
with: {
|
|
entry: true
|
|
},
|
|
orderBy: (pokemon) => pokemon.number
|
|
});
|
|
const pokemon = pokemonRows.map(({ entry, ...rest }) => ({
|
|
...rest,
|
|
entries: entry.map((e): WireEntry => {
|
|
const code = availabilityValues.indexOf(e.availability);
|
|
return e.note === null ? [e.id, e.gameId, code] : [e.id, e.gameId, code, e.note];
|
|
})
|
|
}));
|
|
|
|
const games = await db.query.game.findMany({
|
|
orderBy: (game) => [game.generation, game.id]
|
|
});
|
|
|
|
let caughtIds: number[] = [];
|
|
let selectedGameIds: number[] = [];
|
|
if (locals.user) {
|
|
const caughtRows = await db
|
|
.select({ id: userEntry.pokemonGameEntryId })
|
|
.from(userEntry)
|
|
.where(eq(userEntry.userId, locals.user.id));
|
|
caughtIds = caughtRows.map((r) => r.id);
|
|
|
|
const gameRows = await db
|
|
.select({ id: userGame.gameId })
|
|
.from(userGame)
|
|
.where(eq(userGame.userId, locals.user.id));
|
|
selectedGameIds = gameRows.map((r) => r.id);
|
|
}
|
|
|
|
return { pokemon, games, caughtIds, selectedGameIds };
|
|
};
|
|
|
|
export const actions = {
|
|
togglePokemonCaught: async ({ request, locals }) => {
|
|
if (!locals.user) return fail(401, { message: 'Log in to track your progress' });
|
|
|
|
const data = await request.formData();
|
|
const entryId = Number(data.get('entryId'));
|
|
if (!Number.isInteger(entryId) || entryId <= 0) return fail(400, { message: 'Invalid entry' });
|
|
|
|
// only catchable entries can be toggled — the client disables these
|
|
// checkboxes but we can't trust that
|
|
const [entry] = await db
|
|
.select({ availability: pokemonGameEntry.availability })
|
|
.from(pokemonGameEntry)
|
|
.where(eq(pokemonGameEntry.id, entryId));
|
|
if (!entry) return fail(404, { message: 'Unknown entry' });
|
|
if (entry.availability !== 'catchable') {
|
|
return fail(400, { message: 'This pokemon is not catchable in this game' });
|
|
}
|
|
|
|
const caught = data.get('caught') === 'on';
|
|
|
|
if (caught) {
|
|
await db
|
|
.insert(userEntry)
|
|
.values({ pokemonGameEntryId: entryId, userId: locals.user.id })
|
|
.onConflictDoNothing();
|
|
} else {
|
|
await db
|
|
.delete(userEntry)
|
|
.where(
|
|
and(eq(userEntry.pokemonGameEntryId, entryId), eq(userEntry.userId, locals.user.id))
|
|
);
|
|
}
|
|
},
|
|
|
|
setGames: async ({ request, locals }) => {
|
|
if (!locals.user) return fail(401, { message: 'Log in to set rules' });
|
|
|
|
const data = await request.formData();
|
|
const gameIds = data
|
|
.getAll('gameIds')
|
|
.map(Number)
|
|
.filter((id) => Number.isInteger(id) && id > 0);
|
|
|
|
if (gameIds.length > 0) {
|
|
const validGames = await db
|
|
.select({ id: game.id })
|
|
.from(game)
|
|
.where(inArray(game.id, gameIds));
|
|
if (validGames.length !== gameIds.length) return fail(400, { message: 'Unknown game' });
|
|
}
|
|
|
|
const userId = locals.user.id;
|
|
await db.transaction(async (tx) => {
|
|
await tx.delete(userGame).where(eq(userGame.userId, userId));
|
|
if (gameIds.length > 0) {
|
|
await tx.insert(userGame).values(gameIds.map((gameId) => ({ userId, gameId })));
|
|
}
|
|
});
|
|
}
|
|
} satisfies Actions;
|