Merge with develop

This commit is contained in:
Steven Miers 2015-02-12 14:32:12 -06:00
commit aedcbde1f3
53 changed files with 976 additions and 188 deletions

View File

@ -49,7 +49,7 @@ gem 'unf', '0.1.3' #optional fog dependency
gem 'country-select'
gem 'aasm', '3.0.16'
gem 'postgres-copy', '0.6.0'
gem 'aws-sdk' #, '1.29.1'
gem 'aws-sdk', '~> 1'
gem 'bugsnag'
gem 'gon'
gem 'cocoon'

View File

@ -249,4 +249,5 @@ recorded_backing_tracks.sql
user_model_about_changes.sql
performance_samples.sql
user_presences.sql
recorded_backing_tracks_add_filename.sql
recorded_backing_tracks_add_filename.sql
discard_scores_optimized.sql

View File

@ -0,0 +1,89 @@
DROP FUNCTION IF EXISTS discard_scores();
CREATE FUNCTION discard_scores (keep INTEGER) RETURNS VOID AS $$
BEGIN
DELETE FROM scores WHERE score_dt <
(SELECT score_dt FROM scores s WHERE s.alocidispid = scores.alocidispid AND s.blocidispid = scores.blocidispid ORDER BY score_dt DESC LIMIT 1 OFFSET (keep - 1));
RETURN;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION update_current_network_scores(aloc BIGINT, bloc BIGINT) RETURNS VOID
STRICT VOLATILE AS $$
DECLARE
newscore INTEGER;
newscore_dt TIMESTAMP;
newscore_limited BOOL;
sum INTEGER;
kount INTEGER;
r RECORD;
avgscore INTEGER;
maxscore INTEGER;
minscore INTEGER;
BEGIN
-- find the 6 most recent scores
-- (supposedly newscore is the first...)
-- hybrid scheme: compute the average of some recent scores, then limit newscore to be between 4/5 and 6/5 of the average
newscore := NULL;
newscore_dt := NULL;
newscore_limited := FALSE;
sum := 0;
kount := 0;
FOR r IN SELECT score, score_dt FROM scores WHERE alocidispid = aloc AND blocidispid = bloc ORDER BY score_dt DESC LIMIT 6 LOOP
IF newscore IS NULL THEN
newscore := r.score;
newscore_dt := r.score_dt;
ELSE
sum := sum + r.score;
kount := kount + 1;
END IF;
END LOOP;
-- if no scores in query at all, then delete any current entry
IF newscore IS NULL THEN
DELETE FROM current_network_scores WHERE alocidispid = aloc AND blocidispid = bloc;
IF aloc != bloc THEN
DELETE FROM current_network_scores WHERE alocidispid = bloc AND blocidispid = aloc;
END IF;
END IF;
-- if there are scores older than newscore, then use their average to limit the range of newscore
IF kount > 0 THEN
avgscore := sum / kount;
maxscore := avgscore*6/5;
minscore := avgscore*4/5;
-- the score newscore will be inserted as the current value in current_network_scores, but we will limit it
-- to be no greater than 120% of the average and no less than 80% of the average. this will dampen wild
-- swings in the scores.
IF newscore > maxscore THEN
newscore := maxscore;
newscore_limited := TRUE;
ELSEIF newscore < minscore THEN
newscore := minscore;
newscore_limited := TRUE;
END IF;
END IF;
UPDATE current_network_scores SET score = newscore, limited = newscore_limited, score_dt = newscore_dt WHERE alocidispid = aloc AND blocidispid = bloc;
IF NOT FOUND THEN
INSERT INTO current_network_scores (alocidispid, blocidispid, score, limited, score_dt) VALUES (aloc, bloc, newscore, newscore_limited, newscore_dt);
END IF;
IF aloc != bloc THEN
UPDATE current_network_scores SET score = newscore, limited = newscore_limited, score_dt = newscore_dt WHERE alocidispid = bloc AND blocidispid = aloc;
IF NOT FOUND THEN
INSERT INTO current_network_scores (alocidispid, blocidispid, score, limited, score_dt) VALUES (bloc, aloc, newscore, newscore_limited, newscore_dt);
END IF;
END IF;
-- keep the scores table clean, meaning only up to the most 5 recent scores per group & direction (scorer)
DELETE FROM scores WHERE alocidispid = aloc AND blocidispid = bloc AND scorer = 0 AND score_dt <
(SELECT score_dt FROM scores s WHERE s.alocidispid = aloc AND s.blocidispid = bloc AND s.scorer = 0 ORDER BY score_dt DESC LIMIT 1 OFFSET 4);
DELETE FROM scores WHERE alocidispid = bloc AND blocidispid = aloc AND scorer = 1 AND score_dt <
(SELECT score_dt FROM scores s WHERE s.alocidispid = bloc AND s.blocidispid = aloc AND s.scorer = 1 ORDER BY score_dt DESC LIMIT 1 OFFSET 4);
END;
$$ LANGUAGE plpgsql;

View File

@ -50,6 +50,7 @@ describe "Deployed site at #{www}", :js => true, :type => :feature, :capybara_fe
end
it "is possible for #{user3} to sign in and not get disconnected within 30 seconds" do
pending "continual failures - need to debug - try using Selenium instead of PhantomJS"
as_monitor(user3) do
sign_in_poltergeist(user3)
repeat_for(30.seconds) do

View File

@ -28,7 +28,7 @@ gem 'amqp', '1.0.2'
gem 'will_paginate'
gem 'actionmailer', '3.2.13'
gem 'sendgrid', '1.2.0'
gem 'aws-sdk' #, '1.29.1'
gem 'aws-sdk', '~> 1'
gem 'carrierwave', '0.9.0'
gem 'aasm', '3.0.16'
gem 'devise', '3.3.0' # 3.4.0 causes: uninitialized constant ActionController::Metal (NameError)

View File

@ -42,6 +42,7 @@ require "jam_ruby/resque/resque_hooks"
require "jam_ruby/resque/audiomixer"
require "jam_ruby/resque/quick_mixer"
require "jam_ruby/resque/icecast_config_writer"
require "jam_ruby/resque/stress_job"
require "jam_ruby/resque/scheduled/audiomixer_retry"
require "jam_ruby/resque/scheduled/icecast_config_retry"
require "jam_ruby/resque/scheduled/icecast_source_check"

View File

