1
0
Fork 0
forked from Mirrors/gomuks

web/main: fix restoring state on reload

This commit is contained in:
Tulir Asokan 2024-12-06 15:57:06 +02:00
parent bf7769ee95
commit 2c7ad651e4
6 changed files with 63 additions and 6 deletions

View file

@ -81,6 +81,10 @@ func (c *CommandHandler) Init() {
Data: marshaledPayload, Data: marshaledPayload,
}) })
} }
c.App.EmitEvent("hicli_event", &hicli.JSONCommand{
Command: "init_complete",
RequestID: 0,
})
log.Info().Int("room_count", roomCount).Msg("Sent initial rooms to client") log.Info().Int("room_count", roomCount).Msg("Sent initial rooms to client")
}() }()
} }

View file

@ -259,5 +259,13 @@ func (gmx *Gomuks) sendInitialData(ctx context.Context, conn *websocket.Conn) {
return return
} }
} }
err := writeCmd(ctx, conn, &hicli.JSONCommand{
Command: "init_complete",
RequestID: 0,
})
if err != nil {
log.Err(err).Msg("Failed to send initial rooms done event to client")
return
}
log.Info().Int("room_count", roomCount).Msg("Sent initial rooms to client") log.Info().Int("room_count", roomCount).Msg("Sent initial rooms to client")
} }

View file

@ -34,6 +34,7 @@ import type {
export default class Client { export default class Client {
readonly state = new CachedEventDispatcher<ClientState>() readonly state = new CachedEventDispatcher<ClientState>()
readonly syncStatus = new NonNullCachedEventDispatcher<SyncStatus>({ type: "waiting", error_count: 0 }) readonly syncStatus = new NonNullCachedEventDispatcher<SyncStatus>({ type: "waiting", error_count: 0 })
readonly initComplete = new NonNullCachedEventDispatcher<boolean>(false)
readonly store = new StateStore() readonly store = new StateStore()
#stateRequests: RoomStateGUID[] = [] #stateRequests: RoomStateGUID[] = []
#stateRequestQueued = false #stateRequestQueued = false
@ -105,6 +106,8 @@ export default class Client {
this.state.emit(ev.data) this.state.emit(ev.data)
} else if (ev.command === "sync_status") { } else if (ev.command === "sync_status") {
this.syncStatus.emit(ev.data) this.syncStatus.emit(ev.data)
} else if (ev.command === "init_complete") {
this.initComplete.emit(true)
} else if (ev.command === "sync_complete") { } else if (ev.command === "sync_complete") {
this.store.applySync(ev.data) this.store.applySync(ev.data)
} else if (ev.command === "events_decrypted") { } else if (ev.command === "events_decrypted") {
@ -317,9 +320,16 @@ export default class Client {
} }
} }
async logout() { clearState() {
await this.rpc.logout() this.initComplete.emit(false)
this.syncStatus.emit({ type: "waiting", error_count: 0 })
this.state.clearCache()
localStorage.clear() localStorage.clear()
this.store.clear() this.store.clear()
} }
async logout() {
await this.rpc.logout()
this.clearState()
}
} }

View file

@ -118,6 +118,10 @@ export interface SyncStatusEvent extends RPCCommand<SyncStatus> {
command: "sync_status" command: "sync_status"
} }
export interface InitCompleteEvent extends RPCCommand<void> {
command: "init_complete"
}
export type RPCEvent = export type RPCEvent =
ClientStateEvent | ClientStateEvent |
SyncStatusEvent | SyncStatusEvent |
@ -125,4 +129,5 @@ export type RPCEvent =
SendCompleteEvent | SendCompleteEvent |
EventsDecryptedEvent | EventsDecryptedEvent |
SyncCompleteEvent | SyncCompleteEvent |
ImageAuthTokenEvent ImageAuthTokenEvent |
InitCompleteEvent

View file

@ -159,6 +159,7 @@ const handleURLHash = (client: Client, context: ContextFields) => {
console.error("Invalid matrix URI", decodedURI) console.error("Invalid matrix URI", decodedURI)
return return
} }
console.log("Handling URI", uri)
const newURL = new URL(location.href) const newURL = new URL(location.href)
newURL.hash = "" newURL.hash = ""
if (uri.identifier.startsWith("@")) { if (uri.identifier.startsWith("@")) {
@ -206,9 +207,20 @@ const MainScreen = () => {
context.setRightPanel(evt.state?.right_panel ?? null, false) context.setRightPanel(evt.state?.right_panel ?? null, false)
} }
window.addEventListener("popstate", listener) window.addEventListener("popstate", listener)
const initHandle = () => {
listener({ state: history.state } as PopStateEvent) listener({ state: history.state } as PopStateEvent)
handleURLHash(client, context) handleURLHash(client, context)
return () => window.removeEventListener("popstate", listener) }
let cancel = () => {}
if (client.initComplete.current) {
initHandle()
} else {
cancel = client.initComplete.once(initHandle)
}
return () => {
window.removeEventListener("popstate", listener)
cancel()
}
}, [context, client]) }, [context, client])
useEffect(() => context.keybindings.listen(), [context]) useEffect(() => context.keybindings.listen(), [context])
useInsertionEffect(() => { useInsertionEffect(() => {

View file

@ -33,6 +33,16 @@ export class EventDispatcher<T> {
return this.#listen(listener) return this.#listen(listener)
} }
once(listener: (data: T) => void): () => void {
let unsub: (() => void) | undefined = undefined
const wrapped = (data: T) => {
unsub?.()
listener(data)
}
unsub = this.#listen(wrapped)
return unsub
}
#listen(listener: (data: T) => void): () => void { #listen(listener: (data: T) => void): () => void {
this.#listeners.push(listener) this.#listeners.push(listener)
return () => { return () => {
@ -72,6 +82,10 @@ export class CachedEventDispatcher<T> extends EventDispatcher<T> {
} }
return unlisten return unlisten
} }
clearCache() {
this.current = null
}
} }
export class NonNullCachedEventDispatcher<T> extends CachedEventDispatcher<T> { export class NonNullCachedEventDispatcher<T> extends CachedEventDispatcher<T> {
@ -81,4 +95,8 @@ export class NonNullCachedEventDispatcher<T> extends CachedEventDispatcher<T> {
super(cache) super(cache)
this.current = cache this.current = cache
} }
clearCache() {
throw new Error("Cannot clear cache of NonNullCachedEventDispatcher")
}
} }