Skip to content

Conversation

@gabriellsh
Copy link
Member

@gabriellsh gabriellsh commented Sep 18, 2025

Proposed changes (including videos or screenshots)

Issue(s)

Steps to test or reproduce

Further comments

Summary by CodeRabbit

  • New Features

    • Transfer Call modal to forward ongoing calls to a user or number.
    • Draggable, focus-managed call widget with improved accessibility and visibility control.
    • Connection status indicators and localized UI strings; keypad shows typed digits and localized dialpad button.
  • Refactor

    • Unified peer model (displayName/userId/optional callerId/avatar) and event-driven peer selection.
    • Promise-based call actions (onCall/onAccept) and new context callbacks (onSelectPeer, onToggleWidget).
    • ActionButton forwards refs; widget drag context extracted.
  • Chores

    • Added exports: useMediaCallContext, PeerInfo, MediaCallWidget.

@dionisio-bot
Copy link
Contributor

dionisio-bot bot commented Sep 18, 2025

Looks like this PR is ready to merge! 🎉
If you have any trouble, please check the PR guidelines

@changeset-bot
Copy link

changeset-bot bot commented Sep 18, 2025

⚠️ No Changeset found

Latest commit: 54ec62d

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 18, 2025

Walkthrough

Refactors MediaCallContext for richer peer modeling and async handlers, adds ConnectionState and hidden, rewires autocomplete to external selection, introduces draggable Widget context/handle, adds TransferModal, updates keypad/info slots with i18n, and adjusts many components/stories to the new peer and context shapes.

Changes

Cohort / File(s) Summary of changes
Context & Provider
packages/ui-voip/src/v2/MediaCallContext.ts, packages/ui-voip/src/v2/MockedMediaCallProvider.tsx, packages/ui-voip/src/v2/index.ts
Exported ConnectionState; PeerInfo public shape changed to Internal/External variants (displayName/userId/username?/avatarUrl?/callerId?); MediaCallContextType adds connectionState, hidden, onAccept, onSelectPeer, onToggleWidget; onCall/onAccept become promise-based; provider and defaults updated; new re-exports added.
Autocomplete / Selection flow
packages/ui-voip/src/v2/MediaCallContext.ts (usePeerAutocomplete)
Signature now requires (onSelectPeer, peerInfo); removes internal selected state; selection calls onSelectPeer with ExternalPeerInfo or InternalPeerInfo; throws if local match missing; returned value derives from external peerInfo.
Views & Widget usage
packages/ui-voip/src/v2/views/NewCall.tsx, .../IncomingCall.tsx, .../OngoingCall.tsx, .../OutgoingCall.tsx, packages/ui-voip/src/v2/MediaCallWidget.tsx
Consumers updated to new context API: NewCall wired to onCall/onSelectPeer/onToggleWidget and new autocomplete; IncomingCall uses onAccept; Ongoing/Outgoing use connectionState and i18n; MediaCallWidget respects hidden flag.
Draggable Widget infra
packages/ui-voip/src/v2/components/Widget/Widget.tsx, .../WidgetDraggableContext.ts, .../WidgetHandle.tsx, .../WidgetHeader.tsx, .../WidgetInfo.tsx
Widget converted to functional component with draggable refs, DragContext, useDraggable hook usage, FocusScope, aria-labelledby; new DragContext and useDraggableWidget hook; Handle consumes context; header title id added; WidgetInfo supports optional slot icons.
PeerInfo components & stories
packages/ui-voip/src/v2/components/PeerInfo/InternalUser.tsx, .../PeerInfo.tsx, .../PhoneNumber.tsx, .../PeerInfo.stories.tsx
Prop renames: name/identifier → displayName/callerId; avatarUrl optional with Icon fallback; PeerInfo conditional checks displayName; PhoneNumber id/format tweaked; stories updated to new props.
Device, Action & UI controls
packages/ui-voip/src/v2/components/DevicePicker.tsx, .../ActionButton.tsx
DevicePicker onChange now passes full device object to onDeviceChange; ActionButton wrapped with forwardRef and forwards ref to underlying IconButton.
Keypad & Info slots hooks
packages/ui-voip/src/v2/useKeypad.tsx, packages/ui-voip/src/v2/useInfoSlots.ts
Keypad now shows read-only inputValue, returns buttonProps (title/onClick) instead of toggleOpen, uses i18n; useInfoSlots accepts connectionState, adds optional icon on slots and prepends RECONNECTING warning slot.
New Transfer modal
packages/ui-voip/src/v2/TransferModal.tsx
New TransferModal exported default; uses usePeerAutocomplete to pick a peer or SIP; confirms by calling onConfirm('user'
Stories minor update
packages/ui-voip/src/v2/MediaCallWidget.stories.tsx
onToggleWidget invocation wrapped as () => onToggleWidget() to match no-arg signature.
i18n data
packages/i18n/src/locales/en.i18n.json
Added translation keys New_call and Voice_call__user__hangup.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant UI as NewCall View
  participant AC as usePeerAutocomplete
  participant Ctx as MediaCallContext
  participant Prov as Provider

  UI->>AC: init(onSelectPeer, peerInfo)
  UI->>AC: user selects option
  alt SIP / first option
    AC-->>Ctx: onSelectPeer({ number })
  else known user option
    AC-->>Ctx: onSelectPeer({ userId, displayName, avatarUrl? })
  else no local match
    AC-->>AC: throw Error
  end
  UI->>Ctx: onCall()
  Ctx->>Prov: onCall() Promise
  Prov-->>Ctx: resolve/reject
  Ctx-->>UI: Promise settled
Loading
sequenceDiagram
  autonumber
  participant Modal as TransferModal
  participant AC as usePeerAutocomplete
  participant Parent as Caller

  Modal->>AC: init(onSelectPeer, peerInfo)
  Modal->>Modal: user selects peer
  Modal->>Modal: user confirms
  alt selected user
    Modal-->>Parent: onConfirm('user', userId)
  else selected SIP
    Modal-->>Parent: onConfirm('sip', number)
  else invalid
    Modal-->>Modal: throw Error
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Poem

I twitch my ears and tweak the call,
New peers line up along the wall.
I drag the box and press the keys,
Promises hop on gentle breeze.
A transfer done — a tiny cheer —
From whiskers, wired and full of cheer. 🐇☎️

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "chore: Update Media Call components and missing parts" accurately identifies the PR's scope—updates to the Media Call UI and related pieces—and maps to the changeset which refactors MediaCallContext, widgets, provider, autocomplete, and other media-call components; it is concise and relevant for someone scanning history. The phrase "missing parts" is a bit broad, but the title still meaningfully signals the primary area of change.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/mediaCallComponents

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (30)
packages/ui-voip/src/v2/components/ActionButton.tsx (3)

12-15: Ref forwarding looks good; add displayName for DevTools.

Forwarding the ref to IconButton is correct. Add an explicit displayName for clearer debugging.

-});
+});
+
+ActionButton.displayName = 'ActionButton';
 
 export default ActionButton;

