From c25b6e30c27c6d190a0bf75cee7f3a4a050108f5 Mon Sep 17 00:00:00 2001
From: Mees Frensel <33722705+meesfrensel@users.noreply.github.com>
Date: Mon, 28 Sep 2026 20:34:30 +0200
Subject: [PATCH] refactor(web): rating shortcuts action (#31804)
---
i18n/en.json | 4 +-
.../asset-viewer/AssetViewerNavBar.svelte | 6 ---
.../asset-viewer/actions/RatingAction.svelte | 54 -------------------
web/src/lib/modals/ShortcutsModal.svelte | 2 +-
web/src/lib/services/asset.service.ts | 30 +++++++++++
5 files changed, 33 insertions(+), 63 deletions(-)
delete mode 100644 web/src/lib/components/asset-viewer/actions/RatingAction.svelte
diff --git a/i18n/en.json b/i18n/en.json
index f4ed1fa12..77ea86f7c 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -1751,7 +1751,8 @@
"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",
+ "rate_asset": "Rate asset",
+ "rate_asset_description": "Use the keys from 1 to 5 to quickly rate an asset, and 0 to clear the rating",
"rating": "Star rating",
"rating_clear": "Clear rating",
"rating_count": "{count, plural, =0 {Unrated} one {# star} other {# stars}}",
@@ -2368,6 +2369,5 @@
"you": "You",
"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"
}
diff --git a/web/src/lib/components/asset-viewer/AssetViewerNavBar.svelte b/web/src/lib/components/asset-viewer/AssetViewerNavBar.svelte
index 1a6970f8d..4f6bfcb3b 100644
--- a/web/src/lib/components/asset-viewer/AssetViewerNavBar.svelte
+++ b/web/src/lib/components/asset-viewer/AssetViewerNavBar.svelte
@@ -3,7 +3,6 @@
import type { OnAction, PreAction } from '$lib/components/asset-viewer/actions/action';
import ArchiveAction from '$lib/components/asset-viewer/actions/ArchiveAction.svelte';
import DeleteAction from '$lib/components/asset-viewer/actions/DeleteAction.svelte';
- import RatingAction from '$lib/components/asset-viewer/actions/RatingAction.svelte';
import RestoreAction from '$lib/components/asset-viewer/actions/RestoreAction.svelte';
import SetVisibilityAction from '$lib/components/asset-viewer/actions/SetVisibilityAction.svelte';
import LoadingDots from '$lib/components/LoadingDots.svelte';
@@ -116,11 +115,6 @@
-
- {#if isOwner}
-
- {/if}
-
{#if isOwner}
diff --git a/web/src/lib/components/asset-viewer/actions/RatingAction.svelte b/web/src/lib/components/asset-viewer/actions/RatingAction.svelte
deleted file mode 100644
index 056a932a1..000000000
--- a/web/src/lib/components/asset-viewer/actions/RatingAction.svelte
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
- rateAsset(null) },
- ...[1, 2, 3, 4, 5].map((rating) => ({
- shortcut: { key: String(rating) },
- onShortcut: () => rateAsset(rating),
- })),
- ]
- : []}
-/>
diff --git a/web/src/lib/modals/ShortcutsModal.svelte b/web/src/lib/modals/ShortcutsModal.svelte
index 899f70ed9..bb818fa73 100644
--- a/web/src/lib/modals/ShortcutsModal.svelte
+++ b/web/src/lib/modals/ShortcutsModal.svelte
@@ -46,7 +46,7 @@
{ key: ['Space'], action: $t('play_or_pause_video') },
{ key: ['Del'], action: $t('trash_delete_asset'), info: $t('shift_to_permanent_delete') },
...(authManager.authenticated && authManager.preferences.ratings.enabled
- ? [{ key: ['1-5'], action: $t('rate_asset'), info: $t('zero_to_clear_rating') }]
+ ? [{ key: ['0-5'], action: $t('rate_asset'), info: $t('rate_asset_description') }]
: []),
],
},
diff --git a/web/src/lib/services/asset.service.ts b/web/src/lib/services/asset.service.ts
index d6535c560..fc52b18e2 100644
--- a/web/src/lib/services/asset.service.ts
+++ b/web/src/lib/services/asset.service.ts
@@ -200,6 +200,14 @@ export const getAssetActions = (
shortcuts: [{ key: 'f' }],
};
+ const Rate: ActionItem = {
+ title: $t('rate_asset'),
+ description: $t('rate_asset_description'),
+ $if: () => isOwner && authManager.preferences.ratings.enabled,
+ onAction: ({ event }) => handleRate(asset, event instanceof KeyboardEvent ? Number(event.key) : NaN),
+ shortcuts: [0, 1, 2, 3, 4, 5].map((key) => ({ key: String(key) })),
+ };
+
const AddToAlbum: ActionItem = {
title: $t('add_to_album'),
icon: mdiPlus,
@@ -340,6 +348,7 @@ export const getAssetActions = (
Info,
Favorite,
Unfavorite,
+ Rate,
PlayMotionPhoto,
StopMotionPhoto,
PlaySlideshow,
@@ -435,6 +444,27 @@ const handleUnfavorite = async (asset: AssetResponseDto) => {
}
};
+const handleRate = async (asset: AssetResponseDto, rating: number) => {
+ const $t = await getFormatter();
+
+ if (Number.isNaN(rating)) {
+ toastManager.info($t('rate_asset_description'));
+ return;
+ }
+
+ const newRating = rating === 0 ? null : rating;
+ if (asset.exifInfo && asset.exifInfo.rating === newRating) {
+ return;
+ }
+
+ try {
+ const response = await updateAsset({ id: asset.id, updateAssetDto: { rating: newRating } });
+ eventManager.emit('AssetUpdate', response);
+ } catch (error) {
+ handleError(error, $t('errors.unable_to_set_rating'));
+ }
+};
+
const handleBulkRemoveAssetsFromAlbum = async (assetIds: string[], album: AlbumResponseDto) => {
const $t = await getFormatter();