fix(ui): reliable attachment download and preview error IPC
Export bytes to download-cache, use saved-path for large files, and always send download/preview error events when fetch fails. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -1384,47 +1384,81 @@ async function dispatchUiMessage (platform, msg, hooks = {}) {
|
||||
return true
|
||||
}
|
||||
if (t === 'request-attachment-preview') {
|
||||
const preview = await platform.readAttachmentPreview(msg.attachmentId)
|
||||
send({
|
||||
type: 'attachment-preview',
|
||||
preview,
|
||||
fetchSource: preview?.fetchSource || null
|
||||
})
|
||||
const attachmentId = msg.attachmentId
|
||||
try {
|
||||
const preview = await platform.readAttachmentPreview(attachmentId)
|
||||
send({
|
||||
type: 'attachment-preview',
|
||||
preview,
|
||||
fetchSource: preview?.fetchSource || null
|
||||
})
|
||||
} catch (err) {
|
||||
try {
|
||||
send({
|
||||
type: 'attachment-preview-error',
|
||||
attachmentId,
|
||||
message: err?.message || String(err)
|
||||
})
|
||||
} catch {
|
||||
/* pipe closed */
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
if (t === 'request-attachment-download') {
|
||||
const attachmentId = msg.attachmentId
|
||||
const downloadTimeoutMs = Number(process.env.PEARCORD_ATTACHMENT_DOWNLOAD_MS || 30000)
|
||||
const maxInlineBytes = Number(process.env.PEARCORD_ATTACHMENT_DOWNLOAD_MAX || 6 * 1024 * 1024)
|
||||
const maxInlineBytes = Number(process.env.PEARCORD_ATTACHMENT_DOWNLOAD_MAX || 512 * 1024)
|
||||
const sendDownloadError = (message) => {
|
||||
try {
|
||||
send({
|
||||
type: 'attachment-download-error',
|
||||
attachmentId,
|
||||
message: message || 'download failed'
|
||||
})
|
||||
} catch {
|
||||
/* pipe closed */
|
||||
}
|
||||
}
|
||||
try {
|
||||
const row = await platform.attachments?.get(attachmentId)
|
||||
if (!row) throw new Error('attachment not found')
|
||||
const { buf, source } = await Promise.race([
|
||||
platform.readAttachmentBytesWithSource(attachmentId),
|
||||
const exported = await Promise.race([
|
||||
platform.exportAttachmentForDownload(attachmentId),
|
||||
new Promise((_, reject) => {
|
||||
setTimeout(() => reject(new Error('download timed out')), downloadTimeoutMs)
|
||||
})
|
||||
])
|
||||
if (!buf?.length) throw new Error('attachment file empty')
|
||||
if (buf.length > maxInlineBytes) {
|
||||
throw new Error(
|
||||
`file too large to download inline (${Math.round(buf.length / 1024 / 1024)} MB)`
|
||||
)
|
||||
if (!exported?.filePath) throw new Error('attachment file empty')
|
||||
if (exported.size > maxInlineBytes) {
|
||||
try {
|
||||
send({
|
||||
type: 'attachment-download-saved',
|
||||
attachmentId,
|
||||
filename: exported.filename || 'download',
|
||||
filePath: exported.filePath,
|
||||
mimeType: exported.mimeType || 'application/octet-stream',
|
||||
size: exported.size
|
||||
})
|
||||
} catch (err) {
|
||||
sendDownloadError(err?.message || String(err))
|
||||
}
|
||||
return true
|
||||
}
|
||||
const fs = require('bare-fs')
|
||||
const buf = fs.readFileSync(exported.filePath)
|
||||
try {
|
||||
send({
|
||||
type: 'attachment-download',
|
||||
attachmentId,
|
||||
filename: exported.filename || 'download',
|
||||
mimeType: exported.mimeType || 'application/octet-stream',
|
||||
dataBase64: b4a.toString(buf, 'base64'),
|
||||
fetchSource: 'export-cache'
|
||||
})
|
||||
} catch (err) {
|
||||
sendDownloadError(err?.message || String(err))
|
||||
}
|
||||
send({
|
||||
type: 'attachment-download',
|
||||
attachmentId: row.id,
|
||||
filename: row.filename || 'download',
|
||||
mimeType: row.mimeType || 'application/octet-stream',
|
||||
dataBase64: b4a.toString(buf, 'base64'),
|
||||
fetchSource: source
|
||||
})
|
||||
} catch (err) {
|
||||
send({
|
||||
type: 'attachment-download-error',
|
||||
attachmentId,
|
||||
message: err?.message || String(err)
|
||||
})
|
||||
sendDownloadError(err?.message || String(err))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user