2021-06-23 20:27:51 +00:00
|
|
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
2021-06-25 17:47:57 +00:00
|
|
|
import toast, { Toaster } from 'react-hot-toast'
|
2021-06-30 12:09:37 +00:00
|
|
|
import emojiRegex from 'emoji-regex'
|
2021-08-23 14:02:06 +00:00
|
|
|
import { useClipboard } from 'use-clipboard-copy'
|
2021-06-23 20:27:51 +00:00
|
|
|
|
|
|
|
import { ParsedUrlQuery } from 'querystring'
|
2022-01-06 11:25:10 +00:00
|
|
|
import { FC, MouseEventHandler, SetStateAction, useEffect, useRef, useState } from 'react'
|
2021-06-23 22:51:23 +00:00
|
|
|
|
|
|
|
import { useRouter } from 'next/router'
|
|
|
|
import dynamic from 'next/dynamic'
|
|
|
|
|
2022-01-16 12:17:42 +00:00
|
|
|
import { humanFileSize, formatModifiedDateTime } from '../utils/fileDetails'
|
2022-01-24 07:32:36 +00:00
|
|
|
import { getExtension, getFileIcon } from '../utils/getFileIcon'
|
|
|
|
import { getPreviewType, preview } from '../utils/getPreviewType'
|
2021-12-17 13:21:25 +00:00
|
|
|
import { useProtectedSWRInfinite } from '../utils/fetchWithSWR'
|
|
|
|
import { getBaseUrl } from '../utils/getBaseUrl'
|
2021-12-17 15:16:18 +00:00
|
|
|
import {
|
|
|
|
DownloadingToast,
|
|
|
|
downloadMultipleFiles,
|
|
|
|
downloadTreelikeMultipleFiles,
|
|
|
|
traverseFolder,
|
|
|
|
} from './MultiFileDownloader'
|
2021-09-05 15:03:27 +00:00
|
|
|
|
2021-11-28 11:24:59 +00:00
|
|
|
import Loading, { LoadingIcon } from './Loading'
|
2021-06-25 14:15:00 +00:00
|
|
|
import FourOhFour from './FourOhFour'
|
2021-08-30 00:35:52 +00:00
|
|
|
import Auth from './Auth'
|
2021-06-25 14:15:00 +00:00
|
|
|
import TextPreview from './previews/TextPreview'
|
2021-06-25 15:08:04 +00:00
|
|
|
import MarkdownPreview from './previews/MarkdownPreview'
|
2021-06-25 15:13:31 +00:00
|
|
|
import CodePreview from './previews/CodePreview'
|
2021-06-29 15:20:35 +00:00
|
|
|
import OfficePreview from './previews/OfficePreview'
|
2021-12-17 15:16:18 +00:00
|
|
|
import AudioPreview from './previews/AudioPreview'
|
|
|
|
import VideoPreview from './previews/VideoPreview'
|
2021-12-28 18:13:47 +00:00
|
|
|
import PDFPreview from './previews/PDFPreview'
|
2022-01-08 07:27:39 +00:00
|
|
|
import URLPreview from './previews/URLPreview'
|
2022-01-16 12:17:42 +00:00
|
|
|
import DefaultPreview from './previews/DefaultPreview'
|
2022-01-21 14:12:07 +00:00
|
|
|
import { DownloadBtnContainer, PreviewContainer } from './previews/Containers'
|
|
|
|
import DownloadButtonGroup from './DownloadBtnGtoup'
|
2021-06-22 13:55:53 +00:00
|
|
|
|
2022-02-02 08:41:30 +00:00
|
|
|
import type { OdFileObject, OdFolderObject } from '../types'
|
2022-02-05 07:46:11 +00:00
|
|
|
import Link from 'next/link'
|
2022-01-26 07:32:20 +00:00
|
|
|
|
2021-06-24 23:42:46 +00:00
|
|
|
// Disabling SSR for some previews (image gallery view, and PDF view)
|
2022-02-04 08:05:28 +00:00
|
|
|
const EPUBPreview = dynamic(() => import('./previews/EPUBPreview'), {
|
|
|
|
ssr: false,
|
|
|
|
})
|
2021-06-23 20:27:51 +00:00
|
|
|
|
2021-06-24 13:54:59 +00:00
|
|
|
/**
|
|
|
|
* Convert url query into path string
|
|
|
|
*
|
|
|
|
* @param query Url query property
|
|
|
|
* @returns Path string
|
|
|
|
*/
|
2021-06-23 20:27:51 +00:00
|
|
|
const queryToPath = (query?: ParsedUrlQuery) => {
|
|
|
|
if (query) {
|
|
|
|
const { path } = query
|
|
|
|
if (!path) return '/'
|
2021-08-15 19:38:51 +00:00
|
|
|
if (typeof path === 'string') return `/${encodeURIComponent(path)}`
|
|
|
|
return `/${path.map(p => encodeURIComponent(p)).join('/')}`
|
2021-06-22 23:15:19 +00:00
|
|
|
}
|
2021-06-23 20:27:51 +00:00
|
|
|
return '/'
|
2021-06-22 23:15:19 +00:00
|
|
|
}
|
2021-06-22 13:55:53 +00:00
|
|
|
|
2022-01-26 07:32:20 +00:00
|
|
|
const FileListItem: FC<{ fileContent: OdFolderObject['value'][number] }> = ({ fileContent: c }) => {
|
2021-06-30 12:09:37 +00:00
|
|
|
const emojiIcon = emojiRegex().exec(c.name)
|
|
|
|
const renderEmoji = emojiIcon && !emojiIcon.index
|
|
|
|
|
2021-06-23 22:51:23 +00:00
|
|
|
return (
|
2022-02-04 09:22:40 +00:00
|
|
|
<div className="grid cursor-pointer grid-cols-10 items-center space-x-2 px-3 py-2.5">
|
|
|
|
<div className="col-span-10 flex items-center space-x-2 truncate md:col-span-6" title={c.name}>
|
2021-06-23 22:51:23 +00:00
|
|
|
{/* <div>{c.file ? c.file.mimeType : 'folder'}</div> */}
|
2022-02-04 09:22:40 +00:00
|
|
|
<div className="w-5 flex-shrink-0 text-center">
|
2021-06-30 12:09:37 +00:00
|
|
|
{renderEmoji ? (
|
|
|
|
<span>{emojiIcon ? emojiIcon[0] : '📁'}</span>
|
|
|
|
) : (
|
2022-01-24 08:03:35 +00:00
|
|
|
<FontAwesomeIcon icon={c.file ? getFileIcon(c.name, { video: Boolean(c.video) }) : ['far', 'folder']} />
|
2021-06-30 12:09:37 +00:00
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
<div className="truncate">
|
|
|
|
{renderEmoji ? c.name.replace(emojiIcon ? emojiIcon[0] : '', '').trim() : c.name}
|
2021-06-23 22:51:23 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
2022-02-04 09:22:40 +00:00
|
|
|
<div className="col-span-3 hidden flex-shrink-0 font-mono text-sm text-gray-700 dark:text-gray-500 md:block">
|
2022-01-16 12:17:42 +00:00
|
|
|
{formatModifiedDateTime(c.lastModifiedDateTime)}
|
2021-06-23 22:51:23 +00:00
|
|
|
</div>
|
2022-02-04 09:22:40 +00:00
|
|
|
<div className="col-span-1 hidden flex-shrink-0 truncate font-mono text-sm text-gray-700 dark:text-gray-500 md:block">
|
2021-08-23 15:14:08 +00:00
|
|
|
{humanFileSize(c.size)}
|
|
|
|
</div>
|
2021-06-23 22:51:23 +00:00
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-01-06 11:25:10 +00:00
|
|
|
const Checkbox: FC<{
|
2021-12-15 09:02:06 +00:00
|
|
|
checked: 0 | 1 | 2
|
|
|
|
onChange: () => void
|
|
|
|
title: string
|
|
|
|
indeterminate?: boolean
|
2021-11-18 22:36:23 +00:00
|
|
|
}> = ({ checked, onChange, title, indeterminate }) => {
|
|
|
|
const ref = useRef<HTMLInputElement>(null)
|
2021-12-15 09:02:06 +00:00
|
|
|
|
2021-11-18 22:36:23 +00:00
|
|
|
useEffect(() => {
|
|
|
|
if (ref.current) {
|
|
|
|
ref.current.checked = Boolean(checked)
|
|
|
|
if (indeterminate) {
|
|
|
|
ref.current.indeterminate = checked == 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, [ref, checked, indeterminate])
|
2021-12-15 09:02:06 +00:00
|
|
|
|
|
|
|
const handleClick: MouseEventHandler = e => {
|
2021-11-27 11:04:21 +00:00
|
|
|
if (ref.current) {
|
|
|
|
if (e.target === ref.current) {
|
|
|
|
e.stopPropagation()
|
|
|
|
} else {
|
|
|
|
ref.current.click()
|
|
|
|
}
|
|
|
|
}
|
2021-11-27 08:22:44 +00:00
|
|
|
}
|
2021-11-18 22:36:23 +00:00
|
|
|
|
|
|
|
return (
|
|
|
|
<span
|
|
|
|
title={title}
|
2022-02-04 09:22:40 +00:00
|
|
|
className="inline-flex cursor-pointer items-center rounded p-1.5 hover:bg-gray-300 dark:hover:bg-gray-600"
|
2021-11-18 22:36:23 +00:00
|
|
|
onClick={handleClick}
|
|
|
|
>
|
|
|
|
<input
|
|
|
|
className="form-check-input cursor-pointer"
|
|
|
|
type="checkbox"
|
|
|
|
value={checked ? '1' : ''}
|
|
|
|
ref={ref}
|
|
|
|
aria-label={title}
|
|
|
|
onChange={onChange}
|
|
|
|
/>
|
|
|
|
</span>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-01-06 11:25:10 +00:00
|
|
|
const Downloading: FC<{ title: string }> = ({ title }) => {
|
2021-11-28 11:24:59 +00:00
|
|
|
return (
|
2022-02-04 09:22:40 +00:00
|
|
|
<span title={title} className="rounded p-2" role="status">
|
2021-11-28 11:24:59 +00:00
|
|
|
<LoadingIcon
|
|
|
|
// Use fontawesome far theme via class `svg-inline--fa` to get style `vertical-align` only
|
|
|
|
// for consistent icon alignment, as class `align-*` cannot satisfy it
|
2022-02-04 09:22:40 +00:00
|
|
|
className="svg-inline--fa inline-block h-4 w-4 animate-spin"
|
2021-11-28 11:24:59 +00:00
|
|
|
/>
|
|
|
|
</span>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-01-06 11:25:10 +00:00
|
|
|
const FileListing: FC<{ query?: ParsedUrlQuery }> = ({ query }) => {
|
2021-06-23 22:51:23 +00:00
|
|
|
const [imageViewerVisible, setImageViewerVisibility] = useState(false)
|
|
|
|
const [activeImageIdx, setActiveImageIdx] = useState(0)
|
2021-11-27 08:22:44 +00:00
|
|
|
const [selected, setSelected] = useState<{ [key: string]: boolean }>({})
|
|
|
|
const [totalSelected, setTotalSelected] = useState<0 | 1 | 2>(0)
|
2021-11-19 08:21:42 +00:00
|
|
|
const [totalGenerating, setTotalGenerating] = useState<boolean>(false)
|
2022-02-04 08:05:28 +00:00
|
|
|
const [folderGenerating, setFolderGenerating] = useState<{
|
|
|
|
[key: string]: boolean
|
|
|
|
}>({})
|
2021-06-23 22:51:23 +00:00
|
|
|
|
|
|
|
const router = useRouter()
|
2021-08-23 14:02:06 +00:00
|
|
|
const clipboard = useClipboard()
|
2021-06-23 22:51:23 +00:00
|
|
|
|
2021-06-23 20:27:51 +00:00
|
|
|
const path = queryToPath(query)
|
2021-06-24 21:32:22 +00:00
|
|
|
|
2021-09-05 15:03:27 +00:00
|
|
|
const { data, error, size, setSize } = useProtectedSWRInfinite(path)
|
2021-06-24 21:32:22 +00:00
|
|
|
|
2021-06-25 14:15:00 +00:00
|
|
|
if (error) {
|
2021-12-30 19:44:03 +00:00
|
|
|
console.log(error)
|
|
|
|
|
|
|
|
// If error includes 403 which means the user has not completed initial setup, redirect to OAuth page
|
2022-01-10 08:36:45 +00:00
|
|
|
if (error.status === 403) {
|
2021-12-30 19:44:03 +00:00
|
|
|
router.push('/onedrive-vercel-index-oauth/step-1')
|
|
|
|
return <div></div>
|
|
|
|
}
|
|
|
|
|
2021-06-25 14:15:00 +00:00
|
|
|
return (
|
2022-01-06 08:34:16 +00:00
|
|
|
<PreviewContainer>
|
2022-01-10 08:36:45 +00:00
|
|
|
{error.status === 401 ? <Auth redirect={path} /> : <FourOhFour errorMsg={JSON.stringify(error.message)} />}
|
2022-01-06 08:34:16 +00:00
|
|
|
</PreviewContainer>
|
2021-06-25 14:15:00 +00:00
|
|
|
)
|
|
|
|
}
|
2021-06-23 22:51:23 +00:00
|
|
|
if (!data) {
|
2021-06-22 13:55:53 +00:00
|
|
|
return (
|
2022-01-06 08:34:16 +00:00
|
|
|
<PreviewContainer>
|
2021-06-24 23:42:46 +00:00
|
|
|
<Loading loadingText="Loading ..." />
|
2022-01-06 08:34:16 +00:00
|
|
|
</PreviewContainer>
|
2021-06-22 13:55:53 +00:00
|
|
|
)
|
2021-06-23 22:51:23 +00:00
|
|
|
}
|
2021-06-22 13:55:53 +00:00
|
|
|
|
2021-06-23 22:51:23 +00:00
|
|
|
const fileIsImage = (fileName: string) => {
|
|
|
|
const fileExtension = getExtension(fileName)
|
2022-01-24 07:32:36 +00:00
|
|
|
if (getPreviewType(fileExtension) === preview.image) {
|
|
|
|
return true
|
2021-06-23 22:51:23 +00:00
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2021-09-05 15:03:27 +00:00
|
|
|
const responses: any[] = data ? [].concat(...data) : []
|
|
|
|
|
|
|
|
const isLoadingInitialData = !data && !error
|
|
|
|
const isLoadingMore = isLoadingInitialData || (size > 0 && data && typeof data[size - 1] === 'undefined')
|
|
|
|
const isEmpty = data?.[0]?.length === 0
|
|
|
|
const isReachingEnd = isEmpty || (data && typeof data[data.length - 1]?.next === 'undefined')
|
|
|
|
const onlyOnePage = data && typeof data[0].next === 'undefined'
|
2021-06-30 11:53:17 +00:00
|
|
|
|
2021-09-05 15:03:27 +00:00
|
|
|
if ('folder' in responses[0]) {
|
|
|
|
// Expand list of API returns into flattened file data
|
2022-01-26 07:32:20 +00:00
|
|
|
const children = [].concat(...responses.map(r => r.folder.value)) as OdFolderObject['value']
|
2021-09-05 15:03:27 +00:00
|
|
|
|
2022-02-05 07:46:11 +00:00
|
|
|
// Find README.md file to render
|
|
|
|
const readmeFile = children.find(c => c.name.toLowerCase() === 'readme.md')
|
2021-06-23 22:51:23 +00:00
|
|
|
|
2021-11-19 13:31:07 +00:00
|
|
|
// Filtered file list helper
|
2022-01-26 07:32:20 +00:00
|
|
|
const getFiles = () => children.filter(c => !c.folder && c.name !== '.password')
|
2021-11-19 13:31:07 +00:00
|
|
|
|
2021-11-18 22:36:23 +00:00
|
|
|
// File selection
|
2021-11-27 08:22:44 +00:00
|
|
|
const genTotalSelected = (selected: { [key: string]: boolean }) => {
|
2022-01-26 07:32:20 +00:00
|
|
|
const selectInfo = getFiles().map(c => Boolean(selected[c.id]))
|
2021-11-18 22:36:23 +00:00
|
|
|
const [hasT, hasF] = [selectInfo.some(i => i), selectInfo.some(i => !i)]
|
2021-12-15 09:02:06 +00:00
|
|
|
return hasT && hasF ? 1 : !hasF ? 2 : 0
|
2021-11-18 22:36:23 +00:00
|
|
|
}
|
2021-12-15 09:02:06 +00:00
|
|
|
|
2021-11-18 22:36:23 +00:00
|
|
|
const toggleItemSelected = (id: string) => {
|
2021-12-15 09:02:06 +00:00
|
|
|
let val: SetStateAction<{ [key: string]: boolean }>
|
2021-11-18 22:36:23 +00:00
|
|
|
if (selected[id]) {
|
2021-11-27 08:22:44 +00:00
|
|
|
val = { ...selected }
|
2021-11-18 22:36:23 +00:00
|
|
|
delete val[id]
|
|
|
|
} else {
|
2021-11-27 08:22:44 +00:00
|
|
|
val = { ...selected, [id]: true }
|
2021-11-18 22:36:23 +00:00
|
|
|
}
|
|
|
|
setSelected(val)
|
|
|
|
setTotalSelected(genTotalSelected(val))
|
|
|
|
}
|
2021-12-15 09:02:06 +00:00
|
|
|
|
2021-11-18 22:36:23 +00:00
|
|
|
const toggleTotalSelected = () => {
|
|
|
|
if (genTotalSelected(selected) == 2) {
|
|
|
|
setSelected({})
|
|
|
|
setTotalSelected(0)
|
|
|
|
} else {
|
2022-01-26 07:32:20 +00:00
|
|
|
setSelected(Object.fromEntries(getFiles().map(c => [c.id, true])))
|
2021-11-18 22:36:23 +00:00
|
|
|
setTotalSelected(2)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-18 23:28:05 +00:00
|
|
|
// Selected file download
|
2021-11-27 08:26:22 +00:00
|
|
|
const handleSelectedDownload = () => {
|
2021-12-15 09:02:06 +00:00
|
|
|
const folderName = path.substring(path.lastIndexOf('/') + 1)
|
|
|
|
const folder = folderName ? decodeURIComponent(folderName) : undefined
|
2021-11-19 13:31:07 +00:00
|
|
|
const files = getFiles()
|
2022-01-26 07:32:20 +00:00
|
|
|
.filter(c => selected[c.id])
|
|
|
|
.map(c => ({ name: c.name, url: c['@microsoft.graph.downloadUrl'] }))
|
2021-12-15 09:02:06 +00:00
|
|
|
|
2021-11-19 08:38:00 +00:00
|
|
|
if (files.length == 1) {
|
|
|
|
const el = document.createElement('a')
|
|
|
|
el.style.display = 'none'
|
|
|
|
document.body.appendChild(el)
|
|
|
|
el.href = files[0].url
|
|
|
|
el.click()
|
|
|
|
el.remove()
|
|
|
|
} else if (files.length > 1) {
|
|
|
|
setTotalGenerating(true)
|
2021-12-17 15:16:18 +00:00
|
|
|
|
|
|
|
const toastId = toast.loading(DownloadingToast(router))
|
|
|
|
downloadMultipleFiles({ toastId, router, files, folder })
|
2021-12-15 09:02:06 +00:00
|
|
|
.then(() => {
|
|
|
|
setTotalGenerating(false)
|
2022-02-04 08:05:28 +00:00
|
|
|
toast.success('Finished downloading selected files.', {
|
|
|
|
id: toastId,
|
|
|
|
})
|
2021-12-15 09:02:06 +00:00
|
|
|
})
|
|
|
|
.catch(() => {
|
|
|
|
setTotalGenerating(false)
|
2021-12-17 15:16:18 +00:00
|
|
|
toast.error('Failed to download selected files.', { id: toastId })
|
2021-12-15 09:02:06 +00:00
|
|
|
})
|
2021-11-19 08:38:00 +00:00
|
|
|
}
|
2021-11-18 23:28:05 +00:00
|
|
|
}
|
|
|
|
|
2021-11-22 18:13:11 +00:00
|
|
|
// Folder recursive download
|
2021-11-28 11:42:16 +00:00
|
|
|
const handleFolderDownload = (path: string, id: string, name?: string) => () => {
|
2021-11-27 10:15:52 +00:00
|
|
|
const files = (async function* () {
|
2022-01-24 14:00:08 +00:00
|
|
|
for await (const { meta: c, path: p, isFolder, error } of traverseFolder(path)) {
|
|
|
|
if (error) {
|
2022-01-24 15:16:10 +00:00
|
|
|
toast.error(`Failed to download folder ${p}: ${error.status} ${error.message} Skipped it to continue.`)
|
2022-01-24 14:00:08 +00:00
|
|
|
continue
|
|
|
|
}
|
2021-11-27 10:15:52 +00:00
|
|
|
yield {
|
|
|
|
name: c?.name,
|
|
|
|
url: c ? c['@microsoft.graph.downloadUrl'] : undefined,
|
|
|
|
path: p,
|
|
|
|
isFolder,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})()
|
2021-12-17 05:57:56 +00:00
|
|
|
|
2021-11-28 13:25:38 +00:00
|
|
|
setFolderGenerating({ ...folderGenerating, [id]: true })
|
2021-12-17 15:16:18 +00:00
|
|
|
const toastId = toast.loading(DownloadingToast(router))
|
2021-12-17 05:57:56 +00:00
|
|
|
|
2022-02-04 08:05:28 +00:00
|
|
|
downloadTreelikeMultipleFiles({
|
|
|
|
toastId,
|
|
|
|
router,
|
|
|
|
files,
|
|
|
|
basePath: path,
|
|
|
|
folder: name,
|
|
|
|
})
|
2021-12-17 05:57:56 +00:00
|
|
|
.then(() => {
|
|
|
|
setFolderGenerating({ ...folderGenerating, [id]: false })
|
2021-12-17 15:16:18 +00:00
|
|
|
toast.success('Finished downloading folder.', { id: toastId })
|
2021-12-17 05:57:56 +00:00
|
|
|
})
|
|
|
|
.catch(() => {
|
|
|
|
setFolderGenerating({ ...folderGenerating, [id]: false })
|
2021-12-17 15:16:18 +00:00
|
|
|
toast.error('Failed to download folder.', { id: toastId })
|
2021-12-17 05:57:56 +00:00
|
|
|
})
|
2021-11-22 18:13:11 +00:00
|
|
|
}
|
|
|
|
|
2021-06-23 22:51:23 +00:00
|
|
|
return (
|
2022-01-06 08:34:16 +00:00
|
|
|
<>
|
2022-02-05 07:46:11 +00:00
|
|
|
<Toaster />
|
|
|
|
|
2022-02-04 09:22:40 +00:00
|
|
|
<div className="rounded bg-white dark:bg-gray-900 dark:text-gray-100">
|
|
|
|
<div className="grid grid-cols-12 items-center space-x-2 border-b border-gray-900/10 px-3 dark:border-gray-500/30">
|
|
|
|
<div className="col-span-12 py-2 text-xs font-bold uppercase tracking-widest text-gray-600 dark:text-gray-300 md:col-span-6">
|
2022-01-06 13:10:16 +00:00
|
|
|
Name
|
|
|
|
</div>
|
2022-02-04 09:22:40 +00:00
|
|
|
<div className="col-span-3 hidden text-xs font-bold uppercase tracking-widest text-gray-600 dark:text-gray-300 md:block">
|
2022-01-06 13:10:16 +00:00
|
|
|
Last Modified
|
|
|
|
</div>
|
2022-02-04 09:22:40 +00:00
|
|
|
<div className="hidden text-xs font-bold uppercase tracking-widest text-gray-600 dark:text-gray-300 md:block">
|
2022-01-08 07:10:36 +00:00
|
|
|
Size
|
|
|
|
</div>
|
2022-02-04 09:22:40 +00:00
|
|
|
<div className="hidden text-xs font-bold uppercase tracking-widest text-gray-600 dark:text-gray-300 md:block">
|
2022-01-08 07:10:36 +00:00
|
|
|
Actions
|
|
|
|
</div>
|
2022-02-04 09:22:40 +00:00
|
|
|
<div className="hidden text-xs font-bold uppercase tracking-widest text-gray-600 dark:text-gray-300 md:block">
|
|
|
|
<div className="hidden p-1.5 text-gray-700 dark:text-gray-400 md:flex">
|
2022-01-06 08:34:16 +00:00
|
|
|
<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"
|
2022-02-04 09:22:40 +00:00
|
|
|
className="cursor-pointer rounded p-1.5 hover:bg-gray-300 disabled:cursor-not-allowed disabled:text-gray-400 disabled:hover:bg-white dark:hover:bg-gray-600 disabled:dark:text-gray-600 disabled:hover:dark:bg-gray-900"
|
2022-01-06 08:34:16 +00:00
|
|
|
disabled={totalSelected === 0}
|
|
|
|
onClick={handleSelectedDownload}
|
|
|
|
>
|
2022-01-06 13:10:16 +00:00
|
|
|
<FontAwesomeIcon icon={['far', 'arrow-alt-circle-down']} size="lg" />
|
2022-01-06 08:34:16 +00:00
|
|
|
</button>
|
|
|
|
)}
|
|
|
|
</div>
|
2021-11-18 20:13:19 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
2021-08-29 21:31:42 +00:00
|
|
|
|
2022-01-26 07:32:20 +00:00
|
|
|
{children.map(c => (
|
2022-02-04 09:22:40 +00:00
|
|
|
<div className="grid grid-cols-12 hover:bg-gray-100 dark:hover:bg-gray-850" key={c.id}>
|
2022-02-05 07:46:11 +00:00
|
|
|
<Link href={`${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`} passHref>
|
|
|
|
<a className="col-span-10">
|
|
|
|
<FileListItem fileContent={c} />
|
|
|
|
</a>
|
|
|
|
</Link>
|
|
|
|
|
2022-01-06 08:34:16 +00:00
|
|
|
{c.folder ? (
|
2022-02-04 09:22:40 +00:00
|
|
|
<div className="hidden p-1.5 text-gray-700 dark:text-gray-400 md:flex">
|
2021-11-28 11:42:16 +00:00
|
|
|
<span
|
2022-01-06 08:34:16 +00:00
|
|
|
title="Copy folder permalink"
|
2022-02-04 09:22:40 +00:00
|
|
|
className="cursor-pointer rounded px-1.5 py-1 hover:bg-gray-300 dark:hover:bg-gray-600"
|
2021-11-28 11:42:16 +00:00
|
|
|
onClick={() => {
|
2022-01-06 08:34:16 +00:00
|
|
|
clipboard.copy(`${getBaseUrl()}${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`)
|
|
|
|
toast('Copied folder permalink.', { icon: '👌' })
|
2021-11-28 11:42:16 +00:00
|
|
|
}}
|
|
|
|
>
|
2022-01-06 08:34:16 +00:00
|
|
|
<FontAwesomeIcon icon={['far', 'copy']} />
|
2021-11-28 11:42:16 +00:00
|
|
|
</span>
|
2022-01-06 08:34:16 +00:00
|
|
|
{folderGenerating[c.id] ? (
|
|
|
|
<Downloading title="Downloading folder, refresh page to cancel" />
|
|
|
|
) : (
|
|
|
|
<span
|
|
|
|
title="Download folder"
|
2022-02-04 09:22:40 +00:00
|
|
|
className="cursor-pointer rounded px-1.5 py-1 hover:bg-gray-300 dark:hover:bg-gray-600"
|
2022-01-06 08:34:16 +00:00
|
|
|
onClick={() => {
|
|
|
|
const p = `${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`
|
|
|
|
handleFolderDownload(p, c.id, c.name)()
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<FontAwesomeIcon icon={['far', 'arrow-alt-circle-down']} />
|
|
|
|
</span>
|
|
|
|
)}
|
|
|
|
</div>
|
2021-12-15 09:02:06 +00:00
|
|
|
) : (
|
2022-02-04 09:22:40 +00:00
|
|
|
<div className="hidden p-1.5 text-gray-700 dark:text-gray-400 md:flex">
|
2022-01-06 08:34:16 +00:00
|
|
|
<span
|
|
|
|
title="Copy raw file permalink"
|
2022-02-04 09:22:40 +00:00
|
|
|
className="cursor-pointer rounded px-1.5 py-1 hover:bg-gray-300 dark:hover:bg-gray-600"
|
2022-01-06 08:34:16 +00:00
|
|
|
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"
|
2022-02-04 09:22:40 +00:00
|
|
|
className="cursor-pointer rounded px-1.5 py-1 hover:bg-gray-300 dark:hover:bg-gray-600"
|
2022-01-06 08:34:16 +00:00
|
|
|
href={c['@microsoft.graph.downloadUrl']}
|
|
|
|
>
|
|
|
|
<FontAwesomeIcon icon={['far', 'arrow-alt-circle-down']} />
|
|
|
|
</a>
|
|
|
|
</div>
|
2021-11-18 23:51:28 +00:00
|
|
|
)}
|
2022-02-04 09:22:40 +00:00
|
|
|
<div className="hidden p-1.5 text-gray-700 dark:text-gray-400 md:flex">
|
2022-01-08 07:10:36 +00:00
|
|
|
{!c.folder && !(c.name === '.password') && (
|
2022-01-06 08:34:16 +00:00
|
|
|
<Checkbox
|
|
|
|
checked={selected[c.id] ? 2 : 0}
|
|
|
|
onChange={() => toggleItemSelected(c.id)}
|
|
|
|
title="Select file"
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
</div>
|
2021-11-18 20:13:19 +00:00
|
|
|
</div>
|
2022-01-06 08:34:16 +00:00
|
|
|
))}
|
2021-06-30 11:53:17 +00:00
|
|
|
|
2022-01-06 08:34:16 +00:00
|
|
|
{!onlyOnePage && (
|
|
|
|
<div>
|
2022-02-04 09:22:40 +00:00
|
|
|
<div className="border-b border-gray-200 p-3 text-center font-mono text-sm text-gray-400 dark:border-gray-700">
|
2022-01-06 08:34:16 +00:00
|
|
|
- showing {size} page{size > 1 ? 's' : ''} of {isLoadingMore ? '...' : children.length} files -
|
|
|
|
</div>
|
|
|
|
<button
|
2022-02-04 09:22:40 +00:00
|
|
|
className={`flex w-full items-center justify-center space-x-2 p-3 disabled:cursor-not-allowed ${
|
2022-01-06 08:34:16 +00:00
|
|
|
isLoadingMore || isReachingEnd ? 'opacity-60' : 'hover:bg-gray-100 dark:hover:bg-gray-850'
|
|
|
|
}`}
|
|
|
|
onClick={() => setSize(size + 1)}
|
|
|
|
disabled={isLoadingMore || isReachingEnd}
|
|
|
|
>
|
|
|
|
{isLoadingMore ? (
|
|
|
|
<>
|
2022-02-05 07:46:11 +00:00
|
|
|
<LoadingIcon className="inline-block h-4 w-4 animate-spin" />
|
2022-01-06 08:34:16 +00:00
|
|
|
<span>Loading ...</span>{' '}
|
|
|
|
</>
|
|
|
|
) : isReachingEnd ? (
|
|
|
|
<span>No more files</span>
|
|
|
|
) : (
|
|
|
|
<>
|
|
|
|
<span>Load more</span>
|
|
|
|
<FontAwesomeIcon icon="chevron-circle-down" />
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
</button>
|
2021-09-05 15:03:27 +00:00
|
|
|
</div>
|
2022-01-06 08:34:16 +00:00
|
|
|
)}
|
|
|
|
</div>
|
2022-02-05 07:46:11 +00:00
|
|
|
|
|
|
|
{readmeFile && (
|
2022-01-06 08:34:16 +00:00
|
|
|
<div className="mt-4">
|
2021-08-31 12:59:31 +00:00
|
|
|
<MarkdownPreview file={readmeFile} path={path} standalone={false} />
|
2021-06-30 11:53:17 +00:00
|
|
|
</div>
|
|
|
|
)}
|
2022-01-06 08:34:16 +00:00
|
|
|
</>
|
2021-06-23 22:51:23 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-09-05 15:03:27 +00:00
|
|
|
if ('file' in responses[0] && responses.length === 1) {
|
2022-01-26 07:32:20 +00:00
|
|
|
const file = responses[0].file as OdFileObject
|
2021-09-05 15:03:27 +00:00
|
|
|
const downloadUrl = file['@microsoft.graph.downloadUrl']
|
|
|
|
const fileName = file.name
|
2021-06-23 22:51:23 +00:00
|
|
|
const fileExtension = fileName.slice(((fileName.lastIndexOf('.') - 1) >>> 0) + 2).toLowerCase()
|
|
|
|
|
2022-02-04 08:05:28 +00:00
|
|
|
const previewType = getPreviewType(fileExtension, {
|
|
|
|
video: Boolean(file.video),
|
|
|
|
})
|
2022-01-24 07:32:36 +00:00
|
|
|
if (previewType) {
|
|
|
|
switch (previewType) {
|
2021-06-23 22:51:23 +00:00
|
|
|
case preview.image:
|
2021-06-25 18:22:22 +00:00
|
|
|
return (
|
2022-01-21 14:12:07 +00:00
|
|
|
<>
|
|
|
|
<PreviewContainer>
|
|
|
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
2022-02-02 09:07:33 +00:00
|
|
|
<img
|
|
|
|
className="mx-auto"
|
|
|
|
src={downloadUrl}
|
|
|
|
alt={fileName}
|
|
|
|
width={file.image?.width}
|
|
|
|
height={file.image?.height}
|
|
|
|
/>
|
2022-01-21 14:12:07 +00:00
|
|
|
</PreviewContainer>
|
|
|
|
<DownloadBtnContainer>
|
|
|
|
<DownloadButtonGroup downloadUrl={file['@microsoft.graph.downloadUrl']} />
|
|
|
|
</DownloadBtnContainer>
|
|
|
|
</>
|
2021-06-25 18:22:22 +00:00
|
|
|
)
|
2021-06-23 22:51:23 +00:00
|
|
|
|
2021-06-24 13:54:59 +00:00
|
|
|
case preview.text:
|
2021-09-05 15:03:27 +00:00
|
|
|
return <TextPreview file={file} />
|
2021-06-24 13:54:59 +00:00
|
|
|
|
|
|
|
case preview.code:
|
2021-09-05 15:03:27 +00:00
|
|
|
return <CodePreview file={file} />
|
2021-06-24 13:54:59 +00:00
|
|
|
|
|
|
|
case preview.markdown:
|
2021-09-05 15:03:27 +00:00
|
|
|
return <MarkdownPreview file={file} path={path} />
|
2021-06-24 13:54:59 +00:00
|
|
|
|
|
|
|
case preview.video:
|
2021-09-05 15:03:27 +00:00
|
|
|
return <VideoPreview file={file} />
|
2021-06-24 23:47:57 +00:00
|
|
|
|
2021-06-24 13:54:59 +00:00
|
|
|
case preview.audio:
|
2021-09-05 15:03:27 +00:00
|
|
|
return <AudioPreview file={file} />
|
2021-06-24 13:54:59 +00:00
|
|
|
|
|
|
|
case preview.pdf:
|
2021-09-05 15:03:27 +00:00
|
|
|
return <PDFPreview file={file} />
|
2021-06-24 13:54:59 +00:00
|
|
|
|
2021-06-29 15:20:35 +00:00
|
|
|
case preview.office:
|
2021-09-05 15:03:27 +00:00
|
|
|
return <OfficePreview file={file} />
|
2021-06-29 15:20:35 +00:00
|
|
|
|
2021-10-05 21:28:40 +00:00
|
|
|
case preview.epub:
|
|
|
|
return <EPUBPreview file={file} />
|
|
|
|
|
2022-01-08 07:27:39 +00:00
|
|
|
case preview.url:
|
|
|
|
return <URLPreview file={file} />
|
|
|
|
|
2021-06-23 22:51:23 +00:00
|
|
|
default:
|
2022-01-16 12:17:42 +00:00
|
|
|
return <DefaultPreview file={file} />
|
2021-06-23 22:51:23 +00:00
|
|
|
}
|
2022-01-16 12:17:42 +00:00
|
|
|
} else {
|
|
|
|
return <DefaultPreview file={file} />
|
2021-06-23 22:51:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-25 14:15:00 +00:00
|
|
|
return (
|
2022-01-06 08:34:16 +00:00
|
|
|
<PreviewContainer>
|
2021-09-05 15:03:27 +00:00
|
|
|
<FourOhFour errorMsg={`Cannot preview ${path}`} />
|
2022-01-06 08:34:16 +00:00
|
|
|
</PreviewContainer>
|
2021-06-25 14:15:00 +00:00
|
|
|
)
|
2021-06-22 13:55:53 +00:00
|
|
|
}
|
|
|
|
export default FileListing
|