mirror of
https://github.com/immich-app/immich.git
synced 2025-12-08 09:13:07 +03:00
chore(web): minor UX improvements of "view asset owners" feature (#24319)
* feat: toggle in options modal
* feat(i18n): add labels to display who uploaded each asset and show asset owners
* feat: migrate asset owner settings to TimelineManager and update AlbumOptionsModal
* Revert "feat(i18n): add labels to display who uploaded each asset and show asset owners"
This reverts commit cf8f4eb135.
* fix: simplify AlbumOptionsModal invocation and update aria-label for asset owners
* feat(i18n): add label for viewing asset owners in the interface
* feat: add tests for showAssetOwners functionality in TimelineManager
* chore: move asset owner visibility toggle to kebabu menu
This commit is contained in:
@@ -692,4 +692,42 @@ describe('TimelineManager', () => {
|
|||||||
expect(discoveredAssets.size).toBe(assetCount);
|
expect(discoveredAssets.size).toBe(assetCount);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('showAssetOwners', () => {
|
||||||
|
const LS_KEY = 'album-show-asset-owners';
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// ensure clean state
|
||||||
|
globalThis.localStorage?.removeItem(LS_KEY);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('defaults to false', () => {
|
||||||
|
const timelineManager = new TimelineManager();
|
||||||
|
expect(timelineManager.showAssetOwners).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setShowAssetOwners updates value', () => {
|
||||||
|
const timelineManager = new TimelineManager();
|
||||||
|
timelineManager.setShowAssetOwners(true);
|
||||||
|
expect(timelineManager.showAssetOwners).toBe(true);
|
||||||
|
timelineManager.setShowAssetOwners(false);
|
||||||
|
expect(timelineManager.showAssetOwners).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('toggleShowAssetOwners flips value', () => {
|
||||||
|
const timelineManager = new TimelineManager();
|
||||||
|
expect(timelineManager.showAssetOwners).toBe(false);
|
||||||
|
timelineManager.toggleShowAssetOwners();
|
||||||
|
expect(timelineManager.showAssetOwners).toBe(true);
|
||||||
|
timelineManager.toggleShowAssetOwners();
|
||||||
|
expect(timelineManager.showAssetOwners).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('persists across instances via localStorage', () => {
|
||||||
|
const a = new TimelineManager();
|
||||||
|
a.setShowAssetOwners(true);
|
||||||
|
const b = new TimelineManager();
|
||||||
|
expect(b.showAssetOwners).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
} from '$lib/managers/timeline-manager/internal/search-support.svelte';
|
} from '$lib/managers/timeline-manager/internal/search-support.svelte';
|
||||||
import { WebsocketSupport } from '$lib/managers/timeline-manager/internal/websocket-support.svelte';
|
import { WebsocketSupport } from '$lib/managers/timeline-manager/internal/websocket-support.svelte';
|
||||||
import { CancellableTask } from '$lib/utils/cancellable-task';
|
import { CancellableTask } from '$lib/utils/cancellable-task';
|
||||||
|
import { PersistedLocalStorage } from '$lib/utils/persisted';
|
||||||
import {
|
import {
|
||||||
setDifference,
|
setDifference,
|
||||||
toTimelineAsset,
|
toTimelineAsset,
|
||||||
@@ -90,6 +91,19 @@ export class TimelineManager extends VirtualScrollManager {
|
|||||||
#options: TimelineManagerOptions = TimelineManager.#INIT_OPTIONS;
|
#options: TimelineManagerOptions = TimelineManager.#INIT_OPTIONS;
|
||||||
#updatingIntersections = false;
|
#updatingIntersections = false;
|
||||||
#scrollableElement: HTMLElement | undefined = $state();
|
#scrollableElement: HTMLElement | undefined = $state();
|
||||||
|
#showAssetOwners = new PersistedLocalStorage<boolean>('album-show-asset-owners', false);
|
||||||
|
|
||||||
|
get showAssetOwners() {
|
||||||
|
return this.#showAssetOwners.current;
|
||||||
|
}
|
||||||
|
|
||||||
|
setShowAssetOwners(value: boolean) {
|
||||||
|
this.#showAssetOwners.current = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleShowAssetOwners() {
|
||||||
|
this.#showAssetOwners.current = !this.#showAssetOwners.current;
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|||||||
@@ -66,6 +66,7 @@
|
|||||||
} from '@immich/sdk';
|
} from '@immich/sdk';
|
||||||
import { Button, Icon, IconButton, modalManager, toastManager } from '@immich/ui';
|
import { Button, Icon, IconButton, modalManager, toastManager } from '@immich/ui';
|
||||||
import {
|
import {
|
||||||
|
mdiAccountEye,
|
||||||
mdiAccountEyeOutline,
|
mdiAccountEyeOutline,
|
||||||
mdiArrowLeft,
|
mdiArrowLeft,
|
||||||
mdiCogOutline,
|
mdiCogOutline,
|
||||||
@@ -101,7 +102,9 @@
|
|||||||
let isCreatingSharedAlbum = $state(false);
|
let isCreatingSharedAlbum = $state(false);
|
||||||
let isShowActivity = $state(false);
|
let isShowActivity = $state(false);
|
||||||
let albumOrder: AssetOrder | undefined = $state(data.album.order);
|
let albumOrder: AssetOrder | undefined = $state(data.album.order);
|
||||||
let showAlbumUsers = $state(false);
|
|
||||||
|
let timelineManager = $state<TimelineManager>() as TimelineManager;
|
||||||
|
let showAlbumUsers = $derived(timelineManager?.showAssetOwners ?? false);
|
||||||
|
|
||||||
const assetInteraction = new AssetInteraction();
|
const assetInteraction = new AssetInteraction();
|
||||||
const timelineInteraction = new AssetInteraction();
|
const timelineInteraction = new AssetInteraction();
|
||||||
@@ -303,7 +306,6 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let timelineManager = $state<TimelineManager>() as TimelineManager;
|
|
||||||
const options = $derived.by(() => {
|
const options = $derived.by(() => {
|
||||||
if (viewMode === AlbumPageViewMode.SELECT_ASSETS) {
|
if (viewMode === AlbumPageViewMode.SELECT_ASSETS) {
|
||||||
return {
|
return {
|
||||||
@@ -597,17 +599,6 @@
|
|||||||
{#snippet trailing()}
|
{#snippet trailing()}
|
||||||
<CastButton />
|
<CastButton />
|
||||||
|
|
||||||
{#if containsEditors}
|
|
||||||
<IconButton
|
|
||||||
variant="ghost"
|
|
||||||
shape="round"
|
|
||||||
color="secondary"
|
|
||||||
aria-label={$t('view_asset_owners')}
|
|
||||||
icon={mdiAccountEyeOutline}
|
|
||||||
onclick={() => (showAlbumUsers = !showAlbumUsers)}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if isEditor}
|
{#if isEditor}
|
||||||
<IconButton
|
<IconButton
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@@ -668,6 +659,13 @@
|
|||||||
color="secondary"
|
color="secondary"
|
||||||
offset={{ x: 175, y: 25 }}
|
offset={{ x: 175, y: 25 }}
|
||||||
>
|
>
|
||||||
|
{#if containsEditors}
|
||||||
|
<MenuOption
|
||||||
|
icon={showAlbumUsers ? mdiAccountEye : mdiAccountEyeOutline}
|
||||||
|
text={$t('view_asset_owners')}
|
||||||
|
onClick={() => timelineManager.toggleShowAssetOwners()}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
{#if album.assetCount > 0}
|
{#if album.assetCount > 0}
|
||||||
<MenuOption
|
<MenuOption
|
||||||
icon={mdiImageOutline}
|
icon={mdiImageOutline}
|
||||||
|
|||||||
Reference in New Issue
Block a user