Don't add ChapterPeople / SeriesMetataPeople if already present for that role and series

Redirects on merge for users currently on that page
This commit is contained in:
Amelia 2025-05-07 18:07:36 +02:00
parent 6ec7e80a43
commit 95dc321bf9
No known key found for this signature in database
GPG key ID: D6D0ECE365407EAA
5 changed files with 94 additions and 25 deletions

View file

@ -196,6 +196,8 @@ public class PersonController : BaseApiController
if (src == null) return BadRequest();
await _personService.MergePeopleAsync(dst, src);
await _eventHub.SendMessageAsync(MessageFactory.PersonMerged, MessageFactory.PersonMergedMessage(dst, src));
return Ok(_mapper.Map<PersonDto>(dst));
}

View file

@ -65,29 +65,8 @@ public class PersonService(IUnitOfWork unitOfWork): IPersonService
dst.CoverImage = src.CoverImage;
}
foreach (var chapter in src.ChapterPeople)
{
dst.ChapterPeople.Add(new ChapterPeople
{
Role = chapter.Role,
ChapterId = chapter.ChapterId,
Person = dst,
KavitaPlusConnection = chapter.KavitaPlusConnection,
OrderWeight = chapter.OrderWeight,
});
}
foreach (var series in src.SeriesMetadataPeople)
{
dst.SeriesMetadataPeople.Add(new SeriesMetadataPeople
{
SeriesMetadataId = series.SeriesMetadataId,
Role = series.Role,
Person = dst,
KavitaPlusConnection = series.KavitaPlusConnection,
OrderWeight = series.OrderWeight,
});
}
MergeChapterPeople(dst, src);
MergeSeriesMetadataPeople(dst, src);
dst.Aliases.Add(new PersonAlias
{
@ -105,6 +84,44 @@ public class PersonService(IUnitOfWork unitOfWork): IPersonService
await unitOfWork.CommitAsync();
}
private void MergeChapterPeople(Person dst, Person src)
{
foreach (var chapter in src.ChapterPeople)
{
var alreadyPresent = dst.ChapterPeople
.Any(x => x.ChapterId == chapter.ChapterId && x.Role == chapter.Role);
if (alreadyPresent) continue;
dst.ChapterPeople.Add(new ChapterPeople
{
Role = chapter.Role,
ChapterId = chapter.ChapterId,
Person = dst,
KavitaPlusConnection = chapter.KavitaPlusConnection,
OrderWeight = chapter.OrderWeight,
});
}
}
private void MergeSeriesMetadataPeople(Person dst, Person src)
{
foreach (var series in src.SeriesMetadataPeople)
{
var alreadyPresent = dst.SeriesMetadataPeople
.Any(x => x.SeriesMetadataId == series.SeriesMetadataId && x.Role == series.Role);
if (alreadyPresent) continue;
dst.SeriesMetadataPeople.Add(new SeriesMetadataPeople
{
SeriesMetadataId = series.SeriesMetadataId,
Role = series.Role,
Person = dst,
KavitaPlusConnection = series.KavitaPlusConnection,
OrderWeight = series.OrderWeight,
});
}
}
public async Task<bool> UpdatePersonAliasesAsync(Person person, IList<string> aliases)
{
var normalizedAliases = aliases

View file

@ -1,5 +1,6 @@
using System;
using API.DTOs.Update;
using API.Entities.Person;
using API.Extensions;
using API.Services.Plus;
@ -147,6 +148,10 @@ public static class MessageFactory
/// Volume is removed from server
/// </summary>
public const string VolumeRemoved = "VolumeRemoved";
/// <summary>
/// A Person merged has been merged into another
/// </summary>
public const string PersonMerged = "PersonMerged";
public static SignalRMessage DashboardUpdateEvent(int userId)
{
@ -661,4 +666,17 @@ public static class MessageFactory
EventType = ProgressEventType.Single,
};
}
public static SignalRMessage PersonMergedMessage(Person dst, Person src)
{
return new SignalRMessage()
{
Name = PersonMerged,
Body = new
{
srcId = src.Id,
dstName = dst.Name,
},
};
}
}

View file

@ -109,7 +109,11 @@ export enum EVENTS {
/**
* A Progress event when a smart collection is synchronizing
*/
SmartCollectionSync = 'SmartCollectionSync'
SmartCollectionSync = 'SmartCollectionSync',
/**
* A Person merged has been merged into another
*/
PersonMerged = 'PersonMerged',
}
export interface Message<T> {
@ -336,6 +340,13 @@ export class MessageHubService {
payload: resp.body
});
});
this.hubConnection.on(EVENTS.PersonMerged, resp => {
this.messagesSource.next({
event: EVENTS.PersonMerged,
payload: resp.body
});
})
}
stopHubConnection() {

View file

@ -5,6 +5,7 @@ import {
DestroyRef,
ElementRef,
inject,
OnInit,
ViewChild
} from '@angular/core';
import {ActivatedRoute, Router} from "@angular/router";
@ -42,6 +43,14 @@ import {ToastrService} from "ngx-toastr";
import {LicenseService} from "../_services/license.service";
import {SafeUrlPipe} from "../_pipes/safe-url.pipe";
import {MergePersonModalComponent} from "./_modal/merge-person-modal/merge-person-modal.component";
import {EVENTS, MessageHubService} from "../_services/message-hub.service";
interface PersonMergeEvent {
srcId: number,
dstId: number,
dstName: number,
}
@Component({
selector: 'app-person-detail',
@ -63,7 +72,7 @@ import {MergePersonModalComponent} from "./_modal/merge-person-modal/merge-perso
styleUrl: './person-detail.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class PersonDetailComponent {
export class PersonDetailComponent implements OnInit {
private readonly route = inject(ActivatedRoute);
private readonly router = inject(Router);
private readonly filterUtilityService = inject(FilterUtilitiesService);
@ -77,6 +86,7 @@ export class PersonDetailComponent {
protected readonly licenseService = inject(LicenseService);
private readonly themeService = inject(ThemeService);
private readonly toastr = inject(ToastrService);
private readonly messageHubService = inject(MessageHubService)
protected readonly TagBadgeCursor = TagBadgeCursor;
@ -129,6 +139,17 @@ export class PersonDetailComponent {
).subscribe();
}
ngOnInit(): void {
this.messageHubService.messages$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(message => {
if (message.event !== EVENTS.PersonMerged) return;
const event = message.payload as PersonMergeEvent;
if (event.srcId !== this.person?.id) return;
this.router.navigate(['person', event.dstName]);
});
}
private setPerson(person: Person) {
this.person = person;
this.personSubject.next(person); // emit the person data for subscribers