From f22affd83694f42d40fa3f2ee01ac24b4cbab444 Mon Sep 17 00:00:00 2001 From: Calvin Bochulak Date: Tue, 6 Jan 2026 05:56:29 -0700 Subject: [PATCH] feat(web): star rating keyboard shortcut (#24620) Co-authored-by: idubnori Co-authored-by: Daniel Dietzler --- i18n/en.json | 4 ++ .../components/asset-viewer/actions/action.ts | 1 + .../asset-viewer/actions/rating-action.svelte | 55 +++++++++++++++++++ .../asset-viewer/asset-viewer-nav-bar.svelte | 2 + .../asset-viewer/asset-viewer.svelte | 10 ++++ web/src/lib/constants.ts | 1 + web/src/lib/modals/ShortcutsModal.svelte | 4 ++ 7 files changed, 77 insertions(+) create mode 100644 web/src/lib/components/asset-viewer/actions/rating-action.svelte diff --git a/i18n/en.json b/i18n/en.json index 5af40ea51a..529ab6dfdb 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1082,6 +1082,7 @@ "unable_to_scan_library": "Unable to scan library", "unable_to_set_feature_photo": "Unable to set feature photo", "unable_to_set_profile_picture": "Unable to set profile picture", + "unable_to_set_rating": "Unable to set rating", "unable_to_submit_job": "Unable to submit job", "unable_to_trash_asset": "Unable to trash asset", "unable_to_unlink_account": "Unable to unlink account", @@ -1702,10 +1703,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, plural, one {# star} other {# stars}}", "reaction_options": "Reaction options", "read_changelog": "Read Changelog", "readonly_mode_disabled": "Read-only mode disabled", @@ -2296,6 +2299,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" } diff --git a/web/src/lib/components/asset-viewer/actions/action.ts b/web/src/lib/components/asset-viewer/actions/action.ts index 6a807d2766..df61b5d073 100644 --- a/web/src/lib/components/asset-viewer/actions/action.ts +++ b/web/src/lib/components/asset-viewer/actions/action.ts @@ -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 = { diff --git a/web/src/lib/components/asset-viewer/actions/rating-action.svelte b/web/src/lib/components/asset-viewer/actions/rating-action.svelte new file mode 100644 index 0000000000..3791fccf23 --- /dev/null +++ b/web/src/lib/components/asset-viewer/actions/rating-action.svelte @@ -0,0 +1,55 @@ + + + rateAsset(null) }, + ...[1, 2, 3, 4, 5].map((rating) => ({ + shortcut: { key: String(rating) }, + onShortcut: () => rateAsset(rating), + })), + ] + : []} +/> diff --git a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte index 689cd5c20c..08957a5340 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte @@ -12,6 +12,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} + {/if} {#if isOwner} diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte index 8b35c00fa1..7b07d57fd1 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte @@ -331,6 +331,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(); diff --git a/web/src/lib/constants.ts b/web/src/lib/constants.ts index 6f7c0d8e1e..72056008cd 100644 --- a/web/src/lib/constants.ts +++ b/web/src/lib/constants.ts @@ -18,6 +18,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 { diff --git a/web/src/lib/modals/ShortcutsModal.svelte b/web/src/lib/modals/ShortcutsModal.svelte index ebb5ea3c60..c5b09ffa1a 100644 --- a/web/src/lib/modals/ShortcutsModal.svelte +++ b/web/src/lib/modals/ShortcutsModal.svelte @@ -1,4 +1,5 @@