adding randomized sorting
This commit is contained in:
parent
38a99b1db2
commit
a613373537
@ -1,3 +1,3 @@
|
||||
export enum SortingMethods {
|
||||
ascName = 1, descName = 2, ascDate = 3, descDate = 4
|
||||
ascName = 1, descName = 2, ascDate = 3, descDate = 4, random = 5
|
||||
}
|
||||
|
||||
@ -73,6 +73,7 @@ import {FileSizePipe} from './pipes/FileSizePipe';
|
||||
import {DuplicateService} from './duplicates/duplicates.service';
|
||||
import {DuplicateComponent} from './duplicates/duplicates.component';
|
||||
import {DuplicatesPhotoComponent} from './duplicates/photo/photo.duplicates.component';
|
||||
import {SeededRandomService} from './model/seededRandom.service';
|
||||
|
||||
|
||||
@Injectable()
|
||||
@ -189,6 +190,7 @@ export function translationsFactory(locale: string) {
|
||||
FullScreenService,
|
||||
NavigationService,
|
||||
SettingsService,
|
||||
SeededRandomService,
|
||||
OverlayService,
|
||||
QueryService,
|
||||
DuplicateService,
|
||||
|
||||
@ -10,12 +10,13 @@ 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, Subscription, Observable} from 'rxjs';
|
||||
import {interval, Observable, Subscription} from 'rxjs';
|
||||
import {ContentWrapper} from '../../../common/entities/ConentWrapper';
|
||||
import {PageHelper} from '../model/page.helper';
|
||||
import {SortingMethods} from '../../../common/entities/SortingMethods';
|
||||
import {PhotoDTO} from '../../../common/entities/PhotoDTO';
|
||||
import {QueryParams} from '../../../common/QueryParams';
|
||||
import {SeededRandomService} from '../model/seededRandom.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-gallery',
|
||||
@ -32,6 +33,9 @@ export class GalleryComponent implements OnInit, OnDestroy {
|
||||
|
||||
public directories: DirectoryDTO[] = [];
|
||||
public isPhotoWithLocation = false;
|
||||
public countDown: { day: number, hour: number, minute: number, second: number } = null;
|
||||
public mapEnabled = true;
|
||||
readonly SearchTypes: typeof SearchTypes;
|
||||
private $counter: Observable<number>;
|
||||
private subscription: { [key: string]: Subscription } = {
|
||||
content: null,
|
||||
@ -39,16 +43,14 @@ export class GalleryComponent implements OnInit, OnDestroy {
|
||||
timer: null,
|
||||
sorting: null
|
||||
};
|
||||
public countDown: { day: number, hour: number, minute: number, second: number } = null;
|
||||
public mapEnabled = true;
|
||||
readonly SearchTypes: typeof SearchTypes;
|
||||
|
||||
constructor(public _galleryService: GalleryService,
|
||||
private _authService: AuthenticationService,
|
||||
private _router: Router,
|
||||
private shareService: ShareService,
|
||||
private _route: ActivatedRoute,
|
||||
private _navigation: NavigationService) {
|
||||
private _navigation: NavigationService,
|
||||
private rndService: SeededRandomService) {
|
||||
this.mapEnabled = Config.Client.Map.enabled;
|
||||
this.SearchTypes = SearchTypes;
|
||||
|
||||
@ -70,6 +72,46 @@ export class GalleryComponent implements OnInit, OnDestroy {
|
||||
this.countDown.second = t % 60;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.subscription.content !== null) {
|
||||
this.subscription.content.unsubscribe();
|
||||
}
|
||||
if (this.subscription.route !== null) {
|
||||
this.subscription.route.unsubscribe();
|
||||
}
|
||||
if (this.subscription.timer !== null) {
|
||||
this.subscription.timer.unsubscribe();
|
||||
}
|
||||
if (this.subscription.sorting !== null) {
|
||||
this.subscription.sorting.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
await this.shareService.wait();
|
||||
if (!this._authService.isAuthenticated() &&
|
||||
(!this.shareService.isSharing() ||
|
||||
(this.shareService.isSharing() && Config.Client.Sharing.passwordProtected === true))) {
|
||||
|
||||
return this._navigation.toLogin();
|
||||
}
|
||||
this.showSearchBar = Config.Client.Search.enabled && this._authService.isAuthorized(UserRoles.Guest);
|
||||
this.showShare = Config.Client.Sharing.enabled && this._authService.isAuthorized(UserRoles.User);
|
||||
this.showRandomPhotoBuilder = Config.Client.RandomPhoto.enabled && this._authService.isAuthorized(UserRoles.Guest);
|
||||
this.subscription.content = this._galleryService.content.subscribe(this.onContentChange);
|
||||
this.subscription.route = this._route.params.subscribe(this.onRoute);
|
||||
|
||||
if (this.shareService.isSharing()) {
|
||||
this.$counter = interval(1000);
|
||||
this.subscription.timer = this.$counter.subscribe((x) => this.updateTimer(x));
|
||||
}
|
||||
|
||||
this.subscription.sorting = this._galleryService.sorting.subscribe(() => {
|
||||
this.sortDirectories();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private onRoute = async (params: Params) => {
|
||||
const searchText = params[QueryParams.gallery.searchText];
|
||||
if (searchText && searchText !== '') {
|
||||
@ -100,21 +142,6 @@ export class GalleryComponent implements OnInit, OnDestroy {
|
||||
|
||||
};
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.subscription.content !== null) {
|
||||
this.subscription.content.unsubscribe();
|
||||
}
|
||||
if (this.subscription.route !== null) {
|
||||
this.subscription.route.unsubscribe();
|
||||
}
|
||||
if (this.subscription.timer !== null) {
|
||||
this.subscription.timer.unsubscribe();
|
||||
}
|
||||
if (this.subscription.sorting !== null) {
|
||||
this.subscription.sorting.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
private onContentChange = (content: ContentWrapper) => {
|
||||
const ascdirSorter = (a: DirectoryDTO, b: DirectoryDTO) => {
|
||||
return a.name.localeCompare(b.name);
|
||||
@ -169,34 +196,24 @@ export class GalleryComponent implements OnInit, OnDestroy {
|
||||
return 0;
|
||||
});
|
||||
break;
|
||||
case SortingMethods.random:
|
||||
this.rndService.setSeed(this.directories.length);
|
||||
this.directories.sort((a: DirectoryDTO, b: DirectoryDTO) => {
|
||||
if (a.name.toLowerCase() < b.name.toLowerCase()) {
|
||||
return 1;
|
||||
}
|
||||
if (a.name.toLowerCase() > b.name.toLowerCase()) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}).sort(() => {
|
||||
return this.rndService.get() - 0.5;
|
||||
});
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
await this.shareService.wait();
|
||||
if (!this._authService.isAuthenticated() &&
|
||||
(!this.shareService.isSharing() ||
|
||||
(this.shareService.isSharing() && Config.Client.Sharing.passwordProtected === true))) {
|
||||
|
||||
return this._navigation.toLogin();
|
||||
}
|
||||
this.showSearchBar = Config.Client.Search.enabled && this._authService.isAuthorized(UserRoles.Guest);
|
||||
this.showShare = Config.Client.Sharing.enabled && this._authService.isAuthorized(UserRoles.User);
|
||||
this.showRandomPhotoBuilder = Config.Client.RandomPhoto.enabled && this._authService.isAuthorized(UserRoles.Guest);
|
||||
this.subscription.content = this._galleryService.content.subscribe(this.onContentChange);
|
||||
this.subscription.route = this._route.params.subscribe(this.onRoute);
|
||||
|
||||
if (this.shareService.isSharing()) {
|
||||
this.$counter = interval(1000);
|
||||
this.subscription.timer = this.$counter.subscribe((x) => this.updateTimer(x));
|
||||
}
|
||||
|
||||
this.subscription.sorting = this._galleryService.sorting.subscribe(() => {
|
||||
this.sortDirectories();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -7,10 +7,10 @@ import {
|
||||
Input,
|
||||
OnChanges,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
QueryList,
|
||||
ViewChild,
|
||||
ViewChildren,
|
||||
OnInit
|
||||
ViewChildren
|
||||
} from '@angular/core';
|
||||
import {PhotoDTO} from '../../../../common/entities/PhotoDTO';
|
||||
import {GridRowBuilder} from './GridRowBuilder';
|
||||
@ -27,7 +27,7 @@ import {GalleryService} from '../gallery.service';
|
||||
import {SortingMethods} from '../../../../common/entities/SortingMethods';
|
||||
import {MediaDTO} from '../../../../common/entities/MediaDTO';
|
||||
import {QueryParams} from '../../../../common/QueryParams';
|
||||
import {Media} from '../Media';
|
||||
import {SeededRandomService} from '../../model/seededRandom.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-gallery-grid',
|
||||
@ -38,24 +38,13 @@ export class GalleryGridComponent implements OnChanges, OnInit, AfterViewInit, O
|
||||
|
||||
@ViewChild('gridContainer') gridContainer: ElementRef;
|
||||
@ViewChildren(GalleryPhotoComponent) gridPhotoQL: QueryList<GalleryPhotoComponent>;
|
||||
private scrollListenerPhotos: GalleryPhotoComponent[] = [];
|
||||
|
||||
@Input() media: MediaDTO[];
|
||||
@Input() lightbox: GalleryLightboxComponent;
|
||||
|
||||
photosToRender: Array<GridMedia> = [];
|
||||
containerWidth = 0;
|
||||
screenHeight = 0;
|
||||
|
||||
public IMAGE_MARGIN = 2;
|
||||
private TARGET_COL_COUNT = 5;
|
||||
private MIN_ROW_COUNT = 2;
|
||||
private MAX_ROW_COUNT = 5;
|
||||
|
||||
private onScrollFired = false;
|
||||
private helperTime: number = null;
|
||||
isAfterViewInit = false;
|
||||
private renderedPhotoIndex = 0;
|
||||
subscriptions: {
|
||||
route: Subscription,
|
||||
sorting: Subscription
|
||||
@ -64,13 +53,21 @@ export class GalleryGridComponent implements OnChanges, OnInit, AfterViewInit, O
|
||||
sorting: null
|
||||
};
|
||||
delayedRenderUpToPhoto: string = null;
|
||||
private scrollListenerPhotos: GalleryPhotoComponent[] = [];
|
||||
private TARGET_COL_COUNT = 5;
|
||||
private MIN_ROW_COUNT = 2;
|
||||
private MAX_ROW_COUNT = 5;
|
||||
private onScrollFired = false;
|
||||
private helperTime: number = null;
|
||||
private renderedPhotoIndex = 0;
|
||||
|
||||
constructor(private overlayService: OverlayService,
|
||||
private changeDetector: ChangeDetectorRef,
|
||||
public queryService: QueryService,
|
||||
private router: Router,
|
||||
public galleryService: GalleryService,
|
||||
private route: ActivatedRoute) {
|
||||
private route: ActivatedRoute,
|
||||
private rndService: SeededRandomService) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
@ -159,17 +156,6 @@ export class GalleryGridComponent implements OnChanges, OnInit, AfterViewInit, O
|
||||
this.isAfterViewInit = true;
|
||||
}
|
||||
|
||||
|
||||
private renderUpToMedia(mediaStringId: string) {
|
||||
const index = this.media.findIndex(p => this.queryService.getMediaStringId(p) === mediaStringId);
|
||||
if (index === -1) {
|
||||
this.router.navigate([], {queryParams: this.queryService.getParams()});
|
||||
return;
|
||||
}
|
||||
while (this.renderedPhotoIndex < index && this.renderARow()) {
|
||||
}
|
||||
}
|
||||
|
||||
public renderARow(): number {
|
||||
if (this.renderedPhotoIndex >= this.media.length
|
||||
|| this.containerWidth === 0) {
|
||||
@ -204,6 +190,37 @@ export class GalleryGridComponent implements OnChanges, OnInit, AfterViewInit, O
|
||||
return rowHeight;
|
||||
}
|
||||
|
||||
@HostListener('window:scroll')
|
||||
onScroll() {
|
||||
if (!this.onScrollFired &&
|
||||
// should we trigger this at all?
|
||||
(this.renderedPhotoIndex < this.media.length || this.scrollListenerPhotos.length > 0)) {
|
||||
window.requestAnimationFrame(() => {
|
||||
this.renderPhotos();
|
||||
|
||||
if (Config.Client.Other.enableOnScrollThumbnailPrioritising === true) {
|
||||
this.scrollListenerPhotos.forEach((pc: GalleryPhotoComponent) => {
|
||||
pc.onScroll();
|
||||
});
|
||||
this.scrollListenerPhotos = this.scrollListenerPhotos.filter(pc => pc.ScrollListener);
|
||||
}
|
||||
|
||||
this.onScrollFired = false;
|
||||
});
|
||||
this.onScrollFired = true;
|
||||
}
|
||||
}
|
||||
|
||||
private renderUpToMedia(mediaStringId: string) {
|
||||
const index = this.media.findIndex(p => this.queryService.getMediaStringId(p) === mediaStringId);
|
||||
if (index === -1) {
|
||||
this.router.navigate([], {queryParams: this.queryService.getParams()});
|
||||
return;
|
||||
}
|
||||
while (this.renderedPhotoIndex < index && this.renderARow()) {
|
||||
}
|
||||
}
|
||||
|
||||
private clearRenderedPhotos() {
|
||||
this.photosToRender = [];
|
||||
this.renderedPhotoIndex = 0;
|
||||
@ -244,6 +261,20 @@ export class GalleryGridComponent implements OnChanges, OnInit, AfterViewInit, O
|
||||
return b.metadata.creationDate - a.metadata.creationDate;
|
||||
});
|
||||
break;
|
||||
case SortingMethods.random:
|
||||
this.rndService.setSeed(this.media.length);
|
||||
this.media.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;
|
||||
}).sort(() => {
|
||||
return this.rndService.get() - 0.5;
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@ -281,7 +312,6 @@ export class GalleryGridComponent implements OnChanges, OnInit, AfterViewInit, O
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true, if scroll is >= 70% to render more images.
|
||||
* Or of onscroll rendering is off: return always to render all the images at once
|
||||
@ -294,28 +324,6 @@ export class GalleryGridComponent implements OnChanges, OnInit, AfterViewInit, O
|
||||
|| (document.body.clientHeight + offset) * 0.85 < window.innerHeight;
|
||||
}
|
||||
|
||||
|
||||
@HostListener('window:scroll')
|
||||
onScroll() {
|
||||
if (!this.onScrollFired &&
|
||||
// should we trigger this at all?
|
||||
(this.renderedPhotoIndex < this.media.length || this.scrollListenerPhotos.length > 0)) {
|
||||
window.requestAnimationFrame(() => {
|
||||
this.renderPhotos();
|
||||
|
||||
if (Config.Client.Other.enableOnScrollThumbnailPrioritising === true) {
|
||||
this.scrollListenerPhotos.forEach((pc: GalleryPhotoComponent) => {
|
||||
pc.onScroll();
|
||||
});
|
||||
this.scrollListenerPhotos = this.scrollListenerPhotos.filter(pc => pc.ScrollListener);
|
||||
}
|
||||
|
||||
this.onScrollFired = false;
|
||||
});
|
||||
this.onScrollFired = true;
|
||||
}
|
||||
}
|
||||
|
||||
private renderPhotos(numberOfPhotos: number = 0) {
|
||||
if (this.containerWidth === 0 ||
|
||||
this.renderedPhotoIndex >= this.media.length ||
|
||||
|
||||
@ -24,7 +24,7 @@ ol {
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
min-width: 16rem;
|
||||
min-width: 10rem;
|
||||
}
|
||||
|
||||
.row {
|
||||
|
||||
26
frontend/app/model/seededRandom.service.ts
Normal file
26
frontend/app/model/seededRandom.service.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
@Injectable()
|
||||
export class SeededRandomService {
|
||||
|
||||
private static readonly baseSeed = Math.random() * 2147483647;
|
||||
private seed: number;
|
||||
|
||||
constructor() {
|
||||
this.setSeed(0);
|
||||
|
||||
if (this.seed <= 0) {
|
||||
this.seed += 2147483646;
|
||||
}
|
||||
}
|
||||
|
||||
setSeed(seed: number) {
|
||||
this.seed = (SeededRandomService.baseSeed + seed) % 2147483647; // shifting with 16 to the left
|
||||
}
|
||||
|
||||
get() {
|
||||
this.seed = (this.seed * 16807 % 2147483647);
|
||||
return this.seed / 2147483647;
|
||||
}
|
||||
|
||||
}
|
||||
@ -14,6 +14,8 @@ export class IconizeSortingMethod implements PipeTransform {
|
||||
return '<span class="oi oi-sort-ascending"></span>';
|
||||
case SortingMethods.descDate:
|
||||
return '<span class="oi oi-sort-descending"></span>';
|
||||
case SortingMethods.random:
|
||||
return '<span class="oi oi-random"></span>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,6 +18,8 @@ export class StringifySortingMethod implements PipeTransform {
|
||||
return this.i18n('ascending date');
|
||||
case SortingMethods.descDate:
|
||||
return this.i18n('descending date');
|
||||
case SortingMethods.random:
|
||||
return this.i18n('random');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user