mirror of
https://github.com/immich-app/immich.git
synced 2025-07-07 18:24:10 -04:00
re-add alt-text
This commit is contained in:
parent
6cb7fffe91
commit
0795f8a761
@ -1,26 +1,27 @@
|
||||
<script lang="ts">
|
||||
import { shortcuts } from '$lib/actions/shortcut';
|
||||
import { zoomImageAction, zoomed } from '$lib/actions/zoom-image';
|
||||
import FaceEditor from '$lib/components/asset-viewer/face-editor/face-editor.svelte';
|
||||
import BrokenAsset from '$lib/components/assets/broken-asset.svelte';
|
||||
import { photoViewerImgElement } from '$lib/stores/assets-store.svelte';
|
||||
import { isFaceEditMode } from '$lib/stores/face-edit.svelte';
|
||||
import { boundingBoxesArray } from '$lib/stores/people.store';
|
||||
import { alwaysLoadOriginalFile } from '$lib/stores/preferences.store';
|
||||
import { SlideshowLook, SlideshowState, slideshowLookCssMapping, slideshowStore } from '$lib/stores/slideshow.store';
|
||||
import { photoZoomState } from '$lib/stores/zoom-image.store';
|
||||
import { getAssetOriginalUrl, getAssetThumbnailUrl, handlePromiseError } from '$lib/utils';
|
||||
import { canCopyImageToClipboard, copyImageToClipboard, isWebCompatibleImage } from '$lib/utils/asset-utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { getBoundingBox } from '$lib/utils/people-utils';
|
||||
import { getAltText } from '$lib/utils/thumbnail-util';
|
||||
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
||||
import { AssetMediaSize, type AssetResponseDto, type SharedLinkResponseDto } from '@immich/sdk';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { type SwipeCustomEvent, swipe } from 'svelte-gestures';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { fade } from 'svelte/transition';
|
||||
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
|
||||
import { NotificationType, notificationController } from '../shared-components/notification/notification';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import FaceEditor from '$lib/components/asset-viewer/face-editor/face-editor.svelte';
|
||||
import { photoViewerImgElement } from '$lib/stores/assets-store.svelte';
|
||||
import { isFaceEditMode } from '$lib/stores/face-edit.svelte';
|
||||
|
||||
interface Props {
|
||||
asset: AssetResponseDto;
|
||||
@ -194,7 +195,7 @@
|
||||
bind:clientWidth={containerWidth}
|
||||
bind:clientHeight={containerHeight}
|
||||
>
|
||||
<img style="display:none" src={imageLoaderUrl} alt={$getAltText(asset)} {onload} {onerror} />
|
||||
<img style="display:none" src={imageLoaderUrl} alt="" {onload} {onerror} />
|
||||
{#if !imageLoaded}
|
||||
<div id="spinner" class="flex h-full items-center justify-center">
|
||||
<LoadingSpinner />
|
||||
@ -210,7 +211,7 @@
|
||||
{#if $slideshowState !== SlideshowState.None && $slideshowLook === SlideshowLook.BlurredBackground}
|
||||
<img
|
||||
src={assetFileUrl}
|
||||
alt={$getAltText(asset)}
|
||||
alt=""
|
||||
class="absolute top-0 left-0 -z-10 object-cover h-full w-full blur-lg"
|
||||
draggable="false"
|
||||
/>
|
||||
@ -218,7 +219,7 @@
|
||||
<img
|
||||
bind:this={$photoViewerImgElement}
|
||||
src={assetFileUrl}
|
||||
alt={$getAltText(asset)}
|
||||
alt={$getAltText(toTimelineAsset(asset))}
|
||||
class="h-full w-full {$slideshowState === SlideshowState.None
|
||||
? 'object-contain'
|
||||
: slideshowLookCssMapping[$slideshowLook]}"
|
||||
|
@ -4,7 +4,7 @@
|
||||
import { locale, playVideoThumbnailOnHover } from '$lib/stores/preferences.store';
|
||||
import { getAssetPlaybackUrl, getAssetThumbnailUrl, isSharedLink } from '$lib/utils';
|
||||
import { timeToSeconds } from '$lib/utils/date-time';
|
||||
// import { getAltText } from '$lib/utils/thumbnail-util';
|
||||
import { getAltText } from '$lib/utils/thumbnail-util';
|
||||
import { AssetMediaSize } from '@immich/sdk';
|
||||
import {
|
||||
mdiArchiveArrowDownOutline,
|
||||
@ -21,7 +21,6 @@
|
||||
import { mobileDevice } from '$lib/stores/mobile-device.svelte';
|
||||
import { getFocusable } from '$lib/utils/focus-util';
|
||||
import { currentUrlReplaceAssetId } from '$lib/utils/navigation';
|
||||
import { getAltTextForTimelineAsset } from '$lib/utils/thumbnail-util';
|
||||
import { TUNABLES } from '$lib/utils/tunables';
|
||||
import { onMount } from 'svelte';
|
||||
import type { ClassValue } from 'svelte/elements';
|
||||
@ -368,12 +367,11 @@
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<!-- altText={$getAltText(asset)} -->
|
||||
<ImageThumbnail
|
||||
class={imageClass}
|
||||
{brokenAssetClass}
|
||||
url={getAssetThumbnailUrl({ id: asset.id, size: AssetMediaSize.Thumbnail, cacheKey: asset.thumbhash })}
|
||||
altText={getAltTextForTimelineAsset(asset)}
|
||||
altText={$getAltText(asset)}
|
||||
widthStyle="{width}px"
|
||||
heightStyle="{height}px"
|
||||
curve={selected}
|
||||
|
@ -5,6 +5,7 @@
|
||||
import { memoryStore } from '$lib/stores/memory.store.svelte';
|
||||
import { getAssetThumbnailUrl, memoryLaneTitle } from '$lib/utils';
|
||||
import { getAltText } from '$lib/utils/thumbnail-util';
|
||||
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
||||
import { mdiChevronLeft, mdiChevronRight } from '@mdi/js';
|
||||
import { onMount } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
@ -82,7 +83,7 @@
|
||||
<img
|
||||
class="h-full w-full rounded-xl object-cover"
|
||||
src={getAssetThumbnailUrl(memory.assets[0].id)}
|
||||
alt={$t('memory_lane_title', { values: { title: $getAltText(memory.assets[0]) } })}
|
||||
alt={$t('memory_lane_title', { values: { title: $getAltText(toTimelineAsset(memory.assets[0])) } })}
|
||||
draggable="false"
|
||||
/>
|
||||
<p class="absolute bottom-2 left-4 z-10 text-lg text-white max-md:text-sm">
|
||||
|
@ -3,6 +3,7 @@
|
||||
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { getAssetResolution, getFileSize } from '$lib/utils/asset-utils';
|
||||
import { getAltText } from '$lib/utils/thumbnail-util';
|
||||
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
||||
import { type AssetResponseDto, getAllAlbums } from '@immich/sdk';
|
||||
import { mdiHeart, mdiImageMultipleOutline, mdiMagnifyPlus } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
@ -36,7 +37,7 @@
|
||||
<!-- THUMBNAIL-->
|
||||
<img
|
||||
src={getAssetThumbnailUrl(asset.id)}
|
||||
alt={$getAltText(asset)}
|
||||
alt={$getAltText(toTimelineAsset(asset))}
|
||||
title={assetData}
|
||||
class="h-60 object-cover rounded-t-xl w-full"
|
||||
draggable="false"
|
||||
|
@ -15,7 +15,6 @@ import {
|
||||
getTimeBucket,
|
||||
getTimeBuckets,
|
||||
TimeBucketSize,
|
||||
type AssetResponseDto,
|
||||
type AssetStackResponseDto,
|
||||
} from '@immich/sdk';
|
||||
import { clamp, debounce, isEqual, throttle } from 'lodash-es';
|
||||
@ -85,6 +84,11 @@ export type TimelineAsset = {
|
||||
duration: string | null;
|
||||
projectionType: string | null;
|
||||
livePhotoVideoId: string | null;
|
||||
text: {
|
||||
city: string | null;
|
||||
country: string | null;
|
||||
people: string[];
|
||||
};
|
||||
};
|
||||
class IntersectingAsset {
|
||||
// --- public ---
|
||||
@ -1089,7 +1093,7 @@ export class AssetStore {
|
||||
);
|
||||
if (bucketResponse) {
|
||||
if (this.#options.timelineAlbumId) {
|
||||
const bucketAssets = await getTimeBucket(
|
||||
const albumAssets = await getTimeBucket(
|
||||
{
|
||||
albumId: this.#options.timelineAlbumId,
|
||||
timeBucket: bucketDate,
|
||||
@ -1098,7 +1102,7 @@ export class AssetStore {
|
||||
},
|
||||
{ signal },
|
||||
);
|
||||
for (const { id } of bucketAssets) {
|
||||
for (const { id } of albumAssets) {
|
||||
this.albumAssets.add(id);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import type { TimelineAsset } from '$lib/stores/assets-store.svelte';
|
||||
import { AssetTypeEnum, type AssetResponseDto } from '@immich/sdk';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { derived } from 'svelte/store';
|
||||
import { fromLocalDateTime } from './timeline-util';
|
||||
@ -44,24 +43,18 @@ export const getAltTextForTimelineAsset = (_: TimelineAsset) => {
|
||||
};
|
||||
|
||||
export const getAltText = derived(t, ($t) => {
|
||||
return (asset: AssetResponseDto | null | undefined) => {
|
||||
if (!asset) {
|
||||
return '';
|
||||
}
|
||||
if (asset.exifInfo?.description) {
|
||||
return asset.exifInfo.description;
|
||||
}
|
||||
|
||||
return (asset: TimelineAsset) => {
|
||||
const date = fromLocalDateTime(asset.localDateTime).toLocaleString({ dateStyle: 'long' });
|
||||
const hasPlace = !!asset.exifInfo?.city && !!asset.exifInfo?.country;
|
||||
const names = asset.people?.filter((p) => p.name).map((p) => p.name) ?? [];
|
||||
const { city, country, people: names } = asset.text;
|
||||
const hasPlace = city && country;
|
||||
|
||||
const peopleCount = names.length;
|
||||
const isVideo = asset.type === AssetTypeEnum.Video;
|
||||
const isVideo = asset.isVideo;
|
||||
|
||||
const values = {
|
||||
date,
|
||||
city: asset.exifInfo?.city,
|
||||
country: asset.exifInfo?.country,
|
||||
city,
|
||||
country,
|
||||
person1: names[0],
|
||||
person2: names[1],
|
||||
person3: names[2],
|
||||
|
@ -114,6 +114,14 @@ export const toTimelineAsset = (unknownAsset: AssetResponseDto | TimelineAsset):
|
||||
const assetResponse = unknownAsset as AssetResponseDto;
|
||||
const { width, height } = getAssetRatio(assetResponse);
|
||||
const ratio = width / height;
|
||||
const city = assetResponse.exifInfo?.city;
|
||||
const country = assetResponse.exifInfo?.country;
|
||||
const people = assetResponse.people?.map((person) => person.name) || [];
|
||||
const text = {
|
||||
city: city || null,
|
||||
country: country || null,
|
||||
people,
|
||||
};
|
||||
return {
|
||||
id: assetResponse.id,
|
||||
ownerId: assetResponse.ownerId,
|
||||
@ -129,6 +137,7 @@ export const toTimelineAsset = (unknownAsset: AssetResponseDto | TimelineAsset):
|
||||
duration: assetResponse.duration || null,
|
||||
projectionType: assetResponse.exifInfo?.projectionType || null,
|
||||
livePhotoVideoId: assetResponse.livePhotoVideoId || null,
|
||||
text,
|
||||
};
|
||||
};
|
||||
export const isTimelineAsset = (arg: AssetResponseDto | TimelineAsset): arg is TimelineAsset =>
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { browser } from '$app/environment';
|
||||
|
||||
function getBoolean(string: string | null, fallback: boolean) {
|
||||
if (string === null) {
|
||||
return fallback;
|
||||
@ -11,13 +12,11 @@ function getNumber(string: string | null, fallback: number) {
|
||||
}
|
||||
return Number.parseInt(string);
|
||||
}
|
||||
|
||||
const storage = browser
|
||||
? localStorage
|
||||
: {
|
||||
getItem: () => null,
|
||||
};
|
||||
|
||||
export const TUNABLES = {
|
||||
LAYOUT: {
|
||||
WASM: getBoolean(storage.getItem('LAYOUT.WASM'), false),
|
||||
|
Loading…
x
Reference in New Issue
Block a user