Skip to content

Commit

Permalink
feat: use tanstack/query for data fetching (#80)
Browse files Browse the repository at this point in the history
Closes #48
  • Loading branch information
bradenrayhorn authored Jul 28, 2024
1 parent d320f23 commit a2d12de
Show file tree
Hide file tree
Showing 46 changed files with 736 additions and 454 deletions.
27 changes: 27 additions & 0 deletions ui/package-lock.json

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

1 change: 1 addition & 0 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@sveltejs/adapter-static": "^3.0.2",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@tanstack/svelte-query": "^5.51.1",
"@types/eslint": "^8.56.7",
"autoprefixer": "^10.4.19",
"daisyui": "^4.12.2",
Expand Down
4 changes: 4 additions & 0 deletions ui/src/app.postcss
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@
--color-base-500: #fdfdfd;
--color-base-600: #ffffff;

--color-base-skeleton: #e2e8f0;

--color-base-primaryHover: rgb(230, 240, 235);
--color-base-backdrop: rgba(0, 0, 0, 0.4);

Expand Down Expand Up @@ -127,6 +129,8 @@
--color-base-500: #0e1118;
--color-base-600: #1b1f2a;

--color-base-skeleton: #2f3340;

--color-base-primaryHover: rgba(52, 99, 76, 0.24);
--color-base-backdrop: rgba(0, 0, 0, 0.75);

Expand Down
7 changes: 7 additions & 0 deletions ui/src/lib/AuthProvider.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script lang="ts">
import { initAuthContext } from './auth-context';
initAuthContext();
</script>

<slot />
7 changes: 7 additions & 0 deletions ui/src/lib/NavigatingProvider.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script lang="ts">
import { initNavigatingContext } from './navigating-context';
initNavigatingContext();
</script>

<slot />
4 changes: 2 additions & 2 deletions ui/src/lib/api/action/image.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { APICall } from '../fetch';
import { handleAPIError } from '../handle-error';

export async function uploadImage({ fetch: _fetch, url, image }: APICall & { image: File }) {
export async function uploadImage({ fetch: _fetch, image }: APICall & { image: File }) {
const formData = new FormData();
formData.append('file', image);

Expand All @@ -11,7 +11,7 @@ export async function uploadImage({ fetch: _fetch, url, image }: APICall & { ima
});

if (!res.ok) {
await handleAPIError(res, url);
await handleAPIError(res);
}

return res.json().then((json: { data: string }) => json.data);
Expand Down
12 changes: 5 additions & 7 deletions ui/src/lib/api/action/recipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ type RecipeParams = {

export async function createRecipe({
fetch: _fetch,
url,
recipe: { image, ...recipe },
}: APICall & { recipe: RecipeParams }) {
const res = await fetch('/api/v1/recipes', {
Expand All @@ -25,18 +24,17 @@ export async function createRecipe({
},
body: JSON.stringify({
...cleanRecipeParams(recipe),
image_id: image ? await uploadImage({ fetch: _fetch, url, image }) : undefined,
image_id: image ? await uploadImage({ fetch: _fetch, image }) : undefined,
}),
});

if (!res.ok) {
await handleAPIError(res, url);
await handleAPIError(res);
}
}

export async function updateRecipe({
fetch: _fetch,
url,
id,
hash,
currentRecipe,
Expand All @@ -45,8 +43,8 @@ export async function updateRecipe({
let image_id = currentRecipe.image_id;
if (image) {
// only upload new image if the image has changed
if (image.name !== hash) {
image_id = await uploadImage({ fetch: _fetch, url, image });
if (image.type !== 'mise/image_id') {
image_id = await uploadImage({ fetch: _fetch, image });
}
} else {
image_id = undefined;
Expand All @@ -65,7 +63,7 @@ export async function updateRecipe({
});

if (!res.ok) {
await handleAPIError(res, url);
await handleAPIError(res);
}
}

Expand Down
1 change: 0 additions & 1 deletion ui/src/lib/api/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export type APICall = {
fetch: typeof fetch;
url: URL;
};
8 changes: 2 additions & 6 deletions ui/src/lib/api/handle-error.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import { error, redirect, type NumericRange } from '@sveltejs/kit';
import { error, type NumericRange } from '@sveltejs/kit';

const defaultError = 'Unknown error';

export const handleAPIError = async (res: Response, url: URL) => {
export const handleAPIError = async (res: Response) => {
const errorJson = await res.json().catch(async () => await res.text().catch(() => defaultError));
const msg = errorJson?.error ?? defaultError;

if (res.status >= 400 && res.status <= 599) {
if (res.status === 401) {
redirect(307, `/login/init?redirect_target=${url.pathname}`);
}

error(res.status as NumericRange<400, 599>, msg);
}

Expand Down
20 changes: 0 additions & 20 deletions ui/src/lib/api/load/image.ts

This file was deleted.

6 changes: 2 additions & 4 deletions ui/src/lib/api/load/recipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { handleAPIError } from '../handle-error';

export const getRecipes = async ({
fetch: _fetch,
url,
cursor,
search,
tags,
Expand All @@ -30,7 +29,7 @@ export const getRecipes = async ({
const res = await _fetch('/api/v1/recipes?' + params.toString());

if (!res.ok) {
return await handleAPIError(res, url);
return await handleAPIError(res);
}

return await res.json().then((json: Response) => ({
Expand All @@ -41,7 +40,6 @@ export const getRecipes = async ({

export const getRecipe = async ({
fetch: _fetch,
url,
id,
}: APICall & { id: string }): Promise<DetailedRecipeWithHash> => {
type Response = {
Expand Down Expand Up @@ -73,7 +71,7 @@ export const getRecipe = async ({
const res = await _fetch(`/api/v1/recipes/${id}`);

if (!res.ok) {
return await handleAPIError(res, url);
return await handleAPIError(res);
}

return await res.json().then((json: Response) => ({
Expand Down
4 changes: 2 additions & 2 deletions ui/src/lib/api/load/tag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import type { APICall } from '../fetch';
import type { Tag } from '$lib/types/tag';
import { handleAPIError } from '../handle-error';

export const getTags = async ({ fetch: _fetch, url }: APICall): Promise<Array<Tag>> => {
export const getTags = async ({ fetch: _fetch }: APICall): Promise<Array<Tag>> => {
type Response = {
data: Array<Tag>;
};

const res = await _fetch('/api/v1/tags');

if (!res.ok) {
await handleAPIError(res, url);
await handleAPIError(res);
}

return await res.json().then((json: Response) => json.data);
Expand Down
6 changes: 4 additions & 2 deletions ui/src/lib/api/load/user.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import type { APICall } from '../fetch';
import { handleAPIError } from '../handle-error';

export const getSelf = async ({ fetch: _fetch, url }: APICall): Promise<void> => {
export const getSelf = async ({ fetch: _fetch }: APICall): Promise<string> => {
const res = await _fetch('/api/v1/auth/me');

if (!res.ok) {
await handleAPIError(res, url);
await handleAPIError(res);
}

return 'self';
};
14 changes: 14 additions & 0 deletions ui/src/lib/api/query-keys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const queryKeys = {
recipe: {
list: 'list-recipes',
get: (id: string) => ['get-recipe', id],
},

tag: {
list: 'list-tags',
},

user: {
self: 'get-self',
},
};
16 changes: 16 additions & 0 deletions ui/src/lib/auth-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { getContext, setContext } from 'svelte';
import { writable, type Writable } from 'svelte/store';

const contextKey = 'mise_auth';

export type AuthContext = Writable<{
unauthenticated: boolean;
}>;

export function useAuth(): AuthContext {
return getContext<AuthContext>(contextKey);
}

export function initAuthContext() {
setContext<AuthContext>(contextKey, writable({ unauthenticated: false }));
}
12 changes: 12 additions & 0 deletions ui/src/lib/components/LoginIfUnauthenticated.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<script lang="ts">
import { useAuth } from '$lib/auth-context';
import PageReauthenticateState from './page-states/PageReauthenticateState.svelte';
const auth = useAuth();
</script>

{#if $auth.unauthenticated}
<PageReauthenticateState />
{:else}
<slot />
{/if}
42 changes: 42 additions & 0 deletions ui/src/lib/components/PrefetchLink.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { useNavigating } from '$lib/navigating-context';
export let href: string;
export let prefetch: () => Promise<void>;
const navigating = useNavigating();
let isLoading = false;
async function onClick(e: MouseEvent) {
e.preventDefault();
isLoading = true;
$navigating.to = href;
try {
await prefetch();
} catch (error: unknown) {
// ignore any prefetch errors and trigger primary page loading state
console.error('prefetch error: ', error);
}
// only navigate if another navigation hasn't triggered
if ($navigating.to === href) {
await goto(href);
$navigating.to = undefined;
}
isLoading = false;
}
// the navigation is not going to proceed if another navigation has started
$: if ($navigating.to !== undefined && $navigating.to !== href) {
isLoading = false;
}
</script>

<a {href} on:click={onClick} data-loading={isLoading ? true : undefined} {...$$restProps}
><slot {isLoading} /></a
>
7 changes: 6 additions & 1 deletion ui/src/lib/components/SingleTag.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@
>
<slot />
{#if canDelete}
<button class="ml-2 px-2 border-l border-tag-divider" aria-label="Delete tag" on:click>
<button
class="ml-2 px-2 border-l border-tag-divider"
aria-label="Delete tag"
type="button"
on:click
>
<DeleteIcon />
</button>
{/if}
Expand Down
8 changes: 5 additions & 3 deletions ui/src/lib/components/StreamedError.svelte
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { useAuth } from '$lib/auth-context';
import type { MaybeError } from '$lib/types/error';
import { onMount } from 'svelte';
export let error: MaybeError;
const auth = useAuth();
onMount(() => {
console.error('streamed error: ', error);
if (error?.status && error?.status >= 300 && error?.status <= 399 && error?.location) {
goto(error?.location);
if (error?.status === 401) {
$auth.unauthenticated = true;
}
});
</script>
Expand Down
Loading

0 comments on commit a2d12de

Please sign in to comment.