matrix-js-sdk 21.2.0 → 22.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (350) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +8 -2
  3. package/dist/browser-matrix.js +809 -600
  4. package/dist/browser-matrix.js.map +80 -80
  5. package/dist/browser-matrix.min.js +5 -5
  6. package/dist/browser-matrix.min.js.map +1 -1
  7. package/git-revision.txt +1 -1
  8. package/lib/@types/PushRules.d.ts +9 -9
  9. package/lib/@types/PushRules.d.ts.map +1 -1
  10. package/lib/@types/auth.d.ts +1 -1
  11. package/lib/@types/auth.d.ts.map +1 -1
  12. package/lib/@types/beacon.d.ts +3 -3
  13. package/lib/@types/beacon.d.ts.map +1 -1
  14. package/lib/@types/crypto.d.ts +1 -1
  15. package/lib/@types/crypto.d.ts.map +1 -1
  16. package/lib/@types/location.d.ts +11 -11
  17. package/lib/@types/location.d.ts.map +1 -1
  18. package/lib/@types/partials.d.ts +2 -2
  19. package/lib/@types/partials.d.ts.map +1 -1
  20. package/lib/@types/read_receipts.d.ts +29 -0
  21. package/lib/@types/read_receipts.d.ts.map +1 -1
  22. package/lib/@types/read_receipts.js +3 -1
  23. package/lib/@types/read_receipts.js.map +1 -1
  24. package/lib/@types/search.d.ts +1 -1
  25. package/lib/@types/search.d.ts.map +1 -1
  26. package/lib/@types/topic.d.ts +3 -3
  27. package/lib/@types/topic.d.ts.map +1 -1
  28. package/lib/@types/uia.d.ts +2 -2
  29. package/lib/@types/uia.d.ts.map +1 -1
  30. package/lib/ReEmitter.d.ts.map +1 -1
  31. package/lib/ReEmitter.js.map +1 -1
  32. package/lib/ToDeviceMessageQueue.d.ts.map +1 -1
  33. package/lib/ToDeviceMessageQueue.js.map +1 -1
  34. package/lib/client.d.ts +12 -12
  35. package/lib/client.d.ts.map +1 -1
  36. package/lib/client.js +7 -9
  37. package/lib/client.js.map +1 -1
  38. package/lib/content-helpers.d.ts +13 -37
  39. package/lib/content-helpers.d.ts.map +1 -1
  40. package/lib/content-helpers.js.map +1 -1
  41. package/lib/crypto/CrossSigning.d.ts +1 -1
  42. package/lib/crypto/CrossSigning.d.ts.map +1 -1
  43. package/lib/crypto/CrossSigning.js.map +1 -1
  44. package/lib/crypto/DeviceList.d.ts +2 -2
  45. package/lib/crypto/DeviceList.d.ts.map +1 -1
  46. package/lib/crypto/DeviceList.js.map +1 -1
  47. package/lib/crypto/EncryptionSetup.d.ts.map +1 -1
  48. package/lib/crypto/EncryptionSetup.js.map +1 -1
  49. package/lib/crypto/OlmDevice.d.ts.map +1 -1
  50. package/lib/crypto/OlmDevice.js +6 -5
  51. package/lib/crypto/OlmDevice.js.map +1 -1
  52. package/lib/crypto/OutgoingRoomKeyRequestManager.js.map +1 -1
  53. package/lib/crypto/RoomList.d.ts.map +1 -1
  54. package/lib/crypto/RoomList.js.map +1 -1
  55. package/lib/crypto/SecretStorage.d.ts +2 -2
  56. package/lib/crypto/SecretStorage.d.ts.map +1 -1
  57. package/lib/crypto/SecretStorage.js.map +1 -1
  58. package/lib/crypto/algorithms/base.d.ts +1 -1
  59. package/lib/crypto/algorithms/base.d.ts.map +1 -1
  60. package/lib/crypto/algorithms/base.js.map +1 -1
  61. package/lib/crypto/algorithms/megolm.js +8 -8
  62. package/lib/crypto/algorithms/megolm.js.map +1 -1
  63. package/lib/crypto/backup.d.ts +4 -4
  64. package/lib/crypto/backup.d.ts.map +1 -1
  65. package/lib/crypto/backup.js.map +1 -1
  66. package/lib/crypto/dehydration.d.ts.map +1 -1
  67. package/lib/crypto/dehydration.js.map +1 -1
  68. package/lib/crypto/deviceinfo.d.ts.map +1 -1
  69. package/lib/crypto/deviceinfo.js.map +1 -1
  70. package/lib/crypto/index.d.ts +2 -2
  71. package/lib/crypto/index.d.ts.map +1 -1
  72. package/lib/crypto/index.js +1 -1
  73. package/lib/crypto/index.js.map +1 -1
  74. package/lib/crypto/olmlib.d.ts.map +1 -1
  75. package/lib/crypto/olmlib.js.map +1 -1
  76. package/lib/crypto/store/base.d.ts +1 -1
  77. package/lib/crypto/store/base.d.ts.map +1 -1
  78. package/lib/crypto/store/indexeddb-crypto-store-backend.d.ts.map +1 -1
  79. package/lib/crypto/store/indexeddb-crypto-store-backend.js.map +1 -1
  80. package/lib/crypto/store/indexeddb-crypto-store.d.ts.map +1 -1
  81. package/lib/crypto/store/indexeddb-crypto-store.js.map +1 -1
  82. package/lib/crypto/store/localStorage-crypto-store.d.ts.map +1 -1
  83. package/lib/crypto/store/localStorage-crypto-store.js.map +1 -1
  84. package/lib/crypto/store/memory-crypto-store.d.ts.map +1 -1
  85. package/lib/crypto/store/memory-crypto-store.js.map +1 -1
  86. package/lib/crypto/verification/Base.d.ts +2 -2
  87. package/lib/crypto/verification/Base.d.ts.map +1 -1
  88. package/lib/crypto/verification/Base.js.map +1 -1
  89. package/lib/crypto/verification/QRCode.d.ts +1 -1
  90. package/lib/crypto/verification/QRCode.d.ts.map +1 -1
  91. package/lib/crypto/verification/QRCode.js +1 -1
  92. package/lib/crypto/verification/QRCode.js.map +1 -1
  93. package/lib/crypto/verification/SAS.d.ts +2 -2
  94. package/lib/crypto/verification/SAS.d.ts.map +1 -1
  95. package/lib/crypto/verification/SAS.js.map +1 -1
  96. package/lib/crypto/verification/request/InRoomChannel.js.map +1 -1
  97. package/lib/crypto/verification/request/ToDeviceChannel.d.ts +1 -1
  98. package/lib/crypto/verification/request/ToDeviceChannel.d.ts.map +1 -1
  99. package/lib/crypto/verification/request/ToDeviceChannel.js.map +1 -1
  100. package/lib/crypto/verification/request/VerificationRequest.d.ts +1 -1
  101. package/lib/crypto/verification/request/VerificationRequest.d.ts.map +1 -1
  102. package/lib/crypto/verification/request/VerificationRequest.js.map +1 -1
  103. package/lib/embedded.d.ts.map +1 -1
  104. package/lib/embedded.js +3 -1
  105. package/lib/embedded.js.map +1 -1
  106. package/lib/event-mapper.d.ts +1 -1
  107. package/lib/event-mapper.d.ts.map +1 -1
  108. package/lib/event-mapper.js.map +1 -1
  109. package/lib/filter-component.d.ts.map +1 -1
  110. package/lib/filter-component.js.map +1 -1
  111. package/lib/filter.d.ts.map +1 -1
  112. package/lib/filter.js.map +1 -1
  113. package/lib/http-api/errors.d.ts.map +1 -1
  114. package/lib/http-api/errors.js.map +1 -1
  115. package/lib/http-api/fetch.d.ts +2 -2
  116. package/lib/http-api/fetch.d.ts.map +1 -1
  117. package/lib/http-api/fetch.js.map +1 -1
  118. package/lib/http-api/index.js.map +1 -1
  119. package/lib/http-api/interface.d.ts +2 -2
  120. package/lib/http-api/interface.d.ts.map +1 -1
  121. package/lib/http-api/utils.js.map +1 -1
  122. package/lib/indexeddb-helpers.js +1 -1
  123. package/lib/indexeddb-helpers.js.map +1 -1
  124. package/lib/interactive-auth.d.ts.map +1 -1
  125. package/lib/interactive-auth.js.map +1 -1
  126. package/lib/logger.js.map +1 -1
  127. package/lib/matrix.d.ts +2 -1
  128. package/lib/matrix.d.ts.map +1 -1
  129. package/lib/matrix.js.map +1 -1
  130. package/lib/models/MSC3089Branch.js +1 -1
  131. package/lib/models/MSC3089Branch.js.map +1 -1
  132. package/lib/models/MSC3089TreeSpace.js +3 -3
  133. package/lib/models/MSC3089TreeSpace.js.map +1 -1
  134. package/lib/models/ToDeviceMessage.d.ts +1 -1
  135. package/lib/models/ToDeviceMessage.d.ts.map +1 -1
  136. package/lib/models/beacon.d.ts +2 -2
  137. package/lib/models/beacon.d.ts.map +1 -1
  138. package/lib/models/beacon.js.map +1 -1
  139. package/lib/models/event-context.d.ts.map +1 -1
  140. package/lib/models/event-context.js.map +1 -1
  141. package/lib/models/event-timeline-set.d.ts +2 -2
  142. package/lib/models/event-timeline-set.d.ts.map +1 -1
  143. package/lib/models/event-timeline-set.js +1 -1
  144. package/lib/models/event-timeline-set.js.map +1 -1
  145. package/lib/models/event-timeline.d.ts.map +1 -1
  146. package/lib/models/event-timeline.js.map +1 -1
  147. package/lib/models/event.d.ts +11 -4
  148. package/lib/models/event.d.ts.map +1 -1
  149. package/lib/models/event.js +10 -0
  150. package/lib/models/event.js.map +1 -1
  151. package/lib/models/invites-ignorer.d.ts.map +1 -1
  152. package/lib/models/invites-ignorer.js.map +1 -1
  153. package/lib/models/read-receipt.d.ts +1 -23
  154. package/lib/models/read-receipt.d.ts.map +1 -1
  155. package/lib/models/read-receipt.js +2 -6
  156. package/lib/models/read-receipt.js.map +1 -1
  157. package/lib/models/related-relations.d.ts.map +1 -1
  158. package/lib/models/related-relations.js.map +1 -1
  159. package/lib/models/relations-container.d.ts.map +1 -1
  160. package/lib/models/relations-container.js.map +1 -1
  161. package/lib/models/relations.d.ts +1 -1
  162. package/lib/models/relations.d.ts.map +1 -1
  163. package/lib/models/relations.js.map +1 -1
  164. package/lib/models/room-member.d.ts +1 -1
  165. package/lib/models/room-member.d.ts.map +1 -1
  166. package/lib/models/room-member.js.map +1 -1
  167. package/lib/models/room-state.d.ts +4 -4
  168. package/lib/models/room-state.d.ts.map +1 -1
  169. package/lib/models/room-state.js.map +1 -1
  170. package/lib/models/room-summary.d.ts.map +1 -1
  171. package/lib/models/room-summary.js.map +1 -1
  172. package/lib/models/room.d.ts +4 -4
  173. package/lib/models/room.d.ts.map +1 -1
  174. package/lib/models/room.js +2 -2
  175. package/lib/models/room.js.map +1 -1
  176. package/lib/models/search-result.d.ts.map +1 -1
  177. package/lib/models/search-result.js.map +1 -1
  178. package/lib/models/thread.d.ts +2 -2
  179. package/lib/models/thread.d.ts.map +1 -1
  180. package/lib/models/thread.js.map +1 -1
  181. package/lib/models/typed-event-emitter.d.ts +5 -5
  182. package/lib/models/typed-event-emitter.d.ts.map +1 -1
  183. package/lib/models/user.d.ts +1 -1
  184. package/lib/models/user.d.ts.map +1 -1
  185. package/lib/models/user.js.map +1 -1
  186. package/lib/pushprocessor.d.ts.map +1 -1
  187. package/lib/pushprocessor.js +3 -1
  188. package/lib/pushprocessor.js.map +1 -1
  189. package/lib/realtime-callbacks.js.map +1 -1
  190. package/lib/rendezvous/MSC3906Rendezvous.d.ts.map +1 -1
  191. package/lib/rendezvous/MSC3906Rendezvous.js.map +1 -1
  192. package/lib/rendezvous/RendezvousError.d.ts.map +1 -1
  193. package/lib/rendezvous/RendezvousError.js.map +1 -1
  194. package/lib/rendezvous/RendezvousFailureReason.d.ts +1 -1
  195. package/lib/rendezvous/RendezvousFailureReason.d.ts.map +1 -1
  196. package/lib/rendezvous/channels/MSC3903ECDHv1RendezvousChannel.d.ts +1 -1
  197. package/lib/rendezvous/channels/MSC3903ECDHv1RendezvousChannel.d.ts.map +1 -1
  198. package/lib/room-hierarchy.js.map +1 -1
  199. package/lib/scheduler.d.ts +1 -1
  200. package/lib/scheduler.d.ts.map +1 -1
  201. package/lib/scheduler.js +1 -1
  202. package/lib/scheduler.js.map +1 -1
  203. package/lib/service-types.js +1 -1
  204. package/lib/service-types.js.map +1 -1
  205. package/lib/sliding-sync-sdk.d.ts +1 -1
  206. package/lib/sliding-sync-sdk.d.ts.map +1 -1
  207. package/lib/sliding-sync-sdk.js +69 -1
  208. package/lib/sliding-sync-sdk.js.map +1 -1
  209. package/lib/sliding-sync.d.ts +5 -2
  210. package/lib/sliding-sync.d.ts.map +1 -1
  211. package/lib/sliding-sync.js +7 -1
  212. package/lib/sliding-sync.js.map +1 -1
  213. package/lib/store/indexeddb-backend.d.ts +1 -1
  214. package/lib/store/indexeddb-backend.d.ts.map +1 -1
  215. package/lib/store/indexeddb-local-backend.d.ts.map +1 -1
  216. package/lib/store/indexeddb-local-backend.js +2 -2
  217. package/lib/store/indexeddb-local-backend.js.map +1 -1
  218. package/lib/store/indexeddb-remote-backend.js.map +1 -1
  219. package/lib/store/indexeddb-store-worker.d.ts.map +1 -1
  220. package/lib/store/indexeddb-store-worker.js.map +1 -1
  221. package/lib/store/indexeddb.d.ts +2 -2
  222. package/lib/store/indexeddb.d.ts.map +1 -1
  223. package/lib/store/indexeddb.js.map +1 -1
  224. package/lib/store/local-storage-events-emitter.d.ts +1 -1
  225. package/lib/store/local-storage-events-emitter.d.ts.map +1 -1
  226. package/lib/store/memory.d.ts.map +1 -1
  227. package/lib/store/memory.js.map +1 -1
  228. package/lib/store/stub.d.ts.map +1 -1
  229. package/lib/store/stub.js.map +1 -1
  230. package/lib/sync-accumulator.d.ts.map +1 -1
  231. package/lib/sync-accumulator.js +32 -27
  232. package/lib/sync-accumulator.js.map +1 -1
  233. package/lib/sync.d.ts.map +1 -1
  234. package/lib/sync.js.map +1 -1
  235. package/lib/timeline-window.d.ts.map +1 -1
  236. package/lib/timeline-window.js.map +1 -1
  237. package/lib/utils.d.ts +6 -1
  238. package/lib/utils.d.ts.map +1 -1
  239. package/lib/utils.js +14 -0
  240. package/lib/utils.js.map +1 -1
  241. package/lib/webrtc/audioContext.d.ts.map +1 -1
  242. package/lib/webrtc/audioContext.js.map +1 -1
  243. package/lib/webrtc/call.d.ts +2 -1
  244. package/lib/webrtc/call.d.ts.map +1 -1
  245. package/lib/webrtc/call.js +13 -2
  246. package/lib/webrtc/call.js.map +1 -1
  247. package/lib/webrtc/callEventHandler.d.ts +1 -1
  248. package/lib/webrtc/callEventHandler.d.ts.map +1 -1
  249. package/lib/webrtc/callEventHandler.js +1 -1
  250. package/lib/webrtc/callEventHandler.js.map +1 -1
  251. package/lib/webrtc/callFeed.d.ts +3 -1
  252. package/lib/webrtc/callFeed.d.ts.map +1 -1
  253. package/lib/webrtc/callFeed.js +4 -1
  254. package/lib/webrtc/callFeed.js.map +1 -1
  255. package/lib/webrtc/groupCall.d.ts +74 -52
  256. package/lib/webrtc/groupCall.d.ts.map +1 -1
  257. package/lib/webrtc/groupCall.js +459 -387
  258. package/lib/webrtc/groupCall.js.map +1 -1
  259. package/lib/webrtc/groupCallEventHandler.d.ts +3 -1
  260. package/lib/webrtc/groupCallEventHandler.d.ts.map +1 -1
  261. package/lib/webrtc/groupCallEventHandler.js +1 -6
  262. package/lib/webrtc/groupCallEventHandler.js.map +1 -1
  263. package/lib/webrtc/mediaHandler.d.ts +1 -1
  264. package/lib/webrtc/mediaHandler.d.ts.map +1 -1
  265. package/lib/webrtc/mediaHandler.js.map +1 -1
  266. package/package.json +7 -7
  267. package/src/@types/read_receipts.ts +35 -0
  268. package/src/ReEmitter.ts +3 -3
  269. package/src/ToDeviceMessageQueue.ts +1 -1
  270. package/src/client.ts +35 -36
  271. package/src/content-helpers.ts +7 -6
  272. package/src/crypto/CrossSigning.ts +15 -8
  273. package/src/crypto/DeviceList.ts +4 -4
  274. package/src/crypto/EncryptionSetup.ts +5 -5
  275. package/src/crypto/OlmDevice.ts +12 -12
  276. package/src/crypto/OutgoingRoomKeyRequestManager.ts +2 -2
  277. package/src/crypto/RoomList.ts +1 -1
  278. package/src/crypto/SecretStorage.ts +2 -2
  279. package/src/crypto/algorithms/base.ts +4 -4
  280. package/src/crypto/algorithms/megolm.ts +25 -21
  281. package/src/crypto/backup.ts +5 -5
  282. package/src/crypto/dehydration.ts +2 -2
  283. package/src/crypto/deviceinfo.ts +1 -1
  284. package/src/crypto/index.ts +30 -30
  285. package/src/crypto/olmlib.ts +5 -5
  286. package/src/crypto/store/indexeddb-crypto-store-backend.ts +48 -48
  287. package/src/crypto/store/indexeddb-crypto-store.ts +9 -9
  288. package/src/crypto/store/localStorage-crypto-store.ts +3 -3
  289. package/src/crypto/store/memory-crypto-store.ts +1 -1
  290. package/src/crypto/verification/Base.ts +4 -4
  291. package/src/crypto/verification/QRCode.ts +5 -5
  292. package/src/crypto/verification/SAS.ts +10 -8
  293. package/src/crypto/verification/request/InRoomChannel.ts +1 -1
  294. package/src/crypto/verification/request/ToDeviceChannel.ts +1 -1
  295. package/src/crypto/verification/request/VerificationRequest.ts +4 -4
  296. package/src/embedded.ts +9 -7
  297. package/src/event-mapper.ts +1 -1
  298. package/src/filter-component.ts +1 -1
  299. package/src/filter.ts +6 -6
  300. package/src/http-api/errors.ts +4 -4
  301. package/src/http-api/fetch.ts +1 -1
  302. package/src/http-api/index.ts +3 -3
  303. package/src/http-api/utils.ts +2 -2
  304. package/src/indexeddb-helpers.ts +4 -4
  305. package/src/interactive-auth.ts +3 -3
  306. package/src/logger.ts +2 -2
  307. package/src/matrix.ts +3 -2
  308. package/src/models/MSC3089TreeSpace.ts +2 -2
  309. package/src/models/beacon.ts +2 -2
  310. package/src/models/event-context.ts +1 -1
  311. package/src/models/event-timeline-set.ts +3 -3
  312. package/src/models/event-timeline.ts +2 -2
  313. package/src/models/event.ts +16 -6
  314. package/src/models/invites-ignorer.ts +3 -3
  315. package/src/models/read-receipt.ts +9 -35
  316. package/src/models/related-relations.ts +2 -2
  317. package/src/models/relations-container.ts +2 -2
  318. package/src/models/relations.ts +6 -6
  319. package/src/models/room-member.ts +2 -2
  320. package/src/models/room-state.ts +4 -4
  321. package/src/models/room-summary.ts +1 -1
  322. package/src/models/room.ts +7 -10
  323. package/src/models/search-result.ts +1 -1
  324. package/src/models/thread.ts +6 -6
  325. package/src/models/user.ts +1 -1
  326. package/src/pushprocessor.ts +1 -1
  327. package/src/realtime-callbacks.ts +1 -1
  328. package/src/rendezvous/MSC3906Rendezvous.ts +2 -2
  329. package/src/rendezvous/RendezvousError.ts +1 -1
  330. package/src/room-hierarchy.ts +1 -1
  331. package/src/scheduler.ts +4 -4
  332. package/src/sliding-sync-sdk.ts +90 -9
  333. package/src/sliding-sync.ts +14 -10
  334. package/src/store/indexeddb-local-backend.ts +24 -24
  335. package/src/store/indexeddb-remote-backend.ts +1 -1
  336. package/src/store/indexeddb-store-worker.ts +1 -1
  337. package/src/store/indexeddb.ts +2 -2
  338. package/src/store/memory.ts +6 -6
  339. package/src/store/stub.ts +9 -9
  340. package/src/sync-accumulator.ts +47 -16
  341. package/src/sync.ts +8 -8
  342. package/src/timeline-window.ts +4 -4
  343. package/src/utils.ts +17 -4
  344. package/src/webrtc/audioContext.ts +1 -1
  345. package/src/webrtc/call.ts +16 -7
  346. package/src/webrtc/callEventHandler.ts +9 -7
  347. package/src/webrtc/callFeed.ts +9 -4
  348. package/src/webrtc/groupCall.ts +612 -537
  349. package/src/webrtc/groupCallEventHandler.ts +5 -11
  350. package/src/webrtc/mediaHandler.ts +5 -5
@@ -41853,7 +41853,7 @@ module.exports={
41853
41853
  "⑧": "➇",
41854
41854
  "🄉": "8,",
41855
41855
  "⒏": "8.",
41856
- "��": "8日",
41856
+ "": "8日",
41857
41857
  "㋇": "8月",
41858
41858
  "㍠": "8点",
41859
41859
  "੧": "9",
@@ -45893,7 +45893,7 @@ module.exports={
45893
45893
  "𠠄": "𠠄",
45894
45894
  "カ": "力",
45895
45895
  "力": "力",
45896
- "���": "力",
45896
+ "": "力",
45897
45897
  "劣": "劣",
45898
45898
  "㔕": "㔕",
45899
45899
  "劳": "劳",
@@ -48826,13 +48826,14 @@ See the License for the specific language governing permissions and
48826
48826
  limitations under the License.
48827
48827
  */
48828
48828
  Object.defineProperty(exports, "__esModule", { value: true });
48829
- exports.ReceiptType = void 0;
48829
+ exports.MAIN_ROOM_TIMELINE = exports.ReceiptType = void 0;
48830
48830
  var ReceiptType;
48831
48831
  (function (ReceiptType) {
48832
48832
  ReceiptType["Read"] = "m.read";
48833
48833
  ReceiptType["FullyRead"] = "m.fully_read";
48834
48834
  ReceiptType["ReadPrivate"] = "m.read.private";
48835
48835
  })(ReceiptType = exports.ReceiptType || (exports.ReceiptType = {}));
48836
+ exports.MAIN_ROOM_TIMELINE = "main";
48836
48837
 
48837
48838
  },{}],295:[function(require,module,exports){
48838
48839
  "use strict";
@@ -49919,7 +49920,6 @@ const thread_1 = require("./models/thread");
49919
49920
  const beacon_1 = require("./@types/beacon");
49920
49921
  const NamespacedValue_1 = require("./NamespacedValue");
49921
49922
  const ToDeviceMessageQueue_1 = require("./ToDeviceMessageQueue");
49922
- const read_receipt_1 = require("./models/read-receipt");
49923
49923
  const invites_ignorer_1 = require("./models/invites-ignorer");
49924
49924
  const sync_2 = require("./@types/sync");
49925
49925
  const feature_1 = require("./feature");
@@ -51115,7 +51115,7 @@ class MatrixClient extends typed_event_emitter_1.TypedEventEmitter {
51115
51115
  if (!this.crypto) {
51116
51116
  throw new Error("End-to-end encryption disabled");
51117
51117
  }
51118
- return this.crypto.prepareToEncrypt(room);
51118
+ this.crypto.prepareToEncrypt(room);
51119
51119
  }
51120
51120
  /**
51121
51121
  * Checks whether cross signing:
@@ -51184,7 +51184,7 @@ class MatrixClient extends typed_event_emitter_1.TypedEventEmitter {
51184
51184
  if (!this.crypto) {
51185
51185
  throw new Error("End-to-end encryption disabled");
51186
51186
  }
51187
- return this.crypto.setCryptoTrustCrossSignedDevices(val);
51187
+ this.crypto.setCryptoTrustCrossSignedDevices(val);
51188
51188
  }
51189
51189
  /**
51190
51190
  * Counts the number of end to end session keys that are waiting to be backed up
@@ -52190,7 +52190,9 @@ class MatrixClient extends typed_event_emitter_1.TypedEventEmitter {
52190
52190
  }
52191
52191
  let signPromise = Promise.resolve();
52192
52192
  if (opts.inviteSignUrl) {
52193
- signPromise = this.http.requestOtherUrl(http_api_1.Method.Post, new URL(opts.inviteSignUrl), { mxid: this.credentials.userId });
52193
+ const url = new URL(opts.inviteSignUrl);
52194
+ url.searchParams.set("mxid", this.credentials.userId);
52195
+ signPromise = this.http.requestOtherUrl(http_api_1.Method.Post, url);
52194
52196
  }
52195
52197
  const queryString = {};
52196
52198
  if (opts.viaServers) {
@@ -52792,7 +52794,7 @@ class MatrixClient extends typed_event_emitter_1.TypedEventEmitter {
52792
52794
  const isThread = !!event.threadRootId;
52793
52795
  body = Object.assign(Object.assign({}, body), { thread_id: isThread
52794
52796
  ? event.threadRootId
52795
- : read_receipt_1.MAIN_ROOM_TIMELINE });
52797
+ : read_receipts_1.MAIN_ROOM_TIMELINE });
52796
52798
  }
52797
52799
  const promise = this.http.authedRequest(http_api_1.Method.Post, path, undefined, body || {});
52798
52800
  const room = this.getRoom(event.getRoomId());
@@ -53086,8 +53088,8 @@ class MatrixClient extends typed_event_emitter_1.TypedEventEmitter {
53086
53088
  return this.leave(roomId).then(() => {
53087
53089
  delete populationResults[roomId];
53088
53090
  }).catch((err) => {
53091
+ // suppress error
53089
53092
  populationResults[roomId] = err;
53090
- return null; // suppress error
53091
53093
  });
53092
53094
  };
53093
53095
  for (const room of eligibleToLeave) {
@@ -57114,7 +57116,7 @@ exports.fixNotificationCountOnDecryption = fixNotificationCountOnDecryption;
57114
57116
 
57115
57117
  }).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
57116
57118
 
57117
- },{"./@types/PushRules":288,"./@types/beacon":289,"./@types/event":290,"./@types/partials":293,"./@types/read_receipts":294,"./@types/search":296,"./@types/sync":297,"./NamespacedValue":299,"./ReEmitter":300,"./ToDeviceMessageQueue":301,"./autodiscovery":302,"./content-helpers":305,"./content-repo":306,"./crypto":324,"./crypto/RoomList":312,"./crypto/api":319,"./crypto/backup":320,"./crypto/dehydration":322,"./crypto/key_passphrase":325,"./crypto/olmlib":326,"./crypto/recoverykey":327,"./event-mapper":343,"./feature":344,"./filter":346,"./http-api":349,"./logger":356,"./models/MSC3089TreeSpace":359,"./models/event":365,"./models/event-timeline":364,"./models/invites-ignorer":366,"./models/read-receipt":367,"./models/room":373,"./models/search-result":374,"./models/thread":375,"./models/typed-event-emitter":376,"./models/user":377,"./pushprocessor":378,"./randomstring":379,"./service-types":382,"./sliding-sync-sdk":383,"./store/stub":389,"./sync":391,"./utils":393,"./webrtc/call":395,"./webrtc/callEventHandler":396,"./webrtc/groupCall":399,"./webrtc/groupCallEventHandler":400,"./webrtc/mediaHandler":401,"matrix-events-sdk":168}],305:[function(require,module,exports){
57119
+ },{"./@types/PushRules":288,"./@types/beacon":289,"./@types/event":290,"./@types/partials":293,"./@types/read_receipts":294,"./@types/search":296,"./@types/sync":297,"./NamespacedValue":299,"./ReEmitter":300,"./ToDeviceMessageQueue":301,"./autodiscovery":302,"./content-helpers":305,"./content-repo":306,"./crypto":324,"./crypto/RoomList":312,"./crypto/api":319,"./crypto/backup":320,"./crypto/dehydration":322,"./crypto/key_passphrase":325,"./crypto/olmlib":326,"./crypto/recoverykey":327,"./event-mapper":343,"./feature":344,"./filter":346,"./http-api":349,"./logger":356,"./models/MSC3089TreeSpace":359,"./models/event":365,"./models/event-timeline":364,"./models/invites-ignorer":366,"./models/room":373,"./models/search-result":374,"./models/thread":375,"./models/typed-event-emitter":376,"./models/user":377,"./pushprocessor":378,"./randomstring":379,"./service-types":382,"./sliding-sync-sdk":383,"./store/stub":389,"./sync":391,"./utils":393,"./webrtc/call":395,"./webrtc/callEventHandler":396,"./webrtc/groupCall":399,"./webrtc/groupCallEventHandler":400,"./webrtc/mediaHandler":401,"matrix-events-sdk":168}],305:[function(require,module,exports){
57118
57120
  "use strict";
57119
57121
  /*
57120
57122
  Copyright 2018 - 2022 The Matrix.org Foundation C.I.C.
@@ -60160,6 +60162,7 @@ class OlmDevice {
60160
60162
  }
60161
60163
  recordSessionProblem(deviceKey, type, fixed) {
60162
60164
  return __awaiter(this, void 0, void 0, function* () {
60165
+ logger_1.logger.info(`Recording problem on olm session with ${deviceKey} of type ${type}. Recreating: ${fixed}`);
60163
60166
  yield this.cryptoStore.storeEndToEndSessionProblem(deviceKey, type, fixed);
60164
60167
  });
60165
60168
  }
@@ -60339,15 +60342,14 @@ class OlmDevice {
60339
60342
  senderKey);
60340
60343
  }
60341
60344
  if (existingSession) {
60342
- logger_1.logger.log("Update for megolm session "
60343
- + senderKey + "/" + sessionId);
60345
+ logger_1.logger.log(`Update for megolm session ${senderKey}|${sessionId}`);
60344
60346
  if (existingSession.first_known_index() <= session.first_known_index()) {
60345
60347
  if (!existingSessionData.untrusted || extraSessionData.untrusted) {
60346
60348
  // existing session has less-than-or-equal index
60347
60349
  // (i.e. can decrypt at least as much), and the
60348
60350
  // new session's trust does not win over the old
60349
60351
  // session's trust, so keep it
60350
- logger_1.logger.log(`Keeping existing megolm session ${sessionId}`);
60352
+ logger_1.logger.log(`Keeping existing megolm session ${senderKey}|${sessionId}`);
60351
60353
  return;
60352
60354
  }
60353
60355
  if (existingSession.first_known_index() < session.first_known_index()) {
@@ -60359,12 +60361,12 @@ class OlmDevice {
60359
60361
  if (existingSession.export_session(session.first_known_index())
60360
60362
  === session.export_session(session.first_known_index())) {
60361
60363
  logger_1.logger.info("Upgrading trust of existing megolm session " +
60362
- sessionId + " based on newly-received trusted session");
60364
+ `${senderKey}|${sessionId} based on newly-received trusted session`);
60363
60365
  existingSessionData.untrusted = false;
60364
60366
  this.cryptoStore.storeEndToEndInboundGroupSession(senderKey, sessionId, existingSessionData, txn);
60365
60367
  }
60366
60368
  else {
60367
- logger_1.logger.warn("Newly-received megolm session " + sessionId +
60369
+ logger_1.logger.warn(`Newly-received megolm session ${senderKey}|$sessionId}` +
60368
60370
  " does not match existing session! Keeping existing session");
60369
60371
  }
60370
60372
  return;
@@ -60372,8 +60374,8 @@ class OlmDevice {
60372
60374
  // If the sessions have the same index, go ahead and store the new trusted one.
60373
60375
  }
60374
60376
  }
60375
- logger_1.logger.info("Storing megolm session " + senderKey + "/" + sessionId +
60376
- " with first index " + session.first_known_index());
60377
+ logger_1.logger.info(`Storing megolm session ${senderKey}|${sessionId} with first index ` +
60378
+ session.first_known_index());
60377
60379
  const sessionData = Object.assign({}, extraSessionData, {
60378
60380
  room_id: roomId,
60379
60381
  session: session.pickle(this.pickleKey),
@@ -62635,30 +62637,29 @@ class MegolmEncryption extends base_1.EncryptionAlgorithm {
62635
62637
  return __awaiter(this, void 0, void 0, function* () {
62636
62638
  const obSessionInfo = this.outboundSessions[sessionId];
62637
62639
  if (!obSessionInfo) {
62638
- logger_1.logger.debug(`megolm session ${sessionId} not found: not re-sharing keys`);
62640
+ logger_1.logger.debug(`megolm session ${senderKey}|${sessionId} not found: not re-sharing keys`);
62639
62641
  return;
62640
62642
  }
62641
62643
  // The chain index of the key we previously sent this device
62642
62644
  if (obSessionInfo.sharedWithDevices[userId] === undefined) {
62643
- logger_1.logger.debug(`megolm session ${sessionId} never shared with user ${userId}`);
62645
+ logger_1.logger.debug(`megolm session ${senderKey}|${sessionId} never shared with user ${userId}`);
62644
62646
  return;
62645
62647
  }
62646
62648
  const sessionSharedData = obSessionInfo.sharedWithDevices[userId][device.deviceId];
62647
62649
  if (sessionSharedData === undefined) {
62648
- logger_1.logger.debug("megolm session ID " + sessionId + " never shared with device " +
62649
- userId + ":" + device.deviceId);
62650
+ logger_1.logger.debug(`megolm session ${senderKey}|${sessionId} never shared with device ${userId}:${device.deviceId}`);
62650
62651
  return;
62651
62652
  }
62652
62653
  if (sessionSharedData.deviceKey !== device.getIdentityKey()) {
62653
- logger_1.logger.warn(`Session has been shared with device ${device.deviceId} but with identity ` +
62654
- `key ${sessionSharedData.deviceKey}. Key is now ${device.getIdentityKey()}!`);
62654
+ logger_1.logger.warn(`Megolm session ${senderKey}|${sessionId} has been shared with device ${device.deviceId} but ` +
62655
+ `with identity key ${sessionSharedData.deviceKey}. Key is now ${device.getIdentityKey()}!`);
62655
62656
  return;
62656
62657
  }
62657
62658
  // get the key from the inbound session: the outbound one will already
62658
62659
  // have been ratcheted to the next chain index.
62659
62660
  const key = yield this.olmDevice.getInboundGroupSessionKey(this.roomId, senderKey, sessionId, sessionSharedData.messageIndex);
62660
62661
  if (!key) {
62661
- logger_1.logger.warn(`No inbound session key found for megolm ${sessionId}: not re-sharing keys`);
62662
+ logger_1.logger.warn(`No inbound session key found for megolm session ${senderKey}|${sessionId}: not re-sharing keys`);
62662
62663
  return;
62663
62664
  }
62664
62665
  yield olmlib.ensureOlmSessionsForDevices(this.olmDevice, this.baseApis, {
@@ -62689,7 +62690,7 @@ class MegolmEncryption extends base_1.EncryptionAlgorithm {
62689
62690
  [device.deviceId]: encryptedContent,
62690
62691
  },
62691
62692
  });
62692
- logger_1.logger.debug(`Re-shared key for megolm session ${sessionId} with ${userId}:${device.deviceId}`);
62693
+ logger_1.logger.debug(`Re-shared key for megolm session ${senderKey}|${sessionId} with ${userId}:${device.deviceId}`);
62693
62694
  });
62694
62695
  }
62695
62696
  /**
@@ -63130,6 +63131,8 @@ class MegolmDecryption extends base_1.DecryptionAlgorithm {
63130
63131
  // event was sent. Use a fuzz factor of 2 minutes.
63131
63132
  const problem = yield this.olmDevice.sessionMayHaveProblems(content.sender_key, event.getTs() - 120000);
63132
63133
  if (problem) {
63134
+ logger_1.logger.info(`When handling UISI from ${event.getSender()} (sender key ${content.sender_key}): ` +
63135
+ `recent session problem with that sender: ${problem}`);
63133
63136
  let problemDescription = PROBLEM_DESCRIPTIONS[problem.type] || PROBLEM_DESCRIPTIONS.unknown;
63134
63137
  if (problem.fixed) {
63135
63138
  problemDescription +=
@@ -63573,9 +63576,8 @@ class MegolmDecryption extends base_1.DecryptionAlgorithm {
63573
63576
  sendSharedHistoryInboundSessions(devicesByUser) {
63574
63577
  return __awaiter(this, void 0, void 0, function* () {
63575
63578
  yield olmlib.ensureOlmSessionsForDevices(this.olmDevice, this.baseApis, devicesByUser);
63576
- logger_1.logger.log("sendSharedHistoryInboundSessions to users", Object.keys(devicesByUser));
63577
63579
  const sharedHistorySessions = yield this.olmDevice.getSharedHistoryInboundGroupSessions(this.roomId);
63578
- logger_1.logger.log("shared-history sessions", sharedHistorySessions);
63580
+ logger_1.logger.log(`Sharing history in ${this.roomId} with users ${Object.keys(devicesByUser)}`, sharedHistorySessions.map(([senderKey, sessionId]) => `${senderKey}|${sessionId}`));
63579
63581
  for (const [senderKey, sessionId] of sharedHistorySessions) {
63580
63582
  const payload = yield this.buildKeyForwardingMessage(this.roomId, senderKey, sessionId);
63581
63583
  // FIXME: use encryptAndSendToDevices() rather than duplicating it here.
@@ -65013,15 +65015,6 @@ var DeviceVerification;
65013
65015
  * @param {string} deviceId id of the device
65014
65016
  */
65015
65017
  class DeviceInfo {
65016
- constructor(deviceId) {
65017
- this.deviceId = deviceId;
65018
- this.algorithms = [];
65019
- this.keys = {};
65020
- this.verified = DeviceVerification.Unverified;
65021
- this.known = false;
65022
- this.unsigned = {};
65023
- this.signatures = {};
65024
- }
65025
65018
  /**
65026
65019
  * rehydrate a DeviceInfo from the session store
65027
65020
  *
@@ -65039,6 +65032,15 @@ class DeviceInfo {
65039
65032
  }
65040
65033
  return res;
65041
65034
  }
65035
+ constructor(deviceId) {
65036
+ this.deviceId = deviceId;
65037
+ this.algorithms = [];
65038
+ this.keys = {};
65039
+ this.verified = DeviceVerification.Unverified;
65040
+ this.known = false;
65041
+ this.unsigned = {};
65042
+ this.signatures = {};
65043
+ }
65042
65044
  /**
65043
65045
  * Prepare a DeviceInfo for JSON serialisation in the session store
65044
65046
  *
@@ -65255,6 +65257,12 @@ var CryptoEvent;
65255
65257
  CryptoEvent["KeysChanged"] = "crossSigning.keysChanged";
65256
65258
  })(CryptoEvent = exports.CryptoEvent || (exports.CryptoEvent = {}));
65257
65259
  class Crypto extends typed_event_emitter_1.TypedEventEmitter {
65260
+ /**
65261
+ * @return {string} The version of Olm.
65262
+ */
65263
+ static getOlmVersion() {
65264
+ return OlmDevice_1.OlmDevice.getOlmVersion();
65265
+ }
65258
65266
  /**
65259
65267
  * Cryptography bits
65260
65268
  *
@@ -65505,12 +65513,6 @@ class Crypto extends typed_event_emitter_1.TypedEventEmitter {
65505
65513
  });
65506
65514
  }
65507
65515
  }
65508
- /**
65509
- * @return {string} The version of Olm.
65510
- */
65511
- static getOlmVersion() {
65512
- return OlmDevice_1.OlmDevice.getOlmVersion();
65513
- }
65514
65516
  /**
65515
65517
  * Initialise the crypto module so that it is ready for use
65516
65518
  *
@@ -67806,9 +67808,9 @@ class Crypto extends typed_event_emitter_1.TypedEventEmitter {
67806
67808
  logger_1.logger.error("key withheld event is missing fields");
67807
67809
  return;
67808
67810
  }
67809
- logger_1.logger.info(`Got room key withheld event from ${event.getSender()} (${content.sender_key}) `
67810
- + `for ${content.algorithm}/${content.room_id}/${content.session_id} `
67811
- + `with reason ${content.code} (${content.reason})`);
67811
+ logger_1.logger.info(`Got room key withheld event from ${event.getSender()} `
67812
+ + `for ${content.algorithm} session ${content.sender_key}|${content.session_id} `
67813
+ + `in room ${content.room_id} with code ${content.code} (${content.reason})`);
67812
67814
  const alg = this.getRoomDecryptor(content.room_id, content.algorithm);
67813
67815
  if (alg.onRoomKeyWithheldEvent) {
67814
67816
  alg.onRoomKeyWithheldEvent(event);
@@ -70052,6 +70054,9 @@ const IndexedDBHelpers = __importStar(require("../../indexeddb-helpers"));
70052
70054
  * @implements {module:crypto/store/base~CryptoStore}
70053
70055
  */
70054
70056
  class IndexedDBCryptoStore {
70057
+ static exists(indexedDB, dbName) {
70058
+ return IndexedDBHelpers.exists(indexedDB, dbName);
70059
+ }
70055
70060
  /**
70056
70061
  * Create a new IndexedDBCryptoStore
70057
70062
  *
@@ -70062,9 +70067,6 @@ class IndexedDBCryptoStore {
70062
70067
  this.indexedDB = indexedDB;
70063
70068
  this.dbName = dbName;
70064
70069
  }
70065
- static exists(indexedDB, dbName) {
70066
- return IndexedDBHelpers.exists(indexedDB, dbName);
70067
- }
70068
70070
  /**
70069
70071
  * Ensure the database exists and is up-to-date, or fall back to
70070
70072
  * a local storage or in-memory store.
@@ -70645,10 +70647,6 @@ function keyEndToEndRoomsPrefix(roomId) {
70645
70647
  * @implements {module:crypto/store/base~CryptoStore}
70646
70648
  */
70647
70649
  class LocalStorageCryptoStore extends memory_crypto_store_1.MemoryCryptoStore {
70648
- constructor(store) {
70649
- super();
70650
- this.store = store;
70651
- }
70652
70650
  static exists(store) {
70653
70651
  var _a;
70654
70652
  const length = store.length;
@@ -70659,6 +70657,10 @@ class LocalStorageCryptoStore extends memory_crypto_store_1.MemoryCryptoStore {
70659
70657
  }
70660
70658
  return false;
70661
70659
  }
70660
+ constructor(store) {
70661
+ super();
70662
+ this.store = store;
70663
+ }
70662
70664
  // Olm Sessions
70663
70665
  countEndToEndSessions(txn, func) {
70664
70666
  var _a;
@@ -74382,28 +74384,37 @@ class RoomWidgetClient extends client_1.MatrixClient {
74382
74384
  });
74383
74385
  }
74384
74386
  watchTurnServers() {
74385
- var e_1, _a;
74387
+ var _a, e_1, _b, _c;
74386
74388
  return __awaiter(this, void 0, void 0, function* () {
74387
74389
  const servers = this.widgetApi.getTurnServers();
74388
- const onClientStopped = () => servers.return(undefined);
74390
+ const onClientStopped = () => {
74391
+ servers.return(undefined);
74392
+ };
74389
74393
  this.lifecycle.signal.addEventListener("abort", onClientStopped);
74390
74394
  try {
74391
74395
  try {
74392
- for (var servers_1 = __asyncValues(servers), servers_1_1; servers_1_1 = yield servers_1.next(), !servers_1_1.done;) {
74393
- const server = servers_1_1.value;
74394
- this.turnServers = [{
74395
- urls: server.uris,
74396
- username: server.username,
74397
- credential: server.password,
74398
- }];
74399
- this.emit(client_1.ClientEvent.TurnServers, this.turnServers);
74400
- logger_1.logger.log(`Received TURN server: ${server.uris}`);
74396
+ for (var _d = true, servers_1 = __asyncValues(servers), servers_1_1; servers_1_1 = yield servers_1.next(), _a = servers_1_1.done, !_a;) {
74397
+ _c = servers_1_1.value;
74398
+ _d = false;
74399
+ try {
74400
+ const server = _c;
74401
+ this.turnServers = [{
74402
+ urls: server.uris,
74403
+ username: server.username,
74404
+ credential: server.password,
74405
+ }];
74406
+ this.emit(client_1.ClientEvent.TurnServers, this.turnServers);
74407
+ logger_1.logger.log(`Received TURN server: ${server.uris}`);
74408
+ }
74409
+ finally {
74410
+ _d = true;
74411
+ }
74401
74412
  }
74402
74413
  }
74403
74414
  catch (e_1_1) { e_1 = { error: e_1_1 }; }
74404
74415
  finally {
74405
74416
  try {
74406
- if (servers_1_1 && !servers_1_1.done && (_a = servers_1.return)) yield _a.call(servers_1);
74417
+ if (!_d && !_a && (_b = servers_1.return)) yield _b.call(servers_1);
74407
74418
  }
74408
74419
  finally { if (e_1) throw e_1.error; }
74409
74420
  }
@@ -74842,11 +74853,6 @@ function setProp(obj, keyNesting, val) {
74842
74853
  * @prop {?string} filterId The filter ID
74843
74854
  */
74844
74855
  class Filter {
74845
- constructor(userId, filterId) {
74846
- this.userId = userId;
74847
- this.filterId = filterId;
74848
- this.definition = {};
74849
- }
74850
74856
  /**
74851
74857
  * Create a filter from existing data.
74852
74858
  * @static
@@ -74860,6 +74866,11 @@ class Filter {
74860
74866
  filter.setDefinition(jsonObj);
74861
74867
  return filter;
74862
74868
  }
74869
+ constructor(userId, filterId) {
74870
+ this.userId = userId;
74871
+ this.filterId = filterId;
74872
+ this.definition = {};
74873
+ }
74863
74874
  /**
74864
74875
  * Get the ID of this filter on your homeserver (if known)
74865
74876
  * @return {?string} The filter ID
@@ -75921,7 +75932,7 @@ function exists(indexedDB, dbName) {
75921
75932
  }
75922
75933
  resolve(exists);
75923
75934
  };
75924
- req.onerror = ev => reject(req.error);
75935
+ req.onerror = () => reject(req.error);
75925
75936
  });
75926
75937
  }
75927
75938
  exports.exists = exists;
@@ -77093,7 +77104,7 @@ class MSC3089TreeSpace {
77093
77104
  const currentPls = this.room.currentState.getStateEvents(event_1.EventType.RoomPowerLevels, "");
77094
77105
  if (Array.isArray(currentPls))
77095
77106
  throw new Error("Unexpected return type for power levels");
77096
- const pls = currentPls.getContent() || {};
77107
+ const pls = (currentPls === null || currentPls === void 0 ? void 0 : currentPls.getContent()) || {};
77097
77108
  const viewLevel = pls['users_default'] || 0;
77098
77109
  const editLevel = pls['events_default'] || 50;
77099
77110
  const adminLevel = ((_a = pls['events']) === null || _a === void 0 ? void 0 : _a[event_1.EventType.RoomPowerLevels]) || 100;
@@ -77127,7 +77138,7 @@ class MSC3089TreeSpace {
77127
77138
  const currentPls = this.room.currentState.getStateEvents(event_1.EventType.RoomPowerLevels, "");
77128
77139
  if (Array.isArray(currentPls))
77129
77140
  throw new Error("Unexpected return type for power levels");
77130
- const pls = currentPls.getContent() || {};
77141
+ const pls = (currentPls === null || currentPls === void 0 ? void 0 : currentPls.getContent()) || {};
77131
77142
  const viewLevel = pls['users_default'] || 0;
77132
77143
  const editLevel = pls['events_default'] || 50;
77133
77144
  const adminLevel = ((_a = pls['events']) === null || _a === void 0 ? void 0 : _a[event_1.EventType.RoomPowerLevels]) || 100;
@@ -78276,7 +78287,7 @@ class EventTimelineSet extends typed_event_emitter_1.TypedEventEmitter {
78276
78287
  '`EventTimelineSet.addEventToTimeline(event, timeline, IAddEventToTimelineOptions)`');
78277
78288
  }
78278
78289
  if (timeline.getTimelineSet() !== this) {
78279
- throw new Error(`EventTimelineSet.addEventToTimeline: Timeline=${timeline.toString()} does not belong " +
78290
+ throw new Error(`EventTimelineSet.addEventToTimeline: Timeline=${timeline.toString()} does not belong " +
78280
78291
  "in timelineSet(threadId=${(_a = this.thread) === null || _a === void 0 ? void 0 : _a.id})`);
78281
78292
  }
78282
78293
  // Make sure events don't get mixed in timelines they shouldn't be in (e.g. a
@@ -78513,6 +78524,36 @@ var Direction;
78513
78524
  Direction["Forward"] = "f";
78514
78525
  })(Direction = exports.Direction || (exports.Direction = {}));
78515
78526
  class EventTimeline {
78527
+ /**
78528
+ * Static helper method to set sender and target properties
78529
+ *
78530
+ * @param {MatrixEvent} event the event whose metadata is to be set
78531
+ * @param {RoomState} stateContext the room state to be queried
78532
+ * @param {boolean} toStartOfTimeline if true the event's forwardLooking flag is set false
78533
+ */
78534
+ static setEventMetadata(event, stateContext, toStartOfTimeline) {
78535
+ var _a, _b, _c, _d;
78536
+ // When we try to generate a sentinel member before we have that member
78537
+ // in the members object, we still generate a sentinel but it doesn't
78538
+ // have a membership event, so test to see if events.member is set. We
78539
+ // check this to avoid overriding non-sentinel members by sentinel ones
78540
+ // when adding the event to a filtered timeline
78541
+ if (!((_b = (_a = event.sender) === null || _a === void 0 ? void 0 : _a.events) === null || _b === void 0 ? void 0 : _b.member)) {
78542
+ event.sender = stateContext.getSentinelMember(event.getSender());
78543
+ }
78544
+ if (!((_d = (_c = event.target) === null || _c === void 0 ? void 0 : _c.events) === null || _d === void 0 ? void 0 : _d.member) && event.getType() === event_1.EventType.RoomMember) {
78545
+ event.target = stateContext.getSentinelMember(event.getStateKey());
78546
+ }
78547
+ if (event.isState()) {
78548
+ // room state has no concept of 'old' or 'current', but we want the
78549
+ // room state to regress back to previous values if toStartOfTimeline
78550
+ // is set, which means inspecting prev_content if it exists. This
78551
+ // is done by toggling the forwardLooking flag.
78552
+ if (toStartOfTimeline) {
78553
+ event.forwardLooking = false;
78554
+ }
78555
+ }
78556
+ }
78516
78557
  /**
78517
78558
  * Construct a new EventTimeline
78518
78559
  *
@@ -78557,36 +78598,6 @@ class EventTimeline {
78557
78598
  this.paginationRequests = { 'b': null, 'f': null };
78558
78599
  this.name = this.roomId + ":" + new Date().toISOString();
78559
78600
  }
78560
- /**
78561
- * Static helper method to set sender and target properties
78562
- *
78563
- * @param {MatrixEvent} event the event whose metadata is to be set
78564
- * @param {RoomState} stateContext the room state to be queried
78565
- * @param {boolean} toStartOfTimeline if true the event's forwardLooking flag is set false
78566
- */
78567
- static setEventMetadata(event, stateContext, toStartOfTimeline) {
78568
- var _a, _b, _c, _d;
78569
- // When we try to generate a sentinel member before we have that member
78570
- // in the members object, we still generate a sentinel but it doesn't
78571
- // have a membership event, so test to see if events.member is set. We
78572
- // check this to avoid overriding non-sentinel members by sentinel ones
78573
- // when adding the event to a filtered timeline
78574
- if (!((_b = (_a = event.sender) === null || _a === void 0 ? void 0 : _a.events) === null || _b === void 0 ? void 0 : _b.member)) {
78575
- event.sender = stateContext.getSentinelMember(event.getSender());
78576
- }
78577
- if (!((_d = (_c = event.target) === null || _c === void 0 ? void 0 : _c.events) === null || _d === void 0 ? void 0 : _d.member) && event.getType() === event_1.EventType.RoomMember) {
78578
- event.target = stateContext.getSentinelMember(event.getStateKey());
78579
- }
78580
- if (event.isState()) {
78581
- // room state has no concept of 'old' or 'current', but we want the
78582
- // room state to regress back to previous values if toStartOfTimeline
78583
- // is set, which means inspecting prev_content if it exists. This
78584
- // is done by toggling the forwardLooking flag.
78585
- if (toStartOfTimeline) {
78586
- event.forwardLooking = false;
78587
- }
78588
- }
78589
- }
78590
78601
  /**
78591
78602
  * Initialise the start and end state with the given events
78592
78603
  *
@@ -80071,10 +80082,19 @@ class MatrixEvent extends typed_event_emitter_1.TypedEventEmitter {
80071
80082
  * Checks if this event is associated with another event. See `getAssociatedId`.
80072
80083
  *
80073
80084
  * @return {boolean}
80085
+ * @deprecated use hasAssociation instead.
80074
80086
  */
80075
80087
  hasAssocation() {
80076
80088
  return !!this.getAssociatedId();
80077
80089
  }
80090
+ /**
80091
+ * Checks if this event is associated with another event. See `getAssociatedId`.
80092
+ *
80093
+ * @return {boolean}
80094
+ */
80095
+ hasAssociation() {
80096
+ return !!this.getAssociatedId();
80097
+ }
80078
80098
  /**
80079
80099
  * Update the related id with a new one.
80080
80100
  *
@@ -80640,13 +80660,12 @@ var __importStar = (this && this.__importStar) || function (mod) {
80640
80660
  return result;
80641
80661
  };
80642
80662
  Object.defineProperty(exports, "__esModule", { value: true });
80643
- exports.ReadReceipt = exports.synthesizeReceipt = exports.MAIN_ROOM_TIMELINE = void 0;
80663
+ exports.ReadReceipt = exports.synthesizeReceipt = void 0;
80644
80664
  const read_receipts_1 = require("../@types/read_receipts");
80645
80665
  const typed_event_emitter_1 = require("./typed-event-emitter");
80646
80666
  const utils = __importStar(require("../utils"));
80647
80667
  const event_1 = require("./event");
80648
80668
  const event_2 = require("../@types/event");
80649
- exports.MAIN_ROOM_TIMELINE = "main";
80650
80669
  function synthesizeReceipt(userId, event, receiptType) {
80651
80670
  var _a;
80652
80671
  return new event_1.MatrixEvent({
@@ -80655,7 +80674,7 @@ function synthesizeReceipt(userId, event, receiptType) {
80655
80674
  [receiptType]: {
80656
80675
  [userId]: {
80657
80676
  ts: event.getTs(),
80658
- threadId: (_a = event.threadRootId) !== null && _a !== void 0 ? _a : exports.MAIN_ROOM_TIMELINE,
80677
+ threadId: (_a = event.threadRootId) !== null && _a !== void 0 ? _a : read_receipts_1.MAIN_ROOM_TIMELINE,
80659
80678
  },
80660
80679
  },
80661
80680
  },
@@ -82938,7 +82957,7 @@ class Room extends read_receipt_1.ReadReceipt {
82938
82957
  // If this is in the current state, replace it with the redacted version
82939
82958
  if (redactedEvent.isState()) {
82940
82959
  const currentStateEvent = this.currentState.getStateEvents(redactedEvent.getType(), redactedEvent.getStateKey());
82941
- if (currentStateEvent.getId() === redactedEvent.getId()) {
82960
+ if ((currentStateEvent === null || currentStateEvent === void 0 ? void 0 : currentStateEvent.getId()) === redactedEvent.getId()) {
82942
82961
  this.currentState.setStateEvents([redactedEvent]);
82943
82962
  }
82944
82963
  }
@@ -84884,7 +84903,7 @@ class Room extends read_receipt_1.ReadReceipt {
84884
84903
  Object.keys(content[eventId][receiptType]).forEach((userId) => {
84885
84904
  var _a;
84886
84905
  const receipt = content[eventId][receiptType][userId];
84887
- const receiptForMainTimeline = !receipt.thread_id || receipt.thread_id === read_receipt_1.MAIN_ROOM_TIMELINE;
84906
+ const receiptForMainTimeline = !receipt.thread_id || receipt.thread_id === read_receipts_1.MAIN_ROOM_TIMELINE;
84888
84907
  const receiptDestination = receiptForMainTimeline
84889
84908
  ? this
84890
84909
  : this.threads.get((_a = receipt.thread_id) !== null && _a !== void 0 ? _a : "");
@@ -85574,19 +85593,6 @@ exports.SearchResult = void 0;
85574
85593
  */
85575
85594
  const event_context_1 = require("./event-context");
85576
85595
  class SearchResult {
85577
- /**
85578
- * Construct a new SearchResult
85579
- *
85580
- * @param {number} rank where this SearchResult ranks in the results
85581
- * @param {event-context.EventContext} context the matching event and its
85582
- * context
85583
- *
85584
- * @constructor
85585
- */
85586
- constructor(rank, context) {
85587
- this.rank = rank;
85588
- this.context = context;
85589
- }
85590
85596
  /**
85591
85597
  * Create a SearchResponse from the response to /search
85592
85598
  * @static
@@ -85609,6 +85615,19 @@ class SearchResult {
85609
85615
  context.setPaginateToken(jsonContext.end, false);
85610
85616
  return new SearchResult(jsonObj.rank, context);
85611
85617
  }
85618
+ /**
85619
+ * Construct a new SearchResult
85620
+ *
85621
+ * @param {number} rank where this SearchResult ranks in the results
85622
+ * @param {event-context.EventContext} context the matching event and its
85623
+ * context
85624
+ *
85625
+ * @constructor
85626
+ */
85627
+ constructor(rank, context) {
85628
+ this.rank = rank;
85629
+ this.context = context;
85630
+ }
85612
85631
  }
85613
85632
  exports.SearchResult = SearchResult;
85614
85633
 
@@ -87060,6 +87079,58 @@ const DEBUG = false; // set true to enable console logging.
87060
87079
  */
87061
87080
  // eslint-disable-next-line camelcase
87062
87081
  class MatrixScheduler {
87082
+ /**
87083
+ * Retries events up to 4 times using exponential backoff. This produces wait
87084
+ * times of 2, 4, 8, and 16 seconds (30s total) after which we give up. If the
87085
+ * failure was due to a rate limited request, the time specified in the error is
87086
+ * waited before being retried.
87087
+ * @param {MatrixEvent} event
87088
+ * @param {Number} attempts Number of attempts that have been made, including the one that just failed (ie. starting at 1)
87089
+ * @param {MatrixError} err
87090
+ * @return {Number}
87091
+ * @see module:scheduler~retryAlgorithm
87092
+ */
87093
+ // eslint-disable-next-line @typescript-eslint/naming-convention
87094
+ static RETRY_BACKOFF_RATELIMIT(event, attempts, err) {
87095
+ if (err.httpStatus === 400 || err.httpStatus === 403 || err.httpStatus === 401) {
87096
+ // client error; no amount of retrying with save you now.
87097
+ return -1;
87098
+ }
87099
+ if (err instanceof http_api_1.ConnectionError) {
87100
+ return -1;
87101
+ }
87102
+ // if event that we are trying to send is too large in any way then retrying won't help
87103
+ if (err.name === "M_TOO_LARGE") {
87104
+ return -1;
87105
+ }
87106
+ if (err.name === "M_LIMIT_EXCEEDED") {
87107
+ const waitTime = err.data.retry_after_ms;
87108
+ if (waitTime > 0) {
87109
+ return waitTime;
87110
+ }
87111
+ }
87112
+ if (attempts > 4) {
87113
+ return -1; // give up
87114
+ }
87115
+ return (1000 * Math.pow(2, attempts));
87116
+ }
87117
+ /**
87118
+ * Queues <code>m.room.message</code> events and lets other events continue
87119
+ * concurrently.
87120
+ * @param {MatrixEvent} event
87121
+ * @return {string}
87122
+ * @see module:scheduler~queueAlgorithm
87123
+ */
87124
+ // eslint-disable-next-line @typescript-eslint/naming-convention
87125
+ static QUEUE_MESSAGES(event) {
87126
+ // enqueue messages or events that associate with another event (redactions and relations)
87127
+ if (event.getType() === event_1.EventType.RoomMessage || event.hasAssociation()) {
87128
+ // put these events in the 'message' queue.
87129
+ return "message";
87130
+ }
87131
+ // allow all other events continue concurrently.
87132
+ return null;
87133
+ }
87063
87134
  constructor(retryAlgorithm = MatrixScheduler.RETRY_BACKOFF_RATELIMIT, queueAlgorithm = MatrixScheduler.QUEUE_MESSAGES) {
87064
87135
  this.retryAlgorithm = retryAlgorithm;
87065
87136
  this.queueAlgorithm = queueAlgorithm;
@@ -87118,58 +87189,6 @@ class MatrixScheduler {
87118
87189
  });
87119
87190
  };
87120
87191
  }
87121
- /**
87122
- * Retries events up to 4 times using exponential backoff. This produces wait
87123
- * times of 2, 4, 8, and 16 seconds (30s total) after which we give up. If the
87124
- * failure was due to a rate limited request, the time specified in the error is
87125
- * waited before being retried.
87126
- * @param {MatrixEvent} event
87127
- * @param {Number} attempts Number of attempts that have been made, including the one that just failed (ie. starting at 1)
87128
- * @param {MatrixError} err
87129
- * @return {Number}
87130
- * @see module:scheduler~retryAlgorithm
87131
- */
87132
- // eslint-disable-next-line @typescript-eslint/naming-convention
87133
- static RETRY_BACKOFF_RATELIMIT(event, attempts, err) {
87134
- if (err.httpStatus === 400 || err.httpStatus === 403 || err.httpStatus === 401) {
87135
- // client error; no amount of retrying with save you now.
87136
- return -1;
87137
- }
87138
- if (err instanceof http_api_1.ConnectionError) {
87139
- return -1;
87140
- }
87141
- // if event that we are trying to send is too large in any way then retrying won't help
87142
- if (err.name === "M_TOO_LARGE") {
87143
- return -1;
87144
- }
87145
- if (err.name === "M_LIMIT_EXCEEDED") {
87146
- const waitTime = err.data.retry_after_ms;
87147
- if (waitTime > 0) {
87148
- return waitTime;
87149
- }
87150
- }
87151
- if (attempts > 4) {
87152
- return -1; // give up
87153
- }
87154
- return (1000 * Math.pow(2, attempts));
87155
- }
87156
- /**
87157
- * Queues <code>m.room.message</code> events and lets other events continue
87158
- * concurrently.
87159
- * @param {MatrixEvent} event
87160
- * @return {string}
87161
- * @see module:scheduler~queueAlgorithm
87162
- */
87163
- // eslint-disable-next-line @typescript-eslint/naming-convention
87164
- static QUEUE_MESSAGES(event) {
87165
- // enqueue messages or events that associate with another event (redactions and relations)
87166
- if (event.getType() === event_1.EventType.RoomMessage || event.hasAssocation()) {
87167
- // put these events in the 'message' queue.
87168
- return "message";
87169
- }
87170
- // allow all other events continue concurrently.
87171
- return null;
87172
- }
87173
87192
  /**
87174
87193
  * Retrieve a queue based on an event. The event provided does not need to be in
87175
87194
  * the queue.
@@ -87578,6 +87597,60 @@ class ExtensionAccountData {
87578
87597
  });
87579
87598
  }
87580
87599
  }
87600
+ class ExtensionTyping {
87601
+ constructor(client) {
87602
+ this.client = client;
87603
+ }
87604
+ name() {
87605
+ return "typing";
87606
+ }
87607
+ when() {
87608
+ return sliding_sync_1.ExtensionState.PostProcess;
87609
+ }
87610
+ onRequest(isInitial) {
87611
+ if (!isInitial) {
87612
+ return undefined; // don't send a JSON object for subsequent requests, we don't need to.
87613
+ }
87614
+ return {
87615
+ enabled: true,
87616
+ };
87617
+ }
87618
+ onResponse(data) {
87619
+ if (!data || !data.rooms) {
87620
+ return;
87621
+ }
87622
+ for (const roomId in data.rooms) {
87623
+ processEphemeralEvents(this.client, roomId, [data.rooms[roomId]]);
87624
+ }
87625
+ }
87626
+ }
87627
+ class ExtensionReceipts {
87628
+ constructor(client) {
87629
+ this.client = client;
87630
+ }
87631
+ name() {
87632
+ return "receipts";
87633
+ }
87634
+ when() {
87635
+ return sliding_sync_1.ExtensionState.PostProcess;
87636
+ }
87637
+ onRequest(isInitial) {
87638
+ if (isInitial) {
87639
+ return {
87640
+ enabled: true,
87641
+ };
87642
+ }
87643
+ return undefined; // don't send a JSON object for subsequent requests, we don't need to.
87644
+ }
87645
+ onResponse(data) {
87646
+ if (!data || !data.rooms) {
87647
+ return;
87648
+ }
87649
+ for (const roomId in data.rooms) {
87650
+ processEphemeralEvents(this.client, roomId, [data.rooms[roomId]]);
87651
+ }
87652
+ }
87653
+ }
87581
87654
  /**
87582
87655
  * A copy of SyncApi such that it can be used as a drop-in replacement for sync v2. For the actual
87583
87656
  * sliding sync API, see sliding-sync.ts or the class SlidingSync.
@@ -87613,6 +87686,8 @@ class SlidingSyncSdk {
87613
87686
  const extensions = [
87614
87687
  new ExtensionToDevice(this.client),
87615
87688
  new ExtensionAccountData(this.client),
87689
+ new ExtensionTyping(this.client),
87690
+ new ExtensionReceipts(this.client),
87616
87691
  ];
87617
87692
  if (this.opts.crypto) {
87618
87693
  extensions.push(new ExtensionE2EE(this.opts.crypto));
@@ -88184,6 +88259,18 @@ function mapEvents(client, roomId, events, decrypt = true) {
88184
88259
  return mapper(e);
88185
88260
  });
88186
88261
  }
88262
+ function processEphemeralEvents(client, roomId, ephEvents) {
88263
+ const ephemeralEvents = mapEvents(client, roomId, ephEvents);
88264
+ const room = client.getRoom(roomId);
88265
+ if (!room) {
88266
+ logger_1.logger.warn("got ephemeral events for room but room doesn't exist on client:", roomId);
88267
+ return;
88268
+ }
88269
+ room.addEphemeralEvents(ephemeralEvents);
88270
+ ephemeralEvents.forEach((e) => {
88271
+ client.emit(client_1.ClientEvent.Event, e);
88272
+ });
88273
+ }
88187
88274
 
88188
88275
  },{"./@types/event":290,"./client":304,"./http-api":349,"./logger":356,"./models/event-timeline":364,"./models/room":373,"./models/room-member":370,"./models/room-state":371,"./pushprocessor":378,"./sliding-sync":384,"./sync":391,"./utils":393}],384:[function(require,module,exports){
88189
88276
  "use strict";
@@ -88212,7 +88299,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
88212
88299
  });
88213
88300
  };
88214
88301
  Object.defineProperty(exports, "__esModule", { value: true });
88215
- exports.SlidingSync = exports.SlidingSyncEvent = exports.ExtensionState = exports.SlidingSyncState = void 0;
88302
+ exports.SlidingSync = exports.SlidingSyncEvent = exports.ExtensionState = exports.SlidingSyncState = exports.MSC3575_STATE_KEY_LAZY = exports.MSC3575_STATE_KEY_ME = exports.MSC3575_WILDCARD = void 0;
88216
88303
  const logger_1 = require("./logger");
88217
88304
  const typed_event_emitter_1 = require("./models/typed-event-emitter");
88218
88305
  const utils_1 = require("./utils");
@@ -88221,6 +88308,9 @@ const utils_1 = require("./utils");
88221
88308
  // to keep open the connection. This constant is *ADDED* to the timeout= value
88222
88309
  // to determine the max time we're willing to wait.
88223
88310
  const BUFFER_PERIOD_MS = 10 * 1000;
88311
+ exports.MSC3575_WILDCARD = "*";
88312
+ exports.MSC3575_STATE_KEY_ME = "$ME";
88313
+ exports.MSC3575_STATE_KEY_LAZY = "$LAZY";
88224
88314
  var SlidingSyncState;
88225
88315
  (function (SlidingSyncState) {
88226
88316
  /**
@@ -89045,6 +89135,10 @@ function reqAsCursorPromise(req) {
89045
89135
  return reqAsEventPromise(req).then((event) => req.result);
89046
89136
  }
89047
89137
  class LocalIndexedDBStoreBackend {
89138
+ static exists(indexedDB, dbName) {
89139
+ dbName = "matrix-js-sdk:" + (dbName || "default");
89140
+ return IndexedDBHelpers.exists(indexedDB, dbName);
89141
+ }
89048
89142
  /**
89049
89143
  * Does the actual reading from and writing to the indexeddb
89050
89144
  *
@@ -89065,10 +89159,6 @@ class LocalIndexedDBStoreBackend {
89065
89159
  this.dbName = "matrix-js-sdk:" + dbName;
89066
89160
  this.syncAccumulator = new sync_accumulator_1.SyncAccumulator();
89067
89161
  }
89068
- static exists(indexedDB, dbName) {
89069
- dbName = "matrix-js-sdk:" + (dbName || "default");
89070
- return IndexedDBHelpers.exists(indexedDB, dbName);
89071
- }
89072
89162
  /**
89073
89163
  * Attempt to connect to the database. This can fail if the user does not
89074
89164
  * grant permission.
@@ -89099,7 +89189,7 @@ class LocalIndexedDBStoreBackend {
89099
89189
  logger_1.logger.log(`can't yet open LocalIndexedDBStoreBackend because it is open elsewhere`);
89100
89190
  };
89101
89191
  logger_1.logger.log(`LocalIndexedDBStoreBackend.connect: awaiting connection...`);
89102
- return reqAsEventPromise(req).then(() => {
89192
+ return reqAsEventPromise(req).then(() => __awaiter(this, void 0, void 0, function* () {
89103
89193
  logger_1.logger.log(`LocalIndexedDBStoreBackend.connect: connected`);
89104
89194
  this.db = req.result;
89105
89195
  // add a poorly-named listener for when deleteDatabase is called
@@ -89108,8 +89198,8 @@ class LocalIndexedDBStoreBackend {
89108
89198
  var _a;
89109
89199
  (_a = this.db) === null || _a === void 0 ? void 0 : _a.close();
89110
89200
  };
89111
- return this.init();
89112
- });
89201
+ yield this.init();
89202
+ }));
89113
89203
  }
89114
89204
  /** @return {boolean} whether or not the database was newly created in this session. */
89115
89205
  isNewlyCreated() {
@@ -89718,6 +89808,9 @@ const typed_event_emitter_1 = require("../models/typed-event-emitter");
89718
89808
  // response is persisted each time.
89719
89809
  const WRITE_DELAY_MS = 1000 * 60 * 5; // once every 5 minutes
89720
89810
  class IndexedDBStore extends memory_1.MemoryStore {
89811
+ static exists(indexedDB, dbName) {
89812
+ return indexeddb_local_backend_1.LocalIndexedDBStoreBackend.exists(indexedDB, dbName);
89813
+ }
89721
89814
  /**
89722
89815
  * Construct a new Indexed Database store, which extends MemoryStore.
89723
89816
  *
@@ -89864,9 +89957,6 @@ class IndexedDBStore extends memory_1.MemoryStore {
89864
89957
  this.backend = new indexeddb_local_backend_1.LocalIndexedDBStoreBackend(opts.indexedDB, opts.dbName);
89865
89958
  }
89866
89959
  }
89867
- static exists(indexedDB, dbName) {
89868
- return indexeddb_local_backend_1.LocalIndexedDBStoreBackend.exists(indexedDB, dbName);
89869
- }
89870
89960
  /**
89871
89961
  * @return {Promise} Resolved when loaded from indexed db.
89872
89962
  */
@@ -90716,6 +90806,7 @@ exports.SyncAccumulator = exports.Category = void 0;
90716
90806
  const logger_1 = require("./logger");
90717
90807
  const utils_1 = require("./utils");
90718
90808
  const event_1 = require("./@types/event");
90809
+ const read_receipts_1 = require("./@types/read_receipts");
90719
90810
  const sync_1 = require("./@types/sync");
90720
90811
  /* eslint-enable camelcase */
90721
90812
  var Category;
@@ -90907,6 +90998,7 @@ class SyncAccumulator {
90907
90998
  _unreadThreadNotifications: {},
90908
90999
  _summary: {},
90909
91000
  _readReceipts: {},
91001
+ _threadReadReceipts: {},
90910
91002
  };
90911
91003
  }
90912
91004
  const currentData = this.joinRooms[roomId];
@@ -90957,16 +91049,23 @@ class SyncAccumulator {
90957
91049
  // getJSON() is called.
90958
91050
  Object.keys(e.content).forEach((eventId) => {
90959
91051
  Object.entries(e.content[eventId]).forEach(([key, value]) => {
91052
+ var _a;
90960
91053
  if (!(0, utils_1.isSupportedReceiptType)(key))
90961
91054
  return;
90962
- Object.keys(value).forEach((userId) => {
90963
- // clobber on user ID
90964
- currentData._readReceipts[userId] = {
91055
+ for (const userId of Object.keys(value)) {
91056
+ const data = e.content[eventId][key][userId];
91057
+ const receipt = {
90965
91058
  data: e.content[eventId][key][userId],
90966
91059
  type: key,
90967
91060
  eventId: eventId,
90968
91061
  };
90969
- });
91062
+ if (!data.thread_id || data.thread_id === read_receipts_1.MAIN_ROOM_TIMELINE) {
91063
+ currentData._readReceipts[userId] = receipt;
91064
+ }
91065
+ else {
91066
+ currentData._threadReadReceipts = Object.assign(Object.assign({}, currentData._threadReadReceipts), { [data.thread_id]: Object.assign(Object.assign({}, ((_a = currentData._threadReadReceipts[data.thread_id]) !== null && _a !== void 0 ? _a : {})), { [userId]: receipt }) });
91067
+ }
91068
+ }
90970
91069
  });
90971
91070
  });
90972
91071
  });
@@ -91084,8 +91183,7 @@ class SyncAccumulator {
91084
91183
  // $event_id: { "m.read": { $user_id: $json } }
91085
91184
  },
91086
91185
  };
91087
- Object.keys(roomData._readReceipts).forEach((userId) => {
91088
- const receiptData = roomData._readReceipts[userId];
91186
+ for (const [userId, receiptData] of Object.entries(roomData._readReceipts)) {
91089
91187
  if (!receiptEvent.content[receiptData.eventId]) {
91090
91188
  receiptEvent.content[receiptData.eventId] = {};
91091
91189
  }
@@ -91093,7 +91191,18 @@ class SyncAccumulator {
91093
91191
  receiptEvent.content[receiptData.eventId][receiptData.type] = {};
91094
91192
  }
91095
91193
  receiptEvent.content[receiptData.eventId][receiptData.type][userId] = (receiptData.data);
91096
- });
91194
+ }
91195
+ for (const threadReceipts of Object.values(roomData._threadReadReceipts)) {
91196
+ for (const [userId, receiptData] of Object.entries(threadReceipts)) {
91197
+ if (!receiptEvent.content[receiptData.eventId]) {
91198
+ receiptEvent.content[receiptData.eventId] = {};
91199
+ }
91200
+ if (!receiptEvent.content[receiptData.eventId][receiptData.type]) {
91201
+ receiptEvent.content[receiptData.eventId][receiptData.type] = {};
91202
+ }
91203
+ receiptEvent.content[receiptData.eventId][receiptData.type][userId] = (receiptData.data);
91204
+ }
91205
+ }
91097
91206
  // add only if we have some receipt data
91098
91207
  if (Object.keys(receiptEvent.content).length > 0) {
91099
91208
  roomJson.ephemeral.events.push(receiptEvent);
@@ -91193,7 +91302,7 @@ function setState(eventMap, event) {
91193
91302
  eventMap[event.type][event.state_key] = event;
91194
91303
  }
91195
91304
 
91196
- },{"./@types/event":290,"./@types/sync":297,"./logger":356,"./utils":393}],391:[function(require,module,exports){
91305
+ },{"./@types/event":290,"./@types/read_receipts":294,"./@types/sync":297,"./logger":356,"./utils":393}],391:[function(require,module,exports){
91197
91306
  (function (global){(function (){
91198
91307
  "use strict";
91199
91308
  /*
@@ -93321,7 +93430,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
93321
93430
  return (mod && mod.__esModule) ? mod : { "default": mod };
93322
93431
  };
93323
93432
  Object.defineProperty(exports, "__esModule", { value: true });
93324
- exports.isSupportedReceiptType = exports.sortEventsByLatestContentTimestamp = exports.recursivelyAssign = exports.compare = exports.lexicographicCompare = exports.prevString = exports.nextString = exports.averageBetweenStrings = exports.stringToBase = exports.baseToString = exports.alphabetPad = exports.DEFAULT_ALPHABET = exports.simpleRetryOperation = exports.chunkPromises = exports.promiseTry = exports.promiseMapSeries = exports.defer = exports.isNullOrUndefined = exports.sleep = exports.ensureNoTrailingSlash = exports.globToRegexp = exports.escapeRegExp = exports.normalize = exports.removeDirectionOverrideChars = exports.removeHiddenChars = exports.isNumber = exports.deepSortedObjectEntries = exports.deepCompare = exports.deepCopy = exports.checkObjectHasKeys = exports.isFunction = exports.removeElement = exports.encodeUri = exports.decodeParams = exports.replaceParam = exports.encodeParams = exports.internaliseString = void 0;
93433
+ exports.mapsEqual = exports.isSupportedReceiptType = exports.sortEventsByLatestContentTimestamp = exports.recursivelyAssign = exports.compare = exports.lexicographicCompare = exports.prevString = exports.nextString = exports.averageBetweenStrings = exports.stringToBase = exports.baseToString = exports.alphabetPad = exports.DEFAULT_ALPHABET = exports.simpleRetryOperation = exports.chunkPromises = exports.promiseTry = exports.promiseMapSeries = exports.defer = exports.isNullOrUndefined = exports.sleep = exports.ensureNoTrailingSlash = exports.globToRegexp = exports.escapeRegExp = exports.normalize = exports.removeDirectionOverrideChars = exports.removeHiddenChars = exports.isNumber = exports.deepSortedObjectEntries = exports.deepCompare = exports.deepCopy = exports.checkObjectHasKeys = exports.isFunction = exports.removeElement = exports.encodeUri = exports.decodeParams = exports.replaceParam = exports.encodeParams = exports.internaliseString = void 0;
93325
93434
  /**
93326
93435
  * This is an internal module.
93327
93436
  * @module utils
@@ -93949,6 +94058,21 @@ function isSupportedReceiptType(receiptType) {
93949
94058
  return [read_receipts_1.ReceiptType.Read, read_receipts_1.ReceiptType.ReadPrivate].includes(receiptType);
93950
94059
  }
93951
94060
  exports.isSupportedReceiptType = isSupportedReceiptType;
94061
+ /**
94062
+ * Determines whether two maps are equal.
94063
+ * @param eq The equivalence relation to compare values by. Defaults to strict equality.
94064
+ */
94065
+ function mapsEqual(x, y, eq = (v1, v2) => v1 === v2) {
94066
+ if (x.size !== y.size)
94067
+ return false;
94068
+ for (const [k, v1] of x) {
94069
+ const v2 = y.get(k);
94070
+ if (v2 === undefined || !eq(v1, v2))
94071
+ return false;
94072
+ }
94073
+ return true;
94074
+ }
94075
+ exports.mapsEqual = mapsEqual;
93952
94076
 
93953
94077
  },{"./@types/location":292,"./@types/read_receipts":294,"p-retry":226,"unhomoglyph":282}],394:[function(require,module,exports){
93954
94078
  "use strict";
@@ -94496,6 +94620,9 @@ class MatrixCall extends typed_event_emitter_1.TypedEventEmitter {
94496
94620
  getOpponentMember() {
94497
94621
  return this.opponentMember;
94498
94622
  }
94623
+ getOpponentDeviceId() {
94624
+ return this.opponentDeviceId;
94625
+ }
94499
94626
  getOpponentSessionId() {
94500
94627
  return this.opponentSessionId;
94501
94628
  }
@@ -94660,6 +94787,7 @@ class MatrixCall extends typed_event_emitter_1.TypedEventEmitter {
94660
94787
  client: this.client,
94661
94788
  roomId: this.roomId,
94662
94789
  userId,
94790
+ deviceId: this.getOpponentDeviceId(),
94663
94791
  stream,
94664
94792
  purpose,
94665
94793
  audioMuted,
@@ -94696,6 +94824,7 @@ class MatrixCall extends typed_event_emitter_1.TypedEventEmitter {
94696
94824
  audioMuted: false,
94697
94825
  videoMuted: false,
94698
94826
  userId,
94827
+ deviceId: this.getOpponentDeviceId(),
94699
94828
  stream,
94700
94829
  purpose,
94701
94830
  }));
@@ -94719,6 +94848,7 @@ class MatrixCall extends typed_event_emitter_1.TypedEventEmitter {
94719
94848
  audioMuted: false,
94720
94849
  videoMuted: false,
94721
94850
  userId,
94851
+ deviceId: this.getOpponentDeviceId(),
94722
94852
  stream,
94723
94853
  purpose,
94724
94854
  }), addToPeerConnection);
@@ -94952,6 +95082,7 @@ class MatrixCall extends typed_event_emitter_1.TypedEventEmitter {
94952
95082
  * Answer a call.
94953
95083
  */
94954
95084
  answer(audio, video) {
95085
+ var _a;
94955
95086
  return __awaiter(this, void 0, void 0, function* () {
94956
95087
  if (this.inviteOrAnswerSent)
94957
95088
  return;
@@ -94971,6 +95102,7 @@ class MatrixCall extends typed_event_emitter_1.TypedEventEmitter {
94971
95102
  client: this.client,
94972
95103
  roomId: this.roomId,
94973
95104
  userId: this.client.getUserId(),
95105
+ deviceId: (_a = this.client.getDeviceId()) !== null && _a !== void 0 ? _a : undefined,
94974
95106
  stream,
94975
95107
  purpose: callEventTypes_1.SDPStreamMetadataPurpose.Usermedia,
94976
95108
  audioMuted: false,
@@ -95041,7 +95173,7 @@ class MatrixCall extends typed_event_emitter_1.TypedEventEmitter {
95041
95173
  logger_1.logger.debug(`Ending call ${this.callId} with reason ${reason}`);
95042
95174
  this.terminate(CallParty.Local, reason, !suppressEvent);
95043
95175
  // We don't want to send hangup here if we didn't even get to sending an invite
95044
- if (this.state === CallState.WaitLocalMedia)
95176
+ if ([CallState.Fledgling, CallState.WaitLocalMedia].includes(this.state))
95045
95177
  return;
95046
95178
  const content = {};
95047
95179
  // Don't send UserHangup reason to older clients
@@ -96203,6 +96335,7 @@ class MatrixCall extends typed_event_emitter_1.TypedEventEmitter {
96203
96335
  * @throws if have passed audio=false.
96204
96336
  */
96205
96337
  placeCall(audio, video) {
96338
+ var _a;
96206
96339
  return __awaiter(this, void 0, void 0, function* () {
96207
96340
  if (!audio) {
96208
96341
  throw new Error("You CANNOT start a call without audio");
@@ -96218,6 +96351,7 @@ class MatrixCall extends typed_event_emitter_1.TypedEventEmitter {
96218
96351
  client: this.client,
96219
96352
  roomId: this.roomId,
96220
96353
  userId: this.client.getUserId(),
96354
+ deviceId: (_a = this.client.getDeviceId()) !== null && _a !== void 0 ? _a : undefined,
96221
96355
  stream,
96222
96356
  purpose: callEventTypes_1.SDPStreamMetadataPurpose.Usermedia,
96223
96357
  audioMuted: false,
@@ -96576,7 +96710,6 @@ class CallEventHandler {
96576
96710
  const groupCallId = content.conf_id;
96577
96711
  const type = event.getType();
96578
96712
  const senderId = event.getSender();
96579
- const weSentTheEvent = senderId === this.client.credentials.userId;
96580
96713
  let call = content.call_id ? this.calls.get(content.call_id) : undefined;
96581
96714
  let opponentDeviceId;
96582
96715
  let groupCall;
@@ -96597,6 +96730,8 @@ class CallEventHandler {
96597
96730
  return;
96598
96731
  }
96599
96732
  }
96733
+ const weSentTheEvent = senderId === this.client.credentials.userId
96734
+ && (opponentDeviceId === undefined || opponentDeviceId === this.client.getDeviceId());
96600
96735
  if (!callRoomId)
96601
96736
  return;
96602
96737
  if (type === event_1.EventType.CallInvite) {
@@ -96861,6 +96996,7 @@ class CallFeed extends typed_event_emitter_1.TypedEventEmitter {
96861
96996
  this.client = opts.client;
96862
96997
  this.roomId = opts.roomId;
96863
96998
  this.userId = opts.userId;
96999
+ this.deviceId = opts.deviceId;
96864
97000
  this.purpose = opts.purpose;
96865
97001
  this.audioMuted = opts.audioMuted;
96866
97002
  this.videoMuted = opts.videoMuted;
@@ -96918,7 +97054,8 @@ class CallFeed extends typed_event_emitter_1.TypedEventEmitter {
96918
97054
  * @returns {boolean} is local?
96919
97055
  */
96920
97056
  isLocal() {
96921
- return this.userId === this.client.getUserId();
97057
+ return this.userId === this.client.getUserId()
97058
+ && (this.deviceId === undefined || this.deviceId === this.client.getDeviceId());
96922
97059
  }
96923
97060
  /**
96924
97061
  * Returns true if audio is muted or if there are no audio
@@ -97001,6 +97138,7 @@ class CallFeed extends typed_event_emitter_1.TypedEventEmitter {
97001
97138
  client: this.client,
97002
97139
  roomId: this.roomId,
97003
97140
  userId: this.userId,
97141
+ deviceId: this.deviceId,
97004
97142
  stream,
97005
97143
  purpose: this.purpose,
97006
97144
  audioMuted: this.audioMuted,
@@ -97051,12 +97189,14 @@ exports.GroupCall = exports.GroupCallState = exports.OtherUserSpeakingError = ex
97051
97189
  const typed_event_emitter_1 = require("../models/typed-event-emitter");
97052
97190
  const callFeed_1 = require("./callFeed");
97053
97191
  const call_1 = require("./call");
97192
+ const room_state_1 = require("../models/room-state");
97054
97193
  const logger_1 = require("../logger");
97055
97194
  const ReEmitter_1 = require("../ReEmitter");
97056
97195
  const callEventTypes_1 = require("./callEventTypes");
97057
97196
  const event_1 = require("../@types/event");
97058
97197
  const callEventHandler_1 = require("./callEventHandler");
97059
97198
  const groupCallEventHandler_1 = require("./groupCallEventHandler");
97199
+ const utils_1 = require("../utils");
97060
97200
  var GroupCallIntent;
97061
97201
  (function (GroupCallIntent) {
97062
97202
  GroupCallIntent["Ring"] = "m.ring";
@@ -97121,24 +97261,17 @@ var GroupCallState;
97121
97261
  GroupCallState["LocalCallFeedUninitialized"] = "local_call_feed_uninitialized";
97122
97262
  GroupCallState["InitializingLocalCallFeed"] = "initializing_local_call_feed";
97123
97263
  GroupCallState["LocalCallFeedInitialized"] = "local_call_feed_initialized";
97124
- GroupCallState["Entering"] = "entering";
97125
97264
  GroupCallState["Entered"] = "entered";
97126
97265
  GroupCallState["Ended"] = "ended";
97127
97266
  })(GroupCallState = exports.GroupCallState || (exports.GroupCallState = {}));
97128
- const CALL_MEMBER_STATE_TIMEOUT = 1000 * 60 * 60; // 1 hour
97129
- const callMemberStateIsExpired = (event) => {
97130
- var _a;
97131
- const now = Date.now();
97132
- const content = (_a = event === null || event === void 0 ? void 0 : event.getContent()) !== null && _a !== void 0 ? _a : {};
97133
- const expiresAt = typeof content["m.expires_ts"] === "number" ? content["m.expires_ts"] : -Infinity;
97134
- return expiresAt <= now;
97135
- };
97267
+ const DEVICE_TIMEOUT = 1000 * 60 * 60; // 1 hour
97136
97268
  function getCallUserId(call) {
97137
97269
  var _a;
97138
97270
  return ((_a = call.getOpponentMember()) === null || _a === void 0 ? void 0 : _a.userId) || call.invitee || null;
97139
97271
  }
97140
97272
  class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97141
97273
  constructor(client, room, type, isPtt, intent, groupCallId, dataChannelsEnabled, dataChannelOptions) {
97274
+ var _a, _b;
97142
97275
  super();
97143
97276
  this.client = client;
97144
97277
  this.room = room;
@@ -97152,19 +97285,20 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97152
97285
  this.retryCallInterval = 5000;
97153
97286
  this.participantTimeout = 1000 * 15;
97154
97287
  this.pttMaxTransmitTime = 1000 * 20;
97155
- this.state = GroupCallState.LocalCallFeedUninitialized;
97156
- this.calls = [];
97157
- this.participants = [];
97288
+ this.calls = new Map();
97158
97289
  this.userMediaFeeds = [];
97159
97290
  this.screenshareFeeds = [];
97160
- this.callHandlers = new Map();
97291
+ this.callHandlers = new Map(); // User ID -> device ID -> handlers
97161
97292
  this.retryCallCounts = new Map();
97162
97293
  this.transmitTimer = null;
97163
- this.memberStateExpirationTimers = new Map();
97294
+ this.participantsExpirationTimer = null;
97164
97295
  this.resendMemberStateTimer = null;
97165
97296
  this.initWithAudioMuted = false;
97166
97297
  this.initWithVideoMuted = false;
97167
- /**
97298
+ this._state = GroupCallState.LocalCallFeedUninitialized;
97299
+ this._participants = new Map();
97300
+ this._creationTs = null;
97301
+ /*
97168
97302
  * Call Setup
97169
97303
  *
97170
97304
  * There are two different paths for calls to be created:
@@ -97187,148 +97321,55 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97187
97321
  newCall.reject();
97188
97322
  return;
97189
97323
  }
97190
- const opponentMemberId = (_a = newCall.getOpponentMember()) === null || _a === void 0 ? void 0 : _a.userId;
97191
- const existingCall = opponentMemberId ? this.getCallByUserId(opponentMemberId) : null;
97192
- if (existingCall && existingCall.callId === newCall.callId) {
97324
+ const opponent = newCall.getOpponentMember();
97325
+ if (opponent === undefined) {
97326
+ logger_1.logger.warn("Incoming call with no member. Ignoring.");
97193
97327
  return;
97194
97328
  }
97195
- logger_1.logger.log(`GroupCall: incoming call from: ${opponentMemberId} with ID ${newCall.callId}`);
97196
- // we are handlng this call as a PTT call, so enable PTT semantics
97197
- newCall.isPtt = this.isPtt;
97198
- // Check if the user calling has an existing call and use this call instead.
97199
- if (existingCall) {
97200
- this.replaceCall(existingCall, newCall);
97201
- }
97202
- else {
97203
- this.addCall(newCall);
97204
- }
97329
+ const deviceMap = (_a = this.calls.get(opponent)) !== null && _a !== void 0 ? _a : new Map();
97330
+ const prevCall = deviceMap.get(newCall.getOpponentDeviceId());
97331
+ if ((prevCall === null || prevCall === void 0 ? void 0 : prevCall.callId) === newCall.callId)
97332
+ return;
97333
+ logger_1.logger.log(`GroupCall: incoming call from ${opponent.userId} with ID ${newCall.callId}`);
97334
+ if (prevCall)
97335
+ this.disposeCall(prevCall, call_1.CallErrorCode.Replaced);
97336
+ this.initCall(newCall);
97205
97337
  newCall.answerWithCallFeeds(this.getLocalFeeds().map((feed) => feed.clone()));
97338
+ deviceMap.set(newCall.getOpponentDeviceId(), newCall);
97339
+ this.calls.set(opponent, deviceMap);
97340
+ this.emit(GroupCallEvent.CallsChanged, this.calls);
97206
97341
  };
97207
- this.onMemberStateChanged = (event) => __awaiter(this, void 0, void 0, function* () {
97208
- // If we haven't entered the call yet, we don't care
97209
- if (this.state !== GroupCallState.Entered) {
97210
- return;
97211
- }
97212
- // The member events may be received for another room, which we will ignore.
97213
- if (event.getRoomId() !== this.room.roomId)
97214
- return;
97215
- const member = this.room.getMember(event.getStateKey());
97216
- if (!member) {
97217
- logger_1.logger.warn(`Couldn't find room member for ${event.getStateKey()}: ignoring member state event!`);
97218
- return;
97219
- }
97220
- // Don't process your own member.
97221
- const localUserId = this.client.getUserId();
97222
- if (member.userId === localUserId)
97223
- return;
97224
- logger_1.logger.debug(`Processing member state event for ${member.userId}`);
97225
- const ignore = () => {
97226
- this.removeParticipant(member);
97227
- clearTimeout(this.memberStateExpirationTimers.get(member.userId));
97228
- this.memberStateExpirationTimers.delete(member.userId);
97229
- };
97230
- const content = event.getContent();
97231
- const callsState = !callMemberStateIsExpired(event) && Array.isArray(content["m.calls"])
97232
- ? content["m.calls"].filter((call) => call)
97233
- : []; // Ignore expired device data
97234
- if (callsState.length === 0) {
97235
- logger_1.logger.info(`Ignoring member state from ${member.userId} member not in any calls.`);
97236
- ignore();
97237
- return;
97238
- }
97239
- // Currently we only support a single call per room. So grab the first call.
97240
- const callState = callsState[0];
97241
- const callId = callState["m.call_id"];
97242
- if (!callId) {
97243
- logger_1.logger.warn(`Room member ${member.userId} does not have a valid m.call_id set. Ignoring.`);
97244
- ignore();
97245
- return;
97246
- }
97247
- if (callId !== this.groupCallId) {
97248
- logger_1.logger.warn(`Call id ${callId} does not match group call id ${this.groupCallId}, ignoring.`);
97249
- ignore();
97250
- return;
97251
- }
97252
- this.addParticipant(member);
97253
- clearTimeout(this.memberStateExpirationTimers.get(member.userId));
97254
- this.memberStateExpirationTimers.set(member.userId, setTimeout(() => {
97255
- logger_1.logger.warn(`Call member state for ${member.userId} has expired`);
97256
- this.removeParticipant(member);
97257
- }, content["m.expires_ts"] - Date.now()));
97258
- // Only initiate a call with a user who has a userId that is lexicographically
97259
- // less than your own. Otherwise, that user will call you.
97260
- if (member.userId < localUserId) {
97261
- logger_1.logger.debug(`Waiting for ${member.userId} to send call invite.`);
97262
- return;
97263
- }
97264
- const opponentDevice = this.getDeviceForMember(member.userId);
97265
- if (!opponentDevice) {
97266
- logger_1.logger.warn(`No opponent device found for ${member.userId}, ignoring.`);
97267
- this.emit(GroupCallEvent.Error, new GroupCallUnknownDeviceError(member.userId));
97268
- return;
97269
- }
97270
- const existingCall = this.getCallByUserId(member.userId);
97271
- if (existingCall &&
97272
- existingCall.getOpponentSessionId() === opponentDevice.session_id) {
97273
- return;
97274
- }
97275
- const newCall = (0, call_1.createNewMatrixCall)(this.client, this.room.roomId, {
97276
- invitee: member.userId,
97277
- opponentDeviceId: opponentDevice.device_id,
97278
- opponentSessionId: opponentDevice.session_id,
97279
- groupCallId: this.groupCallId,
97280
- });
97281
- if (!newCall) {
97282
- logger_1.logger.error("Failed to create call!");
97283
- return;
97284
- }
97285
- if (existingCall) {
97286
- logger_1.logger.debug(`Replacing call ${existingCall.callId} to ${member.userId} with ${newCall.callId}`);
97287
- this.replaceCall(existingCall, newCall, call_1.CallErrorCode.NewSession);
97288
- }
97289
- else {
97290
- logger_1.logger.debug(`Adding call ${newCall.callId} to ${member.userId}`);
97291
- this.addCall(newCall);
97292
- }
97293
- newCall.isPtt = this.isPtt;
97294
- const requestScreenshareFeed = opponentDevice.feeds.some((feed) => feed.purpose === callEventTypes_1.SDPStreamMetadataPurpose.Screenshare);
97295
- logger_1.logger.debug(`Placing call to ${member.userId}/${opponentDevice.device_id} session ID ${opponentDevice.session_id}.`);
97296
- try {
97297
- yield newCall.placeCallWithCallFeeds(this.getLocalFeeds().map(feed => feed.clone()), requestScreenshareFeed);
97298
- }
97299
- catch (e) {
97300
- logger_1.logger.warn(`Failed to place call to ${member.userId}!`, e);
97301
- if (e instanceof call_1.CallError && e.code === GroupCallErrorCode.UnknownDevice) {
97302
- this.emit(GroupCallEvent.Error, e);
97303
- }
97304
- else {
97305
- this.emit(GroupCallEvent.Error, new GroupCallError(GroupCallErrorCode.PlaceCallFailed, `Failed to place call to ${member.userId}.`));
97306
- }
97307
- this.removeCall(newCall, call_1.CallErrorCode.SignallingFailed);
97308
- return;
97309
- }
97310
- if (this.dataChannelsEnabled) {
97311
- newCall.createDataChannel("datachannel", this.dataChannelOptions);
97312
- }
97313
- });
97314
97342
  this.onRetryCallLoop = () => {
97315
- for (const event of this.getMemberStateEvents()) {
97316
- const memberId = event.getStateKey();
97317
- const existingCall = this.calls.find((call) => getCallUserId(call) === memberId);
97318
- const retryCallCount = this.retryCallCounts.get(memberId) || 0;
97319
- if (!existingCall && retryCallCount < 3) {
97320
- this.retryCallCounts.set(memberId, retryCallCount + 1);
97321
- this.onMemberStateChanged(event);
97343
+ var _a;
97344
+ let needsRetry = false;
97345
+ for (const [member, participantMap] of this.participants) {
97346
+ const callMap = this.calls.get(member);
97347
+ let retriesMap = this.retryCallCounts.get(member);
97348
+ for (const [deviceId, participant] of participantMap) {
97349
+ const call = callMap === null || callMap === void 0 ? void 0 : callMap.get(deviceId);
97350
+ const retries = (_a = retriesMap === null || retriesMap === void 0 ? void 0 : retriesMap.get(deviceId)) !== null && _a !== void 0 ? _a : 0;
97351
+ if ((call === null || call === void 0 ? void 0 : call.getOpponentSessionId()) !== participant.sessionId
97352
+ && this.wantsOutgoingCall(member.userId, deviceId)
97353
+ && retries < 3) {
97354
+ if (retriesMap === undefined) {
97355
+ retriesMap = new Map();
97356
+ this.retryCallCounts.set(member, retriesMap);
97357
+ }
97358
+ retriesMap.set(deviceId, retries + 1);
97359
+ needsRetry = true;
97360
+ }
97322
97361
  }
97323
97362
  }
97324
- this.retryCallLoopTimeout = setTimeout(this.onRetryCallLoop, this.retryCallInterval);
97363
+ if (needsRetry)
97364
+ this.placeOutgoingCalls();
97325
97365
  };
97326
97366
  this.onCallFeedsChanged = (call) => {
97327
97367
  const opponentMemberId = getCallUserId(call);
97368
+ const opponentDeviceId = call.getOpponentDeviceId();
97328
97369
  if (!opponentMemberId) {
97329
97370
  throw new Error("Cannot change call feeds without user id");
97330
97371
  }
97331
- const currentUserMediaFeed = this.getUserMediaFeedByUserId(opponentMemberId);
97372
+ const currentUserMediaFeed = this.getUserMediaFeed(opponentMemberId, opponentDeviceId);
97332
97373
  const remoteUsermediaFeed = call.remoteUsermediaFeed;
97333
97374
  const remoteFeedChanged = remoteUsermediaFeed !== currentUserMediaFeed;
97334
97375
  if (remoteFeedChanged) {
@@ -97342,7 +97383,7 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97342
97383
  this.removeUserMediaFeed(currentUserMediaFeed);
97343
97384
  }
97344
97385
  }
97345
- const currentScreenshareFeed = this.getScreenshareFeedByUserId(opponentMemberId);
97386
+ const currentScreenshareFeed = this.getScreenshareFeed(opponentMemberId, opponentDeviceId);
97346
97387
  const remoteScreensharingFeed = call.remoteScreensharingFeed;
97347
97388
  const remoteScreenshareFeedChanged = remoteScreensharingFeed !== currentScreenshareFeed;
97348
97389
  if (remoteScreenshareFeedChanged) {
@@ -97369,48 +97410,91 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97369
97410
  call.setLocalVideoMuted(videoMuted);
97370
97411
  }
97371
97412
  if (state === call_1.CallState.Connected) {
97372
- this.retryCallCounts.delete(getCallUserId(call));
97413
+ const opponent = call.getOpponentMember();
97414
+ const retriesMap = this.retryCallCounts.get(opponent);
97415
+ retriesMap === null || retriesMap === void 0 ? void 0 : retriesMap.delete(call.getOpponentDeviceId());
97416
+ if ((retriesMap === null || retriesMap === void 0 ? void 0 : retriesMap.size) === 0)
97417
+ this.retryCallCounts.delete(opponent);
97373
97418
  }
97374
97419
  };
97375
97420
  this.onCallHangup = (call) => {
97376
- if (call.hangupReason === call_1.CallErrorCode.Replaced) {
97421
+ var _a;
97422
+ if (call.hangupReason === call_1.CallErrorCode.Replaced)
97377
97423
  return;
97424
+ const opponent = (_a = call.getOpponentMember()) !== null && _a !== void 0 ? _a : this.room.getMember(call.invitee);
97425
+ const deviceMap = this.calls.get(opponent);
97426
+ // Sanity check that this call is in fact in the map
97427
+ if ((deviceMap === null || deviceMap === void 0 ? void 0 : deviceMap.get(call.getOpponentDeviceId())) === call) {
97428
+ this.disposeCall(call, call.hangupReason);
97429
+ deviceMap.delete(call.getOpponentDeviceId());
97430
+ if (deviceMap.size === 0)
97431
+ this.calls.delete(opponent);
97432
+ this.emit(GroupCallEvent.CallsChanged, this.calls);
97378
97433
  }
97379
- this.removeCall(call, call.hangupReason);
97434
+ };
97435
+ this.onCallReplaced = (prevCall, newCall) => {
97436
+ const opponent = prevCall.getOpponentMember();
97437
+ let deviceMap = this.calls.get(opponent);
97438
+ if (deviceMap === undefined) {
97439
+ deviceMap = new Map();
97440
+ this.calls.set(opponent, deviceMap);
97441
+ }
97442
+ this.disposeCall(prevCall, call_1.CallErrorCode.Replaced);
97443
+ this.initCall(newCall);
97444
+ deviceMap.set(prevCall.getOpponentDeviceId(), newCall);
97445
+ this.emit(GroupCallEvent.CallsChanged, this.calls);
97380
97446
  };
97381
97447
  this.onActiveSpeakerLoop = () => {
97382
97448
  let topAvg = undefined;
97383
97449
  let nextActiveSpeaker = undefined;
97384
97450
  for (const callFeed of this.userMediaFeeds) {
97385
- if (callFeed.userId === this.client.getUserId() && this.userMediaFeeds.length > 1) {
97451
+ if (callFeed.isLocal() && this.userMediaFeeds.length > 1)
97386
97452
  continue;
97387
- }
97388
- let total = 0;
97389
- for (let i = 0; i < callFeed.speakingVolumeSamples.length; i++) {
97390
- const volume = callFeed.speakingVolumeSamples[i];
97391
- total += Math.max(volume, callFeed_1.SPEAKING_THRESHOLD);
97392
- }
97453
+ const total = callFeed.speakingVolumeSamples.reduce((acc, volume) => acc + Math.max(volume, callFeed_1.SPEAKING_THRESHOLD));
97393
97454
  const avg = total / callFeed.speakingVolumeSamples.length;
97394
97455
  if (!topAvg || avg > topAvg) {
97395
97456
  topAvg = avg;
97396
- nextActiveSpeaker = callFeed.userId;
97457
+ nextActiveSpeaker = callFeed;
97397
97458
  }
97398
97459
  }
97399
97460
  if (nextActiveSpeaker && this.activeSpeaker !== nextActiveSpeaker && topAvg && topAvg > callFeed_1.SPEAKING_THRESHOLD) {
97400
97461
  this.activeSpeaker = nextActiveSpeaker;
97401
97462
  this.emit(GroupCallEvent.ActiveSpeakerChanged, this.activeSpeaker);
97402
97463
  }
97403
- this.activeSpeakerLoopTimeout = setTimeout(this.onActiveSpeakerLoop, this.activeSpeakerInterval);
97464
+ };
97465
+ this.onRoomState = () => this.updateParticipants();
97466
+ this.onParticipantsChanged = () => {
97467
+ if (this.state === GroupCallState.Entered)
97468
+ this.placeOutgoingCalls();
97469
+ };
97470
+ this.onStateChanged = (newState, oldState) => {
97471
+ if (newState === GroupCallState.Entered
97472
+ || oldState === GroupCallState.Entered
97473
+ || newState === GroupCallState.Ended) {
97474
+ // We either entered, left, or ended the call
97475
+ this.updateParticipants();
97476
+ this.updateMemberState().catch(e => logger_1.logger.error("Failed to update member state devices", e));
97477
+ }
97478
+ };
97479
+ this.onLocalFeedsChanged = () => {
97480
+ if (this.state === GroupCallState.Entered) {
97481
+ this.updateMemberState().catch(e => logger_1.logger.error("Failed to update member state feeds", e));
97482
+ }
97404
97483
  };
97405
97484
  this.reEmitter = new ReEmitter_1.ReEmitter(this);
97406
- this.groupCallId = groupCallId || (0, call_1.genCallID)();
97407
- for (const stateEvent of this.getMemberStateEvents()) {
97408
- this.onMemberStateChanged(stateEvent);
97409
- }
97485
+ this.groupCallId = groupCallId !== null && groupCallId !== void 0 ? groupCallId : (0, call_1.genCallID)();
97486
+ this.creationTs = (_b = (_a = room.currentState.getStateEvents(event_1.EventType.GroupCallPrefix, this.groupCallId)) === null || _a === void 0 ? void 0 : _a.getTs()) !== null && _b !== void 0 ? _b : null;
97487
+ this.updateParticipants();
97488
+ room.on(room_state_1.RoomStateEvent.Update, this.onRoomState);
97489
+ this.on(GroupCallEvent.ParticipantsChanged, this.onParticipantsChanged);
97490
+ this.on(GroupCallEvent.GroupCallStateChanged, this.onStateChanged);
97491
+ this.on(GroupCallEvent.LocalScreenshareStateChanged, this.onLocalFeedsChanged);
97410
97492
  }
97411
97493
  create() {
97412
97494
  return __awaiter(this, void 0, void 0, function* () {
97495
+ this.creationTs = Date.now();
97413
97496
  this.client.groupCallEventHandler.groupCalls.set(this.room.roomId, this);
97497
+ this.client.emit(groupCallEventHandler_1.GroupCallEventHandlerEvent.Outgoing, this);
97414
97498
  yield this.client.sendStateEvent(this.room.roomId, event_1.EventType.GroupCallPrefix, {
97415
97499
  "m.intent": this.intent,
97416
97500
  "m.type": this.type,
@@ -97422,10 +97506,55 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97422
97506
  return this;
97423
97507
  });
97424
97508
  }
97425
- setState(newState) {
97426
- const oldState = this.state;
97427
- this.state = newState;
97428
- this.emit(GroupCallEvent.GroupCallStateChanged, newState, oldState);
97509
+ /**
97510
+ * The group call's state.
97511
+ */
97512
+ get state() {
97513
+ return this._state;
97514
+ }
97515
+ set state(value) {
97516
+ const prevValue = this._state;
97517
+ if (value !== prevValue) {
97518
+ this._state = value;
97519
+ this.emit(GroupCallEvent.GroupCallStateChanged, value, prevValue);
97520
+ }
97521
+ }
97522
+ /**
97523
+ * The current participants in the call, as a map from members to device IDs
97524
+ * to participant info.
97525
+ */
97526
+ get participants() {
97527
+ return this._participants;
97528
+ }
97529
+ set participants(value) {
97530
+ const prevValue = this._participants;
97531
+ const participantStateEqual = (x, y) => x.sessionId === y.sessionId && x.screensharing === y.screensharing;
97532
+ const deviceMapsEqual = (x, y) => (0, utils_1.mapsEqual)(x, y, participantStateEqual);
97533
+ // Only update if the map actually changed
97534
+ if (!(0, utils_1.mapsEqual)(value, prevValue, deviceMapsEqual)) {
97535
+ this._participants = value;
97536
+ this.emit(GroupCallEvent.ParticipantsChanged, value);
97537
+ }
97538
+ }
97539
+ /**
97540
+ * The timestamp at which the call was created, or null if it has not yet
97541
+ * been created.
97542
+ */
97543
+ get creationTs() {
97544
+ return this._creationTs;
97545
+ }
97546
+ set creationTs(value) {
97547
+ this._creationTs = value;
97548
+ }
97549
+ /**
97550
+ * Executes the given callback on all calls in this group call.
97551
+ * @param f The callback.
97552
+ */
97553
+ forEachCall(f) {
97554
+ for (const deviceMap of this.calls.values()) {
97555
+ for (const call of deviceMap.values())
97556
+ f(call);
97557
+ }
97429
97558
  }
97430
97559
  getLocalFeeds() {
97431
97560
  const feeds = [];
@@ -97436,8 +97565,8 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97436
97565
  return feeds;
97437
97566
  }
97438
97567
  hasLocalParticipant() {
97439
- const userId = this.client.getUserId();
97440
- return this.participants.some((member) => member.userId === userId);
97568
+ var _a, _b;
97569
+ return (_b = (_a = this.participants.get(this.room.getMember(this.client.getUserId()))) === null || _a === void 0 ? void 0 : _a.has(this.client.getDeviceId())) !== null && _b !== void 0 ? _b : false;
97441
97570
  }
97442
97571
  initLocalCallFeed() {
97443
97572
  return __awaiter(this, void 0, void 0, function* () {
@@ -97445,7 +97574,7 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97445
97574
  if (this.state !== GroupCallState.LocalCallFeedUninitialized) {
97446
97575
  throw new Error(`Cannot initialize local call feed in the "${this.state}" state.`);
97447
97576
  }
97448
- this.setState(GroupCallState.InitializingLocalCallFeed);
97577
+ this.state = GroupCallState.InitializingLocalCallFeed;
97449
97578
  let stream;
97450
97579
  let disposed = false;
97451
97580
  const onState = (state) => {
@@ -97458,7 +97587,7 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97458
97587
  stream = yield this.client.getMediaHandler().getUserMediaStream(true, this.type === GroupCallType.Video);
97459
97588
  }
97460
97589
  catch (error) {
97461
- this.setState(GroupCallState.LocalCallFeedUninitialized);
97590
+ this.state = GroupCallState.LocalCallFeedUninitialized;
97462
97591
  throw error;
97463
97592
  }
97464
97593
  finally {
@@ -97467,11 +97596,11 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97467
97596
  // The call could've been disposed while we were waiting
97468
97597
  if (disposed)
97469
97598
  throw new Error("Group call disposed");
97470
- const userId = this.client.getUserId();
97471
97599
  const callFeed = new callFeed_1.CallFeed({
97472
97600
  client: this.client,
97473
97601
  roomId: this.room.roomId,
97474
- userId,
97602
+ userId: this.client.getUserId(),
97603
+ deviceId: this.client.getDeviceId(),
97475
97604
  stream,
97476
97605
  purpose: callEventTypes_1.SDPStreamMetadataPurpose.Usermedia,
97477
97606
  audioMuted: this.initWithAudioMuted || stream.getAudioTracks().length === 0 || this.isPtt,
@@ -97481,7 +97610,7 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97481
97610
  (0, call_1.setTracksEnabled)(stream.getVideoTracks(), !callFeed.isVideoMuted());
97482
97611
  this.localCallFeed = callFeed;
97483
97612
  this.addUserMediaFeed(callFeed);
97484
- this.setState(GroupCallState.LocalCallFeedInitialized);
97613
+ this.state = GroupCallState.LocalCallFeedInitialized;
97485
97614
  return callFeed;
97486
97615
  });
97487
97616
  }
@@ -97501,30 +97630,22 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97501
97630
  }
97502
97631
  enter() {
97503
97632
  return __awaiter(this, void 0, void 0, function* () {
97504
- if (!(this.state === GroupCallState.LocalCallFeedUninitialized ||
97505
- this.state === GroupCallState.LocalCallFeedInitialized)) {
97506
- throw new Error(`Cannot enter call in the "${this.state}" state`);
97507
- }
97508
97633
  if (this.state === GroupCallState.LocalCallFeedUninitialized) {
97509
97634
  yield this.initLocalCallFeed();
97510
97635
  }
97511
- this.addParticipant(this.room.getMember(this.client.getUserId()));
97512
- yield this.sendMemberStateEvent();
97513
- this.activeSpeaker = undefined;
97514
- this.setState(GroupCallState.Entered);
97636
+ else if (this.state !== GroupCallState.LocalCallFeedInitialized) {
97637
+ throw new Error(`Cannot enter call in the "${this.state}" state`);
97638
+ }
97515
97639
  logger_1.logger.log(`Entered group call ${this.groupCallId}`);
97640
+ this.state = GroupCallState.Entered;
97516
97641
  this.client.on(callEventHandler_1.CallEventHandlerEvent.Incoming, this.onIncomingCall);
97517
- const calls = this.client.callEventHandler.calls.values();
97518
- for (const call of calls) {
97642
+ for (const call of this.client.callEventHandler.calls.values()) {
97519
97643
  this.onIncomingCall(call);
97520
97644
  }
97521
- // Set up participants for the members currently in the room.
97522
- // Other members will be picked up by the RoomState.members event.
97523
- for (const stateEvent of this.getMemberStateEvents()) {
97524
- this.onMemberStateChanged(stateEvent);
97525
- }
97526
- this.retryCallLoopTimeout = setTimeout(this.onRetryCallLoop, this.retryCallInterval);
97645
+ this.retryCallLoopInterval = setInterval(this.onRetryCallLoop, this.retryCallInterval);
97646
+ this.activeSpeaker = undefined;
97527
97647
  this.onActiveSpeakerLoop();
97648
+ this.activeSpeakerLoopInterval = setInterval(this.onActiveSpeakerLoop, this.activeSpeakerInterval);
97528
97649
  });
97529
97650
  }
97530
97651
  dispose() {
@@ -97539,54 +97660,43 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97539
97660
  this.localDesktopCapturerSourceId = undefined;
97540
97661
  }
97541
97662
  this.client.getMediaHandler().stopAllStreams();
97663
+ if (this.transmitTimer !== null) {
97664
+ clearTimeout(this.transmitTimer);
97665
+ this.transmitTimer = null;
97666
+ }
97667
+ if (this.retryCallLoopInterval !== undefined) {
97668
+ clearInterval(this.retryCallLoopInterval);
97669
+ this.retryCallLoopInterval = undefined;
97670
+ }
97542
97671
  if (this.state !== GroupCallState.Entered) {
97543
97672
  return;
97544
97673
  }
97545
- this.removeParticipant(this.room.getMember(this.client.getUserId()));
97546
- this.removeMemberStateEvent();
97547
- while (this.calls.length > 0) {
97548
- this.removeCall(this.calls[this.calls.length - 1], call_1.CallErrorCode.UserHangup);
97549
- }
97674
+ this.forEachCall(call => this.disposeCall(call, call_1.CallErrorCode.UserHangup));
97675
+ this.calls.clear();
97550
97676
  this.activeSpeaker = undefined;
97551
- clearTimeout(this.activeSpeakerLoopTimeout);
97677
+ clearInterval(this.activeSpeakerLoopInterval);
97552
97678
  this.retryCallCounts.clear();
97553
- clearTimeout(this.retryCallLoopTimeout);
97554
- for (const [userId] of this.memberStateExpirationTimers) {
97555
- clearTimeout(this.memberStateExpirationTimers.get(userId));
97556
- this.memberStateExpirationTimers.delete(userId);
97557
- }
97558
- if (this.transmitTimer !== null) {
97559
- clearTimeout(this.transmitTimer);
97560
- this.transmitTimer = null;
97561
- }
97679
+ clearInterval(this.retryCallLoopInterval);
97562
97680
  this.client.removeListener(callEventHandler_1.CallEventHandlerEvent.Incoming, this.onIncomingCall);
97563
97681
  }
97564
97682
  leave() {
97565
- if (this.transmitTimer !== null) {
97566
- clearTimeout(this.transmitTimer);
97567
- this.transmitTimer = null;
97568
- }
97569
97683
  this.dispose();
97570
- this.setState(GroupCallState.LocalCallFeedUninitialized);
97684
+ this.state = GroupCallState.LocalCallFeedUninitialized;
97571
97685
  }
97572
97686
  terminate(emitStateEvent = true) {
97573
97687
  return __awaiter(this, void 0, void 0, function* () {
97574
97688
  this.dispose();
97575
- if (this.transmitTimer !== null) {
97576
- clearTimeout(this.transmitTimer);
97577
- this.transmitTimer = null;
97578
- }
97579
- this.participants = [];
97689
+ this.room.off(room_state_1.RoomStateEvent.Update, this.onRoomState);
97580
97690
  this.client.groupCallEventHandler.groupCalls.delete(this.room.roomId);
97691
+ this.client.emit(groupCallEventHandler_1.GroupCallEventHandlerEvent.Ended, this);
97692
+ this.state = GroupCallState.Ended;
97581
97693
  if (emitStateEvent) {
97582
97694
  const existingStateEvent = this.room.currentState.getStateEvents(event_1.EventType.GroupCallPrefix, this.groupCallId);
97583
- yield this.client.sendStateEvent(this.room.roomId, event_1.EventType.GroupCallPrefix, Object.assign(Object.assign({}, existingStateEvent.getContent()), { ["m.terminated"]: GroupCallTerminationReason.CallEnded }), this.groupCallId);
97695
+ yield this.client.sendStateEvent(this.room.roomId, event_1.EventType.GroupCallPrefix, Object.assign(Object.assign({}, existingStateEvent.getContent()), { "m.terminated": GroupCallTerminationReason.CallEnded }), this.groupCallId);
97584
97696
  }
97585
- this.client.emit(groupCallEventHandler_1.GroupCallEventHandlerEvent.Ended, this);
97586
- this.setState(GroupCallState.Ended);
97587
97697
  });
97588
97698
  }
97589
- /**
97699
+ /*
97590
97700
  * Local Usermedia
97591
97701
  */
97592
97702
  isLocalVideoMuted() {
@@ -97607,7 +97717,6 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97607
97717
  * @returns {Promise<boolean>} Whether muting/unmuting was successful
97608
97718
  */
97609
97719
  setMicrophoneMuted(muted) {
97610
- var _a;
97611
97720
  return __awaiter(this, void 0, void 0, function* () {
97612
97721
  // hasAudioDevice can block indefinitely if the window has lost focus,
97613
97722
  // and it doesn't make much sense to keep a device from being muted, so
@@ -97630,17 +97739,14 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97630
97739
  this.transmitTimer = null;
97631
97740
  }
97632
97741
  }
97633
- for (const call of this.calls) {
97634
- (_a = call.localUsermediaFeed) === null || _a === void 0 ? void 0 : _a.setAudioVideoMuted(muted, null);
97635
- }
97636
- if (sendUpdatesBefore) {
97637
- try {
97638
- yield Promise.all(this.calls.map(c => c.sendMetadataUpdate()));
97639
- }
97640
- catch (e) {
97641
- logger_1.logger.info("Failed to send one or more metadata updates", e);
97642
- }
97643
- }
97742
+ this.forEachCall(call => { var _a; return (_a = call.localUsermediaFeed) === null || _a === void 0 ? void 0 : _a.setAudioVideoMuted(muted, null); });
97743
+ const sendUpdates = () => __awaiter(this, void 0, void 0, function* () {
97744
+ const updates = [];
97745
+ this.forEachCall(call => updates.push(call.sendMetadataUpdate()));
97746
+ yield Promise.all(updates).catch(e => logger_1.logger.info("Failed to send some metadata updates", e));
97747
+ });
97748
+ if (sendUpdatesBefore)
97749
+ yield sendUpdates();
97644
97750
  if (this.localCallFeed) {
97645
97751
  logger_1.logger.log(`groupCall ${this.groupCallId} setMicrophoneMuted stream ${this.localCallFeed.stream.id} muted ${muted}`);
97646
97752
  this.localCallFeed.setAudioVideoMuted(muted, null);
@@ -97654,18 +97760,10 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97654
97760
  logger_1.logger.log(`groupCall ${this.groupCallId} setMicrophoneMuted no stream muted ${muted}`);
97655
97761
  this.initWithAudioMuted = muted;
97656
97762
  }
97657
- for (const call of this.calls) {
97658
- (0, call_1.setTracksEnabled)(call.localUsermediaFeed.stream.getAudioTracks(), !muted);
97659
- }
97763
+ this.forEachCall(call => (0, call_1.setTracksEnabled)(call.localUsermediaFeed.stream.getAudioTracks(), !muted));
97660
97764
  this.emit(GroupCallEvent.LocalMuteStateChanged, muted, this.isLocalVideoMuted());
97661
- if (!sendUpdatesBefore) {
97662
- try {
97663
- yield Promise.all(this.calls.map(c => c.sendMetadataUpdate()));
97664
- }
97665
- catch (e) {
97666
- logger_1.logger.info("Failed to send one or more metadata updates", e);
97667
- }
97668
- }
97765
+ if (!sendUpdatesBefore)
97766
+ yield sendUpdates();
97669
97767
  return true;
97670
97768
  });
97671
97769
  }
@@ -97691,9 +97789,9 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97691
97789
  logger_1.logger.log(`groupCall ${this.groupCallId} setLocalVideoMuted no stream muted ${muted}`);
97692
97790
  this.initWithVideoMuted = muted;
97693
97791
  }
97694
- for (const call of this.calls) {
97695
- call.setLocalVideoMuted(muted);
97696
- }
97792
+ const updates = [];
97793
+ this.forEachCall(call => updates.push(call.setLocalVideoMuted(muted)));
97794
+ yield Promise.all(updates);
97697
97795
  this.emit(GroupCallEvent.LocalMuteStateChanged, this.isMicrophoneMuted(), muted);
97698
97796
  return true;
97699
97797
  });
@@ -97720,6 +97818,7 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97720
97818
  client: this.client,
97721
97819
  roomId: this.room.roomId,
97722
97820
  userId: this.client.getUserId(),
97821
+ deviceId: this.client.getDeviceId(),
97723
97822
  stream,
97724
97823
  purpose: callEventTypes_1.SDPStreamMetadataPurpose.Screenshare,
97725
97824
  audioMuted: false,
@@ -97728,8 +97827,7 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97728
97827
  this.addScreenshareFeed(this.localScreenshareFeed);
97729
97828
  this.emit(GroupCallEvent.LocalScreenshareStateChanged, true, this.localScreenshareFeed, this.localDesktopCapturerSourceId);
97730
97829
  // TODO: handle errors
97731
- yield Promise.all(this.calls.map(call => call.pushLocalFeed(this.localScreenshareFeed.clone())));
97732
- yield this.sendMemberStateEvent();
97830
+ this.forEachCall(call => call.pushLocalFeed(this.localScreenshareFeed.clone()));
97733
97831
  return true;
97734
97832
  }
97735
97833
  catch (error) {
@@ -97741,15 +97839,14 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97741
97839
  }
97742
97840
  }
97743
97841
  else {
97744
- yield Promise.all(this.calls.map(call => {
97842
+ this.forEachCall(call => {
97745
97843
  if (call.localScreensharingFeed)
97746
97844
  call.removeLocalFeed(call.localScreensharingFeed);
97747
- }));
97845
+ });
97748
97846
  this.client.getMediaHandler().stopScreensharingStream(this.localScreenshareFeed.stream);
97749
97847
  this.removeScreenshareFeed(this.localScreenshareFeed);
97750
97848
  this.localScreenshareFeed = undefined;
97751
97849
  this.localDesktopCapturerSourceId = undefined;
97752
- yield this.sendMemberStateEvent();
97753
97850
  this.emit(GroupCallEvent.LocalScreenshareStateChanged, false, undefined, undefined);
97754
97851
  return false;
97755
97852
  }
@@ -97758,128 +97855,86 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97758
97855
  isScreensharing() {
97759
97856
  return !!this.localScreenshareFeed;
97760
97857
  }
97761
- getMemberStateEvents(userId) {
97762
- if (userId != null) {
97763
- const event = this.room.currentState.getStateEvents(event_1.EventType.GroupCallMemberPrefix, userId);
97764
- return callMemberStateIsExpired(event) ? null : event;
97765
- }
97766
- else {
97767
- return this.room.currentState.getStateEvents(event_1.EventType.GroupCallMemberPrefix)
97768
- .filter(event => !callMemberStateIsExpired(event));
97769
- }
97770
- }
97771
- sendMemberStateEvent() {
97772
- return __awaiter(this, void 0, void 0, function* () {
97773
- const send = () => this.updateMemberCallState({
97774
- "m.call_id": this.groupCallId,
97775
- "m.devices": [
97776
- {
97777
- "device_id": this.client.getDeviceId(),
97778
- "session_id": this.client.getSessionId(),
97779
- "feeds": this.getLocalFeeds().map((feed) => ({
97780
- purpose: feed.purpose,
97781
- })),
97782
- // TODO: Add data channels
97783
- },
97784
- ],
97785
- // TODO "m.foci"
97786
- });
97787
- const res = yield send();
97788
- // Clear the old interval first, so that it isn't forgot
97789
- if (this.resendMemberStateTimer !== null)
97790
- clearInterval(this.resendMemberStateTimer);
97791
- // Resend the state event every so often so it doesn't become stale
97792
- this.resendMemberStateTimer = setInterval(() => __awaiter(this, void 0, void 0, function* () {
97793
- logger_1.logger.log("Resending call member state");
97794
- yield send();
97795
- }), CALL_MEMBER_STATE_TIMEOUT * 3 / 4);
97796
- return res;
97797
- });
97798
- }
97799
- removeMemberStateEvent() {
97800
- return __awaiter(this, void 0, void 0, function* () {
97801
- if (this.resendMemberStateTimer !== null)
97802
- clearInterval(this.resendMemberStateTimer);
97803
- this.resendMemberStateTimer = null;
97804
- return yield this.updateMemberCallState(undefined, true);
97805
- });
97858
+ /**
97859
+ * Determines whether a given participant expects us to call them (versus
97860
+ * them calling us).
97861
+ * @param userId The participant's user ID.
97862
+ * @param deviceId The participant's device ID.
97863
+ * @returns Whether we need to place an outgoing call to the participant.
97864
+ */
97865
+ wantsOutgoingCall(userId, deviceId) {
97866
+ const localUserId = this.client.getUserId();
97867
+ const localDeviceId = this.client.getDeviceId();
97868
+ return (
97869
+ // If a user's ID is less than our own, they'll call us
97870
+ userId >= localUserId
97871
+ // If this is another one of our devices, compare device IDs to tell whether it'll call us
97872
+ && (userId !== localUserId || deviceId > localDeviceId));
97806
97873
  }
97807
- updateMemberCallState(memberCallState, keepAlive = false) {
97874
+ /**
97875
+ * Places calls to all participants that we're responsible for calling.
97876
+ */
97877
+ placeOutgoingCalls() {
97808
97878
  var _a;
97809
- return __awaiter(this, void 0, void 0, function* () {
97810
- const localUserId = this.client.getUserId();
97811
- const memberState = (_a = this.getMemberStateEvents(localUserId)) === null || _a === void 0 ? void 0 : _a.getContent();
97812
- let calls = [];
97813
- // Sanitize existing member state event
97814
- if (memberState && Array.isArray(memberState["m.calls"])) {
97815
- calls = memberState["m.calls"].filter((call) => !!call);
97816
- }
97817
- const existingCallIndex = calls.findIndex((call) => call && call["m.call_id"] === this.groupCallId);
97818
- if (existingCallIndex !== -1) {
97819
- if (memberCallState) {
97820
- calls.splice(existingCallIndex, 1, memberCallState);
97821
- }
97822
- else {
97823
- calls.splice(existingCallIndex, 1);
97879
+ let callsChanged = false;
97880
+ for (const [member, participantMap] of this.participants) {
97881
+ const callMap = (_a = this.calls.get(member)) !== null && _a !== void 0 ? _a : new Map();
97882
+ for (const [deviceId, participant] of participantMap) {
97883
+ const prevCall = callMap.get(deviceId);
97884
+ if ((prevCall === null || prevCall === void 0 ? void 0 : prevCall.getOpponentSessionId()) !== participant.sessionId
97885
+ && this.wantsOutgoingCall(member.userId, deviceId)) {
97886
+ callsChanged = true;
97887
+ if (prevCall !== undefined) {
97888
+ logger_1.logger.debug(`Replacing call ${prevCall.callId} to ${member.userId} ${deviceId}`);
97889
+ this.disposeCall(prevCall, call_1.CallErrorCode.NewSession);
97890
+ }
97891
+ const newCall = (0, call_1.createNewMatrixCall)(this.client, this.room.roomId, {
97892
+ invitee: member.userId,
97893
+ opponentDeviceId: deviceId,
97894
+ opponentSessionId: participant.sessionId,
97895
+ groupCallId: this.groupCallId,
97896
+ });
97897
+ if (newCall === null) {
97898
+ logger_1.logger.error(`Failed to create call with ${member.userId} ${deviceId}`);
97899
+ callMap.delete(deviceId);
97900
+ }
97901
+ else {
97902
+ this.initCall(newCall);
97903
+ callMap.set(deviceId, newCall);
97904
+ logger_1.logger.debug(`Placing call to ${member.userId} ${deviceId} (session ${participant.sessionId})`);
97905
+ newCall.placeCallWithCallFeeds(this.getLocalFeeds().map(feed => feed.clone()), participant.screensharing).then(() => {
97906
+ if (this.dataChannelsEnabled) {
97907
+ newCall.createDataChannel("datachannel", this.dataChannelOptions);
97908
+ }
97909
+ }).catch(e => {
97910
+ logger_1.logger.warn(`Failed to place call to ${member.userId}`, e);
97911
+ if (e instanceof call_1.CallError && e.code === GroupCallErrorCode.UnknownDevice) {
97912
+ this.emit(GroupCallEvent.Error, e);
97913
+ }
97914
+ else {
97915
+ this.emit(GroupCallEvent.Error, new GroupCallError(GroupCallErrorCode.PlaceCallFailed, `Failed to place call to ${member.userId}`));
97916
+ }
97917
+ this.disposeCall(newCall, call_1.CallErrorCode.SignallingFailed);
97918
+ if (callMap.get(deviceId) === newCall)
97919
+ callMap.delete(deviceId);
97920
+ });
97921
+ }
97824
97922
  }
97825
97923
  }
97826
- else if (memberCallState) {
97827
- calls.push(memberCallState);
97924
+ if (callMap.size > 0) {
97925
+ this.calls.set(member, callMap);
97926
+ }
97927
+ else {
97928
+ this.calls.delete(member);
97828
97929
  }
97829
- const content = {
97830
- "m.calls": calls,
97831
- "m.expires_ts": Date.now() + CALL_MEMBER_STATE_TIMEOUT,
97832
- };
97833
- return this.client.sendStateEvent(this.room.roomId, event_1.EventType.GroupCallMemberPrefix, content, localUserId, { keepAlive });
97834
- });
97835
- }
97836
- getDeviceForMember(userId) {
97837
- var _a;
97838
- const memberStateEvent = this.getMemberStateEvents(userId);
97839
- if (!memberStateEvent) {
97840
- return undefined;
97841
- }
97842
- const memberState = memberStateEvent.getContent();
97843
- const memberGroupCallState = (_a = memberState["m.calls"]) === null || _a === void 0 ? void 0 : _a.find((call) => call && call["m.call_id"] === this.groupCallId);
97844
- if (!memberGroupCallState) {
97845
- return undefined;
97846
- }
97847
- const memberDevices = memberGroupCallState["m.devices"];
97848
- if (!memberDevices || memberDevices.length === 0) {
97849
- return undefined;
97850
- }
97851
- // NOTE: For now we only support one device so we use the device id in the first source.
97852
- return memberDevices[0];
97853
- }
97854
- /**
97855
- * Call Event Handlers
97856
- */
97857
- getCallByUserId(userId) {
97858
- return this.calls.find((call) => getCallUserId(call) === userId);
97859
- }
97860
- addCall(call) {
97861
- this.calls.push(call);
97862
- this.initCall(call);
97863
- this.emit(GroupCallEvent.CallsChanged, this.calls);
97864
- }
97865
- replaceCall(existingCall, replacementCall, hangupReason = call_1.CallErrorCode.Replaced) {
97866
- const existingCallIndex = this.calls.indexOf(existingCall);
97867
- if (existingCallIndex === -1) {
97868
- throw new Error("Couldn't find call to replace");
97869
97930
  }
97870
- this.calls.splice(existingCallIndex, 1, replacementCall);
97871
- this.disposeCall(existingCall, hangupReason);
97872
- this.initCall(replacementCall);
97873
- this.emit(GroupCallEvent.CallsChanged, this.calls);
97931
+ if (callsChanged)
97932
+ this.emit(GroupCallEvent.CallsChanged, this.calls);
97874
97933
  }
97875
- removeCall(call, hangupReason) {
97876
- this.disposeCall(call, hangupReason);
97877
- const callIndex = this.calls.indexOf(call);
97878
- if (callIndex === -1) {
97879
- throw new Error("Couldn't find call to remove");
97880
- }
97881
- this.calls.splice(callIndex, 1);
97882
- this.emit(GroupCallEvent.CallsChanged, this.calls);
97934
+ getMemberStateEvents(userId) {
97935
+ return userId === undefined
97936
+ ? this.room.currentState.getStateEvents(event_1.EventType.GroupCallMemberPrefix)
97937
+ : this.room.currentState.getStateEvents(event_1.EventType.GroupCallMemberPrefix, userId);
97883
97938
  }
97884
97939
  initCall(call) {
97885
97940
  const opponentMemberId = getCallUserId(call);
@@ -97889,8 +97944,13 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97889
97944
  const onCallFeedsChanged = () => this.onCallFeedsChanged(call);
97890
97945
  const onCallStateChanged = (state, oldState) => this.onCallStateChanged(call, state, oldState);
97891
97946
  const onCallHangup = this.onCallHangup;
97892
- const onCallReplaced = (newCall) => this.replaceCall(call, newCall);
97893
- this.callHandlers.set(opponentMemberId, {
97947
+ const onCallReplaced = (newCall) => this.onCallReplaced(call, newCall);
97948
+ let deviceMap = this.callHandlers.get(opponentMemberId);
97949
+ if (deviceMap === undefined) {
97950
+ deviceMap = new Map();
97951
+ this.callHandlers.set(opponentMemberId, deviceMap);
97952
+ }
97953
+ deviceMap.set(call.getOpponentDeviceId(), {
97894
97954
  onCallFeedsChanged,
97895
97955
  onCallStateChanged,
97896
97956
  onCallHangup,
@@ -97900,40 +97960,45 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97900
97960
  call.on(call_1.CallEvent.State, onCallStateChanged);
97901
97961
  call.on(call_1.CallEvent.Hangup, onCallHangup);
97902
97962
  call.on(call_1.CallEvent.Replaced, onCallReplaced);
97963
+ call.isPtt = this.isPtt;
97903
97964
  this.reEmitter.reEmit(call, Object.values(call_1.CallEvent));
97904
97965
  onCallFeedsChanged();
97905
97966
  }
97906
97967
  disposeCall(call, hangupReason) {
97907
97968
  const opponentMemberId = getCallUserId(call);
97969
+ const opponentDeviceId = call.getOpponentDeviceId();
97908
97970
  if (!opponentMemberId) {
97909
97971
  throw new Error("Cannot dispose call without user id");
97910
97972
  }
97911
- const { onCallFeedsChanged, onCallStateChanged, onCallHangup, onCallReplaced, } = this.callHandlers.get(opponentMemberId);
97973
+ const deviceMap = this.callHandlers.get(opponentMemberId);
97974
+ const { onCallFeedsChanged, onCallStateChanged, onCallHangup, onCallReplaced, } = deviceMap.get(opponentDeviceId);
97912
97975
  call.removeListener(call_1.CallEvent.FeedsChanged, onCallFeedsChanged);
97913
97976
  call.removeListener(call_1.CallEvent.State, onCallStateChanged);
97914
97977
  call.removeListener(call_1.CallEvent.Hangup, onCallHangup);
97915
97978
  call.removeListener(call_1.CallEvent.Replaced, onCallReplaced);
97916
- this.callHandlers.delete(opponentMemberId);
97979
+ deviceMap.delete(opponentMemberId);
97980
+ if (deviceMap.size === 0)
97981
+ this.callHandlers.delete(opponentMemberId);
97917
97982
  if (call.hangupReason === call_1.CallErrorCode.Replaced) {
97918
97983
  return;
97919
97984
  }
97920
97985
  if (call.state !== call_1.CallState.Ended) {
97921
97986
  call.hangup(hangupReason, false);
97922
97987
  }
97923
- const usermediaFeed = this.getUserMediaFeedByUserId(opponentMemberId);
97988
+ const usermediaFeed = this.getUserMediaFeed(opponentMemberId, opponentDeviceId);
97924
97989
  if (usermediaFeed) {
97925
97990
  this.removeUserMediaFeed(usermediaFeed);
97926
97991
  }
97927
- const screenshareFeed = this.getScreenshareFeedByUserId(opponentMemberId);
97992
+ const screenshareFeed = this.getScreenshareFeed(opponentMemberId, opponentDeviceId);
97928
97993
  if (screenshareFeed) {
97929
97994
  this.removeScreenshareFeed(screenshareFeed);
97930
97995
  }
97931
97996
  }
97932
- /**
97997
+ /*
97933
97998
  * UserMedia CallFeed Event Handlers
97934
97999
  */
97935
- getUserMediaFeedByUserId(userId) {
97936
- return this.userMediaFeeds.find((feed) => feed.userId === userId);
98000
+ getUserMediaFeed(userId, deviceId) {
98001
+ return this.userMediaFeeds.find(f => f.userId === userId && f.deviceId === deviceId);
97937
98002
  }
97938
98003
  addUserMediaFeed(callFeed) {
97939
98004
  this.userMediaFeeds.push(callFeed);
@@ -97941,7 +98006,7 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97941
98006
  this.emit(GroupCallEvent.UserMediaFeedsChanged, this.userMediaFeeds);
97942
98007
  }
97943
98008
  replaceUserMediaFeed(existingFeed, replacementFeed) {
97944
- const feedIndex = this.userMediaFeeds.findIndex((feed) => feed.userId === existingFeed.userId);
98009
+ const feedIndex = this.userMediaFeeds.findIndex(f => f.userId === existingFeed.userId && f.deviceId === existingFeed.deviceId);
97945
98010
  if (feedIndex === -1) {
97946
98011
  throw new Error("Couldn't find user media feed to replace");
97947
98012
  }
@@ -97951,31 +98016,30 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97951
98016
  this.emit(GroupCallEvent.UserMediaFeedsChanged, this.userMediaFeeds);
97952
98017
  }
97953
98018
  removeUserMediaFeed(callFeed) {
97954
- const feedIndex = this.userMediaFeeds.findIndex((feed) => feed.userId === callFeed.userId);
98019
+ const feedIndex = this.userMediaFeeds.findIndex(f => f.userId === callFeed.userId && f.deviceId === callFeed.deviceId);
97955
98020
  if (feedIndex === -1) {
97956
98021
  throw new Error("Couldn't find user media feed to remove");
97957
98022
  }
97958
98023
  this.userMediaFeeds.splice(feedIndex, 1);
97959
98024
  callFeed.dispose();
97960
98025
  this.emit(GroupCallEvent.UserMediaFeedsChanged, this.userMediaFeeds);
97961
- if (this.activeSpeaker === callFeed.userId &&
97962
- this.userMediaFeeds.length > 0) {
97963
- this.activeSpeaker = this.userMediaFeeds[0].userId;
98026
+ if (this.activeSpeaker === callFeed) {
98027
+ this.activeSpeaker = this.userMediaFeeds[0];
97964
98028
  this.emit(GroupCallEvent.ActiveSpeakerChanged, this.activeSpeaker);
97965
98029
  }
97966
98030
  }
97967
- /**
98031
+ /*
97968
98032
  * Screenshare Call Feed Event Handlers
97969
98033
  */
97970
- getScreenshareFeedByUserId(userId) {
97971
- return this.screenshareFeeds.find((feed) => feed.userId === userId);
98034
+ getScreenshareFeed(userId, deviceId) {
98035
+ return this.screenshareFeeds.find(f => f.userId === userId && f.deviceId === deviceId);
97972
98036
  }
97973
98037
  addScreenshareFeed(callFeed) {
97974
98038
  this.screenshareFeeds.push(callFeed);
97975
98039
  this.emit(GroupCallEvent.ScreenshareFeedsChanged, this.screenshareFeeds);
97976
98040
  }
97977
98041
  replaceScreenshareFeed(existingFeed, replacementFeed) {
97978
- const feedIndex = this.screenshareFeeds.findIndex((feed) => feed.userId === existingFeed.userId);
98042
+ const feedIndex = this.screenshareFeeds.findIndex(f => f.userId === existingFeed.userId && f.deviceId === existingFeed.deviceId);
97979
98043
  if (feedIndex === -1) {
97980
98044
  throw new Error("Couldn't find screenshare feed to replace");
97981
98045
  }
@@ -97984,7 +98048,7 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97984
98048
  this.emit(GroupCallEvent.ScreenshareFeedsChanged, this.screenshareFeeds);
97985
98049
  }
97986
98050
  removeScreenshareFeed(callFeed) {
97987
- const feedIndex = this.screenshareFeeds.findIndex((feed) => feed.userId === callFeed.userId);
98051
+ const feedIndex = this.screenshareFeeds.findIndex(f => f.userId === callFeed.userId && f.deviceId === callFeed.deviceId);
97988
98052
  if (feedIndex === -1) {
97989
98053
  throw new Error("Couldn't find screenshare feed to remove");
97990
98054
  }
@@ -97993,29 +98057,180 @@ class GroupCall extends typed_event_emitter_1.TypedEventEmitter {
97993
98057
  this.emit(GroupCallEvent.ScreenshareFeedsChanged, this.screenshareFeeds);
97994
98058
  }
97995
98059
  /**
97996
- * Participant Management
98060
+ * Recalculates and updates the participant map to match the room state.
97997
98061
  */
97998
- addParticipant(member) {
97999
- if (this.participants.find((m) => m.userId === member.userId)) {
98000
- return;
98062
+ updateParticipants() {
98063
+ if (this.participantsExpirationTimer !== null) {
98064
+ clearTimeout(this.participantsExpirationTimer);
98065
+ this.participantsExpirationTimer = null;
98001
98066
  }
98002
- this.participants.push(member);
98003
- this.emit(GroupCallEvent.ParticipantsChanged, this.participants);
98004
- this.client.emit(groupCallEventHandler_1.GroupCallEventHandlerEvent.Participants, this.participants, this);
98005
- }
98006
- removeParticipant(member) {
98007
- const index = this.participants.findIndex((m) => m.userId === member.userId);
98008
- if (index === -1) {
98067
+ if (this.state === GroupCallState.Ended) {
98068
+ this.participants = new Map();
98009
98069
  return;
98010
98070
  }
98011
- this.participants.splice(index, 1);
98012
- this.emit(GroupCallEvent.ParticipantsChanged, this.participants);
98013
- this.client.emit(groupCallEventHandler_1.GroupCallEventHandlerEvent.Participants, this.participants, this);
98071
+ const participants = new Map();
98072
+ const now = Date.now();
98073
+ const entered = this.state === GroupCallState.Entered;
98074
+ let nextExpiration = Infinity;
98075
+ for (const e of this.getMemberStateEvents()) {
98076
+ const member = this.room.getMember(e.getStateKey());
98077
+ const content = e.getContent();
98078
+ const calls = Array.isArray(content["m.calls"]) ? content["m.calls"] : [];
98079
+ const call = calls.find(call => call["m.call_id"] === this.groupCallId);
98080
+ const devices = Array.isArray(call === null || call === void 0 ? void 0 : call["m.devices"]) ? call["m.devices"] : [];
98081
+ // Filter out invalid and expired devices
98082
+ let validDevices = devices.filter(d => (typeof d.device_id === "string"
98083
+ && typeof d.session_id === "string"
98084
+ && typeof d.expires_ts === "number"
98085
+ && d.expires_ts > now
98086
+ && Array.isArray(d.feeds)));
98087
+ // Apply local echo for the unentered case
98088
+ if (!entered && (member === null || member === void 0 ? void 0 : member.userId) === this.client.getUserId()) {
98089
+ validDevices = validDevices.filter(d => d.device_id !== this.client.getDeviceId());
98090
+ }
98091
+ // Must have a connected device and be joined to the room
98092
+ if (validDevices.length > 0 && (member === null || member === void 0 ? void 0 : member.membership) === "join") {
98093
+ const deviceMap = new Map();
98094
+ participants.set(member, deviceMap);
98095
+ for (const d of validDevices) {
98096
+ deviceMap.set(d.device_id, {
98097
+ sessionId: d.session_id,
98098
+ screensharing: d.feeds.some(f => f.purpose === callEventTypes_1.SDPStreamMetadataPurpose.Screenshare),
98099
+ });
98100
+ if (d.expires_ts < nextExpiration)
98101
+ nextExpiration = d.expires_ts;
98102
+ }
98103
+ }
98104
+ }
98105
+ // Apply local echo for the entered case
98106
+ if (entered) {
98107
+ const localMember = this.room.getMember(this.client.getUserId());
98108
+ let deviceMap = participants.get(localMember);
98109
+ if (deviceMap === undefined) {
98110
+ deviceMap = new Map();
98111
+ participants.set(localMember, deviceMap);
98112
+ }
98113
+ if (!deviceMap.has(this.client.getDeviceId())) {
98114
+ deviceMap.set(this.client.getDeviceId(), {
98115
+ sessionId: this.client.getSessionId(),
98116
+ screensharing: this.getLocalFeeds().some(f => f.purpose === callEventTypes_1.SDPStreamMetadataPurpose.Screenshare),
98117
+ });
98118
+ }
98119
+ }
98120
+ this.participants = participants;
98121
+ if (nextExpiration < Infinity) {
98122
+ this.participantsExpirationTimer = setTimeout(() => this.updateParticipants(), nextExpiration - now);
98123
+ }
98124
+ }
98125
+ /**
98126
+ * Updates the local user's member state with the devices returned by the given function.
98127
+ * @param fn A function from the current devices to the new devices. If it
98128
+ * returns null, the update will be skipped.
98129
+ * @param keepAlive Whether the request should outlive the window.
98130
+ */
98131
+ updateDevices(fn, keepAlive = false) {
98132
+ var _a;
98133
+ return __awaiter(this, void 0, void 0, function* () {
98134
+ const now = Date.now();
98135
+ const localUserId = this.client.getUserId();
98136
+ const event = this.getMemberStateEvents(localUserId);
98137
+ const content = (_a = event === null || event === void 0 ? void 0 : event.getContent()) !== null && _a !== void 0 ? _a : {};
98138
+ const calls = Array.isArray(content["m.calls"]) ? content["m.calls"] : [];
98139
+ let call = null;
98140
+ const otherCalls = [];
98141
+ for (const c of calls) {
98142
+ if (c["m.call_id"] === this.groupCallId) {
98143
+ call = c;
98144
+ }
98145
+ else {
98146
+ otherCalls.push(c);
98147
+ }
98148
+ }
98149
+ if (call === null)
98150
+ call = {};
98151
+ const devices = Array.isArray(call["m.devices"]) ? call["m.devices"] : [];
98152
+ // Filter out invalid and expired devices
98153
+ const validDevices = devices.filter(d => (typeof d.device_id === "string"
98154
+ && typeof d.session_id === "string"
98155
+ && typeof d.expires_ts === "number"
98156
+ && d.expires_ts > now
98157
+ && Array.isArray(d.feeds)));
98158
+ const newDevices = fn(validDevices);
98159
+ if (newDevices === null)
98160
+ return;
98161
+ const newCalls = [...otherCalls];
98162
+ if (newDevices.length > 0) {
98163
+ newCalls.push(Object.assign(Object.assign({}, call), { "m.call_id": this.groupCallId, "m.devices": newDevices }));
98164
+ }
98165
+ const newContent = { "m.calls": newCalls };
98166
+ yield this.client.sendStateEvent(this.room.roomId, event_1.EventType.GroupCallMemberPrefix, newContent, localUserId, { keepAlive });
98167
+ });
98168
+ }
98169
+ addDeviceToMemberState() {
98170
+ return __awaiter(this, void 0, void 0, function* () {
98171
+ yield this.updateDevices(devices => [
98172
+ ...devices.filter(d => d.device_id !== this.client.getDeviceId()),
98173
+ {
98174
+ "device_id": this.client.getDeviceId(),
98175
+ "session_id": this.client.getSessionId(),
98176
+ "expires_ts": Date.now() + DEVICE_TIMEOUT,
98177
+ "feeds": this.getLocalFeeds().map(feed => ({ purpose: feed.purpose })),
98178
+ // TODO: Add data channels
98179
+ },
98180
+ ]);
98181
+ });
98182
+ }
98183
+ updateMemberState() {
98184
+ return __awaiter(this, void 0, void 0, function* () {
98185
+ // Clear the old update interval before proceeding
98186
+ if (this.resendMemberStateTimer !== null) {
98187
+ clearInterval(this.resendMemberStateTimer);
98188
+ this.resendMemberStateTimer = null;
98189
+ }
98190
+ if (this.state === GroupCallState.Entered) {
98191
+ // Add the local device
98192
+ yield this.addDeviceToMemberState();
98193
+ // Resend the state event every so often so it doesn't become stale
98194
+ this.resendMemberStateTimer = setInterval(() => __awaiter(this, void 0, void 0, function* () {
98195
+ logger_1.logger.log("Resending call member state");
98196
+ try {
98197
+ yield this.addDeviceToMemberState();
98198
+ }
98199
+ catch (e) {
98200
+ logger_1.logger.error("Failed to resend call member state", e);
98201
+ }
98202
+ }), DEVICE_TIMEOUT * 3 / 4);
98203
+ }
98204
+ else {
98205
+ // Remove the local device
98206
+ yield this.updateDevices(devices => devices.filter(d => d.device_id !== this.client.getDeviceId()), true);
98207
+ }
98208
+ });
98209
+ }
98210
+ /**
98211
+ * Cleans up our member state by filtering out logged out devices, inactive
98212
+ * devices, and our own device (if we know we haven't entered).
98213
+ */
98214
+ cleanMemberState() {
98215
+ return __awaiter(this, void 0, void 0, function* () {
98216
+ const { devices: myDevices } = yield this.client.getDevices();
98217
+ const deviceMap = new Map(myDevices.map(d => [d.device_id, d]));
98218
+ // updateDevices takes care of filtering out inactive devices for us
98219
+ yield this.updateDevices(devices => {
98220
+ const newDevices = devices.filter(d => {
98221
+ const device = deviceMap.get(d.device_id);
98222
+ return (device === null || device === void 0 ? void 0 : device.last_seen_ts) !== undefined
98223
+ && !(d.device_id === this.client.getDeviceId() && this.state !== GroupCallState.Entered);
98224
+ });
98225
+ // Skip the update if the devices are unchanged
98226
+ return newDevices.length === devices.length ? null : newDevices;
98227
+ });
98228
+ });
98014
98229
  }
98015
98230
  }
98016
98231
  exports.GroupCall = GroupCall;
98017
98232
 
98018
- },{"../@types/event":290,"../ReEmitter":300,"../logger":356,"../models/typed-event-emitter":376,"./call":395,"./callEventHandler":396,"./callEventTypes":397,"./callFeed":398,"./groupCallEventHandler":400}],400:[function(require,module,exports){
98233
+ },{"../@types/event":290,"../ReEmitter":300,"../logger":356,"../models/room-state":371,"../models/typed-event-emitter":376,"../utils":393,"./call":395,"./callEventHandler":396,"./callEventTypes":397,"./callFeed":398,"./groupCallEventHandler":400}],400:[function(require,module,exports){
98019
98234
  "use strict";
98020
98235
  /*
98021
98236
  Copyright 2021 Šimon Brandner <simon.bra.ag@gmail.com>
@@ -98052,6 +98267,7 @@ const sync_1 = require("../sync");
98052
98267
  var GroupCallEventHandlerEvent;
98053
98268
  (function (GroupCallEventHandlerEvent) {
98054
98269
  GroupCallEventHandlerEvent["Incoming"] = "GroupCall.incoming";
98270
+ GroupCallEventHandlerEvent["Outgoing"] = "GroupCall.outgoing";
98055
98271
  GroupCallEventHandlerEvent["Ended"] = "GroupCall.ended";
98056
98272
  GroupCallEventHandlerEvent["Participants"] = "GroupCall.participants";
98057
98273
  })(GroupCallEventHandlerEvent = exports.GroupCallEventHandlerEvent || (exports.GroupCallEventHandlerEvent = {}));
@@ -98090,13 +98306,6 @@ class GroupCallEventHandler {
98090
98306
  logger_1.logger.warn(`Multiple group calls detected for room: ${state.roomId}. Multiple group calls are currently unsupported.`);
98091
98307
  }
98092
98308
  }
98093
- else if (eventType === event_1.EventType.GroupCallMemberPrefix) {
98094
- const groupCall = this.groupCalls.get(state.roomId);
98095
- if (!groupCall) {
98096
- return;
98097
- }
98098
- groupCall.onMemberStateChanged(event);
98099
- }
98100
98309
  };
98101
98310
  }
98102
98311
  start() {