mirror of https://codeberg.org/pzp/zooboard.git
165 lines
4.6 KiB
JavaScript
165 lines
4.6 KiB
JavaScript
const { app, BrowserWindow, ipcMain } = require('electron')
|
|
const Path = require('node:path')
|
|
const URL = require('node:url')
|
|
const p = require('node:util').promisify
|
|
const Keypair = require('ppppp-keypair')
|
|
|
|
// WARNING monkey patch! --------------------------------------
|
|
const na = require('sodium-native')
|
|
na.sodium_malloc = function sodium_malloc_monkey_patched(n) {
|
|
return Buffer.alloc(n)
|
|
}
|
|
na.sodium_free = function sodium_free_monkey_patched() {}
|
|
// Electron > 20.3.8 breaks a napi method that `sodium_malloc`
|
|
// depends on to create external buffers. (see v8 memory cage)
|
|
//
|
|
// This crashes electron when called by various libraries, so
|
|
// we monkey-patch this particular function.
|
|
// ------------------------------------------------------------
|
|
|
|
process.env.ZOOBOARD_DATA ??= Path.join(app.getPath('appData'), 'zooboard')
|
|
app.setPath('userData', process.env.ZOOBOARD_DATA)
|
|
const path = Path.resolve(app.getPath('userData'), 'ppppp')
|
|
|
|
const keypairPath = Path.join(path, 'keypair.json')
|
|
const keypair = Keypair.loadOrCreateSync(keypairPath)
|
|
let mainWindow
|
|
let globalAccountID = null
|
|
let globalAccountName = null
|
|
|
|
const peer = require('secret-stack/bare')()
|
|
.use(require('secret-stack/plugins/net'))
|
|
.use(require('secret-handshake-ext/secret-stack'))
|
|
.use(require('ppppp-net'))
|
|
.use(require('ppppp-db'))
|
|
.use(require('ppppp-set'))
|
|
.use(require('ppppp-dict'))
|
|
.use(require('ppppp-goals'))
|
|
.use(require('ppppp-sync'))
|
|
.use(require('ppppp-gc'))
|
|
.use(require('ppppp-conductor'))
|
|
.use(require('ppppp-hub-client'))
|
|
.use(require('ppppp-promise'))
|
|
.use(require('ppppp-invite'))
|
|
.call(null, {
|
|
shse: {
|
|
caps: require('ppppp-caps'),
|
|
},
|
|
global: {
|
|
keypair,
|
|
path,
|
|
timers: {
|
|
inactivity: 10 * 60e3,
|
|
},
|
|
connections: {
|
|
incoming: {
|
|
tunnel: [{ transform: 'shse', scope: 'public' }],
|
|
},
|
|
outgoing: {
|
|
net: [{ transform: 'shse' }],
|
|
tunnel: [{ transform: 'shse' }],
|
|
},
|
|
},
|
|
},
|
|
net: {
|
|
autostart: true,
|
|
},
|
|
})
|
|
|
|
function createWindow() {
|
|
mainWindow = new BrowserWindow({
|
|
width: 1200,
|
|
height: 1200,
|
|
title: 'Zooboard',
|
|
webPreferences: {
|
|
preload: Path.join(__dirname, 'preload.js'),
|
|
},
|
|
})
|
|
|
|
const startUrl =
|
|
process.env.ELECTRON_START_URL ??
|
|
URL.format({
|
|
pathname: Path.join(__dirname, '/../build/index.html'),
|
|
protocol: 'file:',
|
|
slashes: true,
|
|
})
|
|
mainWindow.loadURL(startUrl)
|
|
|
|
mainWindow.webContents.openDevTools({ mode: 'bottom', activate: true })
|
|
|
|
// open Web URLs in the default system browser
|
|
mainWindow.webContents.on('new-window', (ev, url) => {
|
|
ev.preventDefault()
|
|
shell.openExternal(url)
|
|
})
|
|
}
|
|
|
|
async function loadAccount() {
|
|
if (globalAccountID !== null) {
|
|
return { id: globalAccountID, name: globalAccountName }
|
|
}
|
|
await peer.db.loaded()
|
|
const id = await p(peer.db.account.findOrCreate)({ subdomain: 'account' })
|
|
globalAccountID = id
|
|
await p(peer.set.load)(id)
|
|
await p(peer.dict.load)(id)
|
|
const profile = peer.dict.read(id, 'profile')
|
|
const name = profile?.name ?? ''
|
|
globalAccountName = name
|
|
return { id, name }
|
|
}
|
|
|
|
async function setProfileName(ev, name) {
|
|
await p(peer.dict.update)('profile', { name })
|
|
return name
|
|
}
|
|
|
|
async function writeElements(ev, actions) {
|
|
if (globalAccountID === null) throw new Error('account not loaded')
|
|
for (const action of actions) {
|
|
await p(peer.db.feed.publish)({
|
|
account: globalAccountID,
|
|
domain: 'zooboardElements',
|
|
data: action,
|
|
})
|
|
}
|
|
}
|
|
|
|
function subscribeToReadElements() {
|
|
const elementsByID = new Map()
|
|
for (const msg of peer.db.msgs()) {
|
|
if (msg.data && msg.metadata.domain === 'zooboardElements') {
|
|
const { id, isDeleted } = msg.data
|
|
if (isDeleted) {
|
|
elementsByID.delete(id)
|
|
} else {
|
|
elementsByID.set(id, msg.data)
|
|
}
|
|
}
|
|
}
|
|
const initialElements = [...elementsByID.values()]
|
|
mainWindow.webContents.send('readElements', initialElements)
|
|
|
|
peer.db.onRecordAdded(({ msg }) => {
|
|
if (msg.data && msg.metadata.domain === 'zooboardElements') {
|
|
mainWindow.webContents.send('readElements', [msg.data])
|
|
}
|
|
})
|
|
}
|
|
|
|
app.whenReady().then(() => {
|
|
ipcMain.handle('loadAccount', loadAccount)
|
|
ipcMain.handle('setProfileName', setProfileName)
|
|
ipcMain.handle('writeElements', writeElements)
|
|
ipcMain.handle('subscribeToReadElements', subscribeToReadElements)
|
|
createWindow()
|
|
|
|
app.on('activate', function () {
|
|
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
|
})
|
|
})
|
|
|
|
app.on('window-all-closed', function () {
|
|
if (process.platform !== 'darwin') app.quit()
|
|
})
|