|
1 | 1 | <script lang="ts"> |
2 | 2 | import Button from '$lib/components/Button.svelte'; |
3 | 3 | import EmptyState from '$lib/components/EmptyState.svelte'; |
4 | | - import PeopleView from '$lib/components/PeopleView.svelte'; |
5 | | - import PhotoLightbox from '$lib/components/PhotoLightbox.svelte'; |
6 | | - import PlacesMap from '$lib/components/PlacesMap.svelte'; |
7 | 4 | import VirtualRows from '$lib/components/VirtualRows.svelte'; |
| 5 | + import { lazyComponent } from '$lib/composables/lazyComponent.svelte'; |
8 | 6 | import { useSelection } from '$lib/composables/useSelection.svelte'; |
9 | 7 | import { errorToast } from '$lib/utils/errors'; |
10 | 8 | import { onMount } from 'svelte'; |
|
24 | 22 |
|
25 | 23 | type Tab = 'moments' | 'places' | 'people'; |
26 | 24 | let tab = $state<Tab>('moments'); |
| 25 | +
|
| 26 | + // The lightbox, the (maplibre-backed) places map and the people view are all |
| 27 | + // heavy and off the initial path, so each loads on first use: the lightbox |
| 28 | + // when a photo is opened, the map/people views when their tab is selected. |
| 29 | + const photoLightbox = lazyComponent(() => import('$lib/components/PhotoLightbox.svelte')); |
| 30 | + const placesMap = lazyComponent(() => import('$lib/components/PlacesMap.svelte')); |
| 31 | + const peopleView = lazyComponent(() => import('$lib/components/PeopleView.svelte')); |
27 | 32 | let peopleAvailable = $state(false); |
28 | 33 |
|
29 | 34 | let items = $state<PhotoItem[]>([]); |
|
44 | 49 | const selected = useSelection(); |
45 | 50 | let lightbox = $state(-1); // index into `items`, -1 = closed |
46 | 51 |
|
| 52 | + $effect(() => { |
| 53 | + if (lightbox >= 0) void photoLightbox.load(); |
| 54 | + if (tab === 'places') void placesMap.load(); |
| 55 | + else if (tab === 'people') void peopleView.load(); |
| 56 | + }); |
| 57 | +
|
47 | 58 | /** Client-generated video frame thumbnails (file id → data/URL). */ |
48 | 59 | let videoThumbs = $state<Record<string, string>>({}); |
49 | 60 |
|
|
290 | 301 | ['large', 800, 800] |
291 | 302 | ]; |
292 | 303 | let previewData = ''; |
293 | | - for (const [size, w, h] of SIZES) { |
294 | | - const blob = await bitmapToBlob(bitmap, w, h); |
295 | | - if (size === 'preview') previewData = await blobToDataUrl(blob); |
296 | | - await uploadThumbnail(file.id, size, blob).catch(() => {}); |
297 | | - } |
| 304 | + // Render the blobs and push all three sizes in parallel; `previewData` |
| 305 | + // is captured before its upload so the local preview shows even if that |
| 306 | + // upload fails (allSettled swallows per-size failures, as before). |
| 307 | + await Promise.allSettled( |
| 308 | + SIZES.map(async ([size, w, h]) => { |
| 309 | + const blob = await bitmapToBlob(bitmap, w, h); |
| 310 | + if (size === 'preview') previewData = await blobToDataUrl(blob); |
| 311 | + await uploadThumbnail(file.id, size, blob); |
| 312 | + }) |
| 313 | + ); |
298 | 314 | if (previewData) videoThumbs = { ...videoThumbs, [file.id]: previewData }; |
299 | 315 | } catch { |
300 | 316 | // Keep the generic play badge on failure. |
|
487 | 503 | <div bind:this={sentinel} class="sentinel" aria-hidden="true"></div> |
488 | 504 | {#if loading}<p class="status">{t('common.loading', 'Loading…')}</p>{/if} |
489 | 505 |
|
490 | | - <PhotoLightbox {items} bind:index={lightbox} onDelete={onDeletePhoto} /> |
| 506 | + {#if photoLightbox.component} |
| 507 | + {@const PhotoLightbox = photoLightbox.component} |
| 508 | + <PhotoLightbox {items} bind:index={lightbox} onDelete={onDeletePhoto} /> |
| 509 | + {/if} |
491 | 510 | {:else if tab === 'places'} |
492 | | - <PlacesMap /> |
| 511 | + {#if placesMap.component} |
| 512 | + {@const PlacesMap = placesMap.component} |
| 513 | + <PlacesMap /> |
| 514 | + {/if} |
493 | 515 | {:else if tab === 'people'} |
494 | | - <PeopleView /> |
| 516 | + {#if peopleView.component} |
| 517 | + {@const PeopleView = peopleView.component} |
| 518 | + <PeopleView /> |
| 519 | + {/if} |
495 | 520 | {/if} |
496 | 521 |
|
497 | 522 | {#snippet tile(photo: PhotoItem, sizeStyle?: string)} |
|
0 commit comments