From eab3f9bac91ae79185c420b7dbd20d98d4a9acd3 Mon Sep 17 00:00:00 2001
From: Marcelo Schmidt <marcelo.schmidt@konecty.com>
Date: Thu, 2 Jul 2015 01:03:26 -0300
Subject: [PATCH] Replace typing messages. Insert into data.Typing instead of
 data.Messages and use ReactiveVar to determine if current user is typing.

---
 client/lib/RoomManager.coffee                 |  1 +
 client/lib/chatMessages.coffee                |  4 ++
 client/lib/collections.coffee                 |  1 +
 client/methods/typingStatus.coffee            |  7 +--
 client/views/app/room.coffee                  | 63 ++++++++++++++-----
 i18n/en.i18n.json                             |  1 +
 .../rocketchat-lib/server/sendMessage.coffee  |  3 +-
 server/lib/collections.coffee                 |  1 +
 server/methods/receiveMessage.coffee          |  4 +-
 server/methods/typingStatus.coffee            |  7 +--
 server/publications/typing.coffee             | 17 +++++
 server/startup/indexes.coffee                 |  4 ++
 12 files changed, 81 insertions(+), 32 deletions(-)
 create mode 100644 server/publications/typing.coffee

diff --git a/client/lib/RoomManager.coffee b/client/lib/RoomManager.coffee
index 75f27efc820..51dd360cc73 100644
--- a/client/lib/RoomManager.coffee
+++ b/client/lib/RoomManager.coffee
@@ -26,6 +26,7 @@
 			record.sub = [
 				Meteor.subscribe 'room', rid
 				Meteor.subscribe 'messages', rid
+				Meteor.subscribe 'typing', rid
 			]
 
 			record.ready = record.sub[0].ready() and record.sub[1].ready()
diff --git a/client/lib/chatMessages.coffee b/client/lib/chatMessages.coffee
index e9e2d03be9a..d73a1f626e6 100644
--- a/client/lib/chatMessages.coffee
+++ b/client/lib/chatMessages.coffee
@@ -3,6 +3,7 @@
 	wrapper = {}
 	input = {}
 	editing = {}
+	selfTyping = new ReactiveVar false
 
 	init = ->
 		wrapper = $(".messages-container").find(".wrapper")
@@ -108,12 +109,14 @@
 		if _.trim(input.value) isnt ''
 			unless self.typingTimeout
 				if Meteor.userId()?
+					selfTyping.set true
 					Meteor.call 'typingStatus', rid, true
 				self.typingTimeout = Meteor.setTimeout ->
 					stopTyping()
 				, 30000
 
 	stopTyping = ->
+		selfTyping.set false
 		self.typingTimeout = null
 
 	bindEvents = ->
@@ -181,4 +184,5 @@
 	send: send
 	init: init
 	edit: edit
+	selfTyping: selfTyping
 )()
diff --git a/client/lib/collections.coffee b/client/lib/collections.coffee
index aa2e8ebf1cd..347843497b3 100644
--- a/client/lib/collections.coffee
+++ b/client/lib/collections.coffee
@@ -4,6 +4,7 @@
 @ChatRoom = new Meteor.Collection 'data.ChatRoom'
 @ChatSubscription = new Meteor.Collection 'data.ChatSubscription'
 @ChatMessage = new Meteor.Collection 'data.ChatMessage'
+@ChatTyping = new Meteor.Collection 'data.ChatTyping'
 
 Meteor.startup ->
 	ChatMessage.find().observe
diff --git a/client/methods/typingStatus.coffee b/client/methods/typingStatus.coffee
index 2ff27da7c76..ae2b38c8512 100644
--- a/client/methods/typingStatus.coffee
+++ b/client/methods/typingStatus.coffee
@@ -4,7 +4,6 @@ Meteor.methods
 			throw new Meteor.Error 203, t('User_logged_out')
 
 		filter =
-			t: 't'
 			rid: rid
 			$and: [{'u._id': Meteor.userId()}]
 
@@ -12,12 +11,10 @@ Meteor.methods
 
 			msgData =
 				'$setOnInsert':
-					msg: '...'
 					'u._id': Meteor.userId()
 					'u.username': Meteor.user().username
-					ts: moment().add(1, 'years').toDate()
-			ChatMessage.upsert(filter, msgData)
+			ChatTyping.upsert(filter, msgData)
 
 		else
 
-			ChatMessage.remove(filter)
+			ChatTyping.remove(filter)
diff --git a/client/views/app/room.coffee b/client/views/app/room.coffee
index 2801fa64d00..51a8dad1dbe 100644
--- a/client/views/app/room.coffee
+++ b/client/views/app/room.coffee
@@ -58,36 +58,65 @@ Template.room.helpers
 
 	usersTyping: ->
 		console.log 'room.helpers usersTyping' if window.rocketDebug
-		messages = ChatMessage.find { rid: this._id, t: 't' }, { sort: { ts: 1 } }
-		usernames = []
-		selfTyping = false
-		messages.forEach (message) ->
-			if message.u._id is Meteor.userId()
-				selfTyping = true
-			else
-				username = message.u.username
-				if username?
-					usernames.push username
-
-		if usernames.length is 0
+		messages = ChatTyping.find({ rid: this._id, 'u._id': { $ne: Meteor.userId() } }).fetch()
+		if messages.length is 0
 			return
-
-		if usernames.length is 1
+		if messages.length is 1
 			return {
 				multi: false
-				selfTyping: selfTyping
-				users: usernames[0]
+				selfTyping: ChatMessages.selfTyping.get()
+				users: messages[0].u.username
 			}
 
+		usernames = _.map messages, (message) -> return message.u.username
+		
 		last = usernames.pop()
+		if messages.length > 4
+			last = t('others')
+		else
 		usernames = usernames.join(', ')
 		usernames = [usernames, last]
 		return {
 			multi: true
-			selfTyping: selfTyping
+			selfTyping: ChatMessages.selfTyping.get()
 			users: usernames.join " #{t 'and'} "
 		}
 
+
+		# usernames = []
+		# selfTyping = false
+		# total = 0
+		# messages.forEach (message) ->
+		# 	total++
+		# 	if message.u._id is Meteor.userId()
+		# 		selfTyping = true
+		# 	else
+		# 		username = message.u.username
+		# 		if username?
+		# 			usernames.push username
+
+		# if usernames.length is 0
+		# 	return
+
+		# if usernames.length is 1
+		# 	return {
+		# 		multi: false
+		# 		selfTyping: selfTyping
+		# 		users: usernames[0]
+		# 	}
+
+		# last = usernames.pop()
+		# if total > 4
+		# 	last = t('others')
+			
+		# usernames = usernames.join(', ')
+		# usernames = [usernames, last]
+		# return {
+		# 	multi: true
+		# 	selfTyping: selfTyping
+		# 	users: usernames.join " #{t 'and'} "
+		# }
+
 	roomName: ->
 		console.log 'room.helpers roomName' if window.rocketDebug
 		roomData = Session.get('roomData' + this._id)
diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json
index 25fe6e04099..e45154ae300 100755
--- a/i18n/en.i18n.json
+++ b/i18n/en.i18n.json
@@ -90,6 +90,7 @@
   "Nothing_found" : "Nothing found",
   "Online" : "Online",
   "Oops!" : "Oops",
+  "others": "others",
   "Password" : "Password",
   "Please_wait" : "Please wait",
   "Powered_by" : "Powered by",
diff --git a/packages/rocketchat-lib/server/sendMessage.coffee b/packages/rocketchat-lib/server/sendMessage.coffee
index 5628bb91739..540c60df1d1 100644
--- a/packages/rocketchat-lib/server/sendMessage.coffee
+++ b/packages/rocketchat-lib/server/sendMessage.coffee
@@ -34,9 +34,8 @@ RocketChat.sendMessage = (user, message, room) ->
 	###
 	Meteor.defer ->
 
-		ChatMessage.remove
+		ChatTyping.remove
 			rid: message.rid
-			t: 't'
 			'u._id': message.u._id
 
 	###
diff --git a/server/lib/collections.coffee b/server/lib/collections.coffee
index 5bc7b20bf63..5989a5cea75 100644
--- a/server/lib/collections.coffee
+++ b/server/lib/collections.coffee
@@ -1,3 +1,4 @@
 @ChatMessage = new Meteor.Collection 'data.ChatMessage'
 @ChatRoom = new Meteor.Collection 'data.ChatRoom'
 @ChatSubscription = new Meteor.Collection 'data.ChatSubscription'
+@ChatTyping = new Meteor.Collection 'data.ChatTyping'
\ No newline at end of file
diff --git a/server/methods/receiveMessage.coffee b/server/methods/receiveMessage.coffee
index 3be6b1d6745..e5cfb59007e 100644
--- a/server/methods/receiveMessage.coffee
+++ b/server/methods/receiveMessage.coffee
@@ -108,14 +108,12 @@ Meteor.methods
 		###
 		Save the message. If there was already a typing record, update it.
 		###
-		ChatMessage.upsert
+		ChatTyping.upsert
 			rid: message.rid
-			t: 't'
 			$and: [{ 'u._id': message.u._id }]
 		,
 			$set: message
 			$unset:
-				t: 1
 				expireAt: 1
 
 		Meteor.defer ->
diff --git a/server/methods/typingStatus.coffee b/server/methods/typingStatus.coffee
index f57b359f3ad..409ea8282d3 100644
--- a/server/methods/typingStatus.coffee
+++ b/server/methods/typingStatus.coffee
@@ -6,7 +6,6 @@ Meteor.methods
 		console.log '[methods] typingStatus -> '.green, 'userId:', Meteor.userId(), 'arguments:', arguments
 
 		filter =
-			t: 't'
 			rid: rid
 			$and: [{'u._id': Meteor.userId()}]
 
@@ -15,12 +14,10 @@ Meteor.methods
 				'$set':
 					expireAt: moment().add(30, 'seconds').toDate()
 				'$setOnInsert':
-					msg: '...'
-					ts: moment().add(1, 'years').toDate()
 					'u._id': Meteor.userId()
 					'u.username': Meteor.user().username
 
-			ChatMessage.upsert(filter, msgData)
+			ChatTyping.upsert(filter, msgData)
 		else
 
-			ChatMessage.remove(filter)
+			ChatTyping.remove(filter)
diff --git a/server/publications/typing.coffee b/server/publications/typing.coffee
new file mode 100644
index 00000000000..d7493098bcc
--- /dev/null
+++ b/server/publications/typing.coffee
@@ -0,0 +1,17 @@
+Meteor.publish 'typing', (rid, start) ->
+	unless this.userId
+		return this.ready()
+
+	console.log '[publish] typing ->'.green, 'rid:', rid, 'start:', start
+
+	if typeof rid isnt 'string'
+		return this.ready()
+
+	if not Meteor.call 'canAccessRoom', rid, this.userId
+		return this.ready()
+
+	ChatTyping.find
+		rid: rid
+		'u._id': { $ne: this.userId }
+	,
+		limit: 5
\ No newline at end of file
diff --git a/server/startup/indexes.coffee b/server/startup/indexes.coffee
index e58c9b75b44..72985526016 100644
--- a/server/startup/indexes.coffee
+++ b/server/startup/indexes.coffee
@@ -13,3 +13,7 @@ Meteor.startup ->
 		try ChatMessage._ensureIndex { 'rid': 1, 'ts': 1 } catch e then console.log e
 		try ChatMessage._ensureIndex { 'rid': 1, 't': 1, 'u._id': 1 } catch e then console.log e
 		try ChatMessage._ensureIndex { 'expireAt': 1 }, { expireAfterSeconds: 0 } catch e then console.log e
+
+		try ChatTyping._ensureIndex { 'rid': 1 } catch e then console.log e
+		try ChatTyping._ensureIndex { 'rid': 1, 'u._id': 1 } catch e then console.log e
+		try ChatTyping._ensureIndex { 'expireAt': 1 }, { expireAfterSeconds: 0 } catch e then console.log e
-- 
GitLab