@tellescope/react-components 1.246.2 → 1.248.0

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 (88) hide show
  1. package/lib/cjs/Forms/form_responses.d.ts +11 -0
  2. package/lib/cjs/Forms/form_responses.d.ts.map +1 -1
  3. package/lib/cjs/Forms/form_responses.js +38 -2
  4. package/lib/cjs/Forms/form_responses.js.map +1 -1
  5. package/lib/cjs/Forms/forms.d.ts +2 -1
  6. package/lib/cjs/Forms/forms.d.ts.map +1 -1
  7. package/lib/cjs/Forms/forms.js +34 -8
  8. package/lib/cjs/Forms/forms.js.map +1 -1
  9. package/lib/cjs/Forms/forms.v2.js +1 -1
  10. package/lib/cjs/Forms/forms.v2.js.map +1 -1
  11. package/lib/cjs/Forms/hooks.d.ts +1 -0
  12. package/lib/cjs/Forms/hooks.d.ts.map +1 -1
  13. package/lib/cjs/Forms/hooks.js +5 -4
  14. package/lib/cjs/Forms/hooks.js.map +1 -1
  15. package/lib/cjs/TwilioVideo/TwilioControls.d.ts +2 -0
  16. package/lib/cjs/TwilioVideo/TwilioControls.d.ts.map +1 -1
  17. package/lib/cjs/TwilioVideo/TwilioControls.js +14 -3
  18. package/lib/cjs/TwilioVideo/TwilioControls.js.map +1 -1
  19. package/lib/cjs/TwilioVideo/TwilioParticipant.d.ts +2 -0
  20. package/lib/cjs/TwilioVideo/TwilioParticipant.d.ts.map +1 -1
  21. package/lib/cjs/TwilioVideo/TwilioParticipant.js +52 -21
  22. package/lib/cjs/TwilioVideo/TwilioParticipant.js.map +1 -1
  23. package/lib/cjs/TwilioVideo/TwilioVideoContext.d.ts +5 -0
  24. package/lib/cjs/TwilioVideo/TwilioVideoContext.d.ts.map +1 -1
  25. package/lib/cjs/TwilioVideo/TwilioVideoContext.js +107 -5
  26. package/lib/cjs/TwilioVideo/TwilioVideoContext.js.map +1 -1
  27. package/lib/cjs/TwilioVideo/TwilioVideoRoom.d.ts +2 -0
  28. package/lib/cjs/TwilioVideo/TwilioVideoRoom.d.ts.map +1 -1
  29. package/lib/cjs/TwilioVideo/TwilioVideoRoom.js +49 -3
  30. package/lib/cjs/TwilioVideo/TwilioVideoRoom.js.map +1 -1
  31. package/lib/cjs/TwilioVideo/index.d.ts +1 -1
  32. package/lib/cjs/TwilioVideo/index.d.ts.map +1 -1
  33. package/lib/cjs/TwilioVideo/index.js +2 -1
  34. package/lib/cjs/TwilioVideo/index.js.map +1 -1
  35. package/lib/cjs/state.d.ts +7 -1
  36. package/lib/cjs/state.d.ts.map +1 -1
  37. package/lib/cjs/state.js +43 -1
  38. package/lib/cjs/state.js.map +1 -1
  39. package/lib/esm/Forms/form_responses.d.ts +11 -0
  40. package/lib/esm/Forms/form_responses.d.ts.map +1 -1
  41. package/lib/esm/Forms/form_responses.js +37 -2
  42. package/lib/esm/Forms/form_responses.js.map +1 -1
  43. package/lib/esm/Forms/forms.d.ts +2 -1
  44. package/lib/esm/Forms/forms.d.ts.map +1 -1
  45. package/lib/esm/Forms/forms.js +34 -8
  46. package/lib/esm/Forms/forms.js.map +1 -1
  47. package/lib/esm/Forms/forms.v2.js +1 -1
  48. package/lib/esm/Forms/forms.v2.js.map +1 -1
  49. package/lib/esm/Forms/hooks.d.ts +1 -0
  50. package/lib/esm/Forms/hooks.d.ts.map +1 -1
  51. package/lib/esm/Forms/hooks.js +6 -5
  52. package/lib/esm/Forms/hooks.js.map +1 -1
  53. package/lib/esm/TwilioVideo/TwilioControls.d.ts +2 -0
  54. package/lib/esm/TwilioVideo/TwilioControls.d.ts.map +1 -1
  55. package/lib/esm/TwilioVideo/TwilioControls.js +15 -4
  56. package/lib/esm/TwilioVideo/TwilioControls.js.map +1 -1
  57. package/lib/esm/TwilioVideo/TwilioParticipant.d.ts +2 -0
  58. package/lib/esm/TwilioVideo/TwilioParticipant.d.ts.map +1 -1
  59. package/lib/esm/TwilioVideo/TwilioParticipant.js +52 -21
  60. package/lib/esm/TwilioVideo/TwilioParticipant.js.map +1 -1
  61. package/lib/esm/TwilioVideo/TwilioVideoContext.d.ts +5 -0
  62. package/lib/esm/TwilioVideo/TwilioVideoContext.d.ts.map +1 -1
  63. package/lib/esm/TwilioVideo/TwilioVideoContext.js +83 -1
  64. package/lib/esm/TwilioVideo/TwilioVideoContext.js.map +1 -1
  65. package/lib/esm/TwilioVideo/TwilioVideoRoom.d.ts +2 -0
  66. package/lib/esm/TwilioVideo/TwilioVideoRoom.d.ts.map +1 -1
  67. package/lib/esm/TwilioVideo/TwilioVideoRoom.js +49 -3
  68. package/lib/esm/TwilioVideo/TwilioVideoRoom.js.map +1 -1
  69. package/lib/esm/TwilioVideo/index.d.ts +1 -1
  70. package/lib/esm/TwilioVideo/index.d.ts.map +1 -1
  71. package/lib/esm/TwilioVideo/index.js +1 -1
  72. package/lib/esm/TwilioVideo/index.js.map +1 -1
  73. package/lib/esm/state.d.ts +7 -1
  74. package/lib/esm/state.d.ts.map +1 -1
  75. package/lib/esm/state.js +42 -1
  76. package/lib/esm/state.js.map +1 -1
  77. package/lib/tsconfig.tsbuildinfo +1 -1
  78. package/package.json +9 -9
  79. package/src/Forms/form_responses.tsx +133 -2
  80. package/src/Forms/forms.tsx +32 -6
  81. package/src/Forms/forms.v2.tsx +1 -1
  82. package/src/Forms/hooks.tsx +9 -5
  83. package/src/TwilioVideo/TwilioControls.tsx +30 -0
  84. package/src/TwilioVideo/TwilioParticipant.tsx +51 -17
  85. package/src/TwilioVideo/TwilioVideoContext.tsx +78 -0
  86. package/src/TwilioVideo/TwilioVideoRoom.tsx +92 -2
  87. package/src/TwilioVideo/index.ts +1 -0
  88. package/src/state.tsx +37 -0
