Refactored the toc into its own drawer, but need to get a mechanism setup to sync pageNumber, chapterId.

This commit is contained in:
Joseph Milazzo 2025-07-04 13:35:20 -05:00
parent 4d4b3c7285
commit 28755bdb34
7 changed files with 180 additions and 90 deletions

View File

@ -11,7 +11,11 @@ import {
ViewBookmarkDrawerComponent
} from "../book-reader/_components/_drawers/view-bookmarks-drawer/view-bookmark-drawer.component";
import {ActivatedRoute} from "@angular/router";
import {ViewTocDrawerComponent} from "../book-reader/_components/_drawers/view-toc-drawer/view-toc-drawer.component";
import {
LoadPageEvent,
ViewTocDrawerComponent
} from "../book-reader/_components/_drawers/view-toc-drawer/view-toc-drawer.component";
import {UserBreakpoint, UtilityService} from "../shared/_services/utility.service";
/**
* Responsible for opening the different readers and providing any context needed. Handles closing or keeping a stack of menus open.
@ -22,6 +26,7 @@ import {ViewTocDrawerComponent} from "../book-reader/_components/_drawers/view-t
export class EpubReaderMenuService {
private readonly offcanvasService = inject(NgbOffcanvas);
private readonly utilityService = inject(UtilityService);
private readonly route = inject(ActivatedRoute);
@ -38,11 +43,19 @@ export class EpubReaderMenuService {
const ref = this.offcanvasService.open(ViewAnnotationDrawerComponent, {position: 'end', panelClass: ''});
}
openTocDrawer() {
openViewTocDrawer(chapterId: number, callbackFn: (evt: LoadPageEvent | null) => void) {
if (this.offcanvasService.hasOpenOffcanvas()) {
this.offcanvasService.dismiss();
}
const ref = this.offcanvasService.open(ViewTocDrawerComponent, {position: 'end', panelClass: ''});
ref.componentInstance.chapterId.set(chapterId);
ref.componentInstance.loadPage.subscribe((res: LoadPageEvent | null) => {
// Check if we are on mobile to collapse the menu
if (this.utilityService.activeUserBreakpoint() <= UserBreakpoint.Mobile) {
this.closeAll();
}
callbackFn(res);
});
}
openViewBookmarksDrawer(chapterId: number) {

View File

@ -30,8 +30,10 @@ export class ViewBookmarkDrawerComponent {
constructor() {
effect(() => {
const id = this.chapterId();
console.log('chapter id', id);
if (!id) return;
if (!id) {
console.error('You must pass chapterId');
return;
}
this.readerService.getBookmarks(id).subscribe(bookmarks => {
this.bookmarks.set(bookmarks);

View File

@ -8,7 +8,22 @@
<div class="offcanvas-body">
Hello
<ul #subnav="ngbNav" ngbNav [(activeId)]="tocId" class="reader-pills nav nav-pills mb-2" [destroyOnHide]="false">
<li [ngbNavItem]="TabID.TableOfContents">
<a ngbNavLink>{{t('toc-header')}}</a>
<ng-template ngbNavContent>
<app-table-of-contents [chapters]="chapters()" [chapterId]="chapterId()!" [pageNum]="pageNum"
[currentPageAnchor]="currentPageAnchor" (loadChapter)="loadChapterPage($event)"></app-table-of-contents>
</ng-template>
</li>
<li [ngbNavItem]="TabID.PersonalTableOfContents">
<a ngbNavLink>{{t('personal-header')}}</a>
<ng-template ngbNavContent>
<app-personal-table-of-contents [chapterId]="chapterId()!" [pageNum]="pageNum" (loadChapter)="loadChapterPart($event)"
[tocRefresh]="refreshPToC"></app-personal-table-of-contents>
</ng-template>
</li>
</ul>
<div [ngbNavOutlet]="subnav" class="mt-3"></div>
</div>
</ng-container>

View File

@ -1,11 +1,53 @@
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject} from '@angular/core';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
effect,
EventEmitter,
inject,
model
} from '@angular/core';
import {TranslocoDirective} from "@jsverse/transloco";
import {NgbActiveOffcanvas} from "@ng-bootstrap/ng-bootstrap";
import {
NgbActiveOffcanvas,
NgbNav,
NgbNavContent,
NgbNavItem,
NgbNavLink,
NgbNavOutlet
} from "@ng-bootstrap/ng-bootstrap";
import {
PersonalTableOfContentsComponent,
PersonalToCEvent
} from "../../personal-table-of-contents/personal-table-of-contents.component";
import {TableOfContentsComponent} from "../../table-of-contents/table-of-contents.component";
import {BookChapterItem} from "../../../_models/book-chapter-item";
import {BookService} from "../../../_services/book.service";
enum TabID {
TableOfContents = 1,
PersonalTableOfContents = 2
}
export interface LoadPageEvent {
pageNumber: number;
part: string;
}
@Component({
selector: 'app-view-toc-drawer',
imports: [
TranslocoDirective
TranslocoDirective,
PersonalTableOfContentsComponent,
NgbNav,
NgbNavContent,
NgbNavLink,
TableOfContentsComponent,
NgbNavOutlet,
NgbNavItem
],
templateUrl: './view-toc-drawer.component.html',
styleUrl: './view-toc-drawer.component.scss',
@ -14,6 +56,73 @@ import {NgbActiveOffcanvas} from "@ng-bootstrap/ng-bootstrap";
export class ViewTocDrawerComponent {
private readonly activeOffcanvas = inject(NgbActiveOffcanvas);
private readonly cdRef = inject(ChangeDetectorRef);
private readonly bookService = inject(BookService);
chapterId = model<number>();
/**
* Sub Nav tab id
*/
tocId: TabID = TabID.TableOfContents;
/**
* The actual pages from the epub, used for showing on table of contents. This must be here as we need access to it for scroll anchors
*/
chapters = model<Array<BookChapterItem>>([]);
/**
* Current Page
*/
pageNum = 0;
/**
* A anchors that map to the page number. When you click on one of these, we will load a given page up for the user.
*/
pageAnchors: {[n: string]: number } = {};
currentPageAnchor: string = '';
protected readonly TabID = TabID;
/**
* Used to refresh the Personal PoC
*/
refreshPToC: EventEmitter<void> = new EventEmitter<void>();
loadPage: EventEmitter<LoadPageEvent | null> = new EventEmitter<LoadPageEvent | null>();
constructor() {
effect(() => {
const id = this.chapterId();
if (!id) {
console.error('You must pass chapterId');
return;
}
this.bookService.getBookChapters(id).subscribe(bookChapters => {
this.chapters.set(bookChapters);
this.cdRef.markForCheck();
});
});
}
/**
* From personal table of contents/bookmark
* @param event
*/
loadChapterPart(event: PersonalToCEvent) {
// this.setPageNum(event.pageNum);
// this.loadPage(event.scrollPart);
// TODO: Emit this event to let the main book reader handle
const evt = {pageNumber: event.pageNum, part:event.scrollPart} as LoadPageEvent;
this.loadPage.emit(evt);
}
loadChapterPage(event: {pageNum: number, part: string}) {
// this.setPageNum(event.pageNum);
// this.loadPage('id("' + event.part + '")');
// TODO: Emit this event to let the main book reader handle
const evt = {pageNumber: event.pageNum, part: `id("${event.part}")`} as LoadPageEvent;
this.loadPage.emit(evt);
}
close() {

View File

@ -55,51 +55,18 @@
</div>
</div>
<div body class="drawer-body">
<nav role="navigation">
<ul ngbNav #nav="ngbNav" [(activeId)]="activeTabId" class="reader-pills nav nav-pills mb-2" [destroyOnHide]="false">
<li [ngbNavItem]="TabID.Settings">
<a ngbNavLink>{{t('settings-header')}}</a>
<ng-template ngbNavContent>
<app-reader-settings
[seriesId]="seriesId"
[readingProfile]="readingProfile"
(colorThemeUpdate)="updateColorTheme($event)"
(styleUpdate)="updateReaderStyles($event)"
(clickToPaginateChanged)="showPaginationOverlay($event)"
(fullscreen)="toggleFullscreen()"
(bookReaderWritingStyle)="updateWritingStyle($event)"
(layoutModeUpdate)="updateLayoutMode($event)"
(readingDirection)="updateReadingDirection($event)"
(immersiveMode)="updateImmersiveMode($event)"
></app-reader-settings>
</ng-template>
</li>
<li [ngbNavItem]="TabID.TableOfContents">
<a ngbNavLink>{{t('table-of-contents-header')}}</a>
<ng-template ngbNavContent>
<ul #subnav="ngbNav" ngbNav [(activeId)]="tocId" class="reader-pills nav nav-pills mb-2" [destroyOnHide]="false">
<li [ngbNavItem]="TabID.TableOfContents">
<a ngbNavLink>{{t('toc-header')}}</a>
<ng-template ngbNavContent>
<app-table-of-contents [chapters]="chapters" [chapterId]="chapterId" [pageNum]="pageNum"
[currentPageAnchor]="currentPageAnchor" (loadChapter)="loadChapterPage($event)"></app-table-of-contents>
</ng-template>
</li>
<li [ngbNavItem]="TabID.PersonalTableOfContents">
<a ngbNavLink>{{t('bookmarks-header')}}</a>
<ng-template ngbNavContent>
<app-personal-table-of-contents [chapterId]="chapterId" [pageNum]="pageNum" (loadChapter)="loadChapterPart($event)"
[tocRefresh]="refreshPToC"></app-personal-table-of-contents>
</ng-template>
</li>
</ul>
<div [ngbNavOutlet]="subnav" class="mt-3"></div>
</ng-template>
</li>
</ul>
</nav>
<div [ngbNavOutlet]="nav" class="mt-3"></div>
<app-reader-settings
[seriesId]="seriesId"
[readingProfile]="readingProfile"
(colorThemeUpdate)="updateColorTheme($event)"
(styleUpdate)="updateReaderStyles($event)"
(clickToPaginateChanged)="showPaginationOverlay($event)"
(fullscreen)="toggleFullscreen()"
(bookReaderWritingStyle)="updateWritingStyle($event)"
(layoutModeUpdate)="updateLayoutMode($event)"
(readingDirection)="updateReadingDirection($event)"
(immersiveMode)="updateImmersiveMode($event)"
></app-reader-settings>
</div>
</app-drawer>
</div>
@ -174,7 +141,7 @@
<button class="btn btn-secondary btn-icon me-1" (click)="epubMenuService.openViewAnnotationsDrawer(this.chapterId)">
<i class="fa-solid fa-highlighter" aria-hidden="true"></i>
</button>
<button class="btn btn-secondary btn-icon" (click)="epubMenuService.openTocDrawer()">
<button class="btn btn-secondary btn-icon" (click)="viewToCDrawer()">
<i class="fa-regular fa-rectangle-list" aria-hidden="true"></i>
</button>
</div>

View File

@ -44,36 +44,17 @@ import {ThemeService} from 'src/app/_services/theme.service';
import {ScrollService} from 'src/app/_services/scroll.service';
import {PAGING_DIRECTION} from 'src/app/manga-reader/_models/reader-enums';
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import {TableOfContentsComponent} from '../table-of-contents/table-of-contents.component';
import {
NgbNav,
NgbNavContent,
NgbNavItem,
NgbNavItemRole,
NgbNavLink,
NgbNavOutlet,
NgbProgressbar,
NgbTooltip
} from '@ng-bootstrap/ng-bootstrap';
import {NgbProgressbar, NgbTooltip} from '@ng-bootstrap/ng-bootstrap';
import {DrawerComponent} from '../../../shared/drawer/drawer.component';
import {BookLineOverlayComponent} from "../book-line-overlay/book-line-overlay.component";
import {
PersonalTableOfContentsComponent,
PersonalToCEvent
} from "../personal-table-of-contents/personal-table-of-contents.component";
import {PersonalToCEvent} from "../personal-table-of-contents/personal-table-of-contents.component";
import {translate, TranslocoDirective} from "@jsverse/transloco";
import {ReadingProfile} from "../../../_models/preferences/reading-profiles";
import {ConfirmService} from "../../../shared/confirm.service";
import {EpubHighlightComponent} from "../_annotations/epub-highlight/epub-highlight.component";
import {Annotation} from "../../_models/annotation";
import {EpubReaderMenuService} from "../../../_services/epub-reader-menu.service";
enum TabID {
Settings = 1,
TableOfContents = 2,
PersonalTableOfContents = 3
}
import {LoadPageEvent} from "../_drawers/view-toc-drawer/view-toc-drawer.component";
interface HistoryPoint {
@ -116,9 +97,9 @@ const elementLevelStyles = ['line-height', 'font-family'];
transition('false <=> true', animate('4000ms'))
])
],
imports: [NgTemplateOutlet, DrawerComponent, NgIf, NgbProgressbar, NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLink,
NgbNavContent, ReaderSettingsComponent, TableOfContentsComponent, NgbNavOutlet, NgStyle, NgClass, NgbTooltip,
BookLineOverlayComponent, PersonalTableOfContentsComponent, TranslocoDirective]
imports: [NgTemplateOutlet, DrawerComponent, NgIf, NgbProgressbar,
ReaderSettingsComponent, NgStyle, NgClass, NgbTooltip,
BookLineOverlayComponent, TranslocoDirective]
})
export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
@ -142,7 +123,6 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
protected readonly BookPageLayoutMode = BookPageLayoutMode;
protected readonly WritingStyle = WritingStyle;
protected readonly TabID = TabID;
protected readonly ReadingDirection = ReadingDirection;
protected readonly PAGING_DIRECTION = PAGING_DIRECTION;
@ -194,14 +174,6 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
* The current page only contains an image. This is used to determine if we should show the image in the center of the screen.
*/
isSingleImagePage = false;
/**
* Belongs to the drawer component
*/
activeTabId: TabID = TabID.Settings;
/**
* Sub Nav tab id
*/
tocId: TabID = TabID.TableOfContents;
/**
* Belongs to drawer component
*/
@ -1744,4 +1716,14 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
this.isLineOverlayOpen = isOpen;
this.cdRef.markForCheck();
}
viewToCDrawer() {
this.epubMenuService.openViewTocDrawer(this.chapterId, (res: LoadPageEvent | null) => {
if (res === null) return;
this.setPageNum(res.pageNumber);
this.loadPage(res.part);
});
}
}

View File

@ -819,7 +819,9 @@
"view-toc-drawer": {
"title": "Table of Contents",
"close": "{{common.close}}"
"close": "{{common.close}}",
"toc-header": "Book",
"personal-header": "Personal"
},
"create-annotation-drawer": {