From 73651e4f50a725c95320dce6cfbf33ea20cb1fa4 Mon Sep 17 00:00:00 2001 From: myl7 Date: Tue, 8 Feb 2022 02:54:48 +0800 Subject: [PATCH 1/2] add getReadablePath util to make copied url readable --- components/DownloadBtnGtoup.tsx | 3 +- components/FolderGridLayout.tsx | 9 ++++-- components/FolderListLayout.tsx | 9 ++++-- components/previews/VideoPreview.tsx | 3 +- utils/getReadablePath.ts | 43 ++++++++++++++++++++++++++++ 5 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 utils/getReadablePath.ts diff --git a/components/DownloadBtnGtoup.tsx b/components/DownloadBtnGtoup.tsx index 7f0eabc..de4d379 100644 --- a/components/DownloadBtnGtoup.tsx +++ b/components/DownloadBtnGtoup.tsx @@ -8,6 +8,7 @@ import Image from 'next/image' import { useRouter } from 'next/router' import { getBaseUrl } from '../utils/getBaseUrl' +import { getReadablePath } from '../utils/getReadablePath' const btnStyleMap = (btnColor?: string) => { const colorMap = { @@ -81,7 +82,7 @@ const DownloadButtonGroup: React.FC<{ downloadUrl: string }> = ({ downloadUrl }) /> */} { - clipboard.copy(`${getBaseUrl()}/api?path=${asPath}&raw=true`) + clipboard.copy(`${getBaseUrl()}/api?path=${getReadablePath(asPath)}&raw=true`) toast.success('Copied direct link to clipboard.') }} btnColor="pink" diff --git a/components/FolderGridLayout.tsx b/components/FolderGridLayout.tsx index 7f32807..567793b 100644 --- a/components/FolderGridLayout.tsx +++ b/components/FolderGridLayout.tsx @@ -7,6 +7,7 @@ import { useClipboard } from 'use-clipboard-copy' import { getBaseUrl } from '../utils/getBaseUrl' import { formatModifiedDateTime } from '../utils/fileDetails' +import { getReadablePath } from '../utils/getReadablePath' import { Checkbox, ChildIcon, Downloading, formatChildName } from './FileListing' const GridItem = ({ c }: { c: OdFolderChildren }) => { @@ -104,7 +105,9 @@ const FolderGridLayout = ({ title="Copy folder permalink" className="cursor-pointer rounded px-1.5 py-1 hover:bg-gray-300 dark:hover:bg-gray-600" onClick={() => { - clipboard.copy(`${getBaseUrl()}${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`) + clipboard.copy( + `${getBaseUrl()}${getReadablePath(`${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`)}` + ) toast('Copied folder permalink.', { icon: '👌' }) }} > @@ -132,7 +135,9 @@ const FolderGridLayout = ({ className="cursor-pointer rounded px-1.5 py-1 hover:bg-gray-300 dark:hover:bg-gray-600" onClick={() => { 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('Copied raw file permalink.') }} diff --git a/components/FolderListLayout.tsx b/components/FolderListLayout.tsx index 897cd70..2537e3f 100644 --- a/components/FolderListLayout.tsx +++ b/components/FolderListLayout.tsx @@ -7,6 +7,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { getBaseUrl } from '../utils/getBaseUrl' import { humanFileSize, formatModifiedDateTime } from '../utils/fileDetails' +import { getReadablePath } from '../utils/getReadablePath' import { Downloading, Checkbox, formatChildName, ChildIcon } from './FileListing' @@ -100,7 +101,9 @@ const FolderListLayout = ({ title="Copy folder permalink" className="cursor-pointer rounded px-1.5 py-1 hover:bg-gray-300 dark:hover:bg-gray-600" onClick={() => { - clipboard.copy(`${getBaseUrl()}${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`) + clipboard.copy( + `${getBaseUrl()}${getReadablePath(`${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`)}` + ) toast('Copied folder permalink.', { icon: '👌' }) }} > @@ -128,7 +131,9 @@ const FolderListLayout = ({ className="cursor-pointer rounded px-1.5 py-1 hover:bg-gray-300 dark:hover:bg-gray-600" onClick={() => { 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('Copied raw file permalink.') }} diff --git a/components/previews/VideoPreview.tsx b/components/previews/VideoPreview.tsx index e5c0765..875825d 100644 --- a/components/previews/VideoPreview.tsx +++ b/components/previews/VideoPreview.tsx @@ -8,6 +8,7 @@ import { getBaseUrl } from '../../utils/getBaseUrl' import { DownloadButton } from '../DownloadBtnGtoup' import { DownloadBtnContainer, PreviewContainer } from './Containers' import { getExtension } from '../../utils/getFileIcon' +import { getReadablePath } from '../../utils/getReadablePath' const VideoPreview: React.FC<{ file: OdFileObject }> = ({ file }) => { const { asPath } = useRouter() @@ -54,7 +55,7 @@ const VideoPreview: React.FC<{ file: OdFileObject }> = ({ file }) => { /> */} { - clipboard.copy(`${getBaseUrl()}/api?path=${asPath}&raw=true`) + clipboard.copy(`${getBaseUrl()}/api?path=${getReadablePath(asPath)}&raw=true`) toast.success('Copied direct link to clipboard.') }} btnColor="pink" diff --git a/utils/getReadablePath.ts b/utils/getReadablePath.ts new file mode 100644 index 0000000..9081feb --- /dev/null +++ b/utils/getReadablePath.ts @@ -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 +} From 070394a66160fb3b6f09253aa9a7307e234a1d13 Mon Sep 17 00:00:00 2001 From: spencerwooo Date: Tue, 8 Feb 2022 16:31:56 +0800 Subject: [PATCH 2/2] some housekeeping --- components/DownloadBtnGtoup.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/DownloadBtnGtoup.tsx b/components/DownloadBtnGtoup.tsx index de4d379..22e5fd6 100644 --- a/components/DownloadBtnGtoup.tsx +++ b/components/DownloadBtnGtoup.tsx @@ -47,7 +47,7 @@ export const DownloadButton = ({ }) => { return (