diff --git a/admin/Gemfile b/admin/Gemfile index b5e4ca236..7502a288e 100644 --- a/admin/Gemfile +++ b/admin/Gemfile @@ -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' diff --git a/db/manifest b/db/manifest index a95040283..6483cb000 100755 --- a/db/manifest +++ b/db/manifest @@ -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 \ No newline at end of file +recorded_backing_tracks_add_filename.sql +discard_scores_optimized.sql diff --git a/db/up/discard_scores_optimized.sql b/db/up/discard_scores_optimized.sql new file mode 100644 index 000000000..e33c22aa1 --- /dev/null +++ b/db/up/discard_scores_optimized.sql @@ -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; \ No newline at end of file diff --git a/monitor/spec/production_spec.rb b/monitor/spec/production_spec.rb index 99e296464..01cf90fa1 100755 --- a/monitor/spec/production_spec.rb +++ b/monitor/spec/production_spec.rb @@ -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 diff --git a/ruby/Gemfile b/ruby/Gemfile index f38b2de02..7a31165d1 100644 --- a/ruby/Gemfile +++ b/ruby/Gemfile @@ -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) diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb index f707fcf27..0f14d015d 100755 --- a/ruby/lib/jam_ruby.rb +++ b/ruby/lib/jam_ruby.rb @@ -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" diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb index 439aefe3a..35e380618 100644 --- a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.html.erb @@ -1,45 +1,64 @@ <% provide(:title, 'Welcome to JamKazam!') %> +

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. + 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
+ There are basically three kinds of setups you can use to play on JamKazam.
+

+

+ +

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:
+

+

+ +

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 + 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 + .

-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 + Again, welcome to JamKazam, and we look forward to seeing – and hearing – you online soon!

-

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

- -

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

- -

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

- -

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

- -

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

- -  -- Team JamKazam +

Best Regards,
+ Team JamKazam

