fix: admin/home: infinite warnings when the update is failing
Massimo Melina committed
Jul 19, 2023 at 15:38 UTC
cbf07db8a863a56aadced249b49f67d990ba8672
4 files changed
+52
-42
admin/src/HomePage.ts
+4
-2
@@ -129,11 +129,13 @@ async function update(tag?: string) {
129
await apiCall('update', { tag })
130
toast("Restarting")
131
const restarting = Date.now()
132
+ let warning: undefined | ReturnType<typeof alertDialog>
133
while (await apiCall('NONE').then(() => 0, e => !e.code)) { // while we get no response
133
- if (Date.now() - restarting > 10_000)
134
- toast("This is taking too long, please check your server", 'warning')
134
+ if (!warning && Date.now() - restarting > 10_000)
135
+ warning = alertDialog("This is taking too long, please check your server", 'warning')
136
await wait(500)
137
}
138
+ warning?.close()
139
// the server is back on, SSE is restored and login dialog may appear, unwanted because we are just waiting to reload
140
subscribeKey(state, 'loginRequired', () => state.loginRequired = false)
141
await alertDialog("Procedure complete", 'success')
admin/src/dialog.ts
+38
-33
@@ -18,7 +18,7 @@ import {
18
useState
19
} from 'react'
20
import { Check, Close, Error as ErrorIcon, Forward, Info, Warning } from '@mui/icons-material'
21
-import { newDialog, closeDialog, dialogsDefaults, DialogOptions, componentOrNode } from '@hfs/shared'
21
+import { newDialog, closeDialog, dialogsDefaults, DialogOptions, componentOrNode, pendingPromise } from '@hfs/shared'
22
import { Form, FormProps } from '@hfs/mui-grid-form'
23
import { IconBtn, Flex } from './misc'
24
import { useDark } from './theme'
@@ -85,46 +85,49 @@ const type2ico = {
85
info: Info,
86
success: Check,
87
}
88
-export async function alertDialog(msg: ReactElement | string | Error, options?: AlertType | ({ type?:AlertType, icon?: ReactElement } & Partial<DialogOptions>)) {
89
- return new Promise(resolve => {
90
- const opt = typeof options === 'string' ? { type: options } : (options ?? {})
91
- let { type='info', ...rest } = opt
92
- if (msg instanceof Error) {
93
- msg = msg.message || String(msg)
94
- type = 'error'
88
+export function alertDialog(msg: ReactElement | string | Error, options?: AlertType | ({ type?:AlertType, icon?: ReactElement } & Partial<DialogOptions>)) {
89
+ const opt = typeof options === 'string' ? { type: options } : (options ?? {})
90
+ let { type='info', ...rest } = opt
91
+ if (msg instanceof Error) {
92
+ msg = msg.message || String(msg)
93
+ type = 'error'
94
+ }
95
+
96
+ const promise = pendingPromise()
97
+ const dialog = newDialog({
98
+ className: 'dialog-alert-' + type,
99
+ icon: '!',
100
+ onClose: promise.resolve,
101
+ ...rest,
102
+ Content() {
103
+ return h(Box, { display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 1 },
104
+ h(IconButton, {
105
+ onClick() {
106
+ dialog.close()
107
+ },
108
+ size: 'small',
109
+ sx: { position: 'absolute', right: 0, top: 0, opacity: .5 }
110
+ }, h(Close)),
111
+ opt.icon ?? h(type2ico[type], { color: type, fontSize: 'large' }),
112
+ isValidElement(msg) ? msg
113
+ : h(Box, { fontSize: 'large', mb: 1, lineHeight: '1.8em' }, String(msg)),
114
+ )
115
}
96
- const { close } = newDialog({
97
- className: 'dialog-alert-' + type,
98
- icon: '!',
99
- onClose: resolve,
100
- ...rest,
101
- Content() {
102
- return h(Box, { display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 1 },
103
- h(IconButton, {
104
- onClick() {
105
- close()
106
- },
107
- size: 'small',
108
- sx: { position: 'absolute', right: 0, top: 0, opacity: .5 }
109
- }, h(Close)),
110
- opt.icon ?? h(type2ico[type], { color: type, fontSize: 'large' }),
111
- isValidElement(msg) ? msg
112
- : h(Box, { fontSize: 'large', mb: 1, lineHeight: '1.8em' }, String(msg)),
113
- )
114
- }
115
- })
116
})
117
+ return Object.assign(promise, dialog)
118
}
119
120
interface ConfirmOptions extends Omit<DialogOptions, 'Content'> { href?: string }
120
-export async function confirmDialog(msg: ReactNode, { href, ...rest }: ConfirmOptions={}) : Promise<boolean> {
121
- return new Promise(resolve => newDialog({
121
+export function confirmDialog(msg: ReactNode, { href, ...rest }: ConfirmOptions={}) {
122
+ const promise = pendingPromise<boolean>()
123
+ const dialog = newDialog({
124
className: 'dialog-confirm',
125
icon: '?',
124
- onClose: resolve,
126
+ onClose: promise.resolve,
127
...rest,
128
Content
127
- }) )
129
+ })
130
+ return Object.assign(promise, dialog)
131
132
function Content() {
133
return h(Fragment, {},
@@ -212,7 +215,7 @@ export function waitDialog() {
215
216
export function toast(msg: string | ReactElement, type: AlertType | ReactElement='info') {
217
const ms = 3000
215
- const { close } = newDialog({
218
+ const dialog = newDialog({
219
Content,
220
dialogProps: {
221
fullScreen: false,
@@ -225,7 +228,9 @@ export function toast(msg: string | ReactElement, type: AlertType | ReactElement
228
}
229
}
230
})
231
+ const { close } = dialog
232
setTimeout(close, ms)
233
+ return dialog
234
235
function Content(){
236
return h(Box, { display:'flex', flexDirection: 'column', alignItems: 'center', gap: 1 },
frontend/src/dialog.ts
+3
-3
@@ -95,15 +95,15 @@ export function confirmDialog(msg: ReactElement | string, options: ConfirmOption
95
const { href, afterButtons, timeout, timeoutConfirm=false, ...rest } = options
96
if (typeof msg === 'string')
97
msg = h('p', {}, msg)
98
- const ret = pendingPromise<boolean>()
98
+ const promise = pendingPromise<boolean>()
99
const dialog = newDialog({
100
className: 'dialog-confirm',
101
icon: '?',
102
- onClose: ret.resolve,
102
+ onClose: promise.resolve,
103
...rest,
104
Content
105
})
106
- return Object.assign(ret, dialog)
106
+ return Object.assign(promise, dialog)
107
108
function Content() {
109
const [sec,setSec] = useState(Math.ceil(timeout||0))
shared/dialogs.ts
+7
-4
@@ -107,8 +107,7 @@ export function newDialog(options: DialogOptions) {
107
function close(v?:any) {
108
const i = dialogs.findIndex(x => (x as any).$id === $id)
109
if (i < 0) return
110
- dialogs.splice(i,1)
111
- options.onClose?.(v)
110
+ return closeDialogAt(i, v)
111
}
112
}
113
@@ -118,8 +117,12 @@ export function closeDialog(v?:any) {
117
const d = dialogs[i]
118
if (d.reserveClosing)
119
continue
121
- dialogs.splice(i,1)
122
- return d.onClose?.(v)
120
+ closeDialogAt(i, v)
121
+ return d
122
}
123
}
124
125
+function closeDialogAt(i: number, value?: any) {
126
+ const [d] = dialogs.splice(i,1)
127
+ return d?.onClose?.(value)
128
+}
\ No newline at end of file