1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2024-12-29 15:11:58 +00:00

feat(web): theme/locale preferences and improve SSR (#1832)

This commit is contained in:
Michel Heusschen 2023-02-22 18:53:08 +01:00 committed by GitHub
parent a9a769d902
commit 10cb612fb1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 142 additions and 144 deletions

View file

@ -0,0 +1,3 @@
module.exports = {
browser: false
};

18
web/package-lock.json generated
View file

@ -16,6 +16,7 @@
"luxon": "^3.1.1",
"rxjs": "^7.8.0",
"socket.io-client": "^4.5.1",
"svelte-local-storage-store": "^0.4.0",
"svelte-material-icons": "^2.0.2"
},
"devDependencies": {
@ -10584,6 +10585,17 @@
"svelte": ">= 3"
}
},
"node_modules/svelte-local-storage-store": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/svelte-local-storage-store/-/svelte-local-storage-store-0.4.0.tgz",
"integrity": "sha512-ctPykTt4S3BE5bF0mfV0jKiUR1qlmqLvnAkQvYHLeb9wRyO1MdIFDVI23X+TZEFleATHkTaOpYZswIvf3b2tWA==",
"engines": {
"node": ">=0.14"
},
"peerDependencies": {
"svelte": "^3.48.0"
}
},
"node_modules/svelte-material-icons": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/svelte-material-icons/-/svelte-material-icons-2.0.4.tgz",
@ -19014,6 +19026,12 @@
"dev": true,
"requires": {}
},
"svelte-local-storage-store": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/svelte-local-storage-store/-/svelte-local-storage-store-0.4.0.tgz",
"integrity": "sha512-ctPykTt4S3BE5bF0mfV0jKiUR1qlmqLvnAkQvYHLeb9wRyO1MdIFDVI23X+TZEFleATHkTaOpYZswIvf3b2tWA==",
"requires": {}
},
"svelte-material-icons": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/svelte-material-icons/-/svelte-material-icons-2.0.4.tgz",

View file

@ -68,6 +68,7 @@
"luxon": "^3.1.1",
"rxjs": "^7.8.0",
"socket.io-client": "^4.5.1",
"svelte-local-storage-store": "^0.4.0",
"svelte-material-icons": "^2.0.2"
}
}

View file

@ -4,6 +4,15 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
<script>
/**
* Prevent FOUC on page load.
*/
const theme = localStorage.getItem('color-theme') || 'dark';
if (theme === 'light') {
document.documentElement.classList.remove('dark');
}
</script>
</head>
<body class="bg-immich-bg dark:bg-immich-dark-bg">

View file

