mirror of
https://github.com/Nezumi-2711/onedrive-vercel-index.git
synced 2026-09-22 13:38:45 +00:00
Merge branch 'main' into i18n
This commit is contained in:
@@ -9,6 +9,7 @@ import Image from 'next/image'
|
|||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
|
|
||||||
import { getBaseUrl } from '../utils/getBaseUrl'
|
import { getBaseUrl } from '../utils/getBaseUrl'
|
||||||
|
import { getReadablePath } from '../utils/getReadablePath'
|
||||||
|
|
||||||
const btnStyleMap = (btnColor?: string) => {
|
const btnStyleMap = (btnColor?: string) => {
|
||||||
const colorMap = {
|
const colorMap = {
|
||||||
@@ -47,7 +48,7 @@ export const DownloadButton = ({
|
|||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className={`flex items-center space-x-2 py-2 px-4 text-sm font-medium text-gray-900 bg-white rounded-lg border hover:bg-gray-100/10 focus:z-10 focus:ring-2 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-900 ${btnStyleMap(
|
className={`flex items-center space-x-2 rounded-lg border bg-white py-2 px-4 text-sm font-medium text-gray-900 hover:bg-gray-100/10 focus:z-10 focus:ring-2 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-900 ${btnStyleMap(
|
||||||
btnColor
|
btnColor
|
||||||
)}`}
|
)}`}
|
||||||
title={btnTitle}
|
title={btnTitle}
|
||||||
@@ -84,7 +85,7 @@ const DownloadButtonGroup: React.FC<{ downloadUrl: string }> = ({ downloadUrl })
|
|||||||
/> */}
|
/> */}
|
||||||
<DownloadButton
|
<DownloadButton
|
||||||
onClickCallback={() => {
|
onClickCallback={() => {
|
||||||
clipboard.copy(`${getBaseUrl()}/api?path=${asPath}&raw=true`)
|
clipboard.copy(`${getBaseUrl()}/api?path=${getReadablePath(asPath)}&raw=true`)
|
||||||
toast.success(t('Copied direct link to clipboard.'))
|
toast.success(t('Copied direct link to clipboard.'))
|
||||||
}}
|
}}
|
||||||
btnColor="pink"
|
btnColor="pink"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ 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'
|
||||||
|
import { getReadablePath } from '../utils/getReadablePath'
|
||||||
import { Checkbox, ChildIcon, Downloading, formatChildName } from './FileListing'
|
import { Checkbox, ChildIcon, Downloading, formatChildName } from './FileListing'
|
||||||
|
|
||||||
const GridItem = ({ c }: { c: OdFolderChildren }) => {
|
const GridItem = ({ c }: { c: OdFolderChildren }) => {
|
||||||
@@ -107,7 +108,9 @@ const FolderGridLayout = ({
|
|||||||
title={t('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()}${getReadablePath(`${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`)}`
|
||||||
|
)
|
||||||
toast(t('Copied folder permalink.'), { icon: '👌' })
|
toast(t('Copied folder permalink.'), { icon: '👌' })
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -135,7 +138,9 @@ const FolderGridLayout = ({
|
|||||||
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=${getReadablePath(
|
||||||
|
`${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`
|
||||||
|
)}&raw=true`
|
||||||
)
|
)
|
||||||
toast.success(t('Copied raw file permalink.'))
|
toast.success(t('Copied raw file permalink.'))
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { useTranslation } from 'next-i18next'
|
|||||||
|
|
||||||
import { getBaseUrl } from '../utils/getBaseUrl'
|
import { getBaseUrl } from '../utils/getBaseUrl'
|
||||||
import { humanFileSize, formatModifiedDateTime } from '../utils/fileDetails'
|
import { humanFileSize, formatModifiedDateTime } from '../utils/fileDetails'
|
||||||
|
import { getReadablePath } from '../utils/getReadablePath'
|
||||||
|
|
||||||
import { Downloading, Checkbox, formatChildName, ChildIcon } from './FileListing'
|
import { Downloading, Checkbox, formatChildName, ChildIcon } from './FileListing'
|
||||||
|
|
||||||
@@ -103,7 +104,9 @@ const FolderListLayout = ({
|
|||||||
title={t('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()}${getReadablePath(`${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`)}`
|
||||||
|
)
|
||||||
toast(t('Copied folder permalink.'), { icon: '👌' })
|
toast(t('Copied folder permalink.'), { icon: '👌' })
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -131,7 +134,9 @@ const FolderListLayout = ({
|
|||||||
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=${getReadablePath(
|
||||||
|
`${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`
|
||||||
|
)}&raw=true`
|
||||||
)
|
)
|
||||||
toast.success(t('Copied raw file permalink.'))
|
toast.success(t('Copied raw file permalink.'))
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -4,11 +4,15 @@ import { useClipboard } from 'use-clipboard-copy'
|
|||||||
import DPlayer from 'react-dplayer'
|
import DPlayer from 'react-dplayer'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
|
import { useAsync } from 'react-async-hook'
|
||||||
|
|
||||||
import { getBaseUrl } from '../../utils/getBaseUrl'
|
import { getBaseUrl } from '../../utils/getBaseUrl'
|
||||||
|
import { getExtension } from '../../utils/getFileIcon'
|
||||||
|
import { getReadablePath } from '../../utils/getReadablePath'
|
||||||
import { DownloadButton } from '../DownloadBtnGtoup'
|
import { DownloadButton } from '../DownloadBtnGtoup'
|
||||||
import { DownloadBtnContainer, PreviewContainer } from './Containers'
|
import { DownloadBtnContainer, PreviewContainer } from './Containers'
|
||||||
import { getExtension } from '../../utils/getFileIcon'
|
import FourOhFour from '../FourOhFour'
|
||||||
|
import Loading from '../Loading'
|
||||||
|
|
||||||
const VideoPreview: React.FC<{ file: OdFileObject }> = ({ file }) => {
|
const VideoPreview: React.FC<{ file: OdFileObject }> = ({ file }) => {
|
||||||
const { asPath } = useRouter()
|
const { asPath } = useRouter()
|
||||||
@@ -22,21 +26,49 @@ const VideoPreview: React.FC<{ file: OdFileObject }> = ({ file }) => {
|
|||||||
// We assume subtitle files are beside the video with the same name, only webvtt '.vtt' files are supported
|
// We assume subtitle files are beside the video with the same name, only webvtt '.vtt' files are supported
|
||||||
const subtitle = `/api?path=${asPath.replace(getExtension(file.name), 'vtt')}&raw=true`
|
const subtitle = `/api?path=${asPath.replace(getExtension(file.name), 'vtt')}&raw=true`
|
||||||
|
|
||||||
|
const isFlv = getExtension(file.name) === 'flv'
|
||||||
|
const {
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
result: flvjs,
|
||||||
|
} = useAsync(async () => {
|
||||||
|
if (isFlv) {
|
||||||
|
return (await import('flv.js')).default
|
||||||
|
}
|
||||||
|
}, [isFlv])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PreviewContainer>
|
<PreviewContainer>
|
||||||
<DPlayer
|
{error ? (
|
||||||
className="aspect-video"
|
<FourOhFour errorMsg={error.message} />
|
||||||
options={{
|
) : loading && isFlv ? (
|
||||||
volume: 1.0,
|
<Loading loadingText="Loading FLV extension..." />
|
||||||
lang: 'en',
|
) : (
|
||||||
video: {
|
<DPlayer
|
||||||
url: file['@microsoft.graph.downloadUrl'],
|
className="aspect-video"
|
||||||
pic: thumbnail,
|
options={{
|
||||||
},
|
volume: 1.0,
|
||||||
subtitle: { url: subtitle },
|
lang: 'en',
|
||||||
}}
|
video: {
|
||||||
/>
|
url: file['@microsoft.graph.downloadUrl'],
|
||||||
|
pic: thumbnail,
|
||||||
|
type: isFlv ? 'customFlv' : 'auto',
|
||||||
|
customType: {
|
||||||
|
customFlv: (video: HTMLVideoElement) => {
|
||||||
|
const flvPlayer = flvjs!.createPlayer({
|
||||||
|
type: 'flv',
|
||||||
|
url: video.src,
|
||||||
|
})
|
||||||
|
flvPlayer.attachMediaElement(video)
|
||||||
|
flvPlayer.load()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
subtitle: { url: subtitle },
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</PreviewContainer>
|
</PreviewContainer>
|
||||||
|
|
||||||
<DownloadBtnContainer>
|
<DownloadBtnContainer>
|
||||||
@@ -57,7 +89,7 @@ const VideoPreview: React.FC<{ file: OdFileObject }> = ({ file }) => {
|
|||||||
/> */}
|
/> */}
|
||||||
<DownloadButton
|
<DownloadButton
|
||||||
onClickCallback={() => {
|
onClickCallback={() => {
|
||||||
clipboard.copy(`${getBaseUrl()}/api?path=${asPath}&raw=true`)
|
clipboard.copy(`${getBaseUrl()}/api?path=${getReadablePath(asPath)}&raw=true`)
|
||||||
toast.success(t('Copied direct link to clipboard.'))
|
toast.success(t('Copied direct link to clipboard.'))
|
||||||
}}
|
}}
|
||||||
btnColor="pink"
|
btnColor="pink"
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
"emoji-regex": "^10.0.0",
|
"emoji-regex": "^10.0.0",
|
||||||
|
"flv.js": "^1.6.2",
|
||||||
"ioredis": "^4.28.2",
|
"ioredis": "^4.28.2",
|
||||||
"jszip": "^3.7.1",
|
"jszip": "^3.7.1",
|
||||||
"next": "^12.0.10",
|
"next": "^12.0.10",
|
||||||
|
|||||||
+11
-2
@@ -9,10 +9,19 @@ import siteConfig from '../../config/site.config'
|
|||||||
* Sanitize the search query
|
* Sanitize the search query
|
||||||
*
|
*
|
||||||
* @param query User search query, which may contain special characters
|
* @param query User search query, which may contain special characters
|
||||||
* @returns Sanitised query string which replaces non-alphanumeric characters with ' '
|
* @returns Sanitised query string, which:
|
||||||
|
* - encodes the '<' and '>' characters,
|
||||||
|
* - replaces '?' and '/' characters with ' ',
|
||||||
|
* - replaces ''' with ''''
|
||||||
|
* Reference: https://stackoverflow.com/questions/41491222/single-quote-escaping-in-microsoft-graph.
|
||||||
*/
|
*/
|
||||||
function sanitiseQuery(query: string): string {
|
function sanitiseQuery(query: string): string {
|
||||||
const sanitisedQuery = query.replace(/[^a-zA-Z0-9]/g, ' ')
|
const sanitisedQuery = query
|
||||||
|
.replace(/'/g, "''")
|
||||||
|
.replace('<', ' < ')
|
||||||
|
.replace('>', ' > ')
|
||||||
|
.replace('?', ' ')
|
||||||
|
.replace('/', ' ')
|
||||||
return encodeURIComponent(sanitisedQuery)
|
return encodeURIComponent(sanitisedQuery)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Generated
+16
@@ -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-parser: ^5.4.0
|
i18next-parser: ^5.4.0
|
||||||
|
flv.js: ^1.6.2
|
||||||
ioredis: ^4.28.2
|
ioredis: ^4.28.2
|
||||||
jszip: ^3.7.1
|
jszip: ^3.7.1
|
||||||
next: ^12.0.10
|
next: ^12.0.10
|
||||||
@@ -70,6 +71,7 @@ dependencies:
|
|||||||
cors: 2.8.5
|
cors: 2.8.5
|
||||||
crypto-js: 4.1.1
|
crypto-js: 4.1.1
|
||||||
emoji-regex: 10.0.0
|
emoji-regex: 10.0.0
|
||||||
|
flv.js: 1.6.2
|
||||||
ioredis: 4.28.3
|
ioredis: 4.28.3
|
||||||
jszip: 3.7.1
|
jszip: 3.7.1
|
||||||
next: 12.0.10_react-dom@17.0.2+react@17.0.2
|
next: 12.0.10_react-dom@17.0.2+react@17.0.2
|
||||||
@@ -1387,6 +1389,10 @@ packages:
|
|||||||
es6-symbol: 3.1.3
|
es6-symbol: 3.1.3
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/es6-promise/4.2.8:
|
||||||
|
resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/es6-symbol/3.1.3:
|
/es6-symbol/3.1.3:
|
||||||
resolution: {integrity: sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==}
|
resolution: {integrity: sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -1746,6 +1752,12 @@ packages:
|
|||||||
inherits: 2.0.4
|
inherits: 2.0.4
|
||||||
readable-stream: 2.3.7
|
readable-stream: 2.3.7
|
||||||
dev: true
|
dev: true
|
||||||
|
/flv.js/1.6.2:
|
||||||
|
resolution: {integrity: sha512-xre4gUbX1MPtgQRKj2pxJENp/RnaHaxYvy3YToVVCrSmAWUu85b9mug6pTXF6zakUjNP2lFWZ1rkSX7gxhB/2A==}
|
||||||
|
dependencies:
|
||||||
|
es6-promise: 4.2.8
|
||||||
|
webworkify-webpack: 2.1.5
|
||||||
|
dev: false
|
||||||
|
|
||||||
/follow-redirects/1.14.7:
|
/follow-redirects/1.14.7:
|
||||||
resolution: {integrity: sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==}
|
resolution: {integrity: sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==}
|
||||||
@@ -4486,6 +4498,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
|
resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/webworkify-webpack/2.1.5:
|
||||||
|
resolution: {integrity: sha512-2akF8FIyUvbiBBdD+RoHpoTbHMQF2HwjcxfDvgztAX5YwbZNyrtfUMgvfgFVsgDhDPVTlkbb5vyasqDHfIDPQw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/which-boxed-primitive/1.0.2:
|
/which-boxed-primitive/1.0.2:
|
||||||
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
|
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* Make path readable but still valid in URL (means the whole URL is still recognized as a URL)
|
||||||
|
* @param path Path. May be used as URL path or query value.
|
||||||
|
* @returns Readable but still valid path
|
||||||
|
*/
|
||||||
|
export function getReadablePath(path: string) {
|
||||||
|
path = path
|
||||||
|
.split('/')
|
||||||
|
.map(s => decodeURIComponent(s))
|
||||||
|
.map(s =>
|
||||||
|
Array.from(s)
|
||||||
|
.map(c => (isSafeChar(c) ? c : encodeURIComponent(c)))
|
||||||
|
.join('')
|
||||||
|
)
|
||||||
|
.join('/')
|
||||||
|
// Handle trailing char for autolink
|
||||||
|
// Ref: https://github.github.com/gfm/#autolinks-extension-
|
||||||
|
if (/^[!,:*]$/.test(path[path.length - 1])) {
|
||||||
|
path = path.substring(0, path.length - 1) + encodeURIComponent(path[path.length - 1])
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the character is safe (means no need of percent-encoding)
|
||||||
|
function isSafeChar(c: string) {
|
||||||
|
if (c.charCodeAt(0) < 0x80) {
|
||||||
|
// ASCII
|
||||||
|
if (/^[a-zA-Z0-9\-._~]$/.test(c)) {
|
||||||
|
// RFC3986 unreserved chars
|
||||||
|
return true
|
||||||
|
} else if (/^[*:+@,!]$/.test(c)) {
|
||||||
|
// Some extra pretty safe chars for URL path or query
|
||||||
|
// Ref: https://stackoverflow.com/a/42287988/11691878
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!/\s|\u180e/.test(c)) {
|
||||||
|
// Non-whitespace char. \u180e is missed in \s.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user