@dxos/plugin-thread 0.8.4-main.2e9d522 → 0.8.4-main.5ea62a8

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 (247) hide show
  1. package/dist/lib/browser/ChannelContainer-GHDNEZT2.mjs +16 -0
  2. package/dist/lib/browser/{ChatContainer-AIGIWAUG.mjs → ChatContainer-42ALOWUA.mjs} +4 -4
  3. package/dist/lib/browser/{app-graph-builder-N7BE5TYW.mjs → app-graph-builder-FHAOPO57.mjs} +5 -5
  4. package/dist/lib/browser/app-graph-builder-FHAOPO57.mjs.map +7 -0
  5. package/dist/lib/browser/{call-manager-NIPDYUVD.mjs → call-manager-EW2QC6Q4.mjs} +4 -4
  6. package/dist/lib/browser/call-manager-EW2QC6Q4.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-2I277V52.mjs +23 -0
  8. package/dist/lib/browser/{chunk-4OYAYON2.mjs.map → chunk-2I277V52.mjs.map} +3 -3
  9. package/dist/lib/browser/{chunk-ERWASUQK.mjs → chunk-2P3GVRHP.mjs} +38 -6
  10. package/dist/lib/{node-esm/chunk-25ABSYP2.mjs.map → browser/chunk-2P3GVRHP.mjs.map} +4 -4
  11. package/dist/lib/browser/{chunk-LGSDKXPF.mjs → chunk-4APCDRSH.mjs} +1 -1
  12. package/dist/lib/browser/{chunk-LGSDKXPF.mjs.map → chunk-4APCDRSH.mjs.map} +1 -1
  13. package/dist/lib/browser/chunk-H63NDV62.mjs +376 -0
  14. package/dist/lib/browser/chunk-H63NDV62.mjs.map +7 -0
  15. package/dist/lib/browser/chunk-J26C57ZY.mjs +548 -0
  16. package/dist/lib/browser/chunk-J26C57ZY.mjs.map +7 -0
  17. package/dist/lib/browser/{chunk-6NSMBO3B.mjs → chunk-KEXJGF2R.mjs} +4 -4
  18. package/dist/lib/browser/chunk-KEXJGF2R.mjs.map +7 -0
  19. package/dist/lib/browser/{chunk-GCLD5ESP.mjs → chunk-KTWYVC5X.mjs} +22 -12
  20. package/dist/lib/browser/chunk-KTWYVC5X.mjs.map +7 -0
  21. package/dist/lib/browser/{chunk-KWPRNHTK.mjs → chunk-TQJYDFBU.mjs} +232 -230
  22. package/dist/lib/browser/chunk-TQJYDFBU.mjs.map +7 -0
  23. package/dist/lib/browser/{chunk-ZE7KWN36.mjs → chunk-TUSBPMNU.mjs} +398 -174
  24. package/dist/lib/browser/chunk-TUSBPMNU.mjs.map +7 -0
  25. package/dist/lib/browser/index.mjs +18 -8
  26. package/dist/lib/browser/index.mjs.map +3 -3
  27. package/dist/lib/browser/{intent-resolver-JFGLZ6VI.mjs → intent-resolver-IB5GYU2D.mjs} +68 -9
  28. package/dist/lib/browser/intent-resolver-IB5GYU2D.mjs.map +7 -0
  29. package/dist/lib/browser/{markdown-GEXBVJYN.mjs → markdown-Z677YORA.mjs} +4 -4
  30. package/dist/lib/browser/markdown-Z677YORA.mjs.map +7 -0
  31. package/dist/lib/browser/meta.json +1 -1
  32. package/dist/lib/browser/{react-root-X4UIVGTX.mjs → react-root-PTTTW2MO.mjs} +9 -9
  33. package/dist/lib/browser/{react-surface-YCYEFBPM.mjs → react-surface-X2VB6P37.mjs} +9 -9
  34. package/dist/lib/browser/react-surface-X2VB6P37.mjs.map +7 -0
  35. package/dist/lib/browser/{settings-E32MTL7A.mjs → settings-GRKEID73.mjs} +2 -2
  36. package/dist/lib/browser/{state-H6CYONWE.mjs → state-2T4KDWBX.mjs} +2 -2
  37. package/dist/lib/browser/{state-H6CYONWE.mjs.map → state-2T4KDWBX.mjs.map} +2 -2
  38. package/dist/lib/browser/toolkit-KUPIIFC6.mjs +80 -0
  39. package/dist/lib/browser/toolkit-KUPIIFC6.mjs.map +7 -0
  40. package/dist/lib/browser/types/index.mjs +1 -1
  41. package/dist/lib/node-esm/{ChannelContainer-DET6T5N2.mjs → ChannelContainer-D5E2S6AI.mjs} +7 -7
  42. package/dist/lib/node-esm/{ChatContainer-IQUWU426.mjs → ChatContainer-4EGHKHIU.mjs} +4 -4
  43. package/dist/lib/node-esm/{app-graph-builder-VCW76Z6V.mjs → app-graph-builder-XEYWX5FC.mjs} +5 -5
  44. package/dist/lib/node-esm/app-graph-builder-XEYWX5FC.mjs.map +7 -0
  45. package/dist/lib/node-esm/{call-manager-D6O5ZJJ4.mjs → call-manager-N3KASRDQ.mjs} +4 -4
  46. package/dist/lib/node-esm/call-manager-N3KASRDQ.mjs.map +7 -0
  47. package/dist/lib/node-esm/{chunk-C3XFMUAY.mjs → chunk-CCE5LHBE.mjs} +232 -230
  48. package/dist/lib/node-esm/chunk-CCE5LHBE.mjs.map +7 -0
  49. package/dist/lib/node-esm/{chunk-25ABSYP2.mjs → chunk-CTZ75IFK.mjs} +38 -6
  50. package/dist/lib/node-esm/chunk-CTZ75IFK.mjs.map +7 -0
  51. package/dist/lib/node-esm/{chunk-IHDNQLLR.mjs → chunk-JR7KESLL.mjs} +398 -174
  52. package/dist/lib/node-esm/chunk-JR7KESLL.mjs.map +7 -0
  53. package/dist/lib/node-esm/chunk-NNKKNW57.mjs +549 -0
  54. package/dist/lib/node-esm/chunk-NNKKNW57.mjs.map +7 -0
  55. package/dist/lib/node-esm/{chunk-TDFHTCZZ.mjs → chunk-P6E247RF.mjs} +22 -12
  56. package/dist/lib/node-esm/chunk-P6E247RF.mjs.map +7 -0
  57. package/dist/lib/node-esm/{chunk-75RJ5DJI.mjs → chunk-PYTVBN5J.mjs} +1 -1
  58. package/dist/lib/node-esm/{chunk-75RJ5DJI.mjs.map → chunk-PYTVBN5J.mjs.map} +1 -1
  59. package/dist/lib/node-esm/{chunk-OU5BYIDO.mjs → chunk-S3D7DTBF.mjs} +4 -4
  60. package/dist/lib/node-esm/chunk-S3D7DTBF.mjs.map +7 -0
  61. package/dist/lib/node-esm/chunk-UHZQTPXU.mjs +377 -0
  62. package/dist/lib/node-esm/chunk-UHZQTPXU.mjs.map +7 -0
  63. package/dist/lib/node-esm/chunk-UJ2QNRLS.mjs +25 -0
  64. package/dist/lib/node-esm/{chunk-GW37AAM4.mjs.map → chunk-UJ2QNRLS.mjs.map} +3 -3
  65. package/dist/lib/node-esm/index.mjs +18 -8
  66. package/dist/lib/node-esm/index.mjs.map +3 -3
  67. package/dist/lib/node-esm/{intent-resolver-OXGNQ44Y.mjs → intent-resolver-VIMI5VLH.mjs} +68 -9
  68. package/dist/lib/node-esm/intent-resolver-VIMI5VLH.mjs.map +7 -0
  69. package/dist/lib/node-esm/{markdown-LIJZDK2J.mjs → markdown-FL4IXLRP.mjs} +4 -4
  70. package/dist/lib/node-esm/markdown-FL4IXLRP.mjs.map +7 -0
  71. package/dist/lib/node-esm/meta.json +1 -1
  72. package/dist/lib/node-esm/{react-root-M7J6PZVW.mjs → react-root-2HHRRUME.mjs} +9 -9
  73. package/dist/lib/node-esm/{react-surface-IRSEKM42.mjs → react-surface-OLN3XNND.mjs} +9 -9
  74. package/dist/lib/node-esm/react-surface-OLN3XNND.mjs.map +7 -0
  75. package/dist/lib/node-esm/{settings-SJ24YG7M.mjs → settings-F2ODVNNW.mjs} +2 -2
  76. package/dist/lib/node-esm/{state-D7WNDT3N.mjs → state-62TQKQVB.mjs} +2 -2
  77. package/dist/lib/node-esm/{state-D7WNDT3N.mjs.map → state-62TQKQVB.mjs.map} +2 -2
  78. package/dist/lib/node-esm/toolkit-KZIS7G36.mjs +81 -0
  79. package/dist/lib/node-esm/toolkit-KZIS7G36.mjs.map +7 -0
  80. package/dist/lib/node-esm/types/index.mjs +1 -1
  81. package/dist/types/src/ThreadPlugin.d.ts.map +1 -1
  82. package/dist/types/src/calls/call-manager.d.ts.map +1 -1
  83. package/dist/types/src/calls/call-swarm-synchronizer.d.ts.map +1 -1
  84. package/dist/types/src/calls/media-manager.d.ts +1 -1
  85. package/dist/types/src/calls/media-manager.d.ts.map +1 -1
  86. package/dist/types/src/calls/types.d.ts +1 -1
  87. package/dist/types/src/calls/types.d.ts.map +1 -1
  88. package/dist/types/src/calls/util/calls-service.d.ts +2 -2
  89. package/dist/types/src/calls/util/calls-service.d.ts.map +1 -1
  90. package/dist/types/src/calls/util/stub-media-streams.d.ts.map +1 -1
  91. package/dist/types/src/calls-service.stories.d.ts +10 -8
  92. package/dist/types/src/calls-service.stories.d.ts.map +1 -1
  93. package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
  94. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  95. package/dist/types/src/capabilities/call-manager.d.ts +1 -1
  96. package/dist/types/src/capabilities/call-manager.d.ts.map +1 -1
  97. package/dist/types/src/capabilities/capabilities.d.ts +2 -2
  98. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  99. package/dist/types/src/capabilities/index.d.ts +11 -10
  100. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  101. package/dist/types/src/capabilities/intent-resolver.d.ts +1 -1
  102. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  103. package/dist/types/src/capabilities/markdown.d.ts +1 -1
  104. package/dist/types/src/capabilities/markdown.d.ts.map +1 -1
  105. package/dist/types/src/capabilities/react-root.d.ts +2 -2
  106. package/dist/types/src/capabilities/react-surface.d.ts +1 -1
  107. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  108. package/dist/types/src/capabilities/settings.d.ts +1 -1
  109. package/dist/types/src/capabilities/state.d.ts +2 -2
  110. package/dist/types/src/capabilities/state.d.ts.map +1 -1
  111. package/dist/types/src/capabilities/toolkit.d.ts +5 -0
  112. package/dist/types/src/capabilities/toolkit.d.ts.map +1 -0
  113. package/dist/types/src/components/Call/Call.d.ts.map +1 -1
  114. package/dist/types/src/components/Call/Toolbar.d.ts.map +1 -1
  115. package/dist/types/src/components/CallDebugPanel.d.ts.map +1 -1
  116. package/dist/types/src/components/CallSidebar.d.ts.map +1 -1
  117. package/dist/types/src/components/ChannelContainer.d.ts.map +1 -1
  118. package/dist/types/src/components/ChannelContainer.stories.d.ts +87 -3
  119. package/dist/types/src/components/ChannelContainer.stories.d.ts.map +1 -1
  120. package/dist/types/src/components/Chat.stories.d.ts +89 -4
  121. package/dist/types/src/components/Chat.stories.d.ts.map +1 -1
  122. package/dist/types/src/components/ChatContainer.d.ts.map +1 -1
  123. package/dist/types/src/components/Comments.stories.d.ts +154 -3
  124. package/dist/types/src/components/Comments.stories.d.ts.map +1 -1
  125. package/dist/types/src/components/CommentsContainer.d.ts.map +1 -1
  126. package/dist/types/src/components/CommentsThreadContainer.d.ts +2 -2
  127. package/dist/types/src/components/CommentsThreadContainer.d.ts.map +1 -1
  128. package/dist/types/src/components/Lobby/Lobby.d.ts.map +1 -1
  129. package/dist/types/src/components/Media/VideoObject.d.ts +1 -1
  130. package/dist/types/src/components/Media/VideoObject.stories.d.ts +15 -3
  131. package/dist/types/src/components/Media/VideoObject.stories.d.ts.map +1 -1
  132. package/dist/types/src/components/MessageContainer.d.ts +4 -1
  133. package/dist/types/src/components/MessageContainer.d.ts.map +1 -1
  134. package/dist/types/src/components/Participant/Participant.d.ts.map +1 -1
  135. package/dist/types/src/components/Participant/ParticipantGrid.d.ts.map +1 -1
  136. package/dist/types/src/components/ResponsiveGrid/ResponsiveContainer.stories.d.ts +152 -2
  137. package/dist/types/src/components/ResponsiveGrid/ResponsiveContainer.stories.d.ts.map +1 -1
  138. package/dist/types/src/components/ResponsiveGrid/ResponsiveGrid.stories.d.ts +154 -3
  139. package/dist/types/src/components/ResponsiveGrid/ResponsiveGrid.stories.d.ts.map +1 -1
  140. package/dist/types/src/components/ResponsiveGrid/ResponsiveGridItem.d.ts.map +1 -1
  141. package/dist/types/src/components/ThreadComplementary.d.ts.map +1 -1
  142. package/dist/types/src/components/command-extension.d.ts.map +1 -1
  143. package/dist/types/src/components/testing.d.ts +1 -0
  144. package/dist/types/src/components/testing.d.ts.map +1 -1
  145. package/dist/types/src/extensions/threads.d.ts +2 -2
  146. package/dist/types/src/extensions/threads.d.ts.map +1 -1
  147. package/dist/types/src/hooks/index.d.ts +1 -0
  148. package/dist/types/src/hooks/index.d.ts.map +1 -1
  149. package/dist/types/src/hooks/useBlackCanvasStreamTrack.d.ts +2 -0
  150. package/dist/types/src/hooks/useBlackCanvasStreamTrack.d.ts.map +1 -0
  151. package/dist/types/src/hooks/useVideoStreamTrack.d.ts.map +1 -1
  152. package/dist/types/src/translations.d.ts +1 -0
  153. package/dist/types/src/translations.d.ts.map +1 -1
  154. package/dist/types/src/types/schema.d.ts +122 -107
  155. package/dist/types/src/types/schema.d.ts.map +1 -1
  156. package/dist/types/src/types/types.d.ts +253 -168
  157. package/dist/types/src/types/types.d.ts.map +1 -1
  158. package/dist/types/tsconfig.tsbuildinfo +1 -1
  159. package/package.json +51 -47
  160. package/src/ThreadPlugin.tsx +9 -2
  161. package/src/calls/call-manager.ts +1 -1
  162. package/src/calls/call-swarm-synchronizer.ts +1 -1
  163. package/src/calls/media-manager.ts +58 -33
  164. package/src/calls/types.ts +1 -1
  165. package/src/calls/util/calls-service.ts +56 -10
  166. package/src/calls/util/stub-media-streams.ts +4 -5
  167. package/src/calls-service.stories.tsx +76 -60
  168. package/src/capabilities/app-graph-builder.ts +8 -5
  169. package/src/capabilities/call-manager.ts +3 -2
  170. package/src/capabilities/capabilities.ts +2 -2
  171. package/src/capabilities/index.ts +1 -0
  172. package/src/capabilities/intent-resolver.ts +39 -4
  173. package/src/capabilities/markdown.ts +4 -3
  174. package/src/capabilities/react-surface.tsx +5 -4
  175. package/src/capabilities/state.ts +2 -1
  176. package/src/capabilities/toolkit.ts +78 -0
  177. package/src/components/Call/Call.tsx +3 -2
  178. package/src/components/Call/Toolbar.tsx +2 -2
  179. package/src/components/CallDebugPanel.tsx +1 -1
  180. package/src/components/CallSidebar.tsx +2 -1
  181. package/src/components/ChannelContainer.stories.tsx +6 -5
  182. package/src/components/ChannelContainer.tsx +7 -6
  183. package/src/components/Chat.stories.tsx +28 -26
  184. package/src/components/ChatContainer.tsx +8 -4
  185. package/src/components/Comments.stories.tsx +33 -29
  186. package/src/components/CommentsContainer.tsx +2 -1
  187. package/src/components/CommentsThreadContainer.tsx +9 -7
  188. package/src/components/Lobby/Lobby.tsx +1 -1
  189. package/src/components/Media/VideoObject.stories.tsx +30 -27
  190. package/src/components/MessageContainer.tsx +43 -9
  191. package/src/components/Participant/Participant.tsx +13 -7
  192. package/src/components/Participant/ParticipantGrid.tsx +2 -1
  193. package/src/components/ResponsiveGrid/ResponsiveContainer.stories.tsx +6 -5
  194. package/src/components/ResponsiveGrid/ResponsiveGrid.stories.tsx +42 -39
  195. package/src/components/ResponsiveGrid/ResponsiveGrid.tsx +1 -1
  196. package/src/components/ResponsiveGrid/ResponsiveGridItem.tsx +2 -2
  197. package/src/components/ThreadComplementary.tsx +38 -8
  198. package/src/components/ThreadSettings.tsx +2 -2
  199. package/src/components/command-extension.ts +5 -2
  200. package/src/components/testing.ts +22 -0
  201. package/src/extensions/threads.ts +6 -6
  202. package/src/hooks/index.ts +1 -0
  203. package/src/hooks/useBlackCanvasStreamTrack.tsx +27 -0
  204. package/src/hooks/useDebugMode.ts +1 -1
  205. package/src/hooks/useVideoStreamTrack.tsx +5 -2
  206. package/src/testing/stories.ts +2 -2
  207. package/src/translations.ts +1 -0
  208. package/src/types/types.ts +21 -12
  209. package/src/typings.d.ts +8 -0
  210. package/dist/lib/browser/ChannelContainer-IY2UIRLF.mjs +0 -16
  211. package/dist/lib/browser/app-graph-builder-N7BE5TYW.mjs.map +0 -7
  212. package/dist/lib/browser/call-manager-NIPDYUVD.mjs.map +0 -7
  213. package/dist/lib/browser/chunk-4OYAYON2.mjs +0 -21
  214. package/dist/lib/browser/chunk-6NSMBO3B.mjs.map +0 -7
  215. package/dist/lib/browser/chunk-ERWASUQK.mjs.map +0 -7
  216. package/dist/lib/browser/chunk-GCLD5ESP.mjs.map +0 -7
  217. package/dist/lib/browser/chunk-KWPRNHTK.mjs.map +0 -7
  218. package/dist/lib/browser/chunk-VUWC4IJM.mjs +0 -479
  219. package/dist/lib/browser/chunk-VUWC4IJM.mjs.map +0 -7
  220. package/dist/lib/browser/chunk-XLPJXUKO.mjs +0 -367
  221. package/dist/lib/browser/chunk-XLPJXUKO.mjs.map +0 -7
  222. package/dist/lib/browser/chunk-ZE7KWN36.mjs.map +0 -7
  223. package/dist/lib/browser/intent-resolver-JFGLZ6VI.mjs.map +0 -7
  224. package/dist/lib/browser/markdown-GEXBVJYN.mjs.map +0 -7
  225. package/dist/lib/browser/react-surface-YCYEFBPM.mjs.map +0 -7
  226. package/dist/lib/node-esm/app-graph-builder-VCW76Z6V.mjs.map +0 -7
  227. package/dist/lib/node-esm/call-manager-D6O5ZJJ4.mjs.map +0 -7
  228. package/dist/lib/node-esm/chunk-2MFNK53T.mjs +0 -480
  229. package/dist/lib/node-esm/chunk-2MFNK53T.mjs.map +0 -7
  230. package/dist/lib/node-esm/chunk-C3XFMUAY.mjs.map +0 -7
  231. package/dist/lib/node-esm/chunk-GW37AAM4.mjs +0 -23
  232. package/dist/lib/node-esm/chunk-IHDNQLLR.mjs.map +0 -7
  233. package/dist/lib/node-esm/chunk-J4NSON5Q.mjs +0 -368
  234. package/dist/lib/node-esm/chunk-J4NSON5Q.mjs.map +0 -7
  235. package/dist/lib/node-esm/chunk-OU5BYIDO.mjs.map +0 -7
  236. package/dist/lib/node-esm/chunk-TDFHTCZZ.mjs.map +0 -7
  237. package/dist/lib/node-esm/intent-resolver-OXGNQ44Y.mjs.map +0 -7
  238. package/dist/lib/node-esm/markdown-LIJZDK2J.mjs.map +0 -7
  239. package/dist/lib/node-esm/react-surface-IRSEKM42.mjs.map +0 -7
  240. /package/dist/lib/browser/{ChannelContainer-IY2UIRLF.mjs.map → ChannelContainer-GHDNEZT2.mjs.map} +0 -0
  241. /package/dist/lib/browser/{ChatContainer-AIGIWAUG.mjs.map → ChatContainer-42ALOWUA.mjs.map} +0 -0
  242. /package/dist/lib/browser/{react-root-X4UIVGTX.mjs.map → react-root-PTTTW2MO.mjs.map} +0 -0
  243. /package/dist/lib/browser/{settings-E32MTL7A.mjs.map → settings-GRKEID73.mjs.map} +0 -0
  244. /package/dist/lib/node-esm/{ChannelContainer-DET6T5N2.mjs.map → ChannelContainer-D5E2S6AI.mjs.map} +0 -0
  245. /package/dist/lib/node-esm/{ChatContainer-IQUWU426.mjs.map → ChatContainer-4EGHKHIU.mjs.map} +0 -0
  246. /package/dist/lib/node-esm/{react-root-M7J6PZVW.mjs.map → react-root-2HHRRUME.mjs.map} +0 -0
  247. /package/dist/lib/node-esm/{settings-SJ24YG7M.mjs.map → settings-F2ODVNNW.mjs.map} +0 -0
