chore: fix issue with downloading multiple files on macOS

This commit is contained in:
Aman Harwara
2024-01-03 22:56:01 +05:30
parent bc0f444ed8
commit 64351dd304
8 changed files with 52 additions and 20 deletions

View File

@@ -29,7 +29,7 @@
"@standardnotes/common": "^1.50.0", "@standardnotes/common": "^1.50.0",
"@standardnotes/files": "workspace:*", "@standardnotes/files": "workspace:*",
"@standardnotes/utils": "workspace:*", "@standardnotes/utils": "workspace:*",
"@types/wicg-file-system-access": "^2020.9.5", "@types/wicg-file-system-access": "^2023.10.4",
"reflect-metadata": "^0.1.13" "reflect-metadata": "^0.1.13"
} }
} }

View File

@@ -22,12 +22,14 @@ export class StreamingFileSaver {
} }
/** This function must be called in response to a user interaction, otherwise, it will be rejected by the browser. */ /** This function must be called in response to a user interaction, otherwise, it will be rejected by the browser. */
async selectFileToSaveTo(): Promise<void> { async selectFileToSaveTo(handle?: FileSystemFileHandle): Promise<void> {
this.log('Showing save file picker') this.log('Showing save file picker')
const downloadHandle = await window.showSaveFilePicker({ const downloadHandle = handle
suggestedName: this.name, ? handle
}) : await window.showSaveFilePicker({
suggestedName: this.name,
})
this.writableStream = await downloadHandle.createWritable() this.writableStream = await downloadHandle.createWritable()
} }

View File

@@ -59,7 +59,7 @@
"@types/jest": "^29.2.4", "@types/jest": "^29.2.4",
"@types/react": "^18.2.39", "@types/react": "^18.2.39",
"@types/react-dom": "^18.2.17", "@types/react-dom": "^18.2.17",
"@types/wicg-file-system-access": "^2020.9.5", "@types/wicg-file-system-access": "^2023.10.4",
"@zip.js/zip.js": "^2.6.60", "@zip.js/zip.js": "^2.6.60",
"autoprefixer": "^10.4.13", "autoprefixer": "^10.4.13",
"babel-loader": "^9.1.2", "babel-loader": "^9.1.2",

View File

