Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IMPROVE] Added identification on calls to/from existing contacts #26334

Merged
merged 53 commits into from
Aug 25, 2022
Merged
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
9a136d6
[IMPROVE] Improved voip call identification
aleksandernsilva Jul 20, 2022
0166bed
Chore: Improved styling logic for the call dial button
aleksandernsilva Jul 6, 2022
ad5818c
[FIX] Fixed FilterByText passing invalid props to div
aleksandernsilva Jul 21, 2022
b331a6d
[FIX] Changed useContactName phone parse fn
aleksandernsilva Jul 21, 2022
6abd4e3
[IMPROVE] Moved call table row to its own component and added contact…
aleksandernsilva Jul 21, 2022
3eb082e
Chore: Adjusted endpoint typings
aleksandernsilva Jul 21, 2022
f98a7e3
Chore: Adjusted hook typings
aleksandernsilva Jul 21, 2022
579d637
Merge branch 'develop' into improve/call-contact-id
aleksandernsilva Jul 22, 2022
c35e746
Merge branch 'develop' into improve/call-contact-id
aleksandernsilva Jul 22, 2022
dad97c9
Merge branch 'develop' into improve/call-contact-id
KevLehman Jul 26, 2022
ddbd469
small change on error message
KevLehman Jul 26, 2022
cdaacd5
Merge branch 'develop' of github.com:RocketChat/Rocket.Chat into impr…
aleksandernsilva Aug 1, 2022
21eb8bf
[FIX] Contact name not being displayed in the contact table
aleksandernsilva Aug 2, 2022
fc37ab1
[FIX] Contact name not being displayed in the voip contextual bar
aleksandernsilva Aug 2, 2022
f6f71f5
Chore: Adjusting import order
aleksandernsilva Aug 2, 2022
7d3211c
[REFACTOR] Moved contacts fetch logic to context to reduce fetch requ…
aleksandernsilva Aug 2, 2022
6155b8b
Merge branch 'develop' of github.com:RocketChat/Rocket.Chat into impr…
aleksandernsilva Aug 8, 2022
6f536ec
Merge branch 'develop' of github.com:RocketChat/Rocket.Chat into impr…
aleksandernsilva Aug 10, 2022
e64a427
Chore: Removing useContactName from call table and voip info
aleksandernsilva Aug 14, 2022
7c89f34
Chore: Removing contacts provider restoring useOmnichannelContacts hook
aleksandernsilva Aug 15, 2022
d3f68de
Chore: Added page objects for dialpad modal, voip footer and omnichan…
aleksandernsilva Aug 15, 2022
2711521
[FIX] Fixed voip enabled initial value on useVoipClient
aleksandernsilva Aug 15, 2022
8f2b9ac
Chore: Implemented e2e tests for the VoipFooter component
aleksandernsilva Aug 15, 2022
4321378
Merge branch 'develop' of github.com:RocketChat/Rocket.Chat into impr…
aleksandernsilva Aug 15, 2022
085fe47
Chore: Uncommenting playwright global setupo
aleksandernsilva Aug 15, 2022
5f7e860
Chore: Skip e2e for community edition
aleksandernsilva Aug 15, 2022
e0a929d
Support receiving duplicated dialend events
KevLehman Aug 15, 2022
ddbb78d
Chore: Skipping e2e test until CI license issue is fixed
aleksandernsilva Aug 16, 2022
185580d
Merge branch 'develop' into improve/call-contact-id
tiagoevanp Aug 16, 2022
72ae189
[FIX] Removing asterisk from visitor's numbers
aleksandernsilva Aug 18, 2022
2d4fdfa
Chore: Removing cache and simplifying hook
aleksandernsilva Aug 18, 2022
4ecdf0a
Merge branch 'develop' into improve/call-contact-id
tiagoevanp Aug 19, 2022
8786cf0
Merge branch 'develop' into improve/call-contact-id
aleksandernsilva Aug 19, 2022
6ecba02
Chore: Refactored useOmnichannelContact to use useQuery
aleksandernsilva Aug 23, 2022
03f68ab
Chore: Removed duplicated qa ids and refactored to use roles
aleksandernsilva Aug 23, 2022
3076938
Merge branch 'develop' of github.com:RocketChat/Rocket.Chat into impr…
aleksandernsilva Aug 23, 2022
dd3b6b2
Chore: Fixing typing
aleksandernsilva Aug 23, 2022
3c04aa6
Chore: Suggestions on PR #26334 (#26670)
murtaza98 Aug 24, 2022
cae6385
Chore: Added isLoading and isError to useOmnichannelContact return value
aleksandernsilva Aug 24, 2022
098ca9c
Merge branch 'improve/call-contact-id' of github.com:RocketChat/Rocke…
aleksandernsilva Aug 24, 2022
2daf6a6
Merge branch 'develop' into improve/call-contact-id
aleksandernsilva Aug 24, 2022
68fee75
Chore: Enabling voip footer e2e tests
aleksandernsilva Aug 24, 2022
976474e
Chore: ts-ignore for type circular reference
aleksandernsilva Aug 24, 2022
7c3f383
Chore: Adjusting config logic
aleksandernsilva Aug 24, 2022
80d79ef
Chore: Removing e2e tests
aleksandernsilva Aug 24, 2022
9b8ae32
Merge branch 'develop' of github.com:RocketChat/Rocket.Chat into impr…
ggazzo Aug 25, 2022
26b5ed4
Merge branch 'improve/call-contact-id' of github.com:RocketChat/Rocke…
ggazzo Aug 25, 2022
b3481ae
review
ggazzo Aug 25, 2022
cf82287
Chore: Removing todo's
aleksandernsilva Aug 25, 2022
bbf2889
[FIX] Query adjustment to prevent 400 when there's no phone
aleksandernsilva Aug 25, 2022
971e332
Chore: Improving useVoipClient logic
aleksandernsilva Aug 25, 2022
dae4b1b
Chore: Removed unnecessary effect
aleksandernsilva Aug 25, 2022
6b74f77
Merge branch 'develop' into improve/call-contact-id
kodiakhq[bot] Aug 25, 2022
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
2 changes: 1 addition & 1 deletion apps/meteor/client/components/FilterByText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const FilterByText = ({
}, []);

