From 1bda97d87b10b86b97280a4f855abe9a5411259b Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Tue, 6 Feb 2018 15:11:37 -0200 Subject: [PATCH 1/7] Store message read receips --- .../message-read-receipt/server/dbIndexes.js | 5 ++ imports/message-read-receipt/server/index.js | 2 + .../server/lib/ReadReceipt.js | 55 +++++++++++++++++++ .../server/models/ReadReceipts.js | 15 +++++ .../message-read-receipt/server/settings.js | 12 ++++ imports/startup/server/index.js | 1 + .../server/functions/sendMessage.js | 4 ++ .../rocketchat-lib/server/models/Messages.js | 26 +++++++++ .../server/models/Subscriptions.js | 13 +++++ .../client/imports/components/messages.css | 8 +++ .../client/imports/general/base.css | 1 + .../rocketchat-ui-master/public/icons.svg | 1 + .../rocketchat-ui-message/client/message.html | 5 ++ .../rocketchat-ui-message/client/message.js | 9 +++ server/main.js | 1 + server/methods/readMessages.js | 17 +++++- 16 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 imports/message-read-receipt/server/dbIndexes.js create mode 100644 imports/message-read-receipt/server/index.js create mode 100644 imports/message-read-receipt/server/lib/ReadReceipt.js create mode 100644 imports/message-read-receipt/server/models/ReadReceipts.js create mode 100644 imports/message-read-receipt/server/settings.js create mode 100644 imports/startup/server/index.js create mode 100644 server/main.js diff --git a/imports/message-read-receipt/server/dbIndexes.js b/imports/message-read-receipt/server/dbIndexes.js new file mode 100644 index 000000000000..b88d4021a7bf --- /dev/null +++ b/imports/message-read-receipt/server/dbIndexes.js @@ -0,0 +1,5 @@ +RocketChat.models.Messages.tryEnsureIndex({ + unread: 1 +}, { + sparse: true +}); diff --git a/imports/message-read-receipt/server/index.js b/imports/message-read-receipt/server/index.js new file mode 100644 index 000000000000..b0a36ad00ee1 --- /dev/null +++ b/imports/message-read-receipt/server/index.js @@ -0,0 +1,2 @@ +import './dbIndexes'; +import './settings'; diff --git a/imports/message-read-receipt/server/lib/ReadReceipt.js b/imports/message-read-receipt/server/lib/ReadReceipt.js new file mode 100644 index 000000000000..fe033a8e8722 --- /dev/null +++ b/imports/message-read-receipt/server/lib/ReadReceipt.js @@ -0,0 +1,55 @@ +import { Random } from 'meteor/random'; +import ModelReadReceipts from '../models/ReadReceipts'; + +const rawReadReceipts = ModelReadReceipts.model.rawCollection(); + +// @TODO create a debounced function by roomId, so multiple calls to same roomId runs only once + +export const ReadReceipt = { + markMessagesAsRead(roomId, userId, userLastSeen) { + if (!RocketChat.settings.get('Message_Read_Receipt_Enabled')) { + return; + } + + const room = RocketChat.models.Rooms.findOneById(roomId, { fields: { lm: 1 } }); + + // if users last seen is greater than room's last message, it means the user already have this room marked as read + if (userLastSeen > room.lm) { + return; + } + + const firstSubscription = RocketChat.models.Subscriptions.getMinimumLastSeenByRoomId(roomId); + // console.log('userLastSeen ->', userLastSeen); + // console.log('firstSubscription ->', firstSubscription); + // console.log('room ->', room); + + // last time room was read is already past room's last message, so does nothing everybody have this room already + // if (firstSubscription.ls > room.lm) { + // console.log('already read by everyone'); + + // return; + // } + + // @TODO maybe store firstSubscription in room object so we don't need to call the above update method + // if firstSubscription on room didn't change + + if (RocketChat.settings.get('Message_Read_Receipt_Store_Users')) { + const receipts = RocketChat.models.Messages.findUnreadMessagesByRoomAndDate(roomId, userLastSeen).map(message => { + return { + _id: Random.id(), + roomId, + userId, + messageId: message._id + }; + }); + + try { + rawReadReceipts.insertMany(receipts); + } catch (e) { + console.error('Error inserting read receipts per user'); + } + } + + RocketChat.models.Messages.setAsRead(roomId, firstSubscription.ls); + } +}; diff --git a/imports/message-read-receipt/server/models/ReadReceipts.js b/imports/message-read-receipt/server/models/ReadReceipts.js new file mode 100644 index 000000000000..b5031aa3a8d5 --- /dev/null +++ b/imports/message-read-receipt/server/models/ReadReceipts.js @@ -0,0 +1,15 @@ +class ModelReadReceipts extends RocketChat.models._Base { + constructor() { + super(...arguments); + + this.tryEnsureIndex({ + roomId: 1, + userId: 1, + messageId: 1 + }, { + unique: 1 + }); + } +} + +export default new ModelReadReceipts('message_read_receipt', true); diff --git a/imports/message-read-receipt/server/settings.js b/imports/message-read-receipt/server/settings.js new file mode 100644 index 000000000000..94f49ecfd73b --- /dev/null +++ b/imports/message-read-receipt/server/settings.js @@ -0,0 +1,12 @@ +RocketChat.settings.add('Message_Read_Receipt_Enabled', false, { + group: 'Message', + type: 'boolean', + public: true +}); + +RocketChat.settings.add('Message_Read_Receipt_Store_Users', false, { + group: 'Message', + type: 'boolean', + public: false, + enableQuery: { _id: 'Message_Read_Receipt_Enabled', value: true } +}); diff --git a/imports/startup/server/index.js b/imports/startup/server/index.js new file mode 100644 index 000000000000..261b86e752db --- /dev/null +++ b/imports/startup/server/index.js @@ -0,0 +1 @@ +import '../../message-read-receipt/server'; diff --git a/packages/rocketchat-lib/server/functions/sendMessage.js b/packages/rocketchat-lib/server/functions/sendMessage.js index 46b44edd90c1..af100e1f547c 100644 --- a/packages/rocketchat-lib/server/functions/sendMessage.js +++ b/packages/rocketchat-lib/server/functions/sendMessage.js @@ -31,6 +31,10 @@ RocketChat.sendMessage = function(user, message, room, upsert = false) { }); } } + + // @TODO test if setting is enabled? + message.unread = true; + message = RocketChat.callbacks.run('beforeSaveMessage', message); if (message) { // Avoid saving sandstormSessionId to the database diff --git a/packages/rocketchat-lib/server/models/Messages.js b/packages/rocketchat-lib/server/models/Messages.js index 90f8bf068765..f74a0cefa1ec 100644 --- a/packages/rocketchat-lib/server/models/Messages.js +++ b/packages/rocketchat-lib/server/models/Messages.js @@ -622,4 +622,30 @@ RocketChat.models.Messages = new class extends RocketChat.models._Base { getMessageByFileId(fileID) { return this.findOne({ 'file._id': fileID }); } + + setAsRead(rid, until) { + return this.update({ + rid, + unread: true, + ts: { $lt: until } + }, { + $unset: { + unread: 1 + } + }, { + multi: true + }); + } + + findUnreadMessagesByRoomAndDate(rid, after) { + return this.find({ + unread: true, + rid, + ts: { $gt: after } + }, { + fields: { + _id: 1 + } + }); + } }; diff --git a/packages/rocketchat-lib/server/models/Subscriptions.js b/packages/rocketchat-lib/server/models/Subscriptions.js index 4c59e26add7f..a2c92b0aae70 100644 --- a/packages/rocketchat-lib/server/models/Subscriptions.js +++ b/packages/rocketchat-lib/server/models/Subscriptions.js @@ -182,6 +182,19 @@ class ModelSubscriptions extends RocketChat.models._Base { return this.find(query, { fields: { unread: 1 } }); } + getMinimumLastSeenByRoomId(rid) { + return this.db.findOne({ + rid + }, { + sort: { + ls: 1 + }, + fields: { + ls: 1 + } + }); + } + // UPDATE archiveByRoomId(roomId) { const query = diff --git a/packages/rocketchat-theme/client/imports/components/messages.css b/packages/rocketchat-theme/client/imports/components/messages.css index 16e1b2986988..56228e89aa25 100644 --- a/packages/rocketchat-theme/client/imports/components/messages.css +++ b/packages/rocketchat-theme/client/imports/components/messages.css @@ -77,6 +77,14 @@ left: 0; } } + + &.temp .read-receipt { + color: #999; + } + + & .read-receipt.read { + color: blue; + } } .messages-box .rc-popover__list { diff --git a/packages/rocketchat-theme/client/imports/general/base.css b/packages/rocketchat-theme/client/imports/general/base.css index 1a62424e1c15..bb35fc58eac1 100644 --- a/packages/rocketchat-theme/client/imports/general/base.css +++ b/packages/rocketchat-theme/client/imports/general/base.css @@ -123,6 +123,7 @@ button { vertical-align: -0.15em; + fill: currentColor; } .ps-scrollbar-y-rail { diff --git a/packages/rocketchat-ui-master/public/icons.svg b/packages/rocketchat-ui-master/public/icons.svg index 3bf31b7d5ca9..2105c2fb1df5 100644 --- a/packages/rocketchat-ui-master/public/icons.svg +++ b/packages/rocketchat-ui-master/public/icons.svg @@ -80,6 +80,7 @@ + diff --git a/packages/rocketchat-ui-message/client/message.html b/packages/rocketchat-ui-message/client/message.html index 9beba25aa67e..d5ccdb88f43f 100644 --- a/packages/rocketchat-ui-message/client/message.html +++ b/packages/rocketchat-ui-message/client/message.html @@ -114,5 +114,10 @@ + {{#with readReceipt}} +
+ {{> icon icon="check" }} +
+ {{/with}} diff --git a/packages/rocketchat-ui-message/client/message.js b/packages/rocketchat-ui-message/client/message.js index 8938fa772141..c24d6b55c10b 100644 --- a/packages/rocketchat-ui-message/client/message.js +++ b/packages/rocketchat-ui-message/client/message.js @@ -284,6 +284,15 @@ Template.message.helpers({ }, isSnippet() { return this.actionContext === 'snippeted'; + }, + readReceipt() { + if (!RocketChat.settings.get('Message_Read_Receipt_Enabled')) { + return; + } + + return { + readByEveryone: !this.unread && 'read' + }; } }); diff --git a/server/main.js b/server/main.js new file mode 100644 index 000000000000..282492e67e21 --- /dev/null +++ b/server/main.js @@ -0,0 +1 @@ +import '/imports/startup/server'; diff --git a/server/methods/readMessages.js b/server/methods/readMessages.js index d40e92ab289d..cd3e635da2ee 100644 --- a/server/methods/readMessages.js +++ b/server/methods/readMessages.js @@ -1,13 +1,26 @@ +import { ReadReceipt } from '../../imports/message-read-receipt/server/lib/ReadReceipt'; + Meteor.methods({ readMessages(rid) { check(rid, String); - if (!Meteor.userId()) { + const userId = Meteor.userId(); + + if (!userId) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'readMessages' }); } - return RocketChat.models.Subscriptions.setAsReadByRoomIdAndUserId(rid, Meteor.userId()); + const userSubscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(rid, userId); + + // this prevents cache from updating object reference/pointer + const { ls: lastSeen } = userSubscription; + + RocketChat.models.Subscriptions.setAsReadByRoomIdAndUserId(rid, userId); + + Meteor.defer(() => { + ReadReceipt.markMessagesAsRead(rid, userId, lastSeen); + }); } }); From d9ce4c9a628d1a14a6021b3ce056b2ec6384e466 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Tue, 6 Feb 2018 19:33:01 -0200 Subject: [PATCH 2/7] Read receipts modal --- client/main.js | 1 + imports/message-read-receipt/client/index.js | 1 + .../client/readReceipts.html | 8 +++++ .../client/readReceipts.js | 29 +++++++++++++++++++ .../server/api/methods/getReadReceipts.js | 11 +++++++ imports/message-read-receipt/server/index.js | 2 ++ .../server/lib/ReadReceipt.js | 11 ++++++- .../server/models/ReadReceipts.js | 6 +++- imports/startup/client/index.js | 1 + .../rocketchat-ui/client/views/app/room.js | 14 +++++++++ 10 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 client/main.js create mode 100644 imports/message-read-receipt/client/index.js create mode 100644 imports/message-read-receipt/client/readReceipts.html create mode 100644 imports/message-read-receipt/client/readReceipts.js create mode 100644 imports/message-read-receipt/server/api/methods/getReadReceipts.js create mode 100644 imports/startup/client/index.js diff --git a/client/main.js b/client/main.js new file mode 100644 index 000000000000..090ed9e37925 --- /dev/null +++ b/client/main.js @@ -0,0 +1 @@ +import '/imports/startup/client'; diff --git a/imports/message-read-receipt/client/index.js b/imports/message-read-receipt/client/index.js new file mode 100644 index 000000000000..d6e76a58e077 --- /dev/null +++ b/imports/message-read-receipt/client/index.js @@ -0,0 +1 @@ +import './readReceipts'; diff --git a/imports/message-read-receipt/client/readReceipts.html b/imports/message-read-receipt/client/readReceipts.html new file mode 100644 index 000000000000..dde1677a3945 --- /dev/null +++ b/imports/message-read-receipt/client/readReceipts.html @@ -0,0 +1,8 @@ + diff --git a/imports/message-read-receipt/client/readReceipts.js b/imports/message-read-receipt/client/readReceipts.js new file mode 100644 index 000000000000..afc5506b6e3a --- /dev/null +++ b/imports/message-read-receipt/client/readReceipts.js @@ -0,0 +1,29 @@ +import { ReactiveVar } from 'meteor/reactive-var'; +import moment from 'moment'; + +import './readReceipts.html'; + +Template.readReceipts.helpers({ + receipts() { + return Template.instance().readReceipts.get(); + }, + user() { + return this.user.name || this.user.username; + }, + time() { + return moment(this.ts).format('L LTS'); + } +}); + +Template.readReceipts.onCreated(function readReceiptsOnCreated() { + this.loading = new ReactiveVar(false); + this.readReceipts = new ReactiveVar([]); +}); + +Template.readReceipts.onRendered(function readReceiptsOnRendered() { + this.loading.set(true); + Meteor.call('getReadReceipts', { messageId: this.data.messageId }, (error, result) => { + this.loading.set(false); + this.readReceipts.set(result); + }); +}); diff --git a/imports/message-read-receipt/server/api/methods/getReadReceipts.js b/imports/message-read-receipt/server/api/methods/getReadReceipts.js new file mode 100644 index 000000000000..887c1a53f34d --- /dev/null +++ b/imports/message-read-receipt/server/api/methods/getReadReceipts.js @@ -0,0 +1,11 @@ +import { Meteor } from 'meteor/meteor'; + +import { ReadReceipt } from '../../lib/ReadReceipt'; + +Meteor.methods({ + getReadReceipts({ messageId }) { + const message = RocketChat.models.Messages.findOneById(messageId); + + return ReadReceipt.getReceipts(message); + } +}); diff --git a/imports/message-read-receipt/server/index.js b/imports/message-read-receipt/server/index.js index b0a36ad00ee1..7061d5e4f662 100644 --- a/imports/message-read-receipt/server/index.js +++ b/imports/message-read-receipt/server/index.js @@ -1,2 +1,4 @@ import './dbIndexes'; import './settings'; + +import './api/methods/getReadReceipts'; diff --git a/imports/message-read-receipt/server/lib/ReadReceipt.js b/imports/message-read-receipt/server/lib/ReadReceipt.js index fe033a8e8722..879dff368bab 100644 --- a/imports/message-read-receipt/server/lib/ReadReceipt.js +++ b/imports/message-read-receipt/server/lib/ReadReceipt.js @@ -34,12 +34,14 @@ export const ReadReceipt = { // if firstSubscription on room didn't change if (RocketChat.settings.get('Message_Read_Receipt_Store_Users')) { + const ts = new Date(); const receipts = RocketChat.models.Messages.findUnreadMessagesByRoomAndDate(roomId, userLastSeen).map(message => { return { _id: Random.id(), roomId, userId, - messageId: message._id + messageId: message._id, + ts }; }); @@ -51,5 +53,12 @@ export const ReadReceipt = { } RocketChat.models.Messages.setAsRead(roomId, firstSubscription.ls); + }, + + getReceipts(message) { + return ModelReadReceipts.findByMessageId(message._id).map(receipt => ({ + ...receipt, + user: RocketChat.models.Users.findOneById(receipt.userId, { fields: { username: 1, name: 1 }}) + })); } }; diff --git a/imports/message-read-receipt/server/models/ReadReceipts.js b/imports/message-read-receipt/server/models/ReadReceipts.js index b5031aa3a8d5..4749ae5bb281 100644 --- a/imports/message-read-receipt/server/models/ReadReceipts.js +++ b/imports/message-read-receipt/server/models/ReadReceipts.js @@ -10,6 +10,10 @@ class ModelReadReceipts extends RocketChat.models._Base { unique: 1 }); } + + findByMessageId(messageId) { + return this.find({ messageId }); + } } -export default new ModelReadReceipts('message_read_receipt', true); +export default new ModelReadReceipts('message_read_receipt'); diff --git a/imports/startup/client/index.js b/imports/startup/client/index.js new file mode 100644 index 000000000000..d61ded85f56a --- /dev/null +++ b/imports/startup/client/index.js @@ -0,0 +1 @@ +import '../../message-read-receipt/client'; diff --git a/packages/rocketchat-ui/client/views/app/room.js b/packages/rocketchat-ui/client/views/app/room.js index 30a446740c3e..cc378bd43ee8 100644 --- a/packages/rocketchat-ui/client/views/app/room.js +++ b/packages/rocketchat-ui/client/views/app/room.js @@ -718,6 +718,20 @@ Template.room.events({ showCancelButton: true, cancelButtonText: t('Close') }); + }, + 'click .read-receipt'(event) { + const data = Blaze.getData(event.currentTarget); + const messageId = data && data._arguments && data._arguments[1] && data._arguments[1]._id; + modal.open({ + title: 'Read receipts', + content: 'readReceipts', + data: { + messageId + }, + showConfirmButton: true, + showCancelButton: false, + confirmButtonText: t('Close') + }); } }); From a3e34040ce8d18047659dee3624066de53abb87e Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Fri, 9 Feb 2018 11:04:29 -0200 Subject: [PATCH 3/7] Improve read receipts modal UI --- .../client/readReceipts.css | 22 +++++++++++++++++++ .../client/readReceipts.html | 19 +++++++++++----- .../client/readReceipts.js | 12 +++++++--- .../server/api/methods/getReadReceipts.js | 9 ++++++++ .../client/imports/components/modal.css | 4 ++++ packages/rocketchat-theme/server/colors.less | 6 ++++- .../rocketchat-ui-master/client/loading.html | 8 +++---- .../rocketchat-ui-master/public/loading.css | 2 +- 8 files changed, 67 insertions(+), 15 deletions(-) create mode 100644 imports/message-read-receipt/client/readReceipts.css diff --git a/imports/message-read-receipt/client/readReceipts.css b/imports/message-read-receipt/client/readReceipts.css new file mode 100644 index 000000000000..662cbedb9aeb --- /dev/null +++ b/imports/message-read-receipt/client/readReceipts.css @@ -0,0 +1,22 @@ +.read-receipts__user { + display: flex; + padding: 8px 8px; + align-items: center; +} + +.read-receipts__name { + flex: 1 1 auto; + margin: 0 10px; + font-size: 16px; +} + +.read-receipts__time { + font-size: 80%; +} + +.read-receipts__user > .avatar { + width: 36px; + width: var(--sidebar-account-thumb-size); + height: 36px; + height: var(--sidebar-account-thumb-size); +} diff --git a/imports/message-read-receipt/client/readReceipts.html b/imports/message-read-receipt/client/readReceipts.html index dde1677a3945..137b4a915169 100644 --- a/imports/message-read-receipt/client/readReceipts.html +++ b/imports/message-read-receipt/client/readReceipts.html @@ -1,8 +1,15 @@ diff --git a/imports/message-read-receipt/client/readReceipts.js b/imports/message-read-receipt/client/readReceipts.js index afc5506b6e3a..b1a9cde21644 100644 --- a/imports/message-read-receipt/client/readReceipts.js +++ b/imports/message-read-receipt/client/readReceipts.js @@ -1,17 +1,21 @@ import { ReactiveVar } from 'meteor/reactive-var'; import moment from 'moment'; +import './readReceipts.css'; import './readReceipts.html'; Template.readReceipts.helpers({ receipts() { return Template.instance().readReceipts.get(); }, - user() { - return this.user.name || this.user.username; + displayName() { + return (RocketChat.settings.get('UI_Use_Real_Name') && this.user.name) || this.user.username; }, time() { return moment(this.ts).format('L LTS'); + }, + isLoading() { + return Template.instance().loading.get(); } }); @@ -23,7 +27,9 @@ Template.readReceipts.onCreated(function readReceiptsOnCreated() { Template.readReceipts.onRendered(function readReceiptsOnRendered() { this.loading.set(true); Meteor.call('getReadReceipts', { messageId: this.data.messageId }, (error, result) => { + if (!error) { + this.readReceipts.set(result); + } this.loading.set(false); - this.readReceipts.set(result); }); }); diff --git a/imports/message-read-receipt/server/api/methods/getReadReceipts.js b/imports/message-read-receipt/server/api/methods/getReadReceipts.js index 887c1a53f34d..0e6684fa129c 100644 --- a/imports/message-read-receipt/server/api/methods/getReadReceipts.js +++ b/imports/message-read-receipt/server/api/methods/getReadReceipts.js @@ -4,8 +4,17 @@ import { ReadReceipt } from '../../lib/ReadReceipt'; Meteor.methods({ getReadReceipts({ messageId }) { + if (!Meteor.userId()) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'getReadReceipts' }); + } + const message = RocketChat.models.Messages.findOneById(messageId); + const room = Meteor.call('canAccessRoom', message.rid, Meteor.userId()); + if (!room) { + throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'getReadReceipts' }); + } + return ReadReceipt.getReceipts(message); } }); diff --git a/packages/rocketchat-theme/client/imports/components/modal.css b/packages/rocketchat-theme/client/imports/components/modal.css index 2242466f7517..cfea0e5e591f 100644 --- a/packages/rocketchat-theme/client/imports/components/modal.css +++ b/packages/rocketchat-theme/client/imports/components/modal.css @@ -50,6 +50,8 @@ } &__content { + position: relative; + display: flex; flex-direction: column; @@ -58,6 +60,8 @@ animation: dropdown-show 0.1s cubic-bezier(0.45, 0.05, 0.55, 0.95); align-items: stretch; + + min-height: 72px; } &__content-icon { diff --git a/packages/rocketchat-theme/server/colors.less b/packages/rocketchat-theme/server/colors.less index 5ef8cd9cd1e3..e08ee3581845 100755 --- a/packages/rocketchat-theme/server/colors.less +++ b/packages/rocketchat-theme/server/colors.less @@ -882,11 +882,15 @@ label.required::after { .main-content, .flex-tab { - .loading-animation > div { + .loading-animation > .bounce { background-color: @primary-font-color; } } +.loading-animation.loading-animation--primary > .bounce { + background-color: @primary-font-color; +} + @keyframes blink { from { color: @selection-color; diff --git a/packages/rocketchat-ui-master/client/loading.html b/packages/rocketchat-ui-master/client/loading.html index 8cfdfef28ffc..273ed2274262 100644 --- a/packages/rocketchat-ui-master/client/loading.html +++ b/packages/rocketchat-ui-master/client/loading.html @@ -1,7 +1,7 @@ diff --git a/packages/rocketchat-ui-master/public/loading.css b/packages/rocketchat-ui-master/public/loading.css index 73ad608bcda1..42984694f28c 100644 --- a/packages/rocketchat-ui-master/public/loading.css +++ b/packages/rocketchat-ui-master/public/loading.css @@ -12,7 +12,7 @@ justify-content: center; } -.loading-animation > div { +.loading-animation > .bounce { display: inline-block; width: 10px; From d300e6076f76786180e9e240bc3b77989c1cbe19 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Wed, 14 Feb 2018 17:18:51 -0200 Subject: [PATCH 4/7] Improve set as read logic --- imports/message-read-receipt/server/hooks.js | 10 ++++ imports/message-read-receipt/server/index.js | 1 + .../server/lib/ReadReceipt.js | 50 +++++++++++++------ .../rocketchat-lib/server/models/Messages.js | 10 ++++ 4 files changed, 55 insertions(+), 16 deletions(-) create mode 100644 imports/message-read-receipt/server/hooks.js diff --git a/imports/message-read-receipt/server/hooks.js b/imports/message-read-receipt/server/hooks.js new file mode 100644 index 000000000000..560ebf449ac4 --- /dev/null +++ b/imports/message-read-receipt/server/hooks.js @@ -0,0 +1,10 @@ +import { ReadReceipt } from './lib/ReadReceipt'; + +RocketChat.callbacks.add('afterSaveMessage', (message, room) => { + + // set subscription as read right after message was sent + RocketChat.models.Subscriptions.setAsReadByRoomIdAndUserId(room._id, message.u._id); + + // mark message as read as well + ReadReceipt.markMessageAsReadBySender(message, room._id, message.u._id); +}); diff --git a/imports/message-read-receipt/server/index.js b/imports/message-read-receipt/server/index.js index 7061d5e4f662..2be56832fd46 100644 --- a/imports/message-read-receipt/server/index.js +++ b/imports/message-read-receipt/server/index.js @@ -1,4 +1,5 @@ import './dbIndexes'; +import './hooks'; import './settings'; import './api/methods/getReadReceipts'; diff --git a/imports/message-read-receipt/server/lib/ReadReceipt.js b/imports/message-read-receipt/server/lib/ReadReceipt.js index 879dff368bab..9984cf7331ff 100644 --- a/imports/message-read-receipt/server/lib/ReadReceipt.js +++ b/imports/message-read-receipt/server/lib/ReadReceipt.js @@ -3,7 +3,22 @@ import ModelReadReceipts from '../models/ReadReceipts'; const rawReadReceipts = ModelReadReceipts.model.rawCollection(); -// @TODO create a debounced function by roomId, so multiple calls to same roomId runs only once +// debounced function by roomId, so multiple calls within 2 seconds to same roomId runs only once +const list = {}; +const debounceByRoomId = function(fn) { + return function(roomId, ...args) { + clearTimeout(list[roomId]); + list[roomId] = setTimeout(() => { console.log('vai rodar'); fn.call(this, roomId, ...args); }, 2000); + }; +}; + +const updateMessages = debounceByRoomId(Meteor.bindEnvironment((roomId) => { + // @TODO maybe store firstSubscription in room object so we don't need to call the above update method + // if firstSubscription on room didn't change + const firstSubscription = RocketChat.models.Subscriptions.getMinimumLastSeenByRoomId(roomId); + // console.log('firstSubscription ->', firstSubscription); + RocketChat.models.Messages.setAsRead(roomId, firstSubscription.ls); +})); export const ReadReceipt = { markMessagesAsRead(roomId, userId, userLastSeen) { @@ -13,29 +28,34 @@ export const ReadReceipt = { const room = RocketChat.models.Rooms.findOneById(roomId, { fields: { lm: 1 } }); - // if users last seen is greater than room's last message, it means the user already have this room marked as read + // if users last seen is greadebounceByRoomIdter than room's last message, it means the user already have this room marked as read if (userLastSeen > room.lm) { return; } - const firstSubscription = RocketChat.models.Subscriptions.getMinimumLastSeenByRoomId(roomId); - // console.log('userLastSeen ->', userLastSeen); - // console.log('firstSubscription ->', firstSubscription); - // console.log('room ->', room); + this.storeReadReceipts(RocketChat.models.Messages.findUnreadMessagesByRoomAndDate(roomId, userLastSeen), roomId, userId); - // last time room was read is already past room's last message, so does nothing everybody have this room already - // if (firstSubscription.ls > room.lm) { - // console.log('already read by everyone'); + updateMessages(roomId); + }, + + markMessageAsReadBySender(message, roomId, userId) { + if (!RocketChat.settings.get('Message_Read_Receipt_Enabled')) { + return; + } - // return; - // } + // this will usually happens if the message sender is the only one on the room + const firstSubscription = RocketChat.models.Subscriptions.getMinimumLastSeenByRoomId(roomId); + if (message.unread && message.ts < firstSubscription.ls) { + RocketChat.models.Messages.setAsReadById(message._id, firstSubscription.ls); + } - // @TODO maybe store firstSubscription in room object so we don't need to call the above update method - // if firstSubscription on room didn't change + this.storeReadReceipts([{ _id: message._id }], roomId, userId); + }, + storeReadReceipts(messages, roomId, userId) { if (RocketChat.settings.get('Message_Read_Receipt_Store_Users')) { const ts = new Date(); - const receipts = RocketChat.models.Messages.findUnreadMessagesByRoomAndDate(roomId, userLastSeen).map(message => { + const receipts = messages.map(message => { return { _id: Random.id(), roomId, @@ -51,8 +71,6 @@ export const ReadReceipt = { console.error('Error inserting read receipts per user'); } } - - RocketChat.models.Messages.setAsRead(roomId, firstSubscription.ls); }, getReceipts(message) { diff --git a/packages/rocketchat-lib/server/models/Messages.js b/packages/rocketchat-lib/server/models/Messages.js index f74a0cefa1ec..2d4a45e7b6d2 100644 --- a/packages/rocketchat-lib/server/models/Messages.js +++ b/packages/rocketchat-lib/server/models/Messages.js @@ -637,6 +637,16 @@ RocketChat.models.Messages = new class extends RocketChat.models._Base { }); } + setAsReadById(_id) { + return this.update({ + _id + }, { + $unset: { + unread: 1 + } + }); + } + findUnreadMessagesByRoomAndDate(rid, after) { return this.find({ unread: true, From f5d9132e3f6f603859a39317d4d8f1d9d3f27cb7 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Wed, 14 Feb 2018 18:59:48 -0200 Subject: [PATCH 5/7] Improve read receipts UI --- .../message-read-receipt/client/readReceipts.css | 16 ++++++++++++++++ .../server/lib/ReadReceipt.js | 2 +- packages/rocketchat-i18n/i18n/en.i18n.json | 4 ++++ .../rocketchat-lib/client/methods/sendMessage.js | 1 + .../client/imports/components/messages.css | 8 -------- .../rocketchat-ui-message/client/message.html | 10 +++++----- packages/rocketchat-ui/client/views/app/room.js | 5 ++++- 7 files changed, 31 insertions(+), 15 deletions(-) diff --git a/imports/message-read-receipt/client/readReceipts.css b/imports/message-read-receipt/client/readReceipts.css index 662cbedb9aeb..47b7c6968c84 100644 --- a/imports/message-read-receipt/client/readReceipts.css +++ b/imports/message-read-receipt/client/readReceipts.css @@ -1,3 +1,19 @@ +.read-receipt { + display: inline-block; + margin: 0 6px; + color: #cbced1; + color: var(--message-box-container-border-color); + cursor: pointer; +} + +.read-receipt.read { + color: inherit; +} + +.message.temp .read-receipt { + opacity: 0.4; +} + .read-receipts__user { display: flex; padding: 8px 8px; diff --git a/imports/message-read-receipt/server/lib/ReadReceipt.js b/imports/message-read-receipt/server/lib/ReadReceipt.js index 9984cf7331ff..260ab4b2ffaf 100644 --- a/imports/message-read-receipt/server/lib/ReadReceipt.js +++ b/imports/message-read-receipt/server/lib/ReadReceipt.js @@ -8,7 +8,7 @@ const list = {}; const debounceByRoomId = function(fn) { return function(roomId, ...args) { clearTimeout(list[roomId]); - list[roomId] = setTimeout(() => { console.log('vai rodar'); fn.call(this, roomId, ...args); }, 2000); + list[roomId] = setTimeout(() => { fn.call(this, roomId, ...args); }, 2000); }; }; diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index b5294036a25c..a03a95b18762 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -1253,6 +1253,9 @@ "Message_MaxAllowedSize": "Maximum Allowed Characters Per Message", "Message_pinning": "Message pinning", "Message_QuoteChainLimit": "Maximum Number of Chained Quotes", + "Message_Read_Receipt_Enabled": "Show Read Receipts", + "Message_Read_Receipt_Store_Users": "Detailed Read Receipts", + "Message_Read_Receipt_Store_Users_Description": "Shows each user's read receipts", "Message_removed": "Message removed", "Message_sent_by_email": "Message sent by Email", "Message_SetNameToAliasEnabled": "Set a User Name to Alias in Message", @@ -1521,6 +1524,7 @@ "Read_only_changed_successfully": "Read only changed successfully", "Read_only_channel": "Read Only Channel", "Read_only_group": "Read Only Group", + "Read_receipts": "Read receipts", "RealName_Change_Disabled": "Your Rocket.Chat administrator has disabled the changing of names", "Receive_alerts": "Receive alerts", "Record": "Record", diff --git a/packages/rocketchat-lib/client/methods/sendMessage.js b/packages/rocketchat-lib/client/methods/sendMessage.js index 5138c696f9cd..a4e234b7163d 100644 --- a/packages/rocketchat-lib/client/methods/sendMessage.js +++ b/packages/rocketchat-lib/client/methods/sendMessage.js @@ -15,6 +15,7 @@ Meteor.methods({ message.u.name = user.name; } message.temp = true; + message.unread = true; message = RocketChat.callbacks.run('beforeSaveMessage', message); RocketChat.promises.run('onClientMessageReceived', message).then(function(message) { ChatMessage.insert(message); diff --git a/packages/rocketchat-theme/client/imports/components/messages.css b/packages/rocketchat-theme/client/imports/components/messages.css index 56228e89aa25..16e1b2986988 100644 --- a/packages/rocketchat-theme/client/imports/components/messages.css +++ b/packages/rocketchat-theme/client/imports/components/messages.css @@ -77,14 +77,6 @@ left: 0; } } - - &.temp .read-receipt { - color: #999; - } - - & .read-receipt.read { - color: blue; - } } .messages-box .rc-popover__list { diff --git a/packages/rocketchat-ui-message/client/message.html b/packages/rocketchat-ui-message/client/message.html index d5ccdb88f43f..ae46b3052bcf 100644 --- a/packages/rocketchat-ui-message/client/message.html +++ b/packages/rocketchat-ui-message/client/message.html @@ -63,6 +63,11 @@ {{#each attachments}} {{injectIndex . @index}} {{> messageAttachment}} {{/each}} + {{#with readReceipt}} +
+ {{> icon icon="check" }} +
+ {{/with}} {{#unless system}}
@@ -114,10 +119,5 @@ - {{#with readReceipt}} -
- {{> icon icon="check" }} -
- {{/with}} diff --git a/packages/rocketchat-ui/client/views/app/room.js b/packages/rocketchat-ui/client/views/app/room.js index cc378bd43ee8..41c34cb37105 100644 --- a/packages/rocketchat-ui/client/views/app/room.js +++ b/packages/rocketchat-ui/client/views/app/room.js @@ -720,10 +720,13 @@ Template.room.events({ }); }, 'click .read-receipt'(event) { + if (!RocketChat.settings.get('Message_Read_Receipt_Store_Users')) { + return; + } const data = Blaze.getData(event.currentTarget); const messageId = data && data._arguments && data._arguments[1] && data._arguments[1]._id; modal.open({ - title: 'Read receipts', + title: t('Read_receipts'), content: 'readReceipts', data: { messageId From 141e4df515ff06729c615ab4c428c7fa6ceaf0cc Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Thu, 15 Feb 2018 14:58:05 -0200 Subject: [PATCH 6/7] Code cleanup --- imports/message-read-receipt/client/index.js | 3 +++ imports/message-read-receipt/client/main.js | 7 ++++++ .../message-read-receipt/client/message.js | 17 ++++++++++++++ .../client/readReceipts.css | 3 +++ imports/message-read-receipt/client/room.js | 22 +++++++++++++++++++ .../server/lib/ReadReceipt.js | 2 -- .../message-read-receipt/server/settings.js | 2 +- .../client/methods/sendMessage.js | 4 +++- .../server/functions/sendMessage.js | 5 +++-- .../rocketchat-lib/server/models/Messages.js | 6 +++++ .../rocketchat-ui-master/client/main.html | 2 +- .../rocketchat-ui-message/client/message.html | 2 +- .../rocketchat-ui-message/client/message.js | 9 -------- .../rocketchat-ui/client/views/app/room.js | 17 -------------- server/methods/readMessages.js | 6 ++--- 15 files changed, 69 insertions(+), 38 deletions(-) create mode 100644 imports/message-read-receipt/client/main.js create mode 100644 imports/message-read-receipt/client/message.js create mode 100644 imports/message-read-receipt/client/room.js diff --git a/imports/message-read-receipt/client/index.js b/imports/message-read-receipt/client/index.js index d6e76a58e077..f8ffac5fbb31 100644 --- a/imports/message-read-receipt/client/index.js +++ b/imports/message-read-receipt/client/index.js @@ -1 +1,4 @@ +import './main'; +import './message'; import './readReceipts'; +import './room'; diff --git a/imports/message-read-receipt/client/main.js b/imports/message-read-receipt/client/main.js new file mode 100644 index 000000000000..c2711f6663dd --- /dev/null +++ b/imports/message-read-receipt/client/main.js @@ -0,0 +1,7 @@ +Template.main.helpers({ + readReceiptsEnabled() { + if (RocketChat.settings.get('Message_Read_Receipt_Store_Users')) { + return 'read-receipts-enabled'; + } + } +}); diff --git a/imports/message-read-receipt/client/message.js b/imports/message-read-receipt/client/message.js new file mode 100644 index 000000000000..6fa003e39aea --- /dev/null +++ b/imports/message-read-receipt/client/message.js @@ -0,0 +1,17 @@ +import { Template } from 'meteor/templating'; +import moment from 'moment'; + +Template.message.helpers({ + readReceipt() { + if (!RocketChat.settings.get('Message_Read_Receipt_Enabled')) { + return; + } + + return { + readByEveryone: !this.unread && 'rc-tooltip read' + }; + }, + readTime() { + return moment(this.readAt).format('LLL'); + } +}); diff --git a/imports/message-read-receipt/client/readReceipts.css b/imports/message-read-receipt/client/readReceipts.css index 47b7c6968c84..08291e947158 100644 --- a/imports/message-read-receipt/client/readReceipts.css +++ b/imports/message-read-receipt/client/readReceipts.css @@ -3,6 +3,9 @@ margin: 0 6px; color: #cbced1; color: var(--message-box-container-border-color); +} + +.read-receipts-enabled .read-receipt { cursor: pointer; } diff --git a/imports/message-read-receipt/client/room.js b/imports/message-read-receipt/client/room.js new file mode 100644 index 000000000000..3d1b6bd39a09 --- /dev/null +++ b/imports/message-read-receipt/client/room.js @@ -0,0 +1,22 @@ +import { Template } from 'meteor/templating'; +import { Blaze } from 'meteor/blaze'; + +Template.room.events({ + 'click .read-receipt'(event) { + if (!RocketChat.settings.get('Message_Read_Receipt_Store_Users')) { + return; + } + const data = Blaze.getData(event.currentTarget); + const messageId = data && data._arguments && data._arguments[1] && data._arguments[1]._id; + modal.open({ + title: t('Read_receipts'), + content: 'readReceipts', + data: { + messageId + }, + showConfirmButton: true, + showCancelButton: false, + confirmButtonText: t('Close') + }); + } +}); diff --git a/imports/message-read-receipt/server/lib/ReadReceipt.js b/imports/message-read-receipt/server/lib/ReadReceipt.js index 260ab4b2ffaf..7c2a531df242 100644 --- a/imports/message-read-receipt/server/lib/ReadReceipt.js +++ b/imports/message-read-receipt/server/lib/ReadReceipt.js @@ -14,9 +14,7 @@ const debounceByRoomId = function(fn) { const updateMessages = debounceByRoomId(Meteor.bindEnvironment((roomId) => { // @TODO maybe store firstSubscription in room object so we don't need to call the above update method - // if firstSubscription on room didn't change const firstSubscription = RocketChat.models.Subscriptions.getMinimumLastSeenByRoomId(roomId); - // console.log('firstSubscription ->', firstSubscription); RocketChat.models.Messages.setAsRead(roomId, firstSubscription.ls); })); diff --git a/imports/message-read-receipt/server/settings.js b/imports/message-read-receipt/server/settings.js index 94f49ecfd73b..5349ab399b8b 100644 --- a/imports/message-read-receipt/server/settings.js +++ b/imports/message-read-receipt/server/settings.js @@ -7,6 +7,6 @@ RocketChat.settings.add('Message_Read_Receipt_Enabled', false, { RocketChat.settings.add('Message_Read_Receipt_Store_Users', false, { group: 'Message', type: 'boolean', - public: false, + public: true, enableQuery: { _id: 'Message_Read_Receipt_Enabled', value: true } }); diff --git a/packages/rocketchat-lib/client/methods/sendMessage.js b/packages/rocketchat-lib/client/methods/sendMessage.js index a4e234b7163d..6fa83bfbb2c2 100644 --- a/packages/rocketchat-lib/client/methods/sendMessage.js +++ b/packages/rocketchat-lib/client/methods/sendMessage.js @@ -15,7 +15,9 @@ Meteor.methods({ message.u.name = user.name; } message.temp = true; - message.unread = true; + if (RocketChat.settings.get('Message_Read_Receipt_Enabled')) { + message.unread = true; + } message = RocketChat.callbacks.run('beforeSaveMessage', message); RocketChat.promises.run('onClientMessageReceived', message).then(function(message) { ChatMessage.insert(message); diff --git a/packages/rocketchat-lib/server/functions/sendMessage.js b/packages/rocketchat-lib/server/functions/sendMessage.js index af100e1f547c..197ca28729fd 100644 --- a/packages/rocketchat-lib/server/functions/sendMessage.js +++ b/packages/rocketchat-lib/server/functions/sendMessage.js @@ -32,8 +32,9 @@ RocketChat.sendMessage = function(user, message, room, upsert = false) { } } - // @TODO test if setting is enabled? - message.unread = true; + if (RocketChat.settings.get('Message_Read_Receipt_Enabled')) { + message.unread = true; + } message = RocketChat.callbacks.run('beforeSaveMessage', message); if (message) { diff --git a/packages/rocketchat-lib/server/models/Messages.js b/packages/rocketchat-lib/server/models/Messages.js index 2d4a45e7b6d2..3bfdde99b243 100644 --- a/packages/rocketchat-lib/server/models/Messages.js +++ b/packages/rocketchat-lib/server/models/Messages.js @@ -629,6 +629,9 @@ RocketChat.models.Messages = new class extends RocketChat.models._Base { unread: true, ts: { $lt: until } }, { + $set: { + readAt: new Date() + }, $unset: { unread: 1 } @@ -641,6 +644,9 @@ RocketChat.models.Messages = new class extends RocketChat.models._Base { return this.update({ _id }, { + $set: { + readAt: new Date() + }, $unset: { unread: 1 } diff --git a/packages/rocketchat-ui-master/client/main.html b/packages/rocketchat-ui-master/client/main.html index d8b5bc3f7b90..67a2f8b01771 100644 --- a/packages/rocketchat-ui-master/client/main.html +++ b/packages/rocketchat-ui-master/client/main.html @@ -48,7 +48,7 @@ {{#unless modal}} {{> sideNav }} {{/unless}} -
+
{{> Template.dynamic template=center}}
diff --git a/packages/rocketchat-ui-message/client/message.html b/packages/rocketchat-ui-message/client/message.html index ae46b3052bcf..2191bd7fb3bf 100644 --- a/packages/rocketchat-ui-message/client/message.html +++ b/packages/rocketchat-ui-message/client/message.html @@ -64,7 +64,7 @@ {{injectIndex . @index}} {{> messageAttachment}} {{/each}} {{#with readReceipt}} -
+
{{> icon icon="check" }}
{{/with}} diff --git a/packages/rocketchat-ui-message/client/message.js b/packages/rocketchat-ui-message/client/message.js index c24d6b55c10b..8938fa772141 100644 --- a/packages/rocketchat-ui-message/client/message.js +++ b/packages/rocketchat-ui-message/client/message.js @@ -284,15 +284,6 @@ Template.message.helpers({ }, isSnippet() { return this.actionContext === 'snippeted'; - }, - readReceipt() { - if (!RocketChat.settings.get('Message_Read_Receipt_Enabled')) { - return; - } - - return { - readByEveryone: !this.unread && 'read' - }; } }); diff --git a/packages/rocketchat-ui/client/views/app/room.js b/packages/rocketchat-ui/client/views/app/room.js index 41c34cb37105..30a446740c3e 100644 --- a/packages/rocketchat-ui/client/views/app/room.js +++ b/packages/rocketchat-ui/client/views/app/room.js @@ -718,23 +718,6 @@ Template.room.events({ showCancelButton: true, cancelButtonText: t('Close') }); - }, - 'click .read-receipt'(event) { - if (!RocketChat.settings.get('Message_Read_Receipt_Store_Users')) { - return; - } - const data = Blaze.getData(event.currentTarget); - const messageId = data && data._arguments && data._arguments[1] && data._arguments[1]._id; - modal.open({ - title: t('Read_receipts'), - content: 'readReceipts', - data: { - messageId - }, - showConfirmButton: true, - showCancelButton: false, - confirmButtonText: t('Close') - }); } }); diff --git a/server/methods/readMessages.js b/server/methods/readMessages.js index cd3e635da2ee..47da836cdddc 100644 --- a/server/methods/readMessages.js +++ b/server/methods/readMessages.js @@ -12,15 +12,13 @@ Meteor.methods({ }); } - const userSubscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(rid, userId); - // this prevents cache from updating object reference/pointer - const { ls: lastSeen } = userSubscription; + const userSubscription = Object.assign({}, RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(rid, userId)); RocketChat.models.Subscriptions.setAsReadByRoomIdAndUserId(rid, userId); Meteor.defer(() => { - ReadReceipt.markMessagesAsRead(rid, userId, lastSeen); + ReadReceipt.markMessagesAsRead(rid, userId, userSubscription.ls); }); } }); From bb7567c70e97510ea665f927aab4975182c242a8 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Fri, 16 Feb 2018 13:14:31 -0200 Subject: [PATCH 7/7] Read receipts redesign --- .../message-read-receipt/client/message.js | 6 +--- .../client/readReceipts.css | 20 +++++++++---- .../client/readReceipts.html | 1 + imports/message-read-receipt/client/room.js | 28 ++++++++++--------- .../server/lib/ReadReceipt.js | 8 +++++- packages/rocketchat-i18n/i18n/en.i18n.json | 3 +- .../rocketchat-lib/server/models/Messages.js | 23 ++++++++------- packages/rocketchat-theme/server/colors.less | 4 +++ .../rocketchat-ui-message/client/message.html | 10 +++---- 9 files changed, 63 insertions(+), 40 deletions(-) diff --git a/imports/message-read-receipt/client/message.js b/imports/message-read-receipt/client/message.js index 6fa003e39aea..9a89d937b6c4 100644 --- a/imports/message-read-receipt/client/message.js +++ b/imports/message-read-receipt/client/message.js @@ -1,5 +1,4 @@ import { Template } from 'meteor/templating'; -import moment from 'moment'; Template.message.helpers({ readReceipt() { @@ -8,10 +7,7 @@ Template.message.helpers({ } return { - readByEveryone: !this.unread && 'rc-tooltip read' + readByEveryone: (!this.unread && 'read') || 'color-component-color' }; - }, - readTime() { - return moment(this.readAt).format('LLL'); } }); diff --git a/imports/message-read-receipt/client/readReceipts.css b/imports/message-read-receipt/client/readReceipts.css index 08291e947158..22788f691665 100644 --- a/imports/message-read-receipt/client/readReceipts.css +++ b/imports/message-read-receipt/client/readReceipts.css @@ -1,8 +1,16 @@ .read-receipt { - display: inline-block; - margin: 0 6px; - color: #cbced1; - color: var(--message-box-container-border-color); + position: absolute; + top: 2px; + right: 0.5rem; +} + +.read-receipt .rc-icon { + height: 0.8em; + width: 0.8em; +} + +.message:hover .read-receipt, .message.active .read-receipt { + display: none; } .read-receipts-enabled .read-receipt { @@ -10,7 +18,9 @@ } .read-receipt.read { - color: inherit; + color: #1d74f5; + color: var(--rc-color-button-primary); + font-style: normal; } .message.temp .read-receipt { diff --git a/imports/message-read-receipt/client/readReceipts.html b/imports/message-read-receipt/client/readReceipts.html index 137b4a915169..3a81ee9cdcdf 100644 --- a/imports/message-read-receipt/client/readReceipts.html +++ b/imports/message-read-receipt/client/readReceipts.html @@ -2,6 +2,7 @@ {{#if isLoading}} {{> loading class="loading-animation--primary"}} {{else}} +

{{_ "Read_by"}}:

    {{#each receipts}}
  • diff --git a/imports/message-read-receipt/client/room.js b/imports/message-read-receipt/client/room.js index 3d1b6bd39a09..dc5cde1e4a6b 100644 --- a/imports/message-read-receipt/client/room.js +++ b/imports/message-read-receipt/client/room.js @@ -1,22 +1,24 @@ -import { Template } from 'meteor/templating'; -import { Blaze } from 'meteor/blaze'; - -Template.room.events({ - 'click .read-receipt'(event) { - if (!RocketChat.settings.get('Message_Read_Receipt_Store_Users')) { - return; - } - const data = Blaze.getData(event.currentTarget); - const messageId = data && data._arguments && data._arguments[1] && data._arguments[1]._id; +RocketChat.MessageAction.addButton({ + id: 'receipt-detail', + icon: 'info-circled', + label: 'Message_info', + context: ['starred', 'message', 'message-mobile'], + action() { + const message = this._arguments[1]; modal.open({ - title: t('Read_receipts'), + title: t('Message_info'), content: 'readReceipts', data: { - messageId + messageId: message._id }, showConfirmButton: true, showCancelButton: false, confirmButtonText: t('Close') }); - } + }, + condition() { + return RocketChat.settings.get('Message_Read_Receipt_Store_Users'); + }, + order: 1, + group: 'menu' }); diff --git a/imports/message-read-receipt/server/lib/ReadReceipt.js b/imports/message-read-receipt/server/lib/ReadReceipt.js index 7c2a531df242..8cde6157ab29 100644 --- a/imports/message-read-receipt/server/lib/ReadReceipt.js +++ b/imports/message-read-receipt/server/lib/ReadReceipt.js @@ -31,7 +31,9 @@ export const ReadReceipt = { return; } - this.storeReadReceipts(RocketChat.models.Messages.findUnreadMessagesByRoomAndDate(roomId, userLastSeen), roomId, userId); + if (userLastSeen) { + this.storeReadReceipts(RocketChat.models.Messages.findUnreadMessagesByRoomAndDate(roomId, userLastSeen), roomId, userId); + } updateMessages(roomId); }, @@ -63,6 +65,10 @@ export const ReadReceipt = { }; }); + if (receipts.length === 0) { + return; + } + try { rawReadReceipts.insertMany(receipts); } catch (e) { diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 2923cfc299b3..104a79994b0c 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -1252,6 +1252,7 @@ "Message_HideType_ru": "Hide \"User Removed\" messages", "Message_HideType_uj": "Hide \"User Join\" messages", "Message_HideType_ul": "Hide \"User Leave\" messages", + "Message_info": "Message info", "Message_KeepHistory": "Keep Per Message Editing History", "Message_MaxAll": "Maximum Channel Size for ALL Message", "Message_MaxAllowedSize": "Maximum Allowed Characters Per Message", @@ -1525,11 +1526,11 @@ "React_when_read_only_changed_successfully": "Allow reacting when read only changed successfully", "Reacted_with": "Reacted with", "Reactions": "Reactions", + "Read_by": "Read by", "Read_only": "Read Only", "Read_only_changed_successfully": "Read only changed successfully", "Read_only_channel": "Read Only Channel", "Read_only_group": "Read Only Group", - "Read_receipts": "Read receipts", "RealName_Change_Disabled": "Your Rocket.Chat administrator has disabled the changing of names", "Receive_alerts": "Receive alerts", "Record": "Record", diff --git a/packages/rocketchat-lib/server/models/Messages.js b/packages/rocketchat-lib/server/models/Messages.js index 3bfdde99b243..a52fcdaa1843 100644 --- a/packages/rocketchat-lib/server/models/Messages.js +++ b/packages/rocketchat-lib/server/models/Messages.js @@ -519,6 +519,10 @@ RocketChat.models.Messages = new class extends RocketChat.models._Base { groupable: false }; + if (RocketChat.settings.get('Message_Read_Receipt_Enabled')) { + record.unread = true; + } + _.extend(record, extraData); record._id = this.insertOrUpsert(record); @@ -629,9 +633,6 @@ RocketChat.models.Messages = new class extends RocketChat.models._Base { unread: true, ts: { $lt: until } }, { - $set: { - readAt: new Date() - }, $unset: { unread: 1 } @@ -644,9 +645,6 @@ RocketChat.models.Messages = new class extends RocketChat.models._Base { return this.update({ _id }, { - $set: { - readAt: new Date() - }, $unset: { unread: 1 } @@ -654,11 +652,16 @@ RocketChat.models.Messages = new class extends RocketChat.models._Base { } findUnreadMessagesByRoomAndDate(rid, after) { - return this.find({ + const query = { unread: true, - rid, - ts: { $gt: after } - }, { + rid + }; + + if (after) { + query.ts = { $gt: after }; + } + + return this.find(query, { fields: { _id: 1 } diff --git a/packages/rocketchat-theme/server/colors.less b/packages/rocketchat-theme/server/colors.less index e08ee3581845..89bfaa2391ac 100755 --- a/packages/rocketchat-theme/server/colors.less +++ b/packages/rocketchat-theme/server/colors.less @@ -95,6 +95,10 @@ background-color: @component-color; } +.color-component-color { + color: @component-color; +} + .success-color { color: @success-color; } diff --git a/packages/rocketchat-ui-message/client/message.html b/packages/rocketchat-ui-message/client/message.html index 2191bd7fb3bf..7febbabc4237 100644 --- a/packages/rocketchat-ui-message/client/message.html +++ b/packages/rocketchat-ui-message/client/message.html @@ -63,12 +63,12 @@ {{#each attachments}} {{injectIndex . @index}} {{> messageAttachment}} {{/each}} - {{#with readReceipt}} -
    - {{> icon icon="check" }} -
    - {{/with}}
+ {{#with readReceipt}} +
+ {{> icon icon="check" }} +
+ {{/with}} {{#unless system}}
{{#if messageActions 'message'}}