\ No newline at end of file diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb index 90efc88ef..a9f2ed06b 100644 --- a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/welcome_message.text.erb @@ -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 diff --git a/ruby/lib/jam_ruby/resque/resque_hooks.rb b/ruby/lib/jam_ruby/resque/resque_hooks.rb index e58298569..be09d0925 100644 --- a/ruby/lib/jam_ruby/resque/resque_hooks.rb +++ b/ruby/lib/jam_ruby/resque/resque_hooks.rb @@ -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 diff --git a/ruby/lib/jam_ruby/resque/stress_job.rb b/ruby/lib/jam_ruby/resque/stress_job.rb new file mode 100644 index 000000000..b5004dd11 --- /dev/null +++ b/ruby/lib/jam_ruby/resque/stress_job.rb @@ -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 + + diff --git a/ruby/spec/jam_ruby/models/score_spec.rb b/ruby/spec/jam_ruby/models/score_spec.rb index 0e87f433d..b7daca9d7 100644 --- a/ruby/spec/jam_ruby/models/score_spec.rb +++ b/ruby/spec/jam_ruby/models/score_spec.rb @@ -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 diff --git a/web/Gemfile b/web/Gemfile index ebd231f77..0405c352f 100644 --- a/web/Gemfile +++ b/web/Gemfile @@ -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' diff --git a/web/app/assets/images/web/carousel_community.png b/web/app/assets/images/web/carousel_community.png new file mode 100644 index 000000000..ace54546b Binary files /dev/null and b/web/app/assets/images/web/carousel_community.png differ diff --git a/web/app/assets/images/web/carousel_jamblaster.png b/web/app/assets/images/web/carousel_jamblaster.png new file mode 100644 index 000000000..636ede273 Binary files /dev/null and b/web/app/assets/images/web/carousel_jamblaster.png differ diff --git a/web/app/assets/images/web/carousel_overview.png b/web/app/assets/images/web/carousel_overview.png new file mode 100644 index 000000000..f8493c1f4 Binary files /dev/null and b/web/app/assets/images/web/carousel_overview.png differ diff --git a/web/app/assets/javascripts/configureTracksHelper.js b/web/app/assets/javascripts/configureTracksHelper.js index 32f220366..ba16f1d28 100644 --- a/web/app/assets/javascripts/configureTracksHelper.js +++ b/web/app/assets/javascripts/configureTracksHelper.js @@ -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; } }); diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js index 04100c7db..bb1109c9c 100644 --- a/web/app/assets/javascripts/jam_rest.js +++ b/web/app/assets/javascripts/jam_rest.js @@ -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; }; diff --git a/web/app/assets/javascripts/layout.js b/web/app/assets/javascripts/layout.js index 723e75650..6c40f596d 100644 --- a/web/app/assets/javascripts/layout.js +++ b/web/app/assets/javascripts/layout.js @@ -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) + } }); } diff --git a/web/app/assets/javascripts/notificationPanel.js b/web/app/assets/javascripts/notificationPanel.js index fdf0ac3ee..7c3e608de 100644 --- a/web/app/assets/javascripts/notificationPanel.js +++ b/web/app/assets/javascripts/notificationPanel.js @@ -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(); diff --git a/web/app/assets/javascripts/sessionList.js b/web/app/assets/javascripts/sessionList.js index 5a02eb429..9662b83ff 100644 --- a/web/app/assets/javascripts/sessionList.js +++ b/web/app/assets/javascripts/sessionList.js @@ -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 = $('Start session now?'); - noLinkText.find('a').click(function() { + sessionLinkText = $('Start session now?'); + 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 = $('Start session now? | Cancel RSVP'); - noLinkText.find('a.start').click(function() { + sessionLinkText = $('Start session now? | Cancel RSVP'); + sessionLinkText.find('a.start').click(function() { ui.launchSessionStartDialog(session); return false; }); } else { - noLinkText = $('Cancel RSVP'); + sessionLinkText = $('Cancel RSVP'); } // 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 = $('Start session now?'); - noLinkText.find('a').click(function() { + sessionLinkText = $('Start session now?'); + sessionLinkText.find('a').click(function() { ui.launchSessionStartDialog(session); return false; }); @@ -428,8 +410,8 @@ } else if (pendingRsvpId) { showRsvpLink = false; - noLinkText = $('Cancel RSVP'); - noLinkText.find('a').click(function() { + sessionLinkText = $('Cancel RSVP'); + 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 = 'No more openings in this session.'; + sessionLinkText = 'No more openings in this session.'; } else if (!openRsvps && !hasInvitation) { showRsvpLink = false; - noLinkText = 'You need an invitation to RSVP to this session.'; + sessionLinkText = 'You need an invitation to RSVP to this session.'; } if (showRsvpLink) { @@ -472,7 +454,7 @@ }); } else { - $('.rsvp-msg', $parentRow).html(noLinkText).show(); + $('.rsvp-msg', $parentRow).html(sessionLinkText).show(); $('.rsvp-link', $parentRow).hide(); } } diff --git a/web/app/assets/javascripts/session_utils.js b/web/app/assets/javascripts/session_utils.js index 071f05567..d1e2358f7 100644 --- a/web/app/assets/javascripts/session_utils.js +++ b/web/app/assets/javascripts/session_utils.js @@ -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 diff --git a/web/app/assets/javascripts/site_validator.js.coffee b/web/app/assets/javascripts/site_validator.js.coffee new file mode 100644 index 000000000..f40270ada --- /dev/null +++ b/web/app/assets/javascripts/site_validator.js.coffee @@ -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('') diff --git a/web/app/assets/javascripts/ui_helper.js b/web/app/assets/javascripts/ui_helper.js index 9213c43cb..01f204bfb 100644 --- a/web/app/assets/javascripts/ui_helper.js +++ b/web/app/assets/javascripts/ui_helper.js @@ -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; diff --git a/web/app/assets/stylesheets/client/jamkazam.css.scss b/web/app/assets/stylesheets/client/jamkazam.css.scss index 8ccd7a022..ab1444769 100644 --- a/web/app/assets/stylesheets/client/jamkazam.css.scss +++ b/web/app/assets/stylesheets/client/jamkazam.css.scss @@ -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; } \ No newline at end of file diff --git a/web/app/assets/stylesheets/client/site_validator.css.scss b/web/app/assets/stylesheets/client/site_validator.css.scss new file mode 100644 index 000000000..549d58594 --- /dev/null +++ b/web/app/assets/stylesheets/client/site_validator.css.scss @@ -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; + } +} diff --git a/web/app/assets/stylesheets/landings/landing_page.css.scss b/web/app/assets/stylesheets/landings/landing_page.css.scss index 51491d12a..fe5209df2 100644 --- a/web/app/assets/stylesheets/landings/landing_page.css.scss +++ b/web/app/assets/stylesheets/landings/landing_page.css.scss @@ -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 { diff --git a/web/app/assets/stylesheets/web/main.css.scss b/web/app/assets/stylesheets/web/main.css.scss index 291f61967..82c5160fd 100644 --- a/web/app/assets/stylesheets/web/main.css.scss +++ b/web/app/assets/stylesheets/web/main.css.scss @@ -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; diff --git a/web/app/assets/stylesheets/web/welcome.css.scss b/web/app/assets/stylesheets/web/welcome.css.scss index c4bd44786..2171789eb 100644 --- a/web/app/assets/stylesheets/web/welcome.css.scss +++ b/web/app/assets/stylesheets/web/welcome.css.scss @@ -19,6 +19,15 @@ body.web { } } + .jamfest { + top:-70px; + position:relative; + + .jamblaster { + font-weight:bold; + } + } + .follow-links { position: absolute; right: 0; diff --git a/web/app/controllers/api_users_controller.rb b/web/app/controllers/api_users_controller.rb index 7d8d40f94..34ea1154d 100644 --- a/web/app/controllers/api_users_controller.rb +++ b/web/app/controllers/api_users_controller.rb @@ -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]) diff --git a/web/app/controllers/spikes_controller.rb b/web/app/controllers/spikes_controller.rb index b07987b4d..1c3c39e6e 100644 --- a/web/app/controllers/spikes_controller.rb +++ b/web/app/controllers/spikes_controller.rb @@ -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 diff --git a/web/app/controllers/users_controller.rb b/web/app/controllers/users_controller.rb index 6cd34cabd..61c42fe28 100644 --- a/web/app/controllers/users_controller.rb +++ b/web/app/controllers/users_controller.rb @@ -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 diff --git a/web/app/views/clients/_home.html.slim b/web/app/views/clients/_home.html.slim index 5492f904c..8130e82cc 100644 --- a/web/app/views/clients/_home.html.slim +++ b/web/app/views/clients/_home.html.slim @@ -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 diff --git a/web/app/views/clients/_sidebar.html.erb b/web/app/views/clients/_sidebar.html.erb index dbb92c66a..b8d44dc68 100644 --- a/web/app/views/clients/_sidebar.html.erb +++ b/web/app/views/clients/_sidebar.html.erb @@ -210,7 +210,7 @@