merging develop

This commit is contained in:
Jonathan Kolyer 2014-04-30 14:21:23 +00:00
commit 2ee96c49ec
41 changed files with 803 additions and 199 deletions

View File

@ -147,3 +147,8 @@ email_change_default_sender.sql
affiliate_partners.sql
chat_messages.sql
session_ratings.sql
diagnostics.sql
user_mods.sql
connection_stale_expire.sql
rename_chat_messages.sql
fix_connection_fields.sql

View File

@ -0,0 +1,2 @@
ALTER TABLE connections ADD COLUMN stale_time INTEGER NOT NULL DEFAULT 40;
ALTER TABLE connections ADD COLUMN expire_time INTEGER NOT NULL DEFAULT 60;

11
db/up/diagnostics.sql Normal file
View File

@ -0,0 +1,11 @@
CREATE TABLE diagnostics
(
id VARCHAR(64) NOT NULL DEFAULT uuid_generate_v4(),
user_id VARCHAR(64) NOT NULL REFERENCES users (id) ON DELETE CASCADE,
type VARCHAR(255) NOT NULL,
creator VARCHAR(255) NOT NULL,
data TEXT,
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX diagnostics_type_idx ON diagnostics(type);

View File

@ -0,0 +1,8 @@
ALTER TABLE connections ALTER COLUMN user_id SET NOT NULL;
ALTER TABLE connections ALTER COLUMN ip_address SET NOT NULL;
ALTER TABLE connections DROP COLUMN latitude;
ALTER TABLE connections DROP COLUMN longitude;
ALTER TABLE connections DROP COLUMN countrycode;
ALTER TABLE connections DROP COLUMN region;
ALTER TABLE connections DROP COLUMN city;

View File

@ -0,0 +1,2 @@
-- Rename columns .
ALTER TABLE chat_messages RENAME COLUMN messsage TO message;

1
db/up/user_mods.sql Normal file
View File

@ -0,0 +1 @@
ALTER TABLE users ADD COLUMN mods JSON;

View File

@ -141,6 +141,7 @@ require "jam_ruby/models/email_error"
require "jam_ruby/app/mailers/async_mailer"
require "jam_ruby/app/mailers/batch_mailer"
require "jam_ruby/models/affiliate_partner"
require "jam_ruby/models/chat_message"
include Jampb

View File

@ -75,28 +75,13 @@ module JamRuby
if location.nil?
# todo what's a better default location?
locidispid = 0
latitude = 0.0
longitude = 0.0
countrycode = 'US'
region = 'TX'
city = 'Austin'
else
locidispid = locid*1000000+ispid
latitude = location.latitude
longitude = location.longitude
countrycode = location.countrycode
region = location.region
city = location.city
end
conn.ip_address = ip_address
conn.addr = addr
conn.locidispid = locidispid
conn.latitude = latitude
conn.longitude = longitude
conn.countrycode = countrycode
conn.region = region
conn.city = city
conn.save!(validate: false)
end
@ -230,24 +215,14 @@ SQL
if location.nil?
# todo what's a better default location?
locidispid = 0
latitude = 0.0
longitude = 0.0
countrycode = 'US'
region = 'TX'
city = 'Austin'
else
locidispid = locid*1000000+ispid
latitude = location.latitude
longitude = location.longitude
countrycode = location.countrycode
region = location.region
city = location.city
end
lock_connections(conn)
conn.exec("INSERT INTO connections (user_id, client_id, ip_address, client_type, addr, locidispid, latitude, longitude, countrycode, region, city, aasm_state) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)",
[user_id, client_id, ip_address, client_type, addr, locidispid, latitude, longitude, countrycode, region, city, Connection::CONNECT_STATE.to_s]).clear
conn.exec("INSERT INTO connections (user_id, client_id, ip_address, client_type, addr, locidispid, aasm_state) VALUES ($1, $2, $3, $4, $5, $6, $7)",
[user_id, client_id, ip_address, client_type, addr, locidispid, Connection::CONNECT_STATE.to_s]).clear
# we just created a new connection-if this is the first time the user has shown up, we need to send out a message to his friends
conn.exec("SELECT count(user_id) FROM connections WHERE user_id = $1", [user_id]) do |result|
@ -369,23 +344,21 @@ SQL
end
end
# if a blk is passed in, upon success, it will be called and you can issue notifications
# within the connection table lock
def join_music_session(user, client_id, music_session, as_musician, tracks, &blk)
def join_music_session(user, client_id, music_session, as_musician, tracks)
connection = nil
user_id = user.id
music_session_id = music_session.id
ConnectionManager.active_record_transaction do |connection_manager|
db_conn = connection_manager.pg_conn
connection = Connection.find_by_client_id_and_user_id!(client_id, user_id)
connection.music_session_id = music_session_id
connection.as_musician = as_musician
connection.joining_session = true
connection.joined_session_at = Time.now
associate_tracks(connection, tracks)
connection.save
connection = Connection.find_by_client_id_and_user_id!(client_id, user.id)
connection.join_the_session(music_session, as_musician, tracks)
# connection.music_session_id = music_session.id
# connection.as_musician = as_musician
# connection.joining_session = true
# connection.joined_session_at = Time.now
# associate_tracks(connection, tracks)
# connection.save
if connection.errors.any?
raise ActiveRecord::Rollback
@ -419,7 +392,7 @@ SQL
end
# can throw exception if the session is deleted just before this
conn.exec("UPDATE connections SET music_session_id = NULL, joined_session_at = NULL, as_musician = NULL WHERE client_id = $1 AND user_id =$2", [client_id, user_id]) do |result|
conn.exec("UPDATE connections SET music_session_id = NULL, joined_session_at = NULL, as_musician = NULL WHERE client_id = $1 AND user_id = $2 AND music_session_id = $3", [client_id, user_id, music_session_id]) do |result|
if result.cmd_tuples == 1
@log.debug("disassociated music_session with connection for client_id=#{client_id}, user_id=#{user_id}")
@ -442,23 +415,23 @@ SQL
conn.exec("LOCK connections IN EXCLUSIVE MODE").clear
end
def associate_tracks(connection, tracks)
@log.debug "Tracks:"
@log.debug tracks
connection.tracks.clear()
unless tracks.nil?
tracks.each do |track|
instrument = Instrument.find(track["instrument_id"])
t = Track.new
t.instrument = instrument
t.connection = connection
t.sound = track["sound"]
t.client_track_id = track["client_track_id"]
t.save
connection.tracks << t
end
end
end
# def associate_tracks(connection, tracks)
# @log.debug "Tracks:"
# @log.debug tracks
# connection.tracks.clear()
#
# unless tracks.nil?
# tracks.each do |track|
# instrument = Instrument.find(track["instrument_id"])
# t = Track.new
# t.instrument = instrument
# t.connection = connection
# t.sound = track["sound"]
# t.client_track_id = track["client_track_id"]
# t.save
# connection.tracks << t
# end
# end
# end
end
end

View File

@ -12,7 +12,7 @@ require 'jam_ruby'
module JamWebEventMachine
@@log = Logging.logger[JamWebEventMachine]
# starts amqp & eventmachine up first.
# and then calls your block.
# After the supplied block is done,
@ -36,8 +36,9 @@ module JamWebEventMachine
end
def self.run_em(calling_thread = nil)
def self.run_em(calling_thread = nil, semaphore = nil, ran = {})
semaphore.lock if semaphore
EM.run do
# this is global because we need to check elsewhere if we are currently connected to amqp before signalling success with some APIs, such as 'create session'
$amqp_connection_manager = AmqpConnectionManager.new(true, 4, :host => APP_CONFIG.rabbitmq_host, :port => APP_CONFIG.rabbitmq_port)
@ -54,6 +55,8 @@ module JamWebEventMachine
end
end
ran[:ran] = true
semaphore.unlock if semaphore
calling_thread.wakeup if calling_thread
end
end
@ -65,12 +68,18 @@ module JamWebEventMachine
end
def self.run
return if defined?(Rails::Console)
ran = {}
semaphore = Mutex.new
current = Thread.current
Thread.new do
run_em(current)
run_em(current, semaphore, ran)
end
Thread.stop
semaphore.synchronize {
unless ran[:ran]
semaphore.sleep(10)
end
}
end
def self.start

View File

@ -1,26 +1,55 @@
module JamRuby
class ChatMessage < ActiveRecord::Base
self.table_name = 'chat_messages'
self.primary_key = 'id'
default_scope order('created_at DESC')
belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "user_id"
belongs_to :session, :class_name => "JamRuby::MusicSession", :foreign_key => "session_id"
attr_accessible :user_id, :message, :music_session_id
belongs_to :user
belongs_to :music_session
validates :message, length: {minimum: 1, maximum: 255}, no_profanity: true
def self.send_chat_msg(music_session, chat_msg, user)
msg = @@message_factory.chat_message(
music_session.id,
user.name,
user.id,
chat_msg.message,
chat_msg.id,
chat_msg.created_at
)
class << self
@@mq_router = MQRouter.new
@@message_factory = MessageFactory.new
def index(user, params = {})
limit = params[:limit]
limit ||= 20
limit = limit.to_i
start = params[:start].presence
start = start.to_i || 0
query = ChatMessage.offset(start).limit(limit)
if query.length == 0
[query, nil]
elsif query.length < limit
[query, nil]
else
[query, start + limit]
end
end
def send_chat_msg(music_session, chat_msg, user, client_id)
msg = @@message_factory.chat_message(
music_session.id,
user.name,
user.id,
chat_msg.message,
chat_msg.id,
chat_msg.created_at.utc.iso8601
)
@@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => client_id})
end
@@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => chat_msg.user_id})
end
end

View File

@ -68,6 +68,13 @@ module JamRuby
def can_join_music_session
# puts "can_join_music_session: #{music_session_id} was #{music_session_id_was}" if music_session_id_changed?
if music_session_id_changed? and !(music_session_id_was.nil? or music_session_id_was.blank?)
errors.add(:music_session, ValidationMessages::CANT_JOIN_MULTIPLE_SESSIONS)
return false
end
if music_session.nil?
errors.add(:music_session, ValidationMessages::MUSIC_SESSION_MUST_BE_SPECIFIED)
return false
@ -139,14 +146,35 @@ module JamRuby
true
end
def join_the_session(music_session, as_musician)
self.music_session = music_session
def join_the_session(music_session, as_musician, tracks)
self.music_session_id = music_session.id
self.as_musician = as_musician
self.joining_session = true
self.joined_session_at = Time.now
self.save!
associate_tracks(tracks) unless tracks.nil?
self.save
end
def associate_tracks(tracks)
# @log.debug "Tracks:"
# @log.debug tracks
unless tracks.nil?
self.tracks.clear()
tracks.each do |track|
t = Track.new
t.instrument = Instrument.find(track["instrument_id"])
t.connection = self
t.sound = track["sound"]
t.client_track_id = track["client_track_id"]
t.save # todo what if it fails?
self.tracks << t
end
end
end
private
def require_at_least_one_track_when_in_session

