このコミットが含まれているのは:
+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)
|
||||
}
|
||||
|
||||
新しいイシューから参照
ユーザーをブロックする