mirror of
https://github.com/Nezumi-2711/onedrive-vercel-index.git
synced 2026-09-22 13:38:45 +00:00
Merge pull request #283 from spencerwooo/native-search
This commit is contained in:
@@ -34,7 +34,8 @@ import VideoPreview from './previews/VideoPreview'
|
||||
import PDFPreview from './previews/PDFPreview'
|
||||
import URLPreview from './previews/URLPreview'
|
||||
import DefaultPreview from './previews/DefaultPreview'
|
||||
import { PreviewContainer } from './previews/Containers'
|
||||
import { DownloadBtnContainer, PreviewContainer } from './previews/Containers'
|
||||
import DownloadButtonGroup from './DownloadBtnGtoup'
|
||||
|
||||
// Disabling SSR for some previews (image gallery view, and PDF view)
|
||||
const ReactViewer = dynamic(() => import('react-viewer'), { ssr: false })
|
||||
@@ -545,10 +546,15 @@ const FileListing: FC<{ query?: ParsedUrlQuery }> = ({ query }) => {
|
||||
switch (extensions[fileExtension]) {
|
||||
case preview.image:
|
||||
return (
|
||||
<PreviewContainer>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img className="mx-auto" src={downloadUrl} alt={fileName} />
|
||||
</PreviewContainer>
|
||||
<>
|
||||
<PreviewContainer>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img className="mx-auto" src={downloadUrl} alt={fileName} />
|
||||
</PreviewContainer>
|
||||
<DownloadBtnContainer>
|
||||
<DownloadButtonGroup downloadUrl={file['@microsoft.graph.downloadUrl']} />
|
||||
</DownloadBtnContainer>
|
||||
</>
|
||||
)
|
||||
|
||||
case preview.text:
|
||||
|
||||
+32
-3
@@ -2,6 +2,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { IconName } from '@fortawesome/fontawesome-svg-core'
|
||||
import { Dialog, Transition } from '@headlessui/react'
|
||||
import toast, { Toaster } from 'react-hot-toast'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
|
||||
import Link from 'next/link'
|
||||
import Image from 'next/image'
|
||||
@@ -9,12 +10,21 @@ import { useRouter } from 'next/router'
|
||||
import { Fragment, useEffect, useState } from 'react'
|
||||
|
||||
import siteConfig from '../config/site.json'
|
||||
import SearchModal from './SearchModal'
|
||||
import useDeviceOS from '../utils/useDeviceOS'
|
||||
|
||||
const Navbar = () => {
|
||||
const router = useRouter()
|
||||
const os = useDeviceOS()
|
||||
|
||||
const [tokenPresent, setTokenPresent] = useState(false)
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
|
||||
const [searchOpen, setSearchOpen] = useState(false)
|
||||
const openSearchBox = () => setSearchOpen(true)
|
||||
|
||||
useHotkeys(`${os === 'mac' ? 'cmd' : 'ctrl'}+k`, openSearchBox)
|
||||
|
||||
useEffect(() => {
|
||||
const storedToken = () => {
|
||||
for (const r of siteConfig.protectedRoutes) {
|
||||
@@ -44,15 +54,34 @@ const Navbar = () => {
|
||||
<div className="bg-white dark:bg-gray-900 dark:border-gray-500/30 sticky top-0 bg-opacity-80 border-b border-gray-900/10 backdrop-blur-md z-[100]">
|
||||
<Toaster />
|
||||
|
||||
<div className="flex items-center justify-between w-full mx-auto px-4 py-1">
|
||||
<SearchModal searchOpen={searchOpen} setSearchOpen={setSearchOpen} />
|
||||
|
||||
<div className="flex items-center space-x-4 justify-between w-full mx-auto px-4 py-1">
|
||||
<Link href="/" passHref>
|
||||
<a className="dark:text-white hover:opacity-80 flex items-center p-2 space-x-2">
|
||||
<a className="dark:text-white hover:opacity-80 flex items-center py-2 md:p-2 space-x-2">
|
||||
<Image src={siteConfig.icon} alt="icon" width="25" height="25" priority />
|
||||
<span className="sm:block hidden font-bold">{siteConfig.title}</span>
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
<div className="flex items-center space-x-4 text-gray-700">
|
||||
<div className="flex items-center flex-1 md:flex-initial space-x-4 text-gray-700">
|
||||
<button
|
||||
className="flex-1 flex items-center justify-between rounded-lg bg-gray-100 dark:bg-gray-800 md:w-48 px-2.5 py-1.5 dark:text-white hover:opacity-80"
|
||||
onClick={openSearchBox}
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
<FontAwesomeIcon icon="search" />
|
||||
<span className="text-sm font-medium">Search ...</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-1">
|
||||
<div className="px-2 py-1 rounded-lg bg-gray-100 dark:bg-gray-700 font-medium text-xs">
|
||||
{os === 'mac' ? '⌘' : 'Ctrl'}
|
||||
</div>
|
||||
<div className="px-2 py-1 rounded-lg bg-gray-100 dark:bg-gray-700 font-medium text-xs">K</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{siteConfig.links.length !== 0 &&
|
||||
siteConfig.links.map((l: { name: string; link: string }) => (
|
||||
<a
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
import axios from 'axios'
|
||||
import AwesomeDebouncePromise from 'awesome-debounce-promise'
|
||||
import { useAsync } from 'react-async-hook'
|
||||
import useConstant from 'use-constant'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
|
||||
import { Dispatch, FC, Fragment, SetStateAction, useState } from 'react'
|
||||
import { Dialog, Transition } from '@headlessui/react'
|
||||
import Link from 'next/link'
|
||||
|
||||
import { OdSearchResult } from '../types'
|
||||
import { getFileIcon } from '../utils/getFileIcon'
|
||||
import siteConfig from '../config/site.json'
|
||||
import { LoadingIcon } from './Loading'
|
||||
|
||||
function useDriveItemSearch() {
|
||||
const [query, setQuery] = useState('')
|
||||
const searchDriveItem = async (q: string) => {
|
||||
const { data } = await axios.get<OdSearchResult>(`/api/search?q=${q}`)
|
||||
|
||||
// Extract the searched item's path and convert it to the absolute path in onedrive-vercel-index
|
||||
function mapAbsolutePath(path: string): string {
|
||||
return siteConfig.baseDirectory === '/' ? path.split('root:')[1] : path.split(siteConfig.baseDirectory)[1]
|
||||
}
|
||||
|
||||
// Map parentReference to the absolute path of the search result
|
||||
data.map(item => {
|
||||
item['path'] = `${mapAbsolutePath(item.parentReference.path)}/${encodeURIComponent(item.name)}`
|
||||
})
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
const debouncedNotionSearch = useConstant(() => AwesomeDebouncePromise(searchDriveItem, 1000))
|
||||
const results = useAsync(async () => {
|
||||
if (query.length === 0) {
|
||||
return []
|
||||
} else {
|
||||
return debouncedNotionSearch(query)
|
||||
}
|
||||
}, [query])
|
||||
|
||||
return {
|
||||
query,
|
||||
setQuery,
|
||||
results,
|
||||
}
|
||||
}
|
||||
|
||||
function SearchModal({
|
||||
searchOpen,
|
||||
setSearchOpen,
|
||||
}: {
|
||||
searchOpen: boolean
|
||||
setSearchOpen: Dispatch<SetStateAction<boolean>>
|
||||
}) {
|
||||
const closeSearchBox = () => setSearchOpen(false)
|
||||
|
||||
const { query, setQuery, results } = useDriveItemSearch()
|
||||
|
||||
return (
|
||||
<Transition appear show={searchOpen} as={Fragment}>
|
||||
<Dialog as="div" className="inset-0 z-[200] fixed overflow-y-auto" onClose={closeSearchBox}>
|
||||
<div className="min-h-screen text-center px-4">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-100"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-100"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<Dialog.Overlay className="bg-white/80 inset-0 fixed dark:bg-gray-800/80" />
|
||||
</Transition.Child>
|
||||
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-100"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-100"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<div className="border rounded border-gray-400/30 shadow-xl my-12 text-left w-full max-w-3xl transform transition-all inline-block overflow-hidden">
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="flex items-center space-x-4 dark:text-white border-b bg-gray-50 border-gray-400/30 p-4 dark:bg-gray-800"
|
||||
>
|
||||
<FontAwesomeIcon icon="search" className="w-4 h-4" />
|
||||
<input
|
||||
type="text"
|
||||
id="search-box"
|
||||
className="w-full bg-transparent focus:outline-none focus-visible:outline-none"
|
||||
placeholder="Search ..."
|
||||
value={query}
|
||||
onChange={e => setQuery(e.target.value)}
|
||||
/>
|
||||
<div className="px-2 py-1 rounded-lg bg-gray-100 dark:bg-gray-700 font-medium text-xs">ESC</div>
|
||||
</Dialog.Title>
|
||||
|
||||
<div className="bg-white dark:text-white dark:bg-gray-900 max-h-[80vh] overflow-x-hidden overflow-y-scroll">
|
||||
{results.loading && (
|
||||
<div className="text-center px-4 py-12 text-sm font-medium">
|
||||
<LoadingIcon className="animate-spin w-4 h-4 mr-2 inline-block svg-inline--fa" />
|
||||
<span>Loading ...</span>
|
||||
</div>
|
||||
)}
|
||||
{results.error && (
|
||||
<div className="text-center px-4 py-12 text-sm font-medium">Error: {results.error.message}</div>
|
||||
)}
|
||||
{results.result && (
|
||||
<>
|
||||
{results.result.length === 0 ? (
|
||||
<div className="text-center px-4 py-12 text-sm font-medium">Nothing here.</div>
|
||||
) : (
|
||||
results.result.map(result => (
|
||||
<Link href={result.path} key={result.id} passHref>
|
||||
<div
|
||||
className="flex items-center space-x-4 border-b cursor-pointer border-gray-400/30 px-4 py-1.5 hover:bg-gray-50 dark:hover:bg-gray-850"
|
||||
onClick={closeSearchBox}
|
||||
>
|
||||
<FontAwesomeIcon icon={result.file ? getFileIcon(result.name) : ['far', 'folder']} />
|
||||
<div>
|
||||
<div className="text-sm font-medium leading-8">{result.name}</div>
|
||||
<div className="text-xs font-mono opacity-60 truncate overflow-hidden">
|
||||
{decodeURIComponent(result.path)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
)
|
||||
}
|
||||
|
||||
export default SearchModal
|
||||
Generated
+147
-1
@@ -14,6 +14,7 @@
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.3",
|
||||
"@fortawesome/react-fontawesome": "^0.1.14",
|
||||
"@headlessui/react": "^1.4.0",
|
||||
"awesome-debounce-promise": "^2.1.0",
|
||||
"axios": "^0.21.1",
|
||||
"crypto-js": "^4.1.1",
|
||||
"emoji-regex": "^9.2.2",
|
||||
@@ -24,9 +25,11 @@
|
||||
"preview-office-docs": "^1.0.2",
|
||||
"prismjs": "^1.23.0",
|
||||
"react": "^17.0.2",
|
||||
"react-async-hook": "^4.0.0",
|
||||
"react-copy-to-clipboard": "^5.0.3",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-hot-toast": "^2.0.0",
|
||||
"react-hotkeys-hook": "^3.4.4",
|
||||
"react-markdown": "^6.0.2",
|
||||
"react-player": "^2.9.0",
|
||||
"react-reader": "^0.20.4",
|
||||
@@ -36,7 +39,8 @@
|
||||
"remark-gfm": "^1.0.0",
|
||||
"remark-math": "^4.0.0",
|
||||
"swr": "^0.5.6",
|
||||
"use-clipboard-copy": "^0.2.0"
|
||||
"use-clipboard-copy": "^0.2.0",
|
||||
"use-constant": "^1.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/crypto-js": "^4.0.2",
|
||||
@@ -619,6 +623,11 @@
|
||||
"integrity": "sha512-sCVniU+h3GcGqxOmng11BRvf9TfN9yIs8KKjB8C8d75W69cpTfZG80gau9yTx5SxF3gvHGbJhdESzzvnjtf3Og==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/debounce-promise": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/debounce-promise/-/debounce-promise-3.1.4.tgz",
|
||||
"integrity": "sha512-9SEVY3nsz+uMN2DwDocftB5TAgZe7D0cOzxxRhpotWs6T4QFqRaTXpXbOSzbk31/7iYcfCkJJPwWGzTxyuGhCg=="
|
||||
},
|
||||
"node_modules/@types/hast": {
|
||||
"version": "2.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz",
|
||||
@@ -1139,6 +1148,42 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/awesome-debounce-promise": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/awesome-debounce-promise/-/awesome-debounce-promise-2.1.0.tgz",
|
||||
"integrity": "sha512-0Dv4j2wKk5BrNZh4jgV2HUdznaeVgEK/WTvcHhZWUElhmQ1RR+iURRoLEwICFyR0S/5VtxfcvY6gR+qSe95jNg==",
|
||||
"dependencies": {
|
||||
"@types/debounce-promise": "^3.1.1",
|
||||
"awesome-imperative-promise": "^1.0.1",
|
||||
"awesome-only-resolves-last-promise": "^1.0.3",
|
||||
"debounce-promise": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8",
|
||||
"npm": ">=5"
|
||||
}
|
||||
},
|
||||
"node_modules/awesome-imperative-promise": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/awesome-imperative-promise/-/awesome-imperative-promise-1.0.1.tgz",
|
||||
"integrity": "sha512-EmPr3FqbQGqlNh+WxMNcF9pO9uDQJnOC4/3rLBQNH9m4E9qI+8lbfHCmHpVAsmGqPJPKhCjJLHUQzQW/RBHRdQ==",
|
||||
"engines": {
|
||||
"node": ">=8",
|
||||
"npm": ">=5"
|
||||
}
|
||||
},
|
||||
"node_modules/awesome-only-resolves-last-promise": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/awesome-only-resolves-last-promise/-/awesome-only-resolves-last-promise-1.0.3.tgz",
|
||||
"integrity": "sha512-7q4WPsYiD8Omvi/yHL314DkvsD/lM//Z2/KcU1vWk0xJotiV0GMJTgHTpWl3n90HJqpXKg7qX+VVNs5YbQyPRQ==",
|
||||
"dependencies": {
|
||||
"awesome-imperative-promise": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8",
|
||||
"npm": ">=5"
|
||||
}
|
||||
},
|
||||
"node_modules/axe-core": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.3.3.tgz",
|
||||
@@ -1804,6 +1849,11 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/debounce-promise": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/debounce-promise/-/debounce-promise-3.1.2.tgz",
|
||||
"integrity": "sha512-rZHcgBkbYavBeD9ej6sP56XfG53d51CD4dnaw989YX/nZ/ZJfgRx/9ePKmTNiUiyQvh4mtrMoS3OAWW+yoYtpg=="
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
|
||||
@@ -3397,6 +3447,11 @@
|
||||
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/hotkeys-js": {
|
||||
"version": "3.8.7",
|
||||
"resolved": "https://registry.npmjs.org/hotkeys-js/-/hotkeys-js-3.8.7.tgz",
|
||||
"integrity": "sha512-ckAx3EkUr5XjDwjEHDorHxRO2Kb7z6Z2Sxul4MbBkN8Nho7XDslQsgMJT+CiJ5Z4TgRxxvKHEpuLE3imzqy4Lg=="
|
||||
},
|
||||
"node_modules/html-void-elements": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-2.0.0.tgz",
|
||||
@@ -5697,6 +5752,18 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-async-hook": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-async-hook/-/react-async-hook-4.0.0.tgz",
|
||||
"integrity": "sha512-97lgjFkOcHCTYSrsKBpsXg3iVWM0LnzedB749iP76sb3/8Ouu4nHIkCLEOrQWHVYqrYxjF05NN6GHoXWFkB3Kw==",
|
||||
"engines": {
|
||||
"node": ">=8",
|
||||
"npm": ">=5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8"
|
||||
}
|
||||
},
|
||||
"node_modules/react-copy-to-clipboard": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.4.tgz",
|
||||
@@ -5742,6 +5809,18 @@
|
||||
"react-dom": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/react-hotkeys-hook": {
|
||||
"version": "3.4.4",
|
||||
"resolved": "https://registry.npmjs.org/react-hotkeys-hook/-/react-hotkeys-hook-3.4.4.tgz",
|
||||
"integrity": "sha512-vaORq07rWgmuF3owWRhgFV/3VL8/l2q9lz0WyVEddJnWTtKW+AOgU5YgYKuwN6h6h7bCcLG3MFsJIjCrM/5DvQ==",
|
||||
"dependencies": {
|
||||
"hotkeys-js": "3.8.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.1",
|
||||
"react-dom": ">=16.8.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||
@@ -7202,6 +7281,14 @@
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-constant": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/use-constant/-/use-constant-1.1.0.tgz",
|
||||
"integrity": "sha512-yrflEfv7Xv/W8WlYV6nwRH01K+2BpR4cWxuzY03yPRjYZuHixhGlvnJN5O2bRYrXGpJ4zy8QjFABGIQ2QXeBOA==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-subscription": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.5.1.tgz",
|
||||
@@ -7798,6 +7885,11 @@
|
||||
"integrity": "sha512-sCVniU+h3GcGqxOmng11BRvf9TfN9yIs8KKjB8C8d75W69cpTfZG80gau9yTx5SxF3gvHGbJhdESzzvnjtf3Og==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/debounce-promise": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/debounce-promise/-/debounce-promise-3.1.4.tgz",
|
||||
"integrity": "sha512-9SEVY3nsz+uMN2DwDocftB5TAgZe7D0cOzxxRhpotWs6T4QFqRaTXpXbOSzbk31/7iYcfCkJJPwWGzTxyuGhCg=="
|
||||
},
|
||||
"@types/hast": {
|
||||
"version": "2.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz",
|
||||
@@ -8192,6 +8284,30 @@
|
||||
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
|
||||
"integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw=="
|
||||
},
|
||||
"awesome-debounce-promise": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/awesome-debounce-promise/-/awesome-debounce-promise-2.1.0.tgz",
|
||||
"integrity": "sha512-0Dv4j2wKk5BrNZh4jgV2HUdznaeVgEK/WTvcHhZWUElhmQ1RR+iURRoLEwICFyR0S/5VtxfcvY6gR+qSe95jNg==",
|
||||
"requires": {
|
||||
"@types/debounce-promise": "^3.1.1",
|
||||
"awesome-imperative-promise": "^1.0.1",
|
||||
"awesome-only-resolves-last-promise": "^1.0.3",
|
||||
"debounce-promise": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"awesome-imperative-promise": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/awesome-imperative-promise/-/awesome-imperative-promise-1.0.1.tgz",
|
||||
"integrity": "sha512-EmPr3FqbQGqlNh+WxMNcF9pO9uDQJnOC4/3rLBQNH9m4E9qI+8lbfHCmHpVAsmGqPJPKhCjJLHUQzQW/RBHRdQ=="
|
||||
},
|
||||
"awesome-only-resolves-last-promise": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/awesome-only-resolves-last-promise/-/awesome-only-resolves-last-promise-1.0.3.tgz",
|
||||
"integrity": "sha512-7q4WPsYiD8Omvi/yHL314DkvsD/lM//Z2/KcU1vWk0xJotiV0GMJTgHTpWl3n90HJqpXKg7qX+VVNs5YbQyPRQ==",
|
||||
"requires": {
|
||||
"awesome-imperative-promise": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"axe-core": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.3.3.tgz",
|
||||
@@ -8710,6 +8826,11 @@
|
||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz",
|
||||
"integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og=="
|
||||
},
|
||||
"debounce-promise": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/debounce-promise/-/debounce-promise-3.1.2.tgz",
|
||||
"integrity": "sha512-rZHcgBkbYavBeD9ej6sP56XfG53d51CD4dnaw989YX/nZ/ZJfgRx/9ePKmTNiUiyQvh4mtrMoS3OAWW+yoYtpg=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
|
||||
@@ -9931,6 +10052,11 @@
|
||||
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
|
||||
"dev": true
|
||||
},
|
||||
"hotkeys-js": {
|
||||
"version": "3.8.7",
|
||||
"resolved": "https://registry.npmjs.org/hotkeys-js/-/hotkeys-js-3.8.7.tgz",
|
||||
"integrity": "sha512-ckAx3EkUr5XjDwjEHDorHxRO2Kb7z6Z2Sxul4MbBkN8Nho7XDslQsgMJT+CiJ5Z4TgRxxvKHEpuLE3imzqy4Lg=="
|
||||
},
|
||||
"html-void-elements": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-2.0.0.tgz",
|
||||
@@ -11620,6 +11746,12 @@
|
||||
"object-assign": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"react-async-hook": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-async-hook/-/react-async-hook-4.0.0.tgz",
|
||||
"integrity": "sha512-97lgjFkOcHCTYSrsKBpsXg3iVWM0LnzedB749iP76sb3/8Ouu4nHIkCLEOrQWHVYqrYxjF05NN6GHoXWFkB3Kw==",
|
||||
"requires": {}
|
||||
},
|
||||
"react-copy-to-clipboard": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.4.tgz",
|
||||
@@ -11652,6 +11784,14 @@
|
||||
"goober": "^2.0.35"
|
||||
}
|
||||
},
|
||||
"react-hotkeys-hook": {
|
||||
"version": "3.4.4",
|
||||
"resolved": "https://registry.npmjs.org/react-hotkeys-hook/-/react-hotkeys-hook-3.4.4.tgz",
|
||||
"integrity": "sha512-vaORq07rWgmuF3owWRhgFV/3VL8/l2q9lz0WyVEddJnWTtKW+AOgU5YgYKuwN6h6h7bCcLG3MFsJIjCrM/5DvQ==",
|
||||
"requires": {
|
||||
"hotkeys-js": "3.8.7"
|
||||
}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||
@@ -12741,6 +12881,12 @@
|
||||
"clipboard-copy": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"use-constant": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/use-constant/-/use-constant-1.1.0.tgz",
|
||||
"integrity": "sha512-yrflEfv7Xv/W8WlYV6nwRH01K+2BpR4cWxuzY03yPRjYZuHixhGlvnJN5O2bRYrXGpJ4zy8QjFABGIQ2QXeBOA==",
|
||||
"requires": {}
|
||||
},
|
||||
"use-subscription": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.5.1.tgz",
|
||||
|
||||
+5
-1
@@ -15,6 +15,7 @@
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.3",
|
||||
"@fortawesome/react-fontawesome": "^0.1.14",
|
||||
"@headlessui/react": "^1.4.0",
|
||||
"awesome-debounce-promise": "^2.1.0",
|
||||
"axios": "^0.21.1",
|
||||
"crypto-js": "^4.1.1",
|
||||
"emoji-regex": "^9.2.2",
|
||||
@@ -25,9 +26,11 @@
|
||||
"preview-office-docs": "^1.0.2",
|
||||
"prismjs": "^1.23.0",
|
||||
"react": "^17.0.2",
|
||||
"react-async-hook": "^4.0.0",
|
||||
"react-copy-to-clipboard": "^5.0.3",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-hot-toast": "^2.0.0",
|
||||
"react-hotkeys-hook": "^3.4.4",
|
||||
"react-markdown": "^6.0.2",
|
||||
"react-player": "^2.9.0",
|
||||
"react-reader": "^0.20.4",
|
||||
@@ -37,7 +40,8 @@
|
||||
"remark-gfm": "^1.0.0",
|
||||
"remark-math": "^4.0.0",
|
||||
"swr": "^0.5.6",
|
||||
"use-clipboard-copy": "^0.2.0"
|
||||
"use-clipboard-copy": "^0.2.0",
|
||||
"use-constant": "^1.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/crypto-js": "^4.0.2",
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
faCheckCircle,
|
||||
} from '@fortawesome/free-regular-svg-icons'
|
||||
import {
|
||||
faSearch,
|
||||
faCheck,
|
||||
faPlus,
|
||||
faMinus,
|
||||
@@ -92,6 +93,7 @@ library.add(
|
||||
faHome,
|
||||
faCheck,
|
||||
faCheckCircle,
|
||||
faSearch,
|
||||
...iconList
|
||||
)
|
||||
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@ import { getOdAuthTokens, storeOdAuthTokens } from '../../utils/odAuthTokenStore
|
||||
const basePath = pathPosix.resolve('/', siteConfig.baseDirectory)
|
||||
const clientSecret = revealObfuscatedToken(apiConfig.obfuscatedClientSecret)
|
||||
|
||||
const encodePath = (path: string) => {
|
||||
export function encodePath(path: string): string {
|
||||
let encodedPath = pathPosix.join(basePath, pathPosix.resolve('/', path))
|
||||
if (encodedPath === '/' || encodedPath === '') {
|
||||
return ''
|
||||
@@ -21,7 +21,7 @@ const encodePath = (path: string) => {
|
||||
return `:${encodeURIComponent(encodedPath)}`
|
||||
}
|
||||
|
||||
async function getAccessToken(): Promise<any> {
|
||||
export async function getAccessToken(): Promise<string> {
|
||||
const { accessToken, refreshToken } = await getOdAuthTokens()
|
||||
|
||||
// Return in storage access token if it is still valid
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import axios from 'axios'
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
import { encodePath, getAccessToken } from '.'
|
||||
import apiConfig from '../../config/api.json'
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
// Get access token from storage
|
||||
const accessToken = await getAccessToken()
|
||||
|
||||
// Query parameter from request
|
||||
const { q: searchQuery = '' } = req.query
|
||||
|
||||
if (typeof searchQuery === 'string') {
|
||||
// Construct Microsoft Graph Search API URL, and perform search only under the base dir
|
||||
const encodedPath = encodePath('/') === '' ? encodePath('/') : encodePath('/') + ':'
|
||||
const searchApi = `${apiConfig.driveApi}/root${encodedPath}/search(q='${encodeURIComponent(searchQuery)}')`
|
||||
|
||||
try {
|
||||
const { data } = await axios.get(searchApi, {
|
||||
headers: { Authorization: `Bearer ${accessToken}` },
|
||||
params: {
|
||||
select: 'id,name,file,folder,parentReference',
|
||||
},
|
||||
})
|
||||
res.status(200).json(data.value)
|
||||
} catch (error: any) {
|
||||
res.status(error.response.status).json({ error: error.response.data })
|
||||
}
|
||||
} else {
|
||||
res.status(200).json([])
|
||||
}
|
||||
return
|
||||
}
|
||||
Vendored
+31
@@ -13,3 +13,34 @@ export type OdFileObject = {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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'
|
||||
}
|
||||
}
|
||||
}>
|
||||
}
|
||||
|
||||
export type OdSearchResult = Array<{
|
||||
id: string
|
||||
name: string
|
||||
file?: OdFileObject
|
||||
folder?: OdFolderObject
|
||||
path: string
|
||||
parentReference: {
|
||||
id: string
|
||||
name: string
|
||||
path: string
|
||||
}
|
||||
}>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
export default function useDeviceOS(): string {
|
||||
const [os, setOS] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
const userAgent = window.navigator.userAgent
|
||||
|
||||
if (userAgent.indexOf('Windows') > -1) {
|
||||
setOS('windows')
|
||||
} else if (userAgent.indexOf('Mac OS') > -1) {
|
||||
setOS('mac')
|
||||
} else if (userAgent.indexOf('Linux') > -1) {
|
||||
setOS('linux')
|
||||
} else {
|
||||
setOS('other')
|
||||
}
|
||||
}, [])
|
||||
|
||||
return os
|
||||
}
|
||||
Reference in New Issue
Block a user