fix: upload: resume dialog and file were not disposed of after complete upload
Massimo Melina committed
Jan 22, 2023 at 21:43 UTC
7320d51fea0fd0d37543c39584b3e55f18128080
4 files changed
+28
-13
frontend/src/dialog.ts
+17
-9
@@ -2,7 +2,7 @@
2
3
import { createElement as h, ReactElement, ReactNode, useEffect, useRef, useState } from 'react'
4
import './dialog.css'
5
-import { newDialog, closeDialog, DialogOptions } from '@hfs/shared/dialogs'
5
+import { newDialog, closeDialog, DialogOptions, DialogCloser } from '@hfs/shared/dialogs'
6
import _ from 'lodash'
7
import { useInterval } from 'usehooks-ts'
8
export * from '@hfs/shared/dialogs'
@@ -73,16 +73,24 @@ export async function alertDialog(msg: ReactElement | string | Error, type:Alert
73
}
74
}
75
76
-export interface ConfirmOptions { href?: string, afterButtons?: ReactNode, timeout?: number, timeoutConfirm?: boolean }
77
-export async function confirmDialog(msg: ReactElement | string, { href, afterButtons, timeout, timeoutConfirm=false }: ConfirmOptions={}) : Promise<boolean> {
76
+export interface ConfirmOptions {
77
+ href?: string
78
+ afterButtons?: ReactNode
79
+ timeout?: number
80
+ timeoutConfirm?: boolean
81
+ getClose?: (cb: DialogCloser) => unknown
82
+}
83
+export async function confirmDialog(msg: ReactElement | string, options: ConfirmOptions={}) : Promise<unknown> {
84
+ const { href, afterButtons, timeout, timeoutConfirm=false, getClose=_.noop } = options
85
if (typeof msg === 'string')
86
msg = h('p', {}, msg)
80
- return new Promise(resolve => newDialog({
81
- className: 'dialog-confirm',
82
- icon: '?',
83
- onClose: resolve,
84
- Content
85
- }) )
87
+ return new Promise(resolve =>
88
+ getClose(newDialog({
89
+ className: 'dialog-confirm',
90
+ icon: '?',
91
+ onClose: resolve,
92
+ Content
93
+ })) )
94
95
function Content() {
96
const [sec,setSec] = useState(Math.ceil(timeout||0))
frontend/src/upload.ts
+5
-2
@@ -2,7 +2,7 @@
2
3
import { createElement as h, useState } from 'react'
4
import { Flex, FlexV } from './components'
5
-import { formatBytes, hIcon, newDialog, prefix } from './misc'
5
+import { DialogCloser, formatBytes, hIcon, newDialog, prefix } from './misc'
6
import _ from 'lodash'
7
import { proxy, ref, subscribe, useSnapshot } from 'valtio'
8
import { alertDialog, confirmDialog } from './dialog'
@@ -176,6 +176,7 @@ let req: XMLHttpRequest | undefined
176
let overrideStatus = 0
177
let notificationChannel = ''
178
let notificationSource: EventSource
179
+let closeResumeDialog: DialogCloser | undefined
180
181
async function startUpload(f: File, to: string, resume=0) {
182
let resuming = false
@@ -213,7 +214,8 @@ async function startUpload(f: File, to: string, resume=0) {
214
const {expires} = data
215
const timeout = typeof expires !== 'number' ? 0
216
: (Number(new Date(expires)) - Date.now()) / 1000
216
- if (!await confirmDialog(`Resume upload? (${formatPerc(size/f.size)} = ${formatBytes(size)})`, { timeout })) return
217
+ const msg = `Resume upload? (${formatPerc(size/f.size)} = ${formatBytes(size)})`
218
+ if (!await confirmDialog(msg, { timeout, getClose: x => closeResumeDialog=x })) return
219
if (uploading !== uploadState.uploading) return // too late
220
resuming = true
221
abortCurrentUpload()
@@ -241,6 +243,7 @@ async function startUpload(f: File, to: string, resume=0) {
243
}
244
245
function next() {
246
+ closeResumeDialog?.()
247
uploadState.uploading = undefined
248
const { qs } = uploadState
249
if (!qs.length) return
shared/dialogs.ts
+1
@@ -20,6 +20,7 @@ export interface DialogOptions {
20
}
21
22
const dialogs = proxy<DialogOptions[]>([])
23
+export type DialogCloser = ReturnType<typeof newDialog>
24
25
export const dialogsDefaults: Partial<DialogOptions> = {
26
closableContent: 'x',
src/middlewares.ts
+5
-2
@@ -168,8 +168,11 @@ export const serveGuiAndSharedFiles: Koa.Middleware = async (ctx, next) => {
168
cancelDeletion(tempName)
169
ret.on('close', () => {
170
if (!ctx.req.aborted)
171
- return fs.rename(tempName, fullPath, err =>
172
- err && console.error("couldn't rename temp to", fullPath, String(err)))
171
+ return fs.rename(tempName, fullPath, err => {
172
+ err && console.error("couldn't rename temp to", fullPath, String(err))
173
+ if (resumable)
174
+ delayedDelete(resumable, 0)
175
+ })
176
const sec = deleteUnfinishedUploadsAfter.get()
177
if (typeof sec !== 'number') return
178
delayedDelete(tempName, sec)