feat: use shortcut to favorite and archive on asset-grid
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
export const initInput = (element: HTMLInputElement) => {
|
||||
element.focus();
|
||||
export const initInput = (element: HTMLInputElement, timeOut: number = 0) => {
|
||||
setTimeout(() => element.focus(), timeOut);
|
||||
};
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
<script lang="ts">
|
||||
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||
import {
|
||||
NotificationType,
|
||||
notificationController,
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import type { OnArchive } from '$lib/utils/actions';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { updateAssets } from '@immich/sdk';
|
||||
import { archiveAssets, type OnArchive } from '$lib/utils/actions';
|
||||
import { mdiArchiveArrowDownOutline, mdiArchiveArrowUpOutline, mdiTimerSand } from '@mdi/js';
|
||||
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
|
||||
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
import { getAssetControlContext } from '$lib/components/photos-page/asset-select-control-bar.svelte';
|
||||
|
||||
export let onArchive: OnArchive;
|
||||
|
||||
@@ -26,32 +20,11 @@
|
||||
const handleArchive = async () => {
|
||||
const isArchived = !unarchive;
|
||||
loading = true;
|
||||
|
||||
try {
|
||||
const assets = [...getOwnedAssets()].filter((asset) => asset.isArchived !== isArchived);
|
||||
const ids = assets.map(({ id }) => id);
|
||||
|
||||
if (ids.length > 0) {
|
||||
await updateAssets({ assetBulkUpdateDto: { ids, isArchived } });
|
||||
}
|
||||
|
||||
for (const asset of assets) {
|
||||
asset.isArchived = isArchived;
|
||||
}
|
||||
|
||||
onArchive(ids, isArchived);
|
||||
|
||||
notificationController.show({
|
||||
message: `${isArchived ? 'Archived' : 'Unarchived'} ${ids.length}`,
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
|
||||
clearSelect();
|
||||
} catch (error) {
|
||||
handleError(error, `Unable to ${isArchived ? 'archive' : 'unarchive'}`);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
const assets = [...getOwnedAssets()].filter((asset) => asset.isArchived !== isArchived);
|
||||
const ids = assets.map(({ id }) => id);
|
||||
await archiveAssets(isArchived, onArchive, ids);
|
||||
clearSelect();
|
||||
loading = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
<script lang="ts">
|
||||
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
import {
|
||||
NotificationType,
|
||||
notificationController,
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import type { OnFavorite } from '$lib/utils/actions';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { updateAssets } from '@immich/sdk';
|
||||
import { favoriteAssets, type OnFavorite } from '$lib/utils/actions';
|
||||
import { mdiHeartMinusOutline, mdiHeartOutline, mdiTimerSand } from '@mdi/js';
|
||||
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
||||
import { getAssetControlContext } from '$lib/components/photos-page/asset-select-control-bar.svelte';
|
||||
|
||||
export let onFavorite: OnFavorite;
|
||||
|
||||
@@ -26,33 +20,10 @@
|
||||
const handleFavorite = async () => {
|
||||
const isFavorite = !removeFavorite;
|
||||
loading = true;
|
||||
|
||||
try {
|
||||
const assets = [...getOwnedAssets()].filter((asset) => asset.isFavorite !== isFavorite);
|
||||
|
||||
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);
|
||||
|
||||
notificationController.show({
|
||||
message: isFavorite ? `Added ${ids.length} to favorites` : `Removed ${ids.length} from favorites`,
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
|
||||
clearSelect();
|
||||
} catch (error) {
|
||||
handleError(error, `Unable to ${isFavorite ? 'add to' : 'remove from'} favorites`);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
const assets = [...getOwnedAssets()].filter((asset) => asset.isFavorite !== isFavorite);
|
||||
await favoriteAssets(isFavorite, () => onFavorite(assets, isFavorite), assets);
|
||||
clearSelect();
|
||||
loading = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -7,22 +7,23 @@
|
||||
import { locale, showDeleteModal } from '$lib/stores/preferences.store';
|
||||
import { isSearchEnabled } from '$lib/stores/search.store';
|
||||
import { featureFlags } from '$lib/stores/server-config.store';
|
||||
import { deleteAssets } from '$lib/utils/actions';
|
||||
import { archiveAssets, deleteAssets, favoriteAssets } from '$lib/utils/actions';
|
||||
import { type ShortcutOptions, shortcuts } from '$lib/actions/shortcut';
|
||||
import { formatGroupTitle, splitBucketIntoDateGroups } from '$lib/utils/timeline-util';
|
||||
import type { AlbumResponseDto, AssetResponseDto } from '@immich/sdk';
|
||||
import { DateTime } from 'luxon';
|
||||
import { createEventDispatcher, onDestroy, onMount } from 'svelte';
|
||||
import IntersectionObserver from '../asset-viewer/intersection-observer.svelte';
|
||||
import Portal from '../shared-components/portal/portal.svelte';
|
||||
import Scrollbar from '../shared-components/scrollbar/scrollbar.svelte';
|
||||
import ShowShortcuts from '../shared-components/show-shortcuts.svelte';
|
||||
import AssetDateGroup from './asset-date-group.svelte';
|
||||
import { stackAssets } from '$lib/utils/asset-utils';
|
||||
import DeleteAssetDialog from './delete-asset-dialog.svelte';
|
||||
import IntersectionObserver from '$lib/components/asset-viewer/intersection-observer.svelte';
|
||||
import Portal from '$lib/components/shared-components/portal/portal.svelte';
|
||||
import Scrollbar from '$lib/components/shared-components/scrollbar/scrollbar.svelte';
|
||||
import ShowShortcuts from '$lib/components/shared-components/show-shortcuts.svelte';
|
||||
import AssetDateGroup from '$lib/components/photos-page/asset-date-group.svelte';
|
||||
import { handleFavoriteAssetGrid, stackAssets } from '$lib/utils/asset-utils';
|
||||
import DeleteAssetDialog from '$lib/components/photos-page/delete-asset-dialog.svelte';
|
||||
import { handlePromiseError } from '$lib/utils';
|
||||
import { selectAllAssets } from '$lib/utils/asset-utils';
|
||||
import { navigate } from '$lib/utils/navigation';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
export let isSelectionMode = false;
|
||||
export let singleSelect = false;
|
||||
@@ -34,6 +35,7 @@
|
||||
export let isShared = false;
|
||||
export let album: AlbumResponseDto | null = null;
|
||||
export let isShowDeleteConfirmation = false;
|
||||
export let isAllFavorite: boolean | null = null;
|
||||
|
||||
$: isTrashEnabled = $featureFlags.loaded && $featureFlags.trash;
|
||||
|
||||
@@ -93,6 +95,22 @@
|
||||
handlePromiseError(trashOrDelete(true));
|
||||
};
|
||||
|
||||
const onArchive = async () => {
|
||||
const assets = Array.from($selectedAssets);
|
||||
const ids = assets.map((asset) => asset.id);
|
||||
await archiveAssets($page.url.pathname !== AppRoute.ARCHIVE, (assetIds) => assetStore.removeAssets(assetIds), ids);
|
||||
assetInteractionStore.clearMultiselect();
|
||||
};
|
||||
|
||||
const onFavorite = async () => {
|
||||
await favoriteAssets(
|
||||
!isAllFavorite,
|
||||
(assets, isFavorite) => handleFavoriteAssetGrid(assets, isFavorite, assetStore),
|
||||
Array.from($selectedAssets),
|
||||
);
|
||||
assetInteractionStore.clearMultiselect();
|
||||
};
|
||||
|
||||
const onStackAssets = async () => {
|
||||
const ids = await stackAssets(Array.from($selectedAssets));
|
||||
if (ids) {
|
||||
@@ -128,6 +146,14 @@
|
||||
{ shortcut: { key: 'D', ctrl: true }, onShortcut: () => deselectAllAssets() },
|
||||
{ shortcut: { key: 's' }, onShortcut: () => onStackAssets() },
|
||||
);
|
||||
// you're not supposed to manage your assets on the trash page: there's no button to manage it, then there's no reason to have shortcuts to do it.
|
||||
if ($page.url.pathname !== AppRoute.TRASH) {
|
||||
shortcuts.push({ shortcut: { key: 'a', shift: true }, onShortcut: onArchive });
|
||||
// on some page, you can't favorite assets (like on `/parthner`)
|
||||
if (isAllFavorite !== null) {
|
||||
shortcuts.push({ shortcut: { key: 'f' }, onShortcut: onFavorite });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return shortcuts;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { notificationController, NotificationType } from '$lib/components/shared-components/notification/notification';
|
||||
import { deleteAssets as deleteBulk, type AssetResponseDto } from '@immich/sdk';
|
||||
import { deleteAssets as deleteBulk, updateAssets, type AssetResponseDto } from '@immich/sdk';
|
||||
import { handleError } from './handle-error';
|
||||
|
||||
export type OnDelete = (assetIds: string[]) => void;
|
||||
export type OnRestore = (ids: string[]) => void;
|
||||
export type OnArchive = (ids: string[], isArchived: boolean) => void;
|
||||
export type OnFavorite = (ids: string[], favorite: boolean) => void;
|
||||
export type OnFavorite = (assets: AssetResponseDto[], isFavorite: boolean) => void;
|
||||
export type OnStack = (ids: string[]) => void;
|
||||
export type OnUnstack = (assets: AssetResponseDto[]) => void;
|
||||
|
||||
@@ -22,3 +22,34 @@ export const deleteAssets = async (force: boolean, onAssetDelete: OnDelete, ids:
|
||||
handleError(error, 'Error deleting assets');
|
||||
}
|
||||
};
|
||||
|
||||
export const favoriteAssets = async (isFavorite: boolean, onAssetFavorite: OnFavorite, assets: AssetResponseDto[]) => {
|
||||
try {
|
||||
const ids = assets.map((asset) => asset.id);
|
||||
await updateAssets({ assetBulkUpdateDto: { ids, isFavorite } });
|
||||
|
||||
onAssetFavorite(assets, isFavorite);
|
||||
|
||||
notificationController.show({
|
||||
message: isFavorite ? `Added ${ids.length} to favorites` : `Removed ${ids.length} from favorites`,
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
} catch (error) {
|
||||
handleError(error, `Unable to ${isFavorite ? 'add to' : 'remove from'} favorites`);
|
||||
}
|
||||
};
|
||||
|
||||
export const archiveAssets = async (isArchived: boolean, onAssetArchive: OnArchive, ids: string[]) => {
|
||||
try {
|
||||
await updateAssets({ assetBulkUpdateDto: { ids, isArchived } });
|
||||
|
||||
onAssetArchive(ids, isArchived);
|
||||
|
||||
notificationController.show({
|
||||
message: `${isArchived ? 'Archived' : 'Unarchived'} ${ids.length}`,
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
} catch (error) {
|
||||
handleError(error, `Unable to ${isArchived ? 'archive' : 'unarchive'}`);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -397,3 +397,10 @@ export const selectAllAssets = async (assetStore: AssetStore, assetInteractionSt
|
||||
export const delay = async (ms: number) => {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
};
|
||||
|
||||
export const handleFavoriteAssetGrid = (assets: AssetResponseDto[], isFavorite: boolean, assetStore: AssetStore) => {
|
||||
for (const asset of assets) {
|
||||
asset.isFavorite = isFavorite;
|
||||
}
|
||||
assetStore.triggerUpdate();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user