diff --git a/packages/ui/src/lib/components/Differ/Differ.svelte b/packages/ui/src/lib/components/Differ/Differ.svelte
index acd6d904d..55d6d2069 100644
--- a/packages/ui/src/lib/components/Differ/Differ.svelte
+++ b/packages/ui/src/lib/components/Differ/Differ.svelte
@@ -199,7 +199,7 @@
{#each rows as row}
@@ -211,24 +211,20 @@
row.type === RowType.Equal || row.type === RowType.Addition
? String(row.currentLineNumber)
: ''}
-
+
{baseNumber}
-
+
{curNumber}
{#each row.render.html as content}
diff --git a/packages/ui/src/lib/stores/bookmarks.ts b/packages/ui/src/lib/stores/bookmarks.ts
index 01d9c39f5..f99649e3b 100644
--- a/packages/ui/src/lib/stores/bookmarks.ts
+++ b/packages/ui/src/lib/stores/bookmarks.ts
@@ -1,36 +1,29 @@
-import { writable, type Loadable, derived, Loaded } from 'svelte-loadable-store';
import * as bookmarks from '$lib/api/ipc/bookmarks';
-import { get as getValue, type Readable } from '@square/svelte-store';
+import { type Loadable, asyncWritable, asyncDerived } from '@square/svelte-store';
-const stores: Partial>>> = {};
-
-export function getBookmarksStore(params: {
- projectId: string;
-}): Readable> {
- const cached = stores[params.projectId];
- if (cached) return cached;
-
- const store = writable(bookmarks.list(params), (set) => {
- const unsubscribe = bookmarks.subscribe(params, (bookmark) => {
- const oldValue = getValue(store);
- if (oldValue.isLoading) {
- bookmarks.list(params).then(set);
- } else if (Loaded.isError(oldValue)) {
- bookmarks.list(params).then(set);
- } else {
- set(oldValue.value.filter((b) => b.timestampMs !== bookmark.timestampMs).concat(bookmark));
- }
- });
- return () => {
- Promise.resolve(unsubscribe).then((unsubscribe) => unsubscribe());
- };
- });
- stores[params.projectId] = store;
- return store as Readable>;
+export function getBookmarksStore(params: { projectId: string }): Loadable {
+ return asyncWritable(
+ [],
+ async () => await bookmarks.list(params),
+ undefined,
+ { trackState: true },
+ (set, update) => {
+ const unsubscribe = bookmarks.subscribe(params, (bookmark) => {
+ update((oldValue) =>
+ oldValue.filter((b) => b.timestampMs !== bookmark.timestampMs).concat(bookmark)
+ );
+ });
+ return () => {
+ Promise.resolve(unsubscribe).then((unsubscribe) => unsubscribe());
+ };
+ }
+ );
}
export function getBookmark(params: { projectId: string; timestampMs: number }) {
- return derived(getBookmarksStore({ projectId: params.projectId }), (bookmarks) =>
- bookmarks.find((b) => b.timestampMs === params.timestampMs)
+ return asyncDerived(
+ getBookmarksStore({ projectId: params.projectId }),
+ async (bookmarks) => bookmarks.find((b) => b.timestampMs === params.timestampMs),
+ { trackState: true }
);
}
diff --git a/packages/ui/src/lib/stores/deltas.ts b/packages/ui/src/lib/stores/deltas.ts
index 109b3b263..778b3064b 100644
--- a/packages/ui/src/lib/stores/deltas.ts
+++ b/packages/ui/src/lib/stores/deltas.ts
@@ -1,6 +1,5 @@
-import { asyncWritable, isReloadable } from '@square/svelte-store';
+import { asyncWritable, isReloadable, type AsyncWritable, type Stores } from '@square/svelte-store';
import { subscribeToDeltas, type Delta, listDeltas } from '$lib/api/ipc/deltas';
-import type { Stores, Writable } from 'svelte/store';
/**
* We have a special situation here where we use deltas to know when to re-run
@@ -10,20 +9,23 @@ import type { Stores, Writable } from 'svelte/store';
*/
export function getDeltasStore(
projectId: string,
- sessionId: string | undefined = undefined
-): Writable>> & { setSessionId: (sid: string) => void } {
+ sessionId: string | undefined = undefined,
+ subscribe = false
+): AsyncWritable>> & { setSessionId: (sid: string) => void } {
let unsubscribe: () => void;
const store = asyncWritable>>(
[],
async () => {
if (!sessionId) return {};
if (unsubscribe) unsubscribe();
- unsubscribe = subscribeToDeltas(projectId, sessionId, ({ filePath, deltas }) => {
- store.update((storeValue) => {
- storeValue[filePath] = [...(storeValue[filePath] || []), ...deltas];
- return storeValue;
+ if (subscribe) {
+ unsubscribe = subscribeToDeltas(projectId, sessionId, ({ filePath, deltas }) => {
+ store.update((storeValue) => {
+ storeValue[filePath] = [...(storeValue[filePath] || []), ...deltas];
+ return storeValue;
+ });
});
- });
+ }
return await listDeltas({ projectId, sessionId });
},
undefined,
diff --git a/packages/ui/src/lib/stores/files.ts b/packages/ui/src/lib/stores/files.ts
deleted file mode 100644
index 087e50ad3..000000000
--- a/packages/ui/src/lib/stores/files.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { writable, type Loadable, Loaded } from 'svelte-loadable-store';
-import * as files from '$lib/api/ipc/files';
-import { get, type Readable } from '@square/svelte-store';
-
-type Files = Partial>;
-
-const stores: Partial>>> = {};
-
-export function getFilesStore(params: {
- projectId: string;
- sessionId: string;
-}): Readable> {
- const key = `${params.projectId}/${params.sessionId}`;
- const cached = stores[key];
- if (cached) return cached;
-
- const store = writable(files.list(params), (set) => {
- const unsubscribe = files.subscribe(params, ({ filePath, contents }) => {
- const oldValue = get(store);
- if (oldValue.isLoading) {
- files.list(params).then(set);
- } else if (Loaded.isError(oldValue)) {
- files.list(params).then(set);
- } else {
- set({
- ...oldValue.value,
- [filePath]: contents || undefined
- });
- }
- });
- return () => {
- Promise.resolve(unsubscribe).then((unsubscribe) => unsubscribe());
- };
- });
- stores[key] = store;
- return store as Readable>;
-}
diff --git a/packages/ui/src/routes/projects/[projectId]/+layout.svelte b/packages/ui/src/routes/projects/[projectId]/+layout.svelte
index e66e13f41..ed67cf5ce 100644
--- a/packages/ui/src/routes/projects/[projectId]/+layout.svelte
+++ b/packages/ui/src/routes/projects/[projectId]/+layout.svelte
@@ -63,7 +63,7 @@
goto(`/projects/${$project.id}/player`)}
+ on:click={() => goto(`/projects/${$project?.id}/player`)}
kind="plain"
icon={IconRewind}
/>
@@ -72,7 +72,7 @@
goto(`/projects/${$project.id}/settings`)}
+ on:click={() => goto(`/projects/${$project?.id}/settings`)}
kind="plain"
icon={IconSettings}
/>
diff --git a/packages/ui/src/routes/projects/[projectId]/+layout.ts b/packages/ui/src/routes/projects/[projectId]/+layout.ts
index 49a0b0689..0155245f6 100644
--- a/packages/ui/src/routes/projects/[projectId]/+layout.ts
+++ b/packages/ui/src/routes/projects/[projectId]/+layout.ts
@@ -3,7 +3,6 @@ import { getSessionStore } from '$lib/stores/sessions';
import { getDiffsStore } from '$lib/api/git/diffs';
import { error } from '@sveltejs/kit';
import type { LayoutLoad } from './$types';
-import type { Loadable } from '@square/svelte-store';
import { getProjectStore, type Project } from '$lib/api/ipc/projects';
export const prerender = false;
@@ -15,6 +14,6 @@ export const load: LayoutLoad = async ({ params }) => {
head: getHeadStore(params.projectId),
sessions: getSessionStore(params.projectId),
diffs: getDiffsStore({ projectId: params.projectId }),
- project: project as Loadable & Pick
+ project: project
};
};
diff --git a/packages/ui/src/routes/projects/[projectId]/+page.svelte b/packages/ui/src/routes/projects/[projectId]/+page.svelte
index 1dd6dce51..bfdb06e9f 100644
--- a/packages/ui/src/routes/projects/[projectId]/+page.svelte
+++ b/packages/ui/src/routes/projects/[projectId]/+page.svelte
@@ -2,24 +2,18 @@
import { getTime, subDays } from 'date-fns';
import type { PageData } from './$types';
import { IconGitBranch } from '$lib/icons';
- import { derived } from '@square/svelte-store';
+ import { asyncDerived } from '@square/svelte-store';
import FileSummaries from './FileSummaries.svelte';
import { Tooltip } from '$lib/components';
- import Chat from './Chat.svelte';
export let data: PageData;
const { project, sessions, head } = data;
- $: recentSessions = derived(
+ $: recentSessions = asyncDerived(
sessions,
- (item) => {
- const lastFourDaysOfSessions = item?.filter(
- (result) => result.meta.startTimestampMs >= getTime(subDays(new Date(), 4))
- );
- if (lastFourDaysOfSessions?.length >= 4) return lastFourDaysOfSessions;
- return item?.slice(0, 4).sort((a, b) => b.meta.startTimestampMs - a.meta.startTimestampMs);
- },
- []
+ async (item) =>
+ item?.filter((result) => result.meta.startTimestampMs >= getTime(subDays(new Date(), 4))),
+ { trackState: true }
);
@@ -46,9 +40,9 @@
-
diff --git a/packages/ui/src/routes/projects/[projectId]/FileSummaries.svelte b/packages/ui/src/routes/projects/[projectId]/FileSummaries.svelte
index 0be63ce20..0e79580a1 100644
--- a/packages/ui/src/routes/projects/[projectId]/FileSummaries.svelte
+++ b/packages/ui/src/routes/projects/[projectId]/FileSummaries.svelte
@@ -2,7 +2,6 @@
import { format, startOfDay } from 'date-fns';
import type { Delta } from '$lib/api/ipc/deltas';
import { generateBuckets } from './histogram';
- import { derived, Loaded } from 'svelte-loadable-store';
import FileActivity from './FileActivity.svelte';
import { page } from '$app/stores';
import { Link } from '$lib/components';
@@ -10,13 +9,14 @@
import { collapse } from '$lib/paths';
import type { Session } from '$lib/api/ipc/sessions';
import { getDeltasStore } from '$lib/stores/deltas';
+ import { asyncDerived } from '@square/svelte-store';
export let sessions: Session[];
$: sessionDeltas = (sessions ?? []).map(({ id, projectId }) => getDeltasStore(projectId, id));
- $: deltasByDate = derived(sessionDeltas, (sessionDeltas) =>
- sessionDeltas.reduce(
+ $: deltasByDate = asyncDerived(sessionDeltas, async (sessionDeltas) => {
+ return sessionDeltas.reduce(
(acc, sessionDelta) => {
Object.entries(sessionDelta).forEach(([filepath, deltas]) => {
if (!deltas) return;
@@ -28,10 +28,11 @@
return acc;
},
{} as Record
>
- )
- );
+ );
+ });
+ $: deltasByDateState = deltasByDate?.state;
- $: buckets = derived(sessionDeltas, (sessionDeltas) => {
+ $: buckets = asyncDerived(sessionDeltas, async (sessionDeltas) => {
const deltas = sessionDeltas.flatMap((deltas) => Object.values(deltas).flat() as Delta[]);
const timestamps = deltas.map((delta) => delta.timestampMs);
return generateBuckets(timestamps, 18);
@@ -39,14 +40,14 @@
- {#if $deltasByDate.isLoading || $buckets.isLoading}
+ {#if $deltasByDate?.isLoading}
Loading file changes...
- {:else if Loaded.isError($deltasByDate) || Loaded.isError($buckets)}
+ {:else if $deltasByDateState?.isError}
@@ -54,14 +55,14 @@
We couldn't load your file changes. Please try again later.
- {:else}
- {#each Object.entries($deltasByDate.value) as [ts, fileDeltas]}
+ {:else if $deltasByDate}
+ {#each Object.entries($deltasByDate) as [ts, fileDeltas]}
{@const date = new Date(ts)}