mirror of
https://github.com/booklore-app/booklore.git
synced 2026-02-18 00:17:53 +01:00
Fix subscription leak
This commit is contained in:
@@ -1,24 +1,48 @@
|
||||
import {Component, inject, OnDestroy, OnInit, Optional} from '@angular/core';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {UserService} from '../../../settings/user-management/user.service';
|
||||
import {Book, BookRecommendation} from '../../model/book.model';
|
||||
import {BehaviorSubject, Observable, Subscription} from 'rxjs';
|
||||
import {distinctUntilChanged, filter, map, shareReplay, switchMap, take, tap} from 'rxjs/operators';
|
||||
import {BookService} from '../../service/book.service';
|
||||
import {AppSettingsService} from '../../../core/service/app-settings.service';
|
||||
import {Tab, TabList, TabPanel, TabPanels, Tabs} from 'primeng/tabs';
|
||||
import {MetadataViewerComponent} from './metadata-viewer/metadata-viewer.component';
|
||||
import {MetadataEditorComponent} from './metadata-editor/metadata-editor.component';
|
||||
import {MetadataSearcherComponent} from './metadata-searcher/metadata-searcher.component';
|
||||
import {DynamicDialogConfig} from 'primeng/dynamicdialog';
|
||||
import {BookMetadataHostService} from '../../../book-metadata-host-service';
|
||||
import { Component, inject, OnDestroy, OnInit, Optional } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { UserService } from '../../../settings/user-management/user.service';
|
||||
import { Book, BookRecommendation } from '../../model/book.model';
|
||||
import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
||||
import {
|
||||
distinctUntilChanged,
|
||||
filter,
|
||||
map,
|
||||
shareReplay,
|
||||
switchMap,
|
||||
takeUntil,
|
||||
tap,
|
||||
take,
|
||||
} from 'rxjs/operators';
|
||||
import { BookService } from '../../service/book.service';
|
||||
import { AppSettingsService } from '../../../core/service/app-settings.service';
|
||||
import {
|
||||
Tab,
|
||||
TabList,
|
||||
TabPanel,
|
||||
TabPanels,
|
||||
Tabs,
|
||||
} from 'primeng/tabs';
|
||||
import { MetadataViewerComponent } from './metadata-viewer/metadata-viewer.component';
|
||||
import { MetadataEditorComponent } from './metadata-editor/metadata-editor.component';
|
||||
import { MetadataSearcherComponent } from './metadata-searcher/metadata-searcher.component';
|
||||
import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
|
||||
import { BookMetadataHostService } from '../../../book-metadata-host-service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-book-metadata-center',
|
||||
standalone: true,
|
||||
templateUrl: './book-metadata-center.component.html',
|
||||
imports: [Tabs, TabList, Tab, TabPanels, TabPanel, MetadataViewerComponent, MetadataEditorComponent, MetadataSearcherComponent],
|
||||
styleUrls: ['./book-metadata-center.component.scss']
|
||||
imports: [
|
||||
Tabs,
|
||||
TabList,
|
||||
Tab,
|
||||
TabPanels,
|
||||
TabPanel,
|
||||
MetadataViewerComponent,
|
||||
MetadataEditorComponent,
|
||||
MetadataSearcherComponent,
|
||||
],
|
||||
styleUrls: ['./book-metadata-center.component.scss'],
|
||||
})
|
||||
export class BookMetadataCenterComponent implements OnInit, OnDestroy {
|
||||
private route = inject(ActivatedRoute);
|
||||
@@ -26,6 +50,7 @@ export class BookMetadataCenterComponent implements OnInit, OnDestroy {
|
||||
private userService = inject(UserService);
|
||||
private appSettingsService = inject(AppSettingsService);
|
||||
private metadataHostService = inject(BookMetadataHostService);
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
book$!: Observable<Book>;
|
||||
recommendedBooks: BookRecommendation[] = [];
|
||||
@@ -33,14 +58,13 @@ export class BookMetadataCenterComponent implements OnInit, OnDestroy {
|
||||
canEditMetadata: boolean = false;
|
||||
admin: boolean = false;
|
||||
|
||||
private userSubscription: Subscription = Subscription.EMPTY;
|
||||
private tabSubscription: Subscription = Subscription.EMPTY;
|
||||
private recommendationSubscription: Subscription = Subscription.EMPTY;
|
||||
private appSettings$ = this.appSettingsService.appSettings$;
|
||||
private currentBookId$ = new BehaviorSubject<number | null>(null);
|
||||
|
||||
constructor(@Optional() private config?: DynamicDialogConfig) {
|
||||
}
|
||||
constructor(
|
||||
@Optional() private config?: DynamicDialogConfig,
|
||||
@Optional() private ref?: DynamicDialogRef
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.bookService.loadBooks();
|
||||
@@ -51,7 +75,8 @@ export class BookMetadataCenterComponent implements OnInit, OnDestroy {
|
||||
this.route.paramMap
|
||||
.pipe(
|
||||
map(params => Number(params.get('bookId'))),
|
||||
filter(bookId => !isNaN(bookId))
|
||||
filter(bookId => !isNaN(bookId)),
|
||||
takeUntil(this.destroy$)
|
||||
)
|
||||
.subscribe(bookId => this.currentBookId$.next(bookId));
|
||||
}
|
||||
@@ -59,7 +84,8 @@ export class BookMetadataCenterComponent implements OnInit, OnDestroy {
|
||||
this.metadataHostService.bookSwitches$
|
||||
.pipe(
|
||||
filter((bookId): bookId is number => !!bookId),
|
||||
distinctUntilChanged()
|
||||
distinctUntilChanged(),
|
||||
takeUntil(this.destroy$)
|
||||
)
|
||||
.subscribe(bookId => {
|
||||
this.currentBookId$.next(bookId);
|
||||
@@ -72,29 +98,39 @@ export class BookMetadataCenterComponent implements OnInit, OnDestroy {
|
||||
this.bookService.bookState$.pipe(
|
||||
map(state => state.books?.find(b => b.id === bookId)),
|
||||
filter((book): book is Book => !!book && !!book.metadata),
|
||||
switchMap(book => this.bookService.getBookByIdFromAPI(book.id, true))
|
||||
switchMap(book =>
|
||||
this.bookService.getBookByIdFromAPI(book.id, true)
|
||||
)
|
||||
)
|
||||
),
|
||||
takeUntil(this.destroy$),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
this.recommendationSubscription = this.currentBookId$.pipe(
|
||||
filter((id): id is number => id != null),
|
||||
tap(bookId => this.fetchBookRecommendationsIfNeeded(bookId))
|
||||
).subscribe();
|
||||
this.currentBookId$
|
||||
.pipe(
|
||||
filter((id): id is number => id != null),
|
||||
takeUntil(this.destroy$)
|
||||
)
|
||||
.subscribe(bookId => this.fetchBookRecommendationsIfNeeded(bookId));
|
||||
|
||||
this.tabSubscription = this.route.queryParamMap.pipe(
|
||||
map(params => params.get('tab') ?? 'view'),
|
||||
distinctUntilChanged()
|
||||
).subscribe(tab => {
|
||||
const validTabs = ['view', 'edit', 'match'];
|
||||
this.tab = validTabs.includes(tab) ? tab : 'view';
|
||||
});
|
||||
this.route.queryParamMap
|
||||
.pipe(
|
||||
map(params => params.get('tab') ?? 'view'),
|
||||
distinctUntilChanged(),
|
||||
takeUntil(this.destroy$)
|
||||
)
|
||||
.subscribe(tab => {
|
||||
const validTabs = ['view', 'edit', 'match'];
|
||||
this.tab = validTabs.includes(tab) ? tab : 'view';
|
||||
});
|
||||
|
||||
this.userSubscription = this.userService.userState$.subscribe(userData => {
|
||||
this.canEditMetadata = userData?.permissions?.canEditMetadata ?? false;
|
||||
this.admin = userData?.permissions?.admin ?? false;
|
||||
});
|
||||
this.userService.userState$
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(userData => {
|
||||
this.canEditMetadata = userData?.permissions?.canEditMetadata ?? false;
|
||||
this.admin = userData?.permissions?.admin ?? false;
|
||||
});
|
||||
}
|
||||
|
||||
private fetchBookRecommendationsIfNeeded(bookId: number): void {
|
||||
@@ -105,18 +141,20 @@ export class BookMetadataCenterComponent implements OnInit, OnDestroy {
|
||||
)
|
||||
.subscribe(settings => {
|
||||
if (settings!.similarBookRecommendation ?? false) {
|
||||
this.bookService.getBookRecommendations(bookId).subscribe(recommendations => {
|
||||
this.recommendedBooks = recommendations.sort(
|
||||
(a, b) => (b.similarityScore ?? 0) - (a.similarityScore ?? 0)
|
||||
);
|
||||
});
|
||||
this.bookService
|
||||
.getBookRecommendations(bookId)
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(recommendations => {
|
||||
this.recommendedBooks = recommendations.sort(
|
||||
(a, b) => (b.similarityScore ?? 0) - (a.similarityScore ?? 0)
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.userSubscription.unsubscribe();
|
||||
this.tabSubscription.unsubscribe();
|
||||
this.recommendationSubscription.unsubscribe();
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user