/* eslint-disable no-console */ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: MIT-0 /* eslint-disable no-use-before-define */ /* eslint-disable import/no-unresolved */ /* eslint-disable react/prop-types */ import React, { useState, useEffect, useRef } from 'react'; import { PopOverItem, PopOverSeparator, PopOverSubMenu, IconButton, Dots, useNotificationDispatch, useMeetingManager, ChannelList, ChannelItem, SecondaryButton, } from 'amazon-chime-sdk-component-library-react'; import { MeetingSessionConfiguration } from 'amazon-chime-sdk-js'; import { useHistory } from 'react-router-dom'; import { Persistence, MessageType, associateChannelFlow, createMemberArn, createChannelMembership, createChannel, updateChannel, listChannelMessages, sendChannelMessage, listChannels, listChannelMembershipsForAppInstanceUser, listChannelsModeratedByAppInstanceUser, deleteChannel, describeChannel, listChannelMemberships, deleteChannelMembership, listChannelModerators, listChannelBans, listSubChannels, createChannelBan, deleteChannelBan, createMeeting, createAttendee, getMeeting, endMeeting, createGetAttendeeCallback, listChannelFlows, describeChannelFlow, disassociateChannelFlow, resetAWSClients, } from '../../api/ChimeAPI'; import appConfig from '../../Config'; import { useUserPermission } from '../../providers/UserPermissionProvider'; import mergeArrayOfObjects from '../../utilities/mergeArrays'; import { useChatChannelState, useChatMessagingState, } from '../../providers/ChatMessagesProvider'; import { useAppState } from '../../providers/AppStateProvider'; import { useAuthContext } from '../../providers/AuthProvider'; import ModalManager from './ModalManager'; import routes from '../../constants/routes'; import { PresenceAutoStatus, PresenceMode, PresenceStatusPrefix, PUBLISH_INTERVAL, toPresenceMap, toPresenceMessage, } from '../../utilities/presence'; import './ChannelsWrapper.css'; const ChannelsWrapper = () => { const history = useHistory(); const meetingManager = useMeetingManager(); const dispatch = useNotificationDispatch(); const [modal, setModal] = useState(''); const [selectedMember, setSelectedMember] = useState({}); // TODO change to an empty array when using batch api const [activeChannelModerators, setActiveChannelModerators] = useState([]); const [banList, setBanList] = useState([]); const { userId } = useAuthContext().member; const { member, isAuthenticated } = useAuthContext(); const userPermission = useUserPermission(); const isAuthenticatedRef = useRef(isAuthenticated); const messagingUserArn = `${appConfig.appInstanceArn}/user/${userId}`; const [anchorEl, setAnchorEl] = React.useState(null); const [elasticChannelArnList, setElastiChannelArnList] = useState([]); const [standardChannelArnList, setStandardChannelArnList] = useState([]); const maximumMembershipsAllowed = '1000000'; const handleClose = () => { setAnchorEl(null); }; const handleClick = (event) => { setAnchorEl(event.currentTarget); }; const { activeChannelRef, activeSubChannelRef, channelList, channelListRef, channelListModerator, setChannelListModerator, subChannelList, setSubChannelList, setChannelList, setActiveChannel, setActiveSubChannel, activeChannel, activeSubChannel, activeChannelMemberships, setActiveChannelMemberships, setChannelMessageToken, unreadChannels, setUnreadChannels, hasMembership, meetingInfo, setMeetingInfo, activeChannelFlow, setActiveChannelFlow, activeView, setActiveView, moderatedChannel, setModeratedChannel, subChannelIds, setSubChannelIds, } = useChatChannelState(); const { setMessages } = useChatMessagingState(); const { setAppMeetingInfo } = useAppState(); useEffect(() => { isAuthenticatedRef.current = isAuthenticated; }); const handleSwichViewClick = (e) => { if (activeView === 'Moderator') { setActiveView('User'); setActiveChannel(''); } else { setActiveView('Moderator'); } }; // get all channels useEffect(() => { if (!userId) return; const fetchChannels = async () => { if (activeView === 'User') { const userChannelMemberships = await listChannelMembershipsForAppInstanceUser( userId ); const userChannelList = userChannelMemberships.map( (channelMembership) => { const channelSummary = channelMembership.ChannelSummary; channelSummary.SubChannelId = channelMembership.AppInstanceUserMembershipSummary.SubChannelId; return channelSummary; } ); const tempChannelList = [...userChannelList]; for (const channel of tempChannelList) { if (channel.SubChannelId && !subChannelIds.includes(channel.SubChannelId)) { subChannelIds.push(channel.SubChannelId); } } setSubChannelIds(subChannelIds); const publicChannels = await listChannels( appConfig.appInstanceArn, userId ); const moderatorChannels = await listChannelsModeratedByAppInstanceUser( userId ); const moderatorChannelList = moderatorChannels.map( (channelMembership) => channelMembership.ChannelSummary ); const tempModeratorChannelList = [...moderatorChannelList]; for (const moderatorChannel of tempModeratorChannelList) { const channel = await describeChannel(moderatorChannel.ChannelArn, userId); if (!elasticChannelArnList.includes(channel.ChannelArn) && channel.ElasticChannelConfiguration) { elasticChannelArnList.push(channel.ChannelArn); } } setElastiChannelArnList(elasticChannelArnList); setChannelList( mergeArrayOfObjects( mergeArrayOfObjects(publicChannels, userChannelList, 'ChannelArn'), moderatorChannelList, 'ChannelArn' ) ); } else { setModeratedChannel(activeChannel); } await publishStatusToAllChannels(); }; fetchChannels(); }, [userId, activeView]); useEffect(() => { if (!isAuthenticated) { resetAWSClients(); console.clear(); setActiveView('User'); setActiveChannel(''); setChannelList([]); }; }, [isAuthenticated]); // get channel memberships useEffect(() => { if ( activeChannel.ChannelArn && activeChannel.ElasticChannelConfiguration == null ) { activeChannelRef.current = activeChannel; fetchMemberships(); publishStatusToAllChannels(); } }, [activeChannel.ChannelArn, activeChannel.SubChannelId]); // track channel presence useEffect(() => { if (channelList.length > 0) { channelListRef.current = channelList; startPublishStatusWithInterval(); } if(activeChannel.ElasticChannelConfiguration){ getSubChannelList(activeChannel); } }, [channelList]); // get meeting id useEffect(() => { if (meetingInfo) { setModal('JoinMeeting'); } }, [meetingInfo]); function startPublishStatusWithInterval() { let publishTimeout; (async function publishStatusWithInterval() { if (!isAuthenticatedRef.current) { clearTimeout(publishTimeout); return; } await updateChannelArnLists(); await publishStatusToAllChannels(); publishTimeout = setTimeout(publishStatusWithInterval, PUBLISH_INTERVAL); })(); } function computeAutoStatusForAChannel(channel) { const persistedPresence = JSON.parse(channel.Metadata || '{}').Presence; const isCustomStatus = persistedPresence && persistedPresence.filter((p) => p.u === userId)[0]; if (isCustomStatus) { return null; } if (location.pathname.includes(routes.MEETING)) { return PresenceAutoStatus.Busy; } else if (channel.ChannelArn === activeChannelRef.current.ChannelArn) { return PresenceAutoStatus.Online; } else { return PresenceAutoStatus.Idle; } } const updateChannelArnLists = async () => { for (const channel of channelList) { if (!elasticChannelArnList.includes(channel.ChannelArn) && !standardChannelArnList.includes(channel.ChannelArn)) { let newChannel; if (channel.ElasticChannelConfiguration) { newChannel = channel; } else { newChannel = await describeChannel(channel.ChannelArn, userId); } if (newChannel) { if (newChannel.ElasticChannelConfiguration) { elasticChannelArnList.push(channel.ChannelArn); setElastiChannelArnList(elasticChannelArnList); } else { standardChannelArnList.push(channel.ChannelArn); setStandardChannelArnList(standardChannelArnList); } } } } } async function publishStatusToAllChannels() { const servicePromises = []; for (const channel of channelListRef.current) { const channelType = JSON.parse(channel.Metadata || '{}').ChannelType; if (!channel.SubChannelId && !elasticChannelArnList.includes(channel.ChannelArn) && channelType != 'PUBLIC_ELASTIC') { //Elastic channels doesnt support presence const status = computeAutoStatusForAChannel(channel); if (status) { servicePromises.push(sendChannelMessage( channel.ChannelArn, toPresenceMessage(PresenceMode.Auto, status, true), Persistence.NON_PERSISTENT, MessageType.CONTROL, member, channel.SubChannelId, )); } } } return await Promise.all(servicePromises); } const getBanList = async () => { const banListResponse = await listChannelBans( activeChannel.ChannelArn, 50, null, userId ); setBanList(banListResponse.ChannelBans.map((m) => m.Member.Arn)); }; const getSubChannelList = async (channel) => { const appInstanceUserArn = `${appConfig.appInstanceArn}/user/${userId}`; const subChannelListResponse = await listSubChannels( channel.ChannelArn, userId, ); setSubChannelList([]); if (subChannelListResponse) { const memberSubChannelList = subChannelListResponse.SubChannels.filter( (m) => subChannelIds.includes(m.SubChannelId) ) setSubChannelList(memberSubChannelList); } }; const getChannels = (channel) => { { return ( { e.stopPropagation(); console.log('Calling channel change handler'); channelIdChangeHandler(channel); }} unread={unreadChannels.includes(channel.ChannelArn)} unreadBadgeLabel="New" /> {/* check ElasticChannelConfiguration to know channel is Elastic */} {activeView != 'User' && activeSubChannel.ElasticChannelConfiguration && subChannelList.length > 0 && channel.ChannelArn === activeSubChannel.ChannelArn && userPermission.role === 'moderator' && ( {activeSubChannel.ElasticChannelConfiguration && subChannelList.map((m) => getSubChannels(m))} )} ); } }; const getSubChannels = (subChannelSummary) => { { return ( { e.stopPropagation(); console.log('Calling sub channel change handler'); subChannelIdChangeHandler( subChannelSummary.SubChannelId, subChannelSummary.MembershipCount, activeSubChannel.ChannelArn ); }} /> ); } }; const getSubChannelName = (subChannelId) => { return ( activeSubChannel.Name + "-" + subChannelId.substring(0, 3) + "-" + subChannelId.substring(3, 6) ); }; const joinSubChannel = async (subChannelId) => { try { const membership = await createChannelMembership( activeChannel.ChannelArn, `${appConfig.appInstanceArn}/user/${userId}`, userId, subChannelId ); if (membership) { const memberships = activeChannelMemberships; memberships.push({ Member: membership }); setActiveChannelMemberships(memberships); channelIdChangeHandler(activeChannel); dispatch({ type: 0, payload: { message: `Successfully joined SubChannel ${getSubChannelName(subChannelId)}`, severity: 'success', autoClose: true, }, }); } else { dispatch({ type: 0, payload: { message: 'Error occurred. Unable to join SubChannel.', severity: 'error', autoClose: true, }, }); } } catch (err) { dispatch({ type: 0, payload: { message: `Error occurred. ${err.message}`, severity: 'error', }, }); } }; const banUser = async (memberArn) => { try { await createChannelBan(activeChannel.ChannelArn, memberArn, userId); const newBanList = banList.concat(...banList, memberArn); setBanList(newBanList); dispatch({ type: 0, payload: { message: 'Successfully banned user.', severity: 'success', }, }); } catch { dispatch({ type: 0, payload: { message: 'Error, unable to perform this action.', severity: 'error', }, }); } }; const unbanUser = async (memberArn) => { try { await deleteChannelBan(activeChannel.ChannelArn, memberArn, userId); await getBanList(); dispatch({ type: 0, payload: { message: 'Successfully removed ban.', severity: 'success', }, }); } catch { dispatch({ type: 0, payload: { message: 'Error, unable to perform this action.', severity: 'error', }, }); } }; useEffect(() => { if (activeChannel.ChannelArn && modal === 'Ban') { getBanList(); } }, [activeChannel.ChannelArn, modal]); const onCreateChannel = async (e, newName, mode, privacy, elasticChannelConfiguration) => { e.preventDefault(); if (!newName) { dispatch({ type: 0, payload: { message: 'Error, channel name cannot be blank.', severity: 'error', }, }); } else if (elasticChannelConfiguration && (!elasticChannelConfiguration.MaximumSubChannels || !elasticChannelConfiguration.TargetMembershipsPerSubChannel || !elasticChannelConfiguration.MinimumMembershipPercentage)) { dispatch({ type: 0, payload: { message: 'Error, one or nmore of elastic channel attribute is blank.', severity: 'error', autoClose: false, }, }); } else if (elasticChannelConfiguration && (elasticChannelConfiguration.MaximumSubChannels * elasticChannelConfiguration.TargetMembershipsPerSubChannel > maximumMembershipsAllowed)) { dispatch({ type: 0, payload: { message: 'Error, total allowable memberships across all sub-channels must be less than or equal to 1000000.', severity: 'error', autoClose: false, }, }); } else { const channelArn = await createChannel( appConfig.appInstanceArn, null, newName, mode, privacy, elasticChannelConfiguration, userId ); if (channelArn) { const channel = await describeChannel(channelArn, userId); setModal(''); if (channel) { if (elasticChannelConfiguration !== null) { setModeratedChannel(channel); } setChannelList([...channelList, channel]); dispatch({ type: 0, payload: { message: 'Successfully created channel.', severity: 'success', autoClose: true, }, }); setActiveChannel(channel); channelIdChangeHandler(channel); } else { dispatch({ type: 0, payload: { message: 'Error, could not retrieve channel information.', severity: 'error', autoClose: false, }, }); } } else { dispatch({ type: 0, payload: { message: 'Error, could not create new channel.', severity: 'error', autoClose: false, }, }); } } }; const joinMeeting = async (e) => { e.preventDefault(); if (activeChannel.Metadata) { let metadata = JSON.parse(activeChannel.Metadata); let meetingId = metadata.meetingId; // Getting meeting details let meetingResponse = await getMeeting(meetingId); // Create own attendee and join meeting meetingManager.getAttendee = createGetAttendeeCallback(); const { JoinInfo } = await createAttendee(member.username, member.userId, activeChannel.ChannelArn, JSON.stringify(meetingResponse.Meeting)); const meetingSessionConfiguration = new MeetingSessionConfiguration( JoinInfo.Meeting, JoinInfo.Attendee ); await meetingManager.join(meetingSessionConfiguration); setAppMeetingInfo(JoinInfo.Meeting.MeetingId, member.username); history.push(routes.DEVICE); } }; const startMeeting = async (e) => { e.preventDefault(); let meetingName = `${activeChannel.Name} Instant Meeting`; // Create Meeting Channel and Memberships from existing Channel const meetingChannelArn = await createChannel( appConfig.appInstanceArn, null, meetingName, 'RESTRICTED', 'PRIVATE', null, userId ); const meetingChannel = await describeChannel(meetingChannelArn, userId); channelIdChangeHandler(meetingChannel); const memberships = activeChannelMemberships; memberships.forEach((membership) => createChannelMembership( meetingChannelArn, membership.Member.Arn, userId )); // Create meeting and attendee for self meetingManager.getAttendee = createGetAttendeeCallback(); const { JoinInfo } = await createMeeting(member.username, member.userId, meetingChannelArn); const meetingSessionConfiguration = new MeetingSessionConfiguration( JoinInfo.Meeting, JoinInfo.Attendee ); await meetingManager.join(meetingSessionConfiguration); const meetingId = JoinInfo.Meeting.MeetingId; const meeting = JSON.stringify(JoinInfo.Meeting); // Update meeting channel metadata with meeting info let meetingChannelmetadata = { isMeeting: true, meetingId: meetingId }; await updateChannel( meetingChannelArn, meetingName, 'RESTRICTED', JSON.stringify(meetingChannelmetadata), userId ); // Send the meeting info as a chat message in the existing channel const options = {}; options.Metadata = `{"isMeetingInfo":true}`; let meetingInfoMessage = { meeting: meeting, channelArn: meetingChannelArn, channelName: meetingName, inviter: member.username, }; await sendChannelMessage( activeChannel.ChannelArn, JSON.stringify(meetingInfoMessage), Persistence.NON_PERSISTENT, MessageType.STANDARD, member, null, options ); setAppMeetingInfo(meetingId, member.username); history.push(routes.DEVICE); }; const setCustomStatus = async (e, status) => { e.preventDefault(); await changeStatus(PresenceMode.Custom, status); }; const changeStatus = async (type, status) => { if (!activeChannel.ElasticChannelConfiguration && !activeChannel.SubChannelId) { await sendChannelMessage( activeChannel.ChannelArn, toPresenceMessage(type, status, true), Persistence.NON_PERSISTENT, MessageType.CONTROL, member, activeChannel.SubChannelId ); const isAutomatic = type === PresenceMode.Auto; const persistedPresenceMap = toPresenceMap(activeChannel.Metadata); const customPresenceExists = persistedPresenceMap && persistedPresenceMap[userId]; if (!isAutomatic || customPresenceExists) { if (!activeChannel.ChannelFlowArn) { dispatch({ type: 0, payload: { message: 'Error, enable presence channel flow first.', severity: 'error', }, }); return; } // persist presence using standard message and channel flows const options = {}; options.Metadata = JSON.stringify({ IsPresenceInfo: true, Status: toPresenceMessage(type, status, false), }); await sendChannelMessage( activeChannel.ChannelArn, `changed status to ${status}`, Persistence.PERSISTENT, MessageType.STANDARD, member, activeChannel.SubChannelId, options ); } } }; const joinChannel = async (e) => { e.preventDefault(); const membership = await createChannelMembership( activeChannel.ChannelArn, `${appConfig.appInstanceArn}/user/${userId}`, userId, activeChannel.SubChannelId ); if (membership) { const memberships = activeChannelMemberships; memberships.push({ Member: membership }); setActiveChannelMemberships(memberships); channelIdChangeHandler(activeChannel); dispatch({ type: 0, payload: { message: `Successfully joined ${activeChannel.Name}`, severity: 'success', autoClose: true, }, }); } else { dispatch({ type: 0, payload: { message: 'Error occurred. Unable to join channel.', severity: 'error', autoClose: true, }, }); } }; const onAddMember = async () => { if (!selectedMember) { dispatch({ type: 0, payload: { message: 'Error, user name cannot be blank.', severity: 'error', }, }); return; } try { const membership = await createChannelMembership( activeChannel.ChannelArn, `${appConfig.appInstanceArn}/user/${selectedMember.value}`, userId, activeChannel.SubChannelId ); const memberships = activeChannelMemberships; memberships.push({ Member: membership }); setActiveChannelMemberships(memberships); channelIdChangeHandler(activeChannel); dispatch({ type: 0, payload: { message: `New ${selectedMember.label} successfully added to ${activeChannel.Name}`, severity: 'success', autoClose: true, }, }); setModal(''); } catch (err) { if (activeChannel.ElasticChannelConfiguration) { dispatch({ type: 0, payload: { message: `Error occurred. ${err.message}`, severity: 'error', autoClose: true, }, }); } else { dispatch({ type: 0, payload: { message: 'Error occurred. Member not added to channel.', severity: 'error', autoClose: true, }, }); } } }; const onManageChannelFlow = async () => { if (!selectedMember) { dispatch({ type: 0, payload: { message: 'Error, channel flow name cannot be blank.', severity: 'error', }, }); return; } if (selectedMember.value === 'None') { try { await disassociateChannelFlow( activeChannel.ChannelArn, activeChannelFlow.ChannelFlowArn, userId ); setActiveChannelFlow({}); dispatch({ type: 0, payload: { message: `Channel flow successfully removed from ${activeChannel.Name}`, severity: 'success', autoClose: true, }, }); setModal(''); } catch { dispatch({ type: 0, payload: { message: `Error occurred. Channel flow could not removed from ${activeChannel.Name}`, severity: 'error', autoClose: true, }, }); } return; } try { await associateChannelFlow( activeChannel.ChannelArn, selectedMember.value, userId ); let flow = { Name: selectedMember.label, ChannelFlowArn: selectedMember.value }; setActiveChannelFlow(flow); dispatch({ type: 0, payload: { message: `Channel flow ${selectedMember.label} successfully associated with ${activeChannel.Name}`, severity: 'success', autoClose: true, }, }); setModal(''); } catch { dispatch({ type: 0, payload: { message: `Error occurred. Channel flow not associated with channel ${activeChannel.Name}`, severity: 'error', autoClose: true, }, }); } }; const loadChannelFlow = async (channel) => { if (channel.ChannelFlowArn == null) { setActiveChannelFlow({}); } else { let flow; try { flow = await describeChannelFlow(channel.ChannelFlowArn); setActiveChannelFlow(flow); } catch (err) { console.error('ERROR', err); } } }; const channelIdChangeHandler = async (channel) => { if (activeChannel.ChannelArn === channel.ChannelArn && !activeChannel.SubChannelId) return; let mods = []; setActiveChannelModerators([]); var isModerator = false; const channelType = JSON.parse(channel.Metadata || '{}').ChannelType; // Moderator is for channel only, not subChannel if (!channel.SubChannelId) { try { mods = await listChannelModerators(channel.ChannelArn, userId); setActiveChannelModerators(mods); } catch (err) { if (channel.Privacy != 'PUBLIC') console.error('ERROR', err); } isModerator = mods?.find( (moderator) => moderator.Moderator.Arn === messagingUserArn ) || false; } // Assessing user role for given channel userPermission.setRole(isModerator ? 'moderator' : 'user'); const newChannel = await describeChannel(channel.ChannelArn, userId); newChannel.SubChannelId = channel.SubChannelId; channel = newChannel; // listChannelMessages is available to regular channels and subChannels if ( (!channel.ElasticChannelConfiguration && channelType != 'PUBLIC_ELASTIC') || (channel.SubChannelId) ) { const newMessages = await listChannelMessages( channel.ChannelArn, userId, channel.SubChannelId ); setMessages(newMessages.Messages); setChannelMessageToken(newMessages.NextToken); } else if (channelType == 'PUBLIC_ELASTIC' && !channel.SubChannelId) { setMessages([]); } setActiveChannel(channel); if (channel.ElasticChannelConfiguration && isModerator) { setActiveSubChannel(channel); setActiveView('Moderator'); } else { setActiveSubChannel({}); } await loadChannelFlow(channel); setUnreadChannels(unreadChannels.filter((c) => c !== channel.ChannelArn)); if ( !channel.SubChannelId && isModerator && channel.ElasticChannelConfiguration ) { // check ElasticChannelConfiguration to know channel is Elastic getSubChannelList(channel); } }; const subChannelIdChangeHandler = async ( subChannelId, membershipCount, elasticChannelArn ) => { if (activeSubChannel.SubChannelId === subChannelId) return; const subChannel = { ChannelArn: elasticChannelArn, Name: getSubChannelName(subChannelId), SubChannelId: subChannelId, MembershipCount: membershipCount, }; const newMessages = await listChannelMessages( elasticChannelArn, userId, subChannelId ); setMessages(newMessages.Messages); setChannelMessageToken(newMessages.NextToken); setActiveChannel(subChannel); }; const handleChannelDeletion = async (e, channelArn, channelMetadata) => { e.preventDefault(); await deleteChannel(channelArn, userId); const newChannelList = channelList.filter( (channel) => channel.ChannelArn !== channelArn ); setChannelList(newChannelList); setActiveChannel(''); setMessages([]); setModal(''); setActiveView('User'); dispatch({ type: 0, payload: { message: 'Channel successfully deleted.', severity: 'success', autoClose: true, }, }); // If the channel was a meeting channel, end the associated meeting if (channelMetadata) { const metadata = JSON.parse(channelMetadata); if (metadata.isMeeting) { await endMeeting(metadata.meetingId); } } }; const formatMemberships = (memArr) => memArr.flatMap((m) => m.Member.Arn !== messagingUserArn ? [{ value: m.Member.Arn, label: m.Member.Name }] : [] ); const fetchMemberships = async () => { const channelType = JSON.parse(activeChannel.Metadata || '{}').ChannelType; if (!activeChannel.SubChannelId && !activeChannel.ElasticChannelConfiguration && channelType == 'PUBLIC_ELASTIC') { setActiveChannelMemberships([]); } else { const memberships = await listChannelMemberships( activeChannel.ChannelArn, userId, activeChannel.SubChannelId ); setActiveChannelMemberships(memberships); } }; const handlePickerChange = (changes) => { setSelectedMember(changes); }; const handleJoinMeeting = async (e, meeting, meetingChannelArn) => { e.preventDefault(); const meetingChannel = { ChannelArn: meetingChannelArn }; await channelIdChangeHandler(meetingChannel); meetingManager.getAttendee = createGetAttendeeCallback(); const { JoinInfo } = await createAttendee(member.username, member.userId, meetingChannelArn, meeting); const meetingSessionConfiguration = new MeetingSessionConfiguration( JoinInfo.Meeting, JoinInfo.Attendee ); await meetingManager.join(meetingSessionConfiguration); setAppMeetingInfo(JoinInfo.Meeting.MeetingId, member.username); setModal(''); setMeetingInfo(null); history.push(routes.DEVICE); }; const handleMessageAll = async (e, meetingChannelArn) => { e.preventDefault(); setModal(''); setMeetingInfo(null); const meetingChannel = { ChannelArn: meetingChannelArn }; await channelIdChangeHandler(meetingChannel); }; const handleDeleteMemberships = () => { try { deleteChannelMembership( activeChannel.ChannelArn, selectedMember.value, userId ); dispatch({ type: 0, payload: { message: 'Successfully removed members from the channel.', severity: 'success', autoClose: true, }, }); setSelectedMember({}); } catch (err) { dispatch({ type: 0, payload: { message: 'Error, unable to remove members.', severity: 'error', }, }); } }; const leaveSubChannel = async (subChannelId) => { try { const subId = subChannelId != null? subChannelId : activeChannel.SubChannelId; await deleteChannelMembership( activeChannel.ChannelArn, createMemberArn(userId), userId, subId ); dispatch({ type: 0, payload: { message: `Successfully left subChannel ${getSubChannelName(subId)}.`, severity: 'success', autoClose: true, }, }); } catch (err) { dispatch({ type: 0, payload: { message: 'Error, unable to leave the sub channel.', severity: 'error', }, }); } }; const handleLeaveChannel = async () => { try { await deleteChannelMembership( activeChannel.ChannelArn, createMemberArn(userId), userId ); dispatch({ type: 0, payload: { message: `Successfully left ${activeChannel.Name}.`, severity: 'success', autoClose: true, }, }); setSelectedMember({}); } catch (err) { dispatch({ type: 0, payload: { message: 'Error, unable to leave the channel.', severity: 'error', }, }); } }; const [isRestricted, setIsRestricted] = useState( activeChannel.Mode === 'RESTRICTED' ); useEffect(() => { setIsRestricted(activeChannel.Mode === 'RESTRICTED'); }, [activeChannel]); const loadUserActions = (role, channel) => { const map = channel.Metadata && JSON.parse(channel.Metadata).Presence && Object.fromEntries( JSON.parse(channel.Metadata).Presence?.map((entry) => [ entry.u, entry.s, ]) ); const status = (map && map[member.userId]) || PresenceStatusPrefix.Auto; const presenceStatusOption = ( changeStatus(PresenceMode.Auto, 'Online')} checked={status.startsWith(PresenceStatusPrefix.Auto)} children={Automatic} /> changeStatus(PresenceMode.Wfh, 'Working from Home')} checked={status.startsWith(PresenceStatusPrefix.Wfh)} children={Working from Home} /> setModal('CustomStatus')} checked={status.startsWith(PresenceStatusPrefix.Custom)} children={Custom{status.startsWith(PresenceStatusPrefix.Custom) ? '(' + status.substr(status.indexOf('|') + 1) + ')' : ''}} /> ); const joinChannelOption = ( Join Channel ); const viewDetailsOption = ( setModal('ViewDetails')} > View channel details ); const manageChannelFlowOption = ( setModal('ManageChannelFlow')} > Manage channel flow ); const editChannelOption = ( setModal('EditChannel')} > Edit channel ); const viewMembersOption = ( setModal('ViewMembers')} > View members ); const addMembersOption = ( setModal('AddMembers')} > Add members ); const manageMembersOption = ( setModal('ManageMembers')} > Manage members ); const banOrAllowOption = ( setModal('Ban')}> Ban/Allow members ); const startMeetingOption = ( Start meeting ); const joinMeetingOption = ( Join meeting ); const leaveChannelOption = ( setModal('LeaveChannel')} > { (activeChannel.SubChannelId && role === 'moderator') ? (Leave subchannel) : (Leave channel) } ); const deleteChannelOption = ( setModal('DeleteChannel')} > Delete channel ); const listSubChannelsOption = ( setModal('ListSubChannel')} > List subchannels ); const joinSubChannelOption = ( setModal('JoinSubChannel')} > Join subchannel ); const meetingModeratorActions = [ viewDetailsOption, , addMembersOption, manageMembersOption, , joinMeetingOption, , leaveChannelOption, deleteChannelOption, ]; const meetingMemberActions = [ viewDetailsOption, , viewMembersOption, , joinMeetingOption, , leaveChannelOption, ]; const moderatorActions = [ presenceStatusOption, , viewDetailsOption, editChannelOption, , addMembersOption, manageMembersOption, banOrAllowOption, , manageChannelFlowOption, , startMeetingOption, , leaveChannelOption, deleteChannelOption, ]; const elasticChannelModeratorActions = [ viewDetailsOption, , addMembersOption, banOrAllowOption, , leaveChannelOption, deleteChannelOption, , listSubChannelsOption, joinSubChannelOption, ]; const restrictedMemberActions = [ presenceStatusOption, , viewDetailsOption, , viewMembersOption, , startMeetingOption, , leaveChannelOption, ]; const unrestrictedMemberActions = [ presenceStatusOption, , viewDetailsOption, , viewMembersOption, addMembersOption, , leaveChannelOption, ]; const noMeetingModeratorActions = [ viewDetailsOption, editChannelOption, , addMembersOption, manageMembersOption, banOrAllowOption, , manageChannelFlowOption, , leaveChannelOption, deleteChannelOption, ]; const noMeetingRestrictedMemberActions = [ viewDetailsOption, , viewMembersOption, , leaveChannelOption, ]; const subChannelModeratorActions = [ viewDetailsOption, , manageMembersOption, , viewMembersOption, , leaveChannelOption, ]; const subChannelMemberActions = [ viewMembersOption, , leaveChannelOption, ]; const noMeetingUnrestrictedMemberActions = [ viewDetailsOption, , viewMembersOption, addMembersOption, , startMeetingOption, , leaveChannelOption, ]; const nonMemberActions = [ joinChannelOption, viewDetailsOption, viewMembersOption, ]; //User is a moderator of Elastic Channel if (activeChannel.ElasticChannelConfiguration && role === 'moderator') { return elasticChannelModeratorActions; } if (activeChannel.SubChannelId && role === 'user') { return subChannelMemberActions; } if (!hasMembership) { return nonMemberActions; } //User is moderator of a SubChannel if (activeChannel.SubChannelId && role === 'moderator') { return subChannelModeratorActions; } if (appConfig.apiGatewayInvokeUrl) { if (channel.Metadata) { let metadata = JSON.parse(channel.Metadata); if (metadata.isMeeting) { return role === 'moderator' ? meetingModeratorActions : meetingMemberActions; } } if (role === 'moderator') { return moderatorActions; } return isRestricted ? restrictedMemberActions : unrestrictedMemberActions; } if (role === 'moderator') { return noMeetingModeratorActions; } return isRestricted ? noMeetingRestrictedMemberActions : noMeetingUnrestrictedMemberActions; }; return ( <>
{activeView != 'Moderator' ? (
Channels
setModal('NewChannel')} icon={} />
) : (
)} {activeView == 'User' ? channelList.map((channel) => getChannels(channel)) : getChannels(moderatedChannel)}
); }; export default ChannelsWrapper;