Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
2f0492c
feat(voip): add wide layout mode for CallView and CallButtons
diegolmello Apr 6, 2026
2879ae1
feat(voip): side-by-side dialpad layout for landscape orientation
diegolmello Apr 6, 2026
b6dd822
fix(test): add global TrueSheet mock to jest.setup.js
diegolmello Apr 6, 2026
3bd309d
chore: format code and fix lint issues
diegolmello Apr 6, 2026
c32e3f5
chore: regenerate yarn.lock after merge
diegolmello Apr 6, 2026
43deb06
chore: update snapshots after merge
diegolmello Apr 6, 2026
14df681
fix: remove deprecated bottomSheet prop and add layoutMode to tests
diegolmello Apr 6, 2026
bab15a8
chore: update snapshots after develop merge
diegolmello Apr 6, 2026
204305e
Add mock
diegolmello Apr 7, 2026
92ff0d9
Add CallView to tablet stack
diegolmello Apr 7, 2026
eb1f951
Fix UI
diegolmello Apr 7, 2026
d46f24a
Stories and tests
diegolmello Apr 7, 2026
bd3f362
refactor(CallView): extract LayoutMode to types module
diegolmello Apr 7, 2026
0425eb1
chore(CallView): regenerate snapshots after alignContent removal
diegolmello Apr 7, 2026
ca64d5b
refactor(CallView): collapse CallButtons into config array
diegolmello Apr 7, 2026
14b37df
test(CallView): use within() for row-membership assertions
diegolmello Apr 7, 2026
d7852b6
refactor(CallView): centralize layout mode in useCallLayoutMode
diegolmello Apr 7, 2026
c50a477
fix(CallView): tighten CallButtons config-array types
diegolmello Apr 7, 2026
13f532c
refactor(Dialpad): collapse portrait/landscape into single component
diegolmello Apr 7, 2026
9f53e6f
chore: format code and fix lint issues
diegolmello Apr 7, 2026
fdd2229
Update: .gitignore CLAUDE.md
diegolmello Apr 8, 2026
46bb7c7
chore: format code and fix lint issues
diegolmello Apr 8, 2026
6daeebe
chore(voip): enable Message action in default mockCall
diegolmello Apr 8, 2026
dca8b5e
test(Dialpad): drive layout stories via ResponsiveLayoutContext
diegolmello Apr 8, 2026
fbea0eb
test(CallView): fix responsive layout mock for wide-layout assertions
diegolmello Apr 8, 2026
00c16c4
test(CallActionButton): align stories responsive mock with prod clamp
diegolmello Apr 8, 2026
dde84b1
chore: remove CLAUDE.md from repo
diegolmello Apr 8, 2026
f8974e2
refactor(CallView): inline layout/theme hooks at component leaves
diegolmello Apr 8, 2026
0864e40
chore(CallView): refresh stale CallerInfo and Dialpad snapshots
diegolmello Apr 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,12 @@ e2e/e2e_account.ts
**/e2e_account.js
**/e2e_account.ts

