OverviewFilesCommitsIssuesDocsReleasesPeers

holepunchto-deps / @holepunchto / keet-core / lib / core / call / active-call.js

1 lines · javascript ref:
const e=require("ready-guard"),t=require("hypercore-id-encoding"),s=require("scope-lock"),a=require("b4a"),i=require("../sync/loader.js"),r=require("./ghost.js"),{KeetErrorV2:l}=require("../../errors");module.exports=class{constructor(a,i,r){this.keet=a,this.roomId=i,this.callId=r,this.callKey=t.decode(r),this.swarmId=t.encode(a.callSwarmKeyPair.publicKey),this.guard=new e,this.closing=null,this.room=null,this.callRecord=null,this.callDevice=null,this.swarm=null,this.joinedPeers=new Set,this.heartbeatPaused=!0===a.callHeartbeatOpts.paused,this.suspended=!1,this._opening=null,this._ghostThreshold=a.callHeartbeatOpts.ghostThreshold,this._heartbeatInterval=a.callHeartbeatOpts.heartbeatInterval||3e5,this._heartbeatTimer=null,this._heartbeatPromise=null,this._syncLock=new s({debounce:!0})}ready(){return this.guard.enter()&&(this._opening=this._open()),this.guard.ready()}close(){return this.closing||(this.closing=this._close()),this.closing}async _open(){try{this.room=this.keet.internalOpenExistingRoom(this.roomId),this.room.opened||await this.room.ready();const{call:e,callDevice:t}=await this._loadCallState();if(!e||!t)throw l.CALL_NOT_ACTIVE("",{callId:this.callId});this.callRecord=e,this.callDevice=t;for(const e of this.keet.closingCallSwarms)await e;this.swarm=this.keet.internalCreateCallSwarm(),this.keet.callProxy.open(this.callId,this.swarm),this._heartbeatTimer=setInterval(this._sendHeartbeat.bind(this),this._heartbeatInterval),this._syncPeers(),this.keet.emit("call-swarm-changed",this.callId,this.swarm),t.lastHeartbeatAt>0&&Date.now()-t.lastHeartbeatAt>=this._heartbeatInterval&&this._sendHeartbeat(),this.guard.exit()}catch(e){await this._teardown(),this.guard.destroy(e)}}async _close(){this.guard.destroy(),this._opening&&await this._opening,this._syncLock.destroy(),await this._syncLock.flush(),await this._teardown()}async _teardown(){clearInterval(this._heartbeatTimer),this._heartbeatTimer=null;for(const e of this.joinedPeers)this.keet.debugLeavePeer(e);if(this.joinedPeers.clear(),this.swarm){this.keet.callProxy.close(this.callId),this.keet.emit("call-swarm-changed",null,null);const e=this.swarm.destroy().catch(e=>this.keet.onerror(e));this.keet.closingCallSwarms.add(e),e.then(()=>this.keet.closingCallSwarms.delete(e)),this.swarm=null}this.room&&(await this.keet.internalReleaseRoom(this.room),this.room=null)}async sync(){if(this.guard.opened&&await this._syncLock.lock())try{const{call:e,callDevice:t}=await this._loadCallState();if(this.keet.activeCall!==this)return;if(!e||!t)return void this.keet.internalEndActiveCall();this.callRecord=e,this.callDevice=t,this._syncPeers()}catch(e){this.keet.onerror(e)}finally{this._syncLock.unlock()}}pauseHeartbeat(){this.heartbeatPaused=!0}resumeHeartbeat(){this.heartbeatPaused=!1,this._sendHeartbeat()}async suspend(){this.suspended||(this.suspended=!0,this.swarm&&await this.swarm.suspend())}async resume(){this.suspended&&(this.suspended=!1,this.swarm&&await this.swarm.resume(),this._sendHeartbeat())}async _loadCallState(){await this.room.engine.flush();const e=new i(this.keet.local,this.room.view,this.room.engine);await e.ready();try{const t=await e.loadActiveCall();return t&&a.equals(t.callKey,this.callKey)?{call:t,callDevice:await e.loadActiveCallDevice(t.callKey,this.room.deviceKey)}:{call:null,callDevice:null}}finally{await e.close()}}_sendHeartbeat(){if(this.callRecord&&!this.closing&&!this.suspended&&!this.heartbeatPaused)return this._heartbeatPromise||(this._heartbeatPromise=this._dispatchHeartbeat()),this._heartbeatPromise}async _dispatchHeartbeat(){try{if(this.keet.closing||this.closing)return;await this.keet.dispatch(this.roomId,"@keet/call-device-heartbeat",{callKey:this.callKey})}finally{this._heartbeatPromise=null}}_syncPeers(){const e=Date.now(),s=new Set;for(const a of this.callRecord.members)for(const i of a.devices){if(!r(e,i,this._ghostThreshold))continue;const a=t.encode(i.swarmKey);a!==this.swarmId&&s.add(a)}for(const e of s)this.joinedPeers.has(e)||(this.swarm.joinPeer(t.decode(e)),this.joinedPeers.add(e),this.keet.debugJoinPeer(e));for(const e of this.joinedPeers)s.has(e)||(this.swarm.leavePeer(t.decode(e)),this.joinedPeers.delete(e),this.keet.debugLeavePeer(e))}};