mirror of
https://github.com/immich-app/immich.git
synced 2025-07-09 03:04:16 -04:00
feat: add shortcuts to asset-grid
This commit is contained in:
parent
b4d1470586
commit
e6e7f78f9f
@ -1,57 +1,32 @@
|
|||||||
<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 {
|
|
||||||
NotificationType,
|
|
||||||
notificationController,
|
|
||||||
} from '$lib/components/shared-components/notification/notification';
|
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
|
||||||
import { api } from '@api';
|
|
||||||
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
|
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
|
||||||
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
||||||
import { mdiArchiveArrowUpOutline, mdiArchiveArrowDownOutline, mdiTimerSand } from '@mdi/js';
|
import { mdiArchiveArrowUpOutline, mdiArchiveArrowDownOutline, mdiTimerSand } from '@mdi/js';
|
||||||
import type { OnArchive } from '$lib/utils/actions';
|
import { archiveAssets, type OnArchive } from '$lib/utils/actions';
|
||||||
|
import { isAllArchived } from '$lib/stores/asset-interaction.store';
|
||||||
|
|
||||||
export let onArchive: OnArchive | undefined = undefined;
|
export let onArchive: OnArchive | undefined = undefined;
|
||||||
|
|
||||||
export let menuItem = false;
|
export let menuItem = false;
|
||||||
export let unarchive = false;
|
|
||||||
|
|
||||||
$: text = unarchive ? 'Unarchive' : 'Archive';
|
$: text = $isAllArchived ? 'Unarchive' : 'Archive';
|
||||||
$: icon = unarchive ? mdiArchiveArrowUpOutline : mdiArchiveArrowDownOutline;
|
$: icon = $isAllArchived ? mdiArchiveArrowUpOutline : mdiArchiveArrowDownOutline;
|
||||||
|
|
||||||
let loading = false;
|
let loading = false;
|
||||||
|
|
||||||
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
||||||
|
|
||||||
const handleArchive = async () => {
|
const handleArchive = async () => {
|
||||||
const isArchived = !unarchive;
|
const isArchived = !$isAllArchived;
|
||||||
loading = true;
|
loading = true;
|
||||||
|
|
||||||
try {
|
|
||||||
const assets = Array.from(getOwnedAssets()).filter((asset) => asset.isArchived !== isArchived);
|
const assets = Array.from(getOwnedAssets()).filter((asset) => asset.isArchived !== isArchived);
|
||||||
const ids = assets.map(({ id }) => id);
|
if (await archiveAssets(isArchived, onArchive, assets)) {
|
||||||
|
|
||||||
if (ids.length > 0) {
|
|
||||||
await api.assetApi.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();
|
clearSelect();
|
||||||
} catch (error) {
|
|
||||||
handleError(error, `Unable to ${isArchived ? 'archive' : 'unarchive'}`);
|
|
||||||
} finally {
|
|
||||||
loading = false;
|
|
||||||
}
|
}
|
||||||
|
loading = false;
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,58 +1,33 @@
|
|||||||
<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 {
|
|
||||||
NotificationType,
|
|
||||||
notificationController,
|
|
||||||
} from '$lib/components/shared-components/notification/notification';
|
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
|
||||||
import { api } from '@api';
|
|
||||||
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
||||||
import { mdiHeartMinusOutline, mdiHeartOutline, mdiTimerSand } from '@mdi/js';
|
import { mdiHeartMinusOutline, mdiHeartOutline, mdiTimerSand } from '@mdi/js';
|
||||||
import type { OnFavorite } from '$lib/utils/actions';
|
import { favoriteAssets, type OnFavorite } from '$lib/utils/actions';
|
||||||
|
import { isAllFavorite } from '$lib/stores/asset-interaction.store';
|
||||||
|
|
||||||
export let onFavorite: OnFavorite | undefined = undefined;
|
export let onFavorite: OnFavorite | undefined = undefined;
|
||||||
|
|
||||||
export let menuItem = false;
|
export let menuItem = false;
|
||||||
export let removeFavorite: boolean;
|
|
||||||
|
|
||||||
$: text = removeFavorite ? 'Remove from Favorites' : 'Favorite';
|
$: text = $isAllFavorite ? 'Remove from Favorites' : 'Favorite';
|
||||||
$: icon = removeFavorite ? mdiHeartMinusOutline : mdiHeartOutline;
|
$: icon = $isAllFavorite ? mdiHeartMinusOutline : mdiHeartOutline;
|
||||||
|
|
||||||
let loading = false;
|
let loading = false;
|
||||||
|
|
||||||
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
||||||
|
|
||||||
const handleFavorite = async () => {
|
const handleFavorite = async () => {
|
||||||
const isFavorite = !removeFavorite;
|
const isFavorite = !$isAllFavorite;
|
||||||
loading = true;
|
loading = true;
|
||||||
|
|
||||||
try {
|
|
||||||
const assets = Array.from(getOwnedAssets()).filter((asset) => asset.isFavorite !== isFavorite);
|
const assets = Array.from(getOwnedAssets()).filter((asset) => asset.isFavorite !== isFavorite);
|
||||||
|
|
||||||
const ids = assets.map(({ id }) => id);
|
if (await favoriteAssets(isFavorite, onFavorite, assets)) {
|
||||||
|
|
||||||
if (ids.length > 0) {
|
|
||||||
await api.assetApi.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();
|
clearSelect();
|
||||||
} catch (error) {
|
|
||||||
handleError(error, `Unable to ${isFavorite ? 'add to' : 'remove from'} favorites`);
|
|
||||||
} finally {
|
|
||||||
loading = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loading = false;
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
<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 type { AssetInteractionStore } from '$lib/stores/asset-interaction.store';
|
import type { AssetInteractionStore } from '$lib/stores/asset-interaction.store';
|
||||||
import { BucketPosition, type AssetStore, isSelectAllCancelled } from '$lib/stores/assets.store';
|
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
|
||||||
import { get } from 'svelte/store';
|
import { get } from 'svelte/store';
|
||||||
import { mdiTimerSand, mdiSelectAll } from '@mdi/js';
|
import { mdiTimerSand, mdiSelectAll } from '@mdi/js';
|
||||||
|
import { selectAll } from '$lib/utils/actions';
|
||||||
|
import type { AssetStore } from '$lib/stores/assets.store';
|
||||||
|
|
||||||
export let assetStore: AssetStore;
|
export let assetStore: AssetStore;
|
||||||
export let assetInteractionStore: AssetInteractionStore;
|
export let assetInteractionStore: AssetInteractionStore;
|
||||||
@ -12,25 +13,11 @@
|
|||||||
let selecting = false;
|
let selecting = false;
|
||||||
|
|
||||||
const handleSelectAll = async () => {
|
const handleSelectAll = async () => {
|
||||||
try {
|
|
||||||
$isSelectAllCancelled = false;
|
|
||||||
selecting = true;
|
selecting = true;
|
||||||
|
|
||||||
const assetGridState = get(assetStore);
|
selectAll(get(assetStore), assetStore, assetInteractionStore);
|
||||||
for (const bucket of assetGridState.buckets) {
|
|
||||||
if ($isSelectAllCancelled) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
await assetStore.loadBucket(bucket.bucketDate, BucketPosition.Unknown);
|
|
||||||
for (const asset of bucket.assets) {
|
|
||||||
assetInteractionStore.selectAsset(asset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
selecting = false;
|
selecting = false;
|
||||||
} catch (e) {
|
|
||||||
handleError(e, 'Error selecting all assets');
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -2,7 +2,12 @@
|
|||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { AppRoute, AssetAction } from '$lib/constants';
|
import { AppRoute, AssetAction } from '$lib/constants';
|
||||||
import type { AssetInteractionStore } from '$lib/stores/asset-interaction.store';
|
import {
|
||||||
|
isAllUserOwned,
|
||||||
|
type AssetInteractionStore,
|
||||||
|
isAllFavorite,
|
||||||
|
isAllArchived,
|
||||||
|
} from '$lib/stores/asset-interaction.store';
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
import { BucketPosition, type AssetStore, type Viewport } from '$lib/stores/assets.store';
|
import { BucketPosition, type AssetStore, type Viewport } from '$lib/stores/assets.store';
|
||||||
import { locale, showDeleteModal } from '$lib/stores/preferences.store';
|
import { locale, showDeleteModal } from '$lib/stores/preferences.store';
|
||||||
@ -19,8 +24,11 @@
|
|||||||
import AssetDateGroup from './asset-date-group.svelte';
|
import AssetDateGroup from './asset-date-group.svelte';
|
||||||
import { featureFlags } from '$lib/stores/server-config.store';
|
import { featureFlags } from '$lib/stores/server-config.store';
|
||||||
import { shouldIgnoreShortcut } from '$lib/utils/shortcut';
|
import { shouldIgnoreShortcut } from '$lib/utils/shortcut';
|
||||||
import { deleteAssets } from '$lib/utils/actions';
|
import { archiveAssets, deleteAssets, favoriteAssets, selectAll } from '$lib/utils/actions';
|
||||||
import DeleteAssetDialog from './delete-asset-dialog.svelte';
|
import DeleteAssetDialog from './delete-asset-dialog.svelte';
|
||||||
|
import { downloadArchive, downloadFile } from '$lib/utils/asset-utils';
|
||||||
|
import { user } from '$lib/stores/user.store';
|
||||||
|
import { get } from 'svelte/store';
|
||||||
|
|
||||||
export let isSelectionMode = false;
|
export let isSelectionMode = false;
|
||||||
export let singleSelect = false;
|
export let singleSelect = false;
|
||||||
@ -47,6 +55,9 @@
|
|||||||
$: idsSelectedAssets = Array.from($selectedAssets)
|
$: idsSelectedAssets = Array.from($selectedAssets)
|
||||||
.filter((a) => !a.isExternal)
|
.filter((a) => !a.isExternal)
|
||||||
.map((a) => a.id);
|
.map((a) => a.id);
|
||||||
|
$: $isAllUserOwned = Array.from($selectedAssets).every((asset) => asset.ownerId === $user.id);
|
||||||
|
$: $isAllFavorite = Array.from($selectedAssets).every((asset) => asset.isFavorite);
|
||||||
|
$: $isAllArchived = Array.from($selectedAssets).every((asset) => asset.isArchived);
|
||||||
|
|
||||||
const onKeyboardPress = (event: KeyboardEvent) => handleKeyboardPress(event);
|
const onKeyboardPress = (event: KeyboardEvent) => handleKeyboardPress(event);
|
||||||
const dispatch = createEventDispatcher<{ select: AssetResponseDto; escape: void }>();
|
const dispatch = createEventDispatcher<{ select: AssetResponseDto; escape: void }>();
|
||||||
@ -70,24 +81,30 @@
|
|||||||
assetStore.disconnect();
|
assetStore.disconnect();
|
||||||
});
|
});
|
||||||
|
|
||||||
const trashOrDelete = (force: boolean = false) => {
|
const trashOrDelete = async (force: boolean = false) => {
|
||||||
isShowDeleteConfirmation = false;
|
isShowDeleteConfirmation = false;
|
||||||
deleteAssets(!(isTrashEnabled && !force), (assetId) => assetStore.removeAsset(assetId), idsSelectedAssets);
|
if (
|
||||||
|
await deleteAssets(!(isTrashEnabled && !force), (assetId) => assetStore.removeAsset(assetId), idsSelectedAssets)
|
||||||
|
) {
|
||||||
assetInteractionStore.clearMultiselect();
|
assetInteractionStore.clearMultiselect();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleKeyboardPress = (event: KeyboardEvent) => {
|
const handleKeyboardPress = async (event: KeyboardEvent) => {
|
||||||
if ($isSearchEnabled || shouldIgnoreShortcut(event)) {
|
if ($isSearchEnabled || shouldIgnoreShortcut(event)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const key = event.key;
|
const key = event.key;
|
||||||
const shiftKey = event.shiftKey;
|
const shiftKey = event.shiftKey;
|
||||||
|
const ctrlKey = event.ctrlKey;
|
||||||
|
const assets = Array.from($selectedAssets);
|
||||||
|
|
||||||
if (!$showAssetViewer) {
|
if (!$showAssetViewer) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'Escape':
|
case '/':
|
||||||
dispatch('escape');
|
event.preventDefault();
|
||||||
|
goto(AppRoute.EXPLORE);
|
||||||
return;
|
return;
|
||||||
case '?':
|
case '?':
|
||||||
if (event.shiftKey) {
|
if (event.shiftKey) {
|
||||||
@ -95,9 +112,43 @@
|
|||||||
showShortcuts = !showShortcuts;
|
showShortcuts = !showShortcuts;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case '/':
|
case 'a':
|
||||||
|
case 'A':
|
||||||
|
if ($isMultiSelectState) {
|
||||||
|
if (ctrlKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
goto(AppRoute.EXPLORE);
|
selectAll(get(assetStore), assetStore, assetInteractionStore);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($isAllUserOwned) {
|
||||||
|
if (
|
||||||
|
await archiveAssets(
|
||||||
|
!$isAllArchived,
|
||||||
|
(assetIds) => {
|
||||||
|
for (const assetId of assetIds) {
|
||||||
|
assetStore.removeAsset(assetId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
assets,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
assetInteractionStore.clearMultiselect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case 'd':
|
||||||
|
case 'D':
|
||||||
|
if ($isMultiSelectState) {
|
||||||
|
assetInteractionStore.clearMultiselect();
|
||||||
|
if (assets.length === 1) {
|
||||||
|
await downloadFile(assets[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await downloadArchive('immich.zip', { assetIds: assets.map((asset) => asset.id) });
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
case 'Delete':
|
case 'Delete':
|
||||||
if ($isMultiSelectState) {
|
if ($isMultiSelectState) {
|
||||||
@ -113,6 +164,16 @@
|
|||||||
trashOrDelete(force);
|
trashOrDelete(force);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
case 'Escape':
|
||||||
|
dispatch('escape');
|
||||||
|
return;
|
||||||
|
case 'f':
|
||||||
|
if ($isMultiSelectState && $isAllUserOwned) {
|
||||||
|
if (await favoriteAssets(!$isAllFavorite, undefined, assets)) {
|
||||||
|
assetInteractionStore.clearMultiselect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -147,3 +147,7 @@ export function createAssetInteractionStore(): AssetInteractionStore {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const isAllUserOwned = writable<boolean>();
|
||||||
|
export const isAllFavorite = writable<boolean>();
|
||||||
|
export const isAllArchived = writable<boolean>();
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import { notificationController, NotificationType } from '$lib/components/shared-components/notification/notification';
|
import { notificationController, NotificationType } from '$lib/components/shared-components/notification/notification';
|
||||||
import { api } from '@api';
|
import { api, AssetResponseDto } from '@api';
|
||||||
import { handleError } from './handle-error';
|
import { handleError } from './handle-error';
|
||||||
|
import { isSelectAllCancelled, type AssetStore, BucketPosition } from '$lib/stores/assets.store';
|
||||||
|
import { get } from 'svelte/store';
|
||||||
|
import type { AssetInteractionStore } from '$lib/stores/asset-interaction.store';
|
||||||
|
|
||||||
export type OnDelete = (assetId: string) => void;
|
export type OnDelete = (assetId: string) => void;
|
||||||
export type OnRestore = (ids: string[]) => void;
|
export type OnRestore = (ids: string[]) => void;
|
||||||
@ -8,7 +11,7 @@ export type OnArchive = (ids: string[], isArchived: boolean) => void;
|
|||||||
export type OnFavorite = (ids: string[], favorite: boolean) => void;
|
export type OnFavorite = (ids: string[], favorite: boolean) => void;
|
||||||
export type OnStack = (ids: string[]) => void;
|
export type OnStack = (ids: string[]) => void;
|
||||||
|
|
||||||
export const deleteAssets = async (force: boolean, onAssetDelete: OnDelete, ids: string[]) => {
|
export const deleteAssets = async (force: boolean, onAssetDelete: OnDelete, ids: string[]): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
await api.assetApi.deleteAssets({ assetBulkDeleteDto: { ids, force } });
|
await api.assetApi.deleteAssets({ assetBulkDeleteDto: { ids, force } });
|
||||||
for (const id of ids) {
|
for (const id of ids) {
|
||||||
@ -19,7 +22,92 @@ export const deleteAssets = async (force: boolean, onAssetDelete: OnDelete, ids:
|
|||||||
message: `${force ? 'Permanently deleted' : 'Trashed'} ${ids.length} assets`,
|
message: `${force ? 'Permanently deleted' : 'Trashed'} ${ids.length} assets`,
|
||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
});
|
});
|
||||||
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(e, 'Error deleting assets');
|
handleError(e, 'Error deleting assets');
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const favoriteAssets = async (
|
||||||
|
isFavorite: boolean,
|
||||||
|
onFavorite: OnFavorite | undefined,
|
||||||
|
assets: AssetResponseDto[],
|
||||||
|
): Promise<boolean> => {
|
||||||
|
try {
|
||||||
|
const ids = assets.map(({ id }) => id);
|
||||||
|
|
||||||
|
if (ids.length > 0) {
|
||||||
|
await api.assetApi.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,
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, `Unable to ${isFavorite ? 'add to' : 'remove from'} favorites`);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const archiveAssets = async (
|
||||||
|
isArchived: boolean,
|
||||||
|
onArchive: OnArchive | undefined,
|
||||||
|
assets: AssetResponseDto[],
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const ids = assets.map(({ id }) => id);
|
||||||
|
|
||||||
|
if (ids.length > 0) {
|
||||||
|
await api.assetApi.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,
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, `Unable to ${isArchived ? 'archive' : 'unarchive'}`);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const selectAll = async (
|
||||||
|
assetGridState: AssetStore,
|
||||||
|
assetStore: AssetStore,
|
||||||
|
assetInteractionStore: AssetInteractionStore,
|
||||||
|
): Promise<boolean> => {
|
||||||
|
isSelectAllCancelled.set(false);
|
||||||
|
try {
|
||||||
|
for (const bucket of assetGridState.buckets) {
|
||||||
|
if (get(isSelectAllCancelled)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await assetStore.loadBucket(bucket.bucketDate, BucketPosition.Unknown);
|
||||||
|
for (const asset of bucket.assets) {
|
||||||
|
assetInteractionStore.selectAsset(asset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
handleError(e, 'Error selecting all assets');
|
||||||
|
}
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
} from '$lib/components/shared-components/notification/notification';
|
} from '$lib/components/shared-components/notification/notification';
|
||||||
import UserAvatar from '$lib/components/shared-components/user-avatar.svelte';
|
import UserAvatar from '$lib/components/shared-components/user-avatar.svelte';
|
||||||
import { AppRoute, dateFormats } from '$lib/constants';
|
import { AppRoute, dateFormats } from '$lib/constants';
|
||||||
import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store';
|
import { createAssetInteractionStore, isAllUserOwned } from '$lib/stores/asset-interaction.store';
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
import { SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
|
import { SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
|
||||||
import { AssetStore } from '$lib/stores/assets.store';
|
import { AssetStore } from '$lib/stores/assets.store';
|
||||||
@ -110,8 +110,6 @@
|
|||||||
const { selectedAssets: timelineSelected } = timelineInteractionStore;
|
const { selectedAssets: timelineSelected } = timelineInteractionStore;
|
||||||
|
|
||||||
$: isOwned = $user.id == album.ownerId;
|
$: isOwned = $user.id == album.ownerId;
|
||||||
$: isAllUserOwned = Array.from($selectedAssets).every((asset) => asset.ownerId === $user.id);
|
|
||||||
$: isAllFavorite = Array.from($selectedAssets).every((asset) => asset.isFavorite);
|
|
||||||
$: {
|
$: {
|
||||||
if (isShowActivity) {
|
if (isShowActivity) {
|
||||||
assetGridWidth = globalWidth - (globalWidth < 768 ? 360 : 460);
|
assetGridWidth = globalWidth - (globalWidth < 768 ? 360 : 460);
|
||||||
@ -440,15 +438,15 @@
|
|||||||
<AddToAlbum shared />
|
<AddToAlbum shared />
|
||||||
</AssetSelectContextMenu>
|
</AssetSelectContextMenu>
|
||||||
<AssetSelectContextMenu icon={mdiDotsVertical} title="Menu">
|
<AssetSelectContextMenu icon={mdiDotsVertical} title="Menu">
|
||||||
{#if isAllUserOwned}
|
{#if $isAllUserOwned}
|
||||||
<FavoriteAction menuItem removeFavorite={isAllFavorite} />
|
<FavoriteAction menuItem />
|
||||||
{/if}
|
|
||||||
<ArchiveAction menuItem />
|
<ArchiveAction menuItem />
|
||||||
|
{/if}
|
||||||
<DownloadAction menuItem filename="{album.albumName}.zip" />
|
<DownloadAction menuItem filename="{album.albumName}.zip" />
|
||||||
{#if isOwned || isAllUserOwned}
|
{#if isOwned || $isAllUserOwned}
|
||||||
<RemoveFromAlbum menuItem bind:album onRemove={(assetIds) => handleRemoveAssets(assetIds)} />
|
<RemoveFromAlbum menuItem bind:album onRemove={(assetIds) => handleRemoveAssets(assetIds)} />
|
||||||
{/if}
|
{/if}
|
||||||
{#if isAllUserOwned}
|
{#if $isAllUserOwned}
|
||||||
<DeleteAssets menuItem onAssetDelete={(assetId) => assetStore.removeAsset(assetId)} />
|
<DeleteAssets menuItem onAssetDelete={(assetId) => assetStore.removeAsset(assetId)} />
|
||||||
<ChangeDate menuItem />
|
<ChangeDate menuItem />
|
||||||
<ChangeLocation menuItem />
|
<ChangeLocation menuItem />
|
||||||
|
@ -23,13 +23,11 @@
|
|||||||
const assetStore = new AssetStore({ isArchived: true });
|
const assetStore = new AssetStore({ isArchived: true });
|
||||||
const assetInteractionStore = createAssetInteractionStore();
|
const assetInteractionStore = createAssetInteractionStore();
|
||||||
const { isMultiSelectState, selectedAssets } = assetInteractionStore;
|
const { isMultiSelectState, selectedAssets } = assetInteractionStore;
|
||||||
|
|
||||||
$: isAllFavorite = Array.from($selectedAssets).every((asset) => asset.isFavorite);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $isMultiSelectState}
|
{#if $isMultiSelectState}
|
||||||
<AssetSelectControlBar assets={$selectedAssets} clearSelect={() => assetInteractionStore.clearMultiselect()}>
|
<AssetSelectControlBar assets={$selectedAssets} clearSelect={() => assetInteractionStore.clearMultiselect()}>
|
||||||
<ArchiveAction unarchive onArchive={(ids) => assetStore.removeAssets(ids)} />
|
<ArchiveAction onArchive={(ids) => assetStore.removeAssets(ids)} />
|
||||||
<CreateSharedLink />
|
<CreateSharedLink />
|
||||||
<SelectAllAssets {assetStore} {assetInteractionStore} />
|
<SelectAllAssets {assetStore} {assetInteractionStore} />
|
||||||
<AssetSelectContextMenu icon={mdiPlus} title="Add">
|
<AssetSelectContextMenu icon={mdiPlus} title="Add">
|
||||||
@ -39,7 +37,7 @@
|
|||||||
<DeleteAssets onAssetDelete={(assetId) => assetStore.removeAsset(assetId)} />
|
<DeleteAssets onAssetDelete={(assetId) => assetStore.removeAsset(assetId)} />
|
||||||
<AssetSelectContextMenu icon={mdiDotsVertical} title="Add">
|
<AssetSelectContextMenu icon={mdiDotsVertical} title="Add">
|
||||||
<DownloadAction menuItem />
|
<DownloadAction menuItem />
|
||||||
<FavoriteAction menuItem removeFavorite={isAllFavorite} />
|
<FavoriteAction menuItem />
|
||||||
</AssetSelectContextMenu>
|
</AssetSelectContextMenu>
|
||||||
</AssetSelectControlBar>
|
</AssetSelectControlBar>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -25,14 +25,12 @@
|
|||||||
const assetStore = new AssetStore({ isFavorite: true });
|
const assetStore = new AssetStore({ isFavorite: true });
|
||||||
const assetInteractionStore = createAssetInteractionStore();
|
const assetInteractionStore = createAssetInteractionStore();
|
||||||
const { isMultiSelectState, selectedAssets } = assetInteractionStore;
|
const { isMultiSelectState, selectedAssets } = assetInteractionStore;
|
||||||
|
|
||||||
$: isAllArchive = Array.from($selectedAssets).every((asset) => asset.isArchived);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- 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={(ids) => assetStore.removeAssets(ids)} />
|
<FavoriteAction onFavorite={(ids) => assetStore.removeAssets(ids)} />
|
||||||
<CreateSharedLink />
|
<CreateSharedLink />
|
||||||
<SelectAllAssets {assetStore} {assetInteractionStore} />
|
<SelectAllAssets {assetStore} {assetInteractionStore} />
|
||||||
<AssetSelectContextMenu icon={mdiPlus} title="Add">
|
<AssetSelectContextMenu icon={mdiPlus} title="Add">
|
||||||
@ -42,7 +40,7 @@
|
|||||||
<DeleteAssets onAssetDelete={(assetId) => assetStore.removeAsset(assetId)} />
|
<DeleteAssets onAssetDelete={(assetId) => assetStore.removeAsset(assetId)} />
|
||||||
<AssetSelectContextMenu icon={mdiDotsVertical} title="Menu">
|
<AssetSelectContextMenu icon={mdiDotsVertical} title="Menu">
|
||||||
<DownloadAction menuItem />
|
<DownloadAction menuItem />
|
||||||
<ArchiveAction menuItem unarchive={isAllArchive} />
|
<ArchiveAction menuItem />
|
||||||
<ChangeDate menuItem />
|
<ChangeDate menuItem />
|
||||||
<ChangeLocation menuItem />
|
<ChangeLocation menuItem />
|
||||||
</AssetSelectContextMenu>
|
</AssetSelectContextMenu>
|
||||||
|
@ -108,8 +108,6 @@
|
|||||||
isSearchingPeople = false;
|
isSearchingPeople = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
$: isAllArchive = Array.from($selectedAssets).every((asset) => asset.isArchived);
|
|
||||||
$: isAllFavorite = Array.from($selectedAssets).every((asset) => asset.isFavorite);
|
|
||||||
$: $onPersonThumbnail === data.person.id &&
|
$: $onPersonThumbnail === data.person.id &&
|
||||||
(thumbnailData = api.getPeopleThumbnailUrl(data.person.id) + `?now=${Date.now()}`);
|
(thumbnailData = api.getPeopleThumbnailUrl(data.person.id) + `?now=${Date.now()}`);
|
||||||
|
|
||||||
@ -394,8 +392,8 @@
|
|||||||
<DeleteAssets onAssetDelete={(assetId) => $assetStore.removeAsset(assetId)} />
|
<DeleteAssets onAssetDelete={(assetId) => $assetStore.removeAsset(assetId)} />
|
||||||
<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" />
|
||||||
<FavoriteAction menuItem removeFavorite={isAllFavorite} />
|
<FavoriteAction menuItem />
|
||||||
<ArchiveAction menuItem unarchive={isAllArchive} onArchive={(ids) => $assetStore.removeAssets(ids)} />
|
<ArchiveAction menuItem onArchive={(ids) => $assetStore.removeAssets(ids)} />
|
||||||
<MenuOption text="Fix incorrect match" on:click={handleReassignAssets} />
|
<MenuOption text="Fix incorrect match" on:click={handleReassignAssets} />
|
||||||
<ChangeDate menuItem />
|
<ChangeDate menuItem />
|
||||||
<ChangeLocation menuItem />
|
<ChangeLocation menuItem />
|
||||||
|
@ -31,8 +31,6 @@
|
|||||||
const assetInteractionStore = createAssetInteractionStore();
|
const assetInteractionStore = createAssetInteractionStore();
|
||||||
const { isMultiSelectState, selectedAssets } = assetInteractionStore;
|
const { isMultiSelectState, selectedAssets } = assetInteractionStore;
|
||||||
|
|
||||||
$: isAllFavorite = Array.from($selectedAssets).every((asset) => asset.isFavorite);
|
|
||||||
|
|
||||||
const handleEscape = () => {
|
const handleEscape = () => {
|
||||||
if ($showAssetViewer) {
|
if ($showAssetViewer) {
|
||||||
return;
|
return;
|
||||||
@ -65,7 +63,7 @@
|
|||||||
onAssetDelete={(assetId) => assetStore.removeAsset(assetId)}
|
onAssetDelete={(assetId) => assetStore.removeAsset(assetId)}
|
||||||
/>
|
/>
|
||||||
<AssetSelectContextMenu icon={mdiDotsVertical} title="Menu">
|
<AssetSelectContextMenu icon={mdiDotsVertical} title="Menu">
|
||||||
<FavoriteAction menuItem removeFavorite={isAllFavorite} />
|
<FavoriteAction menuItem />
|
||||||
<DownloadAction menuItem />
|
<DownloadAction menuItem />
|
||||||
<ArchiveAction menuItem onArchive={(ids) => assetStore.removeAssets(ids)} />
|
<ArchiveAction menuItem onArchive={(ids) => assetStore.removeAssets(ids)} />
|
||||||
{#if $selectedAssets.size > 1}
|
{#if $selectedAssets.size > 1}
|
||||||
|
@ -96,8 +96,6 @@
|
|||||||
|
|
||||||
let selectedAssets: Set<AssetResponseDto> = new Set();
|
let selectedAssets: Set<AssetResponseDto> = new Set();
|
||||||
$: isMultiSelectionMode = selectedAssets.size > 0;
|
$: isMultiSelectionMode = selectedAssets.size > 0;
|
||||||
$: isAllArchived = Array.from(selectedAssets).every((asset) => asset.isArchived);
|
|
||||||
$: isAllFavorite = Array.from(selectedAssets).every((asset) => asset.isFavorite);
|
|
||||||
$: searchResultAssets = data.results?.assets.items;
|
$: searchResultAssets = data.results?.assets.items;
|
||||||
|
|
||||||
const onAssetDelete = (assetId: string) => {
|
const onAssetDelete = (assetId: string) => {
|
||||||
@ -122,8 +120,8 @@
|
|||||||
|
|
||||||
<AssetSelectContextMenu icon={mdiDotsVertical} title="Add">
|
<AssetSelectContextMenu icon={mdiDotsVertical} title="Add">
|
||||||
<DownloadAction menuItem />
|
<DownloadAction menuItem />
|
||||||
<FavoriteAction menuItem removeFavorite={isAllFavorite} />
|
<FavoriteAction menuItem />
|
||||||
<ArchiveAction menuItem unarchive={isAllArchived} />
|
<ArchiveAction menuItem />
|
||||||
<ChangeDate menuItem />
|
<ChangeDate menuItem />
|
||||||
<ChangeLocation menuItem />
|
<ChangeLocation menuItem />
|
||||||
</AssetSelectContextMenu>
|
</AssetSelectContextMenu>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user