add support for pdf previews with pdf.js

This commit is contained in:
spencerwooo
2021-06-25 00:42:46 +01:00
parent b1cced5e7e
commit a2f853259f
10 changed files with 1323 additions and 65 deletions
+3 -3
View File
@@ -8,12 +8,12 @@ const Breadcrumb: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) =>
const { path } = query
if (Array.isArray(path)) {
return (
<div className="pb-4 text-sm text-gray-600 flex flex-wrap">
<div className="p-1 hover:text-black transition-all duration-75">
<div className="pb-4 text-sm text-gray-600 flex overflow-x-scroll">
<div className="p-1 hover:text-black transition-all duration-75 flex-shrink-0">
<Link href="/">🚩 Home</Link>
</div>
{path.map((q: string, i: number) => (
<div key={i} className="flex items-center">
<div key={i} className="flex items-center flex-shrink-0">
<div>/</div>
<div className="p-1 hover:text-black transition-all duration-75">
<Link href={`/${path.slice(0, i + 1).join('/')}`}>{q}</Link>
+12 -22
View File
@@ -2,20 +2,23 @@ import axios from 'axios'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { ParsedUrlQuery } from 'querystring'
import { FunctionComponent, useEffect, useState } from 'react'
import { FunctionComponent, useState } from 'react'
import { ImageDecorator } from 'react-viewer/lib/ViewerProps'
import useSWR from 'swr'
import { useRouter } from 'next/router'
import dynamic from 'next/dynamic'
import Loading from './Loading'
import { getExtension, getFileIcon, hasKey } from '../utils/getFileIcon'
import { extensions, preview } from '../utils/getPreviewType'
import { VideoPreview } from './previews/VideoPreview'
import { AudioPreview } from './previews/AudioPreview'
// View images as gallery
// Disabling SSR for some previews (image gallery view, and PDF view)
const ReactViewer = dynamic(() => import('react-viewer'), { ssr: false })
const PDFPreview = dynamic(() => import('./previews/PDFPreview'), { ssr: false })
/**
* Convert raw bits file/folder size into a human readable string
@@ -62,13 +65,13 @@ const FileListItem: FunctionComponent<{
</div>
<div className="truncate">{c.name}</div>
</div>
<div className="hidden md:visible font-mono text-sm col-span-2 text-gray-700">
<div className="invisible md:visible font-mono text-sm col-span-2 text-gray-700">
{new Date(c.lastModifiedDateTime).toLocaleString(undefined, {
dateStyle: 'short',
timeStyle: 'short',
})}
</div>
<div className="hidden md:visible font-mono text-sm text-gray-700">{humanFileSize(c.size)}</div>
<div className="invisible md:visible font-mono text-sm text-gray-700">{humanFileSize(c.size)}</div>
</div>
)
}
@@ -89,21 +92,8 @@ const FileListing: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) =
if (error) return <div>Failed to load</div>
if (!data) {
return (
<div className="flex items-center justify-center bg-white shadow rounded py-32 space-x-1">
<svg
className="animate-spin -ml-1 mr-3 h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
<div>Loading</div>
<div className="shadow bg-white rounded p-3">
<Loading loadingText="Loading ..." />
</div>
)
}
@@ -140,8 +130,8 @@ const FileListing: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) =
<div className="bg-white shadow rounded">
<div className="p-3 grid grid-cols-10 items-center space-x-2 border-b border-gray-200">
<div className="col-span-10 md:col-span-7 font-bold">Name</div>
<div className="hidden md:visible font-bold col-span-2">Last Modified</div>
<div className="hidden md:visible font-bold">Size</div>
<div className="invisible md:visible font-bold col-span-2">Last Modified</div>
<div className="invisible md:visible font-bold">Size</div>
</div>
{imagesInFolder.length !== 0 && (
@@ -211,7 +201,7 @@ const FileListing: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) =
return <AudioPreview file={resp} />
case preview.pdf:
return <div>pdf</div>
return <PDFPreview file={resp} />
default:
return <div className="bg-white shadow rounded">{fileName}</div>
+24
View File
@@ -0,0 +1,24 @@
import { FunctionComponent } from 'react'
const Loading: FunctionComponent<{ loadingText: string }> = ({ loadingText }) => {
return (
<div className="flex items-center justify-center rounded py-32 space-x-1">
<svg
className="animate-spin -ml-1 mr-3 h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
<div>{loadingText}</div>
</div>
)
}
export default Loading
+1 -1
View File
@@ -4,7 +4,7 @@ import siteConfig from '../config/site.json'
const Navbar = () => {
return (
<div className="text-left bg-white p-3 sticky top-0 bg-opacity-80 backdrop-blur-md shadow-sm">
<div className="text-left bg-white p-3 sticky top-0 bg-opacity-80 backdrop-blur-md shadow-sm z-20">
<div className="max-w-4xl w-full mx-auto flex items-center justify-between">
<h1 className="font-bold text-xl">{siteConfig.title}</h1>
<a href="https://github.com/spencerwooo/onedrive-vercel-index" target="_blank" rel="noopener noreferrer">
+4 -4
View File
@@ -5,13 +5,13 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
export const AudioPreview: FunctionComponent<{ file: any }> = ({ file }) => {
return (
<div className="bg-white rounded shadow p-3 w-full">
<div className="flex space-x-4">
<div className="flex items-center justify-center p-10 bg-gray-100 rounded">
<FontAwesomeIcon className="" icon="music" size="lg" />
<div className="flex flex-col space-y-4 md:flex-row md:space-x-4">
<div className="flex items-center justify-center bg-gray-100 rounded p-28 md:p-14">
<FontAwesomeIcon icon="music" size="lg" />
</div>
<div className="flex flex-col w-full space-y-2">
<div>{file.name}</div>
<div className="text-gray-500 text-sm">
<div className="text-gray-500 text-sm pb-4">
Last modified:{' '}
{new Date(file.lastModifiedDateTime).toLocaleString(undefined, {
dateStyle: 'short',
+70
View File
@@ -0,0 +1,70 @@
import { FunctionComponent, useState } from 'react'
import { Document, Page, pdfjs } from 'react-pdf'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import Loading from '../Loading'
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.min.js`
const PDFPreview: FunctionComponent<{ file: any }> = ({ file }) => {
const [pageNumber, setPageNumber] = useState(1)
const [totalPages, setTotalPages] = useState(0)
const [loadingText, setLoadingText] = useState('Loading PDF ...')
const onDocumentLoadSuccess = (pdf: any) => {
setTotalPages(pdf.numPages)
}
return (
<div className="bg-white rounded shadow md:p-3 w-full overflow-scroll" style={{ maxHeight: '90vh' }}>
<div className="w-full mx-auto border-2 md:shadow overflow-scroll" style={{ maxHeight: '60vh' }}>
<Document
file={file['@microsoft.graph.downloadUrl']}
onLoadSuccess={onDocumentLoadSuccess}
loading={<Loading loadingText={loadingText} />}
onLoadProgress={({ loaded, total }) => {
setLoadingText(`Loading PDF ${Math.round((loaded / total) * 100)}%`)
}}
>
<Page pageNumber={pageNumber} />
</Document>
</div>
<div className="flex space-x-2 my-4 md:mb-0 w-full items-center justify-center">
<button
className="px-3 py-1 bg-red-500 text-white rounded cursor-pointer focus:ring-2 focus:ring-red-500 focus:outline-none hover:bg-red-600 transition-all duration-75 disabled:opacity-50"
onClick={() => {
pageNumber > 1 && setPageNumber(pageNumber - 1)
}}
disabled={!(pageNumber > 1)}
>
<FontAwesomeIcon icon="arrow-left" />
</button>
<div className="px-3 py-1">
Page{' '}
<input
value={pageNumber}
className="w-10 mr-1 text-center p-1 bg-red-50 rounded focus:ring-2 focus:ring-red-500 focus:outline-none"
onChange={e => {
const v = parseInt(e.target.value)
if (v <= totalPages && v >= 0) {
setPageNumber(v)
}
}}
></input>
/<span className="ml-1 text-center">{totalPages}</span>
</div>
<button
className="px-3 py-1 bg-red-500 text-white rounded cursor-pointer focus:ring-2 focus:ring-red-500 focus:outline-none hover:bg-red-600 transition-all duration-75 disabled:opacity-50"
onClick={() => {
pageNumber < totalPages && setPageNumber(pageNumber + 1)
}}
disabled={!(pageNumber < totalPages)}
>
<FontAwesomeIcon icon="arrow-right" />
</button>
</div>
</div>
)
}
export default PDFPreview
+18
View File
@@ -1,4 +1,22 @@
module.exports = {
webpack: (config) => {
// load worker files as a urls with `file-loader`
config.module.rules.unshift({
test: /pdf\.worker\.(min\.)?js/,
use: [
{
loader: "file-loader",
options: {
name: "[contenthash].[ext]",
publicPath: "_next/static/worker",
outputPath: "static/worker"
}
}
]
});
return config;
},
reactStrictMode: true,
images: {
domains: ['public.dm.files.1drv.com'],
+1185 -33
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -18,12 +18,14 @@
"next": "11.0.0",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-pdf": "^5.3.0",
"react-player": "^2.9.0",
"react-viewer": "^3.2.2",
"swr": "^0.5.6"
},
"devDependencies": {
"@types/react": "17.0.11",
"@types/react-pdf": "^5.0.4",
"autoprefixer": "^10.2.6",
"eslint": "7.29.0",
"eslint-config-next": "11.0.0",
+4 -2
View File
@@ -14,7 +14,7 @@ import {
faFile,
faFolder,
} from '@fortawesome/free-regular-svg-icons'
import { faMusic } from '@fortawesome/free-solid-svg-icons'
import { faMusic, faArrowLeft, faArrowRight } from '@fortawesome/free-solid-svg-icons'
import { faGithub, faMarkdown } from '@fortawesome/free-brands-svg-icons'
import type { AppProps } from 'next/app'
@@ -34,7 +34,9 @@ library.add(
faFolder,
faGithub,
faMarkdown,
faMusic
faMusic,
faArrowLeft,
faArrowRight
)
function MyApp({ Component, pageProps }: AppProps) {