Added a Browse tag page as well. Need to decide how to link to this.
This commit is contained in:
parent
9f91004f3e
commit
7ef95b3e12
23 changed files with 238 additions and 71 deletions
|
|
@ -112,6 +112,22 @@ public class MetadataController(IUnitOfWork unitOfWork, ILocalizationService loc
|
||||||
return Ok(await unitOfWork.TagRepository.GetAllTagDtosForLibrariesAsync(User.GetUserId()));
|
return Ok(await unitOfWork.TagRepository.GetAllTagDtosForLibrariesAsync(User.GetUserId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a list of Tags with counts for counts when Tag is on Series/Chapter
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost("tags-with-counts")]
|
||||||
|
[ResponseCache(CacheProfileName = ResponseCacheProfiles.FiveMinute)]
|
||||||
|
public async Task<ActionResult<PagedList<BrowseTagDto>>> GetBrowseTags(UserParams? userParams = null)
|
||||||
|
{
|
||||||
|
userParams ??= UserParams.Default;
|
||||||
|
|
||||||
|
var list = await unitOfWork.TagRepository.GetBrowseableTag(User.GetUserId(), userParams);
|
||||||
|
Response.AddPaginationHeader(list.CurrentPage, list.PageSize, list.TotalCount, list.TotalPages);
|
||||||
|
|
||||||
|
return Ok(list);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fetches all age ratings from the instance
|
/// Fetches all age ratings from the instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
15
API/DTOs/Metadata/BrowseTagDto.cs
Normal file
15
API/DTOs/Metadata/BrowseTagDto.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
using API.Entities;
|
||||||
|
|
||||||
|
namespace API.DTOs.Metadata;
|
||||||
|
|
||||||
|
public sealed record BrowseTagDto : TagDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Number of Series this Entity is on
|
||||||
|
/// </summary>
|
||||||
|
public int SeriesCount { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Number of Issues this Entity is on
|
||||||
|
/// </summary>
|
||||||
|
public int IssueCount { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
namespace API.DTOs.Metadata;
|
namespace API.DTOs.Metadata;
|
||||||
|
|
||||||
public sealed record TagDto
|
public record TagDto
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public required string Title { get; set; }
|
public required string Title { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using API.DTOs.Metadata;
|
||||||
using API.Entities;
|
using API.Entities;
|
||||||
using API.Extensions;
|
using API.Extensions;
|
||||||
using API.Extensions.QueryExtensions;
|
using API.Extensions.QueryExtensions;
|
||||||
|
using API.Helpers;
|
||||||
using API.Services.Tasks.Scanner.Parser;
|
using API.Services.Tasks.Scanner.Parser;
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using AutoMapper.QueryableExtensions;
|
using AutoMapper.QueryableExtensions;
|
||||||
|
|
@ -23,6 +24,7 @@ public interface ITagRepository
|
||||||
Task RemoveAllTagNoLongerAssociated();
|
Task RemoveAllTagNoLongerAssociated();
|
||||||
Task<IList<TagDto>> GetAllTagDtosForLibrariesAsync(int userId, IList<int>? libraryIds = null);
|
Task<IList<TagDto>> GetAllTagDtosForLibrariesAsync(int userId, IList<int>? libraryIds = null);
|
||||||
Task<List<string>> GetAllTagsNotInListAsync(ICollection<string> tags);
|
Task<List<string>> GetAllTagsNotInListAsync(ICollection<string> tags);
|
||||||
|
Task<PagedList<BrowseTagDto>> GetBrowseableTag(int userId, UserParams userParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TagRepository : ITagRepository
|
public class TagRepository : ITagRepository
|
||||||
|
|
@ -104,6 +106,30 @@ public class TagRepository : ITagRepository
|
||||||
return missingTags.Select(normalizedName => normalizedToOriginalMap[normalizedName]).ToList();
|
return missingTags.Select(normalizedName => normalizedToOriginalMap[normalizedName]).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<PagedList<BrowseTagDto>> GetBrowseableTag(int userId, UserParams userParams)
|
||||||
|
{
|
||||||
|
var ageRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
||||||
|
|
||||||
|
var query = _context.Tag
|
||||||
|
.RestrictAgainstAgeRestriction(ageRating)
|
||||||
|
.Select(g => new BrowseTagDto
|
||||||
|
{
|
||||||
|
Id = g.Id,
|
||||||
|
Title = g.Title,
|
||||||
|
SeriesCount = g.SeriesMetadatas
|
||||||
|
.Select(sm => sm.Id)
|
||||||
|
.Distinct()
|
||||||
|
.Count(),
|
||||||
|
IssueCount = g.Chapters
|
||||||
|
.Select(ch => ch.Id)
|
||||||
|
.Distinct()
|
||||||
|
.Count()
|
||||||
|
})
|
||||||
|
.OrderBy(g => g.Title);
|
||||||
|
|
||||||
|
return await PagedList<BrowseTagDto>.CreateAsync(query, userParams.PageNumber, userParams.PageSize);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<IList<Tag>> GetAllTagsAsync()
|
public async Task<IList<Tag>> GetAllTagsAsync()
|
||||||
{
|
{
|
||||||
return await _context.Tag.ToListAsync();
|
return await _context.Tag.ToListAsync();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
.genre-card {
|
.tag-card {
|
||||||
background-color: var(--bs-card-color, #2c2c2c);
|
background-color: var(--bs-card-color, #2c2c2c);
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
|
|
@ -7,12 +7,12 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.genre-card:hover {
|
.tag-card:hover {
|
||||||
background-color: #3a3a3a;
|
background-color: #3a3a3a;
|
||||||
transform: translateY(-3px);
|
transform: translateY(-3px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.genre-name {
|
.tag-name {
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.genre-meta {
|
.tag-meta {
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import {Genre} from "./genre";
|
import {Genre} from "../genre";
|
||||||
|
|
||||||
export interface BrowseGenre extends Genre {
|
export interface BrowseGenre extends Genre {
|
||||||
seriesCount: number;
|
seriesCount: number;
|
||||||
6
UI/Web/src/app/_models/metadata/browse/browse-tag.ts
Normal file
6
UI/Web/src/app/_models/metadata/browse/browse-tag.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
import {Tag} from "../../tag";
|
||||||
|
|
||||||
|
export interface BrowseTag extends Tag {
|
||||||
|
seriesCount: number;
|
||||||
|
issueCount: number;
|
||||||
|
}
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
import {Routes} from "@angular/router";
|
|
||||||
import {BrowseAuthorsComponent} from "../browse-people/browse-authors.component";
|
|
||||||
|
|
||||||
|
|
||||||
export const routes: Routes = [
|
|
||||||
{path: '', component: BrowseAuthorsComponent, pathMatch: 'full'},
|
|
||||||
];
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
import {Routes} from "@angular/router";
|
|
||||||
import {BrowseGenresComponent} from "../all-genres/browse-genres.component";
|
|
||||||
|
|
||||||
|
|
||||||
export const routes: Routes = [
|
|
||||||
{path: '', component: BrowseGenresComponent, pathMatch: 'full'},
|
|
||||||
];
|
|
||||||
11
UI/Web/src/app/_routes/browse-routing.module.ts
Normal file
11
UI/Web/src/app/_routes/browse-routing.module.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import {Routes} from "@angular/router";
|
||||||
|
import {BrowseAuthorsComponent} from "../browse/browse-people/browse-authors.component";
|
||||||
|
import {BrowseGenresComponent} from "../browse/browse-genres/browse-genres.component";
|
||||||
|
import {BrowseTagsComponent} from "../browse/browse-tags/browse-tags.component";
|
||||||
|
|
||||||
|
|
||||||
|
export const routes: Routes = [
|
||||||
|
{path: 'authors', component: BrowseAuthorsComponent, pathMatch: 'full'},
|
||||||
|
{path: 'genres', component: BrowseGenresComponent, pathMatch: 'full'},
|
||||||
|
{path: 'tags', component: BrowseTagsComponent, pathMatch: 'full'},
|
||||||
|
];
|
||||||
|
|
@ -16,5 +16,5 @@ export const routes: Routes = [
|
||||||
runGuardsAndResolvers: 'always',
|
runGuardsAndResolvers: 'always',
|
||||||
canActivate: [AuthGuard, LibraryAccessGuard],
|
canActivate: [AuthGuard, LibraryAccessGuard],
|
||||||
component: LibraryDetailComponent
|
component: LibraryDetailComponent
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,8 @@ import {LibraryService} from './library.service';
|
||||||
import {CollectionTagService} from "./collection-tag.service";
|
import {CollectionTagService} from "./collection-tag.service";
|
||||||
import {PaginatedResult} from "../_models/pagination";
|
import {PaginatedResult} from "../_models/pagination";
|
||||||
import {UtilityService} from "../shared/_services/utility.service";
|
import {UtilityService} from "../shared/_services/utility.service";
|
||||||
import {BrowseGenre} from "../_models/metadata/browse-genre";
|
import {BrowseGenre} from "../_models/metadata/browse/browse-genre";
|
||||||
|
import {BrowseTag} from "../_models/metadata/browse/browse-tag";
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
|
|
@ -100,6 +101,17 @@ export class MetadataService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTagWithCounts(pageNum?: number, itemsPerPage?: number) {
|
||||||
|
let params = new HttpParams();
|
||||||
|
params = this.utilityService.addPaginationIfExists(params, pageNum, itemsPerPage);
|
||||||
|
|
||||||
|
return this.httpClient.post<PaginatedResult<BrowseTag[]>>(this.baseUrl + 'metadata/tags-with-counts', {}, {observe: 'response', params}).pipe(
|
||||||
|
map((response: any) => {
|
||||||
|
return this.utilityService.createPaginatedResult(response) as PaginatedResult<BrowseTag[]>;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
getAllLanguages(libraries?: Array<number>) {
|
getAllLanguages(libraries?: Array<number>) {
|
||||||
let method = 'metadata/languages'
|
let method = 'metadata/languages'
|
||||||
if (libraries != undefined && libraries.length > 0) {
|
if (libraries != undefined && libraries.length > 0) {
|
||||||
|
|
|
||||||
|
|
@ -50,12 +50,8 @@ const routes: Routes = [
|
||||||
loadChildren: () => import('./_routes/person-detail-routing.module').then(m => m.routes)
|
loadChildren: () => import('./_routes/person-detail-routing.module').then(m => m.routes)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'browse/authors',
|
path: 'browse',
|
||||||
loadChildren: () => import('./_routes/browse-authors-routing.module').then(m => m.routes)
|
loadChildren: () => import('./_routes/browse-routing.module').then(m => m.routes)
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'browse/genres',
|
|
||||||
loadChildren: () => import('./_routes/browse-genres-routing.module').then(m => m.routes)
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'library',
|
path: 'library',
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,11 @@
|
||||||
>
|
>
|
||||||
<ng-template #cardItem let-item let-position="idx">
|
<ng-template #cardItem let-item let-position="idx">
|
||||||
|
|
||||||
<div class="genre-card" (click)="openFilter(FilterField.Genres, item.id)">
|
<div class="tag-card" (click)="openFilter(FilterField.Genres, item.id)">
|
||||||
<div class="genre-name">{{ item.title }}</div>
|
<div class="tag-name">{{ item.title }}</div>
|
||||||
<div class="genre-meta">
|
<div class="tag-meta">
|
||||||
<span>{{ item.seriesCount }} Series</span>
|
<span>{{t('series-count', {num: item.seriesCount | compactNumber})}}</span>
|
||||||
<span>{{ item.issueCount }} Issues</span>
|
<span>{{t('issue-count', {num: item.issueCount | compactNumber})}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
@use '../../../tag-card-common';
|
||||||
|
|
@ -1,35 +1,29 @@
|
||||||
import {
|
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, inject, OnInit} from '@angular/core';
|
||||||
ChangeDetectionStrategy,
|
import {CardDetailLayoutComponent} from "../../cards/card-detail-layout/card-detail-layout.component";
|
||||||
ChangeDetectorRef,
|
|
||||||
Component,
|
|
||||||
DestroyRef,
|
|
||||||
EventEmitter,
|
|
||||||
inject,
|
|
||||||
OnInit
|
|
||||||
} from '@angular/core';
|
|
||||||
import {CardDetailLayoutComponent} from "../cards/card-detail-layout/card-detail-layout.component";
|
|
||||||
import {DecimalPipe} from "@angular/common";
|
import {DecimalPipe} from "@angular/common";
|
||||||
import {
|
import {
|
||||||
SideNavCompanionBarComponent
|
SideNavCompanionBarComponent
|
||||||
} from "../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component";
|
} from "../../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component";
|
||||||
import {TranslocoDirective} from "@jsverse/transloco";
|
import {TranslocoDirective} from "@jsverse/transloco";
|
||||||
import {JumpbarService} from "../_services/jumpbar.service";
|
import {JumpbarService} from "../../_services/jumpbar.service";
|
||||||
import {BrowsePerson} from "../_models/person/browse-person";
|
import {BrowsePerson} from "../../_models/person/browse-person";
|
||||||
import {Pagination} from "../_models/pagination";
|
import {Pagination} from "../../_models/pagination";
|
||||||
import {JumpKey} from "../_models/jumpbar/jump-key";
|
import {JumpKey} from "../../_models/jumpbar/jump-key";
|
||||||
import {MetadataService} from "../_services/metadata.service";
|
import {MetadataService} from "../../_services/metadata.service";
|
||||||
import {BrowseGenre} from "../_models/metadata/browse-genre";
|
import {BrowseGenre} from "../../_models/metadata/browse/browse-genre";
|
||||||
import {FilterField} from "../_models/metadata/v2/filter-field";
|
import {FilterField} from "../../_models/metadata/v2/filter-field";
|
||||||
import {FilterComparison} from "../_models/metadata/v2/filter-comparison";
|
import {FilterComparison} from "../../_models/metadata/v2/filter-comparison";
|
||||||
import {FilterUtilitiesService} from "../shared/_services/filter-utilities.service";
|
import {FilterUtilitiesService} from "../../shared/_services/filter-utilities.service";
|
||||||
|
import {CompactNumberPipe} from "../../_pipes/compact-number.pipe";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-all-genres',
|
selector: 'app-browse-genres',
|
||||||
imports: [
|
imports: [
|
||||||
CardDetailLayoutComponent,
|
CardDetailLayoutComponent,
|
||||||
DecimalPipe,
|
DecimalPipe,
|
||||||
SideNavCompanionBarComponent,
|
SideNavCompanionBarComponent,
|
||||||
TranslocoDirective
|
TranslocoDirective,
|
||||||
|
CompactNumberPipe
|
||||||
],
|
],
|
||||||
templateUrl: './browse-genres.component.html',
|
templateUrl: './browse-genres.component.html',
|
||||||
styleUrl: './browse-genres.component.scss',
|
styleUrl: './browse-genres.component.scss',
|
||||||
|
|
@ -39,7 +33,6 @@ export class BrowseGenresComponent implements OnInit {
|
||||||
|
|
||||||
protected readonly FilterField = FilterField;
|
protected readonly FilterField = FilterField;
|
||||||
|
|
||||||
private readonly destroyRef = inject(DestroyRef);
|
|
||||||
private readonly cdRef = inject(ChangeDetectorRef);
|
private readonly cdRef = inject(ChangeDetectorRef);
|
||||||
private readonly metadataService = inject(MetadataService);
|
private readonly metadataService = inject(MetadataService);
|
||||||
private readonly jumpbarService = inject(JumpbarService);
|
private readonly jumpbarService = inject(JumpbarService);
|
||||||
|
|
@ -9,20 +9,20 @@ import {
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import {
|
import {
|
||||||
SideNavCompanionBarComponent
|
SideNavCompanionBarComponent
|
||||||
} from "../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component";
|
} from "../../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component";
|
||||||
import {CardDetailLayoutComponent} from "../cards/card-detail-layout/card-detail-layout.component";
|
import {CardDetailLayoutComponent} from "../../cards/card-detail-layout/card-detail-layout.component";
|
||||||
import {DecimalPipe} from "@angular/common";
|
import {DecimalPipe} from "@angular/common";
|
||||||
import {Series} from "../_models/series";
|
import {Series} from "../../_models/series";
|
||||||
import {Pagination} from "../_models/pagination";
|
import {Pagination} from "../../_models/pagination";
|
||||||
import {JumpKey} from "../_models/jumpbar/jump-key";
|
import {JumpKey} from "../../_models/jumpbar/jump-key";
|
||||||
import {Router} from "@angular/router";
|
import {Router} from "@angular/router";
|
||||||
import {PersonService} from "../_services/person.service";
|
import {PersonService} from "../../_services/person.service";
|
||||||
import {BrowsePerson} from "../_models/person/browse-person";
|
import {BrowsePerson} from "../../_models/person/browse-person";
|
||||||
import {JumpbarService} from "../_services/jumpbar.service";
|
import {JumpbarService} from "../../_services/jumpbar.service";
|
||||||
import {PersonCardComponent} from "../cards/person-card/person-card.component";
|
import {PersonCardComponent} from "../../cards/person-card/person-card.component";
|
||||||
import {ImageService} from "../_services/image.service";
|
import {ImageService} from "../../_services/image.service";
|
||||||
import {TranslocoDirective} from "@jsverse/transloco";
|
import {TranslocoDirective} from "@jsverse/transloco";
|
||||||
import {CompactNumberPipe} from "../_pipes/compact-number.pipe";
|
import {CompactNumberPipe} from "../../_pipes/compact-number.pipe";
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
34
UI/Web/src/app/browse/browse-tags/browse-tags.component.html
Normal file
34
UI/Web/src/app/browse/browse-tags/browse-tags.component.html
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
<div class="main-container container-fluid">
|
||||||
|
<ng-container *transloco="let t; read:'browse-tags'" >
|
||||||
|
<app-side-nav-companion-bar [hasFilter]="false">
|
||||||
|
<h2 title>
|
||||||
|
<span>{{t('title')}}</span>
|
||||||
|
</h2>
|
||||||
|
<h6 subtitle>{{t('genre-count', {num: pagination.totalItems | number})}} </h6>
|
||||||
|
|
||||||
|
</app-side-nav-companion-bar>
|
||||||
|
|
||||||
|
<app-card-detail-layout
|
||||||
|
[isLoading]="isLoading"
|
||||||
|
[items]="tags"
|
||||||
|
[pagination]="pagination"
|
||||||
|
[trackByIdentity]="trackByIdentity"
|
||||||
|
[jumpBarKeys]="jumpKeys"
|
||||||
|
[filteringDisabled]="true"
|
||||||
|
[refresh]="refresh"
|
||||||
|
>
|
||||||
|
<ng-template #cardItem let-item let-position="idx">
|
||||||
|
|
||||||
|
<div class="tag-card" (click)="openFilter(FilterField.Tags, item.id)">
|
||||||
|
<div class="tag-name">{{ item.title }}</div>
|
||||||
|
<div class="tag-meta">
|
||||||
|
<span>{{t('series-count', {num: item.seriesCount | compactNumber})}}</span>
|
||||||
|
<span>{{t('issue-count', {num: item.issueCount | compactNumber})}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</ng-template>
|
||||||
|
</app-card-detail-layout>
|
||||||
|
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
@use '../../../tag-card-common';
|
||||||
63
UI/Web/src/app/browse/browse-tags/browse-tags.component.ts
Normal file
63
UI/Web/src/app/browse/browse-tags/browse-tags.component.ts
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, inject, OnInit} from '@angular/core';
|
||||||
|
import {CardDetailLayoutComponent} from "../../cards/card-detail-layout/card-detail-layout.component";
|
||||||
|
import {DecimalPipe} from "@angular/common";
|
||||||
|
import {
|
||||||
|
SideNavCompanionBarComponent
|
||||||
|
} from "../../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component";
|
||||||
|
import {TranslocoDirective} from "@jsverse/transloco";
|
||||||
|
import {MetadataService} from "../../_services/metadata.service";
|
||||||
|
import {JumpbarService} from "../../_services/jumpbar.service";
|
||||||
|
import {FilterUtilitiesService} from "../../shared/_services/filter-utilities.service";
|
||||||
|
import {BrowseGenre} from "../../_models/metadata/browse/browse-genre";
|
||||||
|
import {Pagination} from "../../_models/pagination";
|
||||||
|
import {JumpKey} from "../../_models/jumpbar/jump-key";
|
||||||
|
import {BrowsePerson} from "../../_models/person/browse-person";
|
||||||
|
import {FilterField} from "../../_models/metadata/v2/filter-field";
|
||||||
|
import {FilterComparison} from "../../_models/metadata/v2/filter-comparison";
|
||||||
|
import {BrowseTag} from "../../_models/metadata/browse/browse-tag";
|
||||||
|
import {CompactNumberPipe} from "../../_pipes/compact-number.pipe";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-browse-tags',
|
||||||
|
imports: [
|
||||||
|
CardDetailLayoutComponent,
|
||||||
|
DecimalPipe,
|
||||||
|
SideNavCompanionBarComponent,
|
||||||
|
TranslocoDirective,
|
||||||
|
CompactNumberPipe
|
||||||
|
],
|
||||||
|
templateUrl: './browse-tags.component.html',
|
||||||
|
styleUrl: './browse-tags.component.scss',
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class BrowseTagsComponent implements OnInit {
|
||||||
|
protected readonly FilterField = FilterField;
|
||||||
|
|
||||||
|
private readonly cdRef = inject(ChangeDetectorRef);
|
||||||
|
private readonly metadataService = inject(MetadataService);
|
||||||
|
private readonly jumpbarService = inject(JumpbarService);
|
||||||
|
protected readonly filterUtilityService = inject(FilterUtilitiesService);
|
||||||
|
|
||||||
|
isLoading = false;
|
||||||
|
tags: Array<BrowseTag> = [];
|
||||||
|
pagination: Pagination = {currentPage: 0, totalPages: 0, totalItems: 0, itemsPerPage: 0};
|
||||||
|
refresh: EventEmitter<void> = new EventEmitter();
|
||||||
|
jumpKeys: Array<JumpKey> = [];
|
||||||
|
trackByIdentity = (index: number, item: BrowsePerson) => `${item.id}`;
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.isLoading = true;
|
||||||
|
this.cdRef.markForCheck();
|
||||||
|
this.metadataService.getTagWithCounts(undefined, undefined).subscribe(d => {
|
||||||
|
this.tags = d.result;
|
||||||
|
this.pagination = d.pagination;
|
||||||
|
this.jumpKeys = this.jumpbarService.getJumpKeys(this.tags, (d: BrowseGenre) => d.title);
|
||||||
|
this.isLoading = false;
|
||||||
|
this.cdRef.markForCheck();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
openFilter(field: FilterField, value: string | number) {
|
||||||
|
this.filterUtilityService.applyFilter(['all-series'], field, FilterComparison.Equal, `${value}`).subscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1118,6 +1118,13 @@
|
||||||
"series-count": "{{common.series-count}}"
|
"series-count": "{{common.series-count}}"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"browse-tags": {
|
||||||
|
"title": "Browse Tags",
|
||||||
|
"genre-count": "{{num}} Tags",
|
||||||
|
"issue-count": "{{common.issue-count}}",
|
||||||
|
"series-count": "{{common.series-count}}"
|
||||||
|
},
|
||||||
|
|
||||||
"person-detail": {
|
"person-detail": {
|
||||||
"aka-title": "Also known as ",
|
"aka-title": "Also known as ",
|
||||||
"known-for-title": "Known For",
|
"known-for-title": "Known For",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue