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 @@
-
-
- {{> avatar username=username}}
-
-
-
{{name}}
-
{{username}}
- {{#if utcOffset}}
{{utcOffset}}
{{/if}}
- {{#each emails}}
{{address}}{{#if verified}} {{/if}}
{{/each}}
- {{#each phone}}
{{phoneNumber}}
{{/each}}
- {{#if lastLogin}}
{{_ "Last_login"}}: {{lastLogin}}
{{/if}}
-
-
+ {{> userInfo user=.}}