Skip to content

Commit

Permalink
Implemented bulk delete chapter/volume on volume detail page.
Browse files Browse the repository at this point in the history
  • Loading branch information
majora2007 committed Nov 17, 2024
1 parent 4eca880 commit 0023a7d
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 6 deletions.
85 changes: 83 additions & 2 deletions API/Controllers/ChapterController.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using API.Data;
Expand All @@ -13,6 +14,7 @@
using API.SignalR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Nager.ArticleNumber;

namespace API.Controllers;
Expand All @@ -22,12 +24,14 @@ public class ChapterController : BaseApiController
private readonly IUnitOfWork _unitOfWork;
private readonly ILocalizationService _localizationService;
private readonly IEventHub _eventHub;
private readonly ILogger<ChapterController> _logger;

public ChapterController(IUnitOfWork unitOfWork, ILocalizationService localizationService, IEventHub eventHub)
public ChapterController(IUnitOfWork unitOfWork, ILocalizationService localizationService, IEventHub eventHub, ILogger<ChapterController> logger)
{
_unitOfWork = unitOfWork;
_localizationService = localizationService;
_eventHub = eventHub;
_logger = logger;
}

/// <summary>
Expand Down Expand Up @@ -84,6 +88,83 @@ public async Task<ActionResult<bool>> DeleteChapter(int chapterId)
return Ok(true);
}

/// <summary>
/// Deletes multiple chapters and any volumes with no leftover chapters
/// </summary>
/// <param name="seriesId">The ID of the series</param>
/// <param name="chapterIds">The IDs of the chapters to be deleted</param>
/// <returns></returns>
[Authorize(Policy = "RequireAdminRole")]
[HttpPost("delete-multiple")]
public async Task<ActionResult<bool>> DeleteMultipleChapters([FromQuery] int seriesId, DeleteChaptersDto dto)
{
try
{
var chapterIds = dto.ChapterIds;
if (chapterIds == null || chapterIds.Count == 0)
{
return BadRequest("ChapterIds required");
}

// Fetch all chapters to be deleted
var chapters = (await _unitOfWork.ChapterRepository.GetChaptersByIdsAsync(chapterIds)).ToList();

// Group chapters by their volume
var volumesToUpdate = chapters.GroupBy(c => c.VolumeId).ToList();
var removedVolumes = new List<int>();

foreach (var volumeGroup in volumesToUpdate)
{
var volumeId = volumeGroup.Key;
var chaptersToDelete = volumeGroup.ToList();

// Fetch the volume
var volume = await _unitOfWork.VolumeRepository.GetVolumeAsync(volumeId, VolumeIncludes.Chapters);
if (volume == null)
return BadRequest(_localizationService.Translate(User.GetUserId(), "volume-doesnt-exist"));

// Check if all chapters in the volume are being deleted
var isVolumeToBeRemoved = volume.Chapters.Count == chaptersToDelete.Count;

if (isVolumeToBeRemoved)
{
_unitOfWork.VolumeRepository.Remove(volume);
removedVolumes.Add(volume.Id);
}
else
{
// Remove only the specified chapters
_unitOfWork.ChapterRepository.Remove(chaptersToDelete);
}
}

if (!await _unitOfWork.CommitAsync()) return Ok(false);

// Send events for removed chapters
foreach (var chapter in chapters)
{
await _eventHub.SendMessageAsync(MessageFactory.ChapterRemoved,
MessageFactory.ChapterRemovedEvent(chapter.Id, seriesId), false);
}

// Send events for removed volumes
foreach (var volumeId in removedVolumes)
{
await _eventHub.SendMessageAsync(MessageFactory.VolumeRemoved,
MessageFactory.VolumeRemovedEvent(volumeId, seriesId), false);
}

return Ok(true);
}
catch (Exception ex)
{
_logger.LogError(ex, "An error occured while deleting chapters");
return BadRequest(_localizationService.Translate(User.GetUserId(), "generic-error"));
}

}


/// <summary>
/// Update chapter metadata
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion API/Controllers/SeriesController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ public async Task<ActionResult<bool>> DeleteSeries(int seriesId)
var username = User.GetUsername();
_logger.LogInformation("Series {SeriesId} is being deleted by {UserName}", seriesId, username);

return Ok(await _seriesService.DeleteMultipleSeries(new[] {seriesId}));
return Ok(await _seriesService.DeleteMultipleSeries([seriesId]));
}

[Authorize(Policy = "RequireAdminRole")]
Expand Down
6 changes: 6 additions & 0 deletions API/Data/Repositories/ChapterRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public interface IChapterRepository
{
void Update(Chapter chapter);
void Remove(Chapter chapter);
void Remove(IList<Chapter> chapters);
Task<IEnumerable<Chapter>> GetChaptersByIdsAsync(IList<int> chapterIds, ChapterIncludes includes = ChapterIncludes.None);
Task<IChapterInfoDto?> GetChapterInfoDtoAsync(int chapterId);
Task<int> GetChapterTotalPagesAsync(int chapterId);
Expand Down Expand Up @@ -68,6 +69,11 @@ public void Remove(Chapter chapter)
_context.Chapter.Remove(chapter);
}

public void Remove(IList<Chapter> chapters)
{
_context.Chapter.RemoveRange(chapters);
}

public async Task<IEnumerable<Chapter>> GetChaptersByIdsAsync(IList<int> chapterIds, ChapterIncludes includes = ChapterIncludes.None)
{
return await _context.Chapter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
Component,
DestroyRef,
ElementRef,
HostListener,
Inject,
inject,
OnInit,
Expand Down Expand Up @@ -45,7 +44,7 @@ import {
EditSeriesModalComponent
} from 'src/app/cards/_modals/edit-series-modal/edit-series-modal.component';
import {DownloadEvent, DownloadService} from 'src/app/shared/_services/download.service';
import {Breakpoint, KEY_CODES, UtilityService} from 'src/app/shared/_services/utility.service';
import {Breakpoint, UtilityService} from 'src/app/shared/_services/utility.service';
import {Chapter, LooseLeafOrDefaultNumber, SpecialVolumeNumber} from 'src/app/_models/chapter';
import {Device} from 'src/app/_models/device/device';
import {ScanSeriesEvent} from 'src/app/_models/events/scan-series-event';
Expand Down
14 changes: 13 additions & 1 deletion UI/Web/src/app/volume-detail/volume-detail.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ export class VolumeDetailComponent implements OnInit {
volumeActions: Array<ActionItem<Volume>> = this.actionFactoryService.getVolumeActions(this.handleVolumeAction.bind(this));
chapterActions: Array<ActionItem<Chapter>> = this.actionFactoryService.getChapterActions(this.handleChapterActionCallback.bind(this));

bulkActionCallback = (action: ActionItem<Chapter>, data: any) => {
bulkActionCallback = async (action: ActionItem<Chapter>, data: any) => {
if (this.volume === null) {
return;
}
Expand Down Expand Up @@ -256,6 +256,18 @@ export class VolumeDetailComponent implements OnInit {
this.cdRef.markForCheck();
});
break;
case Action.SendTo:
// this.actionService.sendToDevice(selectedChapterIds, _, () => {
//
// });
break;
case Action.Delete:
await this.actionService.deleteMultipleChapters(this.seriesId, selectedChapterIds, () => {
// No need to update the page as the backend will spam volume/chapter deletions
this.bulkSelectionService.deselectAll();
this.cdRef.markForCheck();
});
break;
}
}

Expand Down

0 comments on commit 0023a7d

Please sign in to comment.