Skip to content

Commit

Permalink
[NEW] Omnichannel source identification fields (#23090)
Browse files Browse the repository at this point in the history
* Add new fields to IOmnichannelRoom and adapt livechat bridge to them

* Add source information to places that create livechat rooms

* Add deprecation warning to unused livechat methods and remove source from native facebook integration

* Remove unnecesary alias for widget

* Update app/lib/server/lib/deprecationWarningLogger.ts

* Update Apps-Engine version

* frontend part

* Change way of identifying when a chat is coming through widget

* Update deprecationlogger to new logger interface

* Fix usage of source.type as key

* sms icon

* [NEW] Add Channel Source details to room info panel (#23224)

* Add Source icon/text to room info

* Fix TS error and styling

* Change order

* Typing and style fix

* Use source.id instead of email object

* useEndpoint

* Revert "Merge branch 'develop' into new/omnichannel-source-fields"

This reverts commit 7fea2ad, reversing
changes made to dbbcaf4.

* Removing unnecesary changes from branch

* why github, why?

* Bump icons & fuselage to latest versions

Co-authored-by: Kevin Aleman <kevin.aleman@rocket.chat>
Co-authored-by: Tiago Evangelista Pinto <tiago.evangelista@rocket.chat>
Co-authored-by: Martin Schoeler <martin.schoeler@rocket.chat>
  • Loading branch information
4 people authored Sep 24, 2021
1 parent c2ab5e1 commit 0f36b36
Show file tree
Hide file tree
Showing 29 changed files with 4,974 additions and 4,686 deletions.
14 changes: 0 additions & 14 deletions app/api/server/helpers/deprecationWarning.js

This file was deleted.

15 changes: 15 additions & 0 deletions app/api/server/helpers/deprecationWarning.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { API } from '../api';
import { apiDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger';

(API as any).helperMethods.set('deprecationWarning', function _deprecationWarning({ endpoint, versionWillBeRemoved, response }: { endpoint: string; versionWillBeRemoved: string; response: any }) {
const warningMessage = `The endpoint "${ endpoint }" is deprecated and will be removed after version ${ versionWillBeRemoved }`;
apiDeprecationLogger.warn(warningMessage);
if (process.env.NODE_ENV === 'development') {
return {
warning: warningMessage,
...response,
};
}

return response;
});
13 changes: 13 additions & 0 deletions app/api/server/helpers/isWidget.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { parse } from 'cookie';

import { API } from '../api';

(API as any).helperMethods.set('isWidget', function _isWidget() {
// @ts-expect-error
const { headers } = this.request;

const { rc_room_type: roomType, rc_is_widget: isWidget } = parse(headers.cookie);

const isLivechatRoom = roomType && roomType === 'l';
return !!(isLivechatRoom && isWidget === 't');
});
1 change: 1 addition & 0 deletions app/api/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import './helpers/insertUserObject';
import './helpers/isUserFromParams';
import './helpers/parseJsonQuery';
import './helpers/requestParams';
import './helpers/isWidget';
import './default/info';
import './v1/assets';
import './v1/channels';
Expand Down
17 changes: 15 additions & 2 deletions app/apps/server/bridges/livechat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
LivechatRooms,
} from '../../../models/server';
import { AppServerOrchestrator } from '../orchestrator';
import { OmnichannelSourceType } from '../../../../definition/IRoom';

export class AppLivechatBridge extends LivechatBridge {
// eslint-disable-next-line no-empty-function
Expand All @@ -45,7 +46,13 @@ export class AppLivechatBridge extends LivechatBridge {
guest: this.orch.getConverters()?.get('visitors').convertAppVisitor(message.visitor),
message: this.orch.getConverters()?.get('messages').convertAppMessage(message),
agent: undefined,
roomInfo: undefined,
roomInfo: {
source: {
type: OmnichannelSourceType.APP,
id: appId,
alias: this.orch.getManager()?.getOneById(appId)?.getNameSlug(),
},
},
});

return msg._id;
Expand Down Expand Up @@ -81,7 +88,13 @@ export class AppLivechatBridge extends LivechatBridge {
guest: this.orch.getConverters()?.get('visitors').convertAppVisitor(visitor),
agent: agentRoom,
rid: Random.id(),
roomInfo: undefined,
roomInfo: {
source: {
type: OmnichannelSourceType.APP,
id: appId,
alias: this.orch.getManager()?.getOneById(appId)?.getNameSlug(),
},
},
extraParams: undefined,
});

Expand Down
7 changes: 7 additions & 0 deletions app/lib/server/lib/deprecationWarningLogger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Logger } from '../../../logger/server';

const deprecationLogger = new Logger('DeprecationWarning');

export const apiDeprecationLogger = deprecationLogger.section('API');
export const methodDeprecationLogger = deprecationLogger.section('METHOD');
export const functionDeprecationLogger = deprecationLogger.section('FUNCTION');
5 changes: 5 additions & 0 deletions app/livechat/imports/server/rest/sms.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { LivechatRooms, LivechatVisitors, LivechatDepartment } from '../../../..
import { API } from '../../../../api/server';
import { SMS } from '../../../../sms';
import { Livechat } from '../../../server/lib/Livechat';
import { OmnichannelSourceType } from '../../../../../definition/IRoom';

const getUploadFile = (details, fileUrl) => {
const response = HTTP.get(fileUrl, { npmRequestOptions: { encoding: null } });
Expand Down Expand Up @@ -83,6 +84,10 @@ API.v1.addRoute('livechat/sms-incoming/:service', {
sms: {
from: sms.to,
},
source: {
type: OmnichannelSourceType.SMS,
alias: this.urlParams.service,
},
},
};

Expand Down
2 changes: 1 addition & 1 deletion app/livechat/imports/server/rest/upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,6 @@ API.v1.addRoute('livechat/upload/:rid', {
uploadedFile.description = fields.description;

delete fields.description;
API.v1.success(Meteor.call('sendFileLivechatMessage', this.urlParams.rid, visitorToken, uploadedFile, fields));
return API.v1.success(Meteor.call('sendFileLivechatMessage', this.urlParams.rid, visitorToken, uploadedFile, fields));
},
});
11 changes: 11 additions & 0 deletions app/livechat/server/api/v1/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { findGuest, findRoom, normalizeHttpHeaderData } from '../lib/livechat';
import { Livechat } from '../../lib/Livechat';
import { normalizeMessageFileUpload } from '../../../../utils/server/functions/normalizeMessageFileUpload';
import { settings } from '../../../../settings/server';
import { OmnichannelSourceType } from '../../../../../definition/IRoom';

