-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add album keyboard shortcuts (#16442)
* 15712: Added keyboard shortcuts for opening add to album modal and highlighting/selecting an album to add to. * 15712: Re-factored logic from template code into script. Extracted new album button into separate cmponent. * 15712: Document new keyboard shortucts now that they work everywhere. * 15712: Extract some constants/helper functions. * 15712: Missing comma. * 15712: Pulled logic out into separate unit testable class. * 15712: Added a unit test. * 15712: Move the modal back up to keep the github PR happy. * 15712: PR feedback - renamed typescript files and switch to class bind directive. * 15712:Move selection modal into correct package. * 15712: Better naming of module and files.
- Loading branch information
1 parent
366f237
commit 6bf2e8d
Showing
9 changed files
with
455 additions
and
116 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
113 changes: 0 additions & 113 deletions
113
web/src/lib/components/shared-components/album-selection-modal.svelte
This file was deleted.
Oops, something went wrong.
126 changes: 126 additions & 0 deletions
126
web/src/lib/components/shared-components/album-selection/album-selection-modal.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
<script lang="ts"> | ||
import { type AlbumResponseDto, getAllAlbums } from '@immich/sdk'; | ||
import { onMount } from 'svelte'; | ||
import AlbumListItem from '../../asset-viewer/album-list-item.svelte'; | ||
import NewAlbumListItem from './new-album-list-item.svelte'; | ||
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte'; | ||
import { initInput } from '$lib/actions/focus'; | ||
import { t } from 'svelte-i18n'; | ||
import { albumViewSettings } from '$lib/stores/preferences.store'; | ||
import { | ||
AlbumModalRowConverter, | ||
AlbumModalRowType, | ||
isSelectableRowType, | ||
} from '$lib/components/shared-components/album-selection/album-selection-utils'; | ||
let albums: AlbumResponseDto[] = $state([]); | ||
let recentAlbums: AlbumResponseDto[] = $state([]); | ||
let loading = $state(true); | ||
let search = $state(''); | ||
let selectedRowIndex: number = $state(-1); | ||
interface Props { | ||
onNewAlbum: (search: string) => void; | ||
onAlbumClick: (album: AlbumResponseDto) => void; | ||
shared: boolean; | ||
onClose: () => void; | ||
} | ||
let { onNewAlbum, onAlbumClick, shared, onClose }: Props = $props(); | ||
onMount(async () => { | ||
albums = await getAllAlbums({ shared: shared || undefined }); | ||
recentAlbums = albums.sort((a, b) => (new Date(a.createdAt) > new Date(b.createdAt) ? -1 : 1)).slice(0, 3); | ||
loading = false; | ||
}); | ||
const rowConverter = new AlbumModalRowConverter(shared, $albumViewSettings.sortBy, $albumViewSettings.sortOrder); | ||
const albumModalRows = $derived(rowConverter.toModalRows(search, recentAlbums, albums, selectedRowIndex)); | ||
const selectableRowCount = $derived(albumModalRows.filter((row) => isSelectableRowType(row.type)).length); | ||
const onkeydown = (e: KeyboardEvent) => { | ||
switch (e.key) { | ||
case 'ArrowUp': { | ||
e.preventDefault(); | ||
if (selectedRowIndex > 0) { | ||
selectedRowIndex--; | ||
} else { | ||
selectedRowIndex = selectableRowCount - 1; | ||
} | ||
break; | ||
} | ||
case 'ArrowDown': { | ||
e.preventDefault(); | ||
if (selectedRowIndex < selectableRowCount - 1) { | ||
selectedRowIndex++; | ||
} else { | ||
selectedRowIndex = 0; | ||
} | ||
break; | ||
} | ||
case 'Enter': { | ||
e.preventDefault(); | ||
const selectedRow = albumModalRows.find((row) => row.selected); | ||
if (selectedRow) { | ||
if (selectedRow.type === AlbumModalRowType.NEW_ALBUM) { | ||
onNewAlbum(search); | ||
} else if (selectedRow.type === AlbumModalRowType.ALBUM_ITEM && selectedRow.album) { | ||
onAlbumClick(selectedRow.album); | ||
} | ||
selectedRowIndex = -1; | ||
} | ||
break; | ||
} | ||
default: { | ||
selectedRowIndex = -1; | ||
} | ||
} | ||
}; | ||
const handleAlbumClick = (album: AlbumResponseDto) => () => onAlbumClick(album); | ||
</script> | ||
|
||
<FullScreenModal title={shared ? $t('add_to_shared_album') : $t('add_to_album')} {onClose}> | ||
<div class="mb-2 flex max-h-[400px] flex-col"> | ||
{#if loading} | ||
{#each { length: 3 } as _} | ||
<div class="flex animate-pulse gap-4 px-6 py-2"> | ||
<div class="h-12 w-12 rounded-xl bg-slate-200"></div> | ||
<div class="flex flex-col items-start justify-center gap-2"> | ||
<span class="h-4 w-36 animate-pulse bg-slate-200"></span> | ||
<div class="flex animate-pulse gap-1"> | ||
<span class="h-3 w-8 bg-slate-200"></span> | ||
<span class="h-3 w-20 bg-slate-200"></span> | ||
</div> | ||
</div> | ||
</div> | ||
{/each} | ||
{:else} | ||
<input | ||
class="border-b-4 border-immich-bg bg-immich-bg px-6 py-2 text-2xl focus:border-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:focus:border-immich-dark-primary" | ||
placeholder={$t('search')} | ||
{onkeydown} | ||
bind:value={search} | ||
use:initInput | ||
/> | ||
<div class="immich-scrollbar overflow-y-auto"> | ||
{#each albumModalRows as row} | ||
{#if row.type === AlbumModalRowType.NEW_ALBUM} | ||
<NewAlbumListItem selected={row.selected || false} {onNewAlbum} searchQuery={search} /> | ||
{:else if row.type === AlbumModalRowType.SECTION} | ||
<p class="px-5 py-3 text-xs">{row.text}</p> | ||
{:else if row.type === AlbumModalRowType.MESSAGE} | ||
<p class="px-5 py-1 text-sm">{row.text}</p> | ||
{:else if row.type === AlbumModalRowType.ALBUM_ITEM && row.album} | ||
<AlbumListItem | ||
album={row.album} | ||
selected={row.selected || false} | ||
searchQuery={search} | ||
onAlbumClick={handleAlbumClick(row.album)} | ||
/> | ||
{/if} | ||
{/each} | ||
</div> | ||
{/if} | ||
</div> | ||
</FullScreenModal> |
Oops, something went wrong.