119 lines
3.3 KiB
TypeScript
119 lines
3.3 KiB
TypeScript
import { useDocumentStore } from "@/stores/documents"
|
|
import type { DirEntry, UpdateEntry, errorEvent } from "./Document"
|
|
|
|
export const controlUrl = '/api/control'
|
|
export const uploadUrl = '/api/upload'
|
|
export const watchUrl = '/api/watch'
|
|
|
|
let tree = null as DirEntry | null
|
|
let reconnectDuration = 500
|
|
let wsWatch = null as WebSocket | null
|
|
|
|
export const connect = (path: string, handlers: Partial<Record<keyof WebSocketEventMap, any>>) => {
|
|
const webSocket = new WebSocket(new URL(path, location.origin.replace(/^http/, 'ws')))
|
|
for (const [event, handler] of Object.entries(handlers)) webSocket.addEventListener(event, handler)
|
|
return webSocket
|
|
}
|
|
|
|
export const watchConnect = async () => {
|
|
wsWatch = connect(watchUrl, {
|
|
open() { console.log("Connected to", watchUrl)},
|
|
message: handleWatchMessage,
|
|
close: watchReconnect,
|
|
})
|
|
await wsWatch
|
|
}
|
|
|
|
export const watchDisconnect = () => {
|
|
if (!wsWatch) return
|
|
wsWatch.close()
|
|
wsWatch = null
|
|
}
|
|
|
|
const watchReconnect = (event: MessageEvent) => {
|
|
const store = useDocumentStore()
|
|
if (store.connected) {
|
|
console.warn("Disconnected from server", event)
|
|
store.connected = false
|
|
}
|
|
reconnectDuration = Math.min(5000, reconnectDuration + 500)
|
|
// The server closes the websocket after errors, so we need to reopen it
|
|
setTimeout(() => {
|
|
wsWatch = connect(watchUrl, {
|
|
message: handleWatchMessage,
|
|
close: watchReconnect,
|
|
})
|
|
console.log("Attempting to reconnect...")
|
|
}, reconnectDuration)
|
|
}
|
|
|
|
|
|
const handleWatchMessage = (event: MessageEvent) => {
|
|
const store = useDocumentStore()
|
|
const msg = JSON.parse(event.data)
|
|
if ('error' in msg) {
|
|
if (msg.error.code === 401) {
|
|
store.user.isLoggedIn = false
|
|
store.user.isOpenLoginModal = true
|
|
} else {
|
|
store.error = msg.error.message
|
|
}
|
|
}
|
|
switch (true) {
|
|
case !!msg.root:
|
|
handleRootMessage(msg)
|
|
break
|
|
case !!msg.update:
|
|
handleUpdateMessage(msg)
|
|
break
|
|
case !!msg.space:
|
|
console.log('Watch space', msg.space)
|
|
break
|
|
case !!msg.error:
|
|
handleError(msg)
|
|
break
|
|
default:
|
|
}
|
|
}
|
|
|
|
function handleRootMessage({ root }: { root: DirEntry }) {
|
|
const store = useDocumentStore()
|
|
console.log('Watch root', root)
|
|
reconnectDuration = 500
|
|
store.connected = true
|
|
store.user.isLoggedIn = true
|
|
store.updateRoot(root)
|
|
tree = root
|
|
}
|
|
|
|
function handleUpdateMessage(updateData: { update: UpdateEntry[] }) {
|
|
const store = useDocumentStore()
|
|
console.log('Watch update', updateData.update)
|
|
if (!tree) return console.error('Watch update before root')
|
|
let node: DirEntry = tree
|
|
for (const elem of updateData.update) {
|
|
if (elem.deleted) {
|
|
delete node.dir[elem.name]
|
|
break // Deleted elements can't have further children
|
|
}
|
|
if (elem.name !== undefined) {
|
|
// @ts-ignore
|
|
node = node.dir[elem.name] ||= {}
|
|
}
|
|
if (elem.key !== undefined) node.key = elem.key
|
|
if (elem.size !== undefined) node.size = elem.size
|
|
if (elem.mtime !== undefined) node.mtime = elem.mtime
|
|
if (elem.dir !== undefined) node.dir = elem.dir
|
|
}
|
|
store.updateRoot(tree)
|
|
}
|
|
|
|
function handleError(msg: errorEvent) {
|
|
const store = useDocumentStore()
|
|
if (msg.error.code === 401) {
|
|
store.user.isOpenLoginModal = true
|
|
store.user.isLoggedIn = false
|
|
return
|
|
}
|
|
}
|