1
0
mirror of https://github.com/xuthus83/pigallery2.git synced 2025-01-14 14:43:17 +08:00

Adding grouping selector UI

This commit is contained in:
Patrik J. Braun 2023-08-28 23:50:41 +02:00
parent 0a1fb29c9f
commit 3f35c35a13
15 changed files with 262 additions and 228 deletions

View File

@ -1,4 +1,4 @@
import {SortingMethods} from './entities/SortingMethods';
import {SortingByTypes, SortingMethod} from './entities/SortingMethods';
/**
* This contains the action of the supported list of *.pg2conf files.
@ -6,15 +6,15 @@ import {SortingMethods} from './entities/SortingMethods';
*/
export const PG2ConfMap = {
sorting: {
'.order_descending_name.pg2conf': SortingMethods.descName,
'.order_ascending_name.pg2conf': SortingMethods.ascName,
'.order_descending_date.pg2conf': SortingMethods.descDate,
'.order_ascending_date.pg2conf': SortingMethods.ascDate,
'.order_descending_rating.pg2conf': SortingMethods.descRating,
'.order_ascending_rating.pg2conf': SortingMethods.ascRating,
'.order_random.pg2conf': SortingMethods.random,
'.order_descending_person_count.pg2conf': SortingMethods.descPersonCount,
'.order_ascending_person_count.pg2conf': SortingMethods.descPersonCount,
'.order_descending_name.pg2conf': {method: SortingByTypes.Name, ascending: false} as SortingMethod,
'.order_ascending_name.pg2conf': {method: SortingByTypes.Name, ascending: true} as SortingMethod,
'.order_descending_date.pg2conf': {method: SortingByTypes.Date, ascending: false} as SortingMethod,
'.order_ascending_date.pg2conf': {method: SortingByTypes.Date, ascending: true} as SortingMethod,
'.order_descending_rating.pg2conf': {method: SortingByTypes.Rating, ascending: false} as SortingMethod,
'.order_ascending_rating.pg2conf': {method: SortingByTypes.Rating, ascending: true} as SortingMethod,
'.order_random.pg2conf': {method: SortingByTypes.Rating, ascending: null} as SortingMethod,
'.order_descending_person_count.pg2conf': {method: SortingByTypes.PersonCount, ascending: false} as SortingMethod,
'.order_ascending_person_count.pg2conf': {method: SortingByTypes.PersonCount, ascending: true} as SortingMethod,
},
};

View File

