Skip to content

Commit

Permalink
Implementing grouping
Browse files Browse the repository at this point in the history
  • Loading branch information
bpatrik committed Aug 28, 2023
1 parent 0cf0e73 commit 0a1fb29
Show file tree
Hide file tree
Showing 14 changed files with 619 additions and 431 deletions.
10 changes: 6 additions & 4 deletions src/frontend/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ import {
} from '@ng-icons/ionicons';
import {SortingMethodIconComponent} from './ui/sorting-method-icon/sorting-method-icon.component';
import {SafeHtmlPipe} from './pipes/SafeHTMLPipe';
import {DatePipe} from '@angular/common';

@Injectable()
export class MyHammerConfig extends HammerGestureConfig {
Expand All @@ -190,7 +191,7 @@ export class MyHammerConfig extends HammerGestureConfig {

export class CustomUrlSerializer implements UrlSerializer {
private defaultUrlSerializer: DefaultUrlSerializer =
new DefaultUrlSerializer();
new DefaultUrlSerializer();

parse(url: string): UrlTree {
// Encode parentheses
Expand All @@ -201,9 +202,9 @@ export class CustomUrlSerializer implements UrlSerializer {

serialize(tree: UrlTree): string {
return this.defaultUrlSerializer
.serialize(tree)
.replace(/%28/g, '(')
.replace(/%29/g, ')');
.serialize(tree)
.replace(/%28/g, '(')
.replace(/%29/g, ')');
}
}

Expand Down Expand Up @@ -361,6 +362,7 @@ Marker.prototype.options.icon = MarkerFactory.defIcon;
CookieService,
GPXFilesFilterPipe,
MDFilesFilterPipe,
DatePipe
],
bootstrap: [AppComponent],
})
Expand Down
15 changes: 10 additions & 5 deletions src/frontend/app/pipes/PhotoFilterPipe.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { Pipe, PipeTransform } from '@angular/core';
import { MediaDTO, MediaDTOUtils } from '../../../common/entities/MediaDTO';
import { PhotoDTO } from '../../../common/entities/PhotoDTO';
import {MediaGroup} from '../ui/gallery/navigator/sorting.service';

@Pipe({ name: 'photosOnly' })
export class PhotoFilterPipe implements PipeTransform {
transform(media: MediaDTO[]): PhotoDTO[] | null {
if (!media) {
transform(mediaGroups: MediaGroup[]): PhotoDTO[] | null {
if (!mediaGroups) {
return null;
}
return media.filter((m: MediaDTO): boolean =>
MediaDTOUtils.isPhoto(m)
) as PhotoDTO[];
const ret = [];
for(let i = 0; i < mediaGroups.length; ++i){
ret.push(...mediaGroups[i].media.filter((m: MediaDTO): boolean =>
MediaDTOUtils.isPhoto(m)
) as PhotoDTO[])
}
return ret;
}
}
234 changes: 117 additions & 117 deletions src/frontend/app/ui/gallery/filter/filter.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,19 @@ export class FilterService {
{
name: $localize`Faces`,
mapFn: (m: PhotoDTO): string[] =>
m.metadata.faces
? m.metadata.faces.map((f) => f.name)
: ['<' + $localize`no face` + '>'],
m.metadata.faces
? m.metadata.faces.map((f) => f.name)
: ['<' + $localize`no face` + '>'],
renderType: FilterRenderType.enum,
isArrayValue: true,
},
{
name: $localize`Faces groups`,
mapFn: (m: PhotoDTO): string =>
m.metadata.faces
?.map((f) => f.name)
.sort()
.join(', '),
m.metadata.faces
?.map((f) => f.name)
.sort()
.join(', '),
renderType: FilterRenderType.enum,
isArrayValue: false,
},
Expand Down Expand Up @@ -124,18 +124,18 @@ export class FilterService {

private getStatistic(prefiltered: DirectoryContent): { date: Date, endDate: Date, dateStr: string, count: number, max: number }[] {
if (!prefiltered ||
!prefiltered.media ||
prefiltered.media.length === 0) {
!prefiltered.media ||
prefiltered.media.length === 0) {
return [];
}
const ret: { date: Date, endDate: Date, dateStr: string, count: number, max: number }[] = [];
const minDate = prefiltered.media.reduce(
(p, curr) => Math.min(p, curr.metadata.creationDate),
Number.MAX_VALUE - 1
(p, curr) => Math.min(p, curr.metadata.creationDate),
Number.MAX_VALUE - 1
);
const maxDate = prefiltered.media.reduce(
(p, curr) => Math.max(p, curr.metadata.creationDate),
Number.MIN_VALUE + 1
(p, curr) => Math.max(p, curr.metadata.creationDate),
Number.MIN_VALUE + 1
);
const diff = (maxDate - minDate) / 1000;
const H = 60 * 60;
Expand Down Expand Up @@ -221,124 +221,124 @@ export class FilterService {
}

public applyFilters(
directoryContent: Observable<DirectoryContent>
directoryContent: Observable<DirectoryContent>
): Observable<DirectoryContent> {
return directoryContent.pipe(
switchMap((dirContent: DirectoryContent) => {
this.statistic = this.getStatistic(dirContent);
this.resetFilters(false);
return this.activeFilters.pipe(
map((afilters) => {
if (!dirContent || !dirContent.media || (!afilters.filtersVisible && !afilters.areFiltersActive)) {
return dirContent;
}

// clone, so the original won't get overwritten
const c = {
media: dirContent.media,
directories: dirContent.directories,
metaFile: dirContent.metaFile,
};
switchMap((dirContent: DirectoryContent) => {
this.statistic = this.getStatistic(dirContent);
this.resetFilters(false);
return this.activeFilters.pipe(
map((afilters) => {
if (!dirContent || !dirContent.media || (!afilters.filtersVisible && !afilters.areFiltersActive)) {
return dirContent;
}

/* Date Selector */
if (c.media.length > 0) {
// Update date filter range
afilters.dateFilter.minDate = c.media.reduce(
(p, curr) => Math.min(p, curr.metadata.creationDate),
Number.MAX_VALUE - 1
);
afilters.dateFilter.maxDate = c.media.reduce(
(p, curr) => Math.max(p, curr.metadata.creationDate),
Number.MIN_VALUE + 1
);
// Add a few sec padding
afilters.dateFilter.minDate -= (afilters.dateFilter.minDate % 1000) + 1000;
afilters.dateFilter.maxDate += (afilters.dateFilter.maxDate % 1000) + 1000;
// clone, so the original won't get overwritten
const c = {
media: dirContent.media,
directories: dirContent.directories,
metaFile: dirContent.metaFile,
};

if (afilters.dateFilter.minFilter === Number.MIN_VALUE) {
afilters.dateFilter.minFilter = afilters.dateFilter.minDate;
}
if (afilters.dateFilter.maxFilter === Number.MAX_VALUE) {
afilters.dateFilter.maxFilter = afilters.dateFilter.maxDate;
}
/* Date Selector */
if (c.media.length > 0) {
// Update date filter range
afilters.dateFilter.minDate = c.media.reduce(
(p, curr) => Math.min(p, curr.metadata.creationDate),
Number.MAX_VALUE - 1
);
afilters.dateFilter.maxDate = c.media.reduce(
(p, curr) => Math.max(p, curr.metadata.creationDate),
Number.MIN_VALUE + 1
);
// Add a few sec padding
afilters.dateFilter.minDate -= (afilters.dateFilter.minDate % 1000) + 1000;
afilters.dateFilter.maxDate += (afilters.dateFilter.maxDate % 1000) + 1000;

// Apply Date filter
c.media = c.media.filter(
(m) =>
m.metadata.creationDate >= afilters.dateFilter.minFilter &&
m.metadata.creationDate <= afilters.dateFilter.maxFilter
);
} else {
afilters.dateFilter.minDate = Number.MIN_VALUE;
afilters.dateFilter.maxDate = Number.MAX_VALUE;
afilters.dateFilter.minFilter = Number.MIN_VALUE;
afilters.dateFilter.maxFilter = Number.MAX_VALUE;
}
if (afilters.dateFilter.minFilter === Number.MIN_VALUE) {
afilters.dateFilter.minFilter = afilters.dateFilter.minDate;
}
if (afilters.dateFilter.maxFilter === Number.MAX_VALUE) {
afilters.dateFilter.maxFilter = afilters.dateFilter.maxDate;
}

// filters
for (const f of afilters.selectedFilters) {
// Apply Date filter
c.media = c.media.filter(
(m) =>
m.metadata.creationDate >= afilters.dateFilter.minFilter &&
m.metadata.creationDate <= afilters.dateFilter.maxFilter
);
} else {
afilters.dateFilter.minDate = Number.MIN_VALUE;
afilters.dateFilter.maxDate = Number.MAX_VALUE;
afilters.dateFilter.minFilter = Number.MIN_VALUE;
afilters.dateFilter.maxFilter = Number.MAX_VALUE;
}

/* Update filter 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
});
// filters
for (const f of afilters.selectedFilters) {

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++;
/* Update filter 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
});
});
} 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);
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++;
});
}

/* 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;
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 mapped.indexOf(opt.name) === -1;
});
} else {
c.media = c.media.filter(
(m) =>
(f.filter.mapFn(m as PhotoDTO) as string) !== opt.name
);
}
});
}
// If th e number of photos did not change, the filters are not active
afilters.areFiltersActive = c.media.length !== dirContent.media.length;
return c;
})
);
})
// If the number of photos did not change, the filters are not active
afilters.areFiltersActive = c.media.length !== dirContent.media.length;
return c;
})
);
})
);
}

Expand Down
4 changes: 2 additions & 2 deletions src/frontend/app/ui/gallery/gallery.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@
class="rounded"
[class.rounded-start-0]="ShowMarkDown"
*ngIf="ShowMap"
[photos]="directoryContent?.media | photosOnly"
[photos]="directoryContent?.mediaGroups | photosOnly"
[gpxFiles]="directoryContent?.metaFile | gpxFiles"></app-gallery-map>
</div>
<app-gallery-grid [media]="directoryContent?.media"
<app-gallery-grid [mediaGroups]="directoryContent?.mediaGroups"
[lightbox]="lightbox"></app-gallery-grid>

</ng-container>
Expand Down
Loading

0 comments on commit 0a1fb29

Please sign in to comment.