feat(web): enable the shortcut "f" to favorite/unfavorite in multiselection mode (#31411)
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev> (cherry picked from commit aeee9be333f4faac877b99cacd355329a5001c25)
This commit is contained in:
@@ -1,10 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import MenuOption from '#lib/components/shared-components/context-menu/MenuOption.svelte';
|
import MenuOption from '#lib/components/shared-components/context-menu/MenuOption.svelte';
|
||||||
import { assetMultiSelectManager } from '#lib/managers/asset-multi-select-manager.svelte.ts';
|
import { assetMultiSelectManager } from '#lib/managers/asset-multi-select-manager.svelte.ts';
|
||||||
|
import { toggleFavoriteAssets } from '#lib/services/asset.service.ts';
|
||||||
import type { OnFavorite } from '#lib/utils/actions.ts';
|
import type { OnFavorite } from '#lib/utils/actions.ts';
|
||||||
import { handleError } from '#lib/utils/handle-error.ts';
|
import { IconButton } from '@immich/ui';
|
||||||
import { updateAssets } from '@immich/sdk';
|
|
||||||
import { IconButton, toastManager } from '@immich/ui';
|
|
||||||
import { mdiHeartMinusOutline, mdiHeartOutline, mdiTimerSand } from '@mdi/js';
|
import { mdiHeartMinusOutline, mdiHeartOutline, mdiTimerSand } from '@mdi/js';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
@@ -27,28 +26,8 @@
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const assets = assetMultiSelectManager.ownedAssets.filter((asset) => asset.isFavorite !== isFavorite);
|
const assets = assetMultiSelectManager.ownedAssets.filter((asset) => asset.isFavorite !== isFavorite);
|
||||||
|
await toggleFavoriteAssets(assets, isFavorite, onFavorite);
|
||||||
const ids = assets.map(({ id }) => id);
|
|
||||||
|
|
||||||
if (ids.length > 0) {
|
|
||||||
await updateAssets({ assetBulkUpdateDto: { ids, isFavorite } });
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const asset of assets) {
|
|
||||||
asset.isFavorite = isFavorite;
|
|
||||||
}
|
|
||||||
|
|
||||||
onFavorite?.(ids, isFavorite);
|
|
||||||
|
|
||||||
toastManager.primary(
|
|
||||||
isFavorite
|
|
||||||
? $t('added_to_favorites_count', { values: { count: ids.length } })
|
|
||||||
: $t('removed_from_favorites_count', { values: { count: ids.length } }),
|
|
||||||
);
|
|
||||||
|
|
||||||
assetMultiSelectManager.clear();
|
assetMultiSelectManager.clear();
|
||||||
} catch (error) {
|
|
||||||
handleError(error, $t('errors.unable_to_add_remove_favorites', { values: { favorite: isFavorite } }));
|
|
||||||
} finally {
|
} finally {
|
||||||
loading = false;
|
loading = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { goto } from '$app/navigation';
|
|
||||||
import { shortcuts, type ShortcutOptions } from '#lib/actions/shortcut.ts';
|
import { shortcuts, type ShortcutOptions } from '#lib/actions/shortcut.ts';
|
||||||
import {
|
import {
|
||||||
setFocusToAsset as setFocusAssetInit,
|
setFocusToAsset as setFocusAssetInit,
|
||||||
@@ -14,6 +13,7 @@
|
|||||||
import NavigateToDateModal from '#lib/modals/NavigateToDateModal.svelte';
|
import NavigateToDateModal from '#lib/modals/NavigateToDateModal.svelte';
|
||||||
import ShortcutsModal from '#lib/modals/ShortcutsModal.svelte';
|
import ShortcutsModal from '#lib/modals/ShortcutsModal.svelte';
|
||||||
import { Route } from '#lib/route.ts';
|
import { Route } from '#lib/route.ts';
|
||||||
|
import { toggleFavoriteAssets } from '#lib/services/asset.service.ts';
|
||||||
import { handleStack } from '#lib/services/stack.service.ts';
|
import { handleStack } from '#lib/services/stack.service.ts';
|
||||||
import { keyboardManager } from '#lib/stores/keyboard-manager.svelte.ts';
|
import { keyboardManager } from '#lib/stores/keyboard-manager.svelte.ts';
|
||||||
import { showDeleteModal } from '#lib/stores/preferences.store.ts';
|
import { showDeleteModal } from '#lib/stores/preferences.store.ts';
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
import { handlePromiseError } from '#lib/utils.ts';
|
import { handlePromiseError } from '#lib/utils.ts';
|
||||||
import { deleteAssets } from '#lib/utils/actions.ts';
|
import { deleteAssets } from '#lib/utils/actions.ts';
|
||||||
import { selectAllAssets } from '#lib/utils/asset-utils.ts';
|
import { selectAllAssets } from '#lib/utils/asset-utils.ts';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
import { isModalOpen, modalManager } from '@immich/ui';
|
import { isModalOpen, modalManager } from '@immich/ui';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -62,6 +63,14 @@
|
|||||||
onEscape?.();
|
onEscape?.();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toggleFavoriteSelected = async () => {
|
||||||
|
const allAssets = assetInteraction.ownedAssets;
|
||||||
|
const isFavorite = allAssets.some((a) => !a.isFavorite);
|
||||||
|
const assetsToUpdate = allAssets.filter((asset) => asset.isFavorite !== isFavorite);
|
||||||
|
await toggleFavoriteAssets(assetsToUpdate, isFavorite);
|
||||||
|
assetInteraction.clear();
|
||||||
|
};
|
||||||
|
|
||||||
const onSelectStart = (e: Event) => {
|
const onSelectStart = (e: Event) => {
|
||||||
if (!searchStore.isSearchEnabled && assetInteraction.selectionActive && keyboardManager.shift) {
|
if (!searchStore.isSearchEnabled && assetInteraction.selectionActive && keyboardManager.shift) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -125,6 +134,7 @@
|
|||||||
{ shortcut: { key: 'Delete', shift: true }, onShortcut: () => trashOrDelete(true) },
|
{ shortcut: { key: 'Delete', shift: true }, onShortcut: () => trashOrDelete(true) },
|
||||||
{ shortcut: { key: 'D', ctrl: true }, onShortcut: () => assetInteraction.clear() },
|
{ shortcut: { key: 'D', ctrl: true }, onShortcut: () => assetInteraction.clear() },
|
||||||
{ shortcut: { key: 's' }, onShortcut: () => onStackAssets() },
|
{ shortcut: { key: 's' }, onShortcut: () => onStackAssets() },
|
||||||
|
{ shortcut: { key: 'f' }, onShortcut: () => toggleFavoriteSelected() },
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// conflicting shortcuts
|
// conflicting shortcuts
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { Route } from '#lib/route.ts';
|
|||||||
import { SlideshowState, slideshowStore } from '#lib/stores/slideshow.store.ts';
|
import { SlideshowState, slideshowStore } from '#lib/stores/slideshow.store.ts';
|
||||||
import { getAssetMediaUrl, getSharedLink, sleep } from '#lib/utils.ts';
|
import { getAssetMediaUrl, getSharedLink, sleep } from '#lib/utils.ts';
|
||||||
import { downloadUrl } from '#lib/utils.ts';
|
import { downloadUrl } from '#lib/utils.ts';
|
||||||
|
import type { OnFavorite } from '#lib/utils/actions.ts';
|
||||||
import { handleError } from '#lib/utils/handle-error.ts';
|
import { handleError } from '#lib/utils/handle-error.ts';
|
||||||
import { getFormatter } from '#lib/utils/i18n.ts';
|
import { getFormatter } from '#lib/utils/i18n.ts';
|
||||||
import {
|
import {
|
||||||
@@ -63,6 +64,8 @@ import {
|
|||||||
mdiTune,
|
mdiTune,
|
||||||
} from '@mdi/js';
|
} from '@mdi/js';
|
||||||
import type { MessageFormatter } from 'svelte-i18n';
|
import type { MessageFormatter } from 'svelte-i18n';
|
||||||
|
import { t } from 'svelte-i18n';
|
||||||
|
import { get } from 'svelte/store';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
export const getAssetBulkActions = ($t: MessageFormatter, album?: AlbumResponseDto) => {
|
export const getAssetBulkActions = ($t: MessageFormatter, album?: AlbumResponseDto) => {
|
||||||
@@ -496,6 +499,35 @@ const handleUnfavorite = async (asset: AssetResponseDto) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const toggleFavoriteAssets = async (
|
||||||
|
assets: Pick<AssetResponseDto, 'id' | 'isFavorite'>[],
|
||||||
|
isFavorite: boolean,
|
||||||
|
onFavorite?: OnFavorite,
|
||||||
|
) => {
|
||||||
|
const ids = assets.map((a) => a.id);
|
||||||
|
const $t = get(t);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (ids.length > 0) {
|
||||||
|
await updateAssets({ assetBulkUpdateDto: { ids, isFavorite } });
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const asset of assets) {
|
||||||
|
asset.isFavorite = isFavorite;
|
||||||
|
}
|
||||||
|
|
||||||
|
onFavorite?.(ids, isFavorite);
|
||||||
|
|
||||||
|
toastManager.primary(
|
||||||
|
isFavorite
|
||||||
|
? $t('added_to_favorites_count', { values: { count: ids.length } })
|
||||||
|
: $t('removed_from_favorites_count', { values: { count: ids.length } }),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, $t('errors.unable_to_add_remove_favorites', { values: { favorite: isFavorite } }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleRate = async (asset: AssetResponseDto, rating: number) => {
|
const handleRate = async (asset: AssetResponseDto, rating: number) => {
|
||||||
const $t = await getFormatter();
|
const $t = await getFormatter();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user