From 0343a0a84ba6709a403eed3b241f779d7d4606e8 Mon Sep 17 00:00:00 2001 From: Yasmim Nagat <117310290+yasnagat@users.noreply.github.com> Date: Thu, 20 Aug 2026 18:51:07 +0000 Subject: [PATCH 1/2] chore(deps): patch `image-size` dependency (#41887) --- .../image-size-npm-1.2.1-e285f3c080.patch | 31 +++++++++++++++++++ apps/meteor/package.json | 2 +- package.json | 1 + yarn.lock | 15 +++++++-- 4 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 .yarn/patches/image-size-npm-1.2.1-e285f3c080.patch diff --git a/.yarn/patches/image-size-npm-1.2.1-e285f3c080.patch b/.yarn/patches/image-size-npm-1.2.1-e285f3c080.patch new file mode 100644 index 0000000000000..f2880176dccd5 --- /dev/null +++ b/.yarn/patches/image-size-npm-1.2.1-e285f3c080.patch @@ -0,0 +1,31 @@ +diff --git a/dist/types/icns.js b/dist/types/icns.js +index f2bfafef3723cb423b110304815e56e3f97f81e0..87e0b73af9d789c3b9b5fb0c303fab35b62d6462 100644 +--- a/dist/types/icns.js ++++ b/dist/types/icns.js +@@ -92,6 +92,9 @@ exports.ICNS = { + }; + while (imageOffset < fileLength && imageOffset < inputLength) { + imageHeader = readImageHeader(input, imageOffset); ++ if (imageHeader[1] <= 0) { ++ break; ++ } + imageSize = getImageSize(imageHeader[0]); + imageOffset += imageHeader[1]; + result.images.push(imageSize); +diff --git a/dist/types/jxl.js b/dist/types/jxl.js +index f557b0aea7e679d2cb84d9915e957dd2254eba97..3f551b98cb7160dff283dd7ebbc9280505d3ac3c 100644 +--- a/dist/types/jxl.js ++++ b/dist/types/jxl.js +@@ -24,7 +24,11 @@ function extractPartialStreams(input) { + if (!jxlpBox) + break; + partialStreams.push(input.slice(jxlpBox.offset + 12, jxlpBox.offset + jxlpBox.size)); +- offset = jxlpBox.offset + jxlpBox.size; ++ const nextOffset = jxlpBox.offset + jxlpBox.size; ++ if (nextOffset <= offset) { ++ break; ++ } ++ offset = nextOffset; + } + return partialStreams; + } diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 22797e713d057..38366c5c77aec 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -225,7 +225,7 @@ "i18next-http-backend": "^3.0.6", "i18next-sprintf-postprocessor": "^0.2.2", "iconv-lite": "^0.6.3", - "image-size": "^1.2.1", + "image-size": "patch:image-size@npm%3A1.2.1#~/.yarn/patches/image-size-npm-1.2.1-e285f3c080.patch", "imap": "^0.8.19", "ip-range-check": "^0.2.1", "is-svg": "^5.1.0", diff --git a/package.json b/package.json index 888e33d8d7bfb..1c8f4ccdfcb6a 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ }, "resolutions": { "file-type@npm:^16.5.4": "patch:file-type@npm%3A16.5.4#~/.yarn/patches/file-type-npm-16.5.4-d653e66a40.patch", + "image-size@npm:^1.2.1": "patch:image-size@npm%3A1.2.1#~/.yarn/patches/image-size-npm-1.2.1-e285f3c080.patch", "postcss-svgo/svgo": "4.0.2", "@vue/compiler-sfc/postcss": "8.5.26", "css-loader@npm:6.11.0/postcss": "8.5.26", diff --git a/yarn.lock b/yarn.lock index 9da5dacb8076a..ff63635194833 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10329,7 +10329,7 @@ __metadata: i18next-http-backend: "npm:^3.0.6" i18next-sprintf-postprocessor: "npm:^0.2.2" iconv-lite: "npm:^0.6.3" - image-size: "npm:^1.2.1" + image-size: "patch:image-size@npm%3A1.2.1#~/.yarn/patches/image-size-npm-1.2.1-e285f3c080.patch" imap: "npm:^0.8.19" ip-range-check: "npm:^0.2.1" is-svg: "npm:^5.1.0" @@ -25214,7 +25214,7 @@ __metadata: languageName: node linkType: hard -"image-size@npm:^1.2.1": +"image-size@npm:1.2.1": version: 1.2.1 resolution: "image-size@npm:1.2.1" dependencies: @@ -25225,6 +25225,17 @@ __metadata: languageName: node linkType: hard +"image-size@patch:image-size@npm%3A1.2.1#~/.yarn/patches/image-size-npm-1.2.1-e285f3c080.patch": + version: 1.2.1 + resolution: "image-size@patch:image-size@npm%3A1.2.1#~/.yarn/patches/image-size-npm-1.2.1-e285f3c080.patch::version=1.2.1&hash=ddb7b6" + dependencies: + queue: "npm:6.0.2" + bin: + image-size: bin/image-size.js + checksum: 10/29889b4eeba18c20a79dd2d7ce4734b7019a3459c73e3ef5670056a25db208f7248911edfd8e8d772b3eece979ec42110a275fa9d15258b0e592862fcad96178 + languageName: node + linkType: hard + "imap@npm:^0.8.19": version: 0.8.19 resolution: "imap@npm:0.8.19" From 2a7de457074cbb4d4373fbd9a4e5bea292c9c764 Mon Sep 17 00:00:00 2001 From: gabriellsh <40830821+gabriellsh@users.noreply.github.com> Date: Thu, 20 Aug 2026 19:31:27 +0000 Subject: [PATCH 2/2] feat: Screen Sharing General Availability (#41837) --- .../client/views/room/body/MediaCallRoom.tsx | 14 +- apps/meteor/ee/server/settings/voip.ts | 1 - .../e2e/page-objects/fragments/voice-calls.ts | 8 +- apps/meteor/tests/e2e/voice-calls-ee.spec.ts | 40 +- packages/i18n/src/locales/de.i18n.json | 2 - packages/i18n/src/locales/en.i18n.json | 2 - packages/i18n/src/locales/pt-BR.i18n.json | 2 - packages/i18n/src/locales/sv.i18n.json | 2 - .../src/providers/MockedMediaCallProvider.tsx | 6 +- .../ui-voip/src/views/MediaCallPopoutView.tsx | 38 +- .../MediaCallRoomSection.tsx | 42 +- .../MediaCallWidget.stories.tsx | 14 + .../src/views/MediaCallWidget/OngoingCall.tsx | 34 +- .../MediaCallWidget/OngoingCallWithScreen.tsx | 44 +- .../MediaCallWidget.spec.tsx.snap | 384 +++++++++++++++++- 15 files changed, 513 insertions(+), 120 deletions(-) diff --git a/apps/meteor/client/views/room/body/MediaCallRoom.tsx b/apps/meteor/client/views/room/body/MediaCallRoom.tsx index d6c03f9a01547..d9f950a0fabb5 100644 --- a/apps/meteor/client/views/room/body/MediaCallRoom.tsx +++ b/apps/meteor/client/views/room/body/MediaCallRoom.tsx @@ -1,12 +1,7 @@ import type { IRoom } from '@rocket.chat/core-typings'; import { isDirectMessageRoom } from '@rocket.chat/core-typings'; import type { PeerInfo } from '@rocket.chat/ui-voip'; -import { - MediaCallRoomActivity, - usePeekMediaSessionState, - usePeekMediaSessionPeerInfo, - usePeekMediaSessionFeatures, -} from '@rocket.chat/ui-voip'; +import { MediaCallRoomActivity, usePeekMediaSessionState, usePeekMediaSessionPeerInfo } from '@rocket.chat/ui-voip'; import type { ReactNode } from 'react'; import { memo } from 'react'; @@ -33,15 +28,8 @@ export type MediaCallRoomProps = { const MediaCallRoom = ({ children }: MediaCallRoomProps) => { const state = usePeekMediaSessionState(); const peerInfo = usePeekMediaSessionPeerInfo(); - const features = usePeekMediaSessionFeatures(); const room = useRoom(); - const screenShareEnabled = features.includes('screen-share'); - - if (!screenShareEnabled) { - return children; - } - if (state !== 'ongoing' || !isMediaCallRoom(room, peerInfo)) { return children; } diff --git a/apps/meteor/ee/server/settings/voip.ts b/apps/meteor/ee/server/settings/voip.ts index 42c424005679a..7c83bdf9e8599 100644 --- a/apps/meteor/ee/server/settings/voip.ts +++ b/apps/meteor/ee/server/settings/voip.ts @@ -13,7 +13,6 @@ export function addSettings(): Promise { type: 'boolean', public: true, invalidValue: false, - alert: 'VoIP_TeamCollab_Screen_Sharing_Enabled_Alert', i18nDescription: 'VoIP_TeamCollab_Screen_Sharing_Enabled_Description', }); diff --git a/apps/meteor/tests/e2e/page-objects/fragments/voice-calls.ts b/apps/meteor/tests/e2e/page-objects/fragments/voice-calls.ts index d23e44b4b6eb6..0fc68a03bf716 100644 --- a/apps/meteor/tests/e2e/page-objects/fragments/voice-calls.ts +++ b/apps/meteor/tests/e2e/page-objects/fragments/voice-calls.ts @@ -111,7 +111,7 @@ export class Widget { private readonly transferModal: TransferModal; - constructor(page: Page) { + constructor(private readonly page: Page) { this.transferModal = new TransferModal(page, page.getByRole('dialog', { name: 'Transfer call' })); this.root = page.getByRole('dialog', { name: 'Voice call', exact: false }); this.callControls = new VoiceCallControls(this.root.getByRole('group')); @@ -144,7 +144,11 @@ export class Widget { return timerToSeconds(text); } - async initiateCall(): Promise { + async initiateCall(username?: string): Promise { + if (username) { + await this.root.getByRole('textbox', { name: 'Enter username or number' }).fill(username); + await this.page.getByRole('listbox').getByRole('option', { name: username }).click(); + } await this.callControls.call.click(); await expect(this.callControls.cancel).toBeVisible(); } diff --git a/apps/meteor/tests/e2e/voice-calls-ee.spec.ts b/apps/meteor/tests/e2e/voice-calls-ee.spec.ts index 2726cf8cc1788..cbbfab3d260d7 100644 --- a/apps/meteor/tests/e2e/voice-calls-ee.spec.ts +++ b/apps/meteor/tests/e2e/voice-calls-ee.spec.ts @@ -34,15 +34,10 @@ test.describe('Internal Voice Calls - Enterprise Edition', () => { test('should initiate voice call from direct message', async () => { const [user1, user2] = sessions; - await test.step('should open direct message with user2', async () => { - await user1.poHomeChannel.navbar.openChat('user2'); - await expect(user1.poHomeChannel.composer.inputMessage).toBeVisible(); - }); - - await test.step('initiate a voice call from room toolbar', async () => { - await user1.poHomeChannel.content.btnVoiceCall.click(); + await test.step('establish call connection', async () => { + await user1.poHomeChannel.navbar.btnNewVoiceCall.click(); await expect(user1.poHomeChannel.voiceCalls.widget.content).toBeVisible(); - await user1.poHomeChannel.voiceCalls.widget.initiateCall(); + await user1.poHomeChannel.voiceCalls.widget.initiateCall(Users.user2.data.username); }); await test.step('user2 accepts the call', async () => { @@ -59,37 +54,34 @@ test.describe('Internal Voice Calls - Enterprise Edition', () => { test('should handle call controls during active call', async () => { const [user1, user2] = sessions; await test.step('establish call connection', async () => { - await user1.poHomeChannel.navbar.openChat('user2'); - await expect(user1.poHomeChannel.composer.inputMessage).toBeVisible(); - await user1.poHomeChannel.content.btnVoiceCall.click(); - await user1.poHomeChannel.voiceCalls.widget.initiateCall(); + await user1.poHomeChannel.navbar.btnNewVoiceCall.click(); + await user1.poHomeChannel.voiceCalls.widget.initiateCall(Users.user2.data.username); await user2.poHomeChannel.voiceCalls.widget.acceptCall(); }); await test.step('should mute/unmute microphone from user1', async () => { - // User1 mutes microphone await user1.poHomeChannel.voiceCalls.widget.muteSelf(); - // User1 unmutes microphone await user1.poHomeChannel.voiceCalls.widget.unmuteSelf(); }); await test.step('should put call on hold from user1', async () => { - // User1 puts call on hold await user1.poHomeChannel.voiceCalls.widget.holdSelf(); - // User1 resumes call await user1.poHomeChannel.voiceCalls.widget.resumeSelf(); }); await test.step('should access dialpad during call', async () => { - // User1 opens dial pad await user1.poHomeChannel.voiceCalls.widget.openDialpad(); - // User1 closes dial pad await user1.poHomeChannel.voiceCalls.widget.closeDialpad(); }); + await test.step('should not display screen share button', async () => { + await expect(user1.poHomeChannel.voiceCalls.widget.controls.shareScreen).not.toBeVisible(); + await expect(user2.poHomeChannel.voiceCalls.widget.controls.shareScreen).not.toBeVisible(); + }); + await test.step('should end the call from user1', async () => { await user1.poHomeChannel.voiceCalls.widget.hangup(); await expect(user2.poHomeChannel.voiceCalls.widget.content).not.toBeVisible(); @@ -106,10 +98,8 @@ test.describe('Internal Voice Calls - Enterprise Edition', () => { const user3 = { page: user3Context.page, poHomeChannel: new HomeChannel(user3Context.page) }; await test.step('establish call between user1 and user2', async () => { - await user1.poHomeChannel.navbar.openChat('user2'); - await expect(user1.poHomeChannel.composer.inputMessage).toBeVisible(); - await user1.poHomeChannel.content.btnVoiceCall.click(); - await user1.poHomeChannel.voiceCalls.widget.initiateCall(); + await user1.poHomeChannel.navbar.btnNewVoiceCall.click(); + await user1.poHomeChannel.voiceCalls.widget.initiateCall(Users.user2.data.username); await user2.poHomeChannel.voiceCalls.widget.acceptCall(); }); @@ -139,10 +129,8 @@ test.describe('Internal Voice Calls - Enterprise Edition', () => { const [user1, user2] = sessions; await test.step('user1 initiates call to user2', async () => { - await user1.poHomeChannel.navbar.openChat('user2'); - await expect(user1.poHomeChannel.composer.inputMessage).toBeVisible(); - await user1.poHomeChannel.content.btnVoiceCall.click(); - await user1.poHomeChannel.voiceCalls.widget.initiateCall(); + await user1.poHomeChannel.navbar.btnNewVoiceCall.click(); + await user1.poHomeChannel.voiceCalls.widget.initiateCall(Users.user2.data.username); }); await test.step('user2 declines the call', async () => { diff --git a/packages/i18n/src/locales/de.i18n.json b/packages/i18n/src/locales/de.i18n.json index b6a6ff61c1522..a6c16dd3d24eb 100644 --- a/packages/i18n/src/locales/de.i18n.json +++ b/packages/i18n/src/locales/de.i18n.json @@ -1051,7 +1051,6 @@ "Call_ended_bold": "*Sprachanruf beendet*", "Call_not_answered_bold": "*Sprachanruf nicht angenommen*", "Call_failed_bold": "*Sprachanruf fehlgeschlagen*", - "Call_feature_unsupported": "Die Gegenseite unterstützt dies nicht", "Call_transferred_bold": "*Sprachanruf weitergeleitet*", "Call_history": "Anrufverlauf", "Call_history_provides_a_record_of_when_calls_took_place_and_who_joined": "In der Anrufliste wird festgehalten, wann Anrufe getätigt wurden und wer daran teilgenommen hat.", @@ -5930,7 +5929,6 @@ "VoIP_TeamCollab_Mobile_Ringing_Enabled_Description": "Ermöglicht Benutzern, Anrufe in der mobilen App zu tätigen und zu empfangen.", "VoIP_TeamCollab_Screen_Sharing_Enabled": "Bildschirmfreigabe", "VoIP_TeamCollab_Screen_Sharing_Enabled_Description": "Ermöglicht Benutzern, ihren Bildschirm während Sprachanrufen freizugeben.", - "VoIP_TeamCollab_Screen_Sharing_Enabled_Alert": "Die Bildschirmfreigabe befindet sich derzeit in der Beta-Phase.", "VoIP_TeamCollab_Internal_SIP_Beta_Alert": "Das Weiterleiten interner Anrufe über SIP befindet sich in der Beta-Phase und wird nicht für den Produktiveinsatz empfohlen.", "VoIP_TeamCollab_Drachtio_Host": "Drachtio-Host", "VoIP_TeamCollab_Drachtio_Port": "Drachtio-Port", diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index 41369a7c14efa..0d97b7fe396bf 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -1130,7 +1130,6 @@ "Call_ended_bold": "*Voice call ended*", "Call_not_answered_bold": "*Voice call not answered*", "Call_failed_bold": "*Voice call failed*", - "Call_feature_unsupported": "Other party doesn't support this", "Call_transferred_bold": "*Voice call transferred*", "Call_history": "Call history", "Call_history_provides_a_record_of_when_calls_took_place_and_who_joined": "Call history provides a record of when calls took place and who joined.", @@ -6088,7 +6087,6 @@ "VoIP_TeamCollab_Mobile_Ringing_Enabled_Description": "Allow users to make and receive calls on the mobile app.", "VoIP_TeamCollab_Screen_Sharing_Enabled": "Screen sharing", "VoIP_TeamCollab_Screen_Sharing_Enabled_Description": "Allow users to share their screen during voice calls.", - "VoIP_TeamCollab_Screen_Sharing_Enabled_Alert": "Screen sharing is currently in beta.", "VoIP_TeamCollab_Internal_SIP_Beta_Alert": "Routing of internal calls through SIP is in beta and is not recommended for production use.", "VoIP_TeamCollab_Drachtio_Host": "Drachtio Host", "VoIP_TeamCollab_Drachtio_Port": "Drachtio Port", diff --git a/packages/i18n/src/locales/pt-BR.i18n.json b/packages/i18n/src/locales/pt-BR.i18n.json index e904c242ccfda..d5d5958083f0d 100644 --- a/packages/i18n/src/locales/pt-BR.i18n.json +++ b/packages/i18n/src/locales/pt-BR.i18n.json @@ -1047,7 +1047,6 @@ "Call_ended_bold": "*Chamada de voz encerrada*", "Call_not_answered_bold": "*Chamada de voz não atendida*", "Call_failed_bold": "*Falha na chamada de voz*", - "Call_feature_unsupported": "A outra parte não suporta isso", "Call_transferred_bold": "*Chamada de voz transferida*", "Call_history": "Histórico de chamadas", "Call_history_provides_a_record_of_when_calls_took_place_and_who_joined": "O histórico de chamadas fornece um registro de quando as chamadas foram realizadas e quem participou.", @@ -5875,7 +5874,6 @@ "VoIP_TeamCollab_Mobile_Ringing_Enabled_Description": "Permitir que os usuários façam e recebam chamadas no aplicativo móvel.", "VoIP_TeamCollab_Screen_Sharing_Enabled": "Compartilhamento de tela", "VoIP_TeamCollab_Screen_Sharing_Enabled_Description": "Permitir que os usuários compartilhem a tela durante as chamadas de voz.", - "VoIP_TeamCollab_Screen_Sharing_Enabled_Alert": "O compartilhamento de tela está atualmente na versão beta.", "VoIP_TeamCollab_Internal_SIP_Beta_Alert": "O roteamento de chamadas internas por SIP está em fase beta e não é recomendado para uso em produção.", "VoIP_TeamCollab_Drachtio_Host": "Host do Drachtio", "VoIP_TeamCollab_Drachtio_Port": "Porta do Drachtio", diff --git a/packages/i18n/src/locales/sv.i18n.json b/packages/i18n/src/locales/sv.i18n.json index 0f80bf8bd2e4c..eb02bd8879bb8 100644 --- a/packages/i18n/src/locales/sv.i18n.json +++ b/packages/i18n/src/locales/sv.i18n.json @@ -1044,7 +1044,6 @@ "Call_ended_bold": "*Röstsamtal avslutad*", "Call_not_answered_bold": "*Röstsamtal besvarades inte*", "Call_failed_bold": "*Röstsamtal misslyckades*", - "Call_feature_unsupported": "Andra parten stöder inte detta", "Call_transferred_bold": "*Röstsamtal överfördes*", "Call_history": "Samtalhistorik", "Call_history_provides_a_record_of_when_calls_took_place_and_who_joined": "I samtalshistoriken samlas information om när samtal ägt rum och vilka som deltog.", @@ -5858,7 +5857,6 @@ "VoIP_TeamCollab_Mobile_Ringing_Enabled_Description": "Tillåt användare att göra och ta emot samtal i mobilappen.", "VoIP_TeamCollab_Screen_Sharing_Enabled": "Skärmdelning", "VoIP_TeamCollab_Screen_Sharing_Enabled_Description": "Tillåt användare att dela sin skärm under röstsamtal.", - "VoIP_TeamCollab_Screen_Sharing_Enabled_Alert": "Skärmdelning är för närvarande i beta.", "VoIP_TeamCollab_Internal_SIP_Beta_Alert": "Routning av interna samtal genom SIP är i beta och rekommenderas inte för produktionsanvändning.", "VoIP_TeamCollab_Drachtio_Host": "Drachtio Host", "VoIP_TeamCollab_Drachtio_Port": "Drachtio Port", diff --git a/packages/ui-voip/src/providers/MockedMediaCallProvider.tsx b/packages/ui-voip/src/providers/MockedMediaCallProvider.tsx index 88a9fe20951bf..d9b9eb20b0953 100644 --- a/packages/ui-voip/src/providers/MockedMediaCallProvider.tsx +++ b/packages/ui-voip/src/providers/MockedMediaCallProvider.tsx @@ -1,4 +1,6 @@ import { UserStatus } from '@rocket.chat/core-typings'; +import type { CallFeature } from '@rocket.chat/media-signaling'; +import { callFeatureList } from '@rocket.chat/media-signaling'; import type { ReactNode } from 'react'; import { useState } from 'react'; @@ -18,6 +20,7 @@ export type MockedMediaCallProviderProps = { held?: boolean; onClickDirectMessage?: () => void; instanceProps?: Partial; + supportedFeatures?: readonly CallFeature[]; }; const MockedMediaCallProvider = ({ @@ -31,6 +34,7 @@ const MockedMediaCallProvider = ({ muted = false, held = false, instanceProps, + supportedFeatures = callFeatureList, }: MockedMediaCallProviderProps) => { const [peerInfo, setPeerInfo] = useState({ displayName: 'John Doe', @@ -122,7 +126,7 @@ const MockedMediaCallProvider = ({ remoteMuted, remoteHeld, callId: undefined, - supportedFeatures: ['audio', 'screen-share', 'transfer', 'hold'], + supportedFeatures, } as SessionState; const contextValue = { diff --git a/packages/ui-voip/src/views/MediaCallPopoutView.tsx b/packages/ui-voip/src/views/MediaCallPopoutView.tsx index efb569c82f25d..5562dc714856d 100644 --- a/packages/ui-voip/src/views/MediaCallPopoutView.tsx +++ b/packages/ui-voip/src/views/MediaCallPopoutView.tsx @@ -31,7 +31,7 @@ const MediaCallPopoutView = ({ user, onClickClosePopout }: MediaCallPopoutViewPr streams: { localScreen }, } = useMediaCallView(); - const { muted, held, peerInfo, connectionState, startedAt } = sessionState; + const { muted, held, peerInfo, connectionState, startedAt, supportedFeatures } = sessionState; const { ref, borderBoxSize } = useResizeObserver(); @@ -82,21 +82,27 @@ const MediaCallPopoutView = ({ user, onClickClosePopout }: MediaCallPopoutViewPr } > - - - + {supportedFeatures.includes('hold') && ( + + )} + {supportedFeatures.includes('screen-share') && ( + + )} + {supportedFeatures.includes('transfer') && ( + + )} diff --git a/packages/ui-voip/src/views/MediaCallRoomSection/MediaCallRoomSection.tsx b/packages/ui-voip/src/views/MediaCallRoomSection/MediaCallRoomSection.tsx index 71b04cb5d5e62..b5dfd764699f3 100644 --- a/packages/ui-voip/src/views/MediaCallRoomSection/MediaCallRoomSection.tsx +++ b/packages/ui-voip/src/views/MediaCallRoomSection/MediaCallRoomSection.tsx @@ -60,7 +60,7 @@ const MediaCallRoomSection = ({ showChat, onToggleChat, user, containerHeight }: const isPopout = currentViews.has('popout'); - const { muted, held, peerInfo, connectionState, startedAt } = sessionState; + const { muted, held, peerInfo, connectionState, startedAt, supportedFeatures } = sessionState; const shouldWrapCards = useShouldWrapCards(showChat, containerHeight); @@ -69,6 +69,10 @@ const MediaCallRoomSection = ({ showChat, onToggleChat, user, containerHeight }: useRegisterView('room'); + const screenShareAvailable = supportedFeatures.includes('screen-share'); + const holdAvailable = supportedFeatures.includes('hold'); + const transferAvailable = supportedFeatures.includes('transfer'); + if (!peerInfo || 'number' in peerInfo) { return null; } @@ -108,21 +112,27 @@ const MediaCallRoomSection = ({ showChat, onToggleChat, user, containerHeight }: } > - - - + {holdAvailable && ( + + )} + {screenShareAvailable && ( + + )} + {transferAvailable && ( + + )} diff --git a/packages/ui-voip/src/views/MediaCallWidget/MediaCallWidget.stories.tsx b/packages/ui-voip/src/views/MediaCallWidget/MediaCallWidget.stories.tsx index 1cdc4241ef714..0ee6e3755586c 100644 --- a/packages/ui-voip/src/views/MediaCallWidget/MediaCallWidget.stories.tsx +++ b/packages/ui-voip/src/views/MediaCallWidget/MediaCallWidget.stories.tsx @@ -118,3 +118,17 @@ export const OngoingCallConnecting: Story = { connectionState: 'CONNECTING', }, }; + +export const OngoingCallDisabledAllFeatures: Story = { + args: { + state: 'ongoing', + supportedFeatures: [], + }, +}; + +export const OngoingCallDisabledAllButScreenshare: Story = { + args: { + state: 'ongoing', + supportedFeatures: ['screen-share'], + }, +}; diff --git a/packages/ui-voip/src/views/MediaCallWidget/OngoingCall.tsx b/packages/ui-voip/src/views/MediaCallWidget/OngoingCall.tsx index cc335933fb8d4..934691bed18f7 100644 --- a/packages/ui-voip/src/views/MediaCallWidget/OngoingCall.tsx +++ b/packages/ui-voip/src/views/MediaCallWidget/OngoingCall.tsx @@ -34,8 +34,8 @@ const OngoingCall = () => { const connecting = connectionState === 'CONNECTING'; const reconnecting = connectionState === 'RECONNECTING'; - const transferDisabled = !supportedFeatures.includes('transfer'); - const holdDisabled = !supportedFeatures.includes('hold'); + const holdAvailable = supportedFeatures.includes('hold'); + const transferAvailable = supportedFeatures.includes('transfer'); // TODO: Figure out how to ensure this always exist before rendering the component if (!peerInfo) { @@ -72,7 +72,7 @@ const OngoingCall = () => { ) : null} - + { onClick={() => setOpen((open) => !open)} /> - - + {holdAvailable && ( + + )} + {transferAvailable && ( + + )} { onToggleScreenSharing, onClosePopout, } = useMediaCallView(); - const { muted, held, remoteMuted, remoteHeld, peerInfo, connectionState, startedAt } = sessionState; + const { muted, held, remoteMuted, remoteHeld, peerInfo, connectionState, startedAt, supportedFeatures } = sessionState; const { currentViews } = useMediaCallInstance(); const isPopout = currentViews.has('popout'); + const screenShareAvailable = supportedFeatures.includes('screen-share'); + const holdAvailable = supportedFeatures.includes('hold'); + const transferAvailable = supportedFeatures.includes('transfer'); + const { localScreen, remoteScreen } = streams; const [remoteStreamRefCallback] = usePlayMediaStream(remoteScreen?.stream ?? null); @@ -117,24 +121,30 @@ const OngoingCall = () => { - + - - - + {holdAvailable && ( + + )} + {screenShareAvailable && ( + + )} + {transferAvailable && ( + + )}
+ +
+ +
+
+
+
+
+ +
+
+
+
+ + +
+ John Doe +
+
+
+
+ 1234567890 +
+
+
+
+
+
+
+ + + +
+
+ +