@unwanted/matrix-sdk-mini 34.12.0-1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (1203) hide show
  1. package/CHANGELOG.md +5910 -0
  2. package/LICENSE +177 -0
  3. package/README.md +459 -0
  4. package/git-revision.txt +1 -0
  5. package/lib/@types/AESEncryptedSecretStoragePayload.d.ts +14 -0
  6. package/lib/@types/AESEncryptedSecretStoragePayload.d.ts.map +1 -0
  7. package/lib/@types/AESEncryptedSecretStoragePayload.js +1 -0
  8. package/lib/@types/AESEncryptedSecretStoragePayload.js.map +1 -0
  9. package/lib/@types/IIdentityServerProvider.d.ts +9 -0
  10. package/lib/@types/IIdentityServerProvider.d.ts.map +1 -0
  11. package/lib/@types/IIdentityServerProvider.js +1 -0
  12. package/lib/@types/IIdentityServerProvider.js.map +1 -0
  13. package/lib/@types/PushRules.d.ts +140 -0
  14. package/lib/@types/PushRules.d.ts.map +1 -0
  15. package/lib/@types/PushRules.js +94 -0
  16. package/lib/@types/PushRules.js.map +1 -0
  17. package/lib/@types/another-json.d.js +0 -0
  18. package/lib/@types/another-json.d.js.map +1 -0
  19. package/lib/@types/auth.d.ts +208 -0
  20. package/lib/@types/auth.d.ts.map +1 -0
  21. package/lib/@types/auth.js +99 -0
  22. package/lib/@types/auth.js.map +1 -0
  23. package/lib/@types/beacon.d.ts +106 -0
  24. package/lib/@types/beacon.d.ts.map +1 -0
  25. package/lib/@types/beacon.js +119 -0
  26. package/lib/@types/beacon.js.map +1 -0
  27. package/lib/@types/common.d.ts +9 -0
  28. package/lib/@types/common.d.ts.map +1 -0
  29. package/lib/@types/common.js +1 -0
  30. package/lib/@types/common.js.map +1 -0
  31. package/lib/@types/crypto.d.ts +47 -0
  32. package/lib/@types/crypto.d.ts.map +1 -0
  33. package/lib/@types/crypto.js +1 -0
  34. package/lib/@types/crypto.js.map +1 -0
  35. package/lib/@types/event.d.ts +258 -0
  36. package/lib/@types/event.d.ts.map +1 -0
  37. package/lib/@types/event.js +239 -0
  38. package/lib/@types/event.js.map +1 -0
  39. package/lib/@types/events.d.ts +92 -0
  40. package/lib/@types/events.d.ts.map +1 -0
  41. package/lib/@types/events.js +1 -0
  42. package/lib/@types/events.js.map +1 -0
  43. package/lib/@types/extensible_events.d.ts +98 -0
  44. package/lib/@types/extensible_events.d.ts.map +1 -0
  45. package/lib/@types/extensible_events.js +116 -0
  46. package/lib/@types/extensible_events.js.map +1 -0
  47. package/lib/@types/global.d.js +20 -0
  48. package/lib/@types/global.d.js.map +1 -0
  49. package/lib/@types/local_notifications.d.ts +4 -0
  50. package/lib/@types/local_notifications.d.ts.map +1 -0
  51. package/lib/@types/local_notifications.js +1 -0
  52. package/lib/@types/local_notifications.js.map +1 -0
  53. package/lib/@types/location.d.ts +60 -0
  54. package/lib/@types/location.d.ts.map +1 -0
  55. package/lib/@types/location.js +67 -0
  56. package/lib/@types/location.js.map +1 -0
  57. package/lib/@types/matrix-sdk-crypto-wasm.d.js +1 -0
  58. package/lib/@types/matrix-sdk-crypto-wasm.d.js.map +1 -0
  59. package/lib/@types/media.d.ts +220 -0
  60. package/lib/@types/media.d.ts.map +1 -0
  61. package/lib/@types/media.js +1 -0
  62. package/lib/@types/media.js.map +1 -0
  63. package/lib/@types/membership.d.ts +41 -0
  64. package/lib/@types/membership.d.ts.map +1 -0
  65. package/lib/@types/membership.js +37 -0
  66. package/lib/@types/membership.js.map +1 -0
  67. package/lib/@types/oidc-client-ts.d.js +18 -0
  68. package/lib/@types/oidc-client-ts.d.js.map +1 -0
  69. package/lib/@types/partials.d.ts +39 -0
  70. package/lib/@types/partials.d.ts.map +1 -0
  71. package/lib/@types/partials.js +53 -0
  72. package/lib/@types/partials.js.map +1 -0
  73. package/lib/@types/polls.d.ts +88 -0
  74. package/lib/@types/polls.d.ts.map +1 -0
  75. package/lib/@types/polls.js +86 -0
  76. package/lib/@types/polls.js.map +1 -0
  77. package/lib/@types/read_receipts.d.ts +36 -0
  78. package/lib/@types/read_receipts.d.ts.map +1 -0
  79. package/lib/@types/read_receipts.js +27 -0
  80. package/lib/@types/read_receipts.js.map +1 -0
  81. package/lib/@types/registration.d.ts +85 -0
  82. package/lib/@types/registration.d.ts.map +1 -0
  83. package/lib/@types/registration.js +1 -0
  84. package/lib/@types/registration.js.map +1 -0
  85. package/lib/@types/requests.d.ts +241 -0
  86. package/lib/@types/requests.d.ts.map +1 -0
  87. package/lib/@types/requests.js +28 -0
  88. package/lib/@types/requests.js.map +1 -0
  89. package/lib/@types/search.d.ts +90 -0
  90. package/lib/@types/search.d.ts.map +1 -0
  91. package/lib/@types/search.js +30 -0
  92. package/lib/@types/search.js.map +1 -0
  93. package/lib/@types/signed.d.ts +9 -0
  94. package/lib/@types/signed.d.ts.map +1 -0
  95. package/lib/@types/signed.js +1 -0
  96. package/lib/@types/signed.js.map +1 -0
  97. package/lib/@types/spaces.d.ts +16 -0
  98. package/lib/@types/spaces.d.ts.map +1 -0
  99. package/lib/@types/spaces.js +1 -0
  100. package/lib/@types/spaces.js.map +1 -0
  101. package/lib/@types/state_events.d.ts +116 -0
  102. package/lib/@types/state_events.d.ts.map +1 -0
  103. package/lib/@types/state_events.js +1 -0
  104. package/lib/@types/state_events.js.map +1 -0
  105. package/lib/@types/synapse.d.ts +19 -0
  106. package/lib/@types/synapse.d.ts.map +1 -0
  107. package/lib/@types/synapse.js +1 -0
  108. package/lib/@types/synapse.js.map +1 -0
  109. package/lib/@types/sync.d.ts +8 -0
  110. package/lib/@types/sync.d.ts.map +1 -0
  111. package/lib/@types/sync.js +25 -0
  112. package/lib/@types/sync.js.map +1 -0
  113. package/lib/@types/threepids.d.ts +12 -0
  114. package/lib/@types/threepids.d.ts.map +1 -0
  115. package/lib/@types/threepids.js +24 -0
  116. package/lib/@types/threepids.js.map +1 -0
  117. package/lib/@types/topic.d.ts +48 -0
  118. package/lib/@types/topic.d.ts.map +1 -0
  119. package/lib/@types/topic.js +57 -0
  120. package/lib/@types/topic.js.map +1 -0
  121. package/lib/@types/uia.d.ts +12 -0
  122. package/lib/@types/uia.d.ts.map +1 -0
  123. package/lib/@types/uia.js +1 -0
  124. package/lib/@types/uia.js.map +1 -0
  125. package/lib/NamespacedValue.d.ts +33 -0
  126. package/lib/NamespacedValue.d.ts.map +1 -0
  127. package/lib/NamespacedValue.js +113 -0
  128. package/lib/NamespacedValue.js.map +1 -0
  129. package/lib/ReEmitter.d.ts +15 -0
  130. package/lib/ReEmitter.d.ts.map +1 -0
  131. package/lib/ReEmitter.js +87 -0
  132. package/lib/ReEmitter.js.map +1 -0
  133. package/lib/ToDeviceMessageQueue.d.ts +28 -0
  134. package/lib/ToDeviceMessageQueue.d.ts.map +1 -0
  135. package/lib/ToDeviceMessageQueue.js +135 -0
  136. package/lib/ToDeviceMessageQueue.js.map +1 -0
  137. package/lib/autodiscovery.d.ts +136 -0
  138. package/lib/autodiscovery.d.ts.map +1 -0
  139. package/lib/autodiscovery.js +464 -0
  140. package/lib/autodiscovery.js.map +1 -0
  141. package/lib/base64.d.ts +28 -0
  142. package/lib/base64.d.ts.map +1 -0
  143. package/lib/base64.js +88 -0
  144. package/lib/base64.js.map +1 -0
  145. package/lib/browser-index.d.ts +8 -0
  146. package/lib/browser-index.d.ts.map +1 -0
  147. package/lib/browser-index.js +35 -0
  148. package/lib/browser-index.js.map +1 -0
  149. package/lib/client.d.ts +4232 -0
  150. package/lib/client.d.ts.map +1 -0
  151. package/lib/client.js +8622 -0
  152. package/lib/client.js.map +1 -0
  153. package/lib/common-crypto/CryptoBackend.d.ts +240 -0
  154. package/lib/common-crypto/CryptoBackend.d.ts.map +1 -0
  155. package/lib/common-crypto/CryptoBackend.js +73 -0
  156. package/lib/common-crypto/CryptoBackend.js.map +1 -0
  157. package/lib/common-crypto/key-passphrase.d.ts +14 -0
  158. package/lib/common-crypto/key-passphrase.d.ts.map +1 -0
  159. package/lib/common-crypto/key-passphrase.js +33 -0
  160. package/lib/common-crypto/key-passphrase.js.map +1 -0
  161. package/lib/content-helpers.d.ts +90 -0
  162. package/lib/content-helpers.d.ts.map +1 -0
  163. package/lib/content-helpers.js +250 -0
  164. package/lib/content-helpers.js.map +1 -0
  165. package/lib/content-repo.d.ts +24 -0
  166. package/lib/content-repo.d.ts.map +1 -0
  167. package/lib/content-repo.js +104 -0
  168. package/lib/content-repo.js.map +1 -0
  169. package/lib/crypto/CrossSigning.d.ts +184 -0
  170. package/lib/crypto/CrossSigning.d.ts.map +1 -0
  171. package/lib/crypto/CrossSigning.js +718 -0
  172. package/lib/crypto/CrossSigning.js.map +1 -0
  173. package/lib/crypto/DeviceList.d.ts +216 -0
  174. package/lib/crypto/DeviceList.d.ts.map +1 -0
  175. package/lib/crypto/DeviceList.js +892 -0
  176. package/lib/crypto/DeviceList.js.map +1 -0
  177. package/lib/crypto/EncryptionSetup.d.ts +152 -0
  178. package/lib/crypto/EncryptionSetup.d.ts.map +1 -0
  179. package/lib/crypto/EncryptionSetup.js +356 -0
  180. package/lib/crypto/EncryptionSetup.js.map +1 -0
  181. package/lib/crypto/OlmDevice.d.ts +457 -0
  182. package/lib/crypto/OlmDevice.d.ts.map +1 -0
  183. package/lib/crypto/OlmDevice.js +1241 -0
  184. package/lib/crypto/OlmDevice.js.map +1 -0
  185. package/lib/crypto/OutgoingRoomKeyRequestManager.d.ts +109 -0
  186. package/lib/crypto/OutgoingRoomKeyRequestManager.d.ts.map +1 -0
  187. package/lib/crypto/OutgoingRoomKeyRequestManager.js +415 -0
  188. package/lib/crypto/OutgoingRoomKeyRequestManager.js.map +1 -0
  189. package/lib/crypto/RoomList.d.ts +26 -0
  190. package/lib/crypto/RoomList.d.ts.map +1 -0
  191. package/lib/crypto/RoomList.js +71 -0
  192. package/lib/crypto/RoomList.js.map +1 -0
  193. package/lib/crypto/SecretSharing.d.ts +24 -0
  194. package/lib/crypto/SecretSharing.d.ts.map +1 -0
  195. package/lib/crypto/SecretSharing.js +194 -0
  196. package/lib/crypto/SecretSharing.js.map +1 -0
  197. package/lib/crypto/SecretStorage.d.ts +55 -0
  198. package/lib/crypto/SecretStorage.d.ts.map +1 -0
  199. package/lib/crypto/SecretStorage.js +118 -0
  200. package/lib/crypto/SecretStorage.js.map +1 -0
  201. package/lib/crypto/aes.d.ts +6 -0
  202. package/lib/crypto/aes.d.ts.map +1 -0
  203. package/lib/crypto/aes.js +24 -0
  204. package/lib/crypto/aes.js.map +1 -0
  205. package/lib/crypto/algorithms/base.d.ts +156 -0
  206. package/lib/crypto/algorithms/base.d.ts.map +1 -0
  207. package/lib/crypto/algorithms/base.js +187 -0
  208. package/lib/crypto/algorithms/base.js.map +1 -0
  209. package/lib/crypto/algorithms/index.d.ts +4 -0
  210. package/lib/crypto/algorithms/index.d.ts.map +1 -0
  211. package/lib/crypto/algorithms/index.js +20 -0
  212. package/lib/crypto/algorithms/index.js.map +1 -0
  213. package/lib/crypto/algorithms/megolm.d.ts +385 -0
  214. package/lib/crypto/algorithms/megolm.d.ts.map +1 -0
  215. package/lib/crypto/algorithms/megolm.js +1822 -0
  216. package/lib/crypto/algorithms/megolm.js.map +1 -0
  217. package/lib/crypto/algorithms/olm.d.ts +5 -0
  218. package/lib/crypto/algorithms/olm.d.ts.map +1 -0
  219. package/lib/crypto/algorithms/olm.js +299 -0
  220. package/lib/crypto/algorithms/olm.js.map +1 -0
  221. package/lib/crypto/api.d.ts +32 -0
  222. package/lib/crypto/api.d.ts.map +1 -0
  223. package/lib/crypto/api.js +22 -0
  224. package/lib/crypto/api.js.map +1 -0
  225. package/lib/crypto/backup.d.ts +227 -0
  226. package/lib/crypto/backup.d.ts.map +1 -0
  227. package/lib/crypto/backup.js +824 -0
  228. package/lib/crypto/backup.js.map +1 -0
  229. package/lib/crypto/crypto.d.ts +3 -0
  230. package/lib/crypto/crypto.d.ts.map +1 -0
  231. package/lib/crypto/crypto.js +19 -0
  232. package/lib/crypto/crypto.js.map +1 -0
  233. package/lib/crypto/dehydration.d.ts +34 -0
  234. package/lib/crypto/dehydration.d.ts.map +1 -0
  235. package/lib/crypto/dehydration.js +252 -0
  236. package/lib/crypto/dehydration.js.map +1 -0
  237. package/lib/crypto/device-converter.d.ts +9 -0
  238. package/lib/crypto/device-converter.d.ts.map +1 -0
  239. package/lib/crypto/device-converter.js +42 -0
  240. package/lib/crypto/device-converter.js.map +1 -0
  241. package/lib/crypto/deviceinfo.d.ts +99 -0
  242. package/lib/crypto/deviceinfo.d.ts.map +1 -0
  243. package/lib/crypto/deviceinfo.js +148 -0
  244. package/lib/crypto/deviceinfo.js.map +1 -0
  245. package/lib/crypto/index.d.ts +1209 -0
  246. package/lib/crypto/index.d.ts.map +1 -0
  247. package/lib/crypto/index.js +4097 -0
  248. package/lib/crypto/index.js.map +1 -0
  249. package/lib/crypto/key_passphrase.d.ts +14 -0
  250. package/lib/crypto/key_passphrase.d.ts.map +1 -0
  251. package/lib/crypto/key_passphrase.js +44 -0
  252. package/lib/crypto/key_passphrase.js.map +1 -0
  253. package/lib/crypto/keybackup.d.ts +18 -0
  254. package/lib/crypto/keybackup.d.ts.map +1 -0
  255. package/lib/crypto/keybackup.js +1 -0
  256. package/lib/crypto/keybackup.js.map +1 -0
  257. package/lib/crypto/olmlib.d.ts +129 -0
  258. package/lib/crypto/olmlib.d.ts.map +1 -0
  259. package/lib/crypto/olmlib.js +492 -0
  260. package/lib/crypto/olmlib.js.map +1 -0
  261. package/lib/crypto/recoverykey.d.ts +2 -0
  262. package/lib/crypto/recoverykey.d.ts.map +1 -0
  263. package/lib/crypto/recoverykey.js +19 -0
  264. package/lib/crypto/recoverykey.js.map +1 -0
  265. package/lib/crypto/store/base.d.ts +252 -0
  266. package/lib/crypto/store/base.d.ts.map +1 -0
  267. package/lib/crypto/store/base.js +64 -0
  268. package/lib/crypto/store/base.js.map +1 -0
  269. package/lib/crypto/store/indexeddb-crypto-store-backend.d.ts +187 -0
  270. package/lib/crypto/store/indexeddb-crypto-store-backend.d.ts.map +1 -0
  271. package/lib/crypto/store/indexeddb-crypto-store-backend.js +1145 -0
  272. package/lib/crypto/store/indexeddb-crypto-store-backend.js.map +1 -0
  273. package/lib/crypto/store/indexeddb-crypto-store.d.ts +432 -0
  274. package/lib/crypto/store/indexeddb-crypto-store.d.ts.map +1 -0
  275. package/lib/crypto/store/indexeddb-crypto-store.js +728 -0
  276. package/lib/crypto/store/indexeddb-crypto-store.js.map +1 -0
  277. package/lib/crypto/store/localStorage-crypto-store.d.ts +119 -0
  278. package/lib/crypto/store/localStorage-crypto-store.d.ts.map +1 -0
  279. package/lib/crypto/store/localStorage-crypto-store.js +531 -0
  280. package/lib/crypto/store/localStorage-crypto-store.js.map +1 -0
  281. package/lib/crypto/store/memory-crypto-store.d.ts +215 -0
  282. package/lib/crypto/store/memory-crypto-store.d.ts.map +1 -0
  283. package/lib/crypto/store/memory-crypto-store.js +622 -0
  284. package/lib/crypto/store/memory-crypto-store.js.map +1 -0
  285. package/lib/crypto/verification/Base.d.ts +105 -0
  286. package/lib/crypto/verification/Base.d.ts.map +1 -0
  287. package/lib/crypto/verification/Base.js +372 -0
  288. package/lib/crypto/verification/Base.js.map +1 -0
  289. package/lib/crypto/verification/Error.d.ts +35 -0
  290. package/lib/crypto/verification/Error.d.ts.map +1 -0
  291. package/lib/crypto/verification/Error.js +86 -0
  292. package/lib/crypto/verification/Error.js.map +1 -0
  293. package/lib/crypto/verification/IllegalMethod.d.ts +15 -0
  294. package/lib/crypto/verification/IllegalMethod.d.ts.map +1 -0
  295. package/lib/crypto/verification/IllegalMethod.js +43 -0
  296. package/lib/crypto/verification/IllegalMethod.js.map +1 -0
  297. package/lib/crypto/verification/QRCode.d.ts +51 -0
  298. package/lib/crypto/verification/QRCode.d.ts.map +1 -0
  299. package/lib/crypto/verification/QRCode.js +277 -0
  300. package/lib/crypto/verification/QRCode.js.map +1 -0
  301. package/lib/crypto/verification/SAS.d.ts +27 -0
  302. package/lib/crypto/verification/SAS.d.ts.map +1 -0
  303. package/lib/crypto/verification/SAS.js +485 -0
  304. package/lib/crypto/verification/SAS.js.map +1 -0
  305. package/lib/crypto/verification/SASDecimal.d.ts +8 -0
  306. package/lib/crypto/verification/SASDecimal.d.ts.map +1 -0
  307. package/lib/crypto/verification/SASDecimal.js +34 -0
  308. package/lib/crypto/verification/SASDecimal.js.map +1 -0
  309. package/lib/crypto/verification/request/Channel.d.ts +18 -0
  310. package/lib/crypto/verification/request/Channel.d.ts.map +1 -0
  311. package/lib/crypto/verification/request/Channel.js +1 -0
  312. package/lib/crypto/verification/request/Channel.js.map +1 -0
  313. package/lib/crypto/verification/request/InRoomChannel.d.ts +113 -0
  314. package/lib/crypto/verification/request/InRoomChannel.d.ts.map +1 -0
  315. package/lib/crypto/verification/request/InRoomChannel.js +351 -0
  316. package/lib/crypto/verification/request/InRoomChannel.js.map +1 -0
  317. package/lib/crypto/verification/request/ToDeviceChannel.d.ts +105 -0
  318. package/lib/crypto/verification/request/ToDeviceChannel.d.ts.map +1 -0
  319. package/lib/crypto/verification/request/ToDeviceChannel.js +328 -0
  320. package/lib/crypto/verification/request/ToDeviceChannel.js.map +1 -0
  321. package/lib/crypto/verification/request/VerificationRequest.d.ts +227 -0
  322. package/lib/crypto/verification/request/VerificationRequest.d.ts.map +1 -0
  323. package/lib/crypto/verification/request/VerificationRequest.js +937 -0
  324. package/lib/crypto/verification/request/VerificationRequest.js.map +1 -0
  325. package/lib/crypto-api/CryptoEvent.d.ts +69 -0
  326. package/lib/crypto-api/CryptoEvent.d.ts.map +1 -0
  327. package/lib/crypto-api/CryptoEvent.js +33 -0
  328. package/lib/crypto-api/CryptoEvent.js.map +1 -0
  329. package/lib/crypto-api/CryptoEventHandlerMap.d.ts +16 -0
  330. package/lib/crypto-api/CryptoEventHandlerMap.d.ts.map +1 -0
  331. package/lib/crypto-api/CryptoEventHandlerMap.js +22 -0
  332. package/lib/crypto-api/CryptoEventHandlerMap.js.map +1 -0
  333. package/lib/crypto-api/index.d.ts +978 -0
  334. package/lib/crypto-api/index.d.ts.map +1 -0
  335. package/lib/crypto-api/index.js +304 -0
  336. package/lib/crypto-api/index.js.map +1 -0
  337. package/lib/crypto-api/key-passphrase.d.ts +11 -0
  338. package/lib/crypto-api/key-passphrase.d.ts.map +1 -0
  339. package/lib/crypto-api/key-passphrase.js +51 -0
  340. package/lib/crypto-api/key-passphrase.js.map +1 -0
  341. package/lib/crypto-api/keybackup.d.ts +88 -0
  342. package/lib/crypto-api/keybackup.d.ts.map +1 -0
  343. package/lib/crypto-api/keybackup.js +1 -0
  344. package/lib/crypto-api/keybackup.js.map +1 -0
  345. package/lib/crypto-api/recovery-key.d.ts +11 -0
  346. package/lib/crypto-api/recovery-key.d.ts.map +1 -0
  347. package/lib/crypto-api/recovery-key.js +65 -0
  348. package/lib/crypto-api/recovery-key.js.map +1 -0
  349. package/lib/crypto-api/verification.d.ts +344 -0
  350. package/lib/crypto-api/verification.d.ts.map +1 -0
  351. package/lib/crypto-api/verification.js +91 -0
  352. package/lib/crypto-api/verification.js.map +1 -0
  353. package/lib/digest.d.ts +10 -0
  354. package/lib/digest.d.ts.map +1 -0
  355. package/lib/digest.js +40 -0
  356. package/lib/digest.js.map +1 -0
  357. package/lib/embedded.d.ts +143 -0
  358. package/lib/embedded.d.ts.map +1 -0
  359. package/lib/embedded.js +567 -0
  360. package/lib/embedded.js.map +1 -0
  361. package/lib/errors.d.ts +24 -0
  362. package/lib/errors.d.ts.map +1 -0
  363. package/lib/errors.js +51 -0
  364. package/lib/errors.js.map +1 -0
  365. package/lib/event-mapper.d.ts +10 -0
  366. package/lib/event-mapper.d.ts.map +1 -0
  367. package/lib/event-mapper.js +81 -0
  368. package/lib/event-mapper.js.map +1 -0
  369. package/lib/extensible_events_v1/ExtensibleEvent.d.ts +38 -0
  370. package/lib/extensible_events_v1/ExtensibleEvent.d.ts.map +1 -0
  371. package/lib/extensible_events_v1/ExtensibleEvent.js +57 -0
  372. package/lib/extensible_events_v1/ExtensibleEvent.js.map +1 -0
  373. package/lib/extensible_events_v1/InvalidEventError.d.ts +7 -0
  374. package/lib/extensible_events_v1/InvalidEventError.d.ts.map +1 -0
  375. package/lib/extensible_events_v1/InvalidEventError.js +25 -0
  376. package/lib/extensible_events_v1/InvalidEventError.js.map +1 -0
  377. package/lib/extensible_events_v1/MessageEvent.d.ts +45 -0
  378. package/lib/extensible_events_v1/MessageEvent.d.ts.map +1 -0
  379. package/lib/extensible_events_v1/MessageEvent.js +134 -0
  380. package/lib/extensible_events_v1/MessageEvent.js.map +1 -0
  381. package/lib/extensible_events_v1/PollEndEvent.d.ts +33 -0
  382. package/lib/extensible_events_v1/PollEndEvent.d.ts.map +1 -0
  383. package/lib/extensible_events_v1/PollEndEvent.js +88 -0
  384. package/lib/extensible_events_v1/PollEndEvent.js.map +1 -0
  385. package/lib/extensible_events_v1/PollResponseEvent.d.ts +49 -0
  386. package/lib/extensible_events_v1/PollResponseEvent.d.ts.map +1 -0
  387. package/lib/extensible_events_v1/PollResponseEvent.js +135 -0
  388. package/lib/extensible_events_v1/PollResponseEvent.js.map +1 -0
  389. package/lib/extensible_events_v1/PollStartEvent.d.ts +71 -0
  390. package/lib/extensible_events_v1/PollStartEvent.d.ts.map +1 -0
  391. package/lib/extensible_events_v1/PollStartEvent.js +185 -0
  392. package/lib/extensible_events_v1/PollStartEvent.js.map +1 -0
  393. package/lib/extensible_events_v1/utilities.d.ts +14 -0
  394. package/lib/extensible_events_v1/utilities.d.ts.map +1 -0
  395. package/lib/extensible_events_v1/utilities.js +34 -0
  396. package/lib/extensible_events_v1/utilities.js.map +1 -0
  397. package/lib/feature.d.ts +20 -0
  398. package/lib/feature.d.ts.map +1 -0
  399. package/lib/feature.js +85 -0
  400. package/lib/feature.js.map +1 -0
  401. package/lib/filter-component.d.ts +64 -0
  402. package/lib/filter-component.d.ts.map +1 -0
  403. package/lib/filter-component.js +167 -0
  404. package/lib/filter-component.js.map +1 -0
  405. package/lib/filter.d.ts +97 -0
  406. package/lib/filter.d.ts.map +1 -0
  407. package/lib/filter.js +207 -0
  408. package/lib/filter.js.map +1 -0
  409. package/lib/http-api/errors.d.ts +80 -0
  410. package/lib/http-api/errors.d.ts.map +1 -0
  411. package/lib/http-api/errors.js +185 -0
  412. package/lib/http-api/errors.js.map +1 -0
  413. package/lib/http-api/fetch.d.ts +114 -0
  414. package/lib/http-api/fetch.d.ts.map +1 -0
  415. package/lib/http-api/fetch.js +346 -0
  416. package/lib/http-api/fetch.js.map +1 -0
  417. package/lib/http-api/index.d.ts +33 -0
  418. package/lib/http-api/index.d.ts.map +1 -0
  419. package/lib/http-api/index.js +180 -0
  420. package/lib/http-api/index.js.map +1 -0
  421. package/lib/http-api/interface.d.ts +142 -0
  422. package/lib/http-api/interface.d.ts.map +1 -0
  423. package/lib/http-api/interface.js +35 -0
  424. package/lib/http-api/interface.js.map +1 -0
  425. package/lib/http-api/method.d.ts +10 -0
  426. package/lib/http-api/method.d.ts.map +1 -0
  427. package/lib/http-api/method.js +27 -0
  428. package/lib/http-api/method.js.map +1 -0
  429. package/lib/http-api/prefix.d.ts +31 -0
  430. package/lib/http-api/prefix.d.ts.map +1 -0
  431. package/lib/http-api/prefix.js +32 -0
  432. package/lib/http-api/prefix.js.map +1 -0
  433. package/lib/http-api/utils.d.ts +37 -0
  434. package/lib/http-api/utils.d.ts.map +1 -0
  435. package/lib/http-api/utils.js +178 -0
  436. package/lib/http-api/utils.js.map +1 -0
  437. package/lib/index.d.ts +4 -0
  438. package/lib/index.d.ts.map +1 -0
  439. package/lib/index.js +24 -0
  440. package/lib/index.js.map +1 -0
  441. package/lib/indexeddb-helpers.d.ts +10 -0
  442. package/lib/indexeddb-helpers.d.ts.map +1 -0
  443. package/lib/indexeddb-helpers.js +51 -0
  444. package/lib/indexeddb-helpers.js.map +1 -0
  445. package/lib/indexeddb-worker.d.ts +7 -0
  446. package/lib/indexeddb-worker.d.ts.map +1 -0
  447. package/lib/indexeddb-worker.js +25 -0
  448. package/lib/indexeddb-worker.js.map +1 -0
  449. package/lib/interactive-auth.d.ts +337 -0
  450. package/lib/interactive-auth.d.ts.map +1 -0
  451. package/lib/interactive-auth.js +557 -0
  452. package/lib/interactive-auth.js.map +1 -0
  453. package/lib/logger.d.ts +81 -0
  454. package/lib/logger.d.ts.map +1 -0
  455. package/lib/logger.js +139 -0
  456. package/lib/logger.js.map +1 -0
  457. package/lib/matrix.d.ts +118 -0
  458. package/lib/matrix.d.ts.map +1 -0
  459. package/lib/matrix.js +146 -0
  460. package/lib/matrix.js.map +1 -0
  461. package/lib/matrixrtc/CallMembership.d.ts +66 -0
  462. package/lib/matrixrtc/CallMembership.d.ts.map +1 -0
  463. package/lib/matrixrtc/CallMembership.js +197 -0
  464. package/lib/matrixrtc/CallMembership.js.map +1 -0
  465. package/lib/matrixrtc/LivekitFocus.d.ts +16 -0
  466. package/lib/matrixrtc/LivekitFocus.d.ts.map +1 -0
  467. package/lib/matrixrtc/LivekitFocus.js +20 -0
  468. package/lib/matrixrtc/LivekitFocus.js.map +1 -0
  469. package/lib/matrixrtc/MatrixRTCSession.d.ts +295 -0
  470. package/lib/matrixrtc/MatrixRTCSession.d.ts.map +1 -0
  471. package/lib/matrixrtc/MatrixRTCSession.js +1043 -0
  472. package/lib/matrixrtc/MatrixRTCSession.js.map +1 -0
  473. package/lib/matrixrtc/MatrixRTCSessionManager.d.ts +40 -0
  474. package/lib/matrixrtc/MatrixRTCSessionManager.d.ts.map +1 -0
  475. package/lib/matrixrtc/MatrixRTCSessionManager.js +146 -0
  476. package/lib/matrixrtc/MatrixRTCSessionManager.js.map +1 -0
  477. package/lib/matrixrtc/focus.d.ts +10 -0
  478. package/lib/matrixrtc/focus.d.ts.map +1 -0
  479. package/lib/matrixrtc/focus.js +1 -0
  480. package/lib/matrixrtc/focus.js.map +1 -0
  481. package/lib/matrixrtc/index.d.ts +7 -0
  482. package/lib/matrixrtc/index.d.ts.map +1 -0
  483. package/lib/matrixrtc/index.js +21 -0
  484. package/lib/matrixrtc/index.js.map +1 -0
  485. package/lib/matrixrtc/types.d.ts +19 -0
  486. package/lib/matrixrtc/types.d.ts.map +1 -0
  487. package/lib/matrixrtc/types.js +1 -0
  488. package/lib/matrixrtc/types.js.map +1 -0
  489. package/lib/models/MSC3089Branch.d.ts +98 -0
  490. package/lib/models/MSC3089Branch.d.ts.map +1 -0
  491. package/lib/models/MSC3089Branch.js +240 -0
  492. package/lib/models/MSC3089Branch.js.map +1 -0
  493. package/lib/models/MSC3089TreeSpace.d.ts +165 -0
  494. package/lib/models/MSC3089TreeSpace.d.ts.map +1 -0
  495. package/lib/models/MSC3089TreeSpace.js +520 -0
  496. package/lib/models/MSC3089TreeSpace.js.map +1 -0
  497. package/lib/models/ToDeviceMessage.d.ts +17 -0
  498. package/lib/models/ToDeviceMessage.d.ts.map +1 -0
  499. package/lib/models/ToDeviceMessage.js +1 -0
  500. package/lib/models/ToDeviceMessage.js.map +1 -0
  501. package/lib/models/beacon.d.ts +53 -0
  502. package/lib/models/beacon.d.ts.map +1 -0
  503. package/lib/models/beacon.js +174 -0
  504. package/lib/models/beacon.js.map +1 -0
  505. package/lib/models/compare-event-ordering.d.ts +24 -0
  506. package/lib/models/compare-event-ordering.d.ts.map +1 -0
  507. package/lib/models/compare-event-ordering.js +120 -0
  508. package/lib/models/compare-event-ordering.js.map +1 -0
  509. package/lib/models/device.d.ts +45 -0
  510. package/lib/models/device.d.ts.map +1 -0
  511. package/lib/models/device.js +77 -0
  512. package/lib/models/device.js.map +1 -0
  513. package/lib/models/event-context.d.ts +62 -0
  514. package/lib/models/event-context.d.ts.map +1 -0
  515. package/lib/models/event-context.js +113 -0
  516. package/lib/models/event-context.js.map +1 -0
  517. package/lib/models/event-status.d.ts +19 -0
  518. package/lib/models/event-status.d.ts.map +1 -0
  519. package/lib/models/event-status.js +30 -0
  520. package/lib/models/event-status.js.map +1 -0
  521. package/lib/models/event-timeline-set.d.ts +312 -0
  522. package/lib/models/event-timeline-set.d.ts.map +1 -0
  523. package/lib/models/event-timeline-set.js +813 -0
  524. package/lib/models/event-timeline-set.js.map +1 -0
  525. package/lib/models/event-timeline.d.ts +219 -0
  526. package/lib/models/event-timeline.d.ts.map +1 -0
  527. package/lib/models/event-timeline.js +455 -0
  528. package/lib/models/event-timeline.js.map +1 -0
  529. package/lib/models/event.d.ts +811 -0
  530. package/lib/models/event.d.ts.map +1 -0
  531. package/lib/models/event.js +1520 -0
  532. package/lib/models/event.js.map +1 -0
  533. package/lib/models/invites-ignorer.d.ts +136 -0
  534. package/lib/models/invites-ignorer.d.ts.map +1 -0
  535. package/lib/models/invites-ignorer.js +382 -0
  536. package/lib/models/invites-ignorer.js.map +1 -0
  537. package/lib/models/poll.d.ts +67 -0
  538. package/lib/models/poll.d.ts.map +1 -0
  539. package/lib/models/poll.js +241 -0
  540. package/lib/models/poll.js.map +1 -0
  541. package/lib/models/profile-keys.d.ts +8 -0
  542. package/lib/models/profile-keys.d.ts.map +1 -0
  543. package/lib/models/profile-keys.js +8 -0
  544. package/lib/models/profile-keys.js.map +1 -0
  545. package/lib/models/read-receipt.d.ts +115 -0
  546. package/lib/models/read-receipt.d.ts.map +1 -0
  547. package/lib/models/read-receipt.js +366 -0
  548. package/lib/models/read-receipt.js.map +1 -0
  549. package/lib/models/related-relations.d.ts +11 -0
  550. package/lib/models/related-relations.d.ts.map +1 -0
  551. package/lib/models/related-relations.js +33 -0
  552. package/lib/models/related-relations.js.map +1 -0
  553. package/lib/models/relations-container.d.ts +44 -0
  554. package/lib/models/relations-container.d.ts.map +1 -0
  555. package/lib/models/relations-container.js +132 -0
  556. package/lib/models/relations-container.js.map +1 -0
  557. package/lib/models/relations.d.ts +114 -0
  558. package/lib/models/relations.d.ts.map +1 -0
  559. package/lib/models/relations.js +354 -0
  560. package/lib/models/relations.js.map +1 -0
  561. package/lib/models/room-member.d.ts +204 -0
  562. package/lib/models/room-member.d.ts.map +1 -0
  563. package/lib/models/room-member.js +360 -0
  564. package/lib/models/room-member.js.map +1 -0
  565. package/lib/models/room-receipts.d.ts +39 -0
  566. package/lib/models/room-receipts.d.ts.map +1 -0
  567. package/lib/models/room-receipts.js +392 -0
  568. package/lib/models/room-receipts.js.map +1 -0
  569. package/lib/models/room-state.d.ts +468 -0
  570. package/lib/models/room-state.d.ts.map +1 -0
  571. package/lib/models/room-state.js +984 -0
  572. package/lib/models/room-state.js.map +1 -0
  573. package/lib/models/room-summary.d.ts +29 -0
  574. package/lib/models/room-summary.d.ts.map +1 -0
  575. package/lib/models/room-summary.js +28 -0
  576. package/lib/models/room-summary.js.map +1 -0
  577. package/lib/models/room.d.ts +1203 -0
  578. package/lib/models/room.d.ts.map +1 -0
  579. package/lib/models/room.js +3336 -0
  580. package/lib/models/room.js.map +1 -0
  581. package/lib/models/search-result.d.ts +20 -0
  582. package/lib/models/search-result.d.ts.map +1 -0
  583. package/lib/models/search-result.js +52 -0
  584. package/lib/models/search-result.js.map +1 -0
  585. package/lib/models/thread.d.ts +246 -0
  586. package/lib/models/thread.d.ts.map +1 -0
  587. package/lib/models/thread.js +861 -0
  588. package/lib/models/thread.js.map +1 -0
  589. package/lib/models/typed-event-emitter.d.ts +157 -0
  590. package/lib/models/typed-event-emitter.d.ts.map +1 -0
  591. package/lib/models/typed-event-emitter.js +227 -0
  592. package/lib/models/typed-event-emitter.js.map +1 -0
  593. package/lib/models/user.d.ts +195 -0
  594. package/lib/models/user.d.ts.map +1 -0
  595. package/lib/models/user.js +218 -0
  596. package/lib/models/user.js.map +1 -0
  597. package/lib/oidc/authorize.d.ts +90 -0
  598. package/lib/oidc/authorize.d.ts.map +1 -0
  599. package/lib/oidc/authorize.js +278 -0
  600. package/lib/oidc/authorize.js.map +1 -0
  601. package/lib/oidc/discovery.d.ts +14 -0
  602. package/lib/oidc/discovery.d.ts.map +1 -0
  603. package/lib/oidc/discovery.js +66 -0
  604. package/lib/oidc/discovery.js.map +1 -0
  605. package/lib/oidc/error.d.ts +18 -0
  606. package/lib/oidc/error.d.ts.map +1 -0
  607. package/lib/oidc/error.js +35 -0
  608. package/lib/oidc/error.js.map +1 -0
  609. package/lib/oidc/index.d.ts +17 -0
  610. package/lib/oidc/index.d.ts.map +1 -0
  611. package/lib/oidc/index.js +29 -0
  612. package/lib/oidc/index.js.map +1 -0
  613. package/lib/oidc/register.d.ts +43 -0
  614. package/lib/oidc/register.d.ts.map +1 -0
  615. package/lib/oidc/register.js +96 -0
  616. package/lib/oidc/register.js.map +1 -0
  617. package/lib/oidc/tokenRefresher.d.ts +69 -0
  618. package/lib/oidc/tokenRefresher.d.ts.map +1 -0
  619. package/lib/oidc/tokenRefresher.js +148 -0
  620. package/lib/oidc/tokenRefresher.js.map +1 -0
  621. package/lib/oidc/validate.d.ts +90 -0
  622. package/lib/oidc/validate.d.ts.map +1 -0
  623. package/lib/oidc/validate.js +194 -0
  624. package/lib/oidc/validate.js.map +1 -0
  625. package/lib/pushprocessor.d.ts +128 -0
  626. package/lib/pushprocessor.d.ts.map +1 -0
  627. package/lib/pushprocessor.js +685 -0
  628. package/lib/pushprocessor.js.map +1 -0
  629. package/lib/randomstring.d.ts +5 -0
  630. package/lib/randomstring.d.ts.map +1 -0
  631. package/lib/randomstring.js +43 -0
  632. package/lib/randomstring.js.map +1 -0
  633. package/lib/realtime-callbacks.d.ts +18 -0
  634. package/lib/realtime-callbacks.d.ts.map +1 -0
  635. package/lib/realtime-callbacks.js +177 -0
  636. package/lib/realtime-callbacks.js.map +1 -0
  637. package/lib/receipt-accumulator.d.ts +51 -0
  638. package/lib/receipt-accumulator.d.ts.map +1 -0
  639. package/lib/receipt-accumulator.js +164 -0
  640. package/lib/receipt-accumulator.js.map +1 -0
  641. package/lib/rendezvous/MSC4108SignInWithQR.d.ts +112 -0
  642. package/lib/rendezvous/MSC4108SignInWithQR.d.ts.map +1 -0
  643. package/lib/rendezvous/MSC4108SignInWithQR.js +392 -0
  644. package/lib/rendezvous/MSC4108SignInWithQR.js.map +1 -0
  645. package/lib/rendezvous/RendezvousChannel.d.ts +27 -0
  646. package/lib/rendezvous/RendezvousChannel.d.ts.map +1 -0
  647. package/lib/rendezvous/RendezvousChannel.js +1 -0
  648. package/lib/rendezvous/RendezvousChannel.js.map +1 -0
  649. package/lib/rendezvous/RendezvousCode.d.ts +9 -0
  650. package/lib/rendezvous/RendezvousCode.d.ts.map +1 -0
  651. package/lib/rendezvous/RendezvousCode.js +1 -0
  652. package/lib/rendezvous/RendezvousCode.js.map +1 -0
  653. package/lib/rendezvous/RendezvousError.d.ts +6 -0
  654. package/lib/rendezvous/RendezvousError.d.ts.map +1 -0
  655. package/lib/rendezvous/RendezvousError.js +23 -0
  656. package/lib/rendezvous/RendezvousError.js.map +1 -0
  657. package/lib/rendezvous/RendezvousFailureReason.d.ts +31 -0
  658. package/lib/rendezvous/RendezvousFailureReason.d.ts.map +1 -0
  659. package/lib/rendezvous/RendezvousFailureReason.js +38 -0
  660. package/lib/rendezvous/RendezvousFailureReason.js.map +1 -0
  661. package/lib/rendezvous/RendezvousIntent.d.ts +5 -0
  662. package/lib/rendezvous/RendezvousIntent.d.ts.map +1 -0
  663. package/lib/rendezvous/RendezvousIntent.js +22 -0
  664. package/lib/rendezvous/RendezvousIntent.js.map +1 -0
  665. package/lib/rendezvous/RendezvousTransport.d.ts +36 -0
  666. package/lib/rendezvous/RendezvousTransport.d.ts.map +1 -0
  667. package/lib/rendezvous/RendezvousTransport.js +1 -0
  668. package/lib/rendezvous/RendezvousTransport.js.map +1 -0
  669. package/lib/rendezvous/channels/MSC4108SecureChannel.d.ts +58 -0
  670. package/lib/rendezvous/channels/MSC4108SecureChannel.d.ts.map +1 -0
  671. package/lib/rendezvous/channels/MSC4108SecureChannel.js +246 -0
  672. package/lib/rendezvous/channels/MSC4108SecureChannel.js.map +1 -0
  673. package/lib/rendezvous/channels/index.d.ts +2 -0
  674. package/lib/rendezvous/channels/index.d.ts.map +1 -0
  675. package/lib/rendezvous/channels/index.js +18 -0
  676. package/lib/rendezvous/channels/index.js.map +1 -0
  677. package/lib/rendezvous/index.d.ts +10 -0
  678. package/lib/rendezvous/index.d.ts.map +1 -0
  679. package/lib/rendezvous/index.js +23 -0
  680. package/lib/rendezvous/index.js.map +1 -0
  681. package/lib/rendezvous/transports/MSC4108RendezvousSession.d.ts +61 -0
  682. package/lib/rendezvous/transports/MSC4108RendezvousSession.d.ts.map +1 -0
  683. package/lib/rendezvous/transports/MSC4108RendezvousSession.js +253 -0
  684. package/lib/rendezvous/transports/MSC4108RendezvousSession.js.map +1 -0
  685. package/lib/rendezvous/transports/index.d.ts +2 -0
  686. package/lib/rendezvous/transports/index.d.ts.map +1 -0
  687. package/lib/rendezvous/transports/index.js +18 -0
  688. package/lib/rendezvous/transports/index.js.map +1 -0
  689. package/lib/room-hierarchy.d.ts +35 -0
  690. package/lib/room-hierarchy.d.ts.map +1 -0
  691. package/lib/room-hierarchy.js +136 -0
  692. package/lib/room-hierarchy.js.map +1 -0
  693. package/lib/rust-crypto/CrossSigningIdentity.d.ts +33 -0
  694. package/lib/rust-crypto/CrossSigningIdentity.d.ts.map +1 -0
  695. package/lib/rust-crypto/CrossSigningIdentity.js +157 -0
  696. package/lib/rust-crypto/CrossSigningIdentity.js.map +1 -0
  697. package/lib/rust-crypto/DehydratedDeviceManager.d.ts +98 -0
  698. package/lib/rust-crypto/DehydratedDeviceManager.d.ts.map +1 -0
  699. package/lib/rust-crypto/DehydratedDeviceManager.js +285 -0
  700. package/lib/rust-crypto/DehydratedDeviceManager.js.map +1 -0
  701. package/lib/rust-crypto/KeyClaimManager.d.ts +33 -0
  702. package/lib/rust-crypto/KeyClaimManager.d.ts.map +1 -0
  703. package/lib/rust-crypto/KeyClaimManager.js +82 -0
  704. package/lib/rust-crypto/KeyClaimManager.js.map +1 -0
  705. package/lib/rust-crypto/OutgoingRequestProcessor.d.ts +43 -0
  706. package/lib/rust-crypto/OutgoingRequestProcessor.d.ts.map +1 -0
  707. package/lib/rust-crypto/OutgoingRequestProcessor.js +195 -0
  708. package/lib/rust-crypto/OutgoingRequestProcessor.js.map +1 -0
  709. package/lib/rust-crypto/OutgoingRequestsManager.d.ts +47 -0
  710. package/lib/rust-crypto/OutgoingRequestsManager.d.ts.map +1 -0
  711. package/lib/rust-crypto/OutgoingRequestsManager.js +148 -0
  712. package/lib/rust-crypto/OutgoingRequestsManager.js.map +1 -0
  713. package/lib/rust-crypto/PerSessionKeyBackupDownloader.d.ts +120 -0
  714. package/lib/rust-crypto/PerSessionKeyBackupDownloader.d.ts.map +1 -0
  715. package/lib/rust-crypto/PerSessionKeyBackupDownloader.js +467 -0
  716. package/lib/rust-crypto/PerSessionKeyBackupDownloader.js.map +1 -0
  717. package/lib/rust-crypto/RoomEncryptor.d.ts +98 -0
  718. package/lib/rust-crypto/RoomEncryptor.d.ts.map +1 -0
  719. package/lib/rust-crypto/RoomEncryptor.js +299 -0
  720. package/lib/rust-crypto/RoomEncryptor.js.map +1 -0
  721. package/lib/rust-crypto/backup.d.ts +254 -0
  722. package/lib/rust-crypto/backup.d.ts.map +1 -0
  723. package/lib/rust-crypto/backup.js +837 -0
  724. package/lib/rust-crypto/backup.js.map +1 -0
  725. package/lib/rust-crypto/constants.d.ts +3 -0
  726. package/lib/rust-crypto/constants.d.ts.map +1 -0
  727. package/lib/rust-crypto/constants.js +19 -0
  728. package/lib/rust-crypto/constants.js.map +1 -0
  729. package/lib/rust-crypto/device-converter.d.ts +28 -0
  730. package/lib/rust-crypto/device-converter.d.ts.map +1 -0
  731. package/lib/rust-crypto/device-converter.js +123 -0
  732. package/lib/rust-crypto/device-converter.js.map +1 -0
  733. package/lib/rust-crypto/index.d.ts +61 -0
  734. package/lib/rust-crypto/index.d.ts.map +1 -0
  735. package/lib/rust-crypto/index.js +152 -0
  736. package/lib/rust-crypto/index.js.map +1 -0
  737. package/lib/rust-crypto/libolm_migration.d.ts +81 -0
  738. package/lib/rust-crypto/libolm_migration.d.ts.map +1 -0
  739. package/lib/rust-crypto/libolm_migration.js +459 -0
  740. package/lib/rust-crypto/libolm_migration.js.map +1 -0
  741. package/lib/rust-crypto/rust-crypto.d.ts +556 -0
  742. package/lib/rust-crypto/rust-crypto.d.ts.map +1 -0
  743. package/lib/rust-crypto/rust-crypto.js +2016 -0
  744. package/lib/rust-crypto/rust-crypto.js.map +1 -0
  745. package/lib/rust-crypto/secret-storage.d.ts +22 -0
  746. package/lib/rust-crypto/secret-storage.d.ts.map +1 -0
  747. package/lib/rust-crypto/secret-storage.js +63 -0
  748. package/lib/rust-crypto/secret-storage.js.map +1 -0
  749. package/lib/rust-crypto/verification.d.ts +319 -0
  750. package/lib/rust-crypto/verification.d.ts.map +1 -0
  751. package/lib/rust-crypto/verification.js +816 -0
  752. package/lib/rust-crypto/verification.js.map +1 -0
  753. package/lib/scheduler.d.ts +132 -0
  754. package/lib/scheduler.d.ts.map +1 -0
  755. package/lib/scheduler.js +259 -0
  756. package/lib/scheduler.js.map +1 -0
  757. package/lib/secret-storage.d.ts +370 -0
  758. package/lib/secret-storage.d.ts.map +1 -0
  759. package/lib/secret-storage.js +466 -0
  760. package/lib/secret-storage.js.map +1 -0
  761. package/lib/serverCapabilities.d.ts +72 -0
  762. package/lib/serverCapabilities.d.ts.map +1 -0
  763. package/lib/serverCapabilities.js +105 -0
  764. package/lib/serverCapabilities.js.map +1 -0
  765. package/lib/service-types.d.ts +5 -0
  766. package/lib/service-types.d.ts.map +1 -0
  767. package/lib/service-types.js +22 -0
  768. package/lib/service-types.js.map +1 -0
  769. package/lib/sliding-sync-sdk.d.ts +107 -0
  770. package/lib/sliding-sync-sdk.d.ts.map +1 -0
  771. package/lib/sliding-sync-sdk.js +903 -0
  772. package/lib/sliding-sync-sdk.js.map +1 -0
  773. package/lib/sliding-sync.d.ts +343 -0
  774. package/lib/sliding-sync.d.ts.map +1 -0
  775. package/lib/sliding-sync.js +817 -0
  776. package/lib/sliding-sync.js.map +1 -0
  777. package/lib/store/index.d.ts +201 -0
  778. package/lib/store/index.d.ts.map +1 -0
  779. package/lib/store/index.js +1 -0
  780. package/lib/store/index.js.map +1 -0
  781. package/lib/store/indexeddb-backend.d.ts +24 -0
  782. package/lib/store/indexeddb-backend.d.ts.map +1 -0
  783. package/lib/store/indexeddb-backend.js +1 -0
  784. package/lib/store/indexeddb-backend.js.map +1 -0
  785. package/lib/store/indexeddb-local-backend.d.ts +129 -0
  786. package/lib/store/indexeddb-local-backend.d.ts.map +1 -0
  787. package/lib/store/indexeddb-local-backend.js +597 -0
  788. package/lib/store/indexeddb-local-backend.js.map +1 -0
  789. package/lib/store/indexeddb-remote-backend.d.ts +79 -0
  790. package/lib/store/indexeddb-remote-backend.d.ts.map +1 -0
  791. package/lib/store/indexeddb-remote-backend.js +210 -0
  792. package/lib/store/indexeddb-remote-backend.js.map +1 -0
  793. package/lib/store/indexeddb-store-worker.d.ts +35 -0
  794. package/lib/store/indexeddb-store-worker.d.ts.map +1 -0
  795. package/lib/store/indexeddb-store-worker.js +146 -0
  796. package/lib/store/indexeddb-store-worker.js.map +1 -0
  797. package/lib/store/indexeddb.d.ts +142 -0
  798. package/lib/store/indexeddb.d.ts.map +1 -0
  799. package/lib/store/indexeddb.js +347 -0
  800. package/lib/store/indexeddb.js.map +1 -0
  801. package/lib/store/local-storage-events-emitter.d.ts +30 -0
  802. package/lib/store/local-storage-events-emitter.d.ts.map +1 -0
  803. package/lib/store/local-storage-events-emitter.js +37 -0
  804. package/lib/store/local-storage-events-emitter.js.map +1 -0
  805. package/lib/store/memory.d.ts +209 -0
  806. package/lib/store/memory.d.ts.map +1 -0
  807. package/lib/store/memory.js +432 -0
  808. package/lib/store/memory.js.map +1 -0
  809. package/lib/store/stub.d.ts +161 -0
  810. package/lib/store/stub.d.ts.map +1 -0
  811. package/lib/store/stub.js +268 -0
  812. package/lib/store/stub.js.map +1 -0
  813. package/lib/sync-accumulator.d.ts +172 -0
  814. package/lib/sync-accumulator.d.ts.map +1 -0
  815. package/lib/sync-accumulator.js +532 -0
  816. package/lib/sync-accumulator.js.map +1 -0
  817. package/lib/sync.d.ts +260 -0
  818. package/lib/sync.d.ts.map +1 -0
  819. package/lib/sync.js +1686 -0
  820. package/lib/sync.js.map +1 -0
  821. package/lib/testing.d.ts +81 -0
  822. package/lib/testing.d.ts.map +1 -0
  823. package/lib/testing.js +162 -0
  824. package/lib/testing.js.map +1 -0
  825. package/lib/thread-utils.d.ts +10 -0
  826. package/lib/thread-utils.d.ts.map +1 -0
  827. package/lib/thread-utils.js +31 -0
  828. package/lib/thread-utils.js.map +1 -0
  829. package/lib/timeline-window.d.ts +168 -0
  830. package/lib/timeline-window.d.ts.map +1 -0
  831. package/lib/timeline-window.js +494 -0
  832. package/lib/timeline-window.js.map +1 -0
  833. package/lib/types.d.ts +33 -0
  834. package/lib/types.d.ts.map +1 -0
  835. package/lib/types.js +33 -0
  836. package/lib/types.js.map +1 -0
  837. package/lib/utils/decryptAESSecretStorageItem.d.ts +12 -0
  838. package/lib/utils/decryptAESSecretStorageItem.d.ts.map +1 -0
  839. package/lib/utils/decryptAESSecretStorageItem.js +50 -0
  840. package/lib/utils/decryptAESSecretStorageItem.js.map +1 -0
  841. package/lib/utils/encryptAESSecretStorageItem.d.ts +16 -0
  842. package/lib/utils/encryptAESSecretStorageItem.d.ts.map +1 -0
  843. package/lib/utils/encryptAESSecretStorageItem.js +68 -0
  844. package/lib/utils/encryptAESSecretStorageItem.js.map +1 -0
  845. package/lib/utils/internal/deriveKeys.d.ts +10 -0
  846. package/lib/utils/internal/deriveKeys.d.ts.map +1 -0
  847. package/lib/utils/internal/deriveKeys.js +60 -0
  848. package/lib/utils/internal/deriveKeys.js.map +1 -0
  849. package/lib/utils.d.ts +267 -0
  850. package/lib/utils.d.ts.map +1 -0
  851. package/lib/utils.js +749 -0
  852. package/lib/utils.js.map +1 -0
  853. package/lib/version-support.d.ts +19 -0
  854. package/lib/version-support.d.ts.map +1 -0
  855. package/lib/version-support.js +37 -0
  856. package/lib/version-support.js.map +1 -0
  857. package/lib/webrtc/audioContext.d.ts +15 -0
  858. package/lib/webrtc/audioContext.d.ts.map +1 -0
  859. package/lib/webrtc/audioContext.js +46 -0
  860. package/lib/webrtc/audioContext.js.map +1 -0
  861. package/lib/webrtc/call.d.ts +560 -0
  862. package/lib/webrtc/call.d.ts.map +1 -0
  863. package/lib/webrtc/call.js +2541 -0
  864. package/lib/webrtc/call.js.map +1 -0
  865. package/lib/webrtc/callEventHandler.d.ts +37 -0
  866. package/lib/webrtc/callEventHandler.d.ts.map +1 -0
  867. package/lib/webrtc/callEventHandler.js +344 -0
  868. package/lib/webrtc/callEventHandler.js.map +1 -0
  869. package/lib/webrtc/callEventTypes.d.ts +73 -0
  870. package/lib/webrtc/callEventTypes.d.ts.map +1 -0
  871. package/lib/webrtc/callEventTypes.js +13 -0
  872. package/lib/webrtc/callEventTypes.js.map +1 -0
  873. package/lib/webrtc/callFeed.d.ts +128 -0
  874. package/lib/webrtc/callFeed.d.ts.map +1 -0
  875. package/lib/webrtc/callFeed.js +289 -0
  876. package/lib/webrtc/callFeed.js.map +1 -0
  877. package/lib/webrtc/groupCall.d.ts +323 -0
  878. package/lib/webrtc/groupCall.d.ts.map +1 -0
  879. package/lib/webrtc/groupCall.js +1337 -0
  880. package/lib/webrtc/groupCall.js.map +1 -0
  881. package/lib/webrtc/groupCallEventHandler.d.ts +31 -0
  882. package/lib/webrtc/groupCallEventHandler.d.ts.map +1 -0
  883. package/lib/webrtc/groupCallEventHandler.js +178 -0
  884. package/lib/webrtc/groupCallEventHandler.js.map +1 -0
  885. package/lib/webrtc/mediaHandler.d.ts +89 -0
  886. package/lib/webrtc/mediaHandler.d.ts.map +1 -0
  887. package/lib/webrtc/mediaHandler.js +437 -0
  888. package/lib/webrtc/mediaHandler.js.map +1 -0
  889. package/lib/webrtc/stats/callFeedStatsReporter.d.ts +8 -0
  890. package/lib/webrtc/stats/callFeedStatsReporter.d.ts.map +1 -0
  891. package/lib/webrtc/stats/callFeedStatsReporter.js +82 -0
  892. package/lib/webrtc/stats/callFeedStatsReporter.js.map +1 -0
  893. package/lib/webrtc/stats/callStatsReportGatherer.d.ts +25 -0
  894. package/lib/webrtc/stats/callStatsReportGatherer.d.ts.map +1 -0
  895. package/lib/webrtc/stats/callStatsReportGatherer.js +199 -0
  896. package/lib/webrtc/stats/callStatsReportGatherer.js.map +1 -0
  897. package/lib/webrtc/stats/callStatsReportSummary.d.ts +17 -0
  898. package/lib/webrtc/stats/callStatsReportSummary.d.ts.map +1 -0
  899. package/lib/webrtc/stats/callStatsReportSummary.js +1 -0
  900. package/lib/webrtc/stats/callStatsReportSummary.js.map +1 -0
  901. package/lib/webrtc/stats/connectionStats.d.ts +28 -0
  902. package/lib/webrtc/stats/connectionStats.d.ts.map +1 -0
  903. package/lib/webrtc/stats/connectionStats.js +26 -0
  904. package/lib/webrtc/stats/connectionStats.js.map +1 -0
  905. package/lib/webrtc/stats/connectionStatsBuilder.d.ts +5 -0
  906. package/lib/webrtc/stats/connectionStatsBuilder.d.ts.map +1 -0
  907. package/lib/webrtc/stats/connectionStatsBuilder.js +27 -0
  908. package/lib/webrtc/stats/connectionStatsBuilder.js.map +1 -0
  909. package/lib/webrtc/stats/connectionStatsReportBuilder.d.ts +7 -0
  910. package/lib/webrtc/stats/connectionStatsReportBuilder.d.ts.map +1 -0
  911. package/lib/webrtc/stats/connectionStatsReportBuilder.js +121 -0
  912. package/lib/webrtc/stats/connectionStatsReportBuilder.js.map +1 -0
  913. package/lib/webrtc/stats/groupCallStats.d.ts +22 -0
  914. package/lib/webrtc/stats/groupCallStats.d.ts.map +1 -0
  915. package/lib/webrtc/stats/groupCallStats.js +78 -0
  916. package/lib/webrtc/stats/groupCallStats.js.map +1 -0
  917. package/lib/webrtc/stats/media/mediaSsrcHandler.d.ts +10 -0
  918. package/lib/webrtc/stats/media/mediaSsrcHandler.d.ts.map +1 -0
  919. package/lib/webrtc/stats/media/mediaSsrcHandler.js +57 -0
  920. package/lib/webrtc/stats/media/mediaSsrcHandler.js.map +1 -0
  921. package/lib/webrtc/stats/media/mediaTrackHandler.d.ts +12 -0
  922. package/lib/webrtc/stats/media/mediaTrackHandler.d.ts.map +1 -0
  923. package/lib/webrtc/stats/media/mediaTrackHandler.js +62 -0
  924. package/lib/webrtc/stats/media/mediaTrackHandler.js.map +1 -0
  925. package/lib/webrtc/stats/media/mediaTrackStats.d.ts +86 -0
  926. package/lib/webrtc/stats/media/mediaTrackStats.d.ts.map +1 -0
  927. package/lib/webrtc/stats/media/mediaTrackStats.js +142 -0
  928. package/lib/webrtc/stats/media/mediaTrackStats.js.map +1 -0
  929. package/lib/webrtc/stats/media/mediaTrackStatsHandler.d.ts +22 -0
  930. package/lib/webrtc/stats/media/mediaTrackStatsHandler.d.ts.map +1 -0
  931. package/lib/webrtc/stats/media/mediaTrackStatsHandler.js +76 -0
  932. package/lib/webrtc/stats/media/mediaTrackStatsHandler.js.map +1 -0
  933. package/lib/webrtc/stats/statsReport.d.ts +99 -0
  934. package/lib/webrtc/stats/statsReport.d.ts.map +1 -0
  935. package/lib/webrtc/stats/statsReport.js +32 -0
  936. package/lib/webrtc/stats/statsReport.js.map +1 -0
  937. package/lib/webrtc/stats/statsReportEmitter.d.ts +15 -0
  938. package/lib/webrtc/stats/statsReportEmitter.d.ts.map +1 -0
  939. package/lib/webrtc/stats/statsReportEmitter.js +33 -0
  940. package/lib/webrtc/stats/statsReportEmitter.js.map +1 -0
  941. package/lib/webrtc/stats/summaryStatsReportGatherer.d.ts +16 -0
  942. package/lib/webrtc/stats/summaryStatsReportGatherer.d.ts.map +1 -0
  943. package/lib/webrtc/stats/summaryStatsReportGatherer.js +116 -0
  944. package/lib/webrtc/stats/summaryStatsReportGatherer.js.map +1 -0
  945. package/lib/webrtc/stats/trackStatsBuilder.d.ts +19 -0
  946. package/lib/webrtc/stats/trackStatsBuilder.d.ts.map +1 -0
  947. package/lib/webrtc/stats/trackStatsBuilder.js +168 -0
  948. package/lib/webrtc/stats/trackStatsBuilder.js.map +1 -0
  949. package/lib/webrtc/stats/transportStats.d.ts +11 -0
  950. package/lib/webrtc/stats/transportStats.d.ts.map +1 -0
  951. package/lib/webrtc/stats/transportStats.js +1 -0
  952. package/lib/webrtc/stats/transportStats.js.map +1 -0
  953. package/lib/webrtc/stats/transportStatsBuilder.d.ts +5 -0
  954. package/lib/webrtc/stats/transportStatsBuilder.d.ts.map +1 -0
  955. package/lib/webrtc/stats/transportStatsBuilder.js +34 -0
  956. package/lib/webrtc/stats/transportStatsBuilder.js.map +1 -0
  957. package/lib/webrtc/stats/valueFormatter.d.ts +4 -0
  958. package/lib/webrtc/stats/valueFormatter.d.ts.map +1 -0
  959. package/lib/webrtc/stats/valueFormatter.js +25 -0
  960. package/lib/webrtc/stats/valueFormatter.js.map +1 -0
  961. package/package.json +134 -0
  962. package/src/@types/AESEncryptedSecretStoragePayload.ts +29 -0
  963. package/src/@types/IIdentityServerProvider.ts +24 -0
  964. package/src/@types/PushRules.ts +209 -0
  965. package/src/@types/another-json.d.ts +19 -0
  966. package/src/@types/auth.ts +252 -0
  967. package/src/@types/beacon.ts +140 -0
  968. package/src/@types/common.ts +22 -0
  969. package/src/@types/crypto.ts +73 -0
  970. package/src/@types/event.ts +370 -0
  971. package/src/@types/events.ts +119 -0
  972. package/src/@types/extensible_events.ts +150 -0
  973. package/src/@types/global.d.ts +70 -0
  974. package/src/@types/local_notifications.ts +19 -0
  975. package/src/@types/location.ts +92 -0
  976. package/src/@types/matrix-sdk-crypto-wasm.d.ts +44 -0
  977. package/src/@types/media.ts +245 -0
  978. package/src/@types/membership.ts +57 -0
  979. package/src/@types/oidc-client-ts.d.ts +24 -0
  980. package/src/@types/partials.ts +67 -0
  981. package/src/@types/polls.ts +119 -0
  982. package/src/@types/read_receipts.ts +61 -0
  983. package/src/@types/registration.ts +102 -0
  984. package/src/@types/requests.ts +314 -0
  985. package/src/@types/search.ts +119 -0
  986. package/src/@types/signed.ts +25 -0
  987. package/src/@types/spaces.ts +37 -0
  988. package/src/@types/state_events.ts +147 -0
  989. package/src/@types/synapse.ts +40 -0
  990. package/src/@types/sync.ts +27 -0
  991. package/src/@types/threepids.ts +29 -0
  992. package/src/@types/topic.ts +63 -0
  993. package/src/@types/uia.ts +29 -0
  994. package/src/NamespacedValue.ts +123 -0
  995. package/src/ReEmitter.ts +93 -0
  996. package/src/ToDeviceMessageQueue.ts +153 -0
  997. package/src/autodiscovery.ts +505 -0
  998. package/src/base64.ts +88 -0
  999. package/src/browser-index.ts +44 -0
  1000. package/src/client.ts +10474 -0
  1001. package/src/common-crypto/CryptoBackend.ts +302 -0
  1002. package/src/common-crypto/README.md +4 -0
  1003. package/src/common-crypto/key-passphrase.ts +43 -0
  1004. package/src/content-helpers.ts +288 -0
  1005. package/src/content-repo.ts +117 -0
  1006. package/src/crypto/CrossSigning.ts +773 -0
  1007. package/src/crypto/DeviceList.ts +989 -0
  1008. package/src/crypto/EncryptionSetup.ts +351 -0
  1009. package/src/crypto/OlmDevice.ts +1500 -0
  1010. package/src/crypto/OutgoingRoomKeyRequestManager.ts +485 -0
  1011. package/src/crypto/RoomList.ts +70 -0
  1012. package/src/crypto/SecretSharing.ts +240 -0
  1013. package/src/crypto/SecretStorage.ts +136 -0
  1014. package/src/crypto/aes.ts +23 -0
  1015. package/src/crypto/algorithms/base.ts +236 -0
  1016. package/src/crypto/algorithms/index.ts +20 -0
  1017. package/src/crypto/algorithms/megolm.ts +2216 -0
  1018. package/src/crypto/algorithms/olm.ts +381 -0
  1019. package/src/crypto/api.ts +70 -0
  1020. package/src/crypto/backup.ts +922 -0
  1021. package/src/crypto/crypto.ts +18 -0
  1022. package/src/crypto/dehydration.ts +272 -0
  1023. package/src/crypto/device-converter.ts +45 -0
  1024. package/src/crypto/deviceinfo.ts +158 -0
  1025. package/src/crypto/index.ts +4414 -0
  1026. package/src/crypto/key_passphrase.ts +42 -0
  1027. package/src/crypto/keybackup.ts +47 -0
  1028. package/src/crypto/olmlib.ts +539 -0
  1029. package/src/crypto/recoverykey.ts +18 -0
  1030. package/src/crypto/store/base.ts +348 -0
  1031. package/src/crypto/store/indexeddb-crypto-store-backend.ts +1250 -0
  1032. package/src/crypto/store/indexeddb-crypto-store.ts +845 -0
  1033. package/src/crypto/store/localStorage-crypto-store.ts +579 -0
  1034. package/src/crypto/store/memory-crypto-store.ts +680 -0
  1035. package/src/crypto/verification/Base.ts +409 -0
  1036. package/src/crypto/verification/Error.ts +76 -0
  1037. package/src/crypto/verification/IllegalMethod.ts +50 -0
  1038. package/src/crypto/verification/QRCode.ts +310 -0
  1039. package/src/crypto/verification/SAS.ts +494 -0
  1040. package/src/crypto/verification/SASDecimal.ts +37 -0
  1041. package/src/crypto/verification/request/Channel.ts +34 -0
  1042. package/src/crypto/verification/request/InRoomChannel.ts +371 -0
  1043. package/src/crypto/verification/request/ToDeviceChannel.ts +354 -0
  1044. package/src/crypto/verification/request/VerificationRequest.ts +976 -0
  1045. package/src/crypto-api/CryptoEvent.ts +93 -0
  1046. package/src/crypto-api/CryptoEventHandlerMap.ts +32 -0
  1047. package/src/crypto-api/index.ts +1175 -0
  1048. package/src/crypto-api/key-passphrase.ts +58 -0
  1049. package/src/crypto-api/keybackup.ts +115 -0
  1050. package/src/crypto-api/recovery-key.ts +69 -0
  1051. package/src/crypto-api/verification.ts +408 -0
  1052. package/src/digest.ts +34 -0
  1053. package/src/embedded.ts +631 -0
  1054. package/src/errors.ts +54 -0
  1055. package/src/event-mapper.ts +97 -0
  1056. package/src/extensible_events_v1/ExtensibleEvent.ts +58 -0
  1057. package/src/extensible_events_v1/InvalidEventError.ts +24 -0
  1058. package/src/extensible_events_v1/MessageEvent.ts +145 -0
  1059. package/src/extensible_events_v1/PollEndEvent.ts +97 -0
  1060. package/src/extensible_events_v1/PollResponseEvent.ts +148 -0
  1061. package/src/extensible_events_v1/PollStartEvent.ts +207 -0
  1062. package/src/extensible_events_v1/utilities.ts +35 -0
  1063. package/src/feature.ts +87 -0
  1064. package/src/filter-component.ts +207 -0
  1065. package/src/filter.ts +245 -0
  1066. package/src/http-api/errors.ts +199 -0
  1067. package/src/http-api/fetch.ts +383 -0
  1068. package/src/http-api/index.ts +191 -0
  1069. package/src/http-api/interface.ts +178 -0
  1070. package/src/http-api/method.ts +25 -0
  1071. package/src/http-api/prefix.ts +48 -0
  1072. package/src/http-api/utils.ts +200 -0
  1073. package/src/index.ts +25 -0
  1074. package/src/indexeddb-helpers.ts +50 -0
  1075. package/src/indexeddb-worker.ts +24 -0
  1076. package/src/interactive-auth.ts +694 -0
  1077. package/src/logger.ts +185 -0
  1078. package/src/matrix.ts +177 -0
  1079. package/src/matrixrtc/CallMembership.ts +247 -0
  1080. package/src/matrixrtc/LivekitFocus.ts +39 -0
  1081. package/src/matrixrtc/MatrixRTCSession.ts +1319 -0
  1082. package/src/matrixrtc/MatrixRTCSessionManager.ts +166 -0
  1083. package/src/matrixrtc/focus.ts +25 -0
  1084. package/src/matrixrtc/index.ts +22 -0
  1085. package/src/matrixrtc/types.ts +36 -0
  1086. package/src/models/MSC3089Branch.ts +272 -0
  1087. package/src/models/MSC3089TreeSpace.ts +565 -0
  1088. package/src/models/ToDeviceMessage.ts +38 -0
  1089. package/src/models/beacon.ts +214 -0
  1090. package/src/models/compare-event-ordering.ts +139 -0
  1091. package/src/models/device.ts +85 -0
  1092. package/src/models/event-context.ts +110 -0
  1093. package/src/models/event-status.ts +39 -0
  1094. package/src/models/event-timeline-set.ts +979 -0
  1095. package/src/models/event-timeline.ts +476 -0
  1096. package/src/models/event.ts +1751 -0
  1097. package/src/models/invites-ignorer.ts +376 -0
  1098. package/src/models/poll.ts +285 -0
  1099. package/src/models/profile-keys.ts +7 -0
  1100. package/src/models/read-receipt.ts +422 -0
  1101. package/src/models/related-relations.ts +39 -0
  1102. package/src/models/relations-container.ts +149 -0
  1103. package/src/models/relations.ts +368 -0
  1104. package/src/models/room-member.ts +457 -0
  1105. package/src/models/room-receipts.ts +439 -0
  1106. package/src/models/room-state.ts +1130 -0
  1107. package/src/models/room-summary.ts +47 -0
  1108. package/src/models/room.ts +3822 -0
  1109. package/src/models/search-result.ts +57 -0
  1110. package/src/models/thread.ts +923 -0
  1111. package/src/models/typed-event-emitter.ts +246 -0
  1112. package/src/models/user.ts +302 -0
  1113. package/src/oidc/authorize.ts +274 -0
  1114. package/src/oidc/discovery.ts +60 -0
  1115. package/src/oidc/error.ts +33 -0
  1116. package/src/oidc/index.ts +34 -0
  1117. package/src/oidc/register.ts +123 -0
  1118. package/src/oidc/tokenRefresher.ts +149 -0
  1119. package/src/oidc/validate.ts +282 -0
  1120. package/src/pushprocessor.ts +837 -0
  1121. package/src/randomstring.ts +51 -0
  1122. package/src/realtime-callbacks.ts +191 -0
  1123. package/src/receipt-accumulator.ts +189 -0
  1124. package/src/rendezvous/MSC4108SignInWithQR.ts +444 -0
  1125. package/src/rendezvous/RendezvousChannel.ts +48 -0
  1126. package/src/rendezvous/RendezvousCode.ts +25 -0
  1127. package/src/rendezvous/RendezvousError.ts +26 -0
  1128. package/src/rendezvous/RendezvousFailureReason.ts +49 -0
  1129. package/src/rendezvous/RendezvousIntent.ts +20 -0
  1130. package/src/rendezvous/RendezvousTransport.ts +58 -0
  1131. package/src/rendezvous/channels/MSC4108SecureChannel.ts +270 -0
  1132. package/src/rendezvous/channels/index.ts +17 -0
  1133. package/src/rendezvous/index.ts +25 -0
  1134. package/src/rendezvous/transports/MSC4108RendezvousSession.ts +270 -0
  1135. package/src/rendezvous/transports/index.ts +17 -0
  1136. package/src/room-hierarchy.ts +152 -0
  1137. package/src/rust-crypto/CrossSigningIdentity.ts +183 -0
  1138. package/src/rust-crypto/DehydratedDeviceManager.ts +306 -0
  1139. package/src/rust-crypto/KeyClaimManager.ts +86 -0
  1140. package/src/rust-crypto/OutgoingRequestProcessor.ts +236 -0
  1141. package/src/rust-crypto/OutgoingRequestsManager.ts +143 -0
  1142. package/src/rust-crypto/PerSessionKeyBackupDownloader.ts +501 -0
  1143. package/src/rust-crypto/RoomEncryptor.ts +352 -0
  1144. package/src/rust-crypto/backup.ts +881 -0
  1145. package/src/rust-crypto/constants.ts +18 -0
  1146. package/src/rust-crypto/device-converter.ts +128 -0
  1147. package/src/rust-crypto/index.ts +237 -0
  1148. package/src/rust-crypto/libolm_migration.ts +530 -0
  1149. package/src/rust-crypto/rust-crypto.ts +2205 -0
  1150. package/src/rust-crypto/secret-storage.ts +60 -0
  1151. package/src/rust-crypto/verification.ts +830 -0
  1152. package/src/scheduler.ts +309 -0
  1153. package/src/secret-storage.ts +693 -0
  1154. package/src/serverCapabilities.ts +139 -0
  1155. package/src/service-types.ts +20 -0
  1156. package/src/sliding-sync-sdk.ts +1026 -0
  1157. package/src/sliding-sync.ts +965 -0
  1158. package/src/store/index.ts +261 -0
  1159. package/src/store/indexeddb-backend.ts +41 -0
  1160. package/src/store/indexeddb-local-backend.ts +610 -0
  1161. package/src/store/indexeddb-remote-backend.ts +213 -0
  1162. package/src/store/indexeddb-store-worker.ts +157 -0
  1163. package/src/store/indexeddb.ts +397 -0
  1164. package/src/store/local-storage-events-emitter.ts +46 -0
  1165. package/src/store/memory.ts +448 -0
  1166. package/src/store/stub.ts +280 -0
  1167. package/src/sync-accumulator.ts +689 -0
  1168. package/src/sync.ts +1920 -0
  1169. package/src/testing.ts +191 -0
  1170. package/src/thread-utils.ts +31 -0
  1171. package/src/timeline-window.ts +536 -0
  1172. package/src/types.ts +59 -0
  1173. package/src/utils/decryptAESSecretStorageItem.ts +54 -0
  1174. package/src/utils/encryptAESSecretStorageItem.ts +73 -0
  1175. package/src/utils/internal/deriveKeys.ts +63 -0
  1176. package/src/utils.ts +763 -0
  1177. package/src/version-support.ts +36 -0
  1178. package/src/webrtc/audioContext.ts +44 -0
  1179. package/src/webrtc/call.ts +3074 -0
  1180. package/src/webrtc/callEventHandler.ts +425 -0
  1181. package/src/webrtc/callEventTypes.ts +93 -0
  1182. package/src/webrtc/callFeed.ts +364 -0
  1183. package/src/webrtc/groupCall.ts +1735 -0
  1184. package/src/webrtc/groupCallEventHandler.ts +234 -0
  1185. package/src/webrtc/mediaHandler.ts +484 -0
  1186. package/src/webrtc/stats/callFeedStatsReporter.ts +94 -0
  1187. package/src/webrtc/stats/callStatsReportGatherer.ts +219 -0
  1188. package/src/webrtc/stats/callStatsReportSummary.ts +30 -0
  1189. package/src/webrtc/stats/connectionStats.ts +47 -0
  1190. package/src/webrtc/stats/connectionStatsBuilder.ts +28 -0
  1191. package/src/webrtc/stats/connectionStatsReportBuilder.ts +140 -0
  1192. package/src/webrtc/stats/groupCallStats.ts +93 -0
  1193. package/src/webrtc/stats/media/mediaSsrcHandler.ts +57 -0
  1194. package/src/webrtc/stats/media/mediaTrackHandler.ts +76 -0
  1195. package/src/webrtc/stats/media/mediaTrackStats.ts +176 -0
  1196. package/src/webrtc/stats/media/mediaTrackStatsHandler.ts +90 -0
  1197. package/src/webrtc/stats/statsReport.ts +133 -0
  1198. package/src/webrtc/stats/statsReportEmitter.ts +49 -0
  1199. package/src/webrtc/stats/summaryStatsReportGatherer.ts +148 -0
  1200. package/src/webrtc/stats/trackStatsBuilder.ts +207 -0
  1201. package/src/webrtc/stats/transportStats.ts +26 -0
  1202. package/src/webrtc/stats/transportStatsBuilder.ts +48 -0
  1203. package/src/webrtc/stats/valueFormatter.ts +27 -0
