Skip to content

Commit

Permalink
SvelteKit v1 and use query params where possible (#13)
Browse files Browse the repository at this point in the history
* Dependencies to SvelteKit v1

* Use query params for filter state

* Use zod schema and parse in load function

* Refactor selected filters to use query params

* Move filter out of load

* Refactor filter UI

* Try patching zod-form-data

* Revert "Try patching zod-form-data"

This reverts commit e053baa.

* Try patch-package

* Install patch-package

* Try just patching `exports`

* Revert "Try just patching `exports`"

This reverts commit 5d1b195.

* Use ssr.noExternal

* Do some cleanup

* Misc changes

- show submit when no js
- pass `name` to filters
- show filter selection count on summary

* Polyfill requestSubmit

* Don't prerender og images and set cache headers

* Fix build
  • Loading branch information
geoffrich committed Jan 16, 2023
1 parent bc5ce23 commit 76e2eaa
Show file tree
Hide file tree
Showing 16 changed files with 362 additions and 352 deletions.
280 changes: 132 additions & 148 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 7 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@
},
"devDependencies": {
"@resvg/resvg-js": "^2.1.0",
"@sveltejs/kit": "^1.0.0-next.582",
"@sveltejs/kit": "^1.0.12",
"@types/crypto-js": "^4.0.2",
"@types/ioredis": "^4.27.2",
"focus-visible": "^5.2.0",
"form-request-submit-polyfill": "^2.0.0",
"just-debounce-it": "^3.2.0",
"open-props": "^1.3.9",
"prettier": "^2.7.1",
"prettier-plugin-svelte": "^2.2.0",
Expand All @@ -27,11 +29,13 @@
"svelte-preprocess": "^4.0.0",
"tslib": "^2.0.0",
"typescript": "^4.6.2",
"vite": "^4.0.0"
"vite": "^4.0.0",
"zod": "^3.20.2",
"zod-form-data": "^1.2.4"
},
"type": "module",
"dependencies": {
"@sveltejs/adapter-netlify": "^1.0.0-next.85",
"@sveltejs/adapter-netlify": "^1.0.1",
"crypto-js": "^4.1.1",
"dayjs": "^1.10.6",
"ioredis": "^5.2.1",
Expand Down
6 changes: 5 additions & 1 deletion src/app.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<body data-sveltekit-preload-data="hover" class="no-js">
<script>
document.body.classList.add('has-js');
document.body.classList.remove('no-js');
</script>
<div id="svelte">%sveltekit.body%</div>
</body>
</html>
8 changes: 8 additions & 0 deletions src/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,11 @@ select {
display: grid;
gap: var(--size-4);
}

body.has-js .no-js-only {
display: none;
}

body.no-js .js-only {
display: none;
}
28 changes: 14 additions & 14 deletions src/lib/comics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@ import dayjs from './dayjs';
const NO_EVENT = '(no event)';
const NO_CREATOR = '(unknown)';

export function getSeries(c: Comic): string {
return c.series.name;
export function getSeries(c: Comic) {
return c.series;
}

export function getCreators({ creators }: Comic): string[] {
export function getCreators({ creators }: Comic) {
if (creators.length > 0) {
return creators.map((cr) => cr.name);
return creators;
} else {
return [NO_CREATOR];
return [{ id: -1, name: NO_CREATOR }];
}
}

export function getEvents({ events }: Comic): string[] {
export function getEvents({ events }: Comic) {
if (events.length > 0) {
return events.map((e) => e.name);
return events;
} else {
return [NO_EVENT];
return [{ id: -1, name: NO_EVENT }];
}
}

Expand All @@ -38,18 +38,18 @@ export function compareTitles({ title: titleA }: Comic, { title: titleB }: Comic
return titleA.localeCompare(titleB, undefined, { numeric: true, sensitivity: 'base' });
}

export function isEventSelected({ events }: Comic, selectedEvents: Set<string>) {
export function isEventSelected({ events }: Comic, selectedEvents: Set<number>) {
return (
events.find((e) => selectedEvents.has(e.name)) !== undefined ||
(selectedEvents.has(NO_EVENT) && events.length === 0)
events.find((e) => selectedEvents.has(e.id)) !== undefined ||
(selectedEvents.has(-1) && events.length === 0)
);
}

// TODO: make more efficient
export function isCreatorSelected({ creators }: Comic, selectedCreators: Set<string>) {
export function isCreatorSelected({ creators }: Comic, selectedCreators: Set<number>) {
return (
creators.find((e) => selectedCreators.has(e.name)) !== undefined ||
(selectedCreators.has(NO_CREATOR) && creators.length === 0)
creators.find((e) => selectedCreators.has(e.id)) !== undefined ||
(selectedCreators.has(-1) && creators.length === 0)
);
}

Expand Down
48 changes: 21 additions & 27 deletions src/lib/components/Filter.svelte
Original file line number Diff line number Diff line change
@@ -1,47 +1,37 @@
<script lang="ts">
import type { Writable } from 'svelte/store';
import { Plus, Minus } from '$lib/icons';
import IconButton from './IconButton.svelte';
export let items: Set<string>;
/** Mapping of IDs (included in the set) with text values */
export let items: Record<number, string>;
export let legend: string;
export let name: string;
export let included: Writable<Set<string>>;
export let included: Set<number>;
let showItems = true;
$: sortedItems = [...items].sort();
$: isFiltered = included.size > 0;
$: sortedItems = Object.entries(items).sort((a, b) => (a[1] > b[1] ? 1 : -1));
function uncheckAll() {
$included.clear();
$included = $included;
}
function checkAll() {
$included = new Set(items);
included.clear();
included = included;
}
function updateShowItems() {
showItems = !showItems;
}
function handleChange({ target }) {
if (target.checked) {
$included.add(target.value);
} else {
$included.delete(target.value);
}
$included = $included;
}
</script>

{#if items.size > 1}
{#if sortedItems.length > 1}
<fieldset>
<legend>{legend} {$included.size} / {items.size}</legend>
<legend
>{legend}
{#if isFiltered}({included.size} selected){/if}</legend
>
<div class="buttons">
<button on:click={checkAll}>Check all</button>
<button on:click={uncheckAll}>Uncheck all</button>
<button on:click={uncheckAll} class:hidden={!isFiltered}>Show all</button>
<IconButton size="1.5rem" altText={showItems ? 'Hide' : 'Show'} on:click={updateShowItems}>
{#if showItems}
<Minus />
Expand All @@ -51,10 +41,10 @@
</IconButton>
</div>
{#if showItems}
{#each sortedItems as i (i)}
{#each sortedItems as [id, title] (id)}
<label
><input on:change={handleChange} type="checkbox" checked={$included.has(i)} value={i} />
{i}</label
><input type="checkbox" checked={included.has(Number(id))} value={id} {name} />
{title}</label
>
{/each}
{/if}
Expand Down Expand Up @@ -92,4 +82,8 @@
button {
padding-inline: var(--size-2);
}
button.hidden {
opacity: 0.5;
}
</style>
6 changes: 4 additions & 2 deletions src/lib/components/PageLinks.svelte
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
<script lang="ts">
import { MAX_YEAR, MIN_YEAR } from '$lib/years';
import { page } from '$app/stores';
export let year: number;
$: hasPrevious = year > MIN_YEAR;
$: hasNext = year < MAX_YEAR;
$: search = $page.url.search || '';
</script>

{#if hasPrevious || hasNext}
<div class="links">
{#if hasPrevious}
<a href="/year/{year - 1}">Previous year</a>
<a href="/year/{year - 1}{search}">Previous year</a>
{/if}
{#if hasNext}
<a href="/year/{year + 1}">Next year</a>
<a href="/year/{year + 1}{search}">Next year</a>
{/if}
</div>
{/if}
Expand Down
52 changes: 0 additions & 52 deletions src/lib/stores/selected.ts

This file was deleted.

12 changes: 12 additions & 0 deletions src/lib/util.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { Comic } from './types';

export function dedupe<Item>(arr: Item[], getId: (arg: Item) => any): Item[] {
const deduped = arr.filter((c, idx) => {
const matchingIdIndex = arr.findIndex((c2) => getId(c) === getId(c2));
Expand Down Expand Up @@ -28,3 +30,13 @@ export function promiseTimeout<T>(ms: number, promise: Promise<T>): Promise<T> {
// Returns a race between our timeout and the passed in promise
return Promise.race([promise, timeout]);
}

export function toMapping(
comics: Comic[],
mapping: (c: Comic) => { id: number; name: string } | { id: number; name: string }[]
): Record<number, string> {
return comics.flatMap(mapping).reduce<Record<number, string>>((acc, cur) => {
acc[cur.id] = cur.name;
return acc;
}, {});
}
7 changes: 7 additions & 0 deletions src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
import { navigating, page } from '$app/stores';
import 'focus-visible'; // polyfill :focus-visible for Safari
import '../global.css';
import { browser } from '$app/environment';
if (browser) {
// polyfill .requestSubmit for Safari < 16
// accesses HTMLFormElement so needs to be only run in browser
import('form-request-submit-polyfill');
}
$: title = $page.data.title;
</script>
Expand Down
7 changes: 6 additions & 1 deletion src/routes/og/[year=year].png/+server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,17 @@ import Image from '../Image.svelte';

export const prerender = 'auto';

export const GET: RequestHandler = async ({ params, url }) => {
export const GET: RequestHandler = async ({ params, url, setHeaders }) => {
const year = parseInt(params.year);
console.log('Generating social image for ', year);
const redis = new Redis();
const api = new Api(redis, url.origin);
const comics = await api.getRandomComics(year, year + 1, 5);
const images = comics.map((c) => getImage(c.image, ImageSize.XXLarge, c.ext));

setHeaders({
'cache-control': 'public, max-age=86400'
});

return componentToPng(Image, { text: `Marvel Comics from ${year}`, images }, 630, 1200);
};
1 change: 0 additions & 1 deletion src/routes/og/test/+page.js

This file was deleted.

1 change: 0 additions & 1 deletion src/routes/year/[year=year]/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ async function getComics({ params, setHeaders, url }: RequestEvent) {
if (badStatus === undefined) {
const response = adaptResponses(results);

// TODO: not currently working due to https://github.com/sveltejs/kit/issues/6477
setHeaders({
'cache-control': 'public, max-age=86400'
});
Expand Down
Loading

0 comments on commit 76e2eaa

Please sign in to comment.