Calvin Bochulak 2025-12-17 16:41:35 -07:00 committed by GitHub
commit 36a473e304
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 87 additions and 1 deletions

View File

@ -1074,7 +1074,8 @@
"unable_to_update_settings": "Unable to update settings",
"unable_to_update_timeline_display_status": "Unable to update timeline display status",
"unable_to_update_user": "Unable to update user",
"unable_to_upload_file": "Unable to upload file"
"unable_to_upload_file": "Unable to upload file",
"unable_to_set_rating": "Unable to set rating"
},
"exclusion_pattern": "Exclusion pattern",
"exif": "Exif",
@ -1667,10 +1668,12 @@
"purchase_settings_server_activated": "The server product key is managed by the admin",
"query_asset_id": "Query Asset ID",
"queue_status": "Queuing {count}/{total}",
"rate_asset": "Rate Asset",
"rating": "Star rating",
"rating_clear": "Clear rating",
"rating_count": "{count, plural, one {# star} other {# stars}}",
"rating_description": "Display the EXIF rating in the info panel",
"rating_set": "Rating set to {rating} stars",
"reaction_options": "Reaction options",
"read_changelog": "Read Changelog",
"readonly_mode_disabled": "Read-only mode disabled",
@ -2231,6 +2234,7 @@
"yes": "Yes",
"you_dont_have_any_shared_links": "You don't have any shared links",
"your_wifi_name": "Your Wi-Fi name",
"zero_to_clear_rating": "press 0 to clear asset rating",
"zoom_image": "Zoom Image",
"zoom_to_bounds": "Zoom to bounds"
}

View File

@ -20,6 +20,7 @@ type ActionMap = {
[AssetAction.SET_VISIBILITY_LOCKED]: { asset: TimelineAsset };
[AssetAction.SET_VISIBILITY_TIMELINE]: { asset: TimelineAsset };
[AssetAction.SET_PERSON_FEATURED_PHOTO]: { asset: AssetResponseDto; person: PersonResponseDto };
[AssetAction.RATING]: { asset: TimelineAsset; rating: number | null };
};
export type Action = {

View File

@ -0,0 +1,64 @@
<script lang="ts">
import { shortcuts } from '$lib/actions/shortcut';
import type { OnAction } from '$lib/components/asset-viewer/actions/action';
import { AssetAction } from '$lib/constants';
import { preferences } from '$lib/stores/user.store';
import { handleError } from '$lib/utils/handle-error';
import { toTimelineAsset } from '$lib/utils/timeline-util';
import { updateAsset, type AssetResponseDto } from '@immich/sdk';
import { t } from 'svelte-i18n';
interface Props {
asset: AssetResponseDto;
onAction: OnAction;
}
let { asset, onAction }: Props = $props();
const rateAsset = async (rating: number | null) => {
try {
const updateAssetDto = rating === null ? {} : { rating };
await updateAsset({
id: asset.id,
updateAssetDto,
});
asset = {
...asset,
exifInfo: {
...asset.exifInfo,
rating,
},
};
onAction({
type: AssetAction.RATING,
asset: toTimelineAsset(asset),
rating,
});
} catch (error) {
handleError(error, $t('errors.unable_to_set_rating'));
}
};
const onShortcut0 = () => rateAsset(null);
const onShortcut1 = () => rateAsset(1);
const onShortcut2 = () => rateAsset(2);
const onShortcut3 = () => rateAsset(3);
const onShortcut4 = () => rateAsset(4);
const onShortcut5 = () => rateAsset(5);
</script>
<svelte:document
use:shortcuts={$preferences?.ratings.enabled
? [
{ shortcut: { key: '0' }, onShortcut: onShortcut0 },
{ shortcut: { key: '1' }, onShortcut: onShortcut1 },
{ shortcut: { key: '2' }, onShortcut: onShortcut2 },
{ shortcut: { key: '3' }, onShortcut: onShortcut3 },
{ shortcut: { key: '4' }, onShortcut: onShortcut4 },
{ shortcut: { key: '5' }, onShortcut: onShortcut5 },
]
: []}
/>

View File

@ -11,6 +11,7 @@
import DownloadAction from '$lib/components/asset-viewer/actions/download-action.svelte';
import FavoriteAction from '$lib/components/asset-viewer/actions/favorite-action.svelte';
import KeepThisDeleteOthersAction from '$lib/components/asset-viewer/actions/keep-this-delete-others.svelte';
import RatingAction from '$lib/components/asset-viewer/actions/rating-action.svelte';
import RemoveAssetFromStack from '$lib/components/asset-viewer/actions/remove-asset-from-stack.svelte';
import RestoreAction from '$lib/components/asset-viewer/actions/restore-action.svelte';
import SetAlbumCoverAction from '$lib/components/asset-viewer/actions/set-album-cover-action.svelte';
@ -179,6 +180,7 @@
{#if isOwner}
<FavoriteAction {asset} {onAction} />
<RatingAction {asset} {onAction} />
{/if}
{#if isOwner}

View File

@ -353,6 +353,16 @@
asset = { ...asset, people: assetInfo.people };
break;
}
case AssetAction.RATING: {
asset = {
...asset,
exifInfo: {
...asset.exifInfo,
rating: action.rating
}
};
break;
}
case AssetAction.KEEP_THIS_DELETE_OTHERS:
case AssetAction.UNSTACK: {
closeViewer();

View File

@ -16,6 +16,7 @@ export enum AssetAction {
SET_VISIBILITY_LOCKED = 'set-visibility-locked',
SET_VISIBILITY_TIMELINE = 'set-visibility-timeline',
SET_PERSON_FEATURED_PHOTO = 'set-person-featured-photo',
RATING = 'rating',
}
export enum AppRoute {

View File

@ -1,4 +1,5 @@
<script lang="ts">
import { preferences } from '$lib/stores/user.store';
import { Icon, Modal, ModalBody } from '@immich/ui';
import { mdiInformationOutline } from '@mdi/js';
import { t } from 'svelte-i18n';
@ -44,6 +45,9 @@
{ key: ['⇧', 'd'], action: $t('download') },
{ key: ['Space'], action: $t('play_or_pause_video') },
{ key: ['Del'], action: $t('trash_delete_asset'), info: $t('shift_to_permanent_delete') },
...($preferences?.ratings.enabled
? [{ key: ['1-5'], action: $t('rate_asset'), info: $t('zero_to_clear_rating') }]
: []),
],
},
}: Props = $props();