add font awesome, file icons, and more

This commit is contained in:
spencerwooo 2021-06-23 21:27:51 +01:00
parent bada403a9d
commit 40ac051d33
No known key found for this signature in database
GPG Key ID: 24CD550268849CA0
11 changed files with 6199 additions and 71 deletions

37
components/Breadcrumb.tsx Normal file
View File

@ -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

View File

@ -1,8 +1,13 @@
import axios from 'axios'
import useSWR from 'swr'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { ParsedUrlQuery } from 'querystring'
import { FunctionComponent } from 'react'
import useSWR from 'swr'
import Link from 'next/link'
import getFileIcon from '../utils/getFileIcon'
const humanFileSize = (size: number) => {
if (size < 1024) return size + ' B'
const i = Math.floor(Math.log(size) / Math.log(1024))
@ -12,17 +17,20 @@ const humanFileSize = (size: number) => {
return `${formatted} ${'KMGTPEZY'[i - 1]}B`
}
const encodePath = (path: string) => {
const encodedPath = path === '/' ? '' : path
if (encodedPath === '/' || encodedPath === '') {
return ''
const queryToPath = (query?: ParsedUrlQuery) => {
if (query) {
const { path } = query
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 FileListing: FunctionComponent<{ path: string }> = ({ path }) => {
const FileListing: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) => {
const path = queryToPath(query)
const { data, error } = useSWR(`/api?path=${path}`, fetcher)
if (error) return <div>Failed to load</div>
if (!data)
@ -50,19 +58,25 @@ const FileListing: FunctionComponent<{ path: string }> = ({ path }) => {
} = data
return (
<div className="bg-white shadow rounded">
<div className="p-3 grid grid-cols-9 items-center space-x-2">
<div className="col-span-6 font-bold">Name</div>
<div className="p-3 grid grid-cols-10 items-center space-x-2 border-b border-gray-200">
<div className="col-span-7 font-bold">Name</div>
<div className="font-bold col-span-2">Created</div>
<div className="font-bold">Size</div>
</div>
{children.map((c: any) => (
<Link href={`${encodePath(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="col-span-6 truncate">{c.name}</div>
<div className="font-mono text-sm col-span-2">
<Link href={`${path === '/' ? '' : path}/${c.name}`} key={c.id} passHref>
<div className="p-3 grid grid-cols-10 items-center space-x-2 cursor-pointer hover:bg-gray-100">
<div className="flex space-x-2 items-center col-span-7 truncate">
{/* <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' })}
</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>
</Link>
))}

View File

@ -1,10 +1,15 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import siteConfig from '../config/site.json'
const Navbar = () => {
return (
<div className="text-left bg-white p-2">
<div className="max-w-4xl w-full mx-auto">
<h1 className="font-bold text-lg">{siteConfig.title}</h1>
<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 flex items-center justify-between">
<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>
)

6032
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,11 +9,14 @@
"lint": "next lint"
},
"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",
"next": "11.0.0",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-file-icon": "^1.0.0",
"swr": "^0.5.6"
},
"devDependencies": {

View File

@ -1,14 +1,14 @@
import Head from 'next/head'
import { useRouter } from 'next/router'
import Link from 'next/link'
import siteConfig from '../config/site.json'
import Navbar from '../components/Navbar'
import FileListing from '../components/FileListing'
import Footer from '../components/Footer'
import Breadcrumb from '../components/Breadcrumb'
export default function Folders() {
const { asPath } = useRouter()
const { query } = useRouter()
return (
<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">
<Navbar />
<div className="mx-auto w-full max-w-4xl">
<div className="py-3 text-sm text-gray-600">
<Link href="/">🚩 Home </Link>
{decodeURIComponent(asPath)}
</div>
<FileListing path={asPath} />
<div className="mx-auto w-full max-w-4xl mb-8">
<Breadcrumb query={query} />
<FileListing query={query} />
</div>
</main>

View File

@ -1,6 +1,42 @@
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'
library.add(
faFileImage,
faFilePdf,
faFileWord,
faFilePowerpoint,
faFileExcel,
faFileAudio,
faFileVideo,
faFileArchive,
faFileCode,
faFileAlt,
faFile,
faFolder,
faGithub,
faMarkdown,
faCaretSquareDown
)
function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />
}

View File

@ -40,6 +40,9 @@ const getAccessToken = async () => {
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { path = '/' } = req.query
if (path === '[...path]') {
res.status(400).json({ error: 'Path query invalid.' })
}
if (typeof path === 'string') {
const accessToken = await getAccessToken()

View File

@ -1,15 +1,12 @@
import Head from 'next/head'
import { useRouter } from 'next/router'
import Link from 'next/link'
import siteConfig from '../config/site.json'
import Navbar from '../components/Navbar'
import FileListing from '../components/FileListing'
import Footer from '../components/Footer'
import Breadcrumb from '../components/Breadcrumb'
export default function Home() {
const { asPath } = useRouter()
return (
<div className="flex flex-col items-center justify-center min-h-screen">
<Head>
@ -18,11 +15,9 @@ export default function Home() {
<main className="flex flex-col w-full flex-1 bg-gray-50">
<Navbar />
<div className="mx-auto w-full max-w-4xl">
<div className="py-3 text-sm text-gray-600">
<Link href="/">🚩 Home</Link>
</div>
<FileListing path={asPath} />
<div className="mx-auto w-full max-w-4xl mb-8">
<Breadcrumb />
<FileListing />
</div>
</main>

View File

@ -1,4 +1,5 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap');
@tailwind base;
@tailwind components;
@tailwind utilities;

77
utils/getFileIcon.ts Normal file
View File

@ -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
}