feat: add caching mechanism for pull requests list to improve performance and handle offline scenarios

This commit is contained in:
Kiril Videlov 2023-10-27 16:50:52 +02:00 committed by Kiril Videlov
parent b0374e2f7a
commit 3eba2c9bdb
2 changed files with 74 additions and 55 deletions

View File

@ -1,6 +1,8 @@
import { Octokit } from '@octokit/rest';
import { PullRequest, User, Label, type GitHubIntegrationContext } from '$lib/github/types';
import type { RestEndpointMethodTypes } from '@octokit/rest';
import { asyncWritable, type WritableLoadable, type Loadable } from '@square/svelte-store';
import lscache from 'lscache';
function newClient(ctx: GitHubIntegrationContext) {
return new Octokit({
@ -10,9 +12,27 @@ function newClient(ctx: GitHubIntegrationContext) {
});
}
export async function listPullRequests(
ctx: GitHubIntegrationContext
): Promise<PullRequest[] | undefined> {
// Uses the cached value as the initial state and also in the event of being offline
export function listPullRequestsWithCache(ctx: GitHubIntegrationContext): Loadable<PullRequest[]> {
const key = ctx.owner + '/' + ctx.repo;
const store = asyncWritable(
[],
async () => lscache.get(key) || [],
async (data) => data,
{ trackState: true },
(set) => {
listPullRequests(ctx).then((prs) => {
if (prs !== undefined) {
lscache.set(key, prs, 1440); // 1 day ttl
set(prs);
}
});
}
) as WritableLoadable<PullRequest[]>;
return store;
}
async function listPullRequests(ctx: GitHubIntegrationContext): Promise<PullRequest[] | undefined> {
const octokit = newClient(ctx);
try {
const rsp = await octokit.rest.pulls.list({

View File

@ -1,6 +1,6 @@
<script lang="ts">
import type { GitHubIntegrationContext } from '$lib/github/types';
import { listPullRequests } from '$lib/github/pullrequest';
import { listPullRequestsWithCache } from '$lib/github/pullrequest';
import TimeAgo from '$lib/components/TimeAgo/TimeAgo.svelte';
import { IconPullRequest, IconDraftPullRequest } from '$lib/icons';
import Scrollbar from '$lib/components/Scrollbar.svelte';
@ -10,7 +10,8 @@
import { createEventDispatcher } from 'svelte';
export let githubContext: GitHubIntegrationContext;
let pullRequestsPromise = listPullRequests(githubContext);
let prs = listPullRequestsWithCache(githubContext);
$: pullRequestsState = prs.state;
let rbViewport: HTMLElement;
let rbContents: HTMLElement;
@ -54,61 +55,59 @@
class="hide-native-scrollbar flex max-h-full flex-grow flex-col overflow-y-scroll overscroll-none"
>
<div bind:this={rbContents}>
{#await pullRequestsPromise}
{#if $pullRequestsState?.isLoading}
<span>loading...</span>
{:then prs}
{#if prs}
{#each prs as pr, i}
<div
role="button"
tabindex="0"
on:click={() => select(pr, i)}
on:keypress={() => select(pr, i)}
class="border-color-4 flex flex-col justify-between gap-1 border-b px-2 py-1 pt-2 -outline-offset-2 outline-blue-200 last:border-b focus:outline-2"
>
<div class="flex flex-row items-center gap-x-2">
<div>
{#if pr.draft}
<IconDraftPullRequest class="text-color-3 h-3.5 w-3.5"></IconDraftPullRequest>
{:else}
<IconPullRequest class="h-3.5 w-3.5 text-green-500"></IconPullRequest>
{/if}
</div>
<div class="text-color-2 flex-grow truncate font-semibold" title={pr.title}>
{pr.title}
</div>
</div>
<div
class="text-color-4 flex flex-row gap-x-1 whitespace-nowrap text-sm first-letter:items-center"
>
<span>
#{pr.number}
</span>
<span>
opened
<TimeAgo date={new Date(pr.created_at)} />
</span>
by
<span class="text-color-3 font-semibold">
{pr.author?.username}
</span>
{:else if $pullRequestsState?.isError}
<span>something went wrong</span>
{:else}
{#each $prs as pr, i}
<div
role="button"
tabindex="0"
on:click={() => select(pr, i)}
on:keypress={() => select(pr, i)}
class="border-color-4 flex flex-col justify-between gap-1 border-b px-2 py-1 pt-2 -outline-offset-2 outline-blue-200 last:border-b focus:outline-2"
>
<div class="flex flex-row items-center gap-x-2">
<div>
{#if pr.draft}
(draft)
{/if}
{#if pr.author?.is_bot}
<div
class="text-color-3 border-color-3 rounded-full border px-1.5 text-xs font-semibold"
>
bot
</div>
<IconDraftPullRequest class="text-color-3 h-3.5 w-3.5"></IconDraftPullRequest>
{:else}
<IconPullRequest class="h-3.5 w-3.5 text-green-500"></IconPullRequest>
{/if}
</div>
<div class="text-color-2 flex-grow truncate font-semibold" title={pr.title}>
{pr.title}
</div>
</div>
{/each}
{:else}
<span>something went wrong</span>
{/if}
{/await}
<div
class="text-color-4 flex flex-row gap-x-1 whitespace-nowrap text-sm first-letter:items-center"
>
<span>
#{pr.number}
</span>
<span>
opened
<TimeAgo date={new Date(pr.created_at)} />
</span>
by
<span class="text-color-3 font-semibold">
{pr.author?.username}
</span>
{#if pr.draft}
(draft)
{/if}
{#if pr.author?.is_bot}
<div
class="text-color-3 border-color-3 rounded-full border px-1.5 text-xs font-semibold"
>
bot
</div>
{/if}
</div>
</div>
{/each}
{/if}
</div>
</div>
<Scrollbar viewport={rbViewport} contents={rbContents} width="0.5rem" />