From 90e10c76f73c97705e57c52d10de4e29f4a40a94 Mon Sep 17 00:00:00 2001 From: Aman Harwara Date: Sun, 4 Dec 2022 11:11:37 +0530 Subject: [PATCH] refactor: separate selectAndUploadFile and uploadFile functions (#2085) --- .../ContentListView/ContentListView.tsx | 8 +- .../Components/FileDragNDropProvider.tsx | 8 +- .../FileView/FileViewWithoutProtection.tsx | 6 +- .../LinkedItems/LinkedItemsPanel.tsx | 10 +- .../NoteView/NoteViewFileDropTarget.tsx | 8 +- .../Plugins/EncryptedFilePlugin/FilePlugin.ts | 8 +- .../Components/Tags/TagsListItem.tsx | 6 +- .../Controllers/FilesController.ts | 163 +++++++++--------- .../Controllers/Moments/MomentsService.ts | 4 +- 9 files changed, 108 insertions(+), 113 deletions(-) diff --git a/packages/web/src/javascripts/Components/ContentListView/ContentListView.tsx b/packages/web/src/javascripts/Components/ContentListView/ContentListView.tsx index df30fcc54..eefcc85aa 100644 --- a/packages/web/src/javascripts/Components/ContentListView/ContentListView.tsx +++ b/packages/web/src/javascripts/Components/ContentListView/ContentListView.tsx @@ -114,7 +114,7 @@ const ContentListView = forwardRef( }, [selectedTag, application, onPanelWidthLoad]) const fileDropCallback = useCallback( - async (files: FileItem[]) => { + async (file: FileItem) => { const currentTag = navigationController.selected if (!currentTag) { @@ -126,9 +126,7 @@ const ContentListView = forwardRef( return } - files.forEach(async (file) => { - await linkingController.linkItems(file, currentTag) - }) + await linkingController.linkItems(file, currentTag) }, [navigationController, linkingController], ) @@ -170,7 +168,7 @@ const ContentListView = forwardRef( return } - void filesController.uploadNewFile() + void filesController.selectAndUploadNewFiles() } else { await createNewNote() toggleAppPane(AppPaneId.Editor) diff --git a/packages/web/src/javascripts/Components/FileDragNDropProvider.tsx b/packages/web/src/javascripts/Components/FileDragNDropProvider.tsx index b06383584..e98e293b6 100644 --- a/packages/web/src/javascripts/Components/FileDragNDropProvider.tsx +++ b/packages/web/src/javascripts/Components/FileDragNDropProvider.tsx @@ -11,7 +11,7 @@ import Portal from './Portal/Portal' type FileDragTargetData = { tooltipText: string - callback: (files: FileItem[]) => void + callback: (files: FileItem) => void } type FileDnDContextData = { @@ -203,14 +203,14 @@ const FileDragNDropProvider = ({ application, children, featuresController, file return } - const uploadedFiles = await filesController.uploadNewFile(fileOrHandle) + const uploadedFile = await filesController.uploadNewFile(fileOrHandle) - if (!uploadedFiles) { + if (!uploadedFile) { return } if (closestDragTarget && dragTargets.current.has(closestDragTarget)) { - dragTargets.current.get(closestDragTarget)?.callback(uploadedFiles) + dragTargets.current.get(closestDragTarget)?.callback(uploadedFile) } }) diff --git a/packages/web/src/javascripts/Components/FileView/FileViewWithoutProtection.tsx b/packages/web/src/javascripts/Components/FileView/FileViewWithoutProtection.tsx index d0d701f25..523d5278c 100644 --- a/packages/web/src/javascripts/Components/FileView/FileViewWithoutProtection.tsx +++ b/packages/web/src/javascripts/Components/FileView/FileViewWithoutProtection.tsx @@ -51,10 +51,8 @@ const FileViewWithoutProtection = ({ application, viewControllerManager, file }: if (target) { addDragTarget(target, { tooltipText: 'Drop your files to upload and link them to the current file', - callback(files) { - files.forEach(async (uploadedFile) => { - await viewControllerManager.linkingController.linkItems(uploadedFile, file) - }) + async callback(uploadedFile) { + await viewControllerManager.linkingController.linkItems(uploadedFile, file) }, }) } diff --git a/packages/web/src/javascripts/Components/LinkedItems/LinkedItemsPanel.tsx b/packages/web/src/javascripts/Components/LinkedItems/LinkedItemsPanel.tsx index b2926a913..a7633bd4f 100644 --- a/packages/web/src/javascripts/Components/LinkedItems/LinkedItemsPanel.tsx +++ b/packages/web/src/javascripts/Components/LinkedItems/LinkedItemsPanel.tsx @@ -63,13 +63,9 @@ const LinkedItemsPanel = ({ return } - const uploadedFiles = await filesController.uploadNewFile() - - if (uploadedFiles && uploadedFiles.length) { - uploadedFiles.forEach((file) => { - void linkItemToSelectedItem(file) - }) - } + void filesController.selectAndUploadNewFiles((file) => { + void linkItemToSelectedItem(file) + }) } return ( diff --git a/packages/web/src/javascripts/Components/NoteView/NoteViewFileDropTarget.tsx b/packages/web/src/javascripts/Components/NoteView/NoteViewFileDropTarget.tsx index 5666dad2f..9a538e08f 100644 --- a/packages/web/src/javascripts/Components/NoteView/NoteViewFileDropTarget.tsx +++ b/packages/web/src/javascripts/Components/NoteView/NoteViewFileDropTarget.tsx @@ -20,11 +20,9 @@ const NoteViewFileDropTarget = ({ note, linkingController, noteViewElement, file if (target) { addDragTarget(target, { tooltipText: 'Drop your files to upload and link them to the current note', - callback: (files) => { - files.forEach(async (uploadedFile) => { - await linkingController.linkItems(note, uploadedFile) - filesController.notifyObserversOfUploadedFileLinkingToCurrentNote(uploadedFile.uuid) - }) + callback: async (uploadedFile) => { + await linkingController.linkItems(note, uploadedFile) + filesController.notifyObserversOfUploadedFileLinkingToCurrentNote(uploadedFile.uuid) }, }) } diff --git a/packages/web/src/javascripts/Components/NoteView/SuperEditor/Plugins/EncryptedFilePlugin/FilePlugin.ts b/packages/web/src/javascripts/Components/NoteView/SuperEditor/Plugins/EncryptedFilePlugin/FilePlugin.ts index 6fb824333..ea493df31 100644 --- a/packages/web/src/javascripts/Components/NoteView/SuperEditor/Plugins/EncryptedFilePlugin/FilePlugin.ts +++ b/packages/web/src/javascripts/Components/NoteView/SuperEditor/Plugins/EncryptedFilePlugin/FilePlugin.ts @@ -28,11 +28,9 @@ export default function FilePlugin(): JSX.Element | null { const uploadFilesList = (files: FileList) => { Array.from(files).forEach(async (file) => { try { - const uploadedFiles = await filesController.uploadNewFile(file) - if (uploadedFiles) { - uploadedFiles.forEach((uploadedFile) => { - editor.dispatchCommand(INSERT_FILE_COMMAND, uploadedFile.uuid) - }) + const uploadedFile = await filesController.uploadNewFile(file) + if (uploadedFile) { + editor.dispatchCommand(INSERT_FILE_COMMAND, uploadedFile.uuid) } } catch (error) { console.error(error) diff --git a/packages/web/src/javascripts/Components/Tags/TagsListItem.tsx b/packages/web/src/javascripts/Components/Tags/TagsListItem.tsx index 99f66167c..f7e963ebb 100644 --- a/packages/web/src/javascripts/Components/Tags/TagsListItem.tsx +++ b/packages/web/src/javascripts/Components/Tags/TagsListItem.tsx @@ -178,10 +178,8 @@ export const TagsListItem: FunctionComponent = observer( if (target) { addDragTarget(target, { tooltipText: `Drop your files to upload and link them to tag "${tag.title}"`, - callback(files) { - files.forEach(async (file) => { - await linkingController.linkItems(file, tag) - }) + async callback(file) { + await linkingController.linkItems(file, tag) }, }) } diff --git a/packages/web/src/javascripts/Controllers/FilesController.ts b/packages/web/src/javascripts/Controllers/FilesController.ts index fe7e18b67..ba7a5b129 100644 --- a/packages/web/src/javascripts/Controllers/FilesController.ts +++ b/packages/web/src/javascripts/Controllers/FilesController.ts @@ -56,6 +56,10 @@ export class FilesController extends AbstractViewController { + if (!this.shouldUseStreamingReader && this.maxFileSize && file.size >= this.maxFileSize) { + this.application.alertService + .alert( + `This file exceeds the limits supported in this browser. To upload files greater than ${ + this.maxFileSize / BYTES_IN_ONE_MEGABYTE + }MB, please use the desktop application or the Chrome browser.`, + `Cannot upload file "${file.name}"`, + ) + .catch(console.error) + return true + } + return false + } + + public async selectAndUploadNewFiles(callback?: (file: FileItem) => void) { + const selectedFiles = await this.reader.selectFiles() + + selectedFiles.forEach(async (file) => { + if (this.alertIfFileExceedsSizeLimit(file)) { + return + } + const uploadedFile = await this.uploadNewFile(file) + if (uploadedFile && callback) { + callback(uploadedFile) + } + }) + } + + public async uploadNewFile(fileOrHandle: File | FileSystemFileHandle): Promise { let toastId = '' try { const minimumChunkSize = this.application.files.minimumChunkSize() - const shouldUseStreamingReader = StreamingFileReader.available() - - const picker = shouldUseStreamingReader ? StreamingFileReader : ClassicFileReader - const maxFileSize = picker.maximumFileSize() - - const selectedFiles = - fileOrHandle instanceof File - ? [fileOrHandle] - : shouldUseStreamingReader && fileOrHandle instanceof FileSystemFileHandle - ? await StreamingFileReader.getFilesFromHandles([fileOrHandle]) - : await picker.selectFiles() - - if (selectedFiles.length === 0) { + if (fileOrHandle instanceof FileSystemFileHandle && !this.shouldUseStreamingReader) { return } - const uploadedFiles: FileItem[] = [] + const fileToUpload = fileOrHandle instanceof File ? fileOrHandle : await fileOrHandle.getFile() - for (const file of selectedFiles) { - if (!shouldUseStreamingReader && maxFileSize && file.size >= maxFileSize) { - this.application.alertService - .alert( - `This file exceeds the limits supported in this browser. To upload files greater than ${ - maxFileSize / BYTES_IN_ONE_MEGABYTE - }MB, please use the desktop application or the Chrome browser.`, - `Cannot upload file "${file.name}"`, - ) - .catch(console.error) - continue - } + if (this.alertIfFileExceedsSizeLimit(fileToUpload)) { + return + } - const operation = await this.application.files.beginNewFileUpload(file.size) + const operation = await this.application.files.beginNewFileUpload(fileToUpload.size) - if (operation instanceof ClientDisplayableError) { - addToast({ - type: ToastType.Error, - message: 'Unable to start upload session', - }) - throw new Error('Unable to start upload session') - } - - const initialProgress = operation.getProgress().percentComplete - - toastId = addToast({ - type: ToastType.Progress, - message: `Uploading file "${file.name}" (${initialProgress}%)`, - progress: initialProgress, - }) - - const onChunk: OnChunkCallbackNoProgress = async ({ data, index, isLast }) => { - await this.application.files.pushBytesForUpload(operation, data, index, isLast) - - const percentComplete = Math.round(operation.getProgress().percentComplete) - updateToast(toastId, { - message: `Uploading file "${file.name}" (${percentComplete}%)`, - progress: percentComplete, - }) - } - - const fileResult = await picker.readFile(file, minimumChunkSize, onChunk) - - if (!fileResult.mimeType) { - const { ext } = parseFileName(file.name) - fileResult.mimeType = await this.application.getArchiveService().getMimeType(ext) - } - - const uploadedFile = await this.application.files.finishUpload(operation, fileResult) - - if (uploadedFile instanceof ClientDisplayableError) { - addToast({ - type: ToastType.Error, - message: 'Unable to close upload session', - }) - throw new Error('Unable to close upload session') - } - - uploadedFiles.push(uploadedFile) - - dismissToast(toastId) + if (operation instanceof ClientDisplayableError) { addToast({ - type: ToastType.Success, - message: `Uploaded file "${uploadedFile.name}"`, + type: ToastType.Error, + message: 'Unable to start upload session', + }) + throw new Error('Unable to start upload session') + } + + const initialProgress = operation.getProgress().percentComplete + + toastId = addToast({ + type: ToastType.Progress, + message: `Uploading file "${fileToUpload.name}" (${initialProgress}%)`, + progress: initialProgress, + }) + + const onChunk: OnChunkCallbackNoProgress = async ({ data, index, isLast }) => { + await this.application.files.pushBytesForUpload(operation, data, index, isLast) + + const percentComplete = Math.round(operation.getProgress().percentComplete) + updateToast(toastId, { + message: `Uploading file "${fileToUpload.name}" (${percentComplete}%)`, + progress: percentComplete, }) } - return uploadedFiles + const fileResult = await this.reader.readFile(fileToUpload, minimumChunkSize, onChunk) + + if (!fileResult.mimeType) { + const { ext } = parseFileName(fileToUpload.name) + fileResult.mimeType = await this.application.getArchiveService().getMimeType(ext) + } + + const uploadedFile = await this.application.files.finishUpload(operation, fileResult) + + if (uploadedFile instanceof ClientDisplayableError) { + addToast({ + type: ToastType.Error, + message: 'Unable to close upload session', + }) + throw new Error('Unable to close upload session') + } + + dismissToast(toastId) + addToast({ + type: ToastType.Success, + message: `Uploaded file "${uploadedFile.name}"`, + }) + + return uploadedFile } catch (error) { console.error(error) diff --git a/packages/web/src/javascripts/Controllers/Moments/MomentsService.ts b/packages/web/src/javascripts/Controllers/Moments/MomentsService.ts index 2fcb0f622..67eda5350 100644 --- a/packages/web/src/javascripts/Controllers/Moments/MomentsService.ts +++ b/packages/web/src/javascripts/Controllers/Moments/MomentsService.ts @@ -80,7 +80,7 @@ export class MomentsService extends AbstractViewController { } } - public async takePhoto(): Promise { + public async takePhoto(): Promise { const toastId = addToast({ type: ToastType.Loading, message: 'Capturing Moment...', @@ -123,7 +123,7 @@ export class MomentsService extends AbstractViewController { const defaultTag = this.getDefaultTag() if (defaultTag && uploadedFile) { - void this.application.linkingController.linkItems(uploadedFile[0], defaultTag) + void this.application.linkingController.linkItems(uploadedFile, defaultTag) } stopCameraStream(canvas, video, stream)