@ -3,7 +3,7 @@
import SelectionSearch from 'svelte-material-icons/SelectionSearch.svelte';
import Play from 'svelte-material-icons/Play.svelte';
import AllInclusive from 'svelte-material-icons/AllInclusive.svelte';
import { locale } from '$lib/stores/preferences.store';
import { createEventDispatcher } from 'svelte';
import { JobCounts } from '@api';
@ -22,8 +22,6 @@
const run = (includeAllAssets: boolean) => {
dispatch('click', { includeAllAssets });
};
const locale = navigator.language;
</script>
<div class="flex justify-between rounded-3xl bg-gray-100 dark:bg-immich-dark-gray">
@ -45,7 +43,7 @@
<p>Active</p>
<p class="text-2xl">
{#if jobCounts.active !== undefined}
{jobCounts.active.toLocaleString(locale)}
{jobCounts.active.toLocaleString($locale)}
{:else}
<LoadingSpinner />
{/if}
@ -57,7 +55,7 @@
>
<p class="text-2xl">
{#if jobCounts.waiting !== undefined}
{jobCounts.waiting.toLocaleString(locale)}
{jobCounts.waiting.toLocaleString($locale)}
{:else}
<LoadingSpinner />
{/if}

View file

@ -7,6 +7,7 @@
import { getBytesWithUnit, asByteUnitString } from '../../../utils/byte-units';
import { onMount, onDestroy } from 'svelte';
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
import { locale } from '$lib/stores/preferences.store';
export let allUsers: Array<UserResponseDto>;
@ -37,8 +38,6 @@
// Stats are unavailable if data is not loaded yet
$: [spaceUsage, spaceUnit] = getBytesWithUnit(stats ? stats.usageRaw : 0);
const locale = navigator.language;
</script>
<div class="flex flex-col gap-5">
@ -83,8 +82,10 @@
}`}
>
<td class="text-sm px-2 w-1/4 text-ellipsis">{getFullName(user.userId)}</td>
<td class="text-sm px-2 w-1/4 text-ellipsis">{user.photos.toLocaleString(locale)}</td>
<td class="text-sm px-2 w-1/4 text-ellipsis">{user.videos.toLocaleString(locale)}</td>
<td class="text-sm px-2 w-1/4 text-ellipsis">{user.photos.toLocaleString($locale)}</td
>
<td class="text-sm px-2 w-1/4 text-ellipsis">{user.videos.toLocaleString($locale)}</td
>
<td class="text-sm px-2 w-1/4 text-ellipsis">{asByteUnitString(user.usageRaw)}</td>
</tr>
{/each}

View file

@ -17,6 +17,7 @@
import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
import CircleIconButton from '../shared-components/circle-icon-button.svelte';
import noThumbnailUrl from '$lib/assets/no-thumbnail.png';
import { locale } from '$lib/stores/preferences.store';
export let album: AlbumResponseDto;
@ -52,8 +53,6 @@
onMount(async () => {
imageData = (await loadHighQualityThumbnail(album.albumThumbnailAssetId)) || noThumbnailUrl;
});
const locale = navigator.language;
</script>
<div
@ -91,7 +90,10 @@
</p>
<span class="text-xs flex gap-2 dark:text-immich-dark-fg" data-testid="album-details">
<p>{album.assetCount.toLocaleString(locale)} {album.assetCount == 1 ? `item` : `items`}</p>
<p>
{album.assetCount.toLocaleString($locale)}
{album.assetCount == 1 ? `item` : `items`}
</p>
{#if album.shared}
<p>·</p>

View file

@ -39,6 +39,7 @@
import ThemeButton from '../shared-components/theme-button.svelte';
import { openFileUploadDialog } from '$lib/utils/file-uploader';
import { bulkDownload } from '$lib/utils/asset-utils';
import { locale } from '$lib/stores/preferences.store';
import GalleryViewer from '../shared-components/gallery-viewer/gallery-viewer.svelte';
import ImmichLogo from '../shared-components/immich-logo.svelte';
@ -88,7 +89,6 @@
}
});
const locale = navigator.language;
const albumDateFormat: Intl.DateTimeFormatOptions = {
month: 'short',
day: 'numeric',
@ -99,8 +99,8 @@
const startDate = new Date(album.assets[0].fileCreatedAt);
const endDate = new Date(album.assets[album.assetCount - 1].fileCreatedAt);
const startDateString = startDate.toLocaleDateString(locale, albumDateFormat);
const endDateString = endDate.toLocaleDateString(locale, albumDateFormat);
const startDateString = startDate.toLocaleDateString($locale, albumDateFormat);
const endDateString = endDate.toLocaleDateString($locale, albumDateFormat);
// If the start and end date are the same, only show one date
return startDateString === endDateString
@ -380,7 +380,7 @@
>
<svelte:fragment slot="leading">
<p class="font-medium text-immich-primary dark:text-immich-dark-primary">
Selected {multiSelectAsset.size.toLocaleString(locale)}
Selected {multiSelectAsset.size.toLocaleString($locale)}
</p>
</svelte:fragment>
<svelte:fragment slot="trailing">

View file

@ -11,12 +11,12 @@
assetsInAlbumStoreState,
selectedAssets
} from '$lib/stores/asset-interaction.store';
import { locale } from '$lib/stores/preferences.store';
const dispatch = createEventDispatcher();
export let albumId: string;
export let assetsInAlbum: AssetResponseDto[];
const locale = navigator.language;
onMount(() => {
$assetsInAlbumStoreState = assetsInAlbum;
@ -51,7 +51,7 @@
<p class="text-lg dark:text-immich-dark-fg">Add to album</p>
{:else}
<p class="text-lg dark:text-immich-dark-fg">
{$selectedAssets.size.toLocaleString(locale)} selected
{$selectedAssets.size.toLocaleString($locale)} selected
</p>
{/if}
</svelte:fragment>

View file

@ -24,6 +24,7 @@
import { assetStore } from '$lib/stores/assets.store';
import { addAssetsToAlbum } from '$lib/utils/asset-utils';
import { browser } from '$app/environment';
export let asset: AssetResponseDto;
export let publicSharedKey = '';
@ -54,7 +55,9 @@
});
onDestroy(() => {
document.removeEventListener('keydown', onKeyboardPress);
if (browser) {
document.removeEventListener('keydown', onKeyboardPress);
}
});
$: asset.id && getAllAlbums(); // Update the album information when the asset ID changes

View file

@ -8,6 +8,7 @@
import { browser } from '$app/environment';
import { AssetResponseDto, AlbumResponseDto } from '@api';
import { asByteUnitString } from '../../utils/byte-units';
import { locale } from '$lib/stores/preferences.store';
type Leaflet = typeof import('leaflet');
type LeafletMap = import('leaflet').Map;
@ -69,8 +70,6 @@
return undefined;
};
const locale = navigator.language;
</script>
<section class="p-2 dark:bg-immich-dark-bg dark:text-immich-dark-fg">
@ -101,7 +100,7 @@
<div>
<p>
{assetDateTimeOriginal.toLocaleDateString(locale, {
{assetDateTimeOriginal.toLocaleDateString($locale, {
month: 'short',
day: 'numeric',
year: 'numeric'
@ -109,7 +108,7 @@
</p>
<div class="flex gap-2 text-sm">
<p>
{assetDateTimeOriginal.toLocaleString(locale, {
{assetDateTimeOriginal.toLocaleString($locale, {
weekday: 'short',
hour: 'numeric',
minute: '2-digit',
@ -149,14 +148,14 @@
<div>
<p>{asset.exifInfo.make || ''} {asset.exifInfo.model || ''}</p>
<div class="flex text-sm gap-2">
<p>{`ƒ/${asset.exifInfo.fNumber.toLocaleString(locale)}` || ''}</p>
<p>{`ƒ/${asset.exifInfo.fNumber.toLocaleString($locale)}` || ''}</p>
{#if asset.exifInfo.exposureTime}
<p>{`${asset.exifInfo.exposureTime}`}</p>
{/if}
{#if asset.exifInfo.focalLength}
<p>{`${asset.exifInfo.focalLength.toLocaleString(locale)} mm`}</p>
<p>{`${asset.exifInfo.focalLength.toLocaleString($locale)} mm`}</p>
{/if}
{#if asset.exifInfo.iso}

View file

@ -13,12 +13,13 @@
selectedAssets,
selectedGroup
} from '$lib/stores/asset-interaction.store';
import { locale } from '$lib/stores/preferences.store';
export let assets: AssetResponseDto[];
export let bucketDate: string;
export let bucketHeight: number;
export let isAlbumSelectionMode = false;
const locale = navigator.language;
const groupDateFormat: Intl.DateTimeFormatOptions = {
weekday: 'short',
month: 'short',
@ -31,7 +32,7 @@
let hoveredDateGroup = '';
$: assetsGroupByDate = lodash
.chain(assets)
.groupBy((a) => new Date(a.fileCreatedAt).toLocaleDateString(locale, groupDateFormat))
.groupBy((a) => new Date(a.fileCreatedAt).toLocaleDateString($locale, groupDateFormat))
.sortBy((group) => assets.indexOf(group[0]))
.value();
@ -115,7 +116,7 @@
>
{#each assetsGroupByDate as assetsInDateGroup, groupIndex (assetsInDateGroup[0].id)}
{@const dateGroupTitle = new Date(assetsInDateGroup[0].fileCreatedAt).toLocaleDateString(
locale,
$locale,
groupDateFormat
)}
<!-- Asset Group By Date -->

View file

@ -18,6 +18,7 @@
notificationController,
NotificationType
} from '../shared-components/notification/notification';
import { locale } from '$lib/stores/preferences.store';
export let sharedLink: SharedLinkResponseDto;
export let isOwned: boolean;
@ -86,8 +87,6 @@
clearMultiSelectAssetAssetHandler();
}
};
const locale = navigator.language;
</script>
<section class="bg-immich-bg dark:bg-immich-dark-bg">
@ -99,7 +98,7 @@
>
<svelte:fragment slot="leading">
<p class="font-medium text-immich-primary dark:text-immich-dark-primary">
Selected {selectedAssets.size.toLocaleString(locale)}
Selected {selectedAssets.size.toLocaleString($locale)}
</p>
</svelte:fragment>
<svelte:fragment slot="trailing">

View file

@ -9,6 +9,7 @@
import LoadingSpinner from '../loading-spinner.svelte';
import StatusBox from '../status-box.svelte';
import SideBarButton from './side-bar-button.svelte';
import { locale } from '$lib/stores/preferences.store';
const getAssetCount = async () => {
const { data: assetCount } = await api.assetApi.getAssetCountByUserId();
@ -35,8 +36,6 @@
owned: albumCount.owned
};
};
const locale = navigator.language;
</script>
<section id="sidebar" class="flex flex-col gap-1 pt-8 pr-6 bg-immich-bg dark:bg-immich-dark-bg">
@ -56,8 +55,8 @@
<LoadingSpinner />
{:then data}
<div>
<p>{data.videos.toLocaleString(locale)} Videos</p>
<p>{data.photos.toLocaleString(locale)} Photos</p>
<p>{data.videos.toLocaleString($locale)} Videos</p>
<p>{data.photos.toLocaleString($locale)} Photos</p>
</div>
{/await}
</svelte:fragment>
@ -74,7 +73,7 @@
<LoadingSpinner />
{:then data}
<div>
<p>{(data.shared + data.sharing).toLocaleString(locale)} Albums</p>
<p>{(data.shared + data.sharing).toLocaleString($locale)} Albums</p>
</div>
{/await}
</svelte:fragment>
@ -108,7 +107,7 @@
<LoadingSpinner />
{:then data}
<div>
<p>{data.owned.toLocaleString(locale)} Albums</p>
<p>{data.owned.toLocaleString($locale)} Albums</p>
</div>
{/await}
</svelte:fragment>

View file

@ -1,74 +1,38 @@
<script lang="ts">
import { onMount } from 'svelte';
onMount(() => {
var themeToggleDarkIcon = document.getElementById('theme-toggle-dark-icon');
var themeToggleLightIcon = document.getElementById('theme-toggle-light-icon');
// Change the icons inside the button based on previous settings
if (
localStorage.getItem('color-theme') === 'dark' ||
(!('color-theme' in localStorage) &&
window.matchMedia('(prefers-color-scheme: dark)').matches)
) {
themeToggleLightIcon?.classList.remove('hidden');
} else {
themeToggleDarkIcon?.classList.remove('hidden');
}
});
import { browser } from '$app/environment';
import { colorTheme } from '$lib/stores/preferences.store';
const toggleTheme = () => {
var themeToggleDarkIcon = document.getElementById('theme-toggle-dark-icon');
var themeToggleLightIcon = document.getElementById('theme-toggle-light-icon');
// toggle icons inside button
themeToggleDarkIcon?.classList.toggle('hidden');
themeToggleLightIcon?.classList.toggle('hidden');
$colorTheme = $colorTheme === 'dark' ? 'light' : 'dark';
};
// if set via local storage previously
if (localStorage.getItem('color-theme')) {
if (localStorage.getItem('color-theme') === 'light') {
document.documentElement.classList.add('dark');
localStorage.setItem('color-theme', 'dark');
} else {
$: {
if (browser) {
if ($colorTheme === 'light') {
document.documentElement.classList.remove('dark');
localStorage.setItem('color-theme', 'light');
}
} else {
if (document.documentElement.classList.contains('dark')) {
document.documentElement.classList.remove('dark');
localStorage.setItem('color-theme', 'light');
} else {
document.documentElement.classList.add('dark');
localStorage.setItem('color-theme', 'dark');
}
}
};
}
</script>
<button
on:click={toggleTheme}
id="theme-toggle"
type="button"
class="text-gray-500 dark:text-immich-dark-primary hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none rounded-full text-sm p-2.5"
class="text-gray-500 dark:text-immich-dark-primary hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none rounded-full p-2.5"
>
<svg
id="theme-toggle-dark-icon"
class="hidden w-6 h-6"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
><path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" /></svg
>
<svg
id="theme-toggle-light-icon"
class="hidden w-6 h-6"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
><path
d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
fill-rule="evenodd"
clip-rule="evenodd"
/></svg
>
{#if $colorTheme === 'light'}
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"
><path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" /></svg
>
{:else}
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"
><path
d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
fill-rule="evenodd"
clip-rule="evenodd"
/></svg
>
{/if}
</button>

View file

@ -12,6 +12,7 @@
notificationController,
NotificationType
} from '../shared-components/notification/notification';
import { locale } from '$lib/stores/preferences.store';
let keys: APIKeyResponseDto[] = [];
@ -20,7 +21,6 @@
let deleteKey: APIKeyResponseDto | null = null;
let secret = '';
const locale = navigator.language;
const format: Intl.DateTimeFormatOptions = {
month: 'short',
day: 'numeric',
@ -154,7 +154,7 @@
>
<td class="text-sm px-4 w-1/3 text-ellipsis">{key.name}</td>
<td class="text-sm px-4 w-1/3 text-ellipsis"
>{new Date(key.createdAt).toLocaleDateString(locale, format)}
>{new Date(key.createdAt).toLocaleDateString($locale, format)}
</td>
<td class="text-sm px-4 w-1/3 text-ellipsis">
<button

View file

@ -0,0 +1,21 @@
import { browser } from '$app/environment';
import { persisted } from 'svelte-local-storage-store';
const initialTheme =
browser && !window.matchMedia('(prefers-color-scheme: dark)').matches ? 'light' : 'dark';
// The 'color-theme' key is also used by app.html to prevent FOUC on page load.
export const colorTheme = persisted<'dark' | 'light'>('color-theme', initialTheme, {
serializer: {
parse: (text) => (text === 'light' ? text : 'dark'),
stringify: (obj) => obj
}
});
// Locale to use for formatting dates, numbers, etc.
export const locale = persisted<string | undefined>('locale', undefined, {
serializer: {
parse: (text) => text,
stringify: (obj) => obj ?? ''
}
});

View file

@ -19,11 +19,9 @@
let localVersion: string;
let remoteVersion: string;
let showNavigationLoadingBar = false;
let canShow = false;
let showUploadCover = false;
onMount(async () => {
checkUserTheme();
const res = await checkAppVersion();
shouldShowAnnouncement = res.shouldShowAnnouncement;
@ -31,21 +29,6 @@
remoteVersion = res.remoteVersion ?? 'unknown';
});
const checkUserTheme = () => {
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
if (
localStorage.getItem('color-theme') === 'dark' ||
(!('color-theme' in localStorage) &&
window.matchMedia('(prefers-color-scheme: dark)').matches)
) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
canShow = true;
};
beforeNavigate(() => {
showNavigationLoadingBar = true;
});
@ -99,32 +82,30 @@
</svelte:head>
<main on:dragenter={() => (showUploadCover = true)}>
{#if canShow}
<div in:fade={{ duration: 100 }}>
{#if showNavigationLoadingBar}
<NavigationLoadingBar />
{/if}
<div in:fade={{ duration: 100 }}>
{#if showNavigationLoadingBar}
<NavigationLoadingBar />
{/if}
<slot />
<slot />
{#if showUploadCover}
<UploadCover
{dropHandler}
{dragOverHandler}
dragLeaveHandler={() => (showUploadCover = false)}
/>
{/if}
{#if showUploadCover}
<UploadCover
{dropHandler}
{dragOverHandler}
dragLeaveHandler={() => (showUploadCover = false)}
/>
{/if}
<DownloadPanel />
<UploadPanel />
<NotificationList />
{#if shouldShowAnnouncement}
<AnnouncementBox
{localVersion}
{remoteVersion}
on:close={() => (shouldShowAnnouncement = false)}
/>
{/if}
</div>
{/if}
<DownloadPanel />
<UploadPanel />
<NotificationList />
{#if shouldShowAnnouncement}
<AnnouncementBox
{localVersion}
{remoteVersion}
on:close={() => (shouldShowAnnouncement = false)}
/>
{/if}
</div>
</main>

View file

@ -11,6 +11,7 @@
import DeleteConfirmDialog from '$lib/components/admin-page/delete-confirm-dialoge.svelte';
import RestoreDialogue from '$lib/components/admin-page/restore-dialoge.svelte';
import { page } from '$app/stores';
import { locale } from '$lib/stores/preferences.store';
let allUsers: UserResponseDto[] = [];
let shouldShowEditUserForm = false;
@ -28,7 +29,6 @@
return user.deletedAt != null;
};
const locale = navigator.language;
const deleteDateFormat: Intl.DateTimeFormatOptions = {
month: 'long',
day: 'numeric',
@ -38,7 +38,7 @@
const getDeleteDate = (user: UserResponseDto): string => {
let deletedAt = new Date(user.deletedAt ? user.deletedAt : Date.now());
deletedAt.setDate(deletedAt.getDate() + 7);
return deletedAt.toLocaleString(locale, deleteDateFormat);
return deletedAt.toLocaleString($locale, deleteDateFormat);
};
const onUserCreated = async () => {

View file

@ -28,6 +28,7 @@
import Plus from 'svelte-material-icons/Plus.svelte';
import ShareVariantOutline from 'svelte-material-icons/ShareVariantOutline.svelte';
import type { PageData } from './$types';
import { locale } from '$lib/stores/preferences.store';
export let data: PageData;
let isShowCreateSharedLinkModal = false;
@ -141,8 +142,6 @@
assetInteractionStore.clearMultiselect();
isShowCreateSharedLinkModal = false;
};
const locale = navigator.language;
</script>
<section>
@ -154,7 +153,7 @@
>
<svelte:fragment slot="leading">
<p class="font-medium text-immich-primary dark:text-immich-dark-primary">
Selected {$selectedAssets.size.toLocaleString(locale)}
Selected {$selectedAssets.size.toLocaleString($locale)}
</p>
</svelte:fragment>
<svelte:fragment slot="trailing">