omnivore/packages/web/components/templates/article/pdf.js/PdfArticleContainer.tsx
Tom Rogers 4e582fb55d
Some checks failed
Build Docker Images / Build docker images (push) Has been cancelled
Build Self-Hosting Docker Images / Build self-host docker images (push) Has been cancelled
Run tests / Run Codebase tests (push) Has been cancelled
Run tests / Build docker images (push) Has been cancelled
Improving Self-Hosting and Removing 3rd Party dependencies. (#4513)
* fix: Library Header layout shift

* Bump Github Actions versions.

* Self-Hosting Changes

* Fix Minio Environment Variable

* Just make pdfs successful, due to lack of PDFHandler

* Fix issue where flag was set wrong

* Added an NGINX Example file

* Add some documentation for self-hosting via Docker Compose

* Make some adjustments to Puppeteer due to failing sites.

* adjust timings

* Add start of Mail Service

* Fix Docker Files

* More email service stuff

* Add Guide to use Zapier for Email-Importing.

* Ensure that if no env is provided it uses the old email settings

* Add some instructions for self-hosted email

* Add SNS Endpoints for Mail Watcher

* Add steps and functionality for using SES and SNS for email

* Uncomment a few jobs.

* Added option for Firefox for parser. Was having issues with Chromium on Docker.

* Add missing space.

Co-authored-by: Russ Taylor <729694+russtaylor@users.noreply.github.com>

* Fix some wording on the Guide

* update browser extension to handle self-hosted instances

* add slight documentation to options page

* Fix MV

* Do raw handlers for Medium

* Fix images in Medium

* Update self-hosting/GUIDE.md

Co-authored-by: Mike Baker <1426795+mbaker3@users.noreply.github.com>

* Update Guide with other variables

* Add The Verge to JS-less handlers

* Update regex and image-proxy

* Update self-hosting/nginx/nginx.conf

Co-authored-by: Mike Baker <1426795+mbaker3@users.noreply.github.com>

* Update regex and image-proxy

* Update self-hosting/docker-compose/docker-compose.yml

Co-authored-by: Mike Baker <1426795+mbaker3@users.noreply.github.com>

* Fix Minio for Export

* Merge to main

* Update GUIDE with newer NGINX

* Update nginx config to include api/save route

* Enable Native PDF View for PDFS

* Enable Native PDF View for PDFS

* feat:lover packages test

* feat:working build

* feat:alpine build

* docs:api dockerfile docs

* Write a PDF.js wrapper to replace pspdfkit

* Revert changes for replication, set settings to have default mode

* build folder got removed due to gitignore on pdf

* Add Box shadow to pdf pages

* Add Toggle for Progress in PDFS, enabled native viewer toggle

* Update node version to LTS

* Update node version to LTS

* Fix Linting issues

* Fix Linting issues

* Make env variable nullable

* Add touchend listener for mobile

* Make changes to PDF for mobile

* fix(android): change serverUrl to selfhosted first

* feat:2 stage alpine content fetch

* feat:separated start script

* fix:changed to node 22

* Add back youtube functionality and add guide

* trigger build

* Fix cache issue on YouTube

* Allow empty AWS_S3_ENDPOINT

* Allow empty AWS_S3_ENDPOINT

* Add GCHR for all images

* Add GCHR For self hosting.

* Add GCHR For self hosting.

* Test prebuilt.

* Test prebuilt

* Test prebuilt...

* Fix web image

* Remove Web Image (For now)

* Move docker-compose to images

* Move docker-compose files to correct locations

* Remove the need for ARGS

* Update packages, and Typescript versions

* Fix

* Fix issues with build on Web

* Correct push

* Fix Linting issues

* Fix Trace import

* Add missing types

* Fix Tasks

* Add information into guide about self-build

* Fix issues with PDF Viewer

---------

Co-authored-by: keumky2 <keumky2@woowahan.com>
Co-authored-by: William Theaker <wtheaker@nvidia.com>
Co-authored-by: Russ Taylor <729694+russtaylor@users.noreply.github.com>
Co-authored-by: David Adams <david@dadams2.com>
Co-authored-by: Mike Baker <1426795+mbaker3@users.noreply.github.com>
Co-authored-by: m1xxos <66390094+m1xxos@users.noreply.github.com>
Co-authored-by: Adil <mr.adil777@gmail.com>
2025-01-27 13:33:16 +01:00

290 lines
9.5 KiB
TypeScript

import {
ArticleAttributes,
ArticleReadingProgressMutationInput,
useUpdateItemReadStatus,
} from '../../../../lib/networking/library_items/useLibraryItems'
import { HStack, VStack } from '../../../elements/LayoutPrimitives'
import React, { useEffect, useRef, useState } from 'react'
import { UserBasicData } from '../../../../lib/networking/queries/useGetViewerQuery'
import 'react-sliding-pane/dist/react-sliding-pane.css'
import {
CreateHighlightInput,
useCreateHighlight,
useDeleteHighlight,
useMergeHighlight,
useUpdateHighlight,
} from '../../../../lib/networking/highlights/useItemHighlights'
import 'pdfjs-dist/web/pdf_viewer.css'
import { EventBus, PDFViewer } from 'pdfjs-dist/types/web/pdf_viewer'
import PdfViewer from './PdfViewer'
import PdfToolbar from './PdfToolbar'
import PdfSearchBar from './PdfSearchBar'
import { NotebookHeader } from '../NotebookHeader'
import { NotebookContent } from '../Notebook'
import { ResizableSidebar } from '../ResizableSidebar'
import { PDFDocumentProxy } from 'pdfjs-dist'
import { PDFLinkService } from 'pdfjs-dist/types/web/pdf_link_service'
import PdfSideBar from './PdfSideBar'
export type PdfArticleContainerProps = {
viewer: UserBasicData
article: ArticleAttributes
showHighlightsModal: boolean
setShowHighlightsModal: React.Dispatch<React.SetStateAction<boolean>>
}
export default function PdfArticleContainer(props: PdfArticleContainerProps) {
// @ts-ignore
const pdfJS = import('pdfjs-dist/build/pdf.min.mjs')
const containerRef = useRef<HTMLDivElement | null>(null)
const [pdfViewer, setPdfViewer] = useState<PDFViewer | null>(null)
const [eventBus, setEventBus] = useState<EventBus | null>(null)
const [pageNumber, setPageNumber] = useState<number>(1)
const [pageCount, setTotalPageCount] = useState<number>(0)
const [showSearch, setShowSearch] = useState(false)
const [showToolbar, setShowToolbar] = useState(true)
const [saveLatestPage, setSaveLatestPage] = useState(true);
const [sidebarActive, setSidebarActive] = useState<boolean>(false)
const createHighlight = useCreateHighlight()
const deleteHighlight = useDeleteHighlight()
const mergeHighlight = useMergeHighlight()
const updateHighlight = useUpdateHighlight()
const updateItemReadStatus = useUpdateItemReadStatus()
const createPdfViewer = async (): Promise<PDFViewer> => {
const pdfJSLib = await pdfJS
const pdfjsViewer = await import('pdfjs-dist/web/pdf_viewer.mjs')
pdfJSLib.GlobalWorkerOptions.workerSrc =
window.location.origin + '/pdfjs-dist/pdf.worker.min.mjs'
const eventBus = new pdfjsViewer.EventBus()
const pdfLinkService = new pdfjsViewer.PDFLinkService({
eventBus,
})
const pdfFindController = new pdfjsViewer.PDFFindController({
eventBus,
linkService: pdfLinkService,
})
const pdfScriptingManager = new pdfjsViewer.PDFScriptingManager({
eventBus,
sandboxBundleSrc: window.location.origin + '/pdfjs-dist/pdf.sandbox.mjs',
})
const pdfViewer = new pdfjsViewer.PDFViewer({
container: containerRef.current!,
eventBus,
linkService: pdfLinkService,
findController: pdfFindController,
scriptingManager: pdfScriptingManager,
})
pdfScriptingManager.setViewer(pdfViewer)
pdfLinkService.pdfViewer = pdfViewer
return pdfViewer
}
const loadPdfDocument = async (): Promise<PDFDocumentProxy> => {
const pdfJsLib = await pdfJS
const loadingTask = pdfJsLib.getDocument({
url: props.article.url,
cMapUrl: window.location.origin + '/pdfjs-dist/cmaps/',
cMapPacked: true,
enableXfa: true,
})
return loadingTask.promise
}
useEffect(() => {
// Uses the existing mechanism to hide the reader toolbar from pspdfkit
const updateReaderSettings = () => {
const show = localStorage.getItem('reader-show-pdf-tool-bar')
const showBar = show ? JSON.parse(show) == true : true
setShowToolbar(showBar)
const latestPage = localStorage.getItem('reader-remember-latest-page')
const latestPageSave = latestPage ? JSON.parse(latestPage) == true : false
setSaveLatestPage(latestPageSave)
}
document.addEventListener('pdfReaderUpdateSettings', updateReaderSettings)
updateReaderSettings();
;(async () => {
const pdfViewer = await createPdfViewer()
const pdfDocument = await loadPdfDocument()
setTotalPageCount(pdfDocument.numPages)
pdfViewer.setDocument(pdfDocument)
const linkService = pdfViewer.linkService as PDFLinkService
linkService.setDocument(pdfDocument, null)
// Doesn't seem to get applied straight away, causing an issue where the scale would
// be set to 0. We do a 200 ms timeout to avoid this bug.
setTimeout(() => {
pdfViewer.currentScale = 1
pdfViewer.scrollPageIntoView({
pageNumber: props.article.readingProgressAnchorIndex,
})
}, 200)
setPdfViewer(pdfViewer)
setEventBus(pdfViewer.eventBus)
pdfViewer.eventBus.on(
'pagechanging',
(e: { previous: number; pageNumber: number }) => {
console.log('Page Changing....')
setPageNumber(e.pageNumber)
}
)
})()
}, [])
return (
<VStack css={{ width: '100%' }}>
{showSearch && <PdfSearchBar pdfViewer={pdfViewer} eventBus={eventBus} />}
{showToolbar && (
<PdfToolbar
setShowSidebar={setSidebarActive}
sidebarActive={sidebarActive}
viewer={props.viewer}
article={props.article}
pdfViewer={pdfViewer}
eventBus={eventBus}
pageNumber={pageNumber}
setPageNumber={setPageNumber}
totalPageNumbers={pageCount}
showSearch={showSearch}
setShowSearch={setShowSearch}
/>
)}
<HStack>
<PdfSideBar
setPage={(page: number) => {
if (pdfViewer) {
pdfViewer.currentPageNumber = page
}
}}
pdfDocument={pdfViewer?.pdfDocument}
activePage={pageNumber}
sidebarActive={sidebarActive}
totalPages={pageCount}
/>
<PdfViewer
viewer={props.viewer}
article={props.article}
containerRef={containerRef}
eventBus={eventBus}
sidebarActive={sidebarActive}
saveLatestPage={saveLatestPage}
pdfViewer={pdfViewer}
articleMutations={{
createHighlightMutation: async (input: CreateHighlightInput) => {
try {
return await createHighlight.mutateAsync({
itemId: props.article.id,
slug: props.article.slug,
input,
})
} catch (err) {
console.log('error creating highlight', err)
return undefined
}
},
deleteHighlightMutation: async (
_libraryItemId: string,
highlightId: string
) => {
try {
await deleteHighlight.mutateAsync({
itemId: props.article.id,
slug: props.article.slug,
highlightId,
})
return true
} catch (err) {
console.log('error deleting highlight', err)
return false
}
},
mergeHighlightMutation: async (input) => {
try {
const result = await mergeHighlight.mutateAsync({
itemId: props.article.id,
slug: props.article.slug,
input,
})
return result?.highlight
} catch (err) {
console.log('error merging highlight', err)
return undefined
}
},
updateHighlightMutation: async (input) => {
try {
const result = await updateHighlight.mutateAsync({
itemId: props.article.id,
slug: props.article.slug,
input,
})
return result?.id
} catch (err) {
console.log('error updating highlight', err)
return undefined
}
},
articleReadingProgressMutation: async (
input: ArticleReadingProgressMutationInput
) => {
try {
await updateItemReadStatus.mutateAsync({
itemId: props.article.id,
slug: props.article.slug,
input,
})
} catch {
return false
}
return true
},
}}
/>
</HStack>
<ResizableSidebar
isShow={props.showHighlightsModal}
onClose={() => {
props.setShowHighlightsModal(false)
}}
>
<NotebookHeader
viewer={props.viewer}
item={props.article}
setShowNotebook={props.setShowHighlightsModal}
/>
<NotebookContent
viewer={props.viewer}
item={props.article}
viewInReader={(highlightId) => {
const highlight = props.article.highlights?.filter(
(it) => it.id == highlightId
)
if (highlight && highlight.length == 1) {
const pageId = highlight[0].highlightPositionAnchorIndex
if (pdfViewer) {
pdfViewer.currentPageNumber = pageId ?? 1
}
}
}}
/>
</ResizableSidebar>
</VStack>
)
}