Skip to content

Commit 6ad7019

Browse files
committed
Improve importing legacy notes behavior
1 parent 6f3d320 commit 6ad7019

File tree

2 files changed

+92
-73
lines changed

2 files changed

+92
-73
lines changed

src/components/PreferencesModal/StorageTab.tsx

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,6 @@ const StorageEditPage = ({ storage }: StorageEditPageProps) => {
6969
setEditingName(false)
7070
}, [storage.id, db, newName])
7171

72-
const [importLegacyFormIsFolded, setImportLegacyFormIsFolded] = useState(true)
73-
74-
const openImportLegacyForm = useCallback(() => {
75-
setImportLegacyFormIsFolded(false)
76-
}, [])
77-
78-
const foldImportLegacyForm = useCallback(() => {
79-
setImportLegacyFormIsFolded(true)
80-
}, [])
81-
8272
const [convertPouchFormIsFolded, setConvertPouchFormIsFolded] = useState(true)
8373

8474
const openConvertPouchForm = useCallback(() => {
@@ -138,21 +128,7 @@ const StorageEditPage = ({ storage }: StorageEditPageProps) => {
138128
{storage.type === 'fs' && (
139129
<>
140130
<hr />
141-
<FormHeading depth={2}>
142-
Import Notes from Legacy BoostNote
143-
</FormHeading>
144-
{importLegacyFormIsFolded ? (
145-
<FormGroup>
146-
<FormSecondaryButton onClick={openImportLegacyForm}>
147-
Import
148-
</FormSecondaryButton>
149-
</FormGroup>
150-
) : (
151-
<ImportLegacyNotesForm
152-
storageId={storage.id}
153-
onCancel={foldImportLegacyForm}
154-
/>
155-
)}
131+
<ImportLegacyNotesForm storageId={storage.id} />
156132
</>
157133
)}
158134

src/components/organisms/ImportLegacyNotesForm.tsx

Lines changed: 91 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
FormCheckItem,
88
FormLabel,
99
FormTextInput,
10+
FormHeading,
1011
} from '../atoms/form'
1112
import FormFolderSelector from '../atoms/FormFolderSelector'
1213
import {
@@ -22,27 +23,35 @@ import { NoteDocEditibleProps } from '../../lib/db/types'
2223
import { isFolderPathnameValid } from '../../lib/db/utils'
2324
import { useDb } from '../../lib/db'
2425
import { JsonObject } from 'type-fest'
25-
import { useRouter } from '../../lib/router'
2626
import { filenamify } from '../../lib/string'
2727

2828
interface ImportLegacyNotesFormProps {
2929
storageId: string
30-
onCancel: () => void
3130
}
3231

33-
const ImportLegacyNotesForm = ({
34-
storageId,
35-
onCancel,
36-
}: ImportLegacyNotesFormProps) => {
32+
const ImportLegacyNotesForm = ({ storageId }: ImportLegacyNotesFormProps) => {
33+
const { addAttachments, createFolder, createNote } = useDb()
34+
3735
const [location, setLocation] = useState('')
36+
const [opened, setOpened] = useState(false)
37+
const [doneMessage, setDoneMessage] = useState<string | null>(null)
3838
const [errorMessage, setErrorMessage] = useState(null)
3939
const [convertingSnippetNotes, setConvertingSnippetNotes] = useState(true)
4040
const [destinationFolderPathname, setDestinationFolderPathname] = useState(
4141
'/imported'
4242
)
4343
const [importing, setImporting] = useState(false)
44-
const { addAttachments, createFolder, createNote } = useDb()
45-
const { push } = useRouter()
44+
const openForm = useCallback(() => {
45+
setOpened(true)
46+
setDoneMessage(null)
47+
setErrorMessage(null)
48+
setConvertingSnippetNotes(true)
49+
setDestinationFolderPathname('/imported')
50+
}, [])
51+
52+
const closeForm = useCallback(() => {
53+
setOpened(false)
54+
}, [])
4655

4756
const importNotes = useCallback(async () => {
4857
if (importing) {
@@ -68,17 +77,22 @@ const ImportLegacyNotesForm = ({
6877
)
6978
)
7079

71-
const noteFileNames = await readdir(join(location, 'notes'))
80+
const noteFileNames = (
81+
await readdir(join(location, 'notes'))
82+
).filter((fileName) => fileName.match(/\.cson$/))
83+
84+
const invalidCsonFilePathnames: string[] = []
7285
const parsedNotes: [string, JsonObject | null][] = await Promise.all(
7386
noteFileNames.map(async (noteFileName) => {
7487
const noteFilePathname = join(location, 'notes', noteFileName)
75-
const rawCson = await readFileAsString(noteFilePathname)
7688
try {
89+
const rawCson = await readFileAsString(noteFilePathname)
7790
const noteCson = parseCSON(rawCson)
7891
return [noteFileName, noteCson] as [string, JsonObject]
7992
} catch (error) {
80-
console.error(`Failed to parse ${noteFilePathname}`)
81-
console.error(error)
93+
console.warn(`Failed to parse ${noteFilePathname}`)
94+
console.warn(error)
95+
invalidCsonFilePathnames.push(noteFilePathname)
8296
return [noteFileName, null] as [string, null]
8397
}
8498
})
@@ -113,15 +127,28 @@ const ImportLegacyNotesForm = ({
113127
const attachmentNameTupleList: [string, string][] = []
114128
await Promise.all(
115129
attachmentFileNames.map(async (fileName) => {
116-
const result = await readFile(
117-
join(noteAttachmentsFolderPathname, fileName)
130+
const attachmentPathname = join(
131+
noteAttachmentsFolderPathname,
132+
fileName
118133
)
134+
try {
135+
const result = await readFile(attachmentPathname)
119136

120-
const file = await convertBufferToFile(result, fileName)
137+
const file = await convertBufferToFile(result, fileName)
121138

122-
const [attachment] = await addAttachments(storageId, [file])
139+
const [attachment] = await addAttachments(storageId, [file])
123140

124-
attachmentNameTupleList.push([fileName, attachment.name])
141+
attachmentNameTupleList.push([fileName, attachment.name])
142+
} catch (error) {
143+
switch (error.code) {
144+
case 'EISDIR':
145+
break
146+
default:
147+
console.warn(
148+
`Failed to add ${attachmentPathname} attachment`
149+
)
150+
}
151+
}
125152
})
126153
)
127154

@@ -187,11 +214,14 @@ const ImportLegacyNotesForm = ({
187214
await createNote(storageId, note)
188215
}
189216

190-
push(
191-
`/app/storages/${storageId}/notes${
192-
destinationFolderPathname === '/' ? '' : destinationFolderPathname
217+
setDoneMessage(
218+
`The notes are imported to ${destinationFolderPathname}${
219+
invalidCsonFilePathnames.length > 0
220+
? ` (${invalidCsonFilePathnames.length} invalid file(s). Please check console in developer tool to know more)`
221+
: ''
193222
}`
194223
)
224+
setOpened(false)
195225
} catch (error) {
196226
setErrorMessage(error.message)
197227
console.error(error)
@@ -206,7 +236,6 @@ const ImportLegacyNotesForm = ({
206236
createFolder,
207237
createNote,
208238
importing,
209-
push,
210239
])
211240

212241
const updateConvertingSnippetNotes = useCallback(
@@ -225,34 +254,48 @@ const ImportLegacyNotesForm = ({
225254

226255
return (
227256
<>
228-
{errorMessage != null && (
229-
<FormBlockquote variant='danger'>{errorMessage}</FormBlockquote>
257+
<FormHeading depth={2}>Import Notes from Legacy BoostNote</FormHeading>
258+
{opened ? (
259+
<>
260+
{errorMessage != null && (
261+
<FormBlockquote variant='danger'>{errorMessage}</FormBlockquote>
262+
)}
263+
<FormGroup>
264+
<FormLabel>Legacy Storage Location</FormLabel>
265+
<FormFolderSelector value={location} setValue={setLocation} />
266+
</FormGroup>
267+
<FormGroup>
268+
<FormLabel>Destination Folder</FormLabel>
269+
<FormTextInput
270+
value={destinationFolderPathname}
271+
onChange={updateDestinationFolderPathname}
272+
/>
273+
</FormGroup>
274+
<FormGroup>
275+
<FormCheckItem
276+
id='convertingSnippetNotes-checkbox'
277+
type='checkbox'
278+
checked={convertingSnippetNotes}
279+
onChange={updateConvertingSnippetNotes}
280+
>
281+
Converting snippet notes
282+
</FormCheckItem>
283+
</FormGroup>
284+
<FormGroup>
285+
<FormPrimaryButton onClick={importNotes}>Import</FormPrimaryButton>
286+
<FormSecondaryButton onClick={closeForm}>
287+
Cancel
288+
</FormSecondaryButton>
289+
</FormGroup>
290+
</>
291+
) : (
292+
<FormGroup>
293+
{doneMessage != null && (
294+
<FormBlockquote variant='primary'>{doneMessage}</FormBlockquote>
295+
)}
296+
<FormSecondaryButton onClick={openForm}>Import</FormSecondaryButton>
297+
</FormGroup>
230298
)}
231-
<FormGroup>
232-
<FormLabel>Legacy Storage Location</FormLabel>
233-
<FormFolderSelector value={location} setValue={setLocation} />
234-
</FormGroup>
235-
<FormGroup>
236-
<FormLabel>Destination Folder</FormLabel>
237-
<FormTextInput
238-
value={destinationFolderPathname}
239-
onChange={updateDestinationFolderPathname}
240-
/>
241-
</FormGroup>
242-
<FormGroup>
243-
<FormCheckItem
244-
id='convertingSnippetNotes-checkbox'
245-
type='checkbox'
246-
checked={convertingSnippetNotes}
247-
onChange={updateConvertingSnippetNotes}
248-
>
249-
Converting snippet notes
250-
</FormCheckItem>
251-
</FormGroup>
252-
<FormGroup>
253-
<FormPrimaryButton onClick={importNotes}>Import</FormPrimaryButton>
254-
<FormSecondaryButton onClick={onCancel}>Cancel</FormSecondaryButton>
255-
</FormGroup>
256299
</>
257300
)
258301
}

0 commit comments

Comments
 (0)