@webex/plugin-meetings 3.0.0-beta.27 → 3.0.0-beta.271

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (362) hide show
  1. package/README.md +46 -8
  2. package/dist/annotation/annotation.types.js +7 -0
  3. package/dist/annotation/annotation.types.js.map +1 -0
  4. package/dist/annotation/constants.js +49 -0
  5. package/dist/annotation/constants.js.map +1 -0
  6. package/dist/annotation/index.js +342 -0
  7. package/dist/annotation/index.js.map +1 -0
  8. package/dist/breakouts/breakout.js +114 -14
  9. package/dist/breakouts/breakout.js.map +1 -1
  10. package/dist/breakouts/edit-lock-error.js +52 -0
  11. package/dist/breakouts/edit-lock-error.js.map +1 -0
  12. package/dist/breakouts/events.js +45 -0
  13. package/dist/breakouts/events.js.map +1 -0
  14. package/dist/breakouts/index.js +763 -31
  15. package/dist/breakouts/index.js.map +1 -1
  16. package/dist/breakouts/request.js +78 -0
  17. package/dist/breakouts/request.js.map +1 -0
  18. package/dist/breakouts/utils.js +67 -0
  19. package/dist/breakouts/utils.js.map +1 -0
  20. package/dist/common/errors/no-meeting-info.js +51 -0
  21. package/dist/common/errors/no-meeting-info.js.map +1 -0
  22. package/dist/common/errors/webex-errors.js +28 -7
  23. package/dist/common/errors/webex-errors.js.map +1 -1
  24. package/dist/common/logs/logger-proxy.js +1 -1
  25. package/dist/common/logs/logger-proxy.js.map +1 -1
  26. package/dist/common/queue.js +24 -9
  27. package/dist/common/queue.js.map +1 -1
  28. package/dist/config.js +5 -10
  29. package/dist/config.js.map +1 -1
  30. package/dist/constants.js +203 -28
  31. package/dist/constants.js.map +1 -1
  32. package/dist/controls-options-manager/enums.js +14 -2
  33. package/dist/controls-options-manager/enums.js.map +1 -1
  34. package/dist/controls-options-manager/index.js +109 -15
  35. package/dist/controls-options-manager/index.js.map +1 -1
  36. package/dist/controls-options-manager/types.js +7 -0
  37. package/dist/controls-options-manager/types.js.map +1 -0
  38. package/dist/controls-options-manager/util.js +309 -18
  39. package/dist/controls-options-manager/util.js.map +1 -1
  40. package/dist/index.js +112 -1
  41. package/dist/index.js.map +1 -1
  42. package/dist/interpretation/collection.js +23 -0
  43. package/dist/interpretation/collection.js.map +1 -0
  44. package/dist/interpretation/index.js +366 -0
  45. package/dist/interpretation/index.js.map +1 -0
  46. package/dist/interpretation/siLanguage.js +25 -0
  47. package/dist/interpretation/siLanguage.js.map +1 -0
  48. package/dist/locus-info/controlsUtils.js +91 -2
  49. package/dist/locus-info/controlsUtils.js.map +1 -1
  50. package/dist/locus-info/index.js +381 -62
  51. package/dist/locus-info/index.js.map +1 -1
  52. package/dist/locus-info/infoUtils.js +7 -1
  53. package/dist/locus-info/infoUtils.js.map +1 -1
  54. package/dist/locus-info/mediaSharesUtils.js +57 -1
  55. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  56. package/dist/locus-info/parser.js +224 -63
  57. package/dist/locus-info/parser.js.map +1 -1
  58. package/dist/locus-info/selfUtils.js +89 -14
  59. package/dist/locus-info/selfUtils.js.map +1 -1
  60. package/dist/media/index.js +58 -116
  61. package/dist/media/index.js.map +1 -1
  62. package/dist/media/properties.js +72 -123
  63. package/dist/media/properties.js.map +1 -1
  64. package/dist/meeting/in-meeting-actions.js +82 -2
  65. package/dist/meeting/in-meeting-actions.js.map +1 -1
  66. package/dist/meeting/index.js +3118 -2814
  67. package/dist/meeting/index.js.map +1 -1
  68. package/dist/meeting/locusMediaRequest.js +292 -0
  69. package/dist/meeting/locusMediaRequest.js.map +1 -0
  70. package/dist/meeting/muteState.js +230 -124
  71. package/dist/meeting/muteState.js.map +1 -1
  72. package/dist/meeting/request.js +256 -196
  73. package/dist/meeting/request.js.map +1 -1
  74. package/dist/meeting/util.js +601 -417
  75. package/dist/meeting/util.js.map +1 -1
  76. package/dist/meeting-info/index.js +70 -7
  77. package/dist/meeting-info/index.js.map +1 -1
  78. package/dist/meeting-info/meeting-info-v2.js +189 -51
  79. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  80. package/dist/meeting-info/util.js +1 -1
  81. package/dist/meeting-info/util.js.map +1 -1
  82. package/dist/meeting-info/utilv2.js +36 -36
  83. package/dist/meeting-info/utilv2.js.map +1 -1
  84. package/dist/meetings/collection.js +22 -0
  85. package/dist/meetings/collection.js.map +1 -1
  86. package/dist/meetings/index.js +394 -94
  87. package/dist/meetings/index.js.map +1 -1
  88. package/dist/meetings/meetings.types.js +7 -0
  89. package/dist/meetings/meetings.types.js.map +1 -0
  90. package/dist/meetings/request.js +2 -0
  91. package/dist/meetings/request.js.map +1 -1
  92. package/dist/meetings/util.js +71 -1
  93. package/dist/meetings/util.js.map +1 -1
  94. package/dist/member/index.js +49 -0
  95. package/dist/member/index.js.map +1 -1
  96. package/dist/member/types.js +25 -0
  97. package/dist/member/types.js.map +1 -0
  98. package/dist/member/util.js +121 -25
  99. package/dist/member/util.js.map +1 -1
  100. package/dist/members/collection.js +10 -0
  101. package/dist/members/collection.js.map +1 -1
  102. package/dist/members/index.js +86 -5
  103. package/dist/members/index.js.map +1 -1
  104. package/dist/members/request.js +106 -38
  105. package/dist/members/request.js.map +1 -1
  106. package/dist/members/types.js +15 -0
  107. package/dist/members/types.js.map +1 -0
  108. package/dist/members/util.js +316 -233
  109. package/dist/members/util.js.map +1 -1
  110. package/dist/metrics/constants.js +12 -5
  111. package/dist/metrics/constants.js.map +1 -1
  112. package/dist/metrics/index.js +1 -468
  113. package/dist/metrics/index.js.map +1 -1
  114. package/dist/multistream/mediaRequestManager.js +238 -49
  115. package/dist/multistream/mediaRequestManager.js.map +1 -1
  116. package/dist/multistream/receiveSlot.js +49 -16
  117. package/dist/multistream/receiveSlot.js.map +1 -1
  118. package/dist/multistream/receiveSlotManager.js +52 -34
  119. package/dist/multistream/receiveSlotManager.js.map +1 -1
  120. package/dist/multistream/remoteMedia.js +44 -18
  121. package/dist/multistream/remoteMedia.js.map +1 -1
  122. package/dist/multistream/remoteMediaGroup.js +60 -3
  123. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  124. package/dist/multistream/remoteMediaManager.js +209 -59
  125. package/dist/multistream/remoteMediaManager.js.map +1 -1
  126. package/dist/multistream/sendSlotManager.js +233 -0
  127. package/dist/multistream/sendSlotManager.js.map +1 -0
  128. package/dist/reachability/index.js +225 -59
  129. package/dist/reachability/index.js.map +1 -1
  130. package/dist/reachability/request.js +17 -8
  131. package/dist/reachability/request.js.map +1 -1
  132. package/dist/reconnection-manager/index.js +199 -154
  133. package/dist/reconnection-manager/index.js.map +1 -1
  134. package/dist/recording-controller/index.js +21 -2
  135. package/dist/recording-controller/index.js.map +1 -1
  136. package/dist/recording-controller/util.js +9 -8
  137. package/dist/recording-controller/util.js.map +1 -1
  138. package/dist/roap/index.js +23 -29
  139. package/dist/roap/index.js.map +1 -1
  140. package/dist/roap/request.js +112 -97
  141. package/dist/roap/request.js.map +1 -1
  142. package/dist/roap/turnDiscovery.js +96 -36
  143. package/dist/roap/turnDiscovery.js.map +1 -1
  144. package/dist/rtcMetrics/constants.js +12 -0
  145. package/dist/rtcMetrics/constants.js.map +1 -0
  146. package/dist/rtcMetrics/index.js +117 -0
  147. package/dist/rtcMetrics/index.js.map +1 -0
  148. package/dist/statsAnalyzer/index.js +51 -34
  149. package/dist/statsAnalyzer/index.js.map +1 -1
  150. package/dist/statsAnalyzer/mqaUtil.js +6 -6
  151. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  152. package/dist/types/annotation/annotation.types.d.ts +42 -0
  153. package/dist/types/annotation/constants.d.ts +31 -0
  154. package/dist/types/annotation/index.d.ts +117 -0
  155. package/dist/types/breakouts/edit-lock-error.d.ts +15 -0
  156. package/dist/types/breakouts/events.d.ts +8 -0
  157. package/dist/types/breakouts/request.d.ts +22 -0
  158. package/dist/types/breakouts/utils.d.ts +15 -0
  159. package/dist/types/common/errors/no-meeting-info.d.ts +14 -0
  160. package/dist/types/common/errors/webex-errors.d.ts +13 -1
  161. package/dist/types/common/queue.d.ts +9 -7
  162. package/dist/types/config.d.ts +1 -6
  163. package/dist/types/constants.d.ts +161 -21
  164. package/dist/types/controls-options-manager/enums.d.ts +11 -1
  165. package/dist/types/controls-options-manager/index.d.ts +17 -1
  166. package/dist/types/controls-options-manager/types.d.ts +43 -0
  167. package/dist/types/controls-options-manager/util.d.ts +1 -7
  168. package/dist/types/index.d.ts +6 -4
  169. package/dist/types/interpretation/collection.d.ts +5 -0
  170. package/dist/types/interpretation/index.d.ts +5 -0
  171. package/dist/types/interpretation/siLanguage.d.ts +5 -0
  172. package/dist/types/locus-info/index.d.ts +57 -4
  173. package/dist/types/locus-info/parser.d.ts +65 -6
  174. package/dist/types/media/index.d.ts +2 -0
  175. package/dist/types/media/properties.d.ts +34 -48
  176. package/dist/types/meeting/in-meeting-actions.d.ts +82 -2
  177. package/dist/types/meeting/index.d.ts +345 -507
  178. package/dist/types/meeting/locusMediaRequest.d.ts +74 -0
  179. package/dist/types/meeting/muteState.d.ts +99 -23
  180. package/dist/types/meeting/request.d.ts +72 -43
  181. package/dist/types/meeting/util.d.ts +101 -1
  182. package/dist/types/meeting-info/index.d.ts +13 -1
  183. package/dist/types/meeting-info/meeting-info-v2.d.ts +31 -1
  184. package/dist/types/meetings/collection.d.ts +8 -0
  185. package/dist/types/meetings/index.d.ts +88 -12
  186. package/dist/types/meetings/meetings.types.d.ts +4 -0
  187. package/dist/types/member/index.d.ts +13 -0
  188. package/dist/types/member/types.d.ts +32 -0
  189. package/dist/types/members/collection.d.ts +5 -0
  190. package/dist/types/members/index.d.ts +35 -2
  191. package/dist/types/members/request.d.ts +73 -9
  192. package/dist/types/members/types.d.ts +24 -0
  193. package/dist/types/members/util.d.ts +209 -1
  194. package/dist/types/metrics/constants.d.ts +11 -4
  195. package/dist/types/metrics/index.d.ts +4 -119
  196. package/dist/types/multistream/mediaRequestManager.d.ts +73 -5
  197. package/dist/types/multistream/receiveSlot.d.ts +16 -12
  198. package/dist/types/multistream/receiveSlotManager.d.ts +19 -4
  199. package/dist/types/multistream/remoteMedia.d.ts +8 -29
  200. package/dist/types/multistream/remoteMediaGroup.d.ts +0 -9
  201. package/dist/types/multistream/remoteMediaManager.d.ts +46 -2
  202. package/dist/types/multistream/sendSlotManager.d.ts +61 -0
  203. package/dist/types/reachability/index.d.ts +61 -7
  204. package/dist/types/reachability/request.d.ts +7 -3
  205. package/dist/types/reconnection-manager/index.d.ts +9 -0
  206. package/dist/types/recording-controller/index.d.ts +15 -1
  207. package/dist/types/recording-controller/util.d.ts +5 -4
  208. package/dist/types/roap/request.d.ts +15 -11
  209. package/dist/types/roap/turnDiscovery.d.ts +18 -1
  210. package/dist/types/rtcMetrics/constants.d.ts +4 -0
  211. package/dist/types/rtcMetrics/index.d.ts +47 -0
  212. package/dist/types/statsAnalyzer/index.d.ts +6 -1
  213. package/package.json +23 -20
  214. package/src/annotation/annotation.types.ts +50 -0
  215. package/src/annotation/constants.ts +36 -0
  216. package/src/annotation/index.ts +328 -0
  217. package/src/breakouts/README.md +44 -14
  218. package/src/breakouts/breakout.ts +87 -9
  219. package/src/breakouts/edit-lock-error.ts +25 -0
  220. package/src/breakouts/events.ts +56 -0
  221. package/src/breakouts/index.ts +646 -18
  222. package/src/breakouts/request.ts +55 -0
  223. package/src/breakouts/utils.ts +57 -0
  224. package/src/common/errors/no-meeting-info.ts +24 -0
  225. package/src/common/errors/webex-errors.ts +27 -2
  226. package/src/common/logs/logger-proxy.ts +1 -1
  227. package/src/common/queue.ts +22 -8
  228. package/src/config.ts +4 -9
  229. package/src/constants.ts +184 -18
  230. package/src/controls-options-manager/enums.ts +12 -0
  231. package/src/controls-options-manager/index.ts +116 -21
  232. package/src/controls-options-manager/types.ts +59 -0
  233. package/src/controls-options-manager/util.ts +294 -14
  234. package/src/index.ts +40 -0
  235. package/src/interpretation/README.md +60 -0
  236. package/src/interpretation/collection.ts +19 -0
  237. package/src/interpretation/index.ts +332 -0
  238. package/src/interpretation/siLanguage.ts +18 -0
  239. package/src/locus-info/controlsUtils.ts +108 -0
  240. package/src/locus-info/index.ts +412 -59
  241. package/src/locus-info/infoUtils.ts +10 -2
  242. package/src/locus-info/mediaSharesUtils.ts +64 -0
  243. package/src/locus-info/parser.ts +231 -39
  244. package/src/locus-info/selfUtils.ts +81 -5
  245. package/src/media/index.ts +100 -122
  246. package/src/media/properties.ts +85 -108
  247. package/src/meeting/in-meeting-actions.ts +163 -3
  248. package/src/meeting/index.ts +2526 -2309
  249. package/src/meeting/locusMediaRequest.ts +313 -0
  250. package/src/meeting/muteState.ts +229 -131
  251. package/src/meeting/request.ts +172 -121
  252. package/src/meeting/util.ts +588 -394
  253. package/src/meeting-info/index.ts +79 -8
  254. package/src/meeting-info/meeting-info-v2.ts +168 -14
  255. package/src/meeting-info/util.ts +1 -1
  256. package/src/meeting-info/utilv2.ts +23 -23
  257. package/src/meetings/collection.ts +20 -0
  258. package/src/meetings/index.ts +428 -108
  259. package/src/meetings/meetings.types.ts +12 -0
  260. package/src/meetings/request.ts +2 -0
  261. package/src/meetings/util.ts +79 -4
  262. package/src/member/index.ts +49 -0
  263. package/src/member/types.ts +38 -0
  264. package/src/member/util.ts +127 -25
  265. package/src/members/collection.ts +8 -0
  266. package/src/members/index.ts +107 -6
  267. package/src/members/request.ts +97 -17
  268. package/src/members/types.ts +28 -0
  269. package/src/members/util.ts +319 -240
  270. package/src/metrics/constants.ts +11 -4
  271. package/src/metrics/index.ts +1 -490
  272. package/src/multistream/mediaRequestManager.ts +289 -79
  273. package/src/multistream/receiveSlot.ts +55 -18
  274. package/src/multistream/receiveSlotManager.ts +46 -24
  275. package/src/multistream/remoteMedia.ts +27 -2
  276. package/src/multistream/remoteMediaGroup.ts +59 -0
  277. package/src/multistream/remoteMediaManager.ts +148 -30
  278. package/src/multistream/sendSlotManager.ts +170 -0
  279. package/src/reachability/index.ts +228 -37
  280. package/src/reachability/request.ts +17 -8
  281. package/src/reconnection-manager/index.ts +81 -54
  282. package/src/recording-controller/index.ts +20 -3
  283. package/src/recording-controller/util.ts +26 -9
  284. package/src/roap/index.ts +23 -30
  285. package/src/roap/request.ts +100 -104
  286. package/src/roap/turnDiscovery.ts +51 -25
  287. package/src/rtcMetrics/constants.ts +3 -0
  288. package/src/rtcMetrics/index.ts +100 -0
  289. package/src/statsAnalyzer/index.ts +73 -35
  290. package/src/statsAnalyzer/mqaUtil.ts +8 -10
  291. package/test/integration/spec/converged-space-meetings.js +60 -3
  292. package/test/integration/spec/journey.js +320 -261
  293. package/test/integration/spec/space-meeting.js +76 -3
  294. package/test/unit/spec/annotation/index.ts +418 -0
  295. package/test/unit/spec/breakouts/breakout.ts +142 -24
  296. package/test/unit/spec/breakouts/edit-lock-error.ts +30 -0
  297. package/test/unit/spec/breakouts/events.ts +89 -0
  298. package/test/unit/spec/breakouts/index.ts +1488 -67
  299. package/test/unit/spec/breakouts/request.ts +104 -0
  300. package/test/unit/spec/breakouts/utils.js +72 -0
  301. package/test/unit/spec/common/queue.js +31 -2
  302. package/test/unit/spec/controls-options-manager/index.js +163 -0
  303. package/test/unit/spec/controls-options-manager/util.js +576 -60
  304. package/test/unit/spec/fixture/locus.js +1 -0
  305. package/test/unit/spec/interpretation/collection.ts +15 -0
  306. package/test/unit/spec/interpretation/index.ts +589 -0
  307. package/test/unit/spec/interpretation/siLanguage.ts +28 -0
  308. package/test/unit/spec/locus-info/controlsUtils.js +316 -43
  309. package/test/unit/spec/locus-info/index.js +1283 -33
  310. package/test/unit/spec/locus-info/infoUtils.js +37 -15
  311. package/test/unit/spec/locus-info/mediaSharesUtils.ts +32 -0
  312. package/test/unit/spec/locus-info/parser.js +62 -22
  313. package/test/unit/spec/locus-info/selfConstant.js +27 -4
  314. package/test/unit/spec/locus-info/selfUtils.js +208 -17
  315. package/test/unit/spec/media/index.ts +104 -37
  316. package/test/unit/spec/meeting/in-meeting-actions.ts +81 -3
  317. package/test/unit/spec/meeting/index.js +4307 -1938
  318. package/test/unit/spec/meeting/locusMediaRequest.ts +442 -0
  319. package/test/unit/spec/meeting/muteState.js +408 -208
  320. package/test/unit/spec/meeting/request.js +440 -45
  321. package/test/unit/spec/meeting/utils.js +679 -64
  322. package/test/unit/spec/meeting-info/index.js +293 -0
  323. package/test/unit/spec/meeting-info/meetinginfov2.js +517 -5
  324. package/test/unit/spec/meeting-info/utilv2.js +21 -0
  325. package/test/unit/spec/meetings/collection.js +14 -0
  326. package/test/unit/spec/meetings/index.js +1007 -177
  327. package/test/unit/spec/meetings/utils.js +206 -2
  328. package/test/unit/spec/member/index.js +58 -4
  329. package/test/unit/spec/member/util.js +479 -35
  330. package/test/unit/spec/members/index.js +319 -1
  331. package/test/unit/spec/members/request.js +206 -27
  332. package/test/unit/spec/members/utils.js +184 -0
  333. package/test/unit/spec/metrics/index.js +1 -50
  334. package/test/unit/spec/multistream/mediaRequestManager.ts +803 -162
  335. package/test/unit/spec/multistream/receiveSlot.ts +72 -13
  336. package/test/unit/spec/multistream/receiveSlotManager.ts +58 -28
  337. package/test/unit/spec/multistream/remoteMedia.ts +30 -0
  338. package/test/unit/spec/multistream/remoteMediaGroup.ts +266 -0
  339. package/test/unit/spec/multistream/remoteMediaManager.ts +326 -0
  340. package/test/unit/spec/multistream/sendSlotManager.ts +242 -0
  341. package/test/unit/spec/reachability/index.ts +549 -9
  342. package/test/unit/spec/reachability/request.js +68 -0
  343. package/test/unit/spec/reconnection-manager/index.js +84 -9
  344. package/test/unit/spec/recording-controller/index.js +294 -218
  345. package/test/unit/spec/recording-controller/util.js +223 -96
  346. package/test/unit/spec/roap/index.ts +31 -51
  347. package/test/unit/spec/roap/request.ts +203 -85
  348. package/test/unit/spec/roap/turnDiscovery.ts +48 -13
  349. package/test/unit/spec/rtcMetrics/index.ts +68 -0
  350. package/test/unit/spec/stats-analyzer/index.js +29 -2
  351. package/test/utils/integrationTestUtils.js +46 -0
  352. package/test/utils/testUtils.js +0 -52
  353. package/dist/meeting/effectsState.js +0 -262
  354. package/dist/meeting/effectsState.js.map +0 -1
  355. package/dist/metrics/config.js +0 -299
  356. package/dist/metrics/config.js.map +0 -1
  357. package/dist/types/meeting/effectsState.d.ts +0 -42
  358. package/dist/types/metrics/config.d.ts +0 -178
  359. package/src/index.js +0 -16
  360. package/src/meeting/effectsState.ts +0 -211
  361. package/src/metrics/config.ts +0 -495
  362. package/test/unit/spec/meeting/effectsState.js +0 -285
