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,
})
}
c.App.EmitEvent("hicli_event", &hicli.JSONCommand{
Command: "init_complete",
RequestID: 0,
})
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
}
}
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")
}

View file

@ -34,6 +34,7 @@ import type {
export default class Client {
readonly state = new CachedEventDispatcher<ClientState>()
readonly syncStatus = new NonNullCachedEventDispatcher<SyncStatus>({ type: "waiting", error_count: 0 })
readonly initComplete = new NonNullCachedEventDispatcher<boolean>(false)
readonly store = new StateStore()
#stateRequests: RoomStateGUID[] = []
#stateRequestQueued = false
@ -105,6 +106,8 @@ export default class Client {
this.state.emit(ev.data)
} else if (ev.command === "sync_status") {
this.syncStatus.emit(ev.data)
} else if (ev.command === "init_complete") {
this.initComplete.emit(true)
} else if (ev.command === "sync_complete") {
this.store.applySync(ev.data)
} else if (ev.command === "events_decrypted") {
@ -317,9 +320,16 @@ export default class Client {
}
}
async logout() {
await this.rpc.logout()
clearState() {
this.initComplete.emit(false)
this.syncStatus.emit({ type: "waiting", error_count: 0 })
this.state.clearCache()
localStorage.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"
}
export interface InitCompleteEvent extends RPCCommand<void> {
command: "init_complete"
}
export type RPCEvent =
ClientStateEvent |
SyncStatusEvent |
@ -125,4 +129,5 @@ export type RPCEvent =
SendCompleteEvent |
EventsDecryptedEvent |
SyncCompleteEvent |
ImageAuthTokenEvent
ImageAuthTokenEvent |
InitCompleteEvent

View file

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

View file

@ -33,6 +33,16 @@ export class EventDispatcher<T> {
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 {
this.#listeners.push(listener)
return () => {
@ -72,6 +82,10 @@ export class CachedEventDispatcher<T> extends EventDispatcher<T> {
}
return unlisten
}
clearCache() {
this.current = null
}
}
export class NonNullCachedEventDispatcher<T> extends CachedEventDispatcher<T> {
@ -81,4 +95,8 @@ export class NonNullCachedEventDispatcher<T> extends CachedEventDispatcher<T> {
super(cache)
this.current = cache
}
clearCache() {
throw new Error("Cannot clear cache of NonNullCachedEventDispatcher")
}
}