mirror of
https://github.com/Nezumi-2711/onedrive-vercel-index.git
synced 2026-09-22 13:38:45 +00:00
add trans support and hook all compnents excluding preview dir
This commit is contained in:
+6
-3
@@ -3,6 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
|||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { FC, useState } from 'react'
|
import { FC, useState } from 'react'
|
||||||
|
import { useTranslation } from 'next-i18next'
|
||||||
|
|
||||||
import { matchProtectedRoute } from '../utils/protectedRouteHandler'
|
import { matchProtectedRoute } from '../utils/protectedRouteHandler'
|
||||||
import useLocalStorage from '../utils/useLocalStorage'
|
import useLocalStorage from '../utils/useLocalStorage'
|
||||||
@@ -14,16 +15,18 @@ const Auth: FC<{ redirect: string }> = ({ redirect }) => {
|
|||||||
const [token, setToken] = useState('')
|
const [token, setToken] = useState('')
|
||||||
const [_, setPersistedToken] = useLocalStorage(authTokenPath, '')
|
const [_, setPersistedToken] = useLocalStorage(authTokenPath, '')
|
||||||
|
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="md:my-10 flex flex-col max-w-sm mx-auto space-y-4">
|
<div className="md:my-10 flex flex-col max-w-sm mx-auto space-y-4">
|
||||||
<div className="md:w-5/6 w-3/4 mx-auto">
|
<div className="md:w-5/6 w-3/4 mx-auto">
|
||||||
<Image src={'/images/fabulous-wapmire-weekdays.png'} alt="authenticate" width={912} height={912} priority />
|
<Image src={'/images/fabulous-wapmire-weekdays.png'} alt="authenticate" width={912} height={912} priority />
|
||||||
</div>
|
</div>
|
||||||
<div className="dark:text-gray-100 text-lg font-bold text-gray-900">Enter Password</div>
|
<div className="dark:text-gray-100 text-lg font-bold text-gray-900">{t('Enter Password')}</div>
|
||||||
|
|
||||||
<p className="text-sm text-gray-500 font-medium">
|
<p className="text-sm text-gray-500 font-medium">
|
||||||
This route (the folder itself and the files inside) is password protected. If you know the password, please
|
{t('This route (the folder itself and the files inside) is password protected. ') +
|
||||||
enter it below.
|
t('If you know the password, please enter it below.')}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
|||||||
import { IconProp } from '@fortawesome/fontawesome-svg-core'
|
import { IconProp } from '@fortawesome/fontawesome-svg-core'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { useClipboard } from 'use-clipboard-copy'
|
import { useClipboard } from 'use-clipboard-copy'
|
||||||
|
import { useTranslation } from 'next-i18next'
|
||||||
|
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
@@ -63,31 +64,33 @@ const DownloadButtonGroup: React.FC<{ downloadUrl: string }> = ({ downloadUrl })
|
|||||||
const { asPath } = useRouter()
|
const { asPath } = useRouter()
|
||||||
const clipboard = useClipboard()
|
const clipboard = useClipboard()
|
||||||
|
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-wrap justify-center gap-2">
|
<div className="flex flex-wrap justify-center gap-2">
|
||||||
<DownloadButton
|
<DownloadButton
|
||||||
onClickCallback={() => window.open(downloadUrl)}
|
onClickCallback={() => window.open(downloadUrl)}
|
||||||
btnColor="blue"
|
btnColor="blue"
|
||||||
btnText="Download"
|
btnText={t('Download')}
|
||||||
btnIcon="file-download"
|
btnIcon="file-download"
|
||||||
btnTitle="Download the file directly through OneDrive"
|
btnTitle={t('Download the file directly through OneDrive')}
|
||||||
/>
|
/>
|
||||||
{/* <DownloadButton
|
{/* <DownloadButton
|
||||||
onClickCallback={() => window.open(`/api/proxy?url=${encodeURIComponent(downloadUrl)}`)}
|
onClickCallback={() => window.open(`/api/proxy?url=${encodeURIComponent(downloadUrl)}`)}
|
||||||
btnColor="teal"
|
btnColor="teal"
|
||||||
btnText="Proxy download"
|
btnText={t('Proxy download')}
|
||||||
btnIcon="download"
|
btnIcon="download"
|
||||||
btnTitle="Download the file with the stream proxied through Vercel Serverless"
|
btnTitle={t('Download the file with the stream proxied through Vercel Serverless')}
|
||||||
/> */}
|
/> */}
|
||||||
<DownloadButton
|
<DownloadButton
|
||||||
onClickCallback={() => {
|
onClickCallback={() => {
|
||||||
clipboard.copy(`${getBaseUrl()}/api?path=${asPath}&raw=true`)
|
clipboard.copy(`${getBaseUrl()}/api?path=${asPath}&raw=true`)
|
||||||
toast.success('Copied direct link to clipboard.')
|
toast.success(t('Copied direct link to clipboard.'))
|
||||||
}}
|
}}
|
||||||
btnColor="pink"
|
btnColor="pink"
|
||||||
btnText="Copy direct link"
|
btnText={t('Copy direct link')}
|
||||||
btnIcon="copy"
|
btnIcon="copy"
|
||||||
btnTitle="Copy the permalink to the file to the clipboard"
|
btnTitle={t('Copy the permalink to the file to the clipboard')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -321,7 +321,10 @@ const FileListing: FC<{ query?: ParsedUrlQuery }> = ({ query }) => {
|
|||||||
{!onlyOnePage && (
|
{!onlyOnePage && (
|
||||||
<div className="rounded-b bg-white dark:bg-gray-900 dark:text-gray-100">
|
<div className="rounded-b bg-white dark:bg-gray-900 dark:text-gray-100">
|
||||||
<div className="border-b border-gray-200 p-3 text-center font-mono text-sm text-gray-400 dark:border-gray-700">
|
<div className="border-b border-gray-200 p-3 text-center font-mono text-sm text-gray-400 dark:border-gray-700">
|
||||||
- showing {size} page{size > 1 ? 's' : ''} of {isLoadingMore ? '...' : folderChildren.length} files -
|
{t('- showing {{count}} page(s) of {{totalFileNum}} files -', {
|
||||||
|
count: size,
|
||||||
|
totalFileNum: isLoadingMore ? '...' : folderChildren.length
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
className={`flex w-full items-center justify-center space-x-2 p-3 disabled:cursor-not-allowed ${
|
className={`flex w-full items-center justify-center space-x-2 p-3 disabled:cursor-not-allowed ${
|
||||||
@@ -406,7 +409,7 @@ const FileListing: FC<{ query?: ParsedUrlQuery }> = ({ query }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PreviewContainer>
|
<PreviewContainer>
|
||||||
<FourOhFour errorMsg={`Cannot preview ${path}`} />
|
<FourOhFour errorMsg={t('Cannot preview {{path}}', { path })} />
|
||||||
</PreviewContainer>
|
</PreviewContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import Link from 'next/link'
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import { useClipboard } from 'use-clipboard-copy'
|
import { useClipboard } from 'use-clipboard-copy'
|
||||||
|
import { useTranslation } from 'next-i18next'
|
||||||
|
|
||||||
import { getBaseUrl } from '../utils/getBaseUrl'
|
import { getBaseUrl } from '../utils/getBaseUrl'
|
||||||
import { formatModifiedDateTime } from '../utils/fileDetails'
|
import { formatModifiedDateTime } from '../utils/fileDetails'
|
||||||
@@ -65,6 +66,8 @@ const FolderGridLayout = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const clipboard = useClipboard()
|
const clipboard = useClipboard()
|
||||||
|
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded bg-white dark:bg-gray-900 dark:text-gray-100">
|
<div className="rounded bg-white dark:bg-gray-900 dark:text-gray-100">
|
||||||
<div className="flex items-center border-b border-gray-900/10 px-3 text-xs font-bold uppercase tracking-widest text-gray-600 dark:border-gray-500/30 dark:text-gray-400">
|
<div className="flex items-center border-b border-gray-900/10 px-3 text-xs font-bold uppercase tracking-widest text-gray-600 dark:border-gray-500/30 dark:text-gray-400">
|
||||||
@@ -74,13 +77,13 @@ const FolderGridLayout = ({
|
|||||||
checked={totalSelected}
|
checked={totalSelected}
|
||||||
onChange={toggleTotalSelected}
|
onChange={toggleTotalSelected}
|
||||||
indeterminate={true}
|
indeterminate={true}
|
||||||
title={'Select all files'}
|
title={t('Select all files')}
|
||||||
/>
|
/>
|
||||||
{totalGenerating ? (
|
{totalGenerating ? (
|
||||||
<Downloading title="Downloading selected files, refresh page to cancel" />
|
<Downloading title={t('Downloading selected files, refresh page to cancel')} />
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
title="Download selected files"
|
title={t('Download selected files')}
|
||||||
className="cursor-pointer rounded p-1.5 hover:bg-gray-300 disabled:cursor-not-allowed disabled:text-gray-400 disabled:hover:bg-white dark:hover:bg-gray-600 disabled:dark:text-gray-600 disabled:hover:dark:bg-gray-900"
|
className="cursor-pointer rounded p-1.5 hover:bg-gray-300 disabled:cursor-not-allowed disabled:text-gray-400 disabled:hover:bg-white dark:hover:bg-gray-600 disabled:dark:text-gray-600 disabled:hover:dark:bg-gray-900"
|
||||||
disabled={totalSelected === 0}
|
disabled={totalSelected === 0}
|
||||||
onClick={handleSelectedDownload}
|
onClick={handleSelectedDownload}
|
||||||
@@ -101,20 +104,20 @@ const FolderGridLayout = ({
|
|||||||
{c.folder ? (
|
{c.folder ? (
|
||||||
<div>
|
<div>
|
||||||
<span
|
<span
|
||||||
title="Copy folder permalink"
|
title={t('Copy folder permalink')}
|
||||||
className="cursor-pointer rounded px-1.5 py-1 hover:bg-gray-300 dark:hover:bg-gray-600"
|
className="cursor-pointer rounded px-1.5 py-1 hover:bg-gray-300 dark:hover:bg-gray-600"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
clipboard.copy(`${getBaseUrl()}${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`)
|
clipboard.copy(`${getBaseUrl()}${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`)
|
||||||
toast('Copied folder permalink.', { icon: '👌' })
|
toast(t('Copied folder permalink.'), { icon: '👌' })
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={['far', 'copy']} />
|
<FontAwesomeIcon icon={['far', 'copy']} />
|
||||||
</span>
|
</span>
|
||||||
{folderGenerating[c.id] ? (
|
{folderGenerating[c.id] ? (
|
||||||
<Downloading title="Downloading folder, refresh page to cancel" />
|
<Downloading title={t('Downloading folder, refresh page to cancel')} />
|
||||||
) : (
|
) : (
|
||||||
<span
|
<span
|
||||||
title="Download folder"
|
title={t('Download folder')}
|
||||||
className="cursor-pointer rounded px-1.5 py-1 hover:bg-gray-300 dark:hover:bg-gray-600"
|
className="cursor-pointer rounded px-1.5 py-1 hover:bg-gray-300 dark:hover:bg-gray-600"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const p = `${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`
|
const p = `${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`
|
||||||
@@ -128,19 +131,19 @@ const FolderGridLayout = ({
|
|||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
<span
|
<span
|
||||||
title="Copy raw file permalink"
|
title={t('Copy raw file permalink')}
|
||||||
className="cursor-pointer rounded px-1.5 py-1 hover:bg-gray-300 dark:hover:bg-gray-600"
|
className="cursor-pointer rounded px-1.5 py-1 hover:bg-gray-300 dark:hover:bg-gray-600"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
clipboard.copy(
|
clipboard.copy(
|
||||||
`${getBaseUrl()}/api?path=${path === '/' ? '' : path}/${encodeURIComponent(c.name)}&raw=true`
|
`${getBaseUrl()}/api?path=${path === '/' ? '' : path}/${encodeURIComponent(c.name)}&raw=true`
|
||||||
)
|
)
|
||||||
toast.success('Copied raw file permalink.')
|
toast.success(t('Copied raw file permalink.'))
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={['far', 'copy']} />
|
<FontAwesomeIcon icon={['far', 'copy']} />
|
||||||
</span>
|
</span>
|
||||||
<a
|
<a
|
||||||
title="Download file"
|
title={t('Download file')}
|
||||||
className="cursor-pointer rounded px-1.5 py-1 hover:bg-gray-300 dark:hover:bg-gray-600"
|
className="cursor-pointer rounded px-1.5 py-1 hover:bg-gray-300 dark:hover:bg-gray-600"
|
||||||
href={c['@microsoft.graph.downloadUrl']}
|
href={c['@microsoft.graph.downloadUrl']}
|
||||||
>
|
>
|
||||||
@@ -159,7 +162,7 @@ const FolderGridLayout = ({
|
|||||||
<Checkbox
|
<Checkbox
|
||||||
checked={selected[c.id] ? 2 : 0}
|
checked={selected[c.id] ? 2 : 0}
|
||||||
onChange={() => toggleItemSelected(c.id)}
|
onChange={() => toggleItemSelected(c.id)}
|
||||||
title="Select file"
|
title={t('Select file')}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+20
-13
@@ -1,4 +1,5 @@
|
|||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
|
import { Trans } from 'next-i18next'
|
||||||
|
|
||||||
const FourOhFour: React.FC<{ errorMsg: string }> = ({ errorMsg }) => {
|
const FourOhFour: React.FC<{ errorMsg: string }> = ({ errorMsg }) => {
|
||||||
return (
|
return (
|
||||||
@@ -8,24 +9,30 @@ const FourOhFour: React.FC<{ errorMsg: string }> = ({ errorMsg }) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="mt-6 text-gray-500 max-w-xl mx-auto">
|
<div className="mt-6 text-gray-500 max-w-xl mx-auto">
|
||||||
<div className="text-xl font-bold mb-8">
|
<div className="text-xl font-bold mb-8">
|
||||||
Oops, that's a <span className="underline decoration-wavy decoration-red-500">four-oh-four</span>.
|
<Trans>
|
||||||
|
Oops, that's a <span className="underline decoration-wavy decoration-red-500">four-oh-four</span>.
|
||||||
|
</Trans>
|
||||||
</div>
|
</div>
|
||||||
<div className="font-mono border border-gray-400/20 rounded p-2 mb-4 text-xs bg-gray-50 dark:bg-gray-800">
|
<div className="font-mono border border-gray-400/20 rounded p-2 mb-4 text-xs bg-gray-50 dark:bg-gray-800">
|
||||||
{errorMsg}
|
{errorMsg}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm">
|
<div className="text-sm">
|
||||||
Press{' '}
|
<Trans>
|
||||||
<kbd className="border-opacity-20 font-mono text-xs p-1 bg-gray-100 dark:bg-gray-800 border rounded">F12</kbd>{' '}
|
Press{' '}
|
||||||
and open devtools for more details, or seek help at{' '}
|
<kbd className="border-opacity-20 font-mono text-xs p-1 bg-gray-100 dark:bg-gray-800 border rounded">
|
||||||
<a
|
F12
|
||||||
className="text-blue-600 hover:text-blue-700 hover:underline"
|
</kbd>{' '}
|
||||||
href="https://github.com/spencerwooo/onedrive-vercel-index/discussions"
|
and open devtools for more details, or seek help at{' '}
|
||||||
target="_blank"
|
<a
|
||||||
rel="noopener noreferrer"
|
className="text-blue-600 hover:text-blue-700 hover:underline"
|
||||||
>
|
href="https://github.com/spencerwooo/onedrive-vercel-index/discussions"
|
||||||
onedrive-vercel-index discussions
|
target="_blank"
|
||||||
</a>
|
rel="noopener noreferrer"
|
||||||
.
|
>
|
||||||
|
onedrive-vercel-index discussions
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</Trans>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+15
-9
@@ -47,7 +47,7 @@ const Navbar = () => {
|
|||||||
localStorage.removeItem(r)
|
localStorage.removeItem(r)
|
||||||
})
|
})
|
||||||
|
|
||||||
toast.success('Cleared all tokens')
|
toast.success(t('Cleared all tokens'))
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
router.reload()
|
router.reload()
|
||||||
}, 1000)
|
}, 1000)
|
||||||
@@ -95,14 +95,20 @@ const Navbar = () => {
|
|||||||
className="flex items-center space-x-2 hover:opacity-80 dark:text-white"
|
className="flex items-center space-x-2 hover:opacity-80 dark:text-white"
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={['fab', l.name.toLowerCase() as IconName]} />
|
<FontAwesomeIcon icon={['fab', l.name.toLowerCase() as IconName]} />
|
||||||
<span className="hidden text-sm font-medium md:inline-block">{l.name}</span>
|
<span className="hidden text-sm font-medium md:inline-block">
|
||||||
|
{
|
||||||
|
// Append link name comments here to add translations
|
||||||
|
// t('Weibo')
|
||||||
|
t(l.name)
|
||||||
|
}
|
||||||
|
</span>
|
||||||
</a>
|
</a>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{siteConfig.email && (
|
{siteConfig.email && (
|
||||||
<a href={siteConfig.email} className="flex items-center space-x-2 hover:opacity-80 dark:text-white">
|
<a href={siteConfig.email} className="flex items-center space-x-2 hover:opacity-80 dark:text-white">
|
||||||
<FontAwesomeIcon icon={['far', 'envelope']} />
|
<FontAwesomeIcon icon={['far', 'envelope']} />
|
||||||
<span className="hidden text-sm font-medium md:inline-block">Email</span>
|
<span className="hidden text-sm font-medium md:inline-block">{t('Email')}</span>
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -111,7 +117,7 @@ const Navbar = () => {
|
|||||||
className="flex items-center space-x-2 p-2 hover:opacity-80 dark:text-white"
|
className="flex items-center space-x-2 p-2 hover:opacity-80 dark:text-white"
|
||||||
onClick={() => setIsOpen(true)}
|
onClick={() => setIsOpen(true)}
|
||||||
>
|
>
|
||||||
<span className="text-sm font-medium">Logout</span>
|
<span className="text-sm font-medium">{t('Logout')}</span>
|
||||||
<FontAwesomeIcon icon="sign-out-alt" />
|
<FontAwesomeIcon icon="sign-out-alt" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
@@ -148,12 +154,12 @@ const Navbar = () => {
|
|||||||
>
|
>
|
||||||
<div className="my-8 inline-block w-full max-w-md transform overflow-hidden rounded-lg bg-white p-6 text-left align-middle transition-all dark:bg-gray-900">
|
<div className="my-8 inline-block w-full max-w-md transform overflow-hidden rounded-lg bg-white p-6 text-left align-middle transition-all dark:bg-gray-900">
|
||||||
<Dialog.Title className="text-lg font-bold text-gray-900 dark:text-gray-100">
|
<Dialog.Title className="text-lg font-bold text-gray-900 dark:text-gray-100">
|
||||||
Clear all tokens?
|
{t('Clear all tokens?')}
|
||||||
</Dialog.Title>
|
</Dialog.Title>
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<p className="text-sm text-gray-500">
|
<p className="text-sm text-gray-500">
|
||||||
These tokens are used to authenticate yourself into password protected folders, clearing them means
|
{t('These tokens are used to authenticate yourself into password protected folders, ') +
|
||||||
that you will need to re-enter the passwords again.
|
t('clearing them means that you will need to re-enter the passwords again.')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -171,14 +177,14 @@ const Navbar = () => {
|
|||||||
className="mr-3 inline-flex items-center justify-center space-x-2 rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-400 focus:outline-none focus:ring focus:ring-blue-300"
|
className="mr-3 inline-flex items-center justify-center space-x-2 rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-400 focus:outline-none focus:ring focus:ring-blue-300"
|
||||||
onClick={() => setIsOpen(false)}
|
onClick={() => setIsOpen(false)}
|
||||||
>
|
>
|
||||||
Cancel
|
{t('Cancel')}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="inline-flex items-center justify-center space-x-2 rounded bg-red-500 px-4 py-2 text-white hover:bg-red-400 focus:outline-none focus:ring focus:ring-red-300"
|
className="inline-flex items-center justify-center space-x-2 rounded bg-red-500 px-4 py-2 text-white hover:bg-red-400 focus:outline-none focus:ring focus:ring-red-300"
|
||||||
onClick={() => clearTokens()}
|
onClick={() => clearTokens()}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={['far', 'trash-alt']} />
|
<FontAwesomeIcon icon={['far', 'trash-alt']} />
|
||||||
<span>Clear all</span>
|
<span>{t('Clear all')}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Dispatch, Fragment, SetStateAction, useState } from 'react'
|
|||||||
import AwesomeDebouncePromise from 'awesome-debounce-promise'
|
import AwesomeDebouncePromise from 'awesome-debounce-promise'
|
||||||
import { useAsync } from 'react-async-hook'
|
import { useAsync } from 'react-async-hook'
|
||||||
import useConstant from 'use-constant'
|
import useConstant from 'use-constant'
|
||||||
|
import { useTranslation } from 'next-i18next'
|
||||||
|
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
@@ -112,12 +113,19 @@ function SearchResultItemTemplate({
|
|||||||
function SearchResultItemLoadRemote({ result }: { result: OdSearchResult[number] }) {
|
function SearchResultItemLoadRemote({ result }: { result: OdSearchResult[number] }) {
|
||||||
const { data, error }: SWRResponse<OdDriveItem, string> = useSWR(`/api/item?id=${result.id}`, fetcher)
|
const { data, error }: SWRResponse<OdDriveItem, string> = useSWR(`/api/item?id=${result.id}`, fetcher)
|
||||||
|
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return <SearchResultItemTemplate driveItem={result} driveItemPath={''} itemDescription={error} disabled={true} />
|
return <SearchResultItemTemplate driveItem={result} driveItemPath={''} itemDescription={error} disabled={true} />
|
||||||
}
|
}
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return (
|
return (
|
||||||
<SearchResultItemTemplate driveItem={result} driveItemPath={''} itemDescription={'Loading ...'} disabled={true} />
|
<SearchResultItemTemplate
|
||||||
|
driveItem={result}
|
||||||
|
driveItemPath={''}
|
||||||
|
itemDescription={t('Loading ...')}
|
||||||
|
disabled={true}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,6 +167,8 @@ export default function SearchModal({
|
|||||||
}) {
|
}) {
|
||||||
const { query, setQuery, results } = useDriveItemSearch()
|
const { query, setQuery, results } = useDriveItemSearch()
|
||||||
|
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const closeSearchBox = () => {
|
const closeSearchBox = () => {
|
||||||
setSearchOpen(false)
|
setSearchOpen(false)
|
||||||
setQuery('')
|
setQuery('')
|
||||||
@@ -199,13 +209,13 @@ export default function SearchModal({
|
|||||||
type="text"
|
type="text"
|
||||||
id="search-box"
|
id="search-box"
|
||||||
className="w-full bg-transparent focus:outline-none focus-visible:outline-none"
|
className="w-full bg-transparent focus:outline-none focus-visible:outline-none"
|
||||||
placeholder="Search ..."
|
placeholder={t('Search ...')}
|
||||||
value={query}
|
value={query}
|
||||||
onChange={e => setQuery(e.target.value)}
|
onChange={e => setQuery(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<div className="px-2 py-1 rounded-lg bg-gray-200 dark:bg-gray-700 font-medium text-xs">ESC</div>
|
<div className="px-2 py-1 rounded-lg bg-gray-200 dark:bg-gray-700 font-medium text-xs">ESC</div>
|
||||||
</Dialog.Title>
|
</Dialog.Title>
|
||||||
|
<div className="text-center px-4 py-12 text-sm font-medium">{t('Nothing here.')}</div>
|
||||||
<div
|
<div
|
||||||
className="bg-white dark:text-white dark:bg-gray-900 max-h-[80vh] overflow-x-hidden overflow-y-scroll"
|
className="bg-white dark:text-white dark:bg-gray-900 max-h-[80vh] overflow-x-hidden overflow-y-scroll"
|
||||||
onClick={closeSearchBox}
|
onClick={closeSearchBox}
|
||||||
@@ -213,16 +223,18 @@ export default function SearchModal({
|
|||||||
{results.loading && (
|
{results.loading && (
|
||||||
<div className="text-center px-4 py-12 text-sm font-medium">
|
<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" />
|
<LoadingIcon className="animate-spin w-4 h-4 mr-2 inline-block svg-inline--fa" />
|
||||||
<span>Loading ...</span>
|
<span>{t('Loading ...')}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{results.error && (
|
{results.error && (
|
||||||
<div className="text-center px-4 py-12 text-sm font-medium">Error: {results.error.message}</div>
|
<div className="text-center px-4 py-12 text-sm font-medium">
|
||||||
|
{t('Error: {{message}}', { message: results.error.message })}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
{results.result && (
|
{results.result && (
|
||||||
<>
|
<>
|
||||||
{results.result.length === 0 ? (
|
{results.result.length === 0 ? (
|
||||||
<div className="text-center px-4 py-12 text-sm font-medium">Nothing here.</div>
|
<div className="text-center px-4 py-12 text-sm font-medium">{t('Nothing here.')}</div>
|
||||||
) : (
|
) : (
|
||||||
results.result.map(result => <SearchResultItem key={result.id} result={result} />)
|
results.result.map(result => <SearchResultItem key={result.id} result={result} />)
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
const tsTransform = require('i18next-scanner-typescript')
|
||||||
|
|
||||||
const { i18n, localePath } = require('./next-i18next.config')
|
const { i18n, localePath } = require('./next-i18next.config')
|
||||||
|
|
||||||
@@ -8,8 +9,10 @@ module.exports = {
|
|||||||
sort: true,
|
sort: true,
|
||||||
removeUnusedKeys: true,
|
removeUnusedKeys: true,
|
||||||
func: {
|
func: {
|
||||||
list: ['t'],
|
list: ['t']
|
||||||
extensions: ['.ts', '.tsx']
|
},
|
||||||
|
trans: {
|
||||||
|
fallbackKey: (_ns, val) => val
|
||||||
},
|
},
|
||||||
lngs: i18n.locales,
|
lngs: i18n.locales,
|
||||||
ns: ['common'],
|
ns: ['common'],
|
||||||
@@ -22,5 +25,6 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
nsSeparator: false,
|
nsSeparator: false,
|
||||||
keySeparator: false
|
keySeparator: false
|
||||||
}
|
},
|
||||||
|
transform: tsTransform()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,7 @@
|
|||||||
"eslint-config-next": "12.0.10",
|
"eslint-config-next": "12.0.10",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
"i18next-scanner": "^3.1.0",
|
"i18next-scanner": "^3.1.0",
|
||||||
|
"i18next-scanner-typescript": "^1.0.6",
|
||||||
"postcss": "^8.4.5",
|
"postcss": "^8.4.5",
|
||||||
"prettier": "^2.5.1",
|
"prettier": "^2.5.1",
|
||||||
"prettier-plugin-tailwindcss": "^0.1.4",
|
"prettier-plugin-tailwindcss": "^0.1.4",
|
||||||
|
|||||||
Generated
+14
@@ -27,6 +27,7 @@ specifiers:
|
|||||||
eslint-config-next: 12.0.10
|
eslint-config-next: 12.0.10
|
||||||
eslint-config-prettier: ^8.3.0
|
eslint-config-prettier: ^8.3.0
|
||||||
i18next-scanner: ^3.1.0
|
i18next-scanner: ^3.1.0
|
||||||
|
i18next-scanner-typescript: ^1.0.6
|
||||||
ioredis: ^4.28.2
|
ioredis: ^4.28.2
|
||||||
jszip: ^3.7.1
|
jszip: ^3.7.1
|
||||||
next: ^12.0.10
|
next: ^12.0.10
|
||||||
@@ -110,6 +111,7 @@ devDependencies:
|
|||||||
eslint-config-next: 12.0.10_9534215cc73b6f260bf33f1b86e3ae0e
|
eslint-config-next: 12.0.10_9534215cc73b6f260bf33f1b86e3ae0e
|
||||||
eslint-config-prettier: 8.3.0_eslint@8.8.0
|
eslint-config-prettier: 8.3.0_eslint@8.8.0
|
||||||
i18next-scanner: 3.1.0
|
i18next-scanner: 3.1.0
|
||||||
|
i18next-scanner-typescript: 1.0.6
|
||||||
postcss: 8.4.6
|
postcss: 8.4.6
|
||||||
prettier: 2.5.1
|
prettier: 2.5.1
|
||||||
prettier-plugin-tailwindcss: 0.1.4_prettier@2.5.1
|
prettier-plugin-tailwindcss: 0.1.4_prettier@2.5.1
|
||||||
@@ -2009,6 +2011,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-/MfAGMP0jHonV966uFf9PkWWuDjPYLIcsipnSO3NxpNtAgRUKLTwvm85fEmsF6hGeu0zbZiCQ3W74jwO6K9uXA==}
|
resolution: {integrity: sha512-/MfAGMP0jHonV966uFf9PkWWuDjPYLIcsipnSO3NxpNtAgRUKLTwvm85fEmsF6hGeu0zbZiCQ3W74jwO6K9uXA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/i18next-scanner-typescript/1.0.6:
|
||||||
|
resolution: {integrity: sha512-v6kULnsv32OlFM9pnWTH+wNtRXf5owmFUNBYcX78jbZxQBFnTCUo3w99JjnxuzuXA9ch4UKNQBnt2P/o94eyeQ==}
|
||||||
|
dependencies:
|
||||||
|
typescript: 3.9.10
|
||||||
|
dev: true
|
||||||
|
|
||||||
/i18next-scanner/3.1.0:
|
/i18next-scanner/3.1.0:
|
||||||
resolution: {integrity: sha512-dHLXUJIiF1CYJNslCkJFDYJySk5fg+dzdg9O73XXqHcdZwJ2947SWusqq8HdNFB7LpkBi8oTG6TWLZPmqbAh8Q==}
|
resolution: {integrity: sha512-dHLXUJIiF1CYJNslCkJFDYJySk5fg+dzdg9O73XXqHcdZwJ2947SWusqq8HdNFB7LpkBi8oTG6TWLZPmqbAh8Q==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@@ -4024,6 +4032,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==}
|
resolution: {integrity: sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/typescript/3.9.10:
|
||||||
|
resolution: {integrity: sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==}
|
||||||
|
engines: {node: '>=4.2.0'}
|
||||||
|
hasBin: true
|
||||||
|
dev: true
|
||||||
|
|
||||||
/typescript/4.5.5:
|
/typescript/4.5.5:
|
||||||
resolution: {integrity: sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==}
|
resolution: {integrity: sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==}
|
||||||
engines: {node: '>=4.2.0'}
|
engines: {node: '>=4.2.0'}
|
||||||
|
|||||||
Reference in New Issue
Block a user