@ -18,6 +18,7 @@ import {
ClientPhotoConvertingConfig,
ClientServiceConfig,
ClientSharingConfig,
ClientSortingConfig,
ClientThumbnailConfig,
ClientUserConfig,
ClientVideoConfig,
@ -28,7 +29,7 @@ import {SubConfigClass} from 'typeconfig/src/decorators/class/SubConfigClass';
import {ConfigProperty} from 'typeconfig/src/decorators/property/ConfigPropoerty';
import {DefaultsJobs} from '../../entities/job/JobDTO';
import {SearchQueryDTO, SearchQueryTypes, TextSearch,} from '../../entities/SearchQueryDTO';
import {SortingMethods} from '../../entities/SortingMethods';
import {SortingByTypes} from '../../entities/SortingMethods';
import {UserRoles} from '../../entities/UserDTO';
import {MediaPickDTO} from '../../entities/MediaPickDTO';
import {MessagingConfig} from './MessagingConfig';
@ -888,7 +889,7 @@ export class ServerAlbumCoverConfig {
text: '',
} as TextSearch;
@ConfigProperty({
arrayType: SortingMethods,
arrayType: ClientSortingConfig,
tags: {
name: $localize`Cover Sorting`,
uiResetNeeded: {db: true},
@ -896,10 +897,10 @@ export class ServerAlbumCoverConfig {
},
description: $localize`If multiple cover is available sorts them by these methods and selects the first one.`,
})
Sorting: SortingMethods[] = [
SortingMethods.descRating,
SortingMethods.descDate,
SortingMethods.descPersonCount,
Sorting: ClientSortingConfig[] = [
new ClientSortingConfig(SortingByTypes.Rating, false),
new ClientSortingConfig(SortingByTypes.Date, false),
new ClientSortingConfig(SortingByTypes.PersonCount, false)
];
}

View File

@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-inferrable-types */
import 'reflect-metadata';
import {SortingMethods} from '../../entities/SortingMethods';
import {SortingByTypes, SortingMethod} from '../../entities/SortingMethods';
import {UserRoles} from '../../entities/UserDTO';
import {ConfigProperty, SubConfigClass} from 'typeconfig/common';
import {SearchQueryDTO} from '../../entities/SearchQueryDTO';
@ -519,7 +519,7 @@ export class ClientMapConfig {
arrayType: MapPathGroupConfig,
tags: {
name: $localize`Path theme groups`,
githubIssue:647,
githubIssue: 647,
priority: ConfigPriority.underTheHood
} as TAGS,
description: $localize`Markers are grouped and themed by these settings`,
@ -880,6 +880,30 @@ export class ThemesConfig {
)];
}
@SubConfigClass<TAGS>({tags: {client: true}, softReadonly: true})
export class ClientSortingConfig implements SortingMethod {
constructor(method: SortingByTypes = SortingByTypes.Date, ascending: boolean = true) {
this.method = method;
this.ascending = ascending;
}
@ConfigProperty({
type: SortingByTypes,
tags: {
name: $localize`Method`,
},
})
method: SortingByTypes = SortingByTypes.Date;
@ConfigProperty({
tags: {
name: $localize`Ascending`,
},
})
ascending: boolean = true;
}
@SubConfigClass<TAGS>({tags: {client: true}, softReadonly: true})
export class ClientGalleryConfig {
@ -901,22 +925,24 @@ export class ClientGalleryConfig {
enableOnScrollRendering: boolean = true;
@ConfigProperty({
type: SortingMethods, tags: {
type: ClientSortingConfig,
tags: {
name: $localize`Default sorting`,
priority: ConfigPriority.advanced,
},
description: $localize`Default sorting method for photo and video in a directory results.`
})
defaultPhotoSortingMethod: SortingMethods = SortingMethods.ascDate;
defaultPhotoSortingMethod: ClientSortingConfig = new ClientSortingConfig(SortingByTypes.Date, true);
@ConfigProperty({
type: SortingMethods, tags: {
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: SortingMethods = SortingMethods.descDate;
defaultSearchSortingMethod: ClientSortingConfig = new ClientSortingConfig(SortingByTypes.Date, false);
@ConfigProperty({
tags: {

View File

@ -1,8 +1,8 @@
import {SearchQueryDTO} from './SearchQueryDTO';
import {SortingMethods} from './SortingMethods';
import {SortingMethod} from './SortingMethods';
export interface MediaPickDTO {
searchQuery: SearchQueryDTO;
sortBy: SortingMethods[];
sortBy: SortingMethod[];
pick: number;
}

View File

@ -2,14 +2,15 @@
* Order of these enums determines the order in the UI.
* Keep spaces between the values, so new value can be added in between without changing the existing ones
*/
export enum SortingMethods {
ascName = 10,
descName = 11,
ascDate = 20,
descDate = 21,
ascRating = 30,
descRating = 31,
ascPersonCount = 40,
descPersonCount = 41,
export enum SortingByTypes {
Name = 10,
Date = 20,
Rating = 30,
PersonCount = 40,
random = 100 // let's keep random as the last in the UI
}
export interface SortingMethod {
method: SortingByTypes;
ascending: boolean;
}

View File

@ -1,12 +1,12 @@
import { Pipe, PipeTransform } from '@angular/core';
import { SortingMethods } from '../../../common/entities/SortingMethods';
import {Pipe, PipeTransform} from '@angular/core';
import {SortingByTypes} from '../../../common/entities/SortingMethods';
import {EnumTranslations} from '../ui/EnumTranslations';
@Pipe({ name: 'stringifySorting' })
@Pipe({name: 'stringifySorting'})
export class StringifySortingMethod implements PipeTransform {
transform(method: SortingMethods): string {
return EnumTranslations[SortingMethods[method]];
transform(method: SortingByTypes): string {
return EnumTranslations[SortingByTypes[method]];
}
}

View File

@ -1,9 +1,9 @@
import {UserRoles} from '../../../common/entities/UserDTO';
import {ConfigPriority, MapProviders, NavigationLinkTypes, ScrollUpModes} from '../../../common/config/public/ClientConfig';
import {ReIndexingSensitivity} from '../../../common/config/private/PrivateConfig';
import {SortingMethods} from '../../../common/entities/SortingMethods';
import {SearchQueryTypes} from '../../../common/entities/SearchQueryDTO';
import {ConfigStyle} from './settings/settings.service';
import {SortingByTypes} from '../../../common/entities/SortingMethods';
export const EnumTranslations: Record<string, string> = {};
export const enumToTranslatedArray = (EnumType: any): { key: number; value: string }[] => {
@ -44,15 +44,11 @@ EnumTranslations[ReIndexingSensitivity[ReIndexingSensitivity.high]] = $localize`
EnumTranslations[ReIndexingSensitivity[ReIndexingSensitivity.medium]] = $localize`medium`;
EnumTranslations[SortingMethods[SortingMethods.descDate]] = $localize`descending date`;
EnumTranslations[SortingMethods[SortingMethods.ascDate]] = $localize`ascending date`;
EnumTranslations[SortingMethods[SortingMethods.descName]] = $localize`descending name`;
EnumTranslations[SortingMethods[SortingMethods.ascName]] = $localize`ascending name`;
EnumTranslations[SortingMethods[SortingMethods.descRating]] = $localize`descending rating`;
EnumTranslations[SortingMethods[SortingMethods.ascRating]] = $localize`ascending rating`;
EnumTranslations[SortingMethods[SortingMethods.random]] = $localize`random`;
EnumTranslations[SortingMethods[SortingMethods.ascPersonCount]] = $localize`ascending persons`;
EnumTranslations[SortingMethods[SortingMethods.descPersonCount]] = $localize`descending persons`;
EnumTranslations[SortingByTypes[SortingByTypes.Date]] = $localize`date`;
EnumTranslations[SortingByTypes[SortingByTypes.Name]] = $localize`name`;
EnumTranslations[SortingByTypes[SortingByTypes.Rating]] = $localize`rating`;
EnumTranslations[SortingByTypes[SortingByTypes.random]] = $localize`random`;
EnumTranslations[SortingByTypes[SortingByTypes.PersonCount]] = $localize`persons`;
EnumTranslations[NavigationLinkTypes[NavigationLinkTypes.url]] = $localize`Url`;

View File

@ -1,16 +1,15 @@
import {Injectable} from '@angular/core';
import {DirectoryPathDTO, ParentDirectoryDTO,} from '../../../../common/entities/DirectoryDTO';
import {ParentDirectoryDTO,} from '../../../../common/entities/DirectoryDTO';
import {Utils} from '../../../../common/Utils';
import {Config} from '../../../../common/config/public/Config';
import {IAutoCompleteItem} from '../../../../common/entities/AutoCompleteItem';
import {MediaDTO} from '../../../../common/entities/MediaDTO';
import {SortingMethods} from '../../../../common/entities/SortingMethods';
import {SortingMethod} from '../../../../common/entities/SortingMethods';
import {VersionService} from '../../model/version.service';
import {SearchQueryDTO, SearchQueryTypes,} from '../../../../common/entities/SearchQueryDTO';
import {ContentWrapper} from '../../../../common/entities/ConentWrapper';
import {ContentWrapperWithError} from './content.service';
import {ThemeModes} from '../../../../common/config/public/ClientConfig';
import {SearchResultDTO} from '../../../../common/entities/SearchResultDTO';
interface CacheItem<T> {
timestamp: number;
@ -97,7 +96,7 @@ export class GalleryCacheService {
}
}
public getSorting(cw: ContentWrapper): SortingMethods {
public getSorting(cw: ContentWrapper): SortingMethod {
let key = GalleryCacheService.SORTING_PREFIX;
if (cw?.searchResult?.searchQuery) {
key += JSON.stringify(cw.searchResult.searchQuery);
@ -106,7 +105,7 @@ export class GalleryCacheService {
}
const tmp = localStorage.getItem(key);
if (tmp != null) {
return parseInt(tmp, 10);
return JSON.parse(tmp);
}
return null;
}
@ -128,8 +127,8 @@ export class GalleryCacheService {
public setSorting(
cw: ContentWrapper,
sorting: SortingMethods
): SortingMethods {
sorting: SortingMethod
): void {
try {
let key = GalleryCacheService.SORTING_PREFIX;
if (cw?.searchResult?.searchQuery) {
@ -137,12 +136,11 @@ export class GalleryCacheService {
} else {
key += cw?.directory?.path + '/' + cw?.directory?.name;
}
localStorage.setItem(key, sorting.toString());
localStorage.setItem(key, JSON.stringify(sorting));
} catch (e) {
this.reset();
console.error(e);
}
return null;
}
public getAutoComplete(

View File

@ -59,35 +59,77 @@
[class.btn-secondary]="sortingService.sorting.value !== DefaultSorting"
[class.btn-outline-secondary]="sortingService.sorting.value == DefaultSorting"
aria-controls="sorting-dropdown">
<app-sorting-method-icon [method]="sortingService.sorting.value"></app-sorting-method-icon>
<ng-icon
[name]="sortingService.sorting.value.ascending ? 'ionArrowDownOutline' : 'ionArrowUpOutline'"></ng-icon>
<app-sorting-method-icon [method]="sortingService.sorting.value.method"></app-sorting-method-icon>
</button>
<div id="sorting-dropdown" *dropdownMenu class="dropdown-menu dropdown-menu-right"
role="menu" aria-labelledby="button-alignment">
<div class="row flex-nowrap">
<div class="col-6">
Sorting
<div class="col-6 p-1 border-end">
<span class="ps-2">Sorting</span>
<div class="row">
<div class="dropdown-item sorting-grouping-item" role="menuitem"
*ngFor="let type of sortingMethodsType"
(click)="setSorting(type.key)">
<div class="dropdown-item sorting-grouping-item ps-3 pe-3" role="menuitem"
[class.active]="sortingService.sorting.value.method == type.key"
*ngFor="let type of sortingByTypes"
(click)="setSortingBy(type.key)">
<div class="me-2 d-inline-block">
<app-sorting-method-icon [method]="type.key"></app-sorting-method-icon>
</div>
<div class="d-inline-block">{{type.key | stringifySorting}}</div>
</div>
<hr>
<div class="dropdown-item sorting-grouping-item ps-3 pe-3" role="menuitem"
[class.active]="sortingService.sorting.value.ascending == true"
(click)="setSortingAscending(true)">
<div class="me-2 d-inline-block">
<ng-icon name="">ionArrowUpOutline</ng-icon>
</div>
<div class="d-inline-block" i18n>ascending</div>
</div>
<div class="dropdown-item sorting-grouping-item ps-3 pe-3" role="menuitem"
[class.active]="sortingService.sorting.value.ascending == false"
(click)="setSortingAscending(false)">
<div class="me-2 d-inline-block">
<ng-icon name="ionArrowDownOutline"></ng-icon>
</div>
<div class="d-inline-block" i18n>descending</div>
</div>
</div>
</div>
<div class="col-6">
Grouping
<div class="col-6 p-1">
<span class="ps-2">Grouping</span>
<div class="row">
<div class="dropdown-item sorting-grouping-item" role="menuitem"
*ngFor="let type of sortingMethodsType"
(click)="setGrouping(type.key)">
<div class="dropdown-item sorting-grouping-item ps-3 pe-3" role="menuitem"
[class.active]="sortingService.grouping.value.method == type.key"
*ngFor="let type of sortingByTypes"
(click)="setGroupingBy(type.key)">
<div class="me-2 d-inline-block">
<app-sorting-method-icon [method]="type.key"></app-sorting-method-icon>
</div>
<div class="d-inline-block">{{type.key | stringifySorting}}</div>
</div>
<hr>
<div class="dropdown-item sorting-grouping-item ps-3 pe-3" role="menuitem"
[class.active]="sortingService.grouping.value.ascending == true"
(click)="setGroupingAscending(true)">
<div class="me-2 d-inline-block">
<ng-icon name="ionArrowUpOutline"></ng-icon>
</div>
<div class="d-inline-block" i18n>ascending</div>
</div>
<div class="dropdown-item sorting-grouping-item ps-3 pe-3" role="menuitem"
[class.active]="sortingService.grouping.value.ascending == false"
(click)="setGroupingAscending(false)">
<div class="me-2 d-inline-block">
<ng-icon name="ionArrowDownOutline"></ng-icon>
</div>
<div class="d-inline-block" i18n>descending</div>
</div>
</div>
</div>
</div>

View File

@ -6,7 +6,7 @@ import {AuthenticationService} from '../../../model/network/authentication.servi
import {QueryService} from '../../../model/query.service';
import {ContentService, ContentWrapperWithError, DirectoryContent,} from '../content.service';
import {Utils} from '../../../../../common/Utils';
import {SortingMethods} from '../../../../../common/entities/SortingMethods';
import {SortingByTypes, SortingMethod} from '../../../../../common/entities/SortingMethods';
import {Config} from '../../../../../common/config/public/Config';
import {SearchQueryTypes, TextSearch, TextSearchQueryMatchTypes,} from '../../../../../common/entities/SearchQueryDTO';
import {Observable} from 'rxjs';
@ -23,8 +23,8 @@ import {FilterService} from '../filter/filter.service';
providers: [RouterLink],
})
export class GalleryNavigatorComponent {
public SortingMethods = SortingMethods;
public sortingMethodsType: { key: number; value: string }[] = [];
public SortingByTypes = SortingByTypes;
public sortingByTypes: { key: number; value: string }[] = [];
public readonly config = Config;
// DefaultSorting = Config.Gallery.defaultPhotoSortingMethod;
public readonly SearchQueryTypes = SearchQueryTypes;
@ -52,7 +52,7 @@ export class GalleryNavigatorComponent {
private router: Router,
public sanitizer: DomSanitizer
) {
this.sortingMethodsType = Utils.enumToArray(SortingMethods);
this.sortingByTypes = Utils.enumToArray(SortingByTypes);
this.RootFolderName = $localize`Home`;
this.wrappedContent = this.galleryService.content;
this.directoryContent = this.wrappedContent.pipe(
@ -137,20 +137,35 @@ export class GalleryNavigatorComponent {
: 0;
}
get DefaultSorting(): SortingMethods {
get DefaultSorting(): SortingMethod {
return this.sortingService.getDefaultSorting(
this.galleryService.content.value
);
}
setSorting(sorting: SortingMethods): void {
this.sortingService.setSorting(sorting);
this.sortingService.setGrouping(sorting);
setSortingBy(sorting: SortingByTypes): void {
const s = {method: sorting, ascending: this.sortingService.sorting.value.ascending};
this.sortingService.setSorting(s);
this.sortingService.setGrouping(s);
}
setGrouping(grouping: SortingMethods): void {
this.sortingService.setGrouping(grouping);
setSortingAscending(asc: boolean) {
const s = {method: this.sortingService.sorting.value.method, ascending: asc};
this.sortingService.setSorting(s);
this.sortingService.setGrouping(s);
}
setGroupingBy(grouping: SortingByTypes): void {
const s = {method: grouping, ascending: this.sortingService.grouping.value.ascending};
this.sortingService.setGrouping(s);
}
setGroupingAscending(asc: boolean) {
const s = {method: this.sortingService.grouping.value.method, ascending: asc};
this.sortingService.setGrouping(s);
}
getDownloadZipLink(): string {
const c = this.galleryService.content.value;
if (!c.directory) {

View File

@ -4,7 +4,7 @@ import {NetworkService} from '../../../model/network/network.service';
import {GalleryCacheService} from '../cache.gallery.service';
import {BehaviorSubject, Observable} from 'rxjs';
import {Config} from '../../../../../common/config/public/Config';
import {SortingMethods} from '../../../../../common/entities/SortingMethods';
import {SortingByTypes, SortingMethod} from '../../../../../common/entities/SortingMethods';
import {PG2ConfMap} from '../../../../../common/PG2ConfMap';
import {ContentService, DirectoryContent} from '../content.service';
import {PhotoDTO} from '../../../../../common/entities/PhotoDTO';
@ -17,8 +17,8 @@ import {FileDTO} from '../../../../../common/entities/FileDTO';
@Injectable()
export class GallerySortingService {
public sorting: BehaviorSubject<SortingMethods>;
public grouping: BehaviorSubject<SortingMethods>;
public sorting: BehaviorSubject<SortingMethod>;
public grouping: BehaviorSubject<SortingMethod>;
private collator = new Intl.Collator(undefined, {numeric: true});
constructor(
@ -28,11 +28,11 @@ export class GallerySortingService {
private rndService: SeededRandomService,
private datePipe: DatePipe
) {
this.sorting = new BehaviorSubject<SortingMethods>(
Config.Gallery.defaultPhotoSortingMethod
this.sorting = new BehaviorSubject(
{method: Config.Gallery.defaultPhotoSortingMethod.method, ascending: Config.Gallery.defaultPhotoSortingMethod.ascending}
);
this.grouping = new BehaviorSubject<SortingMethods>(
SortingMethods.ascDate // TODO: move to config
this.grouping = new BehaviorSubject(
{method: Config.Gallery.defaultPhotoSortingMethod.method, ascending: Config.Gallery.defaultPhotoSortingMethod.ascending}
);
this.galleryService.content.subscribe((c) => {
if (c) {
@ -46,7 +46,7 @@ export class GallerySortingService {
});
}
getDefaultSorting(cw: ContentWrapper): SortingMethods {
getDefaultSorting(cw: ContentWrapper): SortingMethod {
if (cw.directory && cw.directory.metaFile) {
for (const file in PG2ConfMap.sorting) {
if (cw.directory.metaFile.some((f) => f.name === file)) {
@ -60,7 +60,7 @@ export class GallerySortingService {
return Config.Gallery.defaultPhotoSortingMethod;
}
setSorting(sorting: SortingMethods): void {
setSorting(sorting: SortingMethod): void {
this.sorting.next(sorting);
if (this.galleryService.content.value) {
if (
@ -79,60 +79,39 @@ export class GallerySortingService {
}
}
setGrouping(grouping: SortingMethods): void {
setGrouping(grouping: SortingMethod): void {
this.grouping.next(grouping);
}
private sortMedia(sorting: SortingMethods, media: MediaDTO[]): void {
private sortMedia(sorting: SortingMethod, media: MediaDTO[]): void {
if (!media) {
return;
}
switch (sorting) {
case SortingMethods.ascName:
switch (sorting.method) {
case SortingByTypes.Name:
media.sort((a: PhotoDTO, b: PhotoDTO) =>
this.collator.compare(a.name, b.name)
);
break;
case SortingMethods.descName:
media.sort((a: PhotoDTO, b: PhotoDTO) =>
this.collator.compare(b.name, a.name)
);
break;
case SortingMethods.ascDate:
case SortingByTypes.Date:
media.sort((a: PhotoDTO, b: PhotoDTO): number => {
return a.metadata.creationDate - b.metadata.creationDate;
});
break;
case SortingMethods.descDate:
media.sort((a: PhotoDTO, b: PhotoDTO): number => {
return b.metadata.creationDate - a.metadata.creationDate;
});
break;
case SortingMethods.ascRating:
case SortingByTypes.Rating:
media.sort(
(a: PhotoDTO, b: PhotoDTO) =>
(a.metadata.rating || 0) - (b.metadata.rating || 0)
);
break;
case SortingMethods.descRating:
media.sort(
(a: PhotoDTO, b: PhotoDTO) =>
(b.metadata.rating || 0) - (a.metadata.rating || 0)
);
break;
case SortingMethods.ascPersonCount:
case SortingByTypes.PersonCount:
media.sort(
(a: PhotoDTO, b: PhotoDTO) =>
(a.metadata?.faces?.length || 0) - (b.metadata?.faces?.length || 0)
);
break;
case SortingMethods.descPersonCount:
media.sort(
(a: PhotoDTO, b: PhotoDTO) =>
(b.metadata?.faces?.length || 0) - (a.metadata?.faces?.length || 0)
);
break;
case SortingMethods.random:
case SortingByTypes.random:
this.rndService.setSeed(media.length);
media.sort((a: PhotoDTO, b: PhotoDTO): number => {
if (a.name.toLowerCase() < b.name.toLowerCase()) {
@ -148,6 +127,9 @@ export class GallerySortingService {
});
break;
}
if (!sorting.ascending) {
media.reverse();
}
return;
}
@ -169,14 +151,14 @@ export class GallerySortingService {
metaFile: dirContent.metaFile,
};
if (c.directories) {
switch (sorting) {
case SortingMethods.ascRating: // directories do not have rating
case SortingMethods.ascName:
switch (sorting.method) {
case SortingByTypes.Rating: // directories do not have rating
case SortingByTypes.Name:
c.directories.sort((a, b) =>
this.collator.compare(a.name, b.name)
);
break;
case SortingMethods.ascDate:
case SortingByTypes.Date:
if (
Config.Gallery.enableDirectorySortingByDate === true
) {
@ -189,26 +171,7 @@ export class GallerySortingService {
this.collator.compare(a.name, b.name)
);
break;
case SortingMethods.descRating: // directories do not have rating
case SortingMethods.descName:
c.directories.sort((a, b) =>
this.collator.compare(b.name, a.name)
);
break;
case SortingMethods.descDate:
if (
Config.Gallery.enableDirectorySortingByDate === true
) {
c.directories.sort(
(a, b) => b.lastModified - a.lastModified
);
break;
}
c.directories.sort((a, b) =>
this.collator.compare(b.name, a.name)
);
break;
case SortingMethods.random:
case SortingByTypes.random:
this.rndService.setSeed(c.directories.length);
c.directories
.sort((a, b): number => {
@ -225,6 +188,10 @@ export class GallerySortingService {
});
break;
}
if (!sorting.ascending) {
c.directories.reverse();
}
}
// group
@ -232,21 +199,17 @@ export class GallerySortingService {
const mCopy = dirContent.media;
this.sortMedia(grouping, mCopy);
let groupFN = (m: MediaDTO) => '';
switch (grouping) {
case SortingMethods.ascDate:
case SortingMethods.descDate:
switch (grouping.method) {
case SortingByTypes.Date:
groupFN = (m: MediaDTO) => this.datePipe.transform(m.metadata.creationDate, 'longDate');
break;
case SortingMethods.ascName:
case SortingMethods.descName:
case SortingByTypes.Name:
groupFN = (m: MediaDTO) => m.name.at(0).toLowerCase();
break;
case SortingMethods.descRating:
case SortingMethods.ascRating:
case SortingByTypes.Rating:
groupFN = (m: MediaDTO) => ((m as PhotoDTO).metadata.rating || 0).toString();
break;
case SortingMethods.descPersonCount:
case SortingMethods.ascPersonCount:
case SortingByTypes.PersonCount:
groupFN = (m: MediaDTO) => ((m as PhotoDTO).metadata.faces || []).length.toString();
break;
}

View File

@ -253,7 +253,7 @@
(ngModelChange)="onChange($event)"
class="form-select"
[(ngModel)]="mp.sortBy[k]">
<option *ngFor="let opt of SortingMethods" [ngValue]="opt.key">{{opt.value}}
<option *ngFor="let opt of SortingByTypes" [ngValue]="opt.key">{{opt.value}}
</option>
</select>

View File

@ -19,8 +19,8 @@ import {
ScheduledJobTriggerConfig
} from '../../../../../common/config/private/PrivateConfig';
import {ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator} from '@angular/forms';
import {enumToTranslatedArray} from '../../EnumTranslations';
import {SortingMethods} from '../../../../../common/entities/SortingMethods';
import {EnumTranslations} from '../../EnumTranslations';
import {SortingByTypes, SortingMethod} from '../../../../../common/entities/SortingMethods';
import {MediaPickDTO} from '../../../../../common/entities/MediaPickDTO';
import {SearchQueryTypes, TextSearch} from '../../../../../common/entities/SearchQueryDTO';
@ -65,15 +65,15 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
allowParallelRun: false,
};
public readonly ConfigStyle = ConfigStyle;
SortingMethods = enumToTranslatedArray(SortingMethods);
SortingByTypes: { key: SortingMethod; value: string }[];
error: string;
constructor(
public settingsService: SettingsService,
public jobsService: ScheduledJobsService,
public backendTextService: BackendtextService,
public settingsService: SettingsService,
public jobsService: ScheduledJobsService,
public backendTextService: BackendtextService,
) {
this.JobTriggerTypeMap = [
{key: JobTriggerType.after, value: $localize`after`},
@ -91,6 +91,22 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
$localize`Sunday`,
$localize`day`,
]; // 7
this.SortingByTypes = [];
for (const enumMember in SortingByTypes) {
const key = parseInt(enumMember, 10);
if (key >= 0) {
this.SortingByTypes.push({
key: {method: key, ascending: true} as SortingMethod,
value: EnumTranslations[SortingByTypes[enumMember]] || SortingByTypes[enumMember] + $localize`ascending`
});
this.SortingByTypes.push({
key: {method: key, ascending: false} as SortingMethod,
value: EnumTranslations[SortingByTypes[enumMember]] || SortingByTypes[enumMember] + $localize`descending`
});
}
}
}
atTimeLocal(atTime: number): Date {
@ -115,27 +131,27 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
remove(schedule: JobScheduleDTO): void {
this.schedules.splice(
this.schedules.indexOf(schedule),
1
this.schedules.indexOf(schedule),
1
);
}
jobTypeChanged(schedule: JobScheduleDTO): void {
const job = this.jobsService.availableJobs.value.find(
(t) => t.Name === schedule.jobName
(t) => t.Name === schedule.jobName
);
schedule.config = schedule.config || {};
if (job.ConfigTemplate) {
job.ConfigTemplate.forEach(
(ct) => (schedule.config[ct.id] = ct.defaultValue)
(ct) => (schedule.config[ct.id] = ct.defaultValue)
);
}
}
jobTriggerTypeChanged(
triggerType: JobTriggerType,
schedule: JobScheduleDTO
triggerType: JobTriggerType,
schedule: JobScheduleDTO
): void {
switch (triggerType) {
case JobTriggerType.never:
@ -165,7 +181,7 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
value = value.replace(new RegExp(',', 'g'), ';');
value = value.replace(new RegExp(' ', 'g'), ';');
configElement[id] = value
.split(';').filter((i: string) => i != '');
.split(';').filter((i: string) => i != '');
}
getArray(configElement: Record<string, number[]>, id: string): string {
@ -176,46 +192,46 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
value = value.replace(new RegExp(',', 'g'), ';');
value = value.replace(new RegExp(' ', 'g'), ';');
configElement[id] = value
.split(';')
.map((s: string) => parseInt(s, 10))
.filter((i: number) => !isNaN(i) && i > 0);
.split(';')
.map((s: string) => parseInt(s, 10))
.filter((i: number) => !isNaN(i) && i > 0);
}
public shouldIdent(curr: JobScheduleDTO, prev: JobScheduleDTO): boolean {
return (
curr &&
curr.trigger.type === JobTriggerType.after &&
prev &&
prev.name === curr.trigger.afterScheduleName
curr &&
curr.trigger.type === JobTriggerType.after &&
prev &&
prev.name === curr.trigger.afterScheduleName
);
}
public sortedSchedules(): JobScheduleDTO[] {
return (this.schedules || [])
.slice()
.sort((a: JobScheduleDTO, b: JobScheduleDTO) => {
return (
this.getNextRunningDate(a, this.schedules) -
this.getNextRunningDate(b, this.schedules)
);
});
.slice()
.sort((a: JobScheduleDTO, b: JobScheduleDTO) => {
return (
this.getNextRunningDate(a, this.schedules) -
this.getNextRunningDate(b, this.schedules)
);
});
}
prepareNewJob(): void {
const jobName = this.jobsService.availableJobs.value[0].Name;
this.newSchedule = new JobScheduleConfig('new job',
jobName,
new NeverJobTriggerConfig());
jobName,
new NeverJobTriggerConfig());
// setup job specific config
const job = this.jobsService.availableJobs.value.find(
(t) => t.Name === jobName
(t) => t.Name === jobName
);
this.newSchedule.config = this.newSchedule.config || {};
if (job.ConfigTemplate) {
job.ConfigTemplate.forEach(
(ct) => (this.newSchedule.config[ct.id] = ct.defaultValue)
(ct) => (this.newSchedule.config[ct.id] = ct.defaultValue)
);
}
this.jobModalQL.first.show();
@ -225,12 +241,12 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
// make unique job name
const jobName = this.newSchedule.jobName;
const count = this.schedules.filter(
(s: JobScheduleDTO) => s.jobName === jobName
(s: JobScheduleDTO) => s.jobName === jobName
).length;
this.newSchedule.name =
count === 0
? jobName
: this.backendTextService.getJobName(jobName) + ' ' + (count + 1);
count === 0
? jobName
: this.backendTextService.getJobName(jobName) + ' ' + (count + 1);
this.schedules.push(this.newSchedule);
this.jobModalQL.first.hide();
@ -242,24 +258,24 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
}
private getNextRunningDate(
sch: JobScheduleDTO,
list: JobScheduleDTO[],
depth = 0
sch: JobScheduleDTO,
list: JobScheduleDTO[],
depth = 0
): number {
if (depth > list.length) {
return 0;
}
if (sch.trigger.type === JobTriggerType.never) {
return (
list
.map((s) => s.name)
.sort()
.indexOf(sch.name) * -1
list
.map((s) => s.name)
.sort()
.indexOf(sch.name) * -1
);
}
if (sch.trigger.type === JobTriggerType.after) {
const parent = list.find(
(s) => s.name === (sch.trigger as AfterJobTrigger).afterScheduleName
(s) => s.name === (sch.trigger as AfterJobTrigger).afterScheduleName
);
if (parent) {
return this.getNextRunningDate(parent, list, depth + 1) + 0.001;
@ -294,10 +310,6 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
}
AsSortArray(configElement: string | number | string[] | number[] | MediaPickDTO[]): SortingMethods[] {
return configElement as SortingMethods[];
}
AsMediaPickDTOArray(configElement: string | number | string[] | number[] | MediaPickDTO[]): MediaPickDTO[] {
return configElement as MediaPickDTO[];
}
@ -306,14 +318,14 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
configElement.splice(i, 1);
}
AddNewSorting(configElement: string | number | string[] | number[] | MediaPickDTO[]): void {
(configElement as SortingMethods[]).push(SortingMethods.ascDate);
AddNewSorting(configElement: string | number | string[] | number[] | MediaPickDTO[] | SortingMethod[]): void {
(configElement as SortingMethod[]).push({method: SortingByTypes.Date, ascending: true});
}
AddNewMediaPickDTO(configElement: string | number | string[] | number[] | MediaPickDTO[]): void {
(configElement as MediaPickDTO[]).push({
searchQuery: {type: SearchQueryTypes.any_text, text: ''} as TextSearch,
sortBy: [SortingMethods.descRating],
sortBy: [{method: SortingByTypes.Rating, ascending: true}],
pick: 5
});
}

View File

@ -1,37 +1,17 @@
<ng-container [ngSwitch]="method">
<ng-container *ngSwitchCase="SortingMethods.descName">
<ng-icon name="ionArrowDownOutline"></ng-icon>
<ng-container *ngSwitchCase="SortingByTypes.Name">
<span>A</span>
</ng-container>
<ng-container *ngSwitchCase="SortingMethods.ascName">
<ng-icon name="ionArrowUpOutline"></ng-icon>
<span>A</span>
</ng-container>
<ng-container *ngSwitchCase="SortingMethods.descRating">
<ng-icon name="ionArrowDownOutline"></ng-icon>
<ng-container *ngSwitchCase="SortingByTypes.Rating">
<ng-icon name="ionStarOutline"></ng-icon>
</ng-container>
<ng-container *ngSwitchCase="SortingMethods.ascRating">
<ng-icon name="ionArrowUpOutline"></ng-icon>
<ng-icon name="ionStarOutline"></ng-icon>
</ng-container>
<ng-container *ngSwitchCase="SortingMethods.descDate">
<ng-icon name="ionArrowDownOutline"></ng-icon>
<ng-container *ngSwitchCase="SortingByTypes.Date">
<ng-icon name="ionCalendarOutline"></ng-icon>
</ng-container>
<ng-container *ngSwitchCase="SortingMethods.ascDate">
<ng-icon name="ionArrowUpOutline"></ng-icon>
<ng-icon name="ionCalendarOutline"></ng-icon>
</ng-container>
<ng-container *ngSwitchCase="SortingMethods.descPersonCount">
<ng-icon name="ionArrowDownOutline"></ng-icon>
<ng-container *ngSwitchCase="SortingByTypes.PersonCount">
<ng-icon name="ionPersonOutline"></ng-icon>
</ng-container>
<ng-container *ngSwitchCase="SortingMethods.ascPersonCount">
<ng-icon name="ionArrowUpOutline"></ng-icon>
<ng-icon name="ionPersonOutline"></ng-icon>
</ng-container>
<ng-container *ngSwitchCase="SortingMethods.random">
<ng-container *ngSwitchCase="SortingByTypes.random">
<ng-icon name="ionShuffleOutline"></ng-icon>
</ng-container>
</ng-container>

View File

@ -1,5 +1,5 @@
import {Component, Input} from '@angular/core';
import {SortingMethods} from '../../../../common/entities/SortingMethods';
import {SortingByTypes} from '../../../../common/entities/SortingMethods';
@Component({
selector: 'app-sorting-method-icon',
@ -7,6 +7,6 @@ import {SortingMethods} from '../../../../common/entities/SortingMethods';
styleUrls: ['./sorting-method-icon.component.css']
})
export class SortingMethodIconComponent {
@Input() method: SortingMethods;
SortingMethods = SortingMethods;
@Input() method: SortingByTypes;
SortingByTypes = SortingByTypes;
}