From 795c0ebba91984cd6ffffe5afa8373a9206f0b76 Mon Sep 17 00:00:00 2001 From: "Patrik J. Braun" Date: Mon, 28 May 2018 14:03:12 -0400 Subject: [PATCH] implementing photo sorting --- backend/middlewares/AdminMWs.ts | 2 + backend/model/threading/Worker.ts | 2 +- common/Utils.ts | 3 + common/config/private/IPrivateConfig.ts | 12 ++-- common/config/public/ConfigClass.ts | 6 +- common/entities/Error.ts | 24 +++---- common/entities/NotificationDTO.ts | 2 +- common/entities/SortingMethods.ts | 3 + common/entities/UserDTO.ts | 10 +-- common/entities/settings/OtherConfigDTO.ts | 3 + frontend/app/admin/admin.component.html | 2 +- frontend/app/app.module.ts | 6 +- frontend/app/gallery/gallery.service.ts | 9 +++ .../gallery/grid/grid.gallery.component.ts | 68 ++++++++++++++++--- .../lightbox/lightbox.gallery.component.ts | 6 +- .../navigator/navigator.gallery.component.css | 31 +++++++++ .../navigator.gallery.component.html | 19 +++++- .../navigator/navigator.gallery.component.ts | 13 +++- .../gallery/share/share.gallery.component.ts | 2 +- frontend/app/pipes/IconizeSortingMethod.ts | 20 ++++++ frontend/app/pipes/StringifySortingMethod.ts | 24 +++++++ .../_abstract/abstract.settings.component.ts | 5 +- .../other/other.settings.component.html | 12 ++++ .../other/other.settings.component.ts | 11 ++- frontend/app/settings/settings.service.ts | 4 +- 25 files changed, 254 insertions(+), 45 deletions(-) create mode 100644 common/entities/SortingMethods.ts create mode 100644 frontend/app/gallery/navigator/navigator.gallery.component.css create mode 100644 frontend/app/pipes/IconizeSortingMethod.ts create mode 100644 frontend/app/pipes/StringifySortingMethod.ts diff --git a/backend/middlewares/AdminMWs.ts b/backend/middlewares/AdminMWs.ts index 4f8b608..a289324 100644 --- a/backend/middlewares/AdminMWs.ts +++ b/backend/middlewares/AdminMWs.ts @@ -225,12 +225,14 @@ export class AdminMWs { Config.Client.enableCache = settings.enableCache; Config.Client.enableOnScrollRendering = settings.enableOnScrollRendering; Config.Client.enableOnScrollThumbnailPrioritising = settings.enableOnScrollThumbnailPrioritising; + Config.Client.defaultPhotoSortingMethod = settings.defaultPhotoSortingMethod; // only updating explicitly set config (not saving config set by the diagnostics) const original = Config.original(); original.Client.enableCache = settings.enableCache; original.Client.enableOnScrollRendering = settings.enableOnScrollRendering; original.Client.enableOnScrollThumbnailPrioritising = settings.enableOnScrollThumbnailPrioritising; + original.Client.defaultPhotoSortingMethod = settings.defaultPhotoSortingMethod; original.Server.enableThreading = settings.enableThreading; original.save(); await ConfigDiagnostics.runDiagnostics(); diff --git a/backend/model/threading/Worker.ts b/backend/model/threading/Worker.ts index ad74b9b..f0cbe8d 100644 --- a/backend/model/threading/Worker.ts +++ b/backend/model/threading/Worker.ts @@ -38,7 +38,7 @@ export class Worker { export enum WorkerTaskTypes { - thumbnail, diskManager + thumbnail = 1, diskManager = 2 } export interface WorkerTask { diff --git a/common/Utils.ts b/common/Utils.ts index 01a6b7f..17bc503 100644 --- a/common/Utils.ts +++ b/common/Utils.ts @@ -9,6 +9,9 @@ export class Utils { if (typeof filter !== 'object' || filter == null) { return object === filter; } + if (!object) { + return false; + } const keys = Object.keys(filter); for (let i = 0; i < keys.length; i++) { const key = keys[i]; diff --git a/common/config/private/IPrivateConfig.ts b/common/config/private/IPrivateConfig.ts index fa47d55..a1f2e0d 100644 --- a/common/config/private/IPrivateConfig.ts +++ b/common/config/private/IPrivateConfig.ts @@ -1,17 +1,17 @@ import {ClientConfig} from '../public/ConfigClass'; export enum DatabaseType { - memory = 0, mysql = 1, sqlite = 2 + memory = 1, mysql = 2, sqlite = 3 } export enum LogLevel { - error, warn, info, debug, verbose + error = 1, warn = 2, info = 3, debug = 4, verbose = 5 } export enum ThumbnailProcessingLib { - Jimp = 0, - gm = 1, - sharp = 2 + Jimp = 1, + gm = 2, + sharp = 3 } export interface MySQLConfig { @@ -42,7 +42,7 @@ export interface SharingConfig { } export enum ReIndexingSensitivity { - low, medium, high + low = 1, medium = 2, high = 3 } export interface IndexingConfig { diff --git a/common/config/public/ConfigClass.ts b/common/config/public/ConfigClass.ts index a6ad604..c165422 100644 --- a/common/config/public/ConfigClass.ts +++ b/common/config/public/ConfigClass.ts @@ -1,3 +1,5 @@ +import {SortingMethods} from '../../entities/SortingMethods'; + export module ClientConfig { export interface SearchConfig { enabled: boolean; @@ -38,6 +40,7 @@ export module ClientConfig { publicUrl: string; urlBase: string; languages: string[]; + defaultPhotoSortingMethod: SortingMethods; } } @@ -77,7 +80,8 @@ export class PublicConfigClass { authenticationRequired: true, publicUrl: '', urlBase: '', - languages: [] + languages: [], + defaultPhotoSortingMethod: SortingMethods.ascDate }; } diff --git a/common/entities/Error.ts b/common/entities/Error.ts index 0c0baec..d64cbba 100644 --- a/common/entities/Error.ts +++ b/common/entities/Error.ts @@ -1,23 +1,23 @@ export enum ErrorCodes { - NOT_AUTHENTICATED = 0, - ALREADY_AUTHENTICATED = 1, - NOT_AUTHORISED = 2, - PERMISSION_DENIED = 3, - CREDENTIAL_NOT_FOUND = 4, + NOT_AUTHENTICATED = 1, + ALREADY_AUTHENTICATED = 2, + NOT_AUTHORISED = 3, + PERMISSION_DENIED = 4, + CREDENTIAL_NOT_FOUND = 5, - USER_CREATION_ERROR = 5, + USER_CREATION_ERROR = 6, - GENERAL_ERROR = 6, - THUMBNAIL_GENERATION_ERROR = 7, - SERVER_ERROR = 8, + GENERAL_ERROR = 7, + THUMBNAIL_GENERATION_ERROR = 8, + SERVER_ERROR = 9, - USER_MANAGEMENT_DISABLED = 9, + USER_MANAGEMENT_DISABLED = 10, - INPUT_ERROR = 10, + INPUT_ERROR = 11, - SETTINGS_ERROR = 11 + SETTINGS_ERROR = 12 } export class ErrorDTO { diff --git a/common/entities/NotificationDTO.ts b/common/entities/NotificationDTO.ts index c60a703..2395021 100644 --- a/common/entities/NotificationDTO.ts +++ b/common/entities/NotificationDTO.ts @@ -1,5 +1,5 @@ export enum NotificationType { - error, warning, info + error = 1, warning = 2, info = 3 } export interface NotificationDTO { diff --git a/common/entities/SortingMethods.ts b/common/entities/SortingMethods.ts new file mode 100644 index 0000000..9082178 --- /dev/null +++ b/common/entities/SortingMethods.ts @@ -0,0 +1,3 @@ +export enum SortingMethods { + ascName = 1, descName = 2, ascDate = 3, descDate = 4 +} diff --git a/common/entities/UserDTO.ts b/common/entities/UserDTO.ts index a38e464..b20944c 100644 --- a/common/entities/UserDTO.ts +++ b/common/entities/UserDTO.ts @@ -2,11 +2,11 @@ import {DirectoryDTO} from './DirectoryDTO'; import {Utils} from '../Utils'; export enum UserRoles { - LimitedGuest = 0, - Guest = 1, - User = 2, - Admin = 3, - Developer = 4, + LimitedGuest = 1, + Guest = 2, + User = 3, + Admin = 4, + Developer = 5, } diff --git a/common/entities/settings/OtherConfigDTO.ts b/common/entities/settings/OtherConfigDTO.ts index 8a05900..de51fdd 100644 --- a/common/entities/settings/OtherConfigDTO.ts +++ b/common/entities/settings/OtherConfigDTO.ts @@ -1,6 +1,9 @@ +import {SortingMethods} from '../SortingMethods'; + export interface OtherConfigDTO { enableCache: boolean; enableOnScrollRendering: boolean; enableOnScrollThumbnailPrioritising: boolean; enableThreading: boolean; + defaultPhotoSortingMethod: SortingMethods; } diff --git a/frontend/app/admin/admin.component.html b/frontend/app/admin/admin.component.html index b8d6127..e97d136 100644 --- a/frontend/app/admin/admin.component.html +++ b/frontend/app/admin/admin.component.html @@ -50,7 +50,7 @@ - diff --git a/frontend/app/app.module.ts b/frontend/app/app.module.ts index 9099a00..59c02fd 100644 --- a/frontend/app/app.module.ts +++ b/frontend/app/app.module.ts @@ -67,6 +67,8 @@ import {IndexingSettingsComponent} from './settings/indexing/indexing.settings.c import {LanguageComponent} from './language/language.component'; import {I18n, MISSING_TRANSLATION_STRATEGY} from '@ngx-translate/i18n-polyfill'; import {QueryService} from './model/query.service'; +import {IconizeSortingMethod} from './pipes/IconizeSortingMethod'; +import {StringifySortingMethod} from './pipes/StringifySortingMethod'; @Injectable() export class GoogleMapsConfig { @@ -156,7 +158,9 @@ export function translationsFactory(locale: string) { BasicSettingsComponent, OtherSettingsComponent, IndexingSettingsComponent, - StringifyRole], + StringifyRole, + IconizeSortingMethod, + StringifySortingMethod], providers: [ {provide: UrlSerializer, useClass: CustomUrlSerializer}, {provide: LAZY_MAPS_API_CONFIG, useClass: GoogleMapsConfig}, diff --git a/frontend/app/gallery/gallery.service.ts b/frontend/app/gallery/gallery.service.ts index ee375d2..7c8ad8f 100644 --- a/frontend/app/gallery/gallery.service.ts +++ b/frontend/app/gallery/gallery.service.ts @@ -9,11 +9,15 @@ import {SharingDTO} from '../../../common/entities/SharingDTO'; import {Config} from '../../../common/config/public/Config'; import {ShareService} from './share.service'; import {NavigationService} from '../model/navigation.service'; +import {SortingMethods} from '../../../common/entities/SortingMethods'; + @Injectable() export class GalleryService { + public content: BehaviorSubject; + public sorting: BehaviorSubject; private lastDirectory: DirectoryDTO; private searchId: any; @@ -22,12 +26,17 @@ export class GalleryService { private _shareService: ShareService, private navigatoinService: NavigationService) { this.content = new BehaviorSubject(new ContentWrapper()); + this.sorting = new BehaviorSubject(Config.Client.defaultPhotoSortingMethod); } lastRequest: { directory: string } = { directory: null }; + setSorting(sorting: SortingMethods): void { + this.sorting.next(sorting); + } + public loadDirectory(directoryName: string): void { const content = new ContentWrapper(); diff --git a/frontend/app/gallery/grid/grid.gallery.component.ts b/frontend/app/gallery/grid/grid.gallery.component.ts index f12c586..27a2cf5 100644 --- a/frontend/app/gallery/grid/grid.gallery.component.ts +++ b/frontend/app/gallery/grid/grid.gallery.component.ts @@ -14,7 +14,7 @@ import { } from '@angular/core'; import {PhotoDTO} from '../../../../common/entities/PhotoDTO'; import {GridRowBuilder} from './GridRowBuilder'; -import {GalleryLightboxComponent, LightboxStates} from '../lightbox/lightbox.gallery.component'; +import {GalleryLightboxComponent} from '../lightbox/lightbox.gallery.component'; import {GridPhoto} from './GridPhoto'; import {GalleryPhotoComponent} from './photo/photo.grid.gallery.component'; import {OverlayService} from '../overlay.service'; @@ -23,7 +23,8 @@ import {PageHelper} from '../../model/page.helper'; import {Subscription} from 'rxjs'; import {ActivatedRoute, Params, Router} from '@angular/router'; import {QueryService} from '../../model/query.service'; -import {SimpleChanges} from '@angular/core'; +import {GalleryService} from '../gallery.service'; +import {SortingMethods} from '../../../../common/entities/SortingMethods'; @Component({ selector: 'app-gallery-grid', @@ -52,18 +53,25 @@ export class GalleryGridComponent implements OnChanges, OnInit, AfterViewInit, O private helperTime = null; isAfterViewInit = false; private renderedPhotoIndex = 0; - routeSubscription: Subscription = null; + subscriptions: { + route: Subscription, + sorting: Subscription + } = { + route: null, + sorting: null + }; delayedRenderUpToPhoto: string = null; constructor(private overlayService: OverlayService, private changeDetector: ChangeDetectorRef, public queryService: QueryService, private router: Router, + public galleryService: GalleryService, private route: ActivatedRoute) { } ngOnInit() { - this.routeSubscription = this.route.queryParams.subscribe((params: Params) => { + this.subscriptions.route = this.route.queryParams.subscribe((params: Params) => { if (params[QueryService.PHOTO_PARAM] && params[QueryService.PHOTO_PARAM] !== '') { this.delayedRenderUpToPhoto = params[QueryService.PHOTO_PARAM]; if (!this.photos || this.photos.length === 0) { @@ -73,6 +81,11 @@ export class GalleryGridComponent implements OnChanges, OnInit, AfterViewInit, O this.renderUpToPhoto(params[QueryService.PHOTO_PARAM]); } }); + this.subscriptions.sorting = this.galleryService.sorting.subscribe(() => { + this.clearRenderedPhotos(); + this.sortPhotos(); + this.renderPhotos(); + }); } ngOnChanges() { @@ -95,6 +108,14 @@ export class GalleryGridComponent implements OnChanges, OnInit, AfterViewInit, O if (this.helperTime != null) { clearTimeout(this.helperTime); } + if (this.subscriptions.route !== null) { + this.subscriptions.route.unsubscribe(); + this.subscriptions.route = null; + } + if (this.subscriptions.sorting !== null) { + this.subscriptions.sorting.unsubscribe(); + this.subscriptions.sorting = null; + } } @HostListener('window:resize') @@ -183,10 +204,41 @@ export class GalleryGridComponent implements OnChanges, OnInit, AfterViewInit, O } private sortPhotos() { - // sort photos by date - this.photos.sort((a: PhotoDTO, b: PhotoDTO) => { - return a.metadata.creationDate - b.metadata.creationDate; - }); + switch (this.galleryService.sorting.value) { + case SortingMethods.ascName: + this.photos.sort((a: PhotoDTO, b: PhotoDTO) => { + if (a.name.toLowerCase() < b.name.toLowerCase()) { + return -1; + } + if (a.name.toLowerCase() > b.name.toLowerCase()) { + return 1; + } + return 0; + }); + break; + case SortingMethods.descName: + this.photos.sort((a: PhotoDTO, b: PhotoDTO) => { + if (a.name.toLowerCase() < b.name.toLowerCase()) { + return 1; + } + if (a.name.toLowerCase() > b.name.toLowerCase()) { + return -1; + } + return 0; + }); + break; + case SortingMethods.ascDate: + this.photos.sort((a: PhotoDTO, b: PhotoDTO) => { + return a.metadata.creationDate - b.metadata.creationDate; + }); + break; + case SortingMethods.descDate: + this.photos.sort((a: PhotoDTO, b: PhotoDTO) => { + return b.metadata.creationDate - a.metadata.creationDate; + }); + break; + } + } diff --git a/frontend/app/gallery/lightbox/lightbox.gallery.component.ts b/frontend/app/gallery/lightbox/lightbox.gallery.component.ts index 6c5ef53..ce3dda0 100644 --- a/frontend/app/gallery/lightbox/lightbox.gallery.component.ts +++ b/frontend/app/gallery/lightbox/lightbox.gallery.component.ts @@ -24,9 +24,9 @@ import {PageHelper} from '../../model/page.helper'; import {QueryService} from '../../model/query.service'; export enum LightboxStates { - Open, - Closing, - Closed + Open = 1, + Closing = 2, + Closed = 3 } @Component({ diff --git a/frontend/app/gallery/navigator/navigator.gallery.component.css b/frontend/app/gallery/navigator/navigator.gallery.component.css new file mode 100644 index 0000000..421bd67 --- /dev/null +++ b/frontend/app/gallery/navigator/navigator.gallery.component.css @@ -0,0 +1,31 @@ +.nav-container { + position: relative; +} + +.btn-group { + position: absolute; + right: 1rem; + top: 50%; + transform: translateY(-50%); +} + +ol { + padding-right: 130px; +} + +.dropdown-menu { + min-width: 16rem; +} + +.row { + margin: 0; + display: flex; + cursor: pointer; +} + +nav{ + z-index: 1; +} +.dropdown-toggle::after { + display:none; +} diff --git a/frontend/app/gallery/navigator/navigator.gallery.component.html b/frontend/app/gallery/navigator/navigator.gallery.component.html index 7026aba..c107c57 100644 --- a/frontend/app/gallery/navigator/navigator.gallery.component.html +++ b/frontend/app/gallery/navigator/navigator.gallery.component.html @@ -1,4 +1,4 @@ -