@@ -1,4 +1,4 @@
1
- import {SELF_ROLES, DISPLAY_HINTS} from '../constants';
1
+ import {SELF_ROLES, DISPLAY_HINTS, INTERSTITIAL_DISPLAY_HINTS} from '../constants';
2
2
 
3
3
  const InfoUtils: any = {};
4
4
 
@@ -9,7 +9,15 @@ InfoUtils.parse = (info, roles, isJoined = true) => {
9
9
  coHost: InfoUtils.parseCoHost(info),
10
10
  };
11
11
 
12
- let userDisplayHints = isJoined ? {...parsed.policy} : {};
12
+ let userDisplayHints = isJoined
13
+ ? {...parsed.policy}
14
+ : {
15
+ ...Object.fromEntries(
16
+ Object.entries(parsed.policy).filter(([hint]) =>
17
+ INTERSTITIAL_DISPLAY_HINTS.includes(hint)
18
+ )
19
+ ),
20
+ };
13
21
 
14
22
  if (roles.includes(SELF_ROLES.COHOST)) {
15
23
  userDisplayHints = {...userDisplayHints, ...parsed.coHost};
@@ -13,6 +13,10 @@ MediaSharesUtils.parse = (mediaShares: object) => {
13
13
  content: {
14
14
  beneficiaryId: MediaSharesUtils.getContentBeneficiaryId(mediaShares),
15
15
  disposition: MediaSharesUtils.getContentDisposition(mediaShares),
16
+ annotation: MediaSharesUtils.getContentAnnotation(mediaShares),
17
+ url: MediaSharesUtils.getContentUrl(mediaShares),
18
+ shareInstanceId: MediaSharesUtils.getShareInstanceId(mediaShares),
19
+ deviceUrlSharing: MediaSharesUtils.getContentBeneficiaryDeviceUrl(mediaShares),
16
20
  },
17
21
  whiteboard: {
18
22
  beneficiaryId: MediaSharesUtils.getWhiteboardBeneficiaryId(mediaShares),
@@ -140,6 +144,66 @@ MediaSharesUtils.getContentBeneficiaryId = (mediaShares: object) => {
140
144
  return contentFloor.beneficiary.id;
141
145
  };
142
146
 
147
+ /**
148
+ * get live annotation is sharing from media shares (content)
149
+ * @param {Object} mediaShares
150
+ * @returns {Object}
151
+ */
152
+ MediaSharesUtils.getContentAnnotation = (mediaShares: object) => {
153
+ const extractContent = MediaSharesUtils.extractContent(mediaShares);
154
+
155
+ if (!extractContent || !extractContent.annotation) {
156
+ return undefined;
157
+ }
158
+
159
+ return extractContent.annotation;
160
+ };
161
+
162
+ /**
163
+ * get url is sharing from media shares (content)
164
+ * @param {Object} mediaShares
165
+ * @returns {Object}
166
+ */
167
+ MediaSharesUtils.getContentUrl = (mediaShares: object) => {
168
+ const extractContent = MediaSharesUtils.extractContent(mediaShares);
169
+
170
+ if (!extractContent || !extractContent.url) {
171
+ return undefined;
172
+ }
173
+
174
+ return extractContent.url;
175
+ };
176
+
177
+ /**
178
+ * get shareInstanceId is sharing from media shares (content)
179
+ * @param {Object} mediaShares
180
+ * @returns {Object}
181
+ */
182
+ MediaSharesUtils.getShareInstanceId = (mediaShares: object) => {
183
+ const extractContent = MediaSharesUtils.extractContent(mediaShares);
184
+
185
+ if (!extractContent || !extractContent.floor || !extractContent.floor.shareInstanceId) {
186
+ return undefined;
187
+ }
188
+
189
+ return extractContent.floor.shareInstanceId;
190
+ };
191
+
192
+ /**
193
+ * get deviceUrl that is requesting the floor for media shares (content)
194
+ * @param {Object} mediaShares
195
+ * @returns {Object}
196
+ */
197
+ MediaSharesUtils.getContentBeneficiaryDeviceUrl = (mediaShares: object) => {
198
+ const contentFloor = MediaSharesUtils.extractContentFloor(mediaShares);
199
+
200
+ if (!contentFloor || !contentFloor.beneficiary || !contentFloor.beneficiary.deviceUrl) {
201
+ return null;
202
+ }
203
+
204
+ return contentFloor.beneficiary.deviceUrl;
205
+ };
206
+
143
207
  /**
144
208
  * get who is sharing from media shares (whiteboard)
145
209
  * @param {Object} mediaShares
@@ -1,8 +1,29 @@
1
1
  import {difference} from 'lodash';
2
2
 
3
- import SimpleQueue from '../common/queue';
3
+ import SortedQueue from '../common/queue';
4
4
  import LoggerProxy from '../common/logs/logger-proxy';
5
5
 
6
+ import Metrics from '../metrics';
7
+ import BEHAVIORAL_METRICS from '../metrics/constants';
8
+
9
+ const MAX_OOO_DELTA_COUNT = 5; // when we receive an out-of-order delta and the queue builds up to MAX_OOO_DELTA_COUNT, we do a sync with Locus
10
+ const OOO_DELTA_WAIT_TIME = 10000; // [ms] minimum wait time before we do a sync if we get out-of-order deltas
11
+ const OOO_DELTA_WAIT_TIME_RANDOM_DELAY = 5000; // [ms] max random delay added to OOO_DELTA_WAIT_TIME
12
+
13
+ type LocusDeltaDto = {
14
+ baseSequence: {
15
+ rangeStart: number;
16
+ rangeEnd: number;
17
+ entries: number[];
18
+ };
19
+ sequence: {
20
+ rangeStart: number;
21
+ rangeEnd: number;
22
+ entries: number[];
23
+ };
24
+ syncUrl: string;
25
+ };
26
+
6
27
  /**
7
28
  * Locus Delta Parser
8
29
  * @private
@@ -10,11 +31,11 @@ import LoggerProxy from '../common/logs/logger-proxy';
10
31
  */
11
32
  export default class Parser {
12
33
  // processing status
13
- static status = {
14
- IDLE: 'IDLE',
15
- PAUSED: 'PAUSED',
16
- WORKING: 'WORKING',
17
- };
34
+ status:
35
+ | 'IDLE' // not doing anything
36
+ | 'PAUSED' // paused, because we are doing a sync
37
+ | 'WORKING' // processing a delta event
38
+ | 'BLOCKED'; // received an out-of-order delta, so waiting for the missing one
18
39
 
19
40
  // loci comparison states
20
41
  static loci = {
@@ -24,21 +45,59 @@ export default class Parser {
24
45
  DESYNC: 'DESYNC',
25
46
  USE_INCOMING: 'USE_INCOMING',
26
47
  USE_CURRENT: 'USE_CURRENT',
48
+ WAIT: 'WAIT',
27
49
  ERROR: 'ERROR',
28
50
  };
29
51
 
30
- queue: any;
52
+ queue: SortedQueue<LocusDeltaDto>;
31
53
  workingCopy: any;
54
+ syncTimer: null | number | NodeJS.Timeout;
32
55
 
33
56
  /**
34
57
  * @constructs Parser
35
58
  */
36
59
  constructor() {
37
- this.queue = new SimpleQueue();
38
- // @ts-ignore - This is declared as static class member and again being initialized here from same
39
- this.status = Parser.status.IDLE;
60
+ const deltaCompareFunc = (left: LocusDeltaDto, right: LocusDeltaDto) => {
61
+ const {LT, GT} = Parser.loci;
62
+ const {extractComparisonState: extract} = Parser;
63
+
64
+ if (Parser.isSequenceEmpty(left)) {
65
+ return -1;
66
+ }
67
+ if (Parser.isSequenceEmpty(right)) {
68
+ return 1;
69
+ }
70
+ const result = extract(Parser.compareSequence(left.baseSequence, right.baseSequence));
71
+
72
+ if (result === LT) {
73
+ return -1;
74
+ }
75
+ if (result === GT) {
76
+ return 1;
77
+ }
78
+
79
+ return 0;
80
+ };
81
+
82
+ this.queue = new SortedQueue<LocusDeltaDto>(deltaCompareFunc);
83
+ this.status = 'IDLE';
40
84
  this.onDeltaAction = null;
41
85
  this.workingCopy = null;
86
+ this.syncTimer = null;
87
+ }
88
+
89
+ /**
90
+ * Returns a debug string representing a locus delta - useful for logging
91
+ *
92
+ * @param {LocusDeltaDto} locus Locus delta
93
+ * @returns {string}
94
+ */
95
+ static locus2string(locus: LocusDeltaDto) {
96
+ if (!locus.sequence?.entries) {
97
+ return 'invalid';
98
+ }
99
+
100
+ return locus.sequence.entries.length ? `seq=${locus.sequence.entries.at(-1)}` : 'empty';
42
101
  }
43
102
 
44
103
  /**
@@ -208,7 +267,7 @@ export default class Parser {
208
267
  * @returns {string} loci comparison state
209
268
  */
210
269
  private static compareDelta(current, incoming) {
211
- const {LT, GT, EQ, DESYNC, USE_INCOMING} = Parser.loci;
270
+ const {LT, GT, EQ, DESYNC, USE_INCOMING, WAIT} = Parser.loci;
212
271
 
213
272
  const {extractComparisonState: extract} = Parser;
214
273
  const {packComparisonResult: pack} = Parser;
@@ -228,6 +287,21 @@ export default class Parser {
228
287
  comparison = USE_INCOMING;
229
288
  break;
230
289
 
290
+ case LT:
291
+ if (extract(Parser.compareSequence(incoming.baseSequence, incoming.sequence)) === EQ) {
292
+ // special case where Locus sends a delta with baseSequence === sequence to trigger a sync,
293
+ // because the delta event is too large to be sent over mercury connection
294
+ comparison = DESYNC;
295
+ } else {
296
+ // the incoming locus has baseSequence from the future, so it is out-of-order,
297
+ // we are missing 1 or more locus that should be in front of it, we need to wait for it
298
+ comparison = WAIT;
299
+
300
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.LOCUS_DELTA_OUT_OF_ORDER, {
301
+ stack: new Error().stack,
302
+ });
303
+ }
304
+ break;
231
305
  default:
232
306
  comparison = DESYNC;
233
307
  }
@@ -235,6 +309,49 @@ export default class Parser {
235
309
  return pack(comparison, result);
236
310
  }
237
311
 
312
+ /**
313
+ * Compares Locus sequences - it should be called only for full Locus DTOs, not deltas
314
+ *
315
+ * @param {Types~Locus} current Current working copy
316
+ * @param {Types~Locus} incomingFullDto New Full Locus DTO
317
+ * @returns {string} either Parser.loci.USE_INCOMING or Parser.loci.USE_CURRENT
318
+ */
319
+ static compareFullDtoSequence(current, incomingFullDto) {
320
+ if (Parser.isSequenceEmpty(current) || Parser.isSequenceEmpty(incomingFullDto)) {
321
+ return Parser.loci.USE_INCOMING;
322
+ }
323
+
324
+ // the sequence.entries list will always contain at least 1 entry
325
+ // https://sqbu-github.cisco.com/WebExSquared/cloud-apps/wiki/Locus-Sequence-Comparison-Algorithm
326
+
327
+ return incomingFullDto.sequence.entries.slice(-1)[0] > current.sequence.entries.slice(-1)[0]
328
+ ? Parser.loci.USE_INCOMING
329
+ : Parser.loci.USE_CURRENT;
330
+ }
331
+
332
+ /**
333
+ * Returns true if the incoming full locus DTO is newer than the current working copy
334
+ *
335
+ * @param {Types~Locus} incomingFullDto New Full Locus DTO
336
+ * @returns {string} either Parser.loci.USE_INCOMING or Parser.loci.USE_CURRENT
337
+ */
338
+ isNewFullLocus(incomingFullDto) {
339
+ if (!Parser.isLoci(incomingFullDto)) {
340
+ LoggerProxy.logger.info('Locus-info:parser#isNewFullLocus --> Ignoring non-locus object.');
341
+
342
+ return false;
343
+ }
344
+
345
+ if (!this.workingCopy) {
346
+ // we don't have a working copy yet, so any full locus is better than nothing
347
+ return true;
348
+ }
349
+
350
+ const comparisonResult = Parser.compareFullDtoSequence(this.workingCopy, incomingFullDto);
351
+
352
+ return comparisonResult === Parser.loci.USE_INCOMING;
353
+ }
354
+
238
355
  /**
239
356
  * Compares Locus sequences
240
357
  * @param {Types~Locus} current Current working copy
@@ -393,17 +510,10 @@ export default class Parser {
393
510
  */
394
511
  isValidLocus(newLoci) {
395
512
  let isValid = false;
396
- const {IDLE} = Parser.status;
397
513
  const {isLoci} = Parser;
398
- // @ts-ignore
399
- const setStatus = (status) => {
400
- // @ts-ignore
401
- this.status = status;
402
- };
403
514
 
404
515
  // one or both objects are not locus delta events
405
516
  if (!isLoci(this.workingCopy) || !isLoci(newLoci)) {
406
- setStatus(IDLE);
407
517
  LoggerProxy.logger.info(
408
518
  'Locus-info:parser#processDeltaEvent --> Ignoring non-locus object. workingCopy:',
409
519
  this.workingCopy,
@@ -455,19 +565,25 @@ export default class Parser {
455
565
  * @returns {undefined}
456
566
  */
457
567
  nextEvent() {
458
- // @ts-ignore
459
- if (this.status === Parser.status.PAUSED) {
568
+ if (this.status === 'PAUSED') {
460
569
  LoggerProxy.logger.info('Locus-info:parser#nextEvent --> Locus parser paused.');
461
570
 
462
571
  return;
463
572
  }
464
573
 
574
+ if (this.status === 'BLOCKED') {
575
+ LoggerProxy.logger.info(
576
+ 'Locus-info:parser#nextEvent --> Locus parser blocked by out-of-order delta.'
577
+ );
578
+
579
+ return;
580
+ }
581
+
465
582
  // continue processing until queue is empty
466
583
  if (this.queue.size() > 0) {
467
584
  this.processDeltaEvent();
468
585
  } else {
469
- // @ts-ignore
470
- this.status = Parser.status.IDLE;
586
+ this.status = 'IDLE';
471
587
  }
472
588
  }
473
589
 
@@ -478,7 +594,7 @@ export default class Parser {
478
594
  * @param {Types~Locus} locus Locus delta
479
595
  * @returns {undefined}
480
596
  */
481
- // eslint-disable-next-line no-unused-vars
597
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
482
598
  onDeltaAction(action: string, locus) {}
483
599
 
484
600
  /**
@@ -489,15 +605,20 @@ export default class Parser {
489
605
  onDeltaEvent(loci) {
490
606
  // enqueue the new loci
491
607
  this.queue.enqueue(loci);
492
- // start processing events in the queue if idle
493
- // and a function handler is defined
494
- // @ts-ignore
495
- if (this.status === Parser.status.IDLE && this.onDeltaAction) {
496
- // Update status, ensure we only process one event at a time.
497
- // @ts-ignore
498
- this.status = Parser.status.WORKING;
499
608
 
500
- this.processDeltaEvent();
609
+ if (this.onDeltaAction) {
610
+ if (this.status === 'BLOCKED') {
611
+ if (this.queue.size() > MAX_OOO_DELTA_COUNT) {
612
+ this.triggerSync('queue too big, blocked on out-of-order delta');
613
+ } else {
614
+ this.processDeltaEvent();
615
+ }
616
+ } else if (this.status === 'IDLE') {
617
+ // Update status, ensure we only process one event at a time.
618
+ this.status = 'WORKING';
619
+
620
+ this.processDeltaEvent();
621
+ }
501
622
  }
502
623
  }
503
624
 
@@ -516,11 +637,55 @@ export default class Parser {
516
637
  * @returns {undefined}
517
638
  */
518
639
  pause() {
519
- // @ts-ignore
520
- this.status = Parser.status.PAUSED;
640
+ this.status = 'PAUSED';
521
641
  LoggerProxy.logger.info('Locus-info:parser#pause --> Locus parser paused.');
522
642
  }
523
643
 
644
+ /**
645
+ * Triggers a sync with Locus
646
+ *
647
+ * @param {string} reason used just for logging
648
+ * @returns {undefined}
649
+ */
650
+ private triggerSync(reason: string) {
651
+ LoggerProxy.logger.info(`Locus-info:parser#triggerSync --> doing sync, reason: ${reason}`);
652
+ this.stopSyncTimer();
653
+ this.pause();
654
+ this.onDeltaAction(Parser.loci.DESYNC, this.workingCopy);
655
+ }
656
+
657
+ /**
658
+ * Starts a timer with a random delay. When that timer expires we will do a sync.
659
+ *
660
+ * The main purpose of this timer is to handle a case when we get some out-of-order deltas,
661
+ * so we start waiting to receive the missing delta. If that delta never arrives, this timer
662
+ * will trigger a sync with Locus.
663
+ *
664
+ * @returns {undefined}
665
+ */
666
+ private startSyncTimer() {
667
+ if (this.syncTimer === null) {
668
+ const timeout = OOO_DELTA_WAIT_TIME + Math.random() * OOO_DELTA_WAIT_TIME_RANDOM_DELAY;
669
+
670
+ this.syncTimer = setTimeout(() => {
671
+ this.syncTimer = null;
672
+ this.triggerSync('timer expired, blocked on out-of-order delta');
673
+ }, timeout);
674
+ }
675
+ }
676
+
677
+ /**
678
+ * Stops the timer for triggering a sync
679
+ *
680
+ * @returns {undefined}
681
+ */
682
+ private stopSyncTimer() {
683
+ if (this.syncTimer !== null) {
684
+ clearTimeout(this.syncTimer);
685
+ this.syncTimer = null;
686
+ }
687
+ }
688
+
524
689
  /**
525
690
  * Processes next locus delta in the queue,
526
691
  * continues until the queue is empty
@@ -528,11 +693,13 @@ export default class Parser {
528
693
  * @returns {undefined}
529
694
  */
530
695
  processDeltaEvent() {
531
- const {DESYNC, USE_INCOMING} = Parser.loci;
696
+ const {DESYNC, USE_INCOMING, WAIT} = Parser.loci;
532
697
  const {extractComparisonState: extract} = Parser;
533
698
  const newLoci = this.queue.dequeue();
534
699
 
535
700
  if (!this.isValidLocus(newLoci)) {
701
+ this.nextEvent();
702
+
536
703
  return;
537
704
  }
538
705
 
@@ -543,6 +710,8 @@ export default class Parser {
543
710
  // for full debugging.
544
711
  LoggerProxy.logger.debug(`Locus-info:parser#processDeltaEvent --> Locus Debug: ${result}`);
545
712
 
713
+ let needToWait = false;
714
+
546
715
  if (lociComparison === DESYNC) {
547
716
  // wait for desync response
548
717
  this.pause();
@@ -551,15 +720,39 @@ export default class Parser {
551
720
  // Note: The working copy of parser gets updated in .onFullLocus()
552
721
  // and here when USE_INCOMING locus.
553
722
  this.workingCopy = newLoci;
723
+ } else if (lociComparison === WAIT) {
724
+ // we've taken newLoci from the front of the queue, so put it back there as we have to wait
725
+ // for the one that should be in front of it, before we can process it
726
+ this.queue.enqueue(newLoci);
727
+ needToWait = true;
728
+ }
729
+
730
+ if (needToWait) {
731
+ this.status = 'BLOCKED';
732
+ this.startSyncTimer();
733
+ } else {
734
+ this.stopSyncTimer();
735
+
736
+ if (this.status === 'BLOCKED') {
737
+ // we are not blocked anymore
738
+ this.status = 'WORKING';
739
+
740
+ LoggerProxy.logger.info(
741
+ `Locus-info:parser#processDeltaEvent --> received delta that we were waiting for ${Parser.locus2string(
742
+ newLoci
743
+ )}, not blocked anymore`
744
+ );
745
+ }
554
746
  }
555
747
 
556
748
  if (this.onDeltaAction) {
557
749
  LoggerProxy.logger.info(
558
- `Locus-info:parser#processDeltaEvent --> Locus Delta Action: ${lociComparison}`
750
+ `Locus-info:parser#processDeltaEvent --> Locus Delta ${Parser.locus2string(
751
+ newLoci
752
+ )}, Action: ${lociComparison}`
559
753
  );
560
754
 
561
- // eslint-disable-next-line no-useless-call
562
- this.onDeltaAction.call(this, lociComparison, newLoci);
755
+ this.onDeltaAction(lociComparison, newLoci);
563
756
  }
564
757
 
565
758
  this.nextEvent();
@@ -571,8 +764,7 @@ export default class Parser {
571
764
  */
572
765
  resume() {
573
766
  LoggerProxy.logger.info('Locus-info:parser#resume --> Locus parser resumed.');
574
- // @ts-ignore
575
- this.status = Parser.status.WORKING;
767
+ this.status = 'WORKING';
576
768
  this.nextEvent();
577
769
  }
578
770
 
@@ -32,9 +32,11 @@ SelfUtils.parse = (self: any, deviceId: string) => {
32
32
  const pstnDevices = self.devices.filter((device) => PSTN_DEVICE_TYPE === device.deviceType);
33
33
 
34
34
  return {
35
+ remoteVideoMuted: SelfUtils.getRemoteVideoMuted(self),
35
36
  remoteMuted: SelfUtils.getRemoteMuted(self),
36
37
  unmuteAllowed: SelfUtils.getUnmuteAllowed(self),
37
38
  localAudioUnmuteRequested: SelfUtils.getLocalAudioUnmuteRequested(self),
39
+ localAudioUnmuteRequestedTimeStamp: SelfUtils.getLocalAudioUnmuteRequestedTimeStamp(self),
38
40
  localAudioUnmuteRequired: SelfUtils.getLocalAudioUnmuteRequired(self),
39
41
  lastModified: SelfUtils.getLastModified(self),
40
42
  modifiedBy: SelfUtils.getModifiedBy(self),
@@ -61,14 +63,18 @@ SelfUtils.parse = (self: any, deviceId: string) => {
61
63
  layout: SelfUtils.getLayout(self),
62
64
  canNotViewTheParticipantList: SelfUtils.canNotViewTheParticipantList(self),
63
65
  isSharingBlocked: SelfUtils.isSharingBlocked(self),
64
- breakoutSessions: SelfUtils.getBreakouts(self),
66
+ breakoutSessions: SelfUtils.getBreakoutSessions(self),
67
+ breakout: SelfUtils.getBreakout(self),
68
+ interpretation: SelfUtils.getInterpretation(self),
65
69
  };
66
70
  }
67
71
 
68
72
  return null;
69
73
  };
70
74
 
71
- SelfUtils.getBreakouts = (self) => self?.controls?.breakout?.sessions;
75
+ SelfUtils.getBreakoutSessions = (self) => self?.controls?.breakout?.sessions;
76
+ SelfUtils.getBreakout = (self) => self?.controls?.breakout;
77
+ SelfUtils.getInterpretation = (self) => self?.controls?.interpretation;
72
78
 
73
79
  SelfUtils.getLayout = (self) =>
74
80
  Array.isArray(self?.controls?.layouts) ? self.controls.layouts[0].type : undefined;
@@ -93,6 +99,7 @@ SelfUtils.getSelves = (oldSelf, newSelf, deviceId) => {
93
99
 
94
100
  updates.isUserUnadmitted = SelfUtils.isUserUnadmitted(current);
95
101
  updates.isUserAdmitted = SelfUtils.isUserAdmitted(previous, current);
102
+ updates.isVideoMutedByOthersChanged = SelfUtils.videoMutedByOthersChanged(previous, current);
96
103
  updates.isMutedByOthersChanged = SelfUtils.mutedByOthersChanged(previous, current);
97
104
  updates.localAudioUnmuteRequestedByServer = SelfUtils.localAudioUnmuteRequestedByServer(
98
105
  previous,
@@ -103,6 +110,7 @@ SelfUtils.getSelves = (oldSelf, newSelf, deviceId) => {
103
110
  current
104
111
  );
105
112
  updates.moderatorChanged = SelfUtils.moderatorChanged(previous, current);
113
+ updates.isRolesChanged = SelfUtils.isRolesChanged(previous, current);
106
114
  updates.isMediaInactiveOrReleased = SelfUtils.wasMediaInactiveOrReleased(previous, current);
107
115
  updates.isUserObserving = SelfUtils.isDeviceObserving(previous, current);
108
116
  updates.layoutChanged = SelfUtils.layoutChanged(previous, current);
@@ -119,6 +127,7 @@ SelfUtils.getSelves = (oldSelf, newSelf, deviceId) => {
119
127
  previous?.canNotViewTheParticipantList !== current.canNotViewTheParticipantList;
120
128
  updates.isSharingBlockedChanged = previous?.isSharingBlocked !== current.isSharingBlocked;
121
129
  updates.breakoutsChanged = SelfUtils.breakoutsChanged(previous, current);
130
+ updates.interpretationChanged = SelfUtils.interpretationChanged(previous, current);
122
131
 
123
132
  return {
124
133
  previous,
@@ -139,13 +148,16 @@ SelfUtils.isJoined = (self: any) => self?.state === _JOINED_;
139
148
  *
140
149
  * @param {Self} previous - Previous self state
141
150
  * @param {Self} current - Current self state [per event]
142
- * @returns {boolean} - If the MEeting Layout Controls Layout has changed.
151
+ * @returns {boolean} - If the Meeting Layout Controls Layout has changed.
143
152
  */
144
153
  SelfUtils.layoutChanged = (previous: any, current: any) =>
145
154
  current?.layout && previous?.layout !== current?.layout;
146
155
 
147
156
  SelfUtils.breakoutsChanged = (previous, current) =>
148
- !isEqual(previous?.breakoutSessions, current?.breakoutSessions);
157
+ !isEqual(previous?.breakoutSessions, current?.breakoutSessions) && !!current?.breakout;
158
+
159
+ SelfUtils.interpretationChanged = (previous, current) =>
160
+ !isEqual(previous?.interpretation, current?.interpretation) && !!current?.interpretation;
149
161
 
150
162
  SelfUtils.isMediaInactive = (previous, current) => {
151
163
  if (
@@ -236,6 +248,19 @@ SelfUtils.getSelfIdentity = (self: any) => {
236
248
  return self.person.id;
237
249
  };
238
250
 
251
+ /**
252
+ * get the "remote video mute" property from the self object
253
+ * @param {Object} self
254
+ * @returns {Boolean}
255
+ */
256
+ SelfUtils.getRemoteVideoMuted = (self: any) => {
257
+ if (!self || !self.controls || !self.controls.video) {
258
+ return null;
259
+ }
260
+
261
+ return self.controls.video.muted;
262
+ };
263
+
239
264
  /**
240
265
  * get the "remote mute" property from the self object
241
266
  * @param {Object} self
@@ -251,6 +276,10 @@ SelfUtils.getRemoteMuted = (self: any) => {
251
276
 
252
277
  SelfUtils.getLocalAudioUnmuteRequested = (self) => !!self?.controls?.audio?.requestedToUnmute;
253
278
 
279
+ // requestedToUnmute timestamp
280
+ SelfUtils.getLocalAudioUnmuteRequestedTimeStamp = (self) =>
281
+ Date.parse(self?.controls?.audio?.lastModifiedRequestedToUnmute) || 0;
282
+
254
283
  SelfUtils.getUnmuteAllowed = (self) => {
255
284
  if (!self || !self.controls || !self.controls.audio) {
256
285
  return null;
@@ -275,6 +304,7 @@ SelfUtils.getStatus = (status) => ({
275
304
  SelfUtils.wasMediaInactiveOrReleased = (oldSelf: any = {}, changedSelf: any) =>
276
305
  oldSelf.joinedWith &&
277
306
  oldSelf.joinedWith.state === _JOINED_ &&
307
+ changedSelf.joinedWith &&
278
308
  changedSelf.joinedWith.state === _LEFT_ &&
279
309
  (changedSelf.joinedWith.reason === MEETING_END_REASON.INACTIVE ||
280
310
  changedSelf.joinedWith.reason === MEETING_END_REASON.MEDIA_RELEASED);
@@ -319,6 +349,20 @@ SelfUtils.moderatorChanged = (oldSelf, changedSelf) => {
319
349
  return oldSelf.moderator !== changedSelf.moderator;
320
350
  };
321
351
 
352
+ /**
353
+ * determine whether the roles of self is changed or not
354
+ * @param {Object} oldSelf
355
+ * @param {Object} changedSelf
356
+ * @returns {Boolean}
357
+ */
358
+ SelfUtils.isRolesChanged = (oldSelf, changedSelf) => {
359
+ if (!changedSelf) {
360
+ // no new self means no change
361
+ return false;
362
+ }
363
+
364
+ return !isEqual(oldSelf?.roles, changedSelf?.roles);
365
+ };
322
366
  /**
323
367
  * @param {Object} oldSelf
324
368
  * @param {Object} changedSelf
@@ -351,6 +395,25 @@ SelfUtils.isUserAdmitted = (oldSelf: object, changedSelf: object) => {
351
395
  return SelfUtils.isLocusUserUnadmitted(oldSelf) && SelfUtils.isLocusUserAdmitted(changedSelf);
352
396
  };
353
397
 
398
+ SelfUtils.videoMutedByOthersChanged = (oldSelf, changedSelf) => {
399
+ if (!changedSelf) {
400
+ throw new ParameterError(
401
+ 'New self must be defined to determine if self was video muted by others.'
402
+ );
403
+ }
404
+
405
+ if (!oldSelf || oldSelf.remoteVideoMuted === null) {
406
+ if (changedSelf.remoteVideoMuted) {
407
+ return true; // this happens when host disables "Allow start video"
408
+ }
409
+
410
+ // we don't want to be sending the 'meeting:self:videoUnmutedByOthers' notification on meeting join
411
+ return false;
412
+ }
413
+
414
+ return oldSelf.remoteVideoMuted !== changedSelf.remoteVideoMuted;
415
+ };
416
+
354
417
  SelfUtils.mutedByOthersChanged = (oldSelf, changedSelf) => {
355
418
  if (!changedSelf) {
356
419
  throw new ParameterError('New self must be defined to determine if self was muted by others.');
@@ -379,7 +442,10 @@ SelfUtils.localAudioUnmuteRequestedByServer = (oldSelf: any = {}, changedSelf: a
379
442
  );
380
443
  }
381
444
 
382
- return changedSelf.localAudioUnmuteRequested && !oldSelf.localAudioUnmuteRequested;
445
+ return (
446
+ changedSelf.localAudioUnmuteRequested &&
447
+ changedSelf.localAudioUnmuteRequestedTimeStamp > oldSelf.localAudioUnmuteRequestedTimeStamp
448
+ );
383
449
  };
384
450
 
385
451
  SelfUtils.localAudioUnmuteRequiredByServer = (oldSelf: any = {}, changedSelf: any) => {
@@ -432,4 +498,14 @@ SelfUtils.getMediaStatus = (mediaSessions = []) => {
432
498
  return mediaStatus;
433
499
  };
434
500
 
501
+ SelfUtils.getReplacedBreakoutMoveId = (self: any, deviceId: string) => {
502
+ if (self && Array.isArray(self.devices)) {
503
+ const joinedDevice = self.devices.find((device) => deviceId === device.url);
504
+ if (Array.isArray(joinedDevice?.replaces)) {
505
+ return joinedDevice.replaces[0]?.breakoutMoveId;
506
+ }
507
+ }
508
+
509
+ return null;
510
+ };
435
511
  export default SelfUtils;