const i = require("b4a"),
t = require("safety-catch"),
e = require("signal-promise"),
{ Task: s } = require("../task"),
{
getInvitationId: a,
generatePairingCapabilities: o,
generateRoomPairingName: n,
createInvitationJoinOperation: r,
} = require("../../invitations")
module.exports = class extends s {
constructor(i) {
super(i)
const t = this.description.joinRoomInvitation
;((this._invitation = t.invitation),
(this._initiator = t.invitation.initiator),
(this._inviter = t.invitation.inviter),
(this._identity = t.invitation.identity),
(this._mailboxPublicKey = t.invitation.mailbox),
(this._responseKeyPair = t.invitation.responseKeyPair),
(this._invitationSeed = this._invitation.invitationSeed),
(this._capabilities = o(this._mailboxPublicKey)),
(this._invitationId = a(this._invitationSeed)),
(this._roomInfo = null),
(this._sharedRoomInfo = null),
(this._writerInfo = null),
(this._identityInfo = null),
(this._privateProfileInfo = null),
(this._room = null),
(this._core = null),
(this._responseCore = null),
(this._wakeupSession = null),
(this._privateProfile = null),
(this._signal = null),
(this._watcher = null))
}
async _close() {
;(this._signal &&
this._signal.notify(new Error("Invitation join task closed before resolving")),
this._watcher && this._privateProfile.db.unwatch(this._watcher),
this._wakeupSession &&
(this.swarmWorker.stopActiveSwarming(this._capabilities.swarmTopic),
this._wakeupSession.destroy()),
this._core && (await this._core.close()),
this._responseCore && (await this._responseCore.close()),
this._room && this.keet.internalReleaseRoom(this._room),
this._privateProfile && this.keet.internalReleaseRoom(this._privateProfile))
}
_samePendingInvitation(t) {
return (
i.equals(t.id, this._invitationId) &&
i.equals(t.responseKeyPair.publicKey, this._responseKeyPair.publicKey)
)
}
async _appendJoinRequest() {
const i = { key: this._core.key, responsePublicKey: this._responseKeyPair.publicKey }
if (await this.local.get("@local/writer-appends", i)) return
const t = { identity: this._identityInfo, privateProfile: this._privateProfileInfo },
e = {
device: this.keet.internalCreateDeviceUpdate(t, this._core.key, {
isPrivateProfile: !!this._identity,
buildType: this._invitation.buildType,
}),
member: this.keet.internalGetMemberUpdate(t),
}
let s = null
this._identity && (s = { devicePublicKey: this._identity.devicePublicKey })
const a = this.keet.internalGetHeads(this._inviter, this._room),
o = r(
this._core.key,
this._mailboxPublicKey,
this._responseKeyPair,
this._invitationSeed,
e,
{ identity: s, heads: a },
)
if ((await this.keet.internalAppendOptimisticOperation(this._core.key, o), this.closing)) return
const n = await this.keet.internalEnterLocalTransaction()
try {
;(await n.insert("@local/writer-appends", i), await n.flush())
} finally {
await this.keet.internalExitLocalTransaction()
}
}
async _waitForResponse() {
;((this._responseCore = this.store.get({ keyPair: this._responseKeyPair, writable: !1 })),
await this._responseCore.ready())
const i = this._privateProfileInfo.capabilities.mailboxKeyPair.publicKey
if (
(this.keet.blindPeering.addCoreBackground(this._responseCore, {
target: i,
referrer: i,
priority: 1,
}),
this.closing)
)
return
const t = (i) => {
this._wakeupSession.announce(i, [{ key: this._core.key, length: this._core.length }])
}
this._wakeupSession = this.keet.protomuxWakeup.session(this._mailboxPublicKey, {
discoveryKey: this._capabilities.wakeupTopic,
onpeeractive: t,
})
for (const i of this._wakeupSession.peers) i.active && t(i)
;(this.swarmWorker.startActiveSwarming(this._capabilities.swarmTopic),
this.keet.discovery && this.keet.discovery.announce(this._capabilities.discoveryKeyPair))
try {
await this._responseCore.get(0)
} catch (i) {
if (this.closing) return
throw i
}
this.closing ||
(this.swarmWorker.stopActiveSwarming(this._capabilities.swarmTopic),
this.keet.discovery &&
this.keet.discovery.subscribe(this._capabilities.discoveryKeyPair.publicKey, {
since: Date.now(),
}),
await this._responseCore.close(),
this.closing ||
this._privateProfile.base.hintWakeup([{ key: this._responseCore.key, length: 1 }]))
}
async _append() {
const i = await this.keet.store.createKeyPair(n(this._responseKeyPair.publicKey)),
t = await this.keet.internalEnterLocalTransaction()
try {
;((this._writerInfo = {
keyPair: i,
key: this._core.key,
mailbox: this._mailboxPublicKey,
responseKeyPair: this._responseKeyPair,
}),
await t.insert("@local/writers", this._writerInfo),
await t.flush())
} finally {
await this.keet.internalExitLocalTransaction()
}
await this._appendJoinRequest()
}
async _run() {
if (this.closing) return
this.keet.onlineBoot.opened || (await this.keet.onlineBoot.ready())
const [i, s] = await Promise.all([
this.local.get("@local/writers", { mailbox: this._mailboxPublicKey }),
this.local.get("@local/identity"),
]),
[a, o] = await Promise.all([
this.local.get("@local/rooms", { roomKey: s.privateProfileKey }),
this.local.get("@local/rooms-by-mailbox", {
capabilities: { mailboxKeyPair: { publicKey: this._mailboxPublicKey } },
}),
])
if (this.closing) return
if (
((this._roomInfo = o),
(this._writerInfo = i),
(this._identityInfo = s),
(this._privateProfileInfo = a),
(this._privateProfile = this.keet.internalOpenExistingRoom(
this._identityInfo.privateProfileKey,
)),
await this._privateProfile.ready(),
this.closing)
)
return
if (
((this._room = this._roomInfo
? this.keet.internalOpenExistingRoom(this._roomInfo.roomKey)
: null),
this._room && (await this._room.ready(), this.closing))
)
return
const n = await this._privateProfile.db.get("@keet-private-id/pending-invitations", {
id: this._invitationId,
})
!this.closing &&
n &&
this._samePendingInvitation(n) &&
((this._core = this.store.get({ key: this._initiator.key })),
await this._core.ready(),
this.closing ||
(this._core.writable &&
(await this._append(),
this._room && (await this._room.base.setLocal(this._core.key), this.closing))) ||
(this.keet.emit("pending-rooms-changed"),
this.keet.blindPeering.addCoreBackground(this._core, {
target: this._mailboxPublicKey,
referrer: this._mailboxPublicKey,
priority: 1,
}),
this._waitForResponse().catch(this.keet.onerror),
(this._signal = new e()),
(this._watcher = async () => {
try {
const i = await this._privateProfile.db.get("@keet-private-id/pending-invitations", {
id: this._invitationId,
})
;(i && this._samePendingInvitation(i)) || this._signal.notify()
} catch (i) {
t(i)
}
}),
this._privateProfile.db.watch(this._watcher),
this._watcher(),
await this._signal.wait()))
}
}