From c16d0ab2db147fa481c0378a670c0285d64d9168 Mon Sep 17 00:00:00 2001 From: spencerwooo Date: Fri, 4 Feb 2022 17:22:40 +0800 Subject: [PATCH 01/12] new breadcrumb that scrolls to the end automatically --- components/Breadcrumb.tsx | 60 +++++++++++++++++++++++++------------- components/FileListing.tsx | 56 +++++++++++++++++------------------ pages/[...path].tsx | 10 ++++--- pages/_app.tsx | 4 +++ pages/index.tsx | 10 ++++--- 5 files changed, 84 insertions(+), 56 deletions(-) diff --git a/components/Breadcrumb.tsx b/components/Breadcrumb.tsx index b939401..4009265 100644 --- a/components/Breadcrumb.tsx +++ b/components/Breadcrumb.tsx @@ -1,41 +1,61 @@ -import Link from 'next/link' +import type { ParsedUrlQuery } from 'querystring' -import { ParsedUrlQuery } from 'querystring' +import Link from 'next/link' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' + +const HomeCrumb = () => { + return ( + + + + Home + + + ) +} const Breadcrumb: React.FC<{ query?: ParsedUrlQuery }> = ({ query }) => { if (query) { const { path } = query if (Array.isArray(path)) { + // We are rendering the path in reverse, so that the browser automatically scrolls to the end of the breadcrumb + // https://stackoverflow.com/questions/18614301/keep-overflow-div-scrolled-to-bottom-unless-user-scrolls-up/18614561 return ( -
-
- 🚩 Home -
- {path.map((q: string, i: number) => ( -
-
/
-
+
    + {path + .slice(0) + .reverse() + .map((p: string, i: number) => ( +
  1. + encodeURIComponent(p)) .join('/')}`} + passHref > - {q} + + {p} + -
-
- ))} -
+ + ))} +
  • + +
  • + ) } } return ( -
    -
    - 🚩 Home -
    +
    +
    ) } diff --git a/components/FileListing.tsx b/components/FileListing.tsx index 9ffff95..4b9d582 100644 --- a/components/FileListing.tsx +++ b/components/FileListing.tsx @@ -66,10 +66,10 @@ const FileListItem: FC<{ fileContent: OdFolderObject['value'][number] }> = ({ fi const renderEmoji = emojiIcon && !emojiIcon.index return ( -
    -
    +
    +
    {/*
    {c.file ? c.file.mimeType : 'folder'}
    */} -
    +
    {renderEmoji ? ( {emojiIcon ? emojiIcon[0] : '📁'} ) : ( @@ -80,10 +80,10 @@ const FileListItem: FC<{ fileContent: OdFolderObject['value'][number] }> = ({ fi {renderEmoji ? c.name.replace(emojiIcon ? emojiIcon[0] : '', '').trim() : c.name}
    -
    +
    {formatModifiedDateTime(c.lastModifiedDateTime)}
    -
    +
    {humanFileSize(c.size)}
    @@ -120,7 +120,7 @@ const Checkbox: FC<{ return ( = ({ title }) => { return ( - + ) @@ -337,22 +337,22 @@ const FileListing: FC<{ query?: ParsedUrlQuery }> = ({ query }) => { return ( <> -
    -
    -
    +
    +
    +
    Name
    -
    +
    Last Modified
    -
    +
    Size
    -
    +
    Actions
    -
    -
    +
    +
    = ({ query }) => { ) : ( @@ -89,23 +89,23 @@ const Navbar = () => { href={l.link} target="_blank" rel="noopener noreferrer" - className="flex items-center space-x-2 dark:text-white hover:opacity-80" + className="flex items-center space-x-2 hover:opacity-80 dark:text-white" > - {l.name} + {l.name} ))} {siteConfig.email && ( - + - Email + Email )} {tokenPresent && ( - )} -
    -
    -
    + {layout.name === 'Grid' ? : } - {children.map(c => ( -
    - - - - - - - {c.folder ? ( -
    - { - clipboard.copy(`${getBaseUrl()}${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`) - toast('Copied folder permalink.', { icon: '👌' }) - }} - > - - - {folderGenerating[c.id] ? ( - - ) : ( - { - const p = `${path === '/' ? '' : path}/${encodeURIComponent(c.name)}` - handleFolderDownload(p, c.id, c.name)() - }} - > - - - )} -
    + {!onlyOnePage && ( +
    +
    + - showing {size} page{size > 1 ? 's' : ''} of {isLoadingMore ? '...' : folderChildren.length} files - +
    +
    - ))} - - {!onlyOnePage && ( -
    -
    - - showing {size} page{size > 1 ? 's' : ''} of {isLoadingMore ? '...' : children.length} files - -
    - -
    - )} -
    + +
    + )} {readmeFile && (
    diff --git a/components/FolderGridLayout.tsx b/components/FolderGridLayout.tsx new file mode 100644 index 0000000..503256d --- /dev/null +++ b/components/FolderGridLayout.tsx @@ -0,0 +1,83 @@ +import type { OdFolderObject } from '../types' + +import Link from 'next/link' +import emojiRegex from 'emoji-regex' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' + +import { getFileIcon } from '../utils/getFileIcon' +import { formatModifiedDateTime } from '../utils/fileDetails' + +type OdFolderChildren = OdFolderObject['value'][number] + +const GridItem = ({ c }: { c: OdFolderChildren }) => { + const emojiIcon = emojiRegex().exec(c.name) + const renderEmoji = emojiIcon && !emojiIcon.index + + const ChildIcon = () => + renderEmoji ? ( + {emojiIcon ? emojiIcon[0] : '📁'} + ) : ( + + ) + + // We use the generated medium thumbnail for rendering preview images + const thumbnail = c.thumbnails && c.thumbnails.length > 0 ? c.thumbnails[0].medium : null + + return ( +
    +
    + {thumbnail ? ( + // eslint-disable-next-line @next/next/no-img-element + {c.name} + ) : ( +
    + + + {c.folder?.childCount} + +
    + )} +
    + +
    + + + + + {renderEmoji ? c.name.replace(emojiIcon ? emojiIcon[0] : '', '').trim() : c.name} + +
    +
    + {formatModifiedDateTime(c.lastModifiedDateTime)} +
    +
    + ) +} + +const FolderGridLayout = ({ + path, + folderChildren, + selected, + toggleItemSelected, + totalSelected, + toggleTotalSelected, + totalGenerating, + handleSelectedDownload, + folderGenerating, + handleFolderDownload, + toast, +}) => { + return ( +
    + {folderChildren.map((c: OdFolderChildren) => ( + + + + + + ))} +
    + ) +} + +export default FolderGridLayout diff --git a/components/FolderListLayout.tsx b/components/FolderListLayout.tsx new file mode 100644 index 0000000..ed541ec --- /dev/null +++ b/components/FolderListLayout.tsx @@ -0,0 +1,174 @@ +import type { OdFolderObject } from '../types' + +import { FC } from 'react' +import emojiRegex from 'emoji-regex' +import { useClipboard } from 'use-clipboard-copy' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' + +import Link from 'next/link' + +import { getBaseUrl } from '../utils/getBaseUrl' +import { getFileIcon } from '../utils/getFileIcon' +import { humanFileSize, formatModifiedDateTime } from '../utils/fileDetails' + +import { Downloading, Checkbox } from './FileListing' + +type OdFolderChildren = OdFolderObject['value'][number] + +const FileListItem: FC<{ fileContent: OdFolderChildren }> = ({ fileContent: c }) => { + const emojiIcon = emojiRegex().exec(c.name) + const renderEmoji = emojiIcon && !emojiIcon.index + + return ( +
    +
    + {/*
    {c.file ? c.file.mimeType : 'folder'}
    */} +
    + {renderEmoji ? ( + {emojiIcon ? emojiIcon[0] : '📁'} + ) : ( + + )} +
    +
    + {renderEmoji ? c.name.replace(emojiIcon ? emojiIcon[0] : '', '').trim() : c.name} +
    +
    +
    + {formatModifiedDateTime(c.lastModifiedDateTime)} +
    +
    + {humanFileSize(c.size)} +
    +
    + ) +} + +const FolderListLayout = ({ + path, + folderChildren, + selected, + toggleItemSelected, + totalSelected, + toggleTotalSelected, + totalGenerating, + handleSelectedDownload, + folderGenerating, + handleFolderDownload, + toast, +}) => { + const clipboard = useClipboard() + + return ( +
    +
    +
    + Name +
    +
    + Last Modified +
    +
    + Size +
    +
    + Actions +
    +
    +
    + + {totalGenerating ? ( + + ) : ( + + )} +
    +
    +
    + + {folderChildren.map((c: OdFolderChildren) => ( +
    + + + + + + + {c.folder ? ( +
    + { + clipboard.copy(`${getBaseUrl()}${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`) + toast('Copied folder permalink.', { icon: '👌' }) + }} + > + + + {folderGenerating[c.id] ? ( + + ) : ( + { + const p = `${path === '/' ? '' : path}/${encodeURIComponent(c.name)}` + handleFolderDownload(p, c.id, c.name)() + }} + > + + + )} +
    + ) : ( +
    + { + clipboard.copy( + `${getBaseUrl()}/api?path=${path === '/' ? '' : path}/${encodeURIComponent(c.name)}&raw=true` + ) + toast.success('Copied raw file permalink.') + }} + > + + + + + +
    + )} +
    + {!c.folder && !(c.name === '.password') && ( + toggleItemSelected(c.id)} + title="Select file" + /> + )} +
    +
    + ))} +
    + ) +} + +export default FolderListLayout diff --git a/components/SwitchLayout.tsx b/components/SwitchLayout.tsx index fbe70ac..fbdbe5c 100644 --- a/components/SwitchLayout.tsx +++ b/components/SwitchLayout.tsx @@ -5,9 +5,9 @@ import { Listbox, Transition } from '@headlessui/react' import useLocalStorage from '../utils/useLocalStorage' -const layouts: Array<{ id: number; name: 'Grid' | 'List'; icon: IconProp }> = [ - { id: 1, name: 'Grid', icon: 'th' }, - { id: 2, name: 'List', icon: 'th-list' }, +export const layouts: Array<{ id: number; name: 'Grid' | 'List'; icon: IconProp }> = [ + { id: 1, name: 'List', icon: 'th-list' }, + { id: 2, name: 'Grid', icon: 'th' }, ] export const SwitchLayout = () => { diff --git a/pages/_app.tsx b/pages/_app.tsx index c5ef125..9e4f4eb 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -47,6 +47,7 @@ import { faExclamationCircle, faExclamationTriangle, faTh, + faThLarge, faThList, faHome, } from '@fortawesome/free-solid-svg-icons' @@ -103,6 +104,7 @@ library.add( faSearch, faChevronDown, faTh, + faThLarge, faThList, ...iconList ) diff --git a/pages/api/index.ts b/pages/api/index.ts index 5addae6..e4fb79b 100644 --- a/pages/api/index.ts +++ b/pages/api/index.ts @@ -220,11 +220,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) params: next ? { select: '@microsoft.graph.downloadUrl,name,size,id,lastModifiedDateTime,folder,file,video,image', + $expand: 'thumbnails', top: siteConfig.maxItems, $skipToken: next, } : { select: '@microsoft.graph.downloadUrl,name,size,id,lastModifiedDateTime,folder,file,video,image', + $expand: 'thumbnails', top: siteConfig.maxItems, }, }) diff --git a/types/index.d.ts b/types/index.d.ts index 1872f2f..f3dae85 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -17,6 +17,8 @@ export type OdFolderObject = { folder?: { childCount: number; view: { sortBy: string; sortOrder: 'ascending'; viewType: 'thumbnails' } } image?: OdImageFile video?: OdVideoFile + 'thumbnails@odata.context'?: string + thumbnails?: Array }> } // A file object returned from the OneDrive API. This object may contain 'video' if the file is a video. From 4afa6f8c33d8d6829cc6c617df7da11e89215fcb Mon Sep 17 00:00:00 2001 From: spencerwooo Date: Sat, 5 Feb 2022 19:00:25 +0800 Subject: [PATCH 08/12] handle file and folder downloads --- components/FolderGridLayout.tsx | 112 +++++++++++++++++++++++++++++--- components/SwitchLayout.tsx | 2 +- 2 files changed, 104 insertions(+), 10 deletions(-) diff --git a/components/FolderGridLayout.tsx b/components/FolderGridLayout.tsx index 503256d..f0f7f73 100644 --- a/components/FolderGridLayout.tsx +++ b/components/FolderGridLayout.tsx @@ -3,9 +3,12 @@ import type { OdFolderObject } from '../types' import Link from 'next/link' import emojiRegex from 'emoji-regex' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { useClipboard } from 'use-clipboard-copy' import { getFileIcon } from '../utils/getFileIcon' +import { getBaseUrl } from '../utils/getBaseUrl' import { formatModifiedDateTime } from '../utils/fileDetails' +import { Checkbox, Downloading } from './FileListing' type OdFolderChildren = OdFolderObject['value'][number] @@ -25,7 +28,7 @@ const GridItem = ({ c }: { c: OdFolderChildren }) => { return (
    -
    +
    {thumbnail ? ( // eslint-disable-next-line @next/next/no-img-element {c.name} @@ -67,15 +70,106 @@ const FolderGridLayout = ({ handleFolderDownload, toast, }) => { + const clipboard = useClipboard() + return ( -
    - {folderChildren.map((c: OdFolderChildren) => ( - - - - - - ))} +
    +
    +
    {folderChildren.length} items
    +
    + + {totalGenerating ? ( + + ) : ( + + )} +
    +
    + +
    ) } diff --git a/components/SwitchLayout.tsx b/components/SwitchLayout.tsx index fbdbe5c..f4428c4 100644 --- a/components/SwitchLayout.tsx +++ b/components/SwitchLayout.tsx @@ -27,7 +27,7 @@ export const SwitchLayout = () => { - + {layouts.map(layout => ( Date: Sat, 5 Feb 2022 19:05:13 +0800 Subject: [PATCH 09/12] fix onclick events get passed to a tags --- components/FolderGridLayout.tsx | 126 ++++++++++++++++---------------- 1 file changed, 64 insertions(+), 62 deletions(-) diff --git a/components/FolderGridLayout.tsx b/components/FolderGridLayout.tsx index f0f7f73..a895094 100644 --- a/components/FolderGridLayout.tsx +++ b/components/FolderGridLayout.tsx @@ -81,7 +81,7 @@ const FolderGridLayout = ({ checked={totalSelected} onChange={toggleTotalSelected} indeterminate={true} - title={'Select files'} + title={'Select all files'} /> {totalGenerating ? ( @@ -98,76 +98,78 @@ const FolderGridLayout = ({
    -
    +
    {folderChildren.map((c: OdFolderChildren) => ( - - -
    - {c.folder ? ( -
    +
    +
    + {c.folder ? ( +
    + { + clipboard.copy(`${getBaseUrl()}${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`) + toast('Copied folder permalink.', { icon: '👌' }) + }} + > + + + {folderGenerating[c.id] ? ( + + ) : ( { - clipboard.copy(`${getBaseUrl()}${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`) - toast('Copied folder permalink.', { icon: '👌' }) + const p = `${path === '/' ? '' : path}/${encodeURIComponent(c.name)}` + handleFolderDownload(p, c.id, c.name)() }} - > - - - {folderGenerating[c.id] ? ( - - ) : ( - { - const p = `${path === '/' ? '' : path}/${encodeURIComponent(c.name)}` - handleFolderDownload(p, c.id, c.name)() - }} - > - - - )} -
    - ) : ( -
    - )} -
    + + )} +
    + ) : ( +
    + { + clipboard.copy( + `${getBaseUrl()}/api?path=${path === '/' ? '' : path}/${encodeURIComponent(c.name)}&raw=true` + ) + toast.success('Copied raw file permalink.') + }} + > + + + + + +
    + )} +
    -
    - {!c.folder && !(c.name === '.password') && ( - toggleItemSelected(c.id)} - title="Select file" - /> - )} -
    +
    + {!c.folder && !(c.name === '.password') && ( + toggleItemSelected(c.id)} + title="Select file" + /> + )} +
    - - - + + + + + +
    ))}
    From 443d42b16c0a1c7d5b9f2a3e9d655002200ee7ba Mon Sep 17 00:00:00 2001 From: spencerwooo Date: Sat, 5 Feb 2022 19:12:20 +0800 Subject: [PATCH 10/12] fallback component for broken thumbnails --- components/FolderGridLayout.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/components/FolderGridLayout.tsx b/components/FolderGridLayout.tsx index a895094..45c1d18 100644 --- a/components/FolderGridLayout.tsx +++ b/components/FolderGridLayout.tsx @@ -9,6 +9,7 @@ import { getFileIcon } from '../utils/getFileIcon' import { getBaseUrl } from '../utils/getBaseUrl' import { formatModifiedDateTime } from '../utils/fileDetails' import { Checkbox, Downloading } from './FileListing' +import { useState } from 'react' type OdFolderChildren = OdFolderObject['value'][number] @@ -26,12 +27,20 @@ const GridItem = ({ c }: { c: OdFolderChildren }) => { // We use the generated medium thumbnail for rendering preview images const thumbnail = c.thumbnails && c.thumbnails.length > 0 ? c.thumbnails[0].medium : null + // Some thumbnails are broken, so we check for onerror event in the image component + const [brokenThumbnail, setBrokenThumbnail] = useState(false) + return (
    - {thumbnail ? ( + {thumbnail && !brokenThumbnail ? ( // eslint-disable-next-line @next/next/no-img-element - {c.name} + {c.name} setBrokenThumbnail(true)} + /> ) : (
    From b42036afb764b1c61cae56db469202d4302e2cae Mon Sep 17 00:00:00 2001 From: spencerwooo Date: Sat, 5 Feb 2022 20:36:38 +0800 Subject: [PATCH 11/12] clamp multiple lines for long file names --- components/FileListing.tsx | 24 ++++------------------ components/FolderGridLayout.tsx | 2 +- components/SwitchLayout.tsx | 22 ++++++++++---------- components/previews/DefaultPreview.tsx | 12 +++++------ components/previews/ImagePreview.tsx | 28 ++++++++++++++++++++++++++ package.json | 1 + pnpm-lock.yaml | 10 +++++++++ tailwind.config.js | 4 +++- types/index.d.ts | 2 +- 9 files changed, 65 insertions(+), 40 deletions(-) create mode 100644 components/previews/ImagePreview.tsx diff --git a/components/FileListing.tsx b/components/FileListing.tsx index 9e0fd0b..5f47705 100644 --- a/components/FileListing.tsx +++ b/components/FileListing.tsx @@ -43,6 +43,7 @@ import FolderListLayout from './FolderListLayout' import type { OdFileObject, OdFolderObject } from '../types' import FolderGridLayout from './FolderGridLayout' +import ImagePreview from './previews/ImagePreview' // Disabling SSR for some previews const EPUBPreview = dynamic(() => import('./previews/EPUBPreview'), { @@ -340,29 +341,12 @@ const FileListing: FC<{ query?: ParsedUrlQuery }> = ({ query }) => { const fileName = file.name const fileExtension = fileName.slice(((fileName.lastIndexOf('.') - 1) >>> 0) + 2).toLowerCase() - const previewType = getPreviewType(fileExtension, { - video: Boolean(file.video), - }) + const previewType = getPreviewType(fileExtension, { video: Boolean(file.video) }) + if (previewType) { switch (previewType) { case preview.image: - return ( - <> - - {/* eslint-disable-next-line @next/next/no-img-element */} - {fileName} - - - - - - ) + return case preview.text: return diff --git a/components/FolderGridLayout.tsx b/components/FolderGridLayout.tsx index 45c1d18..e48bdaa 100644 --- a/components/FolderGridLayout.tsx +++ b/components/FolderGridLayout.tsx @@ -1,5 +1,6 @@ import type { OdFolderObject } from '../types' +import { useState } from 'react' import Link from 'next/link' import emojiRegex from 'emoji-regex' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' @@ -9,7 +10,6 @@ import { getFileIcon } from '../utils/getFileIcon' import { getBaseUrl } from '../utils/getBaseUrl' import { formatModifiedDateTime } from '../utils/fileDetails' import { Checkbox, Downloading } from './FileListing' -import { useState } from 'react' type OdFolderChildren = OdFolderObject['value'][number] diff --git a/components/SwitchLayout.tsx b/components/SwitchLayout.tsx index f4428c4..ebf31b6 100644 --- a/components/SwitchLayout.tsx +++ b/components/SwitchLayout.tsx @@ -14,38 +14,38 @@ export const SwitchLayout = () => { const [preferredLayout, setPreferredLayout] = useLocalStorage('preferredLayout', layouts[0]) return ( -
    +
    - + {preferredLayout.name} - + - + {layouts.map(layout => ( - {layout.name === preferredLayout.name && ( - - - - )} - + {layout.name} + {layout.name === preferredLayout.name && ( + + + + )} ))} diff --git a/components/previews/DefaultPreview.tsx b/components/previews/DefaultPreview.tsx index 8114c4e..dc795b2 100644 --- a/components/previews/DefaultPreview.tsx +++ b/components/previews/DefaultPreview.tsx @@ -14,9 +14,9 @@ const DefaultPreview: FC<{ file: OdFileObject }> = ({ file }) => {
    -
    +
    -
    {file.name}
    +
    {file.name}
    @@ -32,7 +32,7 @@ const DefaultPreview: FC<{ file: OdFileObject }> = ({ file }) => {
    MIME type
    -
    {file.file.mimeType}
    +
    {file.file?.mimeType || 'Unavailable'}
    @@ -44,7 +44,7 @@ const DefaultPreview: FC<{ file: OdFileObject }> = ({ file }) => { Quick XOR - {file.file.hashes.quickXorHash} + {file.file.hashes?.quickXorHash || 'Unavailable'} @@ -52,7 +52,7 @@ const DefaultPreview: FC<{ file: OdFileObject }> = ({ file }) => { SHA1 - {file.file.hashes.sha1Hash} + {file.file.hashes?.sha1Hash || 'Unavailable'} @@ -60,7 +60,7 @@ const DefaultPreview: FC<{ file: OdFileObject }> = ({ file }) => { SHA256 - {file.file.hashes.sha256Hash} + {file.file.hashes?.sha256Hash || 'Unavailable'} diff --git a/components/previews/ImagePreview.tsx b/components/previews/ImagePreview.tsx new file mode 100644 index 0000000..6166b20 --- /dev/null +++ b/components/previews/ImagePreview.tsx @@ -0,0 +1,28 @@ +import type { OdFileObject } from '../../types' + +import { FC } from 'react' + +import { PreviewContainer, DownloadBtnContainer } from './Containers' +import DownloadButtonGroup from '../DownloadBtnGtoup' + +const ImagePreview: FC<{ file: OdFileObject }> = ({ file }) => { + return ( + <> + + {/* eslint-disable-next-line @next/next/no-img-element */} + {file.name} + + + + + + ) +} + +export default ImagePreview diff --git a/package.json b/package.json index cd22ac6..69b4460 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@fortawesome/free-solid-svg-icons": "^5.15.3", "@fortawesome/react-fontawesome": "^0.1.14", "@headlessui/react": "^1.4.0", + "@tailwindcss/line-clamp": "^0.3.1", "awesome-debounce-promise": "^2.1.0", "axios": "^0.25.0", "cors": "^2.8.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 499dda2..bcdb121 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,7 @@ specifiers: '@fortawesome/free-solid-svg-icons': ^5.15.3 '@fortawesome/react-fontawesome': ^0.1.14 '@headlessui/react': ^1.4.0 + '@tailwindcss/line-clamp': ^0.3.1 '@types/cors': ^2.8.12 '@types/crypto-js': ^4.0.2 '@types/ioredis': ^4.28.5 @@ -62,6 +63,7 @@ dependencies: '@fortawesome/free-solid-svg-icons': 5.15.4 '@fortawesome/react-fontawesome': 0.1.17_f515edce028694561ceb456e3dba224c '@headlessui/react': 1.4.3_react-dom@17.0.2+react@17.0.2 + '@tailwindcss/line-clamp': 0.3.1_tailwindcss@3.0.18 awesome-debounce-promise: 2.1.0 axios: 0.25.0 cors: 2.8.5 @@ -376,6 +378,14 @@ packages: resolution: {integrity: sha512-JLo+Y592QzIE+q7Dl2pMUtt4q8SKYI5jDrZxrozEQxnGVOyYE+GWK9eLkwTaeN9DDctlaRAQ3TBmzZ1qdLE30A==} dev: true + /@tailwindcss/line-clamp/0.3.1_tailwindcss@3.0.18: + resolution: {integrity: sha512-pNr0T8LAc3TUx/gxCfQZRe9NB2dPEo/cedPHzUGIPxqDMhgjwNm6jYxww4W5l0zAsAddxr+XfZcqttGiFDgrGg==} + peerDependencies: + tailwindcss: '>=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1' + dependencies: + tailwindcss: 3.0.18_833e1018ad0d7954aa80c53675939269 + dev: false + /@types/cors/2.8.12: resolution: {integrity: sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==} dev: true diff --git a/tailwind.config.js b/tailwind.config.js index e2adf33..2c01c61 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -35,5 +35,7 @@ module.exports = { } } }, - plugins: [], + plugins: [ + require('@tailwindcss/line-clamp'), + ], } diff --git a/types/index.d.ts b/types/index.d.ts index f3dae85..219bf27 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -13,7 +13,7 @@ export type OdFolderObject = { name: string size: number lastModifiedDateTime: string - file?: { mimeType: string; hashes: { quickXorHash: string; sha1Hash?: string; sha256Hash?: string } } + file?: { mimeType: string; hashes: { quickXorHash?: string; sha1Hash?: string; sha256Hash?: string } } folder?: { childCount: number; view: { sortBy: string; sortOrder: 'ascending'; viewType: 'thumbnails' } } image?: OdImageFile video?: OdVideoFile From 53470ff61e42e8696831e812597800c5168b98f3 Mon Sep 17 00:00:00 2001 From: spencerwooo Date: Sat, 5 Feb 2022 21:16:57 +0800 Subject: [PATCH 12/12] extract and cleanup some functions --- components/FileListing.tsx | 40 +++++++++++++++++++++----------- components/FolderGridLayout.tsx | 41 +++++++++++++-------------------- components/FolderListLayout.tsx | 30 ++++++++---------------- types/index.d.ts | 1 + 4 files changed, 52 insertions(+), 60 deletions(-) diff --git a/components/FileListing.tsx b/components/FileListing.tsx index 5f47705..6dbfa07 100644 --- a/components/FileListing.tsx +++ b/components/FileListing.tsx @@ -1,23 +1,17 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import toast, { Toaster } from 'react-hot-toast' import emojiRegex from 'emoji-regex' -import { useClipboard } from 'use-clipboard-copy' import { ParsedUrlQuery } from 'querystring' import { FC, MouseEventHandler, SetStateAction, useEffect, useRef, useState } from 'react' -import Link from 'next/link' import dynamic from 'next/dynamic' import { useRouter } from 'next/router' -import { layouts } from './SwitchLayout' - import useLocalStorage from '../utils/useLocalStorage' -import { humanFileSize, formatModifiedDateTime } from '../utils/fileDetails' -import { getFileIcon } from '../utils/getFileIcon' import { getPreviewType, preview } from '../utils/getPreviewType' import { useProtectedSWRInfinite } from '../utils/fetchWithSWR' -import { getBaseUrl } from '../utils/getBaseUrl' +import { getFileIcon } from '../utils/getFileIcon' import { DownloadingToast, downloadMultipleFiles, @@ -25,6 +19,7 @@ import { traverseFolder, } from './MultiFileDownloader' +import { layouts } from './SwitchLayout' import Loading, { LoadingIcon } from './Loading' import FourOhFour from './FourOhFour' import Auth from './Auth' @@ -36,14 +31,13 @@ import AudioPreview from './previews/AudioPreview' import VideoPreview from './previews/VideoPreview' import PDFPreview from './previews/PDFPreview' import URLPreview from './previews/URLPreview' -import DefaultPreview from './previews/DefaultPreview' -import { DownloadBtnContainer, PreviewContainer } from './previews/Containers' -import DownloadButtonGroup from './DownloadBtnGtoup' -import FolderListLayout from './FolderListLayout' - -import type { OdFileObject, OdFolderObject } from '../types' -import FolderGridLayout from './FolderGridLayout' import ImagePreview from './previews/ImagePreview' +import DefaultPreview from './previews/DefaultPreview' +import { PreviewContainer } from './previews/Containers' + +import type { OdFileObject, OdFolderChildren, OdFolderObject } from '../types' +import FolderListLayout from './FolderListLayout' +import FolderGridLayout from './FolderGridLayout' // Disabling SSR for some previews const EPUBPreview = dynamic(() => import('./previews/EPUBPreview'), { @@ -66,6 +60,24 @@ const queryToPath = (query?: ParsedUrlQuery) => { return '/' } +// Render the icon of a folder child (may be a file or a folder), use emoji if the name of the child contains emoji +const renderEmoji = (name: string) => { + const emoji = emojiRegex().exec(name) + return { render: emoji && !emoji.index, emoji } +} +export const formatChildName = (name: string) => { + const { render, emoji } = renderEmoji(name) + return render ? name.replace(emoji ? emoji[0] : '', '').trim() : name +} +export const ChildIcon: FC<{ child: OdFolderChildren }> = ({ child }) => { + const { render, emoji } = renderEmoji(child.name) + return render ? ( + {emoji ? emoji[0] : '📁'} + ) : ( + + ) +} + export const Checkbox: FC<{ checked: 0 | 1 | 2 onChange: () => void diff --git a/components/FolderGridLayout.tsx b/components/FolderGridLayout.tsx index e48bdaa..7f32807 100644 --- a/components/FolderGridLayout.tsx +++ b/components/FolderGridLayout.tsx @@ -1,29 +1,15 @@ -import type { OdFolderObject } from '../types' +import type { OdFolderChildren } from '../types' -import { useState } from 'react' import Link from 'next/link' -import emojiRegex from 'emoji-regex' +import { useState } from 'react' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { useClipboard } from 'use-clipboard-copy' -import { getFileIcon } from '../utils/getFileIcon' import { getBaseUrl } from '../utils/getBaseUrl' import { formatModifiedDateTime } from '../utils/fileDetails' -import { Checkbox, Downloading } from './FileListing' - -type OdFolderChildren = OdFolderObject['value'][number] +import { Checkbox, ChildIcon, Downloading, formatChildName } from './FileListing' const GridItem = ({ c }: { c: OdFolderChildren }) => { - const emojiIcon = emojiRegex().exec(c.name) - const renderEmoji = emojiIcon && !emojiIcon.index - - const ChildIcon = () => - renderEmoji ? ( - {emojiIcon ? emojiIcon[0] : '📁'} - ) : ( - - ) - // We use the generated medium thumbnail for rendering preview images const thumbnail = c.thumbnails && c.thumbnails.length > 0 ? c.thumbnails[0].medium : null @@ -43,7 +29,7 @@ const GridItem = ({ c }: { c: OdFolderChildren }) => { /> ) : (
    - + {c.folder?.childCount} @@ -53,11 +39,9 @@ const GridItem = ({ c }: { c: OdFolderChildren }) => {
    - - - - {renderEmoji ? c.name.replace(emojiIcon ? emojiIcon[0] : '', '').trim() : c.name} + + {formatChildName(c.name)}
    {formatModifiedDateTime(c.lastModifiedDateTime)} @@ -109,8 +93,11 @@ const FolderGridLayout = ({
    {folderChildren.map((c: OdFolderChildren) => ( -
    -
    +
    +
    {c.folder ? (
    -
    +
    {!c.folder && !(c.name === '.password') && ( = ({ fileContent: c }) => { - const emojiIcon = emojiRegex().exec(c.name) - const renderEmoji = emojiIcon && !emojiIcon.index - return (
    - {/*
    {c.file ? c.file.mimeType : 'folder'}
    */}
    - {renderEmoji ? ( - {emojiIcon ? emojiIcon[0] : '📁'} - ) : ( - - )} -
    -
    - {renderEmoji ? c.name.replace(emojiIcon ? emojiIcon[0] : '', '').trim() : c.name} +
    +
    {formatChildName(c.name)}
    {formatModifiedDateTime(c.lastModifiedDateTime)} @@ -99,7 +84,10 @@ const FolderListLayout = ({
    {folderChildren.map((c: OdFolderChildren) => ( -