From 4c41f378456b6d68054e140c3642f31b08309df2 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Thu, 25 May 2017 14:27:45 -0300 Subject: [PATCH 1/2] Convert Livechat files from Coffeescript to JavaScript --- .../rocketchat-livechat/app/.meteor/packages | 1 - .../app/client/lib/_visitor.coffee | 57 ---- .../app/client/lib/_visitor.js | 66 ++++ .../app/client/lib/chatMessages.coffee | 220 -------------- .../app/client/lib/chatMessages.js | 281 ++++++++++++++++++ .../app/client/lib/collections.coffee | 2 - .../app/client/lib/collections.js | 2 + .../app/client/lib/error.coffee | 2 - .../app/client/lib/error.js | 3 + .../client/lib/fromApp/Notifications.coffee | 45 --- .../app/client/lib/fromApp/Notifications.js | 79 +++++ .../lib/fromApp/RoomHistoryManager.coffee | 70 ----- .../client/lib/fromApp/RoomHistoryManager.js | 232 +++++++++++++++ .../app/client/lib/fromApp/avatar.coffee | 18 -- .../app/client/lib/fromApp/avatar.js | 21 ++ .../app/client/lib/msgTyping.coffee | 69 ----- .../app/client/lib/msgTyping.js | 77 +++++ .../app/client/lib/parentCall.coffee | 7 - .../app/client/lib/parentCall.js | 9 + .../app/client/lib/tapi18n.coffee | 15 - .../app/client/lib/tapi18n.js | 23 ++ .../client/methods/sendMessageExternal.coffee | 18 -- .../app/client/methods/sendMessageExternal.js | 24 ++ .../app/client/routes/router.coffee | 12 - .../app/client/routes/router.js | 11 + .../app/client/startup/visitor.coffee | 14 - .../app/client/startup/visitor.js | 21 ++ .../app/client/views/avatar.coffee | 14 - .../app/client/views/avatar.js | 18 ++ .../app/client/views/message.coffee | 86 ------ .../app/client/views/message.js | 114 +++++++ 31 files changed, 981 insertions(+), 650 deletions(-) delete mode 100644 packages/rocketchat-livechat/app/client/lib/_visitor.coffee create mode 100644 packages/rocketchat-livechat/app/client/lib/_visitor.js delete mode 100644 packages/rocketchat-livechat/app/client/lib/chatMessages.coffee create mode 100644 packages/rocketchat-livechat/app/client/lib/chatMessages.js delete mode 100644 packages/rocketchat-livechat/app/client/lib/collections.coffee create mode 100644 packages/rocketchat-livechat/app/client/lib/collections.js delete mode 100644 packages/rocketchat-livechat/app/client/lib/error.coffee create mode 100644 packages/rocketchat-livechat/app/client/lib/error.js delete mode 100644 packages/rocketchat-livechat/app/client/lib/fromApp/Notifications.coffee create mode 100644 packages/rocketchat-livechat/app/client/lib/fromApp/Notifications.js delete mode 100644 packages/rocketchat-livechat/app/client/lib/fromApp/RoomHistoryManager.coffee create mode 100644 packages/rocketchat-livechat/app/client/lib/fromApp/RoomHistoryManager.js delete mode 100644 packages/rocketchat-livechat/app/client/lib/fromApp/avatar.coffee create mode 100644 packages/rocketchat-livechat/app/client/lib/fromApp/avatar.js delete mode 100644 packages/rocketchat-livechat/app/client/lib/msgTyping.coffee create mode 100644 packages/rocketchat-livechat/app/client/lib/msgTyping.js delete mode 100644 packages/rocketchat-livechat/app/client/lib/parentCall.coffee create mode 100644 packages/rocketchat-livechat/app/client/lib/parentCall.js delete mode 100644 packages/rocketchat-livechat/app/client/lib/tapi18n.coffee create mode 100644 packages/rocketchat-livechat/app/client/lib/tapi18n.js delete mode 100644 packages/rocketchat-livechat/app/client/methods/sendMessageExternal.coffee create mode 100644 packages/rocketchat-livechat/app/client/methods/sendMessageExternal.js delete mode 100644 packages/rocketchat-livechat/app/client/routes/router.coffee create mode 100644 packages/rocketchat-livechat/app/client/routes/router.js delete mode 100644 packages/rocketchat-livechat/app/client/startup/visitor.coffee create mode 100644 packages/rocketchat-livechat/app/client/startup/visitor.js delete mode 100644 packages/rocketchat-livechat/app/client/views/avatar.coffee create mode 100644 packages/rocketchat-livechat/app/client/views/avatar.js delete mode 100644 packages/rocketchat-livechat/app/client/views/message.coffee create mode 100644 packages/rocketchat-livechat/app/client/views/message.js diff --git a/packages/rocketchat-livechat/app/.meteor/packages b/packages/rocketchat-livechat/app/.meteor/packages index e777a77fc027..b9e32033860a 100644 --- a/packages/rocketchat-livechat/app/.meteor/packages +++ b/packages/rocketchat-livechat/app/.meteor/packages @@ -22,7 +22,6 @@ underscore@1.0.10 jquery@1.11.10 random@1.0.10 ejson@1.0.13 -coffeescript rocketchat:streamer kadira:flow-router kadira:blaze-layout diff --git a/packages/rocketchat-livechat/app/client/lib/_visitor.coffee b/packages/rocketchat-livechat/app/client/lib/_visitor.coffee deleted file mode 100644 index 9b93ddba257d..000000000000 --- a/packages/rocketchat-livechat/app/client/lib/_visitor.coffee +++ /dev/null @@ -1,57 +0,0 @@ -msgStream = new Meteor.Streamer 'room-messages' - -@visitor = new class - token = new ReactiveVar null - room = new ReactiveVar null - roomToSubscribe = new ReactiveVar null - roomSubscribed = null - - register = -> - if not localStorage.getItem 'visitorToken' - localStorage.setItem 'visitorToken', Random.id() - - token.set localStorage.getItem 'visitorToken' - - getToken = -> - return token.get() - - setRoom = (rid) -> - room.set rid - - getRoom = (createOnEmpty = false) -> - roomId = room.get() - if not roomId? and createOnEmpty - roomId = Random.id() - room.set roomId - - return roomId - - isSubscribed = (roomId) -> - return roomSubscribed is roomId - - subscribeToRoom = (roomId) -> - if roomSubscribed? - return if roomSubscribed is roomId - - roomSubscribed = roomId - - msgStream.on roomId, (msg) -> - if msg.t is 'command' - Commands[msg.msg]?() - else if msg.t isnt 'livechat_video_call' - ChatMessage.upsert { _id: msg._id }, msg - - if msg.t is 'livechat-close' - parentCall('callback', 'chat-ended') - - # notification sound - if Session.equals('sound', true) - if msg.u._id isnt Meteor.user()._id - $('#chatAudioNotification')[0].play(); - - register: register - getToken: getToken - setRoom: setRoom - getRoom: getRoom - subscribeToRoom: subscribeToRoom - isSubscribed: isSubscribed diff --git a/packages/rocketchat-livechat/app/client/lib/_visitor.js b/packages/rocketchat-livechat/app/client/lib/_visitor.js new file mode 100644 index 000000000000..21080388f652 --- /dev/null +++ b/packages/rocketchat-livechat/app/client/lib/_visitor.js @@ -0,0 +1,66 @@ +/* globals Commands */ +const msgStream = new Meteor.Streamer('room-messages'); + +this.visitor = new class { + constructor() { + this.token = new ReactiveVar(null); + this.room = new ReactiveVar(null); + this.roomToSubscribe = new ReactiveVar(null); + this.roomSubscribed = null; + } + + register() { + if (!localStorage.getItem('visitorToken')) { + localStorage.setItem('visitorToken', Random.id()); + } + + this.token.set(localStorage.getItem('visitorToken')); + } + + getToken() { + return this.token.get(); + } + + setRoom(rid) { + this.room.set(rid); + } + + getRoom(createOnEmpty = false) { + let roomId = this.room.get(); + if (!roomId && createOnEmpty) { + roomId = Random.id(); + this.room.set(roomId); + } + + return roomId; + } + + isSubscribed(roomId) { + return this.roomSubscribed === roomId; + } + + subscribeToRoom(roomId) { + if (this.roomSubscribed && this.roomSubscribed === roomId) { + return; + } + + this.roomSubscribed = roomId; + + msgStream.on(roomId, (msg) => { + if (msg.t === 'command') { + Commands[msg.msg] && Commands[msg.msg](); + } else if (msg.t !== 'livechat_video_call') { + ChatMessage.upsert({ _id: msg._id }, msg); + + if (msg.t === 'livechat-close') { + parentCall('callback', 'chat-ended'); + } + + // notification sound + if (Session.equals('sound', true) && msg.u._id !== Meteor.userId()) { + $('#chatAudioNotification')[0].play(); + } + } + }); + } +}; diff --git a/packages/rocketchat-livechat/app/client/lib/chatMessages.coffee b/packages/rocketchat-livechat/app/client/lib/chatMessages.coffee deleted file mode 100644 index 271a7caf5f51..000000000000 --- a/packages/rocketchat-livechat/app/client/lib/chatMessages.coffee +++ /dev/null @@ -1,220 +0,0 @@ -import toastr from 'toastr' -class @ChatMessages - init: (node) -> - this.editing = {} - - # this.messageMaxSize = RocketChat.settings.get('Message_MaxAllowedSize') - this.wrapper = $(node).find(".wrapper") - this.input = $(node).find(".input-message").get(0) - # this.bindEvents() - return - - resize: -> - dif = 60 + $(".messages-container").find("footer").outerHeight() - $(".messages-box").css - height: "calc(100% - #{dif}px)" - - toPrevMessage: -> - msgs = this.wrapper.get(0).querySelectorAll(".own:not(.system)") - if msgs.length - if this.editing.element - if msgs[this.editing.index - 1] - this.edit msgs[this.editing.index - 1], this.editing.index - 1 - else - this.edit msgs[msgs.length - 1], msgs.length - 1 - - toNextMessage: -> - if this.editing.element - msgs = this.wrapper.get(0).querySelectorAll(".own:not(.system)") - if msgs[this.editing.index + 1] - this.edit msgs[this.editing.index + 1], this.editing.index + 1 - else - this.clearEditing() - - getEditingIndex: (element) -> - msgs = this.wrapper.get(0).querySelectorAll(".own:not(.system)") - index = 0 - for msg in msgs - if msg is element - return index - index++ - return -1 - - edit: (element, index) -> - return if element.classList.contains("system") - this.clearEditing() - id = element.getAttribute("id") - message = ChatMessage.findOne { _id: id, 'u._id': Meteor.userId() } - this.input.value = message.msg - this.editing.element = element - this.editing.index = index or this.getEditingIndex(element) - this.editing.id = id - element.classList.add("editing") - this.input.classList.add("editing") - setTimeout => - this.input.focus() - , 5 - - clearEditing: -> - if this.editing.element - this.editing.element.classList.remove("editing") - this.input.classList.remove("editing") - this.editing.id = null - this.editing.element = null - this.editing.index = null - this.input.value = this.editing.saved or "" - else - this.editing.saved = this.input.value - - send: (rid, input) -> - if s.trim(input.value) isnt '' - if this.isMessageTooLong(input) - return toastr.error t('Message_too_long') - # KonchatNotification.removeRoomNotification(rid) - msg = input.value - input.value = '' - rid ?= visitor.getRoom(true) - - sendMessage = (callback) -> - msgObject = { - _id: Random.id(), - rid: rid, - msg: msg, - token: visitor.getToken() - } - MsgTyping.stop(rid) - - Meteor.call 'sendMessageLivechat', msgObject, (error, result) -> - if error - ChatMessage.update msgObject._id, { $set: { error: true } } - showError error.reason - - if result?.rid? and not visitor.isSubscribed(result.rid) - Livechat.connecting = result.showConnecting - ChatMessage.update result._id, _.omit(result, '_id') - Livechat.room = result.rid - - parentCall('callback', 'chat-started'); - - if not Meteor.userId() - guest = { - token: visitor.getToken() - } - - if Livechat.department - guest.department = Livechat.department - - Meteor.call 'livechat:registerGuest', guest, (error, result) -> - if error? - return showError error.reason - - Meteor.loginWithToken result.token, (error) -> - if error - return showError error.reason - - sendMessage() - else - sendMessage() - - deleteMsg: (message) -> - Meteor.call 'deleteMessage', message, (error, result) -> - if error - return handleError(error) - - update: (id, rid, input) -> - if s.trim(input.value) isnt '' - msg = input.value - Meteor.call 'updateMessage', { id: id, msg: msg } - this.clearEditing() - MsgTyping.stop(rid) - - startTyping: (rid, input) -> - if s.trim(input.value) isnt '' - MsgTyping.start(rid) - else - MsgTyping.stop(rid) - - bindEvents: -> - if this.wrapper?.length - $(".input-message").autogrow - postGrowCallback: => - this.resize() - - tryCompletion: (input) -> - value = input.value.match(/[^\s]+$/) - if value?.length > 0 - value = value[0] - - re = new RegExp value, 'i' - - user = Meteor.users.findOne username: re - if user? - input.value = input.value.replace value, "@#{user.username} " - - keyup: (rid, event) -> - input = event.currentTarget - k = event.which - keyCodes = [ - 13, # Enter - 20, # Caps lock - 16, # Shift - 9, # Tab - 27, # Escape Key - 17, # Control Key - 91, # Windows Command Key - 19, # Pause Break - 18, # Alt Key - 93, # Right Click Point Key - 45, # Insert Key - 34, # Page Down - 35, # Page Up - 144, # Num Lock - 145 # Scroll Lock - ] - keyCodes.push i for i in [35..40] # Home, End, Arrow Keys - keyCodes.push i for i in [112..123] # F1 - F12 - - unless k in keyCodes - this.startTyping(rid, input) - - keydown: (rid, event) -> - input = event.currentTarget - k = event.which - this.resize(input) - if k is 13 and not event.shiftKey and not event.ctrlKey and not event.altKey # Enter without shift/ctrl/alt - event.preventDefault() - event.stopPropagation() - if this.editing.id - this.update(this.editing.id, rid, input) - else - this.send(rid, input) - return - - if k is 9 - event.preventDefault() - event.stopPropagation() - @tryCompletion input - - if k is 27 - if this.editing.id - event.preventDefault() - event.stopPropagation() - this.clearEditing() - return - # else if k is 38 or k is 40 # Arrow Up or down - # if k is 38 - # return if input.value.slice(0, input.selectionStart).match(/[\n]/) isnt null - # this.toPrevMessage() - # else - # return if input.value.slice(input.selectionEnd, input.value.length).match(/[\n]/) isnt null - # this.toNextMessage() - - # event.preventDefault() - # event.stopPropagation() - - # ctrl (command) + shift + k -> clear room messages - else if k is 75 and ((navigator?.platform?.indexOf('Mac') isnt -1 and event.metaKey and event.shiftKey) or (navigator?.platform?.indexOf('Mac') is -1 and event.ctrlKey and event.shiftKey)) - RoomHistoryManager.clear rid - - isMessageTooLong: (input) -> - input?.value.length > this.messageMaxSize diff --git a/packages/rocketchat-livechat/app/client/lib/chatMessages.js b/packages/rocketchat-livechat/app/client/lib/chatMessages.js new file mode 100644 index 000000000000..2649809fd286 --- /dev/null +++ b/packages/rocketchat-livechat/app/client/lib/chatMessages.js @@ -0,0 +1,281 @@ +/* globals MsgTyping, showError, Livechat */ +import toastr from 'toastr'; + +this.ChatMessages = class ChatMessages { + init(node) { + this.editing = {}; + + // this.messageMaxSize = RocketChat.settings.get('Message_MaxAllowedSize') + this.wrapper = $(node).find('.wrapper'); + this.input = $(node).find('.input-message').get(0); + // this.bindEvents() + return; + } + + resize() { + const dif = 60 + $('.messages-container').find('footer').outerHeight(); + return $('.messages-box').css({ + height: `calc(100% - ${ dif }px)` + }); + } + + toPrevMessage() { + const msgs = this.wrapper.get(0).querySelectorAll('.own:not(.system)'); + if (msgs.length) { + if (this.editing.element) { + if (msgs[this.editing.index - 1]) { + this.edit(msgs[this.editing.index - 1], this.editing.index - 1); + } + } else { + this.edit(msgs[msgs.length - 1], msgs.length - 1); + } + } + } + + toNextMessage() { + if (this.editing.element) { + const msgs = this.wrapper.get(0).querySelectorAll('.own:not(.system)'); + if (msgs[this.editing.index + 1]) { + this.edit(msgs[this.editing.index + 1], this.editing.index + 1); + } else { + this.clearEditing(); + } + } + } + + getEditingIndex(element) { + const msgs = this.wrapper.get(0).querySelectorAll('.own:not(.system)'); + let index = 0; + for (const msg of Array.from(msgs)) { + if (msg === element) { + return index; + } + index++; + } + return -1; + } + + edit(element, index) { + if (element.classList.contains('system')) { + return; + } + this.clearEditing(); + const id = element.getAttribute('id'); + const message = ChatMessage.findOne({ _id: id, 'u._id': Meteor.userId() }); + this.input.value = message.msg; + this.editing.element = element; + this.editing.index = index || this.getEditingIndex(element); + this.editing.id = id; + element.classList.add('editing'); + this.input.classList.add('editing'); + setTimeout(() => { + this.input.focus(); + }, 5); + } + + clearEditing() { + if (this.editing.element) { + this.editing.element.classList.remove('editing'); + this.input.classList.remove('editing'); + this.editing.id = null; + this.editing.element = null; + this.editing.index = null; + this.input.value = this.editing.saved || ''; + } else { + this.editing.saved = this.input.value; + } + } + + send(rid, input) { + if (s.trim(input.value) === '') { + return; + } + if (this.isMessageTooLong(input)) { + return toastr.error(t('Message_too_long')); + } + // KonchatNotification.removeRoomNotification(rid) + const msg = input.value; + input.value = ''; + if (!rid) { + rid = visitor.getRoom(true); + } + + const sendMessage = () => { + const msgObject = { + _id: Random.id(), + rid, + msg, + token: visitor.getToken() + }; + MsgTyping.stop(rid); + + Meteor.call('sendMessageLivechat', msgObject, (error, result) => { + if (error) { + ChatMessage.update(msgObject._id, { $set: { error: true } }); + showError(error.reason); + } + + if (result && result.rid && !visitor.isSubscribed(result.rid)) { + Livechat.connecting = result.showConnecting; + ChatMessage.update(result._id, _.omit(result, '_id')); + Livechat.room = result.rid; + + parentCall('callback', 'chat-started'); + } + }); + }; + + if (!Meteor.userId()) { + const guest = { + token: visitor.getToken() + }; + + if (Livechat.department) { + guest.department = Livechat.department; + } + + Meteor.call('livechat:registerGuest', guest, (error, result) => { + if (error) { + return showError(error.reason); + } + + Meteor.loginWithToken(result.token, (error) => { + if (error) { + return showError(error.reason); + } + + sendMessage(); + }); + }); + } else { + sendMessage(); + } + } + + deleteMsg(message) { + Meteor.call('deleteMessage', message, (error) => { + if (error) { + return handleError(error); + } + }); + } + + update(id, rid, input) { + if (s.trim(input.value) !== '') { + const msg = input.value; + Meteor.call('updateMessage', { id, msg }); + this.clearEditing(); + MsgTyping.stop(rid); + } + } + + startTyping(rid, input) { + if (s.trim(input.value) !== '') { + MsgTyping.start(rid); + } else { + MsgTyping.stop(rid); + } + } + + bindEvents() { + if (this.wrapper && this.wrapper.length) { + $('.input-message').autogrow({ + postGrowCallback: () => { + this.resize(); + } + }); + } + } + + tryCompletion(input) { + let value = input.value.match(/[^\s]+$/); + if (value && value.length > 0) { + value = value[0]; + + const re = new RegExp(value, 'i'); + + const user = Meteor.users.findOne({ username: re }, { fields: { username: 1 } }); + if (user) { + input.value = input.value.replace(value, `@${ user.username } `); + } + } + } + + keyup(rid, event) { + let i; + const input = event.currentTarget; + const k = event.which; + const keyCodes = [ + 13, // Enter + 20, // Caps lock + 16, // Shift + 9, // Tab + 27, // Escape Key + 17, // Control Key + 91, // Windows Command Key + 19, // Pause Break + 18, // Alt Key + 93, // Right Click Point Key + 45, // Insert Key + 34, // Page Down + 35, // Page Up + 144, // Num Lock + 145 // Scroll Lock + ]; + for (i = 35; i <= 40; i++) { keyCodes.push(i); } // Home, End, Arrow Keys + for (i = 112; i <= 123; i++) { keyCodes.push(i); } // F1 - F12 + + if (!Array.from(keyCodes).includes(k)) { + this.startTyping(rid, input); + } + } + + keydown(rid, event) { + const input = event.currentTarget; + const k = event.which; + this.resize(input); + if (k === 13 && !event.shiftKey && !event.ctrlKey && !event.altKey) { // Enter without shift/ctrl/alt + event.preventDefault(); + event.stopPropagation(); + if (this.editing.id) { + this.update(this.editing.id, rid, input); + } else { + this.send(rid, input); + } + return; + } + + if (k === 9) { + event.preventDefault(); + event.stopPropagation(); + this.tryCompletion(input); + } + + if (k === 27) { + if (this.editing.id) { + event.preventDefault(); + event.stopPropagation(); + this.clearEditing(); + return; + } + // else if k is 38 or k is 40 # Arrow Up or down + // if k is 38 + // return if input.value.slice(0, input.selectionStart).match(/[\n]/) isnt null + // this.toPrevMessage() + // else + // return if input.value.slice(input.selectionEnd, input.value.length).match(/[\n]/) isnt null + // this.toNextMessage() + + // event.preventDefault() + // event.stopPropagation() + + // ctrl (command) + shift + k -> clear room messages + } else if (k === 75 && ((navigator.platform.indexOf('Mac') !== -1 && event.metaKey && event.shiftKey) || (navigator.platform.indexOf('Mac') === -1 && event.ctrlKey && event.shiftKey))) { + RoomHistoryManager.clear(rid); + } + } + + isMessageTooLong(input) { + return input && input.value.length > this.messageMaxSize; + } +}; diff --git a/packages/rocketchat-livechat/app/client/lib/collections.coffee b/packages/rocketchat-livechat/app/client/lib/collections.coffee deleted file mode 100644 index 12de3445db87..000000000000 --- a/packages/rocketchat-livechat/app/client/lib/collections.coffee +++ /dev/null @@ -1,2 +0,0 @@ -@ChatMessage = new Mongo.Collection null -@Department = new Mongo.Collection null diff --git a/packages/rocketchat-livechat/app/client/lib/collections.js b/packages/rocketchat-livechat/app/client/lib/collections.js new file mode 100644 index 000000000000..1feb9caace84 --- /dev/null +++ b/packages/rocketchat-livechat/app/client/lib/collections.js @@ -0,0 +1,2 @@ +this.ChatMessage = new Mongo.Collection(null); +this.Department = new Mongo.Collection(null); diff --git a/packages/rocketchat-livechat/app/client/lib/error.coffee b/packages/rocketchat-livechat/app/client/lib/error.coffee deleted file mode 100644 index 0b3058978b73..000000000000 --- a/packages/rocketchat-livechat/app/client/lib/error.coffee +++ /dev/null @@ -1,2 +0,0 @@ -@showError = (msg) -> - $('.error').addClass('show').find('span').html(msg) diff --git a/packages/rocketchat-livechat/app/client/lib/error.js b/packages/rocketchat-livechat/app/client/lib/error.js new file mode 100644 index 000000000000..7fd5501fc2a1 --- /dev/null +++ b/packages/rocketchat-livechat/app/client/lib/error.js @@ -0,0 +1,3 @@ +this.showError = msg => { + $('.error').addClass('show').find('span').html(msg); +}; diff --git a/packages/rocketchat-livechat/app/client/lib/fromApp/Notifications.coffee b/packages/rocketchat-livechat/app/client/lib/fromApp/Notifications.coffee deleted file mode 100644 index 9fd588378e23..000000000000 --- a/packages/rocketchat-livechat/app/client/lib/fromApp/Notifications.coffee +++ /dev/null @@ -1,45 +0,0 @@ -@Notifications = new class - constructor: -> - @debug = false - @streamAll = new Meteor.Streamer 'notify-all' - @streamRoom = new Meteor.Streamer 'notify-room' - @streamUser = new Meteor.Streamer 'notify-user' - - if @debug is true - @onAll -> console.log "RocketChat.Notifications: onAll", arguments - @onUser -> console.log "RocketChat.Notifications: onAll", arguments - - - notifyRoom: (room, eventName, args...) -> - console.log "RocketChat.Notifications: notifyRoom", arguments if @debug is true - - args.unshift "#{room}/#{eventName}" - @streamRoom.emit.apply @streamRoom, args - - notifyUser: (userId, eventName, args...) -> - console.log "RocketChat.Notifications: notifyUser", arguments if @debug is true - - args.unshift "#{userId}/#{eventName}" - @streamUser.emit.apply @streamUser, args - - onAll: (eventName, callback) -> - @streamAll.on eventName, callback - - onRoom: (room, eventName, callback) -> - if @debug is true - @streamRoom.on room, -> console.log "RocketChat.Notifications: onRoom #{room}", arguments - - @streamRoom.on "#{room}/#{eventName}", callback - - onUser: (eventName, callback) -> - @streamUser.on "#{Meteor.userId()}/#{eventName}", callback - - - unAll: (callback) -> - @streamAll.removeListener 'notify', callback - - unRoom: (room, eventName, callback) -> - @streamRoom.removeListener "#{room}/#{eventName}", callback - - unUser: (callback) -> - @streamUser.removeListener Meteor.userId(), callback diff --git a/packages/rocketchat-livechat/app/client/lib/fromApp/Notifications.js b/packages/rocketchat-livechat/app/client/lib/fromApp/Notifications.js new file mode 100644 index 000000000000..d33705486995 --- /dev/null +++ b/packages/rocketchat-livechat/app/client/lib/fromApp/Notifications.js @@ -0,0 +1,79 @@ +this.Notifications = new class { + constructor() { + this.logged = Meteor.userId() !== null; + this.loginCb = []; + Tracker.autorun(() => { + if (Meteor.userId() !== null && this.logged === false) { + this.loginCb.forEach(cb => cb()); + } + return this.logged = Meteor.userId() !== null; + }); + this.debug = false; + this.streamAll = new Meteor.Streamer('notify-all'); + this.streamLogged = new Meteor.Streamer('notify-logged'); + this.streamRoom = new Meteor.Streamer('notify-room'); + this.streamRoomUsers = new Meteor.Streamer('notify-room-users'); + this.streamUser = new Meteor.Streamer('notify-user'); + if (this.debug === true) { + this.onAll(function() { + return console.log('RocketChat.Notifications: onAll', arguments); + }); + this.onUser(function() { + return console.log('RocketChat.Notifications: onAll', arguments); + }); + } + } + + onLogin(cb) { + this.loginCb.push(cb); + if (this.logged) { + return cb(); + } + } + notifyRoom(room, eventName, ...args) { + if (this.debug === true) { + console.log('RocketChat.Notifications: notifyRoom', arguments); + } + args.unshift(`${ room }/${ eventName }`); + return this.streamRoom.emit.apply(this.streamRoom, args); + } + notifyUser(userId, eventName, ...args) { + if (this.debug === true) { + console.log('RocketChat.Notifications: notifyUser', arguments); + } + args.unshift(`${ userId }/${ eventName }`); + return this.streamUser.emit.apply(this.streamUser, args); + } + onAll(eventName, callback) { + return this.streamAll.on(eventName, callback); + } + onLogged(eventName, callback) { + return this.onLogin(() => { + return this.streamLogged.on(eventName, callback); + }); + } + onRoom(room, eventName, callback) { + if (this.debug === true) { + this.streamRoom.on(room, function() { + return console.log(`RocketChat.Notifications: onRoom ${ room }`, arguments); + }); + } + return this.streamRoom.on(`${ room }/${ eventName }`, callback); + } + onUser(eventName, callback) { + return this.streamUser.on(`${ Meteor.userId() }/${ eventName }`, callback); + } + unAll(callback) { + return this.streamAll.removeListener('notify', callback); + } + unLogged(callback) { + return this.streamLogged.removeListener('notify', callback); + } + unRoom(room, eventName, callback) { + return this.streamRoom.removeListener(`${ room }/${ eventName }`, callback); + } + unUser(callback) { + return this.streamUser.removeListener(Meteor.userId(), callback); + } + +}; diff --git a/packages/rocketchat-livechat/app/client/lib/fromApp/RoomHistoryManager.coffee b/packages/rocketchat-livechat/app/client/lib/fromApp/RoomHistoryManager.coffee deleted file mode 100644 index d21a2a296144..000000000000 --- a/packages/rocketchat-livechat/app/client/lib/fromApp/RoomHistoryManager.coffee +++ /dev/null @@ -1,70 +0,0 @@ -@RoomHistoryManager = new class - defaultLimit = 50 - - histories = {} - - getRoom = (rid) -> - if not histories[rid]? - histories[rid] = - hasMore: ReactiveVar true - isLoading: ReactiveVar false - loaded: 0 - - return histories[rid] - - getMore = (rid, limit=defaultLimit) -> - room = getRoom rid - if room.hasMore.curValue isnt true - return - - room.isLoading.set true - - #$('.messages-box .wrapper').data('previous-height', $('.messages-box .wrapper').get(0)?.scrollHeight - $('.messages-box .wrapper').get(0)?.scrollTop) - # ScrollListener.setLoader true - lastMessage = ChatMessage.findOne({rid: rid}, {sort: {ts: 1}}) - # lastMessage ?= ChatMessage.findOne({rid: rid}, {sort: {ts: 1}}) - - if lastMessage? - ts = lastMessage.ts - else - ts = new Date - - Meteor.call 'loadHistory', rid, ts, limit, undefined, (err, result) -> - return if err? - - for item in result?.messages or [] - if item.t isnt 'command' - ChatMessage.upsert {_id: item._id}, item - room.isLoading.set false - room.loaded += result.messages.length - if result.messages.length < limit - room.hasMore.set false - - hasMore = (rid) -> - room = getRoom rid - - return room.hasMore.get() - - getMoreIfIsEmpty = (rid) -> - room = getRoom rid - - if room.loaded is 0 - getMore rid - - isLoading = (rid) -> - room = getRoom rid - - return room.isLoading.get() - - clear = (rid) -> - ChatMessage.remove({ rid: rid }) - if histories[rid]? - histories[rid].hasMore.set true - histories[rid].isLoading.set false - histories[rid].loaded = 0 - - getMore: getMore - getMoreIfIsEmpty: getMoreIfIsEmpty - hasMore: hasMore - isLoading: isLoading - clear: clear diff --git a/packages/rocketchat-livechat/app/client/lib/fromApp/RoomHistoryManager.js b/packages/rocketchat-livechat/app/client/lib/fromApp/RoomHistoryManager.js new file mode 100644 index 000000000000..2f94b4d1c5b4 --- /dev/null +++ b/packages/rocketchat-livechat/app/client/lib/fromApp/RoomHistoryManager.js @@ -0,0 +1,232 @@ +/* globals readMessage UserRoles RoomRoles*/ +export const RoomHistoryManager = new class { + constructor() { + this.defaultLimit = 50; + this.histories = {}; + } + getRoom(rid) { + if ((this.histories[rid] == null)) { + this.histories[rid] = { + hasMore: new ReactiveVar(true), + hasMoreNext: new ReactiveVar(false), + isLoading: new ReactiveVar(false), + unreadNotLoaded: new ReactiveVar(0), + firstUnread: new ReactiveVar, + loaded: undefined + }; + } + + return this.histories[rid]; + } + + getMore(rid, limit) { + if (limit == null) { limit = this.defaultLimit; } + const room = this.getRoom(rid); + if (room.hasMore.curValue !== true) { + return; + } + + room.isLoading.set(true); + + //$('.messages-box .wrapper').data('previous-height', $('.messages-box .wrapper').get(0)?.scrollHeight - $('.messages-box .wrapper').get(0)?.scrollTop) + // ScrollListener.setLoader true + const lastMessage = ChatMessage.findOne({rid}, { fields: { ts: 1 }, sort: { ts: 1 }}); + // lastMessage ?= ChatMessage.findOne({rid: rid}, {sort: {ts: 1}}) + + let ts; + if (lastMessage) { + ts = lastMessage.ts; + } else { + ts = new Date(); + } + + Meteor.call('loadHistory', rid, ts, limit, undefined, (err, result) => { + if (err) { + return; + } + + if (result && result.messages) { + result.messages.forEach((item) => { + if (item.t !== 'command') { + ChatMessage.upsert({_id: item._id}, item); + } + }); + room.isLoading.set(false); + room.loaded += result.messages.length; + if (result.messages.length < limit) { + room.hasMore.set(false); + } + } + }); + } + + getMoreNext(rid, limit) { + if (limit == null) { limit = this.defaultLimit; } + const room = this.getRoom(rid); + if (room.hasMoreNext.curValue !== true) { + return; + } + + const instance = Blaze.getView($('.messages-box .wrapper')[0]).templateInstance(); + instance.atBottom = false; + + room.isLoading.set(true); + + const lastMessage = ChatMessage.findOne({rid}, {sort: {ts: -1}}); + + let typeName = undefined; + + const subscription = ChatSubscription.findOne({rid}); + if (subscription != null) { + // const { ls } = subscription; + typeName = subscription.t + subscription.name; + } else { + const curRoomDoc = ChatRoom.findOne({_id: rid}); + typeName = (curRoomDoc != null ? curRoomDoc.t : undefined) + (curRoomDoc != null ? curRoomDoc.name : undefined); + } + + const { ts } = lastMessage; + + if (ts) { + return Meteor.call('loadNextMessages', rid, ts, limit, function(err, result) { + for (const item of Array.from((result != null ? result.messages : undefined) || [])) { + if (item.t !== 'command') { + const roles = [ + (item.u && item.u._id && UserRoles.findOne(item.u._id, { fields: { roles: 1 }})) || {}, + (item.u && item.u._id && RoomRoles.findOne({rid: item.rid, 'u._id': item.u._id})) || {} + ].map(e => e.roles); + item.roles = _.union.apply(_.union, roles); + ChatMessage.upsert({_id: item._id}, item); + } + } + + Meteor.defer(() => RoomManager.updateMentionsMarksOfRoom(typeName)); + + room.isLoading.set(false); + if (room.loaded == null) { room.loaded = 0; } + + room.loaded += result.messages.length; + if (result.messages.length < limit) { + room.hasMoreNext.set(false); + } + }); + } + } + + getSurroundingMessages(message, limit) { + if (limit == null) { limit = this.defaultLimit; } + if (!(message != null ? message.rid : undefined)) { + return; + } + + const instance = Blaze.getView($('.messages-box .wrapper')[0]).templateInstance(); + + if (ChatMessage.findOne(message._id)) { + const wrapper = $('.messages-box .wrapper'); + const msgElement = $(`#${ message._id }`, wrapper); + const pos = (wrapper.scrollTop() + msgElement.offset().top) - (wrapper.height()/2); + wrapper.animate({ + scrollTop: pos + }, 500); + msgElement.addClass('highlight'); + + setTimeout(function() { + const messages = wrapper[0]; + return instance.atBottom = messages.scrollTop >= (messages.scrollHeight - messages.clientHeight); + }); + + return setTimeout(() => msgElement.removeClass('highlight') + , 500); + } else { + const room = this.getRoom(message.rid); + room.isLoading.set(true); + ChatMessage.remove({ rid: message.rid }); + + let typeName = undefined; + + const subscription = ChatSubscription.findOne({rid: message.rid}); + if (subscription) { + // const { ls } = subscription; + typeName = subscription.t + subscription.name; + } else { + const curRoomDoc = ChatRoom.findOne({_id: message.rid}); + typeName = (curRoomDoc != null ? curRoomDoc.t : undefined) + (curRoomDoc != null ? curRoomDoc.name : undefined); + } + + return Meteor.call('loadSurroundingMessages', message, limit, function(err, result) { + for (const item of Array.from((result != null ? result.messages : undefined) || [])) { + if (item.t !== 'command') { + const roles = [ + (item.u && item.u._id && UserRoles.findOne(item.u._id, { fields: { roles: 1 }})) || {}, + (item.u && item.u._id && RoomRoles.findOne({rid: item.rid, 'u._id': item.u._id})) || {} + ].map(e => e.roles); + item.roles = _.union.apply(_.union, roles); + ChatMessage.upsert({_id: item._id}, item); + } + } + + Meteor.defer(function() { + readMessage.refreshUnreadMark(message.rid, true); + RoomManager.updateMentionsMarksOfRoom(typeName); + const wrapper = $('.messages-box .wrapper'); + const msgElement = $(`#${ message._id }`, wrapper); + const pos = (wrapper.scrollTop() + msgElement.offset().top) - (wrapper.height()/2); + wrapper.animate({ + scrollTop: pos + }, 500); + + msgElement.addClass('highlight'); + + setTimeout(function() { + room.isLoading.set(false); + const messages = wrapper[0]; + instance.atBottom = !result.moreAfter && (messages.scrollTop >= (messages.scrollHeight - messages.clientHeight)); + return 500; + }); + + return setTimeout(() => msgElement.removeClass('highlight') + , 500); + }); + if (room.loaded == null) { room.loaded = 0; } + room.loaded += result.messages.length; + room.hasMore.set(result.moreBefore); + return room.hasMoreNext.set(result.moreAfter); + }); + } + } + + hasMore(rid) { + const room = this.getRoom(rid); + return room.hasMore.get(); + } + + hasMoreNext(rid) { + const room = this.getRoom(rid); + return room.hasMoreNext.get(); + } + + + getMoreIfIsEmpty(rid) { + const room = this.getRoom(rid); + + if (room.loaded === undefined) { + return this.getMore(rid); + } + } + + + isLoading(rid) { + const room = this.getRoom(rid); + return room.isLoading.get(); + } + + clear(rid) { + ChatMessage.remove({ rid }); + if (this.histories[rid] != null) { + this.histories[rid].hasMore.set(true); + this.histories[rid].isLoading.set(false); + return this.histories[rid].loaded = undefined; + } + } +}; +this.RoomHistoryManager = RoomHistoryManager; diff --git a/packages/rocketchat-livechat/app/client/lib/fromApp/avatar.coffee b/packages/rocketchat-livechat/app/client/lib/fromApp/avatar.coffee deleted file mode 100644 index 7988062501b2..000000000000 --- a/packages/rocketchat-livechat/app/client/lib/fromApp/avatar.coffee +++ /dev/null @@ -1,18 +0,0 @@ -@getAvatarUrlFromUsername = (username) -> - key = "avatar_random_#{username}" - random = Session.keys[key] or 0 - if not username? - return - - return "#{Meteor.absoluteUrl()}avatar/#{username}.jpg?_dc=#{random}" - -@updateAvatarOfUsername = (username) -> - key = "avatar_random_#{username}" - Session.set key, Math.round(Math.random() * 1000) - - for key, room of RoomManager.openedRooms - url = getAvatarUrlFromUsername username - - $(room.dom).find(".message[data-username='#{username}'] .avatar-image").css('background-image', "url(#{url})"); - - return true diff --git a/packages/rocketchat-livechat/app/client/lib/fromApp/avatar.js b/packages/rocketchat-livechat/app/client/lib/fromApp/avatar.js new file mode 100644 index 000000000000..07aad70c42ca --- /dev/null +++ b/packages/rocketchat-livechat/app/client/lib/fromApp/avatar.js @@ -0,0 +1,21 @@ +this.getAvatarUrlFromUsername = username => { + const key = `avatar_random_${ username }`; + const random = Session.keys[key] || 0; + if (!username) { + return; + } + + return `${ Meteor.absoluteUrl() }avatar/${ username }.jpg?_dc=${ random }`; +}; + +this.updateAvatarOfUsername = username => { + const key = `avatar_random_${ username }`; + Session.set(key, Math.round(Math.random() * 1000)); + + Object.keys(RoomManager.openedRooms).forEach((key) => { + const room = RoomManager.openedRooms[key]; + const url = getAvatarUrlFromUsername(username); + $(room.dom).find(`.message[data-username='${ username }'] .avatar-image`).css('background-image', `url(${ url })`); + }); + return true; +}; diff --git a/packages/rocketchat-livechat/app/client/lib/msgTyping.coffee b/packages/rocketchat-livechat/app/client/lib/msgTyping.coffee deleted file mode 100644 index ac6bd5b72269..000000000000 --- a/packages/rocketchat-livechat/app/client/lib/msgTyping.coffee +++ /dev/null @@ -1,69 +0,0 @@ -@MsgTyping = do -> - timeout = 15000 - timeouts = {} - renew = true - renewTimeout = 10000 - selfTyping = new ReactiveVar false - usersTyping = {} - dep = new Tracker.Dependency - - addStream = (room) -> - if _.isEmpty usersTyping[room]?.users - usersTyping[room] = { users: {} } - Notifications.onRoom room, 'typing', (username, typing) -> - unless username is Meteor.user()?.username - if typing is true - users = usersTyping[room].users - users[username] = Meteor.setTimeout -> - delete users[username] - usersTyping[room].users = users - dep.changed() - , timeout - usersTyping[room].users = users - dep.changed() - else - users = usersTyping[room].users - delete users[username] - usersTyping[room].users = users - dep.changed() - - Tracker.autorun -> - if visitor.getRoom() and Meteor.userId() - addStream visitor.getRoom() - - start = (room) -> - return unless renew - - setTimeout -> - renew = true - , renewTimeout - - renew = false - selfTyping.set true - Notifications.notifyRoom room, 'typing', Meteor.user()?.username, true - clearTimeout timeouts[room] - timeouts[room] = Meteor.setTimeout -> - stop(room) - , timeout - - stop = (room) -> - renew = true - selfTyping.set false - if timeouts?[room]? - clearTimeout(timeouts[room]) - timeouts[room] = null - Notifications.notifyRoom room, 'typing', Meteor.user()?.username, false - - get = (room) -> - dep.depend() - unless usersTyping[room] - usersTyping[room] = { users: {} } - users = usersTyping[room].users - return _.keys(users) or [] - - return { - start: start - stop: stop - get: get - selfTyping: selfTyping - } diff --git a/packages/rocketchat-livechat/app/client/lib/msgTyping.js b/packages/rocketchat-livechat/app/client/lib/msgTyping.js new file mode 100644 index 000000000000..1d387f744e06 --- /dev/null +++ b/packages/rocketchat-livechat/app/client/lib/msgTyping.js @@ -0,0 +1,77 @@ +/* globals Notifications */ +export const MsgTyping = (function() { + const timeout = 15000; + const timeouts = {}; + let renew = true; + const renewTimeout = 10000; + const selfTyping = new ReactiveVar(false); + const usersTyping = {}; + const dep = new Tracker.Dependency; + + const addStream = function(room) { + if (!_.isEmpty(usersTyping[room] && usersTyping[room].users)) { + return; + } + usersTyping[room] = { users: {} }; + return Notifications.onRoom(room, 'typing', function(username, typing) { + const user = Meteor.user(); + if (username === (user && user.username)) { + return; + } + const { users } = usersTyping[room]; + if (typing === true) { + users[username] = Meteor.setTimeout(function() { + delete users[username]; + usersTyping[room].users = users; + return dep.changed(); + }, timeout); + } else { + delete users[username]; + } + usersTyping[room].users = users; + return dep.changed(); + }); + }; + + Tracker.autorun(() => { + if (visitor.getRoom() && Meteor.userId()) { + addStream(visitor.getRoom()); + } + }); + + const stop = function(room) { + renew = true; + selfTyping.set(false); + if (timeouts && timeouts[room]) { + clearTimeout(timeouts[room]); + timeouts[room] = null; + } + const user = Meteor.user(); + return Notifications.notifyRoom(room, 'typing', user && user.username, false); + }; + const start = function(room) { + if (!renew) { return; } + + setTimeout(() => renew = true, renewTimeout); + + renew = false; + selfTyping.set(true); + const user = Meteor.user(); + Notifications.notifyRoom(room, 'typing', user && user.username, true); + clearTimeout(timeouts[room]); + return timeouts[room] = Meteor.setTimeout(() => stop(room), timeout); + }; + + const get = function(room) { + dep.depend(); + if (!usersTyping[room]) { + usersTyping[room] = { users: {} }; + } + const { users } = usersTyping[room]; + return _.keys(users) || []; + }; + + return { start, stop, get, selfTyping }; +}()); + +this.MsgTyping = MsgTyping; diff --git a/packages/rocketchat-livechat/app/client/lib/parentCall.coffee b/packages/rocketchat-livechat/app/client/lib/parentCall.coffee deleted file mode 100644 index e09e1103c95f..000000000000 --- a/packages/rocketchat-livechat/app/client/lib/parentCall.coffee +++ /dev/null @@ -1,7 +0,0 @@ -@parentCall = (method, args = []) -> - data = - src: 'rocketchat' - fn: method - args: args - - window.parent.postMessage data, '*' diff --git a/packages/rocketchat-livechat/app/client/lib/parentCall.js b/packages/rocketchat-livechat/app/client/lib/parentCall.js new file mode 100644 index 000000000000..ae705ec7618f --- /dev/null +++ b/packages/rocketchat-livechat/app/client/lib/parentCall.js @@ -0,0 +1,9 @@ +this.parentCall = (method, args = []) => { + const data = { + src: 'rocketchat', + fn: method, + args + }; + + window.parent.postMessage(data, '*'); +}; diff --git a/packages/rocketchat-livechat/app/client/lib/tapi18n.coffee b/packages/rocketchat-livechat/app/client/lib/tapi18n.coffee deleted file mode 100644 index 1cf705e3952e..000000000000 --- a/packages/rocketchat-livechat/app/client/lib/tapi18n.coffee +++ /dev/null @@ -1,15 +0,0 @@ -@t = (key, replaces...) -> - if _.isObject replaces[0] - return TAPi18n.__ key, replaces - else - return TAPi18n.__ key, { postProcess: 'sprintf', sprintf: replaces } - -@tr = (key, options, replaces...) -> - if _.isObject replaces[0] - return TAPi18n.__ key, options, replaces - else - return TAPi18n.__ key, options, { postProcess: 'sprintf', sprintf: replaces } - -@isRtl = (language) -> - # https://en.wikipedia.org/wiki/Right-to-left#cite_note-2 - return language?.split('-').shift().toLowerCase() in ['ar', 'dv', 'fa', 'he', 'ku', 'ps', 'sd', 'ug', 'ur', 'yi'] diff --git a/packages/rocketchat-livechat/app/client/lib/tapi18n.js b/packages/rocketchat-livechat/app/client/lib/tapi18n.js new file mode 100644 index 000000000000..2a3f6ae10d0b --- /dev/null +++ b/packages/rocketchat-livechat/app/client/lib/tapi18n.js @@ -0,0 +1,23 @@ +this.t = function(key, ...replaces) { + if (_.isObject(replaces[0])) { + return TAPi18n.__(key, replaces); + } else { + return TAPi18n.__(key, { + postProcess: 'sprintf', + sprintf: replaces + }); + } +}; + +this.tr = function(key, options, ...replaces) { + if (_.isObject(replaces[0])) { + return TAPi18n.__(key, options, replaces); + } else { + return TAPi18n.__(key, options, { + postProcess: 'sprintf', + sprintf: replaces + }); + } +}; + +this.isRtl = (language) => language != null && ['ar', 'dv', 'fa', 'he', 'ku', 'ps', 'sd', 'ug', 'ur', 'yi'].includes(language.split('-').shift().toLowerCase()); diff --git a/packages/rocketchat-livechat/app/client/methods/sendMessageExternal.coffee b/packages/rocketchat-livechat/app/client/methods/sendMessageExternal.coffee deleted file mode 100644 index 24e493f8913c..000000000000 --- a/packages/rocketchat-livechat/app/client/methods/sendMessageExternal.coffee +++ /dev/null @@ -1,18 +0,0 @@ -Meteor.methods - sendMessageLivechat: (message) -> - if s.trim(message.msg) isnt '' - - if isNaN(TimeSync.serverOffset()) - message.ts = new Date() - else - message.ts = new Date(Date.now() + TimeSync.serverOffset()) - - message.u = - _id: Meteor.userId() - username: Meteor.user()?.username || 'visitor' - - message.temp = true - - # message = RocketChat.callbacks.run 'beforeSaveMessage', message - - ChatMessage.insert message diff --git a/packages/rocketchat-livechat/app/client/methods/sendMessageExternal.js b/packages/rocketchat-livechat/app/client/methods/sendMessageExternal.js new file mode 100644 index 000000000000..35c8844fa881 --- /dev/null +++ b/packages/rocketchat-livechat/app/client/methods/sendMessageExternal.js @@ -0,0 +1,24 @@ +Meteor.methods({ + sendMessageLivechat(message) { + if (s.trim(message.msg) !== '') { + if (isNaN(TimeSync.serverOffset())) { + message.ts = new Date(); + } else { + message.ts = new Date(Date.now() + TimeSync.serverOffset()); + } + + const user = Meteor.user(); + + message.u = { + _id: Meteor.userId(), + username: user && user.username || 'visitor' + }; + + message.temp = true; + + // message = RocketChat.callbacks.run 'beforeSaveMessage', message + + ChatMessage.insert(message); + } + } +}); diff --git a/packages/rocketchat-livechat/app/client/routes/router.coffee b/packages/rocketchat-livechat/app/client/routes/router.coffee deleted file mode 100644 index 6b1712849214..000000000000 --- a/packages/rocketchat-livechat/app/client/routes/router.coffee +++ /dev/null @@ -1,12 +0,0 @@ -BlazeLayout.setRoot('body'); - -FlowRouter.route '/livechat', - name: 'index' - - triggersEnter: [ - -> - visitor.register() - ] - - action: -> - BlazeLayout.render 'main', {center: 'livechatWindow'} diff --git a/packages/rocketchat-livechat/app/client/routes/router.js b/packages/rocketchat-livechat/app/client/routes/router.js new file mode 100644 index 000000000000..6762a019a5e2 --- /dev/null +++ b/packages/rocketchat-livechat/app/client/routes/router.js @@ -0,0 +1,11 @@ +BlazeLayout.setRoot('body'); + +FlowRouter.route('/livechat', { + name: 'index', + triggersEnter: [ + () => visitor.register() + ], + action() { + BlazeLayout.render('main', { center: 'livechatWindow' }); + } +}); diff --git a/packages/rocketchat-livechat/app/client/startup/visitor.coffee b/packages/rocketchat-livechat/app/client/startup/visitor.coffee deleted file mode 100644 index ddacd7c4134a..000000000000 --- a/packages/rocketchat-livechat/app/client/startup/visitor.coffee +++ /dev/null @@ -1,14 +0,0 @@ -@visitorId = new ReactiveVar null - -Meteor.startup -> - if not localStorage.getItem('rocketChatLivechat')? - localStorage.setItem('rocketChatLivechat', Random.id()) - else - Tracker.autorun (c) -> - if not Meteor.userId() and visitor.getToken() - Meteor.call 'livechat:loginByToken', visitor.getToken(), (err, result) -> - if result?.token - Meteor.loginWithToken result.token, (err, result) -> - c.stop() - - visitorId.set localStorage.getItem('rocketChatLivechat') diff --git a/packages/rocketchat-livechat/app/client/startup/visitor.js b/packages/rocketchat-livechat/app/client/startup/visitor.js new file mode 100644 index 000000000000..bd27d17e99af --- /dev/null +++ b/packages/rocketchat-livechat/app/client/startup/visitor.js @@ -0,0 +1,21 @@ +this.visitorId = new ReactiveVar(null); + +Meteor.startup(() => { + if (!localStorage.getItem('rocketChatLivechat')) { + localStorage.setItem('rocketChatLivechat', Random.id()); + } else { + Tracker.autorun(c => { + if (!Meteor.userId() && visitor.getToken()) { + Meteor.call('livechat:loginByToken', visitor.getToken(), (err, result) => { + if (result && result.token) { + Meteor.loginWithToken(result.token, () => { + c.stop(); + }); + } + }); + } + }); + } + + this.visitorId.set(localStorage.getItem('rocketChatLivechat')); +}); diff --git a/packages/rocketchat-livechat/app/client/views/avatar.coffee b/packages/rocketchat-livechat/app/client/views/avatar.coffee deleted file mode 100644 index 5e2e62ab9ef3..000000000000 --- a/packages/rocketchat-livechat/app/client/views/avatar.coffee +++ /dev/null @@ -1,14 +0,0 @@ -Template.avatar.helpers - imageUrl: -> - username = this.username - if not username? and this.userId? - username = Meteor.users.findOne(this.userId)?.username - - if not username? or Meteor.user()?.username is username - return - - Session.get "avatar_random_#{username}" - - url = getAvatarUrlFromUsername(username) - - return "background-image:url(#{url});" diff --git a/packages/rocketchat-livechat/app/client/views/avatar.js b/packages/rocketchat-livechat/app/client/views/avatar.js new file mode 100644 index 000000000000..5ab46c366a45 --- /dev/null +++ b/packages/rocketchat-livechat/app/client/views/avatar.js @@ -0,0 +1,18 @@ +Template.avatar.helpers({ + imageUrl() { + let username = this.username; + if (!username && this.userId) { + const user = Meteor.users.findOne(this.userId, { fields: { username: 1 }}); + username = user && user.username; + } + + const currentUser = Meteor.users.findOne(Meteor.userId(), { fields: { username: 1 }}); + if (!username || (currentUser && currentUser.username === username)) { + return; + } + + Session.get(`avatar_random_${ username }`); + + return `background-image:url(${ getAvatarUrlFromUsername(username) });`; + } +}); diff --git a/packages/rocketchat-livechat/app/client/views/message.coffee b/packages/rocketchat-livechat/app/client/views/message.coffee deleted file mode 100644 index ebe1b3b017a5..000000000000 --- a/packages/rocketchat-livechat/app/client/views/message.coffee +++ /dev/null @@ -1,86 +0,0 @@ -import moment from 'moment' - -Template.message.helpers - own: -> - return 'own' if this.u?._id is Meteor.userId() - - time: -> - return moment(this.ts).format('LT') - - date: -> - return moment(this.ts).format('LL') - - isTemp: -> - if @temp is true - return 'temp' - return - - error: -> - return 'msg-error' if @error - - body: -> - switch this.t - when 'r' then t('Room_name_changed', { room_name: this.msg, user_by: this.u.username }) - when 'au' then t('User_added_by', { user_added: this.msg, user_by: this.u.username }) - when 'ru' then t('User_removed_by', { user_removed: this.msg, user_by: this.u.username }) - when 'ul' then tr('User_left', { context: this.u.gender }, { user_left: this.u.username }) - when 'uj' then tr('User_joined', { context: this.u.gender }, { user: this.u.username }) - when 'wm' then t('Welcome', { user: this.u.username }) - when 'livechat-close' then t('Conversation_finished') - # when 'rtc' then RocketChat.callbacks.run 'renderRtcMessage', this - else - this.html = this.msg - if s.trim(this.html) isnt '' - this.html = s.escapeHTML this.html - # message = RocketChat.callbacks.run 'renderMessage', this - message = this - this.html = message.html.replace /\n/gm, '
' - return livechatAutolinker.link this.html - - system: -> - return 'system' if this.t in ['s', 'p', 'f', 'r', 'au', 'ru', 'ul', 'wm', 'uj', 'livechat-close'] - - sender: -> - agent = Livechat.agent - if agent && @u.username is agent.username - return agent.name or agent.username - return @u.username - - -Template.message.onViewRendered = (context) -> - view = this - this._domrange.onAttached (domRange) -> - lastNode = domRange.lastNode() - if lastNode.previousElementSibling?.dataset?.date isnt lastNode.dataset.date - $(lastNode).addClass('new-day') - $(lastNode).removeClass('sequential') - else if lastNode.previousElementSibling?.dataset?.username isnt lastNode.dataset.username - $(lastNode).removeClass('sequential') - - if lastNode.nextElementSibling?.dataset?.date is lastNode.dataset.date - $(lastNode.nextElementSibling).removeClass('new-day') - $(lastNode.nextElementSibling).addClass('sequential') - else - $(lastNode.nextElementSibling).addClass('new-day') - $(lastNode.nextElementSibling).removeClass('sequential') - - if lastNode.nextElementSibling?.dataset?.username isnt lastNode.dataset.username - $(lastNode.nextElementSibling).removeClass('sequential') - - ul = lastNode.parentElement - wrapper = ul.parentElement - - if context.urls?.length > 0 and Template.oembedBaseWidget? - for item in context.urls - do (item) -> - urlNode = lastNode.querySelector('.body a[href="'+item.url+'"]') - if urlNode? - $(urlNode).replaceWith Blaze.toHTMLWithData Template.oembedBaseWidget, item - - if not lastNode.nextElementSibling? - if lastNode.classList.contains('own') is true - view.parentView.parentView.parentView.parentView.parentView.templateInstance().atBottom = true - else - if view.parentView.parentView.parentView.parentView.parentView.templateInstance().atBottom isnt true - newMessage = document.querySelector(".new-message") - newMessage.className = "new-message" diff --git a/packages/rocketchat-livechat/app/client/views/message.js b/packages/rocketchat-livechat/app/client/views/message.js new file mode 100644 index 000000000000..86c64ed26e79 --- /dev/null +++ b/packages/rocketchat-livechat/app/client/views/message.js @@ -0,0 +1,114 @@ +/* globals Livechat, t, tr, livechatAutolinker */ +import moment from 'moment'; + +Template.message.helpers({ + own() { + if (this.u && this.u._id === Meteor.userId()) { + return 'own'; + } + }, + time() { + return moment(this.ts).format('LT'); + }, + date() { + return moment(this.ts).format('LL'); + }, + isTemp() { + if (this.temp === true) { + return 'temp'; + } + }, + error() { + if (this.error) { + return 'msg-error'; + } + }, + body() { + switch (this.t) { + case 'r': + return t('Room_name_changed', { room_name: this.msg, user_by: this.u.username }); + case 'au': + return t('User_added_by', { user_added: this.msg, user_by: this.u.username }); + case 'ru': + return t('User_removed_by', { user_removed: this.msg, user_by: this.u.username }); + case 'ul': + return tr('User_left', { context: this.u.gender }, { user_left: this.u.username }); + case 'uj': + return tr('User_joined', { context: this.u.gender }, { user: this.u.username }); + case 'wm': + return t('Welcome', { user: this.u.username }); + case 'livechat-close': + return t('Conversation_finished'); + // case 'rtc': return RocketChat.callbacks.run('renderRtcMessage', this); + default: + this.html = this.msg; + if (s.trim(this.html) !== '') { + this.html = s.escapeHTML(this.html); + } + // message = RocketChat.callbacks.run 'renderMessage', this + const message = this; + this.html = message.html.replace(/\n/gm, '
'); + return livechatAutolinker.link(this.html); + } + }, + + system() { + if (['s', 'p', 'f', 'r', 'au', 'ru', 'ul', 'wm', 'uj', 'livechat-close'].includes(this.t)) { + return 'system'; + } + }, + + sender() { + const agent = Livechat.agent; + if (agent && this.u.username === agent.username) { + return agent.name || agent.username; + } + return this.u.username; + } +}); + +Template.message.onViewRendered = function(context) { + const view = this; + this._domrange.onAttached(function(domRange) { + const lastNode = domRange.lastNode(); + const previousNode = lastNode.previousElementSibling; + const nextNode = lastNode.nextElementSibling; + + if (!previousNode || previousNode.dataset.date !== lastNode.dataset.date) { + $(lastNode).addClass('new-day'); + $(lastNode).removeClass('sequential'); + } else if (previousNode.dataset.username !== lastNode.dataset.username) { + $(lastNode).removeClass('sequential'); + } + + if (nextNode && nextNode.dataset.date === lastNode.dataset.date) { + $(nextNode).removeClass('new-day'); + $(nextNode).addClass('sequential'); + } else { + $(nextNode).addClass('new-day'); + $(nextNode).removeClass('sequential'); + } + + if (!nextNode || nextNode.dataset.username !== lastNode.dataset.username) { + $(nextNode).removeClass('sequential'); + } + + if (context.urls && context.urls.length > 0 && Template.oembedBaseWidget) { + context.urls.forEach(item => { + const urlNode = lastNode.querySelector(`.body a[href="${ item.url }"]`); + if (urlNode) { + $(urlNode).replaceWith(Blaze.toHTMLWithData(Template.oembedBaseWidget, item)); + } + }); + } + + if (!nextNode) { + if (lastNode.classList.contains('own')) { + view.parentView.parentView.parentView.parentView.parentView.templateInstance().atBottom = true; + } else if (view.parentView.parentView.parentView.parentView.parentView.templateInstance().atBottom !== true) { + const newMessage = document.querySelector('.new-message'); + newMessage.className = 'new-message'; + } + } + }); +}; From 883db06b474c01374390a6412b60e3f2c777e734 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Thu, 25 May 2017 17:24:00 -0300 Subject: [PATCH 2/2] Fix conversion --- packages/rocketchat-lib/server/functions/sendMessage.js | 2 +- .../rocketchat-livechat/app/client/views/switchDepartment.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/rocketchat-lib/server/functions/sendMessage.js b/packages/rocketchat-lib/server/functions/sendMessage.js index d0e4181e733c..ee953ee04a20 100644 --- a/packages/rocketchat-lib/server/functions/sendMessage.js +++ b/packages/rocketchat-lib/server/functions/sendMessage.js @@ -10,7 +10,7 @@ RocketChat.sendMessage = function(user, message, room, upsert = false) { message.msg = ''; } message.rid = room._id; - if (room.usernames || room.usernames.length === 0) { + if (!room.usernames || room.usernames.length === 0) { const updated_room = RocketChat.models.Rooms.findOneById(room._id); if (updated_room != null) { room = updated_room; diff --git a/packages/rocketchat-livechat/app/client/views/switchDepartment.js b/packages/rocketchat-livechat/app/client/views/switchDepartment.js index 9f2990ecd4ba..70ce6893d17b 100644 --- a/packages/rocketchat-livechat/app/client/views/switchDepartment.js +++ b/packages/rocketchat-livechat/app/client/views/switchDepartment.js @@ -44,7 +44,7 @@ Template.switchDepartment.events({ closeOnConfirm: true, html: false }, () => { - Meteor.call('livechat:closeByVisitor', (error) => { + Meteor.call('livechat:closeByVisitor', visitor.getRoom(), (error) => { if (error) { return console.log('Error ->', error); }