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

Commit

Permalink
feat: migrate fetching to Svelte-Query (#291)
Browse files Browse the repository at this point in the history
  • Loading branch information
Cahllagerfeld authored Feb 8, 2023
1 parent 62ce625 commit 9f6dc4d
Show file tree
Hide file tree
Showing 10 changed files with 171 additions and 101 deletions.
46 changes: 44 additions & 2 deletions package-lock.json

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

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@sveltejs/adapter-node": "^1.1.0",
"@sveltejs/kit": "^1.0.11",
"@tailwindcss/typography": "^0.5.9",
"@tanstack/svelte-query": "^4.24.4",
"@types/cookie": "^0.5.1",
"@typescript-eslint/eslint-plugin": "^5.48.1",
"@typescript-eslint/parser": "^5.48.1",
Expand Down Expand Up @@ -64,4 +65,4 @@
"prettier --write --plugin-search-dir=."
]
}
}
}
3 changes: 2 additions & 1 deletion src/lib/components/header.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import ProfilePicture from './profile-picture.svelte';
import { page } from '$app/stores';
import { onMount } from 'svelte';
export let username: string;
export let username: string | undefined;
const navItems = [{ name: 'Docs', path: '/docs' }];
let closeHamburgerMenu: () => void;
Expand Down
21 changes: 21 additions & 0 deletions src/lib/data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { SearchResponse } from '../global';

const url = '/api/get-issues';

export async function fetchIssuesLoad(query: { query: string }, loadFetch: typeof window.fetch) {
const response = await loadFetch(url, {
method: 'POST',
body: JSON.stringify(query),
});
const data = await response.json();
return data as SearchResponse;
}

export async function fetchIssues(query: string, endCursor: string) {
const response = await fetch(url, {
method: 'POST',
body: JSON.stringify({ query, after: endCursor }),
});
const data = await response.json();
return data as SearchResponse;
}
2 changes: 1 addition & 1 deletion src/routes/+layout.server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { LayoutServerLoad } from '../../.svelte-kit/types/src/routes/$types';
import type { LayoutServerLoad } from './$types';

