fix: Fixed issue where exporting multiple files with the same name would error

This commit is contained in:
Aman Harwara
2023-10-31 16:43:02 +05:30
parent d1c295751d
commit f408950aa4
3 changed files with 33 additions and 25 deletions

View File

@@ -19,9 +19,9 @@ function zippableFileName(name: string, suffix = '', format = 'txt'): string {
return sanitizedName.slice(0, maxFileNameLength - nameEnd.length) + nameEnd return sanitizedName.slice(0, maxFileNameLength - nameEnd.length) + nameEnd
} }
export function parseAndCreateZippableFileName(name: string) { export function parseAndCreateZippableFileName(name: string, suffix = '') {
const { name: parsedName, ext } = parseFileName(name) const { name: parsedName, ext } = parseFileName(name)
return zippableFileName(parsedName, '', ext) return zippableFileName(parsedName, suffix, ext)
} }
type ZippableData = { type ZippableData = {

View File

@@ -1,7 +1,7 @@
import Icon from '@/Components/Icon/Icon' import Icon from '@/Components/Icon/Icon'
import { observer } from 'mobx-react-lite' import { observer } from 'mobx-react-lite'
import { useState, useEffect, useMemo, useCallback } from 'react' import { useState, useEffect, useMemo, useCallback } from 'react'
import { NoteType, Platform, SNNote } from '@standardnotes/snjs' import { NoteType, Platform, SNNote, pluralize } from '@standardnotes/snjs'
import { import {
CHANGE_EDITOR_WIDTH_COMMAND, CHANGE_EDITOR_WIDTH_COMMAND,
OPEN_NOTE_HISTORY_COMMAND, OPEN_NOTE_HISTORY_COMMAND,
@@ -99,6 +99,13 @@ const NotesOptions = ({ notes, closeMenu }: NotesOptionsProps) => {
}, []) }, [])
const downloadSelectedItems = useCallback(async () => { const downloadSelectedItems = useCallback(async () => {
if (notes.length === 0) {
return
}
const toast = addToast({
type: ToastType.Progress,
message: `Exporting ${notes.length} ${pluralize(notes.length, 'note', 'notes')}...`,
})
try { try {
const result = await createNoteExport(application, notes) const result = await createNoteExport(application, notes)
if (!result) { if (!result) {
@@ -113,12 +120,14 @@ const NotesOptions = ({ notes, closeMenu }: NotesOptionsProps) => {
filename: fileName, filename: fileName,
isNativeMobileWeb: application.isNativeMobileWeb(), isNativeMobileWeb: application.isNativeMobileWeb(),
}) })
dismissToast(toast)
} catch (error) { } catch (error) {
console.error(error) console.error(error)
addToast({ addToast({
type: ToastType.Error, type: ToastType.Error,
message: 'Could not export notes', message: 'Could not export notes',
}) })
dismissToast(toast)
} }
}, [application, notes]) }, [application, notes])

View File

