Merge branch 'immich-main' into feat/multi-select-asset-viewer
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
echo "Build dependencies for Immich Web"
|
||||
cd /usr/src/app || exit
|
||||
|
||||
pnpm --filter @immich/sdk build
|
||||
|
||||
COUNT=0
|
||||
UPSTREAM="${IMMICH_SERVER_URL:-http://immich-server:2283/}"
|
||||
UPSTREAM="${UPSTREAM%/}"
|
||||
@@ -14,5 +15,4 @@ until wget --spider --quiet "${UPSTREAM}/api/server/config" > /dev/null 2>&1; do
|
||||
sleep 1
|
||||
done
|
||||
echo "Connected to $UPSTREAM, starting Immich Web..."
|
||||
pnpm --filter @immich/sdk build
|
||||
pnpm --filter immich-web exec vite dev --host 0.0.0.0 --port 3000
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"build:stats": "BUILD_STATS=true vite build",
|
||||
"package": "svelte-kit package",
|
||||
"preview": "vite preview",
|
||||
"check:svelte": "svelte-check --no-tsconfig --fail-on-warnings --compiler-warnings 'reactive_declaration_non_reactive_property:ignore' --ignore src/lib/components/photos-page/asset-grid.svelte",
|
||||
"check:svelte": "svelte-check --no-tsconfig --fail-on-warnings --compiler-warnings 'reactive_declaration_non_reactive_property:ignore' --ignore src/lib/components/timeline/Timeline.svelte",
|
||||
"check:typescript": "tsc --noEmit",
|
||||
"check:watch": "npm run check:svelte -- --watch",
|
||||
"check:code": "npm run format && npm run lint:p && npm run check:svelte && npm run check:typescript",
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
<div class="flex items-center gap-4 text-xl font-semibold text-immich-primary dark:text-immich-dark-primary">
|
||||
<span class="flex items-center gap-2">
|
||||
<Icon path={icon} size="1.25em" class="hidden shrink-0 sm:block" />
|
||||
{title.toUpperCase()}
|
||||
<span class="uppercase">{title}</span>
|
||||
</span>
|
||||
<div class="flex gap-2">
|
||||
{#if jobCounts.failed > 0}
|
||||
@@ -137,7 +137,7 @@
|
||||
onClick={() => onCommand({ command: JobCommand.Start, force: false })}
|
||||
>
|
||||
<Icon path={mdiAlertCircle} size="36" />
|
||||
{$t('disabled').toUpperCase()}
|
||||
<span class="uppercase">{$t('disabled')}</span>
|
||||
</JobTileButton>
|
||||
{/if}
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
{#if waitingCount > 0}
|
||||
<JobTileButton color="gray" onClick={() => onCommand({ command: JobCommand.Empty, force: false })}>
|
||||
<Icon path={mdiClose} size="24" />
|
||||
{$t('clear').toUpperCase()}
|
||||
<span class="uppercase">{$t('clear')}</span>
|
||||
</JobTileButton>
|
||||
{/if}
|
||||
{#if queueStatus.isPaused}
|
||||
@@ -153,12 +153,12 @@
|
||||
<JobTileButton color="light-gray" onClick={() => onCommand({ command: JobCommand.Resume, force: false })}>
|
||||
<!-- size property is not reactive, so have to use width and height -->
|
||||
<Icon path={mdiFastForward} {size} />
|
||||
{$t('resume').toUpperCase()}
|
||||
<span class="uppercase">{$t('resume')}</span>
|
||||
</JobTileButton>
|
||||
{:else}
|
||||
<JobTileButton color="light-gray" onClick={() => onCommand({ command: JobCommand.Pause, force: false })}>
|
||||
<Icon path={mdiPause} size="24" />
|
||||
{$t('pause').toUpperCase()}
|
||||
<span class="uppercase">{$t('pause')}</span>
|
||||
</JobTileButton>
|
||||
{/if}
|
||||
{/if}
|
||||
@@ -167,25 +167,25 @@
|
||||
{#if allText}
|
||||
<JobTileButton color="dark-gray" onClick={() => onCommand({ command: JobCommand.Start, force: true })}>
|
||||
<Icon path={mdiAllInclusive} size="24" />
|
||||
{allText}
|
||||
<span class="uppercase">{allText}</span>
|
||||
</JobTileButton>
|
||||
{/if}
|
||||
{#if refreshText}
|
||||
<JobTileButton color="gray" onClick={() => onCommand({ command: JobCommand.Start, force: undefined })}>
|
||||
<Icon path={mdiImageRefreshOutline} size="24" />
|
||||
{refreshText}
|
||||
<span class="uppercase">{refreshText}</span>
|
||||
</JobTileButton>
|
||||
{/if}
|
||||
<JobTileButton color="light-gray" onClick={() => onCommand({ command: JobCommand.Start, force: false })}>
|
||||
<Icon path={mdiSelectionSearch} size="24" />
|
||||
{missingText}
|
||||
<span class="uppercase">{missingText}</span>
|
||||
</JobTileButton>
|
||||
{/if}
|
||||
|
||||
{#if !disabled && !multipleButtons && isIdle}
|
||||
<JobTileButton color="light-gray" onClick={() => onCommand({ command: JobCommand.Start, force: false })}>
|
||||
<Icon path={mdiPlay} size="48" />
|
||||
{missingText}
|
||||
<span class="uppercase">{missingText}</span>
|
||||
</JobTileButton>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -177,9 +177,9 @@
|
||||
{disabled}
|
||||
{subtitle}
|
||||
{description}
|
||||
allText={allText?.toUpperCase()}
|
||||
refreshText={refreshText?.toUpperCase()}
|
||||
missingText={missingText.toUpperCase()}
|
||||
{allText}
|
||||
{refreshText}
|
||||
{missingText}
|
||||
{jobCounts}
|
||||
{queueStatus}
|
||||
onCommand={(command) => (handleCommandOverride || handleCommand)(jobName, command)}
|
||||
|
||||
@@ -36,19 +36,19 @@
|
||||
|
||||
<div class="flex flex-col gap-5">
|
||||
<div>
|
||||
<p class="text-sm dark:text-immich-dark-fg">{$t('total_usage').toUpperCase()}</p>
|
||||
<p class="text-sm dark:text-immich-dark-fg uppercase">{$t('total_usage')}</p>
|
||||
|
||||
<div class="mt-5 hidden justify-between lg:flex gap-4">
|
||||
<StatsCard icon={mdiCameraIris} title={$t('photos').toUpperCase()} value={stats.photos} />
|
||||
<StatsCard icon={mdiPlayCircle} title={$t('videos').toUpperCase()} value={stats.videos} />
|
||||
<StatsCard icon={mdiChartPie} title={$t('storage').toUpperCase()} value={statsUsage} unit={statsUsageUnit} />
|
||||
<StatsCard icon={mdiCameraIris} title={$t('photos')} value={stats.photos} />
|
||||
<StatsCard icon={mdiPlayCircle} title={$t('videos')} value={stats.videos} />
|
||||
<StatsCard icon={mdiChartPie} title={$t('storage')} value={statsUsage} unit={statsUsageUnit} />
|
||||
</div>
|
||||
<div class="mt-5 flex lg:hidden">
|
||||
<div class="flex flex-col justify-between rounded-3xl bg-subtle p-5 dark:bg-immich-dark-gray">
|
||||
<div class="flex flex-wrap gap-x-12">
|
||||
<div class="flex place-items-center gap-4 text-immich-primary dark:text-immich-dark-primary">
|
||||
<Icon path={mdiCameraIris} size="25" />
|
||||
<p>{$t('photos').toUpperCase()}</p>
|
||||
<p class="uppercase">{$t('photos')}</p>
|
||||
</div>
|
||||
|
||||
<div class="relative text-center font-mono text-2xl font-semibold">
|
||||
@@ -60,7 +60,7 @@
|
||||
<div class="flex flex-wrap gap-x-12">
|
||||
<div class="flex place-items-center gap-4 text-immich-primary dark:text-immich-dark-primary">
|
||||
<Icon path={mdiPlayCircle} size="25" />
|
||||
<p>{$t('videos').toUpperCase()}</p>
|
||||
<p class="uppercase">{$t('videos')}</p>
|
||||
</div>
|
||||
|
||||
<div class="relative text-center font-mono text-2xl font-semibold">
|
||||
@@ -72,7 +72,7 @@
|
||||
<div class="flex flex-wrap gap-x-7">
|
||||
<div class="flex place-items-center gap-4 text-immich-primary dark:text-immich-dark-primary">
|
||||
<Icon path={mdiChartPie} size="25" />
|
||||
<p>{$t('storage').toUpperCase()}</p>
|
||||
<p class="uppercase">{$t('storage')}</p>
|
||||
</div>
|
||||
|
||||
<div class="relative flex text-center font-mono text-2xl font-semibold">
|
||||
@@ -87,7 +87,7 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="text-sm dark:text-immich-dark-fg">{$t('user_usage_detail').toUpperCase()}</p>
|
||||
<p class="text-sm dark:text-immich-dark-fg uppercase">{$t('user_usage_detail')}</p>
|
||||
<table class="mt-5 w-full text-start">
|
||||
<thead
|
||||
class="mb-4 flex h-12 w-full rounded-md border bg-gray-50 text-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-primary"
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<div class="flex h-[140px] w-full flex-col justify-between rounded-3xl bg-subtle text-primary p-5">
|
||||
<div class="flex place-items-center gap-4">
|
||||
<Icon path={icon} size="40" />
|
||||
<Text size="large" fontWeight="bold">{title}</Text>
|
||||
<Text size="large" fontWeight="bold" class="uppercase">{title}</Text>
|
||||
</div>
|
||||
|
||||
<div class="relative mx-auto font-mono text-2xl font-semibold">
|
||||
|
||||
@@ -183,7 +183,7 @@
|
||||
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label={$t('admin.oauth_timeout').toUpperCase()}
|
||||
label={$t('admin.oauth_timeout')}
|
||||
description={$t('admin.oauth_timeout_description')}
|
||||
required={true}
|
||||
bind:value={config.oauth.timeout}
|
||||
@@ -193,7 +193,7 @@
|
||||
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label={$t('admin.oauth_storage_label_claim').toUpperCase()}
|
||||
label={$t('admin.oauth_storage_label_claim')}
|
||||
description={$t('admin.oauth_storage_label_claim_description')}
|
||||
bind:value={config.oauth.storageLabelClaim}
|
||||
required={true}
|
||||
@@ -203,7 +203,7 @@
|
||||
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label={$t('admin.oauth_role_claim').toUpperCase()}
|
||||
label={$t('admin.oauth_role_claim')}
|
||||
description={$t('admin.oauth_role_claim_description')}
|
||||
bind:value={config.oauth.roleClaim}
|
||||
required={true}
|
||||
@@ -213,7 +213,7 @@
|
||||
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label={$t('admin.oauth_storage_quota_claim').toUpperCase()}
|
||||
label={$t('admin.oauth_storage_quota_claim')}
|
||||
description={$t('admin.oauth_storage_quota_claim_description')}
|
||||
bind:value={config.oauth.storageQuotaClaim}
|
||||
required={true}
|
||||
@@ -223,7 +223,7 @@
|
||||
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.NUMBER}
|
||||
label={$t('admin.oauth_storage_quota_default').toUpperCase()}
|
||||
label={$t('admin.oauth_storage_quota_default')}
|
||||
description={$t('admin.oauth_storage_quota_default_description')}
|
||||
bind:value={config.oauth.defaultStorageQuota}
|
||||
required={false}
|
||||
@@ -233,7 +233,7 @@
|
||||
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label={$t('admin.oauth_button_text').toUpperCase()}
|
||||
label={$t('admin.oauth_button_text')}
|
||||
bind:value={config.oauth.buttonText}
|
||||
required={false}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
@@ -241,21 +241,21 @@
|
||||
/>
|
||||
|
||||
<SettingSwitch
|
||||
title={$t('admin.oauth_auto_register').toUpperCase()}
|
||||
title={$t('admin.oauth_auto_register')}
|
||||
subtitle={$t('admin.oauth_auto_register_description')}
|
||||
bind:checked={config.oauth.autoRegister}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
/>
|
||||
|
||||
<SettingSwitch
|
||||
title={$t('admin.oauth_auto_launch').toUpperCase()}
|
||||
title={$t('admin.oauth_auto_launch')}
|
||||
subtitle={$t('admin.oauth_auto_launch_description')}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
bind:checked={config.oauth.autoLaunch}
|
||||
/>
|
||||
|
||||
<SettingSwitch
|
||||
title={$t('admin.oauth_mobile_redirect_uri_override').toUpperCase()}
|
||||
title={$t('admin.oauth_mobile_redirect_uri_override')}
|
||||
subtitle={$t('admin.oauth_mobile_redirect_uri_override_description', {
|
||||
values: { callback: 'app.immich:///oauth-callback' },
|
||||
})}
|
||||
@@ -267,7 +267,7 @@
|
||||
{#if config.oauth.mobileOverrideEnabled}
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label={$t('admin.oauth_mobile_redirect_uri').toUpperCase()}
|
||||
label={$t('admin.oauth_mobile_redirect_uri')}
|
||||
bind:value={config.oauth.mobileRedirectUri}
|
||||
required={true}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
|
||||
@@ -184,7 +184,7 @@
|
||||
<h3 class="text-base font-medium text-immich-primary dark:text-immich-dark-primary">{$t('template')}</h3>
|
||||
|
||||
<div class="my-2 text-sm">
|
||||
<h4>{$t('preview').toUpperCase()}</h4>
|
||||
<h4 class="uppercase">{$t('preview')}</h4>
|
||||
</div>
|
||||
|
||||
<p class="text-sm">
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
</script>
|
||||
|
||||
<div class="mt-2 text-sm">
|
||||
<h4>{$t('date_and_time').toUpperCase()}</h4>
|
||||
<h4 class="uppercase">{$t('date_and_time')}</h4>
|
||||
</div>
|
||||
|
||||
<!-- eslint-disable svelte/no-useless-mustaches -->
|
||||
@@ -27,7 +27,7 @@
|
||||
</div>
|
||||
<div class="flex gap-[40px]">
|
||||
<div>
|
||||
<p class="font-medium text-immich-primary dark:text-immich-dark-primary">{$t('year').toUpperCase()}</p>
|
||||
<p class="uppercase font-medium text-immich-primary dark:text-immich-dark-primary">{$t('year')}</p>
|
||||
<ul>
|
||||
{#each options.yearOptions as yearFormat, index (index)}
|
||||
<li>{'{{'}{yearFormat}{'}}'} - {getLuxonExample(yearFormat)}</li>
|
||||
@@ -36,7 +36,7 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="font-medium text-immich-primary dark:text-immich-dark-primary">{$t('month').toUpperCase()}</p>
|
||||
<p class="uppercase font-medium text-immich-primary dark:text-immich-dark-primary">{$t('month')}</p>
|
||||
<ul>
|
||||
{#each options.monthOptions as monthFormat, index (index)}
|
||||
<li>{'{{'}{monthFormat}{'}}'} - {getLuxonExample(monthFormat)}</li>
|
||||
@@ -45,7 +45,7 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="font-medium text-immich-primary dark:text-immich-dark-primary">{$t('week').toUpperCase()}</p>
|
||||
<p class="uppercase font-medium text-immich-primary dark:text-immich-dark-primary">{$t('week')}</p>
|
||||
<ul>
|
||||
{#each options.weekOptions as weekFormat, index (index)}
|
||||
<li>{'{{'}{weekFormat}{'}}'} - {getLuxonExample(weekFormat)}</li>
|
||||
@@ -54,7 +54,7 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="font-medium text-immich-primary dark:text-immich-dark-primary">{$t('day').toUpperCase()}</p>
|
||||
<p class="uppercase font-medium text-immich-primary dark:text-immich-dark-primary">{$t('day')}</p>
|
||||
<ul>
|
||||
{#each options.dayOptions as dayFormat, index (index)}
|
||||
<li>{'{{'}{dayFormat}{'}}'} - {getLuxonExample(dayFormat)}</li>
|
||||
@@ -63,7 +63,7 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="font-medium text-immich-primary dark:text-immich-dark-primary">{$t('hour').toUpperCase()}</p>
|
||||
<p class="uppercase font-medium text-immich-primary dark:text-immich-dark-primary">{$t('hour')}</p>
|
||||
<ul>
|
||||
{#each options.hourOptions as dayFormat, index (index)}
|
||||
<li>{'{{'}{dayFormat}{'}}'} - {getLuxonExample(dayFormat)}</li>
|
||||
@@ -72,7 +72,7 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="font-medium text-immich-primary dark:text-immich-dark-primary">{$t('minute').toUpperCase()}</p>
|
||||
<p class="uppercase font-medium text-immich-primary dark:text-immich-dark-primary">{$t('minute')}</p>
|
||||
<ul>
|
||||
{#each options.minuteOptions as dayFormat, index (index)}
|
||||
<li>{'{{'}{dayFormat}{'}}'} - {getLuxonExample(dayFormat)}</li>
|
||||
@@ -81,7 +81,7 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="font-medium text-immich-primary dark:text-immich-dark-primary">{$t('second').toUpperCase()}</p>
|
||||
<p class="uppercase font-medium text-immich-primary dark:text-immich-dark-primary">{$t('second')}</p>
|
||||
<ul>
|
||||
{#each options.secondOptions as dayFormat, index (index)}
|
||||
<li>{'{{'}{dayFormat}{'}}'} - {getLuxonExample(dayFormat)}</li>
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
</script>
|
||||
|
||||
<div class="mt-4 text-sm">
|
||||
<h4>{$t('other_variables').toUpperCase()}</h4>
|
||||
<h4 class="uppercase">{$t('other_variables')}</h4>
|
||||
</div>
|
||||
|
||||
<div class="p-4 mt-2 text-xs bg-gray-200 rounded-lg dark:bg-gray-700 dark:text-immich-dark-fg">
|
||||
<div class="flex gap-[50px]">
|
||||
<div>
|
||||
<p class="font-medium text-immich-primary dark:text-immich-dark-primary">{$t('filename').toUpperCase()}</p>
|
||||
<p class="uppercase font-medium text-immich-primary dark:text-immich-dark-primary">{$t('filename')}</p>
|
||||
<ul>
|
||||
<li>{`{{filename}}`} - IMG_123</li>
|
||||
<li>{`{{ext}}`} - jpg</li>
|
||||
@@ -17,14 +17,14 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="font-medium text-immich-primary dark:text-immich-dark-primary">{$t('filetype').toUpperCase()}</p>
|
||||
<p class="uppercase font-medium text-immich-primary dark:text-immich-dark-primary">{$t('filetype')}</p>
|
||||
<ul>
|
||||
<li>{`{{filetype}}`} - VID or IMG</li>
|
||||
<li>{`{{filetypefull}}`} - VIDEO or IMAGE</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-medium text-immich-primary dark:text-immich-dark-primary uppercase">{$t('other').toUpperCase()}</p>
|
||||
<p class="uppercase font-medium text-immich-primary dark:text-immich-dark-primary">{$t('other')}</p>
|
||||
<ul>
|
||||
<li>{`{{assetId}}`} - Asset ID</li>
|
||||
<li>{`{{assetIdShort}}`} - Asset ID (last 12 characters)</li>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import AlbumMap from '$lib/components/album-page/album-map.svelte';
|
||||
import SelectAllAssets from '$lib/components/photos-page/actions/select-all-assets.svelte';
|
||||
import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte';
|
||||
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||
@@ -18,7 +19,6 @@
|
||||
import { onDestroy } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import DownloadAction from '../photos-page/actions/download-action.svelte';
|
||||
import AssetGrid from '../photos-page/asset-grid.svelte';
|
||||
import ControlAppBar from '../shared-components/control-app-bar.svelte';
|
||||
import ImmichLogoSmallLink from '../shared-components/immich-logo-small-link.svelte';
|
||||
import ThemeButton from '../shared-components/theme-button.svelte';
|
||||
@@ -61,7 +61,7 @@
|
||||
/>
|
||||
|
||||
<main class="relative h-dvh overflow-hidden px-2 md:px-6 max-md:pt-(--navbar-height-md) pt-(--navbar-height)">
|
||||
<AssetGrid enableRouting={true} {album} {timelineManager} {assetInteraction}>
|
||||
<Timeline enableRouting={true} {album} {timelineManager} {assetInteraction}>
|
||||
<section class="pt-8 md:pt-24 px-2 md:px-0">
|
||||
<!-- ALBUM TITLE -->
|
||||
<h1
|
||||
@@ -83,7 +83,7 @@
|
||||
</p>
|
||||
{/if}
|
||||
</section>
|
||||
</AssetGrid>
|
||||
</Timeline>
|
||||
</main>
|
||||
|
||||
<header>
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
{#if isOwner && !authManager.isSharedLink}
|
||||
<section class="px-4 mt-4">
|
||||
<div class="flex h-10 w-full items-center justify-between text-sm">
|
||||
<h2>{$t('tags').toUpperCase()}</h2>
|
||||
<h2 class="uppercase">{$t('tags')}</h2>
|
||||
</div>
|
||||
<section class="flex flex-wrap pt-2 gap-1" data-testid="detail-panel-tags">
|
||||
{#each tags as tag (tag.id)}
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
{#if !authManager.isSharedLink && isOwner}
|
||||
<section class="px-4 pt-4 text-sm">
|
||||
<div class="flex h-10 w-full items-center justify-between">
|
||||
<h2>{$t('people').toUpperCase()}</h2>
|
||||
<h2 class="uppercase">{$t('people')}</h2>
|
||||
<div class="flex gap-2 items-center">
|
||||
{#if people.some((person) => person.isHidden)}
|
||||
<IconButton
|
||||
@@ -269,10 +269,10 @@
|
||||
<div class="px-4 py-4">
|
||||
{#if asset.exifInfo}
|
||||
<div class="flex h-10 w-full items-center justify-between text-sm">
|
||||
<h2>{$t('details').toUpperCase()}</h2>
|
||||
<h2 class="uppercase">{$t('details')}</h2>
|
||||
</div>
|
||||
{:else}
|
||||
<p class="text-sm">{$t('no_exif_info_available').toUpperCase()}</p>
|
||||
<p class="uppercase text-sm">{$t('no_exif_info_available')}</p>
|
||||
{/if}
|
||||
|
||||
{#if dateTime}
|
||||
@@ -503,7 +503,7 @@
|
||||
|
||||
{#if currentAlbum && currentAlbum.albumUsers.length > 0 && asset.owner}
|
||||
<section class="px-6 dark:text-immich-dark-fg mt-4">
|
||||
<p class="text-sm">{$t('shared_by').toUpperCase()}</p>
|
||||
<p class="uppercase text-sm">{$t('shared_by')}</p>
|
||||
<div class="flex gap-4 pt-4">
|
||||
<div>
|
||||
<UserAvatar user={asset.owner} size="md" />
|
||||
@@ -520,7 +520,7 @@
|
||||
|
||||
{#if albums.length > 0}
|
||||
<section class="px-6 pt-6 dark:text-immich-dark-fg">
|
||||
<p class="pb-4 text-sm">{$t('appears_in').toUpperCase()}</p>
|
||||
<p class="uppercase pb-4 text-sm">{$t('appears_in')}</p>
|
||||
{#each albums as album (album.id)}
|
||||
<a href="{AppRoute.ALBUMS}/{album.id}">
|
||||
<div class="flex gap-4 pt-2 hover:cursor-pointer items-center">
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
transition:fly={{ x: -100, duration: 350 }}
|
||||
class="fixed bottom-10 start-2 max-h-[270px] w-[315px] rounded-2xl border p-4 text-sm shadow-sm bg-light"
|
||||
>
|
||||
<p class="mb-2 text-xs text-gray-500">{$t('downloading').toUpperCase()}</p>
|
||||
<p class="uppercase mb-2 text-xs text-gray-500">{$t('downloading')}</p>
|
||||
<div class="my-2 mb-2 flex max-h-[200px] flex-col overflow-y-auto text-sm">
|
||||
{#each Object.keys(downloadManager.assets) as downloadKey (downloadKey)}
|
||||
{@const download = downloadManager.assets[downloadKey]}
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
|
||||
<div class="mt-3 px-4 py-4">
|
||||
<div class="flex h-10 w-full items-center justify-between text-sm">
|
||||
<h2>{$t('editor_crop_tool_h2_aspect_ratios').toUpperCase()}</h2>
|
||||
<h2 class="uppercase">{$t('editor_crop_tool_h2_aspect_ratios')}</h2>
|
||||
</div>
|
||||
{#each sizesRows as sizesRow, index (index)}
|
||||
<ul class="flex-wrap flex-row flex gap-x-6 py-2 justify-evenly">
|
||||
@@ -145,7 +145,7 @@
|
||||
</ul>
|
||||
{/each}
|
||||
<div class="flex h-10 w-full items-center justify-between text-sm">
|
||||
<h2>{$t('editor_crop_tool_h2_rotation').toUpperCase()}</h2>
|
||||
<h2 class="uppercase">{$t('editor_crop_tool_h2_rotation')}</h2>
|
||||
</div>
|
||||
<ul class="flex-wrap flex-row flex gap-x-6 gap-y-4 justify-center">
|
||||
<li>
|
||||
|
||||
@@ -469,7 +469,7 @@
|
||||
|
||||
{#if current.previousMemory}
|
||||
<div class="absolute bottom-4 end-4 text-start text-white">
|
||||
<p class="text-xs font-semibold text-gray-200">{$t('previous').toUpperCase()}</p>
|
||||
<p class="uppercase text-xs font-semibold text-gray-200">{$t('previous')}</p>
|
||||
<p class="text-xl">{$memoryLaneTitle(current.previousMemory)}</p>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -618,7 +618,7 @@
|
||||
|
||||
{#if current.nextMemory}
|
||||
<div class="absolute bottom-4 start-4 text-start text-white">
|
||||
<p class="text-xs font-semibold text-gray-200">{$t('up_next').toUpperCase()}</p>
|
||||
<p class="uppercase text-xs font-semibold text-gray-200">{$t('up_next')}</p>
|
||||
<p class="text-xl">{$memoryLaneTitle(current.nextMemory)}</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -40,8 +40,8 @@
|
||||
<Icon path={icon} size="30" class="text-immich-primary dark:text-immich-dark-primary" />
|
||||
{/if}
|
||||
{#if title}
|
||||
<p class="text-xl text-immich-primary dark:text-immich-dark-primary">
|
||||
{title.toUpperCase()}
|
||||
<p class="uppercase text-xl text-immich-primary dark:text-immich-dark-primary">
|
||||
{title}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
class="flex flex-col place-items-center place-content-center justify-around h-full w-full text-immich-primary"
|
||||
>
|
||||
<Icon path={sunPath} viewBox={sunViewBox} size="96" />
|
||||
<p class="font-semibold text-4xl">{$t('light').toUpperCase()}</p>
|
||||
<p class="uppercase font-semibold text-4xl">{$t('light')}</p>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
@@ -31,7 +31,7 @@
|
||||
class="flex flex-col place-items-center place-content-center justify-around h-full w-full text-immich-dark-primary"
|
||||
>
|
||||
<Icon path={moonPath} viewBox={moonViewBox} size="96" />
|
||||
<p class="font-semibold text-4xl">{$t('dark').toUpperCase()}</p>
|
||||
<p class="uppercase font-semibold text-4xl">{$t('dark')}</p>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
</script>
|
||||
|
||||
<div id="camera-selection">
|
||||
<p class="immich-form-label">{$t('camera').toUpperCase()}</p>
|
||||
<p class="uppercase immich-form-label">{$t('camera')}</p>
|
||||
|
||||
<div class="grid grid-auto-fit-40 gap-5 mt-1">
|
||||
<div class="w-full">
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
<div id="date-range-selection" class="grid grid-auto-fit-40 gap-5">
|
||||
<label class="immich-form-label" for="start-date">
|
||||
<span>{$t('start_date').toUpperCase()}</span>
|
||||
<span class="uppercase">{$t('start_date')}</span>
|
||||
<DateInput
|
||||
class="immich-form-input w-full mt-1 hover:cursor-pointer"
|
||||
type="date"
|
||||
@@ -30,7 +30,7 @@
|
||||
</label>
|
||||
|
||||
<label class="immich-form-label" for="end-date">
|
||||
<span>{$t('end_date').toUpperCase()}</span>
|
||||
<span class="uppercase">{$t('end_date')}</span>
|
||||
<DateInput
|
||||
class="immich-form-input w-full mt-1 hover:cursor-pointer"
|
||||
type="date"
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
<div id="display-options-selection">
|
||||
<fieldset>
|
||||
<legend class="immich-form-label">{$t('display_options').toUpperCase()}</legend>
|
||||
<legend class="uppercase immich-form-label">{$t('display_options')}</legend>
|
||||
<div class="flex flex-wrap gap-x-5 gap-y-2 mt-1">
|
||||
<div class="flex items-center gap-2">
|
||||
<Checkbox id="not-in-album-checkbox" size="tiny" bind:checked={filters.isNotInAlbum} />
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
class="absolute w-full rounded-b-3xl border-2 border-t-0 border-gray-200 bg-white pb-5 shadow-2xl transition-all dark:border-gray-700 dark:bg-immich-dark-gray dark:text-gray-300 z-1"
|
||||
>
|
||||
<div class="flex items-center justify-between px-5 pt-5 text-xs">
|
||||
<p class="py-2" aria-hidden={true}>{$t('recent_searches').toUpperCase()}</p>
|
||||
<p class="uppercase py-2" aria-hidden={true}>{$t('recent_searches')}</p>
|
||||
{#if showClearAll}
|
||||
<button
|
||||
id={getId(0)}
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
</script>
|
||||
|
||||
<div id="location-selection">
|
||||
<p class="immich-form-label">{$t('place').toUpperCase()}</p>
|
||||
<p class="uppercase immich-form-label">{$t('place')}</p>
|
||||
|
||||
<div class="grid grid-auto-fit-40 gap-5 mt-1">
|
||||
<div class="w-full">
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
<div id="media-type-selection">
|
||||
<fieldset>
|
||||
<legend class="immich-form-label">{$t('media_type').toUpperCase()}</legend>
|
||||
<legend class="uppercase immich-form-label">{$t('media_type')}</legend>
|
||||
<div class="flex flex-wrap gap-x-5 gap-y-2 mt-1">
|
||||
<RadioButton name="media-type" id="type-all" bind:group={filteredMedia} label={$t('all')} value={MediaType.All} />
|
||||
<RadioButton
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
|
||||
<div id="people-selection" class="max-h-60 -mb-4 overflow-y-auto immich-scrollbar">
|
||||
<div class="flex items-center w-full justify-between gap-6">
|
||||
<p class="immich-form-label py-3">{$t('people').toUpperCase()}</p>
|
||||
<p class="uppercase immich-form-label py-3">{$t('people')}</p>
|
||||
<SearchBar bind:name placeholder={$t('filter_people')} showLoadingSpinner={false} />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -20,12 +20,14 @@
|
||||
|
||||
<div class="grid grid-auto-fit-40 gap-5">
|
||||
<label class="immich-form-label" for="start-date">
|
||||
<Combobox
|
||||
label={$t('rating').toUpperCase()}
|
||||
placeholder={$t('search_rating')}
|
||||
{options}
|
||||
selectedOption={rating === undefined ? undefined : options[rating]}
|
||||
onSelect={(r) => (rating = r === undefined ? undefined : Number.parseInt(r.value))}
|
||||
/>
|
||||
<div class="[&_label]:uppercase">
|
||||
<Combobox
|
||||
label={$t('rating')}
|
||||
placeholder={$t('search_rating')}
|
||||
{options}
|
||||
selectedOption={rating === undefined ? undefined : options[rating]}
|
||||
onSelect={(r) => (rating = r === undefined ? undefined : Number.parseInt(r.value))}
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -45,15 +45,17 @@
|
||||
<div id="location-selection">
|
||||
<form autocomplete="off" id="create-tag-form">
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<Combobox
|
||||
disabled={selectedTags === null}
|
||||
onSelect={handleSelect}
|
||||
label={$t('tags').toUpperCase()}
|
||||
defaultFirstOption
|
||||
options={allTags.map((tag) => ({ id: tag.id, label: tag.value, value: tag.id }))}
|
||||
bind:selectedOption
|
||||
placeholder={$t('search_tags')}
|
||||
/>
|
||||
<div class="[&_label]:uppercase">
|
||||
<Combobox
|
||||
disabled={selectedTags === null}
|
||||
onSelect={handleSelect}
|
||||
label={$t('tags')}
|
||||
defaultFirstOption
|
||||
options={allTags.map((tag) => ({ id: tag.id, label: tag.value, value: tag.id }))}
|
||||
bind:selectedOption
|
||||
placeholder={$t('search_tags')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<Checkbox
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
|
||||
<div class="mb-4 w-full">
|
||||
<div class="flex place-items-center gap-1">
|
||||
<label class="font-medium text-immich-primary dark:text-immich-dark-primary text-sm min-h-6" for={label}
|
||||
<label class="font-medium text-immich-primary dark:text-immich-dark-primary text-sm min-h-6 uppercase" for={label}
|
||||
>{label}</label
|
||||
>
|
||||
{#if required}
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
bind:isSelected={isSharingSelected}
|
||||
></SideBarLink>
|
||||
|
||||
<p class="text-xs p-6 dark:text-immich-dark-fg">{$t('library').toUpperCase()}</p>
|
||||
<p class="text-xs p-6 dark:text-immich-dark-fg uppercase">{$t('library')}</p>
|
||||
|
||||
<SideBarLink
|
||||
title={$t('favorites')}
|
||||
|
||||
@@ -93,14 +93,14 @@
|
||||
{/if}
|
||||
{#if showFallback}
|
||||
<span
|
||||
class="flex h-full w-full select-none items-center justify-center font-medium"
|
||||
class="uppercase flex h-full w-full select-none items-center justify-center font-medium"
|
||||
class:text-xs={size === 'sm'}
|
||||
class:text-lg={size === 'lg'}
|
||||
class:text-xl={size === 'xl'}
|
||||
class:text-2xl={size === 'xxl'}
|
||||
class:text-3xl={size === 'xxxl'}
|
||||
>
|
||||
{(user.name[0] || '').toUpperCase()}
|
||||
{user.name[0] || ''}
|
||||
</span>
|
||||
{/if}
|
||||
</figure>
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
{/if}
|
||||
|
||||
{#if link.showMetadata}
|
||||
<Badge rounded="full"><span class="text-xs px-1">{$t('exif').toUpperCase()}</span></Badge>
|
||||
<Badge rounded="full"><span class="uppercase text-xs px-1">{$t('exif')}</span></Badge>
|
||||
{/if}
|
||||
|
||||
{#if link.password}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
setFocusTo as setFocusToInit,
|
||||
} from '$lib/components/photos-page/actions/focus-actions';
|
||||
import Skeleton from '$lib/components/photos-page/skeleton.svelte';
|
||||
import type { AbsoluteResult, RelativeResult } from '$lib/components/shared-components/change-date.svelte';
|
||||
import ChangeDate from '$lib/components/shared-components/change-date.svelte';
|
||||
import Scrubber from '$lib/components/shared-components/scrubber/scrubber.svelte';
|
||||
import { AppRoute, AssetAction } from '$lib/constants';
|
||||
@@ -36,16 +37,16 @@
|
||||
import { DateTime } from 'luxon';
|
||||
import { onMount, type Snippet } from 'svelte';
|
||||
import type { UpdatePayload } from 'vite';
|
||||
import AssetDateGroup from '../photos-page/asset-date-group.svelte';
|
||||
import DeleteAssetDialog from '../photos-page/delete-asset-dialog.svelte';
|
||||
import Portal from '../shared-components/portal/portal.svelte';
|
||||
import AssetDateGroup from './asset-date-group.svelte';
|
||||
import DeleteAssetDialog from './delete-asset-dialog.svelte';
|
||||
|
||||
interface Props {
|
||||
isSelectionMode?: boolean;
|
||||
singleSelect?: boolean;
|
||||
/** `true` if this asset grid is responds to navigation events; if `true`, then look at the
|
||||
`AssetViewingStore.gridScrollTarget` and load and scroll to the asset specified, and
|
||||
additionally, update the page location/url with the asset as the asset-grid is scrolled */
|
||||
additionally, update the page location/url with the asset as the timeline is scrolled */
|
||||
enableRouting: boolean;
|
||||
timelineManager: TimelineManager;
|
||||
assetInteraction: AssetInteraction;
|
||||
@@ -224,14 +225,14 @@
|
||||
|
||||
const hmrSupport = () => {
|
||||
// when hmr happens, skeleton is initialized to true by default
|
||||
// normally, loading asset-grid is part of a navigation event, and the completion of
|
||||
// normally, loading timeline is part of a navigation event, and the completion of
|
||||
// that event triggers a scroll-to-asset, if necessary, when then clears the skeleton.
|
||||
// this handler will run the navigation/scroll-to-asset handler when hmr is performed,
|
||||
// preventing skeleton from showing after hmr
|
||||
if (import.meta && import.meta.hot) {
|
||||
const afterApdate = (payload: UpdatePayload) => {
|
||||
const assetGridUpdate = payload.updates.some(
|
||||
(update) => update.path.endsWith('asset-grid.svelte') || update.path.endsWith('assets-store.ts'),
|
||||
(update) => update.path.endsWith('Timeline.svelte') || update.path.endsWith('assets-store.ts'),
|
||||
);
|
||||
|
||||
if (assetGridUpdate) {
|
||||
@@ -252,7 +253,7 @@
|
||||
};
|
||||
import.meta.hot?.on('vite:afterUpdate', afterApdate);
|
||||
import.meta.hot?.on('vite:beforeUpdate', (payload) => {
|
||||
const assetGridUpdate = payload.updates.some((update) => update.path.endsWith('asset-grid.svelte'));
|
||||
const assetGridUpdate = payload.updates.some((update) => update.path.endsWith('Timeline.svelte'));
|
||||
if (assetGridUpdate) {
|
||||
timelineManager.destroy();
|
||||
}
|
||||
@@ -845,13 +846,15 @@
|
||||
title="Navigate to Time"
|
||||
initialDate={DateTime.now()}
|
||||
timezoneInput={false}
|
||||
onConfirm={async (dateString: string) => {
|
||||
onConfirm={async (dateString: AbsoluteResult | RelativeResult) => {
|
||||
isShowSelectDate = false;
|
||||
const asset = await timelineManager.getClosestAssetToDate(
|
||||
(DateTime.fromISO(dateString) as DateTime<true>).toObject(),
|
||||
);
|
||||
if (asset) {
|
||||
setFocusAsset(asset);
|
||||
if (dateString.mode == 'absolute') {
|
||||
const asset = await timelineManager.getClosestAssetToDate(
|
||||
(DateTime.fromISO(dateString.date) as DateTime<true>).toObject(),
|
||||
);
|
||||
if (asset) {
|
||||
setFocusAsset(asset);
|
||||
}
|
||||
}
|
||||
}}
|
||||
onCancel={() => (isShowSelectDate = false)}
|
||||
@@ -56,16 +56,16 @@
|
||||
<section class="my-4">
|
||||
{#if currentDevice}
|
||||
<div class="mb-6">
|
||||
<h3 class="mb-2 text-xs font-medium text-immich-primary dark:text-immich-dark-primary">
|
||||
{$t('current_device').toUpperCase()}
|
||||
<h3 class="uppercase mb-2 text-xs font-medium text-immich-primary dark:text-immich-dark-primary">
|
||||
{$t('current_device')}
|
||||
</h3>
|
||||
<DeviceCard device={currentDevice} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if otherDevices.length > 0}
|
||||
<div class="mb-6">
|
||||
<h3 class="mb-2 text-xs font-medium text-immich-primary dark:text-immich-dark-primary">
|
||||
{$t('other_devices').toUpperCase()}
|
||||
<h3 class="uppercase mb-2 text-xs font-medium text-immich-primary dark:text-immich-dark-primary">
|
||||
{$t('other_devices')}
|
||||
</h3>
|
||||
{#each otherDevices as device, index (device.id)}
|
||||
<DeviceCard {device} onDelete={() => handleDelete(device)} />
|
||||
@@ -74,8 +74,8 @@
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<h3 class="mb-2 text-xs font-medium text-immich-primary dark:text-immich-dark-primary">
|
||||
{$t('log_out_all_devices').toUpperCase()}
|
||||
<h3 class="uppercase mb-2 text-xs font-medium text-immich-primary dark:text-immich-dark-primary">
|
||||
{$t('log_out_all_devices')}
|
||||
</h3>
|
||||
<div class="flex justify-end">
|
||||
<Button shape="round" color="danger" size="small" onclick={handleDeleteAll}>{$t('log_out_all_devices')}</Button>
|
||||
|
||||
@@ -158,8 +158,8 @@
|
||||
<!-- I am sharing my assets with this user -->
|
||||
{#if partner.sharedByMe}
|
||||
<hr class="my-4 border border-gray-200 dark:border-gray-700" />
|
||||
<p class="text-xs font-medium my-4">
|
||||
{$t('shared_with_partner', { values: { partner: partner.user.name } }).toUpperCase()}
|
||||
<p class="uppercase text-xs font-medium my-4">
|
||||
{$t('shared_with_partner', { values: { partner: partner.user.name } })}
|
||||
</p>
|
||||
<p class="text-md">{$t('partner_can_access', { values: { partner: partner.user.name } })}</p>
|
||||
<ul class="text-sm">
|
||||
@@ -177,8 +177,8 @@
|
||||
<!-- this user is sharing assets with me -->
|
||||
{#if partner.sharedWithMe}
|
||||
<hr class="my-4 border border-gray-200 dark:border-gray-700" />
|
||||
<p class="text-xs font-medium my-4">
|
||||
{$t('shared_from_partner', { values: { partner: partner.user.name } }).toUpperCase()}
|
||||
<p class="uppercase text-xs font-medium my-4">
|
||||
{$t('shared_from_partner', { values: { partner: partner.user.name } })}
|
||||
</p>
|
||||
<SettingSwitch
|
||||
title={$t('show_in_timeline')}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</script>
|
||||
|
||||
<div class="border border-gray-300 dark:border-immich-dark-gray rounded-3xl pt-1 pb-6 dark:text-white">
|
||||
<p class="text-xs font-medium p-4">{$t('organize_your_library').toUpperCase()}</p>
|
||||
<p class="uppercase text-xs font-medium p-4">{$t('organize_your_library')}</p>
|
||||
|
||||
{#each links as link (link.href)}
|
||||
<a href={link.href} class="w-full hover:bg-gray-100 dark:hover:bg-immich-dark-gray flex items-center gap-4 p-4">
|
||||
|
||||
@@ -119,7 +119,7 @@
|
||||
<ModalBody>
|
||||
<div class="items-center justify-center">
|
||||
<div class="py-2">
|
||||
<h2 class="text-gray text-sm mb-2">{$t('settings').toUpperCase()}</h2>
|
||||
<h2 class="uppercase text-gray text-sm mb-2">{$t('settings')}</h2>
|
||||
<div class="grid p-2 gap-y-2">
|
||||
{#if order}
|
||||
<SettingDropdown
|
||||
@@ -138,7 +138,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="py-2">
|
||||
<div class="text-gray text-sm mb-3">{$t('people').toUpperCase()}</div>
|
||||
<div class="uppercase text-gray text-sm mb-3">{$t('people')}</div>
|
||||
<div class="p-2">
|
||||
<button type="button" class="flex items-center gap-2" onclick={() => onClose({ action: 'shareUser' })}>
|
||||
<div class="rounded-full w-10 h-10 border border-gray-500 flex items-center justify-center">
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label={$t('tag').toUpperCase()}
|
||||
label={$t('tag')}
|
||||
bind:value={tagValue}
|
||||
required={true}
|
||||
autofocus={true}
|
||||
|
||||
@@ -40,11 +40,7 @@
|
||||
<ModalBody>
|
||||
<form onsubmit={handleEdit} autocomplete="off" id="edit-tag-form">
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.COLOR}
|
||||
label={$t('color').toUpperCase()}
|
||||
bind:value={tagColor}
|
||||
/>
|
||||
<SettingInputField inputType={SettingInputFieldType.COLOR} label={$t('color')} bind:value={tagColor} />
|
||||
</div>
|
||||
</form>
|
||||
</ModalBody>
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
import SelectAllAssets from '$lib/components/photos-page/actions/select-all-assets.svelte';
|
||||
import SetVisibilityAction from '$lib/components/photos-page/actions/set-visibility-action.svelte';
|
||||
import TagAction from '$lib/components/photos-page/actions/tag-action.svelte';
|
||||
import AssetGrid from '$lib/components/photos-page/asset-grid.svelte';
|
||||
import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte';
|
||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
@@ -32,6 +31,7 @@
|
||||
notificationController,
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import UserAvatar from '$lib/components/shared-components/user-avatar.svelte';
|
||||
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
||||
import { AlbumPageViewMode, AppRoute } from '$lib/constants';
|
||||
import { activityManager } from '$lib/managers/activity-manager.svelte';
|
||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||
@@ -443,7 +443,7 @@
|
||||
<div class="flex overflow-hidden" use:scrollMemoryClearer={{ routeStartsWith: AppRoute.ALBUMS }}>
|
||||
<div class="relative w-full shrink">
|
||||
<main class="relative h-dvh overflow-hidden px-2 md:px-6 max-md:pt-(--navbar-height-md) pt-(--navbar-height)">
|
||||
<AssetGrid
|
||||
<Timeline
|
||||
enableRouting={viewMode === AlbumPageViewMode.SELECT_ASSETS ? false : true}
|
||||
{album}
|
||||
{timelineManager}
|
||||
@@ -529,7 +529,7 @@
|
||||
{#if album.assetCount === 0}
|
||||
<section id="empty-album" class=" mt-[200px] flex place-content-center place-items-center">
|
||||
<div class="w-[300px]">
|
||||
<p class="text-xs dark:text-immich-dark-fg">{$t('add_photos').toUpperCase()}</p>
|
||||
<p class="uppercase text-xs dark:text-immich-dark-fg">{$t('add_photos')}</p>
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => (viewMode = AlbumPageViewMode.SELECT_ASSETS)}
|
||||
@@ -544,7 +544,7 @@
|
||||
</section>
|
||||
{/if}
|
||||
{/if}
|
||||
</AssetGrid>
|
||||
</Timeline>
|
||||
|
||||
{#if showActivityStatus && !activityManager.isLoading}
|
||||
<div class="absolute z-2 bottom-0 end-0 mb-6 me-6 justify-self-end">
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
import DownloadAction from '$lib/components/photos-page/actions/download-action.svelte';
|
||||
import FavoriteAction from '$lib/components/photos-page/actions/favorite-action.svelte';
|
||||
import SelectAllAssets from '$lib/components/photos-page/actions/select-all-assets.svelte';
|
||||
import AssetGrid from '$lib/components/photos-page/asset-grid.svelte';
|
||||
import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte';
|
||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||
import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte';
|
||||
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
||||
import { AssetAction } from '$lib/constants';
|
||||
|
||||
import SetVisibilityAction from '$lib/components/photos-page/actions/set-visibility-action.svelte';
|
||||
@@ -47,7 +47,7 @@
|
||||
</script>
|
||||
|
||||
<UserPageLayout hideNavbar={assetInteraction.selectionActive} title={data.meta.title} scrollbar={false}>
|
||||
<AssetGrid
|
||||
<Timeline
|
||||
enableRouting={true}
|
||||
{timelineManager}
|
||||
{assetInteraction}
|
||||
@@ -57,7 +57,7 @@
|
||||
{#snippet empty()}
|
||||
<EmptyPlaceholder text={$t('no_archived_assets_message')} />
|
||||
{/snippet}
|
||||
</AssetGrid>
|
||||
</Timeline>
|
||||
</UserPageLayout>
|
||||
|
||||
{#if assetInteraction.selectionActive}
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
import SelectAllAssets from '$lib/components/photos-page/actions/select-all-assets.svelte';
|
||||
import SetVisibilityAction from '$lib/components/photos-page/actions/set-visibility-action.svelte';
|
||||
import TagAction from '$lib/components/photos-page/actions/tag-action.svelte';
|
||||
import AssetGrid from '$lib/components/photos-page/asset-grid.svelte';
|
||||
import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte';
|
||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||
import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte';
|
||||
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
||||
import { AssetAction } from '$lib/constants';
|
||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||
@@ -51,7 +51,7 @@
|
||||
</script>
|
||||
|
||||
<UserPageLayout hideNavbar={assetInteraction.selectionActive} title={data.meta.title} scrollbar={false}>
|
||||
<AssetGrid
|
||||
<Timeline
|
||||
enableRouting={true}
|
||||
withStacked={true}
|
||||
{timelineManager}
|
||||
@@ -62,7 +62,7 @@
|
||||
{#snippet empty()}
|
||||
<EmptyPlaceholder text={$t('no_favorites_message')} />
|
||||
{/snippet}
|
||||
</AssetGrid>
|
||||
</Timeline>
|
||||
</UserPageLayout>
|
||||
|
||||
<!-- Multiselection mode app bar -->
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
<Sidebar>
|
||||
<SkipLink target={`#${headerId}`} text={$t('skip_to_folders')} breakpoint="md" />
|
||||
<section>
|
||||
<div class="text-xs ps-4 mb-2 dark:text-white">{$t('explorer').toUpperCase()}</div>
|
||||
<div class="uppercase text-xs ps-4 mb-2 dark:text-white">{$t('explorer')}</div>
|
||||
<div class="h-full">
|
||||
<TreeItems
|
||||
icons={{ default: mdiFolderOutline, active: mdiFolder }}
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
import DownloadAction from '$lib/components/photos-page/actions/download-action.svelte';
|
||||
import SelectAllAssets from '$lib/components/photos-page/actions/select-all-assets.svelte';
|
||||
import SetVisibilityAction from '$lib/components/photos-page/actions/set-visibility-action.svelte';
|
||||
import AssetGrid from '$lib/components/photos-page/asset-grid.svelte';
|
||||
import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte';
|
||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||
import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte';
|
||||
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
||||
import { AppRoute, AssetAction } from '$lib/constants';
|
||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||
@@ -58,7 +58,7 @@
|
||||
</Button>
|
||||
{/snippet}
|
||||
|
||||
<AssetGrid
|
||||
<Timeline
|
||||
enableRouting={true}
|
||||
{timelineManager}
|
||||
{assetInteraction}
|
||||
@@ -68,7 +68,7 @@
|
||||
{#snippet empty()}
|
||||
<EmptyPlaceholder text={$t('no_locked_photos_message')} title={$t('nothing_here_yet')} />
|
||||
{/snippet}
|
||||
</AssetGrid>
|
||||
</Timeline>
|
||||
</UserPageLayout>
|
||||
|
||||
<!-- Multi-selection mode app bar -->
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
import AddToAlbum from '$lib/components/photos-page/actions/add-to-album.svelte';
|
||||
import CreateSharedLink from '$lib/components/photos-page/actions/create-shared-link.svelte';
|
||||
import DownloadAction from '$lib/components/photos-page/actions/download-action.svelte';
|
||||
import AssetGrid from '$lib/components/photos-page/asset-grid.svelte';
|
||||
import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte';
|
||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
|
||||
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||
@@ -43,7 +43,7 @@
|
||||
</script>
|
||||
|
||||
<main class="relative h-dvh overflow-hidden px-2 md:px-6 max-md:pt-(--navbar-height-md) pt-(--navbar-height)">
|
||||
<AssetGrid enableRouting={true} {timelineManager} {assetInteraction} onEscape={handleEscape} />
|
||||
<Timeline enableRouting={true} {timelineManager} {assetInteraction} onEscape={handleEscape} />
|
||||
</main>
|
||||
|
||||
{#if assetInteraction.selectionActive}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
import SelectAllAssets from '$lib/components/photos-page/actions/select-all-assets.svelte';
|
||||
import SetVisibilityAction from '$lib/components/photos-page/actions/set-visibility-action.svelte';
|
||||
import TagAction from '$lib/components/photos-page/actions/tag-action.svelte';
|
||||
import AssetGrid from '$lib/components/photos-page/asset-grid.svelte';
|
||||
import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte';
|
||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
@@ -30,6 +29,7 @@
|
||||
NotificationType,
|
||||
notificationController,
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
||||
import { AppRoute, PersonPageViewMode, QueryParameter, SessionStorageKey } from '$lib/constants';
|
||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||
@@ -386,7 +386,7 @@
|
||||
}}
|
||||
>
|
||||
{#key person.id}
|
||||
<AssetGrid
|
||||
<Timeline
|
||||
enableRouting={true}
|
||||
{person}
|
||||
{timelineManager}
|
||||
@@ -498,7 +498,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</AssetGrid>
|
||||
</Timeline>
|
||||
{/key}
|
||||
</main>
|
||||
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
import SetVisibilityAction from '$lib/components/photos-page/actions/set-visibility-action.svelte';
|
||||
import StackAction from '$lib/components/photos-page/actions/stack-action.svelte';
|
||||
import TagAction from '$lib/components/photos-page/actions/tag-action.svelte';
|
||||
import AssetGrid from '$lib/components/photos-page/asset-grid.svelte';
|
||||
import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte';
|
||||
import MemoryLane from '$lib/components/photos-page/memory-lane.svelte';
|
||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||
import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte';
|
||||
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
||||
import { AssetAction } from '$lib/constants';
|
||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||
@@ -89,7 +89,7 @@
|
||||
</script>
|
||||
|
||||
<UserPageLayout hideNavbar={assetInteraction.selectionActive} showUploadButton scrollbar={false}>
|
||||
<AssetGrid
|
||||
<Timeline
|
||||
enableRouting={true}
|
||||
{timelineManager}
|
||||
{assetInteraction}
|
||||
@@ -103,7 +103,7 @@
|
||||
{#snippet empty()}
|
||||
<EmptyPlaceholder text={$t('no_assets_message')} onClick={() => openFileUploadDialog()} />
|
||||
{/snippet}
|
||||
</AssetGrid>
|
||||
</Timeline>
|
||||
</UserPageLayout>
|
||||
|
||||
{#if assetInteraction.selectionActive}
|
||||
|
||||
@@ -368,11 +368,11 @@
|
||||
>
|
||||
{#if searchResultAlbums.length > 0}
|
||||
<section>
|
||||
<div class="ms-6 text-4xl font-medium text-black/70 dark:text-white/80">{$t('albums').toUpperCase()}</div>
|
||||
<div class="uppercase ms-6 text-4xl font-medium text-black/70 dark:text-white/80">{$t('albums')}</div>
|
||||
<AlbumCardGroup albums={searchResultAlbums} showDateRange showItemCount />
|
||||
|
||||
<div class="m-6 text-4xl font-medium text-black/70 dark:text-white/80">
|
||||
{$t('photos_and_videos').toUpperCase()}
|
||||
<div class="uppercase m-6 text-4xl font-medium text-black/70 dark:text-white/80">
|
||||
{$t('photos_and_videos')}
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import SkipLink from '$lib/components/elements/buttons/skip-link.svelte';
|
||||
import UserPageLayout, { headerId } from '$lib/components/layouts/user-page-layout.svelte';
|
||||
import AssetGrid from '$lib/components/photos-page/asset-grid.svelte';
|
||||
import Breadcrumbs from '$lib/components/shared-components/tree/breadcrumbs.svelte';
|
||||
import TreeItemThumbnails from '$lib/components/shared-components/tree/tree-item-thumbnails.svelte';
|
||||
import TreeItems from '$lib/components/shared-components/tree/tree-items.svelte';
|
||||
import Sidebar from '$lib/components/sidebar/sidebar.svelte';
|
||||
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
||||
import { AppRoute, AssetAction, QueryParameter } from '$lib/constants';
|
||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||
import TagCreateModal from '$lib/modals/TagCreateModal.svelte';
|
||||
@@ -88,7 +88,7 @@
|
||||
<Sidebar>
|
||||
<SkipLink target={`#${headerId}`} text={$t('skip_to_tags')} breakpoint="md" />
|
||||
<section>
|
||||
<div class="text-xs ps-4 mb-2 dark:text-white">{$t('explorer').toUpperCase()}</div>
|
||||
<div class="uppercase text-xs ps-4 mb-2 dark:text-white">{$t('explorer')}</div>
|
||||
<div class="h-full">
|
||||
<TreeItems icons={{ default: mdiTag, active: mdiTag }} {tree} active={tag.path} {getLink} />
|
||||
</div>
|
||||
@@ -117,11 +117,11 @@
|
||||
|
||||
<section class="mt-2 h-[calc(100%-(--spacing(20)))] overflow-auto immich-scrollbar">
|
||||
{#if tag.hasAssets}
|
||||
<AssetGrid enableRouting={true} {timelineManager} {assetInteraction} removeAction={AssetAction.UNARCHIVE}>
|
||||
<Timeline enableRouting={true} {timelineManager} {assetInteraction} removeAction={AssetAction.UNARCHIVE}>
|
||||
{#snippet empty()}
|
||||
<TreeItemThumbnails items={tag.children} icon={mdiTag} onClick={handleNavigation} />
|
||||
{/snippet}
|
||||
</AssetGrid>
|
||||
</Timeline>
|
||||
{:else}
|
||||
<TreeItemThumbnails items={tag.children} icon={mdiTag} onClick={handleNavigation} />
|
||||
{/if}
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
import DeleteAssets from '$lib/components/photos-page/actions/delete-assets.svelte';
|
||||
import RestoreAssets from '$lib/components/photos-page/actions/restore-assets.svelte';
|
||||
import SelectAllAssets from '$lib/components/photos-page/actions/select-all-assets.svelte';
|
||||
import AssetGrid from '$lib/components/photos-page/asset-grid.svelte';
|
||||
import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte';
|
||||
import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte';
|
||||
import {
|
||||
notificationController,
|
||||
NotificationType,
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||
@@ -116,14 +116,14 @@
|
||||
</HStack>
|
||||
{/snippet}
|
||||
|
||||
<AssetGrid enableRouting={true} {timelineManager} {assetInteraction} onEscape={handleEscape}>
|
||||
<Timeline enableRouting={true} {timelineManager} {assetInteraction} onEscape={handleEscape}>
|
||||
<p class="font-medium text-gray-500/60 dark:text-gray-300/60 p-4">
|
||||
{$t('trashed_items_will_be_permanently_deleted_after', { values: { days: $serverConfig.trashDays } })}
|
||||
</p>
|
||||
{#snippet empty()}
|
||||
<EmptyPlaceholder text={$t('trash_no_results_message')} src={empty3Url} />
|
||||
{/snippet}
|
||||
</AssetGrid>
|
||||
</Timeline>
|
||||
</UserPageLayout>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
||||
import AssetGrid from '$lib/components/photos-page/asset-grid.svelte';
|
||||
import ChangeLocation from '$lib/components/shared-components/change-location.svelte';
|
||||
import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte';
|
||||
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
||||
import { AssetAction } from '$lib/constants';
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import type { DayGroup } from '$lib/managers/timeline-manager/day-group.svelte';
|
||||
@@ -185,7 +185,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<AssetGrid
|
||||
<Timeline
|
||||
isSelectionMode={true}
|
||||
enableRouting={true}
|
||||
{timelineManager}
|
||||
@@ -209,5 +209,5 @@
|
||||
{#snippet empty()}
|
||||
<EmptyPlaceholder text={$t('no_assets_message')} onClick={() => {}} />
|
||||
{/snippet}
|
||||
</AssetGrid>
|
||||
</Timeline>
|
||||
</UserPageLayout>
|
||||
|
||||
@@ -242,14 +242,9 @@
|
||||
</div>
|
||||
<div class="col-span-full">
|
||||
<div class="flex flex-col lg:flex-row gap-4 w-full">
|
||||
<StatsCard icon={mdiCameraIris} title={$t('photos').toUpperCase()} value={userStatistics.images} />
|
||||
<StatsCard icon={mdiPlayCircle} title={$t('videos').toUpperCase()} value={userStatistics.videos} />
|
||||
<StatsCard
|
||||
icon={mdiChartPie}
|
||||
title={$t('storage').toUpperCase()}
|
||||
value={statsUsage}
|
||||
unit={statsUsageUnit}
|
||||
/>
|
||||
<StatsCard icon={mdiCameraIris} title={$t('photos')} value={userStatistics.images} />
|
||||
<StatsCard icon={mdiPlayCircle} title={$t('videos')} value={userStatistics.videos} />
|
||||
<StatsCard icon={mdiChartPie} title={$t('storage')} value={statsUsage} unit={statsUsageUnit} />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@@ -156,9 +156,9 @@
|
||||
<div class="inline-flex w-full items-center justify-center my-4">
|
||||
<hr class="my-4 h-px w-3/4 border-0 bg-gray-200 dark:bg-gray-600" />
|
||||
<span
|
||||
class="absolute start-1/2 -translate-x-1/2 bg-gray-50 px-3 font-medium text-gray-900 dark:bg-neutral-900 dark:text-white"
|
||||
class="absolute start-1/2 -translate-x-1/2 bg-gray-50 px-3 font-medium text-gray-900 dark:bg-neutral-900 dark:text-white uppercase"
|
||||
>
|
||||
{$t('or').toUpperCase()}
|
||||
{$t('or')}
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user