View File

@ -22,6 +22,7 @@ module JamRuby
has_many :fan_invitations, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::FanInvitation"
has_many :invited_fans, :through => :fan_invitations, :class_name => "JamRuby::User", :foreign_key => "receiver_id", :source => :receiver
has_many :recordings, :class_name => "JamRuby::Recording", :inverse_of => :music_session
has_many :chats, :class_name => "JamRuby::ChatMessages", :foreign_key => "session_id"
belongs_to :band, :inverse_of => :music_sessions, :class_name => "JamRuby::Band", :foreign_key => "band_id"
after_create :started_session

View File

@ -61,6 +61,9 @@ module JamRuby
has_many :notifications, :class_name => "JamRuby::Notification", :foreign_key => "target_user_id"
has_many :inverse_notifications, :through => :notifications, :class_name => "JamRuby::User"
# chats
has_many :chats, :class_name => "JamRuby::ChatMessage", :foreign_key => "user_id"
# friends
has_many :friendships, :class_name => "JamRuby::Friendship", :foreign_key => "user_id"
has_many :friends, :through => :friendships, :class_name => "JamRuby::User"

View File

@ -91,11 +91,6 @@ FactoryGirl.define do
as_musician true
addr 0
locidispid 0
latitude 0.0
longitude 0.0
countrycode 'US'
region 'TX'
city 'Austin'
client_type 'client'
end

View File

@ -78,11 +78,6 @@ describe ConnectionManager do
cc.ip_address.should eql("1.1.1.1")
cc.addr.should == 0x01010101
cc.locidispid.should == 17192000002
cc.latitude.should == 30.2076
cc.longitude.should == -97.8587
cc.city.should eql('Austin')
cc.region.should eql('TX')
cc.countrycode.should eql('US')
count = @connman.delete_connection(client_id)
count.should == 0
@ -113,11 +108,6 @@ describe ConnectionManager do
cc.ip_address.should eql("1.1.1.1")
cc.addr.should == 0x01010101
cc.locidispid.should == 17192000002
cc.latitude.should == 30.2076
cc.longitude.should == -97.8587
cc.city.should eql('Austin')
cc.region.should eql('TX')
cc.countrycode.should eql('US')
@connman.reconnect(cc, nil, "33.1.2.3")
@ -126,11 +116,6 @@ describe ConnectionManager do
cc.ip_address.should eql("33.1.2.3")
cc.addr.should == 0x21010203
cc.locidispid.should == 30350000003
cc.latitude.should == 29.7633
cc.longitude.should == -95.3633
cc.city.should eql('Houston')
cc.region.should eql('TX')
cc.countrycode.should eql('US')
count = @connman.delete_connection(client_id)
count.should == 0
@ -490,34 +475,57 @@ describe ConnectionManager do
end
it "join_music_session fails if user has music_session already active" do
# there are two different problems: user can only be in one active music_session at a time,
# and a connection can only point to one active music_session at a time. this is a test of
# the former but we need a test of the latter, too.
pending
end
it "join_music_session fails if connection has music_session already active" do
# there are two different problems: user can only be in one active music_session at a time,
# and a connection can only point to one active music_session at a time. this is a test of
# the latter but we need a test of the former, too.
user_id = create_user("test", "user11", "user11@jamkazam.com")
user = User.find(user_id)
music_session = MusicSession.find(create_music_session(user_id))
client_id = Faker::Number.number(20)
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client')
connection = @connman.join_music_session(user, client_id, music_session, true, TRACKS)
client_id1 = Faker::Number.number(20)
@connman.create_connection(user_id, client_id1, "1.1.1.1", 'client')
music_session1 = MusicSession.find(create_music_session(user_id))
connection1 = @connman.join_music_session(user, client_id1, music_session1, true, TRACKS)
client_id = Faker::Number.number(20)
@connman.create_connection(user_id, client_id, Faker::Internet.ip_v4_address, 'client')
music_session = MusicSession.find(create_music_session(user_id))
connection = @connman.join_music_session(user, client_id, music_session, true, TRACKS)
connection1.errors.size.should == 0
connection.errors.size.should == 1
connection.errors.get(:music_session).should == [ValidationMessages::CANT_JOIN_MULTIPLE_SESSIONS]
music_session2 = MusicSession.find(create_music_session(user_id))
connection2 = @connman.join_music_session(user, client_id1, music_session2, true, TRACKS)
user.update_attribute(:admin, true)
client_id = Faker::Number.number(20)
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client')
music_session = MusicSession.find(create_music_session(user_id))
connection = @connman.join_music_session(user, client_id, music_session, true, TRACKS)
client_id = Faker::Number.number(20)
@connman.create_connection(user_id, client_id, Faker::Internet.ip_v4_address, 'client')
music_session = MusicSession.find(create_music_session(user_id))
connection = @connman.join_music_session(user, client_id, music_session, true, TRACKS)
connection.errors.size.should == 0
connection2.errors.size.should == 1
connection2.errors.get(:music_session).should == [ValidationMessages::CANT_JOIN_MULTIPLE_SESSIONS]
# client_id2 = Faker::Number.number(20)
# @connman.create_connection(user_id, client_id2, "2.2.2.2", 'client')
# music_session2 = MusicSession.find(create_music_session(user_id))
# connection2 = @connman.join_music_session(user, client_id2, music_session2, true, TRACKS)
#
# connection2.errors.size.should == 1
# connection2.errors.get(:music_session).should == [ValidationMessages::CANT_JOIN_MULTIPLE_SESSIONS]
#
# user.update_attribute(:admin, true)
# client_id = Faker::Number.number(20)
# @connman.create_connection(user_id, client_id, "1.1.1.1", 'client')
# music_session = MusicSession.find(create_music_session(user_id))
# connection = @connman.join_music_session(user, client_id, music_session, true, TRACKS)
# client_id = Faker::Number.number(20)
# @connman.create_connection(user_id, client_id, Faker::Internet.ip_v4_address, 'client')
# music_session = MusicSession.find(create_music_session(user_id))
# connection = @connman.join_music_session(user, client_id, music_session, true, TRACKS)
# connection.errors.size.should == 0
end
end