@@ -1,8 +1,16 @@
import { WebApplication } from '@/Application/WebApplication' import { WebApplication } from '@/Application/WebApplication'
import { HeadlessSuperConverter } from '@/Components/SuperEditor/Tools/HeadlessSuperConverter' import { HeadlessSuperConverter } from '@/Components/SuperEditor/Tools/HeadlessSuperConverter'
import { NoteType, PrefKey, SNNote, PrefDefaults, FileItem, PrefValue, pluralize } from '@standardnotes/snjs' import { NoteType, PrefKey, SNNote, PrefDefaults, FileItem, PrefValue } from '@standardnotes/snjs'
import { WebApplicationInterface, parseAndCreateZippableFileName, sanitizeFileName } from '@standardnotes/ui-services' import { WebApplicationInterface, parseAndCreateZippableFileName } from '@standardnotes/ui-services'
import { ZipDirectoryEntry } from '@zip.js/zip.js' import { ZipDirectoryEntry } from '@zip.js/zip.js'
// @ts-expect-error Using inline loaders to load CSS as string
import superEditorCSS from '!css-loader!sass-loader!../Components/SuperEditor/Lexical/Theme/editor.scss'
// @ts-expect-error Using inline loaders to load CSS as string
import snColorsCSS from '!css-loader!sass-loader!@standardnotes/styles/src/Styles/_colors.scss'
// @ts-expect-error Using inline loaders to load CSS as string
import exportOverridesCSS from '!css-loader!sass-loader!../Components/SuperEditor/Lexical/Theme/export-overrides.scss'
import { getBase64FromBlob } from './Utils'
import { parseFileName } from '@standardnotes/filepicker'
export const getNoteFormat = (application: WebApplicationInterface, note: SNNote) => { export const getNoteFormat = (application: WebApplicationInterface, note: SNNote) => {
if (note.noteType === NoteType.Super) { if (note.noteType === NoteType.Super) {
@@ -25,15 +33,6 @@ export const getNoteFileName = (application: WebApplicationInterface, note: SNNo
const headlessSuperConverter = new HeadlessSuperConverter() const headlessSuperConverter = new HeadlessSuperConverter()
// @ts-expect-error Using inline loaders to load CSS as string
import superEditorCSS from '!css-loader!sass-loader!../Components/SuperEditor/Lexical/Theme/editor.scss'
// @ts-expect-error Using inline loaders to load CSS as string
import snColorsCSS from '!css-loader!sass-loader!@standardnotes/styles/src/Styles/_colors.scss'
// @ts-expect-error Using inline loaders to load CSS as string
import exportOverridesCSS from '!css-loader!sass-loader!../Components/SuperEditor/Lexical/Theme/export-overrides.scss'
import { getBase64FromBlob } from './Utils'
import { ToastType, addToast, dismissToast } from '@standardnotes/toast'
const superHTML = (note: SNNote, content: string) => `<!DOCTYPE html> const superHTML = (note: SNNote, content: string) => `<!DOCTYPE html>
<html> <html>
<head> <head>
@@ -175,11 +174,6 @@ export const createNoteExport = async (
return return
} }
const toast = addToast({
type: ToastType.Progress,
message: `Exporting ${notes.length} ${pluralize(notes.length, 'note', 'notes')}...`,
})
const superExportFormatPref = application.getPreference( const superExportFormatPref = application.getPreference(
PrefKey.SuperNoteExportFormat, PrefKey.SuperNoteExportFormat,
PrefDefaults[PrefKey.SuperNoteExportFormat], PrefDefaults[PrefKey.SuperNoteExportFormat],
@@ -192,7 +186,6 @@ export const createNoteExport = async (
if (notes.length === 1 && !noteRequiresFolder(notes[0], superExportFormatPref, superEmbedBehaviorPref)) { if (notes.length === 1 && !noteRequiresFolder(notes[0], superExportFormatPref, superEmbedBehaviorPref)) {
const blob = await getNoteBlob(application, notes[0], superEmbedBehaviorPref) const blob = await getNoteBlob(application, notes[0], superEmbedBehaviorPref)
const fileName = getNoteFileName(application, notes[0]) const fileName = getNoteFileName(application, notes[0])
dismissToast(toast)
return { return {
blob, blob,
fileName, fileName,
@@ -211,31 +204,37 @@ export const createNoteExport = async (
await addEmbeddedFilesToFolder(application, notes[0], root) await addEmbeddedFilesToFolder(application, notes[0], root)
const zippedBlob = await zipFS.exportBlob() const zippedBlob = await zipFS.exportBlob()
dismissToast(toast)
return { return {
blob: zippedBlob, blob: zippedBlob,
fileName: fileName + '.zip', fileName: fileName + '.zip',
} }
} }
const filenameCounts: Record<string, number> = {}
for (const note of notes) { for (const note of notes) {
const blob = await getNoteBlob(application, note, superEmbedBehaviorPref) const blob = await getNoteBlob(application, note, superEmbedBehaviorPref)
const fileName = parseAndCreateZippableFileName(getNoteFileName(application, note)) const _name = getNoteFileName(application, note)
filenameCounts[_name] = filenameCounts[_name] == undefined ? 0 : filenameCounts[_name] + 1
const currentFileNameIndex = filenameCounts[_name]
const fileName = parseAndCreateZippableFileName(_name, currentFileNameIndex > 0 ? ` - ${currentFileNameIndex}` : '')
if (!noteRequiresFolder(note, superExportFormatPref, superEmbedBehaviorPref)) { if (!noteRequiresFolder(note, superExportFormatPref, superEmbedBehaviorPref)) {
root.addBlob(fileName, blob) root.addBlob(fileName, blob)
continue continue
} }
const folder = root.addDirectory(sanitizeFileName(note.title)) const { name } = parseFileName(fileName)
const folder = root.addDirectory(name)
folder.addBlob(fileName, blob) folder.addBlob(fileName, blob)
await addEmbeddedFilesToFolder(application, note, folder) await addEmbeddedFilesToFolder(application, note, folder)
} }
const zippedBlob = await zipFS.exportBlob() const zippedBlob = await zipFS.exportBlob()
dismissToast(toast)
return { return {
blob: zippedBlob, blob: zippedBlob,
fileName: `Standard Notes Export - ${application.archiveService.formattedDateForExports()}.zip`, fileName: `Standard Notes Export - ${application.archiveService.formattedDateForExports()}.zip`,