*.p8
*.p8
.worktrees/
.omc/
.claude/
.agents/
skills-lock.json
CLAUDE.local.md
AGENTS.md
.superset/
1 change: 0 additions & 1 deletion app/containers/NewMediaCall/FilterHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ export const FilterHeader = (): React.ReactElement => {
<FormTextInput
containerStyle={styles.searchInputContainer}
iconRight='search'
bottomSheet
showErrorMessage={false}
testID='new-media-call-search-input'
onChangeText={handleChangeText}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,32 +94,41 @@ exports[`Story Snapshots: Default should match snapshot 1`] = `
accessible={true}
autoCapitalize="none"
autoCorrect={false}
keyboardAppearance="light"
onChangeText={[Function]}
placeholderTextColor="#9EA2A8"
style={
[
{
"backgroundColor": "transparent",
"borderRadius": 4,
"borderWidth": 1,
"fontFamily": "Inter",
"fontSize": 16,
"fontWeight": "400",
"paddingHorizontal": 16,
"paddingVertical": 14,
"textAlign": "left",
},
undefined,
{
"paddingRight": 45,
"color": "#1F2329",
},
[
{
"backgroundColor": "transparent",
"borderRadius": 4,
"borderWidth": 1,
"fontFamily": "Inter",
"fontSize": 16,
"fontWeight": "400",
"paddingHorizontal": 16,
"paddingVertical": 14,
"textAlign": "left",
},
undefined,
{
"paddingRight": 45,
},
{
"backgroundColor": "#FFFFFF",
"borderColor": "#9EA2A8",
"color": "#1F2329",
},
{},
undefined,
],
{
"backgroundColor": "#FFFFFF",
"borderColor": "#9EA2A8",
"color": "#1F2329",
"textAlign": "auto",
},
{},
undefined,
]
}
testID="new-media-call-search-input"
Expand Down Expand Up @@ -285,32 +294,41 @@ exports[`Story Snapshots: WithFilter should match snapshot 1`] = `
accessible={true}
autoCapitalize="none"
autoCorrect={false}
keyboardAppearance="light"
onChangeText={[Function]}
placeholderTextColor="#9EA2A8"
style={
[
{
"backgroundColor": "transparent",
"borderRadius": 4,
"borderWidth": 1,
"fontFamily": "Inter",
"fontSize": 16,
"fontWeight": "400",
"paddingHorizontal": 16,
"paddingVertical": 14,
"textAlign": "left",
},
undefined,
{
"paddingRight": 45,
"color": "#1F2329",
},
[
{
"backgroundColor": "transparent",
"borderRadius": 4,
"borderWidth": 1,
"fontFamily": "Inter",
"fontSize": 16,
"fontWeight": "400",
"paddingHorizontal": 16,
"paddingVertical": 14,
"textAlign": "left",
},
undefined,
{
"paddingRight": 45,
},
{
"backgroundColor": "#FFFFFF",
"borderColor": "#9EA2A8",
"color": "#1F2329",
},
{},
undefined,
],
{
"backgroundColor": "#FFFFFF",
"borderColor": "#9EA2A8",
"color": "#1F2329",
"textAlign": "auto",
},
{},
undefined,
]
}
testID="new-media-call-search-input"
Expand Down
88 changes: 88 additions & 0 deletions app/lib/services/voip/mockCall.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import type { CallState, IClientMediaCall } from '@rocket.chat/media-signaling';

import Navigation from '../../navigation/appNavigation';
import { useCallStore } from './useCallStore';

export interface MockCallOverrides {
callState?: CallState;
isMuted?: boolean;
isOnHold?: boolean;
isSpeakerOn?: boolean;
callStartTime?: number | null;
roomId?: string | null;
contact?: {
id?: string;
displayName?: string;
username?: string;
sipExtension?: string;
};
}

const DEFAULT_CONTACT = {
id: 'mock-contact-id',
displayName: 'Bob Burnquist',
username: 'bob.burnquist',
sipExtension: ''
};
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/**
* Build a fake `IClientMediaCall` good enough to render `CallView` without a real SIP/WebRTC stack.
* No-op `setMuted/setHeld/hangup/sendDTMF` and a no-op event emitter so store subscriptions are safe.
*/
export function createMockCall(overrides: MockCallOverrides = {}): IClientMediaCall {
const contact = { ...DEFAULT_CONTACT, ...overrides.contact };
const callState: CallState = overrides.callState ?? 'active';

const mock = {
callId: 'mock-call-id',
state: callState,
muted: overrides.isMuted ?? false,
held: overrides.isOnHold ?? false,
remoteMute: false,
remoteHeld: false,
contact,
setMuted: () => {},
setHeld: () => {},
hangup: () => {},
reject: () => {},
sendDTMF: () => {},
emitter: {
on: () => {},
off: () => {}
}
};

return mock as unknown as IClientMediaCall;
}

/**
* Seed `useCallStore` with a mock call so `CallView` renders without going through `setCall`
* (which subscribes real listeners and starts `InCallManager`).
*/
export function seedMockCall(overrides: MockCallOverrides = {}): void {
const mockCall = createMockCall(overrides);
const callState: CallState = overrides.callState ?? 'active';

useCallStore.setState({
call: mockCall,
callId: mockCall.callId,
callState,
isMuted: overrides.isMuted ?? false,
isOnHold: overrides.isOnHold ?? false,
isSpeakerOn: overrides.isSpeakerOn ?? false,
callStartTime: overrides.callStartTime ?? (callState === 'active' ? Date.now() : null),
contact: { ...DEFAULT_CONTACT, ...overrides.contact },
roomId: overrides.roomId ?? 'mock-room-id',
focused: true,
controlsVisible: true
});
}

/**
* Dev helper: seed a mock call and navigate to `CallView`. Use from a debug button to exercise
* the call UI on the iOS simulator without a real call.
*/
export function launchMockCallView(overrides: MockCallOverrides = {}): void {
seedMockCall(overrides);
Navigation.navigate('CallView');
}
2 changes: 2 additions & 0 deletions app/stacks/MasterDetailStack/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import E2EEncryptionSecurityView from '../../views/E2EEncryptionSecurityView';
import AttachmentView from '../../views/AttachmentView';
import ModalBlockView from '../../views/ModalBlockView';
import JitsiMeetView from '../../views/JitsiMeetView';
import CallView from '../../views/CallView';
import StatusView from '../../views/StatusView';
import CreateDiscussionView from '../../views/CreateDiscussionView';
import E2ESaveYourPasswordView from '../../views/E2ESaveYourPasswordView';
Expand Down Expand Up @@ -234,6 +235,7 @@ const InsideStackNavigator = React.memo(() => {
/>
{/* @ts-ignore */}
<InsideStack.Screen name='ShareView' component={ShareView} />
<InsideStack.Screen name='CallView' component={CallView} options={{ headerShown: false }} />
</InsideStack.Navigator>
);
});
Expand Down
1 change: 1 addition & 0 deletions app/stacks/MasterDetailStack/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,4 +222,5 @@ export type MasterDetailInsideStackParamList = {
room: ISubscription;
thread: any; // TODO: Change
};
CallView: undefined;
};
49 changes: 49 additions & 0 deletions app/views/CallView/CallView.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { View, StyleSheet } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';