@@ -9,12 +9,17 @@ import Video, {
9
9
  LocalParticipant,
10
10
  } from 'twilio-video'
11
11
 
12
+ export const SCREEN_SHARE_TRACK_NAME = 'screen-share'
13
+
12
14
  export interface TwilioVideoState {
13
15
  room: Room | null
14
16
  isConnecting: boolean
15
17
  isConnected: boolean
16
18
  localVideoTrack: LocalVideoTrack | null
17
19
  localAudioTrack: LocalAudioTrack | null
20
+ localScreenTrack: LocalVideoTrack | null
21
+ isScreenSharing: boolean
22
+ screenSharingParticipantSid: string | null
18
23
  participants: RemoteParticipant[]
19
24
  error: Error | null
20
25
  isHost: boolean
@@ -27,6 +32,7 @@ export interface TwilioVideoActions {
27
32
  disconnect: () => void
28
33
  toggleVideo: () => Promise<void>
29
34
  toggleAudio: () => void
35
+ toggleScreenShare: () => Promise<void>
30
36
  setIsHost: (isHost: boolean) => void
31
37
  }
32
38
 
@@ -56,6 +62,9 @@ export const TwilioVideoProvider: React.FC<TwilioVideoProviderProps> = ({ childr
56
62
  const [isHost, setIsHost] = useState(false)
57
63
  const [isVideoEnabled, setIsVideoEnabled] = useState(true)
58
64
  const [isAudioEnabled, setIsAudioEnabled] = useState(true)
65
+ const [localScreenTrack, setLocalScreenTrack] = useState<LocalVideoTrack | null>(null)
66
+ const [isScreenSharing, setIsScreenSharing] = useState(false)
67
+ const [screenSharingParticipantSid, setScreenSharingParticipantSid] = useState<string | null>(null)
59
68
 
60
69
  const localTracksRef = useRef<(LocalVideoTrack | LocalAudioTrack)[]>([])
61
70
 
@@ -101,6 +110,18 @@ export const TwilioVideoProvider: React.FC<TwilioVideoProviderProps> = ({ childr
101
110
  setParticipants(prev => prev.filter(p => p.sid !== participant.sid))
102
111
  })
103
112
 
113
+ // Track remote screen sharing for React re-renders
114
+ newRoom.on('trackSubscribed', (track: RemoteTrack, publication, participant: RemoteParticipant) => {
115
+ if (track.kind === 'video' && track.name === SCREEN_SHARE_TRACK_NAME) {
116
+ setScreenSharingParticipantSid(participant.sid)
117
+ }
118
+ })
119
+ newRoom.on('trackUnsubscribed', (track: RemoteTrack, publication, participant: RemoteParticipant) => {
120
+ if (track.kind === 'video' && track.name === SCREEN_SHARE_TRACK_NAME) {
121
+ setScreenSharingParticipantSid(null)
122
+ }
123
+ })
124
+
104
125
  newRoom.on('disconnected', () => {
105
126
  // Stop all local tracks when disconnected
106
127
  localTracksRef.current.forEach(track => {
@@ -110,6 +131,9 @@ export const TwilioVideoProvider: React.FC<TwilioVideoProviderProps> = ({ childr
110
131
  setRoom(null)
111
132
  setLocalVideoTrack(null)
112
133
  setLocalAudioTrack(null)
134
+ setLocalScreenTrack(null)
135
+ setIsScreenSharing(false)
136
+ setScreenSharingParticipantSid(null)
113
137
  setParticipants([])
114
138
  })
115
139
 
@@ -135,6 +159,9 @@ export const TwilioVideoProvider: React.FC<TwilioVideoProviderProps> = ({ childr
135
159
  setRoom(null)
136
160
  setLocalVideoTrack(null)
137
161
  setLocalAudioTrack(null)
162
+ setLocalScreenTrack(null)
163
+ setIsScreenSharing(false)
164
+ setScreenSharingParticipantSid(null)
138
165
  setParticipants([])
139
166
  }, [room])
140
167
 
@@ -160,6 +187,53 @@ export const TwilioVideoProvider: React.FC<TwilioVideoProviderProps> = ({ childr
160
187
  }
161
188
  }, [localAudioTrack, isAudioEnabled])
162
189
 
190
+ const stopScreenShare = useCallback(() => {
191
+ if (localScreenTrack) {
192
+ if (room) {
193
+ room.localParticipant.unpublishTrack(localScreenTrack)
194
+ }
195
+ localScreenTrack.stop()
196
+ localTracksRef.current = localTracksRef.current.filter(t => t !== localScreenTrack)
197
+ setLocalScreenTrack(null)
198
+ setIsScreenSharing(false)
199
+ }
200
+ }, [localScreenTrack, room])
201
+
202
+ const toggleScreenShare = useCallback(async () => {
203
+ if (isScreenSharing) {
204
+ stopScreenShare()
205
+ return
206
+ }
207
+
208
+ try {
209
+ const stream = await navigator.mediaDevices.getDisplayMedia({ video: true })
210
+ const mediaStreamTrack = stream.getVideoTracks()[0]
211
+ const screenTrack = new LocalVideoTrack(mediaStreamTrack, { name: SCREEN_SHARE_TRACK_NAME })
212
+
213
+ if (room) {
214
+ await room.localParticipant.publishTrack(screenTrack)
215
+ }
216
+
217
+ localTracksRef.current.push(screenTrack)
218
+ setLocalScreenTrack(screenTrack)
219
+ setIsScreenSharing(true)
220
+
221
+ // Handle browser "Stop sharing" button
222
+ mediaStreamTrack.onended = () => {
223
+ if (room) {
224
+ room.localParticipant.unpublishTrack(screenTrack)
225
+ }
226
+ screenTrack.stop()
227
+ localTracksRef.current = localTracksRef.current.filter(t => t !== screenTrack)
228
+ setLocalScreenTrack(null)
229
+ setIsScreenSharing(false)
230
+ }
231
+ } catch (err) {
232
+ // User cancelled the screen share picker — not an error
233
+ console.log('Screen share cancelled or failed:', err)
234
+ }
235
+ }, [isScreenSharing, stopScreenShare, room])
236
+
163
237
  // Cleanup on unmount
164
238
  useEffect(() => {
165
239
  return () => {
@@ -178,6 +252,9 @@ export const TwilioVideoProvider: React.FC<TwilioVideoProviderProps> = ({ childr
178
252
  isConnected: !!room,
179
253
  localVideoTrack,
180
254
  localAudioTrack,
255
+ localScreenTrack,
256
+ isScreenSharing,
257
+ screenSharingParticipantSid,
181
258
  participants,
182
259
  error,
183
260
  isHost,
@@ -187,6 +264,7 @@ export const TwilioVideoProvider: React.FC<TwilioVideoProviderProps> = ({ childr
187
264
  disconnect,
188
265
  toggleVideo,
189
266
  toggleAudio,
267
+ toggleScreenShare,
190
268
  setIsHost,
191
269
  }
192
270
 
@@ -3,6 +3,7 @@ import { Box, Grid } from '@mui/material'
3
3
  import { useTwilioVideo } from './TwilioVideoContext'
4
4
  import { TwilioParticipant } from './TwilioParticipant'
5
5
  import { TwilioControlBar } from './TwilioControls'
6
+ import { RemoteParticipant, LocalParticipant } from 'twilio-video'
6
7
 
7
8
  export interface TwilioVideoRoomProps {
8
9
  onLeave?: () => void
@@ -10,6 +11,8 @@ export interface TwilioVideoRoomProps {
10
11
  style?: React.CSSProperties
11
12
  /** Resolve participant identity to a display label. Defaults to empty string. */
12
13
  resolveIdentity?: (identity: string) => string
14
+ /** Whether to show the screen share button. Defaults to true. */
15
+ showScreenShare?: boolean
13
16
  }
14
17
 
15
18
  export const TwilioVideoRoom: React.FC<TwilioVideoRoomProps> = ({
@@ -17,14 +20,101 @@ export const TwilioVideoRoom: React.FC<TwilioVideoRoomProps> = ({
17
20
  onEndForAll,
18
21
  style,
19
22
  resolveIdentity,
23
+ showScreenShare: showScreenShareProp = true,
20
24
  }) => {
21
- const { room, participants } = useTwilioVideo()
25
+ const { room, participants, isScreenSharing, screenSharingParticipantSid } = useTwilioVideo()
22
26
 
23
27
  if (!room) return null
24
28
 
25
29
  const localParticipant = room.localParticipant
26
30
  const hasRemoteParticipants = participants.length > 0
27
31
 
32
+ // Find who is sharing their screen (context-driven so React re-renders properly)
33
+ const screenShareParticipant: RemoteParticipant | LocalParticipant | null = (() => {
34
+ if (isScreenSharing) return localParticipant
35
+ if (screenSharingParticipantSid) {
36
+ return participants.find(p => p.sid === screenSharingParticipantSid) || null
37
+ }
38
+ return null
39
+ })()
40
+
41
+ const isScreenShareActive = screenShareParticipant !== null
42
+
43
+ // All participants for the camera strip (local + remote)
44
+ const allParticipants: (RemoteParticipant | LocalParticipant)[] = [
45
+ localParticipant,
46
+ ...participants,
47
+ ]
48
+
49
+ if (isScreenShareActive) {
50
+ // Presentation layout: screen share large on top, camera strip on bottom
51
+ return (
52
+ <Box
53
+ sx={{
54
+ display: 'flex',
55
+ flexDirection: 'column',
56
+ height: '100%',
57
+ width: '100%',
58
+ backgroundColor: '#1a1a1a',
59
+ ...style,
60
+ }}
61
+ >
62
+ {/* Screen share - main area */}
63
+ <Box
64
+ sx={{
65
+ flex: 1,
66
+ overflow: 'hidden',
67
+ minHeight: 0,
68
+ }}
69
+ >
70
+ <TwilioParticipant
71
+ participant={screenShareParticipant}
72
+ isLocal={screenShareParticipant === localParticipant}
73
+ showScreenShare
74
+ resolveIdentity={resolveIdentity}
75
+ />
76
+ </Box>
77
+
78
+ {/* Camera strip */}
79
+ <Box
80
+ sx={{
81
+ height: 120,
82
+ display: 'flex',
83
+ flexDirection: 'row',
84
+ gap: 1,
85
+ padding: 1,
86
+ overflowX: 'auto',
87
+ flexShrink: 0,
88
+ }}
89
+ >
90
+ {allParticipants.map((p) => (
91
+ <Box
92
+ key={p.sid}
93
+ sx={{
94
+ height: '100%',
95
+ aspectRatio: '4/3',
96
+ flexShrink: 0,
97
+ borderRadius: 1,
98
+ overflow: 'hidden',
99
+ }}
100
+ >
101
+ <TwilioParticipant
102
+ participant={p}
103
+ isLocal={p === localParticipant}
104
+ showScreenShare={false}
105
+ resolveIdentity={resolveIdentity}
106
+ />
107
+ </Box>
108
+ ))}
109
+ </Box>
110
+
111
+ {/* Control bar */}
112
+ <TwilioControlBar onLeave={onLeave} onEndForAll={onEndForAll} showScreenShare={showScreenShareProp} />
113
+ </Box>
114
+ )
115
+ }
116
+
117
+ // Normal layout (no screen share active)
28
118
  return (
29
119
  <Box
30
120
  sx={{
@@ -92,7 +182,7 @@ export const TwilioVideoRoom: React.FC<TwilioVideoRoomProps> = ({
92
182
  </Box>
93
183
 
94
184
  {/* Control bar */}
95
- <TwilioControlBar onLeave={onLeave} onEndForAll={onEndForAll} />
185
+ <TwilioControlBar onLeave={onLeave} onEndForAll={onEndForAll} showScreenShare={showScreenShareProp} />
96
186
  </Box>
97
187
  )
98
188
  }
@@ -1,6 +1,7 @@
1
1
  export {
2
2
  TwilioVideoProvider,
3
3
  useTwilioVideo,
4
+ SCREEN_SHARE_TRACK_NAME,
4
5
  type TwilioVideoState,
5
6
  type TwilioVideoActions,
6
7
  type TwilioVideoContextType,
package/src/state.tsx CHANGED
@@ -2637,6 +2637,43 @@ export const useIntegrations = (options={} as HookOptions<Integration>) => {
2637
2637
  )
2638
2638
  }
2639
2639
 
2640
+ export const useRedactedIntegrations = () => {
2641
+ const session = useSession()
2642
+ const [loadingState, setLoadingState] = useState<LoadedData<Integration[]>>({ status: LoadingStatus.Fetching, value: [] as any })
2643
+ const fetchedRef = useRef(false)
2644
+
2645
+ useEffect(() => {
2646
+ if (fetchedRef.current) return
2647
+ fetchedRef.current = true
2648
+
2649
+ let cancelled = false
2650
+ session.api.integrations.load_redacted({})
2651
+ .then((result: any) => {
2652
+ if (!cancelled) setLoadingState({ status: LoadingStatus.Loaded, value: result.integrations })
2653
+ })
2654
+ .catch((err: any) => {
2655
+ if (!cancelled) setLoadingState({ status: LoadingStatus.Error, value: err })
2656
+ })
2657
+ return () => { cancelled = true }
2658
+ }, [session])
2659
+
2660
+ const updateIntegration = useCallback(async (id: string, updates: Partial<Integration>) => {
2661
+ const result: any = await session.api.integrations.update_settings({ id, updates })
2662
+ setLoadingState((prev: LoadedData<Integration[]>) => {
2663
+ if (prev.status !== LoadingStatus.Loaded) return prev
2664
+ return {
2665
+ ...prev,
2666
+ value: prev.value.map((i: Integration) => i.id === id ? { ...i, ...result.integration } : i),
2667
+ }
2668
+ })
2669
+ }, [session])
2670
+
2671
+ return [
2672
+ loadingState,
2673
+ { updateIntegration },
2674
+ ] as const
2675
+ }
2676
+
2640
2677
  export const usePortalCustomizations = (options={} as HookOptions<PortalCustomization>) => {
2641
2678
  const session = useResolvedSession()
2642
2679
  return useListStateHook(