@ -1,45 +1,64 @@
<% provide(:title, 'Welcome to JamKazam!') %>
<p>Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
</p>
<p> 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.
and we hope that you will enjoy using JamKazam to play
music with others.
Following are some resources that can help you get oriented and get the most out of JamKazam.
</p>
<p>
<p><b style="color: white;">Getting Started</b><br/>
There are basically three kinds of setups you can use to play on JamKazam.<br/>
<ul>
<li><b style="color: white;">Built-In Audio on Your Computer</b> - You can use a Windows or Mac computer, and just use the built-in mic and headphone jack to
handle your audio. This is cheap and easy, but your audio quality will suffer, and it will also process audio very slowly,
creating problems with latency, or lag, in your sessions. Still, you can at least start experimenting with JamKazam in this way.</li>
<li><b style="color: white;">Computer with External Audio Interface</b> - You can use a Windows or Mac computer with an external audio interface that you
already own and use for recording, if you happen to have one already. If you are going to do this, or use the built-in mic/headphones on your computer, please refer
to our <a style="color: #ffcc00;" href="https://jamkazam.desk.com/customer/portal/articles/1288274-minimum-system-requirements">Minimum System Requirements</a>
to make sure your computer will work. These requirements were on the download page for the app, but you may have sped by them. Also, we'd recommend watching our
<a style="color: #ffcc00;" href="https://www.youtube.com/watch?v=DBo--aj_P1w">Getting Started Video</a> to learn more about your options here.</li>
<li><b style="color: white;">The JamBlaster</b> - JamKazam has designed a new product from the ground up to be the best way to play music online in real time. It's called the JamBlaster.
It processes audio faster than any of the thousands of combinations of computers and interfaces in use on JamKazam today, which means you can play with musicians
who are farther away from you, and closer sessions will feel/sound tighter. The JamBlaster is both a computer and an audio interface, so it also eliminates the
system requirements worries, and it "just works" so you don't have to be an audio and computer genius to get it working. This is a great product - available only
through a Kickstarter program running during a 30-day window during parts of February and March 2015. You can watch the
<a style="color: #ffcc00;" href="https://www.youtube.com/watch?v=gAJAIHMyois">JamBlaster Video</a> to learn more about this amazing new product.</li>
</ul>
</p>
<p><b style="color: white;">JamKazam Features</b><br/>
JamKazam offers a very robust and exciting set of features for playing online and sharing your performances with others. Here are some videos you can watch
to easily get up to speed on some of the things you can do with JamKazam:<br/>
<ul>
<li><a style="color: #ffcc00;" href="https://www.youtube.com/watch?v=EZZuGcDUoWk">Creating a Session</a></li>
<li><a style="color: #ffcc00;" href="https://www.youtube.com/watch?v=xWponSJo-GU">Finding a Session</a></li>
<li><a style="color: #ffcc00;" href="https://www.youtube.com/watch?v=zJ68hA8-fLA">Playing in a Session</a></li>
<li><a style="color: #ffcc00;" href="https://www.youtube.com/watch?v=4KWklSZZxRc">Connecting with Other Musicians</a></li>
<li><a style="color: #ffcc00;" href="https://www.youtube.com/watch?v=Gn-dOqnNLoY">Working with Recordings</a></li>
</ul>
</p>
<p><b style="color: white;">Getting Help</b><br/>
If you run into trouble and need help, please reach out to us. We will be glad to do everything we can to answer your questions and get you up and running.
You can visit our
<a style="color: #ffcc00;" href="https://jamkazam.desk.com/">Support Portal</a>
to find knowledge base articles and post questions that have
not already been answered. You can email us at support@jamkazam.com. And if you just want to chat, share tips and war stories, and hang out with fellow JamKazamers,
you can visit our <a style="color: #ffcc00;" href="http://forums.jamkazam.com/">Community Forum</a>
.
</p>
<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=DBo--aj_P1w">https://www.youtube.com/watch?v=DBo--aj_P1w</a>
Again, welcome to JamKazam, and we look forward to seeing and hearing you online soon!
</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/topics/673198-tutorials-on-major-features/articles">https://jamkazam.desk.com/customer/portal/topics/673198-tutorials-on-major-features/articles</a>
</p>
<p>
Knowledge Base Articles<br />
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:
<a style="color: #ffcc00;" href="https://jamkazam.desk.com/customer/portal/topics/564807-getting-started/articles">https://jamkazam.desk.com/customer/portal/topics/564807-getting-started/articles</a>
</p>
<p>
JamKazam Support Portal<br />
If you run into trouble and need help, please reach out to us. We will be glad to do everything we can to get you up and running. You can find our support portal here:
<a style="color: #ffcc00;" href="https://jamkazam.desk.com/">https://jamkazam.desk.com/</a>
</p>
<p>
JamKazam Community Forum<br />
And if you just want to chat, share tips and war stories, and hang out with fellow JamKazamers, you can visit our community forum here:
<a style="color: #ffcc00;" href="http://forums.jamkazam.com/">http://forums.jamkazam.com/</a>
</p>
<p>
Please take a moment to like or follow us by clicking the icons below, and we look forward to seeing and hearing you online soon!
</p>
&nbsp;&nbsp;-- Team JamKazam
<p>Best Regards,<br/>
Team JamKazam</p>

View File

@ -1,27 +1,43 @@
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.
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 some resources that can help you get oriented and get the most out of JamKazam.
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=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/topics/673198-tutorials-on-major-features/articles
Getting Started
---------------
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:
https://jamkazam.desk.com/customer/portal/topics/564807-getting-started/articles
There are basically three kinds of setups you can use to play on JamKazam.
JamKazam Support Portal
If you run into trouble and need help, please reach out to us. We will be glad to do everything we can to get you up and running. You can find our support portal here:
https://jamkazam.desk.com
* Built-In Audio on Your Computer - You can use a Windows or Mac computer, and just use the built-in mic and headphone jack to handle your audio. This is cheap and easy, but your audio quality will suffer, and it will also process audio very slowly, creating problems with latency, or lag, in your sessions. Still, you can at least start experimenting with JamKazam in this way.
JamKazam Community Forum
And if you just want to chat, share tips and war stories, and hang out with fellow JamKazamers, you can visit our community forum here:
http://forums.jamkazam.com
* Computer with External Audio Interface - - You can use a Windows or Mac computer with an external audio interface that you already own and use for recording, if you happen to have one already. If you are going to do this, or use the built-in mic/headphones on your computer, please refer to our Minimum System Requirements at https://jamkazam.desk.com/customer/portal/articles/1288274-minimum-system-requirements to make sure your computer will work. These requirements were on the download page for the app, but you may have sped by them. Also, we'd recommend watching our Getting Started Video at https://www.youtube.com/watch?v=DBo--aj_P1w to learn more about your options here.
Please take a moment to like or follow us by clicking the icons below, and we look forward to seeing and hearing you online soon!
* The JamBlaster - JamKazam has designed a new product from the ground up to be the best way to play music online in real time. It's called the JamBlaster. It processes audio faster than any of the thousands of combinations of computers and interfaces in use on JamKazam today, which means you can play with musicians who are farther away from you, and closer sessions will feel/sound tighter. The JamBlaster is both a computer and an audio interface, so it also eliminates the system requirements worries, and it "just works" so you don't have to be an audio and computer genius to get it working. This is a great product - available only through a Kickstarter program running during a 30-day window during parts of February and March 2015. You can watch the JamBlaster Video at https://www.youtube.com/watch?v=gAJAIHMyois to learn more about this amazing new product.
JamKazam Features
-----------------
JamKazam offers a very robust and exciting set of features for playing online and sharing your performances with others. Here are some videos you can watch to easily get up to speed on some of the things you can do with JamKazam:
* Creating a Session - https://www.youtube.com/watch?v=EZZuGcDUoWk
* Finding a Session - https://www.youtube.com/watch?v=xWponSJo-GU
* Playing in a Session - https://www.youtube.com/watch?v=zJ68hA8-fLA
* Connecting with Other Musicians - https://www.youtube.com/watch?v=4KWklSZZxRc
* Working with Recordings - https://www.youtube.com/watch?v=Gn-dOqnNLoY
Getting Help
------------
If you run into trouble and need help, please reach out to us. We will be glad to do everything we can to answer your questions and get you up and running. You can visit our Support Portal at https://jamkazam.desk.com/ to find knowledge base articles and post questions that have not already been answered. You can email us at support@jamkazam.com. And if you just want to chat, share tips and war stories, and hang out with fellow JamKazamers, you can visit our Community Forum at http://forums.jamkazam.com/.
Again, welcome to JamKazam, and we look forward to seeing and hearing you online soon!
Best Regards,
Team JamKazam
-- Team JamKazam

View File

@ -35,7 +35,6 @@ Resque.before_first_fork do
end
JamRuby::Stats.init(config)
end
# https://devcenter.heroku.com/articles/forked-pg-connections
Resque.before_fork do

View File

@ -0,0 +1,29 @@
require 'resque'
module JamRuby
# this job exists as a way to manually test a bunch of jobs firing at once. It's not a real job.
class StressJob
extend JamRuby::ResqueStats
@queue = :stress_job
@@log = Logging.logger[StressJob]
def self.perform
@@log.debug("STARTING")
100.times do
user = User.first.id
diagnostic = Diagnostic.first.user_id
count = Diagnostic.all.count
end
@@log.debug("ENDING")
end
end
end

View File

@ -522,29 +522,30 @@ describe Score do
it "works" do
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, nil)
Score.count.should == 2
Score.connection.execute("SELECT discard_scores()").check
Score.connection.execute("SELECT discard_scores(5)").check
Score.count.should == 2
end
it "discards over 5 items" do
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, nil)
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, nil)
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, nil)
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, nil)
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, nil)
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, nil)
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, 6.days.ago)
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, 5.days.ago)
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, 4.days.ago)
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, 3.days.ago)
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, 2.days.ago)
Score.count.should == 12
Score.connection.execute("SELECT discard_scores()").check
Score.count.should == 12
Score.count.should == 10
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 26, nil)
Score.connection.execute("UPDATE scores set created_at = TIMESTAMP '#{2.days.ago}' WHERE score = 26").cmdtuples.should == 2
Score.connection.execute("SELECT discard_scores()").check
Score.count.should == 12
Score.connection.execute("SELECT * FROM scores WHERE score = 20").ntuples.should == 12
Score.connection.execute("SELECT * FROM scores WHERE scorer = 0").ntuples.should == 6
Score.connection.execute("SELECT * FROM scores WHERE scorer = 1").ntuples.should == 6
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, 1.days.ago)
Score.count.should == 10
# make a score older than all the rest; it should get whacked
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 26, 7.days.ago)
Score.count.should == 10
Score.connection.execute("SELECT * FROM scores WHERE score = 20").ntuples.should == 10
Score.connection.execute("SELECT * FROM scores WHERE scorer = 0").ntuples.should == 5
Score.connection.execute("SELECT * FROM scores WHERE scorer = 1").ntuples.should == 5
Score.createx(LOCB, NODEB, ADDRB, LOCA, NODEA, ADDRA, 22, nil)
@ -554,18 +555,35 @@ describe Score do
Score.createx(LOCB, NODEB, ADDRB, LOCA, NODEA, ADDRA, 22, nil)
Score.createx(LOCB, NODEB, ADDRB, LOCA, NODEA, ADDRA, 22, nil)
Score.count.should == 24
Score.connection.execute("SELECT discard_scores()").check
Score.count.should == 24
Score.count.should == 20
Score.createx(LOCB, NODEB, ADDRB, LOCA, NODEA, ADDRA, 36, nil)
Score.connection.execute("UPDATE scores set created_at = TIMESTAMP '#{2.days.ago}' WHERE score = 36").cmdtuples.should == 2
Score.connection.execute("SELECT discard_scores()").check
Score.count.should == 24
Score.connection.execute("SELECT * FROM scores WHERE score = 22").ntuples.should == 12
Score.connection.execute("SELECT * FROM scores WHERE score = 22 AND scorer = 0").ntuples.should == 6
Score.connection.execute("SELECT * FROM scores WHERE score = 22 AND scorer = 1").ntuples.should == 6
Score.connection.execute("SELECT * FROM scores WHERE score = 22").ntuples.should == 10
Score.connection.execute("SELECT * FROM scores WHERE score = 20").ntuples.should == 10
Score.connection.execute("SELECT * FROM scores WHERE scorer = 0").ntuples.should == 10
Score.connection.execute("SELECT * FROM scores WHERE scorer = 1").ntuples.should == 10
Score.createx(LOCB, NODEB, ADDRB, LOCA, NODEA, ADDRA, 36, 7.days.ago)
Score.count.should == 20
Score.connection.execute("SELECT * FROM scores WHERE score = 22").ntuples.should == 10
Score.connection.execute("SELECT * FROM scores WHERE score = 20").ntuples.should == 10
Score.connection.execute("SELECT * FROM scores WHERE scorer = 0").ntuples.should == 10
Score.connection.execute("SELECT * FROM scores WHERE scorer = 1").ntuples.should == 10
# let's create scores between a new location, and make sure they don't distrurb the data we have now
Score.createx(LOCC, NODEC, ADDRC, LOCA, NODEA, ADDRA, 10, nil)
Score.count.should == 22
Score.createx(LOCC, NODEC, ADDRC, LOCA, NODEA, ADDRA, 10, nil)
Score.createx(LOCC, NODEC, ADDRC, LOCA, NODEA, ADDRA, 10, nil)
Score.createx(LOCC, NODEC, ADDRC, LOCA, NODEA, ADDRA, 10, nil)
Score.createx(LOCC, NODEC, ADDRC, LOCA, NODEA, ADDRA, 10, nil)
Score.count.should == 30
Score.connection.execute("SELECT * FROM scores WHERE score = 20").ntuples.should == 10
Score.connection.execute("SELECT * FROM scores WHERE score = 22").ntuples.should == 10
Score.connection.execute("SELECT * FROM scores WHERE score = 10").ntuples.should == 10
end
end
end

View File

@ -51,7 +51,7 @@ gem 'twitter'
gem 'fb_graph', '2.5.9'
gem 'sendgrid', '1.2.0'
gem 'filepicker-rails', '0.1.0'
gem 'aws-sdk' #, '1.29.1'
gem 'aws-sdk', '~> 1'
gem 'aasm', '3.0.16'
gem 'carrierwave', '0.9.0'
gem 'carrierwave_direct'

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

View File

@ -355,7 +355,7 @@
context._.each(tracks.tracks, function(track) {
if(!track.instrument_id) {
logger.debug("ConfigureTracks validation error: all tracks with ports assigned must specify an instrument.");
context.JK.Banner.showAlert('All tracks with ports assigned must specify an instrument.');
context.JK.Banner.showAlert('Please use the instrument icons to choose what you plan to play on each track.');
return false;
}
});

View File

@ -1539,6 +1539,14 @@
});
}
function validateUrlSite(url, sitetype) {
return $.ajax({
type: "GET",
url: '/api/data_validation?sitetype='+sitetype+'&data=' + encodeURIComponent(url),
contentType: 'application/json'
});
}
function initialize() {
return self;
}
@ -1675,6 +1683,7 @@
this.resendBandInvitation = resendBandInvitation;
this.getMount = getMount;
this.createSourceChange = createSourceChange;
this.validateUrlSite = validateUrlSite;
return this;
};

View File

@ -215,6 +215,14 @@
top: childLayout.top,
left: childLayout.left
}, opts.animationDuration);
if($(this).is('.feed')) {
$('#jamblaster-notice').animate({
width: childLayout.width,
bottom: '102%',
left: childLayout.left
}, opts.animationDuration)
}
});
}

View File

