Skip to content

Commit

Permalink
Merge branch 'develop' into fix-jitsi-issue
Browse files Browse the repository at this point in the history
  • Loading branch information
tassoevan authored May 19, 2021
2 parents 4ba4611 + 2dac1dd commit 491f442
Show file tree
Hide file tree
Showing 56 changed files with 963 additions and 273 deletions.
3 changes: 2 additions & 1 deletion app/authentication/server/lib/restrictLoginAttempts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export const notifyFailedLogin = async (ipOrUsername: string, blockedUntil: Date
return;
}
// verify channel exists
const room = await Rooms.findOneByName(channelToNotify);
// to avoid issues when "fname" is presented in the UI, check if the name matches it as well
const room = await Rooms.findOneByNameOrFname(channelToNotify);
if (!room) {
/* @ts-expect-error */
logger.error('Cannot notify failed logins: channel provided doesn\'t exists');
Expand Down
18 changes: 18 additions & 0 deletions app/livechat/client/lib/stream/queueManager.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
import { Meteor } from 'meteor/meteor';

import { APIClient } from '../../../../utils/client';
import { LivechatInquiry } from '../../collections/LivechatInquiry';
import { inquiryDataStream } from './inquiry';
import { call } from '../../../../ui-utils/client';
import { getUserPreference } from '../../../../utils';
import { CustomSounds } from '../../../../custom-sounds/client/lib/CustomSounds';

const departments = new Set();

const newInquirySound = () => {
const userId = Meteor.userId();
const audioVolume = getUserPreference(userId, 'notificationsSoundVolume');
const newRoomNotification = getUserPreference(userId, 'newRoomNotification');
const audioNotificationValue = getUserPreference(userId, 'audioNotifications');

if (audioNotificationValue !== 'none') {
CustomSounds.play(newRoomNotification, {
volume: Number((audioVolume / 100).toPrecision(2)),
});
}
};

const events = {
added: (inquiry) => {
delete inquiry.type;
departments.has(inquiry.department) && LivechatInquiry.insert({ ...inquiry, alert: true, _updatedAt: new Date(inquiry._updatedAt) });
newInquirySound();
},
changed: (inquiry) => {
if (inquiry.status !== 'queued' || (inquiry.department && !departments.has(inquiry.department))) {
Expand Down
1 change: 1 addition & 0 deletions app/livechat/client/views/app/tabbar/visitorForward.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ Template.visitorForward.events({
const transferData = {
roomId: instance.room.get()._id,
comment: event.target.comment.value,
clientAction: true,
};

const [user] = instance.selectedAgents.get();
Expand Down
25 changes: 17 additions & 8 deletions app/livechat/server/lib/Helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Apps, AppEvents } from '../../../apps/server';
import notifications from '../../../notifications/server/lib/Notifications';
import { sendNotification } from '../../../lib/server';
import { sendMessage } from '../../../lib/server/functions/sendMessage';
import { queueInquiry } from './QueueManager';

export const allowAgentSkipQueue = (agent) => {
check(agent, Match.ObjectIncluding({
Expand Down Expand Up @@ -273,7 +274,7 @@ export const forwardRoomToAgent = async (room, transferData) => {
return false;
}

const { userId: agentId } = transferData;
const { userId: agentId, clientAction } = transferData;
const user = Users.findOneOnlineAgentById(agentId);
if (!user) {
throw new Meteor.Error('error-user-is-offline', 'User is offline', { function: 'forwardRoomToAgent' });
Expand All @@ -291,9 +292,11 @@ export const forwardRoomToAgent = async (room, transferData) => {

const { username } = user;
const agent = { agentId, username };
// There are some Enterprise features that may interrupt the fowarding process
// Remove department from inquiry to make sure the routing algorithm treat this as forwarding to agent and not as forwarding to department
inquiry.department = undefined;
// There are some Enterprise features that may interrupt the forwarding process
// Due to that we need to check whether the agent has been changed or not
const roomTaken = await RoutingManager.takeInquiry(inquiry, agent);
const roomTaken = await RoutingManager.takeInquiry(inquiry, agent, { ...clientAction && { clientAction } });
if (!roomTaken) {
return false;
}
Expand Down Expand Up @@ -356,7 +359,7 @@ export const forwardRoomToDepartment = async (room, guest, transferData) => {
throw new Meteor.Error('error-forwarding-chat-same-department', 'The selected department and the current room department are the same', { function: 'forwardRoomToDepartment' });
}

const { userId: agentId } = transferData;
const { userId: agentId, clientAction } = transferData;
if (agentId) {
let user = Users.findOneOnlineAgentById(agentId);
if (!user) {
Expand All @@ -378,26 +381,32 @@ export const forwardRoomToDepartment = async (room, guest, transferData) => {
// Fake the department to forward the inquiry - Case the forward process does not success
// the inquiry will stay in the same original department
inquiry.department = departmentId;
const roomTaken = await RoutingManager.delegateInquiry(inquiry, agent);
const roomTaken = await RoutingManager.delegateInquiry(inquiry, agent, { forwardingToDepartment: { oldDepartmentId }, ...clientAction && { clientAction } });
if (!roomTaken) {
return false;
}

const { servedBy } = roomTaken;
if (oldServedBy && servedBy && oldServedBy._id === servedBy._id) {
const { servedBy, chatQueued } = roomTaken;
if (!chatQueued && oldServedBy && servedBy && oldServedBy._id === servedBy._id) {
return false;
}

Livechat.saveTransferHistory(room, transferData);
if (oldServedBy) {
removeAgentFromSubscription(rid, oldServedBy);
}
if (servedBy) {
if (!chatQueued && servedBy) {
Messages.createUserJoinWithRoomIdAndUser(rid, servedBy);
}

updateChatDepartment({ rid, newDepartmentId: departmentId, oldDepartmentId });

if (chatQueued) {
LivechatInquiry.readyInquiry(inquiry._id);
const newInquiry = LivechatInquiry.findOneById(inquiry._id);
await queueInquiry(room, newInquiry);
}

const { token } = guest;
Livechat.setDepartmentForGuest({ token, department: departmentId });

Expand Down
7 changes: 3 additions & 4 deletions app/livechat/server/lib/RoutingManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const RoutingManager = {
return this.getMethod().getNextAgent(department, ignoreAgentId);
},

async delegateInquiry(inquiry, agent) {
async delegateInquiry(inquiry, agent, options = {}) {
const { department, rid } = inquiry;
if (!agent || (agent.username && !Users.findOneOnlineAgentByUsername(agent.username) && !allowAgentSkipQueue(agent))) {
agent = await this.getNextAgent(department);
Expand All @@ -53,7 +53,7 @@ export const RoutingManager = {
return LivechatRooms.findOneById(rid);
}

return this.takeInquiry(inquiry, agent);
return this.takeInquiry(inquiry, agent, options);
},

assignAgent(inquiry, agent) {
Expand Down Expand Up @@ -134,8 +134,7 @@ export const RoutingManager = {

agent = await callbacks.run('livechat.checkAgentBeforeTakeInquiry', { agent, inquiry, options });
if (!agent) {
await callbacks.run('livechat.onAgentAssignmentFailed', { inquiry, room, options });
return null;
return callbacks.run('livechat.onAgentAssignmentFailed', { inquiry, room, options });
}

if (room.onHold) {
Expand Down
1 change: 1 addition & 0 deletions app/livechat/server/methods/transfer.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Meteor.methods({
userId: Match.Optional(String),
departmentId: Match.Optional(String),
comment: Match.Optional(String),
clientAction: Match.Optional(Boolean),
});

const room = LivechatRooms.findOneById(transferData.roomId);
Expand Down
4 changes: 4 additions & 0 deletions app/models/server/raw/Rooms.js
Original file line number Diff line number Diff line change
Expand Up @@ -365,4 +365,8 @@ export class RoomsRaw extends BaseRaw {

return this.update(query, update, { multi: true });
}

findOneByNameOrFname(name, options = {}) {
return this.col.findOne({ $or: [{ name }, { fname: name }] }, options);
}
}
2 changes: 1 addition & 1 deletion app/threads/client/components/ThreadComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ const ThreadComponent: FC<{
}, [dispatchToastMessage, followMessage, unfollowMessage, mid]);

const handleClose = useCallback(() => {
channelRoute.push(room.t === 'd' ? { rid: room._id } : { name: room.name });
channelRoute.push(room.t === 'd' ? { rid: room._id } : { name: room.name || room._id });
}, [channelRoute, room._id, room.t, room.name]);

const [viewData, setViewData] = useState(() => ({
Expand Down
161 changes: 122 additions & 39 deletions app/ui-sidenav/client/roomList.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,65 +161,148 @@ const mergeSubRoom = (subscription) => {
retention: 1,
teamId: 1,
teamMain: 1,
onHold: 1,
metrics: 1,
servedBy: 1,
ts: 1,
waitingResponse: 1,
},
};

const room = Rooms.findOne({ _id: subscription.rid }, options) || { };

const lastRoomUpdate = room.lm || subscription.ts || subscription._updatedAt;

if (room.uids) {
subscription.uids = room.uids;
}

if (room.v) {
subscription.v = room.v;
}

subscription.usernames = room.usernames;
const {
encrypted,
description,
cl,
topic,
announcement,
broadcast,
archived,
retention,
lastMessage,
streamingOptions,
teamId,
teamMain,
uids,
usernames,

v,
transcriptRequest,
servedBy,
onHold,
tags,
closedAt,
metrics,
waitingResponse,
responseBy,
priorityId,
livechatData,
ts,
} = room;

subscription.lastMessage = room.lastMessage;
subscription.lm = subscription.lr ? new Date(Math.max(subscription.lr, lastRoomUpdate)) : lastRoomUpdate;
subscription.streamingOptions = room.streamingOptions;

subscription.encrypted = room.encrypted;
subscription.description = room.description;
subscription.cl = room.cl;
subscription.topic = room.topic;
subscription.announcement = room.announcement;
subscription.broadcast = room.broadcast;
subscription.archived = room.archived;
subscription.retention = room.retention;

subscription.teamId = room.teamId;
subscription.teamMain = room.teamMain;
return Object.assign(subscription, getLowerCaseNames(subscription));

return Object.assign(subscription, getLowerCaseNames(subscription), {
encrypted,
description,
cl,
topic,
announcement,
broadcast,
archived,
retention,
lastMessage,
streamingOptions,
teamId,
teamMain,
uids,
usernames,

v,
transcriptRequest,
servedBy,
onHold,
tags,
closedAt,
metrics,
waitingResponse,
responseBy,
priorityId,
livechatData,
ts,
});
};

const mergeRoomSub = (room) => {
const sub = Subscriptions.findOne({ rid: room._id });
if (!sub) {
return room;
}

const {
encrypted,
description,
cl,
topic,
announcement,
broadcast,
archived,
retention,
lastMessage,
streamingOptions,
teamId,
teamMain,
uids,
usernames,

v,
transcriptRequest,
servedBy,
onHold,
tags,
closedAt,
metrics,
waitingResponse,
responseBy,
priorityId,
livechatData,
ts,

} = room;

Subscriptions.update({
rid: room._id,
}, {
$set: {
encrypted: room.encrypted,
description: room.description,
cl: room.cl,
topic: room.topic,
announcement: room.announcement,
broadcast: room.broadcast,
archived: room.archived,
retention: room.retention,
...Array.isArray(room.uids) && { uids: room.uids },
...Array.isArray(room.uids) && { usernames: room.usernames },
...room.v && { v: room.v },
lastMessage: room.lastMessage,
streamingOptions: room.streamingOptions,
teamId: room.teamId,
teamMain: room.teamMain,
encrypted,
description,
cl,
topic,
announcement,
broadcast,
archived,
retention,
uids,
usernames,
lastMessage,
streamingOptions,
teamId,
teamMain,
v,
transcriptRequest,
servedBy,
onHold,
tags,
closedAt,
metrics,
waitingResponse,
responseBy,
priorityId,
livechatData,
ts,
...getLowerCaseNames(room, sub.name, sub.fname),
},
});
Expand Down
17 changes: 17 additions & 0 deletions client/components/ErrorBoundary.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Component } from 'react';

export class ErrorBoundary extends Component {
state = { hasError: false };

static getDerivedStateFromError() {
return { hasError: true };
}

render() {
if (this.state.hasError) {
return null;
}

return this.props.children;
}
}
Loading

0 comments on commit 491f442

Please sign in to comment.