Skip to content

Commit

Permalink
refactor(FolderPicker): Use ListItem instead File
Browse files Browse the repository at this point in the history
This decouples the display of the file table from that of the FolderPicker modal. This will allow you to change the lines as Link in the table in order to take advantage of the default mechanisms of browsers
  • Loading branch information
cballevre committed Oct 22, 2024
1 parent d7f279f commit b86d3e4
Show file tree
Hide file tree
Showing 10 changed files with 249 additions and 79 deletions.
87 changes: 87 additions & 0 deletions src/components/FolderPicker/FolderPickerAddFolderItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import React, { FC } from 'react'
import { useDispatch } from 'react-redux'

import { useClient } from 'cozy-client'
import { useVaultClient } from 'cozy-keys-lib'
import Divider from 'cozy-ui/transpiled/react/Divider'
import Icon from 'cozy-ui/transpiled/react/Icon'
import IconFolder from 'cozy-ui/transpiled/react/Icons/FileTypeFolder'
import ListItem from 'cozy-ui/transpiled/react/ListItem'
import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon'
import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
import useBreakpoints from 'cozy-ui/transpiled/react/providers/Breakpoints'
import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'

import FilenameInput from 'modules/filelist/FilenameInput'
import { createFolder } from 'modules/navigation/duck'
import IconEncryptedFolder from 'modules/views/Folder/EncryptedFolderIcon'

interface FolderPickerAddFolderItemProps {
isEncrypted: boolean
currentFolderId: string
visible: boolean
afterSubmit: () => void
afterAbort: () => void
}

const FolderPickerAddFolderItem: FC<FolderPickerAddFolderItemProps> = ({
isEncrypted,
currentFolderId,
visible,
afterSubmit,
afterAbort
}) => {
const { isMobile } = useBreakpoints()
const gutters = isMobile ? 'default' : 'double'
const dispatch = useDispatch()
const { showAlert } = useAlert()
const { t } = useI18n()
const vaultClient = useVaultClient()
const client = useClient()

const handleSubmit = (name: string): void => {
dispatch(
createFolder(client, vaultClient, name, currentFolderId, {
isEncryptedFolder: isEncrypted,
showAlert,
t
})
)
if (typeof afterSubmit === 'function') {
afterSubmit()
}
}

const handleAbort = (accidental: boolean): void => {
if (accidental) {
showAlert({
message: t('alert.folder_abort'), //
severity: 'secondary'
})
}
if (typeof afterAbort === 'function') {
afterAbort()
}
}

if (visible) {
return (
<>
<ListItem gutters={gutters}>
<ListItemIcon>
<Icon
icon={isEncrypted ? IconEncryptedFolder : IconFolder}
size={32}
/>
</ListItemIcon>
<FilenameInput onSubmit={handleSubmit} onAbort={handleAbort} />
</ListItem>
<Divider />
</>
)
}

return null
}

export { FolderPickerAddFolderItem }
44 changes: 19 additions & 25 deletions src/components/FolderPicker/FolderPickerContentCozy.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import React, { useMemo } from 'react'

import { useQuery, useClient } from 'cozy-client'
import { isDirectory } from 'cozy-client/dist/models/file'
import { IOCozyFile } from 'cozy-client/types/types'
import List from 'cozy-ui/transpiled/react/List'