Also applies to: 27-30


3-3: Use type-only imports and align onClick typing with React.

Reduces emitted JS and matches IconButton’s handler type.

-import { ComponentProps, forwardRef } from 'react';
+import { forwardRef } from 'react';
+import type { ComponentProps, MouseEventHandler } from 'react';
@@
   disabled?: boolean;
-  onClick?: () => void;
+  onClick?: MouseEventHandler<HTMLButtonElement>;
 } & Omit<ComponentProps<typeof IconButton>, 'icon' | 'title' | 'aria-label' | 'disabled' | 'onClick'>;

Also applies to: 5-10


17-21: Avoid forcing medium size; let consumers choose or rely on IconButton default.

Prevents conflicting size flags when callers pass small/large.

-      medium
       secondary={secondary}
       icon={<Icon size={16} name={icon} />}
       title={label}
       aria-label={label}
       disabled={disabled}
       onClick={onClick}
       {...props}

Also applies to: 26-26

packages/ui-voip/src/v2/components/PeerInfo/PhoneNumber.tsx (1)

9-12: A11Y: mark decorative icon hidden; consider RTL-safe number rendering; verify static ID uniqueness

  • Icon appears decorative—hide it from SRs.
  • Phone numbers render poorly in RTL locales; force LTR on the numeral span.
  • Static id may collide if multiple widgets exist. Confirm only one instance or derive a unique id.

Apply:

-  <Box display='flex' flexDirection='row' id='rcx-media-call-widget-caller-info'>
-   <Icon size='x20' name='phone' mie={8} />
-   <Box fontScale='p2b'>{number}</Box>
+  <Box display='flex' flexDirection='row' id='rcx-media-call-widget-caller-info'>
+   <Icon size='x20' name='phone' mie={8} aria-hidden='true' />
+   <Box fontScale='p2b' dir='ltr'>{number}</Box>
packages/ui-voip/src/v2/useKeypad.tsx (1)

20-35: A11Y and UX: label the read-only input, clamp length, clear on close; simplify handler

  • Add aria-label and polite live region so SRs get updates.
  • Prevent unbounded growth; clamp digits.
  • Clear digits when closing.
  • Prefer explicit (tone: string) over rest args.

Apply:

