Merge pull request #304 from myl7/ts
This commit is contained in:
commit
1df85f263e
|
|
@ -11,8 +11,8 @@ import { useRouter } from 'next/router'
|
|||
import dynamic from 'next/dynamic'
|
||||
|
||||
import { humanFileSize, formatModifiedDateTime } from '../utils/fileDetails'
|
||||
import { getExtension, getFileIcon, hasKey } from '../utils/getFileIcon'
|
||||
import { extensions, preview } from '../utils/getPreviewType'
|
||||
import { getExtension, getFileIcon } from '../utils/getFileIcon'
|
||||
import { getPreviewType, preview } from '../utils/getPreviewType'
|
||||
import { useProtectedSWRInfinite } from '../utils/fetchWithSWR'
|
||||
import { getBaseUrl } from '../utils/getBaseUrl'
|
||||
import {
|
||||
|
|
@ -37,6 +37,8 @@ import DefaultPreview from './previews/DefaultPreview'
|
|||
import { DownloadBtnContainer, PreviewContainer } from './previews/Containers'
|
||||
import DownloadButtonGroup from './DownloadBtnGtoup'
|
||||
|
||||
import { OdFileObject, OdFolderObject } from '../types'
|
||||
|
||||
// Disabling SSR for some previews (image gallery view, and PDF view)
|
||||
const ReactViewer = dynamic(() => import('react-viewer'), { ssr: false })
|
||||
const EPUBPreview = dynamic(() => import('./previews/EPUBPreview'), { ssr: false })
|
||||
|
|
@ -57,9 +59,7 @@ const queryToPath = (query?: ParsedUrlQuery) => {
|
|||
return '/'
|
||||
}
|
||||
|
||||
const FileListItem: FC<{
|
||||
fileContent: { id: string; name: string; size: number; file: Object; lastModifiedDateTime: string }
|
||||
}> = ({ fileContent: c }) => {
|
||||
const FileListItem: FC<{ fileContent: OdFolderObject['value'][number] }> = ({ fileContent: c }) => {
|
||||
const emojiIcon = emojiRegex().exec(c.name)
|
||||
const renderEmoji = emojiIcon && !emojiIcon.index
|
||||
|
||||
|
|
@ -71,7 +71,7 @@ const FileListItem: FC<{
|
|||
{renderEmoji ? (
|
||||
<span>{emojiIcon ? emojiIcon[0] : '📁'}</span>
|
||||
) : (
|
||||
<FontAwesomeIcon icon={c.file ? getFileIcon(c.name) : ['far', 'folder']} />
|
||||
<FontAwesomeIcon icon={c.file ? getFileIcon(c.name, { video: Boolean(c.video) }) : ['far', 'folder']} />
|
||||
)}
|
||||
</div>
|
||||
<div className="truncate">
|
||||
|
|
@ -185,10 +185,8 @@ const FileListing: FC<{ query?: ParsedUrlQuery }> = ({ query }) => {
|
|||
|
||||
const fileIsImage = (fileName: string) => {
|
||||
const fileExtension = getExtension(fileName)
|
||||
if (hasKey(extensions, fileExtension)) {
|
||||
if (extensions[fileExtension] === preview.image) {
|
||||
return true
|
||||
}
|
||||
if (getPreviewType(fileExtension) === preview.image) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
@ -209,12 +207,12 @@ const FileListing: FC<{ query?: ParsedUrlQuery }> = ({ query }) => {
|
|||
|
||||
// README rendering preparations
|
||||
let renderReadme = false
|
||||
let readmeFile = null
|
||||
let readmeFile = {}
|
||||
|
||||
// Expand list of API returns into flattened file data
|
||||
const children = [].concat(...responses.map(r => r.folder.value))
|
||||
const children = [].concat(...responses.map(r => r.folder.value)) as OdFolderObject['value']
|
||||
|
||||
children.forEach((c: any) => {
|
||||
children.forEach(c => {
|
||||
if (fileIsImage(c.name)) {
|
||||
imagesInFolder.push({
|
||||
src: c['@microsoft.graph.downloadUrl'],
|
||||
|
|
@ -232,11 +230,11 @@ const FileListing: FC<{ query?: ParsedUrlQuery }> = ({ query }) => {
|
|||
})
|
||||
|
||||
// Filtered file list helper
|
||||
const getFiles = () => children.filter((c: any) => !c.folder && c.name !== '.password')
|
||||
const getFiles = () => children.filter(c => !c.folder && c.name !== '.password')
|
||||
|
||||
// File selection
|
||||
const genTotalSelected = (selected: { [key: string]: boolean }) => {
|
||||
const selectInfo = getFiles().map((c: any) => Boolean(selected[c.id]))
|
||||
const selectInfo = getFiles().map(c => Boolean(selected[c.id]))
|
||||
const [hasT, hasF] = [selectInfo.some(i => i), selectInfo.some(i => !i)]
|
||||
return hasT && hasF ? 1 : !hasF ? 2 : 0
|
||||
}
|
||||
|
|
@ -258,7 +256,7 @@ const FileListing: FC<{ query?: ParsedUrlQuery }> = ({ query }) => {
|
|||
setSelected({})
|
||||
setTotalSelected(0)
|
||||
} else {
|
||||
setSelected(Object.fromEntries(getFiles().map((c: any) => [c.id, true])))
|
||||
setSelected(Object.fromEntries(getFiles().map(c => [c.id, true])))
|
||||
setTotalSelected(2)
|
||||
}
|
||||
}
|
||||
|
|
@ -268,8 +266,8 @@ const FileListing: FC<{ query?: ParsedUrlQuery }> = ({ query }) => {
|
|||
const folderName = path.substring(path.lastIndexOf('/') + 1)
|
||||
const folder = folderName ? decodeURIComponent(folderName) : undefined
|
||||
const files = getFiles()
|
||||
.filter((c: any) => selected[c.id])
|
||||
.map((c: any) => ({ name: c.name, url: c['@microsoft.graph.downloadUrl'] }))
|
||||
.filter(c => selected[c.id])
|
||||
.map(c => ({ name: c.name, url: c['@microsoft.graph.downloadUrl'] }))
|
||||
|
||||
if (files.length == 1) {
|
||||
const el = document.createElement('a')
|
||||
|
|
@ -404,7 +402,7 @@ const FileListing: FC<{ query?: ParsedUrlQuery }> = ({ query }) => {
|
|||
/>
|
||||
)}
|
||||
|
||||
{children.map((c: any) => (
|
||||
{children.map(c => (
|
||||
<div className="hover:bg-gray-100 dark:hover:bg-gray-850 grid grid-cols-12" key={c.id}>
|
||||
<div
|
||||
className="col-span-10"
|
||||
|
|
@ -541,13 +539,14 @@ const FileListing: FC<{ query?: ParsedUrlQuery }> = ({ query }) => {
|
|||
}
|
||||
|
||||
if ('file' in responses[0] && responses.length === 1) {
|
||||
const { file } = responses[0]
|
||||
const file = responses[0].file as OdFileObject
|
||||
const downloadUrl = file['@microsoft.graph.downloadUrl']
|
||||
const fileName = file.name
|
||||
const fileExtension = fileName.slice(((fileName.lastIndexOf('.') - 1) >>> 0) + 2).toLowerCase()
|
||||
|
||||
if (hasKey(extensions, fileExtension)) {
|
||||
switch (extensions[fileExtension]) {
|
||||
const previewType = getPreviewType(fileExtension, { video: Boolean(file.video) })
|
||||
if (previewType) {
|
||||
switch (previewType) {
|
||||
case preview.image:
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ const DefaultPreview: FC<{ file: OdFileObject }> = ({ file }) => {
|
|||
<PreviewContainer>
|
||||
<div className="md:flex px-5 py-4 md:space-x-8 items-center">
|
||||
<div className="text-center border rounded-lg px-10 py-20 border-gray-900/10 dark:border-gray-500/30">
|
||||
<FontAwesomeIcon icon={getFileIcon(file.name)} />
|
||||
<FontAwesomeIcon icon={getFileIcon(file.name, { video: Boolean(file.video) })} />
|
||||
<div className="font-medium text-sm mt-6">{file.name}</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
const { data: identityData } = await axios.get(requestUrl, {
|
||||
headers: { Authorization: `Bearer ${accessToken}` },
|
||||
params: {
|
||||
select: '@microsoft.graph.downloadUrl,name,size,id,lastModifiedDateTime,folder,file',
|
||||
select: '@microsoft.graph.downloadUrl,name,size,id,lastModifiedDateTime,folder,file,video',
|
||||
},
|
||||
})
|
||||
|
||||
|
|
@ -203,12 +203,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
headers: { Authorization: `Bearer ${accessToken}` },
|
||||
params: next
|
||||
? {
|
||||
select: '@microsoft.graph.downloadUrl,name,size,id,lastModifiedDateTime,folder,file',
|
||||
select: '@microsoft.graph.downloadUrl,name,size,id,lastModifiedDateTime,folder,file,video',
|
||||
top: siteConfig.maxItems,
|
||||
$skipToken: next,
|
||||
}
|
||||
: {
|
||||
select: '@microsoft.graph.downloadUrl,name,size,id,lastModifiedDateTime,folder,file',
|
||||
select: '@microsoft.graph.downloadUrl,name,size,id,lastModifiedDateTime,folder,file,video',
|
||||
top: siteConfig.maxItems,
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,61 +1,60 @@
|
|||
export type OdFileObject = {
|
||||
// API response object for /api?path=<path_to_file_or_folder>, this may return either a file or a folder.
|
||||
// Pagination is also declared here with the 'next' parameter.
|
||||
export declare type OdAPIResponse = { file?: OdFileObject; folder?: OdFolderObject; next?: string }
|
||||
// A folder object returned from the OneDrive API. This contains the parameter 'value', which is an array of items
|
||||
// inside the folder. The items may also be either files or folders.
|
||||
export declare type OdFolderObject = {
|
||||
'@odata.count': number
|
||||
'@odata.context': string
|
||||
'@odata.nextLink'?: string
|
||||
value: Array<{
|
||||
'@microsoft.graph.downloadUrl': string
|
||||
id: string
|
||||
name: string
|
||||
size: number
|
||||
lastModifiedDateTime: string
|
||||
file?: { mimeType: string; hashes: { quickXorHash: string; sha1Hash?: string; sha256Hash?: string } }
|
||||
folder?: { childCount: number; view: { sortBy: string; sortOrder: 'ascending'; viewType: 'thumbnails' } }
|
||||
video?: OdVideoFile
|
||||
}>
|
||||
}
|
||||
// A file object returned from the OneDrive API. This object may contain 'video' if the file is a video.
|
||||
export declare type OdFileObject = {
|
||||
'@microsoft.graph.downloadUrl': string
|
||||
'@odata.context': string
|
||||
name: string
|
||||
size: number
|
||||
id: string
|
||||
lastModifiedDateTime: string
|
||||
file: {
|
||||
mimeType: string
|
||||
hashes: {
|
||||
quickXorHash: string
|
||||
sha1Hash: string
|
||||
sha256Hash: string
|
||||
}
|
||||
}
|
||||
file: { mimeType: string; hashes: { quickXorHash: string; sha1Hash?: string; sha256Hash?: string } }
|
||||
video?: OdVideoFile
|
||||
}
|
||||
|
||||
export type OdFolderObject = {
|
||||
'@odata.count': number
|
||||
value: Array<{
|
||||
id: string
|
||||
name: string
|
||||
lastModifiedDateTime: string
|
||||
size: number
|
||||
folder: {
|
||||
childCount: number
|
||||
view: {
|
||||
sortBy: 'name'
|
||||
sortOrder: 'ascending'
|
||||
viewType: 'thumbnails'
|
||||
}
|
||||
}
|
||||
}>
|
||||
// A representation of a OneDrive video file. All fields are declared here, but we mainly use 'width' and 'height'.
|
||||
export declare type OdVideoFile = {
|
||||
width: number
|
||||
height: number
|
||||
duration: number
|
||||
bitrate: number
|
||||
frameRate: number
|
||||
audioBitsPerSample: number
|
||||
audioChannels: number
|
||||
audioFormat: string
|
||||
audioSamplesPerSecond: number
|
||||
}
|
||||
|
||||
// Search result type which is returned by /api/search?q={query}
|
||||
export type OdSearchResult = Array<{
|
||||
// API response object for /api/search?q=<query>. Likewise, this array of items may also contain either files or folders.
|
||||
export declare type OdSearchResult = Array<{
|
||||
id: string
|
||||
name: string
|
||||
file?: OdFileObject
|
||||
folder?: OdFolderObject
|
||||
path: string
|
||||
parentReference: {
|
||||
id: string
|
||||
name: string
|
||||
path: string
|
||||
}
|
||||
parentReference: { id: string; name: string; path: string }
|
||||
}>
|
||||
|
||||
// driveItem type which is returned by /api/item?id={id}
|
||||
// API response object for /api/item?id={id}. This is primarily used for determining the path of the driveItem by ID.
|
||||
export type OdDriveItem = {
|
||||
'@odata.context': string
|
||||
'@odata.etag': string
|
||||
id: string
|
||||
name: string
|
||||
parentReference: {
|
||||
driveId: string
|
||||
driveType: string
|
||||
id: string
|
||||
path: string
|
||||
}
|
||||
parentReference: { driveId: string; driveType: string; id: string; path: string }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,17 @@ export function getExtension(fileName: string): string {
|
|||
return fileName.slice(((fileName.lastIndexOf('.') - 1) >>> 0) + 2).toLowerCase()
|
||||
}
|
||||
|
||||
export function getFileIcon(fileName: string): [IconPrefix, IconName] {
|
||||
export function getFileIcon(fileName: string, flags?: { video?: boolean }): [IconPrefix, IconName] {
|
||||
const extension = getExtension(fileName)
|
||||
return hasKey(extensions, extension) ? extensions[extension] : icons.file
|
||||
let icon = hasKey(extensions, extension) ? extensions[extension] : icons.file
|
||||
|
||||
// Files with '.ts' extensions may be TypeScript files or TS Video files, we check for the flag 'video'
|
||||
// to determine which icon to render for '.ts' files.
|
||||
if (extension === 'ts') {
|
||||
if (flags?.video) {
|
||||
icon = icons.video
|
||||
}
|
||||
}
|
||||
|
||||
return icon
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
const preview = {
|
||||
export const preview = {
|
||||
markdown: 'markdown',
|
||||
image: 'image',
|
||||
text: 'text',
|
||||
|
|
@ -11,7 +11,7 @@ const preview = {
|
|||
url: 'url',
|
||||
}
|
||||
|
||||
const extensions = {
|
||||
export const extensions = {
|
||||
gif: preview.image,
|
||||
jpeg: preview.image,
|
||||
jpg: preview.image,
|
||||
|
|
@ -34,13 +34,17 @@ const extensions = {
|
|||
c: preview.code,
|
||||
cpp: preview.code,
|
||||
js: preview.code,
|
||||
jsx: preview.code,
|
||||
java: preview.code,
|
||||
sh: preview.code,
|
||||
cs: preview.code,
|
||||
py: preview.code,
|
||||
css: preview.code,
|
||||
html: preview.code,
|
||||
// typescript or video file, determined below
|
||||
ts: preview.code,
|
||||
tsx: preview.code,
|
||||
rs: preview.code,
|
||||
vue: preview.code,
|
||||
json: preview.code,
|
||||
yaml: preview.code,
|
||||
|
|
@ -70,4 +74,19 @@ const extensions = {
|
|||
url: preview.url,
|
||||
}
|
||||
|
||||
export { extensions, preview }
|
||||
export function getPreviewType(extension: string, flags?: { video?: boolean }): string | undefined {
|
||||
let previewType = extensions[extension]
|
||||
if (!previewType) {
|
||||
return previewType
|
||||
}
|
||||
|
||||
// Files with '.ts' extensions may be TypeScript files or TS Video files, we check for the flag 'video'
|
||||
// to determine what preview renderer to use for '.ts' files.
|
||||
if (extension === 'ts') {
|
||||
if (flags?.video) {
|
||||
previewType = preview.video
|
||||
}
|
||||
}
|
||||
|
||||
return previewType
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue