feat(web): Enable selection interactions in folder view (#15049)

* feat(web): Enable selection interactions in folder view

* feat(web): Add link to parent folder in detail pane, if folders are enabled

* Added invalidation and refreshing of asset cache on changes

* fix: removed unused imports and changed link

* chore: styling

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
Arno
2025-01-03 17:09:31 +01:00
committed by GitHub
parent 007caa26bd
commit b45ff8d09f
5 changed files with 96 additions and 13 deletions

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { goto, invalidateAll } from '$app/navigation';
import { page } from '$app/stores';
import UserPageLayout, { headerId } from '$lib/components/layouts/user-page-layout.svelte';
import GalleryViewer from '$lib/components/shared-components/gallery-viewer/gallery-viewer.svelte';
@@ -10,13 +10,28 @@
import type { Viewport } from '$lib/stores/assets.store';
import { foldersStore } from '$lib/stores/folders.svelte';
import { buildTree, normalizeTreePath } from '$lib/utils/tree-utils';
import { mdiFolder, mdiFolderHome, mdiFolderOutline } from '@mdi/js';
import { mdiDotsVertical, mdiFolder, mdiFolderHome, mdiFolderOutline, mdiPlus, mdiSelectAll } from '@mdi/js';
import { onMount } from 'svelte';
import { t } from 'svelte-i18n';
import type { PageData } from './$types';
import Breadcrumbs from '$lib/components/shared-components/tree/breadcrumbs.svelte';
import SkipLink from '$lib/components/elements/buttons/skip-link.svelte';
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte';
import CreateSharedLink from '$lib/components/photos-page/actions/create-shared-link.svelte';
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
import AddToAlbum from '$lib/components/photos-page/actions/add-to-album.svelte';
import AssetJobActions from '$lib/components/photos-page/actions/asset-job-actions.svelte';
import DeleteAssets from '$lib/components/photos-page/actions/delete-assets.svelte';
import TagAction from '$lib/components/photos-page/actions/tag-action.svelte';
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
import { preferences } from '$lib/stores/user.store';
import { cancelMultiselect } from '$lib/utils/asset-utils';
import DownloadAction from '$lib/components/photos-page/actions/download-action.svelte';
import FavoriteAction from '$lib/components/photos-page/actions/favorite-action.svelte';
import ArchiveAction from '$lib/components/photos-page/actions/archive-action.svelte';
import ChangeDate from '$lib/components/photos-page/actions/change-date-action.svelte';
import ChangeLocation from '$lib/components/photos-page/actions/change-location-action.svelte';
interface Props {
data: PageData;
@@ -31,6 +46,8 @@
let currentPath = $derived($page.url.searchParams.get(QueryParameter.PATH) || '');
let currentTreeItems = $derived(currentPath ? data.currentFolders : Object.keys(tree));
$inspect(data).with(console.log);
const assetInteraction = new AssetInteraction();
onMount(async () => {
@@ -50,8 +67,51 @@
};
const navigateToView = (path: string) => goto(getLink(path));
const triggerAssetUpdate = async () => {
await foldersStore.refreshAssetsByPath(data.path);
await invalidateAll();
};
const handleSelectAll = () => {
if (!data.pathAssets) {
return;
}
assetInteraction.selectAssets(data.pathAssets);
};
</script>
{#if assetInteraction.selectionActive}
<div class="fixed z-[910] top-0 left-0 w-full">
<AssetSelectControlBar
assets={assetInteraction.selectedAssets}
clearSelect={() => cancelMultiselect(assetInteraction)}
>
<CreateSharedLink />
<CircleIconButton title={$t('select_all')} icon={mdiSelectAll} onclick={handleSelectAll} />
<ButtonContextMenu icon={mdiPlus} title={$t('add_to')}>
<AddToAlbum />
<AddToAlbum shared />
</ButtonContextMenu>
<FavoriteAction removeFavorite={assetInteraction.isAllFavorite} onFavorite={triggerAssetUpdate} />
<ButtonContextMenu icon={mdiDotsVertical} title={$t('add')}>
<DownloadAction menuItem />
<ChangeDate menuItem />
<ChangeLocation menuItem />
<ArchiveAction menuItem unarchive={assetInteraction.isAllArchived} onArchive={triggerAssetUpdate} />
{#if $preferences.tags.enabled && assetInteraction.isAllUserOwned}
<TagAction menuItem />
{/if}
<DeleteAssets menuItem onAssetDelete={triggerAssetUpdate} />
<hr />
<AssetJobActions />
</ButtonContextMenu>
</AssetSelectControlBar>
</div>
{/if}
<UserPageLayout title={data.meta.title}>
{#snippet sidebar()}
<SideBarSection>
@@ -78,13 +138,7 @@
<!-- Assets -->
{#if data.pathAssets && data.pathAssets.length > 0}
<div bind:clientHeight={viewport.height} bind:clientWidth={viewport.width} class="mt-2">
<GalleryViewer
assets={data.pathAssets}
{assetInteraction}
{viewport}
disableAssetSelect={true}
showAssetName={true}
/>
<GalleryViewer assets={data.pathAssets} {assetInteraction} {viewport} showAssetName={true} />
</div>
{/if}
</section>

View File

@@ -19,6 +19,10 @@ export const load = (async ({ params, url }) => {
if (path) {
await foldersStore.fetchAssetsByPath(path);
pathAssets = foldersStore.assets[path] || null;
} else {
// If no path is provided, we we're at the root level
// We should bust the asset cache of the folder store, to make sure we don't show stale data
foldersStore.bustAssetCache();
}
let tree = buildTree(foldersStore.uniquePaths);