View File

@ -21,7 +21,7 @@ describe ClaimedRecording do
@music_session = FactoryGirl.create(:music_session, :creator => @user, :musician_access => true)
# @music_session.connections << @connection
@music_session.save
@connection.join_the_session(@music_session, true)
@connection.join_the_session(@music_session, true, nil)
@recording = Recording.start(@music_session, @user)
@recording.stop
@recording.reload

View File

@ -10,7 +10,7 @@ describe Mix do
@music_session = FactoryGirl.create(:music_session, :creator => @user, :musician_access => true)
# @music_session.connections << @connection
@music_session.save
@connection.join_the_session(@music_session, true)
@connection.join_the_session(@music_session, true, nil)
@recording = Recording.start(@music_session, @user)
@recording.stop
@recording.claim(@user, "name", "description", Genre.first, true)

View File

@ -398,7 +398,7 @@ describe MusicSession do
@music_session = FactoryGirl.create(:music_session, :creator => @user1, :musician_access => true)
# @music_session.connections << @connection
@music_session.save!
@connection.join_the_session(@music_session, true)
@connection.join_the_session(@music_session, true, nil)
end
describe "not recording" do

View File

@ -115,7 +115,7 @@ describe 'Musician search' do
music_session = FactoryGirl.create(:music_session, :creator => usr, :musician_access => true)
# music_session.connections << connection
music_session.save
connection.join_the_session(music_session, true)
connection.join_the_session(music_session, true, nil)
recording = Recording.start(music_session, usr)
recording.stop
recording.reload
@ -130,7 +130,7 @@ describe 'Musician search' do
music_session = FactoryGirl.create(:music_session, :creator => usr, :musician_access => true)
# music_session.connections << connection
music_session.save
connection.join_the_session(music_session, true)
connection.join_the_session(music_session, true, nil)
end
context 'musician stat counters' do

View File

@ -80,7 +80,7 @@ describe Recording do
@track2 = FactoryGirl.create(:track, :connection => @connection2, :instrument => @instrument2)
# @music_session.connections << @connection2
@connection2.join_the_session(@music_session, true)
@connection2.join_the_session(@music_session, true, nil)
@recording = Recording.start(@music_session, @user)
@user.recordings.length.should == 0
@ -179,7 +179,7 @@ describe Recording do
@track = FactoryGirl.create(:track, :connection => @connection2, :instrument => @instrument)
# @music_session.connections << @connection2
@music_session.save
@connection2.join_the_session(@music_session, true)
@connection2.join_the_session(@music_session, true, nil)
@recording = Recording.start(@music_session, @user)
@recording.stop
@recording.reload

View File

