mirror of
https://github.com/immich-app/immich.git
synced 2025-01-01 08:31:59 +00:00
feat: use shortcut to favorite and archive on asset-grid
This commit is contained in:
parent
9e71256191
commit
683f503647
12 changed files with 121 additions and 93 deletions
|
@ -1,3 +1,3 @@
|
||||||
export const initInput = (element: HTMLInputElement) => {
|
export const initInput = (element: HTMLInputElement, timeOut: number = 0) => {
|
||||||
element.focus();
|
setTimeout(() => element.focus(), timeOut);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||||
import {
|
import { archiveAssets, type OnArchive } from '$lib/utils/actions';
|
||||||
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 { mdiArchiveArrowDownOutline, mdiArchiveArrowUpOutline, mdiTimerSand } from '@mdi/js';
|
import { mdiArchiveArrowDownOutline, mdiArchiveArrowUpOutline, mdiTimerSand } from '@mdi/js';
|
||||||
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
|
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||||
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
import { getAssetControlContext } from '$lib/components/photos-page/asset-select-control-bar.svelte';
|
||||||
|
|
||||||
export let onArchive: OnArchive;
|
export let onArchive: OnArchive;
|
||||||
|
|
||||||
|
@ -26,32 +20,11 @@
|
||||||
const handleArchive = async () => {
|
const handleArchive = async () => {
|
||||||
const isArchived = !unarchive;
|
const isArchived = !unarchive;
|
||||||
loading = true;
|
loading = true;
|
||||||
|
const assets = [...getOwnedAssets()].filter((asset) => asset.isArchived !== isArchived);
|
||||||
try {
|
const ids = assets.map(({ id }) => id);
|
||||||
const assets = [...getOwnedAssets()].filter((asset) => asset.isArchived !== isArchived);
|
await archiveAssets(isArchived, onArchive, ids);
|
||||||
const ids = assets.map(({ id }) => id);
|
clearSelect();
|
||||||
|
loading = false;
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||||
import {
|
import { favoriteAssets, type OnFavorite } from '$lib/utils/actions';
|
||||||
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 { mdiHeartMinusOutline, mdiHeartOutline, mdiTimerSand } from '@mdi/js';
|
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;
|
export let onFavorite: OnFavorite;
|
||||||
|
|
||||||
|
@ -26,33 +20,10 @@
|
||||||
const handleFavorite = async () => {
|
const handleFavorite = async () => {
|
||||||
const isFavorite = !removeFavorite;
|
const isFavorite = !removeFavorite;
|
||||||
loading = true;
|
loading = true;
|
||||||
|
const assets = [...getOwnedAssets()].filter((asset) => asset.isFavorite !== isFavorite);
|
||||||
try {
|
await favoriteAssets(isFavorite, () => onFavorite(assets, isFavorite), assets);
|
||||||
const assets = [...getOwnedAssets()].filter((asset) => asset.isFavorite !== isFavorite);
|
clearSelect();
|
||||||
|
loading = false;
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -7,22 +7,23 @@
|
||||||
import { locale, showDeleteModal } from '$lib/stores/preferences.store';
|
import { locale, showDeleteModal } from '$lib/stores/preferences.store';
|
||||||
import { isSearchEnabled } from '$lib/stores/search.store';
|
import { isSearchEnabled } from '$lib/stores/search.store';
|
||||||
import { featureFlags } from '$lib/stores/server-config.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 { type ShortcutOptions, shortcuts } from '$lib/actions/shortcut';
|
||||||
import { formatGroupTitle, splitBucketIntoDateGroups } from '$lib/utils/timeline-util';
|
import { formatGroupTitle, splitBucketIntoDateGroups } from '$lib/utils/timeline-util';
|
||||||
import type { AlbumResponseDto, AssetResponseDto } from '@immich/sdk';
|
import type { AlbumResponseDto, AssetResponseDto } from '@immich/sdk';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { createEventDispatcher, onDestroy, onMount } from 'svelte';
|
import { createEventDispatcher, onDestroy, onMount } from 'svelte';
|
||||||
import IntersectionObserver from '../asset-viewer/intersection-observer.svelte';
|
import IntersectionObserver from '$lib/components/asset-viewer/intersection-observer.svelte';
|
||||||
import Portal from '../shared-components/portal/portal.svelte';
|
import Portal from '$lib/components/shared-components/portal/portal.svelte';
|
||||||
import Scrollbar from '../shared-components/scrollbar/scrollbar.svelte';
|
import Scrollbar from '$lib/components/shared-components/scrollbar/scrollbar.svelte';
|
||||||
import ShowShortcuts from '../shared-components/show-shortcuts.svelte';
|
import ShowShortcuts from '$lib/components/shared-components/show-shortcuts.svelte';
|
||||||
import AssetDateGroup from './asset-date-group.svelte';
|
import AssetDateGroup from '$lib/components/photos-page/asset-date-group.svelte';
|
||||||
import { stackAssets } from '$lib/utils/asset-utils';
|
import { handleFavoriteAssetGrid, stackAssets } from '$lib/utils/asset-utils';
|
||||||
import DeleteAssetDialog from './delete-asset-dialog.svelte';
|
import DeleteAssetDialog from '$lib/components/photos-page/delete-asset-dialog.svelte';
|
||||||
import { handlePromiseError } from '$lib/utils';
|
import { handlePromiseError } from '$lib/utils';
|
||||||
import { selectAllAssets } from '$lib/utils/asset-utils';
|
import { selectAllAssets } from '$lib/utils/asset-utils';
|
||||||
import { navigate } from '$lib/utils/navigation';
|
import { navigate } from '$lib/utils/navigation';
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
|
||||||
export let isSelectionMode = false;
|
export let isSelectionMode = false;
|
||||||
export let singleSelect = false;
|
export let singleSelect = false;
|
||||||
|
@ -34,6 +35,7 @@
|
||||||
export let isShared = false;
|
export let isShared = false;
|
||||||
export let album: AlbumResponseDto | null = null;
|
export let album: AlbumResponseDto | null = null;
|
||||||
export let isShowDeleteConfirmation = false;
|
export let isShowDeleteConfirmation = false;
|
||||||
|
export let isAllFavorite: boolean | null = null;
|
||||||
|
|
||||||
$: isTrashEnabled = $featureFlags.loaded && $featureFlags.trash;
|
$: isTrashEnabled = $featureFlags.loaded && $featureFlags.trash;
|
||||||
|
|
||||||
|
@ -93,6 +95,22 @@
|
||||||
handlePromiseError(trashOrDelete(true));
|
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 onStackAssets = async () => {
|
||||||
const ids = await stackAssets(Array.from($selectedAssets));
|
const ids = await stackAssets(Array.from($selectedAssets));
|
||||||
if (ids) {
|
if (ids) {
|
||||||
|
@ -128,6 +146,14 @@
|
||||||
{ shortcut: { key: 'D', ctrl: true }, onShortcut: () => deselectAllAssets() },
|
{ shortcut: { key: 'D', ctrl: true }, onShortcut: () => deselectAllAssets() },
|
||||||
{ shortcut: { key: 's' }, onShortcut: () => onStackAssets() },
|
{ 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;
|
return shortcuts;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { notificationController, NotificationType } from '$lib/components/shared-components/notification/notification';
|
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';
|
import { handleError } from './handle-error';
|
||||||
|
|
||||||
export type OnDelete = (assetIds: string[]) => void;
|
export type OnDelete = (assetIds: string[]) => void;
|
||||||
export type OnRestore = (ids: string[]) => void;
|
export type OnRestore = (ids: string[]) => void;
|
||||||
export type OnArchive = (ids: string[], isArchived: boolean) => 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 OnStack = (ids: string[]) => void;
|
||||||
export type OnUnstack = (assets: AssetResponseDto[]) => 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');
|
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) => {
|
export const delay = async (ms: number) => {
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
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();
|
||||||
|
};
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
import { SlideshowNavigation, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
|
import { SlideshowNavigation, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
|
||||||
import { user } from '$lib/stores/user.store';
|
import { user } from '$lib/stores/user.store';
|
||||||
import { handlePromiseError, s } from '$lib/utils';
|
import { handlePromiseError, s } from '$lib/utils';
|
||||||
import { downloadAlbum } from '$lib/utils/asset-utils';
|
import { downloadAlbum, handleFavoriteAssetGrid } from '$lib/utils/asset-utils';
|
||||||
import { clickOutside } from '$lib/actions/click-outside';
|
import { clickOutside } from '$lib/actions/click-outside';
|
||||||
import { getContextMenuPosition } from '$lib/utils/context-menu';
|
import { getContextMenuPosition } from '$lib/utils/context-menu';
|
||||||
import { openFileUploadDialog } from '$lib/utils/file-uploader';
|
import { openFileUploadDialog } from '$lib/utils/file-uploader';
|
||||||
|
@ -412,7 +412,10 @@
|
||||||
<AddToAlbum shared />
|
<AddToAlbum shared />
|
||||||
</AssetSelectContextMenu>
|
</AssetSelectContextMenu>
|
||||||
{#if isAllUserOwned}
|
{#if isAllUserOwned}
|
||||||
<FavoriteAction removeFavorite={isAllFavorite} onFavorite={() => assetStore.triggerUpdate()} />
|
<FavoriteAction
|
||||||
|
removeFavorite={isAllFavorite}
|
||||||
|
onFavorite={(assets, isFavorite) => handleFavoriteAssetGrid(assets, isFavorite, assetStore)}
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
<AssetSelectContextMenu icon={mdiDotsVertical} title="Menu">
|
<AssetSelectContextMenu icon={mdiDotsVertical} title="Menu">
|
||||||
<DownloadAction menuItem filename="{album.albumName}.zip" />
|
<DownloadAction menuItem filename="{album.albumName}.zip" />
|
||||||
|
@ -546,6 +549,7 @@
|
||||||
{album}
|
{album}
|
||||||
{assetStore}
|
{assetStore}
|
||||||
{assetInteractionStore}
|
{assetInteractionStore}
|
||||||
|
{isAllFavorite}
|
||||||
isShared={album.albumUsers.length > 0}
|
isShared={album.albumUsers.length > 0}
|
||||||
isSelectionMode={viewMode === ViewMode.SELECT_THUMBNAIL}
|
isSelectionMode={viewMode === ViewMode.SELECT_THUMBNAIL}
|
||||||
singleSelect={viewMode === ViewMode.SELECT_THUMBNAIL}
|
singleSelect={viewMode === ViewMode.SELECT_THUMBNAIL}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
import { AssetAction } from '$lib/constants';
|
import { AssetAction } from '$lib/constants';
|
||||||
import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store';
|
import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store';
|
||||||
import { AssetStore } from '$lib/stores/assets.store';
|
import { AssetStore } from '$lib/stores/assets.store';
|
||||||
|
import { handleFavoriteAssetGrid } from '$lib/utils/asset-utils';
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
import { mdiPlus, mdiDotsVertical } from '@mdi/js';
|
import { mdiPlus, mdiDotsVertical } from '@mdi/js';
|
||||||
|
|
||||||
|
@ -35,7 +36,10 @@
|
||||||
<AddToAlbum />
|
<AddToAlbum />
|
||||||
<AddToAlbum shared />
|
<AddToAlbum shared />
|
||||||
</AssetSelectContextMenu>
|
</AssetSelectContextMenu>
|
||||||
<FavoriteAction removeFavorite={isAllFavorite} onFavorite={() => assetStore.triggerUpdate()} />
|
<FavoriteAction
|
||||||
|
removeFavorite={isAllFavorite}
|
||||||
|
onFavorite={(assets, isFavorite) => handleFavoriteAssetGrid(assets, isFavorite, assetStore)}
|
||||||
|
/>
|
||||||
<AssetSelectContextMenu icon={mdiDotsVertical} title="Add">
|
<AssetSelectContextMenu icon={mdiDotsVertical} title="Add">
|
||||||
<DownloadAction menuItem />
|
<DownloadAction menuItem />
|
||||||
<DeleteAssets menuItem onAssetDelete={(assetIds) => assetStore.removeAssets(assetIds)} />
|
<DeleteAssets menuItem onAssetDelete={(assetIds) => assetStore.removeAssets(assetIds)} />
|
||||||
|
@ -44,7 +48,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<UserPageLayout hideNavbar={$isMultiSelectState} title={data.meta.title} scrollbar={false}>
|
<UserPageLayout hideNavbar={$isMultiSelectState} title={data.meta.title} scrollbar={false}>
|
||||||
<AssetGrid {assetStore} {assetInteractionStore} removeAction={AssetAction.UNARCHIVE}>
|
<AssetGrid {isAllFavorite} {assetStore} {assetInteractionStore} removeAction={AssetAction.UNARCHIVE}>
|
||||||
<EmptyPlaceholder text="Archive photos and videos to hide them from your Photos view" slot="empty" />
|
<EmptyPlaceholder text="Archive photos and videos to hide them from your Photos view" slot="empty" />
|
||||||
</AssetGrid>
|
</AssetGrid>
|
||||||
</UserPageLayout>
|
</UserPageLayout>
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
import { AssetAction } from '$lib/constants';
|
import { AssetAction } from '$lib/constants';
|
||||||
import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store';
|
import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store';
|
||||||
import { AssetStore } from '$lib/stores/assets.store';
|
import { AssetStore } from '$lib/stores/assets.store';
|
||||||
|
import { handleFavoriteAssetGrid } from '$lib/utils/asset-utils';
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
import { mdiDotsVertical, mdiPlus } from '@mdi/js';
|
import { mdiDotsVertical, mdiPlus } from '@mdi/js';
|
||||||
|
|
||||||
|
@ -31,7 +32,10 @@
|
||||||
<!-- Multiselection mode app bar -->
|
<!-- Multiselection mode app bar -->
|
||||||
{#if $isMultiSelectState}
|
{#if $isMultiSelectState}
|
||||||
<AssetSelectControlBar assets={$selectedAssets} clearSelect={() => assetInteractionStore.clearMultiselect()}>
|
<AssetSelectControlBar assets={$selectedAssets} clearSelect={() => assetInteractionStore.clearMultiselect()}>
|
||||||
<FavoriteAction removeFavorite onFavorite={(assetIds) => assetStore.removeAssets(assetIds)} />
|
<FavoriteAction
|
||||||
|
removeFavorite
|
||||||
|
onFavorite={(assets, isFavorite) => handleFavoriteAssetGrid(assets, isFavorite, assetStore)}
|
||||||
|
/>
|
||||||
<CreateSharedLink />
|
<CreateSharedLink />
|
||||||
<SelectAllAssets {assetStore} {assetInteractionStore} />
|
<SelectAllAssets {assetStore} {assetInteractionStore} />
|
||||||
<AssetSelectContextMenu icon={mdiPlus} title="Add to...">
|
<AssetSelectContextMenu icon={mdiPlus} title="Add to...">
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
import { clearQueryParam } from '$lib/utils/navigation';
|
import { clearQueryParam } from '$lib/utils/navigation';
|
||||||
import SearchPeople from '$lib/components/faces-page/people-search.svelte';
|
import SearchPeople from '$lib/components/faces-page/people-search.svelte';
|
||||||
import LinkButton from '$lib/components/elements/buttons/link-button.svelte';
|
import LinkButton from '$lib/components/elements/buttons/link-button.svelte';
|
||||||
|
import { initInput } from '$lib/actions/focus';
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
|
|
||||||
|
@ -62,7 +63,6 @@
|
||||||
let handleSearchPeople: (force?: boolean, name?: string) => Promise<void>;
|
let handleSearchPeople: (force?: boolean, name?: string) => Promise<void>;
|
||||||
let showPeople: PersonResponseDto[] = [];
|
let showPeople: PersonResponseDto[] = [];
|
||||||
let countVisiblePeople: number;
|
let countVisiblePeople: number;
|
||||||
let changeNameInputEl: HTMLInputElement | null;
|
|
||||||
let innerHeight: number;
|
let innerHeight: number;
|
||||||
|
|
||||||
for (const person of people) {
|
for (const person of people) {
|
||||||
|
@ -244,8 +244,6 @@
|
||||||
personName = detail.name;
|
personName = detail.name;
|
||||||
personMerge1 = detail;
|
personMerge1 = detail;
|
||||||
edittingPerson = detail;
|
edittingPerson = detail;
|
||||||
|
|
||||||
setTimeout(() => changeNameInputEl?.focus(), 100);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSetBirthDate = (detail: PersonResponseDto) => {
|
const handleSetBirthDate = (detail: PersonResponseDto) => {
|
||||||
|
@ -463,7 +461,7 @@
|
||||||
name="name"
|
name="name"
|
||||||
type="text"
|
type="text"
|
||||||
bind:value={personName}
|
bind:value={personName}
|
||||||
bind:this={changeNameInputEl}
|
use:initInput={100}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -56,6 +56,7 @@
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
import { listNavigation } from '$lib/actions/list-navigation';
|
import { listNavigation } from '$lib/actions/list-navigation';
|
||||||
|
import { handleFavoriteAssetGrid } from '$lib/utils/asset-utils';
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
|
|
||||||
|
@ -383,7 +384,10 @@
|
||||||
<AddToAlbum />
|
<AddToAlbum />
|
||||||
<AddToAlbum shared />
|
<AddToAlbum shared />
|
||||||
</AssetSelectContextMenu>
|
</AssetSelectContextMenu>
|
||||||
<FavoriteAction removeFavorite={isAllFavorite} onFavorite={() => assetStore.triggerUpdate()} />
|
<FavoriteAction
|
||||||
|
removeFavorite={isAllFavorite}
|
||||||
|
onFavorite={(assets, isFavorite) => handleFavoriteAssetGrid(assets, isFavorite, assetStore)}
|
||||||
|
/>
|
||||||
<AssetSelectContextMenu icon={mdiDotsVertical} title="Add">
|
<AssetSelectContextMenu icon={mdiDotsVertical} title="Add">
|
||||||
<DownloadAction menuItem filename="{data.person.name || 'immich'}.zip" />
|
<DownloadAction menuItem filename="{data.person.name || 'immich'}.zip" />
|
||||||
<MenuOption icon={mdiAccountMultipleCheckOutline} text="Fix incorrect match" on:click={handleReassignAssets} />
|
<MenuOption icon={mdiAccountMultipleCheckOutline} text="Fix incorrect match" on:click={handleReassignAssets} />
|
||||||
|
@ -434,6 +438,7 @@
|
||||||
<main class="relative h-screen overflow-hidden bg-immich-bg tall:ml-4 pt-[var(--navbar-height)] dark:bg-immich-dark-bg">
|
<main class="relative h-screen overflow-hidden bg-immich-bg tall:ml-4 pt-[var(--navbar-height)] dark:bg-immich-dark-bg">
|
||||||
{#key refreshAssetGrid}
|
{#key refreshAssetGrid}
|
||||||
<AssetGrid
|
<AssetGrid
|
||||||
|
{isAllFavorite}
|
||||||
{assetStore}
|
{assetStore}
|
||||||
{assetInteractionStore}
|
{assetInteractionStore}
|
||||||
isSelectionMode={viewMode === ViewMode.SELECT_PERSON}
|
isSelectionMode={viewMode === ViewMode.SELECT_PERSON}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
import { mdiDotsVertical, mdiPlus } from '@mdi/js';
|
import { mdiDotsVertical, mdiPlus } from '@mdi/js';
|
||||||
import { user } from '$lib/stores/user.store';
|
import { user } from '$lib/stores/user.store';
|
||||||
|
import { handleFavoriteAssetGrid } from '$lib/utils/asset-utils';
|
||||||
|
|
||||||
let { isViewing: showAssetViewer } = assetViewingStore;
|
let { isViewing: showAssetViewer } = assetViewingStore;
|
||||||
let handleEscapeKey = false;
|
let handleEscapeKey = false;
|
||||||
|
@ -66,7 +67,10 @@
|
||||||
<AddToAlbum />
|
<AddToAlbum />
|
||||||
<AddToAlbum shared />
|
<AddToAlbum shared />
|
||||||
</AssetSelectContextMenu>
|
</AssetSelectContextMenu>
|
||||||
<FavoriteAction removeFavorite={isAllFavorite} onFavorite={() => assetStore.triggerUpdate()} />
|
<FavoriteAction
|
||||||
|
removeFavorite={isAllFavorite}
|
||||||
|
onFavorite={(assets, isFavorite) => handleFavoriteAssetGrid(assets, isFavorite, assetStore)}
|
||||||
|
/>
|
||||||
<AssetSelectContextMenu icon={mdiDotsVertical} title="Menu">
|
<AssetSelectContextMenu icon={mdiDotsVertical} title="Menu">
|
||||||
<DownloadAction menuItem />
|
<DownloadAction menuItem />
|
||||||
{#if $selectedAssets.size > 1 || isAssetStackSelected}
|
{#if $selectedAssets.size > 1 || isAssetStackSelected}
|
||||||
|
@ -92,6 +96,7 @@
|
||||||
|
|
||||||
<UserPageLayout hideNavbar={$isMultiSelectState} showUploadButton scrollbar={false}>
|
<UserPageLayout hideNavbar={$isMultiSelectState} showUploadButton scrollbar={false}>
|
||||||
<AssetGrid
|
<AssetGrid
|
||||||
|
{isAllFavorite}
|
||||||
{assetStore}
|
{assetStore}
|
||||||
{assetInteractionStore}
|
{assetInteractionStore}
|
||||||
removeAction={AssetAction.ARCHIVE}
|
removeAction={AssetAction.ARCHIVE}
|
||||||
|
|
Loading…
Reference in a new issue