diff --git a/API/Controllers/CollectionController.cs b/API/Controllers/CollectionController.cs
index 049413388..681a962d0 100644
--- a/API/Controllers/CollectionController.cs
+++ b/API/Controllers/CollectionController.cs
@@ -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");
}
+ ///
+ /// Adds a collection tag onto multiple Series. If tag id is 0, this will create a new tag.
+ ///
+ ///
+ ///
+ [HttpPost("update-for-series")]
+ public async Task 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");
+ }
+
///
/// For a given tag, update the summary if summary has changed and remove a set of series from the tag.
///
diff --git a/API/Controllers/OPDSController.cs b/API/Controllers/OPDSController.cs
index c9e527e2c..22103eb2b 100644
--- a/API/Controllers/OPDSController.cs
+++ b/API/Controllers/OPDSController.cs
@@ -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;
diff --git a/API/DTOs/CollectionTags/CollectionTagBulkAddDto.cs b/API/DTOs/CollectionTags/CollectionTagBulkAddDto.cs
new file mode 100644
index 000000000..ac28e81cb
--- /dev/null
+++ b/API/DTOs/CollectionTags/CollectionTagBulkAddDto.cs
@@ -0,0 +1,18 @@
+using System.Collections.Generic;
+
+namespace API.DTOs.CollectionTags
+{
+ public class CollectionTagBulkAddDto
+ {
+ ///
+ /// Collection Tag Id
+ ///
+ /// Can be 0 which then will use Title to create a tag
+ public int CollectionTagId { get; init; }
+ public string CollectionTagTitle { get; init; }
+ ///
+ /// Series Ids to add onto Collection Tag
+ ///
+ public IEnumerable SeriesIds { get; init; }
+ }
+}
diff --git a/API/DTOs/CollectionTagDto.cs b/API/DTOs/CollectionTags/CollectionTagDto.cs
similarity index 87%
rename from API/DTOs/CollectionTagDto.cs
rename to API/DTOs/CollectionTags/CollectionTagDto.cs
index cb9870610..8612f19e0 100644
--- a/API/DTOs/CollectionTagDto.cs
+++ b/API/DTOs/CollectionTags/CollectionTagDto.cs
@@ -1,4 +1,4 @@
-namespace API.DTOs
+namespace API.DTOs.CollectionTags
{
public class CollectionTagDto
{
diff --git a/API/DTOs/UpdateSeriesForTagDto.cs b/API/DTOs/CollectionTags/UpdateSeriesForTagDto.cs
similarity index 58%
rename from API/DTOs/UpdateSeriesForTagDto.cs
rename to API/DTOs/CollectionTags/UpdateSeriesForTagDto.cs
index 743981165..1a844ee18 100644
--- a/API/DTOs/UpdateSeriesForTagDto.cs
+++ b/API/DTOs/CollectionTags/UpdateSeriesForTagDto.cs
@@ -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 SeriesIdsToRemove { get; init; }
+ public IEnumerable SeriesIdsToRemove { get; init; }
}
-}
\ No newline at end of file
+}
diff --git a/API/DTOs/SeriesMetadataDto.cs b/API/DTOs/SeriesMetadataDto.cs
index 47d5cbee2..69dcae2d9 100644
--- a/API/DTOs/SeriesMetadataDto.cs
+++ b/API/DTOs/SeriesMetadataDto.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using API.DTOs.CollectionTags;
using API.Entities;
namespace API.DTOs
diff --git a/API/DTOs/UpdateSeriesMetadataDto.cs b/API/DTOs/UpdateSeriesMetadataDto.cs
index a9c852632..dd43167c9 100644
--- a/API/DTOs/UpdateSeriesMetadataDto.cs
+++ b/API/DTOs/UpdateSeriesMetadataDto.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using API.DTOs.CollectionTags;
namespace API.DTOs
{
diff --git a/API/Data/Repositories/CollectionTagRepository.cs b/API/Data/Repositories/CollectionTagRepository.cs
index 2766e6ba7..182fa86b8 100644
--- a/API/Data/Repositories/CollectionTagRepository.cs
+++ b/API/Data/Repositories/CollectionTagRepository.cs
@@ -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);
diff --git a/API/Data/Repositories/SeriesMetadataRepository.cs b/API/Data/Repositories/SeriesMetadataRepository.cs
new file mode 100644
index 000000000..32ab0f4e2
--- /dev/null
+++ b/API/Data/Repositories/SeriesMetadataRepository.cs
@@ -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);
+ }
+ }
+}
diff --git a/API/Data/Repositories/SeriesRepository.cs b/API/Data/Repositories/SeriesRepository.cs
index 67cd83276..281c2e325 100644
--- a/API/Data/Repositories/SeriesRepository.cs
+++ b/API/Data/Repositories/SeriesRepository.cs
@@ -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> GetSeriesMetadataForIdsAsync(IEnumerable seriesIds)
+ {
+ return await _context.SeriesMetadata
+ .Where(sm => seriesIds.Contains(sm.SeriesId))
+ .Include(sm => sm.CollectionTags)
+ .ToListAsync();
+ }
}
}
diff --git a/API/Data/UnitOfWork.cs b/API/Data/UnitOfWork.cs
index 64f9c4fe0..a1f797188 100644
--- a/API/Data/UnitOfWork.cs
+++ b/API/Data/UnitOfWork.cs
@@ -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);
///
/// Commits changes to the DB. Completes the open transaction.
diff --git a/API/Entities/ServerSetting.cs b/API/Entities/ServerSetting.cs
index b09ae71e0..6c4b5f21d 100644
--- a/API/Entities/ServerSetting.cs
+++ b/API/Entities/ServerSetting.cs
@@ -8,6 +8,9 @@ namespace API.Entities
{
[Key]
public ServerSettingKey Key { get; set; }
+ ///
+ /// The value of the Setting. Converter knows how to convert to the correct type
+ ///
public string Value { get; set; }
///
diff --git a/API/Helpers/AutoMapperProfiles.cs b/API/Helpers/AutoMapperProfiles.cs
index ff1a30e34..74bd8d57c 100644
--- a/API/Helpers/AutoMapperProfiles.cs
+++ b/API/Helpers/AutoMapperProfiles.cs
@@ -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;
diff --git a/API/Interfaces/IUnitOfWork.cs b/API/Interfaces/IUnitOfWork.cs
index 06b47bbe8..733008192 100644
--- a/API/Interfaces/IUnitOfWork.cs
+++ b/API/Interfaces/IUnitOfWork.cs
@@ -15,6 +15,7 @@ namespace API.Interfaces
IFileRepository FileRepository { get; }
IChapterRepository ChapterRepository { get; }
IReadingListRepository ReadingListRepository { get; }
+ ISeriesMetadataRepository SeriesMetadataRepository { get; }
bool Commit();
Task CommitAsync();
bool HasChanges();
diff --git a/API/Interfaces/Repositories/ICollectionTagRepository.cs b/API/Interfaces/Repositories/ICollectionTagRepository.cs
index 03a552bd9..18c9f490b 100644
--- a/API/Interfaces/Repositories/ICollectionTagRepository.cs
+++ b/API/Interfaces/Repositories/ICollectionTagRepository.cs
@@ -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> GetAllTagDtosAsync();
Task> SearchTagDtosAsync(string searchQuery);
diff --git a/API/Interfaces/Repositories/ISeriesMetadataRepository.cs b/API/Interfaces/Repositories/ISeriesMetadataRepository.cs
new file mode 100644
index 000000000..00dd234ee
--- /dev/null
+++ b/API/Interfaces/Repositories/ISeriesMetadataRepository.cs
@@ -0,0 +1,9 @@
+using API.Entities;
+
+namespace API.Interfaces.Repositories
+{
+ public interface ISeriesMetadataRepository
+ {
+ void Update(SeriesMetadata seriesMetadata);
+ }
+}
diff --git a/API/Interfaces/Repositories/ISeriesRepository.cs b/API/Interfaces/Repositories/ISeriesRepository.cs
index 0b3ed8eeb..b080a904f 100644
--- a/API/Interfaces/Repositories/ISeriesRepository.cs
+++ b/API/Interfaces/Repositories/ISeriesRepository.cs
@@ -54,5 +54,6 @@ namespace API.Interfaces.Repositories
Task> GetFullSeriesForLibraryIdAsync(int libraryId, UserParams userParams);
Task GetFullSeriesForSeriesIdAsync(int seriesId);
Task GetChunkInfo(int libraryId = 0);
+ Task> GetSeriesMetadataForIdsAsync(IEnumerable seriesIds);
}
}
diff --git a/API/Parser/Parser.cs b/API/Parser/Parser.cs
index 9bd2aab82..e082f06e0 100644
--- a/API/Parser/Parser.cs
+++ b/API/Parser/Parser.cs
@@ -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[\"|'])(?[\\w\\d/\\._-]+)([\"|'];?)",
+ public static readonly Regex CssImportUrlRegex = new Regex("@import\\s([\"|']|url\\([\"|'])(?[^'\"]+)[\"|']\\)?;",
MatchOptions, RegexTimeout);
private static readonly string XmlRegexExtensions = @"\.xml";
diff --git a/UI/Web/src/app/_services/action-factory.service.ts b/UI/Web/src/app/_services/action-factory.service.ts
index 450b577bc..0df929e8f 100644
--- a/UI/Web/src/app/_services/action-factory.service.ts
+++ b/UI/Web/src/app/_services/action-factory.service.ts
@@ -19,7 +19,8 @@ export enum Action {
Download = 7,
Bookmarks = 8,
IncognitoRead = 9,
- AddToReadingList = 10
+ AddToReadingList = 10,
+ AddToCollection = 11
}
export interface ActionItem {
@@ -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 = [
diff --git a/UI/Web/src/app/_services/action.service.ts b/UI/Web/src/app/_services/action.service.ts
index 3d654108f..ad79a5b32 100644
--- a/UI/Web/src/app/_services/action.service.ts
+++ b/UI/Web/src/app/_services/action.service.ts
@@ -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();
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, 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' });
diff --git a/UI/Web/src/app/_services/collection-tag.service.ts b/UI/Web/src/app/_services/collection-tag.service.ts
index 87d275f43..dd8571b6a 100644
--- a/UI/Web/src/app/_services/collection-tag.service.ts
+++ b/UI/Web/src/app/_services/collection-tag.service.ts
@@ -35,4 +35,8 @@ export class CollectionTagService {
updateSeriesForTag(tag: CollectionTag, seriesIdsToRemove: Array) {
return this.httpClient.post(this.baseUrl + 'collection/update-series', {tag, seriesIdsToRemove}, {responseType: 'text' as 'json'});
}
+
+ addByMultiple(tagId: number, seriesIds: Array, tagTitle: string = '') {
+ return this.httpClient.post(this.baseUrl + 'collection/update-for-series', {collectionTagId: tagId, collectionTagTitle: tagTitle, seriesIds}, {responseType: 'text' as 'json'});
+ }
}
diff --git a/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.html b/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.html
new file mode 100644
index 000000000..2e7dcecb2
--- /dev/null
+++ b/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.html
@@ -0,0 +1,46 @@
+
+
+
+
+
diff --git a/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.scss b/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.scss
new file mode 100644
index 000000000..91847160a
--- /dev/null
+++ b/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.scss
@@ -0,0 +1,7 @@
+.clickable {
+ cursor: pointer;
+}
+
+.clickable:hover, .clickable:focus {
+ background-color: lightgreen;
+}
\ No newline at end of file
diff --git a/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.ts b/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.ts
new file mode 100644
index 000000000..13641550b
--- /dev/null
+++ b/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.ts
@@ -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 = [];
+
+ /**
+ * All existing collections sorted by recent use date
+ */
+ lists: Array = [];
+ loading: boolean = false;
+ listForm: FormGroup = new FormGroup({});
+
+ @ViewChild('title') inputElem!: ElementRef;
+
+
+ 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;
+ }
+
+}
diff --git a/UI/Web/src/app/cards/bulk-selection.service.ts b/UI/Web/src/app/cards/bulk-selection.service.ts
index 47072c014..1cc205725 100644
--- a/UI/Web/src/app/cards/bulk-selection.service.ts
+++ b/UI/Web/src/app/cards/bulk-selection.service.ts
@@ -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));
}
diff --git a/UI/Web/src/app/cards/cards.module.ts b/UI/Web/src/app/cards/cards.module.ts
index 7c7db5c54..dc1a23134 100644
--- a/UI/Web/src/app/cards/cards.module.ts
+++ b/UI/Web/src/app/cards/cards.module.ts
@@ -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,
diff --git a/UI/Web/src/app/collections/collection-detail/collection-detail.component.ts b/UI/Web/src/app/collections/collection-detail/collection-detail.component.ts
index 086df24a3..34ca7b659 100644
--- a/UI/Web/src/app/collections/collection-detail/collection-detail.component.ts
+++ b/UI/Web/src/app/collections/collection-detail/collection-detail.component.ts
@@ -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();
diff --git a/UI/Web/src/app/library-detail/library-detail.component.ts b/UI/Web/src/app/library-detail/library-detail.component.ts
index 57c85d789..678a52d20 100644
--- a/UI/Web/src/app/library-detail/library-detail.component.ts
+++ b/UI/Web/src/app/library-detail/library-detail.component.ts
@@ -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();
diff --git a/UI/Web/src/app/reading-list/_modals/add-to-list-modal/add-to-list-modal.component.ts b/UI/Web/src/app/reading-list/_modals/add-to-list-modal/add-to-list-modal.component.ts
index a33d69332..3d88fcd1b 100644
--- a/UI/Web/src/app/reading-list/_modals/add-to-list-modal/add-to-list-modal.component.ts
+++ b/UI/Web/src/app/reading-list/_modals/add-to-list-modal/add-to-list-modal.component.ts
@@ -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';
diff --git a/UI/Web/src/app/typeahead/typeahead.component.scss b/UI/Web/src/app/typeahead/typeahead.component.scss
index 2672736ad..d798e2c5b 100644
--- a/UI/Web/src/app/typeahead/typeahead.component.scss
+++ b/UI/Web/src/app/typeahead/typeahead.component.scss
@@ -10,7 +10,7 @@ input {
.typeahead-input {
border: 1px solid #ccc;
- padding: 4px 6px;
+ padding: 0px 6px;
display: inline-block;
width: 100%;
overflow: hidden;
diff --git a/UI/Web/src/app/typeahead/typeahead.component.ts b/UI/Web/src/app/typeahead/typeahead.component.ts
index 3ebf81280..99b721f18 100644
--- a/UI/Web/src/app/typeahead/typeahead.component.ts
+++ b/UI/Web/src/app/typeahead/typeahead.component.ts
@@ -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';