@ -4,7 +4,7 @@ describe Track do
let (:user) {FactoryGirl.create(:user) }
let (:music_session) { FactoryGirl.create(:music_session, :creator => user)}
let (:connection) { FactoryGirl.create(:connection, :music_session => music_session) }
let (:connection) { FactoryGirl.create(:connection, :user => user, :music_session => music_session) }
let (:track) { FactoryGirl.create(:track, :connection => connection)}
let (:track2) { FactoryGirl.create(:track, :connection => connection)}
let (:msuh) {FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => user, :client_id => connection.client_id) }

View File

@ -157,7 +157,7 @@ describe AudioMixer do
@music_session = FactoryGirl.create(:music_session, :creator => @user, :musician_access => true)
# @music_session.connections << @connection
@music_session.save
@connection.join_the_session(@music_session, true)
@connection.join_the_session(@music_session, true, nil)
@recording = Recording.start(@music_session, @user)
@recording.stop
@recording.claim(@user, "name", "description", Genre.first, true)

View File

@ -0,0 +1,346 @@
(function(context,$) {
"use strict";
context.JK = context.JK || {};
context.JK.ChatPanel = function(app) {
var logger = context.JK.logger;
var rest = context.JK.Rest();
var $panel = null;
var $contents = null;
var $count = null;
var $chatMessages = null;
var $chatMessagesScroller = null;
var $sendChatMessageBtn = null;
var $chatSender = null;
var $form = null;
var $textBox = null;
var $sessionId = null;
var $errorMsg = null;
var sendingMessage = false;
var showing = false;
var fullyInitialized = false;
var renderQueue = [];
var sidebar = null;
var user = null;
var currentPage = 0;
var LIMIT = 20;
var next = null;
function reset() {
fullyInitialized = false;
renderQueue = [];
sendingMessage = false;
$chatMessages.empty();
$textBox.val('');
}
function buildMessage() {
var message = {};
message['message'] = $textBox.val();
message['music_session'] = $sessionId;
message['client_id'] = context.JK.clientId;
return message;
}
function sendMessage() {
if(!context.JK.JamServer.connected) {
return false;
}
var msg = $textBox.val();
if(!msg || msg == '') {
// don't bother the server with empty messages
return false;
}
if(!sendingMessage) {
sendingMessage = true;
rest.createChatMessage(buildMessage())
.done(function() {
$textBox.val('');
renderMessage(msg, user.id, user.name, new Date().toISOString(), true);
})
.fail(function(jqXHR) {
app.notifyServerError(jqXHR, 'Unable to Send Chat Message');
})
.always(function() {
sendingMessage = false;
})
}
return false;
}
function scrollToBottom(instant) {
$chatMessagesScroller.animate({scrollTop: $chatMessagesScroller[0].scrollHeight}, instant ? 0 : 'slow');
}
function renderMessage(msg, senderId, senderName, sent, append) {
var options = {
msg: msg,
sender: senderId == user.id ? 'me' : senderName,
sent: sent
};
var txt = $(context._.template($('#template-chat-message').html(), options, { variable: 'data' }));
txt.find('.timeago').timeago();
if(append) {
$chatMessages.append(txt);
scrollToBottom();
}
else {
$chatMessages.prepend(txt);
}
}
function drainQueue() {
context._.each(renderQueue, function(msg) {
renderMessage(msg.message, msg.user_id, msg.user_name, msg.sent, true);
});
renderQueue = [];
}
// we handled the notification, meaning the dialog showed this message as a chat message
function handledNotification(payload) {
return showing && payload.description == "TEXT_MESSAGE";
}
function pasteIntoInput(el, text) {
el.focus();
if (typeof el.selectionStart == "number"
&& typeof el.selectionEnd == "number") {
var val = el.value;
var 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") {
var textRange = document.selection.createRange();
textRange.text = text;
textRange.collapse(false);
textRange.select();
}
}
function handleEnter(evt) {
if (evt.keyCode == 13 && evt.shiftKey) {
evt.preventDefault();
pasteIntoInput(this, "\n");
}
else if(evt.keyCode == 13 && !evt.shiftKey){
sendMessage();
return false;
}
}
function events(bind) {
if (bind) {
$form.submit(sendMessage);
$textBox.keydown(handleEnter);
$sendChatMessageBtn.click(sendMessage);
registerChatMessage(bind);
}
else {
$form.submit(null);
$textBox.keydown(null);
$sendChatMessageBtn.click(null);
}
}
// 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 {
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) {
logger.debug("Handling CHAT_MESSAGE msg " + JSON.stringify(payload));
chatMessageReceived(payload);
handledNotification(payload);
});
}
else {
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.CHAT_MESSAGE, null);
}
}
function opened() {
lowlightCount();
setCount(0);
drainQueue();
}
function sessionStarted(e, data) {
$sessionId = data.session.id;
// open chat panel
$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')
});
events(true);
}
function sessionStopped(e, data) {
// open chat panel
$chatSender.hide();
$chatMessagesScroller.hide();
$errorMsg.show();
reset();
events(false);
}
function isChatPanelVisible() {
return $contents.is(':visible');
}
function incrementChatCount() {
var count = parseInt($count.text());
setCount(count + 1);
}
function setCount(count) {
$count.text(count);
}
function lowlightCount() {
$count.removeClass('highlighted');
}
function highlightCount() {
$count.addClass('highlighted');
}
function buildQuery() {
var query = {type: 'CHAT_MESSAGE', music_session: $sessionId, limit:LIMIT, page:currentPage};
if(next) {
query.start = next;
}
return query;
}
function renderChats(chats) {
context._.each(chats, function (chatMessage) {
renderMessage(chatMessage.message, chatMessage.user_id, chatMessage.user.name, chatMessage.created_at);
});
}
function handleChatResponse(response) {
next = response.next;
renderChats(response.chats);
if(response.next == null) {
// if we less results than asked for, end searching
$chatMessagesScroller.infinitescroll('pause');
logger.debug("end of chatss");
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();
}
}
else {
currentPage++;
buildQuery();
// registerInfiniteScroll();
}
}
function registerInfiniteScroll() {
$chatMessagesScroller.infinitescroll({
behavior: 'local',
navSelector: '.chat-sender .btn-next-pager',
nextSelector: '.chat-sender .btn-next-pager',
binder: $chatMessagesScroller,
dataType: 'json',
appendCallback: false,
prefill: false,
bufferPx: 100,
loading: {
msg: $('<div class="infinite-scroll-loader">Loading ...</div>'),
img: '/assets/shared/spinner-32.gif'
},
path: function(page) {
return '/api/sessions/' + $sessionId + '/chats?' + $.param(buildQuery());
}
},function(json, opts) {
handleChatResponse(json);
});
$chatMessagesScroller.infinitescroll('resume');
}
function initialize(sidebarInstance) {
sidebar = sidebarInstance;
$panel = $('[layout-id="panelChat"]');
$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();
app.user()
.done(function (userDetail) {
user = userDetail;
});
}
this.initialize = initialize;
this.sessionStarted = sessionStarted;
this.sessionStopped = sessionStopped;
this.registerChatMessage = registerChatMessage;
};
return this;
})(window,jQuery);

