import React, { useEffect, useMemo, useState } from 'react'; import { useSearchParams } from 'react-router-dom'; import { IcFileText, IcNavBack } from '../res/icons'; import * as FileSystem from '@/os/FileSystemService'; import { useFileManagerGestures } from '../hooks/useFileManagerGestures'; import { strings } from '../res/strings'; import { stringsEn } from '../res/strings.en'; import { useAppStrings } from '@/os/useAppStrings'; type LoadState = | { status: 'loading'; text: '' } | { status: 'ready'; text: string } | { status: 'error'; text: '' }; function decodePdfString(value: string): string { return value .replace(/\\n/g, '\n') .replace(/\\r/g, '\n') .replace(/\\t/g, '\t') .replace(/\\\(/g, '(') .replace(/\\\)/g, ')') .replace(/\\\\/g, '\\'); } function extractSimplePdfText(raw: string): string { const lines: string[] = []; const textObjectPattern = /BT([\s\S]*?)ET/g; let objectMatch: RegExpExecArray | null; while ((objectMatch = textObjectPattern.exec(raw)) !== null) { const body = objectMatch[1]; const stringPattern = /\((?:\\.|[^\\)])*\)\s*Tj/g; let stringMatch: RegExpExecArray | null; while ((stringMatch = stringPattern.exec(body)) !== null) { const token = stringMatch[0]; const encoded = token.slice(1, token.lastIndexOf(')')); lines.push(decodePdfString(encoded)); } } return lines.join('\n').trim(); } export const PdfPreviewPage: React.FC = () => { const [searchParams] = useSearchParams(); const { bindBack } = useFileManagerGestures(); const s = useAppStrings(strings, stringsEn); const path = searchParams.get('path') || ''; const [loadState, setLoadState] = useState({ status: 'loading', text: '' }); const fileName = useMemo(() => { if (!path) return s.pdf_preview_no_file; const parts = path.split('/').filter(Boolean); return parts[parts.length - 1] || s.pdf_preview_no_file; }, [path, s.pdf_preview_no_file]); const file = useMemo(() => (path ? FileSystem.getNode(path) : null), [path]); useEffect(() => { let cancelled = false; async function loadPdf() { if (!path || !file || file.type !== 'file') { setLoadState({ status: 'error', text: '' }); return; } setLoadState({ status: 'loading', text: '' }); const blob = await FileSystem.readFile(path); if (cancelled) return; if (!blob) { setLoadState({ status: 'error', text: '' }); return; } const raw = await blob.text(); if (cancelled) return; const text = extractSimplePdfText(raw); if (!text) { setLoadState({ status: 'error', text: '' }); return; } setLoadState({ status: 'ready', text }); } loadPdf(); return () => { cancelled = true; }; }, [file, path]); return (

{fileName}

{file && (
{FileSystem.formatFileSize(file.size)}
)}
{loadState.status === 'loading' && (
{s.pdf_preview_loading}
)} {loadState.status === 'error' && (
{s.pdf_preview_failed}
)} {loadState.status === 'ready' && (
{loadState.text}
)}
); }; export default PdfPreviewPage;