Found this nice way of doing async derived stores

This commit is contained in:
Kiril Videlov 2023-02-24 21:46:09 +01:00
parent e8d8d158ce
commit d9dc79a4b4
3 changed files with 78 additions and 38 deletions

View File

@ -17,6 +17,7 @@
"@codemirror/merge": "^0.1.3", "@codemirror/merge": "^0.1.3",
"@codemirror/state": "^6.2.0", "@codemirror/state": "^6.2.0",
"@codemirror/view": "^6.7.3", "@codemirror/view": "^6.7.3",
"@square/svelte-store": "^1.0.14",
"@tauri-apps/api": "^1.2.0", "@tauri-apps/api": "^1.2.0",
"date-fns": "^2.29.3", "date-fns": "^2.29.3",
"fluent-svelte": "^1.6.0", "fluent-svelte": "^1.6.0",
@ -35,16 +36,16 @@
"@sveltejs/kit": "next", "@sveltejs/kit": "next",
"@tauri-apps/cli": "^1.2.2", "@tauri-apps/cli": "^1.2.2",
"@types/diff": "^5.0.2", "@types/diff": "^5.0.2",
"autoprefixer": "^10.4.7",
"@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0", "@typescript-eslint/parser": "^5.45.0",
"autoprefixer": "^10.4.7",
"eslint": "^8.28.0", "eslint": "^8.28.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.5.0",
"eslint-plugin-svelte3": "^4.0.0", "eslint-plugin-svelte3": "^4.0.0",
"prettier": "^2.8.0",
"prettier-plugin-svelte": "^2.8.1",
"postcss": "^8.4.14", "postcss": "^8.4.14",
"postcss-load-config": "^4.0.1", "postcss-load-config": "^4.0.1",
"prettier": "^2.8.0",
"prettier-plugin-svelte": "^2.8.1",
"svelte": "^3.55.1", "svelte": "^3.55.1",
"svelte-check": "^3.0.1", "svelte-check": "^3.0.1",
"tailwindcss": "^3.1.5", "tailwindcss": "^3.1.5",

View File

@ -5,6 +5,7 @@ specifiers:
'@codemirror/merge': ^0.1.3 '@codemirror/merge': ^0.1.3
'@codemirror/state': ^6.2.0 '@codemirror/state': ^6.2.0
'@codemirror/view': ^6.7.3 '@codemirror/view': ^6.7.3
'@square/svelte-store': ^1.0.14
'@sveltejs/adapter-static': next '@sveltejs/adapter-static': next
'@sveltejs/kit': next '@sveltejs/kit': next
'@tauri-apps/api': ^1.2.0 '@tauri-apps/api': ^1.2.0
@ -43,6 +44,7 @@ dependencies:
'@codemirror/merge': 0.1.3 '@codemirror/merge': 0.1.3
'@codemirror/state': 6.2.0 '@codemirror/state': 6.2.0
'@codemirror/view': 6.7.3 '@codemirror/view': 6.7.3
'@square/svelte-store': 1.0.14
'@tauri-apps/api': 1.2.0 '@tauri-apps/api': 1.2.0
date-fns: 2.29.3 date-fns: 2.29.3
fluent-svelte: 1.6.0 fluent-svelte: 1.6.0
@ -411,6 +413,13 @@ packages:
resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==} resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==}
dev: true dev: true
/@square/svelte-store/1.0.14:
resolution: {integrity: sha512-kNaSpEec2JevfOB9XaWkq00oAOvm2fq8/0yotg++UVEHuHXQqM6pW602ByjBwFHn1gTRSdb568BAtkEDBt5uKQ==}
dependencies:
cookie-storage: 6.1.0
svelte: 3.55.1
dev: false
/@sveltejs/adapter-static/1.0.0-next.50_l5ueyfihz3gpzzvvyo2ean5u3e: /@sveltejs/adapter-static/1.0.0-next.50_l5ueyfihz3gpzzvvyo2ean5u3e:
resolution: {integrity: sha512-xZKBmiwFGW8nrH8+eysUAAo9XrtApI81q0m67y1bexVw8IY7/x741b6VEklNM7BZ7js0Mi2x+yCkHpOee8UZKQ==} resolution: {integrity: sha512-xZKBmiwFGW8nrH8+eysUAAo9XrtApI81q0m67y1bexVw8IY7/x741b6VEklNM7BZ7js0Mi2x+yCkHpOee8UZKQ==}
peerDependencies: peerDependencies:
@ -992,6 +1001,10 @@ packages:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
dev: true dev: true
/cookie-storage/6.1.0:
resolution: {integrity: sha512-HeVqbVy8BjXhAAuFtL6MTG+witHoLbxfky2jgVh9FmxmyL6IKa9gSSyPNjevXCCCxPu6Tzd9J8+eXTRQzYU/cg==}
dev: false
/cookie/0.5.0: /cookie/0.5.0:
resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}

View File

