mirror of
https://github.com/Nezumi-2711/onedrive-vercel-index.git
synced 2026-09-22 13:38:45 +00:00
add font awesome, file icons, and more
This commit is contained in:
@@ -0,0 +1,37 @@
|
|||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
|
import { ParsedUrlQuery } from 'querystring'
|
||||||
|
import { FunctionComponent } from 'react'
|
||||||
|
|
||||||
|
const Breadcrumb: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) => {
|
||||||
|
if (query) {
|
||||||
|
const { path } = query
|
||||||
|
if (Array.isArray(path)) {
|
||||||
|
return (
|
||||||
|
<div className="py-2 text-sm text-gray-600 flex">
|
||||||
|
<div className="p-1 hover:text-black transition-all duration-75">
|
||||||
|
<Link href="/">🚩 Home</Link>
|
||||||
|
</div>
|
||||||
|
{path.map((q: string, i: number) => (
|
||||||
|
<div key={i} className="flex items-center">
|
||||||
|
<div>/</div>
|
||||||
|
<div className="p-1 hover:text-black transition-all duration-75">
|
||||||
|
<Link href={`/${path.slice(0, i + 1).join('/')}`}>{q}</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="py-2 text-sm text-gray-600 hover:text-black transition-all duration-75">
|
||||||
|
<div className="p-1">
|
||||||
|
<Link href="/">🚩 Home</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Breadcrumb
|
||||||
+28
-14
@@ -1,8 +1,13 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import useSWR from 'swr'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
|
|
||||||
|
import { ParsedUrlQuery } from 'querystring'
|
||||||
import { FunctionComponent } from 'react'
|
import { FunctionComponent } from 'react'
|
||||||
|
import useSWR from 'swr'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
|
||||||
|
import getFileIcon from '../utils/getFileIcon'
|
||||||
|
|
||||||
const humanFileSize = (size: number) => {
|
const humanFileSize = (size: number) => {
|
||||||
if (size < 1024) return size + ' B'
|
if (size < 1024) return size + ' B'
|
||||||
const i = Math.floor(Math.log(size) / Math.log(1024))
|
const i = Math.floor(Math.log(size) / Math.log(1024))
|
||||||
@@ -12,17 +17,20 @@ const humanFileSize = (size: number) => {
|
|||||||
return `${formatted} ${'KMGTPEZY'[i - 1]}B`
|
return `${formatted} ${'KMGTPEZY'[i - 1]}B`
|
||||||
}
|
}
|
||||||
|
|
||||||
const encodePath = (path: string) => {
|
const queryToPath = (query?: ParsedUrlQuery) => {
|
||||||
const encodedPath = path === '/' ? '' : path
|
if (query) {
|
||||||
if (encodedPath === '/' || encodedPath === '') {
|
const { path } = query
|
||||||
return ''
|
if (!path) return '/'
|
||||||
|
if (typeof path === 'string') return `/${path}`
|
||||||
|
return `/${path.join('/')}`
|
||||||
}
|
}
|
||||||
return encodedPath
|
return '/'
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetcher = (url: string) => axios.get(url).then(res => res.data)
|
const fetcher = (url: string) => axios.get(url).then(res => res.data)
|
||||||
|
|
||||||
const FileListing: FunctionComponent<{ path: string }> = ({ path }) => {
|
const FileListing: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) => {
|
||||||
|
const path = queryToPath(query)
|
||||||
const { data, error } = useSWR(`/api?path=${path}`, fetcher)
|
const { data, error } = useSWR(`/api?path=${path}`, fetcher)
|
||||||
if (error) return <div>Failed to load</div>
|
if (error) return <div>Failed to load</div>
|
||||||
if (!data)
|
if (!data)
|
||||||
@@ -50,19 +58,25 @@ const FileListing: FunctionComponent<{ path: string }> = ({ path }) => {
|
|||||||
} = data
|
} = data
|
||||||
return (
|
return (
|
||||||
<div className="bg-white shadow rounded">
|
<div className="bg-white shadow rounded">
|
||||||
<div className="p-3 grid grid-cols-9 items-center space-x-2">
|
<div className="p-3 grid grid-cols-10 items-center space-x-2 border-b border-gray-200">
|
||||||
<div className="col-span-6 font-bold">Name</div>
|
<div className="col-span-7 font-bold">Name</div>
|
||||||
<div className="font-bold col-span-2">Created</div>
|
<div className="font-bold col-span-2">Created</div>
|
||||||
<div className="font-bold">Size</div>
|
<div className="font-bold">Size</div>
|
||||||
</div>
|
</div>
|
||||||
{children.map((c: any) => (
|
{children.map((c: any) => (
|
||||||
<Link href={`${encodePath(path)}/${c.name}`} key={c.id} passHref>
|
<Link href={`${path === '/' ? '' : path}/${c.name}`} key={c.id} passHref>
|
||||||
<div className="p-3 grid grid-cols-9 items-center space-x-2 cursor-pointer hover:bg-gray-100">
|
<div className="p-3 grid grid-cols-10 items-center space-x-2 cursor-pointer hover:bg-gray-100">
|
||||||
<div className="col-span-6 truncate">{c.name}</div>
|
<div className="flex space-x-2 items-center col-span-7 truncate">
|
||||||
<div className="font-mono text-sm col-span-2">
|
{/* <div>{c.file ? c.file.mimeType : 'folder'}</div> */}
|
||||||
|
<div className="w-5 text-center">
|
||||||
|
<FontAwesomeIcon icon={c.file ? getFileIcon(c.name) : ['far', 'folder']} />
|
||||||
|
</div>
|
||||||
|
<div className="truncate">{c.name}</div>
|
||||||
|
</div>
|
||||||
|
<div className="font-mono text-sm col-span-2 text-gray-700">
|
||||||
{new Date(c.createdDateTime).toLocaleString(undefined, { dateStyle: 'short', timeStyle: 'short' })}
|
{new Date(c.createdDateTime).toLocaleString(undefined, { dateStyle: 'short', timeStyle: 'short' })}
|
||||||
</div>
|
</div>
|
||||||
<div className="font-mono text-sm">{humanFileSize(c.size)}</div>
|
<div className="font-mono text-sm text-gray-700">{humanFileSize(c.size)}</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
|
|
||||||
import siteConfig from '../config/site.json'
|
import siteConfig from '../config/site.json'
|
||||||
|
|
||||||
const Navbar = () => {
|
const Navbar = () => {
|
||||||
return (
|
return (
|
||||||
<div className="text-left bg-white p-2">
|
<div className="text-left bg-white p-3 sticky top-0 bg-opacity-80 backdrop-blur-md shadow-sm">
|
||||||
<div className="max-w-4xl w-full mx-auto">
|
<div className="max-w-4xl w-full mx-auto flex items-center justify-between">
|
||||||
<h1 className="font-bold text-lg">{siteConfig.title}</h1>
|
<h1 className="font-bold text-xl">{siteConfig.title}</h1>
|
||||||
|
<a href="https://github.com/spencerwooo/onedrive-vercel-index" target="_blank" rel="noopener noreferrer">
|
||||||
|
<FontAwesomeIcon icon={['fab', 'github']} size="lg" />
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
Generated
+5996
-36
File diff suppressed because it is too large
Load Diff
+4
-1
@@ -9,11 +9,14 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-svg-core": "^1.2.35",
|
||||||
|
"@fortawesome/free-brands-svg-icons": "^5.15.3",
|
||||||
|
"@fortawesome/free-regular-svg-icons": "^5.15.3",
|
||||||
|
"@fortawesome/react-fontawesome": "^0.1.14",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"next": "11.0.0",
|
"next": "11.0.0",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "17.0.2",
|
||||||
"react-file-icon": "^1.0.0",
|
|
||||||
"swr": "^0.5.6"
|
"swr": "^0.5.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
+5
-8
@@ -1,14 +1,14 @@
|
|||||||
import Head from 'next/head'
|
import Head from 'next/head'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import Link from 'next/link'
|
|
||||||
|
|
||||||
import siteConfig from '../config/site.json'
|
import siteConfig from '../config/site.json'
|
||||||
import Navbar from '../components/Navbar'
|
import Navbar from '../components/Navbar'
|
||||||
import FileListing from '../components/FileListing'
|
import FileListing from '../components/FileListing'
|
||||||
import Footer from '../components/Footer'
|
import Footer from '../components/Footer'
|
||||||
|
import Breadcrumb from '../components/Breadcrumb'
|
||||||
|
|
||||||
export default function Folders() {
|
export default function Folders() {
|
||||||
const { asPath } = useRouter()
|
const { query } = useRouter()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center justify-center min-h-screen">
|
<div className="flex flex-col items-center justify-center min-h-screen">
|
||||||
@@ -18,12 +18,9 @@ export default function Folders() {
|
|||||||
|
|
||||||
<main className="flex flex-col w-full flex-1 bg-gray-50">
|
<main className="flex flex-col w-full flex-1 bg-gray-50">
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<div className="mx-auto w-full max-w-4xl">
|
<div className="mx-auto w-full max-w-4xl mb-8">
|
||||||
<div className="py-3 text-sm text-gray-600">
|
<Breadcrumb query={query} />
|
||||||
<Link href="/">🚩 Home </Link>
|
<FileListing query={query} />
|
||||||
{decodeURIComponent(asPath)}
|
|
||||||
</div>
|
|
||||||
<FileListing path={asPath} />
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,42 @@
|
|||||||
import '../styles/globals.css'
|
import '../styles/globals.css'
|
||||||
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
|
import {
|
||||||
|
faFileImage,
|
||||||
|
faFilePdf,
|
||||||
|
faFileWord,
|
||||||
|
faFilePowerpoint,
|
||||||
|
faFileExcel,
|
||||||
|
faFileAudio,
|
||||||
|
faFileVideo,
|
||||||
|
faFileArchive,
|
||||||
|
faFileCode,
|
||||||
|
faFileAlt,
|
||||||
|
faFile,
|
||||||
|
faFolder,
|
||||||
|
faCaretSquareDown,
|
||||||
|
} from '@fortawesome/free-regular-svg-icons'
|
||||||
|
import { faGithub, faMarkdown } from '@fortawesome/free-brands-svg-icons'
|
||||||
|
|
||||||
import type { AppProps } from 'next/app'
|
import type { AppProps } from 'next/app'
|
||||||
|
|
||||||
|
library.add(
|
||||||
|
faFileImage,
|
||||||
|
faFilePdf,
|
||||||
|
faFileWord,
|
||||||
|
faFilePowerpoint,
|
||||||
|
faFileExcel,
|
||||||
|
faFileAudio,
|
||||||
|
faFileVideo,
|
||||||
|
faFileArchive,
|
||||||
|
faFileCode,
|
||||||
|
faFileAlt,
|
||||||
|
faFile,
|
||||||
|
faFolder,
|
||||||
|
faGithub,
|
||||||
|
faMarkdown,
|
||||||
|
faCaretSquareDown
|
||||||
|
)
|
||||||
|
|
||||||
function MyApp({ Component, pageProps }: AppProps) {
|
function MyApp({ Component, pageProps }: AppProps) {
|
||||||
return <Component {...pageProps} />
|
return <Component {...pageProps} />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ const getAccessToken = async () => {
|
|||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
const { path = '/' } = req.query
|
const { path = '/' } = req.query
|
||||||
|
if (path === '[...path]') {
|
||||||
|
res.status(400).json({ error: 'Path query invalid.' })
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof path === 'string') {
|
if (typeof path === 'string') {
|
||||||
const accessToken = await getAccessToken()
|
const accessToken = await getAccessToken()
|
||||||
|
|||||||
+4
-9
@@ -1,15 +1,12 @@
|
|||||||
import Head from 'next/head'
|
import Head from 'next/head'
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import Link from 'next/link'
|
|
||||||
|
|
||||||
import siteConfig from '../config/site.json'
|
import siteConfig from '../config/site.json'
|
||||||
import Navbar from '../components/Navbar'
|
import Navbar from '../components/Navbar'
|
||||||
import FileListing from '../components/FileListing'
|
import FileListing from '../components/FileListing'
|
||||||
import Footer from '../components/Footer'
|
import Footer from '../components/Footer'
|
||||||
|
import Breadcrumb from '../components/Breadcrumb'
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const { asPath } = useRouter()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center justify-center min-h-screen">
|
<div className="flex flex-col items-center justify-center min-h-screen">
|
||||||
<Head>
|
<Head>
|
||||||
@@ -18,11 +15,9 @@ export default function Home() {
|
|||||||
|
|
||||||
<main className="flex flex-col w-full flex-1 bg-gray-50">
|
<main className="flex flex-col w-full flex-1 bg-gray-50">
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<div className="mx-auto w-full max-w-4xl">
|
<div className="mx-auto w-full max-w-4xl mb-8">
|
||||||
<div className="py-3 text-sm text-gray-600">
|
<Breadcrumb />
|
||||||
<Link href="/">🚩 Home</Link>
|
<FileListing />
|
||||||
</div>
|
|
||||||
<FileListing path={asPath} />
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap');
|
||||||
|
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
const icons = {
|
||||||
|
image: ['far', 'file-image'],
|
||||||
|
pdf: ['far', 'file-pdf'],
|
||||||
|
word: ['far', 'file-word'],
|
||||||
|
powerpoint: ['far', 'file-powerpoint'],
|
||||||
|
excel: ['far', 'file-excel'],
|
||||||
|
audio: ['far', 'file-audio'],
|
||||||
|
video: ['far', 'file-video'],
|
||||||
|
archive: ['far', 'file-archive'],
|
||||||
|
code: ['far', 'file-code'],
|
||||||
|
text: ['far', 'file-alt'],
|
||||||
|
file: ['far', 'file'],
|
||||||
|
markdown: ['fab', 'markdown'],
|
||||||
|
}
|
||||||
|
|
||||||
|
const extensions = {
|
||||||
|
gif: icons.image,
|
||||||
|
jpeg: icons.image,
|
||||||
|
jpg: icons.image,
|
||||||
|
png: icons.image,
|
||||||
|
heic: icons.image,
|
||||||
|
webp: icons.image,
|
||||||
|
|
||||||
|
pdf: icons.pdf,
|
||||||
|
|
||||||
|
doc: icons.word,
|
||||||
|
docx: icons.word,
|
||||||
|
|
||||||
|
ppt: icons.powerpoint,
|
||||||
|
pptx: icons.powerpoint,
|
||||||
|
|
||||||
|
xls: icons.excel,
|
||||||
|
xlsx: icons.excel,
|
||||||
|
|
||||||
|
aac: icons.audio,
|
||||||
|
mp3: icons.audio,
|
||||||
|
ogg: icons.audio,
|
||||||
|
|
||||||
|
avi: icons.video,
|
||||||
|
flv: icons.video,
|
||||||
|
mkv: icons.video,
|
||||||
|
mp4: icons.video,
|
||||||
|
|
||||||
|
gz: icons.archive,
|
||||||
|
rar: icons.archive,
|
||||||
|
zip: icons.archive,
|
||||||
|
|
||||||
|
css: icons.code,
|
||||||
|
py: icons.code,
|
||||||
|
html: icons.code,
|
||||||
|
js: icons.code,
|
||||||
|
ts: icons.code,
|
||||||
|
c: icons.code,
|
||||||
|
rb: icons.code,
|
||||||
|
cpp: icons.code,
|
||||||
|
|
||||||
|
txt: icons.text,
|
||||||
|
rtf: icons.text,
|
||||||
|
md: icons.markdown,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To stop TypeScript complaining about indexing the object with a non-existent key
|
||||||
|
* https://dev.to/mapleleaf/indexing-objects-in-typescript-1cgi
|
||||||
|
*
|
||||||
|
* @param obj Object with keys to index
|
||||||
|
* @param key The index key
|
||||||
|
* @returns Whether or not the key exists inside the object
|
||||||
|
*/
|
||||||
|
const hasKey = <O>(obj: O, key: PropertyKey): key is keyof O => {
|
||||||
|
return key in obj
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function getFileIcon(fileName: string) {
|
||||||
|
const extension = fileName.slice(((fileName.lastIndexOf('.') - 1) >>> 0) + 2).toLowerCase()
|
||||||
|
return hasKey(extensions, extension) ? extensions[extension] : icons.file
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user