MAL Interest Stacks (#2932)

This commit is contained in:
Joe Milazzo 2024-05-04 15:23:58 -05:00 committed by GitHub
parent 29eb65c783
commit b23300b1a4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
61 changed files with 4104 additions and 382 deletions

View file

@ -11,15 +11,34 @@
</div>
<div [ngStyle]="{'height': ScrollingBlockHeight}" class="main-container container-fluid pt-2" *ngIf="collectionTag !== undefined" #scrollingBlock>
<div class="row mb-3" *ngIf="summary.length > 0">
<div class="col-md-2 col-xs-4 col-sm-6 d-none d-sm-block">
<app-image maxWidth="481px" [imageUrl]="imageService.getCollectionCoverImage(collectionTag.id)"></app-image>
@if (summary.length > 0 || collectionTag.source !== ScrobbleProvider.Kavita) {
<div class="row mb-3">
<div class="col-md-2 col-xs-4 col-sm-6 d-none d-sm-block">
<app-image [styles]="{'max-width': '481px'}" [imageUrl]="imageService.getCollectionCoverImage(collectionTag.id)"></app-image>
@if (collectionTag.source !== ScrobbleProvider.Kavita && collectionTag.missingSeriesFromSource !== null
&& series.length !== collectionTag.totalSourceCount && collectionTag.totalSourceCount > 0) {
<div class="under-image">
<app-image [imageUrl]="collectionTag.source | providerImage"
width="16px" height="16px" [styles]="{'vertical-align': 'text-top'}"
[ngbTooltip]="collectionTag.source | providerName" tabindex="0"></app-image>
<span class="ms-2 me-2">{{t('sync-progress', {title: series.length + ' / ' + collectionTag.totalSourceCount})}}</span>
<i class="fa-solid fa-question-circle" aria-hidden="true" [ngbTooltip]="t('last-sync', {date: collectionTag.lastSyncUtc | date: 'shortDate' | defaultDate })"></i>
</div>
}
</div>
<div class="col-md-10 col-xs-8 col-sm-6 mt-2">
@if (summary.length > 0) {
<div class="mb-2">
<app-read-more [text]="summary" [maxLength]="utilityService.getActiveBreakpoint() < Breakpoint.Tablet ? 250 : 600"></app-read-more>
</div>
}
</div>
<hr>
</div>
<div class="col-md-10 col-xs-8 col-sm-6 mt-2">
<app-read-more [text]="summary" [maxLength]="250"></app-read-more>
</div>
<hr>
</div>
}
<app-bulk-operations [actionCallback]="bulkActionCallback"></app-bulk-operations>
<app-card-detail-layout *ngIf="filter"

View file

@ -41,3 +41,11 @@ h2 {
margin-bottom: 0;
word-break: break-all;
}
.under-image {
background-color: var(--breadcrumb-bg-color);
color: white;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
text-align: center;
}

View file

@ -1,4 +1,4 @@
import {DOCUMENT, NgIf, NgStyle} from '@angular/common';
import {DatePipe, DOCUMENT, NgIf, NgStyle} from '@angular/common';
import {
AfterContentChecked,
ChangeDetectionStrategy,
@ -15,14 +15,14 @@ import {
} from '@angular/core';
import {Title} from '@angular/platform-browser';
import {ActivatedRoute, Router} from '@angular/router';
import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
import {NgbModal, NgbTooltip} from '@ng-bootstrap/ng-bootstrap';
import {ToastrService} from 'ngx-toastr';
import {debounceTime, take} from 'rxjs/operators';
import {BulkSelectionService} from 'src/app/cards/bulk-selection.service';
import {EditCollectionTagsComponent} from 'src/app/cards/_modals/edit-collection-tags/edit-collection-tags.component';
import {FilterSettings} from 'src/app/metadata-filter/filter-settings';
import {FilterUtilitiesService} from 'src/app/shared/_services/filter-utilities.service';
import {KEY_CODES, UtilityService} from 'src/app/shared/_services/utility.service';
import {Breakpoint, KEY_CODES, UtilityService} from 'src/app/shared/_services/utility.service';
import {UserCollection} from 'src/app/_models/collection-tag';
import {SeriesAddedToCollectionEvent} from 'src/app/_models/events/series-added-to-collection-event';
import {JumpKey} from 'src/app/_models/jumpbar/jump-key';
@ -54,6 +54,12 @@ import {FilterComparison} from "../../../_models/metadata/v2/filter-comparison";
import {SeriesFilterV2} from "../../../_models/metadata/v2/series-filter-v2";
import {AccountService} from "../../../_services/account.service";
import {User} from "../../../_models/user";
import {ScrobbleProvider} from "../../../_services/scrobbling.service";
import {SafeHtmlPipe} from "../../../_pipes/safe-html.pipe";
import {TranslocoDatePipe} from "@ngneat/transloco-locale";
import {DefaultDatePipe} from "../../../_pipes/default-date.pipe";
import {ProviderImagePipe} from "../../../_pipes/provider-image.pipe";
import {ProviderNamePipe} from "../../../_pipes/provider-name.pipe";
@Component({
selector: 'app-collection-detail',
@ -61,7 +67,7 @@ import {User} from "../../../_models/user";
styleUrls: ['./collection-detail.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [NgIf, SideNavCompanionBarComponent, CardActionablesComponent, NgStyle, ImageComponent, ReadMoreComponent, BulkOperationsComponent, CardDetailLayoutComponent, SeriesCardComponent, TranslocoDirective]
imports: [NgIf, SideNavCompanionBarComponent, CardActionablesComponent, NgStyle, ImageComponent, ReadMoreComponent, BulkOperationsComponent, CardDetailLayoutComponent, SeriesCardComponent, TranslocoDirective, NgbTooltip, SafeHtmlPipe, TranslocoDatePipe, DatePipe, DefaultDatePipe, ProviderImagePipe, ProviderNamePipe]
})
export class CollectionDetailComponent implements OnInit, AfterContentChecked {
@ -82,7 +88,7 @@ export class CollectionDetailComponent implements OnInit, AfterContentChecked {
private readonly actionService = inject(ActionService);
private readonly messageHub = inject(MessageHubService);
private readonly filterUtilityService = inject(FilterUtilitiesService);
private readonly utilityService = inject(UtilityService);
protected readonly utilityService = inject(UtilityService);
private readonly cdRef = inject(ChangeDetectorRef);
private readonly scrollService = inject(ScrollService);
@ -213,7 +219,7 @@ export class CollectionDetailComponent implements OnInit, AfterContentChecked {
this.messageHub.messages$.pipe(takeUntilDestroyed(this.destroyRef), debounceTime(2000)).subscribe(event => {
if (event.event == EVENTS.SeriesAddedToCollection) {
if (event.event == EVENTS.CollectionUpdated) {
const collectionEvent = event.payload as SeriesAddedToCollectionEvent;
if (collectionEvent.tagId === this.collectionTag.id) {
this.loadPage();
@ -326,4 +332,7 @@ export class CollectionDetailComponent implements OnInit, AfterContentChecked {
this.loadPage();
});
}
protected readonly ScrobbleProvider = ScrobbleProvider;
protected readonly Breakpoint = Breakpoint;
}

View file

@ -9,9 +9,12 @@
<ul>
@for(stack of stacks; track stack.url) {
<li>
<div><a [href]="stack.url" rel="noreferrer noopener" target="_blank">{{stack.title}}</a></div>
<div>by {{stack.author}} • {{t('series-count', {num: stack.seriesCount})}} • <span><i class="fa-solid fa-layer-group me-1" aria-hidden="true"></i>{{t('restack-count', {num: stack.restackCount})}}</span></div>
<li class="mb-2">
<div>
<a [href]="stack.url" rel="noreferrer noopener" target="_blank">{{stack.title}}</a>
<button class="btn btn-primary float-end" [disabled]="collectionMap && collectionMap.hasOwnProperty(stack.url)" (click)="importStack(stack)">Track</button>
</div>
<div>by {{stack.author}} • {{t('series-count', {num: stack.seriesCount | number})}} • <span><i class="fa-solid fa-layer-group me-1" aria-hidden="true"></i>{{t('restack-count', {num: stack.restackCount | number})}}</span></div>
</li>
}
</ul>
@ -31,6 +34,9 @@
<!-- <div class="col-auto">-->
<!-- <button type="button" class="btn btn-primary" (click)="nextStep()" [disabled]="!canMoveToNextStep()">{{t(NextButtonLabel)}}</button>-->
<!-- </div>-->
<div class="col-auto">
<button type="button" class="btn btn-secondary" (click)="ngbModal.dismiss()">{{t('close')}}</button>
</div>
</div>
</ng-container>

View file

@ -1,10 +1,15 @@
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject} from '@angular/core';
import {TranslocoDirective} from "@ngneat/transloco";
import {translate, TranslocoDirective} from "@ngneat/transloco";
import {ReactiveFormsModule} from "@angular/forms";
import {Select2Module} from "ng-select2-component";
import {NgbActiveModal} from "@ng-bootstrap/ng-bootstrap";
import {CollectionTagService} from "../../../_services/collection-tag.service";
import {MalStack} from "../../../_models/collection/mal-stack";
import {UserCollection} from "../../../_models/collection-tag";
import {ScrobbleProvider} from "../../../_services/scrobbling.service";
import {forkJoin} from "rxjs";
import {ToastrService} from "ngx-toastr";
import {DecimalPipe} from "@angular/common";
@Component({
selector: 'app-import-mal-collection-modal',
@ -12,7 +17,8 @@ import {MalStack} from "../../../_models/collection/mal-stack";
imports: [
TranslocoDirective,
ReactiveFormsModule,
Select2Module
Select2Module,
DecimalPipe
],
templateUrl: './import-mal-collection-modal.component.html',
styleUrl: './import-mal-collection-modal.component.scss',
@ -21,19 +27,41 @@ import {MalStack} from "../../../_models/collection/mal-stack";
export class ImportMalCollectionModalComponent {
protected readonly ngbModal = inject(NgbActiveModal);
protected readonly collectionService = inject(CollectionTagService);
protected readonly cdRef = inject(ChangeDetectorRef);
private readonly collectionService = inject(CollectionTagService);
private readonly cdRef = inject(ChangeDetectorRef);
private readonly toastr = inject(ToastrService);
stacks: Array<MalStack> = [];
isLoading = true;
collectionMap: {[key: string]: UserCollection | MalStack} = {};
constructor() {
this.collectionService.getMalStacks().subscribe(stacks => {
this.stacks = stacks;
this.isLoading = false;
this.cdRef.markForCheck();
})
forkJoin({
allCollections: this.collectionService.allCollections(true),
malStacks: this.collectionService.getMalStacks()
}).subscribe(res => {
// Create a map on sourceUrl from collections so that if there are non-null sourceUrl (and source is MAL) then we can disable buttons
const collects = res.allCollections.filter(c => c.source === ScrobbleProvider.Mal && c.sourceUrl);
for(let col of collects) {
if (col.sourceUrl === null) continue;
this.collectionMap[col.sourceUrl] = col;
}
this.stacks = res.malStacks;
this.isLoading = false;
this.cdRef.markForCheck();
});
}
importStack(stack: MalStack) {
this.collectionService.importStack(stack).subscribe(() => {
this.collectionMap[stack.url] = stack;
this.cdRef.markForCheck();
this.toastr.success(translate('toasts.stack-imported'));
})
}