From e040291b116847011f2d88d7a93320eac2c0ed5a Mon Sep 17 00:00:00 2001 From: Aman Harwara Date: Fri, 1 Dec 2023 18:18:22 +0530 Subject: [PATCH] refactor: import modal (#2670) --- .../Components/ImportModal/ImportModal.tsx | 52 +++++++--- .../ImportModal/ImportModalController.ts | 15 ++- .../ImportModal/ImportModalFileItem.tsx | 97 ++++++++++++------- .../Components/ImportModal/InitialPage.tsx | 18 +--- 4 files changed, 119 insertions(+), 63 deletions(-) diff --git a/packages/web/src/javascripts/Components/ImportModal/ImportModal.tsx b/packages/web/src/javascripts/Components/ImportModal/ImportModal.tsx index ea588faf7..1ddf6e8e6 100644 --- a/packages/web/src/javascripts/Components/ImportModal/ImportModal.tsx +++ b/packages/web/src/javascripts/Components/ImportModal/ImportModal.tsx @@ -1,5 +1,5 @@ import { observer } from 'mobx-react-lite' -import { useMemo } from 'react' +import { useCallback, useMemo } from 'react' import ImportModalFileItem from './ImportModalFileItem' import ImportModalInitialPage from './InitialPage' import Modal, { ModalAction } from '../Modal/Modal' @@ -11,6 +11,9 @@ import LinkedItemBubble from '../LinkedItems/LinkedItemBubble' import { createLinkFromItem } from '@/Utils/Items/Search/createLinkFromItem' import ItemSelectionDropdown from '../ItemSelectionDropdown/ItemSelectionDropdown' import { ContentType, SNTag } from '@standardnotes/snjs' +import Button from '../Button/Button' +import { ClassicFileReader } from '@standardnotes/filepicker' +import { NoteImportType } from '@standardnotes/ui-services' const ImportModal = ({ importModalController }: { importModalController: ImportModalController }) => { const application = useApplication() @@ -18,6 +21,7 @@ const ImportModal = ({ importModalController }: { importModalController: ImportM const { files, setFiles, + addFiles, addImportsToTag, setAddImportsToTag, shouldCreateTag, @@ -55,23 +59,45 @@ const ImportModal = ({ importModalController }: { importModalController: ImportM [close, existingTagForImports, importSuccessOrError, isReadyToImport, parseAndImport, shouldCreateTag], ) + const selectFiles = useCallback( + async (service?: NoteImportType) => { + const files = await ClassicFileReader.selectFiles() + + addFiles(files, service) + }, + [addFiles], + ) + return (
- {!files.length && } + {!files.length && } {files.length > 0 && ( -
- {files.map((file) => ( - - ))} -
+ <> +
+ {files.map((file) => ( + + ))} +
+ {!importSuccessOrError && ( + + )} + )}
{files.length > 0 && ( diff --git a/packages/web/src/javascripts/Components/ImportModal/ImportModalController.ts b/packages/web/src/javascripts/Components/ImportModal/ImportModalController.ts index ba3f5b7a2..2c084d2a0 100644 --- a/packages/web/src/javascripts/Components/ImportModal/ImportModalController.ts +++ b/packages/web/src/javascripts/Components/ImportModal/ImportModalController.ts @@ -66,6 +66,7 @@ export class ImportModalController extends AbstractViewController { files: observable, setFiles: action, + addFiles: action, updateFile: action, removeFile: action, @@ -106,13 +107,21 @@ export class ImportModalController extends AbstractViewController { this.preferences.setValue(PrefKey.ExistingTagForImports, tag?.uuid).catch(console.error) } - setFiles = (files: File[], service?: NoteImportType) => { - this.files = files.map((file) => ({ + getImportFromFile = (file: File, service?: NoteImportType) => { + return { id: UuidGenerator.GenerateUuid(), file, service, status: service ? 'ready' : 'pending', - })) + } as ImportModalFile + } + + setFiles = (files: File[], service?: NoteImportType) => { + this.files = files.map((file) => this.getImportFromFile(file, service)) + } + + addFiles = (files: File[], service?: NoteImportType) => { + this.files = [...this.files, ...files.map((file) => this.getImportFromFile(file, service))] } updateFile = (file: ImportModalFile) => { diff --git a/packages/web/src/javascripts/Components/ImportModal/ImportModalFileItem.tsx b/packages/web/src/javascripts/Components/ImportModal/ImportModalFileItem.tsx index 7cac899d7..13e810054 100644 --- a/packages/web/src/javascripts/Components/ImportModal/ImportModalFileItem.tsx +++ b/packages/web/src/javascripts/Components/ImportModal/ImportModalFileItem.tsx @@ -1,8 +1,8 @@ import { ImportModalController, ImportModalFile } from '@/Components/ImportModal/ImportModalController' -import { classNames, ContentType, DecryptedTransferPayload, pluralize } from '@standardnotes/snjs' +import { classNames, ContentType, pluralize } from '@standardnotes/snjs' import { Importer, NoteImportType } from '@standardnotes/ui-services' import { observer } from 'mobx-react-lite' -import { useCallback, useEffect } from 'react' +import { useCallback, useEffect, useState } from 'react' import Icon from '../Icon/Icon' const NoteImportTypeColors: Record = { @@ -36,23 +36,20 @@ const ImportModalFileItem = ({ removeFile: ImportModalController['removeFile'] importer: Importer }) => { + const [changingService, setChangingService] = useState(false) + const setFileService = useCallback( async (service: NoteImportType | null) => { - let payloads: DecryptedTransferPayload[] | undefined - try { - payloads = service ? await importer.getPayloadsFromFile(file.file, service) : undefined - } catch (error) { - console.error(error) + if (!service) { + setChangingService(true) } - updateFile({ ...file, service, status: service ? 'ready' : 'pending', - payloads, }) }, - [file, importer, updateFile], + [file, updateFile], ) useEffect(() => { @@ -82,7 +79,7 @@ const ImportModalFileItem = ({ return (
@@ -100,7 +97,7 @@ const ImportModalFileItem = ({ ? payloadsImportMessage : 'Ready to import' : null} - {file.status === 'pending' && 'Could not auto-detect service. Please select manually.'} + {file.status === 'pending' && !file.service && 'Could not auto-detect service. Please select manually.'} {file.status === 'parsing' && 'Parsing...'} {file.status === 'importing' && 'Importing...'} {file.status === 'uploading-files' && 'Uploading and embedding files...'} @@ -109,35 +106,69 @@ const ImportModalFileItem = ({
- {file.service == null && ( + {(file.status === 'ready' || file.status === 'pending') && (
-
{ - event.preventDefault() - const form = event.target as HTMLFormElement - const service = form.elements[0] as HTMLSelectElement - void setFileService(service.value as NoteImportType) - }} - > - - +
+ + + ) : ( + - + )}
)} diff --git a/packages/web/src/javascripts/Components/ImportModal/InitialPage.tsx b/packages/web/src/javascripts/Components/ImportModal/InitialPage.tsx index 7f44415ec..fa32d483e 100644 --- a/packages/web/src/javascripts/Components/ImportModal/InitialPage.tsx +++ b/packages/web/src/javascripts/Components/ImportModal/InitialPage.tsx @@ -1,30 +1,20 @@ import { ImportModalController } from '@/Components/ImportModal/ImportModalController' -import { ClassicFileReader } from '@standardnotes/filepicker' -import { NoteImportType } from '@standardnotes/ui-services' import { observer } from 'mobx-react-lite' -import { useCallback } from 'react' import Button from '../Button/Button' import Icon from '../Icon/Icon' import { useApplication } from '../ApplicationProvider' -import { FeatureStatus, NativeFeatureIdentifier } from '@standardnotes/snjs' import { FeatureName } from '@/Controllers/FeatureName' +import { NativeFeatureIdentifier, FeatureStatus } from '@standardnotes/snjs' +import { NoteImportType } from '@standardnotes/ui-services' type Props = { setFiles: ImportModalController['setFiles'] + selectFiles: (service?: NoteImportType) => Promise } -const ImportModalInitialPage = ({ setFiles }: Props) => { +const ImportModalInitialPage = ({ setFiles, selectFiles }: Props) => { const application = useApplication() - const selectFiles = useCallback( - async (service?: NoteImportType) => { - const files = await ClassicFileReader.selectFiles() - - setFiles(files, service) - }, - [setFiles], - ) - return ( <>