@ -2,9 +2,11 @@
import { themeIcons } from 'seti-icons'; import { themeIcons } from 'seti-icons';
import type { PageData } from './$types'; import type { PageData } from './$types';
import { derived } from 'svelte/store'; import { derived } from 'svelte/store';
import { asyncDerived } from '@square/svelte-store';
import type { Session } from '$lib/sessions'; import type { Session } from '$lib/sessions';
import { startOfDay } from 'date-fns'; import { startOfDay } from 'date-fns';
import { list as listDeltas } from '$lib/deltas'; import { list as listDeltas } from '$lib/deltas';
import type { Delta } from '$lib/deltas';
export let data: PageData; export let data: PageData;
const { project, sessions } = data; const { project, sessions } = data;
@ -48,17 +50,36 @@
return svg; return svg;
} }
$: dateSessions = derived([sessions], ([sessions]) => { type UISession = { session: Session; deltas: Record<string, Delta[]> };
const dateSessions: Record<number, Session[]> = {};
sessions.forEach((session) => { $: dateSessions = asyncDerived([sessions], async ([sessions]) => {
const date = startOfDay(new Date(session.meta.startTimestampMs)); const deltas = await Promise.all(
sessions.map((session) => {
return listDeltas({
projectId: $project?.id,
sessionId: session.id
});
})
);
const uiSessions = sessions
.map((session, i) => {
return { session, deltas: deltas[i] } as UISession;
})
.filter((uiSession) => {
return Object.keys(uiSession.deltas).length > 0;
});
const dateSessions: Record<number, UISession[]> = {};
uiSessions.forEach((uiSession) => {
const date = startOfDay(new Date(uiSession.session.meta.startTimestampMs));
if (dateSessions[date.getTime()]) { if (dateSessions[date.getTime()]) {
dateSessions[date.getTime()]?.push(session); dateSessions[date.getTime()]?.push(uiSession);
} else { } else {
dateSessions[date.getTime()] = [session]; dateSessions[date.getTime()] = [uiSession];
} }
}); });
// TODO: maybe sort?
return dateSessions; return dateSessions;
}); });
</script> </script>
@ -67,43 +88,48 @@
<div class="m-6 overflow-x-hidden w-full"> <div class="m-6 overflow-x-hidden w-full">
All sessions All sessions
<div class="flex flex-row border overflow-x-auto space-x-12 px-4 py-12"> <div class="flex flex-row border overflow-x-auto space-x-12 px-4 py-12">
{#each Object.entries($dateSessions) as [dateMilliseconds, sessions]} {#if $dateSessions === undefined}
<!-- Day --> <span>Loading...</span>
<div class="bg-zinc-600 py-1 min-w-full overflow-hidden"> {:else}
<div>{formatDate(new Date(+dateMilliseconds))}</div> {#each Object.entries($dateSessions) as [dateMilliseconds, uiSessions]}
<div class="flex space-x-2 "> <!-- Day -->
{#each sessions as session} <div class="bg-zinc-600 py-1 min-w-full overflow-hidden">
<!-- Session --> <div>{formatDate(new Date(+dateMilliseconds))}</div>
<div> <div class="flex space-x-2 ">
<div class="text-sm rounded borded bg-orange-500 text-zinc-200"> {#each uiSessions as uiSession}
{formatTime(new Date(session.meta.startTimestampMs))} <!-- Session -->
- <div>
{formatTime(new Date(session.meta.lastTimestampMs))} <div class="text-sm rounded borded bg-orange-500 text-zinc-200">
</div> {formatTime(new Date(uiSession.session.meta.startTimestampMs))}
<div title="Session duration"> -
{Math.round( {formatTime(new Date(uiSession.session.meta.lastTimestampMs))}
(session.meta.lastTimestampMs - session.meta.startTimestampMs) / 1000 / 60 </div>
)} min <div title="Session duration">
</div> {Math.round(
<div title="Session files"> (uiSession.session.meta.lastTimestampMs -
{#await listDeltas( { projectId: $project?.id, sessionId: session.id } ) then deltas} uiSession.session.meta.startTimestampMs) /
{#each Object.keys(deltas) as delta} 1000 /
60
)} min
</div>
<div title="Session files">
{#each Object.keys(uiSession.deltas) as filePath}
<div class="flex flex-row w-32 items-center"> <div class="flex flex-row w-32 items-center">
<div class="w-6 h-6 text-white fill-blue-400"> <div class="w-6 h-6 text-white fill-blue-400">
{@html pathToIconSvg(delta)} {@html pathToIconSvg(filePath)}
</div> </div>
<div class="text-white w-24 truncate"> <div class="text-white w-24 truncate">
{pathToName(delta)} {pathToName(filePath)}
</div> </div>
</div> </div>
{/each} {/each}
{/await} </div>
</div> </div>
</div> {/each}
{/each} </div>
</div> </div>
</div> {/each}
{/each} {/if}
</div> </div>
</div> </div>
</div> </div>