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:
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,23 +4,32 @@
|
||||
|
||||
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 }) => {
|
||||
state = initializeNavigator(detail.containerSelector);
|
||||
state.totalPages = detail.totalPages;
|
||||
});
|
||||
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 }) => {
|
||||
if (state) {
|
||||
state.currentPage = detail.currentPage;
|
||||
if (detail.contentImage) {
|
||||
state.contentImage = detail.contentImage;
|
||||
updateNavigatorViewport(state);
|
||||
context.events.on(
|
||||
"navigator:update",
|
||||
(detail: { currentPage: number; contentImage?: HTMLImageElement }) => {
|
||||
if (state) {
|
||||
state.currentPage = detail.currentPage;
|
||||
if (detail.contentImage) {
|
||||
state.contentImage = detail.contentImage;
|
||||
updateNavigatorViewport(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
context.events.on("navigator:pan", (detail: { x: number; y: number }) => {
|
||||
if (state) {
|
||||
@@ -124,10 +133,10 @@ function handleNavigatorPan(state: NavigatorState, x: number, y: number): void {
|
||||
if (!state.contentImage) return;
|
||||
|
||||
const imgRect = state.contentImage.getBoundingClientRect();
|
||||
|
||||
|
||||
const viewportX = x * state.container.offsetWidth;
|
||||
const viewportY = y * state.container.offsetHeight;
|
||||
|
||||
state.viewport.style.left = `${viewportX}px`;
|
||||
state.viewport.style.top = `${viewportY}px`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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", () => {
|
||||
@@ -46,4 +49,4 @@ export function checkOnlineStatus(): boolean {
|
||||
|
||||
function syncPendingChanges(context: ReaderContext): void {
|
||||
context.events.emit("offline:sync", {});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" }) => {
|
||||
movePanelToSide(context, state, detail.panelId, detail.side);
|
||||
});
|
||||
context.events.on(
|
||||
"panel:move",
|
||||
(detail: { panelId: string; side: "left" | "right" }) => {
|
||||
movePanelToSide(context, state, detail.panelId, detail.side);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
interface PanelDockState {
|
||||
@@ -88,39 +94,54 @@ 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}`;
|
||||
panel.dataset.panelId = panelId;
|
||||
panel.style.width = `${panelState.width_px}px`;
|
||||
|
||||
|
||||
if (!panelState.visible) {
|
||||
panel.classList.add("panel-hidden");
|
||||
}
|
||||
|
||||
|
||||
if (panelState.collapsed) {
|
||||
panel.classList.add("panel-collapsed");
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
@@ -187,4 +221,4 @@ function movePanelToSide(context: ReaderContext, state: PanelDockState, panelId:
|
||||
}
|
||||
|
||||
context.events.emit("panel:moved", { panelId, side });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 }) => {
|
||||
@@ -122,4 +125,4 @@ async function syncReadingSpeed(
|
||||
} catch (error) {
|
||||
console.error("Failed to sync reading speed:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user