import { FolderPickerContentExplorer } from 'components/FolderPicker/FolderPickerContentExplorer'
import { FolderPickerListItem } from './FolderPickerListItem'
import { FolderPickerAddFolderItem } from 'components/FolderPicker/FolderPickerAddFolderItem'
import { FolderPickerContentLoadMore } from 'components/FolderPicker/FolderPickerContentLoadMore'
import { FolderPickerContentLoader } from 'components/FolderPicker/FolderPickerContentLoader'
import { isInvalidMoveTarget } from 'components/FolderPicker/helpers'
import { computeNextcloudRootFolder } from 'components/FolderPicker/helpers'
import { FolderPickerEntry } from 'components/FolderPicker/types'
import type { File, FolderPickerEntry } from 'components/FolderPicker/types'
import { ROOT_DIR_ID } from 'constants/config'
import { isEncryptedFolder } from 'lib/encryption'
import { AddFolderWithoutState } from 'modules/filelist/AddFolder'
import { DumbFile as File } from 'modules/filelist/File'
import { FolderUnlocker } from 'modules/folder/components/FolderUnlocker'
import {
buildMoveOrImportQuery,
Expand Down Expand Up @@ -92,12 +93,13 @@ const FolderPickerContentCozy: React.FC<FolderPickerContentCozyProps> = ({
navigateTo(parentFolder.data)
}

const handleFolderOpen = (folder: IOCozyFile): void => {
navigateTo(folder)
}
const handleClick = (file: File): void => {
if (isDirectory(file)) {
navigateTo(file)
}

const handleFileOpen = ({ file }: { file: IOCozyFile }): void => {
if (
file._type === 'io.cozy.files' &&
file.cozyMetadata?.createdByApp === 'nextcloud' &&
file.cozyMetadata.sourceAccount
) {
Expand All @@ -110,8 +112,8 @@ const FolderPickerContentCozy: React.FC<FolderPickerContentCozyProps> = ({
}

return (
<FolderPickerContentExplorer>
<AddFolderWithoutState
<List>
<FolderPickerAddFolderItem
isEncrypted={isEncrypted}
currentFolderId={folder._id}
visible={isFolderCreationDisplayed}
Expand All @@ -123,27 +125,19 @@ const FolderPickerContentCozy: React.FC<FolderPickerContentCozyProps> = ({
hasNoData={files.length === 0}
>
<FolderUnlocker folder={folder} onDismiss={handleFolderUnlockerDismiss}>
{files.map(file => (
<File
key={file.id}
{files.map((file, index) => (
<FolderPickerListItem
key={file._id}
file={file}
disabled={isInvalidMoveTarget(entries, file)}
styleDisabled={isInvalidMoveTarget(entries, file)}
attributes={file}
displayedFolder={null}
actions={null}
isRenaming={false}
onFolderOpen={handleFolderOpen}
onFileOpen={handleFileOpen}
withSelectionCheckbox={false}
withFilePath={false}
withSharedBadge
disableSelection={true}
onClick={handleClick}
showDivider={index !== files.length - 1}
/>
))}
</FolderUnlocker>
<FolderPickerContentLoadMore hasMore={hasMore} fetchMore={fetchMore} />
</FolderPickerContentLoader>
</FolderPickerContentExplorer>
</List>
)
}

Expand Down
21 changes: 0 additions & 21 deletions src/components/FolderPicker/FolderPickerContentExplorer.tsx

This file was deleted.

21 changes: 19 additions & 2 deletions src/components/FolderPicker/FolderPickerContentLoader.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React, { ReactNode } from 'react'

import ListItemSkeleton from 'cozy-ui/transpiled/react/Skeletons/ListItemSkeleton'
import useBreakpoints from 'cozy-ui/transpiled/react/providers/Breakpoints'

import { EmptyDrive } from 'components/Error/Empty'
import Oops from 'components/Error/Oops'
import FileListRowsPlaceholder from 'modules/filelist/FileListRowsPlaceholder'

interface FolderPickerContentLoaderProps {
fetchStatus: string
Expand All @@ -15,7 +17,22 @@ const FolderPickerContentLoader: React.FC<FolderPickerContentLoaderProps> = ({
hasNoData,
children
}) => {
if (fetchStatus === 'loading') return <FileListRowsPlaceholder />
const { isMobile } = useBreakpoints()
const gutters = isMobile ? 'default' : 'double'

if (fetchStatus === 'loading')
return (
<>
{Array.from({ length: 8 }, (_, index) => (
<ListItemSkeleton
key={`key_file_placeholder_${index}`}
gutters={gutters}
hasSecondary
divider={index !== 7}
/>
))}
</>
)
else if (fetchStatus === 'failed') return <Oops />
else if (fetchStatus === 'loaded' && hasNoData)
return <EmptyDrive canUpload={false} />
Expand Down
46 changes: 19 additions & 27 deletions src/components/FolderPicker/FolderPickerContentNextcloud.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import React from 'react'

import { useQuery } from 'cozy-client'
import { isDirectory } from 'cozy-client/dist/models/file'
import { NextcloudFile } from 'cozy-client/types/types'
import List from 'cozy-ui/transpiled/react/List'

import { FolderPickerContentExplorer } from 'components/FolderPicker/FolderPickerContentExplorer'
import { FolderPickerListItem } from './FolderPickerListItem'
import { FolderPickerContentLoader } from 'components/FolderPicker/FolderPickerContentLoader'
import { isInvalidMoveTarget } from 'components/FolderPicker/helpers'
import { DumbFile as File } from 'modules/filelist/File'
import type { File, FolderPickerEntry } from 'components/FolderPicker/types'
import { buildNextcloudFolderQuery } from 'queries'

interface Props {
folder: NextcloudFile
entries: import('./types').File[] // Update with the appropriate type
navigateTo: (folder?: import('./types').File) => void // Update with the appropriate type
entries: FolderPickerEntry[] // Update with the appropriate type
navigateTo: (folder: import('./types').File) => void // Update with the appropriate type
}

const FolderPickerContentNextcloud: React.FC<Props> = ({
Expand All @@ -33,41 +35,31 @@ const FolderPickerContentNextcloud: React.FC<Props> = ({
data?: NextcloudFile[]
}

const handleFolderOpen = (folder: import('./types').File): void => {
navigateTo(folder)
}
const files = data ?? []

const handleFileOpen = (): void => {
// Do nothing
const handleClick = (file: File): void => {
if (isDirectory(file)) {
navigateTo(file)
}
}

const files = data ?? []

return (
<FolderPickerContentExplorer>
<List>
<FolderPickerContentLoader
fetchStatus={fetchStatus}
hasNoData={files.length === 0}
>
{files.map(file => (
<File
key={file.id}
{files.map((file, index) => (
<FolderPickerListItem
key={file._id}
file={file}
disabled={isInvalidMoveTarget(entries, file)}
styleDisabled={isInvalidMoveTarget(entries, file)}
attributes={file}
displayedFolder={null}
actions={null}
isRenaming={false}
onFolderOpen={handleFolderOpen}
onFileOpen={handleFileOpen}
withSelectionCheckbox={false}
withFilePath={false}
withSharedBadge
disableSelection={true}
onClick={handleClick}
showDivider={index !== files.length - 1}
/>
))}
</FolderPickerContentLoader>
</FolderPickerContentExplorer>
</List>
)
}

Expand Down
72 changes: 72 additions & 0 deletions src/components/FolderPicker/FolderPickerListItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { filesize } from 'filesize'
import React, { FC } from 'react'

import { isDirectory } from 'cozy-client/dist/models/file'
import Divider from 'cozy-ui/transpiled/react/Divider'
import ListItem from 'cozy-ui/transpiled/react/ListItem'
import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon'
import ListItemText from 'cozy-ui/transpiled/react/ListItemText'
import useBreakpoints from 'cozy-ui/transpiled/react/providers/Breakpoints'
import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'

import type { File } from 'components/FolderPicker/types'
import FileThumbnail from 'modules/filelist/icons/FileThumbnail'

import styles from 'styles/folder-picker.styl'

interface FolderPickerListItemProps {
file: File
disabled?: boolean
onClick: (file: File) => void
showDivider?: boolean
}

const FolderPickerListItem: FC<FolderPickerListItemProps> = ({
file,
disabled = false,
onClick,
showDivider = false
}) => {
const { f, t } = useI18n()
const { isMobile } = useBreakpoints()
const gutters = isMobile ? 'default' : 'double'

const handleClick = (): void => {
onClick(file)
}

const formattedUpdatedAt = f(file.updated_at, t('table.row_update_format'))
const formattedSize = file.size
? filesize(file.size, { base: 10 })
: undefined
const secondaryText = !isDirectory(file)
? `${formattedUpdatedAt}${formattedSize ? ` - ${formattedSize}` : ''}`
: undefined

return (
<>
<ListItem
button
onClick={handleClick}
disabled={disabled}
gutters={gutters}
>
<ListItemIcon className="u-pos-relative">
<FileThumbnail
file={file}
showSharedBadge
componentsProps={{
sharedBadge: {
className: styles['icon-shared']
}
}}
/>
</ListItemIcon>
<ListItemText primary={file.name} secondary={secondaryText} />
</ListItem>
{showDivider && <Divider />}
</>
)
}

export { FolderPickerListItem }
4 changes: 3 additions & 1 deletion src/components/FolderPicker/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ export const computeNextcloudRootFolder = ({
type: 'directory',
links: {
self: 'unknown'
}
},
size: 0,
updated_at: new Date().toISOString()
})

/**
Expand Down
Loading

0 comments on commit b86d3e4

Please sign in to comment.