API.v1.addRoute('livechat/message', {
post() {
Expand Down Expand Up @@ -56,6 +57,11 @@ API.v1.addRoute('livechat/message', {
token,
},
agent,
roomInfo: {
source: {
type: this.isWidget() ? OmnichannelSourceType.WIDGET : OmnichannelSourceType.API,
},
},
};

const result = Promise.await(Livechat.sendMessage(sendMessage));
Expand Down Expand Up @@ -305,6 +311,11 @@ API.v1.addRoute('livechat/messages', { authRequired: true }, {
token: visitorToken,
msg: message.msg,
},
roomInfo: {
source: {
type: this.isWidget() ? OmnichannelSourceType.WIDGET : OmnichannelSourceType.API,
},
},
};
const sentMessage = Promise.await(Livechat.sendMessage(sendMessage));
return {
Expand Down
10 changes: 8 additions & 2 deletions app/livechat/server/api/v1/room.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { findGuest, findRoom, getRoom, settings, findAgent, onCheckRoomParams }
import { Livechat } from '../../lib/Livechat';
import { normalizeTransferredByData } from '../../lib/Helper';
import { findVisitorInfo } from '../lib/visitors';

import { OmnichannelSourceType } from '../../../../../definition/IRoom';

API.v1.addRoute('livechat/room', {
get() {
Expand Down Expand Up @@ -46,7 +46,13 @@ API.v1.addRoute('livechat/room', {
}

const rid = Random.id();
room = Promise.await(getRoom({ guest, rid, agent, extraParams }));
const roomInfo = {
source: {
type: this.isWidget() ? OmnichannelSourceType.WIDGET : OmnichannelSourceType.API,
},
};

room = Promise.await(getRoom({ guest, rid, agent, roomInfo, extraParams }));
return API.v1.success(room);
}

Expand Down
11 changes: 9 additions & 2 deletions app/livechat/server/api/v1/videoCall.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Messages } from '../../../../models';
import { settings as rcSettings } from '../../../../settings';
import { API } from '../../../../api/server';
import { findGuest, getRoom, settings } from '../lib/livechat';
import { OmnichannelSourceType } from '../../../../../definition/IRoom';

API.v1.addRoute('livechat/video.call/:token', {
get() {
Expand All @@ -26,7 +27,13 @@ API.v1.addRoute('livechat/video.call/:token', {
}

const rid = this.queryParams.rid || Random.id();
const roomInfo = { jitsiTimeout: new Date(Date.now() + 3600 * 1000) };
const roomInfo = {
jitsiTimeout: new Date(Date.now() + 3600 * 1000),
source: {
type: OmnichannelSourceType.API,
alias: 'video-call',
},
};
const { room } = getRoom({ guest, rid, roomInfo });
const config = settings();
if (!config.theme || !config.theme.actionLinks) {
Expand All @@ -50,7 +57,7 @@ API.v1.addRoute('livechat/video.call/:token', {
timeout: new Date(Date.now() + 3600 * 1000),
};

return API.v1.success({ videoCall });
return API.v1.success(this.deprecationWarning({ videoCall }));
} catch (e) {
return API.v1.failure(e);
}
Expand Down
7 changes: 7 additions & 0 deletions app/livechat/server/lib/Helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import notifications from '../../../notifications/server/lib/Notifications';
import { sendNotification } from '../../../lib/server';
import { sendMessage } from '../../../lib/server/functions/sendMessage';
import { queueInquiry, saveQueueInquiry } from './QueueManager';
import { OmnichannelSourceType } from '../../../../definition/IRoom';

const logger = new Logger('LivechatHelper');
const emailValidationRegex = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
Expand Down Expand Up @@ -60,6 +61,12 @@ export const createLivechatRoom = (rid, name, guest, roomInfo = {}, extraData =
cl: false,
open: true,
waitingResponse: true,
// this should be overriden by extraRoomInfo when provided
// in case it's not provided, we'll use this "default" type
source: {
type: OmnichannelSourceType.OTHER,
alias: 'unknown',
},
}, extraRoomInfo);

const roomId = Rooms.insert(room);
Expand Down
6 changes: 6 additions & 0 deletions app/livechat/server/methods/sendMessageLivechat.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Match, check } from 'meteor/check';

import { LivechatVisitors } from '../../../models';
import { Livechat } from '../lib/Livechat';
import { OmnichannelSourceType } from '../../../../definition/IRoom';

Meteor.methods({
sendMessageLivechat({ token, _id, rid, msg, file, attachments }, agent) {
Expand Down Expand Up @@ -40,6 +41,11 @@ Meteor.methods({
attachments,
},
agent,
roomInfo: {
source: {
type: OmnichannelSourceType.API,
},
},
});
},
});
10 changes: 9 additions & 1 deletion app/livechat/server/methods/startFileUploadRoom.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ import { Random } from 'meteor/random';

import { LivechatVisitors } from '../../../models';
import { Livechat } from '../lib/Livechat';
import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger';
import { OmnichannelSourceType } from '../../../../definition/IRoom';

Meteor.methods({
'livechat:startFileUploadRoom'(roomId, token) {
methodDeprecationLogger.warn('livechat:startFileUploadRoom will be deprecated in future versions of Rocket.Chat');
const guest = LivechatVisitors.getVisitorByToken(token);

const message = {
Expand All @@ -16,6 +19,11 @@ Meteor.methods({
token: guest.token,
};

return Livechat.getRoom(guest, message);
const roomInfo = {
source: OmnichannelSourceType.API,
alias: 'file-upload',
};

return Livechat.getRoom(guest, message, roomInfo);
},
});
13 changes: 12 additions & 1 deletion app/livechat/server/methods/startVideoCall.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import { Random } from 'meteor/random';
import { Messages } from '../../../models';
import { settings } from '../../../settings';
import { Livechat } from '../lib/Livechat';
import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger';
import { OmnichannelSourceType } from '../../../../definition/IRoom';

Meteor.methods({
async 'livechat:startVideoCall'(roomId) {
methodDeprecationLogger.warn('livechat:startVideoCall will be deprecated in future versions of Rocket.Chat');
if (!Meteor.userId()) {
throw new Meteor.Error('error-not-authorized', 'Not authorized', { method: 'livechat:closeByVisitor' });
}
Expand All @@ -20,7 +23,15 @@ Meteor.methods({
ts: new Date(),
};

const room = await Livechat.getRoom(guest, message, { jitsiTimeout: new Date(Date.now() + 3600 * 1000) });
const roomInfo = {
jitsiTimeout: new Date(Date.now() + 3600 * 1000),
source: {
type: OmnichannelSourceType.API,
alias: 'video-call',
},
};

const room = await Livechat.getRoom(guest, message, roomInfo);
message.rid = room._id;

Messages.createWithTypeRoomIdMessageAndUser('livechat_video_call', room._id, '', guest, {
Expand Down
6 changes: 5 additions & 1 deletion app/ui-sidenav/client/roomList.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ const mergeSubRoom = (subscription) => {
priorityId: 1,
livechatData: 1,
departmentId: 1,
source: 1,
},
};

Expand Down Expand Up @@ -208,6 +209,7 @@ const mergeSubRoom = (subscription) => {
livechatData,
departmentId,
ts,
source,
} = room;

subscription.lm = subscription.lr ? new Date(Math.max(subscription.lr, lastRoomUpdate)) : lastRoomUpdate;
Expand Down Expand Up @@ -243,6 +245,7 @@ const mergeSubRoom = (subscription) => {
livechatData,
departmentId,
ts,
source,
});
};

Expand Down Expand Up @@ -283,7 +286,7 @@ const mergeRoomSub = (room) => {
livechatData,
departmentId,
ts,

source,
} = room;

Subscriptions.update({
Expand Down Expand Up @@ -319,6 +322,7 @@ const mergeRoomSub = (room) => {
departmentId,
jitsiTimeout,
ts,
source,
...getLowerCaseNames(room, sub.name, sub.fname),
},
});
Expand Down
14 changes: 13 additions & 1 deletion client/hooks/useRoomIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,20 @@ export const useRoomIcon = (room: IRoom): ReactNode | { name: string; color?: st
case 'c':
return { name: 'hash' };
case 'l':
const omnichannelRoom = room as IOmnichannelRoom;

const icon =
{
widget: 'livechat',
email: 'mail',
sms: 'sms',
app: 'headset', // TODO: use app icon
api: 'headset', // TODO: use api icon
other: 'headset',
}[omnichannelRoom.source?.type as string] || 'headset';

return {
name: 'headset',
name: icon,
color: colors[(room as unknown as IOmnichannelRoom)?.v.status || 'offline'],
};
case 'd':
Expand Down
8 changes: 4 additions & 4 deletions client/views/admin/emailInbox/EmailInboxForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ import GenericModal from '../../../components/GenericModal';
import Page from '../../../components/Page';
import { useSetModal } from '../../../contexts/ModalContext';
import { useRoute } from '../../../contexts/RouterContext';
import { useEndpoint } from '../../../contexts/ServerContext';
import { useToastMessageDispatch } from '../../../contexts/ToastMessagesContext';
import { useTranslation } from '../../../contexts/TranslationContext';
import { useComponentDidUpdate } from '../../../hooks/useComponentDidUpdate';
import { useEndpointAction } from '../../../hooks/useEndpointAction';
import { useForm } from '../../../hooks/useForm';

const initialValues = {
Expand Down Expand Up @@ -127,9 +127,9 @@ function EmailInboxForm({ id, data }) {

const close = useCallback(() => router.push({}), [router]);

const saveEmailInbox = useEndpointAction('POST', 'email-inbox');
const deleteAction = useEndpointAction('DELETE', `email-inbox/${id}`);
const emailAlreadyExistsAction = useEndpointAction('GET', `email-inbox.search?email=${email}`);
const saveEmailInbox = useEndpoint('POST', 'email-inbox');
const deleteAction = useEndpoint('DELETE', `email-inbox/${id}`);
const emailAlreadyExistsAction = useEndpoint('GET', `email-inbox.search?email=${email}`);

useComponentDidUpdate(() => {
setEmailError(!isEmail(email) ? t('Validate_email_address') : null);
Expand Down
Loading

0 comments on commit 0f36b36

Please sign in to comment.