diff --git a/src/backend/middlewares/GalleryMWs.ts b/src/backend/middlewares/GalleryMWs.ts index 008840f3..82b41a25 100644 --- a/src/backend/middlewares/GalleryMWs.ts +++ b/src/backend/middlewares/GalleryMWs.ts @@ -84,7 +84,7 @@ export class GalleryMWs { res: Response, next: NextFunction ): Promise { - if (Config.Gallery.enableDownloadZip === false) { + if (Config.Gallery.NavBar.enableDownloadZip === false) { return next(); } const directoryName = req.params['directory'] || '/'; diff --git a/src/common/config/public/ClientConfig.ts b/src/common/config/public/ClientConfig.ts index fc6c1576..27463c6d 100644 --- a/src/common/config/public/ClientConfig.ts +++ b/src/common/config/public/ClientConfig.ts @@ -68,10 +68,10 @@ export class AutoCompleteItemsPerCategoryConfig { @ConfigProperty({ type: 'unsignedInt', tags: - { - name: $localize`Maximum items`, - priority: ConfigPriority.underTheHood - }, + { + name: $localize`Maximum items`, + priority: ConfigPriority.underTheHood + }, description: $localize`Maximum number autocomplete items shown at once. If there is not enough items to reach this value, it takes upto double of the individual items.` }) maxItems: number = 20; @@ -79,10 +79,10 @@ export class AutoCompleteItemsPerCategoryConfig { @ConfigProperty({ type: 'unsignedInt', tags: - { - name: $localize`Max photo items`, - priority: ConfigPriority.underTheHood - }, + { + name: $localize`Max photo items`, + priority: ConfigPriority.underTheHood + }, description: $localize`Maximum number autocomplete items shown per photo category.` }) fileName: number = 2; @@ -90,10 +90,10 @@ export class AutoCompleteItemsPerCategoryConfig { @ConfigProperty({ type: 'unsignedInt', tags: - { - name: $localize`Max directory items`, - priority: ConfigPriority.underTheHood - }, + { + name: $localize`Max directory items`, + priority: ConfigPriority.underTheHood + }, description: $localize`Maximum number autocomplete items shown per directory category.` }) directory: number = 2; @@ -101,10 +101,10 @@ export class AutoCompleteItemsPerCategoryConfig { @ConfigProperty({ type: 'unsignedInt', tags: - { - name: $localize`Max caption items`, - priority: ConfigPriority.underTheHood - }, + { + name: $localize`Max caption items`, + priority: ConfigPriority.underTheHood + }, description: $localize`Maximum number autocomplete items shown per caption category.` }) caption: number = 3; @@ -112,10 +112,10 @@ export class AutoCompleteItemsPerCategoryConfig { @ConfigProperty({ type: 'unsignedInt', tags: - { - name: $localize`Max position items`, - priority: ConfigPriority.underTheHood - }, + { + name: $localize`Max position items`, + priority: ConfigPriority.underTheHood + }, description: $localize`Maximum number autocomplete items shown per position category.` }) position: number = 3; @@ -123,10 +123,10 @@ export class AutoCompleteItemsPerCategoryConfig { @ConfigProperty({ type: 'unsignedInt', tags: - { - name: $localize`Max faces items`, - priority: ConfigPriority.underTheHood - }, + { + name: $localize`Max faces items`, + priority: ConfigPriority.underTheHood + }, description: $localize`Maximum number autocomplete items shown per faces category.` }) person: number = 5; @@ -134,10 +134,10 @@ export class AutoCompleteItemsPerCategoryConfig { @ConfigProperty({ type: 'unsignedInt', tags: - { - name: $localize`Max keyword items`, - priority: ConfigPriority.underTheHood - }, + { + name: $localize`Max keyword items`, + priority: ConfigPriority.underTheHood + }, description: $localize`Maximum number autocomplete items shown per keyword category.` }) keyword: number = 5; @@ -147,20 +147,20 @@ export class AutoCompleteItemsPerCategoryConfig { export class AutoCompleteConfig { @ConfigProperty({ tags: - { - name: $localize`Enable Autocomplete`, - priority: ConfigPriority.advanced - }, + { + name: $localize`Enable Autocomplete`, + priority: ConfigPriority.advanced + }, description: $localize`Show hints while typing search query.` }) enabled: boolean = true; @ConfigProperty({ tags: - { - name: $localize`Max items per category`, - priority: ConfigPriority.underTheHood - }, + { + name: $localize`Max items per category`, + priority: ConfigPriority.underTheHood + }, description: $localize`Maximum number autocomplete items shown per category.` }) ItemsPerCategory: AutoCompleteItemsPerCategoryConfig = new AutoCompleteItemsPerCategoryConfig(); @@ -168,11 +168,11 @@ export class AutoCompleteConfig { @ConfigProperty({ type: 'unsignedInt', tags: - { - name: $localize`Cache timeout`, - priority: ConfigPriority.underTheHood, - unit: 'ms' - }, + { + name: $localize`Cache timeout`, + priority: ConfigPriority.underTheHood, + unit: 'ms' + }, description: $localize`Autocomplete cache timeout. ` }) cacheTimeout: number = 1000 * 60 * 60; @@ -182,66 +182,66 @@ export class AutoCompleteConfig { export class ClientSearchConfig { @ConfigProperty({ tags: - { - name: $localize`Enable`, - priority: ConfigPriority.advanced - }, + { + name: $localize`Enable`, + priority: ConfigPriority.advanced + }, description: $localize`Enables searching.` }) enabled: boolean = true; @ConfigProperty({ type: 'unsignedInt', tags: - { - name: $localize`Cache timeout`, - priority: ConfigPriority.underTheHood, - unit: 'ms' - }, + { + name: $localize`Cache timeout`, + priority: ConfigPriority.underTheHood, + unit: 'ms' + }, description: $localize`Search cache timeout.` }) searchCacheTimeout: number = 1000 * 60 * 60; @ConfigProperty({ tags: - { - name: $localize`Autocomplete`, - priority: ConfigPriority.advanced - }, + { + name: $localize`Autocomplete`, + priority: ConfigPriority.advanced + }, }) AutoComplete: AutoCompleteConfig = new AutoCompleteConfig(); @ConfigProperty({ type: 'unsignedInt', tags: - { - name: $localize`Maximum media result`, - priority: ConfigPriority.advanced - }, + { + name: $localize`Maximum media result`, + priority: ConfigPriority.advanced + }, description: $localize`Maximum number of photos and videos that are listed in one search result.` }) maxMediaResult: number = 10000; @ConfigProperty({ type: 'unsignedInt', tags: - { - name: $localize`Maximum directory result`, - priority: ConfigPriority.advanced - }, + { + name: $localize`Maximum directory result`, + priority: ConfigPriority.advanced + }, description: $localize`Maximum number of directories that are listed in one search result.` }) maxDirectoryResult: number = 200; @ConfigProperty({ tags: - { - name: $localize`List directories`, - priority: ConfigPriority.advanced - }, + { + name: $localize`List directories`, + priority: ConfigPriority.advanced + }, description: $localize`Search returns also with directories, not just media.` }) listDirectories: boolean = false; @ConfigProperty({ tags: - { - name: $localize`List metafiles`, - priority: ConfigPriority.advanced - }, + { + name: $localize`List metafiles`, + priority: ConfigPriority.advanced + }, description: $localize`Search also returns with metafiles from directories that contain a media file of the matched search result.`, }) listMetafiles: boolean = true; @@ -251,10 +251,10 @@ export class ClientSearchConfig { export class ClientAlbumConfig { @ConfigProperty({ tags: - { - name: $localize`Enable`, - priority: ConfigPriority.advanced - } + { + name: $localize`Enable`, + priority: ConfigPriority.advanced + } }) enabled: boolean = true; } @@ -263,19 +263,19 @@ export class ClientAlbumConfig { export class ClientSharingConfig { @ConfigProperty({ tags: - { - name: $localize`Enable`, - priority: ConfigPriority.advanced - }, + { + name: $localize`Enable`, + priority: ConfigPriority.advanced + }, description: $localize`Enables sharing.`, }) enabled: boolean = true; @ConfigProperty({ tags: - { - name: $localize`Password protected`, - priority: ConfigPriority.advanced - }, + { + name: $localize`Password protected`, + priority: ConfigPriority.advanced + }, description: $localize`Enables password protected sharing links.`, }) passwordProtected: boolean = true; @@ -285,10 +285,10 @@ export class ClientSharingConfig { export class ClientRandomPhotoConfig { @ConfigProperty({ tags: - { - name: $localize`Enable`, - priority: ConfigPriority.advanced - }, + { + name: $localize`Enable`, + priority: ConfigPriority.advanced + }, description: $localize`Enables random link generation.`, }) enabled: boolean = true; @@ -298,26 +298,26 @@ export class ClientRandomPhotoConfig { export class MapLayers { @ConfigProperty({ tags: - { - priority: ConfigPriority.advanced - }, + { + priority: ConfigPriority.advanced + }, description: $localize`Name of a map layer.`, }) name: string = 'street'; @ConfigProperty({ tags: - { - priority: ConfigPriority.advanced, - hint: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' - }, + { + priority: ConfigPriority.advanced, + hint: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' + }, description: $localize`Url of a map layer.`, }) url: string = ''; @ConfigProperty({ tags: - { - priority: ConfigPriority.advanced, - }, + { + priority: ConfigPriority.advanced, + }, description: $localize`Sets if the layer is dark (used as default in the dark mode).`, }) darkLayer: boolean = false; @@ -485,11 +485,11 @@ export class ClientMapConfig { mapProvider: MapProviders = MapProviders.OpenStreetMap; @ConfigProperty({ tags: - { - name: $localize`Mapbox access token`, - relevant: (c: any) => c.mapProvider === MapProviders.Mapbox, - priority: ConfigPriority.advanced - }, + { + name: $localize`Mapbox access token`, + relevant: (c: any) => c.mapProvider === MapProviders.Mapbox, + priority: ConfigPriority.advanced + }, description: $localize`MapBox needs an access token to work, create one at https://www.mapbox.com.`, }) mapboxAccessToken: string = ''; @@ -527,62 +527,62 @@ export class ClientMapConfig { }) MapPathGroupConfig: MapPathGroupConfig[] = [ new MapPathGroupConfig('Transportation', - [new MapPathGroupThemeConfig( - ['flight', 'flying'], - new PathThemeConfig('var(--bs-orange)', - '4 8', - new SVGIconConfig('0 0 567 512', '') - ) - ), new MapPathGroupThemeConfig( - ['drive', 'driving'], - new PathThemeConfig('var(--bs-orange)', - '4 8', - new SVGIconConfig('0 0 640 512', '') - ) - ), new MapPathGroupThemeConfig( - ['ship', 'sailing', 'cruise'], - new PathThemeConfig('var(--bs-orange)', - '4 8', - new SVGIconConfig('0 0 576 512', '') - ) - )]), + [new MapPathGroupThemeConfig( + ['flight', 'flying'], + new PathThemeConfig('var(--bs-orange)', + '4 8', + new SVGIconConfig('0 0 567 512', '') + ) + ), new MapPathGroupThemeConfig( + ['drive', 'driving'], + new PathThemeConfig('var(--bs-orange)', + '4 8', + new SVGIconConfig('0 0 640 512', '') + ) + ), new MapPathGroupThemeConfig( + ['ship', 'sailing', 'cruise'], + new PathThemeConfig('var(--bs-orange)', + '4 8', + new SVGIconConfig('0 0 576 512', '') + ) + )]), new MapPathGroupConfig('Sport', - [new MapPathGroupThemeConfig( - ['run'], - new PathThemeConfig('var(--bs-primary)', - '', - new SVGIconConfig('0 0 417 512', '') - ) - ), new MapPathGroupThemeConfig( - ['walk'], - new PathThemeConfig('var(--bs-primary)', - '', - new SVGIconConfig('0 0 320 512', '') - ) - ), new MapPathGroupThemeConfig( - ['hike', 'hiking'], - new PathThemeConfig('var(--bs-primary)', - '', - new SVGIconConfig('0 0 384 512', '') - ) - ), new MapPathGroupThemeConfig( - ['bike', 'biking', 'cycling'], - new PathThemeConfig('var(--bs-primary)', - '', - new SVGIconConfig('0 0 640 512', '') - ) - ), new MapPathGroupThemeConfig( - ['skiing', 'ski'], - new PathThemeConfig('var(--bs-primary)', - '', - new SVGIconConfig('0 0 512 512', '') - ) - )]), + [new MapPathGroupThemeConfig( + ['run'], + new PathThemeConfig('var(--bs-primary)', + '', + new SVGIconConfig('0 0 417 512', '') + ) + ), new MapPathGroupThemeConfig( + ['walk'], + new PathThemeConfig('var(--bs-primary)', + '', + new SVGIconConfig('0 0 320 512', '') + ) + ), new MapPathGroupThemeConfig( + ['hike', 'hiking'], + new PathThemeConfig('var(--bs-primary)', + '', + new SVGIconConfig('0 0 384 512', '') + ) + ), new MapPathGroupThemeConfig( + ['bike', 'biking', 'cycling'], + new PathThemeConfig('var(--bs-primary)', + '', + new SVGIconConfig('0 0 640 512', '') + ) + ), new MapPathGroupThemeConfig( + ['skiing', 'ski'], + new PathThemeConfig('var(--bs-primary)', + '', + new SVGIconConfig('0 0 512 512', '') + ) + )]), new MapPathGroupConfig('Other paths', - [new MapPathGroupThemeConfig( - [], // Match all - new PathThemeConfig('var(--bs-secondary)') - )]) + [new MapPathGroupThemeConfig( + [], // Match all + new PathThemeConfig('var(--bs-secondary)') + )]) ]; @ConfigProperty({ type: 'positiveFloat', @@ -702,8 +702,138 @@ export class NavigationLinkConfig { } } + +@SubConfigClass({tags: {client: true}, softReadonly: true}) +export class ClientSortingConfig implements SortingMethod { + constructor(method: number = SortByTypes.Date, ascending: boolean = true) { + this.method = method; + this.ascending = ascending; + } + + @ConfigProperty({ + type: SortByTypes, + tags: { + name: $localize`Method`, + }, + }) + method: number = SortByTypes.Date; + + @ConfigProperty({ + tags: { + name: $localize`Ascending`, + }, + }) + ascending: boolean = true; +} + + +@SubConfigClass({tags: {client: true}, softReadonly: true}) +export class ClientGroupingConfig implements GroupingMethod { + constructor(method: number = GroupByTypes.Date, ascending: boolean = true) { + this.method = method; + this.ascending = ascending; + } + + @ConfigProperty({ + type: GroupByTypes, + tags: { + name: $localize`Method`, + }, + }) + method: number = GroupByTypes.Date; + + @ConfigProperty({ + tags: { + name: $localize`Ascending`, + }, + }) + ascending: boolean = true; +} + + +@SubConfigClass({tags: {client: true}, softReadonly: true}) +export class NavBarSortingAndGroupingConfig { + + @ConfigProperty({ + type: ClientSortingConfig, + tags: { + name: $localize`Default sorting`, + priority: ConfigPriority.advanced, + }, + description: $localize`Default sorting method for photo and video in a directory results.` + }) + defaultPhotoSortingMethod: ClientSortingConfig = new ClientSortingConfig(SortByTypes.Date, true); + + @ConfigProperty({ + type: ClientSortingConfig, + tags: { + name: $localize`Default search sorting`, + priority: ConfigPriority.advanced, + }, + description: $localize`Default sorting method for photo and video in a search results.` + }) + defaultSearchSortingMethod: ClientSortingConfig = new ClientSortingConfig(SortByTypes.Date, false); + + @ConfigProperty({ + type: ClientGroupingConfig, + tags: { + name: $localize`Default grouping`, + githubIssue: 706, + priority: ConfigPriority.advanced, + } as TAGS, + description: $localize`Default grouping method for photo and video in a directory results.` + }) + defaultPhotoGroupingMethod: ClientGroupingConfig = new ClientGroupingConfig(GroupByTypes.Date, true); + + @ConfigProperty({ + type: ClientGroupingConfig, + tags: { + name: $localize`Default search grouping`, + githubIssue: 706, + priority: ConfigPriority.advanced, + }, + description: $localize`Default grouping method for photo and video in a search results.` + }) + defaultSearchGroupingMethod: ClientGroupingConfig = new ClientGroupingConfig(GroupByTypes.Date, false); + +} + @SubConfigClass({tags: {client: true}, softReadonly: true}) export class NavBarConfig { + @ConfigProperty({ + tags: { + name: $localize`Download Zip`, + priority: ConfigPriority.advanced, + experimental: true, + githubIssue: 52 + }, + description: $localize`Enable download zip of a directory contents Directory flattening. (Does not work for searches.)` + }) + enableDownloadZip: boolean = false; + + @ConfigProperty({ + tags: { + name: $localize`Directory flattening`, + priority: ConfigPriority.advanced, + experimental: true, + githubIssue: 174 + }, + description: $localize`Adds a button to flattens the file structure, by listing the content of all subdirectories. (Won't work if the gallery has multiple folders with the same path.)` + }) + enableDirectoryFlattening: boolean = false; + + @ConfigProperty({ + type: GridSizes, + tags: { + name: $localize`Default grid size`, + githubIssue: 716, + priority: ConfigPriority.advanced, + }, + description: $localize`Default grid size that is used to render photos and videos.` + }) + defaultGidSize: GridSizes = GridSizes.medium; + + @ConfigProperty({ tags: { name: $localize`Show item count`, @@ -712,6 +842,7 @@ export class NavBarConfig { description: $localize`Shows the number photos and videos on the navigation bar.`, }) showItemCount: boolean = true; + @ConfigProperty({ arrayType: NavigationLinkConfig, tags: { @@ -755,6 +886,16 @@ export class NavBarConfig { }) showScrollUpButton: ScrollUpModes = ScrollUpModes.mobileOnly; + + @ConfigProperty({ + type: NavBarSortingAndGroupingConfig, + tags: { + name: $localize`Sorting and grouping`, + priority: ConfigPriority.advanced, + }, + }) + SortingGrouping: NavBarSortingAndGroupingConfig = new NavBarSortingAndGroupingConfig(); + } @SubConfigClass({tags: {client: true}, softReadonly: true}) @@ -866,65 +1007,20 @@ export class ThemesConfig { }) availableThemes: ThemeConfig[] = [ new ThemeConfig( - 'classic', - ':root nav.navbar {\n' + - '--bs-navbar-color: rgba(255, 255, 255, 0.55);\n' + - '--bs-navbar-hover-color: rgba(255, 255, 255, 0.75);\n' + - '--bs-navbar-disabled-color: rgba(255, 255, 255, 0.25);\n' + - '--bs-navbar-active-color: #fff;\n' + - '--bs-navbar-brand-color: #fff;\n' + - '--bs-navbar-brand-hover-color: #fff;\n' + - '--bs-bg-opacity: 1;\n' + - 'background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important;\n' + - '}' + 'classic', + ':root nav.navbar {\n' + + '--bs-navbar-color: rgba(255, 255, 255, 0.55);\n' + + '--bs-navbar-hover-color: rgba(255, 255, 255, 0.75);\n' + + '--bs-navbar-disabled-color: rgba(255, 255, 255, 0.25);\n' + + '--bs-navbar-active-color: #fff;\n' + + '--bs-navbar-brand-color: #fff;\n' + + '--bs-navbar-brand-hover-color: #fff;\n' + + '--bs-bg-opacity: 1;\n' + + 'background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important;\n' + + '}' )]; } -@SubConfigClass({tags: {client: true}, softReadonly: true}) -export class ClientSortingConfig implements SortingMethod { - constructor(method: number = SortByTypes.Date, ascending: boolean = true) { - this.method = method; - this.ascending = ascending; - } - - @ConfigProperty({ - type: SortByTypes, - tags: { - name: $localize`Method`, - }, - }) - method: number = SortByTypes.Date; - - @ConfigProperty({ - tags: { - name: $localize`Ascending`, - }, - }) - ascending: boolean = true; -} - -@SubConfigClass({tags: {client: true}, softReadonly: true}) -export class ClientGroupingConfig implements GroupingMethod { - constructor(method: number = GroupByTypes.Date, ascending: boolean = true) { - this.method = method; - this.ascending = ascending; - } - - @ConfigProperty({ - type: GroupByTypes, - tags: { - name: $localize`Method`, - }, - }) - method: number = GroupByTypes.Date; - - @ConfigProperty({ - tags: { - name: $localize`Ascending`, - }, - }) - ascending: boolean = true; -} @SubConfigClass({tags: {client: true}, softReadonly: true}) export class ClientGalleryConfig { @@ -945,59 +1041,6 @@ export class ClientGalleryConfig { }) enableOnScrollRendering: boolean = true; - @ConfigProperty({ - type: ClientSortingConfig, - tags: { - name: $localize`Default sorting`, - priority: ConfigPriority.advanced, - }, - description: $localize`Default sorting method for photo and video in a directory results.` - }) - defaultPhotoSortingMethod: ClientSortingConfig = new ClientSortingConfig(SortByTypes.Date, true); - - @ConfigProperty({ - type: ClientSortingConfig, - tags: { - name: $localize`Default search sorting`, - priority: ConfigPriority.advanced, - }, - description: $localize`Default sorting method for photo and video in a search results.` - }) - defaultSearchSortingMethod: ClientSortingConfig = new ClientSortingConfig(SortByTypes.Date, false); - - @ConfigProperty({ - type: ClientGroupingConfig, - tags: { - name: $localize`Default grouping`, - githubIssue: 706, - priority: ConfigPriority.advanced, - } as TAGS, - description: $localize`Default grouping method for photo and video in a directory results.` - }) - defaultPhotoGroupingMethod: ClientGroupingConfig = new ClientGroupingConfig(GroupByTypes.Date, true); - - @ConfigProperty({ - type: ClientGroupingConfig, - tags: { - name: $localize`Default search grouping`, - githubIssue: 706, - priority: ConfigPriority.advanced, - }, - description: $localize`Default grouping method for photo and video in a search results.` - }) - defaultSearchGroupingMethod: ClientGroupingConfig = new ClientGroupingConfig(GroupByTypes.Date, false); - - @ConfigProperty({ - type: GridSizes, - tags: { - name: $localize`Default grid size`, - githubIssue: 716, - priority: ConfigPriority.advanced, - }, - description: $localize`Default grid size that is used to render photos and videos.` - }) - defaultGidSize: GridSizes = GridSizes.medium; - @ConfigProperty({ tags: { name: $localize`Sort directories by date`, @@ -1018,6 +1061,7 @@ export class ClientGalleryConfig { @ConfigProperty({ tags: { name: $localize`Navigation bar`, + uiIcon: 'ionMenuOutline', priority: ConfigPriority.advanced, } as TAGS }) @@ -1030,26 +1074,7 @@ export class ClientGalleryConfig { description: $localize`Show the caption (IPTC 120) tags from the EXIF data instead of the filenames.` }) captionFirstNaming: boolean = false; // shows the caption instead of the filename in the photo grid - @ConfigProperty({ - tags: { - name: $localize`Download Zip`, - priority: ConfigPriority.advanced, - experimental: true, - githubIssue: 52 - }, - description: $localize`Enable download zip of a directory contents Directory flattening. (Does not work for searches.)` - }) - enableDownloadZip: boolean = false; - @ConfigProperty({ - tags: { - name: $localize`Directory flattening`, - priority: ConfigPriority.advanced, - experimental: true, - githubIssue: 174 - }, - description: $localize`Adds a button to flattens the file structure, by listing the content of all subdirectories. (Won't work if the gallery has multiple folders with the same path.)` - }) - enableDirectoryFlattening: boolean = false; + @ConfigProperty({ tags: { @@ -1141,7 +1166,7 @@ export class ClientPhotoConvertingConfig { name: $localize`Load full resolution image on zoom.`, priority: ConfigPriority.advanced, uiDisabled: (sc: ClientPhotoConvertingConfig) => - !sc.enabled + !sc.enabled }, description: $localize`Enables loading the full resolution image on zoom in the ligthbox (preview).`, }) @@ -1366,7 +1391,7 @@ export class ClientServiceConfig { } as TAGS, description: $localize`Sets the icon of the app`, }) - // source: https://icons.getbootstrap.com/ + // source: https://icons.getbootstrap.com/ svgIcon: SVGIconConfig = new SVGIconConfig(`0 0 512 512`, ''); } diff --git a/src/frontend/app/ui/gallery/navigator/navigator.gallery.component.html b/src/frontend/app/ui/gallery/navigator/navigator.gallery.component.html index dde32229..cb68261c 100644 --- a/src/frontend/app/ui/gallery/navigator/navigator.gallery.component.html +++ b/src/frontend/app/ui/gallery/navigator/navigator.gallery.component.html @@ -26,7 +26,7 @@
 
- + @@ -34,7 +34,7 @@
 
- + diff --git a/src/frontend/app/ui/gallery/navigator/navigator.service.ts b/src/frontend/app/ui/gallery/navigator/navigator.service.ts index 63eee196..407a22da 100644 --- a/src/frontend/app/ui/gallery/navigator/navigator.service.ts +++ b/src/frontend/app/ui/gallery/navigator/navigator.service.ts @@ -55,7 +55,7 @@ export class GalleryNavigatorService { getDefaultGridSize(): GridSizes { - return Config.Gallery.defaultGidSize; + return Config.Gallery.NavBar.defaultGidSize; } } diff --git a/src/frontend/app/ui/gallery/navigator/sorting.service.ts b/src/frontend/app/ui/gallery/navigator/sorting.service.ts index f7287970..3c0daaa6 100644 --- a/src/frontend/app/ui/gallery/navigator/sorting.service.ts +++ b/src/frontend/app/ui/gallery/navigator/sorting.service.ts @@ -22,16 +22,22 @@ export class GallerySortingService { private collator = new Intl.Collator(undefined, {numeric: true}); constructor( - private galleryCacheService: GalleryCacheService, - private galleryService: ContentLoaderService, - private rndService: SeededRandomService, - private datePipe: DatePipe + private galleryCacheService: GalleryCacheService, + private galleryService: ContentLoaderService, + private rndService: SeededRandomService, + private datePipe: DatePipe ) { this.sorting = new BehaviorSubject( - {method: Config.Gallery.defaultPhotoSortingMethod.method, ascending: Config.Gallery.defaultPhotoSortingMethod.ascending} + { + method: Config.Gallery.NavBar.SortingGrouping.defaultPhotoSortingMethod.method, + ascending: Config.Gallery.NavBar.SortingGrouping.defaultPhotoSortingMethod.ascending + } ); this.grouping = new BehaviorSubject( - {method: Config.Gallery.defaultPhotoGroupingMethod.method, ascending: Config.Gallery.defaultPhotoGroupingMethod.ascending} + { + method: Config.Gallery.NavBar.SortingGrouping.defaultPhotoGroupingMethod.method, + ascending: Config.Gallery.NavBar.SortingGrouping.defaultPhotoGroupingMethod.ascending + } ); this.galleryService.content.subscribe((c) => { if (c) { @@ -57,7 +63,7 @@ export class GallerySortingService { const s = this.sorting.value; const g = this.grouping.value; return s.method === defS.method && s.ascending === defS.ascending && - g.method === defG.method && g.ascending === defG.ascending; + g.method === defG.method && g.ascending === defG.ascending; } getDefaultSorting(cw: ContentWrapper): SortingMethod { @@ -69,33 +75,33 @@ export class GallerySortingService { } } if (cw.searchResult) { - return Config.Gallery.defaultSearchSortingMethod; + return Config.Gallery.NavBar.SortingGrouping.defaultSearchSortingMethod; } - return Config.Gallery.defaultPhotoSortingMethod; + return Config.Gallery.NavBar.SortingGrouping.defaultPhotoSortingMethod; } getDefaultGrouping(cw: ContentWrapper): GroupingMethod { if (cw.searchResult) { - return Config.Gallery.defaultSearchGroupingMethod; + return Config.Gallery.NavBar.SortingGrouping.defaultSearchGroupingMethod; } - return Config.Gallery.defaultPhotoGroupingMethod; + return Config.Gallery.NavBar.SortingGrouping.defaultPhotoGroupingMethod; } setSorting(sorting: SortingMethod): void { this.sorting.next(sorting); if (this.galleryService.content.value) { if ( - sorting !== - this.getDefaultSorting(this.galleryService.content.value) + sorting !== + this.getDefaultSorting(this.galleryService.content.value) ) { this.galleryCacheService.setSorting( - this.galleryService.content.value, - sorting + this.galleryService.content.value, + sorting ); } else { this.galleryCacheService.removeSorting( - this.galleryService.content.value + this.galleryService.content.value ); } } @@ -105,16 +111,16 @@ export class GallerySortingService { this.grouping.next(grouping); if (this.galleryService.content.value) { if ( - grouping !== - this.getDefaultGrouping(this.galleryService.content.value) + grouping !== + this.getDefaultGrouping(this.galleryService.content.value) ) { this.galleryCacheService.setGrouping( - this.galleryService.content.value, - grouping + this.galleryService.content.value, + grouping ); } else { this.galleryCacheService.removeGrouping( - this.galleryService.content.value + this.galleryService.content.value ); } } @@ -127,7 +133,7 @@ export class GallerySortingService { switch (sorting.method) { case SortByTypes.Name: media.sort((a: PhotoDTO, b: PhotoDTO) => - this.collator.compare(a.name, b.name) + this.collator.compare(a.name, b.name) ); break; case SortByTypes.Date: @@ -137,20 +143,20 @@ export class GallerySortingService { break; case SortByTypes.Rating: media.sort( - (a: PhotoDTO, b: PhotoDTO) => - (a.metadata.rating || 0) - (b.metadata.rating || 0) + (a: PhotoDTO, b: PhotoDTO) => + (a.metadata.rating || 0) - (b.metadata.rating || 0) ); break; case SortByTypes.PersonCount: media.sort( - (a: PhotoDTO, b: PhotoDTO) => - (a.metadata?.faces?.length || 0) - (b.metadata?.faces?.length || 0) + (a: PhotoDTO, b: PhotoDTO) => + (a.metadata?.faces?.length || 0) - (b.metadata?.faces?.length || 0) ); break; case SortByTypes.FileSize: media.sort( - (a: PhotoDTO, b: PhotoDTO) => - (a.metadata?.fileSize || 0) - (b.metadata?.fileSize || 0) + (a: PhotoDTO, b: PhotoDTO) => + (a.metadata?.fileSize || 0) - (b.metadata?.fileSize || 0) ); break; case SortByTypes.Random: @@ -164,9 +170,9 @@ export class GallerySortingService { } return 0; }) - .sort((): number => { - return this.rndService.get() - 0.5; - }); + .sort((): number => { + return this.rndService.get() - 0.5; + }); break; } if (!sorting.ascending) { @@ -208,103 +214,103 @@ export class GallerySortingService { } public applySorting( - directoryContent: Observable + directoryContent: Observable ): Observable { return directoryContent.pipe( - switchMap((dirContent) => { - return this.grouping.pipe( - switchMap((grouping) => { - return this.sorting.pipe( - map((sorting) => { - if (!dirContent) { - return null; - } - const c: GroupedDirectoryContent = { - mediaGroups: [], - directories: dirContent.directories, - metaFile: dirContent.metaFile, - }; - if (c.directories) { - switch (sorting.method) { - case SortByTypes.FileSize: - case SortByTypes.PersonCount: - case SortByTypes.Rating: // directories do not have rating - case SortByTypes.Name: - c.directories.sort((a, b) => - this.collator.compare(a.name, b.name) - ); - break; - case SortByTypes.Date: - if ( - Config.Gallery.enableDirectorySortingByDate === true - ) { - c.directories.sort( - (a, b) => (a.oldestMedia || a.lastModified) - (b.oldestMedia || b.lastModified) - ); - break; + switchMap((dirContent) => { + return this.grouping.pipe( + switchMap((grouping) => { + return this.sorting.pipe( + map((sorting) => { + if (!dirContent) { + return null; } - c.directories.sort((a, b) => - this.collator.compare(a.name, b.name) - ); - break; - case SortByTypes.Random: - this.rndService.setSeed(c.directories.length); - c.directories - .sort((a, b): number => { - if (a.name.toLowerCase() < b.name.toLowerCase()) { - return 1; + const c: GroupedDirectoryContent = { + mediaGroups: [], + directories: dirContent.directories, + metaFile: dirContent.metaFile, + }; + if (c.directories) { + switch (sorting.method) { + case SortByTypes.FileSize: + case SortByTypes.PersonCount: + case SortByTypes.Rating: // directories do not have rating + case SortByTypes.Name: + c.directories.sort((a, b) => + this.collator.compare(a.name, b.name) + ); + break; + case SortByTypes.Date: + if ( + Config.Gallery.enableDirectorySortingByDate === true + ) { + c.directories.sort( + (a, b) => (a.oldestMedia || a.lastModified) - (b.oldestMedia || b.lastModified) + ); + break; + } + c.directories.sort((a, b) => + this.collator.compare(a.name, b.name) + ); + break; + case SortByTypes.Random: + this.rndService.setSeed(c.directories.length); + c.directories + .sort((a, b): number => { + if (a.name.toLowerCase() < b.name.toLowerCase()) { + return 1; + } + if (a.name.toLowerCase() > b.name.toLowerCase()) { + return -1; + } + return 0; + }) + .sort((): number => { + return this.rndService.get() - 0.5; + }); + break; + } + + if (!sorting.ascending) { + c.directories.reverse(); + } + } + + // group + if (dirContent.media) { + const mCopy = dirContent.media; + this.sortMedia(grouping, mCopy); + const groupFN = this.getGroupByNameFn(grouping); + + c.mediaGroups = []; + + for (const m of mCopy) { + const k = groupFN(m); + if (c.mediaGroups.length == 0 || c.mediaGroups[c.mediaGroups.length - 1].name != k) { + c.mediaGroups.push({name: k, media: []}); } - if (a.name.toLowerCase() > b.name.toLowerCase()) { - return -1; - } - return 0; - }) - .sort((): number => { - return this.rndService.get() - 0.5; + c.mediaGroups[c.mediaGroups.length - 1].media.push(m); + } + } + + if (grouping.method === GroupByTypes.Date) { + // We do not need the youngest as we group by day. All photos are from the same day + c.mediaGroups.forEach(g => { + g.date = Utils.makeUTCMidnight(new Date(g.media?.[0]?.metadata?.creationDate)); }); - break; - } + } - if (!sorting.ascending) { - c.directories.reverse(); - } - } + // sort groups + for (let i = 0; i < c.mediaGroups.length; ++i) { + this.sortMedia(sorting, c.mediaGroups[i].media); + } - // group - if (dirContent.media) { - const mCopy = dirContent.media; - this.sortMedia(grouping, mCopy); - const groupFN = this.getGroupByNameFn(grouping); - - c.mediaGroups = []; - - for (const m of mCopy) { - const k = groupFN(m); - if (c.mediaGroups.length == 0 || c.mediaGroups[c.mediaGroups.length - 1].name != k) { - c.mediaGroups.push({name: k, media: []}); - } - c.mediaGroups[c.mediaGroups.length - 1].media.push(m); - } - } - - if (grouping.method === GroupByTypes.Date) { - // We do not need the youngest as we group by day. All photos are from the same day - c.mediaGroups.forEach(g => { - g.date = Utils.makeUTCMidnight(new Date(g.media?.[0]?.metadata?.creationDate)); - }); - } - - // sort groups - for (let i = 0; i < c.mediaGroups.length; ++i) { - this.sortMedia(sorting, c.mediaGroups[i].media); - } - - return c; + return c; + }) + ); }) - ); - }) - ); - }) + ); + }) ); } }