Skip to content

Commit

Permalink
feat: multiple files selected view (#1062)
Browse files Browse the repository at this point in the history
  • Loading branch information
amanharwara authored Jun 3, 2022
1 parent 4621994 commit 4caf958
Show file tree
Hide file tree
Showing 17 changed files with 399 additions and 298 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,10 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
<>
<NotesContextMenu application={application} viewControllerManager={viewControllerManager} />
<TagsContextMenuWrapper viewControllerManager={viewControllerManager} />
<FileContextMenuWrapper viewControllerManager={viewControllerManager} />
<FileContextMenuWrapper
filesController={viewControllerManager.filesController}
selectionController={viewControllerManager.selectionController}
/>
<PurchaseFlowWrapper application={application} viewControllerManager={viewControllerManager} />
<ConfirmSignoutContainer
applicationGroup={mainApplicationGroup}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ import { observer } from 'mobx-react-lite'
import { FunctionComponent, useCallback, useEffect, useRef, useState } from 'react'
import Icon from '@/Components/Icon/Icon'
import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur'
import { ChallengeReason, ContentType, FileItem, SNNote } from '@standardnotes/snjs'
import { confirmDialog } from '@/Services/AlertService'
import { addToast, dismissToast, ToastType } from '@standardnotes/stylekit'
import { ContentType, FileItem, SNNote } from '@standardnotes/snjs'
import { addToast, ToastType } from '@standardnotes/stylekit'
import { StreamingFileReader } from '@standardnotes/filepicker'
import { PopoverFileItemAction, PopoverFileItemActionType } from './PopoverFileItemAction'
import AttachedFilesPopover from './AttachedFilesPopover'
import { usePremiumModal } from '@/Hooks/usePremiumModal'
import { PopoverTabs } from './PopoverTabs'
Expand Down Expand Up @@ -105,29 +103,6 @@ const AttachedFilesButton: FunctionComponent<Props> = ({
await toggleAttachedFilesMenu()
}, [toggleAttachedFilesMenu, prospectivelyShowFilesPremiumModal])

const deleteFile = async (file: FileItem) => {
const shouldDelete = await confirmDialog({
text: `Are you sure you want to permanently delete "${file.name}"?`,
confirmButtonStyle: 'danger',
})
if (shouldDelete) {
const deletingToastId = addToast({
type: ToastType.Loading,
message: `Deleting file "${file.name}"...`,
})
await application.files.deleteFile(file)
addToast({
type: ToastType.Success,
message: `Deleted file "${file.name}"`,
})
dismissToast(deletingToastId)
}
}

const downloadFile = async (file: FileItem) => {
viewControllerManager.filesController.downloadFile(file).catch(console.error)
}

const attachFileToNote = useCallback(
async (file: FileItem) => {
if (!note) {
Expand All @@ -143,98 +118,6 @@ const AttachedFilesButton: FunctionComponent<Props> = ({
[application.items, note],
)

const detachFileFromNote = async (file: FileItem) => {
if (!note) {
addToast({
type: ToastType.Error,
message: 'Could not attach file because selected note was deleted',
})
return
}
await application.items.disassociateFileWithNote(file, note)
}

const toggleFileProtection = async (file: FileItem) => {
let result: FileItem | undefined
if (file.protected) {
keepMenuOpen(true)
result = await application.mutator.unprotectFile(file)
keepMenuOpen(false)
buttonRef.current?.focus()
} else {
result = await application.mutator.protectFile(file)
}
const isProtected = result ? result.protected : file.protected
return isProtected
}

const authorizeProtectedActionForFile = async (file: FileItem, challengeReason: ChallengeReason) => {
const authorizedFiles = await application.protections.authorizeProtectedActionForItems([file], challengeReason)
const isAuthorized = authorizedFiles.length > 0 && authorizedFiles.includes(file)
return isAuthorized
}

const renameFile = async (file: FileItem, fileName: string) => {
await application.items.renameFile(file, fileName)
}

const handleFileAction = async (action: PopoverFileItemAction) => {
const file = action.type !== PopoverFileItemActionType.RenameFile ? action.payload : action.payload.file
let isAuthorizedForAction = true

if (file.protected && action.type !== PopoverFileItemActionType.ToggleFileProtection) {
keepMenuOpen(true)
isAuthorizedForAction = await authorizeProtectedActionForFile(file, ChallengeReason.AccessProtectedFile)
keepMenuOpen(false)
buttonRef.current?.focus()
}

if (!isAuthorizedForAction) {
return false
}

switch (action.type) {
case PopoverFileItemActionType.AttachFileToNote:
await attachFileToNote(file)
break
case PopoverFileItemActionType.DetachFileToNote:
await detachFileFromNote(file)
break
case PopoverFileItemActionType.DeleteFile:
await deleteFile(file)
break
case PopoverFileItemActionType.DownloadFile:
await downloadFile(file)
break
case PopoverFileItemActionType.ToggleFileProtection: {
const isProtected = await toggleFileProtection(file)
action.callback(isProtected)
break
}
case PopoverFileItemActionType.RenameFile:
await renameFile(file, action.payload.name)
break
case PopoverFileItemActionType.PreviewFile: {
keepMenuOpen(true)
const otherFiles = currentTab === PopoverTabs.AllFiles ? allFiles : attachedFiles
viewControllerManager.filePreviewModalController.activate(
file,
otherFiles.filter((file) => !file.protected),
)
break
}
}

if (
action.type !== PopoverFileItemActionType.DownloadFile &&
action.type !== PopoverFileItemActionType.PreviewFile
) {
application.sync.sync().catch(console.error)
}

return true
}

const [isDraggingFiles, setIsDraggingFiles] = useState(false)
const dragCounter = useRef(0)

Expand Down Expand Up @@ -400,12 +283,11 @@ const AttachedFilesButton: FunctionComponent<Props> = ({
{open && (
<AttachedFilesPopover
application={application}
viewControllerManager={viewControllerManager}
filesController={viewControllerManager.filesController}
attachedFiles={attachedFiles}
allFiles={allFiles}
closeOnBlur={closeOnBlur}
currentTab={currentTab}
handleFileAction={handleFileAction}
isDraggingFiles={isDraggingFiles}
setCurrentTab={setCurrentTab}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,34 @@
import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants/Constants'
import { WebApplication } from '@/Application/Application'
import { ViewControllerManager } from '@/Services/ViewControllerManager'
import { FileItem } from '@standardnotes/snjs'
import { FilesIllustration } from '@standardnotes/icons'
import { observer } from 'mobx-react-lite'
import { Dispatch, FunctionComponent, SetStateAction, useRef, useState } from 'react'
import Button from '@/Components/Button/Button'
import Icon from '@/Components/Icon/Icon'
import PopoverFileItem from './PopoverFileItem'
import { PopoverFileItemAction, PopoverFileItemActionType } from './PopoverFileItemAction'
import { PopoverFileItemActionType } from './PopoverFileItemAction'
import { PopoverTabs } from './PopoverTabs'
import { FilesController } from '@/Controllers/FilesController'

type Props = {
application: WebApplication
viewControllerManager: ViewControllerManager
filesController: FilesController
allFiles: FileItem[]
attachedFiles: FileItem[]
closeOnBlur: (event: { relatedTarget: EventTarget | null }) => void
currentTab: PopoverTabs
handleFileAction: (action: PopoverFileItemAction) => Promise<boolean>
isDraggingFiles: boolean
setCurrentTab: Dispatch<SetStateAction<PopoverTabs>>
}

const AttachedFilesPopover: FunctionComponent<Props> = ({
application,
viewControllerManager,
filesController,
allFiles,
attachedFiles,
closeOnBlur,
currentTab,
handleFileAction,
isDraggingFiles,
setCurrentTab,
}) => {
Expand All @@ -45,20 +43,31 @@ const AttachedFilesPopover: FunctionComponent<Props> = ({
: filesList

const handleAttachFilesClick = async () => {
const uploadedFiles = await viewControllerManager.filesController.uploadNewFile()
const uploadedFiles = await filesController.uploadNewFile()
if (!uploadedFiles) {
return
}
if (currentTab === PopoverTabs.AttachedFiles) {
uploadedFiles.forEach((file) => {
handleFileAction({
type: PopoverFileItemActionType.AttachFileToNote,
payload: file,
}).catch(console.error)
filesController
.handleFileAction({
type: PopoverFileItemActionType.AttachFileToNote,
payload: { file },
})
.catch(console.error)
})
}
}

const previewHandler = (file: FileItem) => {
filesController
.handleFileAction({
type: PopoverFileItemActionType.PreviewFile,
payload: { file, otherFiles: currentTab === PopoverTabs.AllFiles ? allFiles : attachedFiles },
})
.catch(console.error)
}

return (
<div
className="flex flex-col"
Expand Down Expand Up @@ -130,9 +139,10 @@ const AttachedFilesPopover: FunctionComponent<Props> = ({
key={file.uuid}
file={file}
isAttachedToNote={attachedFiles.includes(file)}
handleFileAction={handleFileAction}
handleFileAction={filesController.handleFileAction}
getIconType={application.iconsController.getIconForFileType}
closeOnBlur={closeOnBlur}
previewHandler={previewHandler}
/>
)
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@ import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants/Constants'
import { KeyboardKey } from '@/Services/IOService'
import { formatSizeToReadableString } from '@standardnotes/filepicker'
import { FileItem } from '@standardnotes/snjs'
import { FormEventHandler, FunctionComponent, KeyboardEventHandler, useEffect, useRef, useState } from 'react'
import {
FormEventHandler,
FunctionComponent,
KeyboardEventHandler,
useCallback,
useEffect,
useRef,
useState,
} from 'react'
import Icon from '@/Components/Icon/Icon'
import { PopoverFileItemActionType } from './PopoverFileItemAction'
import PopoverFileSubmenu from './PopoverFileSubmenu'
Expand All @@ -15,6 +23,7 @@ const PopoverFileItem: FunctionComponent<PopoverFileItemProps> = ({
handleFileAction,
getIconType,
closeOnBlur,
previewHandler,
}) => {
const [fileName, setFileName] = useState(file.name)
const [isRenamingFile, setIsRenamingFile] = useState(false)
Expand All @@ -27,45 +36,56 @@ const PopoverFileItem: FunctionComponent<PopoverFileItemProps> = ({
}
}, [isRenamingFile])

const renameFile = async (file: FileItem, name: string) => {
await handleFileAction({
type: PopoverFileItemActionType.RenameFile,
payload: {
file,
name,
},
})
setIsRenamingFile(false)
}
const renameFile = useCallback(
async (file: FileItem, name: string) => {
if (name.length < 1) {
return
}

const handleFileNameInput: FormEventHandler<HTMLInputElement> = (event) => {
await handleFileAction({
type: PopoverFileItemActionType.RenameFile,
payload: {
file,
name,
},
})
setIsRenamingFile(false)
},
[handleFileAction],
)

const handleFileNameInput: FormEventHandler<HTMLInputElement> = useCallback((event) => {
setFileName((event.target as HTMLInputElement).value)
}
}, [])

const handleFileNameInputKeyDown: KeyboardEventHandler = (event) => {
if (event.key === KeyboardKey.Enter) {
itemRef.current?.focus()
}
}
const handleFileNameInputKeyDown: KeyboardEventHandler = useCallback(
(event) => {
if (fileName.length > 0 && event.key === KeyboardKey.Enter) {
itemRef.current?.focus()
}
},
[fileName.length],
)

const handleFileNameInputBlur = () => {
const handleFileNameInputBlur = useCallback(() => {
renameFile(file, fileName).catch(console.error)
}
}, [file, fileName, renameFile])

const handleClick = useCallback(() => {
if (isRenamingFile) {
return
}

const clickPreviewHandler = () => {
handleFileAction({
type: PopoverFileItemActionType.PreviewFile,
payload: file,
}).catch(console.error)
}
previewHandler(file)
}, [file, isRenamingFile, previewHandler])

return (
<div
ref={itemRef}
className="flex items-center justify-between p-3 focus:shadow-none"
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
>
<div onClick={clickPreviewHandler} className="flex items-center cursor-pointer">
<div onClick={handleClick} className="flex items-center cursor-pointer">
{getFileIconComponent(getIconType(file.mimeType), 'w-8 h-8 flex-shrink-0')}
<div className="flex flex-col mx-4">
{isRenamingFile ? (
Expand Down Expand Up @@ -97,7 +117,7 @@ const PopoverFileItem: FunctionComponent<PopoverFileItemProps> = ({
handleFileAction={handleFileAction}
setIsRenamingFile={setIsRenamingFile}
closeOnBlur={closeOnBlur}
previewHandler={clickPreviewHandler}
previewHandler={previewHandler}
/>
</div>
)
Expand Down
Loading

0 comments on commit 4caf958

Please sign in to comment.