-  const element = (
+  const element = (
     <Box display='flex' justifyContent='center' alignItems='center' w='100%' flexDirection='column' mbe={8}>
       <Field mbe={8}>
         <FieldRow>
-          <TextInput value={inputValue} readOnly small mi={24} />
+          <TextInput
+            value={inputValue}
+            readOnly
+            small
+            mi={24}
+            aria-label={t('Dialed_digits')}
+            aria-live='polite'
+          />
         </FieldRow>
       </Field>
       <Keypad
-        onKeyPress={(...args) => {
-          setInputValue((inputValue) => inputValue + args[0]);
-          onPress(...args);
-        }}
+        onKeyPress={(tone: string) => {
+          setInputValue((prev) => (prev + tone).slice(0, 64));
+          onPress(tone);
+        }}
       />
       <Divider w='100%' />
     </Box>
   );
 ...
   return {
     element: open ? element : null,
     buttonProps: {
       title: open ? t('Close_dialpad') : t('Open_dialpad'),
-      onClick: () => setOpen((open) => !open),
+      onClick: () =>
+        setOpen((isOpen) => {
+          if (isOpen) setInputValue('');
+          return !isOpen;
+        }),
     },
   };

Also applies to: 39-42

packages/ui-voip/src/v2/components/Widget/WidgetHeader.tsx (1)

13-15: Avoid static IDs; derive a stable unique title id

Static ids can collide if multiple widgets render. Use useId() or accept an id prop and pass it from the parent that sets aria-labelledby.

Apply:

-import type { ReactElement, ReactNode } from 'react';
+import type { ReactElement, ReactNode } from 'react';
+import { useId } from 'react';
@@
-const WidgetHeader = ({ title, children }: WidgetHeaderProps): ReactElement => {
+const WidgetHeader = ({ title, children }: WidgetHeaderProps): ReactElement => {
+  const titleId = useId();
   return (
     <Box is='header' mi={12} mb={4} display='flex' alignItems='center' justifyContent='space-between'>
-      <Box is='h3' color='titles-labels' fontScale='p1b' id='rcx-media-call-widget-title'>
+      <Box is='h3' color='titles-labels' fontScale='p1b' id={titleId}>
         {title}
       </Box>
       <Box mis={8}>{children}</Box>
     </Box>
   );
packages/ui-voip/src/v2/components/PeerInfo/PeerInfo.stories.tsx (1)

16-26: Trim the large base64 avatar in story to keep bundles/snaps lean.

Replace the long data URI with a 1x1 placeholder or a tiny asset to improve Storybook perf/readability.

Apply:

-            avatarUrl='data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgG...rs//Z'
+            avatarUrl='data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=='
packages/ui-voip/src/v2/views/IncomingCall.tsx (1)

31-31: Keep wrapper to avoid passing click event to onAccept.

onAccept likely takes no args/returns Promise; wrapper is appropriate.

If onAccept is guaranteed to be no-arg, consider onClick={() => void onAccept()} to ignore any returned Promise explicitly.

packages/ui-voip/src/v2/index.ts (1)

1-3: Potential name confusion: type PeerInfo vs component PeerInfo.

Exporting a type named PeerInfo alongside a PeerInfo component elsewhere can confuse consumers’ imports.

Consider aliasing the type:

-export type { PeerInfo } from './MediaCallContext';
+export type { PeerInfo as PeerInfoType } from './MediaCallContext';

and adjust downstream imports accordingly.

packages/ui-voip/src/v2/components/Widget/WidgetHandle.tsx (3)

15-26: Prevent external ref props from overriding handleRef.

{...props} comes after ref={handleRef}, so a consumer‑provided ref would silently replace the drag handle ref and break dragging.

Apply this diff to ensure the internal ref wins:

-			className={dragHandle}
-			ref={handleRef}
-			{...props}
+			className={dragHandle}
+			{...props}
+			ref={handleRef}

5-6: Guard against missing provider (DX).

useDraggableWidget() throws if the context is absent. If WidgetHandle is ever rendered outside the provider, it will crash. Consider a dev‑only warning and a no‑op ref to fail softer.

Also applies to: 15-17


18-29: A11y: identify the drag handle.

Expose an accessible name/role for the handle (e.g., aria-roledescription="drag handle" and aria-label="Drag"). Optional, but helps SR users.

packages/ui-voip/src/v2/views/NewCall.tsx (1)

41-43: Disable “Call” until a peer is selected.

Avoid firing onCall with no peerInfo.

Apply this diff:

-					<Button medium icon='phone' success flexGrow={1} onClick={onCall}>
+					<Button medium icon='phone' success flexGrow={1} onClick={onCall} disabled={!peerInfo}>
packages/ui-voip/src/v2/useInfoSlots.ts (2)

49-66: Don’t drop muted/held badges while reconnecting.

Early return shows only the connection state slot and hides muted/held. If you intended to prepend connection state, remove the short‑circuit and avoid name shadowing for clarity.

Apply this diff:

-		const slots: Slot[] = [];
+		const nextSlots: Slot[] = [];
 		const heldSlot = getHeldSlot(held, t);
 		const mutedSlot = getMutedSlot(muted, t);
 		const connectionStateSlot = getConnectionStateSlot(connectionState, t);
 
-		if (connectionStateSlot) {
-			slots.push(connectionStateSlot);
-			setSlots(slots);
-			return;
-		}
-		if (heldSlot) {
-			slots.push(heldSlot);
-		}
-		if (mutedSlot) {
-			slots.push(mutedSlot);
-		}
-		setSlots(slots);
+		if (connectionStateSlot) {
+			nextSlots.push(connectionStateSlot);
+		}
+		if (heldSlot) {
+			nextSlots.push(heldSlot);
+		}
+		if (mutedSlot) {
+			nextSlots.push(mutedSlot);
+		}
+		setSlots(nextSlots);

44-69: Compute slots without state.

This value is derived; useMemo would simplify and avoid state effects.

If desired, I can provide a full useMemo rewrite.

packages/ui-voip/src/v2/TransferModal.tsx (2)

20-24: Wire isLoading to the confirm button to prevent double submits.

Prop exists but isn’t used. Disable and show loading while confirming.

Apply this diff:

-const TransferModal = ({ onCancel, onConfirm }: TransferModalProps) => {
+const TransferModal = ({ onCancel, onConfirm, isLoading }: TransferModalProps) => {
@@
-					<Button danger onClick={confirm} disabled={!peer}>
+					<Button danger onClick={confirm} disabled={!peer || isLoading} loading={isLoading}>

Also applies to: 26-26, 69-75


35-51: Optional: handle async confirm flow.

If onConfirm can be async, consider making confirm async and delegating isLoading from parent or local state.

Do you expect onConfirm to return a Promise?

packages/ui-voip/src/v2/views/OngoingCall.tsx (2)

51-51: Localize the Dialpad label.

Use t('Dialpad') for consistency with the rest.

Apply this diff:

-					<ActionButton disabled={connecting || reconnecting} icon='dialpad' label='Dialpad' {...keypadButtonProps} />
+					<ActionButton disabled={connecting || reconnecting} icon='dialpad' label={t('Dialpad')} {...keypadButtonProps} />

53-53: Different icons for Hold on/off — replace second icon with 'play'.
File: packages/ui-voip/src/v2/views/OngoingCall.tsx (line 53). Verified @rocket.chat/fuselage uses "play" for resume; apply diff:

-					<ToggleButton label={t('Hold')} icons={['pause-shape-unfilled', 'pause-shape-unfilled']} pressed={held} onToggle={onHold} />
+					<ToggleButton label={t('Hold')} icons={['pause-shape-unfilled', 'play']} pressed={held} onToggle={onHold} />
packages/ui-voip/src/v2/components/PeerInfo/InternalUser.tsx (2)

11-13: A11y: add alt/aria for avatar and hide decorative icon

Provide an accessible name when the avatar renders and mark the fallback icon as decorative.

-			<Box mie={8}>{avatarUrl ? <Avatar url={avatarUrl} size='x20' /> : <Icon name='user' size='x20' />}</Box>
+			<Box mie={8}>
+				{avatarUrl ? (
+					<Avatar url={avatarUrl} size='x20' title={displayName} aria-label={displayName} />
+				) : (
+					<Icon name='user' size='x20' aria-hidden='true' />
+				)}
+			</Box>

11-11: Prevent possible ID collisions

If multiple widgets render simultaneously, a hardcoded id risks duplication. Prefer a prop or useId() to generate a stable unique id, and have Widget.tsx reference it.

-import { Avatar, Box, Icon } from '@rocket.chat/fuselage';
+import { Avatar, Box, Icon } from '@rocket.chat/fuselage';
+import { useId } from 'react';
@@
-const InternalUser = ({ displayName, avatarUrl, callerId }: InternalUserProps) => {
+const InternalUser = ({ displayName, avatarUrl, callerId }: InternalUserProps) => {
+	const callerInfoId = useId();
 	return (
-		<Box display='flex' flexDirection='row' id='rcx-media-call-widget-caller-info'>
+		<Box display='flex' flexDirection='row' id={callerInfoId}>
@@
-				{callerId && (
+				{callerId && (
 					<Box fontScale='c1' color='secondary-info'>
 						{callerId}
 					</Box>
 				)}

Then pass callerInfoId upward or expose via context for aria-describedby.

packages/ui-voip/src/v2/components/Widget/Widget.tsx (4)

44-47: Fix ARIA relationships: use aria-describedby for caller info

aria-labelledby should reference the title; secondary descriptive content belongs in aria-describedby to avoid referencing missing IDs when caller info isn’t rendered.

-				<WidgetBase {...props} ref={draggableRef} aria-labelledby='rcx-media-call-widget-title rcx-media-call-widget-caller-info'>
+				<WidgetBase
+					{...props}
+					ref={draggableRef}
+					aria-labelledby='rcx-media-call-widget-title'
+					aria-describedby='rcx-media-call-widget-caller-info'
+				>

Please confirm that an element with id="rcx-media-call-widget-title" is always present (WidgetHeader).


37-39: Guard DOM usage; avoid SSR warnings

useLayoutEffect doesn’t run on the server, but add a document check to be safe in mixed render environments.

-	useLayoutEffect(() => {
-		boundingRef(document.body);
-	}, [boundingRef]);
+	useLayoutEffect(() => {
+		if (typeof document !== 'undefined') {
+			boundingRef(document.body);
+		}
+	}, [boundingRef]);

43-47: Auto-focus should be opt-in

Auto-focusing can unexpectedly steal focus. Consider a prop (autoFocus = false) and pass it to FocusScope.

-type WidgetProps = {
-	children: React.ReactNode;
-} & ComponentProps<typeof WidgetBase>;
+type WidgetProps = {
+	children: React.ReactNode;
+	autoFocus?: boolean;
+} & ComponentProps<typeof WidgetBase>;
@@
-const Widget = ({ children, ...props }: WidgetProps) => {
+const Widget = ({ children, autoFocus = false, ...props }: WidgetProps) => {
@@
-			<FocusScope autoFocus>
+			<FocusScope autoFocus={autoFocus}>

12-26: Initial position via style: consider persisting/reading from draggable API

You already have a TODO; persisting last position (e.g., in localStorage) improves UX.

packages/ui-voip/src/v2/MockedMediaCallProvider.tsx (3)

29-31: Type onDeviceChange to Device

Match the context signature for stronger type safety.

-import { useState } from 'react';
+import { useState } from 'react';
+import type { Device } from '@rocket.chat/ui-contexts';
@@
-const onDeviceChange = (device: any) => {
+const onDeviceChange = (device: Device) => {

48-56: getPeerInfo: return type OK; consider narrowing local variable name

Minor: avoid overshadowing by reusing the name peerInfo locally; rename to found or record for readability.

-	const getPeerInfo = (id: string) => {
-		const peerInfo = myData.find((item) => item.value === id);
-		if (!peerInfo) {
+	const getPeerInfo = (id: string) => {
+		const found = myData.find((item) => item.value === id);
+		if (!found) {
 			return Promise.resolve(undefined);
 		}
 
 		return Promise.resolve({
-			displayName: peerInfo.label,
-			userId: peerInfo.value,
-			avatarUrl: peerInfo.avatarUrl,
-			username: peerInfo.identifier,
-			callerId: peerInfo.value,
+			displayName: found.label,
+			userId: found.value,
+			avatarUrl: found.avatarUrl,
+			username: found.identifier,
+			callerId: found.value,
 		});
 	};

29-31: Console logs in mock provider

Fine for dev, but ensure these don’t leak into production builds.

Also applies to: 33-41

packages/ui-voip/src/v2/MediaCallContext.ts (2)

146-147: Autocomplete value should reflect external number too

When the selected peer is external ({ number }), consider reflecting it in value to keep input synced.

-		value: peerInfo && 'userId' in peerInfo ? peerInfo.userId : undefined,
+		value:
+			peerInfo ? ('userId' in peerInfo ? peerInfo.userId : (peerInfo.number ?? undefined)) : undefined,

20-29: ConnectionState coverage

If you plan to represent disconnected/error states, consider adding them now to avoid another breaking change later.

Also applies to: 57-64

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b1cefb0 and c3d6925.

⛔ Files ignored due to path filters (1)
  • packages/ui-voip/src/v2/__snapshots__/MediaCallWidget.spec.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (23)
  • packages/ui-voip/src/v2/MediaCallContext.ts (5 hunks)
  • packages/ui-voip/src/v2/MediaCallWidget.stories.tsx (1 hunks)
  • packages/ui-voip/src/v2/MediaCallWidget.tsx (1 hunks)
  • packages/ui-voip/src/v2/MockedMediaCallProvider.tsx (5 hunks)
  • packages/ui-voip/src/v2/TransferModal.tsx (1 hunks)
  • packages/ui-voip/src/v2/components/ActionButton.tsx (3 hunks)
  • packages/ui-voip/src/v2/components/DevicePicker.tsx (2 hunks)
  • packages/ui-voip/src/v2/components/PeerInfo/InternalUser.tsx (1 hunks)
  • packages/ui-voip/src/v2/components/PeerInfo/PeerInfo.stories.tsx (1 hunks)
  • packages/ui-voip/src/v2/components/PeerInfo/PeerInfo.tsx (1 hunks)
  • packages/ui-voip/src/v2/components/PeerInfo/PhoneNumber.tsx (1 hunks)
  • packages/ui-voip/src/v2/components/Widget/Widget.tsx (2 hunks)
  • packages/ui-voip/src/v2/components/Widget/WidgetDraggableContext.ts (1 hunks)
  • packages/ui-voip/src/v2/components/Widget/WidgetHandle.tsx (3 hunks)
  • packages/ui-voip/src/v2/components/Widget/WidgetHeader.tsx (1 hunks)
  • packages/ui-voip/src/v2/components/Widget/WidgetInfo.tsx (2 hunks)
  • packages/ui-voip/src/v2/index.ts (1 hunks)
  • packages/ui-voip/src/v2/useInfoSlots.ts (2 hunks)
  • packages/ui-voip/src/v2/useKeypad.tsx (1 hunks)
  • packages/ui-voip/src/v2/views/IncomingCall.tsx (2 hunks)
  • packages/ui-voip/src/v2/views/NewCall.tsx (2 hunks)
  • packages/ui-voip/src/v2/views/OngoingCall.tsx (3 hunks)
  • packages/ui-voip/src/v2/views/OutgoingCall.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (13)
packages/ui-voip/src/v2/MediaCallWidget.tsx (1)
packages/ui-voip/src/v2/MediaCallContext.ts (1)
  • useMediaCallContext (86-88)
packages/ui-voip/src/v2/views/OutgoingCall.tsx (2)
packages/ui-voip/src/v2/MediaCallContext.ts (1)
  • useMediaCallContext (86-88)
apps/meteor/app/utils/lib/i18n.ts (1)
  • t (6-6)
packages/ui-voip/src/v2/useInfoSlots.ts (1)
packages/ui-voip/src/v2/MediaCallContext.ts (1)
  • ConnectionState (20-20)
packages/ui-voip/src/v2/MediaCallWidget.stories.tsx (1)
packages/livechat/src/components/Button/index.tsx (1)
  • Button (34-99)
packages/ui-voip/src/v2/views/NewCall.tsx (1)
packages/ui-voip/src/v2/MediaCallContext.ts (2)
  • useMediaCallContext (86-88)
  • usePeerAutocomplete (100-150)
packages/ui-voip/src/v2/views/OngoingCall.tsx (3)
packages/ui-voip/src/v2/MediaCallContext.ts (1)
  • useMediaCallContext (86-88)
packages/ui-voip/src/v2/useKeypad.tsx (1)
  • useKeypad (15-44)
packages/ui-voip/src/v2/useInfoSlots.ts (1)
  • useInfoSlots (44-69)
packages/ui-voip/src/v2/components/Widget/WidgetInfo.tsx (1)
packages/ui-voip/src/v2/useInfoSlots.ts (1)
  • Slot (7-11)
packages/ui-voip/src/v2/components/Widget/WidgetHandle.tsx (1)
packages/ui-voip/src/v2/components/Widget/WidgetDraggableContext.ts (1)
  • useDraggableWidget (13-19)
packages/ui-voip/src/v2/views/IncomingCall.tsx (1)
packages/ui-voip/src/v2/MediaCallContext.ts (1)
  • useMediaCallContext (86-88)
packages/ui-voip/src/v2/components/Widget/Widget.tsx (2)
packages/ui-voip/src/components/VoipPopupDraggable/DraggableCore.ts (1)
  • useDraggable (525-584)
packages/ui-voip/src/v2/components/Widget/WidgetDraggableContext.ts (1)
  • DragContext (3-11)
packages/ui-voip/src/v2/components/PeerInfo/InternalUser.tsx (1)
packages/ui-voip/src/v2/components/PeerInfo/PeerInfo.stories.tsx (1)
  • InternalUser (11-29)
packages/ui-voip/src/v2/MockedMediaCallProvider.tsx (2)
packages/ui-voip/src/v2/index.ts (1)
  • PeerInfo (2-2)
packages/ui-voip/src/v2/MediaCallContext.ts (1)
  • PeerInfo (22-22)
packages/ui-voip/src/v2/TransferModal.tsx (1)
packages/ui-voip/src/v2/MediaCallContext.ts (1)
  • usePeerAutocomplete (100-150)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: CodeQL-Build
🔇 Additional comments (13)
packages/ui-voip/src/v2/components/ActionButton.tsx (1)

12-15: Confirm ref target and button type.

Please verify IconButton forwards refs to an HTMLButtonElement and that it sets type="button" by default to avoid accidental form submits.

Also applies to: 17-28

packages/ui-voip/src/v2/components/DevicePicker.tsx (1)

25-26: Type/shape check: onDeviceChange now receives a device object—confirm it matches context type

Ensure the Device object here matches MediaCallContext’s expected shape (id/kind/label, etc.). If context expects a different key (e.g., deviceId), adapt the mapper.

Also applies to: 38-39, 49-50

packages/ui-voip/src/v2/components/PeerInfo/PeerInfo.stories.tsx (1)

14-15: Prop rename alignment looks correct.

Using callerId/displayName matches the updated PeerInfo API and will route to InternalUser.

packages/ui-voip/src/v2/views/IncomingCall.tsx (1)

10-10: API rename to onAccept is handled correctly.

Matches the context change and keeps semantics clear.

packages/ui-voip/src/v2/components/PeerInfo/PeerInfo.tsx (1)

8-11: Approve — 'displayName' type-guard is correct.
InternalUserProps declares displayName and PhoneNumberProps does not, so the 'in' check correctly narrows the union at runtime.

packages/ui-voip/src/v2/views/OutgoingCall.tsx (1)

10-23: Verify connectionState enum casing and i18n key.

  • ConnectionState: I couldn't find the authoritative enum/type in this verification — confirm the ConnectionState definition includes 'CONNECTING' (exact uppercase) and that comparisons in packages/ui-voip/src/v2/views/OutgoingCall.tsx and packages/ui-voip/src/v2/views/OngoingCall.tsx use that exact value or the enum constant.
  • i18n: "meteor_status_connecting" exists in en and many other locales (packages/i18n/src/locales/en.i18n.json and others) — confirmed.
packages/ui-voip/src/v2/MediaCallWidget.tsx (1)

5-9: Approve — early-return on hidden is correct; default verified

MediaCallContext.createContext sets hidden: false and MockedMediaCallProvider supplies hidden: false (packages/ui-voip/src/v2/MediaCallContext.ts, packages/ui-voip/src/v2/MockedMediaCallProvider.tsx).

packages/ui-voip/src/v2/views/OngoingCall.tsx (1)

30-32: Good gating on transient connection states.

Disabling dialpad/forward during CONNECTING/RECONNECTING is solid UX.

Also applies to: 51-55

packages/ui-voip/src/v2/useInfoSlots.ts (1)

33-41: Use a dedicated "meteor_status_reconnecting" i18n key (or confirm intent).

Repo search shows only meteor_status_connecting is present (packages/ui-voip/src/v2/useInfoSlots.ts, packages/ui-voip/src/v2/views/OngoingCall.tsx, packages/ui-voip/src/v2/views/OutgoingCall.tsx and locale files); no meteor_status_reconnecting key found. Add meteor_status_reconnecting and update useInfoSlots for distinct messaging, or confirm that reusing meteor_status_connecting is intentional.

packages/ui-voip/src/v2/TransferModal.tsx (1)

56-75: i18n keys present — no missing keys found.
Transfer_call, Close, Cancel and Hang_up_and_transfer_call are defined (seen in packages/i18n/src/locales/en.i18n.json) and present across locale files; tests reference these keys.

packages/ui-voip/src/v2/components/PeerInfo/InternalUser.tsx (1)

18-18: Verify token name "secondary-info" exists in Fuselage theme

If not, switch to a supported token (e.g., 'hint' or 'secondary-info' equivalent).

packages/ui-voip/src/v2/MockedMediaCallProvider.tsx (1)

11-16: PeerInfo shape change looks consistent

displayName/userId/username/callerId mapping aligns with the new context type.

Please run a repo-wide check for legacy props (name, identifier) to avoid stale usages:

packages/ui-voip/src/v2/MediaCallContext.ts (1)

45-47: API change: onCall/onAccept now Promise-returning with no params — verify call sites

Confirm all call sites await/handle the returned Promise and no longer pass a peerId (selection now flows via onSelectPeer).

@codecov
Copy link

codecov bot commented Sep 18, 2025

Codecov Report

❌ Patch coverage is 76.03306% with 29 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.39%. Comparing base (7f53726) to head (54ec62d).
⚠️ Report is 1 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #36985      +/-   ##
===========================================
+ Coverage    66.25%   66.39%   +0.14%     
===========================================
  Files         3389     3390       +1     
  Lines       115174   115279     +105     
  Branches     21078    21111      +33     
===========================================
+ Hits         76304    76536     +232     
+ Misses       36265    36141     -124     
+ Partials      2605     2602       -3     
Flag Coverage Δ
e2e 57.01% <ø> (+0.05%) ⬆️
unit 71.42% <76.03%> (+0.16%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@gabriellsh gabriellsh added this to the 7.11.0 milestone Sep 18, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
packages/ui-voip/src/v2/TransferModal.tsx (3)

52-58: Hook Modal onClose to support ESC/overlay dismissal

Wire onClose so users can dismiss via ESC/backdrop; improves a11y and matches common Modal behavior.

-        <Modal open aria-labelledby={modalId}>
+        <Modal open aria-labelledby={modalId} onClose={onCancel}>

34-50: Don’t throw from a click handler; gate confirm by valid peer shape

Avoid uncaught exceptions from UI. Compute a safe isConfirmable and disable the action unless the peer has userId or number.

   const autocomplete = usePeerAutocomplete(setPeer, peer);
 
-  const confirm = () => {
-    if (!peer) {
-      return;
-    }
+  const isConfirmable = Boolean(peer && ('userId' in peer || 'number' in peer));
+
+  const confirm = () => {
+    if (!isConfirmable || !peer) {
+      return;
+    }
 
     if ('userId' in peer) {
       onConfirm('user', peer.userId);
       return;
     }
 
     if ('number' in peer) {
       onConfirm('sip', peer.number);
       return;
     }
 
-    throw new Error('Peer info is missing userId and/or number');
+    return;
   };
-          <Button danger onClick={confirm} disabled={!peer}>
+          <Button danger onClick={confirm} disabled={!isConfirmable}>
             {t('Hang_up_and_transfer_call')}
           </Button>

Also applies to: 72-74


30-31: Tiny nit: simplify initial state

You can omit the explicit undefined initializer.

-  const [peer, setPeer] = useState<PeerInfoType | undefined>(undefined);
+  const [peer, setPeer] = useState<PeerInfoType | undefined>();
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c3d6925 and 024bf5c.

📒 Files selected for processing (6)
  • packages/i18n/src/locales/en.i18n.json (2 hunks)
  • packages/ui-voip/src/v2/TransferModal.tsx (1 hunks)
  • packages/ui-voip/src/v2/components/PeerInfo/PhoneNumber.tsx (1 hunks)
  • packages/ui-voip/src/v2/components/Widget/WidgetDraggableContext.ts (1 hunks)
  • packages/ui-voip/src/v2/components/Widget/WidgetInfo.tsx (2 hunks)
  • packages/ui-voip/src/v2/views/IncomingCall.tsx (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/i18n/src/locales/en.i18n.json
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/ui-voip/src/v2/components/PeerInfo/PhoneNumber.tsx
  • packages/ui-voip/src/v2/views/IncomingCall.tsx
  • packages/ui-voip/src/v2/components/Widget/WidgetDraggableContext.ts
  • packages/ui-voip/src/v2/components/Widget/WidgetInfo.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
packages/ui-voip/src/v2/TransferModal.tsx (1)
packages/ui-voip/src/v2/MediaCallContext.ts (1)
  • usePeerAutocomplete (100-150)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build
🔇 Additional comments (2)
packages/ui-voip/src/v2/TransferModal.tsx (2)

32-33: Good integration with the new autocomplete contract

Using usePeerAutocomplete(setPeer, peer) cleanly externalizes selection state.


56-57: i18n keys present — no action required

Transfer_call, Hang_up_and_transfer_call, Close and Cancel exist in packages/i18n/src/locales (e.g. packages/i18n/src/locales/en.i18n.json: "Cancel"@933, "Close"@1039, "Hang_up_and_transfer_call"@2370, "Transfer_call"@5239).

@gabriellsh gabriellsh added the stat: QA assured Means it has been tested and approved by a company insider label Sep 19, 2025
@dionisio-bot dionisio-bot bot added the stat: ready to merge PR tested and approved waiting for merge label Sep 19, 2025
@kodiakhq kodiakhq bot merged commit 9885465 into develop Sep 19, 2025
48 checks passed
@kodiakhq kodiakhq bot deleted the chore/mediaCallComponents branch September 19, 2025 16:24
@coderabbitai coderabbitai bot mentioned this pull request Sep 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stat: QA assured Means it has been tested and approved by a company insider stat: ready to merge PR tested and approved waiting for merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants