reader: convert feature modules to Feature Registration Pattern

- Add init(context) exported function to each feature module
- Store module-level context reference for event handlers
- Fix gestures.ts to use library_type_name instead of library_type
- Features: gestures, keyboard-shortcuts, navigator-panel, offline-manager,
  panel-dock-system, progress-indicator, reading-speed-tracker
This commit is contained in:
2026-04-04 18:17:16 -04:00
parent 4abc814c1b
commit 2b22d7b255
7 changed files with 96 additions and 40 deletions
+2 -2
View File
@@ -101,7 +101,7 @@ function setupGestures() {
function handleSwipeLeft() {
const metadata = context.getState().readerMetadata;
if (metadata?.library_type === "manga") {
if (metadata?.library_type_name === "manga") {
navigateWithPanels("next");
} else {
context.navigation.previousPage();
@@ -110,7 +110,7 @@ function handleSwipeLeft() {
function handleSwipeRight() {
const metadata = context.getState().readerMetadata;
if (metadata?.library_type === "manga") {
if (metadata?.library_type_name === "manga") {
navigateWithPanels("previous");
} else {
context.navigation.nextPage();
@@ -1,6 +1,9 @@
import type { ReaderContext } from "../core/reader-context";
export async function init(context: ReaderContext): Promise<void> {
let context: ReaderContext;
export async function init(readerContext: ReaderContext): Promise<void> {
context = readerContext;
setupKeyboardShortcuts();
}
+14 -5
View File
@@ -4,15 +4,23 @@
import type { ReaderContext } from "../core/reader-context";
export function init(context: ReaderContext): void {
let context: ReaderContext;
export function init(readerContext: ReaderContext): void {
context = readerContext;
let state: NavigatorState | null = null;
context.events.on("navigator:initialize", (detail: { containerSelector: string; totalPages: number }) => {
context.events.on(
"navigator:initialize",
(detail: { containerSelector: string; totalPages: number }) => {
state = initializeNavigator(detail.containerSelector);
state.totalPages = detail.totalPages;
});
},
);
context.events.on("navigator:update", (detail: { currentPage: number; contentImage?: HTMLImageElement }) => {
context.events.on(
"navigator:update",
(detail: { currentPage: number; contentImage?: HTMLImageElement }) => {
if (state) {
state.currentPage = detail.currentPage;
if (detail.contentImage) {
@@ -20,7 +28,8 @@ export function init(context: ReaderContext): void {
updateNavigatorViewport(state);
}
}
});
},
);
context.events.on("navigator:pan", (detail: { x: number; y: number }) => {
if (state) {
+5 -2
View File
@@ -1,9 +1,12 @@
// Offline manager for PWA functionality
// Feature Registration Pattern implementation
import type { ReaderContext } from "./core/reader-context";
import type { ReaderContext } from "../core/reader-context";
export function init(context: ReaderContext): void {
let context: ReaderContext;
export function init(readerContext: ReaderContext): void {
context = readerContext;
registerServiceWorker();
window.addEventListener("online", () => {
+44 -10
View File
@@ -3,7 +3,10 @@
import type { ReaderContext } from "../core/reader-context";
export function init(context: ReaderContext): void {
let context: ReaderContext;
export function init(readerContext: ReaderContext): void {
context = readerContext;
const dockZones: DockZone[] = [
{ side: "left", x: 0, width: 400, height: window.innerHeight },
{
@@ -45,9 +48,12 @@ export function init(context: ReaderContext): void {
}
});
context.events.on("panel:move", (detail: { panelId: string; side: "left" | "right" }) => {
context.events.on(
"panel:move",
(detail: { panelId: string; side: "left" | "right" }) => {
movePanelToSide(context, state, detail.panelId, detail.side);
});
},
);
}
interface PanelDockState {
@@ -88,7 +94,11 @@ async function loadSettings(): Promise<any> {
return await response.json();
}
function createPanel(context: ReaderContext, panelId: string, panelState: PanelState): void {
function createPanel(
context: ReaderContext,
panelId: string,
panelState: PanelState,
): void {
const panel = document.createElement("div");
panel.id = `panel-${panelId}`;
panel.className = `reader-panel panel-${panelState.side}`;
@@ -106,21 +116,32 @@ function createPanel(context: ReaderContext, panelId: string, panelState: PanelS
context.elements.readerContent.appendChild(panel);
}
function updatePanelVisibility(context: ReaderContext, panelId: string, visible: boolean): void {
function updatePanelVisibility(
context: ReaderContext,
panelId: string,
visible: boolean,
): void {
const panel = document.getElementById(`panel-${panelId}`);
if (panel) {
panel.classList.toggle("panel-hidden", !visible);
}
}
function updatePanelCollapsed(context: ReaderContext, panelId: string, collapsed: boolean): void {
function updatePanelCollapsed(
context: ReaderContext,
panelId: string,
collapsed: boolean,
): void {
const panel = document.getElementById(`panel-${panelId}`);
if (panel) {
panel.classList.toggle("panel-collapsed", collapsed);
}
}
function setupGlobalDragHandlers(context: ReaderContext, state: PanelDockState): void {
function setupGlobalDragHandlers(
context: ReaderContext,
state: PanelDockState,
): void {
document.addEventListener("mousedown", (e) => {
const panelHeader = (e.target as HTMLElement).closest(".panel-header");
if (panelHeader) {
@@ -149,9 +170,17 @@ function setupGlobalDragHandlers(context: ReaderContext, state: PanelDockState):
document.addEventListener("mouseup", () => {
if (state.dragState) {
const nearestZone = findNearestDockZone(state.dragState.currentX, state.dockZones);
const nearestZone = findNearestDockZone(
state.dragState.currentX,
state.dockZones,
);
if (nearestZone) {
movePanelToSide(context, state, state.dragState.panelId, nearestZone.side);
movePanelToSide(
context,
state,
state.dragState.panelId,
nearestZone.side,
);
}
state.dragState = null;
}
@@ -173,7 +202,12 @@ function findNearestDockZone(x: number, zones: DockZone[]): DockZone | null {
return nearest;
}
function movePanelToSide(context: ReaderContext, state: PanelDockState, panelId: string, side: "left" | "right"): void {
function movePanelToSide(
context: ReaderContext,
state: PanelDockState,
panelId: string,
side: "left" | "right",
): void {
const panelState = state.panels.get(panelId);
if (!panelState) return;
@@ -3,7 +3,11 @@ interface ProgressDisplay {
mode: "pages" | "chapter" | "percentage" | "time-left";
text: string;
}
export async function init(context: ReaderContext): Promise<void> {
let context: ReaderContext;
export async function init(readerContext: ReaderContext): Promise<void> {
context = readerContext;
context.events.on("pageChanged", () => {
updateProgressDisplay(context);
});
@@ -1,9 +1,12 @@
// Track reading speed and update database
// Feature Registration Pattern implementation
import type { ReaderContext } from "./core/reader-context";
import type { ReaderContext } from "../core/reader-context";
export function init(context: ReaderContext): void {
let context: ReaderContext;
export function init(readerContext: ReaderContext): void {
context = readerContext;
let state: ReadingSpeedTrackerState | null = null;
context.events.on("reader:loaded", (detail: { mediaItemId: string }) => {