@@ -0,0 +1,3822 @@
1
+ /*
2
+ Copyright 2015 - 2023 The Matrix.org Foundation C.I.C.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import { M_POLL_START, Optional } from "matrix-events-sdk";
18
+
19
+ import {
20
+ EventTimelineSet,
21
+ DuplicateStrategy,
22
+ IAddLiveEventOptions,
23
+ EventTimelineSetHandlerMap,
24
+ } from "./event-timeline-set.ts";
25
+ import { Direction, EventTimeline } from "./event-timeline.ts";
26
+ import { getHttpUriForMxc } from "../content-repo.ts";
27
+ import { removeElement } from "../utils.ts";
28
+ import { normalize, noUnsafeEventProps } from "../utils.ts";
29
+ import { IEvent, IThreadBundledRelationship, MatrixEvent, MatrixEventEvent, MatrixEventHandlerMap } from "./event.ts";
30
+ import { EventStatus } from "./event-status.ts";
31
+ import { RoomMember } from "./room-member.ts";
32
+ import { IRoomSummary, RoomSummary } from "./room-summary.ts";
33
+ import { logger } from "../logger.ts";
34
+ import { TypedReEmitter } from "../ReEmitter.ts";
35
+ import {
36
+ EventType,
37
+ RoomCreateTypeField,
38
+ RoomType,
39
+ UNSTABLE_ELEMENT_FUNCTIONAL_USERS,
40
+ EVENT_VISIBILITY_CHANGE_TYPE,
41
+ RelationType,
42
+ UNSIGNED_THREAD_ID_FIELD,
43
+ } from "../@types/event.ts";
44
+ import { MatrixClient, PendingEventOrdering } from "../client.ts";
45
+ import { GuestAccess, HistoryVisibility, JoinRule, ResizeMethod } from "../@types/partials.ts";
46
+ import { Filter, IFilterDefinition } from "../filter.ts";
47
+ import { RoomState, RoomStateEvent, RoomStateEventHandlerMap } from "./room-state.ts";
48
+ import { BeaconEvent, BeaconEventHandlerMap } from "./beacon.ts";
49
+ import {
50
+ Thread,
51
+ ThreadEvent,
52
+ ThreadEventHandlerMap as ThreadHandlerMap,
53
+ FILTER_RELATED_BY_REL_TYPES,
54
+ THREAD_RELATION_TYPE,
55
+ FILTER_RELATED_BY_SENDERS,
56
+ ThreadFilterType,
57
+ } from "./thread.ts";
58
+ import {
59
+ CachedReceiptStructure,
60
+ MAIN_ROOM_TIMELINE,
61
+ Receipt,
62
+ ReceiptContent,
63
+ ReceiptType,
64
+ } from "../@types/read_receipts.ts";
65
+ import { IStateEventWithRoomId } from "../@types/search.ts";
66
+ import { RelationsContainer } from "./relations-container.ts";
67
+ import { ReadReceipt, synthesizeReceipt } from "./read-receipt.ts";
68
+ import { isPollEvent, Poll, PollEvent } from "./poll.ts";
69
+ import { RoomReceipts } from "./room-receipts.ts";
70
+ import { compareEventOrdering } from "./compare-event-ordering.ts";
71
+ import * as utils from "../utils.ts";
72
+ import { KnownMembership, Membership } from "../@types/membership.ts";
73
+ import { Capabilities, IRoomVersionsCapability, RoomVersionStability } from "../serverCapabilities.ts";
74
+
75
+ // These constants are used as sane defaults when the homeserver doesn't support
76
+ // the m.room_versions capability. In practice, KNOWN_SAFE_ROOM_VERSION should be
77
+ // the same as the common default room version whereas SAFE_ROOM_VERSIONS are the
78
+ // room versions which are considered okay for people to run without being asked
79
+ // to upgrade (ie: "stable"). Eventually, we should remove these when all homeservers
80
+ // return an m.room_versions capability.
81
+ export const KNOWN_SAFE_ROOM_VERSION = "10";
82
+ const SAFE_ROOM_VERSIONS = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"];
83
+
84
+ interface IOpts {
85
+ /**
86
+ * Controls where pending messages appear in a room's timeline.
87
+ * If "<b>chronological</b>", messages will appear in the timeline when the call to `sendEvent` was made.
88
+ * If "<b>detached</b>", pending messages will appear in a separate list,
89
+ * accessible via {@link Room#getPendingEvents}.
90
+ * Default: "chronological".
91
+ */
92
+ pendingEventOrdering?: PendingEventOrdering;
93
+ /**
94
+ * Set to true to enable improved timeline support.
95
+ */
96
+ timelineSupport?: boolean;
97
+ lazyLoadMembers?: boolean;
98
+ }
99
+
100
+ export interface IRecommendedVersion {
101
+ version: string;
102
+ needsUpgrade: boolean;
103
+ urgent: boolean;
104
+ }
105
+
106
+ // When inserting a visibility event affecting event `eventId`, we
107
+ // need to scan through existing visibility events for `eventId`.
108
+ // In theory, this could take an unlimited amount of time if:
109
+ //
110
+ // - the visibility event was sent by a moderator; and
111
+ // - `eventId` already has many visibility changes (usually, it should
112
+ // be 2 or less); and
113
+ // - for some reason, the visibility changes are received out of order
114
+ // (usually, this shouldn't happen at all).
115
+ //
116
+ // For this reason, we limit the number of events to scan through,
117
+ // expecting that a broken visibility change for a single event in
118
+ // an extremely uncommon case (possibly a DoS) is a small
119
+ // price to pay to keep matrix-js-sdk responsive.
120
+ const MAX_NUMBER_OF_VISIBILITY_EVENTS_TO_SCAN_THROUGH = 30;
121
+
122
+ export type NotificationCount = Partial<Record<NotificationCountType, number>>;
123
+
124
+ export enum NotificationCountType {
125
+ Highlight = "highlight",
126
+ Total = "total",
127
+ }
128
+
129
+ export interface ICreateFilterOpts {
130
+ // Populate the filtered timeline with already loaded events in the room
131
+ // timeline. Useful to disable for some filters that can't be achieved by the
132
+ // client in an efficient manner
133
+ prepopulateTimeline?: boolean;
134
+ useSyncEvents?: boolean;
135
+ pendingEvents?: boolean;
136
+ }
137
+
138
+ export enum RoomEvent {
139
+ MyMembership = "Room.myMembership",
140
+ Tags = "Room.tags",
141
+ AccountData = "Room.accountData",
142
+ Receipt = "Room.receipt",
143
+ Name = "Room.name",
144
+ Redaction = "Room.redaction",
145
+ RedactionCancelled = "Room.redactionCancelled",
146
+ LocalEchoUpdated = "Room.localEchoUpdated",
147
+ Timeline = "Room.timeline",
148
+ TimelineReset = "Room.timelineReset",
149
+ TimelineRefresh = "Room.TimelineRefresh",
150
+ OldStateUpdated = "Room.OldStateUpdated",
151
+ CurrentStateUpdated = "Room.CurrentStateUpdated",
152
+ HistoryImportedWithinTimeline = "Room.historyImportedWithinTimeline",
153
+ UnreadNotifications = "Room.UnreadNotifications",
154
+ Summary = "Room.Summary",
155
+ }
156
+
157
+ export type RoomEmittedEvents =
158
+ | RoomEvent
159
+ | RoomStateEvent.Events
160
+ | RoomStateEvent.Members
161
+ | RoomStateEvent.NewMember
162
+ | RoomStateEvent.Update
163
+ | RoomStateEvent.Marker
164
+ | ThreadEvent.New
165
+ | ThreadEvent.Update
166
+ | ThreadEvent.NewReply
167
+ | ThreadEvent.Delete
168
+ | MatrixEventEvent.BeforeRedaction
169
+ | BeaconEvent.New
170
+ | BeaconEvent.Update
171
+ | BeaconEvent.Destroy
172
+ | BeaconEvent.LivenessChange
173
+ | PollEvent.New;
174
+
175
+ export type RoomEventHandlerMap = {
176
+ /**
177
+ * Fires when the logged in user's membership in the room is updated.
178
+ *
179
+ * @param room - The room in which the membership has been updated
180
+ * @param membership - The new membership value
181
+ * @param prevMembership - The previous membership value
182
+ */
183
+ [RoomEvent.MyMembership]: (room: Room, membership: Membership, prevMembership?: Membership) => void;
184
+ /**
185
+ * Fires whenever a room's tags are updated.
186
+ * @param event - The tags event
187
+ * @param room - The room whose Room.tags was updated.
188
+ * @example
189
+ * ```
190
+ * matrixClient.on("Room.tags", function(event, room){
191
+ * var newTags = event.getContent().tags;
192
+ * if (newTags["favourite"]) showStar(room);
193
+ * });
194
+ * ```
195
+ */
196
+ [RoomEvent.Tags]: (event: MatrixEvent, room: Room) => void;
197
+ /**
198
+ * Fires whenever a room's account_data is updated.
199
+ * @param event - The account_data event
200
+ * @param room - The room whose account_data was updated.
201
+ * @param prevEvent - The event being replaced by
202
+ * the new account data, if known.
203
+ * @example
204
+ * ```
205
+ * matrixClient.on("Room.accountData", function(event, room, oldEvent){
206
+ * if (event.getType() === "m.room.colorscheme") {
207
+ * applyColorScheme(event.getContents());
208
+ * }
209
+ * });
210
+ * ```
211
+ */
212
+ [RoomEvent.AccountData]: (event: MatrixEvent, room: Room, prevEvent?: MatrixEvent) => void;
213
+ /**
214
+ * Fires whenever a receipt is received for a room
215
+ * @param event - The receipt event
216
+ * @param room - The room whose receipts was updated.
217
+ * @example
218
+ * ```
219
+ * matrixClient.on("Room.receipt", function(event, room){
220
+ * var receiptContent = event.getContent();
221
+ * });
222
+ * ```
223
+ */
224
+ [RoomEvent.Receipt]: (event: MatrixEvent, room: Room) => void;
225
+ /**
226
+ * Fires whenever the name of a room is updated.
227
+ * @param room - The room whose Room.name was updated.
228
+ * @example
229
+ * ```
230
+ * matrixClient.on("Room.name", function(room){
231
+ * var newName = room.name;
232
+ * });
233
+ * ```
234
+ */
235
+ [RoomEvent.Name]: (room: Room) => void;
236
+ /**
237
+ * Fires when an event we had previously received is redacted.
238
+ *
239
+ * (Note this is *not* fired when the redaction happens before we receive the
240
+ * event).
241
+ *
242
+ * @param event - The matrix redaction event
243
+ * @param room - The room containing the redacted event
244
+ * @param threadId - The thread containing the redacted event (before it was redacted)
245
+ */
246
+ [RoomEvent.Redaction]: (event: MatrixEvent, room: Room, threadId?: string) => void;
247
+ /**
248
+ * Fires when an event that was previously redacted isn't anymore.
249
+ * This happens when the redaction couldn't be sent and
250
+ * was subsequently cancelled by the user. Redactions have a local echo
251
+ * which is undone in this scenario.
252
+ *
253
+ * @param event - The matrix redaction event that was cancelled.
254
+ * @param room - The room containing the unredacted event
255
+ */
256
+ [RoomEvent.RedactionCancelled]: (event: MatrixEvent, room: Room) => void;
257
+ /**
258
+ * Fires when the status of a transmitted event is updated.
259
+ *
260
+ * <p>When an event is first transmitted, a temporary copy of the event is
261
+ * inserted into the timeline, with a temporary event id, and a status of
262
+ * 'SENDING'.
263
+ *
264
+ * <p>Once the echo comes back from the server, the content of the event
265
+ * (MatrixEvent.event) is replaced by the complete event from the homeserver,
266
+ * thus updating its event id, as well as server-generated fields such as the
267
+ * timestamp. Its status is set to null.
268
+ *
269
+ * <p>Once the /send request completes, if the remote echo has not already
270
+ * arrived, the event is updated with a new event id and the status is set to
271
+ * 'SENT'. The server-generated fields are of course not updated yet.
272
+ *
273
+ * <p>If the /send fails, In this case, the event's status is set to
274
+ * 'NOT_SENT'. If it is later resent, the process starts again, setting the
275
+ * status to 'SENDING'. Alternatively, the message may be cancelled, which
276
+ * removes the event from the room, and sets the status to 'CANCELLED'.
277
+ *
278
+ * <p>This event is raised to reflect each of the transitions above.
279
+ *
280
+ * @param event - The matrix event which has been updated
281
+ *
282
+ * @param room - The room containing the redacted event
283
+ *
284
+ * @param oldEventId - The previous event id (the temporary event id,
285
+ * except when updating a successfully-sent event when its echo arrives)
286
+ *
287
+ * @param oldStatus - The previous event status.
288
+ */
289
+ [RoomEvent.LocalEchoUpdated]: (
290
+ event: MatrixEvent,
291
+ room: Room,
292
+ oldEventId?: string,
293
+ oldStatus?: EventStatus | null,
294
+ ) => void;
295
+ [RoomEvent.OldStateUpdated]: (room: Room, previousRoomState: RoomState, roomState: RoomState) => void;
296
+ [RoomEvent.CurrentStateUpdated]: (room: Room, previousRoomState: RoomState, roomState: RoomState) => void;
297
+ [RoomEvent.HistoryImportedWithinTimeline]: (markerEvent: MatrixEvent, room: Room) => void;
298
+ [RoomEvent.UnreadNotifications]: (unreadNotifications?: NotificationCount, threadId?: string) => void;
299
+ [RoomEvent.TimelineRefresh]: (room: Room, eventTimelineSet: EventTimelineSet) => void;
300
+ /**
301
+ * Fires when a new room summary is returned by `/sync`.
302
+ *
303
+ * See https://spec.matrix.org/v1.8/client-server-api/#_matrixclientv3sync_roomsummary
304
+ * for full details
305
+ * @param summary - the room summary object
306
+ */
307
+ [RoomEvent.Summary]: (summary: IRoomSummary) => void;
308
+ [ThreadEvent.New]: (thread: Thread, toStartOfTimeline: boolean) => void;
309
+ /**
310
+ * Fires when a new poll instance is added to the room state
311
+ * @param poll - the new poll
312
+ */
313
+ [PollEvent.New]: (poll: Poll) => void;
314
+ } & Pick<ThreadHandlerMap, ThreadEvent.Update | ThreadEvent.NewReply | ThreadEvent.Delete> &
315
+ EventTimelineSetHandlerMap &
316
+ Pick<MatrixEventHandlerMap, MatrixEventEvent.BeforeRedaction> &
317
+ Pick<
318
+ RoomStateEventHandlerMap,
319
+ | RoomStateEvent.Events
320
+ | RoomStateEvent.Members
321
+ | RoomStateEvent.NewMember
322
+ | RoomStateEvent.Update
323
+ | RoomStateEvent.Marker
324
+ | BeaconEvent.New
325
+ > &
326
+ Pick<BeaconEventHandlerMap, BeaconEvent.Update | BeaconEvent.Destroy | BeaconEvent.LivenessChange>;
327
+
328
+ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
329
+ public readonly reEmitter: TypedReEmitter<RoomEmittedEvents, RoomEventHandlerMap>;
330
+ private txnToEvent: Map<string, MatrixEvent> = new Map(); // Pending in-flight requests { string: MatrixEvent }
331
+ private notificationCounts: NotificationCount = {};
332
+ private readonly threadNotifications = new Map<string, NotificationCount>();
333
+ public readonly cachedThreadReadReceipts = new Map<string, CachedReceiptStructure[]>();
334
+ // Useful to know at what point the current user has started using threads in this room
335
+ private oldestThreadedReceiptTs = Infinity;
336
+ /**
337
+ * A record of the latest unthread receipts per user
338
+ * This is useful in determining whether a user has read a thread or not
339
+ */
340
+ private unthreadedReceipts = new Map<string, Receipt>();
341
+ private readonly timelineSets: EventTimelineSet[];
342
+ public readonly polls: Map<string, Poll> = new Map<string, Poll>();
343
+
344
+ /**
345
+ * Empty array if the timeline sets have not been initialised. After initialisation:
346
+ * 0: All threads
347
+ * 1: Threads the current user has participated in
348
+ */
349
+ public readonly threadsTimelineSets: [] | [EventTimelineSet, EventTimelineSet] = [];
350
+
351
+ // any filtered timeline sets we're maintaining for this room
352
+ private readonly filteredTimelineSets: Record<string, EventTimelineSet> = {}; // filter_id: timelineSet
353
+ private timelineNeedsRefresh = false;
354
+ private readonly pendingEventList?: MatrixEvent[];
355
+ // read by megolm via getter; boolean value - null indicates "use global value"
356
+ private blacklistUnverifiedDevices?: boolean;
357
+ private selfMembership?: Membership;
358
+ private summaryHeroes: string[] | null = null;
359
+ // flags to stop logspam about missing m.room.create events
360
+ private getTypeWarning = false;
361
+ private getVersionWarning = false;
362
+ private membersPromise?: Promise<boolean>;
363
+
364
+ // XXX: These should be read-only
365
+ /**
366
+ * The human-readable display name for this room.
367
+ */
368
+ public name: string;
369
+ /**
370
+ * The un-homoglyphed name for this room.
371
+ */
372
+ public normalizedName: string;
373
+ /**
374
+ * Dict of room tags; the keys are the tag name and the values
375
+ * are any metadata associated with the tag - e.g. `{ "fav" : { order: 1 } }`
376
+ */
377
+ public tags: Record<string, Record<string, any>> = {}; // $tagName: { $metadata: $value }
378
+ /**
379
+ * accountData Dict of per-room account_data events; the keys are the
380
+ * event type and the values are the events.
381
+ */
382
+ public accountData: Map<string, MatrixEvent> = new Map(); // $eventType: $event
383
+ /**
384
+ * The room summary.
385
+ */
386
+ public summary: RoomSummary | null = null;
387
+ /**
388
+ * oldState The state of the room at the time of the oldest event in the live timeline.
389
+ *
390
+ * @deprecated Present for backwards compatibility.
391
+ * Use getLiveTimeline().getState(EventTimeline.BACKWARDS) instead
392
+ */
393
+ public oldState!: RoomState;
394
+ /**
395
+ * currentState The state of the room at the time of the newest event in the timeline.
396
+ *
397
+ * @deprecated Present for backwards compatibility.
398
+ * Use getLiveTimeline().getState(EventTimeline.FORWARDS) instead.
399
+ */
400
+ public currentState!: RoomState;
401
+
402
+ public readonly relations;
403
+
404
+ /**
405
+ * A collection of events known by the client
406
+ * This is not a comprehensive list of the threads that exist in this room
407
+ */
408
+ private threads = new Map<string, Thread>();
409
+
410
+ /**
411
+ * A mapping of eventId to all visibility changes to apply
412
+ * to the event, by chronological order, as per
413
+ * https://github.com/matrix-org/matrix-doc/pull/3531
414
+ *
415
+ * # Invariants
416
+ *
417
+ * - within each list, all events are classed by
418
+ * chronological order;
419
+ * - all events are events such that
420
+ * `asVisibilityEvent()` returns a non-null `IVisibilityChange`;
421
+ * - within each list with key `eventId`, all events
422
+ * are in relation to `eventId`.
423
+ *
424
+ * @experimental
425
+ */
426
+ private visibilityEvents = new Map<string, MatrixEvent[]>();
427
+
428
+ /**
429
+ * The latest receipts (synthetic and real) for each user in each thread
430
+ * (and unthreaded).
431
+ */
432
+ private roomReceipts = new RoomReceipts(this);
433
+
434
+ /**
435
+ * Construct a new Room.
436
+ *
437
+ * <p>For a room, we store an ordered sequence of timelines, which may or may not
438
+ * be continuous. Each timeline lists a series of events, as well as tracking
439
+ * the room state at the start and the end of the timeline. It also tracks
440
+ * forward and backward pagination tokens, as well as containing links to the
441
+ * next timeline in the sequence.
442
+ *
443
+ * <p>There is one special timeline - the 'live' timeline, which represents the
444
+ * timeline to which events are being added in real-time as they are received
445
+ * from the /sync API. Note that you should not retain references to this
446
+ * timeline - even if it is the current timeline right now, it may not remain
447
+ * so if the server gives us a timeline gap in /sync.
448
+ *
449
+ * <p>In order that we can find events from their ids later, we also maintain a
450
+ * map from event_id to timeline and index.
451
+ *
452
+ * @param roomId - Required. The ID of this room.
453
+ * @param client - Required. The client, used to lazy load members.
454
+ * @param myUserId - Required. The ID of the syncing user.
455
+ * @param opts - Configuration options
456
+ */
457
+ public constructor(
458
+ public readonly roomId: string,
459
+ public readonly client: MatrixClient,
460
+ public readonly myUserId: string,
461
+ private readonly opts: IOpts = {},
462
+ ) {
463
+ super();
464
+
465
+ // In some cases, we add listeners for every displayed Matrix event, so it's
466
+ // common to have quite a few more than the default limit.
467
+ this.setMaxListeners(100);
468
+ this.reEmitter = new TypedReEmitter(this);
469
+
470
+ opts.pendingEventOrdering = opts.pendingEventOrdering || PendingEventOrdering.Chronological;
471
+
472
+ this.name = roomId;
473
+ this.normalizedName = roomId;
474
+
475
+ this.relations = new RelationsContainer(this.client, this);
476
+
477
+ // Listen to our own receipt event as a more modular way of processing our own
478
+ // receipts. No need to remove the listener: it's on ourself anyway.
479
+ this.on(RoomEvent.Receipt, this.onReceipt);
480
+
481
+ // all our per-room timeline sets. the first one is the unfiltered ones;
482
+ // the subsequent ones are the filtered ones in no particular order.
483
+ this.timelineSets = [new EventTimelineSet(this, opts)];
484
+ this.reEmitter.reEmit(this.getUnfilteredTimelineSet(), [RoomEvent.Timeline, RoomEvent.TimelineReset]);
485
+
486
+ this.fixUpLegacyTimelineFields();
487
+
488
+ if (this.opts.pendingEventOrdering === PendingEventOrdering.Detached) {
489
+ this.pendingEventList = [];
490
+ this.client.store.getPendingEvents(this.roomId).then((events) => {
491
+ const mapper = this.client.getEventMapper({
492
+ toDevice: false,
493
+ decrypt: false,
494
+ });
495
+ events.forEach(async (serializedEvent: Partial<IEvent>) => {
496
+ const event = mapper(serializedEvent);
497
+ await client.decryptEventIfNeeded(event);
498
+ event.setStatus(EventStatus.NOT_SENT);
499
+ this.addPendingEvent(event, event.getTxnId()!);
500
+ });
501
+ });
502
+ }
503
+
504
+ // awaited by getEncryptionTargetMembers while room members are loading
505
+ if (!this.opts.lazyLoadMembers) {
506
+ this.membersPromise = Promise.resolve(false);
507
+ } else {
508
+ this.membersPromise = undefined;
509
+ }
510
+ }
511
+
512
+ private threadTimelineSetsPromise: Promise<[EventTimelineSet, EventTimelineSet]> | null = null;
513
+ public async createThreadsTimelineSets(): Promise<[EventTimelineSet, EventTimelineSet] | null> {
514
+ if (this.threadTimelineSetsPromise) {
515
+ return this.threadTimelineSetsPromise;
516
+ }
517
+
518
+ if (this.client?.supportsThreads()) {
519
+ try {
520
+ this.threadTimelineSetsPromise = Promise.all([
521
+ this.createThreadTimelineSet(),
522
+ this.createThreadTimelineSet(ThreadFilterType.My),
523
+ ]);
524
+ const timelineSets = await this.threadTimelineSetsPromise;
525
+ this.threadsTimelineSets[0] = timelineSets[0];
526
+ this.threadsTimelineSets[1] = timelineSets[1];
527
+ return timelineSets;
528
+ } catch {
529
+ this.threadTimelineSetsPromise = null;
530
+ return null;
531
+ }
532
+ }
533
+ return null;
534
+ }
535
+
536
+ /**
537
+ * Bulk decrypt critical events in a room
538
+ *
539
+ * Critical events represents the minimal set of events to decrypt
540
+ * for a typical UI to function properly
541
+ *
542
+ * - Last event of every room (to generate likely message preview)
543
+ * - All events up to the read receipt (to calculate an accurate notification count)
544
+ *
545
+ * @returns Signals when all events have been decrypted
546
+ */
547
+ public async decryptCriticalEvents(): Promise<void> {
548
+ if (!this.client.isCryptoEnabled()) return;
549
+
550
+ const readReceiptEventId = this.getEventReadUpTo(this.client.getUserId()!, true);
551
+ const events = this.getLiveTimeline().getEvents();
552
+ const readReceiptTimelineIndex = events.findIndex((matrixEvent) => {
553
+ return matrixEvent.event.event_id === readReceiptEventId;
554
+ });
555
+
556
+ const decryptionPromises = events
557
+ .slice(readReceiptTimelineIndex)
558
+ .reverse()
559
+ .map((event) => this.client.decryptEventIfNeeded(event));
560
+
561
+ await Promise.allSettled(decryptionPromises);
562
+ }
563
+
564
+ /**
565
+ * Bulk decrypt events in a room
566
+ *
567
+ * @returns Signals when all events have been decrypted
568
+ */
569
+ public async decryptAllEvents(): Promise<void> {
570
+ if (!this.client.isCryptoEnabled()) return;
571
+
572
+ const decryptionPromises = this.getUnfilteredTimelineSet()
573
+ .getLiveTimeline()
574
+ .getEvents()
575
+ .slice(0) // copy before reversing
576
+ .reverse()
577
+ .map((event) => this.client.decryptEventIfNeeded(event));
578
+
579
+ await Promise.allSettled(decryptionPromises);
580
+ }
581
+
582
+ /**
583
+ * Gets the creator of the room
584
+ * @returns The creator of the room, or null if it could not be determined
585
+ */
586
+ public getCreator(): string | null {
587
+ const createEvent = this.currentState.getStateEvents(EventType.RoomCreate, "");
588
+ return createEvent?.getSender() ?? null;
589
+ }
590
+
591
+ /**
592
+ * Gets the version of the room
593
+ * @returns The version of the room, or null if it could not be determined
594
+ */
595
+ public getVersion(): string {
596
+ const createEvent = this.currentState.getStateEvents(EventType.RoomCreate, "");
597
+ if (!createEvent) {
598
+ if (!this.getVersionWarning) {
599
+ logger.warn("[getVersion] Room " + this.roomId + " does not have an m.room.create event");
600
+ this.getVersionWarning = true;
601
+ }
602
+ return "1";
603
+ }
604
+ return createEvent.getContent()["room_version"] ?? "1";
605
+ }
606
+
607
+ /**
608
+ * Determines the recommended room version for the room. This returns an
609
+ * object with 3 properties: `version` as the new version the
610
+ * room should be upgraded to (may be the same as the current version);
611
+ * `needsUpgrade` to indicate if the room actually can be
612
+ * upgraded (ie: does the current version not match?); and `urgent`
613
+ * to indicate if the new version patches a vulnerability in a previous
614
+ * version.
615
+ * @returns
616
+ * Resolves to the version the room should be upgraded to.
617
+ */
618
+ public async getRecommendedVersion(): Promise<IRecommendedVersion> {
619
+ let capabilities: Capabilities = {};
620
+ try {
621
+ capabilities = await this.client.getCapabilities();
622
+ } catch {}
623
+ let versionCap = capabilities["m.room_versions"];
624
+ if (!versionCap) {
625
+ versionCap = {
626
+ default: KNOWN_SAFE_ROOM_VERSION,
627
+ available: {},
628
+ };
629
+ for (const safeVer of SAFE_ROOM_VERSIONS) {
630
+ versionCap.available[safeVer] = RoomVersionStability.Stable;
631
+ }
632
+ }
633
+
634
+ let result = this.checkVersionAgainstCapability(versionCap);
635
+ if (result.urgent && result.needsUpgrade) {
636
+ // Something doesn't feel right: we shouldn't need to update
637
+ // because the version we're on should be in the protocol's
638
+ // namespace. This usually means that the server was updated
639
+ // before the client was, making us think the newest possible
640
+ // room version is not stable. As a solution, we'll refresh
641
+ // the capability we're using to determine this.
642
+ logger.warn(
643
+ "Refreshing room version capability because the server looks " +
644
+ "to be supporting a newer room version we don't know about.",
645
+ );
646
+
647
+ try {
648
+ capabilities = await this.client.fetchCapabilities();
649
+ } catch (e) {
650
+ logger.warn("Failed to refresh room version capabilities", e);
651
+ }
652
+ versionCap = capabilities["m.room_versions"];
653
+ if (!versionCap) {
654
+ logger.warn("No room version capability - assuming upgrade required.");
655
+ return result;
656
+ } else {
657
+ result = this.checkVersionAgainstCapability(versionCap);
658
+ }
659
+ }
660
+
661
+ return result;
662
+ }
663
+
664
+ private checkVersionAgainstCapability(versionCap: IRoomVersionsCapability): IRecommendedVersion {
665
+ const currentVersion = this.getVersion();
666
+ logger.log(`[${this.roomId}] Current version: ${currentVersion}`);
667
+ logger.log(`[${this.roomId}] Version capability: `, versionCap);
668
+
669
+ const result: IRecommendedVersion = {
670
+ version: currentVersion,
671
+ needsUpgrade: false,
672
+ urgent: false,
673
+ };
674
+
675
+ // If the room is on the default version then nothing needs to change
676
+ if (currentVersion === versionCap.default) return result;
677
+
678
+ const stableVersions = Object.keys(versionCap.available).filter((v) => versionCap.available[v] === "stable");
679
+
680
+ // Check if the room is on an unstable version. We determine urgency based
681
+ // off the version being in the Matrix spec namespace or not (if the version
682
+ // is in the current namespace and unstable, the room is probably vulnerable).
683
+ if (!stableVersions.includes(currentVersion)) {
684
+ result.version = versionCap.default;
685
+ result.needsUpgrade = true;
686
+ result.urgent = !!this.getVersion().match(/^[0-9]+[0-9.]*$/g);
687
+ if (result.urgent) {
688
+ logger.warn(`URGENT upgrade required on ${this.roomId}`);
689
+ } else {
690
+ logger.warn(`Non-urgent upgrade required on ${this.roomId}`);
691
+ }
692
+ return result;
693
+ }
694
+
695
+ // The room is on a stable, but non-default, version by this point.
696
+ // No upgrade needed.
697
+ return result;
698
+ }
699
+
700
+ /**
701
+ * Determines whether the given user is permitted to perform a room upgrade
702
+ * @param userId - The ID of the user to test against
703
+ * @returns True if the given user is permitted to upgrade the room
704
+ */
705
+ public userMayUpgradeRoom(userId: string): boolean {
706
+ return this.currentState.maySendStateEvent(EventType.RoomTombstone, userId);
707
+ }
708
+
709
+ /**
710
+ * Get the list of pending sent events for this room
711
+ *
712
+ * @returns A list of the sent events
713
+ * waiting for remote echo.
714
+ *
715
+ * @throws If `opts.pendingEventOrdering` was not 'detached'
716
+ */
717
+ public getPendingEvents(): MatrixEvent[] {
718
+ if (!this.pendingEventList) {
719
+ throw new Error(
720
+ "Cannot call getPendingEvents with pendingEventOrdering == " + this.opts.pendingEventOrdering,
721
+ );
722
+ }
723
+
724
+ return this.pendingEventList;
725
+ }
726
+
727
+ /**
728
+ * Removes a pending event for this room
729
+ *
730
+ * @returns True if an element was removed.
731
+ */
732
+ public removePendingEvent(eventId: string): boolean {
733
+ if (!this.pendingEventList) {
734
+ throw new Error(
735
+ "Cannot call removePendingEvent with pendingEventOrdering == " + this.opts.pendingEventOrdering,
736
+ );
737
+ }
738
+
739
+ const removed = removeElement(
740
+ this.pendingEventList,
741
+ function (ev) {
742
+ return ev.getId() == eventId;
743
+ },
744
+ false,
745
+ );
746
+
747
+ this.savePendingEvents();
748
+
749
+ return removed;
750
+ }
751
+
752
+ /**
753
+ * Check whether the pending event list contains a given event by ID.
754
+ * If pending event ordering is not "detached" then this returns false.
755
+ *
756
+ * @param eventId - The event ID to check for.
757
+ */
758
+ public hasPendingEvent(eventId: string): boolean {
759
+ return this.pendingEventList?.some((event) => event.getId() === eventId) ?? false;
760
+ }
761
+
762
+ /**
763
+ * Get a specific event from the pending event list, if configured, null otherwise.
764
+ *
765
+ * @param eventId - The event ID to check for.
766
+ */
767
+ public getPendingEvent(eventId: string): MatrixEvent | null {
768
+ return this.pendingEventList?.find((event) => event.getId() === eventId) ?? null;
769
+ }
770
+
771
+ /**
772
+ * Get the live unfiltered timeline for this room.
773
+ *
774
+ * @returns live timeline
775
+ */
776
+ public getLiveTimeline(): EventTimeline {
777
+ return this.getUnfilteredTimelineSet().getLiveTimeline();
778
+ }
779
+
780
+ /**
781
+ * The live event timeline for this room, with the oldest event at index 0.
782
+ *
783
+ * @deprecated Present for backwards compatibility.
784
+ * Use getLiveTimeline().getEvents() instead
785
+ */
786
+ public get timeline(): MatrixEvent[] {
787
+ return this.getLiveTimeline().getEvents();
788
+ }
789
+
790
+ /**
791
+ * Get the timestamp of the last message in the room
792
+ *
793
+ * @returns the timestamp of the last message in the room
794
+ */
795
+ public getLastActiveTimestamp(): number {
796
+ const timeline = this.getLiveTimeline();
797
+ const events = timeline.getEvents();
798
+ if (events.length) {
799
+ const lastEvent = events[events.length - 1];
800
+ return lastEvent.getTs();
801
+ } else {
802
+ return Number.MIN_SAFE_INTEGER;
803
+ }
804
+ }
805
+
806
+ /**
807
+ * Returns the last live event of this room.
808
+ * "last" means latest timestamp.
809
+ * Instead of using timestamps, it would be better to do the comparison based on the order of the homeserver DAG.
810
+ * Unfortunately, this information is currently not available in the client.
811
+ * See {@link https://github.com/matrix-org/matrix-js-sdk/issues/3325}.
812
+ * "live of this room" means from all live timelines: the room and the threads.
813
+ *
814
+ * @returns MatrixEvent if there is a last event; else undefined.
815
+ */
816
+ public getLastLiveEvent(): MatrixEvent | undefined {
817
+ const roomEvents = this.getLiveTimeline().getEvents();
818
+ const lastRoomEvent = roomEvents[roomEvents.length - 1] as MatrixEvent | undefined;
819
+ const lastThread = this.getLastThread();
820
+
821
+ if (!lastThread) return lastRoomEvent;
822
+
823
+ const lastThreadEvent = lastThread.events[lastThread.events.length - 1];
824
+
825
+ return (lastRoomEvent?.getTs() ?? 0) > (lastThreadEvent?.getTs() ?? 0) ? lastRoomEvent : lastThreadEvent;
826
+ }
827
+
828
+ /**
829
+ * Returns the last thread of this room.
830
+ * "last" means latest timestamp of the last thread event.
831
+ * Instead of using timestamps, it would be better to do the comparison based on the order of the homeserver DAG.
832
+ * Unfortunately, this information is currently not available in the client.
833
+ * See {@link https://github.com/matrix-org/matrix-js-sdk/issues/3325}.
834
+ *
835
+ * @returns the thread with the most recent event in its live time line. undefined if there is no thread.
836
+ */
837
+ public getLastThread(): Thread | undefined {
838
+ return this.getThreads().reduce<Thread | undefined>((lastThread: Thread | undefined, thread: Thread) => {
839
+ if (!lastThread) return thread;
840
+
841
+ const threadEvent = thread.events[thread.events.length - 1];
842
+ const lastThreadEvent = lastThread.events[lastThread.events.length - 1];
843
+
844
+ if ((threadEvent?.getTs() ?? 0) >= (lastThreadEvent?.getTs() ?? 0)) {
845
+ // Last message of current thread is newer → new last thread.
846
+ // Equal also means newer, because it was added to the thread map later.
847
+ return thread;
848
+ }
849
+
850
+ return lastThread;
851
+ }, undefined);
852
+ }
853
+
854
+ /**
855
+ * @returns the membership type (join | leave | invite | knock) for the logged in user
856
+ */
857
+ public getMyMembership(): Membership {
858
+ return this.selfMembership ?? KnownMembership.Leave;
859
+ }
860
+
861
+ /**
862
+ * If this room is a DM we're invited to,
863
+ * try to find out who invited us
864
+ * @returns user id of the inviter
865
+ */
866
+ public getDMInviter(): string | undefined {
867
+ const me = this.getMember(this.myUserId);
868
+ if (me) {
869
+ return me.getDMInviter();
870
+ }
871
+
872
+ if (this.selfMembership === KnownMembership.Invite) {
873
+ // fall back to summary information
874
+ const memberCount = this.getInvitedAndJoinedMemberCount();
875
+ if (memberCount === 2) {
876
+ return this.summaryHeroes?.[0];
877
+ }
878
+ }
879
+ }
880
+
881
+ /**
882
+ * Assuming this room is a DM room, tries to guess with which user.
883
+ * @returns user id of the other member (could be syncing user)
884
+ */
885
+ public guessDMUserId(): string {
886
+ const me = this.getMember(this.myUserId);
887
+ if (me) {
888
+ const inviterId = me.getDMInviter();
889
+ if (inviterId) {
890
+ return inviterId;
891
+ }
892
+ }
893
+ // Remember, we're assuming this room is a DM, so returning the first member we find should be fine
894
+ if (Array.isArray(this.summaryHeroes) && this.summaryHeroes.length) {
895
+ return this.summaryHeroes[0];
896
+ }
897
+ const members = this.currentState.getMembers();
898
+ const anyMember = members.find((m) => m.userId !== this.myUserId);
899
+ if (anyMember) {
900
+ return anyMember.userId;
901
+ }
902
+ // it really seems like I'm the only user in the room
903
+ // so I probably created a room with just me in it
904
+ // and marked it as a DM. Ok then
905
+ return this.myUserId;
906
+ }
907
+
908
+ /**
909
+ * Gets the "functional members" in this room.
910
+ *
911
+ * Returns the list of userIDs from the `io.element.functional_members` event. Does not consider the
912
+ * current membership states of those users.
913
+ *
914
+ * @see https://github.com/element-hq/element-meta/blob/develop/spec/functional_members.md.
915
+ */
916
+ private getFunctionalMembers(): string[] {
917
+ const mFunctionalMembers = this.currentState.getStateEvents(UNSTABLE_ELEMENT_FUNCTIONAL_USERS.name, "");
918
+ if (Array.isArray(mFunctionalMembers?.getContent().service_members)) {
919
+ return mFunctionalMembers!.getContent().service_members;
920
+ }
921
+ return [];
922
+ }
923
+
924
+ public getAvatarFallbackMember(): RoomMember | undefined {
925
+ const functionalMembers = this.getFunctionalMembers();
926
+
927
+ // Only generate a fallback avatar if the conversation is with a single specific other user (a "DM").
928
+ let nonFunctionalMemberCount = 0;
929
+ this.getMembers()!.forEach((m) => {
930
+ if (m.membership !== "join" && m.membership !== "invite") return;
931
+ if (functionalMembers.includes(m.userId)) return;
932
+ nonFunctionalMemberCount++;
933
+ });
934
+ if (nonFunctionalMemberCount > 2) return;
935
+
936
+ // Prefer the list of heroes, if present. It should only include the single other user in the DM.
937
+ const nonFunctionalHeroes = this.summaryHeroes?.filter((h) => !functionalMembers.includes(h));
938
+ const hasHeroes = Array.isArray(nonFunctionalHeroes) && nonFunctionalHeroes.length;
939
+ if (hasHeroes) {
940
+ const availableMember = nonFunctionalHeroes
941
+ .map((userId) => {
942
+ return this.getMember(userId);
943
+ })
944
+ .find((member) => !!member);
945
+ if (availableMember) {
946
+ return availableMember;
947
+ }
948
+ }
949
+
950
+ // Consider *all*, including previous, members, to generate the avatar for DMs where the other user left.
951
+ // Needed to generate a matching avatar for rooms named "Empty Room (was Alice)".
952
+ const members = this.getMembers();
953
+ const nonFunctionalMembers = members?.filter((m) => !functionalMembers.includes(m.userId));
954
+ if (nonFunctionalMembers.length <= 2) {
955
+ const availableMember = nonFunctionalMembers.find((m) => {
956
+ return m.userId !== this.myUserId;
957
+ });
958
+ if (availableMember) {
959
+ return availableMember;
960
+ }
961
+ }
962
+
963
+ // If all else failed, but the homeserver gave us heroes that previously could not be found in the room members,
964
+ // trust and try falling back to a hero, creating a one-off member for it
965
+ if (hasHeroes) {
966
+ const availableUser = nonFunctionalHeroes
967
+ .map((userId) => {
968
+ return this.client.getUser(userId);
969
+ })
970
+ .find((user) => !!user);
971
+ if (availableUser) {
972
+ const member = new RoomMember(this.roomId, availableUser.userId);
973
+ member.user = availableUser;
974
+ return member;
975
+ }
976
+ }
977
+ }
978
+
979
+ /**
980
+ * Sets the membership this room was received as during sync
981
+ * @param membership - join | leave | invite
982
+ */
983
+ public updateMyMembership(membership: Membership): void {
984
+ const prevMembership = this.selfMembership;
985
+ this.selfMembership = membership;
986
+ if (prevMembership !== membership) {
987
+ if (membership === KnownMembership.Leave) {
988
+ this.cleanupAfterLeaving();
989
+ }
990
+ this.emit(RoomEvent.MyMembership, this, membership, prevMembership);
991
+ }
992
+ }
993
+
994
+ private async loadMembersFromServer(): Promise<IStateEventWithRoomId[]> {
995
+ const lastSyncToken = this.client.store.getSyncToken();
996
+ const response = await this.client.members(
997
+ this.roomId,
998
+ undefined,
999
+ KnownMembership.Leave,
1000
+ lastSyncToken ?? undefined,
1001
+ );
1002
+ return response.chunk;
1003
+ }
1004
+
1005
+ private async loadMembers(): Promise<{ memberEvents: MatrixEvent[]; fromServer: boolean }> {
1006
+ // were the members loaded from the server?
1007
+ let fromServer = false;
1008
+ let rawMembersEvents = await this.client.store.getOutOfBandMembers(this.roomId);
1009
+ // If the room is encrypted, we always fetch members from the server at
1010
+ // least once, in case the latest state wasn't persisted properly. Note
1011
+ // that this function is only called once (unless loading the members
1012
+ // fails), since loadMembersIfNeeded always returns this.membersPromise
1013
+ // if set, which will be the result of the first (successful) call.
1014
+ if (rawMembersEvents === null || this.hasEncryptionStateEvent()) {
1015
+ fromServer = true;
1016
+ rawMembersEvents = await this.loadMembersFromServer();
1017
+ logger.log(`LL: got ${rawMembersEvents.length} ` + `members from server for room ${this.roomId}`);
1018
+ }
1019
+ const memberEvents = rawMembersEvents.filter(noUnsafeEventProps).map(this.client.getEventMapper());
1020
+ return { memberEvents, fromServer };
1021
+ }
1022
+
1023
+ /**
1024
+ * Check if loading of out-of-band-members has completed
1025
+ *
1026
+ * @returns true if the full membership list of this room has been loaded (including if lazy-loading is disabled).
1027
+ * False if the load is not started or is in progress.
1028
+ */
1029
+ public membersLoaded(): boolean {
1030
+ if (!this.opts.lazyLoadMembers) {
1031
+ return true;
1032
+ }
1033
+
1034
+ return this.currentState.outOfBandMembersReady();
1035
+ }
1036
+
1037
+ /**
1038
+ * Preloads the member list in case lazy loading
1039
+ * of memberships is in use. Can be called multiple times,
1040
+ * it will only preload once.
1041
+ * @returns when preloading is done and
1042
+ * accessing the members on the room will take
1043
+ * all members in the room into account
1044
+ */
1045
+ public loadMembersIfNeeded(): Promise<boolean> {
1046
+ if (this.membersPromise) {
1047
+ return this.membersPromise;
1048
+ }
1049
+
1050
+ // mark the state so that incoming messages while
1051
+ // the request is in flight get marked as superseding
1052
+ // the OOB members
1053
+ this.currentState.markOutOfBandMembersStarted();
1054
+
1055
+ const inMemoryUpdate = this.loadMembers()
1056
+ .then((result) => {
1057
+ this.currentState.setOutOfBandMembers(result.memberEvents);
1058
+ return result.fromServer;
1059
+ })
1060
+ .catch((err) => {
1061
+ // allow retries on fail
1062
+ this.membersPromise = undefined;
1063
+ this.currentState.markOutOfBandMembersFailed();
1064
+ throw err;
1065
+ });
1066
+ // update members in storage, but don't wait for it
1067
+ inMemoryUpdate
1068
+ .then((fromServer) => {
1069
+ if (fromServer) {
1070
+ const oobMembers = this.currentState
1071
+ .getMembers()
1072
+ .filter((m) => m.isOutOfBand())
1073
+ .map((m) => m.events.member?.event as IStateEventWithRoomId);
1074
+ logger.log(`LL: telling store to write ${oobMembers.length}` + ` members for room ${this.roomId}`);
1075
+ const store = this.client.store;
1076
+ return (
1077
+ store
1078
+ .setOutOfBandMembers(this.roomId, oobMembers)
1079
+ // swallow any IDB error as we don't want to fail
1080
+ // because of this
1081
+ .catch((err) => {
1082
+ logger.log("LL: storing OOB room members failed, oh well", err);
1083
+ })
1084
+ );
1085
+ }
1086
+ })
1087
+ .catch((err) => {
1088
+ // as this is not awaited anywhere,
1089
+ // at least show the error in the console
1090
+ logger.error(err);
1091
+ });
1092
+
1093
+ this.membersPromise = inMemoryUpdate;
1094
+
1095
+ return this.membersPromise;
1096
+ }
1097
+
1098
+ /**
1099
+ * Removes the lazily loaded members from storage if needed
1100
+ */
1101
+ public async clearLoadedMembersIfNeeded(): Promise<void> {
1102
+ if (this.opts.lazyLoadMembers && this.membersPromise) {
1103
+ await this.loadMembersIfNeeded();
1104
+ await this.client.store.clearOutOfBandMembers(this.roomId);
1105
+ this.currentState.clearOutOfBandMembers();
1106
+ this.membersPromise = undefined;
1107
+ }
1108
+ }
1109
+
1110
+ /**
1111
+ * called when sync receives this room in the leave section
1112
+ * to do cleanup after leaving a room. Possibly called multiple times.
1113
+ */
1114
+ private cleanupAfterLeaving(): void {
1115
+ this.clearLoadedMembersIfNeeded().catch((err) => {
1116
+ logger.error(`error after clearing loaded members from ` + `room ${this.roomId} after leaving`);
1117
+ logger.log(err);
1118
+ });
1119
+ }
1120
+
1121
+ /**
1122
+ * Empty out the current live timeline and re-request it. This is used when
1123
+ * historical messages are imported into the room via MSC2716 `/batch_send`
1124
+ * because the client may already have that section of the timeline loaded.
1125
+ * We need to force the client to throw away their current timeline so that
1126
+ * when they back paginate over the area again with the historical messages
1127
+ * in between, it grabs the newly imported messages. We can listen for
1128
+ * `UNSTABLE_MSC2716_MARKER`, in order to tell when historical messages are ready
1129
+ * to be discovered in the room and the timeline needs a refresh. The SDK
1130
+ * emits a `RoomEvent.HistoryImportedWithinTimeline` event when we detect a
1131
+ * valid marker and can check the needs refresh status via
1132
+ * `room.getTimelineNeedsRefresh()`.
1133
+ */
1134
+ public async refreshLiveTimeline(): Promise<void> {
1135
+ const liveTimelineBefore = this.getLiveTimeline();
1136
+ const forwardPaginationToken = liveTimelineBefore.getPaginationToken(EventTimeline.FORWARDS);
1137
+ const backwardPaginationToken = liveTimelineBefore.getPaginationToken(EventTimeline.BACKWARDS);
1138
+ const eventsBefore = liveTimelineBefore.getEvents();
1139
+ const mostRecentEventInTimeline = eventsBefore[eventsBefore.length - 1];
1140
+ logger.log(
1141
+ `[refreshLiveTimeline for ${this.roomId}] at ` +
1142
+ `mostRecentEventInTimeline=${mostRecentEventInTimeline && mostRecentEventInTimeline.getId()} ` +
1143
+ `liveTimelineBefore=${liveTimelineBefore.toString()} ` +
1144
+ `forwardPaginationToken=${forwardPaginationToken} ` +
1145
+ `backwardPaginationToken=${backwardPaginationToken}`,
1146
+ );
1147
+
1148
+ // Get the main TimelineSet
1149
+ const timelineSet = this.getUnfilteredTimelineSet();
1150
+
1151
+ let newTimeline: Optional<EventTimeline>;
1152
+ // If there isn't any event in the timeline, let's go fetch the latest
1153
+ // event and construct a timeline from it.
1154
+ //
1155
+ // This should only really happen if the user ran into an error
1156
+ // with refreshing the timeline before which left them in a blank
1157
+ // timeline from `resetLiveTimeline`.
1158
+ if (!mostRecentEventInTimeline) {
1159
+ newTimeline = await this.client.getLatestTimeline(timelineSet);
1160
+ } else {
1161
+ // Empty out all of `this.timelineSets`. But we also need to keep the
1162
+ // same `timelineSet` references around so the React code updates
1163
+ // properly and doesn't ignore the room events we emit because it checks
1164
+ // that the `timelineSet` references are the same. We need the
1165
+ // `timelineSet` empty so that the `client.getEventTimeline(...)` call
1166
+ // later, will call `/context` and create a new timeline instead of
1167
+ // returning the same one.
1168
+ this.resetLiveTimeline(null, null);
1169
+
1170
+ // Make the UI timeline show the new blank live timeline we just
1171
+ // reset so that if the network fails below it's showing the
1172
+ // accurate state of what we're working with instead of the
1173
+ // disconnected one in the TimelineWindow which is just hanging
1174
+ // around by reference.
1175
+ this.emit(RoomEvent.TimelineRefresh, this, timelineSet);
1176
+
1177
+ // Use `client.getEventTimeline(...)` to construct a new timeline from a
1178
+ // `/context` response state and events for the most recent event before
1179
+ // we reset everything. The `timelineSet` we pass in needs to be empty
1180
+ // in order for this function to call `/context` and generate a new
1181
+ // timeline.
1182
+ newTimeline = await this.client.getEventTimeline(timelineSet, mostRecentEventInTimeline.getId()!);
1183
+ }
1184
+
1185
+ // If a racing `/sync` beat us to creating a new timeline, use that
1186
+ // instead because it's the latest in the room and any new messages in
1187
+ // the scrollback will include the history.
1188
+ const liveTimeline = timelineSet.getLiveTimeline();
1189
+ if (
1190
+ !liveTimeline ||
1191
+ (liveTimeline.getPaginationToken(Direction.Forward) === null &&
1192
+ liveTimeline.getPaginationToken(Direction.Backward) === null &&
1193
+ liveTimeline.getEvents().length === 0)
1194
+ ) {
1195
+ logger.log(`[refreshLiveTimeline for ${this.roomId}] using our new live timeline`);
1196
+ // Set the pagination token back to the live sync token (`null`) instead
1197
+ // of using the `/context` historical token (ex. `t12-13_0_0_0_0_0_0_0_0`)
1198
+ // so that it matches the next response from `/sync` and we can properly
1199
+ // continue the timeline.
1200
+ newTimeline!.setPaginationToken(forwardPaginationToken, EventTimeline.FORWARDS);
1201
+
1202
+ // Set our new fresh timeline as the live timeline to continue syncing
1203
+ // forwards and back paginating from.
1204
+ timelineSet.setLiveTimeline(newTimeline!);
1205
+ // Fixup `this.oldstate` so that `scrollback` has the pagination tokens
1206
+ // available
1207
+ this.fixUpLegacyTimelineFields();
1208
+ } else {
1209
+ logger.log(
1210
+ `[refreshLiveTimeline for ${this.roomId}] \`/sync\` or some other request beat us to creating a new ` +
1211
+ `live timeline after we reset it. We'll use that instead since any events in the scrollback from ` +
1212
+ `this timeline will include the history.`,
1213
+ );
1214
+ }
1215
+
1216
+ // The timeline has now been refreshed ✅
1217
+ this.setTimelineNeedsRefresh(false);
1218
+
1219
+ // Emit an event which clients can react to and re-load the timeline
1220
+ // from the SDK
1221
+ this.emit(RoomEvent.TimelineRefresh, this, timelineSet);
1222
+ }
1223
+
1224
+ /**
1225
+ * Reset the live timeline of all timelineSets, and start new ones.
1226
+ *
1227
+ * <p>This is used when /sync returns a 'limited' timeline.
1228
+ *
1229
+ * @param backPaginationToken - token for back-paginating the new timeline
1230
+ * @param forwardPaginationToken - token for forward-paginating the old live timeline,
1231
+ * if absent or null, all timelines are reset, removing old ones (including the previous live
1232
+ * timeline which would otherwise be unable to paginate forwards without this token).
1233
+ * Removing just the old live timeline whilst preserving previous ones is not supported.
1234
+ */
1235
+ public resetLiveTimeline(backPaginationToken?: string | null, forwardPaginationToken?: string | null): void {
1236
+ for (const timelineSet of this.timelineSets) {
1237
+ timelineSet.resetLiveTimeline(backPaginationToken ?? undefined, forwardPaginationToken ?? undefined);
1238
+ }
1239
+ for (const thread of this.threads.values()) {
1240
+ thread.resetLiveTimeline(backPaginationToken, forwardPaginationToken);
1241
+ }
1242
+
1243
+ this.fixUpLegacyTimelineFields();
1244
+ }
1245
+
1246
+ /**
1247
+ * Fix up this.timeline, this.oldState and this.currentState
1248
+ *
1249
+ * @internal
1250
+ */
1251
+ private fixUpLegacyTimelineFields(): void {
1252
+ const previousOldState = this.oldState;
1253
+ const previousCurrentState = this.currentState;
1254
+
1255
+ // maintain this.oldState and this.currentState as references to the
1256
+ // state at the start and end of that timeline. These are more
1257
+ // for backwards-compatibility than anything else.
1258
+ this.oldState = this.getLiveTimeline().getState(EventTimeline.BACKWARDS)!;
1259
+ this.currentState = this.getLiveTimeline().getState(EventTimeline.FORWARDS)!;
1260
+
1261
+ // Let people know to register new listeners for the new state
1262
+ // references. The reference won't necessarily change every time so only
1263
+ // emit when we see a change.
1264
+ if (previousOldState !== this.oldState) {
1265
+ this.emit(RoomEvent.OldStateUpdated, this, previousOldState, this.oldState);
1266
+ }
1267
+
1268
+ if (previousCurrentState !== this.currentState) {
1269
+ this.emit(RoomEvent.CurrentStateUpdated, this, previousCurrentState, this.currentState);
1270
+
1271
+ // Re-emit various events on the current room state
1272
+ // TODO: If currentState really only exists for backwards
1273
+ // compatibility, shouldn't we be doing this some other way?
1274
+ this.reEmitter.stopReEmitting(previousCurrentState, [
1275
+ RoomStateEvent.Events,
1276
+ RoomStateEvent.Members,
1277
+ RoomStateEvent.NewMember,
1278
+ RoomStateEvent.Update,
1279
+ RoomStateEvent.Marker,
1280
+ BeaconEvent.New,
1281
+ BeaconEvent.Update,
1282
+ BeaconEvent.Destroy,
1283
+ BeaconEvent.LivenessChange,
1284
+ ]);
1285
+ this.reEmitter.reEmit(this.currentState, [
1286
+ RoomStateEvent.Events,
1287
+ RoomStateEvent.Members,
1288
+ RoomStateEvent.NewMember,
1289
+ RoomStateEvent.Update,
1290
+ RoomStateEvent.Marker,
1291
+ BeaconEvent.New,
1292
+ BeaconEvent.Update,
1293
+ BeaconEvent.Destroy,
1294
+ BeaconEvent.LivenessChange,
1295
+ ]);
1296
+ }
1297
+ }
1298
+
1299
+ private onReceipt(event: MatrixEvent): void {
1300
+ if (this.hasEncryptionStateEvent()) {
1301
+ this.clearNotificationsOnReceipt(event);
1302
+ }
1303
+ }
1304
+
1305
+ private clearNotificationsOnReceipt(event: MatrixEvent): void {
1306
+ // Like above, we have to listen for read receipts from ourselves in order to
1307
+ // correctly handle notification counts on encrypted rooms.
1308
+ // This fixes https://github.com/vector-im/element-web/issues/9421
1309
+
1310
+ // Figure out if we've read something or if it's just informational
1311
+ // We need to work out what threads we've just recieved receipts for, so we
1312
+ // know which ones to update. If we've received an unthreaded receipt, we'll
1313
+ // need to update all threads.
1314
+ let threadIds: string[] = [];
1315
+ let hasUnthreadedReceipt = false;
1316
+
1317
+ const content = event.getContent();
1318
+
1319
+ for (const receiptGroup of Object.values(content)) {
1320
+ for (const [receiptType, userReceipt] of Object.entries(receiptGroup)) {
1321
+ if (!utils.isSupportedReceiptType(receiptType)) continue;
1322
+ if (!userReceipt) continue;
1323
+
1324
+ for (const [userId, singleReceipt] of Object.entries(userReceipt)) {
1325
+ if (!singleReceipt || typeof singleReceipt !== "object") continue;
1326
+ const typedSingleReceipt = singleReceipt as Record<string, any>;
1327
+ if (userId !== this.client.getUserId()) continue;
1328
+ if (typedSingleReceipt.thread_id === undefined) {
1329
+ hasUnthreadedReceipt = true;
1330
+ } else if (typeof typedSingleReceipt.thread_id === "string") {
1331
+ threadIds.push(typedSingleReceipt.thread_id);
1332
+ }
1333
+ }
1334
+ }
1335
+ }
1336
+
1337
+ if (hasUnthreadedReceipt) {
1338
+ // If we have an unthreaded receipt, we need to update any threads that have a notification
1339
+ // in them (because we know the receipt can't go backwards so we don't need to check any with
1340
+ // no notifications: the number can only decrease from a receipt).
1341
+ threadIds = this.getThreads()
1342
+ .filter(
1343
+ (thread) =>
1344
+ this.getThreadUnreadNotificationCount(thread.id, NotificationCountType.Total) > 0 ||
1345
+ this.getThreadUnreadNotificationCount(thread.id, NotificationCountType.Highlight) > 0,
1346
+ )
1347
+ .map((thread) => thread.id);
1348
+ threadIds.push("main");
1349
+ }
1350
+
1351
+ for (const threadId of threadIds) {
1352
+ // Work backwards to determine how many events are unread. We also set
1353
+ // a limit for how back we'll look to avoid spinning CPU for too long.
1354
+ // If we hit the limit, we assume the count is unchanged.
1355
+ const maxHistory = 20;
1356
+ const timeline = threadId === "main" ? this.getLiveTimeline() : this.getThread(threadId)?.liveTimeline;
1357
+
1358
+ if (!timeline) {
1359
+ logger.warn(`Couldn't find timeline for thread ID ${threadId} in room ${this.roomId}`);
1360
+ continue;
1361
+ }
1362
+
1363
+ const events = timeline.getEvents();
1364
+
1365
+ let highlightCount = 0;
1366
+
1367
+ for (let i = events.length - 1; i >= 0; i--) {
1368
+ if (i === events.length - maxHistory) return; // limit reached
1369
+
1370
+ const event = events[i];
1371
+
1372
+ if (this.hasUserReadEvent(this.client.getUserId()!, event.getId()!)) {
1373
+ // If the user has read the event, then the counting is done.
1374
+ break;
1375
+ }
1376
+
1377
+ const pushActions = this.client.getPushActionsForEvent(event);
1378
+ highlightCount += pushActions?.tweaks?.highlight ? 1 : 0;
1379
+ }
1380
+
1381
+ // Note: we don't need to handle 'total' notifications because the counts
1382
+ // will come from the server.
1383
+ if (threadId === "main") {
1384
+ this.setUnreadNotificationCount(NotificationCountType.Highlight, highlightCount);
1385
+ } else {
1386
+ this.setThreadUnreadNotificationCount(threadId, NotificationCountType.Highlight, highlightCount);
1387
+ }
1388
+ }
1389
+ }
1390
+
1391
+ /**
1392
+ * Return the timeline sets for this room.
1393
+ * @returns array of timeline sets for this room
1394
+ */
1395
+ public getTimelineSets(): EventTimelineSet[] {
1396
+ return this.timelineSets;
1397
+ }
1398
+
1399
+ /**
1400
+ * Helper to return the main unfiltered timeline set for this room
1401
+ * @returns room's unfiltered timeline set
1402
+ */
1403
+ public getUnfilteredTimelineSet(): EventTimelineSet {
1404
+ return this.timelineSets[0];
1405
+ }
1406
+
1407
+ /**
1408
+ * Get the timeline which contains the given event from the unfiltered set, if any
1409
+ *
1410
+ * @param eventId - event ID to look for
1411
+ * @returns timeline containing
1412
+ * the given event, or null if unknown
1413
+ */
1414
+ public getTimelineForEvent(eventId: string): EventTimeline | null {
1415
+ const event = this.findEventById(eventId);
1416
+ const thread = this.findThreadForEvent(event);
1417
+ if (thread) {
1418
+ return thread.timelineSet.getTimelineForEvent(eventId);
1419
+ } else {
1420
+ return this.getUnfilteredTimelineSet().getTimelineForEvent(eventId);
1421
+ }
1422
+ }
1423
+
1424
+ /**
1425
+ * Add a new timeline to this room's unfiltered timeline set
1426
+ *
1427
+ * @returns newly-created timeline
1428
+ */
1429
+ public addTimeline(): EventTimeline {
1430
+ return this.getUnfilteredTimelineSet().addTimeline();
1431
+ }
1432
+
1433
+ /**
1434
+ * Whether the timeline needs to be refreshed in order to pull in new
1435
+ * historical messages that were imported.
1436
+ * @param value - The value to set
1437
+ */
1438
+ public setTimelineNeedsRefresh(value: boolean): void {
1439
+ this.timelineNeedsRefresh = value;
1440
+ }
1441
+
1442
+ /**
1443
+ * Whether the timeline needs to be refreshed in order to pull in new
1444
+ * historical messages that were imported.
1445
+ * @returns .
1446
+ */
1447
+ public getTimelineNeedsRefresh(): boolean {
1448
+ return this.timelineNeedsRefresh;
1449
+ }
1450
+
1451
+ /**
1452
+ * Get an event which is stored in our unfiltered timeline set, or in a thread
1453
+ *
1454
+ * @param eventId - event ID to look for
1455
+ * @returns the given event, or undefined if unknown
1456
+ */
1457
+ public findEventById(eventId: string): MatrixEvent | undefined {
1458
+ let event = this.getUnfilteredTimelineSet().findEventById(eventId);
1459
+
1460
+ if (!event) {
1461
+ const threads = this.getThreads();
1462
+ for (let i = 0; i < threads.length; i++) {
1463
+ const thread = threads[i];
1464
+ event = thread.findEventById(eventId);
1465
+ if (event) {
1466
+ return event;
1467
+ }
1468
+ }
1469
+ }
1470
+
1471
+ return event;
1472
+ }
1473
+
1474
+ /**
1475
+ * Get one of the notification counts for this room
1476
+ * @param type - The type of notification count to get. default: 'total'
1477
+ * @returns The notification count, or undefined if there is no count
1478
+ * for this type.
1479
+ */
1480
+ public getUnreadNotificationCount(type = NotificationCountType.Total): number {
1481
+ let count = this.getRoomUnreadNotificationCount(type);
1482
+ for (const threadNotification of this.threadNotifications.values()) {
1483
+ count += threadNotification[type] ?? 0;
1484
+ }
1485
+ return count;
1486
+ }
1487
+
1488
+ /**
1489
+ * Get the notification for the event context (room or thread timeline)
1490
+ */
1491
+ public getUnreadCountForEventContext(type = NotificationCountType.Total, event: MatrixEvent): number {
1492
+ const isThreadEvent = !!event.threadRootId && !event.isThreadRoot;
1493
+
1494
+ return (
1495
+ (isThreadEvent
1496
+ ? this.getThreadUnreadNotificationCount(event.threadRootId, type)
1497
+ : this.getRoomUnreadNotificationCount(type)) ?? 0
1498
+ );
1499
+ }
1500
+
1501
+ /**
1502
+ * Get one of the notification counts for this room
1503
+ * @param type - The type of notification count to get. default: 'total'
1504
+ * @returns The notification count, or undefined if there is no count
1505
+ * for this type.
1506
+ */
1507
+ public getRoomUnreadNotificationCount(type = NotificationCountType.Total): number {
1508
+ return this.notificationCounts[type] ?? 0;
1509
+ }
1510
+
1511
+ /**
1512
+ * Get one of the notification counts for a thread
1513
+ * @param threadId - the root event ID
1514
+ * @param type - The type of notification count to get. default: 'total'
1515
+ * @returns The notification count, or undefined if there is no count
1516
+ * for this type.
1517
+ */
1518
+ public getThreadUnreadNotificationCount(threadId: string, type = NotificationCountType.Total): number {
1519
+ return this.threadNotifications.get(threadId)?.[type] ?? 0;
1520
+ }
1521
+
1522
+ /**
1523
+ * Checks if the current room has unread thread notifications
1524
+ * @returns
1525
+ */
1526
+ public hasThreadUnreadNotification(): boolean {
1527
+ for (const notification of this.threadNotifications.values()) {
1528
+ if ((notification.highlight ?? 0) > 0 || (notification.total ?? 0) > 0) {
1529
+ return true;
1530
+ }
1531
+ }
1532
+ return false;
1533
+ }
1534
+
1535
+ /**
1536
+ * Swet one of the notification count for a thread
1537
+ * @param threadId - the root event ID
1538
+ * @param type - The type of notification count to get. default: 'total'
1539
+ * @returns
1540
+ */
1541
+ public setThreadUnreadNotificationCount(threadId: string, type: NotificationCountType, count: number): void {
1542
+ const notification: NotificationCount = {
1543
+ highlight: this.threadNotifications.get(threadId)?.highlight,
1544
+ total: this.threadNotifications.get(threadId)?.total,
1545
+ ...{
1546
+ [type]: count,
1547
+ },
1548
+ };
1549
+
1550
+ this.threadNotifications.set(threadId, notification);
1551
+
1552
+ this.emit(RoomEvent.UnreadNotifications, notification, threadId);
1553
+ }
1554
+
1555
+ /**
1556
+ * @returns the notification count type for all the threads in the room
1557
+ */
1558
+ public get threadsAggregateNotificationType(): NotificationCountType | null {
1559
+ let type: NotificationCountType | null = null;
1560
+ for (const threadNotification of this.threadNotifications.values()) {
1561
+ if ((threadNotification.highlight ?? 0) > 0) {
1562
+ return NotificationCountType.Highlight;
1563
+ } else if ((threadNotification.total ?? 0) > 0 && !type) {
1564
+ type = NotificationCountType.Total;
1565
+ }
1566
+ }
1567
+ return type;
1568
+ }
1569
+
1570
+ /**
1571
+ * Resets the total thread notifications for all threads in this room to zero,
1572
+ * excluding any threads whose IDs are given in `exceptThreadIds`.
1573
+ *
1574
+ * If the room is not encrypted, also resets the highlight notification count to zero
1575
+ * for the same set of threads.
1576
+ *
1577
+ * This is intended for use from the sync code since we calculate highlight notification
1578
+ * counts locally from decrypted messages. We want to partially trust the total from the
1579
+ * server such that we clear notifications when read receipts arrive. The weird name is
1580
+ * intended to reflect this. You probably do not want to use this.
1581
+ *
1582
+ * @param exceptThreadIds - The thread IDs to exclude from the reset.
1583
+ */
1584
+ public resetThreadUnreadNotificationCountFromSync(exceptThreadIds: string[] = []): void {
1585
+ const isEncrypted = this.hasEncryptionStateEvent();
1586
+
1587
+ for (const [threadId, notifs] of this.threadNotifications) {
1588
+ if (!exceptThreadIds.includes(threadId)) {
1589
+ notifs.total = 0;
1590
+ if (!isEncrypted) {
1591
+ notifs.highlight = 0;
1592
+ }
1593
+ }
1594
+ }
1595
+
1596
+ this.emit(RoomEvent.UnreadNotifications);
1597
+ }
1598
+
1599
+ /**
1600
+ * Set one of the notification counts for this room
1601
+ * @param type - The type of notification count to set.
1602
+ * @param count - The new count
1603
+ */
1604
+ public setUnreadNotificationCount(type: NotificationCountType, count: number): void {
1605
+ this.notificationCounts[type] = count;
1606
+ this.emit(RoomEvent.UnreadNotifications, this.notificationCounts);
1607
+ }
1608
+
1609
+ public setUnread(type: NotificationCountType, count: number): void {
1610
+ return this.setUnreadNotificationCount(type, count);
1611
+ }
1612
+
1613
+ public setSummary(summary: IRoomSummary): void {
1614
+ const heroes = summary["m.heroes"];
1615
+ const joinedCount = summary["m.joined_member_count"];
1616
+ const invitedCount = summary["m.invited_member_count"];
1617
+ if (Number.isInteger(joinedCount)) {
1618
+ this.currentState.setJoinedMemberCount(joinedCount!);
1619
+ }
1620
+ if (Number.isInteger(invitedCount)) {
1621
+ this.currentState.setInvitedMemberCount(invitedCount!);
1622
+ }
1623
+ if (Array.isArray(heroes)) {
1624
+ // be cautious about trusting server values,
1625
+ // and make sure heroes doesn't contain our own id
1626
+ // just to be sure
1627
+ this.summaryHeroes = heroes.filter((userId) => {
1628
+ return userId !== this.myUserId;
1629
+ });
1630
+ }
1631
+
1632
+ this.emit(RoomEvent.Summary, summary);
1633
+ }
1634
+
1635
+ /**
1636
+ * Whether to send encrypted messages to devices within this room.
1637
+ * @param value - true to blacklist unverified devices, null
1638
+ * to use the global value for this room.
1639
+ */
1640
+ public setBlacklistUnverifiedDevices(value: boolean): void {
1641
+ this.blacklistUnverifiedDevices = value;
1642
+ }
1643
+
1644
+ /**
1645
+ * Whether to send encrypted messages to devices within this room.
1646
+ * @returns true if blacklisting unverified devices, null
1647
+ * if the global value should be used for this room.
1648
+ */
1649
+ public getBlacklistUnverifiedDevices(): boolean | null {
1650
+ if (this.blacklistUnverifiedDevices === undefined) return null;
1651
+ return this.blacklistUnverifiedDevices;
1652
+ }
1653
+
1654
+ /**
1655
+ * Get the avatar URL for a room if one was set.
1656
+ * @param baseUrl - The homeserver base URL. See
1657
+ * {@link MatrixClient#getHomeserverUrl}.
1658
+ * @param width - The desired width of the thumbnail.
1659
+ * @param height - The desired height of the thumbnail.
1660
+ * @param resizeMethod - The thumbnail resize method to use, either
1661
+ * "crop" or "scale".
1662
+ * @param allowDefault - True to allow an identicon for this room if an
1663
+ * avatar URL wasn't explicitly set. Default: true. (Deprecated)
1664
+ * @returns the avatar URL or null.
1665
+ */
1666
+ public getAvatarUrl(
1667
+ baseUrl: string,
1668
+ width: number,
1669
+ height: number,
1670
+ resizeMethod: ResizeMethod,
1671
+ allowDefault = true,
1672
+ ): string | null {
1673
+ const roomAvatarEvent = this.currentState.getStateEvents(EventType.RoomAvatar, "");
1674
+ if (!roomAvatarEvent && !allowDefault) {
1675
+ return null;
1676
+ }
1677
+
1678
+ const mainUrl = roomAvatarEvent ? roomAvatarEvent.getContent().url : null;
1679
+ if (mainUrl) {
1680
+ return getHttpUriForMxc(baseUrl, mainUrl, width, height, resizeMethod);
1681
+ }
1682
+
1683
+ return null;
1684
+ }
1685
+
1686
+ /**
1687
+ * Get the mxc avatar url for the room, if one was set.
1688
+ * @returns the mxc avatar url or falsy
1689
+ */
1690
+ public getMxcAvatarUrl(): string | null {
1691
+ return this.currentState.getStateEvents(EventType.RoomAvatar, "")?.getContent()?.url || null;
1692
+ }
1693
+
1694
+ /**
1695
+ * Get this room's canonical alias
1696
+ * The alias returned by this function may not necessarily
1697
+ * still point to this room.
1698
+ * @returns The room's canonical alias, or null if there is none
1699
+ */
1700
+ public getCanonicalAlias(): string | null {
1701
+ const canonicalAlias = this.currentState.getStateEvents(EventType.RoomCanonicalAlias, "");
1702
+ if (canonicalAlias) {
1703
+ return canonicalAlias.getContent().alias || null;
1704
+ }
1705
+ return null;
1706
+ }
1707
+
1708
+ /**
1709
+ * Get this room's alternative aliases
1710
+ * @returns The room's alternative aliases, or an empty array
1711
+ */
1712
+ public getAltAliases(): string[] {
1713
+ const canonicalAlias = this.currentState.getStateEvents(EventType.RoomCanonicalAlias, "");
1714
+ if (canonicalAlias) {
1715
+ return canonicalAlias.getContent().alt_aliases || [];
1716
+ }
1717
+ return [];
1718
+ }
1719
+
1720
+ /**
1721
+ * Add events to a timeline
1722
+ *
1723
+ * <p>Will fire "Room.timeline" for each event added.
1724
+ *
1725
+ * @param events - A list of events to add.
1726
+ *
1727
+ * @param toStartOfTimeline - True to add these events to the start
1728
+ * (oldest) instead of the end (newest) of the timeline. If true, the oldest
1729
+ * event will be the <b>last</b> element of 'events'.
1730
+ *
1731
+ * @param timeline - timeline to
1732
+ * add events to.
1733
+ *
1734
+ * @param paginationToken - token for the next batch of events
1735
+ *
1736
+ * @remarks
1737
+ * Fires {@link RoomEvent.Timeline}
1738
+ */
1739
+ public addEventsToTimeline(
1740
+ events: MatrixEvent[],
1741
+ toStartOfTimeline: boolean,
1742
+ timeline: EventTimeline,
1743
+ paginationToken?: string,
1744
+ ): void {
1745
+ timeline.getTimelineSet().addEventsToTimeline(events, toStartOfTimeline, timeline, paginationToken);
1746
+ }
1747
+
1748
+ /**
1749
+ * Get the instance of the thread associated with the current event
1750
+ * @param eventId - the ID of the current event
1751
+ * @returns a thread instance if known
1752
+ */
1753
+ public getThread(eventId: string): Thread | null {
1754
+ return this.threads.get(eventId) ?? null;
1755
+ }
1756
+
1757
+ /**
1758
+ * Get all the known threads in the room
1759
+ */
1760
+ public getThreads(): Thread[] {
1761
+ return Array.from(this.threads.values());
1762
+ }
1763
+
1764
+ /**
1765
+ * Get a member from the current room state.
1766
+ * @param userId - The user ID of the member.
1767
+ * @returns The member or `null`.
1768
+ */
1769
+ public getMember(userId: string): RoomMember | null {
1770
+ return this.currentState.getMember(userId);
1771
+ }
1772
+
1773
+ /**
1774
+ * Get all currently loaded members from the current
1775
+ * room state.
1776
+ * @returns Room members
1777
+ */
1778
+ public getMembers(): RoomMember[] {
1779
+ return this.currentState.getMembers();
1780
+ }
1781
+
1782
+ /**
1783
+ * Get a list of members whose membership state is "join".
1784
+ * @returns A list of currently joined members.
1785
+ */
1786
+ public getJoinedMembers(): RoomMember[] {
1787
+ return this.getMembersWithMembership(KnownMembership.Join);
1788
+ }
1789
+
1790
+ /**
1791
+ * Returns the number of joined members in this room
1792
+ * This method caches the result.
1793
+ * This is a wrapper around the method of the same name in roomState, returning
1794
+ * its result for the room's current state.
1795
+ * @returns The number of members in this room whose membership is 'join'
1796
+ */
1797
+ public getJoinedMemberCount(): number {
1798
+ return this.currentState.getJoinedMemberCount();
1799
+ }
1800
+
1801
+ /**
1802
+ * Returns the number of invited members in this room
1803
+ * @returns The number of members in this room whose membership is 'invite'
1804
+ */
1805
+ public getInvitedMemberCount(): number {
1806
+ return this.currentState.getInvitedMemberCount();
1807
+ }
1808
+
1809
+ /**
1810
+ * Returns the number of invited + joined members in this room
1811
+ * @returns The number of members in this room whose membership is 'invite' or 'join'
1812
+ */
1813
+ public getInvitedAndJoinedMemberCount(): number {
1814
+ return this.getInvitedMemberCount() + this.getJoinedMemberCount();
1815
+ }
1816
+
1817
+ /**
1818
+ * Get a list of members with given membership state.
1819
+ * @param membership - The membership state.
1820
+ * @returns A list of members with the given membership state.
1821
+ */
1822
+ public getMembersWithMembership(membership: Membership): RoomMember[] {
1823
+ return this.currentState.getMembers().filter(function (m) {
1824
+ return m.membership === membership;
1825
+ });
1826
+ }
1827
+
1828
+ /**
1829
+ * Get a list of members we should be encrypting for in this room
1830
+ * @returns A list of members who
1831
+ * we should encrypt messages for in this room.
1832
+ */
1833
+ public async getEncryptionTargetMembers(): Promise<RoomMember[]> {
1834
+ await this.loadMembersIfNeeded();
1835
+ let members = this.getMembersWithMembership(KnownMembership.Join);
1836
+ if (this.shouldEncryptForInvitedMembers()) {
1837
+ members = members.concat(this.getMembersWithMembership(KnownMembership.Invite));
1838
+ }
1839
+ return members;
1840
+ }
1841
+
1842
+ /**
1843
+ * Determine whether we should encrypt messages for invited users in this room
1844
+ * @returns if we should encrypt messages for invited users
1845
+ */
1846
+ public shouldEncryptForInvitedMembers(): boolean {
1847
+ const ev = this.currentState.getStateEvents(EventType.RoomHistoryVisibility, "");
1848
+ return ev?.getContent()?.history_visibility !== "joined";
1849
+ }
1850
+
1851
+ /**
1852
+ * Get the default room name (i.e. what a given user would see if the
1853
+ * room had no m.room.name)
1854
+ * @param userId - The userId from whose perspective we want
1855
+ * to calculate the default name
1856
+ * @returns The default room name
1857
+ */
1858
+ public getDefaultRoomName(userId: string): string {
1859
+ return this.calculateRoomName(userId, true);
1860
+ }
1861
+
1862
+ /**
1863
+ * Check if the given user_id has the given membership state.
1864
+ * @param userId - The user ID to check.
1865
+ * @param membership - The membership e.g. `'join'`
1866
+ * @returns True if this user_id has the given membership state.
1867
+ */
1868
+ public hasMembershipState(userId: string, membership: Membership): boolean {
1869
+ const member = this.getMember(userId);
1870
+ if (!member) {
1871
+ return false;
1872
+ }
1873
+ return member.membership === membership;
1874
+ }
1875
+
1876
+ /**
1877
+ * Add a timelineSet for this room with the given filter
1878
+ * @param filter - The filter to be applied to this timelineSet
1879
+ * @param opts - Configuration options
1880
+ * @returns The timelineSet
1881
+ */
1882
+ public getOrCreateFilteredTimelineSet(
1883
+ filter: Filter,
1884
+ { prepopulateTimeline = true, useSyncEvents = true, pendingEvents = true }: ICreateFilterOpts = {},
1885
+ ): EventTimelineSet {
1886
+ if (this.filteredTimelineSets[filter.filterId!]) {
1887
+ return this.filteredTimelineSets[filter.filterId!];
1888
+ }
1889
+ const opts = Object.assign({ filter, pendingEvents }, this.opts);
1890
+ const timelineSet = new EventTimelineSet(this, opts);
1891
+ this.reEmitter.reEmit(timelineSet, [RoomEvent.Timeline, RoomEvent.TimelineReset]);
1892
+ if (useSyncEvents) {
1893
+ this.filteredTimelineSets[filter.filterId!] = timelineSet;
1894
+ this.timelineSets.push(timelineSet);
1895
+ }
1896
+
1897
+ const unfilteredLiveTimeline = this.getLiveTimeline();
1898
+ // Not all filter are possible to replicate client-side only
1899
+ // When that's the case we do not want to prepopulate from the live timeline
1900
+ // as we would get incorrect results compared to what the server would send back
1901
+ if (prepopulateTimeline) {
1902
+ // populate up the new timelineSet with filtered events from our live
1903
+ // unfiltered timeline.
1904
+ //
1905
+ // XXX: This is risky as our timeline
1906
+ // may have grown huge and so take a long time to filter.
1907
+ // see https://github.com/vector-im/vector-web/issues/2109
1908
+
1909
+ unfilteredLiveTimeline.getEvents().forEach(function (event) {
1910
+ timelineSet.addLiveEvent(event);
1911
+ });
1912
+
1913
+ // find the earliest unfiltered timeline
1914
+ let timeline = unfilteredLiveTimeline;
1915
+ while (timeline.getNeighbouringTimeline(EventTimeline.BACKWARDS)) {
1916
+ timeline = timeline.getNeighbouringTimeline(EventTimeline.BACKWARDS)!;
1917
+ }
1918
+
1919
+ timelineSet
1920
+ .getLiveTimeline()
1921
+ .setPaginationToken(timeline.getPaginationToken(EventTimeline.BACKWARDS), EventTimeline.BACKWARDS);
1922
+ } else if (useSyncEvents) {
1923
+ const livePaginationToken = unfilteredLiveTimeline.getPaginationToken(Direction.Forward);
1924
+ timelineSet.getLiveTimeline().setPaginationToken(livePaginationToken, Direction.Backward);
1925
+ }
1926
+
1927
+ // alternatively, we could try to do something like this to try and re-paginate
1928
+ // in the filtered events from nothing, but Mark says it's an abuse of the API
1929
+ // to do so:
1930
+ //
1931
+ // timelineSet.resetLiveTimeline(
1932
+ // unfilteredLiveTimeline.getPaginationToken(EventTimeline.FORWARDS)
1933
+ // );
1934
+
1935
+ return timelineSet;
1936
+ }
1937
+
1938
+ private async getThreadListFilter(filterType = ThreadFilterType.All): Promise<Filter> {
1939
+ const myUserId = this.client.getUserId()!;
1940
+ const filter = new Filter(myUserId);
1941
+
1942
+ const definition: IFilterDefinition = {
1943
+ room: {
1944
+ timeline: {
1945
+ [FILTER_RELATED_BY_REL_TYPES.name]: [THREAD_RELATION_TYPE.name],
1946
+ },
1947
+ },
1948
+ };
1949
+
1950
+ if (filterType === ThreadFilterType.My) {
1951
+ definition!.room!.timeline![FILTER_RELATED_BY_SENDERS.name] = [myUserId];
1952
+ }
1953
+
1954
+ filter.setDefinition(definition);
1955
+ const filterId = await this.client.getOrCreateFilter(`THREAD_PANEL_${this.roomId}_${filterType}`, filter);
1956
+
1957
+ filter.filterId = filterId;
1958
+
1959
+ return filter;
1960
+ }
1961
+
1962
+ private async createThreadTimelineSet(filterType?: ThreadFilterType): Promise<EventTimelineSet> {
1963
+ let timelineSet: EventTimelineSet;
1964
+ if (Thread.hasServerSideListSupport) {
1965
+ timelineSet = new EventTimelineSet(
1966
+ this,
1967
+ {
1968
+ ...this.opts,
1969
+ pendingEvents: false,
1970
+ },
1971
+ undefined,
1972
+ undefined,
1973
+ filterType ?? ThreadFilterType.All,
1974
+ );
1975
+ this.reEmitter.reEmit(timelineSet, [RoomEvent.Timeline, RoomEvent.TimelineReset]);
1976
+ } else if (Thread.hasServerSideSupport) {
1977
+ const filter = await this.getThreadListFilter(filterType);
1978
+
1979
+ timelineSet = this.getOrCreateFilteredTimelineSet(filter, {
1980
+ prepopulateTimeline: false,
1981
+ useSyncEvents: false,
1982
+ pendingEvents: false,
1983
+ });
1984
+ } else {
1985
+ timelineSet = new EventTimelineSet(this, {
1986
+ pendingEvents: false,
1987
+ });
1988
+
1989
+ Array.from(this.threads).forEach(([, thread]) => {
1990
+ if (thread.length === 0) return;
1991
+ const currentUserParticipated = thread.timeline.some((event) => {
1992
+ return event.getSender() === this.client.getUserId();
1993
+ });
1994
+ if (filterType !== ThreadFilterType.My || currentUserParticipated) {
1995
+ timelineSet.getLiveTimeline().addEvent(thread.rootEvent!, {
1996
+ toStartOfTimeline: false,
1997
+ });
1998
+ }
1999
+ });
2000
+ }
2001
+
2002
+ return timelineSet;
2003
+ }
2004
+
2005
+ private threadsReady = false;
2006
+
2007
+ /**
2008
+ * Takes the given thread root events and creates threads for them.
2009
+ */
2010
+ public processThreadRoots(events: MatrixEvent[], toStartOfTimeline: boolean): void {
2011
+ if (!this.client.supportsThreads()) return;
2012
+ for (const rootEvent of events) {
2013
+ EventTimeline.setEventMetadata(rootEvent, this.currentState, toStartOfTimeline);
2014
+ if (!this.getThread(rootEvent.getId()!)) {
2015
+ this.createThread(rootEvent.getId()!, rootEvent, [], toStartOfTimeline);
2016
+ }
2017
+ }
2018
+ }
2019
+
2020
+ /**
2021
+ * Fetch the bare minimum of room threads required for the thread list to work reliably.
2022
+ * With server support that means fetching one page.
2023
+ * Without server support that means fetching as much at once as the server allows us to.
2024
+ */
2025
+ public async fetchRoomThreads(): Promise<void> {
2026
+ if (this.threadsReady || !this.client.supportsThreads()) {
2027
+ return;
2028
+ }
2029
+
2030
+ if (Thread.hasServerSideListSupport) {
2031
+ await Promise.all([
2032
+ this.fetchRoomThreadList(ThreadFilterType.All),
2033
+ this.fetchRoomThreadList(ThreadFilterType.My),
2034
+ ]);
2035
+ } else {
2036
+ const allThreadsFilter = await this.getThreadListFilter();
2037
+
2038
+ const { chunk: events } = await this.client.createMessagesRequest(
2039
+ this.roomId,
2040
+ "",
2041
+ Number.MAX_SAFE_INTEGER,
2042
+ Direction.Backward,
2043
+ allThreadsFilter,
2044
+ );
2045
+
2046
+ if (!events.length) return;
2047
+
2048
+ // Sorted by last_reply origin_server_ts
2049
+ const threadRoots = events.map(this.client.getEventMapper()).sort((eventA, eventB) => {
2050
+ /**
2051
+ * `origin_server_ts` in a decentralised world is far from ideal
2052
+ * but for lack of any better, we will have to use this
2053
+ * Long term the sorting should be handled by homeservers and this
2054
+ * is only meant as a short term patch
2055
+ */
2056
+ const threadAMetadata = eventA.getServerAggregatedRelation<IThreadBundledRelationship>(
2057
+ THREAD_RELATION_TYPE.name,
2058
+ )!;
2059
+ const threadBMetadata = eventB.getServerAggregatedRelation<IThreadBundledRelationship>(
2060
+ THREAD_RELATION_TYPE.name,
2061
+ )!;
2062
+ return threadAMetadata.latest_event.origin_server_ts - threadBMetadata.latest_event.origin_server_ts;
2063
+ });
2064
+
2065
+ let latestMyThreadsRootEvent: MatrixEvent | undefined;
2066
+ const roomState = this.getLiveTimeline().getState(EventTimeline.FORWARDS);
2067
+ for (const rootEvent of threadRoots) {
2068
+ const opts = {
2069
+ duplicateStrategy: DuplicateStrategy.Ignore,
2070
+ fromCache: false,
2071
+ roomState,
2072
+ };
2073
+ this.threadsTimelineSets[0]?.addLiveEvent(rootEvent, opts);
2074
+
2075
+ const threadRelationship = rootEvent.getServerAggregatedRelation<IThreadBundledRelationship>(
2076
+ THREAD_RELATION_TYPE.name,
2077
+ );
2078
+ if (threadRelationship?.current_user_participated) {
2079
+ this.threadsTimelineSets[1]?.addLiveEvent(rootEvent, opts);
2080
+ latestMyThreadsRootEvent = rootEvent;
2081
+ }
2082
+ }
2083
+
2084
+ this.processThreadRoots(threadRoots, true);
2085
+
2086
+ this.client.decryptEventIfNeeded(threadRoots[threadRoots.length - 1]);
2087
+ if (latestMyThreadsRootEvent) {
2088
+ this.client.decryptEventIfNeeded(latestMyThreadsRootEvent);
2089
+ }
2090
+ }
2091
+
2092
+ this.on(ThreadEvent.NewReply, this.onThreadReply);
2093
+ this.on(ThreadEvent.Update, this.onThreadUpdate);
2094
+ this.on(ThreadEvent.Delete, this.onThreadDelete);
2095
+ this.threadsReady = true;
2096
+ }
2097
+
2098
+ /**
2099
+ * Process a list of poll events.
2100
+ *
2101
+ * @param events - List of events
2102
+ */
2103
+ public async processPollEvents(events: MatrixEvent[]): Promise<void> {
2104
+ for (const event of events) {
2105
+ try {
2106
+ // Continue if the event is a clear text, non-poll event.
2107
+ if (!event.isEncrypted() && !isPollEvent(event)) continue;
2108
+
2109
+ /**
2110
+ * Try to decrypt the event. Promise resolution does not guarantee a successful decryption.
2111
+ * Retry is handled in {@link processPollEvent}.
2112
+ */
2113
+ await this.client.decryptEventIfNeeded(event);
2114
+ this.processPollEvent(event);
2115
+ } catch (err) {
2116
+ logger.warn("Error processing poll event", event.getId(), err);
2117
+ }
2118
+ }
2119
+ }
2120
+
2121
+ /**
2122
+ * Processes poll events:
2123
+ * If the event has a decryption failure, it will listen for decryption and tries again.
2124
+ * If it is a poll start event (`m.poll.start`),
2125
+ * it creates and stores a Poll model and emits a PollEvent.New event.
2126
+ * If the event is related to a poll, it will add it to the poll.
2127
+ * Noop for other cases.
2128
+ *
2129
+ * @param event - Event that could be a poll event
2130
+ */
2131
+ private async processPollEvent(event: MatrixEvent): Promise<void> {
2132
+ if (event.isDecryptionFailure()) {
2133
+ event.once(MatrixEventEvent.Decrypted, (maybeDecryptedEvent: MatrixEvent) => {
2134
+ this.processPollEvent(maybeDecryptedEvent);
2135
+ });
2136
+ return;
2137
+ }
2138
+
2139
+ if (M_POLL_START.matches(event.getType())) {
2140
+ try {
2141
+ const poll = new Poll(event, this.client, this);
2142
+ this.polls.set(event.getId()!, poll);
2143
+ this.emit(PollEvent.New, poll);
2144
+
2145
+ // remove the poll when redacted
2146
+ event.once(MatrixEventEvent.BeforeRedaction, (redactedEvent: MatrixEvent) => {
2147
+ this.polls.delete(redactedEvent.getId()!);
2148
+ });
2149
+ } catch {}
2150
+ // poll creation can fail for malformed poll start events
2151
+ return;
2152
+ }
2153
+
2154
+ const relationEventId = event.relationEventId;
2155
+
2156
+ if (relationEventId && this.polls.has(relationEventId)) {
2157
+ const poll = this.polls.get(relationEventId);
2158
+ poll?.onNewRelation(event);
2159
+ }
2160
+ }
2161
+
2162
+ /**
2163
+ * Fetch a single page of threadlist messages for the specific thread filter
2164
+ * @internal
2165
+ */
2166
+ private async fetchRoomThreadList(filter?: ThreadFilterType): Promise<void> {
2167
+ if (!this.client.supportsThreads()) return;
2168
+ if (this.threadsTimelineSets.length === 0) return;
2169
+
2170
+ const timelineSet = filter === ThreadFilterType.My ? this.threadsTimelineSets[1] : this.threadsTimelineSets[0];
2171
+
2172
+ const { chunk: events, end } = await this.client.createThreadListMessagesRequest(
2173
+ this.roomId,
2174
+ null,
2175
+ undefined,
2176
+ Direction.Backward,
2177
+ timelineSet.threadListType,
2178
+ timelineSet.getFilter(),
2179
+ );
2180
+
2181
+ timelineSet.getLiveTimeline().setPaginationToken(end ?? null, Direction.Backward);
2182
+
2183
+ if (!events.length) return;
2184
+
2185
+ const matrixEvents = events.map(this.client.getEventMapper());
2186
+ this.processThreadRoots(matrixEvents, true);
2187
+ const roomState = this.getLiveTimeline().getState(EventTimeline.FORWARDS);
2188
+ for (const rootEvent of matrixEvents) {
2189
+ timelineSet.addLiveEvent(rootEvent, {
2190
+ duplicateStrategy: DuplicateStrategy.Replace,
2191
+ fromCache: false,
2192
+ roomState,
2193
+ });
2194
+ }
2195
+ }
2196
+
2197
+ private onThreadUpdate(thread: Thread): void {
2198
+ this.updateThreadRootEvents(thread, false, false);
2199
+ }
2200
+
2201
+ private onThreadReply(thread: Thread): void {
2202
+ this.updateThreadRootEvents(thread, false, true);
2203
+ }
2204
+
2205
+ private onThreadDelete(thread: Thread): void {
2206
+ this.threads.delete(thread.id);
2207
+
2208
+ const timeline = this.getTimelineForEvent(thread.id);
2209
+ const roomEvent = timeline?.getEvents()?.find((it) => it.getId() === thread.id);
2210
+ if (roomEvent) {
2211
+ thread.clearEventMetadata(roomEvent);
2212
+ } else {
2213
+ logger.debug("onThreadDelete: Could not find root event in room timeline");
2214
+ }
2215
+ for (const timelineSet of this.threadsTimelineSets) {
2216
+ timelineSet.removeEvent(thread.id);
2217
+ }
2218
+ }
2219
+
2220
+ /**
2221
+ * Forget the timelineSet for this room with the given filter
2222
+ *
2223
+ * @param filter - the filter whose timelineSet is to be forgotten
2224
+ */
2225
+ public removeFilteredTimelineSet(filter: Filter): void {
2226
+ const timelineSet = this.filteredTimelineSets[filter.filterId!];
2227
+ delete this.filteredTimelineSets[filter.filterId!];
2228
+ const i = this.timelineSets.indexOf(timelineSet);
2229
+ if (i > -1) {
2230
+ this.timelineSets.splice(i, 1);
2231
+ }
2232
+ }
2233
+
2234
+ /**
2235
+ * Determine which timeline(s) a given event should live in
2236
+ * Thread roots live in both the main timeline and their corresponding thread timeline
2237
+ * Relations, redactions, replies to thread relation events live only in the thread timeline
2238
+ * Relations (other than m.thread), redactions, replies to a thread root live only in the main timeline
2239
+ * Relations, redactions, replies where the parent cannot be found live in no timelines but should be aggregated regardless.
2240
+ * Otherwise, the event lives in the main timeline only.
2241
+ *
2242
+ * Note: when a redaction is applied, the redacted event, events relating
2243
+ * to it, and the redaction event itself, will all move to the main thread.
2244
+ * This method classifies them as inside the thread of the redacted event.
2245
+ * They are moved later as part of makeRedacted.
2246
+ * This will change if MSC3389 is merged.
2247
+ */
2248
+ public eventShouldLiveIn(
2249
+ event: MatrixEvent,
2250
+ events?: MatrixEvent[],
2251
+ roots?: Set<string>,
2252
+ ): {
2253
+ shouldLiveInRoom: boolean;
2254
+ shouldLiveInThread: boolean;
2255
+ threadId?: string;
2256
+ } {
2257
+ if (!this.client?.supportsThreads()) {
2258
+ return {
2259
+ shouldLiveInRoom: true,
2260
+ shouldLiveInThread: false,
2261
+ };
2262
+ }
2263
+
2264
+ // A thread root is the only event shown in both timelines
2265
+ if (event.isThreadRoot || roots?.has(event.getId()!)) {
2266
+ return {
2267
+ shouldLiveInRoom: true,
2268
+ shouldLiveInThread: true,
2269
+ threadId: event.getId(),
2270
+ };
2271
+ }
2272
+
2273
+ const isThreadRelation = event.isRelation(THREAD_RELATION_TYPE.name);
2274
+ const parentEventId = event.getAssociatedId();
2275
+ const threadRootId = event.threadRootId;
2276
+
2277
+ // Where the parent is the thread root and this is a non-thread relation this should live only in the main timeline
2278
+ if (!!parentEventId && !isThreadRelation && (threadRootId === parentEventId || roots?.has(parentEventId!))) {
2279
+ return {
2280
+ shouldLiveInRoom: true,
2281
+ shouldLiveInThread: false,
2282
+ };
2283
+ }
2284
+
2285
+ let parentEvent: MatrixEvent | undefined;
2286
+ if (parentEventId) {
2287
+ parentEvent = this.findEventById(parentEventId) ?? events?.find((e) => e.getId() === parentEventId);
2288
+ }
2289
+
2290
+ // Treat non-thread-relations, redactions, and replies as extensions of their parents so evaluate parentEvent instead
2291
+ if (parentEvent && !isThreadRelation) {
2292
+ return this.eventShouldLiveIn(parentEvent, events, roots);
2293
+ }
2294
+
2295
+ // A thread relation (1st and 2nd order) is always only shown in a thread
2296
+ if (threadRootId != undefined) {
2297
+ return {
2298
+ shouldLiveInRoom: false,
2299
+ shouldLiveInThread: true,
2300
+ threadId: threadRootId,
2301
+ };
2302
+ }
2303
+
2304
+ // Due to replies not being typical relations and being used as fallbacks for threads relations
2305
+ // If we bypass the if case above then we know we are not a thread, so if we are still a reply
2306
+ // then we know that we must be in the main timeline. Same goes if we have no associated parent event.
2307
+ if (!parentEventId || !!event.replyEventId) {
2308
+ return {
2309
+ shouldLiveInRoom: true,
2310
+ shouldLiveInThread: false,
2311
+ };
2312
+ }
2313
+
2314
+ // We've exhausted all scenarios,
2315
+ // we cannot assume that it lives in the main timeline as this may be a relation for an unknown thread
2316
+ // adding the event in the wrong timeline causes stuck notifications and can break ability to send read receipts
2317
+ return {
2318
+ shouldLiveInRoom: false,
2319
+ shouldLiveInThread: false,
2320
+ };
2321
+ }
2322
+
2323
+ public findThreadForEvent(event?: MatrixEvent): Thread | null {
2324
+ if (!event) return null;
2325
+
2326
+ const { threadId } = this.eventShouldLiveIn(event);
2327
+ return threadId ? this.getThread(threadId) : null;
2328
+ }
2329
+
2330
+ private addThreadedEvents(threadId: string, events: MatrixEvent[], toStartOfTimeline = false): void {
2331
+ const thread = this.getThread(threadId);
2332
+ if (thread) {
2333
+ thread.addEvents(events, toStartOfTimeline);
2334
+ } else {
2335
+ const rootEvent = this.findEventById(threadId) ?? events.find((e) => e.getId() === threadId);
2336
+ this.createThread(threadId, rootEvent, events, toStartOfTimeline);
2337
+ }
2338
+ }
2339
+
2340
+ /**
2341
+ * Adds events to a thread's timeline. Will fire "Thread.update"
2342
+ */
2343
+ public processThreadedEvents(events: MatrixEvent[], toStartOfTimeline: boolean): void {
2344
+ events.forEach(this.applyRedaction);
2345
+
2346
+ const eventsByThread: { [threadId: string]: MatrixEvent[] } = {};
2347
+ for (const event of events) {
2348
+ const { threadId, shouldLiveInThread } = this.eventShouldLiveIn(event);
2349
+ if (shouldLiveInThread && !eventsByThread[threadId!]) {
2350
+ eventsByThread[threadId!] = [];
2351
+ }
2352
+ eventsByThread[threadId!]?.push(event);
2353
+ }
2354
+
2355
+ Object.entries(eventsByThread).map(([threadId, threadEvents]) =>
2356
+ this.addThreadedEvents(threadId, threadEvents, toStartOfTimeline),
2357
+ );
2358
+ }
2359
+
2360
+ private updateThreadRootEvents = (thread: Thread, toStartOfTimeline: boolean, recreateEvent: boolean): void => {
2361
+ if (thread.length) {
2362
+ this.updateThreadRootEvent(this.threadsTimelineSets?.[0], thread, toStartOfTimeline, recreateEvent);
2363
+ if (thread.hasCurrentUserParticipated) {
2364
+ this.updateThreadRootEvent(this.threadsTimelineSets?.[1], thread, toStartOfTimeline, recreateEvent);
2365
+ }
2366
+ }
2367
+ };
2368
+
2369
+ private updateThreadRootEvent = (
2370
+ timelineSet: Optional<EventTimelineSet>,
2371
+ thread: Thread,
2372
+ toStartOfTimeline: boolean,
2373
+ recreateEvent: boolean,
2374
+ ): void => {
2375
+ if (timelineSet && thread.rootEvent) {
2376
+ if (recreateEvent) {
2377
+ timelineSet.removeEvent(thread.id);
2378
+ }
2379
+ if (Thread.hasServerSideSupport) {
2380
+ timelineSet.addLiveEvent(thread.rootEvent, {
2381
+ duplicateStrategy: DuplicateStrategy.Replace,
2382
+ fromCache: false,
2383
+ roomState: this.currentState,
2384
+ });
2385
+ } else {
2386
+ timelineSet.addEventToTimeline(thread.rootEvent, timelineSet.getLiveTimeline(), { toStartOfTimeline });
2387
+ }
2388
+ }
2389
+ };
2390
+
2391
+ public createThread(
2392
+ threadId: string,
2393
+ rootEvent: MatrixEvent | undefined,
2394
+ events: MatrixEvent[] = [],
2395
+ toStartOfTimeline: boolean,
2396
+ ): Thread {
2397
+ if (this.threads.has(threadId)) {
2398
+ return this.threads.get(threadId)!;
2399
+ }
2400
+
2401
+ if (rootEvent) {
2402
+ const relatedEvents = this.relations.getAllChildEventsForEvent(rootEvent.getId()!);
2403
+ if (relatedEvents?.length) {
2404
+ // Include all relations of the root event, given it'll be visible in both timelines,
2405
+ // except `m.replace` as that will already be applied atop the event using `MatrixEvent::makeReplaced`
2406
+ events = events.concat(relatedEvents.filter((e) => !e.isRelation(RelationType.Replace)));
2407
+ }
2408
+ }
2409
+
2410
+ const thread = new Thread(threadId, rootEvent, {
2411
+ room: this,
2412
+ client: this.client,
2413
+ pendingEventOrdering: this.opts.pendingEventOrdering,
2414
+ receipts: this.cachedThreadReadReceipts.get(threadId) ?? [],
2415
+ });
2416
+
2417
+ // Add the re-emitter before we start adding events to the thread so we don't miss events
2418
+ this.reEmitter.reEmit(thread, [
2419
+ ThreadEvent.Delete,
2420
+ ThreadEvent.Update,
2421
+ ThreadEvent.NewReply,
2422
+ RoomEvent.Timeline,
2423
+ RoomEvent.TimelineReset,
2424
+ ]);
2425
+
2426
+ // All read receipts should now come down from sync, we do not need to keep
2427
+ // a reference to the cached receipts anymore.
2428
+ this.cachedThreadReadReceipts.delete(threadId);
2429
+
2430
+ // If we managed to create a thread and figure out its `id` then we can use it
2431
+ // This has to happen before thread.addEvents, because that adds events to the eventtimeline, and the
2432
+ // eventtimeline sometimes looks up thread information via the room.
2433
+ this.threads.set(thread.id, thread);
2434
+
2435
+ // This is necessary to be able to jump to events in threads:
2436
+ // If we jump to an event in a thread where neither the event, nor the root,
2437
+ // nor any thread event are loaded yet, we'll load the event as well as the thread root, create the thread,
2438
+ // and pass the event through this.
2439
+ thread.addEvents(events, false);
2440
+
2441
+ // We need to update the thread root events, but the thread may not be ready yet.
2442
+ // If it isn't, it will fire ThreadEvent.Update when it is and we'll call updateThreadRootEvents then.
2443
+ if (this.threadsReady && thread.initialEventsFetched) {
2444
+ this.updateThreadRootEvents(thread, toStartOfTimeline, false);
2445
+ }
2446
+ this.emit(ThreadEvent.New, thread, toStartOfTimeline);
2447
+
2448
+ return thread;
2449
+ }
2450
+
2451
+ private applyRedaction = (event: MatrixEvent): void => {
2452
+ if (event.isRedaction()) {
2453
+ const redactId = event.event.redacts;
2454
+
2455
+ // if we know about this event, redact its contents now.
2456
+ const redactedEvent = redactId ? this.findEventById(redactId) : undefined;
2457
+ if (redactedEvent) {
2458
+ const threadRootId = redactedEvent.threadRootId;
2459
+ redactedEvent.makeRedacted(event, this);
2460
+
2461
+ // If this is in the current state, replace it with the redacted version
2462
+ if (redactedEvent.isState()) {
2463
+ const currentStateEvent = this.currentState.getStateEvents(
2464
+ redactedEvent.getType(),
2465
+ redactedEvent.getStateKey()!,
2466
+ );
2467
+ if (currentStateEvent?.getId() === redactedEvent.getId()) {
2468
+ this.currentState.setStateEvents([redactedEvent]);
2469
+ }
2470
+ }
2471
+
2472
+ this.emit(RoomEvent.Redaction, event, this, threadRootId);
2473
+
2474
+ // TODO: we stash user displaynames (among other things) in
2475
+ // RoomMember objects which are then attached to other events
2476
+ // (in the sender and target fields). We should get those
2477
+ // RoomMember objects to update themselves when the events that
2478
+ // they are based on are changed.
2479
+
2480
+ // Remove any visibility change on this event.
2481
+ this.visibilityEvents.delete(redactId!);
2482
+
2483
+ // If this event is a visibility change event, remove it from the
2484
+ // list of visibility changes and update any event affected by it.
2485
+ if (redactedEvent.isVisibilityEvent()) {
2486
+ this.redactVisibilityChangeEvent(event);
2487
+ }
2488
+ }
2489
+
2490
+ // FIXME: apply redactions to notification list
2491
+
2492
+ // NB: We continue to add the redaction event to the timeline so
2493
+ // clients can say "so and so redacted an event" if they wish to. Also
2494
+ // this may be needed to trigger an update.
2495
+ }
2496
+ };
2497
+
2498
+ private processLiveEvent(event: MatrixEvent): void {
2499
+ this.applyRedaction(event);
2500
+
2501
+ // Implement MSC3531: hiding messages.
2502
+ if (event.isVisibilityEvent()) {
2503
+ // This event changes the visibility of another event, record
2504
+ // the visibility change, inform clients if necessary.
2505
+ this.applyNewVisibilityEvent(event);
2506
+ }
2507
+ // If any pending visibility change is waiting for this (older) event,
2508
+ this.applyPendingVisibilityEvents(event);
2509
+
2510
+ // Sliding Sync modifications:
2511
+ // The proxy cannot guarantee every sent event will have a transaction_id field, so we need
2512
+ // to check the event ID against the list of pending events if there is no transaction ID
2513
+ // field. Only do this for events sent by us though as it's potentially expensive to loop
2514
+ // the pending events map.
2515
+ const txnId = event.getUnsigned().transaction_id;
2516
+ if (!txnId && event.getSender() === this.myUserId) {
2517
+ // check the txn map for a matching event ID
2518
+ for (const [tid, localEvent] of this.txnToEvent) {
2519
+ if (localEvent.getId() === event.getId()) {
2520
+ logger.debug("processLiveEvent: found sent event without txn ID: ", tid, event.getId());
2521
+ // update the unsigned field so we can re-use the same codepaths
2522
+ const unsigned = event.getUnsigned();
2523
+ unsigned.transaction_id = tid;
2524
+ event.setUnsigned(unsigned);
2525
+ break;
2526
+ }
2527
+ }
2528
+ }
2529
+ }
2530
+
2531
+ /**
2532
+ * Add an event to the end of this room's live timelines. Will fire
2533
+ * "Room.timeline".
2534
+ *
2535
+ * @param event - Event to be added
2536
+ * @param addLiveEventOptions - addLiveEvent options
2537
+ * @internal
2538
+ *
2539
+ * @remarks
2540
+ * Fires {@link RoomEvent.Timeline}
2541
+ */
2542
+ private addLiveEvent(event: MatrixEvent, addLiveEventOptions: IAddLiveEventOptions): void {
2543
+ const { duplicateStrategy, timelineWasEmpty, fromCache } = addLiveEventOptions;
2544
+
2545
+ // add to our timeline sets
2546
+ for (const timelineSet of this.timelineSets) {
2547
+ timelineSet.addLiveEvent(event, {
2548
+ duplicateStrategy,
2549
+ fromCache,
2550
+ timelineWasEmpty,
2551
+ });
2552
+ }
2553
+
2554
+ // synthesize and inject implicit read receipts
2555
+ // Done after adding the event because otherwise the app would get a read receipt
2556
+ // pointing to an event that wasn't yet in the timeline
2557
+ // Don't synthesize RR for m.room.redaction as this causes the RR to go missing.
2558
+ if (event.sender && event.getType() !== EventType.RoomRedaction) {
2559
+ this.addReceipt(synthesizeReceipt(event.sender.userId, event, ReceiptType.Read), true);
2560
+
2561
+ // Any live events from a user could be taken as implicit
2562
+ // presence information: evidence that they are currently active.
2563
+ // ...except in a world where we use 'user.currentlyActive' to reduce
2564
+ // presence spam, this isn't very useful - we'll get a transition when
2565
+ // they are no longer currently active anyway. So don't bother to
2566
+ // reset the lastActiveAgo and lastPresenceTs from the RoomState's user.
2567
+ }
2568
+ }
2569
+
2570
+ /**
2571
+ * Add a pending outgoing event to this room.
2572
+ *
2573
+ * <p>The event is added to either the pendingEventList, or the live timeline,
2574
+ * depending on the setting of opts.pendingEventOrdering.
2575
+ *
2576
+ * <p>This is an internal method, intended for use by MatrixClient.
2577
+ *
2578
+ * @param event - The event to add.
2579
+ *
2580
+ * @param txnId - Transaction id for this outgoing event
2581
+ *
2582
+ * @throws if the event doesn't have status SENDING, or we aren't given a
2583
+ * unique transaction id.
2584
+ *
2585
+ * @remarks
2586
+ * Fires {@link RoomEvent.LocalEchoUpdated}
2587
+ */
2588
+ public addPendingEvent(event: MatrixEvent, txnId: string): void {
2589
+ if (event.status !== EventStatus.SENDING && event.status !== EventStatus.NOT_SENT) {
2590
+ throw new Error("addPendingEvent called on an event with status " + event.status);
2591
+ }
2592
+
2593
+ if (this.txnToEvent.get(txnId)) {
2594
+ throw new Error("addPendingEvent called on an event with known txnId " + txnId);
2595
+ }
2596
+
2597
+ // call setEventMetadata to set up event.sender etc
2598
+ // as event is shared over all timelineSets, we set up its metadata based
2599
+ // on the unfiltered timelineSet.
2600
+ EventTimeline.setEventMetadata(event, this.getLiveTimeline().getState(EventTimeline.FORWARDS)!, false);
2601
+
2602
+ this.txnToEvent.set(txnId, event);
2603
+ if (this.pendingEventList) {
2604
+ if (this.pendingEventList.some((e) => e.status === EventStatus.NOT_SENT)) {
2605
+ logger.warn("Setting event as NOT_SENT due to messages in the same state");
2606
+ event.setStatus(EventStatus.NOT_SENT);
2607
+ }
2608
+ this.pendingEventList.push(event);
2609
+ this.savePendingEvents();
2610
+ if (event.isRelation()) {
2611
+ // For pending events, add them to the relations collection immediately.
2612
+ // (The alternate case below already covers this as part of adding to
2613
+ // the timeline set.)
2614
+ this.aggregateNonLiveRelation(event);
2615
+ }
2616
+
2617
+ if (event.isRedaction()) {
2618
+ const redactId = event.event.redacts;
2619
+ let redactedEvent = this.pendingEventList.find((e) => e.getId() === redactId);
2620
+ if (!redactedEvent && redactId) {
2621
+ redactedEvent = this.findEventById(redactId);
2622
+ }
2623
+ if (redactedEvent) {
2624
+ redactedEvent.markLocallyRedacted(event);
2625
+ this.emit(RoomEvent.Redaction, event, this, redactedEvent.threadRootId);
2626
+ }
2627
+ }
2628
+ } else {
2629
+ for (const timelineSet of this.timelineSets) {
2630
+ if (timelineSet.getFilter()) {
2631
+ if (timelineSet.getFilter()!.filterRoomTimeline([event]).length) {
2632
+ timelineSet.addEventToTimeline(event, timelineSet.getLiveTimeline(), {
2633
+ toStartOfTimeline: false,
2634
+ });
2635
+ }
2636
+ } else {
2637
+ timelineSet.addEventToTimeline(event, timelineSet.getLiveTimeline(), {
2638
+ toStartOfTimeline: false,
2639
+ });
2640
+ }
2641
+ }
2642
+ }
2643
+
2644
+ this.emit(RoomEvent.LocalEchoUpdated, event, this);
2645
+ }
2646
+
2647
+ /**
2648
+ * Persists all pending events to local storage
2649
+ *
2650
+ * If the current room is encrypted only encrypted events will be persisted
2651
+ * all messages that are not yet encrypted will be discarded
2652
+ *
2653
+ * This is because the flow of EVENT_STATUS transition is
2654
+ * `queued => sending => encrypting => sending => sent`
2655
+ *
2656
+ * Steps 3 and 4 are skipped for unencrypted room.
2657
+ * It is better to discard an unencrypted message rather than persisting
2658
+ * it locally for everyone to read
2659
+ */
2660
+ private savePendingEvents(): void {
2661
+ if (this.pendingEventList) {
2662
+ const pendingEvents = this.pendingEventList
2663
+ .map((event) => {
2664
+ return {
2665
+ ...event.event,
2666
+ txn_id: event.getTxnId(),
2667
+ };
2668
+ })
2669
+ .filter((event) => {
2670
+ // Filter out the unencrypted messages if the room is encrypted
2671
+ const isEventEncrypted = event.type === EventType.RoomMessageEncrypted;
2672
+ const isRoomEncrypted = this.hasEncryptionStateEvent();
2673
+ return isEventEncrypted || !isRoomEncrypted;
2674
+ });
2675
+
2676
+ this.client.store.setPendingEvents(this.roomId, pendingEvents);
2677
+ }
2678
+ }
2679
+
2680
+ /**
2681
+ * Used to aggregate the local echo for a relation, and also
2682
+ * for re-applying a relation after it's redaction has been cancelled,
2683
+ * as the local echo for the redaction of the relation would have
2684
+ * un-aggregated the relation. Note that this is different from regular messages,
2685
+ * which are just kept detached for their local echo.
2686
+ *
2687
+ * Also note that live events are aggregated in the live EventTimelineSet.
2688
+ * @param event - the relation event that needs to be aggregated.
2689
+ */
2690
+ private aggregateNonLiveRelation(event: MatrixEvent): void {
2691
+ this.relations.aggregateChildEvent(event);
2692
+ }
2693
+
2694
+ public getEventForTxnId(txnId: string): MatrixEvent | undefined {
2695
+ return this.txnToEvent.get(txnId);
2696
+ }
2697
+
2698
+ /**
2699
+ * Deal with the echo of a message we sent.
2700
+ *
2701
+ * <p>We move the event to the live timeline if it isn't there already, and
2702
+ * update it.
2703
+ *
2704
+ * @param remoteEvent - The event received from
2705
+ * /sync
2706
+ * @param localEvent - The local echo, which
2707
+ * should be either in the pendingEventList or the timeline.
2708
+ *
2709
+ * @internal
2710
+ *
2711
+ * @remarks
2712
+ * Fires {@link RoomEvent.LocalEchoUpdated}
2713
+ */
2714
+ public handleRemoteEcho(remoteEvent: MatrixEvent, localEvent: MatrixEvent): void {
2715
+ const oldEventId = localEvent.getId()!;
2716
+ const newEventId = remoteEvent.getId()!;
2717
+ const oldStatus = localEvent.status;
2718
+
2719
+ logger.debug(`Got remote echo for event ${oldEventId} -> ${newEventId} old status ${oldStatus}`);
2720
+
2721
+ // no longer pending
2722
+ this.txnToEvent.delete(remoteEvent.getUnsigned().transaction_id!);
2723
+
2724
+ // if it's in the pending list, remove it
2725
+ if (this.pendingEventList) {
2726
+ this.removePendingEvent(oldEventId);
2727
+ }
2728
+
2729
+ // replace the event source (this will preserve the plaintext payload if
2730
+ // any, which is good, because we don't want to try decoding it again).
2731
+ localEvent.handleRemoteEcho(remoteEvent.event);
2732
+
2733
+ const { shouldLiveInRoom, threadId } = this.eventShouldLiveIn(remoteEvent);
2734
+ const thread = threadId ? this.getThread(threadId) : null;
2735
+ thread?.setEventMetadata(localEvent);
2736
+ thread?.timelineSet.handleRemoteEcho(localEvent, oldEventId, newEventId);
2737
+
2738
+ if (shouldLiveInRoom) {
2739
+ for (const timelineSet of this.timelineSets) {
2740
+ // if it's already in the timeline, update the timeline map. If it's not, add it.
2741
+ timelineSet.handleRemoteEcho(localEvent, oldEventId, newEventId);
2742
+ }
2743
+ }
2744
+
2745
+ this.emit(RoomEvent.LocalEchoUpdated, localEvent, this, oldEventId, oldStatus);
2746
+ }
2747
+
2748
+ /**
2749
+ * Update the status / event id on a pending event, to reflect its transmission
2750
+ * progress.
2751
+ *
2752
+ * <p>This is an internal method.
2753
+ *
2754
+ * @param event - local echo event
2755
+ * @param newStatus - status to assign
2756
+ * @param newEventId - new event id to assign. Ignored unless newStatus == EventStatus.SENT.
2757
+ *
2758
+ * @remarks
2759
+ * Fires {@link RoomEvent.LocalEchoUpdated}
2760
+ */
2761
+ public updatePendingEvent(event: MatrixEvent, newStatus: EventStatus, newEventId?: string): void {
2762
+ logger.log(
2763
+ `setting pendingEvent status to ${newStatus} in ${event.getRoomId()} ` +
2764
+ `event ID ${event.getId()} -> ${newEventId}`,
2765
+ );
2766
+
2767
+ // if the message was sent, we expect an event id
2768
+ if (newStatus == EventStatus.SENT && !newEventId) {
2769
+ throw new Error("updatePendingEvent called with status=SENT, but no new event id");
2770
+ }
2771
+
2772
+ // SENT races against /sync, so we have to special-case it.
2773
+ if (newStatus == EventStatus.SENT) {
2774
+ const timeline = this.getTimelineForEvent(newEventId!);
2775
+ if (timeline) {
2776
+ // we've already received the event via the event stream.
2777
+ // nothing more to do here, assuming the transaction ID was correctly matched.
2778
+ // Let's check that.
2779
+ const remoteEvent = this.findEventById(newEventId!);
2780
+ const remoteTxnId = remoteEvent?.getUnsigned().transaction_id;
2781
+ if (!remoteTxnId && remoteEvent) {
2782
+ // This code path is mostly relevant for the Sliding Sync proxy.
2783
+ // The remote event did not contain a transaction ID, so we did not handle
2784
+ // the remote echo yet. Handle it now.
2785
+ const unsigned = remoteEvent.getUnsigned();
2786
+ unsigned.transaction_id = event.getTxnId();
2787
+ remoteEvent.setUnsigned(unsigned);
2788
+ // the remote event is _already_ in the timeline, so we need to remove it so
2789
+ // we can convert the local event into the final event.
2790
+ this.removeEvent(remoteEvent.getId()!);
2791
+ this.handleRemoteEcho(remoteEvent, event);
2792
+ }
2793
+ return;
2794
+ }
2795
+ }
2796
+
2797
+ const oldStatus = event.status;
2798
+ const oldEventId = event.getId()!;
2799
+
2800
+ if (!oldStatus) {
2801
+ throw new Error("updatePendingEventStatus called on an event which is not a local echo.");
2802
+ }
2803
+
2804
+ const allowed = ALLOWED_TRANSITIONS[oldStatus];
2805
+ if (!allowed?.includes(newStatus)) {
2806
+ throw new Error(`Invalid EventStatus transition ${oldStatus}->${newStatus}`);
2807
+ }
2808
+
2809
+ event.setStatus(newStatus);
2810
+
2811
+ if (newStatus == EventStatus.SENT) {
2812
+ // update the event id
2813
+ event.replaceLocalEventId(newEventId!);
2814
+
2815
+ const { shouldLiveInRoom, threadId } = this.eventShouldLiveIn(event);
2816
+ const thread = threadId ? this.getThread(threadId) : undefined;
2817
+ thread?.setEventMetadata(event);
2818
+ thread?.timelineSet.replaceEventId(oldEventId, newEventId!);
2819
+
2820
+ if (shouldLiveInRoom) {
2821
+ // if the event was already in the timeline (which will be the case if
2822
+ // opts.pendingEventOrdering==chronological), we need to update the
2823
+ // timeline map.
2824
+ for (const timelineSet of this.timelineSets) {
2825
+ timelineSet.replaceEventId(oldEventId, newEventId!);
2826
+ }
2827
+ }
2828
+ } else if (newStatus == EventStatus.CANCELLED) {
2829
+ // remove it from the pending event list, or the timeline.
2830
+ if (this.pendingEventList) {
2831
+ const removedEvent = this.getPendingEvent(oldEventId);
2832
+ this.removePendingEvent(oldEventId);
2833
+ if (removedEvent?.isRedaction()) {
2834
+ this.revertRedactionLocalEcho(removedEvent);
2835
+ }
2836
+ }
2837
+ this.removeEvent(oldEventId);
2838
+ }
2839
+ this.savePendingEvents();
2840
+
2841
+ this.emit(RoomEvent.LocalEchoUpdated, event, this, oldEventId, oldStatus);
2842
+ }
2843
+
2844
+ private revertRedactionLocalEcho(redactionEvent: MatrixEvent): void {
2845
+ const redactId = redactionEvent.event.redacts;
2846
+ if (!redactId) {
2847
+ return;
2848
+ }
2849
+ const redactedEvent = this.getUnfilteredTimelineSet().findEventById(redactId);
2850
+ if (redactedEvent) {
2851
+ redactedEvent.unmarkLocallyRedacted();
2852
+ // re-render after undoing redaction
2853
+ this.emit(RoomEvent.RedactionCancelled, redactionEvent, this);
2854
+ // reapply relation now redaction failed
2855
+ if (redactedEvent.isRelation()) {
2856
+ this.aggregateNonLiveRelation(redactedEvent);
2857
+ }
2858
+ }
2859
+ }
2860
+
2861
+ private assertTimelineSetsAreLive(): void {
2862
+ for (let i = 0; i < this.timelineSets.length; i++) {
2863
+ const liveTimeline = this.timelineSets[i].getLiveTimeline();
2864
+ if (liveTimeline.getPaginationToken(EventTimeline.FORWARDS)) {
2865
+ throw new Error(
2866
+ "live timeline " +
2867
+ i +
2868
+ " is no longer live - it has a pagination token " +
2869
+ "(" +
2870
+ liveTimeline.getPaginationToken(EventTimeline.FORWARDS) +
2871
+ ")",
2872
+ );
2873
+ }
2874
+ if (liveTimeline.getNeighbouringTimeline(EventTimeline.FORWARDS)) {
2875
+ throw new Error(`live timeline ${i} is no longer live - it has a neighbouring timeline`);
2876
+ }
2877
+ }
2878
+ }
2879
+
2880
+ /**
2881
+ * Add some events to this room. This can include state events, message
2882
+ * events and typing notifications. These events are treated as "live" so
2883
+ * they will go to the end of the timeline.
2884
+ *
2885
+ * @param events - A list of events to add.
2886
+ * @param addLiveEventOptions - addLiveEvent options
2887
+ * @throws If `duplicateStrategy` is not falsey, 'replace' or 'ignore'.
2888
+ */
2889
+ public async addLiveEvents(events: MatrixEvent[], addLiveEventOptions?: IAddLiveEventOptions): Promise<void> {
2890
+ const { duplicateStrategy, fromCache, timelineWasEmpty = false } = addLiveEventOptions ?? {};
2891
+ if (duplicateStrategy && ["replace", "ignore"].indexOf(duplicateStrategy) === -1) {
2892
+ throw new Error("duplicateStrategy MUST be either 'replace' or 'ignore'");
2893
+ }
2894
+
2895
+ // sanity check that the live timeline is still live
2896
+ this.assertTimelineSetsAreLive();
2897
+
2898
+ const threadRoots = this.findThreadRoots(events);
2899
+ const eventsByThread: { [threadId: string]: MatrixEvent[] } = {};
2900
+
2901
+ const options: IAddLiveEventOptions = {
2902
+ duplicateStrategy,
2903
+ fromCache,
2904
+ timelineWasEmpty,
2905
+ };
2906
+
2907
+ // List of extra events to check for being parents of any relations encountered
2908
+ const neighbouringEvents = [...events];
2909
+
2910
+ for (const event of events) {
2911
+ // TODO: We should have a filter to say "only add state event types X Y Z to the timeline".
2912
+ this.processLiveEvent(event);
2913
+
2914
+ if (event.getUnsigned().transaction_id) {
2915
+ const existingEvent = this.txnToEvent.get(event.getUnsigned().transaction_id!);
2916
+ if (existingEvent) {
2917
+ // remote echo of an event we sent earlier
2918
+ this.handleRemoteEcho(event, existingEvent);
2919
+ continue; // we can skip adding the event to the timeline sets, it is already there
2920
+ }
2921
+ }
2922
+
2923
+ let {
2924
+ shouldLiveInRoom,
2925
+ shouldLiveInThread,
2926
+ threadId = "",
2927
+ } = this.eventShouldLiveIn(event, neighbouringEvents, threadRoots);
2928
+
2929
+ if (!shouldLiveInThread && !shouldLiveInRoom && event.isRelation()) {
2930
+ try {
2931
+ const parentEvent = new MatrixEvent(
2932
+ await this.client.fetchRoomEvent(this.roomId, event.relationEventId!),
2933
+ );
2934
+ neighbouringEvents.push(parentEvent);
2935
+ if (parentEvent.threadRootId) {
2936
+ threadRoots.add(parentEvent.threadRootId);
2937
+ const unsigned = event.getUnsigned();
2938
+ unsigned[UNSIGNED_THREAD_ID_FIELD.name] = parentEvent.threadRootId;
2939
+ event.setUnsigned(unsigned);
2940
+ }
2941
+
2942
+ ({
2943
+ shouldLiveInRoom,
2944
+ shouldLiveInThread,
2945
+ threadId = "",
2946
+ } = this.eventShouldLiveIn(event, neighbouringEvents, threadRoots));
2947
+ } catch (e) {
2948
+ logger.error("Failed to load parent event of unhandled relation", e);
2949
+ }
2950
+ }
2951
+
2952
+ if (shouldLiveInThread && !eventsByThread[threadId]) {
2953
+ eventsByThread[threadId] = [];
2954
+ }
2955
+ eventsByThread[threadId]?.push(event);
2956
+
2957
+ if (shouldLiveInRoom) {
2958
+ this.addLiveEvent(event, options);
2959
+ } else if (!shouldLiveInThread && event.isRelation()) {
2960
+ this.relations.aggregateChildEvent(event);
2961
+ }
2962
+ }
2963
+
2964
+ Object.entries(eventsByThread).forEach(([threadId, threadEvents]) => {
2965
+ this.addThreadedEvents(threadId, threadEvents, false);
2966
+ });
2967
+ }
2968
+
2969
+ public partitionThreadedEvents(
2970
+ events: MatrixEvent[],
2971
+ ): [timelineEvents: MatrixEvent[], threadedEvents: MatrixEvent[], unknownRelations: MatrixEvent[]] {
2972
+ // Indices to the events array, for readability
2973
+ const ROOM = 0;
2974
+ const THREAD = 1;
2975
+ const UNKNOWN_RELATION = 2;
2976
+ if (this.client.supportsThreads()) {
2977
+ const threadRoots = this.findThreadRoots(events);
2978
+ return events.reduce<[MatrixEvent[], MatrixEvent[], MatrixEvent[]]>(
2979
+ (memo, event: MatrixEvent) => {
2980
+ const { shouldLiveInRoom, shouldLiveInThread, threadId } = this.eventShouldLiveIn(
2981
+ event,
2982
+ events,
2983
+ threadRoots,
2984
+ );
2985
+
2986
+ if (shouldLiveInRoom) {
2987
+ memo[ROOM].push(event);
2988
+ }
2989
+
2990
+ if (shouldLiveInThread) {
2991
+ event.setThreadId(threadId ?? "");
2992
+ memo[THREAD].push(event);
2993
+ }
2994
+
2995
+ if (!shouldLiveInThread && !shouldLiveInRoom) {
2996
+ memo[UNKNOWN_RELATION].push(event);
2997
+ }
2998
+
2999
+ return memo;
3000
+ },
3001
+ [[], [], []],
3002
+ );
3003
+ } else {
3004
+ // When `threadSupport` is disabled treat all events as timelineEvents
3005
+ return [events as MatrixEvent[], [] as MatrixEvent[], [] as MatrixEvent[]];
3006
+ }
3007
+ }
3008
+
3009
+ /**
3010
+ * Given some events, find the IDs of all the thread roots that are referred to by them.
3011
+ */
3012
+ private findThreadRoots(events: MatrixEvent[]): Set<string> {
3013
+ const threadRoots = new Set<string>();
3014
+ for (const event of events) {
3015
+ const threadRootId = event.threadRootId;
3016
+ if (threadRootId != undefined) {
3017
+ threadRoots.add(threadRootId);
3018
+ }
3019
+ }
3020
+ return threadRoots;
3021
+ }
3022
+
3023
+ /**
3024
+ * Add a receipt event to the room.
3025
+ * @param event - The m.receipt event.
3026
+ * @param synthetic - True if this event is implicit.
3027
+ */
3028
+ public addReceipt(event: MatrixEvent, synthetic = false): void {
3029
+ const content = event.getContent<ReceiptContent>();
3030
+
3031
+ this.roomReceipts.add(content, synthetic);
3032
+
3033
+ // TODO: delete the following code when it has been replaced by RoomReceipts
3034
+ Object.keys(content).forEach((eventId: string) => {
3035
+ Object.keys(content[eventId]).forEach((receiptType: ReceiptType | string) => {
3036
+ Object.keys(content[eventId][receiptType]).forEach((userId: string) => {
3037
+ const receipt = content[eventId][receiptType][userId] as Receipt;
3038
+ const receiptForMainTimeline = !receipt.thread_id || receipt.thread_id === MAIN_ROOM_TIMELINE;
3039
+ const receiptDestination: Thread | this | undefined = receiptForMainTimeline
3040
+ ? this
3041
+ : this.threads.get(receipt.thread_id ?? "");
3042
+
3043
+ if (receiptDestination) {
3044
+ receiptDestination.addReceiptToStructure(
3045
+ eventId,
3046
+ receiptType as ReceiptType,
3047
+ userId,
3048
+ receipt,
3049
+ synthetic,
3050
+ );
3051
+
3052
+ // If the read receipt sent for the logged in user matches
3053
+ // the last event of the live timeline, then we know for a fact
3054
+ // that the user has read that message, so we can mark the room
3055
+ // as read and not wait for the remote echo from synapse.
3056
+ //
3057
+ // This needs to be done after the initial sync as we do not want this
3058
+ // logic to run whilst the room is being initialised
3059
+ //
3060
+ // We only do this for non-synthetic receipts, because
3061
+ // our intention is to do this when the user really did
3062
+ // just read a message, not when we are e.g. receiving
3063
+ // an event during the sync. More explanation at:
3064
+ // https://github.com/matrix-org/matrix-js-sdk/issues/3684
3065
+ if (!synthetic && this.client.isInitialSyncComplete() && userId === this.client.getUserId()) {
3066
+ const lastEvent = receiptDestination.timeline[receiptDestination.timeline.length - 1];
3067
+ if (lastEvent && eventId === lastEvent.getId() && userId === lastEvent.getSender()) {
3068
+ receiptDestination.setUnread(NotificationCountType.Total, 0);
3069
+ receiptDestination.setUnread(NotificationCountType.Highlight, 0);
3070
+ }
3071
+ }
3072
+ } else {
3073
+ // The thread does not exist locally, keep the read receipt
3074
+ // in a cache locally, and re-apply the `addReceipt` logic
3075
+ // when the thread is created
3076
+ this.cachedThreadReadReceipts.set(receipt.thread_id!, [
3077
+ ...(this.cachedThreadReadReceipts.get(receipt.thread_id!) ?? []),
3078
+ { eventId, receiptType, userId, receipt, synthetic },
3079
+ ]);
3080
+ }
3081
+
3082
+ const me = this.client.getUserId();
3083
+ // Track the time of the current user's oldest threaded receipt in the room.
3084
+ if (userId === me && !receiptForMainTimeline && receipt.ts < this.oldestThreadedReceiptTs) {
3085
+ this.oldestThreadedReceiptTs = receipt.ts;
3086
+ }
3087
+
3088
+ // Track each user's unthreaded read receipt.
3089
+ if (!receipt.thread_id && receipt.ts > (this.unthreadedReceipts.get(userId)?.ts ?? 0)) {
3090
+ this.unthreadedReceipts.set(userId, receipt);
3091
+ }
3092
+ });
3093
+ });
3094
+ });
3095
+ // End of code to delete when replaced by RoomReceipts
3096
+
3097
+ // send events after we've regenerated the structure & cache, otherwise things that
3098
+ // listened for the event would read stale data.
3099
+ this.emit(RoomEvent.Receipt, event, this);
3100
+ }
3101
+
3102
+ /**
3103
+ * Adds/handles ephemeral events such as typing notifications and read receipts.
3104
+ * @param events - A list of events to process
3105
+ */
3106
+ public addEphemeralEvents(events: MatrixEvent[]): void {
3107
+ for (const event of events) {
3108
+ if (event.getType() === EventType.Typing) {
3109
+ this.currentState.setTypingEvent(event);
3110
+ } else if (event.getType() === EventType.Receipt) {
3111
+ this.addReceipt(event);
3112
+ } // else ignore - life is too short for us to care about these events
3113
+ }
3114
+ }
3115
+
3116
+ /**
3117
+ * Removes events from this room.
3118
+ * @param eventIds - A list of eventIds to remove.
3119
+ */
3120
+ public removeEvents(eventIds: string[]): void {
3121
+ for (const eventId of eventIds) {
3122
+ this.removeEvent(eventId);
3123
+ }
3124
+ }
3125
+
3126
+ /**
3127
+ * Removes a single event from this room.
3128
+ *
3129
+ * @param eventId - The id of the event to remove
3130
+ *
3131
+ * @returns true if the event was removed from any of the room's timeline sets
3132
+ */
3133
+ public removeEvent(eventId: string): boolean {
3134
+ let removedAny = false;
3135
+ for (const timelineSet of this.timelineSets) {
3136
+ const removed = timelineSet.removeEvent(eventId);
3137
+ if (removed) {
3138
+ if (removed.isRedaction()) {
3139
+ this.revertRedactionLocalEcho(removed);
3140
+ }
3141
+ removedAny = true;
3142
+ }
3143
+ }
3144
+ return removedAny;
3145
+ }
3146
+
3147
+ /**
3148
+ * Recalculate various aspects of the room, including the room name and
3149
+ * room summary. Call this any time the room's current state is modified.
3150
+ * May fire "Room.name" if the room name is updated.
3151
+ *
3152
+ * @remarks
3153
+ * Fires {@link RoomEvent.Name}
3154
+ */
3155
+ public recalculate(): void {
3156
+ // set fake stripped state events if this is an invite room so logic remains
3157
+ // consistent elsewhere.
3158
+ const membershipEvent = this.currentState.getStateEvents(EventType.RoomMember, this.myUserId);
3159
+ if (membershipEvent) {
3160
+ const membership = membershipEvent.getContent().membership;
3161
+ this.updateMyMembership(membership!);
3162
+
3163
+ if (membership === KnownMembership.Invite) {
3164
+ const strippedStateEvents = membershipEvent.getUnsigned().invite_room_state || [];
3165
+ strippedStateEvents.forEach((strippedEvent) => {
3166
+ const existingEvent = this.currentState.getStateEvents(strippedEvent.type, strippedEvent.state_key);
3167
+ if (!existingEvent) {
3168
+ // set the fake stripped event instead
3169
+ this.currentState.setStateEvents([
3170
+ new MatrixEvent({
3171
+ type: strippedEvent.type,
3172
+ state_key: strippedEvent.state_key,
3173
+ content: strippedEvent.content,
3174
+ event_id: "$fake" + Date.now(),
3175
+ room_id: this.roomId,
3176
+ sender: this.myUserId, // technically a lie
3177
+ }),
3178
+ ]);
3179
+ }
3180
+ });
3181
+ }
3182
+ }
3183
+
3184
+ const oldName = this.name;
3185
+ this.name = this.calculateRoomName(this.myUserId);
3186
+ this.normalizedName = normalize(this.name);
3187
+ this.summary = new RoomSummary(this.roomId, {
3188
+ title: this.name,
3189
+ });
3190
+
3191
+ if (oldName !== this.name) {
3192
+ this.emit(RoomEvent.Name, this);
3193
+ }
3194
+ }
3195
+
3196
+ /**
3197
+ * Update the room-tag event for the room. The previous one is overwritten.
3198
+ * @param event - the m.tag event
3199
+ */
3200
+ public addTags(event: MatrixEvent): void {
3201
+ // event content looks like:
3202
+ // content: {
3203
+ // tags: {
3204
+ // $tagName: { $metadata: $value },
3205
+ // $tagName: { $metadata: $value },
3206
+ // }
3207
+ // }
3208
+
3209
+ // XXX: do we need to deep copy here?
3210
+ this.tags = event.getContent().tags || {};
3211
+
3212
+ // XXX: we could do a deep-comparison to see if the tags have really
3213
+ // changed - but do we want to bother?
3214
+ this.emit(RoomEvent.Tags, event, this);
3215
+ }
3216
+
3217
+ /**
3218
+ * Update the account_data events for this room, overwriting events of the same type.
3219
+ * @param events - an array of account_data events to add
3220
+ */
3221
+ public addAccountData(events: MatrixEvent[]): void {
3222
+ for (const event of events) {
3223
+ if (event.getType() === "m.tag") {
3224
+ this.addTags(event);
3225
+ }
3226
+ const eventType = event.getType();
3227
+ const lastEvent = this.accountData.get(eventType);
3228
+ this.accountData.set(eventType, event);
3229
+ this.emit(RoomEvent.AccountData, event, this, lastEvent);
3230
+ }
3231
+ }
3232
+
3233
+ /**
3234
+ * Access account_data event of given event type for this room
3235
+ * @param type - the type of account_data event to be accessed
3236
+ * @returns the account_data event in question
3237
+ */
3238
+ public getAccountData(type: EventType | string): MatrixEvent | undefined {
3239
+ return this.accountData.get(type);
3240
+ }
3241
+
3242
+ /**
3243
+ * Returns whether the syncing user has permission to send a message in the room
3244
+ * @returns true if the user should be permitted to send
3245
+ * message events into the room.
3246
+ */
3247
+ public maySendMessage(): boolean {
3248
+ return (
3249
+ this.getMyMembership() === KnownMembership.Join &&
3250
+ (this.hasEncryptionStateEvent()
3251
+ ? this.currentState.maySendEvent(EventType.RoomMessageEncrypted, this.myUserId)
3252
+ : this.currentState.maySendEvent(EventType.RoomMessage, this.myUserId))
3253
+ );
3254
+ }
3255
+
3256
+ /**
3257
+ * Returns whether the given user has permissions to issue an invite for this room.
3258
+ * @param userId - the ID of the Matrix user to check permissions for
3259
+ * @returns true if the user should be permitted to issue invites for this room.
3260
+ */
3261
+ public canInvite(userId: string): boolean {
3262
+ let canInvite = this.getMyMembership() === KnownMembership.Join;
3263
+ const powerLevelsEvent = this.currentState.getStateEvents(EventType.RoomPowerLevels, "");
3264
+ const powerLevels = powerLevelsEvent && powerLevelsEvent.getContent();
3265
+ const me = this.getMember(userId);
3266
+ if (powerLevels && me && powerLevels.invite > me.powerLevel) {
3267
+ canInvite = false;
3268
+ }
3269
+ return canInvite;
3270
+ }
3271
+
3272
+ /**
3273
+ * Returns the join rule based on the m.room.join_rule state event, defaulting to `invite`.
3274
+ * @returns the join_rule applied to this room
3275
+ */
3276
+ public getJoinRule(): JoinRule {
3277
+ return this.currentState.getJoinRule();
3278
+ }
3279
+
3280
+ /**
3281
+ * Returns the history visibility based on the m.room.history_visibility state event, defaulting to `shared`.
3282
+ * @returns the history_visibility applied to this room
3283
+ */
3284
+ public getHistoryVisibility(): HistoryVisibility {
3285
+ return this.currentState.getHistoryVisibility();
3286
+ }
3287
+
3288
+ /**
3289
+ * Returns the history visibility based on the m.room.history_visibility state event, defaulting to `shared`.
3290
+ * @returns the history_visibility applied to this room
3291
+ */
3292
+ public getGuestAccess(): GuestAccess {
3293
+ return this.currentState.getGuestAccess();
3294
+ }
3295
+
3296
+ /**
3297
+ * Returns the type of the room from the `m.room.create` event content or undefined if none is set
3298
+ * @returns the type of the room.
3299
+ */
3300
+ public getType(): RoomType | string | undefined {
3301
+ const createEvent = this.currentState.getStateEvents(EventType.RoomCreate, "");
3302
+ if (!createEvent) {
3303
+ if (!this.getTypeWarning) {
3304
+ logger.warn("[getType] Room " + this.roomId + " does not have an m.room.create event");
3305
+ this.getTypeWarning = true;
3306
+ }
3307
+ return undefined;
3308
+ }
3309
+ return createEvent.getContent()[RoomCreateTypeField];
3310
+ }
3311
+
3312
+ /**
3313
+ * Returns whether the room is a space-room as defined by MSC1772.
3314
+ * @returns true if the room's type is RoomType.Space
3315
+ */
3316
+ public isSpaceRoom(): boolean {
3317
+ return this.getType() === RoomType.Space;
3318
+ }
3319
+
3320
+ /**
3321
+ * Returns whether the room is a call-room as defined by MSC3417.
3322
+ * @returns true if the room's type is RoomType.UnstableCall
3323
+ */
3324
+ public isCallRoom(): boolean {
3325
+ return this.getType() === RoomType.UnstableCall;
3326
+ }
3327
+
3328
+ /**
3329
+ * Returns whether the room is a video room.
3330
+ * @returns true if the room's type is RoomType.ElementVideo
3331
+ */
3332
+ public isElementVideoRoom(): boolean {
3333
+ return this.getType() === RoomType.ElementVideo;
3334
+ }
3335
+
3336
+ /**
3337
+ * Find the predecessor of this room.
3338
+ *
3339
+ * @param msc3946ProcessDynamicPredecessor - if true, look for an
3340
+ * m.room.predecessor state event and use it if found (MSC3946).
3341
+ * @returns null if this room has no predecessor. Otherwise, returns
3342
+ * the roomId, last eventId and viaServers of the predecessor room.
3343
+ *
3344
+ * If msc3946ProcessDynamicPredecessor is true, use m.predecessor events
3345
+ * as well as m.room.create events to find predecessors.
3346
+ *
3347
+ * Note: if an m.predecessor event is used, eventId may be undefined
3348
+ * since last_known_event_id is optional.
3349
+ *
3350
+ * Note: viaServers may be undefined, and will definitely be undefined if
3351
+ * this predecessor comes from a RoomCreate event (rather than a
3352
+ * RoomPredecessor, which has the optional via_servers property).
3353
+ */
3354
+ public findPredecessor(
3355
+ msc3946ProcessDynamicPredecessor = false,
3356
+ ): { roomId: string; eventId?: string; viaServers?: string[] } | null {
3357
+ const currentState = this.getLiveTimeline().getState(EventTimeline.FORWARDS);
3358
+ if (!currentState) {
3359
+ return null;
3360
+ }
3361
+ return currentState.findPredecessor(msc3946ProcessDynamicPredecessor);
3362
+ }
3363
+
3364
+ private roomNameGenerator(state: RoomNameState): string {
3365
+ if (this.client.roomNameGenerator) {
3366
+ const name = this.client.roomNameGenerator(this.roomId, state);
3367
+ if (name !== null) {
3368
+ return name;
3369
+ }
3370
+ }
3371
+
3372
+ switch (state.type) {
3373
+ case RoomNameType.Actual:
3374
+ return state.name;
3375
+ case RoomNameType.Generated:
3376
+ switch (state.subtype) {
3377
+ case "Inviting":
3378
+ return `Inviting ${memberNamesToRoomName(state.names, state.count)}`;
3379
+ default:
3380
+ return memberNamesToRoomName(state.names, state.count);
3381
+ }
3382
+ case RoomNameType.EmptyRoom:
3383
+ if (state.oldName) {
3384
+ return `Empty room (was ${state.oldName})`;
3385
+ } else {
3386
+ return "Empty room";
3387
+ }
3388
+ }
3389
+ }
3390
+
3391
+ /**
3392
+ * This is an internal method. Calculates the name of the room from the current
3393
+ * room state.
3394
+ * @param userId - The client's user ID. Used to filter room members
3395
+ * correctly.
3396
+ * @param ignoreRoomNameEvent - Return the implicit room name that we'd see if there
3397
+ * was no m.room.name event.
3398
+ * @returns The calculated room name.
3399
+ */
3400
+ private calculateRoomName(userId: string, ignoreRoomNameEvent = false): string {
3401
+ if (!ignoreRoomNameEvent) {
3402
+ // check for an alias, if any. for now, assume first alias is the
3403
+ // official one.
3404
+ const mRoomName = this.currentState.getStateEvents(EventType.RoomName, "");
3405
+ if (mRoomName?.getContent().name) {
3406
+ return this.roomNameGenerator({
3407
+ type: RoomNameType.Actual,
3408
+ name: mRoomName.getContent().name,
3409
+ });
3410
+ }
3411
+ }
3412
+
3413
+ const alias = this.getCanonicalAlias();
3414
+ if (alias) {
3415
+ return this.roomNameGenerator({
3416
+ type: RoomNameType.Actual,
3417
+ name: alias,
3418
+ });
3419
+ }
3420
+
3421
+ const joinedMemberCount = this.currentState.getJoinedMemberCount();
3422
+ const invitedMemberCount = this.currentState.getInvitedMemberCount();
3423
+ // -1 because these numbers include the syncing user
3424
+ let inviteJoinCount = joinedMemberCount + invitedMemberCount - 1;
3425
+
3426
+ // get service members (e.g. helper bots) for exclusion
3427
+ const excludedUserIds = this.getFunctionalMembers();
3428
+
3429
+ // get members that are NOT ourselves and are actually in the room.
3430
+ let otherNames: string[] = [];
3431
+ if (this.summaryHeroes) {
3432
+ // if we have a summary, the member state events should be in the room state
3433
+ this.summaryHeroes.forEach((userId) => {
3434
+ // filter service members
3435
+ if (excludedUserIds.includes(userId)) {
3436
+ inviteJoinCount--;
3437
+ return;
3438
+ }
3439
+ const member = this.getMember(userId);
3440
+ otherNames.push(member ? member.name : userId);
3441
+ });
3442
+ } else {
3443
+ let otherMembers = this.currentState.getMembers().filter((m) => {
3444
+ return (
3445
+ m.userId !== userId &&
3446
+ (m.membership === KnownMembership.Invite || m.membership === KnownMembership.Join)
3447
+ );
3448
+ });
3449
+ otherMembers = otherMembers.filter(({ userId }) => {
3450
+ // filter service members
3451
+ if (excludedUserIds.includes(userId)) {
3452
+ inviteJoinCount--;
3453
+ return false;
3454
+ }
3455
+ return true;
3456
+ });
3457
+ // make sure members have stable order
3458
+ const collator = new Intl.Collator();
3459
+ otherMembers.sort((a, b) => collator.compare(a.userId, b.userId));
3460
+ // only 5 first members, immitate summaryHeroes
3461
+ otherMembers = otherMembers.slice(0, 5);
3462
+ otherNames = otherMembers.map((m) => m.name);
3463
+ }
3464
+
3465
+ if (inviteJoinCount) {
3466
+ return this.roomNameGenerator({
3467
+ type: RoomNameType.Generated,
3468
+ names: otherNames,
3469
+ count: inviteJoinCount,
3470
+ });
3471
+ }
3472
+
3473
+ const myMembership = this.getMyMembership();
3474
+ // if I have created a room and invited people through
3475
+ // 3rd party invites
3476
+ if (myMembership == KnownMembership.Join) {
3477
+ const thirdPartyInvites = this.currentState.getStateEvents(EventType.RoomThirdPartyInvite);
3478
+
3479
+ if (thirdPartyInvites?.length) {
3480
+ const thirdPartyNames = thirdPartyInvites.map((i) => {
3481
+ return i.getContent().display_name;
3482
+ });
3483
+
3484
+ return this.roomNameGenerator({
3485
+ type: RoomNameType.Generated,
3486
+ subtype: "Inviting",
3487
+ names: thirdPartyNames,
3488
+ count: thirdPartyNames.length + 1,
3489
+ });
3490
+ }
3491
+ }
3492
+
3493
+ // let's try to figure out who was here before
3494
+ let leftNames = otherNames;
3495
+ // if we didn't have heroes, try finding them in the room state
3496
+ if (!leftNames.length) {
3497
+ leftNames = this.currentState
3498
+ .getMembers()
3499
+ .filter((m) => {
3500
+ return (
3501
+ m.userId !== userId &&
3502
+ m.membership !== KnownMembership.Invite &&
3503
+ m.membership !== KnownMembership.Join
3504
+ );
3505
+ })
3506
+ .map((m) => m.name);
3507
+ }
3508
+
3509
+ let oldName: string | undefined;
3510
+ if (leftNames.length) {
3511
+ oldName = this.roomNameGenerator({
3512
+ type: RoomNameType.Generated,
3513
+ names: leftNames,
3514
+ count: leftNames.length + 1,
3515
+ });
3516
+ }
3517
+
3518
+ return this.roomNameGenerator({
3519
+ type: RoomNameType.EmptyRoom,
3520
+ oldName,
3521
+ });
3522
+ }
3523
+
3524
+ /**
3525
+ * When we receive a new visibility change event:
3526
+ *
3527
+ * - store this visibility change alongside the timeline, in case we
3528
+ * later need to apply it to an event that we haven't received yet;
3529
+ * - if we have already received the event whose visibility has changed,
3530
+ * patch it to reflect the visibility change and inform listeners.
3531
+ */
3532
+ private applyNewVisibilityEvent(event: MatrixEvent): void {
3533
+ const visibilityChange = event.asVisibilityChange();
3534
+ if (!visibilityChange) {
3535
+ // The event is ill-formed.
3536
+ return;
3537
+ }
3538
+
3539
+ // Ignore visibility change events that are not emitted by moderators.
3540
+ const userId = event.getSender();
3541
+ if (!userId) {
3542
+ return;
3543
+ }
3544
+ const isPowerSufficient =
3545
+ (EVENT_VISIBILITY_CHANGE_TYPE.name &&
3546
+ this.currentState.maySendStateEvent(EVENT_VISIBILITY_CHANGE_TYPE.name, userId)) ||
3547
+ (EVENT_VISIBILITY_CHANGE_TYPE.altName &&
3548
+ this.currentState.maySendStateEvent(EVENT_VISIBILITY_CHANGE_TYPE.altName, userId));
3549
+ if (!isPowerSufficient) {
3550
+ // Powerlevel is insufficient.
3551
+ return;
3552
+ }
3553
+
3554
+ // Record this change in visibility.
3555
+ // If the event is not in our timeline and we only receive it later,
3556
+ // we may need to apply the visibility change at a later date.
3557
+
3558
+ const visibilityEventsOnOriginalEvent = this.visibilityEvents.get(visibilityChange.eventId);
3559
+ if (visibilityEventsOnOriginalEvent) {
3560
+ // It would be tempting to simply erase the latest visibility change
3561
+ // but we need to record all of the changes in case the latest change
3562
+ // is ever redacted.
3563
+ //
3564
+ // In practice, linear scans through `visibilityEvents` should be fast.
3565
+ // However, to protect against a potential DoS attack, we limit the
3566
+ // number of iterations in this loop.
3567
+ let index = visibilityEventsOnOriginalEvent.length - 1;
3568
+ const min = Math.max(
3569
+ 0,
3570
+ visibilityEventsOnOriginalEvent.length - MAX_NUMBER_OF_VISIBILITY_EVENTS_TO_SCAN_THROUGH,
3571
+ );
3572
+ for (; index >= min; --index) {
3573
+ const target = visibilityEventsOnOriginalEvent[index];
3574
+ if (target.getTs() < event.getTs()) {
3575
+ break;
3576
+ }
3577
+ }
3578
+ if (index === -1) {
3579
+ visibilityEventsOnOriginalEvent.unshift(event);
3580
+ } else {
3581
+ visibilityEventsOnOriginalEvent.splice(index + 1, 0, event);
3582
+ }
3583
+ } else {
3584
+ this.visibilityEvents.set(visibilityChange.eventId, [event]);
3585
+ }
3586
+
3587
+ // Finally, let's check if the event is already in our timeline.
3588
+ // If so, we need to patch it and inform listeners.
3589
+
3590
+ const originalEvent = this.findEventById(visibilityChange.eventId);
3591
+ if (!originalEvent) {
3592
+ return;
3593
+ }
3594
+ originalEvent.applyVisibilityEvent(visibilityChange);
3595
+ }
3596
+
3597
+ private redactVisibilityChangeEvent(event: MatrixEvent): void {
3598
+ // Sanity checks.
3599
+ if (!event.isVisibilityEvent) {
3600
+ throw new Error("expected a visibility change event");
3601
+ }
3602
+ const relation = event.getRelation();
3603
+ const originalEventId = relation?.event_id;
3604
+ const visibilityEventsOnOriginalEvent = this.visibilityEvents.get(originalEventId!);
3605
+ if (!visibilityEventsOnOriginalEvent) {
3606
+ // No visibility changes on the original event.
3607
+ // In particular, this change event was not recorded,
3608
+ // most likely because it was ill-formed.
3609
+ return;
3610
+ }
3611
+ const index = visibilityEventsOnOriginalEvent.findIndex((change) => change.getId() === event.getId());
3612
+ if (index === -1) {
3613
+ // This change event was not recorded, most likely because
3614
+ // it was ill-formed.
3615
+ return;
3616
+ }
3617
+ // Remove visibility change.
3618
+ visibilityEventsOnOriginalEvent.splice(index, 1);
3619
+
3620
+ // If we removed the latest visibility change event, propagate changes.
3621
+ if (index === visibilityEventsOnOriginalEvent.length) {
3622
+ const originalEvent = this.findEventById(originalEventId!);
3623
+ if (!originalEvent) {
3624
+ return;
3625
+ }
3626
+ if (index === 0) {
3627
+ // We have just removed the only visibility change event.
3628
+ this.visibilityEvents.delete(originalEventId!);
3629
+ originalEvent.applyVisibilityEvent();
3630
+ } else {
3631
+ const newEvent = visibilityEventsOnOriginalEvent[visibilityEventsOnOriginalEvent.length - 1];
3632
+ const newVisibility = newEvent.asVisibilityChange();
3633
+ if (!newVisibility) {
3634
+ // Event is ill-formed.
3635
+ // This breaks our invariant.
3636
+ throw new Error("at this stage, visibility changes should be well-formed");
3637
+ }
3638
+ originalEvent.applyVisibilityEvent(newVisibility);
3639
+ }
3640
+ }
3641
+ }
3642
+
3643
+ /**
3644
+ * When we receive an event whose visibility has been altered by
3645
+ * a (more recent) visibility change event, patch the event in
3646
+ * place so that clients now not to display it.
3647
+ *
3648
+ * @param event - Any matrix event. If this event has at least one a
3649
+ * pending visibility change event, apply the latest visibility
3650
+ * change event.
3651
+ */
3652
+ private applyPendingVisibilityEvents(event: MatrixEvent): void {
3653
+ const visibilityEvents = this.visibilityEvents.get(event.getId()!);
3654
+ if (!visibilityEvents || visibilityEvents.length == 0) {
3655
+ // No pending visibility change in store.
3656
+ return;
3657
+ }
3658
+ const visibilityEvent = visibilityEvents[visibilityEvents.length - 1];
3659
+ const visibilityChange = visibilityEvent.asVisibilityChange();
3660
+ if (!visibilityChange) {
3661
+ return;
3662
+ }
3663
+ if (visibilityChange.visible) {
3664
+ // Events are visible by default, no need to apply a visibility change.
3665
+ // Note that we need to keep the visibility changes in `visibilityEvents`,
3666
+ // in case we later fetch an older visibility change event that is superseded
3667
+ // by `visibilityChange`.
3668
+ }
3669
+ if (visibilityEvent.getTs() < event.getTs()) {
3670
+ // Something is wrong, the visibility change cannot happen before the
3671
+ // event. Presumably an ill-formed event.
3672
+ return;
3673
+ }
3674
+ event.applyVisibilityEvent(visibilityChange);
3675
+ }
3676
+
3677
+ /**
3678
+ * Find when a client has gained thread capabilities by inspecting the oldest
3679
+ * threaded receipt
3680
+ * @returns the timestamp of the oldest threaded receipt
3681
+ */
3682
+ public getOldestThreadedReceiptTs(): number {
3683
+ return this.oldestThreadedReceiptTs;
3684
+ }
3685
+
3686
+ /**
3687
+ * Determines if the given user has read a particular event ID with the known
3688
+ * history of the room. This is not a definitive check as it relies only on
3689
+ * what is available to the room at the time of execution.
3690
+ *
3691
+ * @param userId - The user ID to check the read state of.
3692
+ * @param eventId - The event ID to check if the user read.
3693
+ * @returns true if the user has read the event, false otherwise.
3694
+ */
3695
+ public hasUserReadEvent(userId: string, eventId: string): boolean {
3696
+ return this.roomReceipts.hasUserReadEvent(userId, eventId);
3697
+ }
3698
+
3699
+ /**
3700
+ * Returns the most recent unthreaded receipt for a given user
3701
+ * @param userId - the MxID of the User
3702
+ * @returns an unthreaded Receipt. Can be undefined if receipts have been disabled
3703
+ * or a user chooses to use private read receipts (or we have simply not received
3704
+ * a receipt from this user yet).
3705
+ */
3706
+ public getLastUnthreadedReceiptFor(userId: string): Receipt | undefined {
3707
+ return this.unthreadedReceipts.get(userId);
3708
+ }
3709
+
3710
+ /**
3711
+ * This issue should also be addressed on synapse's side and is tracked as part
3712
+ * of https://github.com/matrix-org/synapse/issues/14837
3713
+ *
3714
+ *
3715
+ * We consider a room fully read if the current user has sent
3716
+ * the last event in the live timeline of that context and if the read receipt
3717
+ * we have on record matches.
3718
+ * This also detects all unread threads and applies the same logic to those
3719
+ * contexts
3720
+ */
3721
+ public fixupNotifications(userId: string): void {
3722
+ super.fixupNotifications(userId);
3723
+
3724
+ const unreadThreads = this.getThreads().filter(
3725
+ (thread) => this.getThreadUnreadNotificationCount(thread.id, NotificationCountType.Total) > 0,
3726
+ );
3727
+
3728
+ for (const thread of unreadThreads) {
3729
+ thread.fixupNotifications(userId);
3730
+ }
3731
+ }
3732
+
3733
+ /**
3734
+ * Determine the order of two events in this room.
3735
+ *
3736
+ * In principle this should use the same order as the server, but in practice
3737
+ * this is difficult for events that were not received over the Sync API. See
3738
+ * MSC4033 for details.
3739
+ *
3740
+ * This implementation leans on the order of events within their timelines, and
3741
+ * falls back to comparing event timestamps when they are in different
3742
+ * timelines.
3743
+ *
3744
+ * See https://github.com/matrix-org/matrix-js-sdk/issues/3325 for where we are
3745
+ * tracking the work to fix this.
3746
+ *
3747
+ * @param leftEventId - the id of the first event
3748
+ * @param rightEventId - the id of the second event
3749
+
3750
+ * @returns -1 if left \< right, 1 if left \> right, 0 if left == right, null if
3751
+ * we can't tell (because we can't find the events).
3752
+ */
3753
+ public compareEventOrdering(leftEventId: string, rightEventId: string): number | null {
3754
+ return compareEventOrdering(this, leftEventId, rightEventId);
3755
+ }
3756
+
3757
+ /**
3758
+ * Return true if this room has an `m.room.encryption` state event.
3759
+ *
3760
+ * If this returns `true`, events sent to this room should be encrypted (and `MatrixClient.sendEvent` and friends
3761
+ * will encrypt outgoing events).
3762
+ */
3763
+ public hasEncryptionStateEvent(): boolean {
3764
+ return Boolean(
3765
+ this.getLiveTimeline().getState(EventTimeline.FORWARDS)?.getStateEvents(EventType.RoomEncryption, ""),
3766
+ );
3767
+ }
3768
+ }
3769
+
3770
+ // a map from current event status to a list of allowed next statuses
3771
+ const ALLOWED_TRANSITIONS: Record<EventStatus, EventStatus[]> = {
3772
+ [EventStatus.ENCRYPTING]: [EventStatus.SENDING, EventStatus.NOT_SENT, EventStatus.CANCELLED],
3773
+ [EventStatus.SENDING]: [EventStatus.ENCRYPTING, EventStatus.QUEUED, EventStatus.NOT_SENT, EventStatus.SENT],
3774
+ [EventStatus.QUEUED]: [EventStatus.SENDING, EventStatus.NOT_SENT, EventStatus.CANCELLED],
3775
+ [EventStatus.SENT]: [],
3776
+ [EventStatus.NOT_SENT]: [EventStatus.SENDING, EventStatus.QUEUED, EventStatus.CANCELLED],
3777
+ [EventStatus.CANCELLED]: [],
3778
+ };
3779
+
3780
+ export enum RoomNameType {
3781
+ EmptyRoom,
3782
+ Generated,
3783
+ Actual,
3784
+ }
3785
+
3786
+ export interface EmptyRoomNameState {
3787
+ type: RoomNameType.EmptyRoom;
3788
+ oldName?: string;
3789
+ }
3790
+
3791
+ export interface GeneratedRoomNameState {
3792
+ type: RoomNameType.Generated;
3793
+ subtype?: "Inviting";
3794
+ names: string[];
3795
+ count: number;
3796
+ }
3797
+
3798
+ export interface ActualRoomNameState {
3799
+ type: RoomNameType.Actual;
3800
+ name: string;
3801
+ }
3802
+
3803
+ export type RoomNameState = EmptyRoomNameState | GeneratedRoomNameState | ActualRoomNameState;
3804
+
3805
+ // Can be overriden by IMatrixClientCreateOpts::memberNamesToRoomNameFn
3806
+ function memberNamesToRoomName(names: string[], count: number): string {
3807
+ const countWithoutMe = count - 1;
3808
+ if (!names.length) {
3809
+ return "Empty room";
3810
+ } else if (names.length === 1 && countWithoutMe <= 1) {
3811
+ return names[0];
3812
+ } else if (names.length === 2 && countWithoutMe <= 2) {
3813
+ return `${names[0]} and ${names[1]}`;
3814
+ } else {
3815
+ const plural = countWithoutMe > 1;
3816
+ if (plural) {
3817
+ return `${names[0]} and ${countWithoutMe} others`;
3818
+ } else {
3819
+ return `${names[0]} and 1 other`;
3820
+ }
3821
+ }
3822
+ }