diff --git a/Project/package-lock.json b/Project/package-lock.json index 4548312..bb83cd2 100644 --- a/Project/package-lock.json +++ b/Project/package-lock.json @@ -8,12 +8,12 @@ "name": "ACSCallingSample", "version": "1.0.0", "dependencies": { - "@azure/communication-calling": "1.33.2-beta.1", + "@azure/communication-calling": "1.34.1-beta.2", "@azure/communication-calling-effects": "1.1.1-beta.1", "@azure/communication-common": "^2.3.0", "@azure/communication-identity": "^1.3.0", "@azure/communication-network-traversal": "^1.1.0-beta.1", - "@azure/communication-rooms": "1.1.1", + "@azure/communication-rooms": "1.2.0", "@azure/logger": "^1.0.3", "@azure/msal-browser": "^2.33.0", "@azure/msal-node": "^1.17.1", @@ -69,9 +69,9 @@ } }, "node_modules/@azure/communication-calling": { - "version": "1.33.2-beta.1", - "resolved": "https://registry.npmjs.org/@azure/communication-calling/-/communication-calling-1.33.2-beta.1.tgz", - "integrity": "sha512-kwMh8RF65dJkk+SDog+DtMD/UZWxDvObd+OSatBQmsFOX6rwAc5NVik0iyqnLk8bm+5Xdieb6AotQaKDgOd4zg==", + "version": "1.34.1-beta.2", + "resolved": "https://registry.npmjs.org/@azure/communication-calling/-/communication-calling-1.34.1-beta.2.tgz", + "integrity": "sha512-rh2JHu4UekXq/Gc/+hF3i2VCLpZYa6fAl7++ByIG0gp6jUoFkFQfi3wT8ziZbAcp1AbSh7BNy9TQKTQr5V1tzg==", "dependencies": { "@azure/communication-common": "^2.3.0", "@azure/logger": "^1.0.3" @@ -178,9 +178,9 @@ "integrity": "sha512-86GgN2vzfUu7m9Wcj63iUkuDzFNYFVmjeDm2GzWpUk+opB0pEpMsw6ePCMrhYkumz2C1ihqtZzOMAg7FiXcNoQ==" }, "node_modules/@azure/communication-rooms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@azure/communication-rooms/-/communication-rooms-1.1.1.tgz", - "integrity": "sha512-zz/vEW4CLep8McLzzJd17jmQh4cyKv4kwSEzgZcxaAHO/re17cruD9NyATQ3fpu0FQ7n+4ikYq5ePZB6nIGBlw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@azure/communication-rooms/-/communication-rooms-1.2.0.tgz", + "integrity": "sha512-jT61/d0MCv7UL4AcEf8lhn5qK709NsuEv7iLIIx6jGbs55U/PVe5DQvPm8FG9hb6sUH1oEP9L1BF8LXmfT8pgg==", "dependencies": { "@azure/communication-common": "^2.3.1", "@azure/core-auth": "^1.9.0", diff --git a/Project/package.json b/Project/package.json index f1e85cc..643548d 100644 --- a/Project/package.json +++ b/Project/package.json @@ -3,12 +3,12 @@ "version": "1.0.0", "private": true, "dependencies": { - "@azure/communication-calling": "1.33.2-beta.1", + "@azure/communication-calling": "1.34.1-beta.2", "@azure/communication-calling-effects": "1.1.1-beta.1", "@azure/communication-common": "^2.3.0", "@azure/communication-identity": "^1.3.0", "@azure/communication-network-traversal": "^1.1.0-beta.1", - "@azure/communication-rooms": "1.1.1", + "@azure/communication-rooms": "1.2.0", "@azure/logger": "^1.0.3", "@azure/msal-browser": "^2.33.0", "@azure/msal-node": "^1.17.1", diff --git a/Project/src/MakeCall/MakeCall.js b/Project/src/MakeCall/MakeCall.js index 822e8bb..b7503ea 100644 --- a/Project/src/MakeCall/MakeCall.js +++ b/Project/src/MakeCall/MakeCall.js @@ -3,7 +3,7 @@ import { CallClient, LocalVideoStream, Features, CallAgentKind, VideoStreamRende import { AzureCommunicationTokenCredential, createIdentifierFromRawId} from '@azure/communication-common'; import { PrimaryButton } from '@fluentui/react/lib/Button'; import { TextField } from '@fluentui/react/lib/TextField'; -import { MessageBar, MessageBarType } from '@fluentui/react'; +import { MessageBar, MessageBarType, Toggle } from '@fluentui/react'; import { Icon } from '@fluentui/react/lib/Icon'; import IncomingCallCard from './IncomingCallCard'; import CallCard from '../MakeCall/CallCard'; @@ -34,9 +34,13 @@ export default class MakeCall extends React.Component { this.meetingLink = null; this.meetingId = null; this.passcode = null; - this.presenterUserId = null; - this.attendeeUserId = null; - this.consumerUserId = null; + this.presenterUserIds = null; + this.collaboratorUserIds = null; + this.attendeeUserIds = null; + this.consumerUserIds = null; + this.patchRoomId = null; + this.patchParticipantId = null; + this.patchParticipantRole = null; this.threadId = null; this.messageId = null; this.organizerId = null; @@ -62,7 +66,8 @@ export default class MakeCall extends React.Component { showPreCallDiagnostcisResults: false, showCustomContext: false, roomId: undefined, - showCreateRoomPanel: false, + roomPstnDialOutEnabled: true, + showManageRoomsPanel: false, xHeadersCount: 1, xHeadersMaxCount: 5, isPreCallDiagnosticsCallInProgress: false, @@ -374,7 +379,7 @@ export default class MakeCall extends React.Component { createRoom = async () => { try { - const roomId = await utils.createRoom(this.presenterUserId.value, this.attendeeUserId.value, this.consumerUserId.value); + const roomId = await utils.createRoom(this.state.roomPstnDialOutEnabled, this.presenterUserIds.value, this.collaboratorUserIds.value, this.attendeeUserIds.value, this.consumerUserIds.value); console.log('Room id created: ', roomId); this.setState({ roomId }); } catch (e) { @@ -382,6 +387,15 @@ export default class MakeCall extends React.Component { } }; + updateParticipant = async () => { + try { + await utils.updateParticipant(this.patchRoomId.value, this.patchParticipantId.value, this.patchParticipantRole.value); + console.log('Participant updated successfully'); + } catch (e) { + console.error('Failed to update participant ', e); + } + }; + joinTeamsMeeting = async (withVideo, micMuted = false) => { try { const callOptions = await this.getCallOptions({video: withVideo, micMuted: micMuted}); @@ -1175,7 +1189,7 @@ this.callAgent.on('incomingCall', async (args) => { disabled={this.state.call || !this.state.loggedIn} label="Rooms id" value={ this.state.roomId } - placeholder="" + placeholder="" onChange={(e) => this.setState({ roomId: e.target.value })}/> @@ -1193,44 +1207,83 @@ this.callAgent.on('incomingCall', async (args) => { this.setState({ showCreateRoomPanel: !this.state.showCreateRoomPanel })}> + text="Manage Rooms" + onClick={() => this.setState({ showManageRoomsPanel: !this.state.showManageRoomsPanel })}> { - this.state.showCreateRoomPanel && + this.state.showManageRoomsPanel &&
-

Create a Room

+

Manage Rooms

+

Create a Room

+
+ { + console.log(this.state.roomPstnDialOutEnabled); + this.setState({roomPstnDialOutEnabled: !this.state.roomPstnDialOutEnabled}) + }} /> +
this.presenterUserId = val} /> + componentRef={(val) => this.presenterUserIds = val} />
this.attendeeUserId = val} /> + componentRef={(val) => this.collaboratorUserIds = val} />
this.consumerUserId = val} /> + componentRef={(val) => this.attendeeUserIds = val} />
-
- + this.consumerUserIds = val} /> +
+ this.createRoom()}> - + + +

Update a Participant

+
+
+ this.patchRoomId = val} /> +
+
+ this.patchParticipantId = val} /> +
+
+ this.patchParticipantRole = val} /> +
+ this.updateParticipant()}> + +
} diff --git a/Project/src/Utils/Utils.js b/Project/src/Utils/Utils.js index c01873d..28378ea 100644 --- a/Project/src/Utils/Utils.js +++ b/Project/src/Utils/Utils.js @@ -97,8 +97,23 @@ export const utils = { } throw new Error('Failed to get Teams User Acccess token'); }, - createRoom: async (presenterUserId, attendeeUserId, consumerUserId) => { + createRoom: async (pstnDialOutEnabled, presenterUserIds, collaboratorUserIds, attendeeUserIds, consumerUserIds) => { try { + const data = {}; + data.pstnDialOutEnabled = pstnDialOutEnabled; + if (presenterUserIds) { + data.presenterUserIds = presenterUserIds.split(',').map(id => id.trim()); + } + if (collaboratorUserIds) { + data.collaboratorUserIds = collaboratorUserIds.split(',').map(id => id.trim()); + } + if (attendeeUserIds) { + data.attendeeUserIds = attendeeUserIds.split(',').map(id => id.trim()); + } + if (consumerUserIds) { + data.consumerUserIds = consumerUserIds.split(',').map(id => id.trim()); + } + const response = await axios({ url: 'createRoom', method: 'POST', @@ -106,15 +121,41 @@ export const utils = { 'Accept': 'application/json, text/plain, */*', 'Content-type': 'application/json' }, - data: JSON.stringify({ presenterUserId, attendeeUserId, consumerUserId }) - }) - + data: JSON.stringify(data) + }); + console.log('Room created successfully:', response.data); return response.data.roomId; } catch (error) { + console.error('Error creating room:', error); throw error.response.data.message; } }, + updateParticipant: async (patchRoomId, patchParticipantId, patchParticipantRole) => { + try { + if (!patchRoomId.trim() || !patchParticipantId.trim() || !patchParticipantRole.trim()) { + throw new Error('All parameters (patchRoomId, patchParticipantId, patchParticipantRole) must be non-empty strings without trailing whitespace.'); + } + + const response = await axios({ + url: 'updateParticipant', + method: 'PATCH', + headers: { + 'Accept': 'application/json, text/plain, */*', + 'Content-type': 'application/json' + }, + data: JSON.stringify({ + patchRoomId: patchRoomId.trim(), + patchParticipantId: patchParticipantId.trim(), + patchParticipantRole: patchParticipantRole.trim() + }) + }); + console.log('Participant updated successfully:', response.data); + } catch (error) { + console.error('Error updating participant:', error); + throw error.response?.data?.message || error.message; + } + }, getIdentifierText: (identifier) => { if (isCommunicationUserIdentifier(identifier)) { return identifier.communicationUserId; diff --git a/Project/webpack.config.js b/Project/webpack.config.js index 473e26b..a67e938 100644 --- a/Project/webpack.config.js +++ b/Project/webpack.config.js @@ -238,18 +238,39 @@ module.exports = { devServer.app.post('/createRoom', async (req, res) => { try { let participants = []; - req.body.presenterUserId ? participants.push({ - id: { communicationUserId: req.body.presenterUserId }, - role: "Presenter" - }) : null; - req.body.attendeeUserId ? participants.push({ - id: { communicationUserId: req.body.attendeeUserId }, - role: "Attendee" - }) : null; - req.body.consumerUserId ? participants.push({ - id: { communicationUserId: req.body.consumerUserId }, - role: "Consumer" - }) : null; + console.log('req.body:', req.body); + if (req.body.presenterUserIds && Array.isArray(req.body.presenterUserIds)) { + req.body.presenterUserIds.forEach(presenterUserId => { + participants.push({ + id: { communicationUserId: presenterUserId }, + role: "Presenter" + }); + }); + } + if (req.body.collaboratorUserIds && Array.isArray(req.body.collaboratorUserIds)) { + req.body.collaboratorUserIds.forEach(collaboratorUserId => { + participants.push({ + id: { communicationUserId: collaboratorUserId }, + role: "Collaborator" + }); + }); + } + if (req.body.attendeeUserIds && Array.isArray(req.body.attendeeUserIds)) { + req.body.attendeeUserIds.forEach(attendeeUserId => { + participants.push({ + id: { communicationUserId: attendeeUserId }, + role: "Attendee" + }); + }); + } + if (req.body.consumerUserIds && Array.isArray(req.body.consumerUserIds)) { + req.body.consumerUserIds.forEach(consumerUserId => { + participants.push({ + id: { communicationUserId: consumerUserId }, + role: "Consumer" + }); + }); + } if (participants.length === 0) { res.status(400).json({ @@ -261,7 +282,7 @@ module.exports = { console.log('participants:', participants); const validFrom = new Date(Date.now()); const validUntil = new Date(validFrom.getTime() + 60 * 60 * 1000); - const pstnDialOutEnabled = true; + const pstnDialOutEnabled = req.body.pstnDialOutEnabled; const roomsClient = new RoomsClient(config.connectionString); const createRoom = await roomsClient.createRoom({ validFrom, @@ -283,6 +304,26 @@ module.exports = { throw e; } }); + devServer.app.patch('/updateParticipant', async (req, res) => { + try { + const roomId = req.body.patchRoomId; + const participantId = req.body.patchParticipantId; + const participantRole = req.body.patchParticipantRole; + const roomsClient = new RoomsClient(config.connectionString); + const participant = [ + { + id: { communicationUserId: participantId}, + role: participantRole, + }, + ]; + await roomsClient.addOrUpdateParticipants(roomId, participant); + res.setHeader('Content-Type', 'application/json'); + res.status(200).json({message: 'Participant updated successfully'}); + } catch (e) { + console.error(e); + throw e; + } + }); return middlewares; }