* merged
This commit is contained in:
commit
9c9a42c2a5
|
|
@ -241,3 +241,5 @@ jam_track_available.sql
|
|||
active_jam_track.sql
|
||||
bpms_on_tap_in.sql
|
||||
jamtracks_job.sql
|
||||
text_messages.sql
|
||||
text_message_migration.sql
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
insert into text_messages(source_user_id, target_user_id, message, created_at, updated_at) (
|
||||
select source_user_id, target_user_id, message, created_at, updated_at
|
||||
from notifications
|
||||
where description='TEXT_MESSAGE'
|
||||
);
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
CREATE TABLE text_messages (
|
||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
source_user_id VARCHAR(64) REFERENCES users(id) ON DELETE CASCADE,
|
||||
target_user_id VARCHAR(64) REFERENCES users(id) ON DELETE CASCADE,
|
||||
message TEXT NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
|
@ -197,6 +197,7 @@ require "jam_ruby/models/jam_company"
|
|||
require "jam_ruby/models/user_sync"
|
||||
require "jam_ruby/models/video_source"
|
||||
require "jam_ruby/models/recorded_video"
|
||||
require "jam_ruby/models/text_message"
|
||||
require "jam_ruby/jam_tracks_manager"
|
||||
|
||||
include Jampb
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ module JamRuby
|
|||
def on_recovery(conn, settings)
|
||||
@connected = true
|
||||
|
||||
@log.debug "reconnected #{conn} #{settings}"
|
||||
@log.warn "reconnected #{conn} #{settings}"
|
||||
|
||||
#puts "#channel before #{@channel}"
|
||||
#puts "recovered channel: #{@channel.reuse}"
|
||||
|
|
|
|||
|
|
@ -11,13 +11,13 @@
|
|||
<p>
|
||||
Getting Started Video<br/>
|
||||
We recommend watching this video before you jump into the service just to get oriented. It will really help you hit the ground running:
|
||||
<a style="color: #ffcc00;" href="https://www.youtube.com/watch?v=VexH4834o9I">https://www.youtube.com/watch?v=VexH4834o9I</a>
|
||||
<a style="color: #ffcc00;" href="https://www.youtube.com/watch?v=DBo--aj_P1w">https://www.youtube.com/watch?v=DBo--aj_P1w</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Other Great Tutorial Videos<br />
|
||||
There are several other very great videos that will help you understand how to find and connect with other musicians on the service, create your own sessions or find and join other musicians’ sessions, play in sessions, record and share your performances, and even live broadcast your sessions to family, friends, and fans. Check these helpful videos out here:
|
||||
<a style="color: #ffcc00;" href="https://jamkazam.desk.com/customer/portal/articles/1304097-tutorial-videos">https://jamkazam.desk.com/customer/portal/articles/1304097-tutorial-videos</a>
|
||||
<a style="color: #ffcc00;" href="https://jamkazam.desk.com/customer/portal/topics/673198-tutorials-on-major-features/articles">https://jamkazam.desk.com/customer/portal/topics/673198-tutorials-on-major-features/articles</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@ Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
|
|||
We're delighted that you have decided to try the JamKazam service, and we hope that you will enjoy using JamKazam to play music with others. Following are links to some resources that can help to get you up and running quickly.
|
||||
|
||||
Getting Started Video
|
||||
We recommend watching this video before you jump into the service just to get oriented. It will really help you hit the ground running:
|
||||
https://www.youtube.com/watch?v=VexH4834o9I
|
||||
We recommend watching this video before you jump into the service just to get oriented. It will really help you hit the ground running:
|
||||
https://www.youtube.com/watch?v=DBo--aj_P1w
|
||||
|
||||
Other Great Tutorial Videos
|
||||
There are several other very great videos that will help you understand how to find and connect with other musicians on the service, create your own sessions or find and join other musicians’ sessions, play in sessions, record and share your performances, and even live broadcast your sessions to family, friends, and fans. Check these helpful videos out here:
|
||||
https://jamkazam.desk.com/customer/portal/articles/1304097-tutorial-videos
|
||||
https://jamkazam.desk.com/customer/portal/topics/673198-tutorials-on-major-features/articles
|
||||
|
||||
Knowledge Base Articles
|
||||
You can find Getting Started knowledge base articles on things like frequently asked questions (FAQ), minimum system requirements for your Windows or Mac computer, how to troubleshoot audio problems in sessions, and more here:
|
||||
|
|
|
|||
|
|
@ -333,6 +333,8 @@ SQL
|
|||
connection = Connection.find_by_client_id_and_user_id!(client_id, user.id)
|
||||
|
||||
connection.join_the_session(music_session, as_musician, tracks, user, audio_latency, video_sources)
|
||||
|
||||
JamRuby::MusicSessionUserHistory.join_music_session(user.id, music_session.id)
|
||||
# connection.music_session_id = music_session.id
|
||||
# connection.as_musician = as_musician
|
||||
# connection.joining_session = true
|
||||
|
|
|
|||
|
|
@ -290,13 +290,8 @@ module JamRuby
|
|||
}
|
||||
)
|
||||
|
||||
if (offset)
|
||||
query = query.offset(offset)
|
||||
end
|
||||
|
||||
if (limit)
|
||||
query = query.limit(limit)
|
||||
end
|
||||
query = query.offset(offset) if offset
|
||||
query = query.limit(limit) if limit
|
||||
|
||||
if as_musician
|
||||
query = query.where(
|
||||
|
|
|
|||
|
|
@ -426,14 +426,19 @@ module JamRuby
|
|||
|
||||
# returns one user history per user, with instruments all crammed together, and with total duration
|
||||
def unique_user_histories
|
||||
|
||||
# only get the active users if the session is in progress
|
||||
user_filter = "music_sessions_user_history.session_removed_at is null" if self.session_removed_at.nil?
|
||||
|
||||
MusicSessionUserHistory
|
||||
.joins(:user)
|
||||
.select("STRING_AGG(instruments, '|') AS total_instruments,
|
||||
SUM(date_part('epoch', COALESCE(music_sessions_user_history.session_removed_at, music_sessions_user_history.created_at) - music_sessions_user_history.created_at)) AS total_duration,
|
||||
music_sessions_user_history.user_id, music_sessions_user_history.music_session_id, users.first_name, users.last_name, users.photo_url")
|
||||
.group("music_sessions_user_history.user_id, music_sessions_user_history.music_session_id, users.first_name, users.last_name, users.photo_url")
|
||||
.order("music_sessions_user_history.user_id")
|
||||
.where(%Q{ music_sessions_user_history.music_session_id = '#{id}'})
|
||||
.joins(:user)
|
||||
.select("STRING_AGG(instruments, '|') AS total_instruments,
|
||||
SUM(date_part('epoch', COALESCE(music_sessions_user_history.session_removed_at, music_sessions_user_history.created_at) - music_sessions_user_history.created_at)) AS total_duration,
|
||||
music_sessions_user_history.user_id, music_sessions_user_history.music_session_id, users.first_name, users.last_name, users.photo_url")
|
||||
.group("music_sessions_user_history.user_id, music_sessions_user_history.music_session_id, users.first_name, users.last_name, users.photo_url")
|
||||
.order("music_sessions_user_history.user_id")
|
||||
.where(%Q{ music_sessions_user_history.music_session_id = '#{id}'})
|
||||
.where(user_filter)
|
||||
end
|
||||
|
||||
def duration_minutes
|
||||
|
|
|
|||
|
|
@ -56,6 +56,20 @@ module JamRuby
|
|||
(end_time - self.created_at) / 60.0
|
||||
end
|
||||
|
||||
def self.join_music_session(user_id, session_id)
|
||||
hist = self
|
||||
.where(:user_id => user_id)
|
||||
.where(:music_session_id => session_id)
|
||||
.limit(1)
|
||||
.first
|
||||
hist.start_history if hist
|
||||
end
|
||||
|
||||
def start_history
|
||||
self.session_removed_at = nil
|
||||
self.update_attributes(:session_removed_at => self.session_removed_at, :max_concurrent_connections => determine_max_concurrent)
|
||||
end
|
||||
|
||||
def self.removed_music_session(user_id, session_id)
|
||||
hist = self
|
||||
.where(:user_id => user_id)
|
||||
|
|
@ -67,7 +81,6 @@ module JamRuby
|
|||
|
||||
def end_history
|
||||
self.session_removed_at = Time.now if self.session_removed_at.nil?
|
||||
|
||||
self.update_attributes(:session_removed_at => self.session_removed_at, :max_concurrent_connections => determine_max_concurrent)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ module JamRuby
|
|||
# used for persisted notifications
|
||||
def formatted_msg
|
||||
# target_user, band, session, recording, invitation, join_request = nil
|
||||
source_user, band = nil
|
||||
source_user, band, session = nil
|
||||
|
||||
unless self.source_user_id.nil?
|
||||
source_user = User.find(self.source_user_id)
|
||||
|
|
@ -53,7 +53,12 @@ module JamRuby
|
|||
end
|
||||
|
||||
unless self.session_id.nil?
|
||||
session = MusicSession.find(self.session_id)
|
||||
session = MusicSession.find_by_id(self.session_id)
|
||||
|
||||
# remove all notifications related to this session if it's not found
|
||||
if session.nil?
|
||||
Notification.delete_all "(session_id = '#{session_id}')"
|
||||
end
|
||||
end
|
||||
|
||||
self.class.format_msg(self.description, {:user => source_user, :band => band, :session => session})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
include Devise::Models
|
||||
|
||||
module JamRuby
|
||||
class TextMessage < ActiveRecord::Base
|
||||
|
||||
self.primary_key = 'id'
|
||||
|
||||
default_scope order('created_at DESC')
|
||||
|
||||
attr_accessible :target_user_id, :source_user_id, :message
|
||||
|
||||
belongs_to :target_user, :class_name => "JamRuby::User", :foreign_key => "target_user_id"
|
||||
belongs_to :source_user, :class_name => "JamRuby::User", :foreign_key => "source_user_id"
|
||||
|
||||
validates :message, length: {minimum: 1, maximum: 400}, no_profanity: true
|
||||
|
||||
def self.index(target_user_id, source_user_id, options = {})
|
||||
offset = options[:offset]
|
||||
limit = options[:limit]
|
||||
|
||||
# if not specified, default offset to 0
|
||||
offset ||= 0
|
||||
offset = offset.to_i
|
||||
|
||||
# if not specified, default limit to 10
|
||||
limit ||= 10
|
||||
limit = limit.to_i
|
||||
|
||||
TextMessage
|
||||
.offset(offset)
|
||||
.limit(limit)
|
||||
.where('(source_user_id = (?) AND target_user_id = (?)) OR (source_user_id = (?) AND target_user_id = (?))', source_user_id, target_user_id, target_user_id, source_user_id)
|
||||
|
||||
end
|
||||
|
||||
def self.create(message, target_user_id, source_user_id)
|
||||
sanitized_text = Sanitize.fragment(message, elements: HtmlSanitize::SAFE)
|
||||
|
||||
# create new message
|
||||
tm = TextMessage.new
|
||||
tm.message = sanitized_text
|
||||
tm.target_user_id = target_user_id
|
||||
tm.source_user_id = source_user_id
|
||||
tm.save
|
||||
|
||||
# send notification
|
||||
@notification = Notification.send_text_message(sanitized_text, User.find(source_user_id), User.find(target_user_id))
|
||||
|
||||
tm
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -25,6 +25,10 @@ module JamRuby
|
|||
|
||||
MOD_NO_SHOW = "no_show"
|
||||
|
||||
# MIN/MAX AUDIO LATENCY
|
||||
MINIMUM_AUDIO_LATENCY = 2
|
||||
MAXIMUM_AUDIO_LATENCY = 10000
|
||||
|
||||
devise :database_authenticatable, :recoverable, :rememberable
|
||||
|
||||
acts_as_mappable
|
||||
|
|
@ -79,6 +83,9 @@ module JamRuby
|
|||
# self.id = followable_id in follows table
|
||||
has_many :followers, :as => :followable, :class_name => "JamRuby::Follow", :dependent => :destroy
|
||||
|
||||
# text messages
|
||||
has_many :text_messages, :class_name => "JamRuby:TextMessage", :foreign_key => "target_user_id"
|
||||
|
||||
# notifications
|
||||
has_many :notifications, :class_name => "JamRuby::Notification", :foreign_key => "target_user_id"
|
||||
has_many :inverse_notifications, :through => :notifications, :class_name => "JamRuby::User"
|
||||
|
|
@ -163,7 +170,7 @@ module JamRuby
|
|||
validates :musician, :inclusion => {:in => [true, false]}
|
||||
validates :show_whats_next, :inclusion => {:in => [nil, true, false]}
|
||||
validates :mods, json: true
|
||||
validates_numericality_of :last_jam_audio_latency, greater_than:0, :allow_nil => true
|
||||
validates_numericality_of :last_jam_audio_latency, greater_than:MINIMUM_AUDIO_LATENCY, less_than:MAXIMUM_AUDIO_LATENCY, :allow_nil => true
|
||||
validates :last_jam_updated_reason, :inclusion => {:in => [nil, JAM_REASON_REGISTRATION, JAM_REASON_NETWORK_TEST, JAM_REASON_FTUE, JAM_REASON_JOIN, JAM_REASON_IMPORT, JAM_REASON_LOGIN] }
|
||||
|
||||
# custom validators
|
||||
|
|
@ -1017,6 +1024,8 @@ module JamRuby
|
|||
|
||||
user.save
|
||||
|
||||
user.errors.add("recaptcha", "verification failed") if recaptcha_failed
|
||||
|
||||
if user.errors.any?
|
||||
raise ActiveRecord::Rollback
|
||||
else
|
||||
|
|
@ -1033,16 +1042,9 @@ module JamRuby
|
|||
UserMailer.confirm_email(user, signup_confirm_url.nil? ? nil : (signup_confirm_url + "/" + user.signup_token) ).deliver
|
||||
end
|
||||
end
|
||||
|
||||
if recaptcha_failed
|
||||
user.errors.add "recaptcha", "verification failed"
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return user
|
||||
end
|
||||
user
|
||||
end # def signup
|
||||
|
||||
# this is intended to be development-mode or test-mode only; VRFS-149
|
||||
# it creates or updates one user per developer, so that we aren't in the business
|
||||
|
|
@ -1379,8 +1381,9 @@ module JamRuby
|
|||
end
|
||||
|
||||
def update_audio_latency(connection, audio_latency)
|
||||
if audio_latency > 2
|
||||
# updating the connection is best effort
|
||||
# the backend sometimes gives tiny numbers, and sometimes very large numbers
|
||||
if audio_latency > MINIMUM_AUDIO_LATENCY && audio_latency < MAXIMUM_AUDIO_LATENCY
|
||||
# updating the connection is best effort; if it's not there that's OK
|
||||
if connection
|
||||
Connection.where(:id => connection.id).update_all(:last_jam_audio_latency => audio_latency)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,7 +11,10 @@ def shutdown
|
|||
end
|
||||
|
||||
Resque.before_first_fork do
|
||||
JamWebEventMachine.start
|
||||
current = Thread.current
|
||||
Thread.new do
|
||||
JamWebEventMachine.run_em(current)
|
||||
end
|
||||
|
||||
#ActiveRecord::Base.establish_connection
|
||||
config = {
|
||||
|
|
@ -41,6 +44,9 @@ Resque.before_fork do
|
|||
# ActiveRecord::Base.connection.disconnect!
|
||||
|
||||
#JamRuby::Stats.destroy!
|
||||
|
||||
# reconnect between jobs
|
||||
ActiveRecord::Base.connection_handler.verify_active_connections!
|
||||
end
|
||||
|
||||
Resque.after_fork do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe TextMessage do
|
||||
|
||||
before do
|
||||
TextMessage.delete_all
|
||||
User.delete_all
|
||||
@target_user = FactoryGirl.create(:user)
|
||||
@source_user = FactoryGirl.create(:user)
|
||||
|
||||
@msg = TextMessage.new(:target_user_id => @target_user.id, :source_user_id => @source_user.id)
|
||||
end
|
||||
|
||||
describe "index" do
|
||||
|
||||
it "should retrieve conversation for both users" do
|
||||
@msg.message = "Test message"
|
||||
@msg.save!
|
||||
|
||||
messages = TextMessage.index(@target_user.id, @source_user.id)
|
||||
messages.count.should == 1
|
||||
|
||||
messages = TextMessage.index(@source_user.id, @target_user.id)
|
||||
messages.count.should == 1
|
||||
end
|
||||
|
||||
it "should page records" do
|
||||
11.times do |n|
|
||||
message = TextMessage.new(:target_user_id => @target_user.id, :source_user_id => @source_user.id)
|
||||
message.message = "Message #{n}"
|
||||
message.save!
|
||||
end
|
||||
|
||||
messages = TextMessage.index(@target_user.id, @source_user.id, {:offset => 0})
|
||||
messages.count.should == 10
|
||||
|
||||
messages = TextMessage.index(@target_user.id, @source_user.id, {:offset => 10})
|
||||
messages.count.should == 1
|
||||
end
|
||||
|
||||
it "should not allow empty message" do
|
||||
expect { @msg.save! }.to raise_error(ActiveRecord::RecordInvalid)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -489,14 +489,14 @@ describe User do
|
|||
|
||||
describe "audio latency" do
|
||||
it "allow update" do
|
||||
@user.last_jam_audio_latency = 1
|
||||
@user.last_jam_audio_latency = 5
|
||||
@user.save!
|
||||
end
|
||||
|
||||
it "prevent negative or 0" do
|
||||
@user.last_jam_audio_latency = 0
|
||||
it "prevent negative" do
|
||||
@user.last_jam_audio_latency = -1
|
||||
@user.save.should be_false
|
||||
@user.errors[:last_jam_audio_latency].should == ['must be greater than 0']
|
||||
@user.errors[:last_jam_audio_latency].should == ['must be greater than 2']
|
||||
end
|
||||
|
||||
it "prevent non numerical" do
|
||||
|
|
@ -504,6 +504,42 @@ describe User do
|
|||
@user.save.should be_false
|
||||
@user.errors[:last_jam_audio_latency].should == ['is not a number']
|
||||
end
|
||||
|
||||
it "prevent 2 (minimum)" do
|
||||
@user.last_jam_audio_latency = User::MINIMUM_AUDIO_LATENCY
|
||||
@user.save.should be_false
|
||||
@user.errors[:last_jam_audio_latency].should == ['must be greater than 2']
|
||||
end
|
||||
|
||||
it "prevent 10000 (maximum)" do
|
||||
@user.last_jam_audio_latency = User::MAXIMUM_AUDIO_LATENCY
|
||||
@user.save.should be_false
|
||||
@user.errors[:last_jam_audio_latency].should == ['must be less than 10000']
|
||||
end
|
||||
end
|
||||
|
||||
describe "update_audio_latency" do
|
||||
|
||||
before(:each) do
|
||||
@user.last_jam_audio_latency.should be_nil
|
||||
end
|
||||
|
||||
it "ignores low latency" do
|
||||
@user.update_audio_latency(nil, User::MINIMUM_AUDIO_LATENCY)
|
||||
@user.last_jam_audio_latency.should be_nil
|
||||
end
|
||||
|
||||
it "ignores high latency" do
|
||||
@user.last_jam_audio_latency.should be_nil
|
||||
@user.update_audio_latency(nil, User::MAXIMUM_AUDIO_LATENCY)
|
||||
@user.last_jam_audio_latency.should be_nil
|
||||
end
|
||||
|
||||
it "accepts normal latency" do
|
||||
@user.last_jam_audio_latency.should be_nil
|
||||
@user.update_audio_latency(nil, User::MINIMUM_AUDIO_LATENCY + 1)
|
||||
@user.last_jam_audio_latency.should == User::MINIMUM_AUDIO_LATENCY + 1
|
||||
end
|
||||
end
|
||||
|
||||
describe "html_sanitize" do
|
||||
|
|
|
|||
|
|
@ -65,7 +65,6 @@ gem 'postgres-copy'
|
|||
#end
|
||||
gem 'geokit-rails'
|
||||
gem 'postgres_ext'
|
||||
gem 'recaptcha', '0.3.6'
|
||||
gem 'resque'
|
||||
gem 'resque-retry'
|
||||
gem 'resque-failed-job-mailer'
|
||||
|
|
|
|||
|
|
@ -339,7 +339,14 @@
|
|||
|
||||
if ("instrument_list" in pending_rsvp_request && pending_rsvp_request.instrument_list != null) {
|
||||
$.each(pending_rsvp_request.instrument_list, function (index, instrument) {
|
||||
var instrumentId = context.JK.getInstrumentId(instrument.id);
|
||||
var instrumentId;
|
||||
|
||||
if (instrument) {
|
||||
instrumentId = context.JK.getInstrumentId(instrument.id);
|
||||
}
|
||||
else {
|
||||
instrumentId = 'other';
|
||||
}
|
||||
var inst = context.JK.getInstrumentIcon24(instrumentId);
|
||||
instrumentLogoHtml += '<img title="' + instrumentId + '" hoveraction="instrument" data-instrument-id="' + instrumentId + '" src="' + inst + '" width="24" height="24" /> ';
|
||||
instrumentDesc.push(instrumentId);
|
||||
|
|
@ -378,7 +385,14 @@
|
|||
$.each(sessionData.approved_rsvps, function(index, approved_rsvp) {
|
||||
if ("instrument_list" in approved_rsvp) {
|
||||
$.each(approved_rsvp.instrument_list, function(index, instrument) {
|
||||
var instrumentId = context.JK.getInstrumentId(instrument.id);
|
||||
var instrumentId;
|
||||
|
||||
if (instrument) {
|
||||
instrumentId = context.JK.getInstrumentId(instrument.id);
|
||||
}
|
||||
else {
|
||||
instrumentId = 'other';
|
||||
}
|
||||
var inst = context.JK.getInstrumentIcon24(instrumentId);
|
||||
instrumentLogoHtml += '<img title="' + instrumentId + '" hoveraction="instrument" data-instrument-id="' + instrumentId + '" src="' + inst + '" width="24" height="24" /> ';
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,130 @@
|
|||
(function(context,$) {
|
||||
|
||||
"use strict";
|
||||
context.JK = context.JK || {};
|
||||
context.JK.SessionStartDialog = function(app, session) {
|
||||
var logger = context.JK.logger;
|
||||
var sessionUtils = context.JK.SessionUtils;
|
||||
var $dialog = null;
|
||||
var dialogId = 'session-start-dialog';
|
||||
var $btnStartSession = null;
|
||||
|
||||
function beforeShow(data) {
|
||||
}
|
||||
|
||||
function afterShow(data) {
|
||||
}
|
||||
|
||||
function afterHide() {
|
||||
}
|
||||
|
||||
function showDialog() {
|
||||
return app.layout.showDialog(dialogId);
|
||||
}
|
||||
|
||||
function events() {
|
||||
$btnStartSession.unbind('click');
|
||||
$btnStartSession.click(function(e) {
|
||||
context.location = '/client#/session/' + session.id;
|
||||
app.layout.closeDialog(dialogId);
|
||||
});
|
||||
}
|
||||
|
||||
function initializeSessionDetails() {
|
||||
$dialog.find('#session-start-type-disp').html('Now!');
|
||||
$dialog.find('#session-name-disp').html(session.name);
|
||||
$dialog.find('#session-description-disp').html(session.description);
|
||||
|
||||
if (session.music_notations && session.music_notations.length > 0) {
|
||||
$dialog.find('#session-notations-disp').html("Notations: " + session.music_notations.join(', '));
|
||||
}
|
||||
|
||||
if (session.band) {
|
||||
$dialog.find('#session-band-disp').html(band.name);
|
||||
}
|
||||
else {
|
||||
$dialog.find('#session-band-disp').html('N/A');
|
||||
}
|
||||
|
||||
$dialog.find('#session-language-disp').html(session.language_description);
|
||||
|
||||
var invitedFriends = session.invitations;
|
||||
|
||||
var sessionInvited = [];
|
||||
$.each(invitedFriends, function(index, invitation) {
|
||||
sessionInvited.push(invitation.receiver_name);
|
||||
});
|
||||
|
||||
var sessionInvitedString = sessionInvited.join(', ');
|
||||
|
||||
if (session.musician_access && session.approval_required) {
|
||||
if (session.open_rsvps) {
|
||||
if (invitedFriends.length == 0)
|
||||
sessionInvitedString = "Any interested JamKazam musicians that I approve";
|
||||
else
|
||||
sessionInvitedString += ", plus any interested JamKazam musicians that I approve";
|
||||
}
|
||||
else {
|
||||
if (invitedFriends.length == 0) {
|
||||
sessionInvitedString = "No open RSVPs";
|
||||
}
|
||||
else {
|
||||
sessionInvitedString += " (No open RSVPs)";
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (session.musician_access && !session.approval_required) {
|
||||
if (invitedFriends.length == 0)
|
||||
sessionInvitedString = "Any interested JamKazam musicians who want to join us";
|
||||
else
|
||||
sessionInvitedString += ", plus any interested JamKazam musicians who want to join us";
|
||||
}
|
||||
|
||||
$dialog.find('#session-invited-disp').html(sessionInvitedString);
|
||||
|
||||
var instrumentsMe = [], instrumentsOthers = [];
|
||||
|
||||
$.each(session.approved_rsvps, function(index, rsvp) {
|
||||
if (rsvp.id === context.JK.currentUserId) {
|
||||
$.each(rsvp.instrument_list, function(index, instrument) {
|
||||
instrumentsMe.push(instrument.desc);
|
||||
});
|
||||
}
|
||||
else {
|
||||
$.each(rsvp.instrument_list, function(index, instrument) {
|
||||
instrumentsOthers.push(instrument.desc);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$dialog.find('#session-instruments-me-disp').html(instrumentsMe.join(', '));
|
||||
$dialog.find('#session-instruments-rsvp-disp').html(instrumentsOthers.join(', '));
|
||||
|
||||
$dialog.find('#session-musician-access-disp').html('Musicians: ' + session.musician_access_description);
|
||||
$dialog.find('#session-fans-access-disp').html('Fans: ' + session.fan_access_description);
|
||||
$dialog.find('#session-policy-disp').html(session.legal_policy);
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
|
||||
var dialogBindings = {
|
||||
'beforeShow' : beforeShow,
|
||||
'afterShow' : afterShow,
|
||||
'afterHide': afterHide
|
||||
};
|
||||
|
||||
app.bindDialog(dialogId, dialogBindings);
|
||||
|
||||
$dialog = $('[layout-id="' + dialogId + '"]');
|
||||
$btnStartSession = $dialog.find('.btnStartSession');
|
||||
|
||||
initializeSessionDetails();
|
||||
events();
|
||||
}
|
||||
|
||||
this.initialize = initialize;
|
||||
this.showDialog = showDialog;
|
||||
}
|
||||
|
||||
return this;
|
||||
})(window,jQuery);
|
||||
|
|
@ -37,14 +37,14 @@
|
|||
}
|
||||
|
||||
function buildParams() {
|
||||
return { type: 'TEXT_MESSAGE', receiver: otherId, offset: offset, limit: LIMIT};
|
||||
return { target_user_id: otherId, offset: offset, limit: LIMIT};
|
||||
}
|
||||
|
||||
function buildMessage() {
|
||||
var message = {};
|
||||
|
||||
message['message'] = $textBox.val();
|
||||
message['receiver'] = otherId;
|
||||
message['target_user_id'] = otherId;
|
||||
|
||||
return message;
|
||||
}
|
||||
|
|
@ -66,17 +66,17 @@
|
|||
$sendTextMessage.text('SENDING...')
|
||||
|
||||
rest.createTextMessage(buildMessage())
|
||||
.done(function() {
|
||||
$textBox.val('');
|
||||
renderMessage(msg, user.id, user.name, new Date().toISOString(), true);
|
||||
})
|
||||
.fail(function(jqXHR) {
|
||||
app.notifyServerError(jqXHR, 'Unable to Send Message');
|
||||
})
|
||||
.always(function() {
|
||||
sendingMessage = false;
|
||||
$sendTextMessage.text('SEND');
|
||||
})
|
||||
.done(function() {
|
||||
$textBox.val('');
|
||||
renderMessage(msg, user.id, user.name, new Date().toISOString(), true);
|
||||
})
|
||||
.fail(function(jqXHR) {
|
||||
app.notifyServerError(jqXHR, 'Unable to Send Message');
|
||||
})
|
||||
.always(function() {
|
||||
sendingMessage = false;
|
||||
$sendTextMessage.text('SEND');
|
||||
});
|
||||
|
||||
}
|
||||
return false;
|
||||
|
|
@ -163,7 +163,7 @@
|
|||
}
|
||||
$sendTextMessage.click(sendMessage);
|
||||
|
||||
rest.getNotifications(buildParams())
|
||||
rest.getTextMessages(buildParams())
|
||||
.done(function (response) {
|
||||
context._.each(response, function (textMessage) {
|
||||
renderMessage(textMessage.message, textMessage.source_user_id, userLookup[textMessage.source_user_id].name, textMessage.created_at);
|
||||
|
|
|
|||
|
|
@ -1262,13 +1262,24 @@
|
|||
var id = getId(options);
|
||||
return $.ajax({
|
||||
type: "POST",
|
||||
url: '/api/users/' + id + '/notifications',
|
||||
url: '/api/text_messages',
|
||||
dataType: "json",
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(options)
|
||||
});
|
||||
}
|
||||
|
||||
function getTextMessages(options) {
|
||||
if(!options) options = {};
|
||||
var id = getId(options);
|
||||
return $.ajax({
|
||||
type: "GET",
|
||||
url: '/api/text_messages?' + $.param(options),
|
||||
dataType: "json",
|
||||
contentType: 'application/json'
|
||||
});
|
||||
}
|
||||
|
||||
function getNotifications(options) {
|
||||
if(!options) options = {};
|
||||
var id = getId(options);
|
||||
|
|
@ -1597,6 +1608,7 @@
|
|||
this.tweet = tweet;
|
||||
this.createFbInviteUrl = createFbInviteUrl;
|
||||
this.createTextMessage = createTextMessage;
|
||||
this.getTextMessages = getTextMessages;
|
||||
this.getNotifications = getNotifications;
|
||||
this.createChatMessage = createChatMessage;
|
||||
this.getChatMessages = getChatMessages;
|
||||
|
|
|
|||
|
|
@ -207,20 +207,25 @@
|
|||
isLoading = true;
|
||||
// retrieve pending notifications for this user
|
||||
rest.getNotifications(buildParams())
|
||||
.done(function(response) {
|
||||
updateNotificationList(response);
|
||||
isLoading = false;
|
||||
})
|
||||
.fail(function() {
|
||||
isLoading = false;
|
||||
app.ajaxError();
|
||||
})
|
||||
.done(function(response) {
|
||||
updateNotificationList(response);
|
||||
isLoading = false;
|
||||
})
|
||||
.fail(function() {
|
||||
isLoading = false;
|
||||
app.ajaxError();
|
||||
})
|
||||
}
|
||||
|
||||
function updateNotificationList(response) {
|
||||
$.each(response, function(index, val) {
|
||||
|
||||
if(val.description == 'TEXT_MESSAGE') {
|
||||
// this means the session no longer exists
|
||||
if (response.fan_access == null && response.musician_access == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(val.description == context.JK.MessageType.TEXT_MESSAGE) {
|
||||
val.formatted_msg = textMessageDialog.formatTextMessage(val.message.substring(0, 200), val.source_user_id, val.source_user.name, val.message.length > 200).html();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -200,14 +200,14 @@
|
|||
|
||||
createSessionSettings.startDate = createSessionSettings.startDate || (new Date().toDateString());
|
||||
|
||||
$("#session-start-date").val(createSessionSettings.startDate);
|
||||
$screen.find("#session-start-date").val(createSessionSettings.startDate);
|
||||
toggleDate(true);
|
||||
toggleStartTime();
|
||||
toggleStepStatus();
|
||||
sessionUtils.defaultTimezone($timezoneList);
|
||||
if(firstTimeShown) {
|
||||
firstTimeShown = false;
|
||||
$('#session-when-start-scheduled').iCheck('check');
|
||||
$screen.find('#session-when-start-scheduled').iCheck('check');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -217,7 +217,7 @@
|
|||
function beforeShowStep3() {
|
||||
rest.getBands(context.JK.currentUserId)
|
||||
.done(function(result) {
|
||||
var options = $("#session-band-list");
|
||||
var options = $screen.find("#session-band-list");
|
||||
options.empty();
|
||||
options.append($("<option />").val('').text('No'));
|
||||
$.each(result, function(idx, item) {
|
||||
|
|
@ -251,10 +251,10 @@
|
|||
|
||||
var sessionName = createSessionSettings.name;
|
||||
sessionName += ' (' + createSessionSettings.genresValues[0] + ')';
|
||||
$('#session-name-disp').html(sessionName);
|
||||
$screen.find('#session-name-disp').html(sessionName);
|
||||
|
||||
var sessionDescription = createSessionSettings.description;
|
||||
$('#session-description-disp').html(sessionDescription);
|
||||
$screen.find('#session-description-disp').html(sessionDescription);
|
||||
|
||||
var sessionNotations = [];
|
||||
for (var i = 0; i < createSessionSettings.notations.length; i++) {
|
||||
|
|
@ -262,16 +262,16 @@
|
|||
sessionNotations.push(name);
|
||||
}
|
||||
if(sessionNotations.length > 0) {
|
||||
$('#session-notations-disp').html("Notations: " + sessionNotations.join(', '));
|
||||
$screen.find('#session-notations-disp').html("Notations: " + sessionNotations.join(', '));
|
||||
}
|
||||
else {
|
||||
$('#session-notations-disp').html('');
|
||||
$screen.find('#session-notations-disp').html('');
|
||||
}
|
||||
|
||||
$('#session-language-disp').html(createSessionSettings.language.label);
|
||||
$('#session-band-disp').html(createSessionSettings.band.label);
|
||||
$screen.find('#session-language-disp').html(createSessionSettings.language.label);
|
||||
$screen.find('#session-band-disp').html(createSessionSettings.band.label);
|
||||
|
||||
var plusMusicians = $('#session-plus-musicians')[0].checked;
|
||||
var plusMusicians = $screen.find('#session-plus-musicians')[0].checked;
|
||||
|
||||
var sessionInvited = [];
|
||||
var invitedFriends = inviteMusiciansUtil.getInvitedFriendNames();
|
||||
|
|
@ -302,7 +302,7 @@
|
|||
else
|
||||
sessionInvitedString += ", plus any interested JamKazam musicians who want to join us";
|
||||
}
|
||||
$('#session-invited-disp').html(sessionInvitedString);
|
||||
$screen.find('#session-invited-disp').html(sessionInvitedString);
|
||||
|
||||
if (createSessionSettings.createType == '<%= MusicSession::CREATE_TYPE_START_SCHEDULED%>') {
|
||||
var session = scheduledSessions[createSessionSettings.selectedSessionId];
|
||||
|
|
@ -315,7 +315,7 @@
|
|||
});
|
||||
}
|
||||
});
|
||||
$('#session-instruments-me-disp').html(instruments_me.join(', '));
|
||||
$screen.find('#session-instruments-me-disp').html(instruments_me.join(', '));
|
||||
}
|
||||
|
||||
if (session.open_slots.length > 0) {
|
||||
|
|
@ -334,7 +334,7 @@
|
|||
$.map(instruments_rsvp_arr, function(val, i) {
|
||||
instruments_str_arr.push(i + ' (' + val.count + ') (' + val.level + ')');
|
||||
})
|
||||
$('#session-instruments-rsvp-disp').html(instruments_str_arr.join(', '));
|
||||
$screen.find('#session-instruments-rsvp-disp').html(instruments_str_arr.join(', '));
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -342,7 +342,7 @@
|
|||
$.each(getCreatorInstruments(), function(index, instrument) {
|
||||
instruments_me.push(instrument.name);
|
||||
});
|
||||
$('#session-instruments-me-disp').html(instruments_me.join(', '));
|
||||
$screen.find('#session-instruments-me-disp').html(instruments_me.join(', '));
|
||||
|
||||
var instruments_rsvp = [];
|
||||
var otherInstruments = instrumentRSVP.getSelectedInstruments();
|
||||
|
|
@ -353,13 +353,13 @@
|
|||
$.each(otherInstruments, function(index, instrument) {
|
||||
instruments_rsvp.push(instrument.name + ' (' + instrument.count + ') (' + proficiencyDescriptionMap[instrument.level] + ')');
|
||||
});
|
||||
$('#session-instruments-rsvp-disp').html(instruments_rsvp.join(', '));
|
||||
$screen.find('#session-instruments-rsvp-disp').html(instruments_rsvp.join(', '));
|
||||
}
|
||||
|
||||
$('#session-musician-access-disp').html('Musicians: ' + createSessionSettings.musician_access.label);
|
||||
$('#session-fans-access-disp').html('Fans: ' + createSessionSettings.fans_access.label);
|
||||
$screen.find('#session-musician-access-disp').html('Musicians: ' + createSessionSettings.musician_access.label);
|
||||
$screen.find('#session-fans-access-disp').html('Fans: ' + createSessionSettings.fans_access.label);
|
||||
|
||||
$('#session-policy-disp').html(createSessionSettings.session_policy);
|
||||
$screen.find('#session-policy-disp').html(createSessionSettings.session_policy);
|
||||
}
|
||||
|
||||
function beforeMoveStep1() {
|
||||
|
|
@ -428,7 +428,7 @@
|
|||
createSessionSettings.recurring_mode.value = 'once';
|
||||
}
|
||||
else {
|
||||
createSessionSettings.startDate = $('#session-start-date').val();
|
||||
createSessionSettings.startDate = $screen.find('#session-start-date').val();
|
||||
createSessionSettings.startTime = $startTimeList.val();
|
||||
createSessionSettings.endTime = $endTimeList.val();
|
||||
createSessionSettings.notations = [];
|
||||
|
|
@ -437,7 +437,7 @@
|
|||
createSessionSettings.timezone.label = $timezoneList.get(0).options[$timezoneList.get(0).selectedIndex].text;
|
||||
createSessionSettings.recurring_mode.label = $recurringModeList.get(0).options[$recurringModeList.get(0).selectedIndex].text;
|
||||
createSessionSettings.recurring_mode.value = $recurringModeList.val();
|
||||
createSessionSettings.open_rsvps = $('#session-plus-musicians')[0].checked;
|
||||
createSessionSettings.open_rsvps = $screen.find('#session-plus-musicians')[0].checked;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -503,22 +503,22 @@
|
|||
|
||||
function beforeMoveStep2() {
|
||||
var isValid = true;
|
||||
var name = $('#session-name').val();
|
||||
var name = $screen.find('#session-name').val();
|
||||
if (!name) {
|
||||
$('#divSessionName .error-text').remove();
|
||||
$('#divSessionName').addClass("error");
|
||||
$('#session-name').after("<ul class='error-text'><li>Name is required</li></ul>");
|
||||
$screen.find('#session-name').after("<ul class='error-text'><li>Name is required</li></ul>");
|
||||
isValid = false;
|
||||
}
|
||||
else {
|
||||
$('#divSessionName').removeClass("error");
|
||||
}
|
||||
|
||||
var description = $('#session-description').val();
|
||||
var description = $screen.find('#session-description').val();
|
||||
if (!description) {
|
||||
$('#divSessionDescription .error-text').remove();
|
||||
$('#divSessionDescription').addClass("error");
|
||||
$('#session-description').after("<ul class='error-text'><li>Description is required</li></ul>");
|
||||
$screen.find('#session-description').after("<ul class='error-text'><li>Description is required</li></ul>");
|
||||
isValid = false;
|
||||
}
|
||||
else {
|
||||
|
|
@ -558,13 +558,13 @@
|
|||
else
|
||||
createSessionSettings.band.label = $bandList.get(0).options[$bandList.get(0).selectedIndex].text;
|
||||
|
||||
createSessionSettings.open_rsvps = $('#session-plus-musicians')[0].checked;
|
||||
createSessionSettings.open_rsvps = $screen.find('#session-plus-musicians')[0].checked;
|
||||
return true;
|
||||
}
|
||||
|
||||
function beforeMoveStep4() {
|
||||
var isValid = true;
|
||||
var sessionPolicyChecked = $('#session-policy-confirm').is(':checked');
|
||||
var sessionPolicyChecked = $screen.find('#session-policy-confirm').is(':checked');
|
||||
if (!sessionPolicyChecked) {
|
||||
$('#divSessionPolicy .error-text').remove();
|
||||
$('#divSessionPolicy').addClass("error");
|
||||
|
|
@ -576,11 +576,11 @@
|
|||
}
|
||||
|
||||
createSessionSettings.session_policy = $('input[name="session-policy-type"][checked="checked"]').attr('policy-id');
|
||||
var $musicianAccess = $('#session-musician-access');
|
||||
var $musicianAccess = $screen.find('#session-musician-access');
|
||||
createSessionSettings.musician_access.value = $musicianAccess.val();
|
||||
createSessionSettings.musician_access.label = $musicianAccess.get(0).options[$musicianAccess.get(0).selectedIndex].text;
|
||||
|
||||
var $fansAccess = $('#session-fans-access');
|
||||
var $fansAccess = $screen.find('#session-fans-access');
|
||||
createSessionSettings.fans_access.value = $fansAccess.val();
|
||||
createSessionSettings.fans_access.label = $fansAccess.get(0).options[$fansAccess.get(0).selectedIndex].text;
|
||||
|
||||
|
|
@ -975,7 +975,7 @@
|
|||
}
|
||||
|
||||
function toggleDate(dontRebuildDropdowns) {
|
||||
var selectedDate = new Date($('#session-start-date').val());
|
||||
var selectedDate = new Date($screen.find('#session-start-date').val());
|
||||
var currentDate = new Date();
|
||||
var startIndex = 0;
|
||||
|
||||
|
|
@ -1046,7 +1046,7 @@
|
|||
radioClass: 'iradio_minimal',
|
||||
inheritClass: true
|
||||
});
|
||||
$("#session-start-date").datepicker({
|
||||
$screen.find("#session-start-date").datepicker({
|
||||
dateFormat: "D d MM yy",
|
||||
onSelect: function() { toggleDate(); }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -836,7 +836,6 @@
|
|||
var metronomeTrackMixers = [];
|
||||
var adhocTrackMixers = [];
|
||||
|
||||
console.log("_renderLocalMediaTracks", localMediaMixers)
|
||||
function groupByType(mixers) {
|
||||
context._.each(mixers, function(mixer) {
|
||||
var mediaType = mixer.media_type;
|
||||
|
|
@ -1254,6 +1253,11 @@
|
|||
|
||||
if (mixer && oppositeMixer) {
|
||||
myTrack = (mixer.group_id === ChannelGroupIds.AudioInputMusicGroup);
|
||||
if(!myTrack) {
|
||||
// it only makes sense to track 'audio established' for tracks that don't belong to you
|
||||
sessionModel.setAudioEstablished(participant.client_id, true);
|
||||
}
|
||||
|
||||
var gainPercent = percentFromMixerValue(
|
||||
mixer.range_low, mixer.range_high, mixer.volume_left);
|
||||
var muteClass = "enabled";
|
||||
|
|
@ -1342,6 +1346,10 @@
|
|||
var muteMixer = mixerData.muteMixer;
|
||||
|
||||
if (mixer && oppositeMixer) {
|
||||
if(!myTrack) {
|
||||
// it only makes sense to track 'audio established' for tracks that don't belong to you
|
||||
sessionModel.setAudioEstablished(clientId, true);
|
||||
}
|
||||
var participant = (sessionModel.getParticipant(clientId) || {name:'unknown'}).name;
|
||||
logger.debug("found mixer=" + mixer.id + ", participant=" + participant)
|
||||
usedMixers[mixer.id] = true;
|
||||
|
|
@ -1369,6 +1377,13 @@
|
|||
$('.disabled-track-overlay', $track).show();
|
||||
$('.track-connection', $track).removeClass('red yellow green').addClass('red');
|
||||
}
|
||||
// if 5 seconds have gone by and no mixer, then we tell the server failed to establish audio
|
||||
else if(lookingForMixersCount == 10) {
|
||||
if(!myTrack) {
|
||||
// it only makes sense to track 'audio established' for tracks that don't belong to you
|
||||
sessionModel.setAudioEstablished(clientId, false);
|
||||
}
|
||||
}
|
||||
var participant = (sessionModel.getParticipant(clientId) || { user: {name: 'unknown'}}).user.name;
|
||||
logger.debug("still looking for mixer for participant=" + participant + ", clientId=" + clientId)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
var $musicianTemplate = $('#template-musician-info');
|
||||
var showJoinLink = true;
|
||||
var showListenLink = true;
|
||||
var showRsvpLink = true;
|
||||
var MAX_MINUTES_SHOW_START = 15;
|
||||
|
||||
// related to listen
|
||||
function stateChange(e, data) {
|
||||
|
|
@ -88,7 +88,6 @@
|
|||
var inSessionUsers = [];
|
||||
|
||||
showJoinLink = session.musician_access;
|
||||
console.log('session', session)
|
||||
showListenLink = session.fan_access && session.active_music_session && session.active_music_session.mount;
|
||||
|
||||
// render musicians who are already in the session
|
||||
|
|
@ -159,6 +158,17 @@
|
|||
sessionVals.in_session_musicians = inSessionUsersHtml.length > 0 ? inSessionUsersHtml : 'N/A';
|
||||
sessionVals.join_link_display_style = showJoinLink ? "block" : "none";
|
||||
sessionVals.listen_link_display_style = showListenLink ? "inline-block" : "none";
|
||||
if (!session.fan_access) {
|
||||
sessionVals.listen_link_text = '';
|
||||
}
|
||||
else if (session.active_music_session && session.active_music_session.mount) {
|
||||
sessionVals.listen_link_text = 'Listen';
|
||||
}
|
||||
else {
|
||||
sessionVals.listen_link_text = '';
|
||||
}
|
||||
|
||||
|
||||
|
||||
var $row = $(context.JK.fillTemplate($activeSessionTemplate.html(), sessionVals));
|
||||
var $offsetParent = $(tbGroup).closest('.content');
|
||||
|
|
@ -359,11 +369,39 @@
|
|||
|
||||
var showRsvpLink = true;
|
||||
var noLinkText = '';
|
||||
$('.rsvp-link-text', $parentRow).hide();
|
||||
|
||||
if (approvedRsvpId) {
|
||||
function showStartSessionButton(scheduledStart) {
|
||||
var now = new Date();
|
||||
var scheduledDate = new Date(scheduledStart);
|
||||
var minutesFromStart = (scheduledDate.getTime() - now.getTime()) / (1000 * 60);
|
||||
return minutesFromStart <= MAX_MINUTES_SHOW_START;
|
||||
};
|
||||
|
||||
if (session.creator.id === context.JK.currentUserId) {
|
||||
showRsvpLink = false;
|
||||
noLinkText = $('<span class="text">You have been confirmed for this session. <a href="#" style="color: #fc0">Cancel</a></span>');
|
||||
noLinkText = $('<span class="text"><a class="start" style="color: #fc0">Start session now?</a></span>');
|
||||
noLinkText.find('a').click(function() {
|
||||
ui.launchSessionStartDialog(session);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
else if (approvedRsvpId) {
|
||||
showRsvpLink = false;
|
||||
|
||||
if (session.scheduled_start && showStartSessionButton(session.scheduled_start)) {
|
||||
noLinkText = $('<span class="text"><a class="start" style="color: #fc0">Start session now?</a> | <a class="cancel" style="color: #fc0">Cancel RSVP</a></span>');
|
||||
noLinkText.find('a.start').click(function() {
|
||||
ui.launchSessionStartDialog(session);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
else {
|
||||
noLinkText = $('<span class="text"><a class="cancel" style="color: #fc0">Cancel RSVP</a></span>');
|
||||
}
|
||||
|
||||
// wire cancel link
|
||||
noLinkText.find('a.cancel').click(function() {
|
||||
ui.launchRsvpCancelDialog(session.id, approvedRsvpId)
|
||||
.one(EVENTS.RSVP_CANCELED, function() {
|
||||
rest.getSessionHistory(session.id)
|
||||
|
|
@ -377,9 +415,20 @@
|
|||
return false;
|
||||
});
|
||||
}
|
||||
else if (hasInvitation) {
|
||||
showRsvpLink = false;
|
||||
|
||||
if (session.scheduled_start && showStartSessionButton(session.scheduled_start)) {
|
||||
noLinkText = $('<span class="text"><a class="start" style="color: #fc0">Start session now?</a></span>');
|
||||
noLinkText.find('a').click(function() {
|
||||
ui.launchSessionStartDialog(session);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (pendingRsvpId) {
|
||||
showRsvpLink = false;
|
||||
noLinkText = $('<span class="text">You have RSVP\'ed to this session. <a href="#" style="color: #fc0">Cancel</a></span>');
|
||||
noLinkText = $('<span class="text"><a class="cancel" style="color: #fc0">Cancel RSVP</a></span>');
|
||||
noLinkText.find('a').click(function() {
|
||||
ui.launchRsvpCancelDialog(session.id, pendingRsvpId)
|
||||
.one(EVENTS.RSVP_CANCELED, function() {
|
||||
|
|
@ -406,6 +455,7 @@
|
|||
if (showRsvpLink) {
|
||||
$('.rsvp-msg', $parentRow).hide();
|
||||
$('.rsvp-link', $parentRow).show();
|
||||
$('.rsvp-link-text', $parentRow).show();
|
||||
|
||||
$('.rsvp-link', $parentRow).click(function(evt) {
|
||||
ui.launchRsvpSubmitDialog(session.id)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
var backendMixerAlertThrottleTimer = null;
|
||||
// we track all the clientIDs of all the participants ever seen by this session, so that we can reliably convert a clientId from the backend into a username/avatar
|
||||
var participantsEverSeen = {};
|
||||
var currentParticipants = {};
|
||||
var $self = $(this);
|
||||
var sessionPageEnterDeferred = null;
|
||||
var sessionPageEnterTimeout = null;
|
||||
|
|
@ -301,6 +302,7 @@
|
|||
$(document).trigger(EVENTS.SESSION_ENDED, {session: {id: currentSessionId}});
|
||||
}
|
||||
currentSessionId = null;
|
||||
currentParticipants = {}
|
||||
}
|
||||
|
||||
// you should only update currentSession with this function
|
||||
|
|
@ -391,6 +393,16 @@
|
|||
};
|
||||
}
|
||||
|
||||
function ParticipantJoined(newSession, participant) {
|
||||
client.ParticipantJoined(newSession, _toJamClientParticipant(participant));
|
||||
currentParticipants[participant.client_id] = {server:participant, client: {audio_established:null}}
|
||||
}
|
||||
|
||||
function ParticipantLeft(newSession, participant) {
|
||||
client.ParticipantLeft(newSession, _toJamClientParticipant(participant));
|
||||
delete currentParticipants[participant.client_id]
|
||||
}
|
||||
|
||||
function sendClientParticipantChanges(oldSession, newSession) {
|
||||
var joins = [], leaves = [], leaveJoins = []; // Will hold JamClientParticipants
|
||||
|
||||
|
|
@ -422,40 +434,41 @@
|
|||
// if the participant is here now, and here before, there is still a chance we missed a
|
||||
// very fast leave/join. So check if joined_session_at is different
|
||||
if(oldParticipantIds[client_id].joined_session_at != participant.joined_session_at) {
|
||||
leaveJoins.push(_toJamClientParticipant(participant))
|
||||
leaveJoins.push(participant)
|
||||
}
|
||||
}
|
||||
else {
|
||||
// new participant id that's not in old participant ids: Join
|
||||
joins.push(_toJamClientParticipant(participant));
|
||||
joins.push(participant);
|
||||
}
|
||||
});
|
||||
$.each(oldParticipantIds, function(client_id, participant) {
|
||||
if (!(client_id in newParticipantIds)) {
|
||||
// old participant id that's not in new participant ids: Leave
|
||||
leaves.push(_toJamClientParticipant(participant));
|
||||
leaves.push(participant);
|
||||
}
|
||||
});
|
||||
|
||||
$.each(joins, function(i,v) {
|
||||
if (v.client_id != clientId) {
|
||||
logger.debug("jamClient.ParticipantJoined", v.clientID)
|
||||
client.ParticipantJoined(newSession, v);
|
||||
logger.debug("jamClient.ParticipantJoined", v.client_id)
|
||||
ParticipantJoined(newSession, v)
|
||||
}
|
||||
});
|
||||
$.each(leaves, function(i,v) {
|
||||
if (v.client_id != clientId) {
|
||||
logger.debug("jamClient.ParticipantLeft", v.clientID)
|
||||
client.ParticipantLeft(newSession, v);
|
||||
logger.debug("jamClient.ParticipantLeft", v.client_id)
|
||||
ParticipantLeft(newSession, v)
|
||||
}
|
||||
});
|
||||
$.each(leaveJoins, function(i,v) {
|
||||
if (v.client_id != clientId) {
|
||||
logger.debug("participant had a rapid leave/join")
|
||||
logger.debug("jamClient.ParticipantLeft", v.clientID)
|
||||
client.ParticipantLeft(newSession, v);
|
||||
logger.debug("jamClient.ParticipantJoined", v.clientID)
|
||||
client.ParticipantJoined(newSession, v);
|
||||
logger.debug("jamClient.ParticipantLeft", v.client_id)
|
||||
ParticipantLeft(newSession, v)
|
||||
|
||||
logger.debug("jamClient.ParticipantJoined", v.client_id)
|
||||
ParticipantJoined(newSession, v)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -754,6 +767,23 @@
|
|||
this.getParticipant = function(clientId) {
|
||||
return participantsEverSeen[clientId]
|
||||
};
|
||||
|
||||
// call to report if the current user was able to establish audio with the specified clientID
|
||||
this.setAudioEstablished = function(clientId, audioEstablished) {
|
||||
var participant = currentParticipants[clientId]
|
||||
if(participant) {
|
||||
if(participant.client.audio_established === null) {
|
||||
participant.client.audio_established = audioEstablished;
|
||||
context.stats.write('client.audio_established.' + (audioEstablished ? 'success': 'failure'), {user_id: context.JK.currentUserId, name: context.JK.currentUserName, remote_user_id: participant.server.user.id, remote_name: participant.server.user.name, value: 1})
|
||||
}
|
||||
else if(participant.client.audio_established === false && audioEstablished === true) {
|
||||
// this means the frontend had declared this audio failed, but later says it works.
|
||||
// this could mean our threshold of time to wait before audio is considered failed is too low, and needs tweaking
|
||||
context.stats.write('client.audio_established.delayed', {user_id: context.JK.currentUserId, name: context.JK.currentUserName, remote_user_id: participant.server.user.id, remote_name: participant.server.user.name, value: 1})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.ensureEnded = function() {
|
||||
updateCurrentSession(null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,12 @@
|
|||
return rsvpDialog.showDialog();
|
||||
}
|
||||
|
||||
function launchSessionStartDialog(session) {
|
||||
var sessionStartDialog = new JK.SessionStartDialog(JK.app, session);
|
||||
sessionStartDialog.initialize();
|
||||
return sessionStartDialog.showDialog();
|
||||
}
|
||||
|
||||
this.addSessionLike = addSessionLike;
|
||||
this.addRecordingLike = addRecordingLike;
|
||||
this.launchCommentDialog = launchCommentDialog;
|
||||
|
|
@ -60,6 +66,7 @@
|
|||
this.launchRsvpSubmitDialog = launchRsvpSubmitDialog;
|
||||
this.launchRsvpCancelDialog = launchRsvpCancelDialog;
|
||||
this.launchRsvpCreateSlotDialog = launchRsvpCreateSlotDialog;
|
||||
this.launchSessionStartDialog = launchSessionStartDialog;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -127,6 +127,10 @@ table.findsession-table, table.local-recordings, table.open-jam-tracks {
|
|||
vertical-align:top;
|
||||
}
|
||||
|
||||
.musician-groups td.nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
a {
|
||||
color:#fff;
|
||||
text-decoration:none;
|
||||
|
|
|
|||
|
|
@ -70,8 +70,8 @@
|
|||
min-height: 600px;
|
||||
}
|
||||
|
||||
.dialog-overlay-sm {
|
||||
width: 600px;
|
||||
.dialog-overlay-lg {
|
||||
width: 800px;
|
||||
height: auto;
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
|
|
@ -90,6 +90,18 @@
|
|||
color: #aaa;
|
||||
}
|
||||
|
||||
.dialog-overlay-sm {
|
||||
width: 600px;
|
||||
height: auto;
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
top: 20%;
|
||||
margin-left: -300px;
|
||||
background-color: #333;
|
||||
border: 1px solid #ed3618;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.dialog-overlay-sm .dialog-inner {
|
||||
width: 550px;
|
||||
height: auto;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
.session-wrapper {
|
||||
padding: 10px 35px 0 0px;
|
||||
white-space: initial;
|
||||
|
||||
h3 {
|
||||
font-weight: bold;
|
||||
color:#dedede;
|
||||
}
|
||||
|
||||
> div.session {
|
||||
width: 50%;
|
||||
|
||||
&.right {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -34,8 +34,8 @@ class ApiJamTracksController < ApiController
|
|||
def download
|
||||
if @jam_track_right.valid?
|
||||
if (@jam_track_right && @jam_track_right.signed && @jam_track_right.url.present? &&@jam_track_right.url.file.exists?)
|
||||
JamTrackRight.where(:id => @jam_track_right.id).update_all(:last_downloaded_at => Time.now)
|
||||
@jam_track_right.update_download_count
|
||||
@jam_track_right.update_download_count
|
||||
@jam_track_right.last_downloaded_at = Time.now
|
||||
@jam_track_right.save!
|
||||
redirect_to @jam_track_right.sign_url
|
||||
else
|
||||
|
|
|
|||
|
|
@ -230,13 +230,13 @@ class ApiRecordingsController < ApiController
|
|||
# POST /api/recordings/:id/videos/:video_id/upload_sign
|
||||
def video_upload_sign
|
||||
length = params[:length]
|
||||
@youtube_client.upload_sign(current_user, @recorded_video.url, length)
|
||||
@youtube_client.sign_youtube_upload(current_user, @recorded_video.url, length)
|
||||
end
|
||||
|
||||
# POST /api/recordings/:id/videos/:video_id/upload_complete
|
||||
def video_upload_start
|
||||
length = params[:length]
|
||||
@youtube_client.get_upload_status(current_user, @recorded_video.url, length)
|
||||
@youtube_client.youtube_upload_status(current_user, @recorded_video.url, length)
|
||||
end
|
||||
|
||||
# POST /api/recordings/:id/videos/:video_id/upload_complete
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
require 'sanitize'
|
||||
class ApiTextMessagesController < ApiController
|
||||
|
||||
before_filter :api_signed_in_user
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@text_messages = TextMessage.index(params[:target_user_id], current_user.id, {:offset => params[:offset]})
|
||||
respond_with @text_messages, responder: ApiResponder, :status => 200
|
||||
end
|
||||
|
||||
def create
|
||||
@text_message = TextMessage.create(params[:message], params[:target_user_id], current_user.id)
|
||||
respond_with_model(@text_message, new: true)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
require 'builder'
|
||||
require 'recaptcha/rails'
|
||||
|
||||
class Slide
|
||||
attr_accessor :img_url, :header, :vid_url
|
||||
|
|
@ -142,10 +141,6 @@ class UsersController < ApplicationController
|
|||
terms_of_service = params[:jam_ruby_user][:terms_of_service].nil? || params[:jam_ruby_user][:terms_of_service] == "0"? false : true
|
||||
musician = params[:jam_ruby_user][:musician]
|
||||
|
||||
if Rails.application.config.recaptcha_enable
|
||||
recaptcha_failed = verify_recaptcha(:private_key=>Rails.application.config.recaptcha_private_key, :timeout=>10)
|
||||
end
|
||||
|
||||
@user = UserManager.new.signup(remote_ip: request.remote_ip,
|
||||
first_name: params[:jam_ruby_user][:first_name],
|
||||
last_name: params[:jam_ruby_user][:last_name],
|
||||
|
|
@ -157,7 +152,7 @@ class UsersController < ApplicationController
|
|||
birth_date: birth_date,
|
||||
location: location,
|
||||
musician: musician,
|
||||
recaptcha_failed: recaptcha_failed,
|
||||
recaptcha_response: params['g-recaptcha-response'],
|
||||
invited_user: @invited_user,
|
||||
fb_signup: @fb_signup,
|
||||
signup_confirm_url: ApplicationHelper.base_uri(request) + "/confirm",
|
||||
|
|
|
|||
|
|
@ -98,7 +98,9 @@ else
|
|||
|
||||
node do |invitation|
|
||||
user_score(invitation.receiver.id).merge({
|
||||
receiver_avatar_url: invitation.receiver.resolved_photo_url, audio_latency: last_jam_audio_latency(invitation.receiver)
|
||||
receiver_name: invitation.receiver.name,
|
||||
receiver_avatar_url: invitation.receiver.resolved_photo_url,
|
||||
audio_latency: last_jam_audio_latency(invitation.receiver)
|
||||
})
|
||||
end
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
collection @text_messages
|
||||
|
||||
attributes :id, :source_user_id, :target_user_id, :message, :created_at
|
||||
|
||||
node :source_user do |msg|
|
||||
attributes :id, :name
|
||||
end
|
||||
|
||||
node :target_user do |msg|
|
||||
attributes :id, :name
|
||||
end
|
||||
|
|
@ -97,7 +97,7 @@
|
|||
<%= image_tag "content/listen-icon.jpg", :size => "40x40" %>
|
||||
</a>
|
||||
<br/>
|
||||
<span class="listen-link-details"><span class="listen-link-text">Listen</span><a href="#" class="listen-detail-hover">?</a></span>
|
||||
<span class="listen-link-details"><span class="listen-link-text">{listen_link_text}</span><a href="#" class="listen-detail-hover">?</a></span>
|
||||
</td>
|
||||
<td>
|
||||
<a class="join-link" style="display:{join_link_display_style};">
|
||||
|
|
@ -196,7 +196,7 @@
|
|||
<tr>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<td class="nowrap">
|
||||
<span class="rsvp-msg" style="display:none;">You cannot RSVP to this session.</span>
|
||||
<a class="rsvp-link">
|
||||
<%= image_tag "content/rsvp-icon.jpg", :size => "40x40" %>
|
||||
|
|
|
|||
|
|
@ -402,9 +402,9 @@
|
|||
|
||||
<h3 class="mt20">What instruments/parts do you need?</h3>
|
||||
<div class="left ib w20 mt5">Me:</div>
|
||||
<div class="left ib w75 mt5" id="session-instruments-me-disp" class="mt5"></div>
|
||||
<div class="left ib w75 mt5" id="session-instruments-me-disp"></div>
|
||||
<div class="clearall left ib w20">Others:</div>
|
||||
<div class="left ib w75" id="session-instruments-rsvp-disp" class="mt5"></div><br clear="all">
|
||||
<div class="left ib w75 mt5" id="session-instruments-rsvp-disp"></div><br clear="all">
|
||||
|
||||
<h3 class="mt20">What access policies are in effect?</h3>
|
||||
<div id="session-musician-access-disp" class="mt5"></div>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<h1>Features & Benefits</h1>
|
||||
|
||||
<p>
|
||||
Musicians can enjoy four major features of the new JamKazam service, as described below. Also, several <a href="https://jamkazam.desk.com/customer/portal/articles/1304097-tutorial-videos" target="_blank">Tutorial Videos</a> are available to see how various features work in more detail.
|
||||
Musicians can enjoy four major features of the new JamKazam service, as described below. Also, several <a href="https://jamkazam.desk.com/customer/portal/topics/673198-tutorials-on-major-features/articles" target="_blank">Tutorial Videos</a> are available to see how various features work in more detail.
|
||||
</p>
|
||||
|
||||
<br/>
|
||||
|
|
|
|||
|
|
@ -23,5 +23,5 @@
|
|||
</p>
|
||||
|
||||
<p>
|
||||
Also, several <a href="https://jamkazam.desk.com/customer/portal/articles/1304097-tutorial-videos" target="_blank">Tutorial Videos</a> are available to see how various features work in more detail.
|
||||
Also, several <a href="https://jamkazam.desk.com/customer/portal/topics/673198-tutorials-on-major-features/articles" target="_blank">Tutorial Videos</a> are available to see how various features work in more detail.
|
||||
</p>
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
= render 'dialogs/rsvpSubmitDialog'
|
||||
= render 'dialogs/rsvpCancelDialog'
|
||||
= render 'dialogs/rsvpCreateSlotDialog'
|
||||
= render 'dialogs/sessionStartDialog'
|
||||
= render 'dialogs/sessionCancelDialog'
|
||||
= render 'dialogs/signinDialog'
|
||||
= render 'dialogs/signupDialog'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
.dialog.dialog-overlay-lg layout='dialog' layout-id='session-start-dialog' id='session-start-dialog'
|
||||
.content-head
|
||||
= image_tag "content/icon_add.png", {:width => 24, :height => 24, :class => 'content-icon' }
|
||||
h1 Start Session
|
||||
|
||||
.dialog-inner
|
||||
.session-wrapper
|
||||
.left.w55
|
||||
h3 When are you starting your session?
|
||||
.mt5 id='session-start-type-disp'
|
||||
h3.mb5.mt20 What are you playing?
|
||||
em id='session-name-disp'
|
||||
#session-description-disp.mt5
|
||||
#session-notations-disp.mt5
|
||||
|
||||
h3.mt20 Which band is playing?
|
||||
#session-band-disp.mt5
|
||||
|
||||
h3.mt20 What language will be spoken?
|
||||
#session-language-disp.mt5
|
||||
.right.w40
|
||||
h3 Who is invited?
|
||||
#session-invited-disp.mt5
|
||||
|
||||
h3.mt20 What instruments/parts do you need?
|
||||
.left.ib.w20.mt5 Me:
|
||||
#session-instruments-me-disp.left.ib.w75.mt5
|
||||
.clearall.left.ib.w20.mt5 Others:
|
||||
#session-instruments-rsvp-disp.left.ib.w75.mt5
|
||||
br clear='all'
|
||||
|
||||
h3.mt20 What access policies are in effect?
|
||||
#session-musician-access-disp.mt5
|
||||
#session-fans-access-disp
|
||||
|
||||
h3.mt20 What legal policy is in effect?
|
||||
#session-policy-disp.mt5
|
||||
.clearall
|
||||
br
|
||||
br
|
||||
.buttons
|
||||
.right
|
||||
a.button-grey class='btnCancel' layout-action='cancel' CANCEL
|
||||
a.button-orange class='btnStartSession' START SESSION
|
||||
|
|
@ -295,6 +295,10 @@ SampleApp::Application.routes.draw do
|
|||
match '/users/:id/friends/:friend_id' => 'api_users#friend_show', :via => :get, :as => 'api_friend_detail'
|
||||
match '/users/:id/friends/:friend_id' => 'api_users#friend_destroy', :via => :delete
|
||||
|
||||
# text messages
|
||||
match '/text_messages' => 'api_text_messages#index', :via => :get
|
||||
match '/text_messages' => 'api_text_messages#create', :via => :post
|
||||
|
||||
# notifications
|
||||
match '/users/:id/notifications' => 'api_users#notification_index', :via => :get
|
||||
match '/users/:id/notifications/:notification_id' => 'api_users#notification_destroy', :via => :delete
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ require 'socket'
|
|||
|
||||
# Youtube OAuth and API functionality:
|
||||
module JamRuby
|
||||
class YouTubeClient
|
||||
class GoogleClient
|
||||
attr_accessor :client
|
||||
attr_accessor :api
|
||||
attr_accessor :request
|
||||
|
|
@ -27,7 +27,7 @@ module JamRuby
|
|||
:application_version => '1.0.0'
|
||||
)
|
||||
|
||||
youtube = client.discovered_api('youtube', 'v3')
|
||||
#youtube = client.discovered_api('youtube', 'v3')
|
||||
end
|
||||
|
||||
# Return a login URL that will show a web page with
|
||||
|
|
@ -52,7 +52,7 @@ module JamRuby
|
|||
|
||||
# https://developers.google.com/youtube/v3/docs/videos/insert
|
||||
# https://developers.google.com/youtube/v3/guides/using_resumable_upload_protocol
|
||||
def upload_sign(user, filename, length)
|
||||
def sign_youtube_upload(user, filename, length)
|
||||
raise ArgumentError, "Length is required and should be > 0" if length.to_i.zero?
|
||||
|
||||
# Something like this:
|
||||
|
|
@ -140,7 +140,7 @@ module JamRuby
|
|||
end
|
||||
|
||||
# https://developers.google.com/youtube/v3/guides/using_resumable_upload_protocol#Check_Upload_Status
|
||||
def get_upload_status(user, upload_url, length)
|
||||
def youtube_upload_status(user, upload_url, length)
|
||||
auth = UserAuthorization.google_auth(user).first
|
||||
if auth.nil? || auth.token.nil?
|
||||
raise SecurityError, "No current google token found for user #{user}"
|
||||
|
|
@ -187,21 +187,50 @@ module JamRuby
|
|||
end
|
||||
|
||||
# @return true if file specified by URL uploaded, false otherwise
|
||||
def verify_upload(user, upload_url, length)
|
||||
status_hash=get_upload_status(user, upload_url, length)
|
||||
def verify_youtube_upload(user, upload_url, length)
|
||||
status_hash=youtube_upload_status(user, upload_url, length)
|
||||
(status_hash['status']>=200 && status_hash['status']<300)
|
||||
end
|
||||
|
||||
# Set fully_uploaded if the upload can be verified.
|
||||
# @return true if verified; false otherwise:
|
||||
def complete_upload(recorded_video)
|
||||
if (verify_upload(recorded_video.user, recorded_video.url, recorded_video.length))
|
||||
if (verify_youtube_upload(recorded_video.user, recorded_video.url, recorded_video.length))
|
||||
recorded_video.update_attribute(:fully_uploaded, true)
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def verify_recaptcha(recaptcha_response)
|
||||
success = false
|
||||
if !Rails.application.config.recaptcha_enable
|
||||
success = true
|
||||
else
|
||||
Rails.logger.info "Login with: #{recaptcha_response}"
|
||||
RestClient.get("https://www.google.com/recaptcha/api/siteverify",
|
||||
params: {
|
||||
secret: Rails.application.config.recaptcha_private_key,
|
||||
response: recaptcha_response
|
||||
}
|
||||
) do |response, request, result|
|
||||
Rails.logger.info "response: #{response.inspect}"
|
||||
case(response.code)
|
||||
when 200..207
|
||||
json = JSON.parse(response.to_str)
|
||||
if json['success']
|
||||
success = true
|
||||
else
|
||||
Rails.logger.info("Error verifying recaptcha: #{json['error-codes'].inspect}")
|
||||
end
|
||||
else
|
||||
Rails.logger.info("Unexpected status from google_recaptcha: [#{response.code}] with headers: #{response.headers.inspect}")
|
||||
end #case
|
||||
end #do
|
||||
end # if
|
||||
success
|
||||
end #def
|
||||
|
||||
# This will also sign in and prompt for login as necessary;
|
||||
# currently requires the server to be running at localhost:3000
|
||||
def signin_flow()
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
require 'google_client'
|
||||
class UserManager < BaseManager
|
||||
|
||||
def initialize(options={})
|
||||
super(options)
|
||||
@log = Logging.logger[self]
|
||||
@google_client = GoogleClient.new()
|
||||
end
|
||||
|
||||
# Note that almost everything can be nil here. This is because when users sign up via social media,
|
||||
|
|
@ -24,7 +26,8 @@ class UserManager < BaseManager
|
|||
fb_signup = options[:fb_signup]
|
||||
signup_confirm_url = options[:signup_confirm_url]
|
||||
affiliate_referral_id = options[:affiliate_referral_id]
|
||||
recaptcha_failed = Rails.application.config.recaptcha_enable && options[:recaptcha_failed]
|
||||
recaptcha_failed=(fb_signup) ? false : !@google_client.verify_recaptcha(options[:recaptcha_response])
|
||||
|
||||
user = User.new
|
||||
|
||||
# check if we have disabled open signup for this site. open == invited users can still get in
|
||||
|
|
|
|||
|
|
@ -192,6 +192,120 @@ describe "Find Session", :js => true, :type => :feature, :capybara_feature => tr
|
|||
end
|
||||
end
|
||||
|
||||
describe "start session behavior" do
|
||||
before(:each) do
|
||||
|
||||
@music_session = FactoryGirl.create(:music_session, creator: user)
|
||||
|
||||
@invited_user = FactoryGirl.create(:user)
|
||||
FactoryGirl.create(:friendship, :user => @invited_user, :friend => user)
|
||||
FactoryGirl.create(:friendship, :user => user, :friend => @invited_user)
|
||||
@invitation = FactoryGirl.create(:invitation, :sender => user, :receiver => @invited_user, :music_session => @music_session)
|
||||
|
||||
@rsvp_user = FactoryGirl.create(:user)
|
||||
@rsvp_slot = FactoryGirl.create(:rsvp_slot, music_session: @music_session)
|
||||
@rsvp_request = FactoryGirl.create(:rsvp_request_for_slots, chosen: nil, user: @rsvp_user, slots: [@rsvp_slot])
|
||||
end
|
||||
|
||||
it "should always show start session link for session creator" do
|
||||
pending
|
||||
fast_signin(user, Nav.find_session)
|
||||
find("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?")
|
||||
fast_signout
|
||||
end
|
||||
|
||||
it "should not show start session link for anyone who is not creator, invitee, or RSVP user" do
|
||||
pending
|
||||
random_user = FactoryGirl.create(:user)
|
||||
fast_signin(random_user, Nav.find_session)
|
||||
page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start")
|
||||
fast_signout
|
||||
end
|
||||
|
||||
it "should show start session link for invited or RSVP users" do
|
||||
pending
|
||||
# make session date/time TBD
|
||||
@music_session.scheduled_start = Time.now + 5.minutes
|
||||
@music_session.save!
|
||||
|
||||
# invited user
|
||||
fast_signin(@invited_user, Nav.find_session)
|
||||
page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?")
|
||||
fast_signout
|
||||
|
||||
# RSVP user
|
||||
fast_signin(@rsvp_user, Nav.find_session)
|
||||
page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?")
|
||||
page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP")
|
||||
fast_signout
|
||||
|
||||
# now approve the RSVP
|
||||
@rsvp_request.rsvp_requests_rsvp_slots[0].chosen = true
|
||||
@rsvp_request.rsvp_requests_rsvp_slots[0].save!
|
||||
|
||||
fast_signin(@rsvp_user, Nav.find_session)
|
||||
page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?")
|
||||
page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP")
|
||||
fast_signout
|
||||
end
|
||||
|
||||
it "should not show start session link for invited or RSVP users when date/time is TBD" do
|
||||
pending
|
||||
# make session date/time TBD
|
||||
@music_session.scheduled_start = nil
|
||||
@music_session.save!
|
||||
|
||||
# invited user
|
||||
fast_signin(@invited_user, Nav.find_session)
|
||||
page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?")
|
||||
fast_signout
|
||||
|
||||
# RSVP user
|
||||
fast_signin(@rsvp_user, Nav.find_session)
|
||||
page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?")
|
||||
page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP")
|
||||
fast_signout
|
||||
|
||||
# now approve the RSVP
|
||||
@rsvp_request.rsvp_requests_rsvp_slots[0].chosen = true
|
||||
@rsvp_request.rsvp_requests_rsvp_slots[0].save!
|
||||
|
||||
# "start session" should still be hidden
|
||||
fast_signin(@rsvp_user, Nav.find_session)
|
||||
page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?")
|
||||
page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP")
|
||||
fast_signout
|
||||
end
|
||||
|
||||
it "should not show start session link for invited or RSVP users when more than 15 minutes remain to start time" do
|
||||
pending
|
||||
# make session date/time more than 15 min away
|
||||
@music_session.scheduled_start = Time.now + 60.minutes
|
||||
@music_session.save!
|
||||
|
||||
# invited user
|
||||
fast_signin(@invited_user, Nav.find_session)
|
||||
page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?")
|
||||
fast_signout
|
||||
|
||||
# RSVP user
|
||||
fast_signin(@rsvp_user, Nav.find_session)
|
||||
page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?")
|
||||
page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP")
|
||||
fast_signout
|
||||
|
||||
# now approve the RSVP
|
||||
@rsvp_request.rsvp_requests_rsvp_slots[0].chosen = true
|
||||
@rsvp_request.rsvp_requests_rsvp_slots[0].save!
|
||||
|
||||
# "start session" should still be hidden
|
||||
fast_signin(@rsvp_user, Nav.find_session)
|
||||
page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?")
|
||||
page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP")
|
||||
fast_signout
|
||||
end
|
||||
end
|
||||
|
||||
describe "rsvp behavior" do
|
||||
before(:each) do
|
||||
stub_const("APP_CONFIG", web_config)
|
||||
|
|
@ -222,10 +336,9 @@ describe "Find Session", :js => true, :type => :feature, :capybara_feature => tr
|
|||
it "RSVP text shows correctly" do
|
||||
music_session = FactoryGirl.create(:music_session, creator: user)
|
||||
|
||||
# session creator cannot cancel
|
||||
fast_signin(user, Nav.find_session)
|
||||
|
||||
find("#sessions-scheduled .rsvp-msg span.text", text: "You have been confirmed for this session. ")
|
||||
|
||||
page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP")
|
||||
sign_out
|
||||
|
||||
# create a slot so the session can be joined
|
||||
|
|
@ -241,15 +354,15 @@ describe "Find Session", :js => true, :type => :feature, :capybara_feature => tr
|
|||
# first state: an unconfirmed RSVP
|
||||
go_to_root
|
||||
fast_signin(finder, Nav.find_session)
|
||||
find("#sessions-scheduled .rsvp-msg span.text", text: "You have RSVP'ed to this session. ")
|
||||
find("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP")
|
||||
|
||||
rsvp_request.rsvp_requests_rsvp_slots[0].chosen = true
|
||||
rsvp_request.rsvp_requests_rsvp_slots[0].save!
|
||||
|
||||
# second state: a connfirmed RSVP
|
||||
# second state: a confirmed RSVP
|
||||
go_to_root
|
||||
fast_signin(finder, Nav.find_session)
|
||||
find("#sessions-scheduled .rsvp-msg span.text", text: "You have been confirmed for this session. ")
|
||||
find("#sessions-scheduled a.cancel", text: "Cancel RSVP")
|
||||
|
||||
|
||||
# need to now CANCEL, and check what it says: // VRFS-1891
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
require 'spec_helper'
|
||||
require 'you_tube_client'
|
||||
require 'google_client'
|
||||
|
||||
describe "OAuth", :slow=>true, :js=>true, :type=>:feature, :capybara_feature=>true do
|
||||
|
||||
|
|
@ -15,7 +15,7 @@ describe "OAuth", :slow=>true, :js=>true, :type=>:feature, :capybara_feature=>tr
|
|||
end
|
||||
|
||||
before(:each) do
|
||||
@youtube_client = YouTubeClient.new()
|
||||
@youtube_client = GoogleClient.new()
|
||||
end
|
||||
|
||||
after(:each) do
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
require 'spec_helper'
|
||||
require 'you_tube_client'
|
||||
require 'google_client'
|
||||
require 'rest_client'
|
||||
|
||||
describe "YouTube", :slow=>true, :js=>true, :type => :feature, :capybara_feature => true do
|
||||
|
|
@ -12,7 +12,7 @@ describe "YouTube", :slow=>true, :js=>true, :type => :feature, :capybara_feature
|
|||
@previous_run_server = Capybara.run_server
|
||||
Capybara.run_server = false
|
||||
@user=FactoryGirl.create(:user, :email => "jamkazamtest@gmail.com")
|
||||
@youtube_client = YouTubeClient.new()
|
||||
@youtube_client = GoogleClient.new()
|
||||
authorize_google_user(@youtube_client, @user, "stinkyblueberryjam")
|
||||
google_auth = UserAuthorization.google_auth(@user).first # Consider returning this from above now that it is reliable
|
||||
end
|
||||
|
|
@ -25,7 +25,7 @@ describe "YouTube", :slow=>true, :js=>true, :type => :feature, :capybara_feature
|
|||
|
||||
it "should retrieve upload url" do
|
||||
length = 3276
|
||||
upload_hash=@youtube_client.upload_sign(@user, "test_video.mp4", length)
|
||||
upload_hash=@youtube_client.sign_youtube_upload(@user, "test_video.mp4", length)
|
||||
upload_hash.should_not be_nil
|
||||
upload_hash.length.should be >=1
|
||||
upload_hash['method'].should eq("PUT")
|
||||
|
|
@ -35,13 +35,13 @@ describe "YouTube", :slow=>true, :js=>true, :type => :feature, :capybara_feature
|
|||
upload_hash['Content-Length'].should eq(length)
|
||||
upload_hash['Content-Type'].should_not be_nil
|
||||
|
||||
@youtube_client.verify_upload(@user, upload_hash['url'], length).should be_false
|
||||
@youtube_client.verify_youtube_upload(@user, upload_hash['url'], length).should be_false
|
||||
end
|
||||
|
||||
it "upload url should allow uploading" do
|
||||
vid_path = Rails.root.join('spec', 'files', 'test_video.mp4')
|
||||
length = File.size?(vid_path)
|
||||
upload_hash=@youtube_client.upload_sign(@user, "test_video.mp4", length)
|
||||
upload_hash=@youtube_client.sign_youtube_upload(@user, "test_video.mp4", length)
|
||||
#puts upload_hash.inspect
|
||||
upload_hash.should_not be_nil
|
||||
upload_hash.length.should be >=1
|
||||
|
|
@ -54,8 +54,8 @@ describe "YouTube", :slow=>true, :js=>true, :type => :feature, :capybara_feature
|
|||
|
||||
# Upload this file as the client would:
|
||||
RestClient.put(upload_hash['url'], File.read(vid_path))
|
||||
@youtube_client.verify_upload(@user, upload_hash['url'], length).should be_true
|
||||
#@youtube_client.get_upload_status(@user, upload_hash['url'], length)
|
||||
@youtube_client.verify_youtube_upload(@user, upload_hash['url'], length).should be_true
|
||||
#@youtube_client.youtube_upload_status(@user, upload_hash['url'], length)
|
||||
end
|
||||
|
||||
it "sets upload flag when complete" do
|
||||
|
|
@ -67,7 +67,7 @@ describe "YouTube", :slow=>true, :js=>true, :type => :feature, :capybara_feature
|
|||
|
||||
vid_path = Rails.root.join('spec', 'files', 'test_video.mp4')
|
||||
length = File.size?(vid_path)
|
||||
upload_hash=@youtube_client.upload_sign(@user, "test_video.mp4", length)
|
||||
upload_hash=@youtube_client.sign_youtube_upload(@user, "test_video.mp4", length)
|
||||
upload_hash.should_not be_nil
|
||||
upload_hash['url'].should_not be_nil
|
||||
RestClient.put(upload_hash['url'], File.read(vid_path))
|
||||
|
|
@ -82,7 +82,7 @@ describe "YouTube", :slow=>true, :js=>true, :type => :feature, :capybara_feature
|
|||
|
||||
@recording.recorded_videos << recorded_video
|
||||
|
||||
@youtube_client.verify_upload(@user, upload_hash['url'], length).should be_true
|
||||
@youtube_client.verify_youtube_upload(@user, upload_hash['url'], length).should be_true
|
||||
@youtube_client.complete_upload(recorded_video).should be_true
|
||||
recorded_video.fully_uploaded.should be_true
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ require 'spec_helper'
|
|||
describe UserManager do
|
||||
|
||||
before(:each) do
|
||||
Rails.application.config.recaptcha_enable=false
|
||||
@user_manager = UserManager.new(:conn => @conn)
|
||||
UserMailer.deliveries.clear
|
||||
@location = { :country => "US", :state => "Arkansas", :city => "Little Rock" }
|
||||
|
|
@ -662,12 +663,13 @@ describe UserManager do
|
|||
end # describe "without nocaptcha"
|
||||
|
||||
describe "with nocaptcha" do
|
||||
before(:all) do
|
||||
before(:each) do
|
||||
@old_recaptcha=Rails.application.config.recaptcha_enable
|
||||
Rails.application.config.recaptcha_enable=true
|
||||
UserMailer.deliveries.clear
|
||||
end
|
||||
|
||||
after(:all) do
|
||||
after(:each) do
|
||||
Rails.application.config.recaptcha_enable=@old_recaptcha
|
||||
end
|
||||
|
||||
|
|
@ -682,11 +684,14 @@ describe UserManager do
|
|||
instruments: @instruments,
|
||||
musician: true,
|
||||
location: @loca,
|
||||
recaptcha_failed: true,
|
||||
recaptcha_response: nil,
|
||||
signup_confirm_url: "http://localhost:3000/confirm")
|
||||
|
||||
user.errors.any?.should be_true
|
||||
UserMailer.deliveries.should have(0).items
|
||||
end
|
||||
|
||||
it "passes when facebook signup" do
|
||||
user = @user_manager.signup(remote_ip: "1.2.3.4",
|
||||
first_name: "bob",
|
||||
last_name: "smith",
|
||||
|
|
@ -694,13 +699,14 @@ describe UserManager do
|
|||
password: "foobar",
|
||||
password_confirmation: "foobar",
|
||||
terms_of_service: true,
|
||||
recaptcha_failed: false,
|
||||
fb_signup: FactoryGirl.create(:facebook_signup),
|
||||
recaptcha_response: nil,
|
||||
instruments: @instruments,
|
||||
musician: true,
|
||||
location: @loca,
|
||||
signup_confirm_url: "http://localhost:3000/confirm")
|
||||
|
||||
user.errors.any?.should be_false
|
||||
end # it "fails when nocaptcha fails"
|
||||
end # it "passes when facebook signup"
|
||||
end # describe "with nocaptcha"
|
||||
end # test
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ To obtain an access token, one must actually log into google using a browser run
|
|||
|
||||
Getting an access token for the purposes of automated testing is tricky, but possible using Capybara with a javascript-enabled driver. (Note, web/spec/support/utilities.rb utilizes the JK youtube client to perform the intricate bits):
|
||||
|
||||
1) Obtain the login URL. It's ugly, but we can get it from the YouTubeClient. It contains the callback URL, as well as a "hint" that will fill in the username for us.
|
||||
1) Obtain the login URL. It's ugly, but we can get it from the GoogleClient. It contains the callback URL, as well as a "hint" that will fill in the username for us.
|
||||
2) Start a web server on an enabled callback server, such as localhost:3000
|
||||
3) Obtain the URL using a known test user
|
||||
4) Visit the URL in a capybara test
|
||||
|
|
@ -12,7 +12,7 @@ Getting an access token for the purposes of automated testing is tricky, but pos
|
|||
4b) Click the login button
|
||||
4c) The approve page should load. Wait for the approve button to be enabled. This is usually a second or two after the page loads, but not immediately.
|
||||
4d) Click the approve button
|
||||
5) After google approves, some javascript will redirect to our test web server, which contains a code. This is not the access_token, but a one-time code that can be exchanged for an access_token, again POSTing to google's auth server. You can see it in gory detail in YouTubeClient.exchange_for_token.
|
||||
5) After google approves, some javascript will redirect to our test web server, which contains a code. This is not the access_token, but a one-time code that can be exchanged for an access_token, again POSTing to google's auth server. You can see it in gory detail in GoogleClient.exchange_for_token.
|
||||
6) If all goes well, the test web server will call back the invoker with a real access token.
|
||||
7) For testing purposes, stick the access token in the user.user_authorizations table for the user for which we are testing.
|
||||
|
||||
|
|
|
|||
|
|
@ -804,7 +804,7 @@ module JamWebsockets
|
|||
id = subscribe.id
|
||||
type = subscribe.type
|
||||
if id && id.length > 0 && type && type.length > 0
|
||||
register_subscription(client, type, id)
|
||||
#register_subscription(client, type, id)
|
||||
else
|
||||
@log.error("handle_subscribe: empty data #{subscribe}")
|
||||
end
|
||||
|
|
@ -814,7 +814,7 @@ module JamWebsockets
|
|||
id = unsubscribe.id
|
||||
type = unsubscribe.type
|
||||
if id && id.length > 0 && type && type.length > 0
|
||||
unregister_subscription(client, type, id)
|
||||
#unregister_subscription(client, type, id)
|
||||
else
|
||||
@log.error("handle_subscribe: empty data #{unsubscribe}")
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue