Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c92c48d
WIP
gabriellsh Dec 3, 2025
37886bf
Merge remote-tracking branch 'origin/develop' into feat/callHistoryCo…
gabriellsh Dec 5, 2025
268c7ba
add fuselage-ui-kit
gabriellsh Dec 8, 2025
338c578
Add Internal and External user components
gabriellsh Dec 8, 2025
42b2350
Translations
gabriellsh Dec 8, 2025
f9b9d19
implement useFullStartDate
gabriellsh Dec 8, 2025
b20679b
Wip 2 contextualbar and more stories
gabriellsh Dec 8, 2025
c9d86d3
Merge branch 'develop' into feat/callHistoryContextual
gabriellsh Dec 9, 2025
da55d2a
Add userCard to internal user
gabriellsh Dec 9, 2025
c5b6081
wip blocks
gabriellsh Dec 9, 2025
51b774d
Implement CallHistoryActions
gabriellsh Dec 9, 2025
5dc7765
export
gabriellsh Dec 9, 2025
034b8ee
fix external export
gabriellsh Dec 9, 2025
409b87c
Merge remote-tracking branch 'origin/develop' into feat/callHistoryCo…
gabriellsh Dec 10, 2025
987c190
use `getHistoryMessagePayload` to generate blocks and fix translation
gabriellsh Dec 10, 2025
0137b94
missing translation string
gabriellsh Dec 10, 2025
6b5f5b3
Merge remote-tracking branch 'origin/develop' into feat/callHistoryCo…
gabriellsh Dec 12, 2025
b87e8b6
export types
gabriellsh Dec 12, 2025
1a94c8e
Fix contextualbar size
gabriellsh Dec 12, 2025
2e26b5a
Merge remote-tracking branch 'origin/develop' into feat/callHistoryCo…
gabriellsh Dec 16, 2025
d3022bf
Fix Icon and make text bold
gabriellsh Dec 16, 2025
b925003
fix review
gabriellsh Dec 16, 2025
7fb6a6a
Add tests
gabriellsh Dec 16, 2025
75d8e5b
Merge branch 'develop' into feat/callHistoryContextual
gabriellsh Dec 16, 2025
f11015f
Fix test timezone
gabriellsh Dec 17, 2025
d1f2b32
Merge branch 'develop' into feat/callHistoryContextual
gabriellsh Dec 17, 2025
386cbdd
Merge branch 'develop' into feat/callHistoryContextual
kodiakhq[bot] Dec 17, 2025
86981c9
Merge branch 'develop' into feat/callHistoryContextual
kodiakhq[bot] Dec 17, 2025
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
6 changes: 6 additions & 0 deletions packages/i18n/src/locales/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,8 @@
"Calendar_settings": "Calendar settings",
"Call": "Call",
"Call_Already_Ended": "Call Already Ended",
"Call_ID": "Call ID",
"Call_info": "Call info",
"Call_Information": "Call Information",
"Call_again": "Call again",
"Call_back": "Call back",
Expand Down Expand Up @@ -1803,6 +1805,7 @@
"Duplicate_file_name_found": "Duplicate file name found.",
"Duplicate_private_group_name": "A Private Group with name '%s' exists",
"Duplicated_Email_address_will_be_ignored": "Duplicated email address will be ignored.",
"Duration": "Duration",
"E2EE_Composer_Unencrypted_Message": "You're sending an unencrypted message",
"E2EE_password_reset": "E2EE password reset",
"E2EE_alert": "<b>Enabling E2EE affects other functionalities</b> <ul><li> - Encrypted content cannot be found by search</li> <li> - Encrypted content cannot be audited</li> <li> - Bot interactions may not work with encrypted messages</li></ul>",
Expand Down Expand Up @@ -2569,6 +2572,7 @@
"Incoming_call_from": "Incoming call from",
"Incoming_call_from__roomName__": "Incoming call from {{roomName}}",
"Incoming_call_transfer": "Incoming call transfer",
"Incoming_voice_call": "Incoming voice call",
"Incoming_voice_call_canceled_suddenly": "An Incoming Voice Call was canceled suddenly.",
"Incoming_voice_call_canceled_user_not_registered": "An Incoming Voice Call was canceled due to an unexpected error.",
"Industry": "Industry",
Expand Down Expand Up @@ -3969,6 +3973,7 @@
"Out_of_seats": "Out of Seats",
"Outdated": "Outdated",
"Outgoing": "Outgoing",
"Outgoing_voice_call": "Outgoing voice call",
"Outgoing_WebHook": "Outgoing WebHook",
"Outgoing_WebHook_Description": "Get data out of Rocket.Chat in real-time.",
"Outlook_Calendar": "Outlook Calendar",
Expand Down Expand Up @@ -5525,6 +5530,7 @@
"UserData_MessageLimitPerRequest": "Message Limit per Request",
"UserData_ProcessingFrequency": "Processing Frequency (Minutes)",
"User_Info": "User Info",
"User_info": "User info",
"User_Interface": "User Interface",
"User_Presence": "User Presence",
"User_Settings": "User Settings",
Expand Down
6 changes: 4 additions & 2 deletions packages/ui-voip/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
"lint": "eslint --ext .js,.jsx,.ts,.tsx .",
"lint:fix": "eslint --ext .js,.jsx,.ts,.tsx . --fix",
"storybook": "storybook dev -p 6006",
"test": "jest",
"test": "yarn testunit",
"test-storybook": "npx concurrently -k -s first -n \"SB,TEST\" \"yarn storybook --ci\" \"npx wait-on tcp:127.0.0.1:6006 && yarn exec test-storybook\"",
"testunit": "jest",
"testunit": "TZ=UTC jest",
"typecheck": "tsc --noEmit --skipLibCheck -p tsconfig.json"
},
"dependencies": {
Expand All @@ -35,6 +35,7 @@
"@rocket.chat/fuselage": "^0.70.0",
"@rocket.chat/fuselage-hooks": "~0.38.1",
"@rocket.chat/fuselage-tokens": "~0.33.2",
"@rocket.chat/fuselage-ui-kit": "workspace:^",
"@rocket.chat/icons": "~0.46.0",
"@rocket.chat/jest-presets": "workspace:~",
"@rocket.chat/mock-providers": "workspace:~",
Expand Down Expand Up @@ -79,6 +80,7 @@
"@rocket.chat/css-in-js": "*",
"@rocket.chat/fuselage": "*",
"@rocket.chat/fuselage-hooks": "*",
"@rocket.chat/fuselage-ui-kit": "workspace:^",
"@rocket.chat/icons": "*",
"@rocket.chat/styled": "*",
"@rocket.chat/ui-avatar": "workspace:^",
Expand Down
3 changes: 3 additions & 0 deletions packages/ui-voip/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ export { MediaCallContext, useMediaCallExternalContext as useMediaCallContext, t

export { useMediaCallAction } from './hooks';

export { CallHistoryContextualBar } from './views';
export type { InternalCallHistoryContact, ExternalCallHistoryContact, CallHistoryData } from './views';

export { getHistoryMessagePayload } from './ui-kit/getHistoryMessagePayload';
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { mockAppRoot } from '@rocket.chat/mock-providers';
import type { Meta, StoryObj } from '@storybook/react';
import type { ReactElement } from 'react';

import type { HistoryActionCallbacks } from './CallHistoryActions';
import CallHistoryActions from './CallHistoryActions';

const noop = () => undefined;

const meta = {
title: 'V2/Views/CallHistoryContextualbar/CallHistoryActions',
component: CallHistoryActions,
decorators: [
mockAppRoot()
.withTranslations('en', 'core', {
Options: 'Options',
Voice_call: 'Voice call',
Video_call: 'Video call',
Jump_to_message: 'Jump to message',
Direct_Message: 'Direct Message',
User_info: 'User info',
})
.withDefaultLanguage('en-US')
.buildStoryDecorator(),
(Story): ReactElement => <Story />,
],
} satisfies Meta<typeof CallHistoryActions>;

export default meta;

type Story = StoryObj<typeof meta>;

const actionList = ['voiceCall', 'videoCall', 'jumpToMessage', 'directMessage', 'userInfo'];

const getArgs = (index: number) => {
return Object.fromEntries(actionList.slice(0, index).map((action) => [action, noop])) as HistoryActionCallbacks;
};

export const Default: Story = {
args: {
onClose: noop,
actions: getArgs(5),
},
};

export const WithLessActions: Story = {
args: {
onClose: noop,
actions: getArgs(3),
},
};

export const WithSingleAction: Story = {
args: {
onClose: noop,
actions: getArgs(1),
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import type { Keys as IconName } from '@rocket.chat/icons';
import { ContextualbarActions, ContextualbarClose, GenericMenu } from '@rocket.chat/ui-client';
import type { TFunction } from 'i18next';
import { useTranslation } from 'react-i18next';

type HistoryActions = 'voiceCall' | 'videoCall' | 'jumpToMessage' | 'directMessage' | 'userInfo';

export type HistoryActionCallbacks = {
[K in HistoryActions]?: () => void;
};

type CallHistoryActionsProps = {
onClose: () => void;
actions: HistoryActionCallbacks;
};

const iconDictionary: Record<HistoryActions, IconName> = {
voiceCall: 'phone',
videoCall: 'video',
jumpToMessage: 'jump',
directMessage: 'balloon',
userInfo: 'user',
} as const;

const i18nDictionary: Record<HistoryActions, string> = {
voiceCall: 'Voice_call',
videoCall: 'Video_call',
jumpToMessage: 'Jump_to_message',
directMessage: 'Direct_Message',
userInfo: 'User_info',
} as const;

const getItems = (actions: HistoryActionCallbacks, t: TFunction) => {
return (Object.entries(actions) as [HistoryActions, () => void][])
.filter(([_, callback]) => callback)
.map(([action, callback]) => ({
id: action,
icon: iconDictionary[action],
content: t(i18nDictionary[action]),
onClick: callback,
}));
};

const CallHistoryActions = ({ onClose, actions }: CallHistoryActionsProps) => {
const { t } = useTranslation();

const items = getItems(actions, t);
return (
<ContextualbarActions>
{items.length > 0 && <GenericMenu title={t('Options')} items={items} />}
<ContextualbarClose onClick={onClose} />
</ContextualbarActions>
);
};

export default CallHistoryActions;
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { mockAppRoot } from '@rocket.chat/mock-providers';
import { composeStories } from '@storybook/react';
import { render } from '@testing-library/react';
import { axe } from 'jest-axe';

import * as contextualbarStories from './CallHistoryContextualbar.stories';

const testCases = Object.values(composeStories(contextualbarStories)).map((Story) => [Story.storyName || 'Story', Story]);

test.each(testCases)(`renders %s without crashing`, async (_storyname, Story) => {
const view = render(<Story />, { wrapper: mockAppRoot().build() });
expect(view.baseElement).toMatchSnapshot();
});

test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => {
const { container } = render(<Story />, { wrapper: mockAppRoot().build() });

const results = await axe(container);
expect(results).toHaveNoViolations();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { mockAppRoot } from '@rocket.chat/mock-providers';
import type { Meta, StoryObj } from '@storybook/react';
import type { ReactElement } from 'react';

import CallHistoryContextualbar from './CallHistoryContextualbar';

const noop = () => undefined;

const meta = {
title: 'V2/Views/CallHistoryContextualbar',
component: CallHistoryContextualbar,
decorators: [
mockAppRoot()
.withTranslations('en', 'core', {
Call_info: 'Call info',
Direct_message: 'Direct message',
Call: 'Call',
Call_ended_bold: '*Voice call ended*',
Incoming_voice_call: 'Incoming voice call',
Outgoing_voice_call: 'Outgoing voice call',
Duration: 'Duration',
Voice_call_extension: 'Voice call extension',
Call_ID: 'Call ID',
Options: 'Options',
Voice_call: 'Voice call',
Video_call: 'Video call',
Jump_to_message: 'Jump to message',
Direct_Message: 'Direct Message',
User_info: 'User info',
})
.withDefaultLanguage('en-US')
.buildStoryDecorator(),
(Story): ReactElement => <Story />,
],
} satisfies Meta<typeof CallHistoryContextualbar>;

export default meta;

type Story = StoryObj<typeof meta>;

const externalContact = {
number: '1234567890',
};

const internalContact = {
_id: '1234567890',
name: 'John Doe',
username: 'john.doe',
voiceCallExtension: '0000',
};

export const Default: Story = {
args: {
onClose: noop,
actions: {
voiceCall: noop,
videoCall: noop,
jumpToMessage: noop,
directMessage: noop,
userInfo: noop,
},
contact: internalContact,
data: {
callId: '1234567890',
direction: 'inbound',
duration: 100,
startedAt: new Date('2025-02-07T12:00:00.000Z'),
state: 'ended',
},
},
};

export const ExternalContact: Story = {
args: {
onClose: noop,
actions: {
voiceCall: noop,
},
data: {
callId: '1234567890',
direction: 'inbound',
duration: 100,
startedAt: new Date('2025-02-07T12:00:00.000Z'),
state: 'ended',
},
contact: externalContact,
},
};
Loading
Loading