fix: Fixed issue where exporting multiple files with the same name would error
This commit is contained in:
@@ -19,9 +19,9 @@ function zippableFileName(name: string, suffix = '', format = 'txt'): string {
|
||||
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)
|
||||
return zippableFileName(parsedName, '', ext)
|
||||
return zippableFileName(parsedName, suffix, ext)
|
||||
}
|
||||
|
||||
type ZippableData = {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { useState, useEffect, useMemo, useCallback } from 'react'
|
||||
import { NoteType, Platform, SNNote } from '@standardnotes/snjs'
|
||||
import { NoteType, Platform, SNNote, pluralize } from '@standardnotes/snjs'
|
||||
import {
|
||||
CHANGE_EDITOR_WIDTH_COMMAND,
|
||||
OPEN_NOTE_HISTORY_COMMAND,
|
||||
@@ -99,6 +99,13 @@ const NotesOptions = ({ notes, closeMenu }: NotesOptionsProps) => {
|
||||
}, [])
|
||||
|
||||
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 {
|
||||
const result = await createNoteExport(application, notes)
|
||||
if (!result) {
|
||||
@@ -113,12 +120,14 @@ const NotesOptions = ({ notes, closeMenu }: NotesOptionsProps) => {
|
||||
filename: fileName,
|
||||
isNativeMobileWeb: application.isNativeMobileWeb(),
|
||||
})
|
||||
dismissToast(toast)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
addToast({
|
||||
type: ToastType.Error,
|
||||
message: 'Could not export notes',
|
||||
})
|
||||
dismissToast(toast)
|
||||
}
|
||||
}, [application, notes])
|
||||
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { HeadlessSuperConverter } from '@/Components/SuperEditor/Tools/HeadlessSuperConverter'
|
||||
import { NoteType, PrefKey, SNNote, PrefDefaults, FileItem, PrefValue, pluralize } from '@standardnotes/snjs'
|
||||
import { WebApplicationInterface, parseAndCreateZippableFileName, sanitizeFileName } from '@standardnotes/ui-services'
|
||||
import { NoteType, PrefKey, SNNote, PrefDefaults, FileItem, PrefValue } from '@standardnotes/snjs'
|
||||
import { WebApplicationInterface, parseAndCreateZippableFileName } from '@standardnotes/ui-services'
|
||||
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) => {
|
||||
if (note.noteType === NoteType.Super) {
|
||||
@@ -25,15 +33,6 @@ export const getNoteFileName = (application: WebApplicationInterface, note: SNNo
|
||||
|
||||
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>
|
||||
<html>
|
||||
<head>
|
||||
@@ -175,11 +174,6 @@ export const createNoteExport = async (
|
||||
return
|
||||
}
|
||||
|
||||
const toast = addToast({
|
||||
type: ToastType.Progress,
|
||||
message: `Exporting ${notes.length} ${pluralize(notes.length, 'note', 'notes')}...`,
|
||||
})
|
||||
|
||||
const superExportFormatPref = application.getPreference(
|
||||
PrefKey.SuperNoteExportFormat,
|
||||
PrefDefaults[PrefKey.SuperNoteExportFormat],
|
||||
@@ -192,7 +186,6 @@ export const createNoteExport = async (
|
||||
if (notes.length === 1 && !noteRequiresFolder(notes[0], superExportFormatPref, superEmbedBehaviorPref)) {
|
||||
const blob = await getNoteBlob(application, notes[0], superEmbedBehaviorPref)
|
||||
const fileName = getNoteFileName(application, notes[0])
|
||||
dismissToast(toast)
|
||||
return {
|
||||
blob,
|
||||
fileName,
|
||||
@@ -211,31 +204,37 @@ export const createNoteExport = async (
|
||||
await addEmbeddedFilesToFolder(application, notes[0], root)
|
||||
|
||||
const zippedBlob = await zipFS.exportBlob()
|
||||
dismissToast(toast)
|
||||
return {
|
||||
blob: zippedBlob,
|
||||
fileName: fileName + '.zip',
|
||||
}
|
||||
}
|
||||
|
||||
const filenameCounts: Record<string, number> = {}
|
||||
|
||||
for (const note of notes) {
|
||||
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)) {
|
||||
root.addBlob(fileName, blob)
|
||||
continue
|
||||
}
|
||||
|
||||
const folder = root.addDirectory(sanitizeFileName(note.title))
|
||||
const { name } = parseFileName(fileName)
|
||||
const folder = root.addDirectory(name)
|
||||
folder.addBlob(fileName, blob)
|
||||
await addEmbeddedFilesToFolder(application, note, folder)
|
||||
}
|
||||
|
||||
const zippedBlob = await zipFS.exportBlob()
|
||||
|
||||
dismissToast(toast)
|
||||
|
||||
return {
|
||||
blob: zippedBlob,
|
||||
fileName: `Standard Notes Export - ${application.archiveService.formattedDateForExports()}.zip`,
|
||||
|
||||
Reference in New Issue
Block a user