mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2025-01-06 01:27:24 +03:00
feat: add caching mechanism for pull requests list to improve performance and handle offline scenarios
This commit is contained in:
parent
b0374e2f7a
commit
3eba2c9bdb
@ -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({
|
||||
|
@ -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" />
|
||||
|
Loading…
Reference in New Issue
Block a user