Skip to content
Snippets Groups Projects
Commit ef387394 authored by Gabriel Engel's avatar Gabriel Engel
Browse files

several performance improvements

parent 53566d71
No related branches found
No related tags found
No related merge requests found
Showing
with 216 additions and 154 deletions
......@@ -44,6 +44,7 @@ raix:handlebar-helpers
rocketchat:file
rocketchat:lib
rocketchat:me
rocketchat:mentions
simple:highlight.js
tap:i18n
tmeasday:crypto-md5
......
......@@ -94,6 +94,7 @@ retry@1.0.3
rocketchat:file@0.0.1
rocketchat:lib@0.0.1
rocketchat:me@0.0.1
rocketchat:mentions@0.0.1
routepolicy@1.0.5
service-configuration@1.0.4
session@1.1.0
......
......@@ -37,25 +37,25 @@
send = (rid, input) ->
if _.trim(input.value) isnt ''
KonchatNotification.removeRoomNotification(rid)
message = input.value
msg = input.value
input.value = ''
stopTyping()
Meteor.call 'sendMessage', {rid: rid, message: message, day: window.day }
Meteor.call 'sendMessage', { rid: rid, msg: msg, day: window.day }
update = (id, input) ->
if _.trim(input.value) isnt ''
message = input.value
msg = input.value
input.value = ''
Meteor.call 'updateMessage', {id: id, message: message }
Meteor.call 'updateMessage', { id: id, msg: msg }
startTyping = (rid, input) ->
unless self.typingTimeout
if Meteor.userId()?
Meteor.call 'typingStatus', { rid: rid }, true
self.typingTimeout = Meteor.setTimeout ->
stopTyping()
, 30000
if _.trim(input.value) isnt ''
unless self.typingTimeout
if Meteor.userId()?
Meteor.call 'typingStatus', { rid: rid }, true
self.typingTimeout = Meteor.setTimeout ->
stopTyping()
, 30000
stopTyping = ->
self.typingTimeout = null
......@@ -131,4 +131,4 @@
stopEditingLastMessage: stopEditingLastMessage
send: send
init: init
)()
\ No newline at end of file
)()
Meteor.methods
sendMessage: (msg) ->
sendMessage: (message) ->
Tracker.nonreactive ->
now = new Date(Date.now() + TimeSync.serverOffset())
msg = RocketChat.callbacks.run 'sendMessage', msg
message.ts = new Date(Date.now() + TimeSync.serverOffset())
message.u =
_id: Meteor.userId()
username: Meteor.user().username
message = RocketChat.callbacks.run 'beforeSaveMessage', message
ChatMessage.upsert { rid: msg.rid, t: 't' },
$set:
ts: now
msg: msg.message
'u.username': Meteor.user().username
ChatMessage.upsert
rid: message.rid
t: 't'
,
$set: message
$unset:
t: 1
expireAt: 1
updateMessage: (msg) ->
Tracker.nonreactive ->
now = new Date(Date.now() + TimeSync.serverOffset())
ChatMessage.update { _id: msg.id, 'u._id': Meteor.userId() },
$set:
ets: now
msg: msg.message
Meteor.methods
updateMessage: (message) ->
Tracker.nonreactive ->
message.ets = new Date(Date.now() + TimeSync.serverOffset())
message = RocketChat.callbacks.run 'beforeSaveMessage', message
ChatMessage.update
_id: message.id
'u._id': Meteor.userId()
,
$set:
ets: message.ets
message: message.msg
//
//
// ----------------
//
@import "global/_variables.less";
@import "utils/_lesshat.import.less";
@import "utils/_reset.import.less";
......@@ -9,7 +9,6 @@
@import url(//fonts.googleapis.com/css?family=Roboto:300,400,500,700,900);
@import url(//fonts.googleapis.com/css?family=Muli:400,300,500);
@import "utils/_emojione.import.less";
.cf_ {
display: inline-block;
&:after {
......@@ -1243,6 +1242,12 @@ a.github-fork {
opacity: 0;
}
}
&.has-alert {
.name {
color: #ffffff;
font-weight: bold;
}
}
&.away {
a {
color: #666;
......@@ -1945,7 +1950,7 @@ a.github-fork {
.custom-scroll(transparent, #DADADA);
margin-top: 60px;
overflow-x: hidden;
overflow-y: auto;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
.calc(height, ~"100% - " @footer-min-height + @header-min-height);
> div {
......
......@@ -15,58 +15,56 @@ Template.chatMessageDashboard.helpers
return this._id is Session.get('editingMessageId')
preProcessingMessage: ->
msg = this.msg
# Separate text in code blocks and non code blocks
msgParts = msg.split(/(```.*\n[\s\S]*?\n```)/)
for part, index in msgParts
# Verify if this part is code
codeMatch = part.match(/```(.*)\n([\s\S]*?)\n```/)
if codeMatch?
# Process highlight if this part is code
lang = codeMatch[1]
code = codeMatch[2]
if lang not in hljs.listLanguages()
result = hljs.highlightAuto code
if _.trim(this.msg) isnt ''
msg = this.msg
# Separate text in code blocks and non code blocks
msgParts = msg.split(/(```.*\n[\s\S]*?\n```)/)
for part, index in msgParts
# Verify if this part is code
codeMatch = part.match(/```(.*)\n([\s\S]*?)\n```/)
if codeMatch?
# Process highlight if this part is code
lang = codeMatch[1]
code = codeMatch[2]
if lang not in hljs.listLanguages()
result = hljs.highlightAuto code
else
result = hljs.highlight lang, code
msgParts[index] = "<pre><code class='hljs " + result.language + "'>" + result.value + "</code></pre>"
else
result = hljs.highlight lang, code
msgParts[index] = "<pre><code class='hljs " + result.language + "'>" + result.value + "</code></pre>"
else
# Escape html and fix line breaks for non code blocks
part = _.escapeHTML part
part = part.replace /\n/g, '<br/>'
msgParts[index] = part
# Re-mount message
msg = msgParts.join('')
# Process links in message
msg = Autolinker.link(msg, { stripPrefix: false, twitter: false })
# Process MD like for strong, italic and strike
msg = msg.replace(/\*([^*]+)\*/g, '<strong>$1</strong>')
msg = msg.replace(/\_([^_]+)\_/g, '<i>$1</i>')
msg = msg.replace(/\~([^_]+)\~/g, '<strike>$1</strike>')
# Highlight mentions
if not this.mentions? or this.mentions.length is 0
mentions = _.map this.mentions, (mention) ->
return mention.username or mention
mentions = mentions.join('|')
msg = msg.replace new RegExp("(?:^|\\s)(@(#{mentions}))(?:\\s|$)", 'g'), (match, mention, username) ->
return match.replace mention, "<a href=\"\" class=\"mention-link\" data-username=\"#{username}\">#{mention}</a>"
# Escape html and fix line breaks for non code blocks
part = _.escapeHTML part
part = part.replace /\n/g, '<br/>'
msgParts[index] = part
# Re-mount message
msg = msgParts.join('')
# Process links in message
msg = Autolinker.link(msg, { stripPrefix: false, twitter: false })
# Process MD like for strong, italic and strike
msg = msg.replace(/\*([^*]+)\*/g, '<strong>$1</strong>')
msg = msg.replace(/\_([^_]+)\_/g, '<i>$1</i>')
msg = msg.replace(/\~([^_]+)\~/g, '<strike>$1</strike>')
# Highlight mentions
if not this.mentions? or this.mentions.length is 0
mentions = _.map this.mentions, (mention) ->
return mention.username or mention
mentions = mentions.join('|')
msg = msg.replace new RegExp("(?:^|\\s)(@(#{mentions}))(?:\\s|$)", 'g'), (match, mention, username) ->
return match.replace mention, "<a href=\"\" class=\"mention-link\" data-username=\"#{username}\">#{mention}</a>"
return msg
message: ->
if this.u._id
UserManager.addUser(this.u._id)
else if this.u?.username
UserManager.addUser this.u.username
switch this.t
when 'p' then "<i class='icon-link-ext'></i><a href=\"#{this.url}\" target=\"_blank\">#{this.msg}</a>"
when 'r' then t('chatMessageDashboard.Room_name_changed', { room_name: this.msg, user_by: Session.get('user_' + this.u._id + '_name') }) + '.'
when 'au' then t('chatMessageDashboard.User_added_by', { user_added: this.msg, user_by: Session.get('user_' + this.u._id + '_name') })
when 'ru' then t('chatMessageDashboard.User_removed_by', { user_removed: this.msg, user_by: Session.get('user_' + this.u._id + '_name') })
......
......@@ -25,7 +25,7 @@
{{#if isEditing}}
<div class="input-message-editing-container">
{{> messagePopupConfig getPupupConfig}}
<textarea class="input-message-editing">{{message}}</textarea>
<textarea class="input-message-editing">{{msg}}</textarea>
</div>
{{else}}
{{#if ../single}}
......@@ -42,7 +42,7 @@
</span>
{{/if}}
<div>
{{#emojione}}{{preProcessingMessage message}}{{/emojione}}
{{#emojione}}{{preProcessingMessage msg}}{{/emojione}}
</div>
{{/if}}
{{/if}}
......
Template.chatRoomItem.helpers
alert: ->
return this.alert if (not Router.current().params._id) or Router.current().params._id isnt this.rid
unread: ->
if (not Router.current().params._id) or Router.current().params._id isnt this.rid
return this.unread
else if Router.current().params._id is this.rid and this.unread > 0
Meteor.call 'readMessages', this.rid
return this.unread if (not Router.current().params._id) or Router.current().params._id isnt this.rid
isDirectRoom: ->
return this.t is 'd'
......@@ -26,7 +27,10 @@ Template.chatRoomItem.helpers
when 'p' then return 'icon-lock'
active: ->
return 'active' if Router.current().params._id? and Router.current().params._id is this.rid
if Router.current().params._id? and Router.current().params._id is this.rid
if this.alert or this.unread > 0
Meteor.call 'readMessages', this.rid
return 'active'
canLeave: ->
roomData = Session.get('roomData' + this.rid)
......
<template name="chatRoomItem">
<li class="link-room-{{rid}} {{active}} {{#if unread}}has-unread{{/if}}">
<li class="link-room-{{rid}} {{active}} {{#if unread}}has-unread{{/if}} {{#if alert}}has-alert{{/if}}">
<a href="{{ pathFor 'room' _id=rid}}" title="{{name}}">
{{#if unread}}
<span class="unread">{{unread}}</span>
{{/if}}
<i class="{{roomIcon}} {{userStatus}}"></i>
<span>{{name}}</span>
<span class='name'>{{name}}</span>
<span class='opt'>
<i class="icon-cancel-circled hide-room" title="{{_ "chatRoomItem.Hide_room"}}"></i>
{{#if canLeave}}
......
###
# Me is a named function that will replace /me commands
# @param {Object} doc - The message object
# @param {Object} message - The message object
###
class Me
constructor: (doc) ->
constructor: (message) ->
# If message starts with /me, replace it for text formatting
if doc.message.indexOf('/me ') is 0
doc.message = '_' + doc.message.substr(4) + '_'
return doc
if message.msg.indexOf('/me ') is 0
message.msg = '_' + message.msg.substr(4) + '_'
return message
RocketChat.callbacks.add 'sendMessage', Me
RocketChat.callbacks.add 'beforeSaveMessage', Me
......@@ -13,7 +13,7 @@ Package.onUse(function(api) {
'rocketchat:lib@0.0.1'
]);
api.addFiles('me.coffee', 'server');
api.addFiles('me.coffee', ['server','client']);
});
Package.onTest(function(api) {
......
Package.describe({
name: 'rocketchat:mentions',
version: '0.0.1',
summary: 'Message pre-processor that will process mentions',
git: ''
});
Package.onUse(function(api) {
api.versionsFrom('1.0');
api.use([
'coffeescript',
'rocketchat:lib@0.0.1'
]);
api.addFiles('server.coffee', 'server');
});
Package.onTest(function(api) {
});
###
# Mentions is a named function that will process Mentions
# @param {Object} message - The message object
###
class Mentions
constructor: (message) ->
# If message starts with /me, replace it for text formatting
mentions = []
message.msg.replace /(?:^|\s|\n)(?:@)([A-Za-z0-9-_.]+)/g, (match, mention) ->
mentions.push mention
if mentions.length isnt 0
mentions = _.unique mentions
verifiedMentions = []
mentions.forEach (mention) ->
verifiedMention = Meteor.users.findOne({username: mention}, {fields: {_id: 1, username: 1}})
verifiedMentions.push verifiedMention if verifiedMention?
if verifiedMentions.length isnt 0
message.mentions = verifiedMentions
return message
RocketChat.callbacks.add 'beforeSaveMessage', Mentions
Meteor.methods
canAccessRoom: (roomId, userId) ->
# fromId = Meteor.userId()
# console.log '[methods] canAccessRoom -> '.green, 'fromId:', fromId, 'roomId:', roomId
# console.log 'userId ->',Meteor.userId(), this.userId
console.log '[methods] canAccessRoom -> '.green, 'userId:', userId, 'roomId:', roomId
user = Meteor.users.findOne userId, fields: username: 1
......@@ -23,8 +20,8 @@ Meteor.methods
else if room.usernames.indexOf(user.username) isnt -1
canAccess = true
# if canAccess isnt true
# throw new Meteor.Error 'without-permission', "[methods] canAccessRoom -> User doesn't have enough permissions"
if canAccess isnt true
throw new Meteor.Error 'without-permission', "[methods] canAccessRoom -> User doesn't have enough permissions"
# # create room subscription
# ChatSubscription.upsert { rid: roomId, $and: [{'u._id': Meteor.userId()}] },
......
Meteor.methods
readMessages: (room) ->
fromId = Meteor.userId()
# console.log '[methods] readMessages -> '.green, 'fromId:', fromId, 'room:', room
if Meteor.userId()
filter = { rid: room, 'u._id': Meteor.userId() }
else
throw new Meteor.Error 203, '[methods] readMessages -> Invalid user'
if not Meteor.userId()
throw new Meteor.Error 'invalid-user', '[methods] readMessages -> Invalid user'
ChatSubscription.update filter, { $set: { unread: 0, ls: (new Date()) } }
ChatSubscription.update
rid: room
'u._id': Meteor.userId()
,
$set:
alert: false
unread: 0
ls: (new Date())
......@@ -8,24 +8,11 @@ Meteor.methods
if not Meteor.call 'canAccessRoom', message.rid, Meteor.userId()
return false
now = new Date()
mentions = []
message.msg.replace /(?:^|\s|\n)(?:@)([A-Za-z0-9-_.]+)/g, (match, mention) ->
mentions.push mention
mentions = _.unique mentions
mentions = mentions.filter (mention) ->
return Meteor.users.findOne({username: mention}, {fields: {_id: 1}})?
mentions = mentions.map (mention) ->
return {
username: mention
}
if mentions.length is 0
mentions = undefined
message = RocketChat.callbacks.run 'sendMessage', message
message.u = Meteor.users.findOne Meteor.userId(), fields: username: 1
message.ts = new Date()
message = RocketChat.callbacks.run 'beforeSaveMessage', message
console.log "message", message
###
Defer other updated as their return is not interesting to the user
......@@ -41,16 +28,36 @@ Meteor.methods
,
# update the last message timestamp
$set:
lm: now
lm: message.ts
# increate the messages counter
$inc:
msgs: 1
#ChatSubscription.update { rid: message.rid, 'u._id': { $ne: Meteor.userId() } }, { $inc: { unread: 1 }, $set: { ts: now } }, { multi: true } // only for mentioned
message.mentions?.forEach (mention) ->
###
Update all other subscriptions of mentioned users to alert their owners and incrementing
the unread counter for mentions and direct messages
###
ChatSubscription.update
# only subscriptions to the same room
rid: message.rid
# not the msg owner
'u._id': mention._id
,
$set:
# alert de user
alert: true
# open the room for the user
open: true
# increment undear couter
$inc:
unread: 1
,
# make sure we alert all matching subscription
multi: true
###
Update all other subscriptions to alert the their owners but witout incrementing
Update all other subscriptions to alert their owners but witout incrementing
the unread counter, as it is only for mentions and direct messages
###
ChatSubscription.update
......@@ -60,7 +67,7 @@ Meteor.methods
alert: false
# not the msg owner
'u._id':
$ne: Meteor.userId()
$ne: message.u._id
,
$set:
# alert de user
......@@ -71,35 +78,15 @@ Meteor.methods
# make sure we alert all matching subscription
multi: true
###
Save the message. If there was already a typing record, update it.
###
ChatMessage.upsert
rid: message.rid
t: 't'
$and: [{ 'u._id': Meteor.userId() }]
$and: [{ 'u._id': message.u._id }]
,
$set:
'u._id': Meteor.userId()
'u.username': Meteor.user().username
ts: now
msg: message.msg
mentions: mentions
$set: message
$unset:
t: 1
expireAt: 1
updateMessage: (msg) ->
fromId = Meteor.userId()
# console.log '[methods] updateMessage -> '.green, 'fromId:', fromId, 'msg:', msg
if not Meteor.userId()
throw new Meteor.Error('invalid-user', "[methods] updateMessage -> Invalid user")
now = new Date()
messageFilter = { _id: message.id, 'u._id': Meteor.userId() }
ChatMessage.update messageFilter,
$set:
ets: now
msg: message.msg
return
Meteor.methods
updateMessage: (message) ->
if not Meteor.userId()
throw new Meteor.Error('invalid-user', "[methods] updateMessage -> Invalid user")
message.ets = new Date()
message = RocketChat.callbacks.run 'beforeSaveMessage', message
ChatMessage.update
_id: message.id
'u._id': Meteor.userId
,
$set: message
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment