From 0f4be8867726365c25ea70c339d58c2a78fbd63e Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sat, 30 Jan 2016 16:08:54 -0600 Subject: [PATCH 1/3] * wip --- db/manifest | 3 +- db/up/chat_channel.sql | 2 + ruby/lib/jam_ruby/jam_track_importer.rb | 7 +- ruby/lib/jam_ruby/models/chat_message.rb | 18 ++- ruby/lib/jam_ruby/mq_router.rb | 6 + web/app/assets/javascripts/chatPanel.js | 59 ++++---- .../react-components/ChatWindow.js.jsx.coffee | 137 +++++++++++++++++- .../actions/ChatActions.js.coffee | 4 + .../stores/ChatStore.js.coffee | 90 +++++++++++- .../react-components/ChatWindow.css.scss | 104 +++++++++++++ .../stylesheets/client/sidebar.css.scss | 63 -------- web/app/controllers/api_chats_controller.rb | 19 ++- web/app/views/api_chats/show.rabl | 2 +- web/app/views/clients/_sidebar.html.erb | 6 +- 14 files changed, 401 insertions(+), 119 deletions(-) create mode 100644 db/up/chat_channel.sql create mode 100644 web/app/assets/stylesheets/client/react-components/ChatWindow.css.scss diff --git a/db/manifest b/db/manifest index 1250059b1..89fd895c4 100755 --- a/db/manifest +++ b/db/manifest @@ -327,4 +327,5 @@ populate_languages.sql populate_subjects.sql reviews.sql download_tracker_fingerprints.sql -connection_active.sql \ No newline at end of file +connection_active.sql +chat_channel.sql \ No newline at end of file diff --git a/db/up/chat_channel.sql b/db/up/chat_channel.sql new file mode 100644 index 000000000..1e94e7e16 --- /dev/null +++ b/db/up/chat_channel.sql @@ -0,0 +1,2 @@ +ALTER TABLE chat_messages ADD COLUMN channel VARCHAR(128) NOT NULL DEFAULT 'session'; +ADD INDEX \ No newline at end of file diff --git a/ruby/lib/jam_ruby/jam_track_importer.rb b/ruby/lib/jam_ruby/jam_track_importer.rb index 01fed9be0..36b1a8655 100644 --- a/ruby/lib/jam_ruby/jam_track_importer.rb +++ b/ruby/lib/jam_ruby/jam_track_importer.rb @@ -2603,8 +2603,13 @@ module JamRuby def generate_jmeps importers = [] + if is_tency_storage? + licensor = JamTrackLicensor.find_by_name!('Tency Music') + elsif is_paris_storage? + licensor = JamTrackLicensor.find_by_name!('Paris Music') + end - JamTrack.all.each do |jam_track| + JamTrack.where(licensor_id: licensor).each do |jam_track| importers << generate_jmep(jam_track) end diff --git a/ruby/lib/jam_ruby/models/chat_message.rb b/ruby/lib/jam_ruby/models/chat_message.rb index 005187415..fc7b51929 100644 --- a/ruby/lib/jam_ruby/models/chat_message.rb +++ b/ruby/lib/jam_ruby/models/chat_message.rb @@ -32,7 +32,7 @@ module JamRuby music_session_id = params[:music_session] query = ChatMessage.where('music_session_id = ?', music_session_id) - .offset(start).limit(limit) + .offset(start).limit(limit) if query.length == 0 [query, nil] @@ -43,21 +43,25 @@ module JamRuby end end - def send_chat_msg(music_session, chat_msg, user, client_id) + def send_chat_msg(music_session, chat_msg, user, client_id, channel) + music_session_id = music_session.id if music_session + msg = @@message_factory.chat_message( - music_session.id, + music_session_id, user.name, user.id, chat_msg.message, chat_msg.id, chat_msg.created_at.utc.iso8601, - 'session' + channel ) - @@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => client_id}) + if channel == 'session' + @@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => client_id}) + elsif channel == 'global' + @@mq_router.publish_to_active_clients(msg) + end end - end - end end \ No newline at end of file diff --git a/ruby/lib/jam_ruby/mq_router.rb b/ruby/lib/jam_ruby/mq_router.rb index c7a9ba417..be4ee21c6 100644 --- a/ruby/lib/jam_ruby/mq_router.rb +++ b/ruby/lib/jam_ruby/mq_router.rb @@ -70,6 +70,12 @@ class MQRouter publish_to_client(MessageFactory::ALL_NATIVE_CLIENTS, client_msg) end + + # sends a message to all clients + def publish_to_active_clients(client_msg) + publish_to_client(MessageFactory::ALL_ACTIVE_CLIENTS, client_msg) + end + # sends a message to a session with no checking of permissions (RAW USAGE) # this method deliberately has no database interactivity/active_record objects def publish_to_session(music_session_id, client_ids, client_msg, sender = {:client_id => nil}) diff --git a/web/app/assets/javascripts/chatPanel.js b/web/app/assets/javascripts/chatPanel.js index 45a13014b..fa6033939 100644 --- a/web/app/assets/javascripts/chatPanel.js +++ b/web/app/assets/javascripts/chatPanel.js @@ -30,8 +30,8 @@ fullyInitialized = false; renderQueue = []; sendingMessage = false; - $chatMessages.empty(); - $textBox.val(''); + //$chatMessages.empty(); + //$textBox.val(''); } function buildMessage() { @@ -135,7 +135,7 @@ } function events(bind) { - if (bind) { + /**if (bind) { $form.submit(sendMessage); $textBox.keydown(handleEnter); $sendChatMessageBtn.click(sendMessage); @@ -144,28 +144,27 @@ $form.submit(null); $textBox.keydown(null); $sendChatMessageBtn.click(null); - } + }*/ - registerChatMessage(bind); } // called from sidebar when messages come in function chatMessageReceived(payload) { - if(fullyInitialized) { - if (isChatPanelVisible()) { - renderMessage(payload.msg, payload.sender_id, payload.sender_name, payload.created_at, true); - } - else { + //if(fullyInitialized) { + //if (isChatPanelVisible()) { + // renderMessage(payload.msg, payload.sender_id, payload.sender_name, payload.created_at, true); + //} + //else { highlightCount(); incrementChatCount(); - renderQueue.push({message: payload.msg, user_id: payload.sender_id, user_name: payload.sender_name, sent: payload.created_at}); + // renderQueue.push({message: payload.msg, user_id: payload.sender_id, user_name: payload.sender_name, sent: payload.created_at}); context.jamClient.UserAttention(true); - } - } - else { - renderQueue.push({message: payload.msg, user_id: payload.sender_id, user_name: payload.sender_name, sent: payload.created_at}); - } + //} + //} + //else { + // renderQueue.push({message: payload.msg, user_id: payload.sender_id, user_name: payload.sender_name, sent: payload.created_at}); + //} } function registerChatMessage(bind) { @@ -173,6 +172,7 @@ context.JK.JamServer.registerMessageCallback(context.JK.MessageType.CHAT_MESSAGE, function(header, payload) { logger.debug("Handling CHAT_MESSAGE msg " + JSON.stringify(payload)); chatMessageReceived(payload); + context.ChatActions.msgReceived(payload); handledNotification(payload); }); @@ -193,8 +193,8 @@ $sessionId = data.session.id; // open chat panel - $chatSender.show(); - $chatMessagesScroller.show(); + //$chatSender.show(); + //$chatMessagesScroller.show(); $errorMsg.hide(); $panel.find('.panel-header').trigger('click'); $panel.on('open', opened); @@ -202,12 +202,15 @@ reset(); + context.ChatActions.sessionStarted($sessionId); + // load previous chat messages rest.getChatMessages(buildQuery()) .done(function (response) { - handleChatResponse(response); + //handleChatResponse(response); - scrollToBottom(true); + context.ChatActions.loadMessages('session', response) + //scrollToBottom(true); showing = true; fullyInitialized = true; drainQueue(); @@ -318,20 +321,22 @@ $contents = $panel.find('.chatcontents'); $chatMessagesScroller = $panel.find('.chat-list-scroller'); $count = $panel.find('#sidebar-chat-count'); - $chatMessages = $panel.find('.previous-chat-list'); - $sendChatMessageBtn = $panel.find('.btn-send-chat-message'); - $chatSender = $panel.find('.chat-sender'); - $form = $panel.find('.chat-message-form'); - $textBox = $form.find('textarea'); + //$chatMessages = $panel.find('.previous-chat-list'); + //$sendChatMessageBtn = $panel.find('.btn-send-chat-message'); + //$chatSender = $panel.find('.chat-sender'); + //$form = $panel.find('.chat-message-form'); + //$textBox = $form.find('textarea'); $errorMsg = $panel.find('.chat-status'); $errorMsg.show(); - $chatSender.hide(); - $chatMessagesScroller.hide(); + //$chatSender.hide(); + //$chatMessagesScroller.hide(); app.user() .done(function (userDetail) { user = userDetail; + + registerChatMessage(true); }); } diff --git a/web/app/assets/javascripts/react-components/ChatWindow.js.jsx.coffee b/web/app/assets/javascripts/react-components/ChatWindow.js.jsx.coffee index 415eaae52..d0e739387 100644 --- a/web/app/assets/javascripts/react-components/ChatWindow.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/ChatWindow.js.jsx.coffee @@ -1,16 +1,145 @@ context = window -MIX_MODES = context.JK.MIX_MODES +rest = new context.JK.Rest() +ChatActions = @ChatActions @ChatWindow = React.createClass({ - mixins: [Reflux.listenTo(@AppStore, "onAppInit"), Reflux.listenTo(@UserStore, "onUserChanged"), Reflux.listenTo(@ChatStore, "onChatChanged")] + mixins: [Reflux.listenTo(@AppStore, "onAppInit"), Reflux.listenTo(@UserStore, "onUserChanged"), + Reflux.listenTo(@ChatStore, "onChatChanged")] + + lastChannel: null getInitialState: () -> - {channels:['global', 'session']} + state = context.ChatStore.getState() + state + + onChatChanged: (chatState) -> + @setState(chatState) + + onUserChanged: (userState) -> + @setState(userState) + + onAppInit: (app) -> + @app = app + + activateChannel: (channel, e) -> + e.preventDefault() + ChatActions.activateChannel(channel); render: () -> + tabs = [] + + for channel of @state.msgs + classes = {} + classes[channel] = true + classes['chat-tab'] = true + classes['active'] = channel == @state.channel + + if channel == 'global' + display = 'Global' + else if channel == 'session' + display = 'Session' + else if !channel? + display = 'Global' + else + display = 'Unknown' + + tabs.push(`
{display}
`) + + msgs = [] + activeMsgs = @state.msgs[@state.channel] || [] + + for msg in activeMsgs + + timeago = $.timeago(msg.created_at) + if msg.sender_id == context.JK.currentUserId + sender = "me" + else + sender = msg.sender_name + + msgs.push(`
+ {sender} + {msg.msg} + +
`) + `
-
+
+ {tabs} +
+
+
+ {msgs}
+
+
+
+ + SEND +
+ +
` + + sendMessage:()-> + if !context.JK.JamServer.connected + return false + + msg = @textBox.val() + logger.debug("text box: " + msg) + if !msg? || msg == '' + # don't bother the server with empty messages + return false; + + if !@sendingMessage + @sendingMessage = true + ChatActions.sendMsg(msg, @sendMsgDone, @sendMsgFail) + + sendMsgDone: () -> + @textBox.val('') + @sendingMessage = false + + sendMsgFail: (jqXHR) -> + @app.notifyServerError(jqXHR, 'Unable to Send Chat Message') + @sendingMessage = false + + handleSendMessage: (e) -> + e.preventDefault() + @sendMessage() + + componentDidMount: () -> + @root = $(@getDOMNode()) + @textBox = @root.find('textarea') + + componentDidUpdate: () -> + if @lastChannel != @state.channel + speed = 0 + else + speed = 'slow' + @lastChannel = @state.channel + + speed = 0 #slow + $scroller = @root.find('.chat-list-scroller') + @root.animate({scrollTop: $scroller[0].scrollHeight}, speed) + + pasteIntoInput: (el, text) -> + el.focus(); + if typeof el.selectionStart == "number" && typeof el.selectionEnd == "number" + val = el.value + selStart = el.selectionStart + el.value = val.slice(0, selStart) + text + val.slice(el.selectionEnd) + el.selectionEnd = el.selectionStart = selStart + text.length + else if typeof document.selection != "undefined" + textRange = document.selection.createRange() + textRange.text = text + textRange.collapse(false) + textRange.select() + + handleEnter: (evt) -> + if evt.keyCode == 13 && evt.shiftKey + evt.preventDefault() + @pasteIntoInput(this, "\n") + else if evt.keyCode == 13 && !evt.shiftKey + @sendMessage() + return false }) \ No newline at end of file diff --git a/web/app/assets/javascripts/react-components/actions/ChatActions.js.coffee b/web/app/assets/javascripts/react-components/actions/ChatActions.js.coffee index 5d99548b0..41b65f36a 100644 --- a/web/app/assets/javascripts/react-components/actions/ChatActions.js.coffee +++ b/web/app/assets/javascripts/react-components/actions/ChatActions.js.coffee @@ -3,4 +3,8 @@ context = window @ChatActions = Reflux.createActions({ msgReceived: {} sendMsg: {} + loadMessages: {} + emptyChannel: {} + sessionStarted: {} + activateChannel: {} }) diff --git a/web/app/assets/javascripts/react-components/stores/ChatStore.js.coffee b/web/app/assets/javascripts/react-components/stores/ChatStore.js.coffee index 207136212..9e504ca2c 100644 --- a/web/app/assets/javascripts/react-components/stores/ChatStore.js.coffee +++ b/web/app/assets/javascripts/react-components/stores/ChatStore.js.coffee @@ -6,14 +6,57 @@ logger = context.JK.logger { listenables: @ChatActions - channel: null, - msgs: {} + channel: 'global', + msgs: {global:[], session:[]} - init: -> + init: () -> # Register with the app store to get @app this.listenTo(context.AppStore, this.onAppInit) onAppInit: (@app) -> + #$(document).on(EVENTS.SESSION_STARTED, ((e, data) => + #@sessionStarted(e, data); + # return false + #)) + + # $(context.AppStore).on('SessionStarted', @onSessionStarted) + + # called from ChatPanel + onSessionStarted: () -> + logger.debug("ChatStore: onSessionStarted") + @msgs['session'] = [] + @channel = 'session' + @textBox.val('') + @onEmptyChannel(@channel) + + onEmptyChannel: (channel) -> + @msgs[channel] = [] + @changed() + + convertServerMessages: (chats) -> + converted = [] + for chat in chats + convert = {} + convert.sender_name = chat.user?.name + convert.sender_id = chat.user_id + convert.msg = chat.message + convert.msg_id = chat.id + convert.created_at = chat.created_at + convert.channel = chat.channel + converted + + # called from ChatPanel + onLoadMessages: (channel, msgs) -> + channelMsgs = @msgs[channel] + + if !channelMsgs? + channelMsgs = [] + @msgs[channel] = channelMsgs + + history = @convertServerMessages(msgs.chats) + + @msgs[channel] = history.concat(channelMsgs) + @changed() onMsgReceived: (msg) -> logger.debug("ChatStore.msgReceived", msg) @@ -27,12 +70,49 @@ logger = context.JK.logger channelMsgs.push(msg) @changed() - onSendMsg: (msg) -> + buildMessage:(msg) -> + payload = {message: msg} + if @channel == 'session' + payload.music_session = SessionStore.currentSessionId + payload.channel = @channel + payload.client_id = @app.clientId + payload + + onActivateChannel: (channel) -> + @channel = channel + @changed() + + onSendMsg: (msg, done, fail) -> + rest.createChatMessage(@buildMessage(msg)) + .done((response) => + + done(response) + + console.log("ON SEND MESSAGE SIMULATE", response) + + @onMsgReceived({ + sender_name: context.JK.currentUserName + sender_id: context.JK.currentuserId + msg: msg, + msg_id: response.id, + created_at: response.created_at, + channel: @channel + }) + ) + .fail((jqXHR) => + fail(jqXHR) + ) + + # unused/untested. send direct to gateway + onSendMsgInstant: (msg) -> logger.debug("ChatStore.sendMsg", msg) window.JK.JamServer.sendChatMessage(@channel, msg) + getState: () -> + return {msgs: @msgs, channel: @channel} + changed: () -> - @trigger(msgs) + @trigger(@getState()) } ) diff --git a/web/app/assets/stylesheets/client/react-components/ChatWindow.css.scss b/web/app/assets/stylesheets/client/react-components/ChatWindow.css.scss new file mode 100644 index 000000000..f17b2a68a --- /dev/null +++ b/web/app/assets/stylesheets/client/react-components/ChatWindow.css.scss @@ -0,0 +1,104 @@ +div[data-react-class="ChatWindow"] { + height:100%; + + .ChatWindow { + height:100%; + } + + .chat-tabs { + text-align:center; + } + .chat-tab { + + margin:0 2px; + display:inline-block; + + padding:2px; + color: #ED3618; + background-color:#09525b; + border-style:solid; + border-color:#09525b; + border-width:1px; + + a { + //color:white; + text-decoration: underline; + } + + &.active { + a { + text-decoration: none; + } + background-color:transparent; + border-color:#09525b; + border-width:0; + } + } + + + .chat-list-scroller { + position: relative; + display: block; + overflow: auto; + margin: 0px 15px; + /*height: 210px;*/ + height: 73%; + } + .chart-text-section { + + } + + .btn-send-chat-message { + float:right; + margin: 3px 0 0; + } + + #new-chat-message { + width: calc(100% - 70px); + height: 20px; + } + + .chat-status { + line-height: 20px; + text-align: center; + padding: 10px; + } + + .chat-message { + margin:5px 0; + + .chat-message-sender { + font-weight:bold; + margin-right:10px; + color: #ED3618; + + &:after { + content:':' + } + } + + .chat-message-text { + line-height:18px; + white-space:pre-line; + color: #D5E2E4; + } + + .chat-message-timestamp { + margin-top:4px; + color:#AAA; + display:block; + font-size:12px; + } + } + + .chat-sender { + width: 100%; + position: absolute; + bottom: 7px; + padding: 0px 7px; + /* height: 20%; */ + /* min-height: 69px; */ + box-sizing: border-box; + } + +} \ No newline at end of file diff --git a/web/app/assets/stylesheets/client/sidebar.css.scss b/web/app/assets/stylesheets/client/sidebar.css.scss index 59a3bdfb5..58c058f8f 100644 --- a/web/app/assets/stylesheets/client/sidebar.css.scss +++ b/web/app/assets/stylesheets/client/sidebar.css.scss @@ -180,69 +180,6 @@ position: relative; } - .chat-list-scroller { - position: relative; - display: block; - overflow: auto; - margin: 0px 15px; - /*height: 210px;*/ - height: 73%; - } - .chart-text-section { - - } - - .btn-send-chat-message { - margin-top: 5px; - margin-right: 30px; - } - - #new-chat-message { - width: 90%; - height: 40px; - } - - .chat-status { - line-height: 20px; - text-align: center; - padding: 10px; - } - - .chat-message { - margin:5px 0; - - .chat-message-sender { - font-weight:bold; - margin-right:10px; - color: #ED3618; - - &:after { - content:':' - } - } - - .chat-message-text { - line-height:18px; - white-space:pre-line; - color: #D5E2E4; - } - - .chat-message-timestamp { - margin-top:4px; - color:#AAA; - display:block; - font-size:12px; - } - } - - .chat-sender { - width: 100%; - position: absolute; - bottom: 10px; - padding: 0px 15px; - height: 20%; - min-height: 69px; - } em { color:#9DB8AF diff --git a/web/app/controllers/api_chats_controller.rb b/web/app/controllers/api_chats_controller.rb index 70988b845..26bd1bac9 100644 --- a/web/app/controllers/api_chats_controller.rb +++ b/web/app/controllers/api_chats_controller.rb @@ -7,11 +7,11 @@ class ApiChatsController < ApiController def create @chat_msg = ChatMessage.new @chat_msg.user_id = current_user.id - @chat_msg.music_session_id = @music_session.id + @chat_msg.music_session_id = @music_session.id if @music_session @chat_msg.message = params[:message] if @chat_msg.save - ChatMessage.send_chat_msg @music_session, @chat_msg, current_user, params[:client_id] + ChatMessage.send_chat_msg @music_session, @chat_msg, current_user, params[:client_id], params[:channel] end respond_with_model(@chat_msg) @@ -25,14 +25,17 @@ class ApiChatsController < ApiController end def check_session - @music_session = ActiveMusicSession.find(params[:music_session]) - if @music_session.nil? - raise ArgumentError, 'specified session not found' + if params.has_key?(:music_session) + @music_session = ActiveMusicSession.find(params[:music_session]) + if @music_session.nil? + raise ArgumentError, 'specified session not found' + end + + unless @music_session.access? current_user + raise JamPermissionError, 'not allowed to join the specified session' + end end - unless @music_session.access? current_user - raise JamPermissionError, 'not allowed to join the specified session' - end end end \ No newline at end of file diff --git a/web/app/views/api_chats/show.rabl b/web/app/views/api_chats/show.rabl index d6ff4caa3..828e09f21 100644 --- a/web/app/views/api_chats/show.rabl +++ b/web/app/views/api_chats/show.rabl @@ -1,6 +1,6 @@ object @chat -attributes :message, :user_id, :session_id, :created_at +attributes :id, :message, :user_id, :session_id, :created_at, :channel node :user do |c| user_data = {} diff --git a/web/app/views/clients/_sidebar.html.erb b/web/app/views/clients/_sidebar.html.erb index b8d44dc68..8c8efaa7f 100644 --- a/web/app/views/clients/_sidebar.html.erb +++ b/web/app/views/clients/_sidebar.html.erb @@ -101,7 +101,9 @@

chat

-
+ <%= react_component 'ChatWindow', {} %> + +
From 8900af51960b33a14686b05814fc44ad012b65c2 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 31 Jan 2016 08:01:33 -0600 Subject: [PATCH 2/3] * wip --- .../react-components/stores/ChatStore.js.coffee | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/web/app/assets/javascripts/react-components/stores/ChatStore.js.coffee b/web/app/assets/javascripts/react-components/stores/ChatStore.js.coffee index 9e504ca2c..87ea6ce42 100644 --- a/web/app/assets/javascripts/react-components/stores/ChatStore.js.coffee +++ b/web/app/assets/javascripts/react-components/stores/ChatStore.js.coffee @@ -23,10 +23,9 @@ logger = context.JK.logger # called from ChatPanel onSessionStarted: () -> - logger.debug("ChatStore: onSessionStarted") + logger.debug("ChatStore: onSessionStarted", this) @msgs['session'] = [] @channel = 'session' - @textBox.val('') @onEmptyChannel(@channel) onEmptyChannel: (channel) -> @@ -88,11 +87,11 @@ logger = context.JK.logger done(response) - console.log("ON SEND MESSAGE SIMULATE", response) + #console.log("ON SEND MESSAGE SIMULATE", response) @onMsgReceived({ - sender_name: context.JK.currentUserName - sender_id: context.JK.currentuserId + sender_name: "me", + sender_id: context.JK.currentUserId, msg: msg, msg_id: response.id, created_at: response.created_at, From 2e95682b10ca6494a5b00c86edd1065aedccda18 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 1 Feb 2016 14:14:06 -0600 Subject: [PATCH 3/3] * wip --- admin/app/admin/chat_messages.rb | 23 ++++ db/up/chat_channel.sql | 4 +- ruby/lib/jam_ruby/models/chat_message.rb | 10 +- ruby/lib/jam_ruby/models/user.rb | 6 +- web/app/assets/javascripts/JamServer.js | 5 + web/app/assets/javascripts/chatPanel.js | 109 ++++++++---------- web/app/assets/javascripts/jam_rest.js | 4 +- web/app/assets/javascripts/layout.js | 4 +- .../assets/javascripts/react-components.js | 4 +- .../react-components/ChatWindow.js.jsx.coffee | 13 ++- .../JamTrackLandingScreen.js.jsx.coffee | 2 +- ...eacherExperienceEditableList.js.jsx.coffee | 2 +- .../react-components/YearSelect.js.jsx.coffee | 2 +- .../actions/ChatActions.js.coffee | 1 + .../actions/UserActivityActions.js.coffee | 6 + .../stores/ChatStore.js.coffee | 93 ++++++++++++--- .../stores/UserActivityStore.js.coffee | 27 +++++ .../react-components/ChatWindow.css.scss | 19 ++- web/app/controllers/api_chats_controller.rb | 3 +- web/config/routes.rb | 2 +- 20 files changed, 233 insertions(+), 106 deletions(-) create mode 100644 admin/app/admin/chat_messages.rb create mode 100644 web/app/assets/javascripts/react-components/actions/UserActivityActions.js.coffee create mode 100644 web/app/assets/javascripts/react-components/stores/UserActivityStore.js.coffee diff --git a/admin/app/admin/chat_messages.rb b/admin/app/admin/chat_messages.rb new file mode 100644 index 000000000..29dfaed52 --- /dev/null +++ b/admin/app/admin/chat_messages.rb @@ -0,0 +1,23 @@ +ActiveAdmin.register JamRuby::ChatMessage, :as => 'ChatMessage' do + # Note: a lame thing is it's not obvious how to make it search on email instead of user_id. + filter :music_session_id + filter :user_id + + menu :parent => 'Misc' + + config.per_page = 200 + + config.sort_order = 'created_at DESC' + + index do + column 'User' do |oo| link_to(oo.user.email, oo.user.admin_url, {:title => oo.user.email}) end + column "Timestamp" do |post| + (post.created_at).strftime('%b %d %Y, %H:%M') + end + column "Message" do |post| + post.message + end + actions + + end +end diff --git a/db/up/chat_channel.sql b/db/up/chat_channel.sql index 1e94e7e16..ea0cbba75 100644 --- a/db/up/chat_channel.sql +++ b/db/up/chat_channel.sql @@ -1,2 +1,4 @@ ALTER TABLE chat_messages ADD COLUMN channel VARCHAR(128) NOT NULL DEFAULT 'session'; -ADD INDEX \ No newline at end of file +CREATE INDEX chat_messages_idx_channels ON chat_messages(channel); +CREATE INDEX chat_messages_idx_created_at ON chat_messages(created_at); +CREATE INDEX chat_messages_idx_music_session_id ON chat_messages(music_session_id); \ No newline at end of file diff --git a/ruby/lib/jam_ruby/models/chat_message.rb b/ruby/lib/jam_ruby/models/chat_message.rb index fc7b51929..8e650bad7 100644 --- a/ruby/lib/jam_ruby/models/chat_message.rb +++ b/ruby/lib/jam_ruby/models/chat_message.rb @@ -29,10 +29,14 @@ module JamRuby start = params[:start].presence start = start.to_i || 0 - music_session_id = params[:music_session] + query = ChatMessage.where('channel = ?', params[:channel]) - query = ChatMessage.where('music_session_id = ?', music_session_id) - .offset(start).limit(limit) + if params.has_key? (:music_session) + music_session_id = params[:music_session] + query = ChatMessage.where('music_session_id = ?', music_session_id) + end + + query = query.offset(start).limit(limit).order('created_at DESC') if query.length == 0 [query, nil] diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index b8e99e6cd..c99b36bcd 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -1230,7 +1230,11 @@ module JamRuby user.errors.add(:email, "is not real") end elsif result == "risky" || result == "unknown" - user.email_needs_verification = true + if response.body["disposable"] + user.errors.add(:email, "is disposable address") + else + user.email_needs_verification = true + end end else user.email_needs_verification = false diff --git a/web/app/assets/javascripts/JamServer.js b/web/app/assets/javascripts/JamServer.js index 0f6c48cbe..139f86a89 100644 --- a/web/app/assets/javascripts/JamServer.js +++ b/web/app/assets/javascripts/JamServer.js @@ -295,11 +295,15 @@ function markAway() { logger.debug("sleep again!") active = false; + context.UserActivityActions.setActive(active) + var userStatus = msg_factory.userStatus(false, null); + server.send(userStatus); } function activityCheck() { var timeoutTime = 300000; // 5 * 1000 * 60 , 5 minutes active = true; + context.UserActivityActions.setActive(active) activityTimeout = setTimeout(markAway, timeoutTime); $(document).ready(function() { $('body').bind('mousedown keydown touchstart focus', function(event) { @@ -316,6 +320,7 @@ } } active = true; + context.UserActivityActions.setActive(active) activityTimeout = setTimeout(markAway, timeoutTime); }); }); diff --git a/web/app/assets/javascripts/chatPanel.js b/web/app/assets/javascripts/chatPanel.js index fa6033939..32aaf043c 100644 --- a/web/app/assets/javascripts/chatPanel.js +++ b/web/app/assets/javascripts/chatPanel.js @@ -1,8 +1,8 @@ -(function(context,$) { +(function (context, $) { "use strict"; context.JK = context.JK || {}; - context.JK.ChatPanel = function(app) { + context.JK.ChatPanel = function (app) { var logger = context.JK.logger; var rest = context.JK.Rest(); var $panel = null; @@ -18,7 +18,7 @@ var $errorMsg = null; var sendingMessage = false; var showing = false; - var fullyInitialized = false; + var fullyInitialized = true; var renderQueue = []; var sidebar = null; var user = null; @@ -27,7 +27,7 @@ var next = null; function reset() { - fullyInitialized = false; + fullyInitialized = true; renderQueue = []; sendingMessage = false; //$chatMessages.empty(); @@ -45,28 +45,28 @@ } function sendMessage() { - if(!context.JK.JamServer.connected) { + if (!context.JK.JamServer.connected) { return false; } var msg = $textBox.val(); - if(!msg || msg == '') { + if (!msg || msg == '') { // don't bother the server with empty messages return false; } - if(!sendingMessage) { + if (!sendingMessage) { sendingMessage = true; rest.createChatMessage(buildMessage()) - .done(function() { + .done(function () { $textBox.val(''); renderMessage(msg, user.id, user.name, new Date().toISOString(), true); }) - .fail(function(jqXHR) { + .fail(function (jqXHR) { app.notifyServerError(jqXHR, 'Unable to Send Chat Message'); }) - .always(function() { + .always(function () { sendingMessage = false; }) @@ -84,9 +84,9 @@ sender: senderId == user.id ? 'me' : senderName, sent: sent }; - var txt = $(context._.template($('#template-chat-message').html(), options, { variable: 'data' })); + var txt = $(context._.template($('#template-chat-message').html(), options, {variable: 'data'})); txt.find('.timeago').timeago(); - if(append) { + if (append) { $chatMessages.append(txt); scrollToBottom(); } @@ -96,7 +96,7 @@ } function drainQueue() { - context._.each(renderQueue, function(msg) { + context._.each(renderQueue, function (msg) { renderMessage(msg.message, msg.user_id, msg.user_name, msg.sent, true); }); renderQueue = []; @@ -128,7 +128,7 @@ evt.preventDefault(); pasteIntoInput(this, "\n"); } - else if(evt.keyCode == 13 && !evt.shiftKey){ + else if (evt.keyCode == 13 && !evt.shiftKey) { sendMessage(); return false; } @@ -140,7 +140,7 @@ $textBox.keydown(handleEnter); $sendChatMessageBtn.click(sendMessage); } - else { + else { $form.submit(null); $textBox.keydown(null); $sendChatMessageBtn.click(null); @@ -150,26 +150,21 @@ // called from sidebar when messages come in function chatMessageReceived(payload) { - //if(fullyInitialized) { - //if (isChatPanelVisible()) { - // renderMessage(payload.msg, payload.sender_id, payload.sender_name, payload.created_at, true); - //} - //else { + if (fullyInitialized) { + if (isChatPanelVisible()) { + + } + else { highlightCount(); incrementChatCount(); - // renderQueue.push({message: payload.msg, user_id: payload.sender_id, user_name: payload.sender_name, sent: payload.created_at}); - context.jamClient.UserAttention(true); - //} - //} - //else { - // renderQueue.push({message: payload.msg, user_id: payload.sender_id, user_name: payload.sender_name, sent: payload.created_at}); - //} + } + } } function registerChatMessage(bind) { if (bind && bind == true) { - context.JK.JamServer.registerMessageCallback(context.JK.MessageType.CHAT_MESSAGE, function(header, payload) { + context.JK.JamServer.registerMessageCallback(context.JK.MessageType.CHAT_MESSAGE, function (header, payload) { logger.debug("Handling CHAT_MESSAGE msg " + JSON.stringify(payload)); chatMessageReceived(payload); context.ChatActions.msgReceived(payload); @@ -185,10 +180,13 @@ function opened() { lowlightCount(); setCount(0); - drainQueue(); } + function fullyOpened() { + context.ChatActions.fullyOpened() + } + function sessionStarted(e, data) { $sessionId = data.session.id; @@ -197,36 +195,23 @@ //$chatMessagesScroller.show(); $errorMsg.hide(); $panel.find('.panel-header').trigger('click'); - $panel.on('open', opened); $panel.find('.btn-next-pager').attr('href', '/api/sessions/' + $sessionId + '/chats?page=1'); reset(); context.ChatActions.sessionStarted($sessionId); - - // load previous chat messages - rest.getChatMessages(buildQuery()) - .done(function (response) { - //handleChatResponse(response); - - context.ChatActions.loadMessages('session', response) - //scrollToBottom(true); - showing = true; - fullyInitialized = true; - drainQueue(); - }) - .fail(function (jqXHR) { - app.notifyServerError(jqXHR, 'Unable to Load Session Conversations') - }); + showing = true + fullyInitialized = true; + drainQueue(); events(true); } function sessionStopped(e, data) { // open chat panel - $chatSender.hide(); - $chatMessagesScroller.hide(); - $errorMsg.show(); + //$chatSender.hide(); + //$chatMessagesScroller.hide(); + //$errorMsg.show(); reset(); events(false); @@ -254,9 +239,9 @@ } function buildQuery() { - var query = {type: 'CHAT_MESSAGE', music_session: $sessionId, limit:LIMIT, page:currentPage}; + var query = {type: 'CHAT_MESSAGE', music_session: $sessionId, limit: LIMIT, page: currentPage}; - if(next) { + if (next) { query.start = next; } @@ -274,11 +259,11 @@ renderChats(response.chats); - if(response.next == null) { + if (response.next == null) { // if we less results than asked for, end searching $chatMessagesScroller.infinitescroll('pause'); - if(currentPage > 0) { + if (currentPage > 0) { // there are bugs with infinitescroll not removing the 'loading'. // it's most noticeable at the end of the list, so whack all such entries $('.infinite-scroll-loader').remove(); @@ -306,10 +291,10 @@ msg: $('
Loading ...
'), img: '/assets/shared/spinner-32.gif' }, - path: function(page) { + path: function (page) { return '/api/sessions/' + $sessionId + '/chats?' + $.param(buildQuery()); } - },function(json, opts) { + }, function (json, opts) { handleChatResponse(json); }); $chatMessagesScroller.infinitescroll('resume'); @@ -321,30 +306,26 @@ $contents = $panel.find('.chatcontents'); $chatMessagesScroller = $panel.find('.chat-list-scroller'); $count = $panel.find('#sidebar-chat-count'); - //$chatMessages = $panel.find('.previous-chat-list'); - //$sendChatMessageBtn = $panel.find('.btn-send-chat-message'); - //$chatSender = $panel.find('.chat-sender'); - //$form = $panel.find('.chat-message-form'); - //$textBox = $form.find('textarea'); + $errorMsg = $panel.find('.chat-status'); - $errorMsg.show(); - //$chatSender.hide(); - //$chatMessagesScroller.hide(); + $panel.on('open', opened); + $panel.on('fullyOpen', fullyOpened); app.user() .done(function (userDetail) { user = userDetail; registerChatMessage(true); + }); } this.initialize = initialize; this.sessionStarted = sessionStarted; - this.sessionStopped = sessionStopped; + this.sessionStopped = sessionStopped; this.registerChatMessage = registerChatMessage; }; return this; -})(window,jQuery); \ No newline at end of file +})(window, jQuery); \ No newline at end of file diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js index 953b9e0a7..b45cf5325 100644 --- a/web/app/assets/javascripts/jam_rest.js +++ b/web/app/assets/javascripts/jam_rest.js @@ -1711,11 +1711,9 @@ } function getChatMessages(options) { - var musciSessionId = options["music_session"]; - delete options["music_session"]; return $.ajax({ type: "GET", - url: '/api/sessions/' + musciSessionId + '/chats?' + $.param(options), + url: '/api/chat?' + $.param(options), dataType: "json", contentType: 'application/json' }) diff --git a/web/app/assets/javascripts/layout.js b/web/app/assets/javascripts/layout.js index 3ea51c8be..19a25056c 100644 --- a/web/app/assets/javascripts/layout.js +++ b/web/app/assets/javascripts/layout.js @@ -290,7 +290,9 @@ $('[layout-panel="contents"]').css({"height": "1px"}); $expandedPanelContents.show(); $expandedPanel.triggerHandler('open') - $expandedPanelContents.animate({"height": expandedPanelHeight + "px"}, opts.animationDuration); + $expandedPanelContents.animate({"height": expandedPanelHeight + "px"}, opts.animationDuration, function() { + $expandedPanel.triggerHandler('fullyOpen') + }); } function layoutHeader(screenWidth, screenHeight) { diff --git a/web/app/assets/javascripts/react-components.js b/web/app/assets/javascripts/react-components.js index 8c4e9b8db..aa06843e3 100644 --- a/web/app/assets/javascripts/react-components.js +++ b/web/app/assets/javascripts/react-components.js @@ -3,18 +3,20 @@ //= require_directory ./react-components/helpers //= require_directory ./react-components/actions //= require ./react-components/stores/AppStore +//= require ./react-components/stores/UserStore +//= require ./react-components/stores/UserActivityStore //= require ./react-components/stores/InstrumentStore //= require ./react-components/stores/LanguageStore //= require ./react-components/stores/GenreStore //= require ./react-components/stores/SubjectStore //= require ./react-components/stores/ProfileStore //= require ./react-components/stores/PlatformStore -//= require ./react-components/stores/ChatStore //= require ./react-components/stores/BrowserMediaStore //= require ./react-components/stores/RecordingStore //= require ./react-components/stores/VideoStore //= require ./react-components/stores/SessionStore //= require ./react-components/stores/SessionStatsStore +//= require ./react-components/stores/ChatStore //= require ./react-components/stores/MixerStore //= require ./react-components/stores/ConfigureTracksStore //= require ./react-components/stores/JamTrackStore diff --git a/web/app/assets/javascripts/react-components/ChatWindow.js.jsx.coffee b/web/app/assets/javascripts/react-components/ChatWindow.js.jsx.coffee index d0e739387..3aa8172dc 100644 --- a/web/app/assets/javascripts/react-components/ChatWindow.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/ChatWindow.js.jsx.coffee @@ -86,7 +86,6 @@ ChatActions = @ChatActions return false msg = @textBox.val() - logger.debug("text box: " + msg) if !msg? || msg == '' # don't bother the server with empty messages return false; @@ -100,7 +99,10 @@ ChatActions = @ChatActions @sendingMessage = false sendMsgFail: (jqXHR) -> - @app.notifyServerError(jqXHR, 'Unable to Send Chat Message') + if jqXHR.status == 404 + @app.notifyServerError(jqXHR, 'Session chat is only available while in session.') + else + @app.notifyServerError(jqXHR, 'Unable to Send Chat Message') @sendingMessage = false handleSendMessage: (e) -> @@ -118,9 +120,9 @@ ChatActions = @ChatActions speed = 'slow' @lastChannel = @state.channel - speed = 0 #slow + #speed = 0 #slow $scroller = @root.find('.chat-list-scroller') - @root.animate({scrollTop: $scroller[0].scrollHeight}, speed) + $scroller.animate({scrollTop: $scroller[0].scrollHeight}, speed) pasteIntoInput: (el, text) -> el.focus(); @@ -138,8 +140,7 @@ ChatActions = @ChatActions handleEnter: (evt) -> if evt.keyCode == 13 && evt.shiftKey evt.preventDefault() - @pasteIntoInput(this, "\n") + @pasteIntoInput($(evt.target).get(0), "\n") else if evt.keyCode == 13 && !evt.shiftKey @sendMessage() - return false }) \ No newline at end of file diff --git a/web/app/assets/javascripts/react-components/JamTrackLandingScreen.js.jsx.coffee b/web/app/assets/javascripts/react-components/JamTrackLandingScreen.js.jsx.coffee index 0a0b8922f..c5d09b153 100644 --- a/web/app/assets/javascripts/react-components/JamTrackLandingScreen.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/JamTrackLandingScreen.js.jsx.coffee @@ -37,7 +37,7 @@ rest = context.JK.Rest() playJamTracks = [] for jamTrack in @state.purchasedJamTracks - playJamTracks.push ` + playJamTracks.push ` {jamTrack.name} by {jamTrack.original_artist}` if @state.purchasedJamTracks.length < 5 diff --git a/web/app/assets/javascripts/react-components/TeacherExperienceEditableList.js.jsx.coffee b/web/app/assets/javascripts/react-components/TeacherExperienceEditableList.js.jsx.coffee index 0177cb7ed..e6f720de5 100644 --- a/web/app/assets/javascripts/react-components/TeacherExperienceEditableList.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/TeacherExperienceEditableList.js.jsx.coffee @@ -78,7 +78,7 @@ logger = context.JK.logger errors = [] if this.state.errors? for error in this.state.errors - errors.push(`{error}`) + errors.push(`{error}`) `
diff --git a/web/app/assets/javascripts/react-components/YearSelect.js.jsx.coffee b/web/app/assets/javascripts/react-components/YearSelect.js.jsx.coffee index 483eb347e..9b543533e 100644 --- a/web/app/assets/javascripts/react-components/YearSelect.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/YearSelect.js.jsx.coffee @@ -10,7 +10,7 @@ logger = context.JK.logger now = new Date().getFullYear() for yr in [now..1916] - options.push `` + options.push `` `