export const load: LayoutServerLoad = ({ locals }): { username: string } => {
return {
Expand Down
22 changes: 13 additions & 9 deletions src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,24 @@
import '../app.css';
import Footer from '$lib/components/footer.svelte';
import Header from '$lib/components/header.svelte';
import { QueryClientProvider } from '@tanstack/svelte-query';
import { setContext } from 'svelte';
import { versionKey } from '$lib/util/version';
import type { LayoutData } from './$types';
export let data: { username: string; version: string };
export let data: LayoutData;
setContext(versionKey, data.version);
</script>

<div class="flex min-h-screen flex-col">
<div class="float-none mx-auto my-0 w-[90%] max-w-[1440px] pb-8">
<Header username={data.username} />
<main>
<slot />
</main>
<QueryClientProvider client={data.queryClient}>
<div class="flex min-h-screen flex-col">
<div class="float-none mx-auto my-0 w-[90%] max-w-[1440px] pb-8">
<Header username={data.username} />
<main>
<slot />
</main>
</div>
<Footer />
</div>
<Footer />
</div>
</QueryClientProvider>
15 changes: 13 additions & 2 deletions src/routes/+layout.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import type { Load } from '@sveltejs/kit';
import { browser } from '$app/environment';
import { QueryClient } from '@tanstack/svelte-query';
import type { LayoutLoad } from './$types';

export const load: LayoutLoad = async ({ fetch, data }) => {
const queryClient = new QueryClient({
defaultOptions: {
queries: {
enabled: browser,
},
},
});

export const load: Load = async ({ fetch, data }) => {
const res = await fetch('/api/version');
if (res.ok) {
const resData = (await res.json()) as { version: string };
return {
...data,
version: resData.version,
queryClient,
};
}
};
2 changes: 1 addition & 1 deletion src/routes/api/get-issues/+server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { SearchResponse } from 'src/global';

type Response = { search: SearchResponse };

export const POST: RequestHandler = async ({ request }) => {
export const POST: RequestHandler = async ({ request, cookies }) => {
const token = cookie.parse(request.headers.get('cookie') || '').access_token || '';

if (!token) {
Expand Down
125 changes: 61 additions & 64 deletions src/routes/app/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,73 +1,70 @@
<script lang="ts">
import IssueCard from '$lib/components/issue-card.svelte';
import Search from '$lib/components/search.svelte';
import Filter from '$lib/components/filter.svelte';
import LoadMore from '$lib/components/load-more.svelte';
import Toggle from '$lib/components/toggle.svelte';
import Seo from '$lib/components/seo.svelte';
import { selectedLabels } from '$lib/stores/selected-labels.store';
import type { SearchResponse } from '../../global';
import { goto } from '$app/navigation';
import { goto, disableScrollHandling, afterNavigate } from '$app/navigation';
import type { PageData } from './$types';
import { query } from '$lib/constants.json';
import { navigating } from '$app/stores';
import Loader from '$lib/components/loader.svelte';
import { createInfiniteQuery } from '@tanstack/svelte-query';
import { fetchIssues } from '$lib/data';
import { selectedLabels } from '$lib/stores/selected-labels.store';
import IssueCard from '$lib/components/issue-card.svelte';
import Filter from '$lib/components/filter.svelte';
import Search from '$lib/components/search.svelte';
export let data: PageData;
let { checked } = data;
let loadDisabled = false;
$: githubData = data.data;
$: searchString = '';
$: filteredLabels = githubData.edges;
$: searchItems = githubData.edges;
let searchString = '';
$: $selectedLabels, filterLabels();
const filterLabels = () => {
filteredLabels = githubData.edges.filter((githubDataset) =>
$selectedLabels.every((label) =>
githubDataset.node.labels.edges.some((edge) => edge.node.name === label),
),
);
};
if (!checked) checked = false;
const fetchMore = async () => {
loadDisabled = true;
const res = await fetch('/api/get-issues', {
method: 'POST',
body: JSON.stringify({
after: githubData.pageInfo.endCursor,
query: checked ? query.global : query.org,
}),
});
if (res.ok) {
const respData = (await res.json()) as SearchResponse;
githubData.edges = [...githubData.edges, ...respData.edges];
githubData.pageInfo = respData.pageInfo;
const uniqueLabels = [...githubData.labels, ...respData.labels];
githubData.labels = [...new Set(uniqueLabels)];
}
loadDisabled = false;
};
$: issues = createInfiniteQuery({
queryKey: ['issues', { global: checked }],
queryFn: ({ pageParam }) => fetchIssues(checked ? query.global : query.org, pageParam),
getNextPageParam: (lastPage) => {
if (lastPage.pageInfo.hasNextPage) return lastPage.pageInfo.endCursor;
return undefined;
},
});
afterNavigate(() => {
disableScrollHandling();
});
const onChangeHandler = async () => {
if (checked) {
await goto('?global=true');
performSearch();
await goto('?global=true', { noScroll: true });
return;
}
await goto('?global=false');
performSearch();
await goto('?global=false', { noScroll: true });
};
const performSearch = () => {
searchItems = githubData.edges.filter((el) =>
el.node.title.toLowerCase().includes(searchString.toLowerCase()),
);
};
$: uniqueTags = $issues.data?.pages?.reduce((acc, page) => {
page.labels.forEach((label) => {
if (!acc.includes(label)) {
acc.push(label);
}
});
return acc;
}, [] as string[]);
$: intersectedArray = filteredLabels.filter((item) => searchItems.includes(item));
$: filteredResponse = $issues.data?.pages?.flatMap((page) => {
return page.edges
.filter((edge) => {
const labels = edge.node.labels.edges.map((node) => node.node.name);
return $selectedLabels.every((label) => labels.includes(label));
})
.filter((edge) => {
if (searchString === '') {
return true;
}
const title = edge.node.title.toLowerCase();
return title.includes(searchString.toLowerCase());
});
});
// SEO Parameters
const title = 'Dashboard | Good First Issue Finder';
Expand All @@ -89,28 +86,28 @@
/>
</div>
</div>
<Search bind:searchTerm={searchString} on:keyup={() => performSearch()} />
<Search bind:searchTerm={searchString} />
</div>
{#if $navigating}
{#if $issues.isInitialLoading}
<div class="mt-8 flex items-center justify-center gap-4">
<Loader background="off-background" /> Loading...
</div>
{:else}
<div class="mb-8 flex flex-col items-center">
<Filter tags={githubData.labels} />
</div>
{#if intersectedArray.length > 0}
{:else if filteredResponse}
{#if filteredResponse.length < 1}
<div class="mt-4 text-center">Unfortunately, there were no issues found.</div>
{:else}
<div class="mb-8 flex flex-col items-center">
<Filter tags={uniqueTags || []} />
</div>
<div class="mb-4 space-y-4">
{#each intersectedArray as node}
<IssueCard issue={node.node} />
{#each filteredResponse as edge}
<IssueCard issue={edge.node} />
{/each}
{#if githubData.pageInfo.hasNextPage}
<div class="flex items-center justify-center">
<LoadMore isDisabled={loadDisabled} on:load={fetchMore} />
</div>
{/if}
</div>
{:else}
<div class="text-center">Unfortunately, there were no issues found.</div>
{#if $issues.hasNextPage}
<div class="flex items-center justify-center">
<LoadMore isDisabled={$issues.isFetchingNextPage} on:load={() => $issues.fetchNextPage()} />
</div>
{/if}
{/if}
{/if}
Loading

0 comments on commit 9f6dc4d

Please sign in to comment.