View File

@ -588,6 +588,10 @@
}
function UserAttention(option) {
}
// passed an array of recording objects from the server
function GetLocalRecordingState(recordings) {
var result = { recordings:[]};
@ -754,6 +758,8 @@
this.OnLoggedIn = OnLoggedIn;
this.OnLoggedOut = OnLoggedOut;
this.UserAttention = UserAttention;
// Recording Playback
this.GetLocalRecordingState = GetLocalRecordingState;
this.OpenRecording = OpenRecording;

View File

@ -944,19 +944,21 @@
});
}
function createChatMessage(session_id, options) {
function createChatMessage(options) {
return $.ajax({
type: "POST",
url: '/api/sessions/' + session_id + '/chat?' + $.param(options),
url: '/api/chat?' + $.param(options),
dataType: "json",
contentType: 'application/json'
});
}
function getChatMessages(session_id, options) {
function getChatMessages(options) {
var musciSessionId = options["music_session"];
delete options["music_session"];
return $.ajax({
type: "GET",
url: '/api/sessions/' + session_id + '/chats' + $.param(options),
url: '/api/sessions/' + musciSessionId + '/chats?' + $.param(options),
dataType: "json",
contentType: 'application/json'
});

View File

@ -95,9 +95,24 @@
notificationPanel.initialize(me, textMessageDialog);
}
// session events for chat
function registerSessionEvents() {
$(document).on('jamkazam.session_started', function(e, data){
chatPanel.sessionStarted(e, data);
return false;
});
$(document).on('jamkazam.session_stopped', function(e, data) {
chatPanel.sessionStopped(e, data);
return false;
});
}
function initializeChatPanel() {
// chatPanel = new context.JK.ChatPanel(app);
// chatPanel.initialize(me, textMessageDialog);
chatPanel = new context.JK.ChatPanel(app);
chatPanel.initialize(me);
registerSessionEvents();
}
function search(query) {

View File

@ -172,29 +172,66 @@
font-size: 100%;
}
.chat-fixed {
position:static;
.chatcontents {
position: relative;
}
.chat-select {
font-size:12px;
text-align:center;
.chat-list-scroller {
position: relative;
display: block;
overflow: auto;
margin: 0px 15px;
height: 210px;
}
.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: #020C81;
&:after {
content:':'
}
}
.chat-message-text {
line-height:18px;
white-space:pre-line;
color: #D5E2E4;
}
.chat-message-timestamp {
margin-top:4px;
}
.chat-select select {
color:#AAA;
display:block;
font-size:12px;
color:$ColorSidebarText;
background-color:shade($ColorElementPrimary, 20);
border:none;
}
}
.chat-text {
float:left;
width:180px;
font-size:11px;
margin-top:5px;
margin-bottom:5px;
color:#D5E2E4;
.chat-sender {
width: 100%;
position: absolute;
bottom: 10px;
padding: 0px 15px;
}
em {

View File

@ -7,18 +7,21 @@ class ApiChatsController < ApiController
def create
@chat_msg = ChatMessage.new
@chat_msg.user_id = current_user.id
@chat_msg.session_id = params[:music_session]
@chat_msg.message = params[:msg]
@chat_msg.music_session_id = @music_session.id
@chat_msg.message = params[:message]
if @chat_msg.save
ChatMessage.send_chat_msg @music_session, @chat_msg, current_user
ChatMessage.send_chat_msg @music_session, @chat_msg, current_user, params[:client_id]
end
respond_with_model(@chat_msg)
end
def index
@chat_msgs = ChatMessage.find_by_session_id(params[:music_session])
.paginate(page: params[:page], per_page: pararms[:per_page] || 20)
data = ChatMessage.index(current_user, params)
@chats = data[0]
@next = data[1]
render "api_chats/index", :layout => nil
end
def check_session

View File

@ -0,0 +1,7 @@
node :next do |page|
@next
end
node :chats do |page|
partial "api_chats/show", object: @chats
end

View File

@ -0,0 +1,11 @@
object @chat
attributes :message, :user_id, :session_id, :created_at
node :user do |c|
user_data = {}
if c.user_id
user_data[:name] = c.user.name
end
user_data
end

View File

@ -93,7 +93,6 @@
</div>
<!-- Chat -->
<!--
<div layout="panel" layout-id="panelChat">
<div layout-panel="collapsed">
</div>
@ -101,24 +100,26 @@
<div layout-panel="header" class="panel-header">
<h2>chat<div id="sidebar-chat-count" class="badge">0</div></h2>
</div>
<div layout-panel="contents" class="panelcontents">
<div class="chat-fixed">
<input id="chat-input" type="text" placeholder="enter message" /><br />
<div class="chat-select">Send to:
<select id="sidebar-chat-friend-list">
<option>Everyone</option>
<option>All Musicians</option>
<option>All Fans</option>
</select>
<div layout-panel="contents" class="chatcontents">
<div class="chat-status">
<span>Chat is available during session is connected.</span>
</div>
<div class="chat-list-scroller">
<div class="previous-chat-list">
</div>
</div>
<div class="chat-sender">
<form class="chat-message-form">
<textarea name="chat-message" id="new-chat-message" placeholder="enter message"></textarea>
</form>
<div class="right">
<%= link_to 'NEXT', '#', class: 'btn-next-pager' %>
<%= link_to 'SEND', '#', class: 'button-orange btn-send-chat-message' %>
</div>
</div>
<ul id="sidebar-chat-list">
</ul>
</div>
</div>
</div>
-->
<!-- Notifications -->
<div layout="panel" layout-id="panelNotifications">
@ -220,20 +221,17 @@
<div id="div-actions">
<a id="btn-notification-action" class="button-orange smallbutton right"></a>
</div>
<br/ >
<br/>
<br clear="all" />
</li>
</script>
<!-- Chat panel template -->
<script type="text/template" id="template-chat-panel">
<li>
<div class="avatar-small"><img src="{avatar_url}" /></div>
<div class="chat-text">
<strong>{label}:</strong>&nbsp;{text}
<em>({date})</em>
</div>
<br clear="all" />
</li>
<script type="text/template" id="template-chat-message">
<div class="chat-message">
<span class="chat-message-sender">{{data.sender}}</span>
<span class="chat-message-text">{{data.msg}}</span>
<time class="chat-message-timestamp timeago" datetime="{{data.sent}}">{{data.sent}}</time>
</div>
</script>

View File

@ -260,8 +260,8 @@ SampleApp::Application.routes.draw do
match '/users/:id/share/recording/:provider' => 'api_users#share_recording', :via => :get
# session chat
match '/sessions/:id/chat' => 'api_chats#create', :via => :post
match '/sessions/:id/chats' => 'api_chats#index', :via => :get
match '/chat' => 'api_chats#create', :via => :post
match '/sessions/:music_session/chats' => 'api_chats#index', :via => :get
# user recordings
# match '/users/:id/recordings' => 'api_users#recording_index', :via => :get

View File

@ -11,7 +11,7 @@ describe ApiClaimedRecordingsController do
@music_session = FactoryGirl.create(:music_session, :creator => @user, :musician_access => true)
# @music_session.connections << @connection
@music_session.save
@connection.join_the_session(@music_session, true)
@connection.join_the_session(@music_session, true, nil)
@recording = Recording.start(@music_session, @user)
@recording.stop
@recording.reload

View File

@ -89,11 +89,6 @@ FactoryGirl.define do
as_musician true
addr 0
locidispid 0
latitude 0.0
longitude 0.0
countrycode 'US'
region 'TX'
city 'Austin'
client_type 'client'
end

View File

@ -0,0 +1,103 @@
require 'spec_helper'
describe "Chat Message", :js => true, :type => :feature, :capybara_feature => true do
before(:all) do
Capybara.default_wait_time = 15
end
let(:user1) { FactoryGirl.create(:user) }
let(:user2) { FactoryGirl.create(:user) }
before(:each) do
UserMailer.deliveries.clear
MusicSession.delete_all
ChatMessage.delete_all
end
# what are all the ways to be in a session?
describe "join session" do
it "on try to send chat before joining session" do
description = "Try to send chat message before joining session!"
create_session(creator: user1, description: description)
in_client(user2) do
sign_in_poltergeist(user1)
find("[layout-id=\"panelChat\"] .panel-header").trigger(:click)
find(".chat-status", text: 'Chat is available during session is connected.')
end
end
it "on join a session" do
description = "Find chat panel expanded when join session"
create_session(creator: user1, description: description)
join_session(user2, description: description)
find(".chatcontents").should be_visible
find("[layout-id=\"panelChat\"] .chat-sender").should be_visible
end
end
describe "sidebar session chat behavior" do
it "send a message" do
description = "Find chat panel expanded when join session"
create_session(creator: user1, description: description)
join_session(user2, description: description)
in_client(user1) do
send_chat_message("Hey, I am #{user1.id}")
find('.chat-message-text', text: "Hey, I am #{user1.id}")
end
in_client(user2) do
find('.chat-message-text', text: "Hey, I am #{user1.id}")
send_chat_message("Received, I am #{user2.id}")
end
in_client(user1) do
find('.chat-message-text', text: "Received, I am #{user2.id}")
end
end
it "shows error with a notify" do
description = "Find chat panel expanded when join session"
create_session(creator: user1, description: description)
join_session(user2, description: description)
in_client(user2) do
chat_max = 256
chat_msg = 'a' * (chat_max + 1)
send_chat_message(chat_msg)
find('#notification').should have_text("Unable to Send Chat Message")
end
end
it "shows badge if not on chat panel" do
description = "Find chat panel expanded when join session"
create_session(creator: user1, description: description)
join_session(user2, description: description)
in_client(user1) do
find("[layout-id=\"panelFriends\"] .panel-header").trigger(:click)
end
in_client(user2) do
send_chat_message("Detect badge information")
end
in_client(user1) do
find('#sidebar-chat-count.badge.highlighted', text: "1")
find("[layout-id=\"panelChat\"] .panel-header").trigger(:click)
find('.chat-message-text', text: "Detect badge information")
end
end
end
end

View File

@ -65,7 +65,7 @@ describe "social metadata" do
ms = FactoryGirl.create(:music_session, :creator => user, :musician_access => true)
# ms.connections << connection
ms.save!
connection.join_the_session(ms, true)
connection.join_the_session(ms, true, nil)
ms
}
@ -93,7 +93,7 @@ describe "social metadata" do
@music_session = FactoryGirl.create(:music_session, :creator => @user, :musician_access => true)
# @music_session.connections << @connection
@music_session.save
@connection.join_the_session(@music_session, true)
@connection.join_the_session(@music_session, true, nil)
@recording = Recording.start(@music_session, @user)
@recording.stop
@recording.reload

View File

@ -10,7 +10,7 @@ describe MusicSessionHelper do
@music_session = FactoryGirl.create(:music_session, :creator => @user, :musician_access => true)
# @music_session.connections << @connection
@music_session.save
@connection.join_the_session(@music_session, true)
@connection.join_the_session(@music_session, true, nil)
@recording = Recording.start(@music_session, @user)
@recording.stop
@recording.reload

View File

@ -700,7 +700,7 @@ describe "Music Session API ", :type => :api do
music_session = FactoryGirl.create(:music_session, :creator => user, :musician_access => true)
# music_session.connections << connection
music_session.save
connection.join_the_session(music_session, true)
connection.join_the_session(music_session, true, nil)
recording = Recording.start(music_session, user)
recording.stop
recording.reload

View File

@ -985,7 +985,7 @@ describe "User API", :type => :api do
ms = FactoryGirl.create(:music_session, :creator => user, :musician_access => true)
# ms.connections << connection
ms.save!
connection.join_the_session(ms, true)
connection.join_the_session(ms, true, nil)
ms }
it "fetches facebook successfully" do
@ -1122,7 +1122,7 @@ describe "User API", :type => :api do
@music_session = FactoryGirl.create(:music_session, :creator => user, :musician_access => true)
# @music_session.connections << @connection
@music_session.save
@connection.join_the_session(@music_session, true)
@connection.join_the_session(@music_session, true, nil)
@recording = Recording.start(@music_session, user)
@recording.stop
@recording.reload

View File

@ -71,6 +71,16 @@ def send_text_message(msg, options={})
end
end
# sends a chat message during session
def send_chat_message(msg)
find("[layout-id=\"panelChat\"] .chat-sender").should be_visible
within("[layout-id=\"panelChat\"] .chat-sender form.chat-message-form") do
fill_in 'new-chat-message', with: msg
end
find("[layout-id=\"panelChat\"] .chat-sender .btn-send-chat-message").trigger(:click)
end
def open_notifications
find("#{NOTIFICATION_PANEL} .panel-header").trigger(:click)
end

View File

@ -35,11 +35,6 @@ FactoryGirl.define do
factory :connection, :class => JamRuby::Connection do
addr 0
locidispid 0
latitude 0.0
longitude 0.0
countrycode 'US'
region 'TX'
city 'Austin'
ip_address '1.1.1.1'
as_musician true
client_type 'client'