@@ -18,6 +18,7 @@ export type FileItemAction =
> >
payload: { payload: {
file: FileItem file: FileItem
directoryHandle?: FileSystemDirectoryHandle
} }
} }
| { | {

View File

@@ -67,8 +67,8 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
showProtectedOverlay = false showProtectedOverlay = false
fileContextMenuLocation: FileContextMenuLocation = { x: 0, y: 0 } fileContextMenuLocation: FileContextMenuLocation = { x: 0, y: 0 }
shouldUseStreamingReader = StreamingFileSaver.available() shouldUseStreamingAPI = StreamingFileSaver.available()
reader = this.shouldUseStreamingReader ? StreamingFileReader : ClassicFileReader reader = this.shouldUseStreamingAPI ? StreamingFileReader : ClassicFileReader
maxFileSize = this.reader.maximumFileSize() maxFileSize = this.reader.maximumFileSize()
override deinit(): void { override deinit(): void {
@@ -251,7 +251,7 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
await this.deleteFile(file) await this.deleteFile(file)
break break
case FileItemActionType.DownloadFile: case FileItemActionType.DownloadFile:
await this.downloadFile(file) await this.downloadFile(file, action.payload.directoryHandle)
break break
case FileItemActionType.ToggleFileProtection: { case FileItemActionType.ToggleFileProtection: {
const isProtected = await this.toggleFileProtection(file) const isProtected = await this.toggleFileProtection(file)
@@ -289,7 +289,7 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
}) })
} }
private async downloadFile(file: FileItem): Promise<void> { private async downloadFile(file: FileItem, directoryHandle?: FileSystemDirectoryHandle): Promise<void> {
let downloadingToastId = '' let downloadingToastId = ''
let canShowProgressNotification = false let canShowProgressNotification = false
@@ -298,12 +298,15 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
} }
try { try {
const saver = StreamingFileSaver.available() ? new StreamingFileSaver(file.name) : new ClassicFileSaver() const saver = this.shouldUseStreamingAPI ? new StreamingFileSaver(file.name) : new ClassicFileSaver()
const isUsingStreamingSaver = saver instanceof StreamingFileSaver const isUsingStreamingSaver = saver instanceof StreamingFileSaver
if (isUsingStreamingSaver) { if (isUsingStreamingSaver) {
await saver.selectFileToSaveTo() const fileHandle = directoryHandle
? await directoryHandle.getFileHandle(file.name, { create: true })
: undefined
await saver.selectFileToSaveTo(fileHandle)
} }
if (this.mobileDevice && canShowProgressNotification) { if (this.mobileDevice && canShowProgressNotification) {
@@ -413,7 +416,7 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
} }
alertIfFileExceedsSizeLimit = (file: File): boolean => { alertIfFileExceedsSizeLimit = (file: File): boolean => {
if (!this.shouldUseStreamingReader && this.maxFileSize && file.size >= this.maxFileSize) { if (!this.shouldUseStreamingAPI && this.maxFileSize && file.size >= this.maxFileSize) {
this.alerts this.alerts
.alert( .alert(
`This file exceeds the limits supported in this browser. To upload files greater than ${ `This file exceeds the limits supported in this browser. To upload files greater than ${
@@ -465,7 +468,7 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
const fileToUpload = const fileToUpload =
fileOrHandle instanceof File fileOrHandle instanceof File
? fileOrHandle ? fileOrHandle
: fileOrHandle instanceof FileSystemFileHandle && this.shouldUseStreamingReader : fileOrHandle instanceof FileSystemFileHandle && this.shouldUseStreamingAPI
? await fileOrHandle.getFile() ? await fileOrHandle.getFile()
: undefined : undefined
@@ -661,15 +664,41 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
void this.sync.sync() void this.sync.sync()
} }
getDirectoryHandleForDownloads = async () => {
if (!this.shouldUseStreamingAPI) {
return
}
const directoryHandle = await window.showDirectoryPicker({
startIn: 'downloads',
})
return directoryHandle
}
downloadFiles = async (files: FileItem[]) => { downloadFiles = async (files: FileItem[]) => {
// macOS doesn't allow multiple calls to the filepicker at the // macOS doesn't allow multiple calls to the filepicker at the
// same time, so we need to iterate one by one // same time, so we need to iterate one by one
if (this.platform === Platform.MacDesktop || this.platform === Platform.MacWeb) { if (this.platform === Platform.MacDesktop || this.platform === Platform.MacWeb) {
let directoryHandle: FileSystemDirectoryHandle | undefined
if (files.length > 1) {
try {
directoryHandle = await this.getDirectoryHandleForDownloads()
} catch (error) {
if (error instanceof DOMException && error.name === 'AbortError') {
return
}
console.error(error)
}
}
for (const file of files) { for (const file of files) {
await this.handleFileAction({ await this.handleFileAction({
type: FileItemActionType.DownloadFile, type: FileItemActionType.DownloadFile,
payload: { payload: {
file, file,
directoryHandle,
}, },
}) })
} }

View File

@@ -6753,7 +6753,7 @@ __metadata:
"@standardnotes/files": "workspace:*" "@standardnotes/files": "workspace:*"
"@standardnotes/utils": "workspace:*" "@standardnotes/utils": "workspace:*"
"@types/jest": ^29.2.3 "@types/jest": ^29.2.3
"@types/wicg-file-system-access": ^2020.9.5 "@types/wicg-file-system-access": ^2023.10.4
"@typescript-eslint/eslint-plugin": "*" "@typescript-eslint/eslint-plugin": "*"
eslint: "*" eslint: "*"
eslint-plugin-prettier: "*" eslint-plugin-prettier: "*"
@@ -7519,7 +7519,7 @@ __metadata:
"@types/jest": ^29.2.4 "@types/jest": ^29.2.4
"@types/react": ^18.2.39 "@types/react": ^18.2.39
"@types/react-dom": ^18.2.17 "@types/react-dom": ^18.2.17
"@types/wicg-file-system-access": ^2020.9.5 "@types/wicg-file-system-access": ^2023.10.4
"@zip.js/zip.js": ^2.6.60 "@zip.js/zip.js": ^2.6.60
autoprefixer: ^10.4.13 autoprefixer: ^10.4.13
babel-loader: ^9.1.2 babel-loader: ^9.1.2
@@ -8457,10 +8457,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/wicg-file-system-access@npm:^2020.9.5": "@types/wicg-file-system-access@npm:^2023.10.4":
version: 2020.9.6 version: 2023.10.4
resolution: "@types/wicg-file-system-access@npm:2020.9.6" resolution: "@types/wicg-file-system-access@npm:2023.10.4"
checksum: 2dbf33ee55684d4ed0607ae4b470e0147806b15ec047563c59d94e6ac13be7bed904cc7eece5e4be7c0d58a4de0b0635089f7cd5b7738372e436c760b0719241 checksum: bfbadfbddcdf04f600e0bb1b3dfe8dd79661c987ded487c2ac03670932e4ab9cd277c8b98d6229d10a910cb149c1bf956f43f189c4349f1c1a97cff310154aeb
languageName: node languageName: node
linkType: hard linkType: hard