This commit is contained in:
Seth Call 2016-01-30 16:08:54 -06:00
parent 9e9c2819dd
commit 0f4be88677
14 changed files with 401 additions and 119 deletions

View File

@ -327,4 +327,5 @@ populate_languages.sql
populate_subjects.sql
reviews.sql
download_tracker_fingerprints.sql
connection_active.sql
connection_active.sql
chat_channel.sql

2
db/up/chat_channel.sql Normal file
View File

@ -0,0 +1,2 @@
ALTER TABLE chat_messages ADD COLUMN channel VARCHAR(128) NOT NULL DEFAULT 'session';
ADD INDEX

View File

@ -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

View File

@ -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

View File

@ -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})

View File

@ -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);
});
}

View File

@ -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(`<div key={channel} className={classNames(classes)}><a onClick={this.activateChannel.bind(this, channel)}>{display}</a></div>`)
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(`<div key={msg.msg_id} className="chat-message">
<span className="chat-message-sender">{sender}</span>
<span className="chat-message-text">{msg.msg}</span>
<time className="chat-message-timestamp timeago">{timeago}</time>
</div>`)
`<div className="ChatWindow">
<div className="tabs">
<div className="chat-tabs">
{tabs}
</div>
<div className="active-tab">
<div className="chat-list-scroller">
{msgs}
</div>
</div>
<div className="chat-sender">
<form onSubmit={this.handleSendMessage} className="chat-message-form">
<textarea onKeyDown={this.handleEnter} name="chat-message" id="new-chat-message" placeholder="enter message"></textarea>
<a className="button-orange btn-send-chat-message" onClick={this.handleSendMessage}>SEND</a>
</form>
</div>
</div>`
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
})

View File

@ -3,4 +3,8 @@ context = window
@ChatActions = Reflux.createActions({
msgReceived: {}
sendMsg: {}
loadMessages: {}
emptyChannel: {}
sessionStarted: {}
activateChannel: {}
})

View File

@ -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())
}
)

View File

@ -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;
}
}

View File

@ -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

View File

@ -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

View File

@ -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 = {}

View File

@ -101,7 +101,9 @@
<h2>chat<div id="sidebar-chat-count" class="badge">0</div></h2>
</div>
<div layout-panel="contents" class="chatcontents">
<div class="chat-status">
<%= react_component 'ChatWindow', {} %>
<!--<div class="chat-status">
<span>Chat is available when in session.</span>
</div>
@ -118,7 +120,7 @@
<%= link_to 'NEXT', '#', class: 'btn-next-pager' %>
<%= link_to 'SEND', '#', class: 'button-orange btn-send-chat-message' %>
</div>
</div>
</div>-->
</div>
</div>
</div>