mirror of
https://github.com/immich-app/immich.git
synced 2025-06-02 21:24:28 -04:00
* 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.
95 lines
3.0 KiB
TypeScript
95 lines
3.0 KiB
TypeScript
import { sortAlbums } from '$lib/utils/album-utils';
|
|
import { normalizeSearchString } from '$lib/utils/string-utils';
|
|
import type { AlbumResponseDto } from '@immich/sdk';
|
|
import { t } from 'svelte-i18n';
|
|
import { get } from 'svelte/store';
|
|
|
|
export const SCROLL_PROPERTIES: ScrollIntoViewOptions = { block: 'center', behavior: 'smooth' };
|
|
|
|
export enum AlbumModalRowType {
|
|
SECTION = 'section',
|
|
MESSAGE = 'message',
|
|
NEW_ALBUM = 'newAlbum',
|
|
ALBUM_ITEM = 'albumItem',
|
|
}
|
|
|
|
export type AlbumModalRow = {
|
|
type: AlbumModalRowType;
|
|
selected?: boolean;
|
|
text?: string;
|
|
album?: AlbumResponseDto;
|
|
};
|
|
|
|
export const isSelectableRowType = (type: AlbumModalRowType) =>
|
|
type === AlbumModalRowType.NEW_ALBUM || type === AlbumModalRowType.ALBUM_ITEM;
|
|
|
|
const $t = get(t);
|
|
|
|
export class AlbumModalRowConverter {
|
|
private readonly shared: boolean;
|
|
private readonly sortBy: string;
|
|
private readonly orderBy: string;
|
|
|
|
constructor(shared: boolean, sortBy: string, orderBy: string) {
|
|
this.shared = shared;
|
|
this.sortBy = sortBy;
|
|
this.orderBy = orderBy;
|
|
}
|
|
|
|
toModalRows(
|
|
search: string,
|
|
recentAlbums: AlbumResponseDto[],
|
|
albums: AlbumResponseDto[],
|
|
selectedRowIndex: number,
|
|
): AlbumModalRow[] {
|
|
// only show recent albums if no search was entered, or we're in the normal albums (non-shared) modal.
|
|
const recentAlbumsToShow = !this.shared && search.length === 0 ? recentAlbums : [];
|
|
const rows: AlbumModalRow[] = [];
|
|
rows.push({ type: AlbumModalRowType.NEW_ALBUM, selected: selectedRowIndex === 0 });
|
|
|
|
const filteredAlbums = sortAlbums(
|
|
search.length > 0 && albums.length > 0
|
|
? albums.filter((album) => {
|
|
return normalizeSearchString(album.albumName).includes(normalizeSearchString(search));
|
|
})
|
|
: albums,
|
|
{ sortBy: this.sortBy, orderBy: this.orderBy },
|
|
);
|
|
|
|
if (filteredAlbums.length > 0) {
|
|
if (recentAlbumsToShow.length > 0) {
|
|
rows.push({ type: AlbumModalRowType.SECTION, text: $t('recent').toUpperCase() });
|
|
const selectedOffsetDueToNewAlbumRow = 1;
|
|
for (const [i, album] of recentAlbums.entries()) {
|
|
rows.push({
|
|
type: AlbumModalRowType.ALBUM_ITEM,
|
|
selected: selectedRowIndex === i + selectedOffsetDueToNewAlbumRow,
|
|
album,
|
|
});
|
|
}
|
|
}
|
|
|
|
if (!this.shared) {
|
|
rows.push({
|
|
type: AlbumModalRowType.SECTION,
|
|
text: (search.length === 0 ? $t('all_albums') : $t('albums')).toUpperCase(),
|
|
});
|
|
}
|
|
|
|
const selectedOffsetDueToNewAndRecents = 1 + recentAlbumsToShow.length;
|
|
for (const [i, album] of filteredAlbums.entries()) {
|
|
rows.push({
|
|
type: AlbumModalRowType.ALBUM_ITEM,
|
|
selected: selectedRowIndex === i + selectedOffsetDueToNewAndRecents,
|
|
album,
|
|
});
|
|
}
|
|
} else if (albums.length > 0) {
|
|
rows.push({ type: AlbumModalRowType.MESSAGE, text: $t('no_albums_with_name_yet') });
|
|
} else {
|
|
rows.push({ type: AlbumModalRowType.MESSAGE, text: $t('no_albums_yet') });
|
|
}
|
|
return rows;
|
|
}
|
|
}
|