Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 .changeset/strong-maps-act.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@rocket.chat/meteor": minor
"@rocket.chat/ui-voip": minor
---

Introduces an info button to voice call's in-chat history message, which opens a contextual bar with more detailed information about the voice call.
2 changes: 1 addition & 1 deletion apps/meteor/server/services/media-call/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ export class MediaCallService extends ServiceClassInternal implements IMediaCall
const state = this.getCallHistoryItemState(call);
const duration = this.getCallDuration(call);

const record = getHistoryMessagePayload(state, duration);
const record = getHistoryMessagePayload(state, duration, call._id);

try {
const message = await sendMessage(user, record, room, false);
Expand Down
71 changes: 50 additions & 21 deletions packages/ui-voip/src/ui-kit/getHistoryMessagePayload.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { callStateToTranslationKey, callStateToIcon, getFormattedCallDuration, getHistoryMessagePayload } from './getHistoryMessagePayload';
import {
callStateToTranslationKey,
callStateToIcon,
getFormattedCallDuration,
getHistoryMessagePayload,
getHistoryAction,
} from './getHistoryMessagePayload';

const appId = 'media-call-core';
describe('callStateToTranslationKey', () => {
Expand Down Expand Up @@ -31,27 +37,27 @@ describe('callStateToTranslationKey', () => {
describe('callStateToIcon', () => {
it('should return correct icon for "ended" state', () => {
const result = callStateToIcon('ended');
expect(result).toEqual({ type: 'icon', icon: 'phone-off', variant: 'secondary' });
expect(result).toEqual({ type: 'icon', icon: 'phone-off', variant: 'secondary', framed: true });
});

it('should return correct icon for "not-answered" state', () => {
const result = callStateToIcon('not-answered');
expect(result).toEqual({ type: 'icon', icon: 'clock', variant: 'danger' });
expect(result).toEqual({ type: 'icon', icon: 'clock', variant: 'danger', framed: true });
});

it('should return correct icon for "failed" state', () => {
const result = callStateToIcon('failed');
expect(result).toEqual({ type: 'icon', icon: 'phone-issue', variant: 'danger' });
expect(result).toEqual({ type: 'icon', icon: 'phone-issue', variant: 'danger', framed: true });
});

it('should return correct icon for "error" state', () => {
const result = callStateToIcon('error');
expect(result).toEqual({ type: 'icon', icon: 'phone-issue', variant: 'danger' });
expect(result).toEqual({ type: 'icon', icon: 'phone-issue', variant: 'danger', framed: true });
});

it('should return correct icon for "transferred" state', () => {
const result = callStateToIcon('transferred');
expect(result).toEqual({ type: 'icon', icon: 'arrow-forward', variant: 'secondary' });
expect(result).toEqual({ type: 'icon', icon: 'arrow-forward', variant: 'secondary', framed: true });
});
});

Expand Down Expand Up @@ -102,9 +108,24 @@ describe('getFormattedCallDuration', () => {
});
});

const actionObj = {
type: 'icon_button',
icon: { type: 'icon', icon: 'info', variant: 'default' },
actionId: 'open-history',
appId,
blockId: 'callid',
};

describe('getHistoryAction', () => {
it('should return correct action for "ended" state', () => {
const result = getHistoryAction('callid');
expect(result).toEqual(actionObj);
});
});

describe('getHistoryMessagePayload', () => {
it('should return correct payload for "ended" state without duration', () => {
const result = getHistoryMessagePayload('ended', undefined);
const result = getHistoryMessagePayload('ended', undefined, 'callid');
expect(result).toEqual({
msg: '',
groupable: false,
Expand All @@ -116,9 +137,10 @@ describe('getHistoryMessagePayload', () => {
{
background: 'default',
elements: [
{ type: 'icon', icon: 'phone-off', variant: 'secondary' },
{ type: 'icon', icon: 'phone-off', variant: 'secondary', framed: true },
{ type: 'mrkdwn', i18n: { key: 'Call_ended_bold' }, text: 'Call ended' },
],
action: actionObj,
},
],
},
Expand All @@ -127,7 +149,7 @@ describe('getHistoryMessagePayload', () => {
});

it('should return correct payload for "ended" state with duration', () => {
const result = getHistoryMessagePayload('ended', 125);
const result = getHistoryMessagePayload('ended', 125, 'callid');
expect(result).toEqual({
msg: '',
groupable: false,
Expand All @@ -139,9 +161,10 @@ describe('getHistoryMessagePayload', () => {
{
background: 'default',
elements: [
{ type: 'icon', icon: 'phone-off', variant: 'secondary' },
{ type: 'icon', icon: 'phone-off', variant: 'secondary', framed: true },
{ type: 'mrkdwn', i18n: { key: 'Call_ended_bold' }, text: 'Call ended' },
],
action: actionObj,
},
{
background: 'secondary',
Expand All @@ -154,7 +177,7 @@ describe('getHistoryMessagePayload', () => {
});

it('should return correct payload for "not-answered" state', () => {
const result = getHistoryMessagePayload('not-answered', undefined);
const result = getHistoryMessagePayload('not-answered', undefined, 'callid');
expect(result).toEqual({
msg: '',
groupable: false,
Expand All @@ -166,9 +189,10 @@ describe('getHistoryMessagePayload', () => {
{
background: 'default',
elements: [
{ type: 'icon', icon: 'clock', variant: 'danger' },
{ type: 'icon', icon: 'clock', variant: 'danger', framed: true },
{ type: 'mrkdwn', i18n: { key: 'Call_not_answered_bold' }, text: 'Call not answered' },
],
action: actionObj,
},
],
},
Expand All @@ -177,7 +201,7 @@ describe('getHistoryMessagePayload', () => {
});

it('should return correct payload for "failed" state', () => {
const result = getHistoryMessagePayload('failed', undefined);
const result = getHistoryMessagePayload('failed', undefined, 'callid');
expect(result).toEqual({
msg: '',
groupable: false,
Expand All @@ -189,9 +213,10 @@ describe('getHistoryMessagePayload', () => {
{
background: 'default',
elements: [
{ type: 'icon', icon: 'phone-issue', variant: 'danger' },
{ type: 'icon', icon: 'phone-issue', variant: 'danger', framed: true },
{ type: 'mrkdwn', i18n: { key: 'Call_failed_bold' }, text: 'Call failed' },
],
action: actionObj,
},
],
},
Expand All @@ -200,7 +225,7 @@ describe('getHistoryMessagePayload', () => {
});

it('should return correct payload for "error" state', () => {
const result = getHistoryMessagePayload('error', undefined);
const result = getHistoryMessagePayload('error', undefined, 'callid');
expect(result).toEqual({
msg: '',
groupable: false,
Expand All @@ -212,9 +237,10 @@ describe('getHistoryMessagePayload', () => {
{
background: 'default',
elements: [
{ type: 'icon', icon: 'phone-issue', variant: 'danger' },
{ type: 'icon', icon: 'phone-issue', variant: 'danger', framed: true },
{ type: 'mrkdwn', i18n: { key: 'Call_failed_bold' }, text: 'Call failed' },
],
action: actionObj,
},
],
},
Expand All @@ -223,7 +249,7 @@ describe('getHistoryMessagePayload', () => {
});

it('should return correct payload for "transferred" state', () => {
const result = getHistoryMessagePayload('transferred', undefined);
const result = getHistoryMessagePayload('transferred', undefined, 'callid');
expect(result).toEqual({
msg: '',
groupable: false,
Expand All @@ -235,9 +261,10 @@ describe('getHistoryMessagePayload', () => {
{
background: 'default',
elements: [
{ type: 'icon', icon: 'arrow-forward', variant: 'secondary' },
{ type: 'icon', icon: 'arrow-forward', variant: 'secondary', framed: true },
{ type: 'mrkdwn', i18n: { key: 'Call_transferred_bold' }, text: 'Call transferred' },
],
action: actionObj,
},
],
},
Expand All @@ -246,7 +273,7 @@ describe('getHistoryMessagePayload', () => {
});

it('should include duration row when duration is provided', () => {
const result = getHistoryMessagePayload('ended', 3665);
const result = getHistoryMessagePayload('ended', 3665, 'callid');

expect(result.blocks[0].rows).toHaveLength(2);
expect(result.blocks[0].rows[1]).toEqual({
Expand All @@ -256,7 +283,7 @@ describe('getHistoryMessagePayload', () => {
});

it('should not include duration row when duration is undefined', () => {
const result = getHistoryMessagePayload('ended', undefined);
const result = getHistoryMessagePayload('ended', undefined, 'callid');
expect(result.blocks[0].rows).toHaveLength(1);
});

Expand All @@ -265,14 +292,16 @@ describe('getHistoryMessagePayload', () => {
const duration = 125;

states.forEach((state) => {
const result = getHistoryMessagePayload(state, duration);
const result = getHistoryMessagePayload(state, duration, 'callid');
expect(result.msg).toBe('');
expect(result.groupable).toBe(false);
expect(result.blocks).toHaveLength(1);
expect(result.blocks[0].type).toBe('info_card');
expect(result.blocks[0].rows).toHaveLength(2);
expect(result.blocks[0].rows[1].background).toBe('secondary');
expect(result.blocks[0].rows[1].elements[0].type).toBe('mrkdwn');
expect(result.blocks[0].rows[0].action).toEqual(actionObj);
expect(result.blocks[0].rows[1].action).toBeUndefined();
});
});
});
24 changes: 18 additions & 6 deletions packages/ui-voip/src/ui-kit/getHistoryMessagePayload.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { CallHistoryItemState, IMessage } from '@rocket.chat/core-typings';
import type { IconElement, InfoCardBlock, TextObject } from '@rocket.chat/ui-kit';
import type { IconButtonElement, FrameableIconElement, InfoCardBlock, TextObject } from '@rocket.chat/ui-kit';
import { intervalToDuration, secondsToMilliseconds } from 'date-fns';

const APP_ID = 'media-call-core';
Expand All @@ -18,17 +18,17 @@ export const callStateToTranslationKey = (callState: CallHistoryItemState): Text
}
};

export const callStateToIcon = (callState: CallHistoryItemState): IconElement => {
export const callStateToIcon = (callState: CallHistoryItemState): FrameableIconElement => {
switch (callState) {
case 'ended':
return { type: 'icon', icon: 'phone-off', variant: 'secondary' };
return { type: 'icon', icon: 'phone-off', variant: 'secondary', framed: true };
case 'not-answered':
return { type: 'icon', icon: 'clock', variant: 'danger' };
return { type: 'icon', icon: 'clock', variant: 'danger', framed: true };
case 'failed':
case 'error':
return { type: 'icon', icon: 'phone-issue', variant: 'danger' };
return { type: 'icon', icon: 'phone-issue', variant: 'danger', framed: true };
case 'transferred':
return { type: 'icon', icon: 'arrow-forward', variant: 'secondary' };
return { type: 'icon', icon: 'arrow-forward', variant: 'secondary', framed: true };
}
};

Expand All @@ -54,9 +54,20 @@ export const getFormattedCallDuration = (callDuration: number | undefined): Text
} as const;
};

export const getHistoryAction = (callId: string): IconButtonElement => {
return {
type: 'icon_button',
icon: { type: 'icon', icon: 'info', variant: 'default' },
actionId: 'open-history',
appId: APP_ID,
blockId: callId,
};
};

export const getHistoryMessagePayload = (
callState: CallHistoryItemState,
callDuration: number | undefined,
callId?: string,
): Pick<IMessage, 'msg' | 'groupable'> & { blocks: [InfoCardBlock] } => {
const callStateTranslationKey = callStateToTranslationKey(callState);
const icon = callStateToIcon(callState);
Expand All @@ -73,6 +84,7 @@ export const getHistoryMessagePayload = (
{
background: 'default',
elements: [icon, callStateTranslationKey],
...(callId && { action: getHistoryAction(callId) }),
},
...(callDurationFormatted
? [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ exports[`renders Default without crashing 1`] = `
>
<i
aria-hidden="true"
class="rcx-box rcx-box--full rcx-icon--name-phone-off rcx-icon rcx-css-nxss1p"
class="rcx-box rcx-box--full rcx-framed-icon--neutral rcx-framed-icon rcx-icon--name-phone-off rcx-icon rcx-css-1bepdyv"
>
</i>
Expand Down Expand Up @@ -485,7 +485,7 @@ exports[`renders ExternalContact without crashing 1`] = `
>
<i
aria-hidden="true"
class="rcx-box rcx-box--full rcx-icon--name-phone-off rcx-icon rcx-css-nxss1p"
class="rcx-box rcx-box--full rcx-framed-icon--neutral rcx-framed-icon rcx-icon--name-phone-off rcx-icon rcx-css-1bepdyv"
>
</i>
Expand Down
Loading