mirror of
https://github.com/bpatrik/pigallery2.git
synced 2024-12-23 01:27:14 +02:00
Implementing filter #287
This commit is contained in:
parent
169d59fb8e
commit
a6b14534ee
@ -3,7 +3,7 @@ import {BrowserModule, HAMMER_GESTURE_CONFIG, HammerGestureConfig, HammerModule}
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {AppComponent} from './app.component';
|
||||
import {UserService} from './model/network/user.service';
|
||||
import {GalleryService} from './ui/gallery/gallery.service';
|
||||
import {ContentService} from './ui/gallery/content.service';
|
||||
import {NetworkService} from './model/network/network.service';
|
||||
import {GalleryCacheService} from './ui/gallery/cache.gallery.service';
|
||||
import {FullScreenService} from './ui/gallery/fullscreen.service';
|
||||
@ -116,6 +116,7 @@ import {PreviewSettingsComponent} from './ui/settings/preview/preview.settings.c
|
||||
import {GallerySearchFieldComponent} from './ui/gallery/search/search-field/search-field.gallery.component';
|
||||
import {GalleryFilterComponent} from './ui/gallery/filter/filter.gallery.component';
|
||||
import {GallerySortingService} from './ui/gallery/navigator/sorting.service';
|
||||
import {FilterService} from './ui/gallery/filter/filter.service';
|
||||
|
||||
@Injectable()
|
||||
export class MyHammerConfig extends HammerGestureConfig {
|
||||
@ -272,7 +273,8 @@ Marker.prototype.options.icon = iconDefault;
|
||||
UserService,
|
||||
AlbumsService,
|
||||
GalleryCacheService,
|
||||
GalleryService,
|
||||
ContentService,
|
||||
FilterService,
|
||||
GallerySortingService,
|
||||
MapService,
|
||||
BlogService,
|
||||
|
@ -3,7 +3,7 @@ import {ShareService} from '../ui/gallery/share.service';
|
||||
import {MediaDTO} from '../../../common/entities/MediaDTO';
|
||||
import {QueryParams} from '../../../common/QueryParams';
|
||||
import {Utils} from '../../../common/Utils';
|
||||
import {GalleryService} from '../ui/gallery/gallery.service';
|
||||
import {ContentService} from '../ui/gallery/content.service';
|
||||
import {Config} from '../../../common/config/public/Config';
|
||||
import {ParentDirectoryDTO, SubDirectoryDTO} from '../../../common/entities/DirectoryDTO';
|
||||
|
||||
@ -12,7 +12,7 @@ export class QueryService {
|
||||
|
||||
|
||||
constructor(private shareService: ShareService,
|
||||
private galleryService: GalleryService) {
|
||||
private galleryService: ContentService) {
|
||||
}
|
||||
|
||||
getMediaStringId(media: MediaDTO): string {
|
||||
|
@ -16,7 +16,7 @@ import {FileDTO} from '../../../../common/entities/FileDTO';
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class GalleryService {
|
||||
export class ContentService {
|
||||
|
||||
public content: BehaviorSubject<ContentWrapperWithError>;
|
||||
public directoryContent: Observable<DirectoryContent>;
|
@ -0,0 +1,23 @@
|
||||
.filter-column {
|
||||
max-height: 12em;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.filter-option {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.filter-container {
|
||||
margin-top: -1rem;
|
||||
}
|
||||
|
||||
.filter-container > card-body {
|
||||
margin: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.unselected {
|
||||
color: #6c757d;
|
||||
background-color: #fff;
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
<div class="card bg-light filter-container">
|
||||
<div class="card-body row ">
|
||||
<div class="col-3" *ngFor="let filter of filterService.selectedFilters | async; let i=index">
|
||||
<select [(ngModel)]="filter.filter"
|
||||
(ngModelChange)="filterService.onFilterChange()"
|
||||
class="form-control" id="gallery-filter-{{i}}">
|
||||
<option *ngFor="let f of filterService.AVAILABLE_FILTERS"
|
||||
[ngValue]="f">{{f.name}}</option>
|
||||
</select>
|
||||
<div class="filter-column">
|
||||
<ul class="list-group" *ngIf="filter.options.length > 0">
|
||||
<li
|
||||
*ngFor="let option of filter.options"
|
||||
[class.unselected] = "!option.selected"
|
||||
(click)="option.selected = !option.selected; filterService.onFilterChange()"
|
||||
class="filter-option list-group-item list-group-item-action d-flex justify-content-between align-items-center">
|
||||
{{option.name === undefined ? unknownText : option.name}}
|
||||
<span class="badge badge-pill"
|
||||
[class.badge-primary] = "option.selected"
|
||||
[class.badge-secondary] = "!option.selected"
|
||||
>{{option.count}}</span>
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
<div class="card-body text-center" *ngIf="filter.options.length === 0" i18n>Nothing to filter</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,29 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {RouterLink} from '@angular/router';
|
||||
import {FilterService} from './filter.service';
|
||||
import {OnDestroy, OnInit} from '../../../../../../node_modules/@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-gallery-filter',
|
||||
styleUrls: ['./filter.gallery.component.css'],
|
||||
templateUrl: './filter.gallery.component.html',
|
||||
providers: [RouterLink],
|
||||
})
|
||||
export class GalleryFilterComponent implements OnInit, OnDestroy {
|
||||
public readonly unknownText;
|
||||
|
||||
constructor(public filterService: FilterService) {
|
||||
this.unknownText = '<' + $localize`unknown` + '>';
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
setTimeout(() => this.filterService.setShowingFilters(false));
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
this.filterService.setShowingFilters(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
173
src/frontend/app/ui/gallery/filter/filter.service.ts
Normal file
173
src/frontend/app/ui/gallery/filter/filter.service.ts
Normal file
@ -0,0 +1,173 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {BehaviorSubject, Observable} from 'rxjs';
|
||||
import {PhotoDTO} from '../../../../../common/entities/PhotoDTO';
|
||||
import {DirectoryContent} from '../content.service';
|
||||
import {map, mergeMap} from 'rxjs/operators';
|
||||
|
||||
export enum FilterRenderType {
|
||||
enum = 1, range = 2
|
||||
}
|
||||
|
||||
interface Filter {
|
||||
name: string;
|
||||
mapFn: (m: PhotoDTO) => (string | number)[] | (string | number);
|
||||
renderType: FilterRenderType;
|
||||
isArrayValue?: boolean;
|
||||
}
|
||||
|
||||
interface SelectedFilter {
|
||||
filter: Filter;
|
||||
options: { name: string, count: number, selected: boolean }[];
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class FilterService {
|
||||
public readonly AVAILABLE_FILTERS: Filter[] = [
|
||||
{
|
||||
name: $localize`Keywords`,
|
||||
mapFn: (m: PhotoDTO): string[] => m.metadata.keywords,
|
||||
renderType: FilterRenderType.enum,
|
||||
isArrayValue: true,
|
||||
},
|
||||
{
|
||||
name: $localize`Faces`,
|
||||
mapFn: (m: PhotoDTO): string[] => m.metadata.faces?.map(f => f.name),
|
||||
renderType: FilterRenderType.enum,
|
||||
isArrayValue: true,
|
||||
},
|
||||
/* {
|
||||
name: $localize`Date`,
|
||||
mapFn: (m: PhotoDTO): number => m.metadata.creationDate,
|
||||
renderType: FilterRenderType.date
|
||||
},*/
|
||||
|
||||
|
||||
{
|
||||
name: $localize`Rating`,
|
||||
mapFn: (m: PhotoDTO): number => m.metadata.rating,
|
||||
renderType: FilterRenderType.enum
|
||||
},
|
||||
{
|
||||
name: $localize`Camera`,
|
||||
mapFn: (m: PhotoDTO): string => m.metadata.cameraData?.model,
|
||||
renderType: FilterRenderType.enum
|
||||
},
|
||||
{
|
||||
name: $localize`City`,
|
||||
mapFn: (m: PhotoDTO): string => m.metadata.positionData?.city,
|
||||
renderType: FilterRenderType.enum
|
||||
},
|
||||
{
|
||||
name: $localize`State`,
|
||||
mapFn: (m: PhotoDTO): string => m.metadata.positionData?.state,
|
||||
renderType: FilterRenderType.enum
|
||||
},
|
||||
{
|
||||
name: $localize`Country`,
|
||||
mapFn: (m: PhotoDTO): string => m.metadata.positionData?.country,
|
||||
renderType: FilterRenderType.enum
|
||||
},
|
||||
];
|
||||
|
||||
public readonly selectedFilters = new BehaviorSubject<SelectedFilter[]>([
|
||||
{
|
||||
filter: this.AVAILABLE_FILTERS[0],
|
||||
options: []
|
||||
}, {
|
||||
filter: this.AVAILABLE_FILTERS[1],
|
||||
options: []
|
||||
}, {
|
||||
filter: this.AVAILABLE_FILTERS[4],
|
||||
options: []
|
||||
}, {
|
||||
filter: this.AVAILABLE_FILTERS[2],
|
||||
options: []
|
||||
}
|
||||
]);
|
||||
filtersVisible = false;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
public applyFilters(directoryContent: Observable<DirectoryContent>): Observable<DirectoryContent> {
|
||||
return directoryContent.pipe(mergeMap((dirContent: DirectoryContent) => {
|
||||
return this.selectedFilters.pipe(map((filters: SelectedFilter[]) => {
|
||||
if (!dirContent || !dirContent.media || !this.filtersVisible) {
|
||||
return dirContent;
|
||||
}
|
||||
|
||||
// clone, so the original won't get overwritten
|
||||
const c = {
|
||||
media: dirContent.media,
|
||||
directories: dirContent.directories,
|
||||
metaFile: dirContent.metaFile
|
||||
};
|
||||
for (const f of filters) {
|
||||
|
||||
// get options
|
||||
const valueMap: { [key: string]: any } = {};
|
||||
f.options.forEach(o => {
|
||||
valueMap[o.name] = o;
|
||||
o.count = 0; // reset count so unknown option can be removed at the end
|
||||
});
|
||||
|
||||
if (f.filter.isArrayValue) {
|
||||
c.media.forEach(m => {
|
||||
(f.filter.mapFn(m as PhotoDTO) as string[])?.forEach(v => {
|
||||
valueMap[v] = valueMap[v] || {name: v, count: 0, selected: true};
|
||||
valueMap[v].count++;
|
||||
});
|
||||
});
|
||||
} else {
|
||||
c.media.forEach(m => {
|
||||
const key = f.filter.mapFn(m as PhotoDTO) as string;
|
||||
valueMap[key] = valueMap[key] || {name: key, count: 0, selected: true};
|
||||
valueMap[key].count++;
|
||||
});
|
||||
}
|
||||
|
||||
f.options = Object.values(valueMap).filter(o => o.count > 0).sort((a, b) => b.count - a.count);
|
||||
|
||||
|
||||
// apply filters
|
||||
f.options.forEach(opt => {
|
||||
if (opt.selected) {
|
||||
return;
|
||||
}
|
||||
if (f.filter.isArrayValue) {
|
||||
c.media = c.media.filter(m => {
|
||||
const mapped = (f.filter.mapFn(m as PhotoDTO) as string[]);
|
||||
if (!mapped) {
|
||||
return true;
|
||||
}
|
||||
return mapped.indexOf(opt.name) === -1;
|
||||
});
|
||||
} else {
|
||||
c.media = c.media.filter(m => (f.filter.mapFn(m as PhotoDTO) as string) !== opt.name);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
return c;
|
||||
}));
|
||||
}));
|
||||
}
|
||||
|
||||
public onFilterChange(): void {
|
||||
this.selectedFilters.next(this.selectedFilters.value);
|
||||
}
|
||||
|
||||
setShowingFilters(value: boolean): void {
|
||||
if (this.filtersVisible === value) {
|
||||
return;
|
||||
}
|
||||
this.filtersVisible = value;
|
||||
if (!this.filtersVisible) {
|
||||
this.selectedFilters.value.forEach(f => f.options = []);
|
||||
}
|
||||
this.onFilterChange();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,14 +45,14 @@
|
||||
|
||||
|
||||
<app-gallery-directories class="directories"
|
||||
[directories]="(directoryContent | async)?.directories || []"></app-gallery-directories>
|
||||
[directories]="directoryContent.directories || []"></app-gallery-directories>
|
||||
|
||||
<div class="blog-map-row">
|
||||
<div class="blog-wrapper"
|
||||
[style.width]="blogOpen ? '100%' : 'calc(100% - 100px)'"
|
||||
*ngIf="config.Client.MetaFile.markdown && (directoryContent | async)?.metaFile && ((directoryContent | async).metaFile | mdFiles).length>0">
|
||||
*ngIf="config.Client.MetaFile.markdown && directoryContent.metaFile && (directoryContent.metaFile | mdFiles).length>0">
|
||||
<app-gallery-blog [collapsed]="!blogOpen"
|
||||
[mdFiles]="(directoryContent | async).metaFile | mdFiles"></app-gallery-blog>
|
||||
[mdFiles]="directoryContent.metaFile | mdFiles"></app-gallery-blog>
|
||||
|
||||
|
||||
<button class="btn btn-blog-details" (click)="blogOpen=!blogOpen"><span
|
||||
@ -60,10 +60,10 @@
|
||||
</button>
|
||||
</div>
|
||||
<app-gallery-map *ngIf="isPhotoWithLocation && mapEnabled"
|
||||
[photos]="(directoryContent | async).media | photosOnly"
|
||||
[gpxFiles]="(directoryContent | async).metaFile | gpxFiles"></app-gallery-map>
|
||||
[photos]="directoryContent.media | photosOnly"
|
||||
[gpxFiles]="directoryContent.metaFile | gpxFiles"></app-gallery-map>
|
||||
</div>
|
||||
<app-gallery-grid [mediaObs]="mediaObs"
|
||||
<app-gallery-grid [media]="directoryContent.media"
|
||||
[lightbox]="lightbox"></app-gallery-grid>
|
||||
|
||||
</ng-container>
|
||||
|
@ -1,23 +1,20 @@
|
||||
import {Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
|
||||
import {AuthenticationService} from '../../model/network/authentication.service';
|
||||
import {ActivatedRoute, Params, Router} from '@angular/router';
|
||||
import {ContentWrapperWithError, DirectoryContent, GalleryService} from './gallery.service';
|
||||
import {ContentService, ContentWrapperWithError, DirectoryContent} from './content.service';
|
||||
import {GalleryGridComponent} from './grid/grid.gallery.component';
|
||||
import {Config} from '../../../../common/config/public/Config';
|
||||
import {ParentDirectoryDTO, SubDirectoryDTO} from '../../../../common/entities/DirectoryDTO';
|
||||
import {SearchResultDTO} from '../../../../common/entities/SearchResultDTO';
|
||||
import {ShareService} from './share.service';
|
||||
import {NavigationService} from '../../model/navigation.service';
|
||||
import {UserRoles} from '../../../../common/entities/UserDTO';
|
||||
import {interval, Observable, Subscription} from 'rxjs';
|
||||
import {ContentWrapper} from '../../../../common/entities/ConentWrapper';
|
||||
import {PageHelper} from '../../model/page.helper';
|
||||
import {PhotoDTO} from '../../../../common/entities/PhotoDTO';
|
||||
import {QueryParams} from '../../../../common/QueryParams';
|
||||
import {map, take} from 'rxjs/operators';
|
||||
import {take} from 'rxjs/operators';
|
||||
import {GallerySortingService} from './navigator/sorting.service';
|
||||
import {Media} from './Media';
|
||||
import {MediaDTO} from '../../../../common/entities/MediaDTO';
|
||||
import {FilterService} from './filter/filter.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-gallery',
|
||||
@ -34,11 +31,10 @@ export class GalleryComponent implements OnInit, OnDestroy {
|
||||
public blogOpen = false;
|
||||
|
||||
config = Config;
|
||||
public directories: SubDirectoryDTO[] = [];
|
||||
public isPhotoWithLocation = false;
|
||||
public countDown: { day: number, hour: number, minute: number, second: number } = null;
|
||||
public readonly mapEnabled: boolean;
|
||||
public readonly directoryContent: Observable<DirectoryContent>;
|
||||
public directoryContent: DirectoryContent;
|
||||
public readonly mediaObs: Observable<MediaDTO[]>;
|
||||
private $counter: Observable<number>;
|
||||
private subscription: { [key: string]: Subscription } = {
|
||||
@ -48,16 +44,15 @@ export class GalleryComponent implements OnInit, OnDestroy {
|
||||
sorting: null
|
||||
};
|
||||
|
||||
constructor(public galleryService: GalleryService,
|
||||
constructor(public galleryService: ContentService,
|
||||
private authService: AuthenticationService,
|
||||
private router: Router,
|
||||
private shareService: ShareService,
|
||||
private route: ActivatedRoute,
|
||||
private navigation: NavigationService,
|
||||
private filterService: FilterService,
|
||||
private sortingService: GallerySortingService) {
|
||||
this.mapEnabled = Config.Client.Map.enabled;
|
||||
this.directoryContent = this.sortingService.applySorting(this.galleryService.directoryContent);
|
||||
this.mediaObs = this.directoryContent.pipe(map(c => c?.media));
|
||||
PageHelper.showScrollY();
|
||||
}
|
||||
|
||||
@ -112,7 +107,10 @@ export class GalleryComponent implements OnInit, OnDestroy {
|
||||
this.showSearchBar = Config.Client.Search.enabled && this.authService.canSearch();
|
||||
this.showShare = Config.Client.Sharing.enabled && this.authService.isAuthorized(UserRoles.User);
|
||||
this.showRandomPhotoBuilder = Config.Client.RandomPhoto.enabled && this.authService.isAuthorized(UserRoles.User);
|
||||
this.subscription.content = this.galleryService.content.subscribe(this.onContentChange);
|
||||
this.subscription.content = this.sortingService.applySorting(
|
||||
this.filterService.applyFilters(this.galleryService.directoryContent)).subscribe((dc: DirectoryContent) => {
|
||||
this.onContentChange(dc);
|
||||
});
|
||||
this.subscription.route = this.route.params.subscribe(this.onRoute);
|
||||
|
||||
if (this.shareService.isSharing()) {
|
||||
@ -149,16 +147,17 @@ export class GalleryComponent implements OnInit, OnDestroy {
|
||||
this.galleryService.loadDirectory(directoryName);
|
||||
};
|
||||
|
||||
private onContentChange = (content: ContentWrapper): void => {
|
||||
const tmp = (content.searchResult || content.directory || {
|
||||
directories: [],
|
||||
media: []
|
||||
}) as ParentDirectoryDTO | SearchResultDTO;
|
||||
this.directories = tmp.directories;
|
||||
// this.sortDirectories();
|
||||
private onContentChange = (content: DirectoryContent): void => {
|
||||
if (!content) {
|
||||
return;
|
||||
}
|
||||
this.directoryContent = content;
|
||||
|
||||
// enforce change detection on grid
|
||||
this.directoryContent.media = this.directoryContent.media?.slice();
|
||||
this.isPhotoWithLocation = false;
|
||||
|
||||
for (const media of tmp.media as PhotoDTO[]) {
|
||||
for (const media of content.media as PhotoDTO[]) {
|
||||
if (media.metadata &&
|
||||
media.metadata.positionData &&
|
||||
media.metadata.positionData.GPSData &&
|
||||
|
@ -5,6 +5,7 @@ import {
|
||||
ElementRef,
|
||||
HostListener,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
QueryList,
|
||||
@ -18,10 +19,10 @@ import {GalleryPhotoComponent} from './photo/photo.grid.gallery.component';
|
||||
import {OverlayService} from '../overlay.service';
|
||||
import {Config} from '../../../../../common/config/public/Config';
|
||||
import {PageHelper} from '../../../model/page.helper';
|
||||
import {Observable, Subscription} from 'rxjs';
|
||||
import {Subscription} from 'rxjs';
|
||||
import {ActivatedRoute, Params, Router} from '@angular/router';
|
||||
import {QueryService} from '../../../model/query.service';
|
||||
import {GalleryService} from '../gallery.service';
|
||||
import {ContentService} from '../content.service';
|
||||
import {MediaDTO, MediaDTOUtils} from '../../../../../common/entities/MediaDTO';
|
||||
import {QueryParams} from '../../../../../common/QueryParams';
|
||||
|
||||
@ -30,13 +31,12 @@ import {QueryParams} from '../../../../../common/QueryParams';
|
||||
templateUrl: './grid.gallery.component.html',
|
||||
styleUrls: ['./grid.gallery.component.css'],
|
||||
})
|
||||
export class GalleryGridComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
export class GalleryGridComponent implements OnInit, OnChanges, AfterViewInit, OnDestroy {
|
||||
|
||||
@ViewChild('gridContainer', {static: false}) gridContainer: ElementRef;
|
||||
@ViewChildren(GalleryPhotoComponent) gridPhotoQL: QueryList<GalleryPhotoComponent>;
|
||||
@Input() mediaObs: Observable<MediaDTO[]>;
|
||||
@Input() lightbox: GalleryLightboxComponent;
|
||||
media: MediaDTO[];
|
||||
@Input() media: MediaDTO[];
|
||||
photosToRender: GridMedia[] = [];
|
||||
containerWidth = 0;
|
||||
screenHeight = 0;
|
||||
@ -60,11 +60,15 @@ export class GalleryGridComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
private changeDetector: ChangeDetectorRef,
|
||||
public queryService: QueryService,
|
||||
private router: Router,
|
||||
public galleryService: GalleryService,
|
||||
public galleryService: ContentService,
|
||||
private route: ActivatedRoute) {
|
||||
|
||||
}
|
||||
|
||||
ngOnChanges(): void {
|
||||
this.onChange();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.subscriptions.route = this.route.queryParams.subscribe((params: Params): void => {
|
||||
if (params[QueryParams.gallery.photo] && params[QueryParams.gallery.photo] !== '') {
|
||||
@ -76,10 +80,6 @@ export class GalleryGridComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.renderUpToMedia(params[QueryParams.gallery.photo]);
|
||||
}
|
||||
});
|
||||
this.mediaObs.subscribe((m) => {
|
||||
this.media = m || [];
|
||||
this.onChange();
|
||||
});
|
||||
}
|
||||
|
||||
onChange = () => {
|
||||
|
@ -11,7 +11,7 @@ import {PageHelper} from '../../../model/page.helper';
|
||||
import {QueryService} from '../../../model/query.service';
|
||||
import {MediaDTO} from '../../../../../common/entities/MediaDTO';
|
||||
import {QueryParams} from '../../../../../common/QueryParams';
|
||||
import {GalleryService} from '../gallery.service';
|
||||
import {ContentService} from '../content.service';
|
||||
import {PhotoDTO} from '../../../../../common/entities/PhotoDTO';
|
||||
import {ControlsLightboxComponent} from './controls/controls.lightbox.gallery.component';
|
||||
import {SupportedFormats} from '../../../../../common/SupportedFormats';
|
||||
@ -64,7 +64,7 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
private builder: AnimationBuilder,
|
||||
private router: Router,
|
||||
private queryService: QueryService,
|
||||
private galleryService: GalleryService,
|
||||
private galleryService: ContentService,
|
||||
private route: ActivatedRoute) {
|
||||
}
|
||||
|
||||
|
@ -12,15 +12,15 @@
|
||||
|
||||
.photos-count {
|
||||
display: inline-block;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.btn-download {
|
||||
padding-left: 1px;
|
||||
padding-right: 1px;
|
||||
.btn-navbar {
|
||||
}
|
||||
|
||||
|
||||
.divider {
|
||||
margin: 5px 5px 5px 10px;
|
||||
margin-left: 5px;
|
||||
border-left: 1px solid #6c757d;
|
||||
display: inline-block;
|
||||
}
|
||||
@ -33,7 +33,7 @@ ol {
|
||||
min-width: 13rem;
|
||||
}
|
||||
|
||||
.dropdown-item{
|
||||
.dropdown-item {
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
<ol *ngIf="!(isDirectory | async)" class="breadcrumb">
|
||||
<li class="active">
|
||||
<ng-container i18n>Searching for:</ng-container>
|
||||
<strong> {{(wrappedContent | async).searchQuery | searchQuery}}</strong>
|
||||
<strong> {{(wrappedContent | async)?.searchResult?.searchQuery | searchQuery}}</strong>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
|
||||
<ng-container *ngIf="config.Client.Other.enableDownloadZip && (isDirectory | async) && (ItemCount | async) > 0">
|
||||
<a [href]="getDownloadZipLink() | async"
|
||||
class="btn btn-download">
|
||||
class="btn btn-navbar">
|
||||
<span class="oi oi-data-transfer-download"
|
||||
title="download" i18n-title></span>
|
||||
</a>
|
||||
@ -34,12 +34,19 @@
|
||||
<ng-container *ngIf="config.Client.Other.enableDirectoryFlattening && (isDirectory | async)">
|
||||
<a
|
||||
[routerLink]="['/search', getDirectoryFlattenSearchQuery() | async]"
|
||||
class="btn btn-download">
|
||||
class="btn btn-navbar">
|
||||
<span class="oi oi-fork"
|
||||
title="Flatten directory" i18n-title></span>
|
||||
</a>
|
||||
<div class="divider"> </div>
|
||||
</ng-container>
|
||||
<a class="btn btn-navbar"
|
||||
[class.btn-secondary]="showFilters"
|
||||
(click)="showFilters = ! showFilters">
|
||||
<span class="oi oi-spreadsheet"
|
||||
title="Filters" i18n-title></span>
|
||||
</a>
|
||||
<div class="divider"> </div>
|
||||
<div class="btn-group" dropdown placement="bottom right">
|
||||
<button id="button-alignment" dropdownToggle type="button"
|
||||
class="btn btn-secondary dropdown-toggle"
|
||||
@ -62,3 +69,5 @@
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
<app-gallery-filter *ngIf="showFilters"></app-gallery-filter>
|
||||
|
@ -3,7 +3,7 @@ import {RouterLink} from '@angular/router';
|
||||
import {UserDTOUtils} from '../../../../../common/entities/UserDTO';
|
||||
import {AuthenticationService} from '../../../model/network/authentication.service';
|
||||
import {QueryService} from '../../../model/query.service';
|
||||
import {ContentWrapperWithError, DirectoryContent, GalleryService} from '../gallery.service';
|
||||
import {ContentService, ContentWrapperWithError, DirectoryContent} from '../content.service';
|
||||
import {Utils} from '../../../../../common/Utils';
|
||||
import {SortingMethods} from '../../../../../common/entities/SortingMethods';
|
||||
import {Config} from '../../../../../common/config/public/Config';
|
||||
@ -27,12 +27,12 @@ export class GalleryNavigatorComponent {
|
||||
readonly SearchQueryTypes = SearchQueryTypes;
|
||||
wrappedContent: Observable<ContentWrapperWithError>;
|
||||
public directoryContent: Observable<DirectoryContent>;
|
||||
showFilters = false;
|
||||
private readonly RootFolderName: string;
|
||||
|
||||
|
||||
constructor(private authService: AuthenticationService,
|
||||
public queryService: QueryService,
|
||||
public galleryService: GalleryService,
|
||||
public galleryService: ContentService,
|
||||
public sortingService: GallerySortingService) {
|
||||
this.sortingMethodsType = Utils.enumToArray(SortingMethods);
|
||||
this.RootFolderName = $localize`Images`;
|
||||
|
@ -6,7 +6,7 @@ import {BehaviorSubject, Observable} from 'rxjs';
|
||||
import {Config} from '../../../../../common/config/public/Config';
|
||||
import {SortingMethods} from '../../../../../common/entities/SortingMethods';
|
||||
import {PG2ConfMap} from '../../../../../common/PG2ConfMap';
|
||||
import {DirectoryContent, GalleryService} from '../gallery.service';
|
||||
import {DirectoryContent, ContentService} from '../content.service';
|
||||
import {PhotoDTO} from '../../../../../common/entities/PhotoDTO';
|
||||
import {map, mergeMap} from 'rxjs/operators';
|
||||
import {SeededRandomService} from '../../../model/seededRandom.service';
|
||||
@ -19,7 +19,7 @@ export class GallerySortingService {
|
||||
|
||||
constructor(private networkService: NetworkService,
|
||||
private galleryCacheService: GalleryCacheService,
|
||||
private galleryService: GalleryService,
|
||||
private galleryService: ContentService,
|
||||
private rndService: SeededRandomService) {
|
||||
this.sorting = new BehaviorSubject<SortingMethods>(Config.Client.Other.defaultPhotoSortingMethod);
|
||||
this.galleryService.content.subscribe((c) => {
|
||||
@ -57,11 +57,16 @@ export class GallerySortingService {
|
||||
}
|
||||
|
||||
public applySorting(directoryContent: Observable<DirectoryContent>): Observable<DirectoryContent> {
|
||||
return directoryContent.pipe(mergeMap((c) => {
|
||||
return directoryContent.pipe(mergeMap((dirContent) => {
|
||||
return this.sorting.pipe(map((sorting: SortingMethods) => {
|
||||
if (!c) {
|
||||
return c;
|
||||
if (!dirContent) {
|
||||
return dirContent;
|
||||
}
|
||||
const c = {
|
||||
media: dirContent.media,
|
||||
directories: dirContent.directories,
|
||||
metaFile: dirContent.metaFile
|
||||
};
|
||||
if (c.directories) {
|
||||
switch (sorting) {
|
||||
case SortingMethods.ascRating: // directories do not have rating
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {Component, OnDestroy, OnInit, TemplateRef} from '@angular/core';
|
||||
import {GalleryService} from '../gallery.service';
|
||||
import {ContentService} from '../content.service';
|
||||
import {ContentWrapper} from '../../../../../common/entities/ConentWrapper';
|
||||
import {Config} from '../../../../../common/config/public/Config';
|
||||
import {NotificationService} from '../../../model/notification.service';
|
||||
@ -31,7 +31,7 @@ export class RandomQueryBuilderGalleryComponent implements OnInit, OnDestroy {
|
||||
|
||||
private readonly subscription: Subscription = null;
|
||||
|
||||
constructor(public galleryService: GalleryService,
|
||||
constructor(public galleryService: ContentService,
|
||||
private notification: NotificationService,
|
||||
private searchQueryParserService: SearchQueryParserService,
|
||||
private route: ActivatedRoute,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {Component, OnDestroy, TemplateRef} from '@angular/core';
|
||||
import {AutoCompleteService} from './autocomplete.service';
|
||||
import {ActivatedRoute, Params, Router, RouterLink} from '@angular/router';
|
||||
import {GalleryService} from '../gallery.service';
|
||||
import {ContentService} from '../content.service';
|
||||
import {Subscription} from 'rxjs';
|
||||
import {NavigationService} from '../../../model/navigation.service';
|
||||
import {QueryParams} from '../../../../../common/QueryParams';
|
||||
@ -34,7 +34,7 @@ export class GallerySearchComponent implements OnDestroy {
|
||||
|
||||
constructor(private autoCompleteService: AutoCompleteService,
|
||||
private searchQueryParserService: SearchQueryParserService,
|
||||
private galleryService: GalleryService,
|
||||
private galleryService: ContentService,
|
||||
private albumService: AlbumsService,
|
||||
private navigationService: NavigationService,
|
||||
private route: ActivatedRoute,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {Component, OnDestroy, OnInit, TemplateRef} from '@angular/core';
|
||||
import {Utils} from '../../../../../common/Utils';
|
||||
import {ShareService} from '../share.service';
|
||||
import {GalleryService} from '../gallery.service';
|
||||
import {ContentService} from '../content.service';
|
||||
import {ContentWrapper} from '../../../../../common/entities/ConentWrapper';
|
||||
import {SharingDTO} from '../../../../../common/entities/SharingDTO';
|
||||
import {Config} from '../../../../../common/config/public/Config';
|
||||
@ -44,7 +44,7 @@ export class GalleryShareComponent implements OnInit, OnDestroy {
|
||||
};
|
||||
|
||||
constructor(private sharingService: ShareService,
|
||||
public galleryService: GalleryService,
|
||||
public galleryService: ContentService,
|
||||
private notification: NotificationService,
|
||||
private modalService: BsModalService) {
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user