return (
<Box mb='x16' is='form' onSubmit={handleFormSubmit} display='flex' flexDirection={shouldFiltersStack ? 'column' : 'row'} {...props}>
aleksandernsilva marked this conversation as resolved.
Show resolved Hide resolved
<Box mb='x16' is='form' onSubmit={handleFormSubmit} display='flex' flexDirection={shouldFiltersStack ? 'column' : 'row'}>
aleksandernsilva marked this conversation as resolved.
Show resolved Hide resolved
<TextInput
placeholder={placeholder ?? t('Search')}
ref={inputRef}
Expand Down
10 changes: 7 additions & 3 deletions apps/meteor/client/providers/CallProvider/CallProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { OutgoingByeRequest } from 'sip.js/lib/core';
import { CustomSounds } from '../../../app/custom-sounds/client';
import { getUserPreference } from '../../../app/utils/client';
import { isOutboundClient, useVoipClient } from '../../../ee/client/hooks/useVoipClient';
import { parseOutboundPhoneNumber } from '../../../ee/client/lib/voip/parseOutboundPhoneNumber';
import { WrapUpCallModal } from '../../../ee/client/voip/components/modals/WrapUpCallModal';
import { CallContext, CallContextValue, useIsVoipEnterprise } from '../../contexts/CallContext';
import { useDialModal } from '../../hooks/useDialModal';
Expand Down Expand Up @@ -166,11 +167,12 @@ export const CallProvider: FC = ({ children }) => {
return '';
}
try {
const phone = parseOutboundPhoneNumber(caller.callerId);
const { visitor } = await visitorEndpoint({
visitor: {
token: Random.id(),
phone: caller.callerId,
name: caller.callerName || caller.callerId,
phone,
name: caller.callerName || phone,
},
});
const voipRoom = await voipEndpoint({ token: visitor.token, agentId: user._id, direction });
Expand Down Expand Up @@ -389,12 +391,14 @@ export const CallProvider: FC = ({ children }) => {
stopAllRingback();
};

const onCallFailed = (reason: 'Not Found' | 'Address Incomplete' | string): void => {
const onCallFailed = (reason: 'Not Found' | 'Address Incomplete' | 'Request Terminated' | string): void => {
switch (reason) {
case 'Not Found':
// This happens when the call matches dialplan and goes to the world, but the trunk doesnt find the number.
openDialModal({ errorMessage: t('Dialed_number_doesnt_exist') });
break;
case 'Address Incomplete':
// This happens when the dialed number doesnt match a valid asterisk dialplan pattern or the number is invalid.
openDialModal({ errorMessage: t('Dialed_number_is_incomplete') });
break;
case 'Request Terminated':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ const VoipFooterTemplate: ComponentStory<typeof VoipFooter> = (args) => {
callsInQueue='2 Calls In Queue'
dispatchEvent={() => null}
openedRoomInfo={{ v: { token: '' }, rid: '' }}
anonymousText={'Anonymous'}
options={{
deviceSettings: {
label: (
Expand Down
13 changes: 8 additions & 5 deletions apps/meteor/client/sidebar/footer/voip/VoipFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import type { IVoipRoom } from '@rocket.chat/core-typings';
import { ICallerInfo, VoIpCallerInfo, VoipClientEvents } from '@rocket.chat/core-typings';
import { css } from '@rocket.chat/css-in-js';
import { Box, Button, ButtonGroup, Icon, SidebarFooter, Menu, IconButton } from '@rocket.chat/fuselage';
import { useTranslation } from '@rocket.chat/ui-contexts';
import React, { ReactElement, MouseEvent, ReactNode } from 'react';

import type { VoipFooterMenuOptions } from '../../../../ee/client/hooks/useVoipFooterMenu';
import { parseOutboundPhoneNumber } from '../../../../ee/client/lib/voip/parseOutboundPhoneNumber';
import { CallActionsType } from '../../../contexts/CallContext';
import { useOmnichannelContactLabel } from './hooks/useOmnichannelContactLabel';

type VoipFooterPropsType = {
caller: ICallerInfo;
Expand All @@ -31,7 +32,6 @@ type VoipFooterPropsType = {
openRoom: (rid: IVoipRoom['_id']) => void;
dispatchEvent: (params: { event: VoipClientEvents; rid: string; comment?: string }) => void;
openedRoomInfo: { v: { token?: string | undefined }; rid: string };
anonymousText: string;
isEnterprise: boolean;
children?: ReactNode;
options: VoipFooterMenuOptions;
Expand All @@ -53,11 +53,13 @@ export const VoipFooter = ({
callsInQueue,
dispatchEvent,
openedRoomInfo,
anonymousText,
isEnterprise = false,
children,
options,
}: VoipFooterPropsType): ReactElement => {
const contactLabel = useOmnichannelContactLabel(caller);
const t = useTranslation();

const cssClickable =
callerState === 'IN_CALL' || callerState === 'ON_HOLD'
? css`
Expand Down Expand Up @@ -117,7 +119,7 @@ export const VoipFooter = ({
<Box display='flex' flexDirection='row' mi='16px' mbe='12px' justifyContent='space-between' alignItems='center'>
<Box>
<Box color='white' fontScale='p2' withTruncatedText>
{caller.callerName || parseOutboundPhoneNumber(caller.callerId) || anonymousText}
{contactLabel || t('Anonymous')}
</Box>
<Box color='hint' fontScale='c1' withTruncatedText>
{subtitle}
Expand All @@ -132,6 +134,7 @@ export const VoipFooter = ({
small
square
danger
aria-label={t('End_call')}
onClick={(e): unknown => {
e.stopPropagation();
muted && toggleMic(false);
Expand All @@ -143,7 +146,7 @@ export const VoipFooter = ({
</Button>
)}
{callerState === 'OFFER_RECEIVED' && (
<Button title={tooltips.endCall} small square danger onClick={callActions.reject}>
<Button title={tooltips.endCall} aria-label={t('Reject_call')} small square danger onClick={callActions.reject}>
<Icon name='phone-off' size='x16' />
</Button>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ICallerInfo } from '@rocket.chat/core-typings';
import { useEndpoint } from '@rocket.chat/ui-contexts';
import { useQuery } from '@tanstack/react-query';

import { parseOutboundPhoneNumber } from '../../../../../ee/client/lib/voip/parseOutboundPhoneNumber';

export const useOmnichannelContactLabel = (caller: ICallerInfo): string => {
const getContactBy = useEndpoint('GET', '/v1/omnichannel/contact.search');
const phone = parseOutboundPhoneNumber(caller.callerId);

const { data } = useQuery(['getContactsByPhone', phone], async () => getContactBy({ phone }).then(({ contact }) => contact), {
enabled: !!phone,
});

return data?.name || caller.callerName || phone;
};
1 change: 0 additions & 1 deletion apps/meteor/client/sidebar/footer/voip/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ export const VoipFooter = (): ReactElement | null => {
callsInQueue={getCallsInQueueText}
dispatchEvent={dispatchEvent}
openedRoomInfo={openedRoomInfo}
anonymousText={t('Anonymous')}
isEnterprise={isEnterprise}
options={options}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const OmniChannelCallDialPad = ({ ...props }): ReactElement => {
icon='dialpad'
onClick={(): void => openDialModal()}
disabled={!outBoundCallsEnabledForUser}
aria-label={t('Open_Dialpad')}
{...props}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ import React, { ReactElement } from 'react';

export const OmnichannelCallToggleLoading = ({ ...props }): ReactElement => {
const t = useTranslation();
return <Sidebar.TopBar.Action icon='phone' data-title={t('Loading')} disabled {...props} />;
return <Sidebar.TopBar.Action icon='phone' data-title={t('Loading')} aria-label={t('VoIP_Toggle')} disabled {...props} />;
};
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,14 @@ export const OmnichannelCallToggleReady = ({ ...props }): ReactElement => {
color: getColor(),
};

return <Sidebar.TopBar.Action disabled={inCall} {...voipCallIcon} {...props} onClick={onClickVoipButton} />;
return (
<Sidebar.TopBar.Action
aria-label={t('VoIP_Toggle')}
aria-checked={registered}
disabled={inCall}
{...voipCallIcon}
{...props}
onClick={onClickVoipButton}
/>
);
};
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { css } from '@rocket.chat/css-in-js';
import { IconButton } from '@rocket.chat/fuselage';
import { useTranslation } from '@rocket.chat/ui-contexts';
import React, { MouseEvent, ReactElement } from 'react';

import { useVoipOutboundStates } from '../../../contexts/CallContext';
import { useDialModal } from '../../../hooks/useDialModal';

export const rcxCallDialButton = css`
.rcx-show-call-button-on-hover:not(:hover) & {
display: none !important;
}
`;

export const CallDialpadButton = ({ phoneNumber }: { phoneNumber: string }): ReactElement => {
const t = useTranslation();

Expand All @@ -20,6 +27,7 @@ export const CallDialpadButton = ({ phoneNumber }: { phoneNumber: string }): Rea
<IconButton
rcx-call-dial-button
title={outBoundCallsAllowed ? t('Call_number') : t('Call_number_enterprise_only')}
className={rcxCallDialButton}
disabled={!outBoundCallsEnabledForUser || !phoneNumber}
tiny
icon='phone'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,11 @@
import { IVoipRoom } from '@rocket.chat/core-typings';
import { css } from '@rocket.chat/css-in-js';
import { Table } from '@rocket.chat/fuselage';
import { useDebouncedValue, useMutableCallback } from '@rocket.chat/fuselage-hooks';
import { useRoute, useTranslation } from '@rocket.chat/ui-contexts';
import { Meteor } from 'meteor/meteor';
import moment from 'moment';
import React, { useState, useMemo, useCallback, FC } from 'react';

import { parseOutboundPhoneNumber } from '../../../../../ee/client/lib/voip/parseOutboundPhoneNumber';
import GenericTable from '../../../../components/GenericTable';
import { useIsCallReady } from '../../../../contexts/CallContext';
import { useEndpointData } from '../../../../hooks/useEndpointData';
import { CallDialpadButton } from '../CallDialpadButton';

export const rcxCallDialButton = css`
&:not(:hover) {
.rcx-call-dial-button {
display: none !important;
}
}
`;
import { CallTableRow } from './CallTableRow';

const useQuery = (
{
Expand Down Expand Up @@ -66,18 +52,6 @@ const CallTable: FC = () => {
const userIdLoggedIn = Meteor.userId();
const query = useQuery(debouncedParams, debouncedSort, userIdLoggedIn);
const directoryRoute = useRoute('omnichannel-directory');
const isCallReady = useIsCallReady();

const resolveDirectionLabel = useCallback(
(direction: IVoipRoom['direction']) => {
const labels = {
inbound: 'Incoming',
outbound: 'Outgoing',
} as const;
return t(labels[direction] || 'Not_Available');
},
[t],
);

const onHeaderClick = useMutableCallback((id) => {
const [sortBy, sortDirection] = sort;
Expand Down Expand Up @@ -156,34 +130,7 @@ const CallTable: FC = () => {
[sort, onHeaderClick, t],
);

const renderRow = useCallback(
({ _id, fname, callStarted, queue, callDuration, v, direction }) => {
const duration = moment.duration(callDuration / 1000, 'seconds');
const phoneNumber = Array.isArray(v?.phone) ? v?.phone[0]?.phoneNumber : v?.phone;

return (
<Table.Row
key={_id}
className={rcxCallDialButton}
tabIndex={0}
role='link'
onClick={(): void => onRowClick(_id, v?.token)}
action
qa-user-id={_id}
height='40px'
>
<Table.Cell withTruncatedText>{parseOutboundPhoneNumber(fname)}</Table.Cell>
<Table.Cell withTruncatedText>{parseOutboundPhoneNumber(phoneNumber)}</Table.Cell>
<Table.Cell withTruncatedText>{queue}</Table.Cell>
<Table.Cell withTruncatedText>{moment(callStarted).format('L LTS')}</Table.Cell>
<Table.Cell withTruncatedText>{duration.isValid() && duration.humanize()}</Table.Cell>
<Table.Cell withTruncatedText>{resolveDirectionLabel(direction)}</Table.Cell>
<Table.Cell>{isCallReady && <CallDialpadButton phoneNumber={phoneNumber} />}</Table.Cell>
</Table.Row>
);
},
[onRowClick, resolveDirectionLabel, isCallReady],
);
const renderRow = useCallback((room) => <CallTableRow room={room} onRowClick={onRowClick} />, [onRowClick]);

return (
<GenericTable
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { IVoipRoom } from '@rocket.chat/core-typings';
import { Table } from '@rocket.chat/fuselage';
import { useTranslation } from '@rocket.chat/ui-contexts';
import moment from 'moment';
import React, { ReactElement, useCallback } from 'react';

import { parseOutboundPhoneNumber } from '../../../../../ee/client/lib/voip/parseOutboundPhoneNumber';
import { useIsCallReady } from '../../../../contexts/CallContext';
import { CallDialpadButton } from '../CallDialpadButton';

type CallTableRowProps = {
room: IVoipRoom;
onRowClick(_id: string, token?: string): void;
};

export const CallTableRow = ({ room, onRowClick }: CallTableRowProps): ReactElement => {
const t = useTranslation();
const isCallReady = useIsCallReady();

const { _id, fname, callStarted, queue, callDuration = 0, v, direction } = room;
const duration = moment.duration(callDuration / 1000, 'seconds');
const phoneNumber = Array.isArray(v?.phone) ? v?.phone[0]?.phoneNumber : v?.phone;

const resolveDirectionLabel = useCallback(
(direction: IVoipRoom['direction']) => {
const labels = {
inbound: 'Incoming',
outbound: 'Outgoing',
} as const;
return t(labels[direction] || 'Not_Available');
},
[t],
);

return (
<Table.Row
key={_id}
rcx-show-call-button-on-hover
tabIndex={0}
role='link'
onClick={(): void => onRowClick(_id, v?.token)}
action
qa-user-id={_id}
height='40px'
>
<Table.Cell withTruncatedText>{parseOutboundPhoneNumber(fname)}</Table.Cell>
<Table.Cell withTruncatedText>{parseOutboundPhoneNumber(phoneNumber)}</Table.Cell>
<Table.Cell withTruncatedText>{queue}</Table.Cell>
<Table.Cell withTruncatedText>{moment(callStarted).format('L LTS')}</Table.Cell>
<Table.Cell withTruncatedText>{duration.isValid() && duration.humanize()}</Table.Cell>
<Table.Cell withTruncatedText>{resolveDirectionLabel(direction)}</Table.Cell>
<Table.Cell>{isCallReady && <CallDialpadButton phoneNumber={phoneNumber} />}</Table.Cell>
</Table.Row>
);
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { css } from '@rocket.chat/css-in-js';
import { Icon, Pagination, States, StatesAction, StatesActions, StatesIcon, StatesTitle, Box } from '@rocket.chat/fuselage';
import { useDebouncedState, useDebouncedValue, useMutableCallback } from '@rocket.chat/fuselage-hooks';
import { useRoute, useTranslation } from '@rocket.chat/ui-contexts';
Expand Down Expand Up @@ -27,13 +26,6 @@ type ContactTableProps = {
setContactReload(fn: () => void): void;
};

export const rcxCallDialButton = css`
&:not(:hover) {
.rcx-call-dial-button {
display: none !important;
}
}
`;
function ContactTable({ setContactReload }: ContactTableProps): ReactElement {
const { current, itemsPerPage, setItemsPerPage, setCurrent, ...paginationProps } = usePagination();
const { sortBy, sortDirection, setSort } = useSort<'username' | 'phone' | 'name' | 'visitorEmails.address' | 'lastchat'>('username');
Expand Down Expand Up @@ -128,7 +120,7 @@ function ContactTable({ setContactReload }: ContactTableProps): ReactElement {
</GenericTableHeader>
{result.phase === AsyncStatePhase.RESOLVED && (
<GenericTableBody>
{result.value.visitors.map(({ _id, username, fname, visitorEmails, phone, lastChat }) => {
{result.value.visitors.map(({ _id, username, fname, name, visitorEmails, phone, lastChat }) => {
const phoneNumber = phone?.length && phone[0].phoneNumber;
const visitorEmail = visitorEmails?.length && visitorEmails[0].address;

Expand All @@ -140,11 +132,11 @@ function ContactTable({ setContactReload }: ContactTableProps): ReactElement {
role='link'
height='40px'
qa-user-id={_id}
className={rcxCallDialButton}
rcx-show-call-button-on-hover
onClick={onRowClick(_id)}
>
<GenericTableCell withTruncatedText>{username}</GenericTableCell>
<GenericTableCell withTruncatedText>{parseOutboundPhoneNumber(fname)}</GenericTableCell>
<GenericTableCell withTruncatedText>{parseOutboundPhoneNumber(fname || name)}</GenericTableCell>
<GenericTableCell withTruncatedText>{parseOutboundPhoneNumber(phoneNumber)}</GenericTableCell>
<GenericTableCell withTruncatedText>{visitorEmail}</GenericTableCell>
<GenericTableCell withTruncatedText>{lastChat && formatDate(lastChat.ts)}</GenericTableCell>
Expand Down
Loading