diff --git a/packages/mobile/src/Lib/MobileDevice.ts b/packages/mobile/src/Lib/MobileDevice.ts index 29d9c75a23f..988201f7cd5 100644 --- a/packages/mobile/src/Lib/MobileDevice.ts +++ b/packages/mobile/src/Lib/MobileDevice.ts @@ -1,4 +1,5 @@ import SNReactNative from '@standardnotes/react-native-utils' +import { sanitizeFileNameForNativeWrite } from './Utils' import { AppleIAPProductId, AppleIAPReceipt, @@ -502,7 +503,8 @@ export class MobileDevice implements MobileDeviceInterface { directory = saveInTempLocation ? CachesDirectoryPath : DownloadDirectoryPath } - return `${directory}/${filename}` + const safeFilename = sanitizeFileNameForNativeWrite(filename) + return `${directory}/${safeFilename}` } async downloadBase64AsFile( diff --git a/packages/mobile/src/Lib/Utils.ts b/packages/mobile/src/Lib/Utils.ts index 193913d9711..6eda635c133 100644 --- a/packages/mobile/src/Lib/Utils.ts +++ b/packages/mobile/src/Lib/Utils.ts @@ -38,3 +38,24 @@ export function isSameDay(dateA: Date, dateB: Date) { dateA.getDate() === dateB.getDate() ) } + +function parseFileName(fileName: string): { + name: string + ext: string +} { + const pattern = /(?:\.([^.]+))$/ + const extMatches = pattern.exec(fileName) + const ext = extMatches?.[1] || '' + const name = fileName.includes('.') ? fileName.substring(0, fileName.lastIndexOf('.')) : fileName + + return { name, ext } +} + +function sanitizeFileName(name: string): string { + return name.trim().replace(/[.\\/:"?*|<>]/g, '_') +} + +export function sanitizeFileNameForNativeWrite(filename: string): string { + const { name, ext } = parseFileName(filename) + return `${sanitizeFileName(name)}.${ext}` +} diff --git a/packages/web/src/javascripts/Components/FilePreview/PreviewComponent.tsx b/packages/web/src/javascripts/Components/FilePreview/PreviewComponent.tsx index 622678c0160..1417ed29f70 100644 --- a/packages/web/src/javascripts/Components/FilePreview/PreviewComponent.tsx +++ b/packages/web/src/javascripts/Components/FilePreview/PreviewComponent.tsx @@ -9,7 +9,6 @@ import ImagePreview from './ImagePreview' import { OptionalSuperEmbeddedImageProps } from './OptionalSuperEmbeddedImageProps' import { PreviewableTextFileTypes, RequiresNativeFilePreview } from './isFilePreviewable' import TextPreview from './TextPreview' -import { parseFileName, sanitizeFileName } from '@standardnotes/utils' import VideoPreview from './VideoPreview' type Props = { @@ -60,11 +59,7 @@ const PreviewComponent: FunctionComponent = ({ }), ) - const { name, ext } = parseFileName(file.name) - const sanitizedName = sanitizeFileName(name) - const filename = `${sanitizedName}.${ext}` - - void application.mobileDevice.previewFile(fileBase64, filename) + void application.mobileDevice.previewFile(fileBase64, file.name) }, [application, bytes, file.mimeType, file.name, isNativeMobileWeb]) if (isNativeMobileWeb && requiresNativePreview) {