mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-05-24 00:52:23 -04:00
Bulk Add to Collection (#674)
* Fixed the typeahead not having the same size input box as other inputs * Implemented the ability to add multiple series to a collection through bulk operations flow. Updated book parser to handle "@import url('...');" syntax as well as @import '...'; * Implemented the ability to create a new Collection tag via bulk operations flow.
This commit is contained in:
parent
908c872f5c
commit
52f8fbe3db
@ -2,7 +2,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Data;
|
||||
using API.DTOs;
|
||||
using API.DTOs.CollectionTags;
|
||||
using API.Entities;
|
||||
using API.Extensions;
|
||||
using API.Interfaces;
|
||||
@ -90,6 +92,40 @@ namespace API.Controllers
|
||||
return BadRequest("Something went wrong, please try again");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a collection tag onto multiple Series. If tag id is 0, this will create a new tag.
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("update-for-series")]
|
||||
public async Task<ActionResult> AddToMultipleSeries(CollectionTagBulkAddDto dto)
|
||||
{
|
||||
var tag = await _unitOfWork.CollectionTagRepository.GetFullTagAsync(dto.CollectionTagId);
|
||||
if (tag == null)
|
||||
{
|
||||
tag = DbFactory.CollectionTag(0, dto.CollectionTagTitle, String.Empty, false);
|
||||
_unitOfWork.CollectionTagRepository.Add(tag);
|
||||
}
|
||||
|
||||
|
||||
var seriesMetadatas = await _unitOfWork.SeriesRepository.GetSeriesMetadataForIdsAsync(dto.SeriesIds);
|
||||
foreach (var metadata in seriesMetadatas)
|
||||
{
|
||||
if (!metadata.CollectionTags.Any(t => t.Title.Equals(tag.Title, StringComparison.InvariantCulture)))
|
||||
{
|
||||
metadata.CollectionTags.Add(tag);
|
||||
_unitOfWork.SeriesMetadataRepository.Update(metadata);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_unitOfWork.HasChanges()) return Ok();
|
||||
if (await _unitOfWork.CommitAsync())
|
||||
{
|
||||
return Ok();
|
||||
}
|
||||
return BadRequest("There was an issue updating series with collection tag");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For a given tag, update the summary if summary has changed and remove a set of series from the tag.
|
||||
/// </summary>
|
||||
|
@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
using API.Comparators;
|
||||
using API.DTOs;
|
||||
using API.DTOs.CollectionTags;
|
||||
using API.DTOs.Filtering;
|
||||
using API.DTOs.OPDS;
|
||||
using API.Entities;
|
||||
|
18
API/DTOs/CollectionTags/CollectionTagBulkAddDto.cs
Normal file
18
API/DTOs/CollectionTags/CollectionTagBulkAddDto.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace API.DTOs.CollectionTags
|
||||
{
|
||||
public class CollectionTagBulkAddDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Collection Tag Id
|
||||
/// </summary>
|
||||
/// <remarks>Can be 0 which then will use Title to create a tag</remarks>
|
||||
public int CollectionTagId { get; init; }
|
||||
public string CollectionTagTitle { get; init; }
|
||||
/// <summary>
|
||||
/// Series Ids to add onto Collection Tag
|
||||
/// </summary>
|
||||
public IEnumerable<int> SeriesIds { get; init; }
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
namespace API.DTOs
|
||||
namespace API.DTOs.CollectionTags
|
||||
{
|
||||
public class CollectionTagDto
|
||||
{
|
@ -1,10 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace API.DTOs
|
||||
namespace API.DTOs.CollectionTags
|
||||
{
|
||||
public class UpdateSeriesForTagDto
|
||||
{
|
||||
public CollectionTagDto Tag { get; init; }
|
||||
public ICollection<int> SeriesIdsToRemove { get; init; }
|
||||
public IEnumerable<int> SeriesIdsToRemove { get; init; }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using API.DTOs.CollectionTags;
|
||||
using API.Entities;
|
||||
|
||||
namespace API.DTOs
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using API.DTOs.CollectionTags;
|
||||
|
||||
namespace API.DTOs
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.DTOs;
|
||||
using API.DTOs.CollectionTags;
|
||||
using API.Entities;
|
||||
using API.Interfaces.Repositories;
|
||||
using AutoMapper;
|
||||
@ -21,6 +22,11 @@ namespace API.Data.Repositories
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public void Add(CollectionTag tag)
|
||||
{
|
||||
_context.CollectionTag.Add(tag);
|
||||
}
|
||||
|
||||
public void Remove(CollectionTag tag)
|
||||
{
|
||||
_context.CollectionTag.Remove(tag);
|
||||
|
20
API/Data/Repositories/SeriesMetadataRepository.cs
Normal file
20
API/Data/Repositories/SeriesMetadataRepository.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using API.Entities;
|
||||
using API.Interfaces.Repositories;
|
||||
|
||||
namespace API.Data.Repositories
|
||||
{
|
||||
public class SeriesMetadataRepository : ISeriesMetadataRepository
|
||||
{
|
||||
private readonly DataContext _context;
|
||||
|
||||
public SeriesMetadataRepository(DataContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public void Update(SeriesMetadata seriesMetadata)
|
||||
{
|
||||
_context.SeriesMetadata.Update(seriesMetadata);
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Data.Scanner;
|
||||
using API.DTOs;
|
||||
using API.DTOs.CollectionTags;
|
||||
using API.DTOs.Filtering;
|
||||
using API.Entities;
|
||||
using API.Extensions;
|
||||
@ -485,5 +486,13 @@ namespace API.Data.Repositories
|
||||
TotalChunks = totalChunks
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<IList<SeriesMetadata>> GetSeriesMetadataForIdsAsync(IEnumerable<int> seriesIds)
|
||||
{
|
||||
return await _context.SeriesMetadata
|
||||
.Where(sm => seriesIds.Contains(sm.SeriesId))
|
||||
.Include(sm => sm.CollectionTags)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ namespace API.Data
|
||||
public IFileRepository FileRepository => new FileRepository(_context);
|
||||
public IChapterRepository ChapterRepository => new ChapterRepository(_context, _mapper);
|
||||
public IReadingListRepository ReadingListRepository => new ReadingListRepository(_context, _mapper);
|
||||
public ISeriesMetadataRepository SeriesMetadataRepository => new SeriesMetadataRepository(_context);
|
||||
|
||||
/// <summary>
|
||||
/// Commits changes to the DB. Completes the open transaction.
|
||||
|
@ -8,6 +8,9 @@ namespace API.Entities
|
||||
{
|
||||
[Key]
|
||||
public ServerSettingKey Key { get; set; }
|
||||
/// <summary>
|
||||
/// The value of the Setting. Converter knows how to convert to the correct type
|
||||
/// </summary>
|
||||
public string Value { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using API.DTOs;
|
||||
using API.DTOs.CollectionTags;
|
||||
using API.DTOs.Reader;
|
||||
using API.DTOs.ReadingLists;
|
||||
using API.DTOs.Settings;
|
||||
|
@ -15,6 +15,7 @@ namespace API.Interfaces
|
||||
IFileRepository FileRepository { get; }
|
||||
IChapterRepository ChapterRepository { get; }
|
||||
IReadingListRepository ReadingListRepository { get; }
|
||||
ISeriesMetadataRepository SeriesMetadataRepository { get; }
|
||||
bool Commit();
|
||||
Task<bool> CommitAsync();
|
||||
bool HasChanges();
|
||||
|
@ -1,12 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using API.DTOs;
|
||||
using API.DTOs.CollectionTags;
|
||||
using API.Entities;
|
||||
|
||||
namespace API.Interfaces.Repositories
|
||||
{
|
||||
public interface ICollectionTagRepository
|
||||
{
|
||||
void Add(CollectionTag tag);
|
||||
void Remove(CollectionTag tag);
|
||||
Task<IEnumerable<CollectionTagDto>> GetAllTagDtosAsync();
|
||||
Task<IEnumerable<CollectionTagDto>> SearchTagDtosAsync(string searchQuery);
|
||||
|
9
API/Interfaces/Repositories/ISeriesMetadataRepository.cs
Normal file
9
API/Interfaces/Repositories/ISeriesMetadataRepository.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using API.Entities;
|
||||
|
||||
namespace API.Interfaces.Repositories
|
||||
{
|
||||
public interface ISeriesMetadataRepository
|
||||
{
|
||||
void Update(SeriesMetadata seriesMetadata);
|
||||
}
|
||||
}
|
@ -54,5 +54,6 @@ namespace API.Interfaces.Repositories
|
||||
Task<PagedList<Series>> GetFullSeriesForLibraryIdAsync(int libraryId, UserParams userParams);
|
||||
Task<Series> GetFullSeriesForSeriesIdAsync(int seriesId);
|
||||
Task<Chunk> GetChunkInfo(int libraryId = 0);
|
||||
Task<IList<SeriesMetadata>> GetSeriesMetadataForIdsAsync(IEnumerable<int> seriesIds);
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ namespace API.Parser
|
||||
|
||||
public static readonly Regex FontSrcUrlRegex = new Regex(@"(src:url\(.{1})" + "([^\"']*)" + @"(.{1}\))",
|
||||
MatchOptions, RegexTimeout);
|
||||
public static readonly Regex CssImportUrlRegex = new Regex("(@import\\s[\"|'])(?<Filename>[\\w\\d/\\._-]+)([\"|'];?)",
|
||||
public static readonly Regex CssImportUrlRegex = new Regex("@import\\s([\"|']|url\\([\"|'])(?<Filename>[^'\"]+)[\"|']\\)?;",
|
||||
MatchOptions, RegexTimeout);
|
||||
|
||||
private static readonly string XmlRegexExtensions = @"\.xml";
|
||||
|
@ -19,7 +19,8 @@ export enum Action {
|
||||
Download = 7,
|
||||
Bookmarks = 8,
|
||||
IncognitoRead = 9,
|
||||
AddToReadingList = 10
|
||||
AddToReadingList = 10,
|
||||
AddToCollection = 11
|
||||
}
|
||||
|
||||
export interface ActionItem<T> {
|
||||
@ -90,6 +91,13 @@ export class ActionFactoryService {
|
||||
requiresAdmin: true
|
||||
});
|
||||
|
||||
this.seriesActions.push({
|
||||
action: Action.AddToCollection,
|
||||
title: 'Add to Collection',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: true
|
||||
});
|
||||
|
||||
this.seriesActions.push({
|
||||
action: Action.Edit,
|
||||
title: 'Edit',
|
||||
@ -209,7 +217,7 @@ export class ActionFactoryService {
|
||||
title: 'Add to Reading List',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false
|
||||
},
|
||||
}
|
||||
];
|
||||
|
||||
this.volumeActions = [
|
||||
|
@ -4,6 +4,7 @@ import { ToastrService } from 'ngx-toastr';
|
||||
import { Subject } from 'rxjs';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { BookmarksModalComponent } from '../cards/_modals/bookmarks-modal/bookmarks-modal.component';
|
||||
import { BulkAddToCollectionComponent } from '../cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component';
|
||||
import { AddToListModalComponent, ADD_FLOW } from '../reading-list/_modals/add-to-list-modal/add-to-list-modal.component';
|
||||
import { EditReadingListModalComponent } from '../reading-list/_modals/edit-reading-list-modal/edit-reading-list-modal.component';
|
||||
import { ConfirmService } from '../shared/confirm.service';
|
||||
@ -34,6 +35,7 @@ export class ActionService implements OnDestroy {
|
||||
private readonly onDestroy = new Subject<void>();
|
||||
private bookmarkModalRef: NgbModalRef | null = null;
|
||||
private readingListModalRef: NgbModalRef | null = null;
|
||||
private collectionModalRef: NgbModalRef | null = null;
|
||||
|
||||
constructor(private libraryService: LibraryService, private seriesService: SeriesService,
|
||||
private readerService: ReaderService, private toastr: ToastrService, private modalService: NgbModal,
|
||||
@ -358,6 +360,32 @@ export class ActionService implements OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a set of series to a collection tag
|
||||
* @param series
|
||||
* @param callback
|
||||
* @returns
|
||||
*/
|
||||
addMultipleSeriesToCollectionTag(series: Array<Series>, callback?: VoidActionCallback) {
|
||||
if (this.collectionModalRef != null) { return; }
|
||||
this.collectionModalRef = this.modalService.open(BulkAddToCollectionComponent, { scrollable: true, size: 'md' });
|
||||
this.collectionModalRef.componentInstance.seriesIds = series.map(v => v.id);
|
||||
this.collectionModalRef.componentInstance.title = 'New Collection';
|
||||
|
||||
this.collectionModalRef.closed.pipe(take(1)).subscribe(() => {
|
||||
this.collectionModalRef = null;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
this.collectionModalRef.dismissed.pipe(take(1)).subscribe(() => {
|
||||
this.collectionModalRef = null;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addSeriesToReadingList(series: Series, callback?: SeriesActionCallback) {
|
||||
if (this.readingListModalRef != null) { return; }
|
||||
this.readingListModalRef = this.modalService.open(AddToListModalComponent, { scrollable: true, size: 'md' });
|
||||
|
@ -35,4 +35,8 @@ export class CollectionTagService {
|
||||
updateSeriesForTag(tag: CollectionTag, seriesIdsToRemove: Array<number>) {
|
||||
return this.httpClient.post(this.baseUrl + 'collection/update-series', {tag, seriesIdsToRemove}, {responseType: 'text' as 'json'});
|
||||
}
|
||||
|
||||
addByMultiple(tagId: number, seriesIds: Array<number>, tagTitle: string = '') {
|
||||
return this.httpClient.post(this.baseUrl + 'collection/update-for-series', {collectionTagId: tagId, collectionTagTitle: tagTitle, seriesIds}, {responseType: 'text' as 'json'});
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,46 @@
|
||||
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="modal-basic-title">Add to Collection</h4>
|
||||
<button type="button" class="close" aria-label="Close" (click)="close()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form style="width: 100%" [formGroup]="listForm">
|
||||
<div class="modal-body">
|
||||
<div class="form-group" *ngIf="lists.length >= 5">
|
||||
<label for="filter">Filter</label>
|
||||
<div class="input-group">
|
||||
<input id="filter" autocomplete="off" class="form-control" formControlName="filterQuery" type="text" aria-describedby="reset-input">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-outline-secondary" type="button" id="reset-input" (click)="listForm.get('filterQuery')?.setValue('');">Clear</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item clickable" tabindex="0" role="button" *ngFor="let collectionTag of lists | filter: filterList; let i = index" (click)="addToCollection(collectionTag)">
|
||||
{{collectionTag.title}} <i class="fa fa-angle-double-up" *ngIf="collectionTag.promoted" title="Promoted"></i>
|
||||
</li>
|
||||
<li class="list-group-item" *ngIf="lists.length === 0 && !loading">No collections created yet</li>
|
||||
<li class="list-group-item" *ngIf="loading">
|
||||
<div class="spinner-border text-secondary" role="status">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="modal-footer" style="justify-content: normal">
|
||||
<div style="width: 100%;">
|
||||
<div class="form-row">
|
||||
<div class="col-9 col-lg-10">
|
||||
<label class="sr-only" for="add-rlist">Collection</label>
|
||||
<input width="100%" #title ngbAutofocus type="text" class="form-control mb-2" id="add-rlist" formControlName="title">
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<button type="submit" class="btn btn-primary" (click)="create()">Create</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
@ -0,0 +1,7 @@
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.clickable:hover, .clickable:focus {
|
||||
background-color: lightgreen;
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
|
||||
import { FormGroup, FormControl } from '@angular/forms';
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { CollectionTag } from 'src/app/_models/collection-tag';
|
||||
import { ReadingList } from 'src/app/_models/reading-list';
|
||||
import { CollectionTagService } from 'src/app/_services/collection-tag.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-bulk-add-to-collection',
|
||||
templateUrl: './bulk-add-to-collection.component.html',
|
||||
styleUrls: ['./bulk-add-to-collection.component.scss']
|
||||
})
|
||||
export class BulkAddToCollectionComponent implements OnInit {
|
||||
|
||||
@Input() title!: string;
|
||||
/**
|
||||
* Series Ids to add to Collection Tag
|
||||
*/
|
||||
@Input() seriesIds: Array<number> = [];
|
||||
|
||||
/**
|
||||
* All existing collections sorted by recent use date
|
||||
*/
|
||||
lists: Array<CollectionTag> = [];
|
||||
loading: boolean = false;
|
||||
listForm: FormGroup = new FormGroup({});
|
||||
|
||||
@ViewChild('title') inputElem!: ElementRef<HTMLInputElement>;
|
||||
|
||||
|
||||
constructor(private modal: NgbActiveModal, private collectionService: CollectionTagService, private toastr: ToastrService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
this.listForm.addControl('title', new FormControl(this.title, []));
|
||||
this.listForm.addControl('filterQuery', new FormControl('', []));
|
||||
|
||||
this.loading = true;
|
||||
this.collectionService.allTags().subscribe(tags => {
|
||||
this.lists = tags;
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
// Shift focus to input
|
||||
if (this.inputElem) {
|
||||
this.inputElem.nativeElement.select();
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
this.modal.close();
|
||||
}
|
||||
|
||||
create() {
|
||||
const tagName = this.listForm.value.title;
|
||||
this.collectionService.addByMultiple(0, this.seriesIds, tagName).subscribe(() => {
|
||||
this.toastr.success('Series added to ' + tagName + ' collection');
|
||||
this.modal.close();
|
||||
});
|
||||
}
|
||||
|
||||
addToCollection(tag: CollectionTag) {
|
||||
if (this.seriesIds.length === 0) return;
|
||||
|
||||
this.collectionService.addByMultiple(tag.id, this.seriesIds, '').subscribe(() => {
|
||||
this.toastr.success('Series added to ' + tag.title + ' collection');
|
||||
this.modal.close();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
filterList = (listItem: ReadingList) => {
|
||||
return listItem.title.toLowerCase().indexOf((this.listForm.value.filterQuery || '').toLowerCase()) >= 0;
|
||||
}
|
||||
|
||||
}
|
@ -127,7 +127,7 @@ export class BulkSelectionService {
|
||||
getActions(callback: (action: Action, data: any) => void) {
|
||||
// checks if series is present. If so, returns only series actions
|
||||
// else returns volume/chapter items
|
||||
const allowedActions = [Action.AddToReadingList, Action.MarkAsRead, Action.MarkAsUnread];
|
||||
const allowedActions = [Action.AddToReadingList, Action.MarkAsRead, Action.MarkAsUnread, Action.AddToCollection];
|
||||
if (Object.keys(this.selectedCards).filter(item => item === 'series').length > 0) {
|
||||
return this.actionFactory.getSeriesActions(callback).filter(item => allowedActions.includes(item.action));
|
||||
}
|
||||
|
@ -16,10 +16,11 @@ import { CardItemComponent } from './card-item/card-item.component';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { TypeaheadModule } from '../typeahead/typeahead.module';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { CardDetailLayoutComponent } from './card-detail-layout/card-detail-layout.component';
|
||||
import { CardDetailsModalComponent } from './_modals/card-details-modal/card-details-modal.component';
|
||||
import { BulkOperationsComponent } from './bulk-operations/bulk-operations.component';
|
||||
import { BulkAddToCollectionComponent } from './_modals/bulk-add-to-collection/bulk-add-to-collection.component';
|
||||
import { PipeModule } from '../pipe/pipe.module';
|
||||
|
||||
|
||||
|
||||
@ -36,11 +37,11 @@ import { BulkOperationsComponent } from './bulk-operations/bulk-operations.compo
|
||||
CardActionablesComponent,
|
||||
CardDetailLayoutComponent,
|
||||
CardDetailsModalComponent,
|
||||
BulkOperationsComponent
|
||||
BulkOperationsComponent,
|
||||
BulkAddToCollectionComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
//BrowserModule,
|
||||
RouterModule,
|
||||
ReactiveFormsModule,
|
||||
FormsModule, // EditCollectionsModal
|
||||
@ -58,6 +59,7 @@ import { BulkOperationsComponent } from './bulk-operations/bulk-operations.compo
|
||||
NgbDropdownModule,
|
||||
NgbProgressbarModule,
|
||||
NgxFileDropModule, // Cover Chooser
|
||||
PipeModule // filter for BulkAddToCollectionComponent
|
||||
],
|
||||
exports: [
|
||||
CardItemComponent,
|
||||
|
@ -55,6 +55,11 @@ export class CollectionDetailComponent implements OnInit, OnDestroy {
|
||||
this.bulkSelectionService.deselectAll();
|
||||
});
|
||||
break;
|
||||
case Action.AddToCollection:
|
||||
this.actionService.addMultipleSeriesToCollectionTag(selectedSeries, () => {
|
||||
this.bulkSelectionService.deselectAll();
|
||||
});
|
||||
break;
|
||||
case Action.MarkAsRead:
|
||||
this.actionService.markMultipleSeriesAsRead(selectedSeries, () => {
|
||||
this.loadPage();
|
||||
|
@ -46,6 +46,11 @@ export class LibraryDetailComponent implements OnInit, OnDestroy {
|
||||
this.bulkSelectionService.deselectAll();
|
||||
});
|
||||
break;
|
||||
case Action.AddToCollection:
|
||||
this.actionService.addMultipleSeriesToCollectionTag(selectedSeries, () => {
|
||||
this.bulkSelectionService.deselectAll();
|
||||
});
|
||||
break;
|
||||
case Action.MarkAsRead:
|
||||
this.actionService.markMultipleSeriesAsRead(selectedSeries, () => {
|
||||
this.loadPage();
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { noUndefined } from '@angular/compiler/src/util';
|
||||
import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { FormControl, FormGroup } from '@angular/forms';
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { ReadingList } from 'src/app/_models/reading-list';
|
||||
|
@ -10,7 +10,7 @@ input {
|
||||
|
||||
.typeahead-input {
|
||||
border: 1px solid #ccc;
|
||||
padding: 4px 6px;
|
||||
padding: 0px 6px;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Component, ContentChild, ElementRef, EventEmitter, HostListener, Input, OnDestroy, OnInit, Output, Renderer2, RendererStyleFlags2, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { FormControl, FormGroup } from '@angular/forms';
|
||||
import { Observable, Observer, of, Subject } from 'rxjs';
|
||||
import { debounceTime, distinctUntilChanged, filter, last, map, shareReplay, switchMap, take, takeLast, takeUntil, tap, withLatestFrom } from 'rxjs/operators';
|
||||
import { Observable, of, Subject } from 'rxjs';
|
||||
import { debounceTime, filter, map, shareReplay, switchMap, take, takeUntil, tap } from 'rxjs/operators';
|
||||
import { KEY_CODES } from '../shared/_services/utility.service';
|
||||
import { TypeaheadSettings } from './typeahead-settings';
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user