import { useAudioVideo, useMeetingManager } from 'amazon-chime-sdk-component-library-react'; import { DataMessage } from 'amazon-chime-sdk-js'; import React, { useEffect, useReducer, createContext, useContext, FC, useCallback, PropsWithChildren } from 'react'; import { DATA_MESSAGE_LIFETIME_MS, DATA_MESSAGE_TOPIC } from '../../constants'; import { useAppState } from '../AppStateProvider'; import { DataMessagesActionType, initialState, ChatDataMessage, reducer } from './state'; interface DataMessagesStateContextType { sendMessage: (message: string) => void; messages: ChatDataMessage[]; } const DataMessagesStateContext = createContext(undefined); export const DataMessagesProvider: FC = ({ children }) => { const { localUserName } = useAppState(); const meetingManager = useMeetingManager(); const audioVideo = useAudioVideo(); const [state, dispatch] = useReducer(reducer, initialState); useEffect(() => { if (!audioVideo) { return; } audioVideo.realtimeSubscribeToReceiveDataMessage(DATA_MESSAGE_TOPIC, handler); return () => { audioVideo.realtimeUnsubscribeFromReceiveDataMessage(DATA_MESSAGE_TOPIC); }; }, [audioVideo]); const handler = useCallback( (dataMessage: DataMessage) => { if (!dataMessage.throttled) { const isSelf = dataMessage.senderAttendeeId === meetingManager.meetingSession?.configuration.credentials?.attendeeId; if (isSelf) { dispatch({ type: DataMessagesActionType.ADD, payload: { message: new TextDecoder().decode(dataMessage.data), senderAttendeeId: dataMessage.senderAttendeeId, timestamp: dataMessage.timestampMs, senderName: dataMessage.senderExternalUserId, isSelf: true, }, }); } else { const data = dataMessage.json(); dispatch({ type: DataMessagesActionType.ADD, payload: { message: data.message, senderAttendeeId: dataMessage.senderAttendeeId, timestamp: dataMessage.timestampMs, senderName: data.senderName, isSelf: false, }, }); } } else { console.warn('DataMessage is throttled. Please resend'); } }, [meetingManager] ); const sendMessage = useCallback( (message: string) => { if ( !meetingManager || !meetingManager.meetingSession || !meetingManager.meetingSession.configuration.credentials || !meetingManager.meetingSession.configuration.credentials.attendeeId || !audioVideo ) { return; } const payload = { message, senderName: localUserName }; const senderAttendeeId = meetingManager.meetingSession.configuration.credentials.attendeeId; audioVideo.realtimeSendDataMessage(DATA_MESSAGE_TOPIC, payload, DATA_MESSAGE_LIFETIME_MS); handler( new DataMessage( Date.now(), DATA_MESSAGE_TOPIC, new TextEncoder().encode(message), senderAttendeeId, localUserName ) ); }, [meetingManager, audioVideo] ); const value = { sendMessage, messages: state.messages, }; return {children}; }; export const useDataMessages = (): { sendMessage: (message: string) => void; messages: ChatDataMessage[]; } => { const meetingManager = useMeetingManager(); const context = useContext(DataMessagesStateContext); if (!meetingManager || !context) { throw new Error( 'Use useDataMessages hook inside DataMessagesProvider. Wrap DataMessagesProvider under MeetingProvider.' ); } return context; };