mirror of
https://github.com/immich-app/immich
synced 2025-11-07 17:27:20 +00:00
fix(web): asset refresh (#21788)
This commit is contained in:
parent
7e377d3e42
commit
761ac074c9
4 changed files with 56 additions and 82 deletions
|
|
@ -21,7 +21,6 @@
|
||||||
|
|
||||||
const handleAddTag = async () => {
|
const handleAddTag = async () => {
|
||||||
const success = await modalManager.show(AssetTagModal, { assetIds: [asset.id] });
|
const success = await modalManager.show(AssetTagModal, { assetIds: [asset.id] });
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
asset = await getAssetInfo({ id: asset.id });
|
asset = await getAssetInfo({ id: asset.id });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,21 +16,14 @@
|
||||||
import { locale } from '$lib/stores/preferences.store';
|
import { locale } from '$lib/stores/preferences.store';
|
||||||
import { featureFlags } from '$lib/stores/server-config.store';
|
import { featureFlags } from '$lib/stores/server-config.store';
|
||||||
import { preferences, user } from '$lib/stores/user.store';
|
import { preferences, user } from '$lib/stores/user.store';
|
||||||
import { getAssetThumbnailUrl, getPeopleThumbnailUrl, handlePromiseError } from '$lib/utils';
|
import { getAssetThumbnailUrl, getPeopleThumbnailUrl } from '$lib/utils';
|
||||||
import { delay, isFlipped } from '$lib/utils/asset-utils';
|
import { delay, getDimensions } from '$lib/utils/asset-utils';
|
||||||
import { getByteUnitString } from '$lib/utils/byte-units';
|
import { getByteUnitString } from '$lib/utils/byte-units';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { getMetadataSearchQuery } from '$lib/utils/metadata-search';
|
import { getMetadataSearchQuery } from '$lib/utils/metadata-search';
|
||||||
import { fromISODateTime, fromISODateTimeUTC } from '$lib/utils/timeline-util';
|
import { fromISODateTime, fromISODateTimeUTC } from '$lib/utils/timeline-util';
|
||||||
import { getParentPath } from '$lib/utils/tree-utils';
|
import { getParentPath } from '$lib/utils/tree-utils';
|
||||||
import {
|
import { AssetMediaSize, getAssetInfo, updateAsset, type AlbumResponseDto, type AssetResponseDto } from '@immich/sdk';
|
||||||
AssetMediaSize,
|
|
||||||
getAssetInfo,
|
|
||||||
updateAsset,
|
|
||||||
type AlbumResponseDto,
|
|
||||||
type AssetResponseDto,
|
|
||||||
type ExifResponseDto,
|
|
||||||
} from '@immich/sdk';
|
|
||||||
import { IconButton } from '@immich/ui';
|
import { IconButton } from '@immich/ui';
|
||||||
import {
|
import {
|
||||||
mdiCalendar,
|
mdiCalendar,
|
||||||
|
|
@ -61,17 +54,28 @@
|
||||||
|
|
||||||
let { asset, albums = [], currentAlbum = null, onClose }: Props = $props();
|
let { asset, albums = [], currentAlbum = null, onClose }: Props = $props();
|
||||||
|
|
||||||
const getDimensions = (exifInfo: ExifResponseDto) => {
|
|
||||||
const { exifImageWidth: width, exifImageHeight: height } = exifInfo;
|
|
||||||
if (isFlipped(exifInfo.orientation)) {
|
|
||||||
return { width: height, height: width };
|
|
||||||
}
|
|
||||||
|
|
||||||
return { width, height };
|
|
||||||
};
|
|
||||||
|
|
||||||
let showAssetPath = $state(false);
|
let showAssetPath = $state(false);
|
||||||
let showEditFaces = $state(false);
|
let showEditFaces = $state(false);
|
||||||
|
let isOwner = $derived($user?.id === asset.ownerId);
|
||||||
|
let people = $derived(asset.people || []);
|
||||||
|
let unassignedFaces = $derived(asset.unassignedFaces || []);
|
||||||
|
let showingHiddenPeople = $state(false);
|
||||||
|
let timeZone = $derived(asset.exifInfo?.timeZone);
|
||||||
|
let dateTime = $derived(
|
||||||
|
timeZone && asset.exifInfo?.dateTimeOriginal
|
||||||
|
? fromISODateTime(asset.exifInfo.dateTimeOriginal, timeZone)
|
||||||
|
: fromISODateTimeUTC(asset.localDateTime),
|
||||||
|
);
|
||||||
|
let latlng = $derived(
|
||||||
|
(() => {
|
||||||
|
const lat = asset.exifInfo?.latitude;
|
||||||
|
const lng = asset.exifInfo?.longitude;
|
||||||
|
|
||||||
|
if (lat && lng) {
|
||||||
|
return { lat: Number(lat.toFixed(7)), lng: Number(lng.toFixed(7)) };
|
||||||
|
}
|
||||||
|
})(),
|
||||||
|
);
|
||||||
let previousId: string | undefined = $state();
|
let previousId: string | undefined = $state();
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
|
|
@ -84,42 +88,6 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let isOwner = $derived($user?.id === asset.ownerId);
|
|
||||||
|
|
||||||
const handleNewAsset = async (newAsset: AssetResponseDto) => {
|
|
||||||
// TODO: check if reloading asset data is necessary
|
|
||||||
if (newAsset.id && !authManager.isSharedLink) {
|
|
||||||
const data = await getAssetInfo({ id: asset.id });
|
|
||||||
people = data?.people || [];
|
|
||||||
unassignedFaces = data?.unassignedFaces || [];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
handlePromiseError(handleNewAsset(asset));
|
|
||||||
});
|
|
||||||
|
|
||||||
let latlng = $derived(
|
|
||||||
(() => {
|
|
||||||
const lat = asset.exifInfo?.latitude;
|
|
||||||
const lng = asset.exifInfo?.longitude;
|
|
||||||
|
|
||||||
if (lat && lng) {
|
|
||||||
return { lat: Number(lat.toFixed(7)), lng: Number(lng.toFixed(7)) };
|
|
||||||
}
|
|
||||||
})(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let people = $state(asset.people || []);
|
|
||||||
let unassignedFaces = $state(asset.unassignedFaces || []);
|
|
||||||
let showingHiddenPeople = $state(false);
|
|
||||||
let timeZone = $derived(asset.exifInfo?.timeZone);
|
|
||||||
let dateTime = $derived(
|
|
||||||
timeZone && asset.exifInfo?.dateTimeOriginal
|
|
||||||
? fromISODateTime(asset.exifInfo.dateTimeOriginal, timeZone)
|
|
||||||
: fromISODateTimeUTC(asset.localDateTime),
|
|
||||||
);
|
|
||||||
|
|
||||||
const getMegapixel = (width: number, height: number): number | undefined => {
|
const getMegapixel = (width: number, height: number): number | undefined => {
|
||||||
const megapixel = Math.round((height * width) / 1_000_000);
|
const megapixel = Math.round((height * width) / 1_000_000);
|
||||||
|
|
||||||
|
|
@ -131,10 +99,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRefreshPeople = async () => {
|
const handleRefreshPeople = async () => {
|
||||||
await getAssetInfo({ id: asset.id }).then((data) => {
|
asset = await getAssetInfo({ id: asset.id });
|
||||||
people = data?.people || [];
|
|
||||||
unassignedFaces = data?.unassignedFaces || [];
|
|
||||||
});
|
|
||||||
showEditFaces = false;
|
showEditFaces = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,12 @@
|
||||||
import { shortcuts } from '$lib/actions/shortcut';
|
import { shortcuts } from '$lib/actions/shortcut';
|
||||||
import Portal from '$lib/components/shared-components/portal/portal.svelte';
|
import Portal from '$lib/components/shared-components/portal/portal.svelte';
|
||||||
import DuplicateAsset from '$lib/components/utilities-page/duplicates/duplicate-asset.svelte';
|
import DuplicateAsset from '$lib/components/utilities-page/duplicates/duplicate-asset.svelte';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
import { handlePromiseError } from '$lib/utils';
|
import { handlePromiseError } from '$lib/utils';
|
||||||
import { suggestDuplicate } from '$lib/utils/duplicate-utils';
|
import { suggestDuplicate } from '$lib/utils/duplicate-utils';
|
||||||
import { navigate } from '$lib/utils/navigation';
|
import { navigate } from '$lib/utils/navigation';
|
||||||
import { type AssetResponseDto } from '@immich/sdk';
|
import { getAssetInfo, type AssetResponseDto } from '@immich/sdk';
|
||||||
import { Button } from '@immich/ui';
|
import { Button } from '@immich/ui';
|
||||||
import { mdiCheck, mdiImageMultipleOutline, mdiTrashCanOutline } from '@mdi/js';
|
import { mdiCheck, mdiImageMultipleOutline, mdiTrashCanOutline } from '@mdi/js';
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
|
|
@ -42,32 +43,32 @@
|
||||||
assetViewingStore.showAssetViewer(false);
|
assetViewingStore.showAssetViewer(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
const onNext = () => {
|
const onNext = async () => {
|
||||||
const index = getAssetIndex($viewingAsset.id) + 1;
|
const index = getAssetIndex($viewingAsset.id) + 1;
|
||||||
if (index >= assets.length) {
|
if (index >= assets.length) {
|
||||||
return Promise.resolve(false);
|
return false;
|
||||||
}
|
}
|
||||||
setAsset(assets[index]);
|
await onViewAsset(assets[index]);
|
||||||
return Promise.resolve(true);
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPrevious = () => {
|
const onPrevious = async () => {
|
||||||
const index = getAssetIndex($viewingAsset.id) - 1;
|
const index = getAssetIndex($viewingAsset.id) - 1;
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
return Promise.resolve(false);
|
return false;
|
||||||
}
|
}
|
||||||
setAsset(assets[index]);
|
await onViewAsset(assets[index]);
|
||||||
return Promise.resolve(true);
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onRandom = () => {
|
const onRandom = async () => {
|
||||||
if (assets.length <= 0) {
|
if (assets.length <= 0) {
|
||||||
return Promise.resolve(undefined);
|
return;
|
||||||
}
|
}
|
||||||
const index = Math.floor(Math.random() * assets.length);
|
const index = Math.floor(Math.random() * assets.length);
|
||||||
const asset = assets[index];
|
const asset = assets[index];
|
||||||
setAsset(asset);
|
await onViewAsset(asset);
|
||||||
return Promise.resolve(asset);
|
return { id: asset.id };
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSelectAsset = (asset: AssetResponseDto) => {
|
const onSelectAsset = (asset: AssetResponseDto) => {
|
||||||
|
|
@ -86,6 +87,12 @@
|
||||||
selectedAssetIds = new SvelteSet(assets.map((asset) => asset.id));
|
selectedAssetIds = new SvelteSet(assets.map((asset) => asset.id));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onViewAsset = async ({ id }: AssetResponseDto) => {
|
||||||
|
const asset = await getAssetInfo({ ...authManager.params, id });
|
||||||
|
setAsset(asset);
|
||||||
|
await navigate({ targetRoute: 'current', assetId: asset.id });
|
||||||
|
};
|
||||||
|
|
||||||
const handleResolve = () => {
|
const handleResolve = () => {
|
||||||
const trashIds = assets.map((asset) => asset.id).filter((id) => !selectedAssetIds.has(id));
|
const trashIds = assets.map((asset) => asset.id).filter((id) => !selectedAssetIds.has(id));
|
||||||
const duplicateAssetIds = assets.map((asset) => asset.id);
|
const duplicateAssetIds = assets.map((asset) => asset.id);
|
||||||
|
|
@ -102,9 +109,7 @@
|
||||||
{ shortcut: { key: 'a' }, onShortcut: onSelectAll },
|
{ shortcut: { key: 'a' }, onShortcut: onSelectAll },
|
||||||
{
|
{
|
||||||
shortcut: { key: 's' },
|
shortcut: { key: 's' },
|
||||||
onShortcut: () => {
|
onShortcut: () => onViewAsset(assets[0]),
|
||||||
setAsset(assets[0]);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{ shortcut: { key: 'd' }, onShortcut: onSelectNone },
|
{ shortcut: { key: 'd' }, onShortcut: onSelectNone },
|
||||||
{ shortcut: { key: 'c', shift: true }, onShortcut: handleResolve },
|
{ shortcut: { key: 'c', shift: true }, onShortcut: handleResolve },
|
||||||
|
|
@ -166,12 +171,7 @@
|
||||||
|
|
||||||
<div class="flex flex-wrap gap-1 mb-4 place-items-center place-content-center px-4 pt-4">
|
<div class="flex flex-wrap gap-1 mb-4 place-items-center place-content-center px-4 pt-4">
|
||||||
{#each assets as asset (asset.id)}
|
{#each assets as asset (asset.id)}
|
||||||
<DuplicateAsset
|
<DuplicateAsset {asset} {onSelectAsset} isSelected={selectedAssetIds.has(asset.id)} {onViewAsset} />
|
||||||
{asset}
|
|
||||||
{onSelectAsset}
|
|
||||||
isSelected={selectedAssetIds.has(asset.id)}
|
|
||||||
onViewAsset={(asset) => setAsset(asset)}
|
|
||||||
/>
|
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import {
|
||||||
type AssetResponseDto,
|
type AssetResponseDto,
|
||||||
type AssetTypeEnum,
|
type AssetTypeEnum,
|
||||||
type DownloadInfoDto,
|
type DownloadInfoDto,
|
||||||
|
type ExifResponseDto,
|
||||||
type StackResponseDto,
|
type StackResponseDto,
|
||||||
type UserPreferencesResponseDto,
|
type UserPreferencesResponseDto,
|
||||||
type UserResponseDto,
|
type UserResponseDto,
|
||||||
|
|
@ -328,6 +329,15 @@ export function isFlipped(orientation?: string | null) {
|
||||||
return value && (isRotated270CW(value) || isRotated90CW(value));
|
return value && (isRotated270CW(value) || isRotated90CW(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getDimensions = (exifInfo: ExifResponseDto) => {
|
||||||
|
const { exifImageWidth: width, exifImageHeight: height } = exifInfo;
|
||||||
|
if (isFlipped(exifInfo.orientation)) {
|
||||||
|
return { width: height, height: width };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { width, height };
|
||||||
|
};
|
||||||
|
|
||||||
export function getFileSize(asset: AssetResponseDto, maxPrecision = 4): string {
|
export function getFileSize(asset: AssetResponseDto, maxPrecision = 4): string {
|
||||||
const size = asset.exifInfo?.fileSizeInByte || 0;
|
const size = asset.exifInfo?.fileSizeInByte || 0;
|
||||||
return size > 0 ? getByteUnitString(size, undefined, maxPrecision) : 'Invalid Data';
|
return size > 0 ? getByteUnitString(size, undefined, maxPrecision) : 'Invalid Data';
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue