mirror of
https://github.com/immich-app/immich.git
synced 2024-12-29 15:11:58 +00:00
feat(web): sort albums in modal (#12331)
This commit is contained in:
parent
0a8bd7dc66
commit
720412645f
3 changed files with 81 additions and 70 deletions
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { groupBy, orderBy } from 'lodash-es';
|
import { groupBy } from 'lodash-es';
|
||||||
import { addUsersToAlbum, deleteAlbum, type AlbumUserAddDto, type AlbumResponseDto, isHttpError } from '@immich/sdk';
|
import { addUsersToAlbum, deleteAlbum, type AlbumUserAddDto, type AlbumResponseDto, isHttpError } from '@immich/sdk';
|
||||||
import { mdiDeleteOutline, mdiShareVariantOutline, mdiFolderDownloadOutline, mdiRenameOutline } from '@mdi/js';
|
import { mdiDeleteOutline, mdiShareVariantOutline, mdiFolderDownloadOutline, mdiRenameOutline } from '@mdi/js';
|
||||||
import EditAlbumForm from '$lib/components/forms/edit-album-form.svelte';
|
import EditAlbumForm from '$lib/components/forms/edit-album-form.svelte';
|
||||||
|
@ -17,7 +17,13 @@
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { downloadAlbum } from '$lib/utils/asset-utils';
|
import { downloadAlbum } from '$lib/utils/asset-utils';
|
||||||
import { normalizeSearchString } from '$lib/utils/string-utils';
|
import { normalizeSearchString } from '$lib/utils/string-utils';
|
||||||
import { getSelectedAlbumGroupOption, type AlbumGroup, confirmAlbumDelete } from '$lib/utils/album-utils';
|
import {
|
||||||
|
getSelectedAlbumGroupOption,
|
||||||
|
type AlbumGroup,
|
||||||
|
confirmAlbumDelete,
|
||||||
|
sortAlbums,
|
||||||
|
stringToSortOrder,
|
||||||
|
} from '$lib/utils/album-utils';
|
||||||
import type { ContextMenuPosition } from '$lib/utils/context-menu';
|
import type { ContextMenuPosition } from '$lib/utils/context-menu';
|
||||||
import { user } from '$lib/stores/user.store';
|
import { user } from '$lib/stores/user.store';
|
||||||
import {
|
import {
|
||||||
|
@ -45,10 +51,6 @@
|
||||||
[option: string]: (order: SortOrder, albums: AlbumResponseDto[]) => AlbumGroup[];
|
[option: string]: (order: SortOrder, albums: AlbumResponseDto[]) => AlbumGroup[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AlbumSortOption {
|
|
||||||
[option: string]: (order: SortOrder, albums: AlbumResponseDto[]) => AlbumResponseDto[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const groupOptions: AlbumGroupOption = {
|
const groupOptions: AlbumGroupOption = {
|
||||||
/** No grouping */
|
/** No grouping */
|
||||||
[AlbumGroupBy.None]: (order, albums): AlbumGroup[] => {
|
[AlbumGroupBy.None]: (order, albums): AlbumGroup[] => {
|
||||||
|
@ -116,41 +118,6 @@
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const sortOptions: AlbumSortOption = {
|
|
||||||
/** Sort by album title */
|
|
||||||
[AlbumSortBy.Title]: (order, albums) => {
|
|
||||||
const sortSign = order === SortOrder.Desc ? -1 : 1;
|
|
||||||
return albums.slice().sort((a, b) => a.albumName.localeCompare(b.albumName, $locale) * sortSign);
|
|
||||||
},
|
|
||||||
|
|
||||||
/** Sort by asset count */
|
|
||||||
[AlbumSortBy.ItemCount]: (order, albums) => {
|
|
||||||
return orderBy(albums, 'assetCount', [order]);
|
|
||||||
},
|
|
||||||
|
|
||||||
/** Sort by last modified */
|
|
||||||
[AlbumSortBy.DateModified]: (order, albums) => {
|
|
||||||
return orderBy(albums, [({ updatedAt }) => new Date(updatedAt)], [order]);
|
|
||||||
},
|
|
||||||
|
|
||||||
/** Sort by creation date */
|
|
||||||
[AlbumSortBy.DateCreated]: (order, albums) => {
|
|
||||||
return orderBy(albums, [({ createdAt }) => new Date(createdAt)], [order]);
|
|
||||||
},
|
|
||||||
|
|
||||||
/** Sort by the most recent photo date */
|
|
||||||
[AlbumSortBy.MostRecentPhoto]: (order, albums) => {
|
|
||||||
albums = orderBy(albums, [({ endDate }) => (endDate ? new Date(endDate) : '')], [order]);
|
|
||||||
return albums.sort(sortUnknownYearAlbums);
|
|
||||||
},
|
|
||||||
|
|
||||||
/** Sort by the oldest photo date */
|
|
||||||
[AlbumSortBy.OldestPhoto]: (order, albums) => {
|
|
||||||
albums = orderBy(albums, [({ startDate }) => (startDate ? new Date(startDate) : '')], [order]);
|
|
||||||
return albums.sort(sortUnknownYearAlbums);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let albums: AlbumResponseDto[] = [];
|
let albums: AlbumResponseDto[] = [];
|
||||||
let filteredAlbums: AlbumResponseDto[] = [];
|
let filteredAlbums: AlbumResponseDto[] = [];
|
||||||
let groupedAlbums: AlbumGroup[] = [];
|
let groupedAlbums: AlbumGroup[] = [];
|
||||||
|
@ -208,16 +175,10 @@
|
||||||
|
|
||||||
// Step 4: Sort albums amongst each group.
|
// Step 4: Sort albums amongst each group.
|
||||||
$: {
|
$: {
|
||||||
const defaultSortOption = AlbumSortBy.DateModified;
|
|
||||||
const selectedSortOption = userSettings.sortBy ?? defaultSortOption;
|
|
||||||
|
|
||||||
const sortFunc = sortOptions[selectedSortOption] ?? sortOptions[defaultSortOption];
|
|
||||||
const sortOrder = stringToSortOrder(userSettings.sortOrder);
|
|
||||||
|
|
||||||
groupedAlbums = groupedAlbums.map((group) => ({
|
groupedAlbums = groupedAlbums.map((group) => ({
|
||||||
id: group.id,
|
id: group.id,
|
||||||
name: group.name,
|
name: group.name,
|
||||||
albums: sortFunc(sortOrder, group.albums),
|
albums: sortAlbums(group.albums, { sortBy: userSettings.sortBy, orderBy: userSettings.sortOrder }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
albumGroupIds = groupedAlbums.map(({ id }) => id);
|
albumGroupIds = groupedAlbums.map(({ id }) => id);
|
||||||
|
@ -231,20 +192,6 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const sortUnknownYearAlbums = (a: AlbumResponseDto, b: AlbumResponseDto) => {
|
|
||||||
if (!a.endDate) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (!b.endDate) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
const stringToSortOrder = (order: string) => {
|
|
||||||
return order === 'desc' ? SortOrder.Desc : SortOrder.Asc;
|
|
||||||
};
|
|
||||||
|
|
||||||
const showAlbumContextMenu = (contextMenuDetail: ContextMenuPosition, album: AlbumResponseDto) => {
|
const showAlbumContextMenu = (contextMenuDetail: ContextMenuPosition, album: AlbumResponseDto) => {
|
||||||
contextMenuTargetAlbum = album;
|
contextMenuTargetAlbum = album;
|
||||||
contextMenuPosition = {
|
contextMenuPosition = {
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
||||||
import { initInput } from '$lib/actions/focus';
|
import { initInput } from '$lib/actions/focus';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
import { sortAlbums } from '$lib/utils/album-utils';
|
||||||
|
import { albumViewSettings } from '$lib/stores/preferences.store';
|
||||||
|
|
||||||
let albums: AlbumResponseDto[] = [];
|
let albums: AlbumResponseDto[] = [];
|
||||||
let recentAlbums: AlbumResponseDto[] = [];
|
let recentAlbums: AlbumResponseDto[] = [];
|
||||||
|
@ -29,14 +31,14 @@
|
||||||
loading = false;
|
loading = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
$: {
|
$: filteredAlbums = sortAlbums(
|
||||||
filteredAlbums =
|
search.length > 0 && albums.length > 0
|
||||||
search.length > 0 && albums.length > 0
|
? albums.filter((album) => {
|
||||||
? albums.filter((album) => {
|
return normalizeSearchString(album.albumName).includes(normalizeSearchString(search));
|
||||||
return normalizeSearchString(album.albumName).includes(normalizeSearchString(search));
|
})
|
||||||
})
|
: albums,
|
||||||
: albums;
|
{ sortBy: $albumViewSettings.sortBy, orderBy: $albumViewSettings.sortOrder },
|
||||||
}
|
);
|
||||||
|
|
||||||
const handleSelect = (album: AlbumResponseDto) => {
|
const handleSelect = (album: AlbumResponseDto) => {
|
||||||
dispatch('album', album);
|
dispatch('album', album);
|
||||||
|
|
|
@ -7,11 +7,13 @@ import {
|
||||||
AlbumSortBy,
|
AlbumSortBy,
|
||||||
SortOrder,
|
SortOrder,
|
||||||
albumViewSettings,
|
albumViewSettings,
|
||||||
|
locale,
|
||||||
type AlbumViewSettings,
|
type AlbumViewSettings,
|
||||||
} from '$lib/stores/preferences.store';
|
} from '$lib/stores/preferences.store';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import type { AlbumResponseDto } from '@immich/sdk';
|
import type { AlbumResponseDto } from '@immich/sdk';
|
||||||
import * as sdk from '@immich/sdk';
|
import * as sdk from '@immich/sdk';
|
||||||
|
import { orderBy } from 'lodash-es';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { get } from 'svelte/store';
|
import { get } from 'svelte/store';
|
||||||
|
|
||||||
|
@ -213,3 +215,63 @@ export const confirmAlbumDelete = async (album: AlbumResponseDto) => {
|
||||||
|
|
||||||
return dialogController.show({ prompt });
|
return dialogController.show({ prompt });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface AlbumSortOption {
|
||||||
|
[option: string]: (order: SortOrder, albums: AlbumResponseDto[]) => AlbumResponseDto[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const sortUnknownYearAlbums = (a: AlbumResponseDto, b: AlbumResponseDto) => {
|
||||||
|
if (!a.endDate) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!b.endDate) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const stringToSortOrder = (order: string) => {
|
||||||
|
return order === 'desc' ? SortOrder.Desc : SortOrder.Asc;
|
||||||
|
};
|
||||||
|
|
||||||
|
const sortOptions: AlbumSortOption = {
|
||||||
|
/** Sort by album title */
|
||||||
|
[AlbumSortBy.Title]: (order, albums) => {
|
||||||
|
const sortSign = order === SortOrder.Desc ? -1 : 1;
|
||||||
|
return albums.slice().sort((a, b) => a.albumName.localeCompare(b.albumName, get(locale)) * sortSign);
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Sort by asset count */
|
||||||
|
[AlbumSortBy.ItemCount]: (order, albums) => {
|
||||||
|
return orderBy(albums, 'assetCount', [order]);
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Sort by last modified */
|
||||||
|
[AlbumSortBy.DateModified]: (order, albums) => {
|
||||||
|
return orderBy(albums, [({ updatedAt }) => new Date(updatedAt)], [order]);
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Sort by creation date */
|
||||||
|
[AlbumSortBy.DateCreated]: (order, albums) => {
|
||||||
|
return orderBy(albums, [({ createdAt }) => new Date(createdAt)], [order]);
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Sort by the most recent photo date */
|
||||||
|
[AlbumSortBy.MostRecentPhoto]: (order, albums) => {
|
||||||
|
albums = orderBy(albums, [({ endDate }) => (endDate ? new Date(endDate) : '')], [order]);
|
||||||
|
return albums.sort(sortUnknownYearAlbums);
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Sort by the oldest photo date */
|
||||||
|
[AlbumSortBy.OldestPhoto]: (order, albums) => {
|
||||||
|
albums = orderBy(albums, [({ startDate }) => (startDate ? new Date(startDate) : '')], [order]);
|
||||||
|
return albums.sort(sortUnknownYearAlbums);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const sortAlbums = (albums: AlbumResponseDto[], { sortBy, orderBy }: { sortBy: string; orderBy: string }) => {
|
||||||
|
const sort = sortOptions[sortBy] ?? sortOptions[AlbumSortBy.DateModified];
|
||||||
|
const order = stringToSortOrder(orderBy);
|
||||||
|
|
||||||
|
return sort(order, albums);
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in a new issue