mirror of https://codeberg.org/pzp/zooboard.git
fix concurrent load and update, add support for gc'ing elements
This commit is contained in:
parent
633682db8a
commit
7a98e3cbcc
70
main.js
70
main.js
|
@ -106,14 +106,6 @@ async function loadAccount() {
|
|||
globalAccountID = id
|
||||
await p(peer.set.load)(id)
|
||||
await p(peer.dict.load)(id)
|
||||
peer.conductor.start(
|
||||
id,
|
||||
[
|
||||
['profile@dict', 'zooboardElements@newest-100', 'hubs@set'],
|
||||
['profile@dict', 'zooboardElements@newest-100'],
|
||||
],
|
||||
64_000_000
|
||||
)
|
||||
|
||||
// Read hubs
|
||||
const multiaddrs = peer.set.values('hubs')
|
||||
|
@ -134,38 +126,82 @@ async function setProfileName(ev, name) {
|
|||
return name
|
||||
}
|
||||
|
||||
async function writeElements(ev, actions) {
|
||||
await loadAccount() // FIXME: ideally the frontend shouldn't do this
|
||||
async function writeElements(ev, elements) {
|
||||
if (globalAccountID === null) throw new Error('account not loaded')
|
||||
for (const action of actions) {
|
||||
for (const element of elements) {
|
||||
await p(peer.db.feed.publish)({
|
||||
account: globalAccountID,
|
||||
domain: 'zooboardElements',
|
||||
data: action,
|
||||
data: element,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let hasSubscribedToReadElements = false
|
||||
function subscribeToReadElements() {
|
||||
if (hasSubscribedToReadElements) return
|
||||
hasSubscribedToReadElements = true
|
||||
|
||||
// Load initial elements and inform renderer
|
||||
const elementsByID = new Map()
|
||||
for (const msg of peer.db.msgs()) {
|
||||
const msgIDToElemID = new Map()
|
||||
for (const { id: msgID, msg } of peer.db.records()) {
|
||||
if (msg.data && msg.metadata.domain === 'zooboardElements') {
|
||||
const { id, isDeleted } = msg.data
|
||||
const { id: elemID, isDeleted } = msg.data
|
||||
if (isDeleted) {
|
||||
elementsByID.delete(id)
|
||||
elementsByID.delete(elemID)
|
||||
} else {
|
||||
elementsByID.set(id, msg.data)
|
||||
msgIDToElemID.set(msgID, elemID)
|
||||
elementsByID.set(elemID, msg.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
const initialElements = [...elementsByID.values()]
|
||||
mainWindow.webContents.send('readElements', initialElements)
|
||||
|
||||
peer.db.onRecordAdded(({ msg }) => {
|
||||
// Subscribe to new elements and inform renderer
|
||||
peer.db.onRecordAdded(({ id: msgID, msg }) => {
|
||||
if (msg.data && msg.metadata.domain === 'zooboardElements') {
|
||||
const { id: elemID, isDeleted } = msg.data
|
||||
if (isDeleted) {
|
||||
elementsByID.delete(elemID)
|
||||
} else {
|
||||
msgIDToElemID.set(msgID, elemID)
|
||||
elementsByID.set(elemID, msg.data)
|
||||
}
|
||||
mainWindow.webContents.send('readElements', [msg.data])
|
||||
}
|
||||
})
|
||||
|
||||
// Subscribe to deleted elements and inform renderer
|
||||
peer.db.onRecordDeletedOrErased((msgID) => {
|
||||
const elemID = msgIDToElemID.get(msgID)
|
||||
if (!elemID) return
|
||||
msgIDToElemID.delete(msgID)
|
||||
// Is there some other msgID that supports this elemID? If so, bail out
|
||||
for (const [, remainingElemID] of msgIDToElemID) {
|
||||
if (remainingElemID === elemID) {
|
||||
return
|
||||
}
|
||||
}
|
||||
// If not, delete the element
|
||||
elementsByID.delete(elemID)
|
||||
mainWindow.webContents.send('readElements', [
|
||||
{ id: elemID, isDeleted: true },
|
||||
])
|
||||
})
|
||||
|
||||
// Finally safe to kickstart replication and garbage collection
|
||||
setTimeout(() => {
|
||||
peer.conductor.start(
|
||||
globalAccountID,
|
||||
[
|
||||
['profile@dict', 'zooboardElements@newest-100', 'hubs@set'],
|
||||
['profile@dict', 'zooboardElements@newest-100'],
|
||||
],
|
||||
64_000
|
||||
)
|
||||
}, 32)
|
||||
}
|
||||
|
||||
async function scheduleWithHub(multiaddr) {
|
||||
|
|
|
@ -3,6 +3,9 @@ import { Excalidraw } from '@excalidraw/excalidraw'
|
|||
import './App.css'
|
||||
import debounce from 'debounce'
|
||||
|
||||
const elemsPersisted = new Map()
|
||||
let sceneInitialized = false
|
||||
|
||||
function MyAccount() {
|
||||
const nameInput = createRef()
|
||||
const [loaded, setLoaded] = useState(false)
|
||||
|
@ -52,7 +55,6 @@ function MyAccount() {
|
|||
}
|
||||
|
||||
function App() {
|
||||
const elemsPersisted = new Map()
|
||||
const [excalidrawAPI, setExcalidrawAPI] = useState(null)
|
||||
|
||||
function loadExcalidraw(api) {
|
||||
|
@ -67,14 +69,15 @@ function App() {
|
|||
}
|
||||
}
|
||||
api.updateScene({ elements: [...elemsPersisted.values()] })
|
||||
if (!sceneInitialized) sceneInitialized = true
|
||||
})
|
||||
}
|
||||
|
||||
const updateElements = debounce((elems) => {
|
||||
if (excalidrawAPI) return
|
||||
if (!sceneInitialized) return
|
||||
const actions = []
|
||||
for (const elem of elems) {
|
||||
const oldVersion = elemsPersisted.get(elem.id)?.version ?? 0
|
||||
const oldVersion = elemsPersisted.get(elem.id)?.version ?? -1
|
||||
if (elem.version > oldVersion) {
|
||||
actions.push(elem)
|
||||
elemsPersisted.set(elem.id, { ...elem })
|
||||
|
|
Loading…
Reference in New Issue