fix: admin/fs: in file dialog, changing base_url won't update the form
Massimo Melina committed
Apr 15, 2024 at 21:49 UTC
9de74e0bdf012477b33580838079bc53113b7578
3 files changed
+22
-5
admin/src/FileForm.ts
+8
-2
@@ -8,7 +8,8 @@ import { BoolField, DisplayField, Field, FieldProps, Form, MultiSelectField, Sel
8
import { apiCall, UseApi } from './api'
9
import {
10
basename, defaultPerms, formatBytes, formatTimestamp, isEqualLax, isWhoObject, newDialog, objSameKeys,
11
- onlyTruthy, prefix, VfsPerms, wantArray, Who, WhoObject, matches, HTTP_MESSAGES, xlate, md, Callback
11
+ onlyTruthy, prefix, VfsPerms, wantArray, Who, WhoObject, matches, HTTP_MESSAGES, xlate, md, Callback,
12
+ useRequestRender
13
} from './misc'
14
import { Btn, IconBtn, LinkBtn, modifiedProps, useBreakpoint, wikiLink } from './mui'
15
import { reloadVfs, VfsNode } from './VfsPage'
@@ -289,7 +290,12 @@ interface LinkFieldProps extends FieldProps<string> {
290
statusApi: UseApi<any> // receive status from parent, to avoid asking server at each click on a file
291
}
292
function LinkField({ value, statusApi }: LinkFieldProps) {
292
- const { data, reload, error } = statusApi
293
+ const { reload, error } = statusApi
294
+ // workaround to get fresh data and be rerendered even when mounted inside imperative dialog
295
+ const requestRender = useRequestRender()
296
+ useEffect(() => statusApi.sub(requestRender), [])
297
+ const data = statusApi.getData()
298
+
299
const urls: string[] = data?.urls.https || data?.urls.http
300
const baseHost = data?.baseUrl && new URL(data.baseUrl).host
301
const root = useMemo(() => baseHost && _.find(data.roots, (root, host) => matches(baseHost, host)),
shared/api.ts
+6
-3
@@ -1,8 +1,8 @@
1
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3
import _ from 'lodash';
4
-import { useCallback, useEffect, useRef } from 'react';
5
-import { Dict, Falsy, getPrefixUrl, pendingPromise, useStateMounted, wait } from '.'
4
+import { useCallback, useEffect, useMemo, useRef } from 'react';
5
+import { Dict, EventEmitter, Falsy, getPrefixUrl, pendingPromise, useStateMounted, wait } from '.'
6
7
export const API_URL = '/~/api/'
8
@@ -112,7 +112,10 @@ export function useApi<T=any>(cmd: string | Falsy, params?: object, options: Api
112
setForcer(v => v + 1)
113
reloadingRef.current = pendingPromise()
114
}, [setForcer])
115
- return { data, setData, error, reload, loading: loadingRef.current || reloadingRef.current, getData: () => dataRef.current, }
115
+ const ee = useMemo(() => new EventEmitter, [])
116
+ const sub = useCallback((cb: EventListener) => ee.on('data', cb), [])
117
+ useEffect(() => ee.emit('data'), [data])
118
+ return { data, setData, error, reload, sub, loading: loadingRef.current || reloadingRef.current, getData: () => dataRef.current, }
119
}
120
121
type EventHandler = (type:string, data?:any) => void
shared/index.ts
+8
@@ -140,6 +140,14 @@ export function createDurationFormatter({ locale=undefined, unitDisplay='narrow'
140
}
141
}
142
143
+export class EventEmitter extends EventTarget {
144
+ emit(name: string) { this.dispatchEvent(new Event(name)) }
145
+ on(name: string, cb: EventListener) {
146
+ this.addEventListener(name, cb)
147
+ return () => this.removeEventListener(name, cb)
148
+ }
149
+}
150
+
151
Element.prototype.replaceChildren ||= function(this:Element, addNodes) { // polyfill
152
while (this.lastChild) this.removeChild(this.lastChild);
153
if (addNodes !== undefined) this.append(addNodes);