diff --git a/client/lib/RoomManager.coffee b/client/lib/RoomManager.coffee index 9c0d6348ae7..1edc98059b9 100644 --- a/client/lib/RoomManager.coffee +++ b/client/lib/RoomManager.coffee @@ -84,7 +84,7 @@ Meteor.startup -> if type in ['c', 'p'] query.name = name else if type is 'd' - query.usernames = $all: [Meteor.user().username, name] + query.usernames = $all: [Meteor.user()?.username, name] room = ChatRoom.findOne query, { reactive: false } diff --git a/client/lib/chatMessages.coffee b/client/lib/chatMessages.coffee index 5945871243e..2ecd8cc4634 100644 --- a/client/lib/chatMessages.coffee +++ b/client/lib/chatMessages.coffee @@ -70,6 +70,10 @@ class @ChatMessages send: (rid, input) -> if _.trim(input.value) isnt '' + readMessage.enable() + readMessage.readNow() + $('.message.first-unread').removeClass('first-unread') + if this.editing.id this.update(this.editing.id, rid, input) return diff --git a/client/lib/readMessages.coffee b/client/lib/readMessages.coffee new file mode 100644 index 00000000000..cf3a774ae19 --- /dev/null +++ b/client/lib/readMessages.coffee @@ -0,0 +1,76 @@ +### DEFINITIONS +- If window loses focus user needs to scroll or click/touch some place +- On hit ESC enable read, force read of current room and remove unread mark +- When user change room disable read until user interaction +- Only read if mark of *first-unread* is visible for user or if flag *force* was passed +- Always read the opened room +- The default method *read* has a delay of 2000ms to prevent multiple reads and to user be able to see the mark +### + +@readMessage = new class + constructor: -> + @canReadMessage = false + + readNow: (force=false) -> + return if @canReadMessage is false + + rid = Session.get 'openedRoom' + if rid? + subscription = ChatSubscription.findOne rid: rid + if subscription? and (subscription.alert is true or subscription.unread > 0) + # Only read messages if user saw the first unread message + position = $('.message.first-unread').position() + if force is true or not position? or position.top >= 0 + Meteor.call 'readMessages', rid + + read: _.debounce (force) -> + @readNow(force) + , 2000 + + disable: -> + @canReadMessage = false + + enable: -> + @canReadMessage = document.hasFocus() + + isEnable: -> + return @canReadMessage is true + + refreshUnreadMark: (rid) -> + rid ?= Session.get 'openedRoom' + if rid? + subscription = ChatSubscription.findOne rid: rid + room = RoomManager.openedRooms[subscription.t + subscription.name] + if room? + $roomDom = $(room.dom) + $roomDom.find('.message.first-unread').addClass('first-unread-opaque') + if (subscription.rid isnt rid or readMessage.isEnable() is false) and (subscription.alert or subscription.unread > 0) + $roomDom.find('.message.first-unread').removeClass('first-unread').removeClass('first-unread-opaque') + firstUnreadId = ChatMessage.findOne({rid: subscription.rid, ts: {$gt: subscription.ls}, 'u._id': {$ne: Meteor.userId()}}, {sort: {ts: 1}})?._id + if firstUnreadId? + $roomDom.find('.message#'+firstUnreadId).addClass('first-unread') + + +Meteor.startup -> + $(window).on 'blur', -> + readMessage.disable() + + $(window).on 'focus', -> + readMessage.enable() + readMessage.read() + + $(window).on 'click', (e) -> + readMessage.enable() + readMessage.read() + + $(window).on 'touchend', (e) -> + readMessage.enable() + readMessage.read() + + $(window).on 'keyup', (e) -> + key = event.which + + if key is 27 + readMessage.enable() + readMessage.readNow(true) + $('.message.first-unread').removeClass('first-unread') diff --git a/client/lib/readMessagesOnFocus.coffee b/client/lib/readMessagesOnFocus.coffee deleted file mode 100644 index 1ef95945c52..00000000000 --- a/client/lib/readMessagesOnFocus.coffee +++ /dev/null @@ -1,8 +0,0 @@ -Meteor.startup -> - $(window).on 'focus', -> - if FlowRouter.getRouteName() in ['channel', 'group', 'direct'] - rid = Session.get 'openedRoom' - if rid? - subscription = ChatSubscription.findOne rid: rid - if subscription? and (subscription.alert is true or subscription.unread > 0) - Meteor.call 'readMessages', rid \ No newline at end of file diff --git a/client/routes/roomRoute.coffee b/client/routes/roomRoute.coffee index 20c3f9e435f..6ae3d24a4cc 100644 --- a/client/routes/roomRoute.coffee +++ b/client/routes/roomRoute.coffee @@ -37,7 +37,12 @@ openRoom = (type, name) -> Session.set 'openedRoom', room._id Session.set 'editRoomTitle', false - Meteor.call 'readMessages', room._id if Meteor.userId()? + readMessage.disable() + Meteor.setTimeout -> + readMessage.refreshUnreadMark() + readMessage.enable() + readMessage.readNow() + , 2000 # KonchatNotification.removeRoomNotification(params._id) if Meteor.Device.isDesktop() @@ -52,7 +57,11 @@ roomExit = -> for child in mainNode.children if child? if child.classList.contains('room-container') - child.oldScrollTop = child.querySelector('.messages-box > .wrapper').scrollTop + wrapper = child.querySelector('.messages-box > .wrapper') + if wrapper.scrollTop >= wrapper.scrollHeight - wrapper.clientHeight + child.oldScrollTop = 10e10 + else + child.oldScrollTop = wrapper.scrollTop mainNode.removeChild child @@ -60,6 +69,7 @@ FlowRouter.route '/channel/:name', name: 'channel' action: (params, queryParams) -> + Session.set 'showUserInfo' openRoom 'c', params.name triggersExit: [roomExit] @@ -69,6 +79,7 @@ FlowRouter.route '/group/:name', name: 'group' action: (params, queryParams) -> + Session.set 'showUserInfo' openRoom 'p', params.name triggersExit: [roomExit] @@ -78,6 +89,7 @@ FlowRouter.route '/direct/:username', name: 'direct' action: (params, queryParams) -> + Session.set 'showUserInfo', params.username openRoom 'd', params.username triggersExit: [roomExit] diff --git a/client/startup/unread.coffee b/client/startup/unread.coffee index d8c52264aeb..6e8a4e1c510 100644 --- a/client/startup/unread.coffee +++ b/client/startup/unread.coffee @@ -12,20 +12,22 @@ Meteor.startup -> unreadCount = 0 unreadAlert = false - subscriptions = ChatSubscription.find({open: true}, { fields: { unread: 1, alert: 1, rid: 1 } }) + subscriptions = ChatSubscription.find({open: true}, { fields: { unread: 1, alert: 1, rid: 1, t: 1, name: 1, ls: 1 } }) rid = undefined if FlowRouter.getRouteName() in ['channel', 'group', 'direct'] rid = Session.get 'openedRoom' for subscription in subscriptions.fetch() - if subscription.rid is rid and (subscription.alert or subscription.unread > 0) and document.hasFocus() - Meteor.call 'readMessages', subscription.rid + if subscription.rid is rid and (subscription.alert or subscription.unread > 0) + readMessage.readNow() else unreadCount += subscription.unread if subscription.alert is true unreadAlert = '•' + readMessage.refreshUnreadMark(subscription.rid) + if unreadCount > 0 if unreadCount > 999 Session.set 'unread', '999+' @@ -43,8 +45,9 @@ Meteor.startup -> animation: 'none' Tracker.autorun -> - + siteName = RocketChat.settings.get 'Site_Name' + unread = Session.get 'unread' fireGlobalEvent 'unread-changed', unread favico?.badge unread, bgColor: if typeof unread isnt 'number' then '#3d8a3a' else '#ac1b1b' - document.title = if unread == '' then 'Rocket.Chat' else '(' + unread + ') Rocket.Chat' + document.title = if unread == '' then siteName else '(' + unread + ') '+ siteName diff --git a/client/stylesheets/base.less b/client/stylesheets/base.less index e6e090c29cb..0697d859c9a 100644 --- a/client/stylesheets/base.less +++ b/client/stylesheets/base.less @@ -128,6 +128,48 @@ blockquote { user-select: none; } +.first-unread { + &.first-unread-opaque { + .body { + &::before { + background-color: #E6E6E6; + } + + &::after { + color: #B3B3B3; + } + } + } + .body { + &::before { + content: ""; + background-color: #FF8080; + display: block; + height: 1px; + position: absolute; + right: 0px; + left: 50px; + top: -3px; + .transition(background-color, .5s, linear); + } + + &::after { + content: "unread messages"; + display: block; + position: absolute; + right: 0px; + top: -7px; + text-transform: uppercase; + font-size: 8px; + line-height: 10px; + background-color: white; + padding-left: 5px; + color: #FF0000; + .transition(color, .5s, linear); + } + } +} + .text-center { text-align: center; } @@ -2810,7 +2852,45 @@ a.github-fork { h3 { font-size: 24px; margin-bottom: 8px; - line-height: 22px; + line-height: 27px; + text-overflow: ellipsis; + width: 100%; + overflow: hidden; + white-space: nowrap; + + i:after { + content: " "; + display: inline-block; + width: 8px; + height: 8px; + border-radius: 4px; + vertical-align: middle; + } + + i.status-offline { + &:after { + border-color: darken(@status-offline, 5%); + background-color: @status-offline; + } + } + i.status-online { + &:after { + border-color: darken(@status-online, 5%); + background-color: @status-online; + } + } + i.status-away { + &:after { + border-color: darken(@status-away, 5%); + background-color: @status-away; + } + } + i.status-busy { + &:after { + border-color: darken(@status-busy, 5%); + background-color: @status-busy; + } + } } p { line-height: 18px; diff --git a/client/stylesheets/global/_variables.less b/client/stylesheets/global/_variables.less index 06715e4cefd..c8bf1d2587a 100644 --- a/client/stylesheets/global/_variables.less +++ b/client/stylesheets/global/_variables.less @@ -25,6 +25,6 @@ @info-font-color: #AAAAAA; @status-online: #35AC19; -@status-offline: rgba(255, 255, 255, 0.35); +@status-offline: rgba(150, 150, 150, 0.50); @status-busy: #D30230; @status-away: #fcb316; diff --git a/client/views/admin/adminStatistics.html b/client/views/admin/adminStatistics.html index c0f969aec78..7a6d8b0ecb2 100644 --- a/client/views/admin/adminStatistics.html +++ b/client/views/admin/adminStatistics.html @@ -52,6 +52,10 @@ {{_ "Stats_Total_Direct_Messages"}} {{statistics.totalDirect}} + + {{_ "Stats_Total_Messages"}} + {{statistics.totalMessages}} + {{_ "Stats_Max_Room_Users"}} {{statistics.maxRoomUsers}} diff --git a/client/views/admin/users/adminUserInfo.html b/client/views/admin/users/adminUserInfo.html index 0567daf89ba..97725f8f477 100644 --- a/client/views/admin/users/adminUserInfo.html +++ b/client/views/admin/users/adminUserInfo.html @@ -1,17 +1,5 @@