diff --git a/app/livechat/client/views/app/livechatAgents.js b/app/livechat/client/views/app/livechatAgents.js
index 56c45da892ae..af3b68b88854 100644
--- a/app/livechat/client/views/app/livechatAgents.js
+++ b/app/livechat/client/views/app/livechatAgents.js
@@ -90,6 +90,10 @@ Template.livechatAgents.helpers({
data: Template.instance().tabBarData.get(),
};
},
+ statusService() {
+ const { status, statusLivechat } = this;
+ return statusLivechat === 'available' && status !== 'offline' ? t('Available') : t('Unavailable');
+ },
});
const DEBOUNCE_TIME_FOR_SEARCH_AGENTS_IN_MS = 300;
diff --git a/app/livechat/client/views/app/livechatCustomFieldForm.html b/app/livechat/client/views/app/livechatCustomFieldForm.html
index d879ce74218c..0dcf4e1ae792 100644
--- a/app/livechat/client/views/app/livechatCustomFieldForm.html
+++ b/app/livechat/client/views/app/livechatCustomFieldForm.html
@@ -7,19 +7,19 @@
{{#if canViewCustomFields }}
- {{#each roomCustomFields}}
-
+ {{#each field in roomCustomFields}}
+ {{> visitorEditCustomField field }}
{{/each}}
{{/if}}
{{/with}}
diff --git a/app/livechat/client/views/app/tabbar/visitorEdit.js b/app/livechat/client/views/app/tabbar/visitorEdit.js
index 3f9cf9b3e768..536d0b77e275 100644
--- a/app/livechat/client/views/app/tabbar/visitorEdit.js
+++ b/app/livechat/client/views/app/tabbar/visitorEdit.js
@@ -11,6 +11,16 @@ import { getCustomFormTemplate } from '../customTemplates/register';
const CUSTOM_FIELDS_COUNT = 100;
+const getCustomFieldsByScope = (customFields = [], data = {}, filter, disabled) =>
+ customFields
+ .filter(({ visibility, scope }) => visibility !== 'hidden' && scope === filter)
+ .map(({ _id: name, scope, label, ...extraData }) => {
+ const value = data[name] ? data[name] : '';
+ return { name, label, scope, value, disabled, ...extraData };
+ });
+
+const isCustomFieldDisabled = () => !hasPermission('edit-livechat-room-customfields');
+
Template.visitorEdit.helpers({
visitor() {
return Template.instance().visitor.get();
@@ -20,28 +30,16 @@ Template.visitorEdit.helpers({
return hasAtLeastOnePermission(['view-livechat-room-customfields', 'edit-livechat-room-customfields']);
},
- canOnlyViewCustomFields() {
- return hasPermission('view-livechat-room-customfields') && !hasPermission('edit-livechat-room-customfields');
- },
-
visitorCustomFields() {
const customFields = Template.instance().customFields.get();
if (!customFields || customFields.length === 0) {
return [];
}
- const fields = [];
const visitor = Template.instance().visitor.get();
const { livechatData = {} } = visitor || {};
- customFields.forEach((field) => {
- if (field.visibility !== 'hidden' && field.scope === 'visitor') {
- const value = livechatData[field._id] ? livechatData[field._id] : '';
- fields.push({ name: field._id, label: field.label, value });
- }
- });
-
- return fields;
+ return getCustomFieldsByScope(customFields, livechatData, 'visitor', isCustomFieldDisabled());
},
room() {
@@ -54,18 +52,10 @@ Template.visitorEdit.helpers({
return [];
}
- const fields = [];
const room = Template.instance().room.get();
const { livechatData = {} } = room || {};
- customFields.forEach((field) => {
- if (field.visibility !== 'hidden' && field.scope === 'room') {
- const value = livechatData[field._id] ? livechatData[field._id] : '';
- fields.push({ name: field._id, label: field.label, value });
- }
- });
-
- return fields;
+ return getCustomFieldsByScope(customFields, livechatData, 'room', isCustomFieldDisabled());
},
email() {
diff --git a/app/livechat/client/views/app/tabbar/visitorEditCustomField.html b/app/livechat/client/views/app/tabbar/visitorEditCustomField.html
new file mode 100644
index 000000000000..bf85704e4b26
--- /dev/null
+++ b/app/livechat/client/views/app/tabbar/visitorEditCustomField.html
@@ -0,0 +1,22 @@
+
+
+
diff --git a/app/livechat/client/views/app/tabbar/visitorEditCustomField.js b/app/livechat/client/views/app/tabbar/visitorEditCustomField.js
new file mode 100644
index 000000000000..f0024c0725dd
--- /dev/null
+++ b/app/livechat/client/views/app/tabbar/visitorEditCustomField.js
@@ -0,0 +1,17 @@
+import { Template } from 'meteor/templating';
+
+import './visitorEditCustomField.html';
+
+Template.visitorEditCustomField.helpers({
+ optionsList() {
+ if (!this.options) {
+ return [];
+ }
+
+ return this.options.split(',');
+ },
+ selectedField(current) {
+ const { fieldData: { value } } = Template.currentData();
+ return value.trim() === current.trim();
+ },
+});
diff --git a/app/livechat/client/views/regular.js b/app/livechat/client/views/regular.js
index 30f1a72c3e14..e2a3f3b8b6fd 100644
--- a/app/livechat/client/views/regular.js
+++ b/app/livechat/client/views/regular.js
@@ -6,6 +6,7 @@ import './app/livechatRoomTagSelector';
import './app/tabbar/agentEdit';
import './app/tabbar/agentInfo';
import './app/tabbar/visitorEdit';
+import './app/tabbar/visitorEditCustomField';
import './app/tabbar/visitorForward';
import './app/tabbar/visitorHistory';
import './app/tabbar/visitorInfo';
diff --git a/app/livechat/imports/server/rest/agent.js b/app/livechat/imports/server/rest/agent.js
index b928501dc2c1..170af7636ff2 100644
--- a/app/livechat/imports/server/rest/agent.js
+++ b/app/livechat/imports/server/rest/agent.js
@@ -1,6 +1,6 @@
import { Match, check } from 'meteor/check';
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { findAgentDepartments } from '../../../server/api/lib/agents';
API.v1.addRoute('livechat/agents/:agentId/departments', { authRequired: true }, {
diff --git a/app/livechat/imports/server/rest/appearance.js b/app/livechat/imports/server/rest/appearance.js
index f8345f7d31dc..c7fe16243a78 100644
--- a/app/livechat/imports/server/rest/appearance.js
+++ b/app/livechat/imports/server/rest/appearance.js
@@ -1,4 +1,4 @@
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { findAppearance } from '../../../server/api/lib/appearance';
API.v1.addRoute('livechat/appearance', { authRequired: true }, {
diff --git a/app/livechat/imports/server/rest/dashboards.js b/app/livechat/imports/server/rest/dashboards.js
index 2537942e74dc..af8972296b20 100644
--- a/app/livechat/imports/server/rest/dashboards.js
+++ b/app/livechat/imports/server/rest/dashboards.js
@@ -1,6 +1,6 @@
import { Match, check } from 'meteor/check';
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { hasPermission } from '../../../../authorization/server';
import {
findAllChatsStatus,
diff --git a/app/livechat/imports/server/rest/departments.js b/app/livechat/imports/server/rest/departments.js
index 8a255185f86f..9ec4113ba66a 100644
--- a/app/livechat/imports/server/rest/departments.js
+++ b/app/livechat/imports/server/rest/departments.js
@@ -1,6 +1,6 @@
import { Match, check } from 'meteor/check';
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { hasPermission } from '../../../../authorization';
import { LivechatDepartment, LivechatDepartmentAgents } from '../../../../models';
import { Livechat } from '../../../server/lib/Livechat';
diff --git a/app/livechat/imports/server/rest/facebook.js b/app/livechat/imports/server/rest/facebook.js
index cce8c53a7165..b4b8efa55034 100644
--- a/app/livechat/imports/server/rest/facebook.js
+++ b/app/livechat/imports/server/rest/facebook.js
@@ -2,7 +2,7 @@ import crypto from 'crypto';
import { Random } from 'meteor/random';
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { LivechatRooms, LivechatVisitors } from '../../../../models';
import { settings } from '../../../../settings';
import { Livechat } from '../../../server/lib/Livechat';
diff --git a/app/livechat/imports/server/rest/inquiries.js b/app/livechat/imports/server/rest/inquiries.js
index a553c875fe84..3abfc5eee735 100644
--- a/app/livechat/imports/server/rest/inquiries.js
+++ b/app/livechat/imports/server/rest/inquiries.js
@@ -1,7 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { hasPermission } from '../../../../authorization';
import { Users, LivechatDepartment, LivechatInquiry } from '../../../../models';
import { findInquiries, findOneInquiryByRoomId } from '../../../server/api/lib/inquiries';
diff --git a/app/livechat/imports/server/rest/integrations.js b/app/livechat/imports/server/rest/integrations.js
index 08d9d064892a..6b7aed33d89f 100644
--- a/app/livechat/imports/server/rest/integrations.js
+++ b/app/livechat/imports/server/rest/integrations.js
@@ -1,4 +1,4 @@
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { findIntegrationSettings } from '../../../server/api/lib/integrations';
API.v1.addRoute('livechat/integrations.settings', { authRequired: true }, {
diff --git a/app/livechat/imports/server/rest/messages.js b/app/livechat/imports/server/rest/messages.js
index a40557231b53..f5ad166c8c70 100644
--- a/app/livechat/imports/server/rest/messages.js
+++ b/app/livechat/imports/server/rest/messages.js
@@ -1,7 +1,7 @@
import { check } from 'meteor/check';
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { findExternalMessages } from '../../../server/api/lib/messages';
API.v1.addRoute('livechat/messages.external/:roomId', { authRequired: true }, {
diff --git a/app/livechat/imports/server/rest/officeHour.js b/app/livechat/imports/server/rest/officeHour.js
index f321a31ea5a3..7b2cd02497ee 100644
--- a/app/livechat/imports/server/rest/officeHour.js
+++ b/app/livechat/imports/server/rest/officeHour.js
@@ -1,4 +1,4 @@
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { findLivechatOfficeHours } from '../../../server/api/lib/officeHour';
API.v1.addRoute('livechat/office-hours', { authRequired: true }, {
diff --git a/app/livechat/imports/server/rest/queue.js b/app/livechat/imports/server/rest/queue.js
index d5f319f2e3c0..43b586431ea2 100644
--- a/app/livechat/imports/server/rest/queue.js
+++ b/app/livechat/imports/server/rest/queue.js
@@ -1,4 +1,4 @@
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { findQueueMetrics } from '../../../server/api/lib/queue';
API.v1.addRoute('livechat/queue', { authRequired: true }, {
diff --git a/app/livechat/imports/server/rest/rooms.js b/app/livechat/imports/server/rest/rooms.js
index d7556006597a..052da5db3963 100644
--- a/app/livechat/imports/server/rest/rooms.js
+++ b/app/livechat/imports/server/rest/rooms.js
@@ -1,7 +1,7 @@
import { Match, check } from 'meteor/check';
import { hasPermission } from '../../../../authorization/server';
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { findRooms } from '../../../server/api/lib/rooms';
const validateDateParams = (property, date) => {
diff --git a/app/livechat/imports/server/rest/sms.js b/app/livechat/imports/server/rest/sms.js
index edebecb583a7..f813e7def804 100644
--- a/app/livechat/imports/server/rest/sms.js
+++ b/app/livechat/imports/server/rest/sms.js
@@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor';
import { Random } from 'meteor/random';
import { LivechatRooms, LivechatVisitors, LivechatDepartment } from '../../../../models';
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { SMS } from '../../../../sms';
import { Livechat } from '../../../server/lib/Livechat';
diff --git a/app/livechat/imports/server/rest/triggers.js b/app/livechat/imports/server/rest/triggers.js
index ca6ebe7a12a0..de3d0b57f27b 100644
--- a/app/livechat/imports/server/rest/triggers.js
+++ b/app/livechat/imports/server/rest/triggers.js
@@ -1,6 +1,6 @@
import { check } from 'meteor/check';
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { findTriggers, findTriggerById } from '../../../server/api/lib/triggers';
API.v1.addRoute('livechat/triggers', { authRequired: true }, {
diff --git a/app/livechat/imports/server/rest/upload.js b/app/livechat/imports/server/rest/upload.js
index 3d28f420402e..4c27811749a6 100644
--- a/app/livechat/imports/server/rest/upload.js
+++ b/app/livechat/imports/server/rest/upload.js
@@ -6,7 +6,7 @@ import { settings } from '../../../../settings';
import { Settings, LivechatRooms, LivechatVisitors } from '../../../../models';
import { fileUploadIsValidContentType } from '../../../../utils';
import { FileUpload } from '../../../../file-upload';
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
let maxFileSize;
diff --git a/app/livechat/imports/server/rest/users.js b/app/livechat/imports/server/rest/users.js
index 04e1815b4f07..f0c88aa25c59 100644
--- a/app/livechat/imports/server/rest/users.js
+++ b/app/livechat/imports/server/rest/users.js
@@ -2,7 +2,7 @@ import { check } from 'meteor/check';
import _ from 'underscore';
import { hasPermission } from '../../../../authorization';
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { Users } from '../../../../models';
import { Livechat } from '../../../server/lib/Livechat';
import { findAgents, findManagers } from '../../../server/api/lib/users';
diff --git a/app/livechat/imports/server/rest/visitors.js b/app/livechat/imports/server/rest/visitors.js
index 42b5a20b25d7..cc38c3bc4008 100644
--- a/app/livechat/imports/server/rest/visitors.js
+++ b/app/livechat/imports/server/rest/visitors.js
@@ -1,8 +1,8 @@
import { check } from 'meteor/check';
-import { API } from '../../../../api';
-import { findVisitorInfo, findVisitedPages, findChatHistory } from '../../../server/api/lib/visitors';
+import { API } from '../../../../api/server';
+import { findVisitorInfo, findVisitedPages, findChatHistory, findVisitorsToAutocomplete } from '../../../server/api/lib/visitors';
API.v1.addRoute('livechat/visitors.info', { authRequired: true }, {
get() {
@@ -62,3 +62,17 @@ API.v1.addRoute('livechat/visitors.chatHistory/room/:roomId/visitor/:visitorId',
return API.v1.success(history);
},
});
+
+API.v1.addRoute('livechat/visitors.autocomplete', { authRequired: true }, {
+ get() {
+ const { selector } = this.queryParams;
+ if (!selector) {
+ return API.v1.failure('The \'selector\' param is required');
+ }
+
+ return API.v1.success(Promise.await(findVisitorsToAutocomplete({
+ userId: this.userId,
+ selector: JSON.parse(selector),
+ })));
+ },
+});
diff --git a/app/livechat/lib/messageTypes.js b/app/livechat/lib/messageTypes.js
index c68e120f0542..8f870923bd28 100644
--- a/app/livechat/lib/messageTypes.js
+++ b/app/livechat/lib/messageTypes.js
@@ -1,12 +1,6 @@
-import { Meteor } from 'meteor/meteor';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
-import { Livechat } from 'meteor/rocketchat:livechat';
import { MessageTypes } from '../../ui-utils';
-import { actionLinks } from '../../action-links';
-import { Notifications } from '../../notifications';
-import { Messages, LivechatRooms } from '../../models';
-import { settings } from '../../settings';
MessageTypes.registerType({
id: 'livechat_navigation_history',
@@ -60,29 +54,3 @@ MessageTypes.registerType({
system: true,
message: 'New_videocall_request',
});
-
-actionLinks.register('createLivechatCall', function(message, params, instance) {
- if (Meteor.isClient) {
- instance.tabBar.open('video');
- }
-});
-
-actionLinks.register('denyLivechatCall', function(message/* , params*/) {
- if (Meteor.isServer) {
- const user = Meteor.user();
-
- Messages.createWithTypeRoomIdMessageAndUser('command', message.rid, 'endCall', user);
- Notifications.notifyRoom(message.rid, 'deleteMessage', { _id: message._id });
-
- const language = user.language || settings.get('Language') || 'en';
-
- Livechat.closeRoom({
- user,
- room: LivechatRooms.findOneById(message.rid),
- comment: TAPi18n.__('Videocall_declined', { lng: language }),
- });
- Meteor.defer(() => {
- Messages.setHiddenById(message._id);
- });
- }
-});
diff --git a/app/livechat/server/api/lib/livechat.js b/app/livechat/server/api/lib/livechat.js
index b1893f9a0f84..4626ce819b35 100644
--- a/app/livechat/server/api/lib/livechat.js
+++ b/app/livechat/server/api/lib/livechat.js
@@ -146,7 +146,7 @@ export function settings() {
}
export async function getExtraConfigInfo(room) {
- return callbacks.run('livechat.onLoadConfigApi', room);
+ return callbacks.run('livechat.onLoadConfigApi', { room });
}
export function onCheckRoomParams(params) {
diff --git a/app/livechat/server/api/lib/transfer.js b/app/livechat/server/api/lib/transfer.js
new file mode 100644
index 000000000000..60070dfc2645
--- /dev/null
+++ b/app/livechat/server/api/lib/transfer.js
@@ -0,0 +1,27 @@
+import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission';
+import { Messages } from '../../../../models/server/raw';
+
+const normalizeTransferHistory = ({ transferData }) => transferData;
+export async function findLivechatTransferHistory({ userId, rid, pagination: { offset, count, sort } }) {
+ if (!await hasPermissionAsync(userId, 'view-livechat-rooms')) {
+ throw new Error('error-not-authorized');
+ }
+
+ const cursor = await Messages.find({ rid, t: 'livechat_transfer_history' }, {
+ fields: { transferData: 1 },
+ sort: sort || { ts: 1 },
+ skip: offset,
+ limit: count,
+ });
+
+ const total = await cursor.count();
+ const messages = await cursor.toArray();
+ const history = messages.map(normalizeTransferHistory);
+
+ return {
+ history,
+ count: history.length,
+ offset,
+ total,
+ };
+}
diff --git a/app/livechat/server/api/lib/visitors.js b/app/livechat/server/api/lib/visitors.js
index ad7cb3da93e5..d1c7477fdd56 100644
--- a/app/livechat/server/api/lib/visitors.js
+++ b/app/livechat/server/api/lib/visitors.js
@@ -72,3 +72,27 @@ export async function findChatHistory({ userId, roomId, visitorId, pagination: {
total,
};
}
+
+export async function findVisitorsToAutocomplete({ userId, selector }) {
+ if (!await hasPermissionAsync(userId, 'view-l-room')) {
+ return { items: [] };
+ }
+ const { exceptions = [], conditions = {} } = selector;
+
+ const options = {
+ fields: {
+ _id: 1,
+ name: 1,
+ username: 1,
+ },
+ limit: 10,
+ sort: {
+ name: 1,
+ },
+ };
+
+ const items = await LivechatVisitors.findByNameRegexWithExceptionsAndConditions(selector.term, exceptions, conditions, options).toArray();
+ return {
+ items,
+ };
+}
diff --git a/app/livechat/server/api/rest.js b/app/livechat/server/api/rest.js
index 3731e72f6b63..a63794bf1db0 100644
--- a/app/livechat/server/api/rest.js
+++ b/app/livechat/server/api/rest.js
@@ -8,3 +8,4 @@ import './v1/message.js';
import './v1/customField.js';
import './v1/room.js';
import './v1/videoCall.js';
+import './v1/transfer.js';
diff --git a/app/livechat/server/api/v1/agent.js b/app/livechat/server/api/v1/agent.js
index 68ceef87da14..7e2329058542 100644
--- a/app/livechat/server/api/v1/agent.js
+++ b/app/livechat/server/api/v1/agent.js
@@ -1,7 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { findRoom, findGuest, findAgent, findOpenRoom } from '../lib/livechat';
import { Livechat } from '../../lib/Livechat';
diff --git a/app/livechat/server/api/v1/config.js b/app/livechat/server/api/v1/config.js
index a1f4bab03405..85ef7a10a865 100644
--- a/app/livechat/server/api/v1/config.js
+++ b/app/livechat/server/api/v1/config.js
@@ -1,6 +1,6 @@
import { Match, check } from 'meteor/check';
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { findGuest, settings, online, findOpenRoom, getExtraConfigInfo, findAgent } from '../lib/livechat';
API.v1.addRoute('livechat/config', {
@@ -27,8 +27,9 @@ API.v1.addRoute('livechat/config', {
room = findOpenRoom(token);
agent = room && room.servedBy && findAgent(room.servedBy._id);
}
- const extraConfig = room && Promise.await(getExtraConfigInfo(room));
- Object.assign(config, { online: status, guest, room, agent }, extraConfig);
+ const extra = Promise.await(getExtraConfigInfo(room));
+ const { config: extraConfig = {} } = extra || {};
+ Object.assign(config, { online: status, guest, room, agent }, { ...extraConfig });
return API.v1.success({ config });
} catch (e) {
diff --git a/app/livechat/server/api/v1/customField.js b/app/livechat/server/api/v1/customField.js
index f64266d3be6c..3b19e832bc65 100644
--- a/app/livechat/server/api/v1/customField.js
+++ b/app/livechat/server/api/v1/customField.js
@@ -1,7 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { findGuest } from '../lib/livechat';
import { Livechat } from '../../lib/Livechat';
import { findLivechatCustomFields, findCustomFieldById } from '../lib/customFields';
diff --git a/app/livechat/server/api/v1/message.js b/app/livechat/server/api/v1/message.js
index e2362baf6d32..0811bc8324fb 100644
--- a/app/livechat/server/api/v1/message.js
+++ b/app/livechat/server/api/v1/message.js
@@ -4,7 +4,7 @@ import { Random } from 'meteor/random';
import { Messages, LivechatRooms, LivechatVisitors } from '../../../../models';
import { hasPermission } from '../../../../authorization';
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { loadMessageHistory } from '../../../../lib';
import { findGuest, findRoom, normalizeHttpHeaderData } from '../lib/livechat';
import { Livechat } from '../../lib/Livechat';
diff --git a/app/livechat/server/api/v1/offlineMessage.js b/app/livechat/server/api/v1/offlineMessage.js
index 6788c30e3d86..8264228c97e7 100644
--- a/app/livechat/server/api/v1/offlineMessage.js
+++ b/app/livechat/server/api/v1/offlineMessage.js
@@ -1,7 +1,7 @@
import { Match, check } from 'meteor/check';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { Livechat } from '../../lib/Livechat';
API.v1.addRoute('livechat/offline.message', {
diff --git a/app/livechat/server/api/v1/pageVisited.js b/app/livechat/server/api/v1/pageVisited.js
index e5ef7c42ba64..4f8c638e6146 100644
--- a/app/livechat/server/api/v1/pageVisited.js
+++ b/app/livechat/server/api/v1/pageVisited.js
@@ -1,7 +1,7 @@
import { Match, check } from 'meteor/check';
import _ from 'underscore';
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { Livechat } from '../../lib/Livechat';
API.v1.addRoute('livechat/page.visited', {
diff --git a/app/livechat/server/api/v1/room.js b/app/livechat/server/api/v1/room.js
index 83af5ffe1b61..5ec9ef1cf06e 100644
--- a/app/livechat/server/api/v1/room.js
+++ b/app/livechat/server/api/v1/room.js
@@ -5,7 +5,7 @@ import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import { settings as rcSettings } from '../../../../settings';
import { Messages, LivechatRooms } from '../../../../models';
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { findGuest, findRoom, getRoom, settings, findAgent, onCheckRoomParams } from '../lib/livechat';
import { Livechat } from '../../lib/Livechat';
import { normalizeTransferredByData } from '../../lib/Helper';
diff --git a/app/livechat/server/api/v1/transcript.js b/app/livechat/server/api/v1/transcript.js
index 02c0d9d27561..f8f3c923d25e 100644
--- a/app/livechat/server/api/v1/transcript.js
+++ b/app/livechat/server/api/v1/transcript.js
@@ -1,7 +1,7 @@
import { check } from 'meteor/check';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { Livechat } from '../../lib/Livechat';
API.v1.addRoute('livechat/transcript', {
diff --git a/app/livechat/server/api/v1/transfer.js b/app/livechat/server/api/v1/transfer.js
new file mode 100644
index 000000000000..aa3fb7facd0e
--- /dev/null
+++ b/app/livechat/server/api/v1/transfer.js
@@ -0,0 +1,36 @@
+import { Meteor } from 'meteor/meteor';
+import { check } from 'meteor/check';
+
+import { LivechatRooms } from '../../../../models';
+import { API } from '../../../../api/server';
+import { findLivechatTransferHistory } from '../lib/transfer';
+
+API.v1.addRoute('livechat/transfer.history/:rid', { authRequired: true }, {
+ get() {
+ check(this.urlParams, {
+ rid: String,
+ });
+
+ const { rid } = this.urlParams;
+
+ const room = LivechatRooms.findOneById(rid, { _id: 1 });
+ if (!room) {
+ throw new Meteor.Error('invalid-room');
+ }
+
+ const { offset, count } = this.getPaginationItems();
+ const { sort } = this.parseJsonQuery();
+
+ const history = Promise.await(findLivechatTransferHistory({
+ userId: this.userId,
+ rid,
+ pagination: {
+ offset,
+ count,
+ sort,
+ },
+ }));
+
+ return API.v1.success(history);
+ },
+});
diff --git a/app/livechat/server/api/v1/videoCall.js b/app/livechat/server/api/v1/videoCall.js
index 0aaa231da654..56159d8c349c 100644
--- a/app/livechat/server/api/v1/videoCall.js
+++ b/app/livechat/server/api/v1/videoCall.js
@@ -4,7 +4,7 @@ import { Random } from 'meteor/random';
import { Messages } from '../../../../models';
import { settings as rcSettings } from '../../../../settings';
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { findGuest, getRoom, settings } from '../lib/livechat';
API.v1.addRoute('livechat/video.call/:token', {
diff --git a/app/livechat/server/api/v1/visitor.js b/app/livechat/server/api/v1/visitor.js
index f34930a1b9c3..98007540876c 100644
--- a/app/livechat/server/api/v1/visitor.js
+++ b/app/livechat/server/api/v1/visitor.js
@@ -3,7 +3,7 @@ import { Match, check } from 'meteor/check';
import { LivechatRooms, LivechatVisitors, LivechatCustomField } from '../../../../models';
import { hasPermission } from '../../../../authorization';
-import { API } from '../../../../api';
+import { API } from '../../../../api/server';
import { findGuest, normalizeHttpHeaderData } from '../lib/livechat';
import { Livechat } from '../../lib/Livechat';
diff --git a/app/livechat/server/index.js b/app/livechat/server/index.js
index 72632b50670e..527bc59802d6 100644
--- a/app/livechat/server/index.js
+++ b/app/livechat/server/index.js
@@ -84,5 +84,6 @@ import './sendMessageBySMS';
import './api';
import './api/rest';
import './externalFrame';
+import './lib/messageTypes';
export { Livechat } from './lib/Livechat';
diff --git a/app/livechat/server/lib/Livechat.js b/app/livechat/server/lib/Livechat.js
index b57571205fa4..ae5004a84500 100644
--- a/app/livechat/server/lib/Livechat.js
+++ b/app/livechat/server/lib/Livechat.js
@@ -117,8 +117,9 @@ export const Livechat = {
}
if (room == null) {
+ const defaultAgent = callbacks.run('livechat.checkDefaultAgentOnNewRoom', agent, guest);
// if no department selected verify if there is at least one active and pick the first
- if (!agent && !guest.department) {
+ if (!defaultAgent && !guest.department) {
const department = this.getRequiredDepartment();
if (department) {
@@ -127,7 +128,7 @@ export const Livechat = {
}
// delegate room creation to QueueManager
- room = await QueueManager.requestRoom({ guest, message, roomInfo, agent, extraData });
+ room = await QueueManager.requestRoom({ guest, message, roomInfo, agent: defaultAgent, extraData });
newRoom = true;
}
diff --git a/app/livechat/server/lib/RoutingManager.js b/app/livechat/server/lib/RoutingManager.js
index f654415af4a8..5558d1be5d0e 100644
--- a/app/livechat/server/lib/RoutingManager.js
+++ b/app/livechat/server/lib/RoutingManager.js
@@ -145,7 +145,7 @@ export const RoutingManager = {
LivechatInquiry.takeInquiry(_id);
const inq = this.assignAgent(inquiry, agent);
- callbacks.run('livechat.afterTakeInquiry', inq);
+ callbacks.runAsync('livechat.afterTakeInquiry', inq, agent);
return LivechatRooms.findOneById(rid);
},
diff --git a/app/livechat/server/lib/messageTypes.js b/app/livechat/server/lib/messageTypes.js
new file mode 100644
index 000000000000..3d32da6f401f
--- /dev/null
+++ b/app/livechat/server/lib/messageTypes.js
@@ -0,0 +1,26 @@
+import { Meteor } from 'meteor/meteor';
+import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
+
+import { actionLinks } from '../../../action-links/server';
+import { Notifications } from '../../../notifications/server';
+import { Messages, LivechatRooms } from '../../../models/server';
+import { settings } from '../../../settings/server';
+import { Livechat } from './Livechat';
+
+actionLinks.register('denyLivechatCall', function(message/* , params*/) {
+ const user = Meteor.user();
+
+ Messages.createWithTypeRoomIdMessageAndUser('command', message.rid, 'endCall', user);
+ Notifications.notifyRoom(message.rid, 'deleteMessage', { _id: message._id });
+
+ const language = user.language || settings.get('Language') || 'en';
+
+ Livechat.closeRoom({
+ user,
+ room: LivechatRooms.findOneById(message.rid),
+ comment: TAPi18n.__('Videocall_declined', { lng: language }),
+ });
+ Meteor.defer(() => {
+ Messages.setHiddenById(message._id);
+ });
+});
diff --git a/app/livechat/server/methods/saveCustomField.js b/app/livechat/server/methods/saveCustomField.js
index 4630b967cd26..772ca03cfae5 100644
--- a/app/livechat/server/methods/saveCustomField.js
+++ b/app/livechat/server/methods/saveCustomField.js
@@ -17,7 +17,7 @@ Meteor.methods({
check(customFieldData, Match.ObjectIncluding({ field: String, label: String, scope: String, visibility: String, regexp: String }));
if (!/^[0-9a-zA-Z-_]+$/.test(customFieldData.field)) {
- throw new Meteor.Error('error-invalid-custom-field-nmae', 'Invalid custom field name. Use only letters, numbers, hyphens and underscores.', { method: 'livechat:saveCustomField' });
+ throw new Meteor.Error('error-invalid-custom-field-name', 'Invalid custom field name. Use only letters, numbers, hyphens and underscores.', { method: 'livechat:saveCustomField' });
}
if (_id) {
@@ -26,6 +26,8 @@ Meteor.methods({
throw new Meteor.Error('error-invalid-custom-field', 'Custom Field Not found', { method: 'livechat:saveCustomField' });
}
}
- return LivechatCustomField.createOrUpdateCustomField(_id, customFieldData.field, customFieldData.label, customFieldData.scope, customFieldData.visibility, { regexp: customFieldData.regexp });
+
+ const { field, label, scope, visibility, ...extraData } = customFieldData;
+ return LivechatCustomField.createOrUpdateCustomField(_id, field, label, scope, visibility, { ...extraData });
},
});
diff --git a/app/livestream/server/routes.js b/app/livestream/server/routes.js
index 8668217d19a5..3a52aec6031c 100644
--- a/app/livestream/server/routes.js
+++ b/app/livestream/server/routes.js
@@ -3,7 +3,7 @@ import google from 'googleapis';
import { settings } from '../../settings';
import { Users } from '../../models';
-import { API } from '../../api';
+import { API } from '../../api/server';
const { OAuth2 } = google.auth;
diff --git a/app/meteor-accounts-saml/server/saml_utils.js b/app/meteor-accounts-saml/server/saml_utils.js
index fa6a9c65c89c..80cadf2d3779 100644
--- a/app/meteor-accounts-saml/server/saml_utils.js
+++ b/app/meteor-accounts-saml/server/saml_utils.js
@@ -373,8 +373,10 @@ SAML.prototype.validateLogoutRequest = function(samlRequest, callback) {
return callback(err, null);
}
- debugLog(`LogoutRequest: ${ decoded }`);
- const doc = new xmldom.DOMParser().parseFromString(array2string(decoded), 'text/xml');
+ const xmlString = array2string(decoded);
+ debugLog(`LogoutRequest: ${ xmlString }`);
+
+ const doc = new xmldom.DOMParser().parseFromString(xmlString, 'text/xml');
if (!doc) {
return callback('No Doc Found');
}
@@ -385,17 +387,10 @@ SAML.prototype.validateLogoutRequest = function(samlRequest, callback) {
}
try {
- const sessionNode = request.getElementsByTagName('samlp:SessionIndex')[0];
+ const sessionNode = request.getElementsByTagNameNS('*', 'SessionIndex')[0];
+ const nameIdNode = request.getElementsByTagNameNS('*', 'NameID')[0];
- const nameNodes1 = request.getElementsByTagName('saml:NameID');
- const nameNodes2 = request.getElementsByTagName('NameID');
-
- let nameIdNode;
- if (nameNodes1 && nameNodes1.length) {
- nameIdNode = nameNodes1[0];
- } else if (nameNodes2 && nameNodes2.length) {
- nameIdNode = nameNodes2[0];
- } else {
+ if (!nameIdNode) {
throw new Error('SAML Logout Request: No NameID node found');
}
diff --git a/app/models/server/models/LivechatInquiry.js b/app/models/server/models/LivechatInquiry.js
index 954c7083ab6a..9dc6251a17cb 100644
--- a/app/models/server/models/LivechatInquiry.js
+++ b/app/models/server/models/LivechatInquiry.js
@@ -45,6 +45,7 @@ export class LivechatInquiry extends Base {
_id: inquiryId,
}, {
$set: { status: 'taken' },
+ $unset: { defaultAgent: 1 },
});
}
@@ -62,11 +63,14 @@ export class LivechatInquiry extends Base {
/*
* mark inquiry as queued
*/
- queueInquiry(inquiryId) {
+ queueInquiry(inquiryId, defaultAgent) {
return this.update({
_id: inquiryId,
}, {
- $set: { status: 'queued' },
+ $set: {
+ status: 'queued',
+ ...defaultAgent && { defaultAgent },
+ },
});
}
@@ -180,6 +184,14 @@ export class LivechatInquiry extends Base {
return collectionObj.aggregate(aggregate).toArray();
}
+ removeDefaultAgentById(inquiryId) {
+ return this.update({
+ _id: inquiryId,
+ }, {
+ $unset: { defaultAgent: 1 },
+ });
+ }
+
/*
* remove the inquiry by roomId
*/
diff --git a/app/models/server/models/LivechatRooms.js b/app/models/server/models/LivechatRooms.js
index 25e2e51c6be2..6f9e1957c491 100644
--- a/app/models/server/models/LivechatRooms.js
+++ b/app/models/server/models/LivechatRooms.js
@@ -16,6 +16,8 @@ export class LivechatRooms extends Base {
this.tryEnsureIndex({ 'metrics.serviceTimeDuration': 1 }, { sparse: true });
this.tryEnsureIndex({ 'metrics.visitorInactivity': 1 }, { sparse: true });
this.tryEnsureIndex({ 'omnichannel.predictedVisitorAbandonmentAt': 1 }, { sparse: true });
+ this.tryEnsureIndex({ closedAt: 1 }, { sparse: true });
+ this.tryEnsureIndex({ servedBy: 1 }, { sparse: true });
}
findLivechat(filter = {}, offset = 0, limit = 20) {
@@ -164,6 +166,18 @@ export class LivechatRooms extends Base {
return this.findOne(query, options);
}
+ findOneLastServedAndClosedByVisitorToken(visitorToken, options = {}) {
+ const query = {
+ t: 'l',
+ 'v.token': visitorToken,
+ closedAt: { $exists: true },
+ servedBy: { $exists: true },
+ };
+
+ options.sort = { closedAt: -1 };
+ return this.findOne(query, options);
+ }
+
findOneByVisitorToken(visitorToken, fields) {
const options = {};
@@ -238,6 +252,16 @@ export class LivechatRooms extends Base {
return this.find(query);
}
+ findByVisitorIdAndAgentId(visitorId, agentId, options) {
+ const query = {
+ t: 'l',
+ ...visitorId && { 'v._id': visitorId },
+ ...agentId && { 'servedBy._id': agentId },
+ };
+
+ return this.find(query, options);
+ }
+
findByVisitorId(visitorId) {
const query = {
t: 'l',
diff --git a/app/models/server/models/LivechatVisitors.js b/app/models/server/models/LivechatVisitors.js
index 803ecf2dc56f..571d950b0fee 100644
--- a/app/models/server/models/LivechatVisitors.js
+++ b/app/models/server/models/LivechatVisitors.js
@@ -78,6 +78,20 @@ export class LivechatVisitors extends Base {
return this.update(query, update);
}
+ updateLastAgentByToken(token, lastAgent) {
+ const query = {
+ token,
+ };
+
+ const update = {
+ $set: {
+ lastAgent,
+ },
+ };
+
+ return this.update(query, update);
+ }
+
/**
* Find a visitor by their phone number
* @return {object} User from db
diff --git a/app/models/server/models/Rooms.js b/app/models/server/models/Rooms.js
index 0f038458b700..6e5007cabf90 100644
--- a/app/models/server/models/Rooms.js
+++ b/app/models/server/models/Rooms.js
@@ -20,9 +20,6 @@ export class Rooms extends Base {
// discussions
this.tryEnsureIndex({ prid: 1 }, { sparse: true });
this.tryEnsureIndex({ fname: 1 }, { sparse: true });
- // Livechat - statistics
- this.tryEnsureIndex({ closedAt: 1 }, { sparse: true });
-
// field used for DMs only
this.tryEnsureIndex({ uids: 1 }, { sparse: true });
}
diff --git a/app/models/server/models/Subscriptions.js b/app/models/server/models/Subscriptions.js
index 3442cfcd5a46..e991c1d925a1 100644
--- a/app/models/server/models/Subscriptions.js
+++ b/app/models/server/models/Subscriptions.js
@@ -180,20 +180,6 @@ export class Subscriptions extends Base {
return this.update(query, update);
}
- updateDesktopNotificationDurationById(_id, value) {
- const query = {
- _id,
- };
-
- const update = {
- $set: {
- desktopNotificationDuration: parseInt(value),
- },
- };
-
- return this.update(query, update);
- }
-
updateMobilePushNotificationsById(_id, mobilePushNotifications) {
const query = {
_id,
@@ -366,7 +352,6 @@ export class Subscriptions extends Base {
ignored: 1,
audioNotifications: 1,
audioNotificationValue: 1,
- desktopNotificationDuration: 1,
desktopNotifications: 1,
mobilePushNotifications: 1,
emailNotifications: 1,
@@ -393,7 +378,6 @@ export class Subscriptions extends Base {
'u._id': 1,
audioNotifications: 1,
audioNotificationValue: 1,
- desktopNotificationDuration: 1,
desktopNotifications: 1,
mobilePushNotifications: 1,
emailNotifications: 1,
diff --git a/app/models/server/models/Users.js b/app/models/server/models/Users.js
index c9515a3b03e7..5f9374fac4a6 100644
--- a/app/models/server/models/Users.js
+++ b/app/models/server/models/Users.js
@@ -124,10 +124,10 @@ export class Users extends Base {
return this.findOne(query);
}
- findOneOnlineAgentByUsername(username) {
+ findOneOnlineAgentByUsername(username, options) {
const query = queryStatusAgentOnline({ username });
- return this.findOne(query);
+ return this.findOne(query, options);
}
findOneOnlineAgentById(_id) {
diff --git a/app/models/server/raw/LivechatVisitors.js b/app/models/server/raw/LivechatVisitors.js
index 7ee3f02f2cca..8be6eccd5d49 100644
--- a/app/models/server/raw/LivechatVisitors.js
+++ b/app/models/server/raw/LivechatVisitors.js
@@ -1,3 +1,5 @@
+import s from 'underscore.string';
+
import { BaseRaw } from './BaseRaw';
export class LivechatVisitorsRaw extends BaseRaw {
@@ -12,4 +14,43 @@ export class LivechatVisitorsRaw extends BaseRaw {
return this.find(query, { fields: { _id: 1 } });
}
+
+ findByNameRegexWithExceptionsAndConditions(searchTerm, exceptions = [], conditions = {}, options = {}) {
+ if (!Array.isArray(exceptions)) {
+ exceptions = [exceptions];
+ }
+
+ const nameRegex = new RegExp(`^${ s.escapeRegExp(searchTerm).trim() }`, 'i');
+
+ const match = {
+ $match: {
+ name: nameRegex,
+ _id: {
+ $nin: exceptions,
+ },
+ ...conditions,
+ },
+ };
+
+ const { fields, sort, offset, count } = options;
+ const project = {
+ $project: {
+ custom_name: { $concat: ['$username', ' - ', '$name'] },
+ ...fields,
+ },
+ };
+
+ const order = { $sort: sort || { name: 1 } };
+ const params = [match, project, order];
+
+ if (offset) {
+ params.push({ $skip: offset });
+ }
+
+ if (count) {
+ params.push({ $limit: count });
+ }
+
+ return this.col.aggregate(params);
+ }
}
diff --git a/app/oauth2-server-config/server/oauth/oauth2-server.js b/app/oauth2-server-config/server/oauth/oauth2-server.js
index ff813497bc5a..f1c51982c760 100644
--- a/app/oauth2-server-config/server/oauth/oauth2-server.js
+++ b/app/oauth2-server-config/server/oauth/oauth2-server.js
@@ -3,7 +3,7 @@ import { WebApp } from 'meteor/webapp';
import { OAuth2Server } from 'meteor/rocketchat:oauth2-server';
import { OAuthApps, Users } from '../../../models';
-import { API } from '../../../api';
+import { API } from '../../../api/server';
const oauth2server = new OAuth2Server({
accessTokensCollectionName: 'rocketchat_oauth_access_tokens',
diff --git a/app/push-notifications/client/views/pushNotificationsFlexTab.html b/app/push-notifications/client/views/pushNotificationsFlexTab.html
index a32f1b6e9713..f1e3a28162ad 100644
--- a/app/push-notifications/client/views/pushNotificationsFlexTab.html
+++ b/app/push-notifications/client/views/pushNotificationsFlexTab.html
@@ -101,20 +101,6 @@
{{/with}}
-
-
{{_ "Notifications_Duration"}}:
- {{# with "desktopNotificationDuration"}}
-
- {{#if desktopNotificationDuration }}
- {{_ "Duration"}} {{desktopNotificationDuration}} {{_ "seconds"}}
- {{else}}
- {{_ "Use_User_Preferences_or_Global_Settings"}}
- {{/if}}
-
- {{> icon block="rc-user-info__config-content-icon" icon="arrow-down"}}
-
- {{/with}}
-
diff --git a/app/push-notifications/client/views/pushNotificationsFlexTab.js b/app/push-notifications/client/views/pushNotificationsFlexTab.js
index a71609be4145..43f026f2aaa9 100644
--- a/app/push-notifications/client/views/pushNotificationsFlexTab.js
+++ b/app/push-notifications/client/views/pushNotificationsFlexTab.js
@@ -74,9 +74,6 @@ Template.pushNotificationsFlexTab.helpers({
emailNotifications() {
return Template.instance().form.emailNotifications.get();
},
- desktopNotificationDuration() {
- return Template.instance().form.desktopNotificationDuration.get();
- },
subValue(field) {
const { form } = Template.instance();
if (form[field]) {
@@ -131,7 +128,6 @@ Template.pushNotificationsFlexTab.onCreated(function() {
desktopNotifications: 1,
mobilePushNotifications: 1,
emailNotifications: 1,
- desktopNotificationDuration: 1,
audioNotificationValue: 1,
muteGroupMentions: 1,
},
@@ -144,7 +140,6 @@ Template.pushNotificationsFlexTab.onCreated(function() {
desktopNotifications = 'default',
mobilePushNotifications = 'default',
emailNotifications = 'default',
- desktopNotificationDuration = 0,
muteGroupMentions = false,
} = sub;
@@ -157,7 +152,6 @@ Template.pushNotificationsFlexTab.onCreated(function() {
desktopNotifications: new ReactiveVar(desktopNotifications),
mobilePushNotifications: new ReactiveVar(mobilePushNotifications),
emailNotifications: new ReactiveVar(emailNotifications),
- desktopNotificationDuration: new ReactiveVar(desktopNotificationDuration),
audioNotificationValue: new ReactiveVar(audioNotificationValue),
muteGroupMentions: new ReactiveVar(muteGroupMentions),
};
@@ -169,7 +163,6 @@ Template.pushNotificationsFlexTab.onCreated(function() {
desktopNotifications: new ReactiveVar(desktopNotifications),
mobilePushNotifications: new ReactiveVar(mobilePushNotifications),
emailNotifications: new ReactiveVar(emailNotifications),
- desktopNotificationDuration: new ReactiveVar(desktopNotificationDuration),
audioNotificationValue: new ReactiveVar(audioNotificationValue),
muteGroupMentions: new ReactiveVar(muteGroupMentions),
};
@@ -186,9 +179,6 @@ Template.pushNotificationsFlexTab.onCreated(function() {
}
const rid = Session.get('openedRoom');
switch (field) {
- case 'desktopNotificationDuration':
- await call('saveDesktopNotificationDuration', rid, value);
- break;
case 'audioNotificationValue':
await call('saveAudioNotificationValue', rid, value.split(' ')[0]);
break;
@@ -262,44 +252,6 @@ Template.pushNotificationsFlexTab.events({
...audioAssetsArray,
];
break;
- case 'desktopNotificationDuration':
- options = [{
- id: 'desktopNotificationDuration',
- name: 'desktopNotificationDuration',
- label: 'Default',
- value: 0,
- },
- {
- id: 'desktopNotificationDuration1s',
- name: 'desktopNotificationDuration',
- label: `1 ${ t('seconds') }`,
- value: 1,
- },
- {
- id: 'desktopNotificationDuration2s',
- name: 'desktopNotificationDuration',
- label: `2 ${ t('seconds') }`,
- value: 2,
- },
- {
- id: 'desktopNotificationDuration3s',
- name: 'desktopNotificationDuration',
- label: `3 ${ t('seconds') }`,
- value: 3,
- },
- {
- id: 'desktopNotificationDuration4s',
- name: 'desktopNotificationDuration',
- label: `4 ${ t('seconds') }`,
- value: 4,
- },
- {
- id: 'desktopNotificationDuration5s',
- name: 'desktopNotificationDuration',
- label: `5 ${ t('seconds') }`,
- value: 5,
- }];
- break;
default:
options = [{
id: 'desktopNotificationsDefault',
@@ -331,7 +283,7 @@ Template.pushNotificationsFlexTab.events({
popoverClass: 'notifications-preferences',
template: 'pushNotificationsPopover',
data: {
- change: (value) => instance.form[key].set(key === 'desktopNotificationDuration' ? parseInt(value) : value),
+ change: (value) => instance.form[key].set(value),
value: instance.form[key].get(),
options,
},
diff --git a/app/push-notifications/server/methods/saveNotificationSettings.js b/app/push-notifications/server/methods/saveNotificationSettings.js
index 3ddd80299e60..faf879b7ed94 100644
--- a/app/push-notifications/server/methods/saveNotificationSettings.js
+++ b/app/push-notifications/server/methods/saveNotificationSettings.js
@@ -59,9 +59,6 @@ Meteor.methods({
muteGroupMentions: {
updateMethod: (subscription, value) => Subscriptions.updateMuteGroupMentions(subscription._id, value === '1'),
},
- desktopNotificationDuration: {
- updateMethod: (subscription, value) => Subscriptions.updateDesktopNotificationDurationById(subscription._id, value),
- },
audioNotificationValue: {
updateMethod: (subscription, value) => Subscriptions.updateAudioNotificationValueById(subscription._id, value),
},
@@ -96,13 +93,4 @@ Meteor.methods({
Subscriptions.updateAudioNotificationValueById(subscription._id, value);
return true;
},
-
- saveDesktopNotificationDuration(rid, value) {
- const subscription = Subscriptions.findOneByRoomIdAndUserId(rid, Meteor.userId());
- if (!subscription) {
- throw new Meteor.Error('error-invalid-subscription', 'Invalid subscription', { method: 'saveDesktopNotificationDuration' });
- }
- Subscriptions.updateDesktopNotificationDurationById(subscription._id, value);
- return true;
- },
});
diff --git a/app/search/server/events/events.js b/app/search/server/events/events.js
index 79e3c8aa8142..7f0f5032a1d7 100644
--- a/app/search/server/events/events.js
+++ b/app/search/server/events/events.js
@@ -24,10 +24,12 @@ const eventService = new EventService();
*/
callbacks.add('afterSaveMessage', function(m) {
eventService.promoteEvent('message.save', m._id, m);
+ return m;
}, callbacks.priority.MEDIUM, 'search-events');
callbacks.add('afterDeleteMessage', function(m) {
eventService.promoteEvent('message.delete', m._id);
+ return m;
}, callbacks.priority.MEDIUM, 'search-events-delete');
/**
diff --git a/app/theme/client/imports/components/header.css b/app/theme/client/imports/components/header.css
index d5b0a05cf493..3dfa4f441492 100644
--- a/app/theme/client/imports/components/header.css
+++ b/app/theme/client/imports/components/header.css
@@ -225,23 +225,23 @@
border-radius: var(--header-title-status-bullet-radius);
&--online {
- background-color: var(--status-online);
+ background-color: var(--rc-status-online);
}
&--away {
- background-color: var(--status-away);
+ background-color: var(--rc-status-away);
}
&--busy {
- background-color: var(--status-busy);
+ background-color: var(--rc-status-busy);
}
&--invisible {
- background-color: var(--status-invisible);
+ background-color: var(--rc-status-invisible);
}
&--offline {
- background-color: var(--status-invisible);
+ background-color: var(--rc-status-invisible);
}
}
}
diff --git a/app/theme/client/imports/components/main-content.css b/app/theme/client/imports/components/main-content.css
index bda5cf955e32..04f7cb05d970 100644
--- a/app/theme/client/imports/components/main-content.css
+++ b/app/theme/client/imports/components/main-content.css
@@ -15,18 +15,18 @@
.messages-container .room-icon {
&.online {
- color: var(--status-online);
+ color: var(--rc-status-online);
}
&.away {
- color: var(--status-away);
+ color: var(--rc-status-away);
}
&.busy {
- color: var(--status-busy);
+ color: var(--rc-status-busy);
}
&.offline {
- color: var(--status-invisible);
+ color: var(--rc-status-invisible);
}
}
diff --git a/app/theme/client/imports/components/memberlist.css b/app/theme/client/imports/components/memberlist.css
index 6d78136b841f..0da2ebec46e8 100644
--- a/app/theme/client/imports/components/memberlist.css
+++ b/app/theme/client/imports/components/memberlist.css
@@ -61,19 +61,19 @@
border-radius: var(--sidebar-item-user-status-radius);
&--online {
- background-color: var(--status-online);
+ background-color: var(--rc-status-online);
}
&--away {
- background-color: var(--status-away);
+ background-color: var(--rc-status-away);
}
&--busy {
- background-color: var(--status-busy);
+ background-color: var(--rc-status-busy);
}
&--offline {
- background-color: var(--status-invisible-sidebar);
+ background-color: var(--rc-status-invisible-sidebar);
}
}
diff --git a/app/theme/client/imports/components/popover.css b/app/theme/client/imports/components/popover.css
index 551c02e9dd40..807bdd7c88d2 100644
--- a/app/theme/client/imports/components/popover.css
+++ b/app/theme/client/imports/components/popover.css
@@ -127,25 +127,25 @@
&--online {
& .rc-popover__icon {
- color: var(--status-online);
+ color: var(--rc-status-online);
}
}
&--away {
& .rc-popover__icon {
- color: var(--status-away);
+ color: var(--rc-status-away);
}
}
&--busy {
& .rc-popover__icon {
- color: var(--status-busy);
+ color: var(--rc-status-busy);
}
}
&--offline {
& .rc-popover__icon {
- color: var(--status-invisible);
+ color: var(--rc-status-invisible);
}
}
}
diff --git a/app/theme/client/imports/components/sidebar/rooms-list.css b/app/theme/client/imports/components/sidebar/rooms-list.css
index b6625f0abe94..ca912108bad1 100644
--- a/app/theme/client/imports/components/sidebar/rooms-list.css
+++ b/app/theme/client/imports/components/sidebar/rooms-list.css
@@ -44,7 +44,7 @@
&__toolbar-search {
position: absolute;
- z-index: 1;
+ z-index: 10;
left: 10px;
diff --git a/app/theme/client/imports/components/sidebar/sidebar-header.css b/app/theme/client/imports/components/sidebar/sidebar-header.css
index de54cba02ae4..fc963c5eda02 100644
--- a/app/theme/client/imports/components/sidebar/sidebar-header.css
+++ b/app/theme/client/imports/components/sidebar/sidebar-header.css
@@ -40,23 +40,23 @@
border-radius: var(--sidebar-account-status-bullet-radius);
&--online {
- background-color: var(--status-online);
+ background-color: var(--rc-status-online);
}
&--away {
- background-color: var(--status-away);
+ background-color: var(--rc-status-away);
}
&--busy {
- background-color: var(--status-busy);
+ background-color: var(--rc-status-busy);
}
&--invisible {
- background-color: var(--status-invisible);
+ background-color: var(--rc-status-invisible);
}
&--offline {
- background-color: var(--status-invisible);
+ background-color: var(--rc-status-invisible);
}
}
}
@@ -109,25 +109,25 @@
& .rc-popover__item {
&--online {
& .rc-icon {
- color: var(--status-online);
+ color: var(--rc-status-online);
}
}
&--away {
& .rc-icon {
- color: var(--status-away);
+ color: var(--rc-status-away);
}
}
&--busy {
& .rc-icon {
- color: var(--status-busy);
+ color: var(--rc-status-busy);
}
}
&--offline {
& .rc-icon {
- color: var(--status-invisible);
+ color: var(--rc-status-invisible);
}
}
}
diff --git a/app/theme/client/imports/components/sidebar/sidebar-item.css b/app/theme/client/imports/components/sidebar/sidebar-item.css
index 125ed7283f45..17771d9a3081 100644
--- a/app/theme/client/imports/components/sidebar/sidebar-item.css
+++ b/app/theme/client/imports/components/sidebar/sidebar-item.css
@@ -137,15 +137,15 @@
&-status {
&--online {
- color: var(--status-online);
+ color: var(--rc-status-online);
}
&--away {
- color: var(--status-away);
+ color: var(--rc-status-away);
}
&--busy {
- color: var(--status-busy);
+ color: var(--rc-status-busy);
}
}
}
@@ -175,19 +175,19 @@
border-radius: var(--sidebar-item-user-status-radius);
&--online {
- background-color: var(--status-online);
+ background-color: var(--rc-status-online);
}
&--away {
- background-color: var(--status-away);
+ background-color: var(--rc-status-away);
}
&--busy {
- background-color: var(--status-busy);
+ background-color: var(--rc-status-busy);
}
&--offline {
- background-color: var(--status-invisible-sidebar);
+ background-color: var(--rc-status-invisible-sidebar);
}
}
diff --git a/app/theme/client/imports/components/sidebar/sidebar.css b/app/theme/client/imports/components/sidebar/sidebar.css
index 033835d8f094..d7896386bce0 100644
--- a/app/theme/client/imports/components/sidebar/sidebar.css
+++ b/app/theme/client/imports/components/sidebar/sidebar.css
@@ -63,10 +63,52 @@
}
}
- & .unread-rooms {
- padding: calc(var(--sidebar-small-default-padding) - 8px);
+ & .wrapper-unread {
+ position: relative;
+ z-index: 2;
- text-align: center;
+ & .unread-rooms {
+ position: absolute;
+ left: 50%;
+
+ overflow: hidden;
+
+ min-width: 120px;
+ max-width: 100%;
+
+ padding: 8px var(--sidebar-small-default-padding);
+
+ -webkit-transform: translateX(-50%);
+ transform: translateX(-50%);
+
+ animation: fade 0.3s;
+
+ text-align: center;
+
+ white-space: nowrap;
+
+ text-overflow: ellipsis;
+
+ border-radius: 25px;
+
+ &.bottom-unread-rooms {
+ bottom: 0;
+ }
+
+ &.top-unread-rooms {
+ top: 0;
+ }
+ }
+ }
+}
+
+@keyframes fade {
+ from {
+ opacity: 0;
+ }
+
+ to {
+ opacity: 1;
}
}
diff --git a/app/theme/client/imports/general/base_old.css b/app/theme/client/imports/general/base_old.css
index d2b2ace62a9b..c5be5ae73bde 100644
--- a/app/theme/client/imports/general/base_old.css
+++ b/app/theme/client/imports/general/base_old.css
@@ -1314,6 +1314,15 @@
border-bottom: none;
}
+ & .add-token {
+ display: flex;
+
+ & .rc-select {
+ width: 40%;
+ margin: 0 0 0 10px;
+ }
+ }
+
&:first-child {
padding-top: 0;
}
@@ -1958,7 +1967,6 @@
width: 10px;
height: 10px;
- border-width: 1px;
border-radius: 10px;
}
@@ -3302,9 +3310,15 @@
}
.rc-old .oauth-login {
+ display: flex;
+
margin-bottom: 16px;
+
margin-left: -4px;
- flex-wrap: wrap;
+
+ flex-flow: row wrap;
+
+ justify-content: space-around;
& h3 {
margin-top: 0;
@@ -3317,10 +3331,17 @@
font-weight: 300;
}
+ button {
+ margin: 0 5px;
+
+ flex-grow: 1;
+ }
+
& .button {
margin-bottom: 4px;
font-size: 18px;
+
line-height: 22px;
flex-grow: 1;
@@ -4147,6 +4168,15 @@
display: none;
}
}
+
+ .add-token {
+ display: block !important;
+
+ & .rc-select {
+ width: auto !important;
+ margin: 10px 0 !important;
+ }
+ }
}
@media (width <= 500px) {
diff --git a/app/theme/client/imports/general/theme_old.css b/app/theme/client/imports/general/theme_old.css
index 657ebb4f3c41..268ea9cdc630 100644
--- a/app/theme/client/imports/general/theme_old.css
+++ b/app/theme/client/imports/general/theme_old.css
@@ -411,19 +411,18 @@ textarea {
}
i.status-online {
- color: var(--status-online);
+ color: var(--rc-status-online);
}
.status-bg-online {
- background-color: var(--status-online);
+ background-color: var(--rc-status-online);
}
.account-box .status-online .thumb::after,
.account-box .status.online::after,
.popup-user-status-online,
.status-online::after {
- border-color: var(--status-online-darken-10);
- background-color: var(--status-online);
+ background-color: var(--rc-status-online);
}
.account-box .status-offline .thumb::after,
@@ -432,11 +431,11 @@ i.status-online {
}
i.status-away {
- color: var(--status-away);
+ color: var(--rc-status-away);
}
.status-bg-away {
- background-color: var(--status-away);
+ background-color: var(--rc-status-away);
}
.account-box .status-away .thumb::after,
@@ -444,38 +443,35 @@ i.status-away {
.popup-user-status-away,
.status-away::after,
.status-pending::after {
- border-color: var(--status-away-darken-10);
- background-color: var(--status-away);
+ background-color: var(--rc-status-away);
}
i.status-busy {
- color: var(--status-busy);
+ color: var(--rc-status-busy);
}
.status-bg-busy {
- background-color: var(--status-busy);
+ background-color: var(--rc-status-busy);
}
.account-box .status-busy .thumb::after,
.account-box .status.busy::after,
.popup-user-status-busy,
.status-busy::after {
- border-color: var(--status-busy-darken-10);
- background-color: var(--status-busy);
+ background-color: var(--rc-status-busy);
}
i.status-offline {
- color: var(--status-offline);
+ color: var(--rc-status-offline);
}
.status-bg-offline {
- background-color: var(--status-offline);
+ background-color: var(--rc-status-offline);
}
.popup-user-status-offline,
.status-offline::after {
- border-color: var(--status-offline-darken-10);
- background-color: var(--status-offline);
+ background-color: var(--rc-status-offline);
}
.alert-warning {
diff --git a/app/theme/client/imports/general/variables.css b/app/theme/client/imports/general/variables.css
index d44f21266256..fb317250592e 100644
--- a/app/theme/client/imports/general/variables.css
+++ b/app/theme/client/imports/general/variables.css
@@ -17,7 +17,7 @@
--color-purple: #861da8;
--color-red: #f5455c;
--color-dark-red: #e0364d;
- --color-orange: #f59547;
+ --color-orange: #f38c39;
--color-yellow: #ffd21f;
--color-dark-yellow: #f6c502;
--color-green: #2de0a5;
@@ -40,7 +40,7 @@
/* #region colors Colors */
--rc-color-error: var(--color-red);
--rc-color-error-light: #e1364c;
- --rc-color-alert: var(--color-yellow);
+ --rc-color-alert: var(--color-orange);
--rc-color-alert-light: var(--color-dark-yellow);
--rc-color-success: var(--color-green);
--rc-color-success-light: #25d198;
@@ -75,6 +75,7 @@
--component-color: #f2f3f5;
--pending-color: #fcb316;
--error-color: #bc2031;
+ --success-color: #2de0a5;
--selection-color: #02acec;
--attention-color: #9c27b0;
@@ -86,7 +87,6 @@
--link-font-color: var(--primary-action-color);
--info-font-color: var(--secondary-font-color);
--custom-scrollbar-color: var(--transparent-darker);
- --status-offline: var(--transparent-darker);
/* #endregion */
@@ -108,11 +108,12 @@
--flex-tab-webrtc-2-width: 850px;
--border: 2px;
--border-radius: 2px;
- --status-online: var(--rc-color-success);
- --status-away: var(--rc-color-alert);
- --status-busy: var(--rc-color-error);
- --status-invisible: var(--color-gray-medium);
- --status-invisible-sidebar: var(--rc-color-primary-darkest);
+ --rc-status-online: var(--rc-color-success);
+ --rc-status-away: var(--rc-color-alert);
+ --rc-status-busy: var(--rc-color-error);
+ --rc-status-invisible: var(--color-gray-medium);
+ --rc-status-offline: var(--transparent-darker);
+ --rc-status-invisible-sidebar: var(--rc-color-primary-darkest);
--default-padding: 1.5rem;
--default-small-padding: 1rem;
--status-bullet-size: 10px;
diff --git a/app/threads/client/flextab/threads.js b/app/threads/client/flextab/threads.js
index a4fa8d9faee1..032dde9b302a 100644
--- a/app/threads/client/flextab/threads.js
+++ b/app/threads/client/flextab/threads.js
@@ -27,7 +27,7 @@ Template.threads.events({
return false;
},
'scroll .js-scroll-threads': _.throttle(({ currentTarget: e }, { incLimit }) => {
- if (e.offsetHeight + e.scrollTop <= e.scrollHeight - 50) {
+ if (e.offsetHeight + e.scrollTop >= e.scrollHeight - 50) {
incLimit && incLimit();
}
}, 500),
diff --git a/app/threads/client/threads.css b/app/threads/client/threads.css
index 5d7bf76c4c8c..fe8ec38b40c6 100644
--- a/app/threads/client/threads.css
+++ b/app/threads/client/threads.css
@@ -63,7 +63,7 @@
left: 40px;
width: 20px;
- height: 20px;
+ height: 16px;
color: var(--rc-color-alert-message-primary);
}
@@ -82,7 +82,7 @@
display: flex;
- margin: calc((var(--default-padding) /2) - 6px) 0 7px 0;
+ margin: calc((var(--default-padding) /2) - 2px) 0 2px 0;
align-items: center;
}
diff --git a/app/threads/server/hooks/aftersavemessage.js b/app/threads/server/hooks/aftersavemessage.js
index a9348619caeb..8de27cafba64 100644
--- a/app/threads/server/hooks/aftersavemessage.js
+++ b/app/threads/server/hooks/aftersavemessage.js
@@ -38,12 +38,12 @@ const notification = (message, room, replies) => {
const processThreads = (message, room) => {
if (!message.tmid) {
- return;
+ return message;
}
const parentMessage = Messages.findOneById(message.tmid);
if (!parentMessage) {
- return;
+ return message;
}
const replies = [
@@ -53,6 +53,8 @@ const processThreads = (message, room) => {
notifyUsersOnReply(message, replies, room);
metaData(message, parentMessage);
notification(message, room, replies);
+
+ return message;
};
Meteor.startup(function() {
diff --git a/app/ui-account/client/accountPreferences.html b/app/ui-account/client/accountPreferences.html
index ade63fece090..0da55e54fdf9 100644
--- a/app/ui-account/client/accountPreferences.html
+++ b/app/ui-account/client/accountPreferences.html
@@ -92,16 +92,6 @@
{{_ "Notifications"}}
{{/if}}
-