@ -221,9 +221,9 @@
$.each(response, function(index, val) {
// this means the session no longer exists
if (response.fan_access == null && response.musician_access == null) {
return;
}
//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();

View File

@ -224,29 +224,11 @@
}
if (showJoinLink) {
// wire up the Join Link to the T&Cs dialog
// wire up the Join Link to the T&Cs dialog
$('.join-link', $parentRow).click(function(evt) {
if(!context.JK.guardAgainstBrowser(app)) {
return false;
}
if (!context.JK.JamServer.connected) {
app.notifyAlert("Not Connected", 'To create or join a session, you must be connected to the server.');
return false;
}
gearUtils.guardAgainstInvalidConfiguration(app)
.fail(function() {
app.notify(
{ title: "Unable to Join Session",
text: "You can only join a session once you have working audio gear and a tested internet connection."
})
})
.done(function(){
sessionUtils.joinSession(session.id);
})
return false;
sessionUtils.ensureValidClient(app, gearUtils, function() {
sessionUtils.joinSession(session.id);
});
});
}
}
@ -368,7 +350,7 @@
$('a.more.rsvps', $parentRow).click(toggleRsvps);
var showRsvpLink = true;
var noLinkText = '';
var sessionLinkText = '';
$('.rsvp-link-text', $parentRow).hide();
function showStartSessionButton(scheduledStart) {
@ -380,8 +362,8 @@
if (session.creator.id === context.JK.currentUserId) {
showRsvpLink = false;
noLinkText = $('<span class="text"><a class="start" style="color: #fc0">Start session now?</a></span>');
noLinkText.find('a').click(function() {
sessionLinkText = $('<span class="text"><a class="start" style="color: #fc0">Start session now?</a></span>');
sessionLinkText.find('a').click(function() {
ui.launchSessionStartDialog(session);
return false;
});
@ -390,18 +372,18 @@
showRsvpLink = false;
if (session.scheduled_start && showStartSessionButton(session.scheduled_start)) {
noLinkText = $('<span class="text"><a class="start" style="color: #fc0">Start session now?</a>&nbsp;|&nbsp;<a class="cancel" style="color: #fc0">Cancel RSVP</a></span>');
noLinkText.find('a.start').click(function() {
sessionLinkText = $('<span class="text"><a class="start" style="color: #fc0">Start session now?</a>&nbsp;|&nbsp;<a class="cancel" style="color: #fc0">Cancel RSVP</a></span>');
sessionLinkText.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>');
sessionLinkText = $('<span class="text"><a class="cancel" style="color: #fc0">Cancel RSVP</a></span>');
}
// wire cancel link
noLinkText.find('a.cancel').click(function() {
sessionLinkText.find('a.cancel').click(function() {
ui.launchRsvpCancelDialog(session.id, approvedRsvpId)
.one(EVENTS.RSVP_CANCELED, function() {
rest.getSessionHistory(session.id)
@ -419,8 +401,8 @@
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() {
sessionLinkText = $('<span class="text"><a class="start" style="color: #fc0">Start session now?</a></span>');
sessionLinkText.find('a').click(function() {
ui.launchSessionStartDialog(session);
return false;
});
@ -428,8 +410,8 @@
}
else if (pendingRsvpId) {
showRsvpLink = false;
noLinkText = $('<span class="text"><a class="cancel" style="color: #fc0">Cancel RSVP</a></span>');
noLinkText.find('a').click(function() {
sessionLinkText = $('<span class="text"><a class="cancel" style="color: #fc0">Cancel RSVP</a></span>');
sessionLinkText.find('a').click(function() {
ui.launchRsvpCancelDialog(session.id, pendingRsvpId)
.one(EVENTS.RSVP_CANCELED, function() {
rest.getSessionHistory(session.id)
@ -445,11 +427,11 @@
}
else if (!openSlots) {
showRsvpLink = false;
noLinkText = '<span class="text">No more openings in this session.</span>';
sessionLinkText = '<span class="text">No more openings in this session.</span>';
}
else if (!openRsvps && !hasInvitation) {
showRsvpLink = false;
noLinkText = '<span class="text">You need an invitation to RSVP to this session.</span>';
sessionLinkText = '<span class="text">You need an invitation to RSVP to this session.</span>';
}
if (showRsvpLink) {
@ -472,7 +454,7 @@
});
}
else {
$('.rsvp-msg', $parentRow).html(noLinkText).show();
$('.rsvp-msg', $parentRow).html(sessionLinkText).show();
$('.rsvp-link', $parentRow).hide();
}
}

View File

@ -125,7 +125,33 @@
}
}
sessionUtils.ensureValidClient = function(app, gearUtils, successCallback) {
if(!context.JK.guardAgainstBrowser(app)) {
return false;
}
if (!context.JK.JamServer.connected) {
app.notifyAlert("Not Connected", 'To create or join a session, you must be connected to the server.');
return false;
}
gearUtils.guardAgainstInvalidConfiguration(app)
.fail(function() {
app.notify(
{ title: "Unable to Join Session",
text: "You can only join a session once you have working audio gear and a tested internet connection."
});
})
.done(function() {
if (successCallback) {
successCallback();
}
});
}
sessionUtils.joinSession = function(sessionId) {
var hasInvitation = false;
var session = null;
// we need to do a real-time check of the session in case the settings have

View File

@ -0,0 +1,84 @@
$ = jQuery
context = window
context.JK ||= {};
context.JK.SiteValidator = class SiteValidator
constructor: (site_type) ->
@EVENTS = context.JK.EVENTS
@rest = context.JK.Rest()
@site_type = site_type
@input_div = $(".site_validator#"+site_type+"_validator")
@data_input = @input_div.find('input')
@logger = context.JK.logger
@site_status = null
@spinner = @input_div.find('span.spinner-small')
@checkmark = @input_div.find('.validate-checkmark')
this.show_format_status()
init: () =>
this.renderErrors({})
@spinner.hide()
validator = this
@data_input.on 'blur', ->
validator.did_blur()
@data_input.on 'focus', ->
validator.show_format_status()
data_to_validate: () =>
url = @data_input.val()
if 0 < url.length
url.substring(0,2000)
else
null
show_format_status: () =>
data = this.data_to_validate()
yn = true
if data && 'url' == @site_type
regexp = /(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/
yn = regexp.test(this.data_to_validate())
if yn
@checkmark.show()
else
@checkmark.hide()
yn
did_blur: () =>
if this.show_format_status()
this.validate_site()
validate_site: () =>
@site_status = null
@spinner.show()
@checkmark.hide()
@rest.validateUrlSite(this.data_to_validate(), @site_type)
.done(this.processSiteCheck)
.fail(this.processSiteCheckFail)
processSiteCheck: (response) =>
@spinner.hide()
if 'Valid Site' == response.message
@site_status = 'valid'
this.renderErrors({})
else
@site_status = 'invalid'
this.renderErrors(response)
@logger.debug("site_status = "+@site_status)
processSiteCheckFail: (response) =>
@spinner.hide()
@checkmark.hide()
@logger.error("site check error")
@site_status = 'invalid'
renderErrors: (errors) =>
errdiv = @input_div.find('.error')
if errmsg = context.JK.format_errors("site", errors)
@checkmark.hide()
errdiv.show()
errdiv.html(errmsg)
else
@checkmark.show()
errdiv.hide()
errdiv.html('')

View File

@ -6,6 +6,7 @@
context.JK.UIHelper = function(app) {
var logger = context.JK.logger;
var rest = new context.JK.Rest();
var sessionUtils = context.JK.SessionUtils;
function addSessionLike(sessionId, userId, $likeCountSelector, $likeButtonSelector) {
rest.addSessionLike(sessionId, userId)
@ -54,9 +55,11 @@
}
function launchSessionStartDialog(session) {
var sessionStartDialog = new JK.SessionStartDialog(JK.app, session);
sessionStartDialog.initialize();
return sessionStartDialog.showDialog();
sessionUtils.ensureValidClient(app, context.JK.GearUtils, function() {
var sessionStartDialog = new JK.SessionStartDialog(JK.app, session);
sessionStartDialog.initialize();
return sessionStartDialog.showDialog();
});
}
this.addSessionLike = addSessionLike;

View File

@ -593,5 +593,16 @@ body.jam .icheckbox_minimal {
display:inline-block;
}
}
#jamblaster-notice {
position:absolute;
width:100%;
bottom:105%;
border-color:#ED3618;
border-style:solid;
border-width:1px;
padding:10px;
text-align:center;
@include border_box_sizing;
}

View File

@ -0,0 +1,33 @@
@import "client/common";
.site_validator {
input {
width: 100%;
padding: 5px;
float: left;
}
.validate-checkmark {
background-image: url('/assets/content/icon_checkmark_circle.png');
background-repeat:no-repeat;
background-position:center;
width:32px;
height:32px;
background-size: 50% 50%;
display:inline-block;
vertical-align: middle;
position: absolute;
margin-top: 0px;
margin-left: 520px;
position: absolute;
left: 0px;
}
.error {
}
span.spinner-small {
display:inline-block;
vertical-align: middle;
position: absolute;
margin-top: 0px;
margin-left: 520px;
}
}

View File

@ -10,9 +10,42 @@ body.web.landing_page {
}
&.wo_1 {
.landing-content h1 {
margin-left:45px;
.landing-tag {
left: 50%;
}
.cta-container {
width:75%;
text-align:center;
margin-left:0% !important;
h2 {
margin-left:0px !important;
}
}
.column:nth-child(1) {
width:50% !important;
}
.column:nth-child(2) {
width:50% !important;
h1 {
}
h2 {
margin-bottom:30px;
}
.cta-container a {
margin-bottom:8px;
}
}
}
&.wo_3 {
.landing-content h1 {

View File

@ -64,10 +64,16 @@ body.web {
}
}
&.register {
.landing-content {
min-height:460px;
}
}
.landing-content {
background-color:black;
width:100%;
min-height: 460px;
min-height: 366px;
position:relative;
padding-bottom:30px;

View File

@ -19,6 +19,15 @@ body.web {
}
}
.jamfest {
top:-70px;
position:relative;
.jamblaster {
font-weight:bold;
}
}
.follow-links {
position: absolute;
right: 0;

View File

@ -1,7 +1,7 @@
require 'sanitize'
class ApiUsersController < ApiController
before_filter :api_signed_in_user, :except => [:create, :show, :signup_confirm, :auth_session_create, :complete, :finalize_update_email, :isp_scoring, :add_play, :crash_dump]
before_filter :api_signed_in_user, :except => [:create, :show, :signup_confirm, :auth_session_create, :complete, :finalize_update_email, :isp_scoring, :add_play, :crash_dump, :validate_data]
before_filter :auth_user, :only => [:session_settings_show, :session_history_index, :session_user_history_index, :update, :delete,
:liking_create, :liking_destroy, # likes
:following_create, :following_show, :following_destroy, # followings
@ -699,6 +699,28 @@ class ApiUsersController < ApiController
end
end
def validate_data
unless (data = params[:data]).present?
render(json: { message: "blank data #{data}" }, status: :unprocessable_entity) && return
end
url = nil
site = params[:sitetype]
if site.blank? || 'url'==site
url = data
else
url = Utils.username_url(data, site)
end
unless url.blank?
if errmsg = Utils.site_validator(url, site)
render json: { message: 'Invalid Site', errors: { site: [errmsg] } }, status: 200
else
render json: { message: 'Valid Site' }, status: 200
end
else
render json: { message: "unknown validation for data '#{params[:data]}', site '#{params[:site]}'" }, status: :unprocessable_entity
end
end
###################### RECORDINGS #######################
# def recording_index
# @recordings = User.recording_index(current_user, params[:id])

View File

@ -37,4 +37,9 @@ class SpikesController < ApplicationController
Notification.send_subscription_message('test', '2', '{"msg": "oh hai 2"}')
render text: 'oh hai'
end
def site_validate
render :layout => 'web'
end
end

View File

@ -190,12 +190,12 @@ class UsersController < ApplicationController
def welcome
@slides = [
Slide.new("JamKazam Overview", "web/carousel_musicians.jpg", "http://www.youtube.com/embed/ylYcvTY9CVo?autoplay=1"),
Slide.new("Getting Started", "web/carousel_fans.jpg", "http://www.youtube.com/embed/DBo--aj_P1w?autoplay=1"),
Slide.new("Playing in a Session", "web/carousel_bands.jpg", "http://www.youtube.com/embed/zJ68hA8-fLA?autoplay=1"),
Slide.new("JamKazam Overview", "web/carousel_musicians.jpg", "http://www.youtube.com/embed/ylYcvTY9CVo?autoplay=1"),
Slide.new("Getting Started", "web/carousel_fans.jpg", "http://www.youtube.com/embed/DBo--aj_P1w?autoplay=1"),
Slide.new("Playing in a Session", "web/carousel_bands.jpg", "http://www.youtube.com/embed/zJ68hA8-fLA?autoplay=1")
Slide.new("JamKazam Overview", "web/carousel_overview.png", "http://www.youtube.com/embed/ylYcvTY9CVo?autoplay=1"),
Slide.new("The Revolutionary New JamBlaster!", "web/carousel_jamblaster.png", "http://www.youtube.com/embed/gAJAIHMyois?autoplay=1"),
Slide.new("Kudos From Our Community", "web/carousel_community.png", "http://www.youtube.com/embed/_7qj5RXyHCo?autoplay=1"),
Slide.new("JamKazam Overview", "web/carousel_overview.png", "http://www.youtube.com/embed/ylYcvTY9CVo?autoplay=1"),
Slide.new("The Revolutionary New JamBlaster!", "web/carousel_fans.jpg", "http://www.youtube.com/embed/gAJAIHMyois?autoplay=1"),
Slide.new("Kudos From Our Community", "web/carousel_community.png", "http://www.youtube.com/embed/_7qj5RXyHCo?autoplay=1")
]
@promo_buzz = PromoBuzz.active
@ -207,8 +207,8 @@ class UsersController < ApplicationController
end
# temporary--will go away soon
@jamfest_2014 = Event.find_by_id('80bb6acf-3ddc-4305-9442-75e6ec047c27') # production ID
@jamfest_2014 = Event.find_by_id('a2dfbd26-9b17-4446-8c61-b67a542ea6ee') unless @jamfest_2014 # development ID
#@jamfest_2014 = Event.find_by_id('80bb6acf-3ddc-4305-9442-75e6ec047c27') # production ID
#@jamfest_2014 = Event.find_by_id('a2dfbd26-9b17-4446-8c61-b67a542ea6ee') unless @jamfest_2014 # development ID
# temporary--end
@welcome_page = true

View File

@ -1,4 +1,8 @@
.screen layout="screen" layout-id="home"
.screen layout="screen" layout-id="home"
-if Rails.configuration.show_jamblaster_notice
#jamblaster-notice
a href='https://www.youtube.com/watch?v=gAJAIHMyois' rel="external"
span Check out the amazing new JamBlaster, and learn how it can improve your sessions!
/ Layout is different if jam_tracks tile available:
-jamtracks=Rails.configuration.jam_tracks_available
-if (jamtracks)
@ -19,7 +23,7 @@
h2 find session
.homebox-info
/! 1 session invitation, 19 public sessions active
.homecard.feed layout-grid-columns="4" layout-grid-position="8,0" layout-grid-rows="1" layout-link="feed"
.homecard.feed layout-grid-columns="4" layout-grid-position="8,0" layout-grid-rows="1" layout-link="feed"
h2 feed
.homebox-info
/! 4 friends online, 2 currently in sessions

View File

@ -210,7 +210,7 @@
<!-- Notification panel template -->
<script type="text/template" id="template-notification-panel">
<li session-id="{sessionId}" notification-id="{notificationId}">
<li class="notification-entry" session-id="{sessionId}" notification-id="{notificationId}">
<div class="avatar-small" user-id="{userId}" hoveraction="musician"><img src="{avatar_url}" /></div>
<div session-id="{sessionId}" hoveraction="hoveraction" class="note-text">
{text}<br/>

View File

@ -0,0 +1,6 @@
div class="site_validator" id="#{site_type}_validator"
span class="validate-checkmark"
span class="spinner-small upload-spinner"
input type='text' id="validate_input_#{site_type}" maxlength="2000"
br
div class="error"

View File

@ -7,11 +7,20 @@
iframe src="//www.youtube.com/embed/ylYcvTY9CVo" frameborder="0" allowfullscreen
.cta-container
h2 It's FREE! Ready to Go?
= link_to image_tag("web/cta_button.png", :alt => "Sign up now for your free account!"), signup_path, class: "signup", id: "signup"
= link_to image_tag("web/cta_button.png", :alt => "Sign up now for your free account!"), signup_path, class: "signup", id: "signup", rel: "external"
.column
= render :partial => "users/buzz", locals: {header: 'From Our Community'}
.more-container
h2 Need to Know More?
= link_to 'Watch 6 Bands Play Using JamKazam', landing_wo_path(p: 2), class: 'landing_wb'
h1
| Learn About the Revolutionary New
b &nbsp;JamBlaster
.video-wrapper
.video-container
iframe src="//www.youtube.com/embed/gAJAIHMyois" frameborder="0" allowfullscreen
.cta-container
h2 More Resources:
a href="https://www.youtube.com/watch?v=_7qj5RXyHCo" rel="external"
| See kudos and endorsements from JamKazam users
a href="https://www.kickstarter.com/projects/jamkazam/jamblaster-play-music-in-real-time-with-others-fro" rel="external"
| Visit the JamBlaster page on KickStarter
br clear="all"

View File

@ -0,0 +1,19 @@
= javascript_include_tag "site_validator"
div style="width:50%"
= render "clients/site_validator", site_type: params[:site_type] || 'url'
= stylesheet_link_tag "client/site_validator"
<br />
= select_tag "site_type", options_for_select(Utils::SITE_TYPES, params[:site_type] || 'url')
javascript:
var initialized = false;
$(document).on('JAMKAZAM_READY', function(e, data) {
setTimeout(function() {
window.site_validator = new JK.SiteValidator('#{params[:site_type] || 'url'}');
site_validator.init();
$('#validate_input_'+'#{params[:site_type] || 'url'}').val('jonathankolyer');
}, 1)
});
$('#site_type').change(function(){
location.href = 'site_validate?site_type='+$(this).val();
});

View File

@ -9,11 +9,14 @@
= link_to "Already have an account?", signin_path, class: "signin", id: "signin"
- content_for :after_black_bar do
= render :partial => 'users/follow_links'
- if @jamfest_2014
.jamfest{style: 'top:-70px;position:relative'}
%a{ href: event_path(@jamfest_2014.slug), style: 'font-size:20px;margin-top:11px' }
Listen to the terrific band performances from Virtual Jam Fest 2014!
//= render :partial => 'users/follow_links'
- if Rails.application.config.show_jamblaster_kickstarter_link
.jamfest
%a{ href: 'https://www.kickstarter.com/projects/jamkazam/jamblaster-play-music-in-real-time-with-others-fro', style: 'font-size:20px;margin-top:11px' }
= "Check out our new "
%span.jamblaster JamBlaster
= " on "
%span Kickstarter, the fastest way to play!
%div{style: "padding-top:20px;"}
.right
= render :partial => "buzz"

View File

@ -309,5 +309,8 @@ if defined?(Bundler)
config.influxdb_hosts = ["localhost"]
config.influxdb_port = 8086
config.influxdb_ignored_environments = ENV["INFLUXDB_ENABLED"] == '1' ? ['test', 'cucumber'] : ['test', 'cucumber', 'development']
config.show_jamblaster_notice = true
config.show_jamblaster_kickstarter_link = true
end
end

View File

@ -92,6 +92,8 @@ SampleApp::Application.routes.draw do
match '/launch_app', to: 'spikes#launch_app'
match '/websocket', to: 'spikes#websocket'
match '/test_subscription', to: 'spikes#subscription'
match '/site_validate', to: 'spikes#site_validate'
match '/username_validate', to: 'spikes#username_validate'
# junk pages
match '/help', to: 'static_pages#help'
@ -237,6 +239,9 @@ SampleApp::Application.routes.draw do
# users
match '/users/isp_scoring' => 'api_users#isp_scoring', :via => :post , :as => 'isp_scoring'
# validation
match '/data_validation' => 'api_users#validate_data', :via => :get
match '/users' => 'api_users#index', :via => :get
match '/users/:id' => 'api_users#show', :via => :get, :as => 'api_user_detail'
#match '/users' => 'api_users#create', :via => :post

59
web/lib/tasks/gear.rake Normal file
View File

@ -0,0 +1,59 @@
namespace :gear do
task analyze: :environment do
successful = {types: {}}
Diagnostic.where("type = 'GEAR_SELECTION'").each do |diagnostic|
data = JSON.parse(diagnostic.data)
os = data["summary"]["os"] # MacOSX or Win32
success = data["summary"]["success"] # bool
if success
latency = data["summary"]["score"]["latencyScore"]["latency"]
id = data["summary"]["selectedDevice"]["input"]["info"]["id"]
displayName = data["summary"]["selectedDevice"]["input"]["info"]["displayName"]
type = data["summary"]["selectedDevice"]["input"]["info"]["type"]
store = successful[:types][type]
if !store
store = {}
successful[:types][type] = store
end
deviceData = store[id + ':' + latency.to_i.to_s]
if !deviceData
deviceData = {id: id, displayName: displayName, type: type, latency: latency, count: 0}
store[id + ':' + latency.to_i.to_s] = deviceData
end
deviceData[:count] = deviceData[:count] + 1
end
end
puts "TYPES:"
successful[:types].each do|type, hits|
puts "#{type}"
end
puts ""
puts ""
successful[:types].each do |type, hits|
results = hits.sort_by do |id_latency, deviceData|
deviceData[:latency]
end
puts "--------------- #{type} -----------------"
results.each do |result|
puts "#{result[1][:latency].to_i.to_s.rjust(2)} #{result[1][:displayName]}"
end
puts ""
puts ""
end
end
end

View File

@ -0,0 +1,8 @@
task :stress_resque do
Rake::Task['environment'].invoke
10.times do
Resque.enqueue(StressJob)
end
end

62
web/lib/utils.rb Normal file
View File

@ -0,0 +1,62 @@
class Utils
USERNAME_SITES = %W{youtube facebook soundcloud bandcamp fandalism twitter reverbnation}
SITE_TYPES = ['url'].concat(USERNAME_SITES)
def self.username_url(username, site)
case site
when 'youtube'
"https://www.youtube.com/c/#{username}"
when 'facebook'
"https://www.facebook.com/#{username}"
when 'soundcloud'
"https://soundcloud.com/#{username}"
when 'bandcamp'
"http://#{username}.bandcamp.com"
when 'bandcamp-fan'
"http://bandcamp.com/#{username}"
when 'fandalism'
"http://fandalism.com/#{username}"
when 'twitter'
"https://twitter.com/#{username}"
when 'reverbnation'
"http://www.reverbnation.com/#{username}"
else
nil
end
end
def self.site_validator(url, site=nil)
uagent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36"
curl_args = "-A '#{uagent}' --silent --head --fail --show-error "
case site
when 'bandcamp'
cmd = "curl #{curl_args} '#{url}' 2>&1"
result = `#{cmd}`.chomp
if $?.success?
if /^HTTP\/\d+\.\d+ 2\d\d OK/ =~ result.lines[0]
return nil
else
if /http:\/\/(.*)\.bandcamp.com/ =~ url
return self.site_validator(self.username_url($1, 'bandcamp-fan'), 'bandcamp-fan')
end
return "Unrecognized url (#{url})"
end
else
result =~ /curl: \(\d+\) (.*)/
return "#{$1} (#{url})"
end
else
curl_args << "-L --output /dev/null "
cmd = "curl #{curl_args} '#{url}' 2>&1"
result = `#{cmd}`.chomp
end
if $?.success?
return nil
else
result =~ /curl: \(\d+\) (.*)/
return "#{$1} (#{url})"
end
end
end

View File

@ -34,6 +34,45 @@ describe ApiUsersController do
end
end
describe 'site validation' do
it 'checks valid and invalid site types' do
site_types = Utils::SITE_TYPES.clone << 'bandcamp-fan'
site_types.each do |sitetype|
case sitetype
when 'url'
valid, invalid = 'http://jamkazam.com', 'http://jamkazamxxx.com'
when 'youtube'
valid, invalid = 'jonathankolyer', 'jonathankolyerxxx'
when 'facebook'
valid, invalid = 'jamkazam', 'jamkazamxxxx'
when 'twitter'
valid, invalid = 'jamkazam', 'jamkazamxxxx'
when 'soundcloud'
valid, invalid = 'zedisdead', 'zedisdeadxxxx'
when 'reverbnation'
valid, invalid = 'jessicabrown', 'jessicabrownasdf'
when 'bandcamp'
valid, invalid = 'hucci', 'huccixxxxxx'
when 'bandcamp-fan'
valid, invalid = 'iguanaden', 'iguanadenxxxxxx'
when 'fandalism'
valid, invalid = 'pud', 'pudxxxx'
else
valid, invalid = '', ''
end
next if valid.blank?
2.times do |nn|
get :validate_data, sitetype:sitetype, data: 0==nn ? valid : invalid
response.should be_success
json = JSON.parse(response.body)
expect(json['message']).to eq(0==nn ? 'Valid Site' : 'Invalid Site')
end
end
end
end
describe "audio_latency" do
it "updates both connection and user" do

View File

@ -11,21 +11,18 @@ describe "Alternate Landing Pages", :js => true, :type => :feature, :capybara_fe
find('h1', text: 'See How JamKazam Works')
find('a.landing_wb', text: 'Watch a Video to See How to Get Started').trigger(:click)
find('h1', text: 'See How to Get Started')
find('div.fb-like')
find('a.twitter-follow-button')
find('.g-follow-btn iframe')
#find('div.fb-like')
#find('a.twitter-follow-button')
#find('.g-follow-btn iframe')
end
it "landing-page watch-overview" do
visit landing_wo_path
find('h1', text: 'Watch Video to See How JamKazam Works')
find('a.landing_wb', text: 'Watch 6 Bands Play Using JamKazam').trigger(:click)
find('h1', text: 'Watch 6 Bands Play Together from Different Locations')
find('a.landing_wb', text: 'See How to Get Started Using JamKazam').trigger(:click)
find('h1', text: 'See How to Get Started Using JamKazam')
find('div.fb-like')
find('a.twitter-follow-button')
find('.g-follow-btn iframe')
find('h1', text: 'Learn About the Revolutionary New JamBlaster')
#find('div.fb-like')
#find('a.twitter-follow-button')
#find('.g-follow-btn iframe')
end
it "landing-page-tight watch-bands" do
@ -33,9 +30,9 @@ describe "Alternate Landing Pages", :js => true, :type => :feature, :capybara_fe
find('h1', text: 'Watch 6 Bands Play Together from Different Locations')
find('h2', text: "It's FREE! Ready to Go?")
find('h2', text: 'Not a Good Time to Sign Up?')
find('div.fb-like')
find('a.twitter-follow-button')
find('.g-follow-btn iframe')
#find('div.fb-like')
#find('a.twitter-follow-button')
#find('.g-follow-btn iframe')
end
it "landing-page-tight watch-overview" do
@ -43,9 +40,9 @@ describe "Alternate Landing Pages", :js => true, :type => :feature, :capybara_fe
find('h1', text: 'Watch Overview Video to See How JamKazam Works')
find('h2', text: "It's FREE! Ready to Go?")
find('h2', text: 'Not a Good Time to Sign Up?')
find('div.fb-like')
find('a.twitter-follow-button')
find('.g-follow-btn iframe')
#find('div.fb-like')
#find('a.twitter-follow-button')
#find('.g-follow-btn iframe')
end

View File

@ -158,8 +158,16 @@ describe "Bands", :js => true, :type => :feature, :capybara_feature => true do
expect(page).to have_selector('.result-name', text: band_musician.name)
end
it "displays photo, name, location, instruments played"
it "displays a hover bubble containing more info on musician"
it "displays photo, name, location for member" do
sign_in_poltergeist fan
visit "/client#/bandProfile/#{band_musician.bands.first.id}"
find('#band-profile-members-link').trigger(:click)
within "div.band-profile-members" do
find(".avatar-small img")
find(".result-name", text: band_musician.name)
find(".result-location", text: "#{band_musician.city}, #{band_musician.state}")
end
end
it "displays any pending band invitations when viewed by current band member" do
friend = user

View File

@ -203,4 +203,30 @@ describe "Feed", :js => true, :type => :feature, :capybara_feature => true do
end
describe "session participants behavior (VRFS-2193)" do
let(:creator) { FactoryGirl.create(:user) }
let(:finder_1) { FactoryGirl.create(:user) }
let(:finder_2) { FactoryGirl.create(:user) }
specify "after session ends all participants are shown in Feed" do
creator, description = create_join_session creator, finder_1
# feed shows user, finder_1
formal_leave_by(finder_1)
# feed shows user
join_session(finder_2, description: description)
# feed shows user, finder_2
formal_leave_by(finder_2)
formal_leave_by(creator)
in_client(creator) { verify_feed_shows_users creator, finder_1, finder_2 }
end
specify "during session only current participants are shown in Feed" do
creator, description = create_join_session creator, finder_1
formal_leave_by(finder_1)
join_session(finder_2, description: description)
in_client(finder_1) { verify_feed_shows_users finder_2, creator }
end
end
end

View File

@ -21,7 +21,7 @@ describe "In a Session", :js => true, :type => :feature, :capybara_feature => tr
description = "Public or private, I cant decide!"
create_session(creator: user, description: description)
in_client(user) do
set_session_as_private
set_session_access :private
end
in_client(finder) do
emulate_client
@ -31,7 +31,7 @@ describe "In a Session", :js => true, :type => :feature, :capybara_feature => tr
sign_out_poltergeist(validate: true)
end
in_client(user) do
set_session_as_public
set_session_access :public
end
join_session(finder, description: description) # verify the public session is able to be joined
end
@ -84,15 +84,61 @@ describe "In a Session", :js => true, :type => :feature, :capybara_feature => tr
end
it "can rejoin private session as creator" do
pending
#pending
creator, description = create_join_session(user, [finder])
in_client(user) do
set_session_as_private
set_session_access :private
formal_leave_by user
sign_out_poltergeist user
end
join_session(user, description: description)
end
specify "creator should receive join requests after changing session to 'by approval' (VRFS-2266)" do
creator, description = create_session
in_client(creator) do
set_session_access :by_approval
end
request_to_join_session(finder, description: description)
in_client(creator) do
expand_sidebar :notifications
expect(page).to have_content "#{finder.name} has requested to join your session."
end
end
specify "invitee receives notification when creator invites musician" do
pending "blocked on testing this via front-end - fakeJamClient doesn't support invite UX"
user = FactoryGirl.create(:user)
invitee = FactoryGirl.create(:user)
FactoryGirl.create(:friendship, :user => user, :friend => invitee)
in_client(user) do
create_session
find('#session-invite-musicians').trigger(:click)
find('#btn-choose-friends-update').trigger(:click)
within('#friend-selector-list') { find("tr[user-id='#{invitee.id}']").trigger(:click) }
find('#btn-save-friends').trigger(:click)
find('#btn-save-invites').trigger(:click)
end
in_client(invitee) do
fast_signin(invitee, "/client#/home")
expand_sidebar :notifications
expect(page).to have_content "invited you"
end
end
specify "user is able to see recording in 'Open Recording' modal" do
recording = FactoryGirl.create(:recording, owner: user)
in_client(user) do
create_session
find('#open-a-recording').trigger(:click)
within('table.local-recordings') do
expect(page).to have_content recording.name
end
end
end
end

View File

@ -0,0 +1,38 @@
require 'spec_helper'
describe "Notification Subpanel", :js => true, :type => :feature, :capybara_feature => true do
subject { page }
let(:user) { FactoryGirl.create(:user, last_jam_locidispid: 1) }
describe "user with no notifications" do
before(:each) do
fast_signin(user, "/client")
end
it "lists no notifications" do
open_notifications
expect(page).to have_selector("#{NOTIFICATION_PANEL} li.notification-entry", count: 0)
end
end
describe "user with 1 notification" do
let!(:other) { FactoryGirl.create(:user, last_jam_locidispid: 1) }
let!(:msg1) {FactoryGirl.create(:notification_text_message, source_user: other, target_user: user) }
before(:each) do
fast_signin(user, "/client")
end
it "lists 1 notifications" do
open_notifications
expect(page).to have_selector("#{NOTIFICATION_PANEL} li.notification-entry", count: 1)
end
end
end

View File

@ -0,0 +1,3 @@
= stylesheet_link_tag "site_validator"
div style="width:50%"
= render "clients/site_validator", site_type: 'url'

View File

@ -0,0 +1,22 @@
describe "SiteValidator", ->
fixture.preload("siteValidator.html")
beforeEach ->
this.server = sinon.fakeServer.create();
window.jamClient = sinon.stub()
@fixtures = fixture.load("siteValidator.html", true)
# $('body').append(@fixtures[0])
window.gon = {}
window.gon.isNativeClient = true
afterEach ->
this.server.restore();
it "displays validator widget", ->
site_validator = new JK.SiteValidator('url')
site_validator.init()
site_validator.data_input.val('http://www.jamkazam.com')
site_validator.did_blur() # focus not working in the test
# expect(site_validator.checkmark).toBeVisible()

View File

@ -0,0 +1 @@
//= require client/site_validator

View File

@ -464,6 +464,12 @@ def join_session(joiner, options)
end
end
def request_to_join_session(joiner, options)
join_session(joiner, options.merge(no_verify: true))
find('#btn-alert-ok').trigger(:click)
# page.should have_no_selector('h1', text: 'Alert')
end
def emulate_client
@ -475,7 +481,7 @@ def create_join_session(creator, joiners=[], options={})
creator, unique_session_desc = create_session(options)
# find session in second client
joiners.each do |joiner|
[*joiners].each do |joiner|
join_session(joiner, description: unique_session_desc)
end
@ -490,6 +496,21 @@ def formal_leave_by user
end
end
def verify_feed_shows_users *users
users = [*users]
visit "/client#/feed"
find('.feed-details a.details').trigger(:click)
within 'div.music-session-history-entry' do
users.each do |user|
# confirm user avatar exists
find("a.avatar-tiny[user-id=\"#{user.id}\"][hoveraction=\"musician\"] img")
# confirm user name exists
find("a.musician-name[user-id=\"#{user.id}\"][hoveraction=\"musician\"]", text: user.name)
end
end
end
def start_recording_with(creator, joiners=[], genre=nil)
create_join_session(creator, joiners, {genre: genre})
in_client(creator) do
@ -529,22 +550,21 @@ def claim_recording(name, description)
page.should have_no_selector('h1', text: 'recording finished')
end
def set_session_as_private()
find('#session-settings-button').trigger(:click)
within('#session-settings-dialog') do
jk_select("Only RSVP musicians may join", '#session-settings-dialog #session-settings-musician-access')
#select('Private', :from => 'session-settings-musician-access')
find('#session-settings-dialog-submit').trigger(:click)
def set_session_access access_type
case access_type
when :only_rsvp, :private
access_level = "Only RSVP musicians may join"
when :by_approval
access_level = "Musicians may join by approval"
when :at_will, :public, :open
access_level = "Musicians may join at will"
else
access_level = "Musicians may join at will"
end
# verify it's dismissed
page.should have_no_selector('h1', text: 'update session settings')
end
def set_session_as_public()
find('#session-settings-button').trigger(:click)
within('#session-settings-dialog') do
jk_select("Musicians may join at will", '#session-settings-dialog #session-settings-musician-access')
# select('Public', :from => 'session-settings-musician-access')
jk_select(access_level, '#session-settings-dialog #session-settings-musician-access')
find('#session-settings-dialog-submit').trigger(:click)
end
# verify it's dismissed
@ -649,6 +669,12 @@ def sidebar_search_for string, category
wait_for_ajax
end
def expand_sidebar header_name
#search, friends, chat, notifications
panel_id = "panel#{header_name.to_s.capitalize}"
within("div[layout-id='#{panel_id}']") { find('div.panel-header').trigger(:click) }
end
def show_user_menu
page.execute_script("$('ul.shortcuts').show()")
#page.execute_script("JK.UserDropdown.menuHoverIn()")

View File

@ -36,7 +36,7 @@ gem 'carrierwave'
gem 'fog'
gem 'devise', '3.3.0' # 3.4.0 causes uninitialized constant ActionController::Metal (NameError)
gem 'postgres-copy'
gem 'aws-sdk' #, '1.29.1'
gem 'aws-sdk', '~> 1'
gem 'bugsnag'
gem 'postgres_ext'
gem 'resque'