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