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/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..ea0cbba75
--- /dev/null
+++ b/db/up/chat_channel.sql
@@ -0,0 +1,4 @@
+ALTER TABLE chat_messages ADD COLUMN channel VARCHAR(128) NOT NULL DEFAULT 'session';
+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/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..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]
@@ -43,21 +47,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/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/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/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 45a13014b..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,11 +27,11 @@
var next = null;
function reset() {
- fullyInitialized = false;
+ fullyInitialized = true;
renderQueue = [];
sendingMessage = false;
- $chatMessages.empty();
- $textBox.val('');
+ //$chatMessages.empty();
+ //$textBox.val('');
}
function buildMessage() {
@@ -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,51 +128,46 @@
evt.preventDefault();
pasteIntoInput(this, "\n");
}
- else if(evt.keyCode == 13 && !evt.shiftKey){
+ else if (evt.keyCode == 13 && !evt.shiftKey) {
sendMessage();
return false;
}
}
function events(bind) {
- if (bind) {
+ /**if (bind) {
$form.submit(sendMessage);
$textBox.keydown(handleEnter);
$sendChatMessageBtn.click(sendMessage);
}
- else {
+ else {
$form.submit(null);
$textBox.keydown(null);
$sendChatMessageBtn.click(null);
- }
+ }*/
- registerChatMessage(bind);
}
// called from sidebar when messages come in
function chatMessageReceived(payload) {
- if(fullyInitialized) {
+ 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});
-
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);
handledNotification(payload);
});
@@ -185,45 +180,38 @@
function opened() {
lowlightCount();
setCount(0);
-
drainQueue();
}
+ function fullyOpened() {
+ context.ChatActions.fullyOpened()
+ }
+
function sessionStarted(e, data) {
$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);
$panel.find('.btn-next-pager').attr('href', '/api/sessions/' + $sessionId + '/chats?page=1');
reset();
- // load previous chat messages
- rest.getChatMessages(buildQuery())
- .done(function (response) {
- handleChatResponse(response);
-
- scrollToBottom(true);
- showing = true;
- fullyInitialized = true;
- drainQueue();
- })
- .fail(function (jqXHR) {
- app.notifyServerError(jqXHR, 'Unable to Load Session Conversations')
- });
+ context.ChatActions.sessionStarted($sessionId);
+ 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);
@@ -251,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;
}
@@ -271,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();
@@ -303,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');
@@ -318,28 +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 415eaae52..3aa8172dc 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,146 @@
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(``)
+
+ 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}
+
+
`)
+
`
-
`
+
+ sendMessage:()->
+ if !context.JK.JamServer.connected
+ return false
+
+ msg = @textBox.val()
+ 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) ->
+ 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) ->
+ 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')
+ $scroller.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($(evt.target).get(0), "\n")
+ else if evt.keyCode == 13 && !evt.shiftKey
+ @sendMessage()
})
\ 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}`)
`
-
+ <%= react_component 'ChatWindow', {} %>
+
+
diff --git a/web/config/routes.rb b/web/config/routes.rb
index 2638fcf93..36643a5e2 100644
--- a/web/config/routes.rb
+++ b/web/config/routes.rb
@@ -448,7 +448,7 @@ SampleApp::Application.routes.draw do
# session chat
match '/chat' => 'api_chats#create', :via => :post
- match '/sessions/:music_session/chats' => 'api_chats#index', :via => :get
+ match '/chat' => 'api_chats#index', :via => :get
# user recordings
# match '/users/:id/recordings' => 'api_users#recording_index', :via => :get