diff --git a/src/app/(app)/html/[id]/layout.tsx b/src/app/(app)/html/[id]/layout.tsx index 45189a7..3cadc04 100644 --- a/src/app/(app)/html/[id]/layout.tsx +++ b/src/app/(app)/html/[id]/layout.tsx @@ -4,14 +4,11 @@ import type { ReactNode } from 'react'; import { ConfigProvider } from '@/contexts/ConfigContext'; import { TTSProvider } from '@/contexts/TTSContext'; -import { HTMLProvider } from '@/contexts/HTMLContext'; export default function HtmlReaderLayout({ children }: { children: ReactNode }) { return ( - - {children} - + {children} ); } diff --git a/src/app/(app)/html/[id]/page.tsx b/src/app/(app)/html/[id]/page.tsx index 7d84452..00d441b 100644 --- a/src/app/(app)/html/[id]/page.tsx +++ b/src/app/(app)/html/[id]/page.tsx @@ -3,7 +3,6 @@ import { useParams, useRouter } from "next/navigation"; import Link from 'next/link'; import { useCallback, useEffect, useRef, useState } from 'react'; -import { useHTML } from '@/contexts/HTMLContext'; import { DocumentSkeleton } from '@/components/documents/DocumentSkeleton'; import { HTMLViewer } from '@/components/views/HTMLViewer'; import { DocumentSettings } from '@/components/documents/DocumentSettings'; @@ -16,11 +15,12 @@ import { DocumentHeaderMenu } from '@/components/documents/DocumentHeaderMenu'; import { SegmentsSidebar } from '@/components/reader/SegmentsSidebar'; import { RateLimitBanner } from '@/components/auth/RateLimitBanner'; import { useAuthRateLimit } from '@/contexts/AuthRateLimitContext'; +import { useHtmlDocument } from './useHtmlDocument'; export default function HTMLPage() { const { id } = useParams(); const router = useRouter(); - const { setCurrentDocument, currDocName, clearCurrDoc } = useHTML(); + const { setCurrentDocument, currDocData, currDocName, clearCurrDoc } = useHtmlDocument(); const { stop } = useTTS(); const { isAtLimit } = useAuthRateLimit(); const [error, setError] = useState(null); @@ -168,7 +168,7 @@ export default function HTMLPage() { ) : (
- +
)} diff --git a/src/app/(app)/html/[id]/useHtmlDocument.ts b/src/app/(app)/html/[id]/useHtmlDocument.ts new file mode 100644 index 0000000..22dec1f --- /dev/null +++ b/src/app/(app)/html/[id]/useHtmlDocument.ts @@ -0,0 +1,70 @@ +'use client'; + +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; + +import { useTTS } from '@/contexts/TTSContext'; +import { getDocumentMetadata } from '@/lib/client/api/documents'; +import { ensureCachedDocument } from '@/lib/client/cache/documents'; + +interface HtmlDocumentState { + currDocData: string | undefined; + currDocName: string | undefined; + currDocText: string | undefined; + setCurrentDocument: (id: string) => Promise; + clearCurrDoc: () => void; +} + +export function useHtmlDocument(): HtmlDocumentState { + const { setText: setTTSText, stop } = useTTS(); + const setTTSTextRef = useRef(setTTSText); + + const [currDocData, setCurrDocData] = useState(); + const [currDocName, setCurrDocName] = useState(); + const [currDocText, setCurrDocText] = useState(); + + useEffect(() => { + setTTSTextRef.current = setTTSText; + }, [setTTSText]); + + const clearCurrDoc = useCallback(() => { + setCurrDocData(undefined); + setCurrDocName(undefined); + setCurrDocText(undefined); + stop(); + }, [stop]); + + const setCurrentDocument = useCallback(async (id: string): Promise => { + try { + const meta = await getDocumentMetadata(id); + if (!meta) { + console.error('Document not found on server'); + return; + } + + const doc = await ensureCachedDocument(meta); + if (doc.type !== 'html') { + console.error('Document is not an HTML/TXT/MD document'); + return; + } + + setCurrDocName(doc.name); + setCurrDocData(doc.data); + setCurrDocText(doc.data); + setTTSTextRef.current(doc.data); + } catch (error) { + console.error('Failed to get HTML document:', error); + clearCurrDoc(); + } + }, [clearCurrDoc]); + + return useMemo( + () => ({ + currDocData, + currDocName, + currDocText, + setCurrentDocument, + clearCurrDoc, + }), + [currDocData, currDocName, currDocText, setCurrentDocument, clearCurrDoc], + ); +} diff --git a/src/components/views/HTMLViewer.tsx b/src/components/views/HTMLViewer.tsx index 959ee79..2657191 100644 --- a/src/components/views/HTMLViewer.tsx +++ b/src/components/views/HTMLViewer.tsx @@ -3,15 +3,15 @@ import { useRef } from 'react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; -import { useHTML } from '@/contexts/HTMLContext'; import { DocumentSkeleton } from '@/components/documents/DocumentSkeleton'; interface HTMLViewerProps { className?: string; + currDocData?: string; + currDocName?: string; } -export function HTMLViewer({ className = '' }: HTMLViewerProps) { - const { currDocData, currDocName } = useHTML(); +export function HTMLViewer({ className = '', currDocData, currDocName }: HTMLViewerProps) { const containerRef = useRef(null); if (!currDocData) { diff --git a/src/contexts/HTMLContext.tsx b/src/contexts/HTMLContext.tsx deleted file mode 100644 index 350d0c7..0000000 --- a/src/contexts/HTMLContext.tsx +++ /dev/null @@ -1,119 +0,0 @@ -'use client'; - -import { - createContext, - useContext, - useState, - ReactNode, - useCallback, - useMemo, - useEffect, - useRef, -} from 'react'; -import { getDocumentMetadata } from '@/lib/client/api/documents'; -import { ensureCachedDocument } from '@/lib/client/cache/documents'; -import { useTTS } from '@/contexts/TTSContext'; - -interface HTMLContextType { - currDocData: string | undefined; - currDocName: string | undefined; - currDocText: string | undefined; - setCurrentDocument: (id: string) => Promise; - clearCurrDoc: () => void; -} - -const HTMLContext = createContext(undefined); - -/** - * Provider component for HTML/Markdown functionality - * Manages the state and operations for HTML document handling - * @param {Object} props - Component props - * @param {ReactNode} props.children - Child components to be wrapped by the provider - */ -export function HTMLProvider({ children }: { children: ReactNode }) { - const { setText: setTTSText, stop } = useTTS(); - const setTTSTextRef = useRef(setTTSText); - - // Current document state - const [currDocData, setCurrDocData] = useState(); - const [currDocName, setCurrDocName] = useState(); - const [currDocText, setCurrDocText] = useState(); - - useEffect(() => { - setTTSTextRef.current = setTTSText; - }, [setTTSText]); - - /** - * Clears all current document state and stops any active TTS - */ - const clearCurrDoc = useCallback(() => { - setCurrDocData(undefined); - setCurrDocName(undefined); - setCurrDocText(undefined); - stop(); - }, [stop]); - - /** - * Sets the current document based on its ID - * @param {string} id - The unique identifier of the document - * @throws {Error} When document data is empty or retrieval fails - */ - const setCurrentDocument = useCallback(async (id: string): Promise => { - try { - const meta = await getDocumentMetadata(id); - if (!meta) { - console.error('Document not found on server'); - return; - } - - const doc = await ensureCachedDocument(meta); - if (doc.type !== 'html') { - console.error('Document is not an HTML/TXT/MD document'); - return; - } - - setCurrDocName(doc.name); - setCurrDocData(doc.data); - setCurrDocText(doc.data); // Use the same text for TTS - setTTSTextRef.current(doc.data); - } catch (error) { - console.error('Failed to get HTML document:', error); - clearCurrDoc(); - } - }, [clearCurrDoc]); - - - - const contextValue = useMemo(() => ({ - currDocData, - currDocName, - currDocText, - setCurrentDocument, - clearCurrDoc, - }), [ - currDocData, - currDocName, - currDocText, - setCurrentDocument, - clearCurrDoc, - ]); - - return ( - - {children} - - ); -} - -/** - * Custom hook to consume the HTML context - * @returns {HTMLContextType} The HTML context value - * @throws {Error} When used outside of HTMLProvider - */ -export function useHTML() { - const context = useContext(HTMLContext); - if (context === undefined) { - throw new Error('useHTML must be used within an HTMLProvider'); - } - return context; -}