From a1b60f7ce17a469ded6721c671a5efdbfecbdd0b Mon Sep 17 00:00:00 2001 From: Krassimir Valev Date: Sun, 1 Sep 2024 22:18:18 +0200 Subject: [PATCH 1/2] revamp places --- .../places-page/places-card-group.svelte | 64 ++++++++++ .../places-page/places-controls.svelte | 93 +++++++++++++++ .../components/places-page/places-list.svelte | 111 ++++++++++++++++++ web/src/lib/stores/preferences.store.ts | 18 +++ web/src/lib/utils/places-utils.ts | 95 +++++++++++++++ web/src/routes/(user)/places/+page.svelte | 64 ++++------ 6 files changed, 407 insertions(+), 38 deletions(-) create mode 100644 web/src/lib/components/places-page/places-card-group.svelte create mode 100644 web/src/lib/components/places-page/places-controls.svelte create mode 100644 web/src/lib/components/places-page/places-list.svelte create mode 100644 web/src/lib/utils/places-utils.ts diff --git a/web/src/lib/components/places-page/places-card-group.svelte b/web/src/lib/components/places-page/places-card-group.svelte new file mode 100644 index 0000000000000..d3b3a00cbeefd --- /dev/null +++ b/web/src/lib/components/places-page/places-card-group.svelte @@ -0,0 +1,64 @@ + + +{#if group} +
+ +
+
+{/if} + +
+ {#if !isCollapsed} +
+ {#each places as item} + {@const city = item.exifInfo?.city} + +
+ {city} +
+ + {city} + +
+ {/each} +
+ {/if} +
diff --git a/web/src/lib/components/places-page/places-controls.svelte b/web/src/lib/components/places-page/places-controls.svelte new file mode 100644 index 0000000000000..b22037ff56311 --- /dev/null +++ b/web/src/lib/components/places-page/places-controls.svelte @@ -0,0 +1,93 @@ + + + + + + + handleChangeGroupBy(detail)} + render={({ id, isDisabled }) => ({ + title: placesGroupByNames[id], + icon: groupIcon, + disabled: isDisabled(), + })} +/> + +{#if getSelectedPlacesGroupOption($placesViewSettings) !== PlacesGroupBy.None} + + + + +{/if} diff --git a/web/src/lib/components/places-page/places-list.svelte b/web/src/lib/components/places-page/places-list.svelte new file mode 100644 index 0000000000000..3a846683f1b60 --- /dev/null +++ b/web/src/lib/components/places-page/places-list.svelte @@ -0,0 +1,111 @@ + + +{#if hasPlaces} + + {#if placesGroupOption === PlacesGroupBy.None} + + {:else} + {#each groupedPlaces as placeGroup (placeGroup.id)} + + {/each} + {/if} +{:else} +
+
+ +

{$t('no_places')}

+
+
+{/if} diff --git a/web/src/lib/stores/preferences.store.ts b/web/src/lib/stores/preferences.store.ts index de80702b95406..ead30bf9f4e63 100644 --- a/web/src/lib/stores/preferences.store.ts +++ b/web/src/lib/stores/preferences.store.ts @@ -91,6 +91,14 @@ export interface AlbumViewSettings { }; } +export interface PlacesViewSettings { + groupBy: string; + collapsedGroups: { + // Grouping Option => Array + [group: string]: string[]; + }; +} + export interface SidebarSettings { people: boolean; sharing: boolean; @@ -137,6 +145,16 @@ export const albumViewSettings = persisted('album-view-settin collapsedGroups: {}, }); +export enum PlacesGroupBy { + None = 'None', + Country = 'Country', +} + +export const placesViewSettings = persisted('places-view-settings', { + groupBy: PlacesGroupBy.None, + collapsedGroups: {}, +}); + export const showDeleteModal = persisted('delete-confirm-dialog', true, {}); export const alwaysLoadOriginalFile = persisted('always-load-original-file', false, {}); diff --git a/web/src/lib/utils/places-utils.ts b/web/src/lib/utils/places-utils.ts new file mode 100644 index 0000000000000..625f42d147a89 --- /dev/null +++ b/web/src/lib/utils/places-utils.ts @@ -0,0 +1,95 @@ +import { PlacesGroupBy, placesViewSettings, type PlacesViewSettings } from '$lib/stores/preferences.store'; +import { type AssetResponseDto } from '@immich/sdk'; +import { get } from 'svelte/store'; + +/** + * -------------- + * Places Grouping + * -------------- + */ +export interface PlacesGroup { + id: string; + name: string; + places: AssetResponseDto[]; +} + +export interface PlacesGroupOptionMetadata { + id: PlacesGroupBy; + isDisabled: () => boolean; +} + +export const groupOptionsMetadata: PlacesGroupOptionMetadata[] = [ + { + id: PlacesGroupBy.None, + isDisabled: () => false, + }, + { + id: PlacesGroupBy.Country, + isDisabled: () => false, + }, +]; + +export const findGroupOptionMetadata = (groupBy: string) => { + // Default is no grouping + const defaultGroupOption = groupOptionsMetadata[0]; + return groupOptionsMetadata.find(({ id }) => groupBy === id) ?? defaultGroupOption; +}; + +export const getSelectedPlacesGroupOption = (settings: PlacesViewSettings) => { + const defaultGroupOption = PlacesGroupBy.None; + const albumGroupOption = settings.groupBy ?? defaultGroupOption; + + if (findGroupOptionMetadata(albumGroupOption).isDisabled()) { + return defaultGroupOption; + } + return albumGroupOption; +}; + +/** + * ---------------------------- + * Places Groups Collapse/Expand + * ---------------------------- + */ +const getCollapsedPlacesGroups = (settings: PlacesViewSettings) => { + settings.collapsedGroups ??= {}; + const { collapsedGroups, groupBy } = settings; + collapsedGroups[groupBy] ??= []; + return collapsedGroups[groupBy]; +}; + +export const isPlacesGroupCollapsed = (settings: PlacesViewSettings, groupId: string) => { + if (settings.groupBy === PlacesGroupBy.None) { + return false; + } + return getCollapsedPlacesGroups(settings).includes(groupId); +}; + +export const togglePlacesGroupCollapsing = (groupId: string) => { + const settings = get(placesViewSettings); + if (settings.groupBy === PlacesGroupBy.None) { + return; + } + const collapsedGroups = getCollapsedPlacesGroups(settings); + const groupIndex = collapsedGroups.indexOf(groupId); + if (groupIndex === -1) { + // Collapse + collapsedGroups.push(groupId); + } else { + // Expand + collapsedGroups.splice(groupIndex, 1); + } + placesViewSettings.set(settings); +}; + +export const collapseAllPlacesGroups = (groupIds: string[]) => { + placesViewSettings.update((settings) => { + const collapsedGroups = getCollapsedPlacesGroups(settings); + collapsedGroups.length = 0; + collapsedGroups.push(...groupIds); + return settings; + }); +}; + +export const expandAllPlacesGroups = () => { + collapseAllPlacesGroups([]); +}; diff --git a/web/src/routes/(user)/places/+page.svelte b/web/src/routes/(user)/places/+page.svelte index 28c8e95cb1331..ca8b0f55b2d67 100644 --- a/web/src/routes/(user)/places/+page.svelte +++ b/web/src/routes/(user)/places/+page.svelte @@ -1,13 +1,12 @@ - - {#if hasPlaces} -
- {#each places as item (item.id)} - {@const city = item.exifInfo.city} - -
- {city} -
- - {city} - -
- {/each} -
- {:else} -
-
- -

{$t('no_places')}

-
-
- {/if} + +
+ +
+ +
From 35dbcd288eea4055270e312ca73a65b2fc78720f Mon Sep 17 00:00:00 2001 From: Krassimir Valev Date: Sun, 1 Sep 2024 23:18:06 +0200 Subject: [PATCH 2/2] add english translations --- web/src/lib/components/places-page/places-card-group.svelte | 2 +- web/src/lib/components/places-page/places-list.svelte | 1 - web/src/lib/i18n/en.json | 4 ++++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/web/src/lib/components/places-page/places-card-group.svelte b/web/src/lib/components/places-page/places-card-group.svelte index d3b3a00cbeefd..394f1fa0770e1 100644 --- a/web/src/lib/components/places-page/places-card-group.svelte +++ b/web/src/lib/components/places-page/places-card-group.svelte @@ -31,7 +31,7 @@ class="inline-block -mt-2.5 transition-all duration-[250ms] {iconRotation}" /> {group.name} - ({$t('places', { values: { count: places.length } })}) + ({$t('places_count', { values: { count: places.length } })})
diff --git a/web/src/lib/components/places-page/places-list.svelte b/web/src/lib/components/places-page/places-list.svelte index 3a846683f1b60..6ba8c2b5c47eb 100644 --- a/web/src/lib/components/places-page/places-list.svelte +++ b/web/src/lib/components/places-page/places-list.svelte @@ -36,7 +36,6 @@ /** Group by year */ [PlacesGroupBy.Country]: (places): PlacesGroup[] => { - // item.exifInfo?.city const unknownCountry = $t('unknown_country'); const groupedByCountry = groupBy(places, (place) => { diff --git a/web/src/lib/i18n/en.json b/web/src/lib/i18n/en.json index d0d330480ab2c..06d9bc5d65639 100644 --- a/web/src/lib/i18n/en.json +++ b/web/src/lib/i18n/en.json @@ -720,8 +720,10 @@ "go_back": "Go back", "go_to_search": "Go to search", "group_albums_by": "Group albums by...", + "group_country": "Group by country", "group_no": "No grouping", "group_owner": "Group by owner", + "group_places_by": "Group places by...", "group_year": "Group by year", "has_quota": "Has quota", "hi_user": "Hi {name} ({email})", @@ -934,6 +936,7 @@ "pick_a_location": "Pick a location", "place": "Place", "places": "Places", + "places_count": "{count, plural, one {{count, number} Place} other {{count, number} Places}}", "play": "Play", "play_memories": "Play memories", "play_motion_photo": "Play Motion Photo", @@ -1208,6 +1211,7 @@ "unfavorite": "Unfavorite", "unhide_person": "Unhide person", "unknown": "Unknown", + "unknown_country": "Unknown Country", "unknown_year": "Unknown Year", "unlimited": "Unlimited", "unlink_oauth": "Unlink OAuth",