@@ -4,10 +4,10 @@
4
4
 
5
5
  import '@dxos-theme';
6
6
 
7
- import { type Meta } from '@storybook/react-vite';
8
- import React, { useEffect, useState } from 'react';
7
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
+ import React, { useState } from 'react';
9
9
 
10
- import { Capabilities, contributes, createSurface, IntentPlugin } from '@dxos/app-framework';
10
+ import { Capabilities, IntentPlugin, contributes, createSurface } from '@dxos/app-framework';
11
11
  import { withPluginManager } from '@dxos/app-framework/testing';
12
12
  import { Obj, Ref } from '@dxos/echo';
13
13
  import { faker } from '@dxos/random';
@@ -15,35 +15,35 @@ import { useClient } from '@dxos/react-client';
15
15
  import { type Space } from '@dxos/react-client/echo';
16
16
  import { useIdentity } from '@dxos/react-client/halo';
17
17
  import { withClientProvider } from '@dxos/react-client/testing';
18
+ import { useAsyncEffect } from '@dxos/react-ui';
18
19
  import { Thread } from '@dxos/react-ui-thread';
19
20
  import { DataType } from '@dxos/schema';
20
- import { withLayout, withTheme } from '@dxos/storybook-utils';
21
+ import { render, withLayout, withTheme } from '@dxos/storybook-utils';
21
22
 
