From d4e1cc848164498eab5be98d46a7a6034f587f90 Mon Sep 17 00:00:00 2001 From: Andre Staltz Date: Thu, 18 Jan 2024 16:59:26 +0200 Subject: [PATCH] pretty good use of gc, conductor, invites, hubs --- main.js | 136 +++++++++++++++++++++++++++++++++++++++++++++------ package.json | 3 +- 2 files changed, 124 insertions(+), 15 deletions(-) diff --git a/main.js b/main.js index 4f6c186..ab85f30 100644 --- a/main.js +++ b/main.js @@ -1,8 +1,9 @@ -const { app, BrowserWindow, ipcMain } = require('electron') +const { app, BrowserWindow, ipcMain, shell } = require('electron') const Path = require('node:path') const URL = require('node:url') const p = require('node:util').promisify const Keypair = require('ppppp-keypair') +const awaitable = require('pull-awaitable') // WARNING monkey patch! -------------------------------------- const na = require('sodium-native') @@ -98,14 +99,33 @@ async function loadAccount() { if (globalAccountID !== null) { return { id: globalAccountID, name: globalAccountName } } + + // Kickstart await peer.db.loaded() - const id = await p(peer.db.account.findOrCreate)({ subdomain: 'account' }) + const id = await p(peer.db.account.findOrCreate)({ subdomain: 'person' }) 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') + for (const multiaddr of multiaddrs) { + scheduleWithHub(multiaddr) + } + + // Read profile const profile = peer.dict.read(id, 'profile') const name = profile?.name ?? '' globalAccountName = name + return { id, name } } @@ -115,6 +135,7 @@ async function setProfileName(ev, name) { } async function writeElements(ev, actions) { + await loadAccount() // FIXME: ideally the frontend shouldn't do this if (globalAccountID === null) throw new Error('account not loaded') for (const action of actions) { await p(peer.db.feed.publish)({ @@ -147,18 +168,105 @@ function subscribeToReadElements() { }) } -app.whenReady().then(() => { - ipcMain.handle('loadAccount', loadAccount) - ipcMain.handle('setProfileName', setProfileName) - ipcMain.handle('writeElements', writeElements) - ipcMain.handle('subscribeToReadElements', subscribeToReadElements) - createWindow() +async function scheduleWithHub(multiaddr) { + const hubRPC = await p(peer.net.connect)(multiaddr) - app.on('activate', function () { - if (BrowserWindow.getAllWindows().length === 0) createWindow() + // FIXME: this should be in the ppppp-net scheduler + for await (const attendants of awaitable(hubRPC.hub.attendants())) { + for (const attendant of attendants) { + if (attendant !== peer.pubkey) { + const tunnelMultiaddr = `/tunnel/${hubRPC.shse.pubkey}.${attendant}/shse/${attendant}` + peer.net.connect(tunnelMultiaddr) + } + } + } +} + +async function handlePPPPPUri(uri) { + if (!uri.startsWith('ppppp://')) return + console.log('handlePPPPPUri', uri) + const commands = peer.invite.parse(uri) + for (const command of commands) { + console.log(command) + switch (command.type) { + case 'join': { + try { + await p(peer.hubClient.addHub)(command.multiaddr) + scheduleWithHub(command.multiaddr) + } catch (err) { + console.error('Failed to properly join hub', err) + } + break + } + case 'follow': { + await p(peer.set.add)('follows', command.id) + break + } + case 'promise.follow': { + const [issuerType, issuerPubkey] = command.issuer + if (issuerType !== 'pubkey') { + throw new Error( + 'dont know how to claim promise issued by ' + issuerType + ) + } + // eslint-disable-next-line no-loop-func + peer.addListener('rpc:connect', function onConnect(rpc) { + if (rpc.shse.pubkey === issuerPubkey) { + rpc.promise.follow(command.token, globalAccountID, (err) => { + if (err) + return console.error('Failed to claim follow promise', err) + peer.removeListener('rpc:connect', onConnect) + }) + } + }) + break + } + default: + console.log('Unknown command type', command.type) + } + } +} + +if (process.defaultApp) { + if (process.argv.length >= 2) { + app.setAsDefaultProtocolClient('ppppp', process.execPath, [ + Path.resolve(process.argv[1]), + ]) + } +} else { + app.setAsDefaultProtocolClient('ppppp') +} + +const hasLock = app.requestSingleInstanceLock() + +if (!hasLock) { + app.quit() +} else { + app.on('second-instance', (ev, argv, cwd, extraData) => { + if (mainWindow) { + if (mainWindow.isMinimized()) mainWindow.restore() + mainWindow.focus() + if (argv.length > 1) { + handlePPPPPUri(argv[argv.length - 1]) + } + } }) -}) -app.on('window-all-closed', function () { - if (process.platform !== 'darwin') app.quit() -}) + app.whenReady().then(() => { + ipcMain.handle('loadAccount', loadAccount) + ipcMain.handle('setProfileName', setProfileName) + ipcMain.handle('writeElements', writeElements) + ipcMain.handle('subscribeToReadElements', subscribeToReadElements) + createWindow() + if (process.argv.length > 1) { + handlePPPPPUri(process.argv[process.argv.length - 1]) + } + + app.on('activate', function () { + if (BrowserWindow.getAllWindows().length === 0) createWindow() + }) + app.on('window-all-closed', function () { + if (process.platform !== 'darwin') app.quit() + }) + }) +} diff --git a/package.json b/package.json index de8db65..18030de 100644 --- a/package.json +++ b/package.json @@ -31,11 +31,12 @@ "ppppp-promise": "github:staltz/ppppp-promise", "ppppp-set": "github:staltz/ppppp-set", "ppppp-sync": "github:staltz/ppppp-sync", + "pull-awaitable": "1.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", "secret-handshake-ext": "0.0.11", - "secret-stack": "8.0.0", + "secret-stack": "8.1.0", "web-vitals": "^2.1.4" }, "devDependencies": {