extract components and custom useFileContent hook

This commit is contained in:
spencerwooo
2022-01-06 16:34:16 +08:00
parent 312bcff515
commit 6671986bac
18 changed files with 7839 additions and 356 deletions
+19 -17
View File
@@ -32,6 +32,7 @@ import AudioPreview from './previews/AudioPreview'
import VideoPreview from './previews/VideoPreview'
import DownloadButtonGroup from './DownloadBtnGtoup'
import PDFPreview from './previews/PDFPreview'
import { DownloadBtnContainer, PreviewContainer } from './previews/Containers'
// Disabling SSR for some previews (image gallery view, and PDF view)
const ReactViewer = dynamic(() => import('react-viewer'), { ssr: false })
@@ -188,16 +189,16 @@ const FileListing: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) =
}
return (
<div className="dark:bg-gray-900 p-3 bg-white rounded">
<PreviewContainer>
{error.message.includes('401') ? <Auth redirect={path} /> : <FourOhFour errorMsg={error.message} />}
</div>
</PreviewContainer>
)
}
if (!data) {
return (
<div className="dark:bg-gray-900 p-3 bg-white rounded">
<PreviewContainer>
<Loading loadingText="Loading ..." />
</div>
</PreviewContainer>
)
}
@@ -340,7 +341,8 @@ const FileListing: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) =
}
return (
<div className="dark:bg-gray-900 dark:text-gray-100 bg-white rounded">
<>
<div className="dark:bg-gray-900 dark:text-gray-100 bg-white rounded border border-gray-899/10">
<div className="dark:border-gray-700 grid items-center grid-cols-12 px-3 space-x-2 border-b border-gray-200">
<div className="md:col-span-6 col-span-12 font-bold py-3">Name</div>
<div className="md:block hidden col-span-3 font-bold">Last Modified</div>
@@ -537,13 +539,13 @@ const FileListing: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) =
</button>
</div>
)}
</div>
{renderReadme && (
<div className="dark:border-gray-700 border-t">
<div className="mt-4">
<MarkdownPreview file={readmeFile} path={path} standalone={false} />
</div>
)}
</div>
</>
)
}
@@ -557,10 +559,10 @@ const FileListing: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) =
switch (extensions[fileExtension]) {
case preview.image:
return (
<div className="w-full p-3 bg-white rounded">
<PreviewContainer>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img className="mx-auto" src={downloadUrl} alt={fileName} />
</div>
</PreviewContainer>
)
case preview.text:
@@ -588,28 +590,28 @@ const FileListing: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) =
return <EPUBPreview file={file} />
default:
return <div className="dark:bg-gray-900 bg-white rounded">{fileName}</div>
return <PreviewContainer>{fileName}</PreviewContainer>
}
}
return (
<>
<div className="dark:bg-gray-900 p-3 bg-white rounded">
<PreviewContainer>
<FourOhFour
errorMsg={`Preview for file ${fileName} is not available, download directly with the button below.`}
/>
</div>
<div className="mt-4">
</PreviewContainer>
<DownloadBtnContainer>
<DownloadButtonGroup downloadUrl={downloadUrl} />
</div>
</DownloadBtnContainer>
</>
)
}
return (
<div className="dark:bg-gray-900 p-3 bg-white rounded">
<PreviewContainer>
<FourOhFour errorMsg={`Cannot preview ${path}`} />
</div>
</PreviewContainer>
)
}
export default FileListing
+6 -1
View File
@@ -7,7 +7,12 @@ const createFooterMarkup = () => {
}
const Footer = () => {
return <div className="p-4 text-sm text-gray-400" dangerouslySetInnerHTML={createFooterMarkup()}></div>
return (
<div
className="p-4 text-sm text-gray-400 w-full text-center border-t border-gray-900/10"
dangerouslySetInnerHTML={createFooterMarkup()}
></div>
)
}
export default Footer
+1 -2
View File
@@ -1,7 +1,6 @@
import Image from 'next/image'
import { FunctionComponent } from 'react'
const FourOhFour: FunctionComponent<{ errorMsg: string }> = ({ errorMsg }) => {
const FourOhFour: React.FC<{ errorMsg: string }> = ({ errorMsg }) => {
return (
<div className="my-12">
<div className="w-1/3 mx-auto">
+2 -4
View File
@@ -1,6 +1,4 @@
import { FunctionComponent } from 'react'
const Loading: FunctionComponent<{ loadingText: string }> = ({ loadingText }) => {
const Loading: React.FC<{ loadingText: string }> = ({ loadingText }) => {
return (
<div className="dark:text-white flex items-center justify-center py-32 space-x-1 rounded">
<LoadingIcon className="animate-spin w-5 h-5 mr-3 -ml-1" />
@@ -10,7 +8,7 @@ const Loading: FunctionComponent<{ loadingText: string }> = ({ loadingText }) =>
}
// As there is no CSS-in-JS styling system, pass class list to override styles
export const LoadingIcon: FunctionComponent<{ className?: string }> = ({ className }) => {
export const LoadingIcon: React.FC<{ className?: string }> = ({ className }) => {
return (
<svg className={className} 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" />
+7 -6
View File
@@ -1,8 +1,9 @@
import { FunctionComponent, useState } from 'react'
import { FC, useState } from 'react'
import ReactPlayer from 'react-player'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import DownloadButtonGroup from '../DownloadBtnGtoup'
import { DownloadBtnContainer, PreviewContainer } from './Containers'
enum PlayerState {
Loading,
@@ -11,12 +12,12 @@ enum PlayerState {
Paused,
}
const AudioPreview: FunctionComponent<{ file: any }> = ({ file }) => {
const AudioPreview: FC<{ file: any }> = ({ file }) => {
const [playerStatus, setPlayerStatus] = useState(PlayerState.Loading)
return (
<>
<div className="dark:bg-gray-900 dark:text-white w-full p-3 bg-white rounded">
<PreviewContainer>
<div className="md:flex-row md:space-x-4 flex flex-col space-y-4">
<div className="dark:bg-gray-700 aspect-square flex items-center justify-center w-full md:w-40 transition-all duration-75 bg-gray-100 rounded">
{playerStatus === PlayerState.Loading ? (
@@ -72,11 +73,11 @@ const AudioPreview: FunctionComponent<{ file: any }> = ({ file }) => {
/>
</div>
</div>
</div>
</PreviewContainer>
<div className="border-t-gray-200 dark:border-t-gray-700 border-t p-2 sticky bottom-0 left-0 right-0 z-10 bg-white bg-opacity-80 backdrop-blur-md dark:bg-gray-900">
<DownloadBtnContainer>
<DownloadButtonGroup downloadUrl={file['@microsoft.graph.downloadUrl']} />
</div>
</DownloadBtnContainer>
</>
)
}
+17 -16
View File
@@ -1,46 +1,47 @@
import { useEffect, FunctionComponent } from 'react'
import { useEffect, FC } from 'react'
import Prism from 'prismjs'
import { getExtension } from '../../utils/getFileIcon'
import { useStaleSWR } from '../../utils/fetchWithSWR'
import useFileContent from '../../utils/fetchOnMount'
import FourOhFour from '../FourOhFour'
import Loading from '../Loading'
import DownloadButtonGroup from '../DownloadBtnGtoup'
import { DownloadBtnContainer, PreviewContainer } from './Containers'
const CodePreview: FunctionComponent<{ file: any }> = ({ file }) => {
const { data, error } = useStaleSWR({ url: file['@microsoft.graph.downloadUrl'] })
const CodePreview: FC<{ file: any }> = ({ file }) => {
const { content, error, validating } = useFileContent(file['@microsoft.graph.downloadUrl'])
useEffect(() => {
if (typeof window !== 'undefined') {
Prism.highlightAll()
}
}, [data])
}, [validating])
if (error) {
return (
<div className="dark:bg-gray-900 p-3 bg-white rounded">
<FourOhFour errorMsg={error.message} />
</div>
<PreviewContainer>
<FourOhFour errorMsg={error} />
</PreviewContainer>
)
}
if (!data) {
if (validating) {
return (
<div className="dark:bg-gray-900 p-3 bg-white rounded">
<PreviewContainer>
<Loading loadingText="Loading file content..." />
</div>
</PreviewContainer>
)
}
return (
<div>
<div className="markdown-body p-3 bg-gray-900 rounded">
<PreviewContainer>
<pre className={`language-${getExtension(file.name)}`}>
<code>{data}</code>
<code className="font-mono">{content}</code>
</pre>
</div>
<div className="border-t-gray-200 dark:border-t-gray-700 border-t p-2 sticky bottom-0 left-0 right-0 z-10 bg-white bg-opacity-80 backdrop-blur-md dark:bg-gray-900">
</PreviewContainer>
<DownloadBtnContainer>
<DownloadButtonGroup downloadUrl={file['@microsoft.graph.downloadUrl']} />
</div>
</DownloadBtnContainer>
</div>
)
}
+11
View File
@@ -0,0 +1,11 @@
export function PreviewContainer({ children }): JSX.Element {
return <div className="dark:bg-gray-900 p-3 bg-white rounded border border-gray-900/10">{children}</div>
}
export function DownloadBtnContainer({ children }): JSX.Element {
return (
<div className="mt-4 border rounded border-gray-900/10 p-2 sticky bottom-0 left-0 right-0 z-10 bg-white bg-opacity-80 backdrop-blur-md dark:bg-gray-900">
{children}
</div>
)
}
+5 -4
View File
@@ -4,8 +4,9 @@ import type { Rendition } from 'epubjs'
import Loading from '../Loading'
import DownloadButtonGroup from '../DownloadBtnGtoup'
import { DownloadBtnContainer } from './Containers'
const EPUBPreview: FunctionComponent<{file: any}> = ({ file }) => {
const EPUBPreview: FunctionComponent<{ file: any }> = ({ file }) => {
const [epubContainerWidth, setEpubContainerWidth] = useState(400)
const epubContainer = useRef<HTMLDivElement>(null)
@@ -41,7 +42,7 @@ const EPUBPreview: FunctionComponent<{file: any}> = ({ file }) => {
<div style={{ position: 'absolute', width: epubContainerWidth, height: '70vh' }}>
<ReactReader
url={file['@microsoft.graph.downloadUrl']}
getRendition={(rendition) => fixEpub(rendition)}
getRendition={rendition => fixEpub(rendition)}
loadingView={<Loading loadingText="Loading EPUB ..." />}
location={location}
locationChanged={onLocationChange}
@@ -51,9 +52,9 @@ const EPUBPreview: FunctionComponent<{file: any}> = ({ file }) => {
</div>
</div>
</div>
<div className="border-t-gray-200 dark:border-t-gray-700 border-t p-2 sticky bottom-0 left-0 right-0 z-10 bg-white bg-opacity-80 backdrop-blur-md dark:bg-gray-900">
<DownloadBtnContainer>
<DownloadButtonGroup downloadUrl={file['@microsoft.graph.downloadUrl']} />
</div>
</DownloadBtnContainer>
</div>
)
}
+16 -19
View File
@@ -11,14 +11,15 @@ import 'katex/dist/katex.min.css'
import FourOhFour from '../FourOhFour'
import Loading from '../Loading'
import DownloadButtonGroup from '../DownloadBtnGtoup'
import { useStaleSWR } from '../../utils/fetchWithSWR'
import useFileContent from '../../utils/fetchOnMount'
import { DownloadBtnContainer, PreviewContainer } from './Containers'
const MarkdownPreview: FunctionComponent<{ file: any; path: string; standalone?: boolean }> = ({
file,
path,
standalone = true,
}) => {
const { data, error } = useStaleSWR({ url: file['@microsoft.graph.downloadUrl'] })
const { content, error, validating } = useFileContent(file['@microsoft.graph.downloadUrl'])
// The parent folder of the markdown file, which is also the relative image folder
const parentPath = path.substring(0, path.lastIndexOf('/'))
@@ -63,45 +64,41 @@ const MarkdownPreview: FunctionComponent<{ file: any; path: string; standalone?:
useEffect(() => {
Prism.highlightAll()
}, [data])
}, [content])
if (error) {
return (
<div className={`${standalone ? 'bg-white dark:bg-gray-900 rounded p-3' : ''}`}>
<FourOhFour errorMsg={error.message} />
</div>
<PreviewContainer>
<FourOhFour errorMsg={error} />
</PreviewContainer>
)
}
if (!data) {
if (validating) {
return (
<div className={standalone ? 'bg-white dark:bg-gray-900 rounded p-3' : ''}>
<PreviewContainer>
<Loading loadingText="Loading file content..." />
</div>
</PreviewContainer>
)
}
return (
<div>
<div
className={
standalone
? 'markdown-body bg-white dark:bg-gray-900 rounded p-3 dark:text-white'
: 'markdown-body p-3 dark:text-white'
}
>
<PreviewContainer>
<div className="markdown-body">
{/* Using rehypeRaw to render HTML inside Markdown is potentially dangerous, use under safe environments. (#18) */}
<ReactMarkdown
remarkPlugins={[gfm, remarkMath]}
rehypePlugins={[rehypeKatex, rehypeRaw as any]}
components={relativeImagePathRenderer}
>
{data}
{content}
</ReactMarkdown>
</div>
</PreviewContainer>
{standalone && (
<div className="border-t-gray-200 dark:border-t-gray-700 border-t p-2 sticky bottom-0 left-0 right-0 z-10 bg-white bg-opacity-80 backdrop-blur-md dark:bg-gray-900">
<DownloadBtnContainer>
<DownloadButtonGroup downloadUrl={file['@microsoft.graph.downloadUrl']} />
</div>
</DownloadBtnContainer>
)}
</div>
)
+3 -2
View File
@@ -2,6 +2,7 @@ import { FunctionComponent, useEffect, useRef, useState } from 'react'
import Preview from 'preview-office-docs'
import DownloadButtonGroup from '../DownloadBtnGtoup'
import { DownloadBtnContainer } from './Containers'
const OfficePreview: FunctionComponent<{ file: any }> = ({ file }) => {
const docContainer = useRef<HTMLDivElement>(null)
@@ -20,9 +21,9 @@ const OfficePreview: FunctionComponent<{ file: any }> = ({ file }) => {
height="600"
/>
</div>
<div className="border-t-gray-200 dark:border-t-gray-700 border-t p-2 sticky bottom-0 left-0 right-0 z-10 bg-white bg-opacity-80 backdrop-blur-md dark:bg-gray-900">
<DownloadBtnContainer>
<DownloadButtonGroup downloadUrl={file['@microsoft.graph.downloadUrl']} />
</div>
</DownloadBtnContainer>
</div>
)
}
+3 -2
View File
@@ -1,6 +1,7 @@
import { FunctionComponent } from 'react'
import DownloadButtonGroup from '../DownloadBtnGtoup'
import { DownloadBtnContainer } from './Containers'
const PDFEmbedPreview: FunctionComponent<{ file: any }> = ({ file }) => {
// const url = `/api/proxy?url=${encodeURIComponent(file['@microsoft.graph.downloadUrl'])}&inline=true`
@@ -13,9 +14,9 @@ const PDFEmbedPreview: FunctionComponent<{ file: any }> = ({ file }) => {
<div className="w-full rounded overflow-hidden" style={{ height: '90vh' }}>
<iframe src={url} frameBorder="0" width="100%" height="100%"></iframe>
</div>
<div className="border-t-gray-200 dark:border-t-gray-700 border-t p-2 sticky bottom-0 left-0 right-0 z-10 bg-white bg-opacity-80 backdrop-blur-md dark:bg-gray-900">
<DownloadBtnContainer>
<DownloadButtonGroup downloadUrl={file['@microsoft.graph.downloadUrl']} />
</div>
</DownloadBtnContainer>
</div>
)
}
+24 -16
View File
@@ -1,35 +1,43 @@
import { FunctionComponent } from 'react'
import FourOhFour from '../FourOhFour'
import Loading from '../Loading'
import DownloadButtonGroup from '../DownloadBtnGtoup'
import { useStaleSWR } from '../../utils/fetchWithSWR'
import useFileContent from '../../utils/fetchOnMount'
import { DownloadBtnContainer, PreviewContainer } from './Containers'
const TextPreview: FunctionComponent<{ file: any }> = ({ file }) => {
const { data, error } = useStaleSWR({ url: file['@microsoft.graph.downloadUrl'] })
const TextPreview = ({ file }) => {
const { content, error, validating } = useFileContent(file['@microsoft.graph.downloadUrl'])
if (error) {
return (
<div className="dark:bg-gray-900 p-3 bg-white rounded">
<FourOhFour errorMsg={error.message} />
</div>
<PreviewContainer>
<FourOhFour errorMsg={error} />
</PreviewContainer>
)
}
if (!data) {
if (validating) {
return (
<div className="dark:bg-gray-900 p-3 bg-white rounded">
<PreviewContainer>
<Loading loadingText="Loading file content..." />
</div>
</PreviewContainer>
)
}
if (!content) {
return (
<PreviewContainer>
<FourOhFour errorMsg="File is empty." />
</PreviewContainer>
)
}
return (
<div>
<div className="dark:bg-gray-900 dark:text-gray-100 p-3 bg-white rounded">
<pre className="md:p-3 p-0 overflow-scroll">{data}</pre>
</div>
<div className="border-t-gray-200 dark:border-t-gray-700 border-t p-2 sticky bottom-0 left-0 right-0 z-10 bg-white bg-opacity-80 backdrop-blur-md dark:bg-gray-900">
<PreviewContainer>
<pre className="md:p-3 p-0 overflow-x-scroll text-sm">{content}</pre>
</PreviewContainer>
<DownloadBtnContainer>
<DownloadButtonGroup downloadUrl={file['@microsoft.graph.downloadUrl']} />
</div>
</DownloadBtnContainer>
</div>
)
}
+7 -4
View File
@@ -6,6 +6,7 @@ import toast from 'react-hot-toast'
import { getBaseUrl } from '../../utils/getBaseUrl'
import { DownloadButton } from '../DownloadBtnGtoup'
import { DownloadBtnContainer, PreviewContainer } from './Containers'
const VideoPreview: FunctionComponent<{ file: any }> = ({ file }) => {
const { asPath } = useRouter()
@@ -13,7 +14,7 @@ const VideoPreview: FunctionComponent<{ file: any }> = ({ file }) => {
return (
<>
<div className="dark:bg-gray-900 p-3 bg-white rounded">
<PreviewContainer>
<ReactPlayer
className="aspect-video"
url={file['@microsoft.graph.downloadUrl']}
@@ -22,9 +23,10 @@ const VideoPreview: FunctionComponent<{ file: any }> = ({ file }) => {
height="100%"
config={{ file: { forceVideo: true } }}
/>
</div>
</PreviewContainer>
<div className="flex flex-wrap justify-center mt-4 gap-2">
<DownloadBtnContainer>
<div className="flex flex-wrap justify-center gap-2">
<DownloadButton
onClickCallback={() => window.open(file['@microsoft.graph.downloadUrl'])}
btnColor="blue"
@@ -44,7 +46,7 @@ const VideoPreview: FunctionComponent<{ file: any }> = ({ file }) => {
clipboard.copy(`${getBaseUrl()}/api?path=${asPath}&raw=true`)
toast.success('Copied direct link to clipboard.')
}}
btnColor="yellow"
btnColor="pink"
btnText="Copy direct link"
btnIcon="copy"
/>
@@ -65,6 +67,7 @@ const VideoPreview: FunctionComponent<{ file: any }> = ({ file }) => {
btnImage="/players/potplayer.png"
/>
</div>
</DownloadBtnContainer>
</>
)
}
+7460 -29
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -16,7 +16,7 @@ export default function Folders() {
<title>{siteConfig.title}</title>
</Head>
<main className="bg-gray-50 dark:bg-gray-800 flex flex-col flex-1 w-full">
<main className=" flex flex-col flex-1 w-full">
<Navbar />
<div className="w-full max-w-5xl p-4 mx-auto">
<Breadcrumb query={query} />
+1 -1
View File
@@ -13,7 +13,7 @@ export default function Home() {
<title>{siteConfig.title}</title>
</Head>
<main className="bg-gray-50 dark:bg-gray-800 flex flex-col flex-1 w-full">
<main className="flex flex-col flex-1 w-full">
<Navbar />
<div className="w-full max-w-5xl p-4 mx-auto">
<Breadcrumb />
+4
View File
@@ -24,3 +24,7 @@
.markdown-body ol {
@apply list-decimal;
}
pre[class*='language-'],
code[class*='language-'] {
@apply font-mono !important;
}
+20
View File
@@ -0,0 +1,20 @@
import axios from 'axios'
import { useEffect, useState } from 'react'
// Custom hook to fetch raw file content on mount
export default function useFileContent(odRawUrl: string): { content: string; error: null; validating: boolean } {
const [content, setContent] = useState('')
const [validating, setValidating] = useState(true)
const [error, setError] = useState(null)
useEffect(() => {
axios
.get(odRawUrl)
.then(res => setContent(res.data))
.catch(e => setError(e.message))
.finally(() => {
setValidating(false)
})
}, [odRawUrl])
return { content, error, validating }
}