22
- import { ChatContainer } from './ChatContainer';
23
23
  import { translations } from '../translations';
24
24
  import { ChannelType, ThreadType } from '../types';
25
25
 
26
+ import { ChatContainer } from './ChatContainer';
27
+
26
28
  faker.seed(1);
27
29
 
28
- const Story = () => {
30
+ const DefaultStory = () => {
29
31
  const client = useClient();
30
32
  const identity = useIdentity();
31
33
  const [space, setSpace] = useState<Space>();
32
34
  const [channel, setChannel] = useState<ChannelType | null>();
33
35
 
34
- useEffect(() => {
36
+ useAsyncEffect(async () => {
35
37
  if (identity) {
36
- setTimeout(async () => {
37
- const space = await client.spaces.create();
38
- const channel = space.db.add(
39
- Obj.make(ChannelType, {
40
- defaultThread: Ref.make(Obj.make(ThreadType, { messages: [], status: 'active' })),
41
- threads: [],
42
- }),
43
- );
44
- setSpace(space);
45
- setChannel(channel);
46
- });
38
+ const space = await client.spaces.create();
39
+ const channel = space.db.add(
40
+ Obj.make(ChannelType, {
41
+ defaultThread: Ref.make(Obj.make(ThreadType, { messages: [], status: 'active' })),
42
+ threads: [],
43
+ }),
44
+ );
45
+ setSpace(space);
46
+ setChannel(channel);
47
47
  }
48
48
  }, [identity]);
49
49
 
@@ -52,18 +52,16 @@ const Story = () => {
52
52
  }
53
53
 
54
54
  return (
55
- <main className='max-is-prose mli-auto bs-dvh overflow-hidden'>
55
+ <main className='is-full max-is-prose mli-auto bs-dvh overflow-hidden'>
56
56
  <ChatContainer space={space} thread={channel.defaultThread.target} />
57
57
  </main>
58
58
  );
59
59
  };
60
60
 
61
- export const Default = {};
62
-
63
- const meta: Meta<typeof Thread.Root> = {
61
+ const meta = {
64
62
  title: 'plugins/plugin-thread/Chat',
65
- component: Thread.Root,
66
- render: () => <Story />,
63
+ component: Thread.Root as any,
64
+ render: render(DefaultStory),
67
65
  decorators: [
68
66
  withPluginManager({
69
67
  plugins: [IntentPlugin()],
@@ -80,9 +78,13 @@ const meta: Meta<typeof Thread.Root> = {
80
78
  }),
81
79
  withTheme,
82
80
  withLayout({ fullscreen: true }),
83
- withClientProvider({ createSpace: true, types: [ThreadType, DataType.Message] }),
81
+ withClientProvider({ createSpace: true, types: [ThreadType, ChannelType, DataType.Message] }),
84
82
  ],
85
83
  parameters: { translations },
86
- };
84
+ } satisfies Meta<typeof DefaultStory>;
87
85
 
88
86
  export default meta;
87
+
88
+ type Story = StoryObj<typeof meta>;
89
+
90
+ export const Default: Story = {};
@@ -5,7 +5,7 @@
5
5
  import React, { useLayoutEffect, useMemo, useRef, useState } from 'react';
6
6
 
7
7
  import { Obj, Ref } from '@dxos/echo';
8
- import { fullyQualifiedId, type Space, useMembers } from '@dxos/react-client/echo';
8
+ import { type Space, fullyQualifiedId, useMembers } from '@dxos/react-client/echo';
9
9
  import { useIdentity } from '@dxos/react-client/halo';
10
10
  import { Icon, ScrollArea, useThemeContext, useTranslation } from '@dxos/react-ui';
11
11
  import { createBasicExtensions, createThemeExtensions, listener } from '@dxos/react-ui-editor';
@@ -21,13 +21,14 @@ import {
21
21
  import { DataType } from '@dxos/schema';
22
22
  import { isNonNullable } from '@dxos/util';
23
23
 
24
- import { MessageContainer } from './MessageContainer';
25
- import { command } from './command-extension';
26
24
  import { useStatus } from '../hooks';
27
25
  import { meta } from '../meta';
28
26
  import { type ThreadType } from '../types';
29
27
  import { getMessageMetadata } from '../util';
30
28
 
29
+ import { command } from './command-extension';
30
+ import { MessageContainer } from './MessageContainer';
31
+
31
32
  export const ChatHeading = ({ attendableId }: { attendableId?: string }) => {
32
33
  const { t } = useTranslation(meta.id);
33
34
  return (
@@ -111,7 +112,10 @@ export const ChatContainer = ({ space, thread, context, current, autoFocusTextbo
111
112
  <Thread.Root
112
113
  current={current}
113
114
  id={id}
114
- classNames='bs-full grid-rows-[1fr_min-content_min-content] overflow-hidden transition-[padding-block-end] [[data-sidebar-inline-start-state=open]_&]:lg:pbe-0'
115
+ classNames={[
116
+ 'bs-full is-full max-is-prose mli-auto grid-rows-[1fr_min-content_min-content] overflow-hidden',
117
+ 'transition-[padding-block-end] [[data-sidebar-inline-start-state=open]_&]:lg:pbe-0',
118
+ ]}
115
119
  >
116
120
  <ScrollArea.Root classNames='col-span-2'>
117
121
  <ScrollArea.Viewport classNames='overflow-anchored after:overflow-anchor after:block after:bs-px after:-mbs-px [&>div]:min-bs-full [&>div]:!grid [&>div]:grid-rows-[1fr_0]'>
@@ -4,8 +4,8 @@
4
4
 
5
5
  import '@dxos-theme';
6
6
 
7
- import { type Meta } from '@storybook/react-vite';
8
- import React, { useEffect } from 'react';
7
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
+ import React from 'react';
9
9
 
10
10
  import { IntentPlugin } from '@dxos/app-framework';
11
11
  import { withPluginManager } from '@dxos/app-framework/testing';
@@ -14,14 +14,16 @@ import { ClientPlugin } from '@dxos/plugin-client';
14
14
  import { faker } from '@dxos/random';
15
15
  import { useQuery, useSpace } from '@dxos/react-client/echo';
16
16
  import { useIdentity } from '@dxos/react-client/halo';
17
+ import { useAsyncEffect } from '@dxos/react-ui';
17
18
  import { AnchoredTo, DataType } from '@dxos/schema';
18
19
  import { layoutCentered, render, withLayout, withTheme } from '@dxos/storybook-utils';
19
20
 
20
- import { CommentsContainer } from './CommentsContainer';
21
- import { createCommentThread } from './testing';
22
21
  import { translations } from '../translations';
23
22
  import { ThreadType } from '../types';
24
23
 
24
+ import { CommentsContainer } from './CommentsContainer';
25
+ import { createCommentThread, createProposalThread } from './testing';
26
+
25
27
  faker.seed(1);
26
28
 
27
29
  const DefaultStory = () => {
@@ -29,27 +31,25 @@ const DefaultStory = () => {
29
31
  const space = useSpace();
30
32
  const anchors = useQuery(space, Query.type(AnchoredTo));
31
33
 
32
- useEffect(() => {
34
+ useAsyncEffect(async () => {
33
35
  if (identity && space) {
34
- const t = setTimeout(async () => {
35
- const object = space.db.add(Obj.make(Type.Expando, {}));
36
- const thread1 = space.db.add(createCommentThread(identity));
37
- const thread2 = space.db.add(createCommentThread(identity));
38
- space.db.add(
39
- Relation.make(AnchoredTo, {
40
- [Relation.Source]: thread1,
41
- [Relation.Target]: object,
42
- }),
43
- );
44
- space.db.add(
45
- Relation.make(AnchoredTo, {
46
- [Relation.Source]: thread2,
47
- [Relation.Target]: object,
48
- }),
49
- );
50
- });
51
-
52
- return () => clearTimeout(t);
36
+ const object = space.db.add(Obj.make(Type.Expando, {}));
37
+ const thread1 = space.db.add(createCommentThread(identity));
38
+ const thread2 = space.db.add(createProposalThread(identity));
39
+ space.db.add(
40
+ Relation.make(AnchoredTo, {
41
+ [Relation.Source]: thread1,
42
+ [Relation.Target]: object,
43
+ anchor: 'test',
44
+ }),
45
+ );
46
+ space.db.add(
47
+ Relation.make(AnchoredTo, {
48
+ [Relation.Source]: thread2,
49
+ [Relation.Target]: object,
50
+ anchor: 'test',
51
+ }),
52
+ );
53
53
  }
54
54
  }, [identity, space]);
55
55
 
@@ -59,23 +59,25 @@ const DefaultStory = () => {
59
59
 
60
60
  return (
61
61
  <div className='w-[30rem] overflow-y-auto bg-baseSurface'>
62
- <CommentsContainer anchors={anchors} onThreadDelete={console.log} />
62
+ <CommentsContainer anchors={anchors} onThreadDelete={console.log} onAcceptProposal={console.log} />
63
63
  </div>
64
64
  );
65
65
  };
66
66
 
67
- const meta: Meta = {
67
+ const meta = {
68
68
  title: 'plugins/plugin-thread/Comments',
69
69
  render: render(DefaultStory),
70
70
  decorators: [
71
71
  withTheme,
72
72
  withLayout({ fullscreen: true, classNames: layoutCentered }),
73
+ // TODO(wittjosiah): This shouldn't depend on app framework. Should use withClientProvider instead.
74
+ // Currently this is required due to useOnEditAnalytics.
73
75
  withPluginManager({
74
76
  plugins: [
75
77
  IntentPlugin(),
76
78
  ClientPlugin({
77
79
  types: [DataType.Message, ThreadType, AnchoredTo],
78
- onClientInitialized: async (_, client) => {
80
+ onClientInitialized: async ({ client }) => {
79
81
  await client.halo.createIdentity();
80
82
  },
81
83
  }),
@@ -85,8 +87,10 @@ const meta: Meta = {
85
87
  parameters: {
86
88
  translations,
87
89
  },
88
- };
90
+ } satisfies Meta<typeof DefaultStory>;
89
91
 
90
92
  export default meta;
91
93
 
92
- export const Default = {};
94
+ type Story = StoryObj<typeof meta>;
95
+
96
+ export const Default: Story = {};
@@ -9,10 +9,11 @@ import { fullyQualifiedId } from '@dxos/react-client/echo';
9
9
  import { Callout, Icon, Trans, useTranslation } from '@dxos/react-ui';
10
10
  import { type AnchoredTo } from '@dxos/schema';
11
11
 
12
- import { CommentsThreadContainer, type CommentsThreadContainerProps } from './CommentsThreadContainer';
13
12
  import { meta } from '../meta';
14
13
  import { type ThreadType } from '../types';
15
14
 
15
+ import { CommentsThreadContainer, type CommentsThreadContainerProps } from './CommentsThreadContainer';
16
+
16
17
  export type CommentsContainerProps = Omit<CommentsThreadContainerProps, 'anchor' | 'current'> & {
17
18
  anchors: AnchoredTo[];
18
19
  currentId?: string;
@@ -14,15 +14,13 @@ import { MessageTextbox, type MessageTextboxProps, Thread, type ThreadRootProps
14
14
  import { type AnchoredTo } from '@dxos/schema';
15
15
  import { isNonNullable } from '@dxos/util';
16
16
 
17
- import { MessageContainer } from './MessageContainer';
18
- import { command } from './command-extension';
19
17
  import { useStatus } from '../hooks';
20
18
  import { meta } from '../meta';
21
19
  import { type ThreadType } from '../types';
22
20
  import { getMessageMetadata } from '../util';
23
21
 
24
- // TODO(burdon): Remove need for p-1.
25
- export const commentControlClassNames = '!p-1 transition-opacity';
22
+ import { command } from './command-extension';
23
+ import { MessageContainer, buttonClassNames, buttonGroupClassNames } from './MessageContainer';
26
24
 
27
25
  export type CommentsThreadContainerProps = {
28
26
  anchor: AnchoredTo;
@@ -31,6 +29,7 @@ export type CommentsThreadContainerProps = {
31
29
  onResolve?: (anchor: AnchoredTo) => void;
32
30
  onMessageDelete?: (anchor: AnchoredTo, messageId: string) => void;
33
31
  onThreadDelete?: (anchor: AnchoredTo) => void;
32
+ onAcceptProposal?: (anchor: AnchoredTo, messageId: string) => void;
34
33
  } & Pick<ThreadRootProps, 'current'>;
35
34
 
36
35
  export const CommentsThreadContainer = ({
@@ -41,6 +40,7 @@ export const CommentsThreadContainer = ({
41
40
  onResolve,
42
41
  onMessageDelete,
43
42
  onThreadDelete,
43
+ onAcceptProposal,
44
44
  }: CommentsThreadContainerProps) => {
45
45
  const { t } = useTranslation(meta.id);
46
46
  const identity = useIdentity()!;
@@ -75,6 +75,7 @@ export const CommentsThreadContainer = ({
75
75
  const handleResolve = useCallback(() => onResolve?.(anchor), [onResolve, anchor]);
76
76
  const handleMessageDelete = useCallback((id: string) => onMessageDelete?.(anchor, id), [onMessageDelete, anchor]);
77
77
  const handleThreadDelete = useCallback(() => onThreadDelete?.(anchor), [onThreadDelete, anchor]);
78
+ const handleAcceptProposal = useCallback((id: string) => onAcceptProposal?.(anchor, id), [onAcceptProposal, anchor]);
78
79
 
79
80
  const handleComment: MessageTextboxProps['onSend'] = useCallback(() => {
80
81
  if (!messageRef.current) {
@@ -112,7 +113,7 @@ export const CommentsThreadContainer = ({
112
113
  ) : (
113
114
  <Thread.Header>{thread.name}</Thread.Header>
114
115
  )}
115
- <div className='flex flex-row items-center gap-0.5'>
116
+ <div role='none' className={buttonGroupClassNames}>
116
117
  {thread.status === 'staged' && <Tag palette='neutral'>{t('draft button')}</Tag>}
117
118
  {onResolve && !(thread?.status === 'staged') && (
118
119
  <IconButton
@@ -121,7 +122,7 @@ export const CommentsThreadContainer = ({
121
122
  icon={thread?.status === 'resolved' ? 'ph--check--fill' : 'ph--check--regular'}
122
123
  iconOnly
123
124
  label={t('resolve thread label')}
124
- classNames={[commentControlClassNames, thread?.status !== 'resolved' && hoverableControlItem]}
125
+ classNames={[buttonClassNames, thread?.status !== 'resolved' && hoverableControlItem]}
125
126
  onClick={handleResolve}
126
127
  />
127
128
  )}
@@ -132,7 +133,7 @@ export const CommentsThreadContainer = ({
132
133
  icon='ph--x--regular'
133
134
  iconOnly
134
135
  label={t('delete thread label')}
135
- classNames={[commentControlClassNames, hoverableControlItem]}
136
+ classNames={[buttonClassNames, hoverableControlItem]}
136
137
  onClick={handleThreadDelete}
137
138
  />
138
139
  )}
@@ -147,6 +148,7 @@ export const CommentsThreadContainer = ({
147
148
  message={message}
148
149
  members={members}
149
150
  onDelete={handleMessageDelete}
151
+ onAcceptProposal={handleAcceptProposal}
150
152
  />
151
153
  ))}
152
154
 
@@ -5,7 +5,7 @@
5
5
  import React, { type FC, type PropsWithChildren, useEffect, useState } from 'react';
6
6
 
7
7
  import { useCapability } from '@dxos/app-framework';
8
- import { useTranslation, type ThemedClassName } from '@dxos/react-ui';
8
+ import { type ThemedClassName, useTranslation } from '@dxos/react-ui';
9
9
  import { mx } from '@dxos/react-ui-theme';
10
10
 
11
11
  import { ThreadCapabilities } from '../../capabilities';
@@ -4,44 +4,47 @@
4
4
 
5
5
  import '@dxos-theme';
6
6
 
7
- import { type StoryObj, type Meta } from '@storybook/react-vite';
7
+ import { type StoryObj } from '@storybook/react-vite';
8
8
  import React, { useEffect, useState } from 'react';
9
9
 
10
10
  import { scheduleTask } from '@dxos/async';
11
11
  import { Context } from '@dxos/context';
12
12
  import { log } from '@dxos/log';
13
- import { withTheme, withLayout } from '@dxos/storybook-utils';
13
+ import { withLayout, withTheme } from '@dxos/storybook-utils';
14
14
 
15
- import { VideoObject } from './VideoObject';
16
15
  import { getUserMediaTrack } from '../../calls';
17
16
  import { ResponsiveContainer } from '../ResponsiveGrid';
18
17
 
19
- const meta: Meta<typeof VideoObject> = {
18
+ import { VideoObject, type VideoObjectProps } from './VideoObject';
19
+
20
+ const DefaultStory = (props: VideoObjectProps) => {
21
+ log.info('render');
22
+ const [videoStream, setVideoStream] = useState<MediaStream>();
23
+ useEffect(() => {
24
+ const ctx = new Context();
25
+ scheduleTask(ctx, async () => {
26
+ const stream = new MediaStream();
27
+ stream.addTrack(await getUserMediaTrack('videoinput'));
28
+ setVideoStream(stream);
29
+ });
30
+
31
+ return () => {
32
+ void ctx.dispose();
33
+ videoStream?.getTracks().forEach((track) => track.stop());
34
+ };
35
+ }, []);
36
+
37
+ return (
38
+ <ResponsiveContainer>
39
+ <VideoObject videoStream={videoStream} flip {...props} />
40
+ </ResponsiveContainer>
41
+ );
42
+ };
43
+
44
+ const meta = {
20
45
  title: 'plugins/plugin-thread/VideoObject',
21
46
  component: VideoObject,
22
- render: (args) => {
23
- log.info('render');
24
- const [videoStream, setVideoStream] = useState<MediaStream>();
25
- useEffect(() => {
26
- const ctx = new Context();
27
- scheduleTask(ctx, async () => {
28
- const stream = new MediaStream();
29
- stream.addTrack(await getUserMediaTrack('videoinput'));
30
- setVideoStream(stream);
31
- });
32
-
33
- return () => {
34
- void ctx.dispose();
35
- videoStream?.getTracks().forEach((track) => track.stop());
36
- };
37
- }, []);
38
-
39
- return (
40
- <ResponsiveContainer>
41
- <VideoObject videoStream={videoStream} {...args} flip />
42
- </ResponsiveContainer>
43
- );
44
- },
47
+ render: DefaultStory,
45
48
  decorators: [withTheme, withLayout({ fullscreen: true, classNames: 'justify-center' })],
46
49
  };
47
50
 
@@ -9,27 +9,37 @@ import { Surface } from '@dxos/app-framework';
9
9
  import { type Obj, Ref, type Type } from '@dxos/echo';
10
10
  import { PublicKey } from '@dxos/react-client';
11
11
  import { type SpaceMember } from '@dxos/react-client/echo';
12
- import { useIdentity, type Identity } from '@dxos/react-client/halo';
12
+ import { type Identity, useIdentity } from '@dxos/react-client/halo';
13
13
  import { IconButton, useOnTransition, useThemeContext, useTranslation } from '@dxos/react-ui';
14
14
  import { createBasicExtensions, createThemeExtensions, useTextEditor } from '@dxos/react-ui-editor';
15
15
  import { hoverableControlItem, hoverableControls, hoverableFocusedWithinControls, mx } from '@dxos/react-ui-theme';
16
16
  import { MessageHeading, MessageRoot } from '@dxos/react-ui-thread';
17
17
  import { type DataType } from '@dxos/schema';
18
18
 
19
- import { commentControlClassNames } from './CommentsThreadContainer';
20
- import { command } from './command-extension';
21
19
  import { useOnEditAnalytics } from '../hooks';
22
20
  import { meta } from '../meta';
23
21
  import { getMessageMetadata } from '../util';
24
22
 
23
+ import { command } from './command-extension';
24
+
25
+ export const buttonGroupClassNames = 'flex flex-row items-center gap-0.5 pie-2';
26
+ export const buttonClassNames = '!p-1 transition-opacity';
27
+
25
28
  export type MessageContainerProps = {
26
29
  message: DataType.Message;
27
30
  members: SpaceMember[];
28
31
  editable?: boolean;
29
32
  onDelete?: (id: string) => void;
33
+ onAcceptProposal?: (id: string) => void;
30
34
  };
31
35
 
32
- export const MessageContainer = ({ message, members, editable = false, onDelete }: MessageContainerProps) => {
36
+ export const MessageContainer = ({
37
+ message,
38
+ members,
39
+ editable = false,
40
+ onDelete,
41
+ onAcceptProposal,
42
+ }: MessageContainerProps) => {
33
43
  const { t } = useTranslation(meta.id);
34
44
  const senderIdentity = members.find(
35
45
  (member) =>
@@ -39,8 +49,11 @@ export const MessageContainer = ({ message, members, editable = false, onDelete
39
49
  const messageMetadata = getMessageMetadata(message.id, senderIdentity);
40
50
  const userIsAuthor = useIdentity()?.did === messageMetadata.authorId;
41
51
  const [editing, setEditing] = useState(false);
52
+ const handleEdit = useCallback(() => setEditing((editing) => !editing), []);
42
53
  const handleDelete = useCallback(() => onDelete?.(message.id), [message, onDelete]);
54
+ const handleAcceptProposal = useCallback(() => onAcceptProposal?.(message.id), [message, onAcceptProposal]);
43
55
  const textBlock = message.blocks.find((block) => block._tag === 'text');
56
+ const proposalBlock = message.blocks.find((block) => block._tag === 'proposal');
44
57
  const references = message.blocks.filter((block) => block._tag === 'reference').map((block) => block.reference);
45
58
 
46
59
  useOnEditAnalytics(message, textBlock, !!editing);
@@ -48,7 +61,7 @@ export const MessageContainer = ({ message, members, editable = false, onDelete
48
61
  return (
49
62
  <MessageRoot {...messageMetadata} classNames={[hoverableControls, hoverableFocusedWithinControls]}>
50
63
  <MessageHeading authorName={messageMetadata.authorName} timestamp={messageMetadata.timestamp}>
51
- <div className='flex flex-row items-center gap-0.5'>
64
+ <div role='none' className={buttonGroupClassNames}>
52
65
  {userIsAuthor && editable && (
53
66
  <IconButton
54
67
  data-testid={editing ? 'thread.message.save' : 'thread.message.edit'}
@@ -56,8 +69,20 @@ export const MessageContainer = ({ message, members, editable = false, onDelete
56
69
  icon={editing ? 'ph--check--regular' : 'ph--pencil-simple--regular'}
57
70
  iconOnly
58
71
  label={t(editing ? 'save message label' : 'edit message label')}
59
- classNames={[commentControlClassNames, hoverableControlItem]}
60
- onClick={() => setEditing((editing) => !editing)}
72
+ classNames={[buttonClassNames, hoverableControlItem]}
73
+ onClick={handleEdit}
74
+ />
75
+ )}
76
+ {/* TODO(wittjosiah): Proposal controls should probably be hoisted to thread level. */}
77
+ {proposalBlock && onAcceptProposal && (
78
+ <IconButton
79
+ data-testid='thread.message.accept'
80
+ variant='ghost'
81
+ icon='ph--check--regular'
82
+ iconOnly
83
+ label={t('accept proposal label')}
84
+ classNames={[buttonClassNames, hoverableControlItem]}
85
+ onClick={handleAcceptProposal}
61
86
  />
62
87
  )}
63
88
  {onDelete && (
@@ -67,13 +92,14 @@ export const MessageContainer = ({ message, members, editable = false, onDelete
67
92
  icon='ph--x--regular'
68
93
  iconOnly
69
94
  label={t('delete message label')}
70
- classNames={[commentControlClassNames, hoverableControlItem]}
71
- onClick={() => handleDelete()}
95
+ classNames={[buttonClassNames, hoverableControlItem]}
96
+ onClick={handleDelete}
72
97
  />
73
98
  )}
74
99
  </div>
75
100
  </MessageHeading>
76
101
  {textBlock && <TextboxBlock block={textBlock} isAuthor={userIsAuthor} editing={editing} />}
102
+ {proposalBlock && <ProposalBlock block={proposalBlock} />}
77
103
  {Ref.Array.targets(references).map((reference, index) => (
78
104
  <MessagePart key={index} part={reference} />
79
105
  ))}
@@ -132,6 +158,14 @@ const TextboxBlock = ({
132
158
  return <div role='none' ref={parentRef} className='mie-4' {...focusAttributes} />;
133
159
  };
134
160
 
161
+ const ProposalBlock = ({ block }: { block: DataType.MessageBlock.Proposal }) => {
162
+ return (
163
+ <div role='none' className='mie-4 italic'>
164
+ {block.text}
165
+ </div>
166
+ );
167
+ };
168
+
135
169
  const MessageBlockObjectTile = forwardRef<HTMLDivElement, { subject: Obj.Any }>(({ subject }, forwardedRef) => {
136
170
  // TODO(burdon): Use annotation to get title.
137
171
  let title = (subject as any).name ?? (subject as any).title ?? (subject as any).type ?? 'Object';
@@ -17,15 +17,21 @@ export const Participant = memo(({ item: user, debug, ...props }: ResponsiveGrid
17
17
  const call = useCapability(ThreadCapabilities.CallManager);
18
18
  const isSelf: boolean = call.self.id !== undefined && user.id !== undefined && user.id.startsWith(call.self.id);
19
19
  const isScreenshare = user.id?.endsWith(SCREENSHARE_SUFFIX);
20
- const pulledVideoStream = call.getVideoStream(
21
- isScreenshare || !isSelf ? (user.tracks?.video as EncodedTrackName) : undefined,
22
- );
20
+
21
+ const pulledVideoStream =
22
+ !isSelf && isScreenshare && user.tracks?.screenshareEnabled
23
+ ? call.getVideoStream(user.tracks?.screenshare as EncodedTrackName)
24
+ : !isSelf && !isScreenshare && user.tracks?.videoEnabled
25
+ ? call.getVideoStream(user.tracks?.video as EncodedTrackName)
26
+ : undefined;
23
27
 
24
28
  const videoStream = useMemo<MediaStream | undefined>(() => {
25
- if (isSelf && !isScreenshare) {
26
- return call.media.videoStream;
27
- } else if (isSelf && isScreenshare) {
28
- return call.media.screenshareVideoStream;
29
+ if (isSelf) {
30
+ if (isScreenshare) {
31
+ return call.media.screenshareVideoStream;
32
+ } else if (!isScreenshare) {
33
+ return call.media.videoStream;
34
+ }
29
35
  } else if (!isSelf) {
30
36
  return pulledVideoStream;
31
37
  }
@@ -6,10 +6,11 @@ import React, { useEffect, useMemo, useState } from 'react';
6
6
 
7
7
  import { type ThemedClassName } from '@dxos/react-ui';
8
8
 
9
- import { Participant, SCREENSHARE_SUFFIX } from './Participant';
10
9
  import { type UserState } from '../../calls';
11
10
  import { ResponsiveGrid } from '../ResponsiveGrid';
12
11
 
12
+ import { Participant, SCREENSHARE_SUFFIX } from './Participant';
13
+
13
14
  const getId = (user: UserState): string => user.id!;
14
15
 
15
16
  export type ParticipantGridProps = ThemedClassName<{
@@ -4,23 +4,24 @@
4
4
 
5
5
  import '@dxos-theme';
6
6
 
7
- import { type StoryObj, type Meta } from '@storybook/react-vite';
7
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
8
  import React from 'react';
9
9
 
10
- import { withTheme, withLayout } from '@dxos/storybook-utils';
10
+ import { withLayout, withTheme } from '@dxos/storybook-utils';
11
11
 
12
- import { ResponsiveContainer } from './ResponsiveContainer';
13
12
  import { translations } from '../../translations';
14
13
  import { VideoObject as VideoObjectComponent } from '../Media';
15
14
 
16
- const meta: Meta<typeof ResponsiveContainer> = {
15
+ import { ResponsiveContainer } from './ResponsiveContainer';
16
+
17
+ const meta = {
17
18
  title: 'plugins/plugin-thread/ResponsiveContainer',
18
19
  component: ResponsiveContainer,
19
20
  decorators: [withTheme, withLayout({ fullscreen: true })],
20
21
  parameters: {
21
22
  translations,
22
23
  },
23
- };
24
+ } satisfies Meta<typeof ResponsiveContainer>;
24
25
 
25
26
  export default meta;
26
27