import {Component, HostListener, OnDestroy} from '@angular/core'; import {DuplicateService} from './duplicates.service'; import {Utils} from '../../../../common/Utils'; import {QueryService} from '../../model/query.service'; import {DuplicatesDTO} from '../../../../common/entities/DuplicatesDTO'; import {DirectoryDTO} from '../../../../common/entities/DirectoryDTO'; import {Subscription} from 'rxjs'; import {Config} from '../../../../common/config/public/Config'; import {PageHelper} from '../../model/page.helper'; interface GroupedDuplicate { name: string; duplicates: DuplicatesDTO[]; } @Component({ selector: 'app-duplicate', templateUrl: './duplicates.component.html', styleUrls: ['./duplicates.component.css'] }) export class DuplicateComponent implements OnDestroy { directoryGroups: GroupedDuplicate[] = null; renderedDirGroups: GroupedDuplicate[] = null; renderedIndex = { group: -1, pairs: 0 }; subscription: Subscription; renderTimer: number = null; duplicateCount = { pairs: 0, photos: 0 }; constructor(public _duplicateService: DuplicateService, public queryService: QueryService) { this._duplicateService.getDuplicates().catch(console.error); this.subscription = this._duplicateService.duplicates.subscribe((duplicates: DuplicatesDTO[]) => { this.directoryGroups = []; this.renderedIndex = {group: -1, pairs: 0}; this.renderedDirGroups = []; this.duplicateCount = { pairs: 0, photos: 0 }; if (duplicates === null) { return; } this.duplicateCount.photos = duplicates.reduce((prev: number, curr) => prev + curr.media.length, 0); this.duplicateCount.pairs = duplicates.length; const getMostFrequentDir = (dupls: DuplicatesDTO[]) => { if (dupls.length === 0) { return null; } const dirFrequency: { [key: number]: { count: number, dir: DirectoryDTO } } = {}; dupls.forEach(d => d.media.forEach(m => { dirFrequency[m.directory.id] = dirFrequency[m.directory.id] || {dir: m.directory, count: 0}; dirFrequency[m.directory.id].count++; })); let max: { count: number, dir: DirectoryDTO } = {count: -1, dir: null}; for (const freq of Object.values(dirFrequency)) { if (max.count <= freq.count) { max = freq; } } return max.dir; }; while (duplicates.length > 0) { const dir = getMostFrequentDir(duplicates); const group = duplicates.filter(d => d.media.find(m => m.directory.id === dir.id)); duplicates = duplicates.filter(d => !d.media.find(m => m.directory.id === dir.id)); this.directoryGroups.push({name: this.getDirectoryPath(dir) + ' (' + group.length + ')', duplicates: group}); } this.renderMore(); }); } ngOnDestroy(): void { if (this.subscription) { this.subscription.unsubscribe(); this.subscription = null; } } getDirectoryPath(directory: DirectoryDTO) { return Utils.concatUrls(directory.path, directory.name); } renderMore = () => { if (this.renderTimer !== null) { clearTimeout(this.renderTimer); this.renderTimer = null; } if (this.directoryGroups.length === 0) { return; } if (this.renderedIndex.group === this.directoryGroups.length - 1 && this.renderedIndex.pairs >= this.directoryGroups[this.renderedIndex.group].duplicates.length) { return; } if (this.shouldRenderMore()) { if (this.renderedDirGroups.length === 0 || this.renderedIndex.pairs >= this.directoryGroups[this.renderedIndex.group].duplicates.length) { this.renderedDirGroups.push({ name: this.directoryGroups[++this.renderedIndex.group].name, duplicates: [] }); this.renderedIndex.pairs = 0; } this.renderedDirGroups[this.renderedDirGroups.length - 1].duplicates .push(this.directoryGroups[this.renderedIndex.group].duplicates[this.renderedIndex.pairs++]); this.renderTimer = window.setTimeout(this.renderMore, 0); } }; @HostListener('window:scroll') onScroll() { this.renderMore(); } private shouldRenderMore(): boolean { return Config.Client.Other.enableOnScrollRendering === false || PageHelper.ScrollY >= PageHelper.MaxScrollY * 0.7 || (document.body.clientHeight) * 0.85 < window.innerHeight; } }