import CallView from '.';
import CallerInfo from './components/CallerInfo';
import { CallButtons } from './components/CallButtons';
import { styles as callViewStyles } from './styles';
import { useTheme } from '../../theme';
import { useCallStore } from '../../lib/services/voip/useCallStore';
import {
BASE_ROW_HEIGHT,
Expand Down Expand Up @@ -113,3 +117,48 @@ export const SpeakerOn = () => {
setStoreState({ callState: 'active', isSpeakerOn: true });
return <CallView />;
};

// Tablet / wide layout stories — force layoutMode='wide' via ResponsiveLayoutContext width
const TabletCallView = () => {
const { colors } = useTheme();
const call = useCallStore(state => state.call);
if (!call) return null;
return (
<ResponsiveLayoutContext.Provider value={{ ...responsiveLayoutProviderLargeFontValue(1), width: 800 }}>
<View style={[callViewStyles.contentContainer, { backgroundColor: colors.surfaceLight }]}>
<CallerInfo />
<CallButtons />
</View>
</ResponsiveLayoutContext.Provider>
);
};

export const TabletConnectedCall = () => {
setStoreState({ callState: 'active', callStartTime: mockCallStartTime - 61000 });
return <TabletCallView />;
};

export const TabletConnectingCall = () => {
setStoreState({ callState: 'accepted', callStartTime: null });
return <TabletCallView />;
};

export const TabletMutedCall = () => {
setStoreState({ callState: 'active', isMuted: true });
return <TabletCallView />;
};

export const TabletOnHoldCall = () => {
setStoreState({ callState: 'active', isOnHold: true });
return <TabletCallView />;
};

export const TabletMutedAndOnHold = () => {
setStoreState({ callState: 'active', isMuted: true, isOnHold: true });
return <TabletCallView />;
};

export const TabletSpeakerOn = () => {
setStoreState({ callState: 'active', isSpeakerOn: true });
return <TabletCallView />;
};
Loading
Loading