Skip to content

Commit b4cf904

Browse files
perf(tracker): stream in data instead of waiting for everything
1 parent 8e51d47 commit b4cf904

File tree

3 files changed

+104
-131
lines changed

3 files changed

+104
-131
lines changed

src/routes/tracker/[org]/[repo]/+layout.svelte

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
<script lang="ts">
22
import { onNavigate } from "$app/navigation";
33
import { resolve } from "$app/paths";
4-
import { navigating } from "$app/state";
5-
import { LoaderCircle, Menu } from "@lucide/svelte";
4+
import { Menu } from "@lucide/svelte";
65
import { uniqueRepos } from "$lib/repositories";
76
import { Button } from "$lib/components/ui/button";
87
import * as Sheet from "$lib/components/ui/sheet";
9-
import { Skeleton } from "$lib/components/ui/skeleton";
108
import RepoSidePanel from "./RepoSidePanel.svelte";
119
1210
let { params, children } = $props();
@@ -17,18 +15,6 @@
1715
});
1816
1917
let open = $state(false);
20-
21-
/**
22-
* The classic function that generates a random integer
23-
* between the specified minimum and maximum values, inclusive.
24-
*
25-
* @param min - The minimum value in the range.
26-
* @param max - The maximum value in the range.
27-
* @returns A random integer within the range [min, max].
28-
*/
29-
function random(min: number, max: number) {
30-
return Math.floor(Math.random() * (max - min + 1)) + min;
31-
}
3218
</script>
3319

3420
{#snippet repoList()}
@@ -57,32 +43,9 @@
5743
{/snippet}
5844

5945
<div class="relative flex gap-8">
60-
{#if navigating.to}
61-
<div class="flex w-full flex-col">
62-
<div class="mt-8 space-y-2">
63-
<Skeleton class="h-16 w-72" />
64-
<Skeleton class="h-8 w-56" />
65-
</div>
66-
<div class="relative w-full space-y-2">
67-
<p
68-
class="absolute top-44 left-1/2 z-10 inline-flex -translate-x-1/2 -translate-y-1/2 justify-center text-xl"
69-
>
70-
<LoaderCircle class="mr-2 h-lh shrink-0 animate-spin" />
71-
Gathering all the data, this may take some time...
72-
</p>
73-
{#each Array(3), i (i)}
74-
<Skeleton class="mt-16 mb-4 h-12 w-52" />
75-
{#each Array(random(1, 6)), j (j)}
76-
<Skeleton class="h-24 w-full" />
77-
{/each}
78-
{/each}
79-
</div>
80-
</div>
81-
{:else}
82-
<div class="min-w-0">
83-
{@render children()}
84-
</div>
85-
{/if}
46+
<div class="min-w-0">
47+
{@render children()}
48+
</div>
8649

8750
<Sheet.Root bind:open>
8851
<Sheet.Trigger>

src/routes/tracker/[org]/[repo]/+page.server.ts

Lines changed: 43 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -21,46 +21,51 @@ export async function load({ params }) {
2121
const membersNames = members.map(({ login }) => login);
2222
const now = new Date();
2323

24-
const [unfilteredPRs, unfilteredIssues, unfilteredDiscussions] = await Promise.all([
25-
githubCache.getAllPRs(params.org, params.repo),
26-
githubCache.getAllIssues(params.org, params.repo),
27-
githubCache.getAllDiscussions(params.org, params.repo)
28-
]);
2924
return {
30-
prs: unfilteredPRs
31-
.filter(({ user, body, updated_at }) => {
32-
if (new Date(updated_at).getFullYear() < now.getFullYear() - 1) return false;
33-
if (!membersNames.includes(user?.login ?? "")) return false;
34-
if (!body) return true;
35-
const lowerBody = body.toLowerCase();
36-
for (const keyword of closingKeywords) {
37-
if (
38-
lowerBody.includes(`${keyword} #`) ||
39-
lowerBody.includes(`${keyword}: #`) ||
40-
lowerBody.includes(`${keyword} https://github.com`) ||
41-
lowerBody.includes(`${keyword}: https://github.com`) ||
42-
new RegExp(`${keyword} [A-Za-z0-9_-]+/[A-Za-z0-9_-]+#[0-9]+`).test(lowerBody)
43-
) {
44-
return false;
25+
prs: githubCache.getAllPRs(params.org, params.repo).then(unfilteredPRs =>
26+
unfilteredPRs
27+
.filter(({ user, body, updated_at }) => {
28+
if (new Date(updated_at).getFullYear() < now.getFullYear() - 1) return false;
29+
if (!membersNames.includes(user?.login ?? "")) return false;
30+
if (!body) return true;
31+
const lowerBody = body.toLowerCase();
32+
for (const keyword of closingKeywords) {
33+
if (
34+
lowerBody.includes(`${keyword} #`) ||
35+
lowerBody.includes(`${keyword}: #`) ||
36+
lowerBody.includes(`${keyword} https://github.com`) ||
37+
lowerBody.includes(`${keyword}: https://github.com`) ||
38+
new RegExp(`${keyword} [A-Za-z0-9_-]+/[A-Za-z0-9_-]+#[0-9]+`).test(lowerBody)
39+
) {
40+
return false;
41+
}
4542
}
46-
}
47-
return true;
48-
})
49-
.toSorted((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()),
50-
issues: unfilteredIssues
51-
.filter(
52-
({ author_association, pull_request, updated_at }) =>
53-
!pull_request &&
54-
author_association === "MEMBER" &&
55-
new Date(updated_at).getFullYear() >= now.getFullYear() - 1
43+
return true;
44+
})
45+
.toSorted((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime())
46+
),
47+
issues: githubCache
48+
.getAllIssues(params.org, params.repo)
49+
.then(unfilteredIssues =>
50+
unfilteredIssues
51+
.filter(
52+
({ author_association, pull_request, updated_at }) =>
53+
!pull_request &&
54+
author_association === "MEMBER" &&
55+
new Date(updated_at).getFullYear() >= now.getFullYear() - 1
56+
)
57+
.toSorted((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime())
58+
),
59+
discussions: githubCache
60+
.getAllDiscussions(params.org, params.repo)
61+
.then(unfilteredDiscussions =>
62+
unfilteredDiscussions
63+
.filter(
64+
({ author_association, updated_at }) =>
65+
author_association === "MEMBER" &&
66+
new Date(updated_at).getFullYear() >= now.getFullYear() - 1
67+
)
68+
.toSorted((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime())
5669
)
57-
.toSorted((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()),
58-
discussions: unfilteredDiscussions
59-
.filter(
60-
({ author_association, updated_at }) =>
61-
author_association === "MEMBER" &&
62-
new Date(updated_at).getFullYear() >= now.getFullYear() - 1
63-
)
64-
.toSorted((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime())
6570
};
6671
}

src/routes/tracker/[org]/[repo]/+page.svelte

Lines changed: 57 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script lang="ts">
22
import { resolve } from "$app/paths";
3-
import { Image, Info } from "@lucide/svelte";
3+
import { Image, Info, LoaderCircle } from "@lucide/svelte";
44
import { Transparent } from "svelte-exmarkdown";
55
import { buttonVariants } from "$lib/components/ui/button";
66
import * as Dialog from "$lib/components/ui/dialog";
@@ -11,9 +11,9 @@
1111
let { data, params } = $props();
1212
1313
type Item =
14-
| NonNullable<typeof data.issues>[number]
15-
| NonNullable<typeof data.prs>[number]
16-
| NonNullable<typeof data.discussions>[number];
14+
| Awaited<NonNullable<typeof data.issues>>[number]
15+
| Awaited<NonNullable<typeof data.prs>>[number]
16+
| Awaited<NonNullable<typeof data.discussions>>[number];
1717
1818
/**
1919
* Checks whether a date is more recent than a month.
@@ -52,15 +52,30 @@
5252
}
5353
</script>
5454

55-
{#snippet list<T extends Item>(title: string, items: T[], itemToLink: (item: T) => string)}
55+
{#snippet list<T extends Item>(
56+
title: string,
57+
items: T[] | Promise<T[]>,
58+
itemToLink: (item: T) => string
59+
)}
5660
<div>
5761
<h2 class="mt-12 mb-4 text-3xl font-semibold tracking-tight">{title}</h2>
58-
{#each items as item, i (item.id)}
59-
{#if i > 0}
60-
<Separator class="my-1" />
61-
{/if}
62-
{@render listItem(item, itemToLink(item))}
63-
{/each}
62+
{#await items}
63+
<p class="mt-2 mb-4 inline-flex justify-center text-xl">
64+
<LoaderCircle class="mr-2 h-lh shrink-0 animate-spin" />
65+
Loading (this may take a while)...
66+
</p>
67+
{:then loadedItems}
68+
{#each loadedItems as item, i (item.id)}
69+
{#if i > 0}
70+
<Separator class="my-1" />
71+
{/if}
72+
{@render listItem(item, itemToLink(item))}
73+
{:else}
74+
<p class="text-lg">Nothing to show there :/</p>
75+
{/each}
76+
{:catch e}
77+
<p class="text-lg text-destructive">Failed to load elements: {e}</p>
78+
{/await}
6479
</div>
6580
{/snippet}
6681

@@ -188,46 +203,36 @@
188203
</div>
189204
</div>
190205

191-
{#if data.prs.length}
192-
{@render list("Pull requests", data.prs, pr =>
193-
resolve("/[pid=pid]/[org]/[repo]/[id=number]", {
194-
pid: "pull",
195-
org: pr.base.repo.owner.login,
196-
repo: pr.base.repo.name,
197-
id: `${pr.number}`
198-
})
199-
)}
200-
{/if}
206+
{@render list("Pull requests", data.prs, pr =>
207+
resolve("/[pid=pid]/[org]/[repo]/[id=number]", {
208+
pid: "pull",
209+
org: pr.base.repo.owner.login,
210+
repo: pr.base.repo.name,
211+
id: `${pr.number}`
212+
})
213+
)}
201214

202-
{#if data.discussions.length}
203-
{@render list("Discussions", data.discussions, d => {
204-
const [org = "", repo = ""] = d.repository_url
205-
.replace("https://api.github.com/repos/", "")
206-
.split("/");
207-
return resolve("/[pid=pid]/[org]/[repo]/[id=number]", {
208-
pid: "discussions",
209-
org,
210-
repo,
211-
id: `${d.number}`
212-
});
213-
})}
214-
{/if}
215+
{@render list("Discussions", data.discussions, d => {
216+
const [org = "", repo = ""] = d.repository_url
217+
.replace("https://api.github.com/repos/", "")
218+
.split("/");
219+
return resolve("/[pid=pid]/[org]/[repo]/[id=number]", {
220+
pid: "discussions",
221+
org,
222+
repo,
223+
id: `${d.number}`
224+
});
225+
})}
215226

216-
{#if data.issues.length}
217-
{@render list("Issues", data.issues, issue => {
218-
const [org = "", repo = ""] = issue.html_url
219-
.replace("https://github.com/", "")
220-
.replace(/\/[A-z]+\/\d+$/, "")
221-
.split("/");
222-
return resolve("/[pid=pid]/[org]/[repo]/[id=number]", {
223-
pid: "issues",
224-
org,
225-
repo,
226-
id: `${issue.number}`
227-
});
228-
})}
229-
{/if}
230-
231-
{#if [data.prs.length, data.discussions.length, data.issues.length].every(len => !len)}
232-
<div class="mt-16 text-2xl">Nothing interesting to show here :/</div>
233-
{/if}
227+
{@render list("Issues", data.issues, issue => {
228+
const [org = "", repo = ""] = issue.html_url
229+
.replace("https://github.com/", "")
230+
.replace(/\/[A-z]+\/\d+$/, "")
231+
.split("/");
232+
return resolve("/[pid=pid]/[org]/[repo]/[id=number]", {
233+
pid: "issues",
234+
org,
235+
repo,
236+
id: `${issue.number}`
237+
});
238+
})}

0 commit comments

Comments
 (0)