Skip to content

Commit

Permalink
perf(web): optimize images and modules (#7088)
Browse files Browse the repository at this point in the history
* perf: optimize images and modules

* fix: tests

* fix: missing font

* fix: delay showing the loading spinner

* simplify

* simplify

* pr feedback

* chore: merge main

* fix: enum

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
  • Loading branch information
martabal and alextran1502 committed Feb 18, 2024
1 parent 3480fe5 commit 36e5d29
Show file tree
Hide file tree
Showing 10 changed files with 796 additions and 59 deletions.
675 changes: 675 additions & 0 deletions web/package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@floating-ui/dom": "^1.5.1",
"@socket.io/component-emitter": "^3.1.0",
"@sveltejs/adapter-static": "^3.0.1",
"@sveltejs/enhanced-img": "^0.1.8",
"@sveltejs/kit": "^2.5.0",
"@sveltejs/vite-plugin-svelte": "^3.0.2",
"@testing-library/jest-dom": "^6.1.5",
Expand Down
34 changes: 22 additions & 12 deletions web/src/lib/components/album-page/album-card.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<script lang="ts">
import { api } from '$lib/api';
import noThumbnailUrl from '$lib/assets/no-thumbnail.png';
import Icon from '$lib/components/elements/icon.svelte';
import { locale } from '$lib/stores/preferences.store';
import { user } from '$lib/stores/user.store';
Expand All @@ -21,7 +20,7 @@
$: imageData = album.albumThumbnailAssetId
? getAssetThumbnailUrl(album.albumThumbnailAssetId, ThumbnailFormat.Webp)
: noThumbnailUrl;
: null;
const dispatchClick = createEventDispatcher<OnClick>();
const dispatchShowContextMenu = createEventDispatcher<OnShowContextMenu>();
Expand Down Expand Up @@ -50,7 +49,7 @@
dispatchShowContextMenu('showalbumcontextmenu', getContextMenuPosition(e));
onMount(async () => {
imageData = (await loadHighQualityThumbnail(album.albumThumbnailAssetId)) || noThumbnailUrl;
imageData = (await loadHighQualityThumbnail(album.albumThumbnailAssetId)) || null;
});
const getAlbumOwnerInfo = () => getUserById({ id: album.ownerId });
Expand Down Expand Up @@ -81,15 +80,26 @@
{/if}

<div class={`relative aspect-square`}>
<img
loading={preload ? 'eager' : 'lazy'}
src={imageData}
alt={album.id}
class={`z-0 h-full w-full rounded-xl object-cover transition-all duration-300 hover:shadow-lg`}
data-testid="album-image"
draggable="false"
/>
<div class="absolute top-0 h-full w-full rounded-3xl" />
{#if album.albumThumbnailAssetId}
<img
loading={preload ? 'eager' : 'lazy'}
src={imageData}
alt={album.id}
class="z-0 h-full w-full rounded-xl object-cover transition-all duration-300 hover:shadow-lg"
data-testid="album-image"
draggable="false"
/>
{:else}
<enhanced:img
loading={preload ? 'eager' : 'lazy'}
src="$lib/assets/no-thumbnail.png"
sizes="min(271px,186px)"
alt={album.id}
class="z-0 h-full w-full rounded-xl object-cover transition-all duration-300 hover:shadow-lg"
data-testid="album-image"
draggable="false"
/>
{/if}
</div>

<div class="mt-4">
Expand Down
56 changes: 33 additions & 23 deletions web/src/lib/components/asset-viewer/detail-panel.svelte
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<script lang="ts">
import Icon from '$lib/components/elements/icon.svelte';
import ChangeDate from '$lib/components/shared-components/change-date.svelte';
import { AppRoute, QueryParameter } from '$lib/constants';
import { AppRoute, QueryParameter, timeToLoadTheMap } from '$lib/constants';
import { boundingBoxesArray } from '$lib/stores/people.store';
import { locale } from '$lib/stores/preferences.store';
import { featureFlags } from '$lib/stores/server-config.store';
import { user } from '$lib/stores/user.store';
import { websocketEvents } from '$lib/stores/websocket';
import { getAssetThumbnailUrl, getPeopleThumbnailUrl, isSharedLink } from '$lib/utils';
import { getAssetFilename } from '$lib/utils/asset-utils';
import { delay, getAssetFilename } from '$lib/utils/asset-utils';
import { autoGrowHeight } from '$lib/utils/autogrow';
import { clickOutside } from '$lib/utils/click-outside';
import {
Expand Down Expand Up @@ -38,8 +38,8 @@
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
import PersonSidePanel from '../faces-page/person-side-panel.svelte';
import ChangeLocation from '../shared-components/change-location.svelte';
import Map from '../shared-components/map/map.svelte';
import UserAvatar from '../shared-components/user-avatar.svelte';
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
export let asset: AssetResponseDto;
export let albums: AlbumResponseDto[] = [];
Expand Down Expand Up @@ -609,27 +609,37 @@

{#if latlng && $featureFlags.loaded && $featureFlags.map}
<div class="h-[360px]">
<Map
mapMarkers={[{ lat: latlng.lat, lon: latlng.lng, id: asset.id }]}
center={latlng}
zoom={15}
simplified
useLocationPin
>
<svelte:fragment slot="popup" let:marker>
{@const { lat, lon } = marker}
<div class="flex flex-col items-center gap-1">
<p class="font-bold">{lat.toPrecision(6)}, {lon.toPrecision(6)}</p>
<a
href="https://www.openstreetmap.org/?mlat={lat}&mlon={lon}&zoom=15#map=15/{lat}/{lon}"
target="_blank"
class="font-medium text-immich-primary"
>
Open in OpenStreetMap
</a>
{#await import('../shared-components/map/map.svelte')}
{#await delay(timeToLoadTheMap) then}
<!-- show the loading spinner only if loading the map takes too much time -->
<div class="flex items-center justify-center h-full w-full">
<LoadingSpinner />
</div>
</svelte:fragment>
</Map>
{/await}
{:then component}
<svelte:component
this={component.default}
mapMarkers={[{ lat: latlng.lat, lon: latlng.lng, id: asset.id }]}
center={latlng}
zoom={15}
simplified
useLocationPin
>
<svelte:fragment slot="popup" let:marker>
{@const { lat, lon } = marker}
<div class="flex flex-col items-center gap-1">
<p class="font-bold">{lat.toPrecision(6)}, {lon.toPrecision(6)}</p>
<a
href="https://www.openstreetmap.org/?mlat={lat}&mlon={lon}&zoom=15#map=15/{lat}/{lon}"
target="_blank"
class="font-medium text-immich-primary"
>
Open in OpenStreetMap
</a>
</div>
</svelte:fragment>
</svelte:component>
{/await}
</div>
{/if}

Expand Down
46 changes: 32 additions & 14 deletions web/src/lib/components/memory-page/memory-viewer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -168,14 +168,22 @@
class:hover:opacity-70={previousMemory}
>
<button class="relative h-full w-full rounded-2xl" disabled={!previousMemory} on:click={toPreviousMemory}>
<img
class="h-full w-full rounded-2xl object-cover"
src={previousMemory
? getAssetThumbnailUrl(previousMemory.assets[0].id, ThumbnailFormat.Jpeg)
: noThumbnailUrl}
alt=""
draggable="false"
/>
{#if previousMemory}
<img
class="h-full w-full rounded-2xl object-cover"
src={getAssetThumbnailUrl(previousMemory.assets[0].id, ThumbnailFormat.Jpeg)}
alt=""
draggable="false"
/>
{:else}
<enhanced:img
class="h-full w-full rounded-2xl object-cover"
src={noThumbnailUrl}
sizes="min(271px,186px)"
alt=""
draggable="false"
/>
{/if}

{#if previousMemory}
<div class="absolute bottom-4 right-4 text-left text-white">
Expand Down Expand Up @@ -233,12 +241,22 @@
class:hover:opacity-70={nextMemory}
>
<button class="relative h-full w-full rounded-2xl" on:click={toNextMemory} disabled={!nextMemory}>
<img
class="h-full w-full rounded-2xl object-cover"
src={nextMemory ? getAssetThumbnailUrl(nextMemory.assets[0].id, ThumbnailFormat.Jpeg) : noThumbnailUrl}
alt=""
draggable="false"
/>
{#if nextMemory}
<img
class="h-full w-full rounded-2xl object-cover"
src={getAssetThumbnailUrl(nextMemory.assets[0].id, ThumbnailFormat.Jpeg)}
alt=""
draggable="false"
/>
{:else}
<enhanced:img
class="h-full w-full rounded-2xl object-cover"
src={noThumbnailUrl}
sizes="min(271px,186px)"
alt=""
draggable="false"
/>
{/if}

{#if nextMemory}
<div class="absolute bottom-4 left-4 text-left text-white">
Expand Down
31 changes: 22 additions & 9 deletions web/src/lib/components/shared-components/change-location.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
import type { AssetResponseDto } from '@immich/sdk';
import { createEventDispatcher } from 'svelte';
import ConfirmDialogue from './confirm-dialogue.svelte';
import Map from './map/map.svelte';
import LoadingSpinner from './loading-spinner.svelte';
import { delay } from '$lib/utils/asset-utils';
import { timeToLoadTheMap } from '$lib/constants';
export const title = 'Change Location';
export let asset: AssetResponseDto | undefined = undefined;
Expand Down Expand Up @@ -48,14 +51,24 @@
<div slot="prompt" class="flex flex-col w-full h-full gap-2">
<label for="datetime">Pick a location</label>
<div class="h-[500px] min-h-[300px] w-full">
<Map
mapMarkers={lat && lng && asset ? [{ id: asset.id, lat, lon: lng }] : []}
{zoom}
center={lat && lng ? { lat, lng } : undefined}
simplified={true}
clickable={true}
on:clickedPoint={({ detail: point }) => handleSelect(point)}
/>
{#await import('../shared-components/map/map.svelte')}
{#await delay(timeToLoadTheMap) then}
<!-- show the loading spinner only if loading the map takes too much time -->
<div class="flex items-center justify-center h-full w-full">
<LoadingSpinner />
</div>
{/await}
{:then component}
<svelte:component
this={component.default}
mapMarkers={lat && lng && asset ? [{ id: asset.id, lat, lon: lng }] : []}
{zoom}
center={lat && lng ? { lat, lng } : undefined}
simplified={true}
clickable={true}
on:clickedPoint={({ detail: point }) => handleSelect(point)}
/>
{/await}
</div>
</div>
</ConfirmDialogue>
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
/>
{/await}
{:else}
<img
<enhanced:img
src={noThumbnailUrl}
alt={'Album without assets'}
class="h-[100px] w-[100px] rounded-lg object-cover"
Expand Down
4 changes: 4 additions & 0 deletions web/src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,11 @@ export enum ActionQueryParameterValue {

export const maximumLengthSearchPeople: number = 20;

// time to load the map before displaying the loading spinner
export const timeToLoadTheMap: number = 100;

export const timeBeforeShowLoadingSpinner: number = 100;

// should be the same values as the ones in the app.html
export enum Theme {
LIGHT = 'light',
Expand Down
4 changes: 4 additions & 0 deletions web/src/lib/utils/asset-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,7 @@ export const getSelectedAssets = (assets: Set<AssetResponseDto>, user: UserRespo
}
return ids;
};

export const delay = async (ms: number) => {
return new Promise((resolve) => setTimeout(resolve, ms));
};
2 changes: 2 additions & 0 deletions web/vite.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { enhancedImages } from '@sveltejs/enhanced-img';
import { sveltekit } from '@sveltejs/kit/vite';
import path from 'node:path';
import { visualizer } from 'rollup-plugin-visualizer';
Expand Down Expand Up @@ -33,6 +34,7 @@ export default defineConfig({
emitFile: true,
filename: 'stats.html',
}),
enhancedImages(),
],
optimizeDeps: {
entries: ['src/**/*.{svelte,ts,html}'],
Expand Down

0 comments on commit 36e5d29

Please sign in to comment.