mirror of
https://github.com/Nezumi-2711/onedrive-vercel-index.git
synced 2026-09-22 13:38:45 +00:00
add support for pdf previews with pdf.js
This commit is contained in:
@@ -8,12 +8,12 @@ const Breadcrumb: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) =>
|
|||||||
const { path } = query
|
const { path } = query
|
||||||
if (Array.isArray(path)) {
|
if (Array.isArray(path)) {
|
||||||
return (
|
return (
|
||||||
<div className="pb-4 text-sm text-gray-600 flex flex-wrap">
|
<div className="pb-4 text-sm text-gray-600 flex overflow-x-scroll">
|
||||||
<div className="p-1 hover:text-black transition-all duration-75">
|
<div className="p-1 hover:text-black transition-all duration-75 flex-shrink-0">
|
||||||
<Link href="/">🚩 Home</Link>
|
<Link href="/">🚩 Home</Link>
|
||||||
</div>
|
</div>
|
||||||
{path.map((q: string, i: number) => (
|
{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>/</div>
|
||||||
<div className="p-1 hover:text-black transition-all duration-75">
|
<div className="p-1 hover:text-black transition-all duration-75">
|
||||||
<Link href={`/${path.slice(0, i + 1).join('/')}`}>{q}</Link>
|
<Link href={`/${path.slice(0, i + 1).join('/')}`}>{q}</Link>
|
||||||
|
|||||||
+12
-22
@@ -2,20 +2,23 @@ import axios from 'axios'
|
|||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
|
|
||||||
import { ParsedUrlQuery } from 'querystring'
|
import { ParsedUrlQuery } from 'querystring'
|
||||||
import { FunctionComponent, useEffect, useState } from 'react'
|
import { FunctionComponent, useState } from 'react'
|
||||||
import { ImageDecorator } from 'react-viewer/lib/ViewerProps'
|
import { ImageDecorator } from 'react-viewer/lib/ViewerProps'
|
||||||
|
|
||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import dynamic from 'next/dynamic'
|
import dynamic from 'next/dynamic'
|
||||||
|
|
||||||
|
import Loading from './Loading'
|
||||||
|
|
||||||
import { getExtension, getFileIcon, hasKey } from '../utils/getFileIcon'
|
import { getExtension, getFileIcon, hasKey } from '../utils/getFileIcon'
|
||||||
import { extensions, preview } from '../utils/getPreviewType'
|
import { extensions, preview } from '../utils/getPreviewType'
|
||||||
import { VideoPreview } from './previews/VideoPreview'
|
import { VideoPreview } from './previews/VideoPreview'
|
||||||
import { AudioPreview } from './previews/AudioPreview'
|
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 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
|
* Convert raw bits file/folder size into a human readable string
|
||||||
@@ -62,13 +65,13 @@ const FileListItem: FunctionComponent<{
|
|||||||
</div>
|
</div>
|
||||||
<div className="truncate">{c.name}</div>
|
<div className="truncate">{c.name}</div>
|
||||||
</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, {
|
{new Date(c.lastModifiedDateTime).toLocaleString(undefined, {
|
||||||
dateStyle: 'short',
|
dateStyle: 'short',
|
||||||
timeStyle: 'short',
|
timeStyle: 'short',
|
||||||
})}
|
})}
|
||||||
</div>
|
</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>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -89,21 +92,8 @@ const FileListing: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) =
|
|||||||
if (error) return <div>Failed to load</div>
|
if (error) return <div>Failed to load</div>
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center bg-white shadow rounded py-32 space-x-1">
|
<div className="shadow bg-white rounded p-3">
|
||||||
<svg
|
<Loading loadingText="Loading ..." />
|
||||||
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>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -140,8 +130,8 @@ const FileListing: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) =
|
|||||||
<div className="bg-white shadow rounded">
|
<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="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="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="invisible 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">Size</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{imagesInFolder.length !== 0 && (
|
{imagesInFolder.length !== 0 && (
|
||||||
@@ -211,7 +201,7 @@ const FileListing: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) =
|
|||||||
return <AudioPreview file={resp} />
|
return <AudioPreview file={resp} />
|
||||||
|
|
||||||
case preview.pdf:
|
case preview.pdf:
|
||||||
return <div>pdf</div>
|
return <PDFPreview file={resp} />
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return <div className="bg-white shadow rounded">{fileName}</div>
|
return <div className="bg-white shadow rounded">{fileName}</div>
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -4,7 +4,7 @@ import siteConfig from '../config/site.json'
|
|||||||
|
|
||||||
const Navbar = () => {
|
const Navbar = () => {
|
||||||
return (
|
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">
|
<div className="max-w-4xl w-full mx-auto flex items-center justify-between">
|
||||||
<h1 className="font-bold text-xl">{siteConfig.title}</h1>
|
<h1 className="font-bold text-xl">{siteConfig.title}</h1>
|
||||||
<a href="https://github.com/spencerwooo/onedrive-vercel-index" target="_blank" rel="noopener noreferrer">
|
<a href="https://github.com/spencerwooo/onedrive-vercel-index" target="_blank" rel="noopener noreferrer">
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
|||||||
export const AudioPreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
export const AudioPreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
||||||
return (
|
return (
|
||||||
<div className="bg-white rounded shadow p-3 w-full">
|
<div className="bg-white rounded shadow p-3 w-full">
|
||||||
<div className="flex space-x-4">
|
<div className="flex flex-col space-y-4 md:flex-row md:space-x-4">
|
||||||
<div className="flex items-center justify-center p-10 bg-gray-100 rounded">
|
<div className="flex items-center justify-center bg-gray-100 rounded p-28 md:p-14">
|
||||||
<FontAwesomeIcon className="" icon="music" size="lg" />
|
<FontAwesomeIcon icon="music" size="lg" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col w-full space-y-2">
|
<div className="flex flex-col w-full space-y-2">
|
||||||
<div>{file.name}</div>
|
<div>{file.name}</div>
|
||||||
<div className="text-gray-500 text-sm">
|
<div className="text-gray-500 text-sm pb-4">
|
||||||
Last modified:{' '}
|
Last modified:{' '}
|
||||||
{new Date(file.lastModifiedDateTime).toLocaleString(undefined, {
|
{new Date(file.lastModifiedDateTime).toLocaleString(undefined, {
|
||||||
dateStyle: 'short',
|
dateStyle: 'short',
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -1,4 +1,22 @@
|
|||||||
module.exports = {
|
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,
|
reactStrictMode: true,
|
||||||
images: {
|
images: {
|
||||||
domains: ['public.dm.files.1drv.com'],
|
domains: ['public.dm.files.1drv.com'],
|
||||||
|
|||||||
Generated
+1185
-33
File diff suppressed because it is too large
Load Diff
@@ -18,12 +18,14 @@
|
|||||||
"next": "11.0.0",
|
"next": "11.0.0",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "17.0.2",
|
||||||
|
"react-pdf": "^5.3.0",
|
||||||
"react-player": "^2.9.0",
|
"react-player": "^2.9.0",
|
||||||
"react-viewer": "^3.2.2",
|
"react-viewer": "^3.2.2",
|
||||||
"swr": "^0.5.6"
|
"swr": "^0.5.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "17.0.11",
|
"@types/react": "17.0.11",
|
||||||
|
"@types/react-pdf": "^5.0.4",
|
||||||
"autoprefixer": "^10.2.6",
|
"autoprefixer": "^10.2.6",
|
||||||
"eslint": "7.29.0",
|
"eslint": "7.29.0",
|
||||||
"eslint-config-next": "11.0.0",
|
"eslint-config-next": "11.0.0",
|
||||||
|
|||||||
+4
-2
@@ -14,7 +14,7 @@ import {
|
|||||||
faFile,
|
faFile,
|
||||||
faFolder,
|
faFolder,
|
||||||
} from '@fortawesome/free-regular-svg-icons'
|
} 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 { faGithub, faMarkdown } from '@fortawesome/free-brands-svg-icons'
|
||||||
|
|
||||||
import type { AppProps } from 'next/app'
|
import type { AppProps } from 'next/app'
|
||||||
@@ -34,7 +34,9 @@ library.add(
|
|||||||
faFolder,
|
faFolder,
|
||||||
faGithub,
|
faGithub,
|
||||||
faMarkdown,
|
faMarkdown,
|
||||||
faMusic
|
faMusic,
|
||||||
|
faArrowLeft,
|
||||||
|
faArrowRight
|
||||||
)
|
)
|
||||||
|
|
||||||
function MyApp({ Component, pageProps }: AppProps) {
|
function MyApp({ Component, pageProps }: AppProps) {
|
||||||
|
|||||||
Reference in New Issue
Block a user