From 9926682b3686dabf411849c2e4415d5f667f7f06 Mon Sep 17 00:00:00 2001 From: Olivia Pyskoty Date: Thu, 5 May 2022 17:14:41 -0700 Subject: [PATCH 01/34] add conversation logic to join-stream-as-viewer endpoint --- serverless/functions/join-stream-as-viewer.js | 57 ++++++++++++++++--- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/serverless/functions/join-stream-as-viewer.js b/serverless/functions/join-stream-as-viewer.js index a5f491f6..912beae0 100644 --- a/serverless/functions/join-stream-as-viewer.js +++ b/serverless/functions/join-stream-as-viewer.js @@ -40,7 +40,7 @@ module.exports.handler = async (context, event, callback) => { return callback(null, response); } - let room, streamMapItem, userDocument; + let room, streamMapItem, userDocument, conversation; const client = context.getTwilioClient(); @@ -117,7 +117,8 @@ module.exports.handler = async (context, event, callback) => { // Give user read access to user document try { - await streamSyncClient.documents(userDocumentName) + await streamSyncClient + .documents(userDocumentName) .documentPermissions(user_identity) .update({ read: true, write: false, manage: false }); } catch (e) { @@ -133,9 +134,10 @@ module.exports.handler = async (context, event, callback) => { // Give user read access to speakers map try { - await streamSyncClient.syncMaps('speakers') + await streamSyncClient + .syncMaps('speakers') .syncMapPermissions(user_identity) - .update({ read: true, write: false, manage: false }) + .update({ read: true, write: false, manage: false }); } catch (e) { response.setStatusCode(500); response.setBody({ @@ -146,10 +148,11 @@ module.exports.handler = async (context, event, callback) => { }); return callback(null, response); } - + // Give user read access to raised hands map try { - await streamSyncClient.syncMaps(`raised_hands`) + await streamSyncClient + .syncMaps(`raised_hands`) .syncMapPermissions(user_identity) .update({ read: true, write: false, manage: false }); } catch (e) { @@ -165,9 +168,10 @@ module.exports.handler = async (context, event, callback) => { // Give user read access to viewers map try { - await streamSyncClient.syncMaps('viewers') + await streamSyncClient + .syncMaps('viewers') .syncMapPermissions(user_identity) - .update({ read: true, write: false, manage: false }) + .update({ read: true, write: false, manage: false }); } catch (e) { response.setStatusCode(500); response.setBody({ @@ -178,7 +182,42 @@ module.exports.handler = async (context, event, callback) => { }); return callback(null, response); } - + + const conversationsClient = client.conversations.services(context.CONVERSATIONS_SERVICE_SID); + + try { + // Find conversation + conversation = await conversationsClient.conversations(room.sid).fetch(); + } catch (e) { + console.error(e); + response.setStatusCode(500); + response.setBody({ + error: { + message: 'error finding conversation', + explanation: 'Something went wrong when finding a conversation.', + }, + }); + return callback(null, response); + } + + try { + // Add participant to conversation + await conversationsClient.conversations(room.sid).participants.create({ identity: user_identity }); + } catch (e) { + // Ignore "Participant already exists" error (50433) + if (e.code !== 50433) { + console.error(e); + response.setStatusCode(500); + response.setBody({ + error: { + message: 'error creating conversation participant', + explanation: 'Something went wrong when creating a conversation participant.', + }, + }); + return callback(null, response); + } + } + let playbackGrant; try { playbackGrant = await getPlaybackGrant(streamMapItem.data.player_streamer_sid); From 2872c28853f4691a88131b0e1a1f16302853b55a Mon Sep 17 00:00:00 2001 From: Olivia Pyskoty Date: Fri, 6 May 2022 16:48:48 -0700 Subject: [PATCH 02/34] add room_sid to backend --- serverless/functions/create-stream.js | 1 + serverless/functions/join-stream-as-speaker.js | 1 + 2 files changed, 2 insertions(+) diff --git a/serverless/functions/create-stream.js b/serverless/functions/create-stream.js index 9c299f9d..e527d8f5 100644 --- a/serverless/functions/create-stream.js +++ b/serverless/functions/create-stream.js @@ -362,6 +362,7 @@ module.exports.handler = async (context, event, callback) => { viewers_map: 'viewers', raised_hands_map: `raised_hands`, }, + room_sid: room.sid, }); return callback(null, response); }; diff --git a/serverless/functions/join-stream-as-speaker.js b/serverless/functions/join-stream-as-speaker.js index f477da73..d49cf9b2 100644 --- a/serverless/functions/join-stream-as-speaker.js +++ b/serverless/functions/join-stream-as-speaker.js @@ -229,6 +229,7 @@ module.exports.handler = async (context, event, callback) => { raised_hands_map: `raised_hands`, user_document: `user-${user_identity}`, }, + room_sid: room.sid, }); return callback(null, response); }; From b52c2a0a3073a7a9a6442dc446a937ae781c8941 Mon Sep 17 00:00:00 2001 From: Olivia Pyskoty Date: Fri, 6 May 2022 16:48:57 -0700 Subject: [PATCH 03/34] add chatfeature to audience --- .../web/src/components/ChatProvider/index.tsx | 18 +++++++++------- .../src/components/ChatWindow/ChatWindow.tsx | 10 ++++----- .../ChatWindow/MessageList/MessageList.tsx | 13 ++++-------- apps/web/src/components/Player/Player.tsx | 12 +++-------- .../Player/PlayerMenuBar/PlayerMenuBar.tsx | 16 ++++++++------ .../PreJoinScreens/PreJoinScreens.tsx | 14 ++++--------- apps/web/src/state/api/api.ts | 2 ++ apps/web/src/state/appState/appReducer.ts | 21 +++++++------------ 8 files changed, 45 insertions(+), 61 deletions(-) diff --git a/apps/web/src/components/ChatProvider/index.tsx b/apps/web/src/components/ChatProvider/index.tsx index f1857f12..30cc607e 100644 --- a/apps/web/src/components/ChatProvider/index.tsx +++ b/apps/web/src/components/ChatProvider/index.tsx @@ -7,7 +7,7 @@ import useVideoContext from '../../hooks/useVideoContext/useVideoContext'; type ChatContextType = { isChatWindowOpen: boolean; setIsChatWindowOpen: (isChatWindowOpen: boolean) => void; - connect: (token: string) => void; + connect: (token: string, roomSid: string) => void; hasUnreadMessages: boolean; messages: Message[]; conversation: Conversation | null; @@ -16,22 +16,24 @@ type ChatContextType = { export const ChatContext = createContext(null!); export const ChatProvider: React.FC = ({ children }) => { - const { room, onError } = useVideoContext(); + const { onError } = useVideoContext(); const isChatWindowOpenRef = useRef(false); const [isChatWindowOpen, setIsChatWindowOpen] = useState(false); const [conversation, setConversation] = useState(null); const [messages, setMessages] = useState([]); const [hasUnreadMessages, setHasUnreadMessages] = useState(false); + const [videoRoomSid, setVideoRoomSid] = useState(''); const [chatClient, setChatClient] = useState(); const connect = useCallback( - (token: string) => { + (token: string, roomSid: string) => { + if (roomSid) setVideoRoomSid(roomSid); let conversationOptions; if (process.env.REACT_APP_TWILIO_ENVIRONMENT) { conversationOptions = { region: `${process.env.REACT_APP_TWILIO_ENVIRONMENT}-us1` }; } Client.create(token, conversationOptions) - .then(client => { + .then(async client => { //@ts-ignore window.chatClient = client; setChatClient(client); @@ -67,19 +69,19 @@ export const ChatProvider: React.FC = ({ children }) => { }, [isChatWindowOpen]); useEffect(() => { - if (room && chatClient) { + if (videoRoomSid && chatClient) { chatClient - .getConversationByUniqueName(room.sid) + .getConversationByUniqueName(videoRoomSid) .then(newConversation => { //@ts-ignore window.chatConversation = newConversation; setConversation(newConversation); }) - .catch(() => { + .catch(error => { onError(new Error('There was a problem getting the Conversation associated with this room.')); }); } - }, [room, chatClient, onError]); + }, [chatClient, onError, videoRoomSid]); return ( createStyles({ chatWindowContainer: { @@ -28,17 +28,17 @@ const useStyles = makeStyles((theme: Theme) => }, }) ); - // In this component, we are toggling the visibility of the ChatWindow with CSS instead of // conditionally rendering the component in the DOM. This is done so that the ChatWindow is // not unmounted while a file upload is in progress. - export default function ChatWindow() { const classes = useStyles(); const { isChatWindowOpen, messages, conversation } = useChatContext(); - + const { appState } = useAppState(); return ( -