From b020b38f54e5592d7d7a337b9ba350bba6d5907a Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Tue, 28 Jan 2020 00:09:08 -0300 Subject: [PATCH 1/9] auto complete --- app/api/server/v1/users.js | 8 +- .../client/views/app/tabbar/agentInfo.html | 4 +- .../client/views/app/tabbar/agentInfo.js | 4 +- app/models/server/models/Users.js | 10 +++ app/ui-flextab/client/tabs/membersList.html | 2 +- app/ui-flextab/client/tabs/userInfo.html | 4 +- app/ui-flextab/client/tabs/userInfo.js | 5 +- .../client/popup/messagePopupUser.html | 2 +- app/ui-sidenav/client/index.js | 1 + app/ui-sidenav/client/sidebarItem.html | 2 +- app/ui-sidenav/client/sidebarItem.js | 3 + app/ui-sidenav/client/userPresence.html | 1 + app/ui-sidenav/client/userPresence.js | 76 +++++++++++++++++++ .../client/components/header/headerRoom.html | 4 +- app/ui/client/components/header/headerRoom.js | 4 +- imports/startup/client/listenActiveUsers.js | 4 +- 16 files changed, 119 insertions(+), 15 deletions(-) create mode 100644 app/ui-sidenav/client/userPresence.html create mode 100644 app/ui-sidenav/client/userPresence.js diff --git a/app/api/server/v1/users.js b/app/api/server/v1/users.js index 6ec98554deb5b..ccc2337d49b7c 100644 --- a/app/api/server/v1/users.js +++ b/app/api/server/v1/users.js @@ -647,7 +647,7 @@ API.v1.addRoute('users.removePersonalAccessToken', { authRequired: true }, { API.v1.addRoute('users.presence', { authRequired: true }, { get() { - const { from } = this.queryParams; + const { from, ids } = this.queryParams; const options = { fields: { @@ -659,6 +659,12 @@ API.v1.addRoute('users.presence', { authRequired: true }, { }, }; + if (ids) { + return API.v1.success({ + users: Users.findNotOfflineByIds(Array.isArray(ids) ? ids : ids.split(','), options).fetch(), + }); + } + if (from) { const ts = new Date(from); const diff = (Date.now() - ts) / 1000 / 60; diff --git a/app/livechat/client/views/app/tabbar/agentInfo.html b/app/livechat/client/views/app/tabbar/agentInfo.html index c3c1cac243845..5c33cc4341afe 100644 --- a/app/livechat/client/views/app/tabbar/agentInfo.html +++ b/app/livechat/client/views/app/tabbar/agentInfo.html @@ -14,10 +14,10 @@

{{name}}

{{#if username}}

@{{username}}

{{/if}} - + {{# userPresence uid=uid}} + {{/userPresence}}
diff --git a/app/livechat/client/views/app/tabbar/agentInfo.js b/app/livechat/client/views/app/tabbar/agentInfo.js index 8d5d04a63e7fe..6894586f32dac 100644 --- a/app/livechat/client/views/app/tabbar/agentInfo.js +++ b/app/livechat/client/views/app/tabbar/agentInfo.js @@ -20,7 +20,9 @@ Template.agentInfo.helpers({ const hasCustomFields = customFieldsTemplate() !== null; return (availableDepartments.length > 0 && hasPermission('add-livechat-department-agents')) || hasCustomFields; }, - + uid() { + return Template.instance().agent.get()._id; + }, name() { const agent = Template.instance().agent.get(); return agent && agent.name ? agent.name : TAPi18n.__('Unnamed'); diff --git a/app/models/server/models/Users.js b/app/models/server/models/Users.js index 2cf52c4e92382..c915e74d59d39 100644 --- a/app/models/server/models/Users.js +++ b/app/models/server/models/Users.js @@ -498,6 +498,16 @@ export class Users extends Base { return this.find(query, options); } + findNotOfflineByIds(users, options) { + const query = { + _id: { $in: users }, + status: { + $in: ['online', 'away', 'busy'], + }, + }; + return this.find(query, options); + } + findUsersNotOffline(options) { const query = { username: { diff --git a/app/ui-flextab/client/tabs/membersList.html b/app/ui-flextab/client/tabs/membersList.html index 62cbfdc3d186b..ad92bc15ff59c 100644 --- a/app/ui-flextab/client/tabs/membersList.html +++ b/app/ui-flextab/client/tabs/membersList.html @@ -36,7 +36,7 @@
  • {{> avatar username=user.username}}
    -
    + {{# userPresence uid=user._id}}
    {{/userPresence}} {{ignored}} {{displayName}} {{utcOffset}}
    {{> icon user=. block="rc-member-list__menu js-more" icon="menu" }} diff --git a/app/ui-flextab/client/tabs/userInfo.html b/app/ui-flextab/client/tabs/userInfo.html index d097c0796a0d5..c57a711be264a 100644 --- a/app/ui-flextab/client/tabs/userInfo.html +++ b/app/ui-flextab/client/tabs/userInfo.html @@ -31,10 +31,10 @@

    {{_ "User_Info"}}

  • {{name}}

    {{#if username}}

    @{{username}}

    {{/if}} - + {{# userPresence uid=uid}} + {{/userPresence}}
    diff --git a/app/ui-flextab/client/tabs/userInfo.js b/app/ui-flextab/client/tabs/userInfo.js index b9afcdc27365b..c03fd2a297e97 100644 --- a/app/ui-flextab/client/tabs/userInfo.js +++ b/app/ui-flextab/client/tabs/userInfo.js @@ -68,7 +68,10 @@ Template.userInfo.helpers({ } return customFields; }, - + uid() { + const user = Template.instance().user.get(); + return user._id; + }, name() { const user = Template.instance().user.get(); return user && user.name ? user.name : TAPi18n.__('Unnamed'); diff --git a/app/ui-message/client/popup/messagePopupUser.html b/app/ui-message/client/popup/messagePopupUser.html index c90a04375d3de..1d93b75756760 100644 --- a/app/ui-message/client/popup/messagePopupUser.html +++ b/app/ui-message/client/popup/messagePopupUser.html @@ -1,6 +1,6 @@ diff --git a/app/ui-sidenav/client/sidebarItem.js b/app/ui-sidenav/client/sidebarItem.js index 5d7f0175d9511..6e4a6a11a0a11 100644 --- a/app/ui-sidenav/client/sidebarItem.js +++ b/app/ui-sidenav/client/sidebarItem.js @@ -201,6 +201,9 @@ Template.sidebarItem.events({ }); Template.sidebarItemIcon.helpers({ + uid() { + return this.rid.replace(this.u._id, ''); + }, isRoom() { return this.rid || this._id; }, diff --git a/app/ui-sidenav/client/userPresence.html b/app/ui-sidenav/client/userPresence.html new file mode 100644 index 0000000000000..28bfe5bc76511 --- /dev/null +++ b/app/ui-sidenav/client/userPresence.html @@ -0,0 +1 @@ + diff --git a/app/ui-sidenav/client/userPresence.js b/app/ui-sidenav/client/userPresence.js new file mode 100644 index 0000000000000..f182b0853dd28 --- /dev/null +++ b/app/ui-sidenav/client/userPresence.js @@ -0,0 +1,76 @@ +// import { Meteor } from 'meteor/meteor'; +// import { ReactiveVar } from 'meteor/reactive-var'; +import { Template } from 'meteor/templating'; +import _ from 'underscore'; +import mem from 'mem'; + +import { APIClient } from '../../utils/client'; +import { saveUser } from '../../../imports/startup/client/listenActiveUsers'; + + +import './userPresence.html'; + +const data = new Map(); +const promises = new Map(); +const pending = new Map(); + +const getAll = _.debounce(async function getAll() { + const ids = Array.from(pending.keys()); + + if (ids.length === 0) { + return; + } + + const params = { + ids, + }; + + try { + const { + users, + } = await APIClient.v1.get('users.presence', params); + + users.forEach((user) => { + saveUser(user); + }); + + ids.forEach((id) => { + const { resolve } = promises.get(id); + resolve(); + }); + } catch (e) { + ids.forEach((id) => { + const { reject } = promises.get(id); + reject(); + }); + } +}, 1000); + + +const get = mem(function get(id) { + console.log(id); + const promise = pending.get(id) || new Promise((resolve, reject) => { + promises.set(id, { resolve, reject }); + }); + pending.set(id, promise); + return promise; +}); + +const options = { + threshold: 0.1, +}; + +const observer = new IntersectionObserver(function(entries, observer) { + entries.filter(({ isIntersecting }) => isIntersecting).forEach(async (entry) => { + const { uid } = data.get(entry.target); + await get(uid); + pending.delete(uid); + observer.unobserve(entry.target); + }); + getAll(); +}, options); + +Template.userPresence.onRendered(function() { + data.set(this.firstNode, this.data); + observer.observe(this.firstNode); +}); diff --git a/app/ui/client/components/header/headerRoom.html b/app/ui/client/components/header/headerRoom.html index 7cd0b5a2e1bb1..456547049feda 100644 --- a/app/ui/client/components/header/headerRoom.html +++ b/app/ui/client/components/header/headerRoom.html @@ -42,10 +42,10 @@ {{/unless}} {{#if isDirect}} - + {{# userPresence uid=uid}}
    {{userStatusText}}
    -
    +
    {{/userPresence}} {{else}} {{#if roomTopic}}{{{roomTopic}}}{{/if}} {{/if}} diff --git a/app/ui/client/components/header/headerRoom.js b/app/ui/client/components/header/headerRoom.js index 28f69ab444a0e..52f5360d7f5cd 100644 --- a/app/ui/client/components/header/headerRoom.js +++ b/app/ui/client/components/header/headerRoom.js @@ -32,7 +32,9 @@ Template.headerRoom.helpers({ isToggleFavoriteButtonChecked: () => Template.instance().state.get('favorite'), toggleFavoriteButtonIconLabel: () => (Template.instance().state.get('favorite') ? t('Unfavorite') : t('Favorite')), toggleFavoriteButtonIcon: () => (Template.instance().state.get('favorite') ? 'star-filled' : 'star'), - + uid() { + return this._id.replace(Meteor.userId(), ''); + }, back() { return Template.instance().data.back; }, diff --git a/imports/startup/client/listenActiveUsers.js b/imports/startup/client/listenActiveUsers.js index a559034926af2..f0352100b9674 100644 --- a/imports/startup/client/listenActiveUsers.js +++ b/imports/startup/client/listenActiveUsers.js @@ -13,7 +13,7 @@ const STATUS_MAP = [ 'busy', ]; -const saveUser = (user, force = false) => { +export const saveUser = (user, force = false) => { // do not update my own user, my user's status will come from a subscription if (user._id === Meteor.userId()) { return; @@ -80,7 +80,7 @@ Tracker.autorun(() => { lastStatusChange = null; - getUsersPresence(wasConnected); + // getUsersPresence(wasConnected); wasConnected = true; }); From 873f72ac061af586e5af1471fd667f10c30ac56f Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 29 Jan 2020 10:08:21 -0300 Subject: [PATCH 2/9] handle reconnect --- app/ui-sidenav/client/userPresence.js | 15 ++++++++++++--- imports/startup/client/listenActiveUsers.js | 20 ++++++++++---------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/app/ui-sidenav/client/userPresence.js b/app/ui-sidenav/client/userPresence.js index f182b0853dd28..d6e215b9c3714 100644 --- a/app/ui-sidenav/client/userPresence.js +++ b/app/ui-sidenav/client/userPresence.js @@ -1,6 +1,7 @@ -// import { Meteor } from 'meteor/meteor'; +import { Meteor } from 'meteor/meteor'; // import { ReactiveVar } from 'meteor/reactive-var'; import { Template } from 'meteor/templating'; +import { Tracker } from 'meteor/tracker'; import _ from 'underscore'; import mem from 'mem'; @@ -56,16 +57,24 @@ const get = mem(function get(id) { return promise; }); + +Tracker.autorun(() => { + if (!Meteor.userId() || !Meteor.status().connected) { + return; + } + + mem.clear(get); +}); + const options = { threshold: 0.1, }; -const observer = new IntersectionObserver(function(entries, observer) { +const observer = new IntersectionObserver(function(entries) { entries.filter(({ isIntersecting }) => isIntersecting).forEach(async (entry) => { const { uid } = data.get(entry.target); await get(uid); pending.delete(uid); - observer.unobserve(entry.target); }); getAll(); }, options); diff --git a/imports/startup/client/listenActiveUsers.js b/imports/startup/client/listenActiveUsers.js index f0352100b9674..96246ea53d836 100644 --- a/imports/startup/client/listenActiveUsers.js +++ b/imports/startup/client/listenActiveUsers.js @@ -1,5 +1,5 @@ import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; +// import { Tracker } from 'meteor/tracker'; import { debounce } from 'underscore'; import { Notifications } from '../../../app/notifications/client'; @@ -72,18 +72,18 @@ const getUsersPresence = debounce(async (isConnected) => { } }, 1000); -let wasConnected = false; -Tracker.autorun(() => { - if (!Meteor.userId() || !Meteor.status().connected) { - return; - } +// let wasConnected = false; +// Tracker.autorun(() => { +// if (!Meteor.userId() || !Meteor.status().connected) { +// return; +// } - lastStatusChange = null; +// lastStatusChange = null; - // getUsersPresence(wasConnected); +// // getUsersPresence(wasConnected); - wasConnected = true; -}); +// // wasConnected = true; +// }); Meteor.startup(function() { Notifications.onLogged('user-status', ([_id, username, status, statusText]) => { From 005dfe8db22ce63fa579c4d2e2c01d9296d147f3 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Mon, 3 Feb 2020 19:47:32 -0300 Subject: [PATCH 3/9] Update app/api/server/v1/users.js Co-Authored-By: Diego Sampaio --- app/api/server/v1/users.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/api/server/v1/users.js b/app/api/server/v1/users.js index ccc2337d49b7c..e1239e1c3b389 100644 --- a/app/api/server/v1/users.js +++ b/app/api/server/v1/users.js @@ -662,6 +662,7 @@ API.v1.addRoute('users.presence', { authRequired: true }, { if (ids) { return API.v1.success({ users: Users.findNotOfflineByIds(Array.isArray(ids) ? ids : ids.split(','), options).fetch(), + full: false }); } From af945d7e6335324a4e99cfb4203f07ee405d0458 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Mon, 3 Feb 2020 19:47:45 -0300 Subject: [PATCH 4/9] Update imports/startup/client/listenActiveUsers.js Co-Authored-By: Diego Sampaio --- imports/startup/client/listenActiveUsers.js | 1 - 1 file changed, 1 deletion(-) diff --git a/imports/startup/client/listenActiveUsers.js b/imports/startup/client/listenActiveUsers.js index 96246ea53d836..004d1464fe2b3 100644 --- a/imports/startup/client/listenActiveUsers.js +++ b/imports/startup/client/listenActiveUsers.js @@ -1,5 +1,4 @@ import { Meteor } from 'meteor/meteor'; -// import { Tracker } from 'meteor/tracker'; import { debounce } from 'underscore'; import { Notifications } from '../../../app/notifications/client'; From 0cbfe09bd5b85bc3ac26bed1a5a53f40fa37b454 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Mon, 3 Feb 2020 19:49:24 -0300 Subject: [PATCH 5/9] fix review --- imports/startup/client/listenActiveUsers.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/imports/startup/client/listenActiveUsers.js b/imports/startup/client/listenActiveUsers.js index 004d1464fe2b3..105be406a228d 100644 --- a/imports/startup/client/listenActiveUsers.js +++ b/imports/startup/client/listenActiveUsers.js @@ -71,19 +71,6 @@ const getUsersPresence = debounce(async (isConnected) => { } }, 1000); -// let wasConnected = false; -// Tracker.autorun(() => { -// if (!Meteor.userId() || !Meteor.status().connected) { -// return; -// } - -// lastStatusChange = null; - -// // getUsersPresence(wasConnected); - -// // wasConnected = true; -// }); - Meteor.startup(function() { Notifications.onLogged('user-status', ([_id, username, status, statusText]) => { // only set after first request completed From d6f7d74b755037657d09ac87a0b53530e67f006a Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Mon, 3 Feb 2020 20:05:29 -0300 Subject: [PATCH 6/9] lint --- app/api/server/v1/users.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/server/v1/users.js b/app/api/server/v1/users.js index e1239e1c3b389..d0115d51fb0c2 100644 --- a/app/api/server/v1/users.js +++ b/app/api/server/v1/users.js @@ -662,7 +662,7 @@ API.v1.addRoute('users.presence', { authRequired: true }, { if (ids) { return API.v1.success({ users: Users.findNotOfflineByIds(Array.isArray(ids) ? ids : ids.split(','), options).fetch(), - full: false + full: false, }); } From 58e5470e5e7e963fab9939c0fe25d01f391faf9f Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Mon, 3 Feb 2020 23:40:29 -0300 Subject: [PATCH 7/9] try to fix reconection --- app/ui-sidenav/client/userPresence.js | 33 +++++++++++++-------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/app/ui-sidenav/client/userPresence.js b/app/ui-sidenav/client/userPresence.js index d6e215b9c3714..c309772a3510b 100644 --- a/app/ui-sidenav/client/userPresence.js +++ b/app/ui-sidenav/client/userPresence.js @@ -8,7 +8,6 @@ import mem from 'mem'; import { APIClient } from '../../utils/client'; import { saveUser } from '../../../imports/startup/client/listenActiveUsers'; - import './userPresence.html'; const data = new Map(); @@ -31,9 +30,7 @@ const getAll = _.debounce(async function getAll() { users, } = await APIClient.v1.get('users.presence', params); - users.forEach((user) => { - saveUser(user); - }); + users.forEach((user) => saveUser(user, true)); ids.forEach((id) => { const { resolve } = promises.get(id); @@ -49,7 +46,6 @@ const getAll = _.debounce(async function getAll() { const get = mem(function get(id) { - console.log(id); const promise = pending.get(id) || new Promise((resolve, reject) => { promises.set(id, { resolve, reject }); }); @@ -57,27 +53,30 @@ const get = mem(function get(id) { return promise; }); - -Tracker.autorun(() => { - if (!Meteor.userId() || !Meteor.status().connected) { - return; - } - - mem.clear(get); -}); - const options = { threshold: 0.1, }; -const observer = new IntersectionObserver(function(entries) { - entries.filter(({ isIntersecting }) => isIntersecting).forEach(async (entry) => { +let lastEntries = []; +const handleEntries = function(entries) { + lastEntries = entries.filter(({ isIntersecting }) => isIntersecting); + lastEntries.forEach(async (entry) => { const { uid } = data.get(entry.target); await get(uid); pending.delete(uid); }); getAll(); -}, options); +}; + +const observer = new IntersectionObserver(handleEntries, options); + +Tracker.autorun(() => { + if (!Meteor.userId() || !Meteor.status().connected) { + return Meteor.users.update({}, { $unset: { status: '' } }, { multi: true }); + } + mem.clear(get); + handleEntries(lastEntries); +}); Template.userPresence.onRendered(function() { data.set(this.firstNode, this.data); From d55f3d4760f9db5a5bfa2e0ef62815079d413de5 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Tue, 4 Feb 2020 17:02:20 -0300 Subject: [PATCH 8/9] Reset observers after reconnection --- app/ui-sidenav/client/userPresence.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/ui-sidenav/client/userPresence.js b/app/ui-sidenav/client/userPresence.js index c309772a3510b..32501e8d92532 100644 --- a/app/ui-sidenav/client/userPresence.js +++ b/app/ui-sidenav/client/userPresence.js @@ -44,7 +44,6 @@ const getAll = _.debounce(async function getAll() { } }, 1000); - const get = mem(function get(id) { const promise = pending.get(id) || new Promise((resolve, reject) => { promises.set(id, { resolve, reject }); @@ -75,7 +74,11 @@ Tracker.autorun(() => { return Meteor.users.update({}, { $unset: { status: '' } }, { multi: true }); } mem.clear(get); - handleEntries(lastEntries); + + for (const node of data.keys()) { + observer.unobserve(node); + observer.observe(node); + } }); Template.userPresence.onRendered(function() { From 3379bab4912b97cf77c703e817ed3af0632ddcb8 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Tue, 4 Feb 2020 17:37:48 -0300 Subject: [PATCH 9/9] Fix user searches --- app/ui-message/client/popup/messagePopupUser.html | 2 +- app/ui-sidenav/client/sidebarItem.js | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/ui-message/client/popup/messagePopupUser.html b/app/ui-message/client/popup/messagePopupUser.html index 1d93b75756760..c90a04375d3de 100644 --- a/app/ui-message/client/popup/messagePopupUser.html +++ b/app/ui-message/client/popup/messagePopupUser.html @@ -1,6 +1,6 @@