Finish active speaker selection

This commit is contained in:
Robert Long committed 2021-09-02 13:36:37 -07:00
1 parent 99ecb8aa28
commit e23357dca2
4 files changed
+67 -29

No files matched your search

+15 -3
View File
@@ -44766,7 +44766,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createClient = exports.setCryptoStoreFactory = exports.wrapRequest = exports.getRequest = exports.request = exports.setMatrixCallVideoInput = exports.setMatrixCallAudioInput = exports.createNewMatrixCall = exports.ContentHelpers = void 0;
exports.createClient = exports.setCryptoStoreFactory = exports.wrapRequest = exports.getRequest = exports.request = exports.CallFeed = exports.setMatrixCallVideoInput = exports.setMatrixCallAudioInput = exports.createNewMatrixCall = exports.ContentHelpers = void 0;
const memory_crypto_store_1 = require("./crypto/store/memory-crypto-store");
const memory_1 = require("./store/memory");
const scheduler_1 = require("./scheduler");
@@ -44800,6 +44800,11 @@ var call_1 = require("./webrtc/call");
Object.defineProperty(exports, "createNewMatrixCall", { enumerable: true, get: function () { return call_1.createNewMatrixCall; } });
Object.defineProperty(exports, "setMatrixCallAudioInput", { enumerable: true, get: function () { return call_1.setAudioInput; } });
Object.defineProperty(exports, "setMatrixCallVideoInput", { enumerable: true, get: function () { return call_1.setVideoInput; } });
// TODO: This export is temporary and is only used for the local call feed for conference calls
// Ideally conference calls will become a first-class concept and we will have a local call feed with
// a lifecycle that matches the conference call, not individual calls to members.
var callFeed_1 = require("./webrtc/callFeed");
Object.defineProperty(exports, "CallFeed", { enumerable: true, get: function () { return callFeed_1.CallFeed; } });
// expose the underlying request object so different environments can use
// different request libs (e.g. request or browser-request)
let requestInstance;
@@ -44922,7 +44927,7 @@ exports.createClient = createClient;
}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./autodiscovery":74,"./client":76,"./content-helpers":77,"./content-repo":78,"./crypto/store/indexeddb-crypto-store":100,"./crypto/store/memory-crypto-store":102,"./errors":111,"./filter":114,"./http-api":115,"./interactive-auth":117,"./models/event":125,"./models/event-timeline":124,"./models/event-timeline-set":123,"./models/group":126,"./models/room":131,"./models/room-member":128,"./models/room-state":129,"./models/user":134,"./scheduler":138,"./service-types":139,"./store/indexeddb":142,"./store/memory":143,"./store/session/webstorage":144,"./sync-accumulator":146,"./timeline-window":149,"./webrtc/call":151}],120:[function(require,module,exports){
},{"./autodiscovery":74,"./client":76,"./content-helpers":77,"./content-repo":78,"./crypto/store/indexeddb-crypto-store":100,"./crypto/store/memory-crypto-store":102,"./errors":111,"./filter":114,"./http-api":115,"./interactive-auth":117,"./models/event":125,"./models/event-timeline":124,"./models/event-timeline-set":123,"./models/group":126,"./models/room":131,"./models/room-member":128,"./models/room-state":129,"./models/user":134,"./scheduler":138,"./service-types":139,"./store/indexeddb":142,"./store/memory":143,"./store/session/webstorage":144,"./sync-accumulator":146,"./timeline-window":149,"./webrtc/call":151,"./webrtc/callFeed":154}],120:[function(require,module,exports){
"use strict";
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
@@ -59157,6 +59162,9 @@ class MatrixCall extends events_1.EventEmitter {
logger_1.logger.info(`Pushed local stream (id="${stream.id}", active="${stream.active}", purpose="${purpose}")`);
}
deleteAllFeeds() {
for (const feed of this.feeds) {
feed.dispose();
}
this.feeds = [];
this.emit(CallEvent.FeedsChanged, this.feeds);
}
@@ -59167,6 +59175,7 @@ class MatrixCall extends events_1.EventEmitter {
logger_1.logger.warn(`Didn't find the feed with stream id ${stream.id} to delete`);
return;
}
feed.dispose();
this.feeds.splice(this.feeds.indexOf(feed), 1);
this.emit(CallEvent.FeedsChanged, this.feeds);
}
@@ -60737,7 +60746,7 @@ class CallFeed extends events_1.default {
volumeLooper() {
if (!this.analyser)
return;
setTimeout(() => {
this.volumeLooperTimeout = setTimeout(() => {
if (!this.measuringVolumeActivity)
return;
this.analyser.getFloatFrequencyData(this.frequencyBinCount);
@@ -60756,6 +60765,9 @@ class CallFeed extends events_1.default {
this.volumeLooper();
}, POLLING_INTERVAL);
}
dispose() {
clearTimeout(this.volumeLooperTimeout);
}
}
exports.CallFeed = CallFeed;
+4 -4
View File
@@ -158,7 +158,7 @@
"src/webrtc/callFeed.ts"
],
"names": [],
"mappings": "AAAA;ACAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;;ACLA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACPA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACTA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACrCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACPA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACzBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACjBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACTA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AClBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACPA;AACA;AACA;AACA;AACA;AACA;;ACLA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACdA;AACA;AACA;AACA;AACA;AACA;;ACLA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC/BA;AACA;AACA;AACA;AACA;AACA;;ACLA;AACA;AACA;AACA;AACA;AACA;;ACLA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACfA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACbA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACbA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACrBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACZA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC5CA;AACA;;ACDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC7FA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC3HA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtJA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACALine truncated
"mappings": "AAAA;ACAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;;ACLA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACPA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACTA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACrCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACPA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACzBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACjBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACTA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AClBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACPA;AACA;AACA;AACA;AACA;AACA;;ACLA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACdA;AACA;AACA;AACA;AACA;AACA;;ACLA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC/BA;AACA;AACA;AACA;AACA;AACA;;ACLA;AACA;AACA;AACA;AACA;AACA;;ACLA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACfA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACbA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACbA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACrBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACZA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC5CA;AACA;;ACDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC7FA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC3HA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtJA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACALine truncated
"file": "generated.js",
"sourceRoot": "",
"sourcesContent": [
@@ -281,7 +281,7 @@
"/*\nCopyright 2019 New Vector Ltd\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * Check if an IndexedDB database exists. The only way to do so is to try opening it, so\n * we do that and then delete it did not exist before.\n *\n * @param {Object} indexedDB The `indexedDB` interface\n * @param {string} dbName The database name to test for\n * @returns {boolean} Whether the database exists\n */\nexport function exists(indexedDB: IDBFactory, dbName: string): Promise<boolean> {\n return new Promise<boolean>((resolve, reject) => {\n let exists = true;\n const req = indexedDB.open(dbName);\n req.onupgradeneeded = () => {\n // Since we did not provide an explicit version when opening, this event\n // should only fire if the DB did not exist before at any version.\n exists = false;\n };\n req.onblocked = () => reject(req.error);\n req.onsuccess = () => {\n const db = req.result;\n db.close();\n if (!exists) {\n // The DB did not exist before, but has been created as part of this\n // existence check. Delete it now to restore previous state. Delete can\n // actually take a while to complete in some browsers, so don't wait for\n // it. This won't block future open calls that a store might issue next to\n // properly set up the DB.\n indexedDB.deleteDatabase(dbName);\n }\n resolve(exists);\n };\n req.onerror = ev => reject(req.error);\n });\n}\n",
"/*\nCopyright 2016 OpenMarket Ltd\nCopyright 2017 Vector Creations Ltd\nCopyright 2019 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/** @module interactive-auth */\n\nimport * as utils from \"./utils\";\nimport { logger } from './logger';\nimport { MatrixClient } from \"./client\";\nimport { defer, IDeferred } from \"./utils\";\nimport { MatrixError } from \"./http-api\";\n\nconst EMAIL_STAGE_TYPE = \"m.login.email.identity\";\nconst MSISDN_STAGE_TYPE = \"m.login.msisdn\";\n\ninterface IFlow {\n stages: AuthType[];\n}\n\nexport interface IInputs {\n emailAddress?: string;\n phoneCountry?: string;\n phoneNumber?: string;\n}\n\nexport interface IStageStatus {\n emailSid?: string;\n errcode?: string;\n error?: string;\n}\n\nexport interface IAuthData {\n session?: string;\n completed?: string[];\n flows?: IFlow[];\n params?: Record<string, Record<string, any>>;\n errcode?: string;\n error?: MatrixError;\n}\n\nexport enum AuthType {\n Password = \"m.login.password\",\n Recaptcha = \"m.login.recaptcha\",\n Terms = \"m.login.terms\",\n Email = \"m.login.email.identity\",\n Msisdn = \"m.login.msisdn\",\n Sso = \"m.login.sso\",\n SsoUnstable = \"org.matrix.login.sso\",\n Dummy = \"m.login.dummy\",\n}\n\nexport interface IAuthDict {\n // [key: string]: any;\n type?: string;\n // session?: string; // TODO\n // TODO: Remove `user` once servers support proper UIA\n // See https://github.com/vector-im/element-web/issues/10312\n user?: string;\n identifier?: any;\n password?: string;\n response?: string;\n // TODO: Remove `threepid_creds` once servers support proper UIA\n // See https://github.com/vector-im/element-web/issues/10312\n // See https://github.com/matrix-org/matrix-doc/issues/2220\n // eslint-disable-next-line camelcase\n threepid_creds?: any;\n threepidCreds?: any;\n}\n\nclass NoAuthFlowFoundError extends Error {\n public name = \"NoAuthFlowFoundError\";\n\n // eslint-disable-next-line @typescript-eslint/naming-convention, camelcase\n constructor(m: string, public readonly required_stages: string[], public readonly flows: IFlow[]) {\n super(m);\n }\n}\n\ninterface IOpts {\n matrixClient: MatrixClient;\n authData?: IAuthData;\n inputs?: IInputs;\n sessionId?: string;\n clientSecret?: string;\n emailSid?: string;\n doRequest(auth: IAuthData, background: boolean): Promise<IAuthData>;\n stateUpdated(nextStage: AuthType, status: IStageStatus): void;\n requestEmailToken(email: string, secret: string, attempt: number, session: string): Promise<{ sid: string }>;\n busyChanged?(busy: boolean): void;\n startAuthStage?(nextStage: string): Promise<void>; // LEGACY\n}\n\n/**\n * Abstracts the logic used to drive the interactive auth process.\n *\n * <p>Components implementing an interactive auth flow should instantiate one of\n * these, passing in the necessary callbacks to the constructor. They should\n * then call attemptAuth, which will return a promise which will resolve or\n * reject when the interactive-auth process completes.\n *\n * <p>Meanwhile, calls will be made to the startAuthStage and doRequest\n * callbacks, and information gathered from the user can be submitted with\n * submitAuthDict.\n *\n * @constructor\n * @alias module:interactive-auth\n *\n * @param {object} opts options object\n *\n * @param {object} opts.matrixClient A matrix client to use for the auth process\n *\n * @param {object?} opts.authData error response from the last request. If\n * null, a request will be made with no auth before starting.\n *\n * @param {function(object?): Promise} opts.doRequest\n * called with the new auth dict to submit the request. Also passes a\n * second deprecated arg which is a flag set to true if this request\n * is a background request. The busyChanged callback should be used\n * instead of the background flag. Should return a promise which resolves\n * to the successful response or rejects with a MatrixError.\n *\n * @param {function(boolean): Promise} opts.busyChanged\n * called whenever the interactive auth logic becomes busy submitting\n * information provided by the user or finishes. After this has been\n * called with true the UI should indicate that a request is in progress\n * until it is called again with false.\n *\n * @param {function(string, object?)} opts.stateUpdatedLine truncated
"/*\nCopyright 2018 André Jaenisch\nCopyright 2019, 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * @module logger\n */\n\nimport log, { Logger } from \"loglevel\";\n\n// This is to demonstrate, that you can use any namespace you want.\n// Namespaces allow you to turn on/off the logging for specific parts of the\n// application.\n// An idea would be to control this via an environment variable (on Node.js).\n// See https://www.npmjs.com/package/debug to see how this could be implemented\n// Part of #332 is introducing a logging library in the first place.\nconst DEFAULT_NAMESPACE = \"matrix\";\n\n// because rageshakes in react-sdk hijack the console log, also at module load time,\n// initializing the logger here races with the initialization of rageshakes.\n// to avoid the issue, we override the methodFactory of loglevel that binds to the\n// console methods at initialization time by a factory that looks up the console methods\n// when logging so we always get the current value of console methods.\nlog.methodFactory = function(methodName, logLevel, loggerName) {\n return function(...args) {\n /* eslint-disable @typescript-eslint/no-invalid-this */\n if (this.prefix) {\n args.unshift(this.prefix);\n }\n /* eslint-enable @typescript-eslint/no-invalid-this */\n const supportedByConsole = methodName === \"error\" ||\n methodName === \"warn\" ||\n methodName === \"trace\" ||\n methodName === \"info\";\n /* eslint-disable no-console */\n if (supportedByConsole) {\n return console[methodName](...args);\n } else {\n return console.log(...args);\n }\n /* eslint-enable no-console */\n };\n};\n\n/**\n * Drop-in replacement for <code>console</code> using {@link https://www.npmjs.com/package/loglevel|loglevel}.\n * Can be tailored down to specific use cases if needed.\n */\nexport const logger: PrefixedLogger = log.getLogger(DEFAULT_NAMESPACE);\nlogger.setLevel(log.levels.DEBUG);\n\nexport interface PrefixedLogger extends Logger {\n withPrefix?: (prefix: string) => PrefixedLogger;\n prefix?: string;\n}\n\nfunction extendLogger(logger: PrefixedLogger) {\n logger.withPrefix = function(prefix: string): PrefixedLogger {\n const existingPrefix = this.prefix || \"\";\n return getPrefixedLogger(existingPrefix + prefix);\n };\n}\n\nextendLogger(logger);\n\nfunction getPrefixedLogger(prefix): PrefixedLogger {\n const prefixLogger: PrefixedLogger = log.getLogger(`${DEFAULT_NAMESPACE}-${prefix}`);\n if (prefixLogger.prefix !== prefix) {\n // Only do this setup work the first time through, as loggers are saved by name.\n extendLogger(prefixLogger);\n prefixLogger.prefix = prefix;\n prefixLogger.setLevel(log.levels.DEBUG);\n }\n return prefixLogger;\n}\n",
"/*\nCopyright 2015-2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { MemoryCryptoStore } from \"./crypto/store/memory-crypto-store\";\nimport { MemoryStore } from \"./store/memory\";\nimport { MatrixScheduler } from \"./scheduler\";\nimport { MatrixClient } from \"./client\";\nimport { ICreateClientOpts } from \"./client\";\nimport { DeviceTrustLevel } from \"./crypto/CrossSigning\";\nimport { ISecretStorageKeyInfo } from \"./crypto/api\";\n\nexport * from \"./client\";\nexport * from \"./http-api\";\nexport * from \"./autodiscovery\";\nexport * from \"./sync-accumulator\";\nexport * from \"./errors\";\nexport * from \"./models/event\";\nexport * from \"./models/room\";\nexport * from \"./models/group\";\nexport * from \"./models/event-timeline\";\nexport * from \"./models/event-timeline-set\";\nexport * from \"./models/room-member\";\nexport * from \"./models/room-state\";\nexport * from \"./models/user\";\nexport * from \"./scheduler\";\nexport * from \"./filter\";\nexport * from \"./timeline-window\";\nexport * from \"./interactive-auth\";\nexport * from \"./service-types\";\nexport * from \"./store/memory\";\nexport * from \"./store/indexeddb\";\nexport * from \"./store/session/webstorage\";\nexport * from \"./crypto/store/memory-crypto-store\";\nexport * from \"./crypto/store/indexeddb-crypto-store\";\nexport * from \"./content-repo\";\nexport * as ContentHelpers from \"./content-helpers\";\nexport {\n createNewMatrixCall,\n setAudioInput as setMatrixCallAudioInput,\n setVideoInput as setMatrixCallVideoInput,\n} from \"./webrtc/call\";\n\n// expose the underlying request object so different environments can use\n// different request libs (e.g. request or browser-request)\nlet requestInstance;\n\n/**\n * The function used to perform HTTP requests. Only use this if you want to\n * use a different HTTP library, e.g. Angular's <code>$http</code>. This should\n * be set prior to calling {@link createClient}.\n * @param {requestFunction} r The request function to use.\n */\nexport function request(r) {\n requestInstance = r;\n}\n\n/**\n * Return the currently-set request function.\n * @return {requestFunction} The current request function.\n */\nexport function getRequest() {\n return requestInstance;\n}\n\n/**\n * Apply wrapping code around the request function. The wrapper function is\n * installed as the new request handler, and when invoked it is passed the\n * previous value, along with the options and callback arguments.\n * @param {requestWrapperFunction} wrapper The wrapping function.\n */\nexport function wrapRequest(wrapper) {\n const origRequest = requestInstance;\n requestInstance = function(options, callback) {\n return wrapper(origRequest, options, callback);\n };\n}\n\nlet cryptoStoreFactory = () => new MemoryCryptoStore;\n\n/**\n * Configure a different factory to be used for creating crypto stores\n *\n * @param {Function} fac a function which will return a new\n * {@link module:crypto.store.base~CryptoStore}.\n */\nexport function setCryptoStoreFactory(fac) {\n cryptoStoreFactory = fac;\n}\n\nexport interface ICryptoCallbacks {\n getCrossSigningKey?: (keyType: string, pubKey: string) => Promise<Uint8Array>;\n saveCrossSigningKeys?: (keys: Record<string, Uint8Array>) => void;\n shouldUpgradeDeviceVerifications?: (\n users: Record<string, any>\n ) => Promise<string[]>;\n getSecretStorageKey?: (\n keys: {keys: Record<string, ISecretStorageKeyInfo>}, name: string\n ) => Promise<[string, Uint8Array] | null>;\n cacheSecretStorageKey?: (\n keyId: string, keyInfo: ISecretStorageKeyInfo, key: Uint8Array\n ) => void;\n onSecretRequested?: (\n userId: string, deviceId: string,\n requestId: string, secretName: string, deviceTrust: DeviceTrustLevel\n ) => Promise<string>;\n getDehydrationKey?: (\n keyInfo: ISecretStorageKeyInfo,\n checkFunc: (Uint8Array) => void,\n ) => Promise<Uint8Array>;\n getBackupKey?: () => Promise<Uint8Array>;\n}\n\n/**\n * Construct a Matrix Client. Similar to {@link module:client.MatrixClient}\n * except that the 'request', 'store' and 'scheduler' dependencies are satisfied.\n * @param {(Object|string)} opts The configuration options for this client. If\n * this is a string, it is assumed to be the base URL. These configuration\n * options will be passed directly to {@link module:client.MatrixClient}.\n * @param {Line truncated
"/*\nCopyright 2015-2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { MemoryCryptoStore } from \"./crypto/store/memory-crypto-store\";\nimport { MemoryStore } from \"./store/memory\";\nimport { MatrixScheduler } from \"./scheduler\";\nimport { MatrixClient } from \"./client\";\nimport { ICreateClientOpts } from \"./client\";\nimport { DeviceTrustLevel } from \"./crypto/CrossSigning\";\nimport { ISecretStorageKeyInfo } from \"./crypto/api\";\n\nexport * from \"./client\";\nexport * from \"./http-api\";\nexport * from \"./autodiscovery\";\nexport * from \"./sync-accumulator\";\nexport * from \"./errors\";\nexport * from \"./models/event\";\nexport * from \"./models/room\";\nexport * from \"./models/group\";\nexport * from \"./models/event-timeline\";\nexport * from \"./models/event-timeline-set\";\nexport * from \"./models/room-member\";\nexport * from \"./models/room-state\";\nexport * from \"./models/user\";\nexport * from \"./scheduler\";\nexport * from \"./filter\";\nexport * from \"./timeline-window\";\nexport * from \"./interactive-auth\";\nexport * from \"./service-types\";\nexport * from \"./store/memory\";\nexport * from \"./store/indexeddb\";\nexport * from \"./store/session/webstorage\";\nexport * from \"./crypto/store/memory-crypto-store\";\nexport * from \"./crypto/store/indexeddb-crypto-store\";\nexport * from \"./content-repo\";\nexport * as ContentHelpers from \"./content-helpers\";\nexport {\n createNewMatrixCall,\n setAudioInput as setMatrixCallAudioInput,\n setVideoInput as setMatrixCallVideoInput,\n} from \"./webrtc/call\";\n\n// TODO: This export is temporary and is only used for the local call feed for conference calls\n// Ideally conference calls will become a first-class concept and we will have a local call feed with\n// a lifecycle that matches the conference call, not individual calls to members.\nexport { CallFeed } from \"./webrtc/callFeed\";\n\n// expose the underlying request object so different environments can use\n// different request libs (e.g. request or browser-request)\nlet requestInstance;\n\n/**\n * The function used to perform HTTP requests. Only use this if you want to\n * use a different HTTP library, e.g. Angular's <code>$http</code>. This should\n * be set prior to calling {@link createClient}.\n * @param {requestFunction} r The request function to use.\n */\nexport function request(r) {\n requestInstance = r;\n}\n\n/**\n * Return the currently-set request function.\n * @return {requestFunction} The current request function.\n */\nexport function getRequest() {\n return requestInstance;\n}\n\n/**\n * Apply wrapping code around the request function. The wrapper function is\n * installed as the new request handler, and when invoked it is passed the\n * previous value, along with the options and callback arguments.\n * @param {requestWrapperFunction} wrapper The wrapping function.\n */\nexport function wrapRequest(wrapper) {\n const origRequest = requestInstance;\n requestInstance = function(options, callback) {\n return wrapper(origRequest, options, callback);\n };\n}\n\nlet cryptoStoreFactory = () => new MemoryCryptoStore;\n\n/**\n * Configure a different factory to be used for creating crypto stores\n *\n * @param {Function} fac a function which will return a new\n * {@link module:crypto.store.base~CryptoStore}.\n */\nexport function setCryptoStoreFactory(fac) {\n cryptoStoreFactory = fac;\n}\n\nexport interface ICryptoCallbacks {\n getCrossSigningKey?: (keyType: string, pubKey: string) => Promise<Uint8Array>;\n saveCrossSigningKeys?: (keys: Record<string, Uint8Array>) => void;\n shouldUpgradeDeviceVerifications?: (\n users: Record<string, any>\n ) => Promise<string[]>;\n getSecretStorageKey?: (\n keys: {keys: Record<string, ISecretStorageKeyInfo>}, name: string\n ) => Promise<[string, Uint8Array] | null>;\n cacheSecretStorageKey?: (\n keyId: string, keyInfo: ISecretStorageKeyInfo, key: Uint8Array\n ) => void;\n onSecretRequested?: (\n userId: string, deviceId: string,\n requestId: string, secretName: string, deviceTrust: DeviceTrustLevel\n ) => Promise<string>;\n getDehydrationKey?: (\n keyInfo: ISecretStorageKeyInfo,\n checkFunc: (Uint8Array) => void,\n ) => Promise<Uint8Array>;\n getBackupKey?: () => Promise<Uint8Array>;\n}\n\n/**\n * Construct a Matrix Client. Similar to {@link module:client.MatrixLine truncated
"/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { MatrixClient } from \"../client\";\nimport { IEncryptedFile, UNSTABLE_MSC3089_BRANCH } from \"../@types/event\";\nimport { MatrixEvent } from \"./event\";\n\n/**\n * Represents a [MSC3089](https://github.com/matrix-org/matrix-doc/pull/3089) branch - a reference\n * to a file (leaf) in the tree. Note that this is UNSTABLE and subject to breaking changes\n * without notice.\n */\nexport class MSC3089Branch {\n public constructor(private client: MatrixClient, public readonly indexEvent: MatrixEvent) {\n // Nothing to do\n }\n\n /**\n * The file ID.\n */\n public get id(): string {\n return this.indexEvent.getStateKey();\n }\n\n /**\n * Whether this branch is active/valid.\n */\n public get isActive(): boolean {\n return this.indexEvent.getContent()[\"active\"] === true;\n }\n\n private get roomId(): string {\n return this.indexEvent.getRoomId();\n }\n\n /**\n * Deletes the file from the tree.\n * @returns {Promise<void>} Resolves when complete.\n */\n public async delete(): Promise<void> {\n await this.client.sendStateEvent(this.roomId, UNSTABLE_MSC3089_BRANCH.name, {}, this.id);\n await this.client.redactEvent(this.roomId, this.id);\n\n // TODO: Delete edit history as well\n }\n\n /**\n * Gets the name for this file.\n * @returns {string} The name, or \"Unnamed File\" if unknown.\n */\n public getName(): string {\n return this.indexEvent.getContent()['name'] || \"Unnamed File\";\n }\n\n /**\n * Sets the name for this file.\n * @param {string} name The new name for this file.\n * @returns {Promise<void>} Resolves when complete.\n */\n public async setName(name: string): Promise<void> {\n await this.client.sendStateEvent(this.roomId, UNSTABLE_MSC3089_BRANCH.name, {\n ...this.indexEvent.getContent(),\n name: name,\n }, this.id);\n }\n\n /**\n * Gets information about the file needed to download it.\n * @returns {Promise<{info: IEncryptedFile, httpUrl: string}>} Information about the file.\n */\n public async getFileInfo(): Promise<{ info: IEncryptedFile, httpUrl: string }> {\n const event = await this.getFileEvent();\n\n const file = event.getContent()['file'];\n const httpUrl = this.client.mxcUrlToHttp(file['url']);\n\n return { info: file, httpUrl: httpUrl };\n }\n\n /**\n * Gets the event the file points to.\n * @returns {Promise<MatrixEvent>} Resolves to the file's event.\n */\n public async getFileEvent(): Promise<MatrixEvent> {\n const room = this.client.getRoom(this.roomId);\n if (!room) throw new Error(\"Unknown room\");\n\n const timeline = await this.client.getEventTimeline(room.getUnfilteredTimelineSet(), this.id);\n if (!timeline) throw new Error(\"Failed to get timeline for room event\");\n\n const event = timeline.getEvents().find(e => e.getId() === this.id);\n if (!event) throw new Error(\"Failed to find event\");\n\n // Sometimes the event context doesn't decrypt for us, so do that.\n await this.client.decryptEventIfNeeded(event, { emit: false, isRetry: false });\n\n return event;\n }\n}\n",
"/*\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { MatrixClient } from \"../client\";\nimport { EventType, IEncryptedFile, MsgType, UNSTABLE_MSC3089_BRANCH, UNSTABLE_MSC3089_LEAF } from \"../@types/event\";\nimport { Room } from \"./room\";\nimport { logger } from \"../logger\";\nimport { IContent, MatrixEvent } from \"./event\";\nimport {\n averageBetweenStrings,\n DEFAULT_ALPHABET,\n lexicographicCompare,\n nextString,\n prevString,\n simpleRetryOperation,\n} from \"../utils\";\nimport { MSC3089Branch } from \"./MSC3089Branch\";\nimport promiseRetry from \"p-retry\";\nimport { isRoomSharedHistory } from \"../crypto/algorithms/megolm\";\n\n/**\n * The recommended defaults for a tree space's power levels. Note that this\n * is UNSTABLE and subject to breaking changes without notice.\n */\nexport const DEFAULT_TREE_POWER_LEVELS_TEMPLATE = {\n // Owner\n invite: 100,\n kick: 100,\n ban: 100,\n\n // Editor\n redact: 50,\n state_default: 50,\n events_default: 50,\n\n // Viewer\n users_default: 0,\n\n // Mixed\n events: {\n [EventType.RoomPowerLevels]: 100,\n [EventType.RoomHistoryVisibility]: 100,\n [EventType.RoomTombstone]: 100,\n [EventType.RoomEncryption]: 100,\n [EventType.RoomName]: 50,\n [EventType.RoomMessage]: 50,\n [EventType.RoomMessageEncrypted]: 50,\n [EventType.Sticker]: 50,\n },\n\n users: {}, // defined by calling code\n};\n\n/**\n * Ease-of-use representation for power levels represented as simple roles.\n * Note that this is UNSTABLE and subject to breaking changes without notice.\n */\nexport enum TreePermissions {\n Viewer = \"viewer\", // Default\n Editor = \"editor\", // \"Moderator\" or ~PL50\n Owner = \"owner\", // \"Admin\" or PL100\n}\n\n/**\n * Represents a [MSC3089](https://github.com/matrix-org/matrix-doc/pull/3089)\n * file tree Space. Note that this is UNSTABLE and subject to breaking changes\n * without notice.\n */\nexport class MSC3089TreeSpace {\n public readonly room: Room;\n\n public constructor(private client: MatrixClient, public readonly roomId: string) {\n this.room = this.client.getRoom(this.roomId);\n\n if (!this.room) throw new Error(\"Unknown room\");\n }\n\n /**\n * Syntactic sugar for room ID of the Space.\n */\n public get id(): string {\n return this.roomId;\n }\n\n /**\n * Whether or not this is a top level space.\n */\n public get isTopLevel(): boolean {\n // XXX: This is absolutely not how you find out if the space is top level\n // but is safe for a managed usecase like we offer in the SDK.\n const parentEvents = this.room.currentState.getStateEvents(EventType.SpaceParent);\n if (!parentEvents?.length) return true;\n return parentEvents.every(e => !e.getContent()?.['via']);\n }\n\n /**\n * Sets the name of the tree space.\n * @param {string} name The new name for the space.\n * @returns {Promise<void>} Resolves when complete.\n */\n public async setName(name: string): Promise<void> {\n await this.client.sendStateEvent(this.roomId, EventType.RoomName, { name }, \"\");\n }\n\n /**\n * Invites a user to the tree space. They will be given the default Viewer\n * permission level unless specified elsewhere.\n * @param {string} userId The user ID to invite.\n * @param {boolean} andSubspaces True (default) to invite the user to all\n * directories/subspaces too, recursively.\n * @param {boolean} shareHistoryKeys True (default) to share encryption keys\n * with the invited user. This will allow them to decrypt the events (files)\n * in the tree. Keys will not be shared if the room is lacking appropriate\n * history visibility (by default, history visibility is \"shared\" in trees,\n * which is an appropriate visibility for these purposes).\n * @returns {Promise<void>} Resolves when complete.\n */\n public async invite(userId: string, andSubspaces = true, shareHistoryKeys = true): Promise<void> {\n const promises: Promise<void>[] = [this.retryInvite(userId)];\n if (andSubspaces) {\n promises.push(...this.getDirectories().map(d => d.invite(userId, andSubspaces, shareHistoryKeys)));\n }\n return Promise.all(promises).then(() => {\n // Note: key sharing is default oLine truncated
"/*\nCopyright 2015 - 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { MatrixEvent } from \"./event\";\nimport { Direction } from \"./event-timeline\";\n\n/**\n * @module models/event-context\n */\nexport class EventContext {\n private timeline: MatrixEvent[];\n private ourEventIndex = 0;\n private paginateTokens: Record<Direction, string | null> = {\n [Direction.Backward]: null,\n [Direction.Forward]: null,\n };\n\n /**\n * Construct a new EventContext\n *\n * An eventcontext is used for circumstances such as search results, when we\n * have a particular event of interest, and a bunch of events before and after\n * it.\n *\n * It also stores pagination tokens for going backwards and forwards in the\n * timeline.\n *\n * @param {MatrixEvent} ourEvent the event at the centre of this context\n *\n * @constructor\n */\n constructor(ourEvent: MatrixEvent) {\n this.timeline = [ourEvent];\n }\n\n /**\n * Get the main event of interest\n *\n * This is a convenience function for getTimeline()[getOurEventIndex()].\n *\n * @return {MatrixEvent} The event at the centre of this context.\n */\n public getEvent(): MatrixEvent {\n return this.timeline[this.ourEventIndex];\n }\n\n /**\n * Get the list of events in this context\n *\n * @return {Array} An array of MatrixEvents\n */\n public getTimeline(): MatrixEvent[] {\n return this.timeline;\n }\n\n /**\n * Get the index in the timeline of our event\n *\n * @return {Number}\n */\n public getOurEventIndex(): number {\n return this.ourEventIndex;\n }\n\n /**\n * Get a pagination token.\n *\n * @param {boolean} backwards true to get the pagination token for going\n * backwards in time\n * @return {string}\n */\n public getPaginateToken(backwards = false): string {\n return this.paginateTokens[backwards ? Direction.Backward : Direction.Forward];\n }\n\n /**\n * Set a pagination token.\n *\n * Generally this will be used only by the matrix js sdk.\n *\n * @param {string} token pagination token\n * @param {boolean} backwards true to set the pagination token for going\n * backwards in time\n */\n public setPaginateToken(token: string, backwards = false): void {\n this.paginateTokens[backwards ? Direction.Backward : Direction.Forward] = token;\n }\n\n /**\n * Add more events to the timeline\n *\n * @param {Array} events new events, in timeline order\n * @param {boolean} atStart true to insert new events at the start\n */\n public addEvents(events: MatrixEvent[], atStart = false): void {\n // TODO: should we share logic with Room.addEventsToTimeline?\n // Should Room even use EventContext?\n\n if (atStart) {\n this.timeline = events.concat(this.timeline);\n this.ourEventIndex += events.length;\n } else {\n this.timeline = this.timeline.concat(events);\n }\n }\n}\n",
@@ -313,9 +313,9 @@
"/*\nCopyright 2015 - 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/*\n * TODO:\n * This class mainly serves to take all the syncing logic out of client.js and\n * into a separate file. It's all very fluid, and this class gut wrenches a lot\n * of MatrixClient props (e.g. http). Given we want to support WebSockets as\n * an alternative syncing API, we may want to have a proper syncing interface\n * for HTTP and WS at some point.\n */\n\nimport { User } from \"./models/user\";\nimport { NotificationCountType, Room } from \"./models/room\";\nimport { Group } from \"./models/group\";\nimport * as utils from \"./utils\";\nimport { IDeferred } from \"./utils\";\nimport { Filter } from \"./filter\";\nimport { EventTimeline } from \"./models/event-timeline\";\nimport { PushProcessor } from \"./pushprocessor\";\nimport { logger } from './logger';\nimport { InvalidStoreError } from './errors';\nimport { IStoredClientOpts, MatrixClient, PendingEventOrdering } from \"./client\";\nimport { SyncState } from \"./sync.api\";\nimport {\n Category,\n IInvitedRoom,\n IInviteState,\n IJoinedRoom,\n ILeftRoom,\n IStateEvent,\n IRoomEvent,\n IStrippedState,\n ISyncResponse,\n ITimeline,\n IEphemeral,\n IMinimalEvent,\n} from \"./sync-accumulator\";\nimport { MatrixEvent } from \"./models/event\";\nimport { MatrixError } from \"./http-api\";\nimport { ISavedSync } from \"./store\";\nimport { EventType } from \"./@types/event\";\nimport { IPushRules } from \"./@types/PushRules\";\n\nconst DEBUG = true;\n\n// /sync requests allow you to set a timeout= but the request may continue\n// beyond that and wedge forever, so we need to track how long we are willing\n// to keep open the connection. This constant is *ADDED* to the timeout= value\n// to determine the max time we're willing to wait.\nconst BUFFER_PERIOD_MS = 80 * 1000;\n\n// Number of consecutive failed syncs that will lead to a syncState of ERROR as opposed\n// to RECONNECTING. This is needed to inform the client of server issues when the\n// keepAlive is successful but the server /sync fails.\nconst FAILED_SYNC_ERROR_THRESHOLD = 3;\n\nfunction getFilterName(userId: string, suffix?: string): string {\n // scope this on the user ID because people may login on many accounts\n // and they all need to be stored!\n return \"FILTER_SYNC_\" + userId + (suffix ? \"_\" + suffix : \"\");\n}\n\nfunction debuglog(...params) {\n if (!DEBUG) {\n return;\n }\n logger.log(...params);\n}\n\ninterface ISyncOptions {\n filterId?: string;\n hasSyncedBefore?: boolean;\n}\n\nexport interface ISyncStateData {\n error?: Error;\n oldSyncToken?: string;\n nextSyncToken?: string;\n catchingUp?: boolean;\n fromCache?: boolean;\n}\n\ninterface ISyncParams {\n filter?: string;\n timeout: number;\n since?: string;\n // eslint-disable-next-line camelcase\n full_state?: boolean;\n // eslint-disable-next-line camelcase\n set_presence?: \"offline\" | \"online\" | \"unavailable\";\n _cacheBuster?: string | number; // not part of the API itself\n}\n\n// http-api mangles an abort method onto its promises\ninterface IRequestPromise<T> extends Promise<T> {\n abort(): void;\n}\n\ntype WrappedRoom<T> = T & {\n room: Room;\n isBrandNewRoom: boolean;\n};\n\n/**\n * <b>Internal class - unstable.</b>\n * Construct an entity which is able to sync with a homeserver.\n * @constructor\n * @param {MatrixClient} client The matrix client instance to use.\n * @param {Object} opts Config options\n * @param {module:crypto=} opts.crypto Crypto manager\n * @param {Function=} opts.canResetEntireTimeline A function which is called\n * with a room ID and returns a boolean. It should return 'true' if the SDK can\n * SAFELY remove events from this room. It may not be safe to remove events if\n * there are other references to the timelines for this room.\n * Default: returns false.\n * @param {Boolean=} opts.disablePresence True to perform syncing without automatically\n * updating presence.\n */\nexport class SyncApi {\n private _peekRoom: Room = null;\n private currentSyncRequest: IRequestPromise<ISyncResponse> = null;\n private syncState: SyncState = null;\n private syncStateData: ISyncStateData = null; // additional data (eg. error object for failed sync)\n private catchingUp = false;\n private running = false;\n private keepAliveTimer: number = null;\n private Line truncated
"/*\nCopyright 2016 - 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/** @module timeline-window */\n\nimport { Direction, EventTimeline } from './models/event-timeline';\nimport { logger } from './logger';\nimport { MatrixClient } from \"./client\";\nimport { EventTimelineSet } from \"./models/event-timeline-set\";\nimport { MatrixEvent } from \"./models/event\";\n\n/**\n * @private\n */\nconst DEBUG = false;\n\n/**\n * @private\n */\nconst debuglog = DEBUG ? logger.log.bind(logger) : function() {};\n\n/**\n * the number of times we ask the server for more events before giving up\n *\n * @private\n */\nconst DEFAULT_PAGINATE_LOOP_LIMIT = 5;\n\ninterface IOpts {\n windowLimit?: number;\n}\n\nexport class TimelineWindow {\n private readonly windowLimit: number;\n // these will be TimelineIndex objects; they delineate the 'start' and\n // 'end' of the window.\n //\n // start.index is inclusive; end.index is exclusive.\n private start?: TimelineIndex = null;\n private end?: TimelineIndex = null;\n private eventCount = 0;\n\n /**\n * Construct a TimelineWindow.\n *\n * <p>This abstracts the separate timelines in a Matrix {@link\n * module:models/room|Room} into a single iterable thing. It keeps track of\n * the start and endpoints of the window, which can be advanced with the help\n * of pagination requests.\n *\n * <p>Before the window is useful, it must be initialised by calling {@link\n * module:timeline-window~TimelineWindow#load|load}.\n *\n * <p>Note that the window will not automatically extend itself when new events\n * are received from /sync; you should arrange to call {@link\n * module:timeline-window~TimelineWindow#paginate|paginate} on {@link\n * module:client~MatrixClient.event:\"Room.timeline\"|Room.timeline} events.\n *\n * @param {MatrixClient} client MatrixClient to be used for context/pagination\n * requests.\n *\n * @param {EventTimelineSet} timelineSet The timelineSet to track\n *\n * @param {Object} [opts] Configuration options for this window\n *\n * @param {number} [opts.windowLimit = 1000] maximum number of events to keep\n * in the window. If more events are retrieved via pagination requests,\n * excess events will be dropped from the other end of the window.\n *\n * @constructor\n */\n constructor(\n private readonly client: MatrixClient,\n private readonly timelineSet: EventTimelineSet,\n opts: IOpts = {},\n ) {\n this.windowLimit = opts.windowLimit || 1000;\n }\n\n /**\n * Initialise the window to point at a given event, or the live timeline\n *\n * @param {string} [initialEventId] If given, the window will contain the\n * given event\n * @param {number} [initialWindowSize = 20] Size of the initial window\n *\n * @return {Promise}\n */\n public load(initialEventId: string, initialWindowSize = 20): Promise<any> {\n // given an EventTimeline, find the event we were looking for, and initialise our\n // fields so that the event in question is in the middle of the window.\n const initFields = (timeline: EventTimeline) => {\n let eventIndex;\n\n const events = timeline.getEvents();\n\n if (!initialEventId) {\n // we were looking for the live timeline: initialise to the end\n eventIndex = events.length;\n } else {\n for (let i = 0; i < events.length; i++) {\n if (events[i].getId() == initialEventId) {\n eventIndex = i;\n break;\n }\n }\n\n if (eventIndex === undefined) {\n throw new Error(\"getEventTimeline result didn't include requested event\");\n }\n }\n\n const endIndex = Math.min(events.length,\n eventIndex + Math.ceil(initialWindowSize / 2));\n const startIndex = Math.max(0, endIndex - initialWindowSize);\n this.start = new TimelineIndex(timeline, startIndex - timeline.getBaseIndex());\n this.end = new TimelineIndex(timeline, endIndex - timeline.getBaseIndex());\n this.eventCount = endIndex - startIndex;\n };\n\n // We avoid delaying the resolution of the promiLine truncated
"/*\nCopyright 2015, 2016 OpenMarket Ltd\nCopyright 2019 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * This is an internal module.\n * @module utils\n */\n\nimport unhomoglyph from \"unhomoglyph\";\nimport promiseRetry from \"p-retry\";\nimport type NodeCrypto from \"crypto\";\n\n/**\n * Encode a dictionary of query parameters.\n * @param {Object} params A dict of key/values to encode e.g.\n * {\"foo\": \"bar\", \"baz\": \"taz\"}\n * @return {string} The encoded string e.g. foo=bar&baz=taz\n */\nexport function encodeParams(params: Record<string, string>): string {\n return new URLSearchParams(params).toString();\n}\n\nexport type QueryDict = Record<string, string | string[]>;\n\n/**\n * Decode a query string in `application/x-www-form-urlencoded` format.\n * @param {string} query A query string to decode e.g.\n * foo=bar&via=server1&server2\n * @return {Object} The decoded object, if any keys occurred multiple times\n * then the value will be an array of strings, else it will be an array.\n * This behaviour matches Node's qs.parse but is built on URLSearchParams\n * for native web compatibility\n */\nexport function decodeParams(query: string): QueryDict {\n const o: QueryDict = {};\n const params = new URLSearchParams(query);\n for (const key of params.keys()) {\n const val = params.getAll(key);\n o[key] = val.length === 1 ? val[0] : val;\n }\n return o;\n}\n\n/**\n * Encodes a URI according to a set of template variables. Variables will be\n * passed through encodeURIComponent.\n * @param {string} pathTemplate The path with template variables e.g. '/foo/$bar'.\n * @param {Object} variables The key/value pairs to replace the template\n * variables with. E.g. { \"$bar\": \"baz\" }.\n * @return {string} The result of replacing all template variables e.g. '/foo/baz'.\n */\nexport function encodeUri(pathTemplate: string,\n variables: Record<string, string>): string {\n for (const key in variables) {\n if (!variables.hasOwnProperty(key)) {\n continue;\n }\n pathTemplate = pathTemplate.replace(\n key, encodeURIComponent(variables[key]),\n );\n }\n return pathTemplate;\n}\n\n/**\n * The removeElement() method removes the first element in the array that\n * satisfies (returns true) the provided testing function.\n * @param {Array} array The array.\n * @param {Function} fn Function to execute on each value in the array, with the\n * function signature <code>fn(element, index, array)</code>. Return true to\n * remove this element and break.\n * @param {boolean} reverse True to search in reverse order.\n * @return {boolean} True if an element was removed.\n */\nexport function removeElement<T>(\n array: T[],\n fn: (t: T, i?: number, a?: T[]) => boolean,\n reverse?: boolean,\n) {\n let i;\n let removed;\n if (reverse) {\n for (i = array.length - 1; i >= 0; i--) {\n if (fn(array[i], i, array)) {\n removed = array[i];\n array.splice(i, 1);\n return removed;\n }\n }\n } else {\n for (i = 0; i < array.length; i++) {\n if (fn(array[i], i, array)) {\n removed = array[i];\n array.splice(i, 1);\n return removed;\n }\n }\n }\n return false;\n}\n\n/**\n * Checks if the given thing is a function.\n * @param {*} value The thing to check.\n * @return {boolean} True if it is a function.\n */\nexport function isFunction(value: any) {\n return Object.prototype.toString.call(value) === \"[object Function]\";\n}\n\n/**\n * Checks that the given object has the specified keys.\n * @param {Object} obj The object to check.\n * @param {string[]} keys The list of keys that 'obj' must have.\n * @throws If the object is missing keys.\n */\n// note using 'keys' here would shadow the 'keys' function defined above\nexport function checkObjectHasKeys(obj: object, keys: string[]) {\n for (let i = 0; i < keys.length; i++) {\n if (!obj.hasOwnProperty(keys[i])) {\n throw new Error(\"Missing required key: \" + keys[i]);\n }\n }\n}\n\n/**\n * Checks that the given object has no extra keys other than the specified ones.\n * @param {Object} obj The object to check.\n * @param {string[]} allowedKeys The list of allowed key names.\n * @throws If there are extra keys.\n */\nexport function checkObjectHasNoAddLine truncated
"/*\nCopyright 2015, 2016 OpenMarket Ltd\nCopyright 2017 New Vector Ltd\nCopyright 2019, 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * This is an internal module. See {@link createNewMatrixCall} for the public API.\n * @module webrtc/call\n */\n\nimport { logger } from '../logger';\nimport { EventEmitter } from 'events';\nimport * as utils from '../utils';\nimport { MatrixEvent } from '../models/event';\nimport { EventType } from '../@types/event';\nimport { RoomMember } from '../models/room-member';\nimport { randomString } from '../randomstring';\nimport {\n MCallReplacesEvent,\n MCallAnswer,\n MCallInviteNegotiate,\n CallCapabilities,\n SDPStreamMetadataPurpose,\n SDPStreamMetadata,\n SDPStreamMetadataKey,\n MCallSDPStreamMetadataChanged,\n MCallSelectAnswer,\n MCAllAssertedIdentity,\n MCallCandidates,\n MCallBase,\n MCallHangupReject,\n} from './callEventTypes';\nimport { CallFeed } from './callFeed';\nimport { MatrixClient } from \"../client\";\nimport { ISendEventResponse } from \"../@types/requests\";\n\n// events: hangup, error(err), replaced(call), state(state, oldState)\n\n/**\n * Fires whenever an error occurs when call.js encounters an issue with setting up the call.\n * <p>\n * The error given will have a code equal to either `MatrixCall.ERR_LOCAL_OFFER_FAILED` or\n * `MatrixCall.ERR_NO_USER_MEDIA`. `ERR_LOCAL_OFFER_FAILED` is emitted when the local client\n * fails to create an offer. `ERR_NO_USER_MEDIA` is emitted when the user has denied access\n * to their audio/video hardware.\n *\n * @event module:webrtc/call~MatrixCall#\"error\"\n * @param {Error} err The error raised by MatrixCall.\n * @example\n * matrixCall.on(\"error\", function(err){\n * console.error(err.code, err);\n * });\n */\n\ninterface CallOpts {\n roomId?: string;\n invitee?: string;\n client?: any; // Fix when client is TSified\n forceTURN?: boolean;\n turnServers?: Array<TurnServer>;\n}\n\ninterface TurnServer {\n urls: Array<string>;\n username?: string;\n password?: string;\n ttl?: number;\n}\n\ninterface AssertedIdentity {\n id: string;\n displayName: string;\n}\n\nexport enum CallState {\n Fledgling = 'fledgling',\n InviteSent = 'invite_sent',\n WaitLocalMedia = 'wait_local_media',\n CreateOffer = 'create_offer',\n CreateAnswer = 'create_answer',\n Connecting = 'connecting',\n Connected = 'connected',\n Ringing = 'ringing',\n Ended = 'ended',\n}\n\nexport enum CallType {\n Voice = 'voice',\n Video = 'video',\n}\n\nexport enum CallDirection {\n Inbound = 'inbound',\n Outbound = 'outbound',\n}\n\nexport enum CallParty {\n Local = 'local',\n Remote = 'remote',\n}\n\nexport enum CallEvent {\n Hangup = 'hangup',\n State = 'state',\n Error = 'error',\n Replaced = 'replaced',\n\n // The value of isLocalOnHold() has changed\n LocalHoldUnhold = 'local_hold_unhold',\n // The value of isRemoteOnHold() has changed\n RemoteHoldUnhold = 'remote_hold_unhold',\n // backwards compat alias for LocalHoldUnhold: remove in a major version bump\n HoldUnhold = 'hold_unhold',\n // Feeds have changed\n FeedsChanged = 'feeds_changed',\n\n AssertedIdentityChanged = 'asserted_identity_changed',\n}\n\nexport enum CallErrorCode {\n /** The user chose to end the call */\n UserHangup = 'user_hangup',\n\n /** An error code when the local client failed to create an offer. */\n LocalOfferFailed = 'local_offer_failed',\n /**\n * An error code when there is no local mic/camera to use. This may be because\n * the hardware isn't plugged in, or the user has explicitly denied access.\n */\n NoUserMedia = 'no_user_media',\n\n /**\n * Error code used when a call event failed to send\n * because unknown devices were present in the room\n */\n UnknownDevices = 'unknown_devices',\n\n /**\n * Error code used when we fail to send the invite\n * for some reason other than there being unknown devices\n */\n SendInvite = 'send_invite',\n\n /**\n * An answer could not be created\n */\n CreateAnswer = 'create_answer',\n\n /**\n * Error code used when we fail to send the answer\n * for some reason other than there being unknown devices\n */\n SendAnswer = 'send_answer',\n\n /**\n * The session description from the other side could not be set\n */\n SetRemoteDescripLine truncated
"/*\nCopyright 2015, 2016 OpenMarket Ltd\nCopyright 2017 New Vector Ltd\nCopyright 2019, 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * This is an internal module. See {@link createNewMatrixCall} for the public API.\n * @module webrtc/call\n */\n\nimport { logger } from '../logger';\nimport { EventEmitter } from 'events';\nimport * as utils from '../utils';\nimport { MatrixEvent } from '../models/event';\nimport { EventType } from '../@types/event';\nimport { RoomMember } from '../models/room-member';\nimport { randomString } from '../randomstring';\nimport {\n MCallReplacesEvent,\n MCallAnswer,\n MCallInviteNegotiate,\n CallCapabilities,\n SDPStreamMetadataPurpose,\n SDPStreamMetadata,\n SDPStreamMetadataKey,\n MCallSDPStreamMetadataChanged,\n MCallSelectAnswer,\n MCAllAssertedIdentity,\n MCallCandidates,\n MCallBase,\n MCallHangupReject,\n} from './callEventTypes';\nimport { CallFeed } from './callFeed';\nimport { MatrixClient } from \"../client\";\nimport { ISendEventResponse } from \"../@types/requests\";\n\n// events: hangup, error(err), replaced(call), state(state, oldState)\n\n/**\n * Fires whenever an error occurs when call.js encounters an issue with setting up the call.\n * <p>\n * The error given will have a code equal to either `MatrixCall.ERR_LOCAL_OFFER_FAILED` or\n * `MatrixCall.ERR_NO_USER_MEDIA`. `ERR_LOCAL_OFFER_FAILED` is emitted when the local client\n * fails to create an offer. `ERR_NO_USER_MEDIA` is emitted when the user has denied access\n * to their audio/video hardware.\n *\n * @event module:webrtc/call~MatrixCall#\"error\"\n * @param {Error} err The error raised by MatrixCall.\n * @example\n * matrixCall.on(\"error\", function(err){\n * console.error(err.code, err);\n * });\n */\n\ninterface CallOpts {\n roomId?: string;\n invitee?: string;\n client?: any; // Fix when client is TSified\n forceTURN?: boolean;\n turnServers?: Array<TurnServer>;\n}\n\ninterface TurnServer {\n urls: Array<string>;\n username?: string;\n password?: string;\n ttl?: number;\n}\n\ninterface AssertedIdentity {\n id: string;\n displayName: string;\n}\n\nexport enum CallState {\n Fledgling = 'fledgling',\n InviteSent = 'invite_sent',\n WaitLocalMedia = 'wait_local_media',\n CreateOffer = 'create_offer',\n CreateAnswer = 'create_answer',\n Connecting = 'connecting',\n Connected = 'connected',\n Ringing = 'ringing',\n Ended = 'ended',\n}\n\nexport enum CallType {\n Voice = 'voice',\n Video = 'video',\n}\n\nexport enum CallDirection {\n Inbound = 'inbound',\n Outbound = 'outbound',\n}\n\nexport enum CallParty {\n Local = 'local',\n Remote = 'remote',\n}\n\nexport enum CallEvent {\n Hangup = 'hangup',\n State = 'state',\n Error = 'error',\n Replaced = 'replaced',\n\n // The value of isLocalOnHold() has changed\n LocalHoldUnhold = 'local_hold_unhold',\n // The value of isRemoteOnHold() has changed\n RemoteHoldUnhold = 'remote_hold_unhold',\n // backwards compat alias for LocalHoldUnhold: remove in a major version bump\n HoldUnhold = 'hold_unhold',\n // Feeds have changed\n FeedsChanged = 'feeds_changed',\n\n AssertedIdentityChanged = 'asserted_identity_changed',\n}\n\nexport enum CallErrorCode {\n /** The user chose to end the call */\n UserHangup = 'user_hangup',\n\n /** An error code when the local client failed to create an offer. */\n LocalOfferFailed = 'local_offer_failed',\n /**\n * An error code when there is no local mic/camera to use. This may be because\n * the hardware isn't plugged in, or the user has explicitly denied access.\n */\n NoUserMedia = 'no_user_media',\n\n /**\n * Error code used when a call event failed to send\n * because unknown devices were present in the room\n */\n UnknownDevices = 'unknown_devices',\n\n /**\n * Error code used when we fail to send the invite\n * for some reason other than there being unknown devices\n */\n SendInvite = 'send_invite',\n\n /**\n * An answer could not be created\n */\n CreateAnswer = 'create_answer',\n\n /**\n * Error code used when we fail to send the answer\n * for some reason other than there being unknown devices\n */\n SendAnswer = 'send_answer',\n\n /**\n * The session description from the other side could not be set\n */\n SetRemoteDescripLine truncated
"/*\nCopyright 2020 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { MatrixEvent } from '../models/event';\nimport { logger } from '../logger';\nimport { createNewMatrixCall, MatrixCall, CallErrorCode, CallState, CallDirection } from './call';\nimport { EventType } from '../@types/event';\nimport { MatrixClient } from '../client';\nimport { MCallAnswer, MCallHangupReject } from \"./callEventTypes\";\n\n// Don't ring unless we'd be ringing for at least 3 seconds: the user needs some\n// time to press the 'accept' button\nconst RING_GRACE_PERIOD = 3000;\n\nexport class CallEventHandler {\n client: MatrixClient;\n calls: Map<string, MatrixCall>;\n callEventBuffer: MatrixEvent[];\n candidateEventsByCall: Map<string, Array<MatrixEvent>>;\n\n constructor(client: MatrixClient) {\n this.client = client;\n this.calls = new Map<string, MatrixCall>();\n // The sync code always emits one event at a time, so it will patiently\n // wait for us to finish processing a call invite before delivering the\n // next event, even if that next event is a hangup. We therefore accumulate\n // all our call events and then process them on the 'sync' event, ie.\n // each time a sync has completed. This way, we can avoid emitting incoming\n // call events if we get both the invite and answer/hangup in the same sync.\n // This happens quite often, eg. replaying sync from storage, catchup sync\n // after loading and after we've been offline for a bit.\n this.callEventBuffer = [];\n this.candidateEventsByCall = new Map<string, Array<MatrixEvent>>();\n }\n\n public start() {\n this.client.on(\"sync\", this.evaluateEventBuffer);\n this.client.on(\"Room.timeline\", this.onRoomTimeline);\n }\n\n public stop() {\n this.client.removeListener(\"sync\", this.evaluateEventBuffer);\n this.client.removeListener(\"Room.timeline\", this.onRoomTimeline);\n }\n\n private evaluateEventBuffer = async () => {\n if (this.client.getSyncState() === \"SYNCING\") {\n await Promise.all(this.callEventBuffer.map(event => {\n this.client.decryptEventIfNeeded(event);\n }));\n\n const ignoreCallIds = new Set<String>();\n // inspect the buffer and mark all calls which have been answered\n // or hung up before passing them to the call event handler.\n for (const ev of this.callEventBuffer) {\n if (ev.getType() === EventType.CallAnswer ||\n ev.getType() === EventType.CallHangup) {\n ignoreCallIds.add(ev.getContent().call_id);\n }\n }\n // now loop through the buffer chronologically and inject them\n for (const e of this.callEventBuffer) {\n if (\n e.getType() === EventType.CallInvite &&\n ignoreCallIds.has(e.getContent().call_id)\n ) {\n // This call has previously been answered or hung up: ignore it\n continue;\n }\n try {\n await this.handleCallEvent(e);\n } catch (e) {\n logger.error(\"Caught exception handling call event\", e);\n }\n }\n this.callEventBuffer = [];\n }\n };\n\n private onRoomTimeline = (event: MatrixEvent) => {\n this.client.decryptEventIfNeeded(event);\n // any call events or ones that might be once they're decrypted\n if (this.eventIsACall(event) || event.isBeingDecrypted()) {\n // queue up for processing once all events from this sync have been\n // processed (see above).\n this.callEventBuffer.push(event);\n }\n\n if (event.isBeingDecrypted() || event.isDecryptionFailure()) {\n // add an event listener for once the event is decrypted.\n event.once(\"Event.decrypted\", async () => {\n if (!this.eventIsACall(event)) return;\n\n if (this.callEventBuffer.includes(event)) {\n // we were waiting for that event to decrypt, so recheck the buffer\n this.evaluateEventBuffer();\n } else {\n // This one wasn't buffered so just run the event handler for it\n Line truncated
"// allow non-camelcase as these are events type that go onto the wire\n/* eslint-disable camelcase */\n\nimport { CallErrorCode } from \"./call\";\n\n// TODO: Change to \"sdp_stream_metadata\" when MSC3077 is merged\nexport const SDPStreamMetadataKey = \"org.matrix.msc3077.sdp_stream_metadata\";\n\nexport enum SDPStreamMetadataPurpose {\n Usermedia = \"m.usermedia\",\n Screenshare = \"m.screenshare\",\n}\n\nexport interface SDPStreamMetadataObject {\n purpose: SDPStreamMetadataPurpose;\n audio_muted: boolean;\n video_muted: boolean;\n}\n\nexport interface SDPStreamMetadata {\n [key: string]: SDPStreamMetadataObject;\n}\n\nexport interface CallCapabilities {\n 'm.call.transferee': boolean;\n 'm.call.dtmf': boolean;\n}\n\nexport interface CallReplacesTarget {\n id: string;\n display_name: string;\n avatar_url: string;\n}\n\nexport interface MCallBase {\n call_id: string;\n version: string | number;\n party_id?: string;\n}\n\nexport interface MCallAnswer extends MCallBase {\n answer: RTCSessionDescription;\n capabilities?: CallCapabilities;\n [SDPStreamMetadataKey]: SDPStreamMetadata;\n}\n\nexport interface MCallSelectAnswer extends MCallBase {\n selected_party_id: string;\n}\n\nexport interface MCallInviteNegotiate extends MCallBase {\n offer: RTCSessionDescription;\n description: RTCSessionDescription;\n lifetime: number;\n capabilities?: CallCapabilities;\n invitee?: string;\n [SDPStreamMetadataKey]: SDPStreamMetadata;\n}\n\nexport interface MCallSDPStreamMetadataChanged extends MCallBase {\n [SDPStreamMetadataKey]: SDPStreamMetadata;\n}\n\nexport interface MCallReplacesEvent extends MCallBase {\n replacement_id: string;\n target_user: CallReplacesTarget;\n create_call: string;\n await_call: string;\n target_room: string;\n}\n\nexport interface MCAllAssertedIdentity extends MCallBase {\n asserted_identity: {\n id: string;\n display_name: string;\n avatar_url: string;\n };\n}\n\nexport interface MCallCandidates extends MCallBase {\n candidates: RTCIceCandidate[];\n}\n\nexport interface MCallHangupReject extends MCallBase {\n reason?: CallErrorCode;\n}\n\n/* eslint-enable camelcase */\n",
"/*\nCopyright 2021 Šimon Brandner <simon.bra.ag@gmail.com>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport EventEmitter from \"events\";\nimport { SDPStreamMetadataPurpose } from \"./callEventTypes\";\nimport { MatrixClient } from \"../client\";\nimport { RoomMember } from \"../models/room-member\";\n\nconst POLLING_INTERVAL = 250; // ms\nconst SPEAKING_THRESHOLD = -60; // dB\n\nexport enum CallFeedEvent {\n NewStream = \"new_stream\",\n MuteStateChanged = \"mute_state_changed\",\n VolumeChanged = \"volume_changed\",\n Speaking = \"speaking\",\n}\n\nexport class CallFeed extends EventEmitter {\n private measuringVolumeActivity = false;\n private audioContext: AudioContext;\n private analyser: AnalyserNode;\n private frequencyBinCount: Float32Array;\n private speakingThreshold = SPEAKING_THRESHOLD;\n private speaking = false;\n\n constructor(\n public stream: MediaStream,\n public userId: string,\n public purpose: SDPStreamMetadataPurpose,\n private client: MatrixClient,\n private roomId: string,\n private audioMuted: boolean,\n private videoMuted: boolean,\n ) {\n super();\n\n if (this.hasAudioTrack) {\n this.initVolumeMeasuring();\n }\n }\n\n private get hasAudioTrack(): boolean {\n return this.stream.getAudioTracks().length > 0;\n }\n\n private initVolumeMeasuring(): void {\n const AudioContext = window.AudioContext || window.webkitAudioContext;\n if (!this.hasAudioTrack || !AudioContext) return;\n\n this.audioContext = new AudioContext();\n\n this.analyser = this.audioContext.createAnalyser();\n this.analyser.fftSize = 512;\n this.analyser.smoothingTimeConstant = 0.1;\n\n const mediaStreamAudioSourceNode = this.audioContext.createMediaStreamSource(this.stream);\n mediaStreamAudioSourceNode.connect(this.analyser);\n\n this.frequencyBinCount = new Float32Array(this.analyser.frequencyBinCount);\n }\n\n /**\n * Returns callRoom member\n * @returns member of the callRoom\n */\n public getMember(): RoomMember {\n const callRoom = this.client.getRoom(this.roomId);\n return callRoom.getMember(this.userId);\n }\n\n /**\n * Returns true if CallFeed is local, otherwise returns false\n * @returns {boolean} is local?\n */\n public isLocal(): boolean {\n return this.userId === this.client.getUserId();\n }\n\n /**\n * Returns true if audio is muted or if there are no audio\n * tracks, otherwise returns false\n * @returns {boolean} is audio muted?\n */\n public isAudioMuted(): boolean {\n return this.stream.getAudioTracks().length === 0 || this.audioMuted;\n }\n\n /**\n * Returns true video is muted or if there are no video\n * tracks, otherwise returns false\n * @returns {boolean} is video muted?\n */\n public isVideoMuted(): boolean {\n // We assume only one video track\n return this.stream.getVideoTracks().length === 0 || this.videoMuted;\n }\n\n /**\n * Replaces the current MediaStream with a new one.\n * This method should be only used by MatrixCall.\n * @param newStream new stream with which to replace the current one\n */\n public setNewStream(newStream: MediaStream): void {\n this.stream = newStream;\n this.emit(CallFeedEvent.NewStream, this.stream);\n\n if (this.hasAudioTrack) {\n this.initVolumeMeasuring();\n } else {\n this.measureVolumeActivity(false);\n }\n }\n\n /**\n * Set feed's internal audio mute state\n * @param muted is the feed's audio muted?\n */\n public setAudioMuted(muted: boolean): void {\n this.audioMuted = muted;\n this.emit(CallFeedEvent.MuteStateChanged, this.audioMuted, this.videoMuted);\n }\n\n /**\n * Set feed's internal video mute state\n * @param muted is the feed's video muted?\n */\n public setVideoMuted(muted: boolean): void {\n this.videoMuted = muted;\n this.emit(CallFeedEvent.MuteStateChanged, this.audioMuted, this.videoMuted);\n }\n\n /**\n * Starts emitting volume_changed events where the emitter value is in decibels\n * @param enabled emit volume changes\n */\n public measureVolumeActivity(enabled: boolean): void {\n if (enabled) {\n if (!this.audioContext ||Line truncated
"/*\nCopyright 2021 Šimon Brandner <simon.bra.ag@gmail.com>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport EventEmitter from \"events\";\nimport { SDPStreamMetadataPurpose } from \"./callEventTypes\";\nimport { MatrixClient } from \"../client\";\nimport { RoomMember } from \"../models/room-member\";\n\nconst POLLING_INTERVAL = 250; // ms\nconst SPEAKING_THRESHOLD = -60; // dB\n\nexport enum CallFeedEvent {\n NewStream = \"new_stream\",\n MuteStateChanged = \"mute_state_changed\",\n VolumeChanged = \"volume_changed\",\n Speaking = \"speaking\",\n}\n\nexport class CallFeed extends EventEmitter {\n private measuringVolumeActivity = false;\n private audioContext: AudioContext;\n private analyser: AnalyserNode;\n private frequencyBinCount: Float32Array;\n private speakingThreshold = SPEAKING_THRESHOLD;\n private speaking = false;\n private volumeLooperTimeout: number;\n\n constructor(\n public stream: MediaStream,\n public userId: string,\n public purpose: SDPStreamMetadataPurpose,\n private client: MatrixClient,\n private roomId: string,\n private audioMuted: boolean,\n private videoMuted: boolean,\n ) {\n super();\n\n if (this.hasAudioTrack) {\n this.initVolumeMeasuring();\n }\n }\n\n private get hasAudioTrack(): boolean {\n return this.stream.getAudioTracks().length > 0;\n }\n\n private initVolumeMeasuring(): void {\n const AudioContext = window.AudioContext || window.webkitAudioContext;\n if (!this.hasAudioTrack || !AudioContext) return;\n\n this.audioContext = new AudioContext();\n\n this.analyser = this.audioContext.createAnalyser();\n this.analyser.fftSize = 512;\n this.analyser.smoothingTimeConstant = 0.1;\n\n const mediaStreamAudioSourceNode = this.audioContext.createMediaStreamSource(this.stream);\n mediaStreamAudioSourceNode.connect(this.analyser);\n\n this.frequencyBinCount = new Float32Array(this.analyser.frequencyBinCount);\n }\n\n /**\n * Returns callRoom member\n * @returns member of the callRoom\n */\n public getMember(): RoomMember {\n const callRoom = this.client.getRoom(this.roomId);\n return callRoom.getMember(this.userId);\n }\n\n /**\n * Returns true if CallFeed is local, otherwise returns false\n * @returns {boolean} is local?\n */\n public isLocal(): boolean {\n return this.userId === this.client.getUserId();\n }\n\n /**\n * Returns true if audio is muted or if there are no audio\n * tracks, otherwise returns false\n * @returns {boolean} is audio muted?\n */\n public isAudioMuted(): boolean {\n return this.stream.getAudioTracks().length === 0 || this.audioMuted;\n }\n\n /**\n * Returns true video is muted or if there are no video\n * tracks, otherwise returns false\n * @returns {boolean} is video muted?\n */\n public isVideoMuted(): boolean {\n // We assume only one video track\n return this.stream.getVideoTracks().length === 0 || this.videoMuted;\n }\n\n /**\n * Replaces the current MediaStream with a new one.\n * This method should be only used by MatrixCall.\n * @param newStream new stream with which to replace the current one\n */\n public setNewStream(newStream: MediaStream): void {\n this.stream = newStream;\n this.emit(CallFeedEvent.NewStream, this.stream);\n\n if (this.hasAudioTrack) {\n this.initVolumeMeasuring();\n } else {\n this.measureVolumeActivity(false);\n }\n }\n\n /**\n * Set feed's internal audio mute state\n * @param muted is the feed's audio muted?\n */\n public setAudioMuted(muted: boolean): void {\n this.audioMuted = muted;\n this.emit(CallFeedEvent.MuteStateChanged, this.audioMuted, this.videoMuted);\n }\n\n /**\n * Set feed's internal video mute state\n * @param muted is the feed's video muted?\n */\n public setVideoMuted(muted: boolean): void {\n this.videoMuted = muted;\n this.emit(CallFeedEvent.MuteStateChanged, this.audioMuted, this.videoMuted);\n }\n\n /**\n * Starts emitting volume_changed events where the emitter value is in decibels\n * @param enabled emit volume changes\n */\n public measureVolumeActivity(enabled: boolean): void {\n if (enabledLine truncated
]
}
+48 -21
View File
@@ -22,6 +22,8 @@ const CONF_ROOM = "me.robertlong.conf";
const CONF_PARTICIPANT = "me.robertlong.conf.participant";
const PARTICIPANT_TIMEOUT = 1000 * 15;
const SPEAKING_THRESHOLD = -80;
const ACTIVE_SPEAKER_INTERVAL = 1000;
const ACTIVE_SPEAKER_SAMPLES = 8;
function waitForSync(client) {
return new Promise((resolve, reject) => {
@@ -172,6 +174,7 @@ export class ConferenceCallManager extends EventEmitter {
this.localVideoStream = null;
this.localParticipant = null;
this.localCallFeed = null;
this.audioMuted = false;
this.videoMuted = false;
@@ -261,6 +264,30 @@ export class ConferenceCallManager extends EventEmitter {
this.participants.push(this.localParticipant);
this.emit("debugstate", userId, null, "you");
this.localCallFeed = new matrixcs.CallFeed(
stream,
this.localParticipant.userId,
"m.usermedia",
this.client,
this.room.roomId,
this.audioMuted,
this.videoMuted
);
this.localCallFeed.on("mute_state_changed", () =>
this._onCallFeedMuteStateChanged(
this.localParticipant,
this.localCallFeed
)
);
this.localCallFeed.setSpeakingThreshold(SPEAKING_THRESHOLD);
this.localCallFeed.measureVolumeActivity(true);
this.localCallFeed.on("speaking", (speaking) => {
this._onCallFeedSpeaking(this.localParticipant, speaking);
});
this.localCallFeed.on("volume_changed", (maxVolume) =>
this._onCallFeedVolumeChange(this.localParticipant, maxVolume)
);
// Announce to the other room members that we have entered the room.
// Continue doing so every PARTICIPANT_TIMEOUT ms
this._updateMemberParticipantState();
@@ -285,7 +312,6 @@ export class ConferenceCallManager extends EventEmitter {
this.emit("entered");
this.emit("participants_changed");
console.log("enter");
this._onActiveSpeakerLoop();
}
@@ -318,6 +344,8 @@ export class ConferenceCallManager extends EventEmitter {
this.client.stopLocalMediaStream();
this.localVideoStream = null;
this.localCallFeed.dispose();
this.localCallFeed = null;
this.room = null;
this.entered = false;
@@ -329,6 +357,7 @@ export class ConferenceCallManager extends EventEmitter {
this.videoMuted = false;
clearTimeout(this._memberParticipantStateTimeout);
clearTimeout(this._activeSpeakerLoopTimeout);
this._speakerMap.clear();
this.emit("participants_changed");
this.emit("left");
@@ -349,8 +378,8 @@ export class ConferenceCallManager extends EventEmitter {
setAudioMuted(muted) {
this.audioMuted = muted;
if (this.localParticipant) {
this.localParticipant.audioMuted = muted;
if (this.localCallFeed) {
this.localCallFeed.setAudioMuted(muted);
}
const localStream = this.localVideoStream;
@@ -381,8 +410,8 @@ export class ConferenceCallManager extends EventEmitter {
setVideoMuted(muted) {
this.videoMuted = muted;
if (this.localParticipant) {
this.localParticipant.videoMuted = muted;
if (this.localCallFeed) {
this.localCallFeed.setVideoMuted(muted);
}
const localStream = this.localVideoStream;
@@ -734,12 +763,10 @@ export class ConferenceCallManager extends EventEmitter {
participant.videoMuted = feed.isVideoMuted();
if (participant.audioMuted) {
this._speakerMap.set(participant.userId, [
-Infinity,
-Infinity,
-Infinity,
-Infinity,
]);
this._speakerMap.set(
participant.userId,
Array(ACTIVE_SPEAKER_SAMPLES).fill(-Infinity)
);
}
this.emit("participants_changed");
@@ -752,12 +779,10 @@ export class ConferenceCallManager extends EventEmitter {
_onCallFeedVolumeChange = (participant, maxVolume) => {
if (!this._speakerMap.has(participant.userId)) {
this._speakerMap.set(participant.userId, [
-Infinity,
-Infinity,
-Infinity,
-Infinity,
]);
this._speakerMap.set(
participant.userId,
Array(ACTIVE_SPEAKER_SAMPLES).fill(-Infinity)
);
}
const volumeArr = this._speakerMap.get(participant.userId);
@@ -777,7 +802,7 @@ export class ConferenceCallManager extends EventEmitter {
total += Math.max(volume, SPEAKING_THRESHOLD);
}
const avg = total / 4;
const avg = total / ACTIVE_SPEAKER_SAMPLES;
if (!topAvg) {
topAvg = avg;
@@ -788,7 +813,7 @@ export class ConferenceCallManager extends EventEmitter {
}
}
if (activeSpeakerId) {
if (activeSpeakerId && topAvg > SPEAKING_THRESHOLD) {
const nextActiveSpeaker = this.participants.find(
(p) => p.userId === activeSpeakerId
);
@@ -797,12 +822,14 @@ export class ConferenceCallManager extends EventEmitter {
this.activeSpeaker.activeSpeaker = false;
nextActiveSpeaker.activeSpeaker = true;
this.activeSpeaker = nextActiveSpeaker;
console.log("activeSpeakerChanged", this.activeSpeaker);
this.emit("participants_changed");
}
}
this._activeSpeakerLoopTimeout = setTimeout(this._onActiveSpeakerLoop, 250);
this._activeSpeakerLoopTimeout = setTimeout(
this._onActiveSpeakerLoop,
ACTIVE_SPEAKER_INTERVAL
);
};
_onCallReplaced = (participant, call, newCall) => {
-1
View File
@@ -81,7 +81,6 @@ export function Room({ manager }) {
const [layout, setLayout] = useState("gallery");
const toggleLayout = useCallback(() => {
console.log(layout);
setLayout(layout === "spotlight" ? "gallery" : "spotlight");
}, [layout]);