Fix resetting CRDT document state on reconnect

This fixes a bug causing old client windows to reconnect and clear out
the state data.
This commit is contained in:
Max Goodhart
2020-07-05 20:26:43 -07:00
parent ee313aad02
commit 067c9c238a

View File

@@ -39,8 +39,8 @@ const hotkeyTriggers = [
'p', 'p',
] ]
function useYDoc(existingDoc, keys) { function useYDoc(keys) {
const { current: doc } = useRef(existingDoc || new Y.Doc()) const [doc, setDoc] = useState(new Y.Doc())
const [docValue, setDocValue] = useState() const [docValue, setDocValue] = useState()
useEffect(() => { useEffect(() => {
function updateDocValue() { function updateDocValue() {
@@ -54,14 +54,14 @@ function useYDoc(existingDoc, keys) {
return () => { return () => {
doc.off('update', updateDocValue) doc.off('update', updateDocValue)
} }
}, []) }, [doc])
return [docValue, doc] return [docValue, doc, setDoc]
} }
function App({ wsEndpoint }) { function App({ wsEndpoint }) {
const wsRef = useRef() const wsRef = useRef()
const [isConnected, setIsConnected] = useState(false) const [isConnected, setIsConnected] = useState(false)
const [sharedState, stateDoc] = useYDoc(null, ['views']) const [sharedState, stateDoc, setStateDoc] = useYDoc(['views'])
const [config, setConfig] = useState({}) const [config, setConfig] = useState({})
const [streams, setStreams] = useState([]) const [streams, setStreams] = useState([])
const [customStreams, setCustomStreams] = useState([]) const [customStreams, setCustomStreams] = useState([])
@@ -78,13 +78,14 @@ function App({ wsEndpoint }) {
}) })
ws.binaryType = 'arraybuffer' ws.binaryType = 'arraybuffer'
ws.addEventListener('open', () => setIsConnected(true)) ws.addEventListener('open', () => setIsConnected(true))
ws.addEventListener('close', () => setIsConnected(false)) ws.addEventListener('close', () => {
setStateDoc(new Y.Doc())
setIsConnected(false)
})
ws.addEventListener('message', (ev) => { ws.addEventListener('message', (ev) => {
if (ev.data instanceof ArrayBuffer) { if (ev.data instanceof ArrayBuffer) {
Y.applyUpdate(stateDoc, new Uint8Array(ev.data))
return return
} }
const msg = JSON.parse(ev.data) const msg = JSON.parse(ev.data)
if (msg.type === 'state') { if (msg.type === 'state') {
const { const {
@@ -126,12 +127,27 @@ function App({ wsEndpoint }) {
console.warn('unexpected ws message', msg) console.warn('unexpected ws message', msg)
} }
}) })
stateDoc.on('update', (update) => {
ws.send(update)
})
wsRef.current = ws wsRef.current = ws
}, []) }, [])
useEffect(() => {
function sendUpdate(update) {
wsRef.current.send(update)
}
function receiveUpdate(ev) {
if (!(ev.data instanceof ArrayBuffer)) {
return
}
Y.applyUpdate(stateDoc, new Uint8Array(ev.data))
}
stateDoc.on('update', sendUpdate)
wsRef.current.addEventListener('message', receiveUpdate)
return () => {
stateDoc.off('update', sendUpdate)
wsRef.current.removeEventListener('message', receiveUpdate)
}
}, [stateDoc])
const handleSetView = useCallback( const handleSetView = useCallback(
(idx, streamId) => { (idx, streamId) => {
const stream = streams.find((d) => d._id === streamId) const stream = streams.find((d) => d._id === streamId)