From 00f072a0936c41cb33293f32e29a62a0ec482352 Mon Sep 17 00:00:00 2001 From: Renato Becker Date: Wed, 20 Nov 2019 18:29:25 -0300 Subject: [PATCH] [NEW] Add a new stream to emit and listen room data events (#15770) --- app/livechat/client/route.js | 2 +- app/livechat/client/ui.js | 11 +++++ .../client/views/{index.js => admin.js} | 11 ----- .../client/views/app/tabbar/visitorInfo.js | 18 ++++++--- app/livechat/client/views/regular.js | 11 +++++ app/livechat/server/roomType.js | 4 ++ app/ui-utils/client/lib/RoomManager.js | 3 ++ app/utils/lib/RoomTypeConfig.js | 4 ++ app/utils/stream/constants.js | 1 + server/main.js | 1 + server/publications/room/emitter.js | 29 ++++++++------ server/stream/rooms/index.js | 40 +++++++++++++++++++ 12 files changed, 105 insertions(+), 30 deletions(-) rename app/livechat/client/views/{index.js => admin.js} (68%) create mode 100644 app/livechat/client/views/regular.js create mode 100644 app/utils/stream/constants.js create mode 100644 server/stream/rooms/index.js diff --git a/app/livechat/client/route.js b/app/livechat/client/route.js index 4fbb1c76dde..1097aea9983 100644 --- a/app/livechat/client/route.js +++ b/app/livechat/client/route.js @@ -7,7 +7,7 @@ export const livechatManagerRoutes = FlowRouter.group({ name: 'livechat-manager', }); -const load = () => import('./views'); +const load = () => import('./views/admin'); AccountBox.addRoute({ name: 'livechat-dashboard', diff --git a/app/livechat/client/ui.js b/app/livechat/client/ui.js index 4ea3d4ee5df..f5e58deae64 100644 --- a/app/livechat/client/ui.js +++ b/app/livechat/client/ui.js @@ -1,7 +1,18 @@ +import { Tracker } from 'meteor/tracker'; + import { settings } from '../../settings'; import { hasAllPermission } from '../../authorization'; import { AccountBox, TabBar, MessageTypes } from '../../ui-utils'; +Tracker.autorun((c) => { + // import livechat tabbar templates right away if livechat enabled + if (!settings.get('Livechat_enabled')) { + return; + } + import('./views/regular'); + c.stop(); +}); + AccountBox.addItem({ name: 'Livechat', icon: 'livechat', diff --git a/app/livechat/client/views/index.js b/app/livechat/client/views/admin.js similarity index 68% rename from app/livechat/client/views/index.js rename to app/livechat/client/views/admin.js index d02c048d4db..592eb9fb759 100644 --- a/app/livechat/client/views/index.js +++ b/app/livechat/client/views/admin.js @@ -5,7 +5,6 @@ import './app/analytics/livechatRealTimeMonitoring'; import './app/livechatAgents'; import './app/livechatAppearance'; import './app/livechatDashboard.html'; -import './app/livechatAutocompleteUser'; import './app/livechatCurrentChats'; import './app/livechatCustomFields'; import './app/livechatCustomFieldForm'; @@ -13,20 +12,10 @@ import './app/livechatDepartmentForm'; import './app/livechatDepartments'; import './app/livechatInstallation'; import './app/livechatOfficeHours'; -import './app/livechatQueue'; -import './app/livechatReadOnly'; import './app/livechatTriggers'; import './app/livechatTriggersForm'; import './app/livechatManagers'; -import './app/livechatNotSubscribed.html'; -import './app/livechatRoomTagSelector.html'; import './app/integrations/livechatIntegrationWebhook'; import './app/integrations/livechatIntegrationFacebook'; -import './app/tabbar/externalSearch'; -import './app/tabbar/visitorEdit'; -import './app/tabbar/visitorForward'; -import './app/tabbar/visitorInfo'; -import './app/tabbar/visitorHistory'; -import './app/tabbar/visitorNavigation'; import './app/triggers/livechatTriggerAction'; import './app/triggers/livechatTriggerCondition'; diff --git a/app/livechat/client/views/app/tabbar/visitorInfo.js b/app/livechat/client/views/app/tabbar/visitorInfo.js index 122c18325e8..d52373a34ec 100644 --- a/app/livechat/client/views/app/tabbar/visitorInfo.js +++ b/app/livechat/client/views/app/tabbar/visitorInfo.js @@ -16,6 +16,7 @@ import { t, handleError, roomTypes } from '../../../../../utils'; import { hasRole, hasPermission, hasAtLeastOnePermission } from '../../../../../authorization'; import './visitorInfo.html'; import { APIClient } from '../../../../../utils/client'; +import { RoomManager } from '../../../../../ui-utils/client'; const isSubscribedToRoom = () => { const data = Template.currentData(); @@ -280,6 +281,10 @@ Template.visitorInfo.onCreated(function() { this.department = new ReactiveVar({}); this.room = new ReactiveVar({}); + this.updateRoom = (room) => { + this.room.set(room); + }; + Meteor.call('livechat:getCustomFields', (err, customFields) => { if (customFields) { this.customFields.set(customFields); @@ -302,12 +307,8 @@ Template.visitorInfo.onCreated(function() { }; if (rid) { - this.autorun(() => { - const action = this.action.get(); - if (action === undefined) { - loadRoomData(rid); - } - }); + loadRoomData(rid); + RoomManager.roomStream.on(rid, this.updateRoom); } this.autorun(async () => { @@ -325,3 +326,8 @@ Template.visitorInfo.onCreated(function() { } }); }); + +Template.visitorInfo.onDestroyed(function() { + const { rid } = Template.currentData(); + RoomManager.roomStream.removeListener(rid, this.updateRoom); +}); diff --git a/app/livechat/client/views/regular.js b/app/livechat/client/views/regular.js new file mode 100644 index 00000000000..24a4f885672 --- /dev/null +++ b/app/livechat/client/views/regular.js @@ -0,0 +1,11 @@ +import './app/livechatAutocompleteUser'; +import './app/livechatQueue'; +import './app/livechatReadOnly'; +import './app/livechatNotSubscribed.html'; +import './app/livechatRoomTagSelector.html'; +import './app/tabbar/externalSearch'; +import './app/tabbar/visitorEdit'; +import './app/tabbar/visitorForward'; +import './app/tabbar/visitorHistory'; +import './app/tabbar/visitorInfo'; +import './app/tabbar/visitorNavigation'; diff --git a/app/livechat/server/roomType.js b/app/livechat/server/roomType.js index a592833b0b4..31d62c609b6 100644 --- a/app/livechat/server/roomType.js +++ b/app/livechat/server/roomType.js @@ -31,6 +31,10 @@ class LivechatRoomTypeServer extends LivechatRoomType { const { token } = message; return { token }; } + + isEmitAllowed() { + return true; + } } roomTypes.add(new LivechatRoomTypeServer()); diff --git a/app/ui-utils/client/lib/RoomManager.js b/app/ui-utils/client/lib/RoomManager.js index 30bcda015e9..a760d1e45de 100644 --- a/app/ui-utils/client/lib/RoomManager.js +++ b/app/ui-utils/client/lib/RoomManager.js @@ -15,6 +15,7 @@ import { Notifications } from '../../../notifications'; import { CachedChatRoom, ChatMessage, ChatSubscription, CachedChatSubscription } from '../../../models'; import { CachedCollectionManager } from '../../../ui-cached-collection'; import { getConfig } from '../config'; +import { ROOM_DATA_STREAM_OBSERVER } from '../../../utils/stream/constants'; import { call } from '..'; @@ -44,12 +45,14 @@ const onDeleteMessageBulkStream = ({ rid, ts, excludePinned, ignoreDiscussion, u export const RoomManager = new function() { const openedRooms = {}; const msgStream = new Meteor.Streamer('room-messages'); + const roomStream = new Meteor.Streamer(ROOM_DATA_STREAM_OBSERVER); const onlineUsers = new ReactiveVar({}); const Dep = new Tracker.Dependency(); const Cls = class { static initClass() { this.prototype.openedRooms = openedRooms; this.prototype.onlineUsers = onlineUsers; + this.prototype.roomStream = roomStream; this.prototype.computation = Tracker.autorun(() => { const ready = CachedChatRoom.ready.get() && mainReady.get(); if (ready !== true) { return; } diff --git a/app/utils/lib/RoomTypeConfig.js b/app/utils/lib/RoomTypeConfig.js index 0cd3c7ff4c2..ac0aa49d351 100644 --- a/app/utils/lib/RoomTypeConfig.js +++ b/app/utils/lib/RoomTypeConfig.js @@ -214,6 +214,10 @@ export class RoomTypeConfig { return false; } + isEmitAllowed() { + return false; + } + /** * Returns a text which can be used in generic UIs. * @param context The role of the text in the UI-Element diff --git a/app/utils/stream/constants.js b/app/utils/stream/constants.js new file mode 100644 index 00000000000..5ae53cc865b --- /dev/null +++ b/app/utils/stream/constants.js @@ -0,0 +1 @@ +export const ROOM_DATA_STREAM_OBSERVER = 'room-data-observer'; diff --git a/server/main.js b/server/main.js index 2d7e2e97270..b2e0bf60a06 100644 --- a/server/main.js +++ b/server/main.js @@ -80,4 +80,5 @@ import './publications/userChannels'; import './publications/userData'; import './routes/avatar'; import './stream/messages'; +import './stream/rooms'; import './stream/streamBroadcast'; diff --git a/server/publications/room/emitter.js b/server/publications/room/emitter.js index fb8c5be1c14..a9c3cf787a6 100644 --- a/server/publications/room/emitter.js +++ b/server/publications/room/emitter.js @@ -1,3 +1,4 @@ +import { emitRoomDataEvent } from '../../stream/rooms'; import { Rooms, Subscriptions } from '../../../app/models'; import { Notifications } from '../../../app/notifications'; @@ -21,17 +22,21 @@ Rooms.on('change', ({ clientAction, id, data }) => { break; } - if (data) { - if (clientAction === 'removed') { - getSubscriptions(clientAction, id).forEach(({ u }) => { - Notifications.notifyUserInThisInstance( - u._id, - 'rooms-changed', - clientAction, - data - ); - }); - } - Notifications.streamUser.__emit(id, clientAction, data); + if (!data) { + return; } + if (clientAction === 'removed') { + getSubscriptions(clientAction, id).forEach(({ u }) => { + Notifications.notifyUserInThisInstance( + u._id, + 'rooms-changed', + clientAction, + data + ); + }); + } + + Notifications.streamUser.__emit(id, clientAction, data); + + emitRoomDataEvent(id, data); }); diff --git a/server/stream/rooms/index.js b/server/stream/rooms/index.js new file mode 100644 index 00000000000..c601d0dc75f --- /dev/null +++ b/server/stream/rooms/index.js @@ -0,0 +1,40 @@ +import { Meteor } from 'meteor/meteor'; + +import { roomTypes } from '../../../app/utils'; +import { ROOM_DATA_STREAM_OBSERVER } from '../../../app/utils/stream/constants'; + +export const roomDataStream = new Meteor.Streamer(ROOM_DATA_STREAM_OBSERVER); + +const isEmitAllowed = (t) => roomTypes.getConfig(t).isEmitAllowed(); + +roomDataStream.allowWrite('none'); + +roomDataStream.allowRead(function(rid) { + try { + const room = Meteor.call('canAccessRoom', rid, this.userId); + + if (!room) { + return false; + } + + if (isEmitAllowed(room.t) === false) { + return false; + } + + return true; + } catch (error) { + return false; + } +}); + +export function emitRoomDataEvent(id, data) { + if (!data) { + return; + } + + if (isEmitAllowed(data.t) === false) { + return; + } + + roomDataStream.emit(id, data); +}