このコミットが含まれているのは:
2026-10-26 12:44:22 +09:00
コミット f91f9d0eb5
23個のファイルの変更、637行の追加、316行の削除
+20 -17
ファイルの表示
@@ -201,26 +201,29 @@ export const isApiError = <T = unknown> (err: unknown): err is AxiosError<T> =>
axios.isAxiosError (err)
// Native downloads let the browser stream to disk without buffering a Blob in app memory.
// The server acknowledges a complete file before native download starts.
export const apiDownload = async (
path: string,
body: Record<string, string>,
locale: LocaleCode,
onError?: () => void,
): Promise<void> => {
const token = await csrfToken ()
const form = document.createElement ('form')
form.method = 'post'
form.action = `${API_BASE_URL}${path}?${new URLSearchParams ({ locale })}`
form.hidden = true
for (const [name, value] of Object.entries ({ ...body, authenticity_token: token }))
{
const input = document.createElement ('input')
input.type = 'hidden'
input.name = name
input.value = value
form.append (input)
}
document.body.append (form)
form.submit ()
form.remove ()
const { token } = await apiPost<{ token: string }> (`${path}/prepare`, body, { locale })
if (typeof token !== 'string' || token.length === 0)
throw new Error ('Missing download token')
const frame = document.createElement ('iframe')
frame.hidden = true
frame.addEventListener ('load', () => {
const document = frame.contentDocument
if (document != null && document.URL !== 'about:blank')
{
onError?.()
frame.remove ()
}
})
frame.addEventListener ('error', () => { onError?.(); frame.remove () })
frame.src = `${API_BASE_URL}${path}/files/${encodeURIComponent (token)}`
document.body.append (frame)
// Attachments do not reliably fire load; keep this isolated from page navigation.
window.setTimeout (() => frame.remove (), 10 * 60_000)
}