extract components and custom useFileContent hook
This commit is contained in:
parent
312bcff515
commit
6671986bac
18 changed files with 7839 additions and 356 deletions
|
@ -32,6 +32,7 @@ import AudioPreview from './previews/AudioPreview'
|
||||||
import VideoPreview from './previews/VideoPreview'
|
import VideoPreview from './previews/VideoPreview'
|
||||||
import DownloadButtonGroup from './DownloadBtnGtoup'
|
import DownloadButtonGroup from './DownloadBtnGtoup'
|
||||||
import PDFPreview from './previews/PDFPreview'
|
import PDFPreview from './previews/PDFPreview'
|
||||||
|
import { DownloadBtnContainer, PreviewContainer } from './previews/Containers'
|
||||||
|
|
||||||
// Disabling SSR for some previews (image gallery view, and PDF view)
|
// 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 })
|
||||||
|
@ -188,16 +189,16 @@ const FileListing: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) =
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dark:bg-gray-900 p-3 bg-white rounded">
|
<PreviewContainer>
|
||||||
{error.message.includes('401') ? <Auth redirect={path} /> : <FourOhFour errorMsg={error.message} />}
|
{error.message.includes('401') ? <Auth redirect={path} /> : <FourOhFour errorMsg={error.message} />}
|
||||||
</div>
|
</PreviewContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return (
|
return (
|
||||||
<div className="dark:bg-gray-900 p-3 bg-white rounded">
|
<PreviewContainer>
|
||||||
<Loading loadingText="Loading ..." />
|
<Loading loadingText="Loading ..." />
|
||||||
</div>
|
</PreviewContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,210 +341,211 @@ const FileListing: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) =
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dark:bg-gray-900 dark:text-gray-100 bg-white rounded">
|
<>
|
||||||
<div className="dark:border-gray-700 grid items-center grid-cols-12 px-3 space-x-2 border-b border-gray-200">
|
<div className="dark:bg-gray-900 dark:text-gray-100 bg-white rounded border border-gray-899/10">
|
||||||
<div className="md:col-span-6 col-span-12 font-bold py-3">Name</div>
|
<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:block hidden col-span-3 font-bold">Last Modified</div>
|
<div className="md:col-span-6 col-span-12 font-bold py-3">Name</div>
|
||||||
<div className="md:block hidden font-bold">Size</div>
|
<div className="md:block hidden col-span-3 font-bold">Last Modified</div>
|
||||||
<div className="md:block hidden font-bold">Actions</div>
|
<div className="md:block hidden font-bold">Size</div>
|
||||||
<div className="md:block hidden font-bold">
|
<div className="md:block hidden font-bold">Actions</div>
|
||||||
<div className="md:flex dark:text-gray-400 hidden p-1 text-gray-700">
|
<div className="md:block hidden font-bold">
|
||||||
<Checkbox
|
|
||||||
checked={totalSelected}
|
|
||||||
onChange={toggleTotalSelected}
|
|
||||||
indeterminate={true}
|
|
||||||
title={'Select files'}
|
|
||||||
/>
|
|
||||||
{totalGenerating ? (
|
|
||||||
<Downloading title="Downloading selected files, refresh page to cancel" />
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
title="Download selected files"
|
|
||||||
className="hover:bg-gray-300 dark:hover:bg-gray-600 p-2 rounded cursor-pointer disabled:text-gray-400 disabled:dark:text-gray-600 disabled:hover:bg-white disabled:hover:dark:bg-gray-900 disabled:cursor-not-allowed"
|
|
||||||
disabled={totalSelected === 0}
|
|
||||||
onClick={handleSelectedDownload}
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={['far', 'arrow-alt-circle-down']} />
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Toaster />
|
|
||||||
|
|
||||||
{imagesInFolder.length !== 0 && (
|
|
||||||
<ReactViewer
|
|
||||||
zIndex={99}
|
|
||||||
visible={imageViewerVisible}
|
|
||||||
activeIndex={activeImageIdx}
|
|
||||||
images={imagesInFolder}
|
|
||||||
drag={false}
|
|
||||||
rotatable={false}
|
|
||||||
noClose={true}
|
|
||||||
scalable={false}
|
|
||||||
zoomSpeed={0.2}
|
|
||||||
downloadable={true}
|
|
||||||
downloadInNewWindow={true}
|
|
||||||
onMaskClick={() => {
|
|
||||||
setImageViewerVisibility(false)
|
|
||||||
}}
|
|
||||||
customToolbar={toolbars => {
|
|
||||||
toolbars[0].render = <FontAwesomeIcon icon="plus" />
|
|
||||||
toolbars[1].render = <FontAwesomeIcon icon="minus" />
|
|
||||||
toolbars[2].render = <FontAwesomeIcon icon="arrow-left" />
|
|
||||||
toolbars[3].render = <FontAwesomeIcon icon="undo" />
|
|
||||||
toolbars[4].render = <FontAwesomeIcon icon="arrow-right" />
|
|
||||||
toolbars[9].render = <FontAwesomeIcon icon="download" />
|
|
||||||
return toolbars.concat([
|
|
||||||
{
|
|
||||||
key: 'copy',
|
|
||||||
render: <FontAwesomeIcon icon={['fas', 'copy']} />,
|
|
||||||
onClick: i => {
|
|
||||||
clipboard.copy(i.alt ? `${getBaseUrl()}/api?path=${path + '/' + i.alt}&raw=true` : '')
|
|
||||||
toast('Copied image permanent link to clipboard.', { icon: '👌' })
|
|
||||||
},
|
|
||||||
},
|
|
||||||
])
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{children.map((c: any) => (
|
|
||||||
<div className="hover:bg-gray-100 dark:hover:bg-gray-850 grid grid-cols-12" key={c.id}>
|
|
||||||
<div
|
|
||||||
className="col-span-10"
|
|
||||||
onClick={e => {
|
|
||||||
e.preventDefault()
|
|
||||||
|
|
||||||
if (!c.folder && fileIsImage(c.name)) {
|
|
||||||
setActiveImageIdx(imageIndexDict[c.id])
|
|
||||||
setImageViewerVisibility(true)
|
|
||||||
} else {
|
|
||||||
router.push(`${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<FileListItem fileContent={c} />
|
|
||||||
</div>
|
|
||||||
{c.folder ? (
|
|
||||||
<div className="md:flex dark:text-gray-400 hidden p-1 text-gray-700">
|
<div className="md:flex dark:text-gray-400 hidden p-1 text-gray-700">
|
||||||
<span
|
<Checkbox
|
||||||
title="Copy folder permalink"
|
checked={totalSelected}
|
||||||
className="hover:bg-gray-300 dark:hover:bg-gray-600 p-2 rounded cursor-pointer"
|
onChange={toggleTotalSelected}
|
||||||
onClick={() => {
|
indeterminate={true}
|
||||||
clipboard.copy(`${getBaseUrl()}${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`)
|
title={'Select files'}
|
||||||
toast('Copied folder permalink.', { icon: '👌' })
|
/>
|
||||||
}}
|
{totalGenerating ? (
|
||||||
>
|
<Downloading title="Downloading selected files, refresh page to cancel" />
|
||||||
<FontAwesomeIcon icon={['far', 'copy']} />
|
|
||||||
</span>
|
|
||||||
{folderGenerating[c.id] ? (
|
|
||||||
<Downloading title="Downloading folder, refresh page to cancel" />
|
|
||||||
) : (
|
) : (
|
||||||
<span
|
<button
|
||||||
title="Download folder"
|
title="Download selected files"
|
||||||
className="hover:bg-gray-300 dark:hover:bg-gray-600 p-2 rounded cursor-pointer"
|
className="hover:bg-gray-300 dark:hover:bg-gray-600 p-2 rounded cursor-pointer disabled:text-gray-400 disabled:dark:text-gray-600 disabled:hover:bg-white disabled:hover:dark:bg-gray-900 disabled:cursor-not-allowed"
|
||||||
onClick={() => {
|
disabled={totalSelected === 0}
|
||||||
const p = `${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`
|
onClick={handleSelectedDownload}
|
||||||
handleFolderDownload(p, c.id, c.name)()
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={['far', 'arrow-alt-circle-down']} />
|
<FontAwesomeIcon icon={['far', 'arrow-alt-circle-down']} />
|
||||||
</span>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
</div>
|
||||||
<div className="md:flex dark:text-gray-400 hidden p-1 text-gray-700">
|
</div>
|
||||||
<span
|
|
||||||
title="Copy raw file permalink"
|
<Toaster />
|
||||||
className="hover:bg-gray-300 dark:hover:bg-gray-600 p-2 rounded cursor-pointer"
|
|
||||||
onClick={() => {
|
{imagesInFolder.length !== 0 && (
|
||||||
clipboard.copy(
|
<ReactViewer
|
||||||
`${getBaseUrl()}/api?path=${path === '/' ? '' : path}/${encodeURIComponent(c.name)}&raw=true`
|
zIndex={99}
|
||||||
)
|
visible={imageViewerVisible}
|
||||||
toast.success('Copied raw file permalink.')
|
activeIndex={activeImageIdx}
|
||||||
}}
|
images={imagesInFolder}
|
||||||
>
|
drag={false}
|
||||||
<FontAwesomeIcon icon={['far', 'copy']} />
|
rotatable={false}
|
||||||
</span>
|
noClose={true}
|
||||||
<a
|
scalable={false}
|
||||||
title="Download file"
|
zoomSpeed={0.2}
|
||||||
className="hover:bg-gray-300 dark:hover:bg-gray-600 p-2 rounded cursor-pointer"
|
downloadable={true}
|
||||||
href={c['@microsoft.graph.downloadUrl']}
|
downloadInNewWindow={true}
|
||||||
>
|
onMaskClick={() => {
|
||||||
<FontAwesomeIcon icon={['far', 'arrow-alt-circle-down']} />
|
setImageViewerVisibility(false)
|
||||||
</a>
|
}}
|
||||||
|
customToolbar={toolbars => {
|
||||||
|
toolbars[0].render = <FontAwesomeIcon icon="plus" />
|
||||||
|
toolbars[1].render = <FontAwesomeIcon icon="minus" />
|
||||||
|
toolbars[2].render = <FontAwesomeIcon icon="arrow-left" />
|
||||||
|
toolbars[3].render = <FontAwesomeIcon icon="undo" />
|
||||||
|
toolbars[4].render = <FontAwesomeIcon icon="arrow-right" />
|
||||||
|
toolbars[9].render = <FontAwesomeIcon icon="download" />
|
||||||
|
return toolbars.concat([
|
||||||
|
{
|
||||||
|
key: 'copy',
|
||||||
|
render: <FontAwesomeIcon icon={['fas', 'copy']} />,
|
||||||
|
onClick: i => {
|
||||||
|
clipboard.copy(i.alt ? `${getBaseUrl()}/api?path=${path + '/' + i.alt}&raw=true` : '')
|
||||||
|
toast('Copied image permanent link to clipboard.', { icon: '👌' })
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{children.map((c: any) => (
|
||||||
|
<div className="hover:bg-gray-100 dark:hover:bg-gray-850 grid grid-cols-12" key={c.id}>
|
||||||
|
<div
|
||||||
|
className="col-span-10"
|
||||||
|
onClick={e => {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
if (!c.folder && fileIsImage(c.name)) {
|
||||||
|
setActiveImageIdx(imageIndexDict[c.id])
|
||||||
|
setImageViewerVisibility(true)
|
||||||
|
} else {
|
||||||
|
router.push(`${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FileListItem fileContent={c} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
{c.folder ? (
|
||||||
<div className="md:flex dark:text-gray-400 hidden p-1 text-gray-700">
|
<div className="md:flex dark:text-gray-400 hidden p-1 text-gray-700">
|
||||||
{c.folder || c.name === '.password' ? (
|
<span
|
||||||
''
|
title="Copy folder permalink"
|
||||||
) : (
|
className="hover:bg-gray-300 dark:hover:bg-gray-600 p-2 rounded cursor-pointer"
|
||||||
<Checkbox
|
onClick={() => {
|
||||||
checked={selected[c.id] ? 2 : 0}
|
clipboard.copy(`${getBaseUrl()}${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`)
|
||||||
onChange={() => toggleItemSelected(c.id)}
|
toast('Copied folder permalink.', { icon: '👌' })
|
||||||
title="Select file"
|
}}
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{!onlyOnePage && (
|
|
||||||
<div>
|
|
||||||
<div className="dark:border-gray-700 p-3 font-mono text-sm text-center text-gray-400 border-b border-gray-200">
|
|
||||||
- showing {size} page{size > 1 ? 's' : ''} of {isLoadingMore ? '...' : children.length} files -
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
className={`flex items-center justify-center w-full p-3 space-x-2 disabled:cursor-not-allowed ${
|
|
||||||
isLoadingMore || isReachingEnd ? 'opacity-60' : 'hover:bg-gray-100 dark:hover:bg-gray-850'
|
|
||||||
}`}
|
|
||||||
onClick={() => setSize(size + 1)}
|
|
||||||
disabled={isLoadingMore || isReachingEnd}
|
|
||||||
>
|
|
||||||
{isLoadingMore ? (
|
|
||||||
<>
|
|
||||||
<span>Loading ...</span>{' '}
|
|
||||||
<svg
|
|
||||||
className="animate-spin w-5 h-5 mr-3 -ml-1"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
>
|
||||||
<circle
|
<FontAwesomeIcon icon={['far', 'copy']} />
|
||||||
className="opacity-25"
|
</span>
|
||||||
cx="12"
|
{folderGenerating[c.id] ? (
|
||||||
cy="12"
|
<Downloading title="Downloading folder, refresh page to cancel" />
|
||||||
r="10"
|
) : (
|
||||||
stroke="currentColor"
|
<span
|
||||||
strokeWidth="4"
|
title="Download folder"
|
||||||
></circle>
|
className="hover:bg-gray-300 dark:hover:bg-gray-600 p-2 rounded cursor-pointer"
|
||||||
<path
|
onClick={() => {
|
||||||
className="opacity-75"
|
const p = `${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`
|
||||||
fill="currentColor"
|
handleFolderDownload(p, c.id, c.name)()
|
||||||
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>
|
<FontAwesomeIcon icon={['far', 'arrow-alt-circle-down']} />
|
||||||
</>
|
</span>
|
||||||
) : isReachingEnd ? (
|
)}
|
||||||
<span>No more files</span>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<div className="md:flex dark:text-gray-400 hidden p-1 text-gray-700">
|
||||||
<span>Load more</span>
|
<span
|
||||||
<FontAwesomeIcon icon="chevron-circle-down" />
|
title="Copy raw file permalink"
|
||||||
</>
|
className="hover:bg-gray-300 dark:hover:bg-gray-600 p-2 rounded cursor-pointer"
|
||||||
|
onClick={() => {
|
||||||
|
clipboard.copy(
|
||||||
|
`${getBaseUrl()}/api?path=${path === '/' ? '' : path}/${encodeURIComponent(c.name)}&raw=true`
|
||||||
|
)
|
||||||
|
toast.success('Copied raw file permalink.')
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={['far', 'copy']} />
|
||||||
|
</span>
|
||||||
|
<a
|
||||||
|
title="Download file"
|
||||||
|
className="hover:bg-gray-300 dark:hover:bg-gray-600 p-2 rounded cursor-pointer"
|
||||||
|
href={c['@microsoft.graph.downloadUrl']}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={['far', 'arrow-alt-circle-down']} />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</button>
|
<div className="md:flex dark:text-gray-400 hidden p-1 text-gray-700">
|
||||||
</div>
|
{c.folder || c.name === '.password' ? (
|
||||||
)}
|
''
|
||||||
|
) : (
|
||||||
|
<Checkbox
|
||||||
|
checked={selected[c.id] ? 2 : 0}
|
||||||
|
onChange={() => toggleItemSelected(c.id)}
|
||||||
|
title="Select file"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{!onlyOnePage && (
|
||||||
|
<div>
|
||||||
|
<div className="dark:border-gray-700 p-3 font-mono text-sm text-center text-gray-400 border-b border-gray-200">
|
||||||
|
- showing {size} page{size > 1 ? 's' : ''} of {isLoadingMore ? '...' : children.length} files -
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className={`flex items-center justify-center w-full p-3 space-x-2 disabled:cursor-not-allowed ${
|
||||||
|
isLoadingMore || isReachingEnd ? 'opacity-60' : 'hover:bg-gray-100 dark:hover:bg-gray-850'
|
||||||
|
}`}
|
||||||
|
onClick={() => setSize(size + 1)}
|
||||||
|
disabled={isLoadingMore || isReachingEnd}
|
||||||
|
>
|
||||||
|
{isLoadingMore ? (
|
||||||
|
<>
|
||||||
|
<span>Loading ...</span>{' '}
|
||||||
|
<svg
|
||||||
|
className="animate-spin w-5 h-5 mr-3 -ml-1"
|
||||||
|
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>
|
||||||
|
</>
|
||||||
|
) : isReachingEnd ? (
|
||||||
|
<span>No more files</span>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span>Load more</span>
|
||||||
|
<FontAwesomeIcon icon="chevron-circle-down" />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
{renderReadme && (
|
{renderReadme && (
|
||||||
<div className="dark:border-gray-700 border-t">
|
<div className="mt-4">
|
||||||
<MarkdownPreview file={readmeFile} path={path} standalone={false} />
|
<MarkdownPreview file={readmeFile} path={path} standalone={false} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -557,10 +559,10 @@ const FileListing: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) =
|
||||||
switch (extensions[fileExtension]) {
|
switch (extensions[fileExtension]) {
|
||||||
case preview.image:
|
case preview.image:
|
||||||
return (
|
return (
|
||||||
<div className="w-full p-3 bg-white rounded">
|
<PreviewContainer>
|
||||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||||
<img className="mx-auto" src={downloadUrl} alt={fileName} />
|
<img className="mx-auto" src={downloadUrl} alt={fileName} />
|
||||||
</div>
|
</PreviewContainer>
|
||||||
)
|
)
|
||||||
|
|
||||||
case preview.text:
|
case preview.text:
|
||||||
|
@ -588,28 +590,28 @@ const FileListing: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) =
|
||||||
return <EPUBPreview file={file} />
|
return <EPUBPreview file={file} />
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return <div className="dark:bg-gray-900 bg-white rounded">{fileName}</div>
|
return <PreviewContainer>{fileName}</PreviewContainer>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="dark:bg-gray-900 p-3 bg-white rounded">
|
<PreviewContainer>
|
||||||
<FourOhFour
|
<FourOhFour
|
||||||
errorMsg={`Preview for file ${fileName} is not available, download directly with the button below.`}
|
errorMsg={`Preview for file ${fileName} is not available, download directly with the button below.`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</PreviewContainer>
|
||||||
<div className="mt-4">
|
<DownloadBtnContainer>
|
||||||
<DownloadButtonGroup downloadUrl={downloadUrl} />
|
<DownloadButtonGroup downloadUrl={downloadUrl} />
|
||||||
</div>
|
</DownloadBtnContainer>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dark:bg-gray-900 p-3 bg-white rounded">
|
<PreviewContainer>
|
||||||
<FourOhFour errorMsg={`Cannot preview ${path}`} />
|
<FourOhFour errorMsg={`Cannot preview ${path}`} />
|
||||||
</div>
|
</PreviewContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export default FileListing
|
export default FileListing
|
||||||
|
|
|
@ -7,7 +7,12 @@ const createFooterMarkup = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Footer = () => {
|
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
|
export default Footer
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import { FunctionComponent } from 'react'
|
|
||||||
|
|
||||||
const FourOhFour: FunctionComponent<{ errorMsg: string }> = ({ errorMsg }) => {
|
const FourOhFour: React.FC<{ errorMsg: string }> = ({ errorMsg }) => {
|
||||||
return (
|
return (
|
||||||
<div className="my-12">
|
<div className="my-12">
|
||||||
<div className="w-1/3 mx-auto">
|
<div className="w-1/3 mx-auto">
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import { FunctionComponent } from 'react'
|
const Loading: React.FC<{ loadingText: string }> = ({ loadingText }) => {
|
||||||
|
|
||||||
const Loading: FunctionComponent<{ loadingText: string }> = ({ loadingText }) => {
|
|
||||||
return (
|
return (
|
||||||
<div className="dark:text-white flex items-center justify-center py-32 space-x-1 rounded">
|
<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" />
|
<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
|
// 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 (
|
return (
|
||||||
<svg className={className} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
<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" />
|
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { FunctionComponent, useState } from 'react'
|
import { FC, useState } from 'react'
|
||||||
import ReactPlayer from 'react-player'
|
import ReactPlayer from 'react-player'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
|
|
||||||
import DownloadButtonGroup from '../DownloadBtnGtoup'
|
import DownloadButtonGroup from '../DownloadBtnGtoup'
|
||||||
|
import { DownloadBtnContainer, PreviewContainer } from './Containers'
|
||||||
|
|
||||||
enum PlayerState {
|
enum PlayerState {
|
||||||
Loading,
|
Loading,
|
||||||
|
@ -11,12 +12,12 @@ enum PlayerState {
|
||||||
Paused,
|
Paused,
|
||||||
}
|
}
|
||||||
|
|
||||||
const AudioPreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
const AudioPreview: FC<{ file: any }> = ({ file }) => {
|
||||||
const [playerStatus, setPlayerStatus] = useState(PlayerState.Loading)
|
const [playerStatus, setPlayerStatus] = useState(PlayerState.Loading)
|
||||||
|
|
||||||
return (
|
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="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">
|
<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 ? (
|
{playerStatus === PlayerState.Loading ? (
|
||||||
|
@ -72,11 +73,11 @@ const AudioPreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</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']} />
|
<DownloadButtonGroup downloadUrl={file['@microsoft.graph.downloadUrl']} />
|
||||||
</div>
|
</DownloadBtnContainer>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,46 +1,47 @@
|
||||||
import { useEffect, FunctionComponent } from 'react'
|
import { useEffect, FC } from 'react'
|
||||||
import Prism from 'prismjs'
|
import Prism from 'prismjs'
|
||||||
|
|
||||||
import { getExtension } from '../../utils/getFileIcon'
|
import { getExtension } from '../../utils/getFileIcon'
|
||||||
import { useStaleSWR } from '../../utils/fetchWithSWR'
|
import useFileContent from '../../utils/fetchOnMount'
|
||||||
import FourOhFour from '../FourOhFour'
|
import FourOhFour from '../FourOhFour'
|
||||||
import Loading from '../Loading'
|
import Loading from '../Loading'
|
||||||
import DownloadButtonGroup from '../DownloadBtnGtoup'
|
import DownloadButtonGroup from '../DownloadBtnGtoup'
|
||||||
|
import { DownloadBtnContainer, PreviewContainer } from './Containers'
|
||||||
|
|
||||||
const CodePreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
const CodePreview: FC<{ file: any }> = ({ file }) => {
|
||||||
const { data, error } = useStaleSWR({ url: file['@microsoft.graph.downloadUrl'] })
|
const { content, error, validating } = useFileContent(file['@microsoft.graph.downloadUrl'])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
Prism.highlightAll()
|
Prism.highlightAll()
|
||||||
}
|
}
|
||||||
}, [data])
|
}, [validating])
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<div className="dark:bg-gray-900 p-3 bg-white rounded">
|
<PreviewContainer>
|
||||||
<FourOhFour errorMsg={error.message} />
|
<FourOhFour errorMsg={error} />
|
||||||
</div>
|
</PreviewContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (!data) {
|
if (validating) {
|
||||||
return (
|
return (
|
||||||
<div className="dark:bg-gray-900 p-3 bg-white rounded">
|
<PreviewContainer>
|
||||||
<Loading loadingText="Loading file content..." />
|
<Loading loadingText="Loading file content..." />
|
||||||
</div>
|
</PreviewContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="markdown-body p-3 bg-gray-900 rounded">
|
<PreviewContainer>
|
||||||
<pre className={`language-${getExtension(file.name)}`}>
|
<pre className={`language-${getExtension(file.name)}`}>
|
||||||
<code>{data}</code>
|
<code className="font-mono">{content}</code>
|
||||||
</pre>
|
</pre>
|
||||||
</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']} />
|
<DownloadButtonGroup downloadUrl={file['@microsoft.graph.downloadUrl']} />
|
||||||
</div>
|
</DownloadBtnContainer>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
11
components/previews/Containers.tsx
Normal file
11
components/previews/Containers.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
|
@ -4,8 +4,9 @@ import type { Rendition } from 'epubjs'
|
||||||
|
|
||||||
import Loading from '../Loading'
|
import Loading from '../Loading'
|
||||||
import DownloadButtonGroup from '../DownloadBtnGtoup'
|
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 [epubContainerWidth, setEpubContainerWidth] = useState(400)
|
||||||
const epubContainer = useRef<HTMLDivElement>(null)
|
const epubContainer = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
@ -41,7 +42,7 @@ const EPUBPreview: FunctionComponent<{file: any}> = ({ file }) => {
|
||||||
<div style={{ position: 'absolute', width: epubContainerWidth, height: '70vh' }}>
|
<div style={{ position: 'absolute', width: epubContainerWidth, height: '70vh' }}>
|
||||||
<ReactReader
|
<ReactReader
|
||||||
url={file['@microsoft.graph.downloadUrl']}
|
url={file['@microsoft.graph.downloadUrl']}
|
||||||
getRendition={(rendition) => fixEpub(rendition)}
|
getRendition={rendition => fixEpub(rendition)}
|
||||||
loadingView={<Loading loadingText="Loading EPUB ..." />}
|
loadingView={<Loading loadingText="Loading EPUB ..." />}
|
||||||
location={location}
|
location={location}
|
||||||
locationChanged={onLocationChange}
|
locationChanged={onLocationChange}
|
||||||
|
@ -51,9 +52,9 @@ const EPUBPreview: FunctionComponent<{file: any}> = ({ file }) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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']} />
|
<DownloadButtonGroup downloadUrl={file['@microsoft.graph.downloadUrl']} />
|
||||||
</div>
|
</DownloadBtnContainer>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,14 +11,15 @@ import 'katex/dist/katex.min.css'
|
||||||
import FourOhFour from '../FourOhFour'
|
import FourOhFour from '../FourOhFour'
|
||||||
import Loading from '../Loading'
|
import Loading from '../Loading'
|
||||||
import DownloadButtonGroup from '../DownloadBtnGtoup'
|
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 }> = ({
|
const MarkdownPreview: FunctionComponent<{ file: any; path: string; standalone?: boolean }> = ({
|
||||||
file,
|
file,
|
||||||
path,
|
path,
|
||||||
standalone = true,
|
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
|
// The parent folder of the markdown file, which is also the relative image folder
|
||||||
const parentPath = path.substring(0, path.lastIndexOf('/'))
|
const parentPath = path.substring(0, path.lastIndexOf('/'))
|
||||||
|
@ -63,45 +64,41 @@ const MarkdownPreview: FunctionComponent<{ file: any; path: string; standalone?:
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Prism.highlightAll()
|
Prism.highlightAll()
|
||||||
}, [data])
|
}, [content])
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<div className={`${standalone ? 'bg-white dark:bg-gray-900 rounded p-3' : ''}`}>
|
<PreviewContainer>
|
||||||
<FourOhFour errorMsg={error.message} />
|
<FourOhFour errorMsg={error} />
|
||||||
</div>
|
</PreviewContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (!data) {
|
if (validating) {
|
||||||
return (
|
return (
|
||||||
<div className={standalone ? 'bg-white dark:bg-gray-900 rounded p-3' : ''}>
|
<PreviewContainer>
|
||||||
<Loading loadingText="Loading file content..." />
|
<Loading loadingText="Loading file content..." />
|
||||||
</div>
|
</PreviewContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div
|
<PreviewContainer>
|
||||||
className={
|
<div className="markdown-body">
|
||||||
standalone
|
{/* Using rehypeRaw to render HTML inside Markdown is potentially dangerous, use under safe environments. (#18) */}
|
||||||
? 'markdown-body bg-white dark:bg-gray-900 rounded p-3 dark:text-white'
|
<ReactMarkdown
|
||||||
: 'markdown-body p-3 dark:text-white'
|
remarkPlugins={[gfm, remarkMath]}
|
||||||
}
|
rehypePlugins={[rehypeKatex, rehypeRaw as any]}
|
||||||
>
|
components={relativeImagePathRenderer}
|
||||||
{/* Using rehypeRaw to render HTML inside Markdown is potentially dangerous, use under safe environments. (#18) */}
|
>
|
||||||
<ReactMarkdown
|
{content}
|
||||||
remarkPlugins={[gfm, remarkMath]}
|
</ReactMarkdown>
|
||||||
rehypePlugins={[rehypeKatex, rehypeRaw as any]}
|
|
||||||
components={relativeImagePathRenderer}
|
|
||||||
>
|
|
||||||
{data}
|
|
||||||
</ReactMarkdown>
|
|
||||||
</div>
|
|
||||||
{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">
|
|
||||||
<DownloadButtonGroup downloadUrl={file['@microsoft.graph.downloadUrl']} />
|
|
||||||
</div>
|
</div>
|
||||||
|
</PreviewContainer>
|
||||||
|
{standalone && (
|
||||||
|
<DownloadBtnContainer>
|
||||||
|
<DownloadButtonGroup downloadUrl={file['@microsoft.graph.downloadUrl']} />
|
||||||
|
</DownloadBtnContainer>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { FunctionComponent, useEffect, useRef, useState } from 'react'
|
||||||
import Preview from 'preview-office-docs'
|
import Preview from 'preview-office-docs'
|
||||||
|
|
||||||
import DownloadButtonGroup from '../DownloadBtnGtoup'
|
import DownloadButtonGroup from '../DownloadBtnGtoup'
|
||||||
|
import { DownloadBtnContainer } from './Containers'
|
||||||
|
|
||||||
const OfficePreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
const OfficePreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
||||||
const docContainer = useRef<HTMLDivElement>(null)
|
const docContainer = useRef<HTMLDivElement>(null)
|
||||||
|
@ -20,9 +21,9 @@ const OfficePreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
||||||
height="600"
|
height="600"
|
||||||
/>
|
/>
|
||||||
</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']} />
|
<DownloadButtonGroup downloadUrl={file['@microsoft.graph.downloadUrl']} />
|
||||||
</div>
|
</DownloadBtnContainer>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { FunctionComponent } from 'react'
|
import { FunctionComponent } from 'react'
|
||||||
|
|
||||||
import DownloadButtonGroup from '../DownloadBtnGtoup'
|
import DownloadButtonGroup from '../DownloadBtnGtoup'
|
||||||
|
import { DownloadBtnContainer } from './Containers'
|
||||||
|
|
||||||
const PDFEmbedPreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
const PDFEmbedPreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
||||||
// const url = `/api/proxy?url=${encodeURIComponent(file['@microsoft.graph.downloadUrl'])}&inline=true`
|
// 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' }}>
|
<div className="w-full rounded overflow-hidden" style={{ height: '90vh' }}>
|
||||||
<iframe src={url} frameBorder="0" width="100%" height="100%"></iframe>
|
<iframe src={url} frameBorder="0" width="100%" height="100%"></iframe>
|
||||||
</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']} />
|
<DownloadButtonGroup downloadUrl={file['@microsoft.graph.downloadUrl']} />
|
||||||
</div>
|
</DownloadBtnContainer>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +1,43 @@
|
||||||
import { FunctionComponent } from 'react'
|
|
||||||
|
|
||||||
import FourOhFour from '../FourOhFour'
|
import FourOhFour from '../FourOhFour'
|
||||||
import Loading from '../Loading'
|
import Loading from '../Loading'
|
||||||
import DownloadButtonGroup from '../DownloadBtnGtoup'
|
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 TextPreview = ({ file }) => {
|
||||||
const { data, error } = useStaleSWR({ url: file['@microsoft.graph.downloadUrl'] })
|
const { content, error, validating } = useFileContent(file['@microsoft.graph.downloadUrl'])
|
||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<div className="dark:bg-gray-900 p-3 bg-white rounded">
|
<PreviewContainer>
|
||||||
<FourOhFour errorMsg={error.message} />
|
<FourOhFour errorMsg={error} />
|
||||||
</div>
|
</PreviewContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (!data) {
|
|
||||||
|
if (validating) {
|
||||||
return (
|
return (
|
||||||
<div className="dark:bg-gray-900 p-3 bg-white rounded">
|
<PreviewContainer>
|
||||||
<Loading loadingText="Loading file content..." />
|
<Loading loadingText="Loading file content..." />
|
||||||
</div>
|
</PreviewContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!content) {
|
||||||
|
return (
|
||||||
|
<PreviewContainer>
|
||||||
|
<FourOhFour errorMsg="File is empty." />
|
||||||
|
</PreviewContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="dark:bg-gray-900 dark:text-gray-100 p-3 bg-white rounded">
|
<PreviewContainer>
|
||||||
<pre className="md:p-3 p-0 overflow-scroll">{data}</pre>
|
<pre className="md:p-3 p-0 overflow-x-scroll text-sm">{content}</pre>
|
||||||
</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']} />
|
<DownloadButtonGroup downloadUrl={file['@microsoft.graph.downloadUrl']} />
|
||||||
</div>
|
</DownloadBtnContainer>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import toast from 'react-hot-toast'
|
||||||
|
|
||||||
import { getBaseUrl } from '../../utils/getBaseUrl'
|
import { getBaseUrl } from '../../utils/getBaseUrl'
|
||||||
import { DownloadButton } from '../DownloadBtnGtoup'
|
import { DownloadButton } from '../DownloadBtnGtoup'
|
||||||
|
import { DownloadBtnContainer, PreviewContainer } from './Containers'
|
||||||
|
|
||||||
const VideoPreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
const VideoPreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
||||||
const { asPath } = useRouter()
|
const { asPath } = useRouter()
|
||||||
|
@ -13,7 +14,7 @@ const VideoPreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="dark:bg-gray-900 p-3 bg-white rounded">
|
<PreviewContainer>
|
||||||
<ReactPlayer
|
<ReactPlayer
|
||||||
className="aspect-video"
|
className="aspect-video"
|
||||||
url={file['@microsoft.graph.downloadUrl']}
|
url={file['@microsoft.graph.downloadUrl']}
|
||||||
|
@ -22,49 +23,51 @@ const VideoPreview: FunctionComponent<{ file: any }> = ({ file }) => {
|
||||||
height="100%"
|
height="100%"
|
||||||
config={{ file: { forceVideo: true } }}
|
config={{ file: { forceVideo: true } }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</PreviewContainer>
|
||||||
|
|
||||||
<div className="flex flex-wrap justify-center mt-4 gap-2">
|
<DownloadBtnContainer>
|
||||||
<DownloadButton
|
<div className="flex flex-wrap justify-center gap-2">
|
||||||
onClickCallback={() => window.open(file['@microsoft.graph.downloadUrl'])}
|
<DownloadButton
|
||||||
btnColor="blue"
|
onClickCallback={() => window.open(file['@microsoft.graph.downloadUrl'])}
|
||||||
btnText="Download"
|
btnColor="blue"
|
||||||
btnIcon="file-download"
|
btnText="Download"
|
||||||
/>
|
btnIcon="file-download"
|
||||||
<DownloadButton
|
/>
|
||||||
onClickCallback={() =>
|
<DownloadButton
|
||||||
window.open(`/api/proxy?url=${encodeURIComponent(file['@microsoft.graph.downloadUrl'])}`)
|
onClickCallback={() =>
|
||||||
}
|
window.open(`/api/proxy?url=${encodeURIComponent(file['@microsoft.graph.downloadUrl'])}`)
|
||||||
btnColor="teal"
|
}
|
||||||
btnText="Proxy download"
|
btnColor="teal"
|
||||||
btnIcon="download"
|
btnText="Proxy download"
|
||||||
/>
|
btnIcon="download"
|
||||||
<DownloadButton
|
/>
|
||||||
onClickCallback={() => {
|
<DownloadButton
|
||||||
clipboard.copy(`${getBaseUrl()}/api?path=${asPath}&raw=true`)
|
onClickCallback={() => {
|
||||||
toast.success('Copied direct link to clipboard.')
|
clipboard.copy(`${getBaseUrl()}/api?path=${asPath}&raw=true`)
|
||||||
}}
|
toast.success('Copied direct link to clipboard.')
|
||||||
btnColor="yellow"
|
}}
|
||||||
btnText="Copy direct link"
|
btnColor="pink"
|
||||||
btnIcon="copy"
|
btnText="Copy direct link"
|
||||||
/>
|
btnIcon="copy"
|
||||||
|
/>
|
||||||
|
|
||||||
<DownloadButton
|
<DownloadButton
|
||||||
onClickCallback={() => window.open(`iina://weblink?url=${file['@microsoft.graph.downloadUrl']}`)}
|
onClickCallback={() => window.open(`iina://weblink?url=${file['@microsoft.graph.downloadUrl']}`)}
|
||||||
btnText="IINA"
|
btnText="IINA"
|
||||||
btnImage="/players/iina.png"
|
btnImage="/players/iina.png"
|
||||||
/>
|
/>
|
||||||
<DownloadButton
|
<DownloadButton
|
||||||
onClickCallback={() => window.open(`vlc://${file['@microsoft.graph.downloadUrl']}`)}
|
onClickCallback={() => window.open(`vlc://${file['@microsoft.graph.downloadUrl']}`)}
|
||||||
btnText="VLC"
|
btnText="VLC"
|
||||||
btnImage="/players/vlc.png"
|
btnImage="/players/vlc.png"
|
||||||
/>
|
/>
|
||||||
<DownloadButton
|
<DownloadButton
|
||||||
onClickCallback={() => window.open(`potplayer://${file['@microsoft.graph.downloadUrl']}`)}
|
onClickCallback={() => window.open(`potplayer://${file['@microsoft.graph.downloadUrl']}`)}
|
||||||
btnText="PotPlayer"
|
btnText="PotPlayer"
|
||||||
btnImage="/players/potplayer.png"
|
btnImage="/players/potplayer.png"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</DownloadBtnContainer>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
7489
package-lock.json
generated
7489
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -16,7 +16,7 @@ export default function Folders() {
|
||||||
<title>{siteConfig.title}</title>
|
<title>{siteConfig.title}</title>
|
||||||
</Head>
|
</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 />
|
<Navbar />
|
||||||
<div className="w-full max-w-5xl p-4 mx-auto">
|
<div className="w-full max-w-5xl p-4 mx-auto">
|
||||||
<Breadcrumb query={query} />
|
<Breadcrumb query={query} />
|
||||||
|
|
|
@ -13,7 +13,7 @@ export default function Home() {
|
||||||
<title>{siteConfig.title}</title>
|
<title>{siteConfig.title}</title>
|
||||||
</Head>
|
</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 />
|
<Navbar />
|
||||||
<div className="w-full max-w-5xl p-4 mx-auto">
|
<div className="w-full max-w-5xl p-4 mx-auto">
|
||||||
<Breadcrumb />
|
<Breadcrumb />
|
||||||
|
|
|
@ -24,3 +24,7 @@
|
||||||
.markdown-body ol {
|
.markdown-body ol {
|
||||||
@apply list-decimal;
|
@apply list-decimal;
|
||||||
}
|
}
|
||||||
|
pre[class*='language-'],
|
||||||
|
code[class*='language-'] {
|
||||||
|
@apply font-mono !important;
|
||||||
|
}
|
||||||
|
|
20
utils/fetchOnMount.ts
Normal file
20
utils/fetchOnMount.ts
Normal 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 }
|
||||||
|
}
|
Loading…
Reference in a new issue