From 17263feea95eb862d0331897a86b65fd2c443cba Mon Sep 17 00:00:00 2001 From: spencerwooo Date: Mon, 24 Jan 2022 20:31:17 +0800 Subject: [PATCH] load item path on result mount --- components/SearchModal.tsx | 148 +++++++++++++++++++++++++++++-------- types/index.d.ts | 15 ++++ utils/fetchOnMount.ts | 2 +- 3 files changed, 132 insertions(+), 33 deletions(-) diff --git a/components/SearchModal.tsx b/components/SearchModal.tsx index 8c0f951..4983442 100644 --- a/components/SearchModal.tsx +++ b/components/SearchModal.tsx @@ -4,25 +4,39 @@ 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 { Dispatch, 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 { OdDriveItem, OdSearchResult } from '../types' import { LoadingIcon } from './Loading' +import { getFileIcon } from '../utils/getFileIcon' +import useAxiosGet from '../utils/fetchOnMount' +import siteConfig from '../config/site.json' + +/** + * Extract the searched item's path in field 'parentReference' and convert it to the + * absolute path represented in onedrive-vercel-index + * + * @param path Path returned from the parentReference field of the driveItem + * @returns The absolute path of the driveItem in the search result + */ +function mapAbsolutePath(path: string): string { + return path.split(siteConfig.baseDirectory === '/' ? 'root:' : siteConfig.baseDirectory)[1] +} + +/** + * Implements a debounced search function that returns a promise that resolves to an array of + * search results. + * + * @returns A react hook for a debounced async search of the drive + */ function useDriveItemSearch() { const [query, setQuery] = useState('') const searchDriveItem = async (q: string) => { const { data } = await axios.get(`/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'] = @@ -36,12 +50,12 @@ function useDriveItemSearch() { return data } - const debouncedNotionSearch = useConstant(() => AwesomeDebouncePromise(searchDriveItem, 1000)) + const debouncedDriveItemSearch = useConstant(() => AwesomeDebouncePromise(searchDriveItem, 1000)) const results = useAsync(async () => { if (query.length === 0) { return [] } else { - return debouncedNotionSearch(query) + return debouncedDriveItemSearch(query) } }, [query]) @@ -52,7 +66,91 @@ function useDriveItemSearch() { } } -function SearchModal({ +function SearchResultItemTemplate({ + driveItem, + driveItemPath, + itemDescription, + disabled, +}: { + driveItem: OdSearchResult[number] + driveItemPath: string + itemDescription: string + disabled: boolean +}) { + return ( + + + +
+
{driveItem.name}
+
+ {itemDescription} +
+
+
+ + ) +} + +function SearchResultItemLoadRemote({ result }: { result: OdSearchResult[number] }) { + const { + content, + error, + validating, + }: { + content: OdDriveItem + error: string + validating: boolean + } = useAxiosGet(`/api/item?id=${result.id}`) + + if (error) { + return + } + + if (validating) { + return ( + + ) + } + + const driveItemPath = `${mapAbsolutePath(content.parentReference.path)}/${encodeURIComponent(content.name)}` + return ( + + ) +} + +function SearchResultItem({ result }: { result: OdSearchResult[number] }) { + if (result.path === '') { + // path is empty, which means we need to fetch the parentReference to get the path + return + } else { + // path is not an empty string in the search result, such that we can directly render the component as is + const driveItemPath = decodeURIComponent(result.path) + return ( + + ) + } +} + +export default function SearchModal({ searchOpen, setSearchOpen, }: { @@ -102,10 +200,13 @@ function SearchModal({ value={query} onChange={e => setQuery(e.target.value)} /> -
ESC
+
ESC
-
+
{results.loading && (
@@ -120,22 +221,7 @@ function SearchModal({ {results.result.length === 0 ? (
Nothing here.
) : ( - results.result.map(result => ( - -
- -
-
{result.name}
-
- {decodeURIComponent(result.path)} -
-
-
- - )) + results.result.map(result => ) )} )} @@ -147,5 +233,3 @@ function SearchModal({ ) } - -export default SearchModal diff --git a/types/index.d.ts b/types/index.d.ts index f93f54d..d03c7c1 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -32,6 +32,7 @@ export type OdFolderObject = { }> } +// Search result type which is returned by /api/search?q={query} export type OdSearchResult = Array<{ id: string name: string @@ -44,3 +45,17 @@ export type OdSearchResult = Array<{ path: string } }> + +// driveItem type which is returned by /api/item?id={id} +export type OdDriveItem = { + '@odata.context': string + '@odata.etag': string + id: string + name: string + parentReference: { + driveId: string + driveType: string + id: string + path: string + } +} diff --git a/utils/fetchOnMount.ts b/utils/fetchOnMount.ts index 116df31..05595d7 100644 --- a/utils/fetchOnMount.ts +++ b/utils/fetchOnMount.ts @@ -2,7 +2,7 @@ import axios from 'axios' import { useEffect, useState } from 'react' // Custom hook to fetch raw file content on mount -export default function useAxiosGet(fetchUrl: string): { content: string; error: string; validating: boolean } { +export default function useAxiosGet(fetchUrl: string): { content: any; error: string; validating: boolean } { const [content, setContent] = useState('') const [validating, setValidating] = useState(true) const [error, setError] = useState('')