From 7d0b9bff6aa826ae42e3ed75581e818b58ae20fd Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Tue, 6 May 2014 02:55:32 +0000 Subject: [PATCH 01/65] replaced find with find_by_id --- ruby/lib/jam_ruby/resque/google_analytics_event.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ruby/lib/jam_ruby/resque/google_analytics_event.rb b/ruby/lib/jam_ruby/resque/google_analytics_event.rb index deb85cde1..c36bb6f01 100644 --- a/ruby/lib/jam_ruby/resque/google_analytics_event.rb +++ b/ruby/lib/jam_ruby/resque/google_analytics_event.rb @@ -24,7 +24,7 @@ module JamRuby def self.perform(args={}) session_id, interval_idx = args['session_id'], args['interval_idx'].to_i - return unless session_id && session = MusicSession.find(session_id) + return unless session_id && session = MusicSession.find_by_id(session_id) GoogleAnalyticsEvent.enqueue(CAT_SESS_DUR, ACTION_SESS_DUR, SESSION_INTERVALS[interval_idx]) interval_idx += 1 @@ -46,7 +46,7 @@ module JamRuby @queue = QUEUE_BAND_TRACKER def self.perform(session_id) - return unless session = MusicSession.find(session_id) + return unless session = MusicSession.find_by_id(session_id) band = session.band if band.in_real_session?(session) band.update_attribute(:did_real_session, true) From 82f5ac77ca3c6b69e17dcf6fac26d0f2f46547b3 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Tue, 2 Sep 2014 09:35:30 -0400 Subject: [PATCH 02/65] * VRFS-2122 - fix for the update --- .../stylesheets/client/clientUpdate.css.scss | 14 ++++++++++++++ web/app/views/clients/_client_update.html.erb | 10 ++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/web/app/assets/stylesheets/client/clientUpdate.css.scss b/web/app/assets/stylesheets/client/clientUpdate.css.scss index eae8b878b..b22d78295 100644 --- a/web/app/assets/stylesheets/client/clientUpdate.css.scss +++ b/web/app/assets/stylesheets/client/clientUpdate.css.scss @@ -27,6 +27,7 @@ font-size:x-large; } + #client-updater-updating #update-steps { margin-top:20px; } @@ -34,5 +35,18 @@ #client-updater-updating span.status { color:white; } + + .download-manual { + + .fix { + font-size:20px; + } + + margin:20px 0 30px; + } + + .error-detail { + font-size:10px; + } } diff --git a/web/app/views/clients/_client_update.html.erb b/web/app/views/clients/_client_update.html.erb index d372cbbdb..51987a3a2 100644 --- a/web/app/views/clients/_client_update.html.erb +++ b/web/app/views/clients/_client_update.html.erb @@ -82,15 +82,17 @@ @@ -90,7 +95,7 @@

Download the latest JamKazam from our downloads page.

- {error_msg} + {{data.error_msg}}
From 2cae3b935c9c93e84304a6f754c5a3e16051a9f8 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 17 Sep 2014 10:09:53 -0500 Subject: [PATCH 06/65] * VRFS-2221 - fix bad reconnect path --- ruby/lib/jam_ruby/connection_manager.rb | 6 +++++- websocket-gateway/lib/jam_websockets/router.rb | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ruby/lib/jam_ruby/connection_manager.rb b/ruby/lib/jam_ruby/connection_manager.rb index bca7d364e..b9e23adf9 100644 --- a/ruby/lib/jam_ruby/connection_manager.rb +++ b/ruby/lib/jam_ruby/connection_manager.rb @@ -84,8 +84,12 @@ module JamRuby conn.save!(validate: false) end + # if udp_reachable is nil, it means it's unknown. Since this is a reconnect, we'll, preserve existing value in this case + # otherwise, pass in the value of boolean udp_reachable var + udp_reachable_value = udp_reachable.nil? ? 'udp_reachable' : udp_reachable + sql =< Date: Wed, 17 Sep 2014 10:21:42 -0500 Subject: [PATCH 07/65] * adding tests for VRFS-2221 --- ruby/spec/jam_ruby/connection_manager_spec.rb | 44 ++++++++++++++++++- web/app/assets/javascripts/JamServer.js | 1 + .../lib/jam_websockets/router.rb | 1 + 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/ruby/spec/jam_ruby/connection_manager_spec.rb b/ruby/spec/jam_ruby/connection_manager_spec.rb index 99c5f1b25..5209dfbb3 100644 --- a/ruby/spec/jam_ruby/connection_manager_spec.rb +++ b/ruby/spec/jam_ruby/connection_manager_spec.rb @@ -104,14 +104,56 @@ describe ConnectionManager, no_transaction: true do cc.ip_address.should eql("1.1.1.1") cc.addr.should == 0x01010101 cc.locidispid.should == 17192000002 + cc.udp_reachable.should == true - @connman.reconnect(cc, channel_id, nil, "33.1.2.3", STALE_TIME, EXPIRE_TIME, REACHABLE) + @connman.reconnect(cc, channel_id, nil, "33.1.2.3", STALE_TIME, EXPIRE_TIME, false) cc = Connection.find_by_client_id!(client_id) cc.connected?.should be_true cc.ip_address.should eql("33.1.2.3") cc.addr.should == 0x21010203 cc.locidispid.should == 30350000003 + cc.udp_reachable.should == false + + count = @connman.delete_connection(client_id) + count.should == 0 + + @conn.exec("SELECT count(*) FROM connections where user_id = $1", [user.id]) do |result| + result.getvalue(0, 0).to_i.should == 0 + end + end + + it "create connection, reconnect via heartbeat" do + + client_id = "client_id3" + #user_id = create_user("test", "user2", "user2@jamkazam.com") + user = FactoryGirl.create(:user) + + count = @connman.create_connection(user.id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME, false) + + count.should == 1 + + # make sure the connection is seen + + @conn.exec("SELECT count(*) FROM connections where user_id = $1", [user.id]) do |result| + result.getvalue(0, 0).to_i.should == 1 + end + + cc = Connection.find_by_client_id!(client_id) + cc.connected?.should be_true + cc.ip_address.should eql("1.1.1.1") + cc.addr.should == 0x01010101 + cc.locidispid.should == 17192000002 + cc.udp_reachable.should == false + + @connman.reconnect(cc, channel_id, nil, "33.1.2.3", STALE_TIME, EXPIRE_TIME, nil) # heartbeat passes nil in for udp_reachable + + cc = Connection.find_by_client_id!(client_id) + cc.connected?.should be_true + cc.ip_address.should eql("33.1.2.3") + cc.addr.should == 0x21010203 + cc.locidispid.should == 30350000003 + cc.udp_reachable.should == false count = @connman.delete_connection(client_id) count.should == 0 diff --git a/web/app/assets/javascripts/JamServer.js b/web/app/assets/javascripts/JamServer.js index 846a8d20e..24fe6b4f6 100644 --- a/web/app/assets/javascripts/JamServer.js +++ b/web/app/assets/javascripts/JamServer.js @@ -222,6 +222,7 @@ heartbeatMS = payload.heartbeat_interval * 1000; connection_expire_time = payload.connection_expire_time * 1000; logger.info("loggedIn(): clientId=" + app.clientId + " heartbeat=" + payload.heartbeat_interval + "s expire_time=" + payload.connection_expire_time + 's'); + heartbeatMS = 40 * 1000; heartbeatInterval = context.setInterval(_heartbeat, heartbeatMS); heartbeatAckCheckInterval = context.setInterval(_heartbeatAckCheck, 1000); lastHeartbeatAckTime = new Date(new Date().getTime() + heartbeatMS); // add a little forgiveness to server for initial heartbeat diff --git a/websocket-gateway/lib/jam_websockets/router.rb b/websocket-gateway/lib/jam_websockets/router.rb index cdcbf4bf0..5a6b42bfb 100644 --- a/websocket-gateway/lib/jam_websockets/router.rb +++ b/websocket-gateway/lib/jam_websockets/router.rb @@ -737,6 +737,7 @@ module JamWebsockets if connection.stale? ConnectionManager.active_record_transaction do |connection_manager| heartbeat_interval, connection_stale_time, connection_expire_time = determine_connection_times(context.user, context.client_type) + puts "RECONNECETETETETETETETET" connection_manager.reconnect(connection, connection.music_session_id, nil, connection_stale_time, connection_expire_time, nil) end end From d2729798a240318cdcafe7dcae2d408a75a12c05 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 17 Sep 2014 10:26:58 -0500 Subject: [PATCH 08/65] * fix reconnect path again --- websocket-gateway/lib/jam_websockets/router.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/websocket-gateway/lib/jam_websockets/router.rb b/websocket-gateway/lib/jam_websockets/router.rb index 5a6b42bfb..83447b9b9 100644 --- a/websocket-gateway/lib/jam_websockets/router.rb +++ b/websocket-gateway/lib/jam_websockets/router.rb @@ -738,7 +738,7 @@ module JamWebsockets ConnectionManager.active_record_transaction do |connection_manager| heartbeat_interval, connection_stale_time, connection_expire_time = determine_connection_times(context.user, context.client_type) puts "RECONNECETETETETETETETET" - connection_manager.reconnect(connection, connection.music_session_id, nil, connection_stale_time, connection_expire_time, nil) + connection_manager.reconnect(connection, client.channel_id, connection.music_session_id, nil, connection_stale_time, connection_expire_time, nil) end end end From 5df675f98b19fd63474b2c11d2122405baf291d4 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 17 Sep 2014 10:27:31 -0500 Subject: [PATCH 09/65] * remove puts --- websocket-gateway/lib/jam_websockets/router.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/websocket-gateway/lib/jam_websockets/router.rb b/websocket-gateway/lib/jam_websockets/router.rb index 83447b9b9..f16dc1fd9 100644 --- a/websocket-gateway/lib/jam_websockets/router.rb +++ b/websocket-gateway/lib/jam_websockets/router.rb @@ -737,7 +737,6 @@ module JamWebsockets if connection.stale? ConnectionManager.active_record_transaction do |connection_manager| heartbeat_interval, connection_stale_time, connection_expire_time = determine_connection_times(context.user, context.client_type) - puts "RECONNECETETETETETETETET" connection_manager.reconnect(connection, client.channel_id, connection.music_session_id, nil, connection_stale_time, connection_expire_time, nil) end end From 6f9dff6473e6cd48803d1391e1c174404fc26401 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 17 Sep 2014 10:43:10 -0500 Subject: [PATCH 10/65] * remove debugging heartbeat --- web/app/assets/javascripts/JamServer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/web/app/assets/javascripts/JamServer.js b/web/app/assets/javascripts/JamServer.js index 24fe6b4f6..846a8d20e 100644 --- a/web/app/assets/javascripts/JamServer.js +++ b/web/app/assets/javascripts/JamServer.js @@ -222,7 +222,6 @@ heartbeatMS = payload.heartbeat_interval * 1000; connection_expire_time = payload.connection_expire_time * 1000; logger.info("loggedIn(): clientId=" + app.clientId + " heartbeat=" + payload.heartbeat_interval + "s expire_time=" + payload.connection_expire_time + 's'); - heartbeatMS = 40 * 1000; heartbeatInterval = context.setInterval(_heartbeat, heartbeatMS); heartbeatAckCheckInterval = context.setInterval(_heartbeatAckCheck, 1000); lastHeartbeatAckTime = new Date(new Date().getTime() + heartbeatMS); // add a little forgiveness to server for initial heartbeat From f1d7dcdb82e33b44e88cbe450cd8bddc819465ab Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 17 Sep 2014 17:19:35 -0500 Subject: [PATCH 11/65] * VRFS-2016 - potential fix for flapping connections --- web/app/assets/javascripts/JamServer.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/web/app/assets/javascripts/JamServer.js b/web/app/assets/javascripts/JamServer.js index 846a8d20e..e3f7f00dc 100644 --- a/web/app/assets/javascripts/JamServer.js +++ b/web/app/assets/javascripts/JamServer.js @@ -542,6 +542,7 @@ server.connecting = true; server.socket = new context.WebSocket(uri); + server.socket.channelId = channelId; server.socket.onopen = server.onOpen; server.socket.onmessage = server.onMessage; server.socket.onclose = server.onClose; @@ -620,6 +621,13 @@ server.onClose = function () { logger.info("Socket to server closed."); + var disconnectedSocket = this; + + if(disconnectedSocket.channelId != server.socket.channelId) { + logger.debug(" ignoring disconnect for non-current socket. current=" + server.socket.channelId + ", disc=" + disconnectedSocket.channelId) + return; + } + if (connectDeferred.state() === "pending") { connectDeferred.reject(); } From d901336b27ccd3b3176137466568af91067dfe9a Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 17 Sep 2014 20:31:58 -0500 Subject: [PATCH 12/65] * VRFS-2204 - udp_reachable showing now --- admin/app/admin/scoring_load.rb | 2 +- web/app/assets/javascripts/JamServer.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/admin/app/admin/scoring_load.rb b/admin/app/admin/scoring_load.rb index 089db30a2..e020a4316 100644 --- a/admin/app/admin/scoring_load.rb +++ b/admin/app/admin/scoring_load.rb @@ -5,7 +5,7 @@ ActiveAdmin.register_page "Current Scoring Load" do table_for GetWork.summary do column "Work", :work_count column "Who", Proc.new { |connection| "#{connection.first_name} #{connection.last_name} - #{connection.email}" } - column "Errors", Proc.new { |connection| "#{connection.udp_reachable != 'f' ? "" : "No STUN"} #{connection.in_timeout != 'f' ? "in timeout," : ""} #{connection.in_session != 'f' ? "in session" : ""}" } + column "Errors", Proc.new { |connection| "#{connection.udp_reachable != false ? "" : "No STUN,"} #{connection.in_timeout != 'f' ? "Timeout," : ""} #{connection.in_session != 'f' ? "In-Session," : ""}" } column "Total Timeouts", :scoring_timeout_occurrences column "Current Scoring Failures", :scoring_failures column "Offset", :scoring_failures_offset diff --git a/web/app/assets/javascripts/JamServer.js b/web/app/assets/javascripts/JamServer.js index e3f7f00dc..85fec6da1 100644 --- a/web/app/assets/javascripts/JamServer.js +++ b/web/app/assets/javascripts/JamServer.js @@ -626,7 +626,7 @@ if(disconnectedSocket.channelId != server.socket.channelId) { logger.debug(" ignoring disconnect for non-current socket. current=" + server.socket.channelId + ", disc=" + disconnectedSocket.channelId) return; - } + } if (connectDeferred.state() === "pending") { connectDeferred.reject(); From cc6b8ae35b3086b5fd2ff2e5362386783dce9058 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 17 Sep 2014 20:57:49 -0500 Subject: [PATCH 13/65] * pinning capybara-screenshot; it just released a breaking version (1.0.0) --- admin/Gemfile | 2 +- web/Gemfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/admin/Gemfile b/admin/Gemfile index 5feb530cc..716543c5e 100644 --- a/admin/Gemfile +++ b/admin/Gemfile @@ -110,7 +110,7 @@ group :test do gem 'simplecov', '~> 0.7.1' gem 'simplecov-rcov' gem 'capybara-webkit' - gem 'capybara-screenshot' + gem 'capybara-screenshot', '0.3.22' # 1.0.0 broke compat with rspec. maybe we need newer rspec gem 'poltergeist' end diff --git a/web/Gemfile b/web/Gemfile index 257bad644..06aa29212 100644 --- a/web/Gemfile +++ b/web/Gemfile @@ -112,7 +112,7 @@ group :test, :cucumber do #else gem "capybara-webkit" #end - gem 'capybara-screenshot' + gem 'capybara-screenshot', '0.3.22' # 1.0.0 broke compat with rspec. maybe we need newer rspec gem 'cucumber-rails', :require => false #, '1.3.0', :require => false gem 'guard-spork', '0.3.2' gem 'spork', '0.9.0' From be218c4e9c108cd4d6df51a8e51d9c6c9d499247 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 22 Sep 2014 14:20:58 -0500 Subject: [PATCH 14/65] * merged --- ruby/lib/jam_ruby/models/band.rb | 8 + ruby/lib/jam_ruby/models/geo_ip_locations.rb | 19 +- ruby/lib/jam_ruby/models/search.rb | 46 +- ruby/lib/jam_ruby/models/user.rb | 5 + ruby/spec/factories.rb | 44 ++ .../jam_ruby/models/musician_search_spec.rb | 727 ++++++++++-------- ruby/spec/support/maxmind.rb | 57 +- web/Gemfile | 3 +- .../javascripts/accounts_audio_profile.js | 35 +- .../assets/javascripts/accounts_profile.js | 60 +- web/app/assets/javascripts/application.js | 2 + .../dialog/audioProfileInvalidDialog.js | 18 +- .../dialog/changeSearchLocationDialog.js | 116 +++ .../dialog/configureTrackDialog.js | 14 +- .../javascripts/dialog/editRecordingDialog.js | 7 - .../dialog/gettingStartedDialog.js | 79 ++ .../javascripts/dialog/joinTestSession.js | 113 +++ .../javascripts/dialog/networkTestDialog.js | 11 +- .../javascripts/everywhere/everywhere.js | 20 +- web/app/assets/javascripts/findMusician.js | 188 +++-- web/app/assets/javascripts/jam_rest.js | 7 +- web/app/assets/javascripts/jamkazam.js | 10 + web/app/assets/javascripts/landing/landing.js | 1 + web/app/assets/javascripts/layout.js | 69 +- web/app/assets/javascripts/selectLocation.js | 271 +++++++ web/app/assets/javascripts/sessionList.js | 34 +- web/app/assets/javascripts/sessionModel.js | 9 + web/app/assets/javascripts/user_dropdown.js | 14 +- web/app/assets/javascripts/utils.js | 6 +- web/app/assets/javascripts/voiceChatHelper.js | 1 + web/app/assets/javascripts/web/web.js | 1 + .../assets/stylesheets/client/ftue.css.scss | 36 - .../stylesheets/client/musician.css.scss | 183 ++++- .../stylesheets/client/screen_common.css.scss | 14 +- .../stylesheets/client/sessionList.css.scss | 2 - .../changeSearchLocationDialog.css.scss | 49 ++ .../dialogs/gettingStartDialog.css.scss | 141 ++++ .../dialogs/joinTestSession.css.scss | 26 + .../dialogs/launchAppDialog.css.scss | 2 +- .../dialogs/whatsNextDialog.css.scss | 8 - web/app/controllers/api_search_controller.rb | 7 +- web/app/controllers/api_users_controller.rb | 2 +- web/app/views/api_search/index.rabl | 3 - web/app/views/api_users/show.rabl | 4 + .../clients/_account_audio_profile.html.erb | 2 +- .../clients/_account_session_detail.html.haml | 1 + web/app/views/clients/_help.html.erb | 6 + web/app/views/clients/_musicians.html.erb | 57 +- web/app/views/clients/_web_filter.html.erb | 13 +- web/app/views/clients/index.html.erb | 3 + .../_acceptFriendRequestDialog.html.haml | 2 +- .../_audioProfileInvalidDialog.html.slim | 2 +- .../_changeSearchLocationDialog.html.slim | 24 + .../_clientPreferencesDialog.html.slim | 2 +- web/app/views/dialogs/_dialogs.html.haml | 5 +- .../dialogs/_edit_recording_dialog.html.haml | 2 +- .../dialogs/_gettingStartedDialog.html.slim | 81 ++ .../dialogs/_joinTestSessionDialog.html.slim | 21 + .../views/dialogs/_launchAppDialog.html.haml | 8 +- .../dialogs/_networkTestDialog.html.haml | 2 +- web/config/environments/test.rb | 3 + web/spec/factories.rb | 46 ++ web/spec/features/gear_wizard_spec.rb | 43 +- .../features/getting_started_dialog_spec.rb | 93 +++ web/spec/features/musician_search_spec.rb | 65 +- web/spec/features/whats_next_spec.rb | 75 -- .../fixtures/location_dropdowns.html | 20 + web/spec/javascripts/spec_helper.js | 31 + web/spec/support/client_interactions.rb | 41 + web/spec/support/maxmind.rb | 72 +- web/spec/teaspoon_env.rb | 182 +++++ .../assets/javascripts/bind-polyfill.js | 39 + web/vendor/assets/javascripts/class.js | 83 ++ 73 files changed, 2661 insertions(+), 835 deletions(-) create mode 100644 web/app/assets/javascripts/dialog/changeSearchLocationDialog.js create mode 100644 web/app/assets/javascripts/dialog/gettingStartedDialog.js create mode 100644 web/app/assets/javascripts/dialog/joinTestSession.js create mode 100644 web/app/assets/javascripts/selectLocation.js create mode 100644 web/app/assets/stylesheets/dialogs/changeSearchLocationDialog.css.scss create mode 100644 web/app/assets/stylesheets/dialogs/gettingStartDialog.css.scss create mode 100644 web/app/assets/stylesheets/dialogs/joinTestSession.css.scss delete mode 100644 web/app/assets/stylesheets/dialogs/whatsNextDialog.css.scss create mode 100644 web/app/views/dialogs/_changeSearchLocationDialog.html.slim create mode 100644 web/app/views/dialogs/_gettingStartedDialog.html.slim create mode 100644 web/app/views/dialogs/_joinTestSessionDialog.html.slim create mode 100644 web/spec/features/getting_started_dialog_spec.rb delete mode 100644 web/spec/features/whats_next_spec.rb create mode 100644 web/spec/javascripts/fixtures/location_dropdowns.html create mode 100644 web/spec/javascripts/spec_helper.js create mode 100644 web/spec/teaspoon_env.rb create mode 100644 web/vendor/assets/javascripts/bind-polyfill.js create mode 100644 web/vendor/assets/javascripts/class.js diff --git a/ruby/lib/jam_ruby/models/band.rb b/ruby/lib/jam_ruby/models/band.rb index 24dee5dae..0409ec100 100644 --- a/ruby/lib/jam_ruby/models/band.rb +++ b/ruby/lib/jam_ruby/models/band.rb @@ -70,6 +70,14 @@ module JamRuby self.music_sessions.size end + def latitude + lat + end + + def longitude + lng + end + def recent_history recordings = Recording.where(:band_id => self.id) .order('created_at DESC') diff --git a/ruby/lib/jam_ruby/models/geo_ip_locations.rb b/ruby/lib/jam_ruby/models/geo_ip_locations.rb index 1f8caeb50..215cd5e26 100644 --- a/ruby/lib/jam_ruby/models/geo_ip_locations.rb +++ b/ruby/lib/jam_ruby/models/geo_ip_locations.rb @@ -54,6 +54,19 @@ module JamRuby {city: city, state: state, country: country, addr: addr, locidispid: (locid.nil? || ispid.nil?) ? nil : Score.compute_locidispid(locid, ispid) } end + # returns a display- friendly bit of info about this location + def info + country_model = Country.where(countrycode: countrycode).first + region_model = Region.where(region: region, countrycode: countrycode).first + { + countrycode: countrycode, + country: country_model ? country_model.countryname : nil, + regioncode: region, + region: region_model ? region_model.regionname : nil, + city: city + } + end + def self.createx(locid, countrycode, region, city, postalcode, latitude, longitude, metrocode, areacode) c = connection.raw_connection c.exec_params("insert into #{self.table_name} (locid, countrycode, region, city, postalcode, latitude, longitude, metrocode, areacode, geog) values($1, $2, $3, $4, $5, $6, $7, $8, $9, ST_SetSRID(ST_MakePoint($7, $6), 4326)::geography)", @@ -82,8 +95,8 @@ module JamRuby # it isn't reasonable for both to be 0... latlng = [geo.latitude, geo.longitude] end - elsif current_user and current_user.locidispid and current_user.locidispid != 0 - location = GeoIpLocations.find_by_locid(current_user.locidispid/1000000) + elsif current_user and current_user.last_jam_locidispid and current_user.last_jam_locidispid != 0 + location = GeoIpLocations.find_by_locid(current_user.last_jam_locidispid/1000000) if location and location.latitude and location.longitude and (location.latitude != 0 or location.longitude != 0) # it isn't reasonable for both to be 0... latlng = [location.latitude, location.longitude] @@ -99,7 +112,7 @@ module JamRuby end if latlng - relation = relation.where(['latitude IS NOT NULL AND longitude IS NOT NULL']).within(distance, origin: latlng) + relation = relation.where(['lat IS NOT NULL AND lng IS NOT NULL']).within(distance, origin: latlng) end end relation diff --git a/ruby/lib/jam_ruby/models/search.rb b/ruby/lib/jam_ruby/models/search.rb index 1575c9b20..a5efa82cf 100644 --- a/ruby/lib/jam_ruby/models/search.rb +++ b/ruby/lib/jam_ruby/models/search.rb @@ -99,12 +99,13 @@ module JamRuby M_ORDER_PLAYS = ['Most Plays', :plays] M_ORDER_PLAYING = ['Playing Now', :playing] M_ORDER_LATENCY = ['Latency To Me', :latency] - M_ORDERINGS = [M_ORDER_LATENCY, M_ORDER_FOLLOWS, M_ORDER_PLAYS] + M_ORDER_DISTANCE = ['Distance To Me', :distance] + M_ORDERINGS = [M_ORDER_LATENCY, M_ORDER_DISTANCE, M_ORDER_FOLLOWS, M_ORDER_PLAYS] ORDERINGS = B_ORDERINGS = [M_ORDER_FOLLOWS, M_ORDER_PLAYS, M_ORDER_PLAYING] M_ORDERING_KEYS = M_ORDERINGS.collect { |oo| oo[1] } B_ORDERING_KEYS = B_ORDERINGS.collect { |oo| oo[1] } - DISTANCE_OPTS = B_DISTANCE_OPTS = M_DISTANCE_OPTS = [['Any', 0], [1000.to_s, 1000], [500.to_s, 500], [250.to_s, 250], [100.to_s, 100], [50.to_s, 50], [25.to_s, 25]] + DISTANCE_OPTS = B_DISTANCE_OPTS = M_DISTANCE_OPTS = [[25.to_s, 25], [50.to_s, 50], [100.to_s, 100], [250.to_s, 250], [500.to_s, 500], [1000.to_s, 1000] ] # the values for score ranges are raw roundtrip scores. david often talks of one way scores (<= 20 is good), but # the client reports scores as roundtrip and the server uses those values throughout @@ -117,6 +118,7 @@ module JamRuby ANY_SCORE = '' M_SCORE_OPTS = [['Any', ANY_SCORE], ['Good', GOOD_SCORE], ['Moderate', MODERATE_SCORE], ['Poor', POOR_SCORE], ['Unacceptable', UNACCEPTABLE_SCORE]] M_SCORE_DEFAULT = ANY_SCORE + M_DISTANCE_DEFAULT = 500 F_SORT_RECENT = ['Most Recent', :date] F_SORT_OLDEST = ['Most Liked', :likes] @@ -148,10 +150,7 @@ module JamRuby # distance - defunct! # city - defunct! # remote_ip - defunct! - def self.musician_filter(params={}, user=nil, conn=nil) - # puts "================ params #{params.inspect}" - # puts "================ user #{user.inspect}" - # puts "================ conn #{conn.inspect}" + def self.musician_filter(params={}, user=nil) rel = User.musicians # not musicians_geocoded on purpose; we allow 'unknowns' to surface in the search page rel = rel.select('users.*') @@ -173,11 +172,23 @@ module JamRuby score_limit = l end - # puts "================ score_limit #{score_limit}" + locidispid = user.nil? ? 0 : (user.last_jam_locidispid || 0) - locidispid = ((conn and conn.client_type == 'client') ? conn.locidispid : ((user and user.musician) ? user.last_jam_locidispid : nil)) + # user can override their location with these 3 values + country = params[:country] + region = params[:region] + city = params[:city] - # puts "================ locidispid #{locidispid}" + my_locid = nil # this is used for distance searches only + + if country && region && city + geoiplocation = GeoIpLocations.where(countrycode: country, region: region, city: city).first + my_locid = geoiplocation.locid + end + + unless my_locid + my_locid = locidispid/1000000 # if the user didn't specify a location to search on, user their account locidispid + end unless locidispid.nil? # score_join of left allows for null scores, whereas score_join of inner requires a score however good or bad @@ -229,10 +240,18 @@ module JamRuby end ordering = self.order_param(params) - # puts "================ ordering #{ordering}" case ordering when :latency # nothing to do. the sort added below 'current_scores.score ASC NULLS LAST' handles this + when :distance + # convert miles to meters for PostGIS functions + miles = params[:distance].blank? ? 500 : params[:distance].to_i + meters = miles * 1609.34 + rel = rel.joins("INNER JOIN geoiplocations AS my_geo ON #{my_locid} = my_geo.locid") + rel = rel.joins("INNER JOIN geoiplocations AS other_geo ON users.last_jam_locidispid/1000000 = other_geo.locid") + rel = rel.where("users.last_jam_locidispid/1000000 IN (SELECT locid FROM geoiplocations WHERE geog && st_buffer((SELECT geog FROM geoiplocations WHERE locid = #{my_locid}), #{meters}))") + rel = rel.group("my_geo.geog, other_geo.geog") + rel = rel.order('st_distance(my_geo.geog, other_geo.geog)') when :plays # FIXME: double counting? # sel_str = "COUNT(records)+COUNT(sessions) AS play_count, #{sel_str}" rel = rel.select('COUNT(records.id)+COUNT(sessions.id) AS search_play_count') @@ -254,17 +273,10 @@ module JamRuby rel = rel.order('users.created_at DESC') - # rel = rel.select(sel_str) rel, page = self.relation_pagination(rel, params) rel = rel.includes([:instruments, :followings, :friends]) - # puts "======================== sql #{rel.to_sql}" objs = rel.all - # puts "======================== objs #{objs.inspect}" - # if objs.length > 0 - # puts "======================== attributes #{objs[0].attributes}" - # puts "======================== score #{objs[0].score}" - # end srch = Search.new srch.search_type = :musicians_filter diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index c245314ee..75dc28113 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -1001,6 +1001,11 @@ module JamRuby self.save end + # gets the GeoIpLocation for the user's last_jam_locidispid (where are they REALLY, vs profile info) + def geoiplocation + GeoIpLocations.find_by_locid(last_jam_locidispid / 1000000) if last_jam_locidispid + end + def update_last_jam(remote_ip, reason) location = GeoIpLocations.lookup(remote_ip) self.last_jam_addr = location[:addr] diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb index a348d1a1e..1e5faf4c4 100644 --- a/ruby/spec/factories.rb +++ b/ruby/spec/factories.rb @@ -39,6 +39,50 @@ FactoryGirl.define do admin true end + factory :austin_user do + first_name 'Austin' + sequence(:last_name) { |n| "#{n}" } + state 'TX' + city 'Austin' + last_jam_locidispid { austin_geoip[:locidispid] } + last_jam_addr { austin_ip } + end + + factory :dallas_user do + first_name 'Dallas' + sequence(:last_name) { |n| "#{n}" } + state 'TX' + city 'Dallas' + last_jam_locidispid { dallas_geoip[:locidispid] } + last_jam_addr { dallas_ip } + end + + factory :houston_user do + first_name 'Houston' + sequence(:last_name) { |n| "#{n}" } + state 'TX' + city 'Houston' + last_jam_locidispid { houston_geoip[:locidispid] } + last_jam_addr { houston_ip } + end + + factory :miami_user do + first_name 'Miami' + sequence(:last_name) { |n| "#{n}" } + state 'FL' + city 'Miami' + last_jam_locidispid { miami_geoip[:locidispid] } + last_jam_addr { miami_ip } + end + + factory :seattle_user do + first_name 'Seattle' + sequence(:last_name) { |n| "#{n}" } + state 'WA' + city 'Seattle' + last_jam_locidispid { seattle_geoip[:locidispid] } + last_jam_addr { seattle_ip } + end factory :single_user_session do after(:create) do |user, evaluator| active_music_session = FactoryGirl.create(:active_music_session, :creator => user) diff --git a/ruby/spec/jam_ruby/models/musician_search_spec.rb b/ruby/spec/jam_ruby/models/musician_search_spec.rb index 0482e8ba1..9765e9785 100644 --- a/ruby/spec/jam_ruby/models/musician_search_spec.rb +++ b/ruby/spec/jam_ruby/models/musician_search_spec.rb @@ -2,356 +2,423 @@ require 'spec_helper' describe 'Musician search' do - before(:each) do - # @geocode1 = FactoryGirl.create(:geocoder) - # @geocode2 = FactoryGirl.create(:geocoder) - t = Time.now - 10.minute + # need a data set with actual distances + describe "test set A" do - @user1 = FactoryGirl.create(:user, created_at: t+1.minute, last_jam_locidispid: 1) - @user2 = FactoryGirl.create(:user, created_at: t+2.minute, last_jam_locidispid: 2) - @user3 = FactoryGirl.create(:user, created_at: t+3.minute, last_jam_locidispid: 3) - @user4 = FactoryGirl.create(:user, created_at: t+4.minute, last_jam_locidispid: 4) - @user5 = FactoryGirl.create(:user, created_at: t+5.minute, last_jam_locidispid: 5) - @user6 = FactoryGirl.create(:user, created_at: t+6.minute) # not geocoded - @user7 = FactoryGirl.create(:user, created_at: t+7.minute, musician: false) # not musician - - @musicians = [] - @musicians << @user1 - @musicians << @user2 - @musicians << @user3 - @musicians << @user4 - @musicians << @user5 - @musicians << @user6 - - @geomusicians = [] - @geomusicians << @user1 - @geomusicians << @user2 - @geomusicians << @user3 - @geomusicians << @user4 - @geomusicians << @user5 - - # from these scores: - # user1 has scores other users in user1 location, and with user2, user3, user4 - # user2 has scores with users in user3 and user4 location - # Score.delete_all - Score.createx(1, 'a', 1, 1, 'a', 1, 10) - Score.createx(1, 'a', 1, 2, 'b', 2, 20) - Score.createx(1, 'a', 1, 3, 'c', 3, 30) - Score.createx(1, 'a', 1, 4, 'd', 4, 40) - Score.createx(2, 'b', 2, 3, 'c', 3, 15) - Score.createx(2, 'b', 2, 4, 'd', 4, 70) - end - - context 'default filter settings' do - - it "finds all musicians" do - # expects all the musicians (geocoded) - results = Search.musician_filter({score_limit: Search::TEST_SCORE}) - results.search_type.should == :musicians_filter - results.results.count.should == @musicians.length - results.results.should eq @musicians.reverse + before(:each) do + create_phony_database end - it "finds all musicians page 1" do - # expects all the musicians - results = Search.musician_filter({page: 1, score_limit: Search::TEST_SCORE}) - results.search_type.should == :musicians_filter - results.results.count.should == @musicians.length - results.results.should eq @musicians.reverse - end + let!(:austin_user) { FactoryGirl.create(:austin_user) } + let!(:dallas_user) { FactoryGirl.create(:dallas_user) } + let!(:miami_user) { FactoryGirl.create(:miami_user) } + let!(:seattle_user) { FactoryGirl.create(:seattle_user) } - it "finds all musicians page 2" do - # expects no musicians (all fit on page 1) - results = Search.musician_filter({page: 2, score_limit: Search::TEST_SCORE}) - results.search_type.should == :musicians_filter - results.results.count.should == 0 - end + describe "search on distance" do - it "finds all musicians page 1 per_page 3" do - # expects three of the musicians - results = Search.musician_filter({per_page: 3, score_limit: Search::TEST_SCORE}) - results.search_type.should == :musicians_filter - results.results.count.should == 3 - results.results.should eq @musicians.reverse.slice(0, 3) - end - - it "finds all musicians page 2 per_page 3" do - # expects two of the musicians - results = Search.musician_filter({page: 2, per_page: 3, score_limit: Search::TEST_SCORE}) - results.search_type.should == :musicians_filter - results.results.count.should == 3 - results.results.should eq @musicians.reverse.slice(3, 3) - end - - it "finds all musicians page 3 per_page 3" do - # expects two of the musicians - results = Search.musician_filter({page: 3, per_page: 3, score_limit: Search::TEST_SCORE}) - results.search_type.should == :musicians_filter - results.results.count.should == 0 - end - - it "sorts musicians by followers" do - # establish sorting order - - # @user4 - f1 = Follow.new - f1.user = @user2 - f1.followable = @user4 - f1.save - - f2 = Follow.new - f2.user = @user3 - f2.followable = @user4 - f2.save - - f3 = Follow.new - f3.user = @user4 - f3.followable = @user4 - f3.save - - # @user3 - f4 = Follow.new - f4.user = @user3 - f4.followable = @user3 - f4.save - - f5 = Follow.new - f5.user = @user4 - f5.followable = @user3 - f5.save - - # @user2 - f6 = Follow.new - f6.user = @user1 - f6.followable = @user2 - f6.save - - # @user4.followers.concat([@user2, @user3, @user4]) - # @user3.followers.concat([@user3, @user4]) - # @user2.followers.concat([@user1]) - expect(@user4.followers.count).to be 3 - expect(Follow.count).to be 6 - - # refresh the order to ensure it works right - f1 = Follow.new - f1.user = @user3 - f1.followable = @user2 - f1.save - - f2 = Follow.new - f2.user = @user4 - f2.followable = @user2 - f2.save - - f3 = Follow.new - f3.user = @user2 - f3.followable = @user2 - f3.save - - # @user2.followers.concat([@user3, @user4, @user2]) - results = Search.musician_filter({ :per_page => @musicians.size, score_limit: Search::TEST_SCORE, orderby: 'followed'}, @user3) - expect(results.results[0].id).to eq(@user2.id) - - # check the follower count for given entry - expect(results.results[0].search_follow_count.to_i).not_to eq(0) - # check the follow relationship between current_user and result - expect(results.is_follower?(@user2)).to be true - end - - it 'paginates properly' do - # make sure pagination works right - params = { :per_page => 2, :page => 1 , score_limit: Search::TEST_SCORE} - results = Search.musician_filter(params) - expect(results.results.count).to be 2 - end - - end - - def make_recording(usr) - connection = FactoryGirl.create(:connection, :user => usr, locidispid: usr.last_jam_locidispid) - instrument = FactoryGirl.create(:instrument, :description => 'a great instrument') - track = FactoryGirl.create(:track, :connection => connection, :instrument => instrument) - music_session = FactoryGirl.create(:active_music_session, :creator => usr, :musician_access => true) - # music_session.connections << connection - # music_session.save - connection.join_the_session(music_session, true, nil, usr, 10) - recording = Recording.start(music_session, usr) - recording.stop - recording.reload - genre = FactoryGirl.create(:genre) - recording.claim(usr, "name", "description", genre, true) - recording.reload - recording - end - - def make_session(usr) - connection = FactoryGirl.create(:connection, :user => usr, locidispid: usr.last_jam_locidispid) - music_session = FactoryGirl.create(:active_music_session, :creator => usr, :musician_access => true) - # music_session.connections << connection - # music_session.save - connection.join_the_session(music_session, true, nil, usr, 10) - end - - context 'musician stat counters' do - - it "displays musicians top followings" do - f1 = Follow.new - f1.user = @user4 - f1.followable = @user4 - f1.save - - f2 = Follow.new - f2.user = @user4 - f2.followable = @user3 - f2.save - - f3 = Follow.new - f3.user = @user4 - f3.followable = @user2 - f3.save - - # @user4.followers.concat([@user4]) - # @user3.followers.concat([@user4]) - # @user2.followers.concat([@user4]) - expect(@user4.top_followings.count).to eq 3 - expect(@user4.top_followings.map(&:id)).to match_array((@musicians - [@user1, @user5, @user6]).map(&:id)) - end - - it "friends stat shows friend count" do - # create friendship record - Friendship.save(@user1.id, @user2.id) - # search on user2 - results = Search.musician_filter({score_limit: Search::TEST_SCORE}, @user2) - friend = results.results.detect { |mm| mm.id == @user1.id } - expect(friend).to_not be_nil - expect(results.friend_count(friend)).to be 1 - @user1.reload - expect(friend.friends?(@user2)).to be true - expect(results.is_friend?(@user1)).to be true - end - - it "recording stat shows recording count" do - Recording.delete_all - - recording = make_recording(@user1) - expect(recording.users.length).to be 1 - expect(recording.users.first).to eq(@user1) - @user1.reload - expect(@user1.recordings.length).to be 1 - expect(@user1.recordings.first).to eq(recording) - expect(recording.claimed_recordings.length).to be 1 - expect(@user1.recordings.detect { |rr| rr == recording }).to_not be_nil - - results = Search.musician_filter({score_limit: Search::TEST_SCORE},@user1) - # puts "====================== results #{results.inspect}" - uu = results.results.detect { |mm| mm.id == @user1.id } - expect(uu).to_not be_nil - - expect(results.record_count(uu)).to be 1 - expect(results.session_count(uu)).to be 1 - end - - end - - context 'musician sorting' do - - it "by plays" do - Recording.delete_all - - make_recording(@user1) - # order results by num recordings - results = Search.musician_filter({ orderby: 'plays', score_limit: Search::TEST_SCORE}, @user2) - # puts "========= results #{results.inspect}" - expect(results.results.length).to eq(2) - expect(results.results[0].id).to eq(@user1.id) - expect(results.results[1].id).to eq(@user3.id) - - # add more data and make sure order still correct - make_recording(@user3) - make_recording(@user3) - results = Search.musician_filter({ :orderby => 'plays', score_limit: Search::TEST_SCORE }, @user2) - expect(results.results.length).to eq(2) - expect(results.results[0].id).to eq(@user3.id) - expect(results.results[1].id).to eq(@user1.id) - end - - it "by now playing" do - pending "these tests worked, so leaving them in, but we don't currently have 'Now Playing' in the find musicians screen" - # should get 1 result with 1 active session - make_session(@user1) - results = Search.musician_filter({ :orderby => 'playing', score_limit: Search::TEST_SCORE}, @user2) - expect(results.results.count).to be 1 - expect(results.results.first.id).to eq(@user1.id) - - # should get 2 results with 2 active sessions - # sort order should be created_at DESC - make_session(@user3) - results = Search.musician_filter({ :orderby => 'playing', score_limit: Search::TEST_SCORE}, @user2) - expect(results.results.count).to be 2 - expect(results.results[0].id).to eq(@user3.id) - expect(results.results[1].id).to eq(@user1.id) - end - - end - - context 'filter settings' do - it "searches musicisns for an instrument" do - minst = FactoryGirl.create(:musician_instrument, { - :user => @user1, - :instrument => Instrument.find('tuba') }) - @user1.musician_instruments << minst - @user1.reload - ii = @user1.instruments.detect { |inst| inst.id == 'tuba' } - expect(ii).to_not be_nil - results = Search.musician_filter({ :instrument => ii.id, score_limit: Search::TEST_SCORE }) - results.results.each do |rr| - expect(rr.instruments.detect { |inst| inst.id=='tuba' }.id).to eq(ii.id) + it "finds self when very local search" do + Search.musician_filter({distance: 1, orderby: 'distance'}, austin_user).results.should == [austin_user] # just to see that distance is 0 to self + Search.musician_filter({distance: 1, orderby: 'distance'}, dallas_user).results.should == [dallas_user] # just to see that distance is 0 to self + Search.musician_filter({distance: 1, orderby: 'distance'}, miami_user).results.should == [miami_user] # just to see that distance is 0 to self + Search.musician_filter({distance: 1, orderby: 'distance'}, seattle_user).results.should == [seattle_user] # just to see that distance is 0 to self + end + + it "finds dallas when in range of austin" do + expected_results = [austin_user, dallas_user] + + Search.musician_filter({distance: 500, orderby: 'distance'}, austin_user).results.should == expected_results + Search.musician_filter({distance: 100, orderby: 'distance'}, austin_user).results.should == [austin_user] + end + + it "finds miami when in range of austin" do + expected_results = [austin_user, dallas_user, miami_user] + + Search.musician_filter({distance: 1500, orderby: 'distance'}, austin_user).results.should == expected_results + Search.musician_filter({distance: 300, orderby: 'distance'}, austin_user).results.should == [austin_user, dallas_user] + Search.musician_filter({distance: 100, orderby: 'distance'}, austin_user).results.should == [austin_user] + end + + it "finds seattle when in range of austin" do + expected_results = [austin_user, dallas_user, miami_user, seattle_user] + + Search.musician_filter({distance: 2000, orderby: 'distance'}, austin_user).results.should == expected_results + Search.musician_filter({distance: 1500, orderby: 'distance'}, austin_user).results.should == [austin_user, dallas_user, miami_user] + Search.musician_filter({distance: 300, orderby: 'distance'}, austin_user).results.should == [austin_user, dallas_user] + Search.musician_filter({distance: 100, orderby: 'distance'}, austin_user).results.should == [austin_user] + end + + it "finds austin & dallas by user-specified location when in range" do + Search.musician_filter({distance: 500, orderby: 'distance', city: 'Austin', region: 'TX', country: 'US'}, austin_user).results.should == [austin_user, dallas_user] + end + + it "finds dallas & austin by user-specified location when in range" do + Search.musician_filter({distance: 500, orderby: 'distance', city: 'Dallas', region: 'TX', country: 'US'}, austin_user).results.should == [dallas_user, austin_user] + end + + it "finds miami user-specified location when in range" do + Search.musician_filter({distance: 300, orderby: 'distance', city: 'Tampa', region: 'FL', country: 'US'}, austin_user).results.should == [miami_user] + end + + it "finds all users with user-specified location when in range" do + Search.musician_filter({distance: 2500, orderby: 'distance', city: 'Tampa', region: 'FL', country: 'US'}, austin_user).results.should == [miami_user, dallas_user, austin_user, seattle_user] end - expect(results.results.count).to be 1 end end - context 'new users' do + describe "test set B" do - it "find three for user1" do - # user2..4 are scored against user1 - ms = Search.new_musicians(@user1, Time.now - 1.week) - ms.should_not be_nil - ms.length.should == 3 - ms.should eq [@user2, @user3, @user4] + before(:each) do + # @geocode1 = FactoryGirl.create(:geocoder) + # @geocode2 = FactoryGirl.create(:geocoder) + t = Time.now - 10.minute + + @user1 = FactoryGirl.create(:user, created_at: t+1.minute, last_jam_locidispid: 1) + @user2 = FactoryGirl.create(:user, created_at: t+2.minute, last_jam_locidispid: 2) + @user3 = FactoryGirl.create(:user, created_at: t+3.minute, last_jam_locidispid: 3) + @user4 = FactoryGirl.create(:user, created_at: t+4.minute, last_jam_locidispid: 4) + @user5 = FactoryGirl.create(:user, created_at: t+5.minute, last_jam_locidispid: 5) + @user6 = FactoryGirl.create(:user, created_at: t+6.minute) # not geocoded + @user7 = FactoryGirl.create(:user, created_at: t+7.minute, musician: false) # not musician + + @musicians = [] + @musicians << @user1 + @musicians << @user2 + @musicians << @user3 + @musicians << @user4 + @musicians << @user5 + @musicians << @user6 + + @geomusicians = [] + @geomusicians << @user1 + @geomusicians << @user2 + @geomusicians << @user3 + @geomusicians << @user4 + @geomusicians << @user5 + + # from these scores: + # user1 has scores other users in user1 location, and with user2, user3, user4 + # user2 has scores with users in user3 and user4 location + # Score.delete_all + Score.createx(1, 'a', 1, 1, 'a', 1, 10) + Score.createx(1, 'a', 1, 2, 'b', 2, 20) + Score.createx(1, 'a', 1, 3, 'c', 3, 30) + Score.createx(1, 'a', 1, 4, 'd', 4, 40) + Score.createx(2, 'b', 2, 3, 'c', 3, 15) + Score.createx(2, 'b', 2, 4, 'd', 4, 70) end - it "find two for user2" do - # user1,3,4 are scored against user1, but user4 is bad - ms = Search.new_musicians(@user2, Time.now - 1.week) - ms.should_not be_nil - ms.length.should == 2 - ms.should eq [@user3, @user1] + + context 'default filter settings' do + + it "finds all musicians" do + # expects all the musicians (geocoded) + results = Search.musician_filter({score_limit: Search::TEST_SCORE}) + results.search_type.should == :musicians_filter + results.results.count.should == @musicians.length + results.results.should eq @musicians.reverse + end + + it "finds all musicians page 1" do + # expects all the musicians + results = Search.musician_filter({page: 1, score_limit: Search::TEST_SCORE}) + results.search_type.should == :musicians_filter + results.results.count.should == @musicians.length + results.results.should eq @musicians.reverse + end + + it "finds all musicians page 2" do + # expects no musicians (all fit on page 1) + results = Search.musician_filter({page: 2, score_limit: Search::TEST_SCORE}) + results.search_type.should == :musicians_filter + results.results.count.should == 0 + end + + it "finds all musicians page 1 per_page 3" do + # expects three of the musicians + results = Search.musician_filter({per_page: 3, score_limit: Search::TEST_SCORE}) + results.search_type.should == :musicians_filter + results.results.count.should == 3 + results.results.should eq @musicians.reverse.slice(0, 3) + end + + it "finds all musicians page 2 per_page 3" do + # expects two of the musicians + results = Search.musician_filter({page: 2, per_page: 3, score_limit: Search::TEST_SCORE}) + results.search_type.should == :musicians_filter + results.results.count.should == 3 + results.results.should eq @musicians.reverse.slice(3, 3) + end + + it "finds all musicians page 3 per_page 3" do + # expects two of the musicians + results = Search.musician_filter({page: 3, per_page: 3, score_limit: Search::TEST_SCORE}) + results.search_type.should == :musicians_filter + results.results.count.should == 0 + end + + it "sorts musicians by followers" do + # establish sorting order + + # @user4 + f1 = Follow.new + f1.user = @user2 + f1.followable = @user4 + f1.save + + f2 = Follow.new + f2.user = @user3 + f2.followable = @user4 + f2.save + + f3 = Follow.new + f3.user = @user4 + f3.followable = @user4 + f3.save + + # @user3 + f4 = Follow.new + f4.user = @user3 + f4.followable = @user3 + f4.save + + f5 = Follow.new + f5.user = @user4 + f5.followable = @user3 + f5.save + + # @user2 + f6 = Follow.new + f6.user = @user1 + f6.followable = @user2 + f6.save + + # @user4.followers.concat([@user2, @user3, @user4]) + # @user3.followers.concat([@user3, @user4]) + # @user2.followers.concat([@user1]) + expect(@user4.followers.count).to be 3 + expect(Follow.count).to be 6 + + # refresh the order to ensure it works right + f1 = Follow.new + f1.user = @user3 + f1.followable = @user2 + f1.save + + f2 = Follow.new + f2.user = @user4 + f2.followable = @user2 + f2.save + + f3 = Follow.new + f3.user = @user2 + f3.followable = @user2 + f3.save + + # @user2.followers.concat([@user3, @user4, @user2]) + results = Search.musician_filter({:per_page => @musicians.size, score_limit: Search::TEST_SCORE, orderby: 'followed'}, @user3) + expect(results.results[0].id).to eq(@user2.id) + + # check the follower count for given entry + expect(results.results[0].search_follow_count.to_i).not_to eq(0) + # check the follow relationship between current_user and result + expect(results.is_follower?(@user2)).to be true + end + + it 'paginates properly' do + # make sure pagination works right + params = {:per_page => 2, :page => 1, score_limit: Search::TEST_SCORE} + results = Search.musician_filter(params) + expect(results.results.count).to be 2 + end + end - it "find two for user3" do - # user1..2 are scored against user3 - ms = Search.new_musicians(@user3, Time.now - 1.week) - ms.should_not be_nil - ms.length.should == 2 - ms.should eq [@user2, @user1] + def make_recording(usr) + connection = FactoryGirl.create(:connection, :user => usr, locidispid: usr.last_jam_locidispid) + instrument = FactoryGirl.create(:instrument, :description => 'a great instrument') + track = FactoryGirl.create(:track, :connection => connection, :instrument => instrument) + music_session = FactoryGirl.create(:active_music_session, :creator => usr, :musician_access => true) + # music_session.connections << connection + # music_session.save + connection.join_the_session(music_session, true, nil, usr, 10) + recording = Recording.start(music_session, usr) + recording.stop + recording.reload + genre = FactoryGirl.create(:genre) + recording.claim(usr, "name", "description", genre, true) + recording.reload + recording end - it "find one for user4" do - # user1..2 are scored against user4, but user2 is bad - ms = Search.new_musicians(@user4, Time.now - 1.week) - ms.should_not be_nil - ms.length.should == 1 - ms.should eq [@user1] + def make_session(usr) + connection = FactoryGirl.create(:connection, :user => usr, locidispid: usr.last_jam_locidispid) + music_session = FactoryGirl.create(:active_music_session, :creator => usr, :musician_access => true) + # music_session.connections << connection + # music_session.save + connection.join_the_session(music_session, true, nil, usr, 10) end - it "find none for user5" do - # user1..4 are not scored against user5 - ms = Search.new_musicians(@user5, Time.now - 1.week) - ms.should_not be_nil - ms.length.should == 0 + context 'musician stat counters' do + + it "displays musicians top followings" do + f1 = Follow.new + f1.user = @user4 + f1.followable = @user4 + f1.save + + f2 = Follow.new + f2.user = @user4 + f2.followable = @user3 + f2.save + + f3 = Follow.new + f3.user = @user4 + f3.followable = @user2 + f3.save + + # @user4.followers.concat([@user4]) + # @user3.followers.concat([@user4]) + # @user2.followers.concat([@user4]) + expect(@user4.top_followings.count).to eq 3 + expect(@user4.top_followings.map(&:id)).to match_array((@musicians - [@user1, @user5, @user6]).map(&:id)) + end + + it "friends stat shows friend count" do + # create friendship record + Friendship.save(@user1.id, @user2.id) + # search on user2 + results = Search.musician_filter({score_limit: Search::TEST_SCORE}, @user2) + friend = results.results.detect { |mm| mm.id == @user1.id } + expect(friend).to_not be_nil + expect(results.friend_count(friend)).to be 1 + @user1.reload + expect(friend.friends?(@user2)).to be true + expect(results.is_friend?(@user1)).to be true + end + + it "recording stat shows recording count" do + Recording.delete_all + + recording = make_recording(@user1) + expect(recording.users.length).to be 1 + expect(recording.users.first).to eq(@user1) + @user1.reload + expect(@user1.recordings.length).to be 1 + expect(@user1.recordings.first).to eq(recording) + expect(recording.claimed_recordings.length).to be 1 + expect(@user1.recordings.detect { |rr| rr == recording }).to_not be_nil + + results = Search.musician_filter({score_limit: Search::TEST_SCORE}, @user1) + # puts "====================== results #{results.inspect}" + uu = results.results.detect { |mm| mm.id == @user1.id } + expect(uu).to_not be_nil + + expect(results.record_count(uu)).to be 1 + expect(results.session_count(uu)).to be 1 + end + end + context 'musician sorting' do + + it "by plays" do + Recording.delete_all + + make_recording(@user1) + # order results by num recordings + results = Search.musician_filter({orderby: 'plays', score_limit: Search::TEST_SCORE}, @user2) + # puts "========= results #{results.inspect}" + expect(results.results.length).to eq(2) + expect(results.results[0].id).to eq(@user1.id) + expect(results.results[1].id).to eq(@user3.id) + + # add more data and make sure order still correct + make_recording(@user3) + make_recording(@user3) + results = Search.musician_filter({:orderby => 'plays', score_limit: Search::TEST_SCORE}, @user2) + expect(results.results.length).to eq(2) + expect(results.results[0].id).to eq(@user3.id) + expect(results.results[1].id).to eq(@user1.id) + end + + it "by now playing" do + pending "these tests worked, so leaving them in, but we don't currently have 'Now Playing' in the find musicians screen" + # should get 1 result with 1 active session + make_session(@user1) + results = Search.musician_filter({:orderby => 'playing', score_limit: Search::TEST_SCORE}, @user2) + expect(results.results.count).to be 1 + expect(results.results.first.id).to eq(@user1.id) + + # should get 2 results with 2 active sessions + # sort order should be created_at DESC + make_session(@user3) + results = Search.musician_filter({:orderby => 'playing', score_limit: Search::TEST_SCORE}, @user2) + expect(results.results.count).to be 2 + expect(results.results[0].id).to eq(@user3.id) + expect(results.results[1].id).to eq(@user1.id) + end + + end + + context 'filter settings' do + it "searches musicisns for an instrument" do + minst = FactoryGirl.create(:musician_instrument, { + :user => @user1, + :instrument => Instrument.find('tuba')}) + @user1.musician_instruments << minst + @user1.reload + ii = @user1.instruments.detect { |inst| inst.id == 'tuba' } + expect(ii).to_not be_nil + results = Search.musician_filter({:instrument => ii.id, score_limit: Search::TEST_SCORE}) + results.results.each do |rr| + expect(rr.instruments.detect { |inst| inst.id=='tuba' }.id).to eq(ii.id) + end + expect(results.results.count).to be 1 + end + end + + context 'new users' do + + it "find three for user1" do + # user2..4 are scored against user1 + ms = Search.new_musicians(@user1, Time.now - 1.week) + ms.should_not be_nil + ms.length.should == 3 + ms.should eq [@user2, @user3, @user4] + end + + it "find two for user2" do + # user1,3,4 are scored against user1, but user4 is bad + ms = Search.new_musicians(@user2, Time.now - 1.week) + ms.should_not be_nil + ms.length.should == 2 + ms.should eq [@user3, @user1] + end + + it "find two for user3" do + # user1..2 are scored against user3 + ms = Search.new_musicians(@user3, Time.now - 1.week) + ms.should_not be_nil + ms.length.should == 2 + ms.should eq [@user2, @user1] + end + + it "find one for user4" do + # user1..2 are scored against user4, but user2 is bad + ms = Search.new_musicians(@user4, Time.now - 1.week) + ms.should_not be_nil + ms.length.should == 1 + ms.should eq [@user1] + end + + it "find none for user5" do + # user1..4 are not scored against user5 + ms = Search.new_musicians(@user5, Time.now - 1.week) + ms.should_not be_nil + ms.length.should == 0 + end + + end end end diff --git a/ruby/spec/support/maxmind.rb b/ruby/spec/support/maxmind.rb index 73b78d663..60a257bcc 100644 --- a/ruby/spec/support/maxmind.rb +++ b/ruby/spec/support/maxmind.rb @@ -169,7 +169,7 @@ def ip_from_num(num) end def austin_ip - IPAddr.new(0x0FFFFFFF, Socket::AF_INET).to_s + IPAddr.new(austin_ip_as_num, Socket::AF_INET).to_s end def austin_ip_as_num @@ -177,35 +177,64 @@ def austin_ip_as_num end def dallas_ip - IPAddr.new(0x1FFFFFFF, Socket::AF_INET).to_s + IPAddr.new(dallas_ip_as_num, Socket::AF_INET).to_s end def dallas_ip_as_num 0x1FFFFFFF end -# gets related models for an IP in the 1st block from the scores_better_test_data.sql -def austin_geoip - geoiplocation = GeoIpLocations.find_by_locid(17192) - geoipblock = GeoIpBlocks.find_by_locid(17192) +def houston_ip + IPAddr.new(houston_ip_as_num, Socket::AF_INET).to_s +end + +def houston_ip_as_num + 0x2FFFFFFF +end + +def miami_ip + IPAddr.new(miami_ip_as_num, Socket::AF_INET).to_s +end + +def miami_ip_as_num + 0x5FFFFFFF +end + +def seattle_ip + IPAddr.new(seattle_ip_as_num, Socket::AF_INET).to_s +end + +def seattle_ip_as_num + 0xAFFFFFFF +end + +def create_geoip(locid) + geoiplocation = GeoIpLocations.find_by_locid(locid) + geoipblock = GeoIpBlocks.find_by_locid(locid) jamisp = JamIsp.find_by_beginip(geoipblock.beginip) {jamisp: jamisp, geoiplocation: geoiplocation, geoipblock: geoipblock, locidispid: Score.compute_locidispid(geoiplocation.locid, jamisp.coid)} end +# gets related models for an IP in the 1st block from the scores_better_test_data.sql +def austin_geoip + create_geoip(17192) +end # gets related models for an IP in the 1st block from the scores_better_test_data.sql def dallas_geoip - geoiplocation = GeoIpLocations.find_by_locid(667) - geoipblock = GeoIpBlocks.find_by_locid(667) - jamisp = JamIsp.find_by_beginip(geoipblock.beginip) - {jamisp: jamisp, geoiplocation: geoiplocation, geoipblock: geoipblock, locidispid: Score.compute_locidispid(geoiplocation.locid, jamisp.coid)} + create_geoip(667) end # gets related models for an IP in the 1st block from the scores_better_test_data.sql def houston_geoip - geoiplocation = GeoIpLocations.find_by_locid(30350) - geoipblock = GeoIpBlocks.find_by_locid(30350) - jamisp = JamIsp.find_by_beginip(geoipblock.beginip) - {jamisp: jamisp, geoiplocation: geoiplocation, geoipblock: geoipblock, locidispid: Score.compute_locidispid(geoiplocation.locid, jamisp.coid)} + create_geoip(30350) +end + +def miami_geoip + create_geoip(23565) +end + +def seattle_geoip + create_geoip(1539) end # attempts to make the creation of a score more straightforward. diff --git a/web/Gemfile b/web/Gemfile index 06aa29212..ad080e413 100644 --- a/web/Gemfile +++ b/web/Gemfile @@ -84,11 +84,12 @@ group :development, :test do gem 'rspec-rails', '2.14.2' gem "activerecord-import", "~> 0.4.1" gem 'guard-rspec', '0.5.5' - gem 'jasmine', '1.3.1' +# gem 'jasmine', '1.3.1' gem 'pry' gem 'execjs', '1.4.0' gem 'factory_girl_rails', '4.1.0' # in dev because in use by rake task gem 'database_cleaner', '1.3.0' #in dev because in use by rake task + gem 'teaspoon' end group :unix do gem 'therubyracer' #, '0.11.0beta8' diff --git a/web/app/assets/javascripts/accounts_audio_profile.js b/web/app/assets/javascripts/accounts_audio_profile.js index b9be0128e..d35a28c5b 100644 --- a/web/app/assets/javascripts/accounts_audio_profile.js +++ b/web/app/assets/javascripts/accounts_audio_profile.js @@ -13,6 +13,8 @@ var self = this; var reloadAudioTimeout = null; var $root; + var $dialog; + var $addNewGearBtn; var userId; var showingGearWizard = false; var cancelRescanFunc = null; @@ -88,7 +90,13 @@ context.JK.guardAgainstBrowser(app, {d1: 'gear'}); } else { - populateAccountAudio() + var profiles = populateAccountAudio(); + if(profiles.length == 1) { + setTimeout(function() { + context.JK.prodBubble($addNewGearBtn, 'no-audio-profiles', {}, {positions: ['bottom'], offsetParent: $addNewGearBtn.closest('.screen')}); + }, 1000); + + } } } @@ -115,6 +123,8 @@ var template = context._.template($('#template-account-audio').html(), {is_admin: context.JK.currentUserAdmin, profiles: cleansedProfiles}, {variable: 'data'}); appendAudio(template); + + return profiles; } function appendAudio(template) { @@ -202,16 +212,16 @@ function handleStartAudioQualification() { - if(true) { - app.afterFtue = function() { showingGearWizard = false; populateAccountAudio() }; - app.cancelFtue = function() { showingGearWizard = false; populateAccountAudio() }; - showingGearWizard = true; - app.layout.startNewFtue(); - } - else { - app.setWizardStep(1); - app.layout.showDialog('ftue'); - } + app.afterFtue = function() { + showingGearWizard = false; + if(populateAccountAudio().length == 1) { + app.layout.showDialog('join-test-session'); + } + }; + app.cancelFtue = function() { showingGearWizard = false; populateAccountAudio() }; + showingGearWizard = true; + app.layout.startNewFtue(); + } function reloadAudio() { @@ -309,8 +319,11 @@ 'beforeShow': beforeShow, 'afterShow': afterShow }; + app.bindScreen('account/audio', screenBindings); + $dialog = $('#account-audio-profile') + $addNewGearBtn = $dialog.find('a[data-purpose=add-profile]'); events(); } diff --git a/web/app/assets/javascripts/accounts_profile.js b/web/app/assets/javascripts/accounts_profile.js index d904f5f0e..91e7f2665 100644 --- a/web/app/assets/javascripts/accounts_profile.js +++ b/web/app/assets/javascripts/accounts_profile.js @@ -10,6 +10,7 @@ var api = context.JK.Rest(); var userId; var user = {}; + var selectLocation = null; var recentUserDetail = null; var loadingCitiesData = false; var loadingRegionsData = false; @@ -252,28 +253,8 @@ $('#account-profile-content-scroller').on('click', '#account-edit-profile-cancel', function(evt) { evt.stopPropagation(); navToAccount(); return false; } ); $('#account-profile-content-scroller').on('click', '#account-edit-profile-submit', function(evt) { evt.stopPropagation(); handleUpdateProfile(); return false; } ); $('#account-profile-content-scroller').on('submit', '#account-edit-email-form', function(evt) { evt.stopPropagation(); handleUpdateProfile(); return false; } ); - $('#account-profile-content-scroller').on('change', 'select[name=country]', function(evt) { evt.stopPropagation(); handleCountryChanged(); return false; } ); - $('#account-profile-content-scroller').on('change', 'select[name=region]', function(evt) { evt.stopPropagation(); handleRegionChanged(); return false; } ); $('#account-profile-content-scroller').on('click', '#account-change-avatar', function(evt) { evt.stopPropagation(); navToAvatar(); return false; } ); } - - function regionListFailure(jqXHR, textStatus, errorThrown) { - if(jqXHR.status == 422) { - logger.debug("no regions found for country: " + recentUserDetail.country); - } - else { - app.ajaxError(arguments); - } - } - - function cityListFailure(jqXHR, textStatus, errorThrown) { - if(jqXHR.status == 422) { - logger.debug("no cities found for country/region: " + recentUserDetail.country + "/" + recentUserDetail.state); - } - else { - app.ajaxError(arguments); - } - } function renderAccountProfile() { $.when( api.getUserDetail(), @@ -285,43 +266,8 @@ // show page; which can be done quickly at this point populateAccountProfile(userDetail, instruments); - var country = userDetail.country; - - if(!country) { - // this case shouldn't happen because sign up makes you pick a location. This is just 'in case', so that the UI is more error-resilient - country = 'US'; - } - - loadingCountriesData = true; - loadingRegionsData = true; - loadingCitiesData = true; - - // make the 3 slower requests, which only matter if the user wants to affect their ISP or location - - api.getCountries() - .done(function(countriesx) { populateCountriesx(countriesx["countriesx"], userDetail.country); } ) - .fail(app.ajaxError) - .always(function() { loadingCountriesData = false; }) - - var country = userDetail.country; - var state = userDetail.state; - - if(country) { - api.getRegions( { country: country } ) - .done(function(regions) { populateRegions(regions["regions"], userDetail.state); } ) - .fail(regionListFailure) - .always(function() { loadingRegionsData = false; }) - - if(state) { - api.getCities( { country: country, region: state }) - .done(function(cities) { - populateCities(cities["cities"], userDetail.city) - }) - .fail(cityListFailure) - .always(function() { loadingCitiesData = false;}) - } - } - + selectLocation = new context.JK.SelectLocation(getCountryElement(), getRegionElement(), getCityElement(), app); + selectLocation.load(userDetail.country, userDetail.state, userDetail.city) }) context.JK.dropdown($('select')); } diff --git a/web/app/assets/javascripts/application.js b/web/app/assets/javascripts/application.js index 61b264f38..ec05b467c 100644 --- a/web/app/assets/javascripts/application.js +++ b/web/app/assets/javascripts/application.js @@ -10,6 +10,7 @@ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD // GO AFTER THE REQUIRES BELOW. // +//= require bind-polyfill //= require jquery //= require jquery.monkeypatch //= require jquery_ujs @@ -34,6 +35,7 @@ //= require jquery.browser //= require jquery.custom-protocol //= require jstz +//= require class //= require AAC_underscore //= require AAA_Log //= require globals diff --git a/web/app/assets/javascripts/dialog/audioProfileInvalidDialog.js b/web/app/assets/javascripts/dialog/audioProfileInvalidDialog.js index 11397f7a3..6fb4a5ec4 100644 --- a/web/app/assets/javascripts/dialog/audioProfileInvalidDialog.js +++ b/web/app/assets/javascripts/dialog/audioProfileInvalidDialog.js @@ -77,6 +77,13 @@ context.JK.onBackendEvent(ALERT_NAMES.USB_DISCONNECTED, 'audio-profile-invalid-dialog', onUsbDeviceDisconnected); } + function onCancel() { + if($btnCancel.is('.disabled')) return false; + + $dialog.data('result', 'cancel'); + return true; + } + function beforeHide() { context.JK.offBackendEvent(ALERT_NAMES.USB_CONNECTED, 'audio-profile-invalid-dialog', onUsbDeviceConnected); context.JK.offBackendEvent(ALERT_NAMES.USB_DISCONNECTED, 'audio-profile-invalid-dialog', onUsbDeviceDisconnected); @@ -166,14 +173,6 @@ app.layout.closeDialog('audio-profile-invalid-dialog'); return false; }) - - $btnCancel.click(function() { - if($(this).is('.disabled')) return false; - - $dialog.data('result', 'cancel'); - app.layout.closeDialog('audio-profile-invalid-dialog'); - return false; - }) } function initialize() { @@ -182,7 +181,8 @@ 'beforeShow': beforeShow, 'afterShow' : afterShow, 'beforeHide' : beforeHide, - 'afterHide': afterHide + 'afterHide': afterHide, + 'onCancel' : onCancel }; app.bindDialog('audio-profile-invalid-dialog', dialogBindings); diff --git a/web/app/assets/javascripts/dialog/changeSearchLocationDialog.js b/web/app/assets/javascripts/dialog/changeSearchLocationDialog.js new file mode 100644 index 000000000..f28786ad7 --- /dev/null +++ b/web/app/assets/javascripts/dialog/changeSearchLocationDialog.js @@ -0,0 +1,116 @@ +(function (context, $) { + + "use strict"; + context.JK = context.JK || {}; + context.JK.ChangeSearchLocationDialog = function (app) { + var logger = context.JK.logger; + var rest = context.JK.Rest(); + var $dialog = null; + var initialized = false; + var $countries = null; + var $regions = null; + var $cities = null; + var selectLocation = null; + var $resetLocation = null; + var $btnSave = null; + + function getSelection() { + var countryVal = $countries.val(); + var regionVal = $regions.val(); + var cityVal = $cities.val(); + + if(countryVal && regionVal && cityVal) { + // if any bit of info is not set, then null out info; user didn't pick a full set of data + var $country = $($countries).find('option:selected'); + var $region = $($regions).find('option:selected'); + + return { + countrycode : countryVal, + regioncode : regionVal, + city: cityVal, + country: $country.text(), + region: $region.text() + } + } + else + { + return null; + } + } + + function events() { + $btnSave.click(function() { + + var selection = getSelection(); + + app.user().done(function(user) { + if(selection != null && + selection.countrycode == user.country && + selection.regioncode == user.state && + selection.city == user.city) { + // if this is the same location on the user's account, suppress selection + selection = null; + } + + $dialog.data('result', selection); + app.layout.closeDialog('change-search-location') + }) + + return false; + }) + + $resetLocation.click(function() { + app.user().done(function(user) { + selectLocation.load(user.country, user.state, user.city); + }) + return false; + }) + } + + function beforeShow() { + $dialog.data('result', null); + + if(!initialized) { + initialized = true; + app.user().done(function(user) { + selectLocation = new context.JK.SelectLocation($countries, $regions, $cities, app); + selectLocation.load(user.country, user.state, user.city); + }) + } + else { + + } + } + + function beforeHide() { + + } + + function getSelectedLocation() { + return searchLocation; + } + + function initialize() { + var dialogBindings = { + 'beforeShow': beforeShow, + 'beforeHide': beforeHide + }; + + app.bindDialog('change-search-location', dialogBindings); + + $dialog = $('#change-search-location-dialog'); + $countries = $dialog.find('select[name="country"]') + $regions = $dialog.find('select[name="region"]') + $cities = $dialog.find('select[name="city"]') + $resetLocation = $dialog.find('.reset-location'); + $btnSave = $dialog.find('.btnSave') + + events(); + }; + + this.initialize = initialize; + this.getSelectedLocation = getSelectedLocation; + } + + return this; +})(window, jQuery); \ No newline at end of file diff --git a/web/app/assets/javascripts/dialog/configureTrackDialog.js b/web/app/assets/javascripts/dialog/configureTrackDialog.js index d7943734f..f89efe96b 100644 --- a/web/app/assets/javascripts/dialog/configureTrackDialog.js +++ b/web/app/assets/javascripts/dialog/configureTrackDialog.js @@ -119,13 +119,10 @@ }); $btnCancel.click(function() { - if(voiceChatHelper.cancel()) { - app.layout.closeDialog('configure-tracks') - } + app.layout.cancelDialog('configure-tracks'); return false; }); - //$btnAddNewGear.click(function() { // return false; @@ -206,10 +203,14 @@ function afterShow() { sessionUtils.SessionPageEnter(); } + + function onCancel() { + return voiceChatHelper.cancel(); + } + function afterHide() { voiceChatHelper.beforeHide(); sessionUtils.SessionPageLeave(); - } function initialize() { @@ -217,7 +218,8 @@ var dialogBindings = { 'beforeShow' : beforeShow, 'afterShow' : afterShow, - 'afterHide': afterHide + 'afterHide': afterHide, + 'onCancel' : onCancel }; app.bindDialog('configure-tracks', dialogBindings); diff --git a/web/app/assets/javascripts/dialog/editRecordingDialog.js b/web/app/assets/javascripts/dialog/editRecordingDialog.js index 23c1d4919..9aac9d33a 100644 --- a/web/app/assets/javascripts/dialog/editRecordingDialog.js +++ b/web/app/assets/javascripts/dialog/editRecordingDialog.js @@ -12,7 +12,6 @@ var $description = null; var $genre = null; var $isPublic = null; - var $cancelBtn = null; var $saveBtn = null; var $deleteBtn = null; @@ -129,14 +128,9 @@ }) } - function cancel() { - app.layout.closeDialog('edit-recording'); - } - function events() { $saveBtn.click(attemptUpdate); $deleteBtn.click(attemptDelete); - $cancelBtn.click(cancel) $form.submit(false); } @@ -151,7 +145,6 @@ $dialog = $('#edit-recording-dialog'); $form = $dialog.find('form'); - $cancelBtn = $dialog.find('.cancel-btn'); $saveBtn = $dialog.find('.save-btn'); $deleteBtn = $dialog.find('.delete-btn'); $name = $dialog.find('input[name="name"]'); diff --git a/web/app/assets/javascripts/dialog/gettingStartedDialog.js b/web/app/assets/javascripts/dialog/gettingStartedDialog.js new file mode 100644 index 000000000..ff93b8fc7 --- /dev/null +++ b/web/app/assets/javascripts/dialog/gettingStartedDialog.js @@ -0,0 +1,79 @@ +(function (context, $) { + + "use strict"; + context.JK = context.JK || {}; + context.JK.GettingStartedDialog = function (app) { + var logger = context.JK.logger; + var rest = context.JK.Rest(); + var invitationDialog = null; + var $dialog = null; + var $dontShowAgain = null; + var $setupGearBtn = null; + + + function registerEvents() { + + $setupGearBtn.click(function() { + if (gon.isNativeClient) { + app.layout.closeDialog('getting-started'); + window.location = '/client#/account/audio' + } + else { + context.JK.guardAgainstBrowser(app, {d1: 'gear'}); + } + return false; + }) + + $('#getting-started-dialog a.facebook-invite').on('click', function (e) { + invitationDialog.showFacebookDialog(e); + }); + + $('#getting-started-dialog a.google-invite').on('click', function (e) { + invitationDialog.showGoogleDialog(); + }); + + $('#getting-started-dialog a.email-invite').on('click', function (e) { + invitationDialog.showEmailDialog(); + }); + } + + function beforeShow() { + } + + function beforeHide() { + + if ($dontShowAgain.is(':checked')) { + app.updateUserModel({show_whats_next: false}) + } + } + + function initializeButtons() { + + context.JK.checkbox($dontShowAgain); + } + + function initialize(invitationDialogInstance) { + var dialogBindings = { + 'beforeShow': beforeShow, + 'beforeHide': beforeHide + }; + + app.bindDialog('getting-started', dialogBindings); + + $dialog = $('#getting-started-dialog'); + $dontShowAgain = $dialog.find('#show_getting_started'); + $setupGearBtn = $dialog.find('.setup-gear-btn') + + registerEvents(); + + invitationDialog = invitationDialogInstance; + + initializeButtons(); + }; + + + this.initialize = initialize; + } + + return this; +})(window, jQuery); \ No newline at end of file diff --git a/web/app/assets/javascripts/dialog/joinTestSession.js b/web/app/assets/javascripts/dialog/joinTestSession.js new file mode 100644 index 000000000..89d0b0df6 --- /dev/null +++ b/web/app/assets/javascripts/dialog/joinTestSession.js @@ -0,0 +1,113 @@ +(function (context, $) { + + "use strict"; + context.JK = context.JK || {}; + context.JK.JoinTestSessionDialog = function (app) { + var logger = context.JK.logger; + var rest = context.JK.Rest(); + var $dialog = null; + var $joinTestSessionBtn = null; + + function joinSession(sessionId) { + app.layout.closeDialog('join-test-session') + $joinTestSessionBtn.removeClass('disabled'); + context.JK.GA.trackSessionCount(true, true, 0); + + // we redirect to the session screen, which handles the REST call to POST /participants. + logger.debug("joining session screen: " + sessionId) + context.location = '/client#/session/' + sessionId; + }; + + function createSession(sessionId) { + var data = {}; + + data.name = 'First Session'; + data.description = 'This is my first ever session on JamKazam, so please be gentle. :)'; + data.genres = ['other'] + data.musician_access = true; + data.approval_required = false; + data.fan_access = true; + data.fan_chat = true; + data.legal_policy = 'Standard' + data.legal_terms = true; + data.language = 'eng'; + data.start = new Date().toDateString() + ' ' + context.JK.formatUtcTime(new Date(), false); + data.duration = "60"; + data.recurring_mode = 'once' + data.music_notations = []; + data.timezone = 'Central Time (US & Canada),America/Chicago' + data.open_rsvps = true + data.rsvp_slots = []; + + // auto pick an 'other' instrument + var otherId = context.JK.server_to_client_instrument_map.Other.server_id; // get server ID + var otherInstrumentInfo = context.JK.instrument_id_to_instrument[otherId]; // get display name + var beginnerLevel = 1; // default to beginner + var instruments = [ {id: otherId, name: otherInstrumentInfo.display, level: beginnerLevel} ]; + $.each(instruments, function(index, instrument) { + var slot = {}; + slot.instrument_id = instrument.id; + slot.proficiency_level = instrument.level; + slot.approve = true; + data.rsvp_slots.push(slot); + }); + + data.isUnstructuredRsvp = true; + + rest.createScheduledSession(data) + .done(function(response) { + logger.debug("created test session on server"); + //$('#create-session-buttons .btn-next').off('click'); + var newSessionId = response.id; + + joinSession(newSessionId); + }) + .fail(function(jqXHR){ + $joinTestSessionBtn.removeClass('disabled'); + logger.debug("unable to schedule a test session") + app.notifyServerError(jqXHR, "Unable to schedule a test session"); + }) + } + + function registerEvents() { + + $joinTestSessionBtn.click(function() { + + if($joinTestSessionBtn.is('.disabled')) return false; + + $joinTestSessionBtn.addClass('disabled'); + + createSession(); + + return false; + }) + } + + function beforeShow() { + $joinTestSessionBtn.removeClass('disabled'); + } + + function beforeHide() { + + } + + function initialize() { + var dialogBindings = { + 'beforeShow': beforeShow, + 'beforeHide': beforeHide + }; + + app.bindDialog('join-test-session', dialogBindings); + + $dialog = $('#join-test-session-dialog'); + $joinTestSessionBtn = $dialog.find('.join-test-session') + + registerEvents(); + }; + + + this.initialize = initialize; + } + + return this; +})(window, jQuery); \ No newline at end of file diff --git a/web/app/assets/javascripts/dialog/networkTestDialog.js b/web/app/assets/javascripts/dialog/networkTestDialog.js index ad00388fb..5d64ceb64 100644 --- a/web/app/assets/javascripts/dialog/networkTestDialog.js +++ b/web/app/assets/javascripts/dialog/networkTestDialog.js @@ -7,7 +7,6 @@ var logger = context.JK.logger; var $dialog = null; - var $btnCancel = null; var $btnClose = null; var $btnHelp = null; var networkTest = new context.JK.NetworkTest(app); @@ -37,13 +36,10 @@ $btnClose.removeClass('button-grey').addClass('button-orange'); } + function onCancel() { + // should we stop this if the test is going? + } function events() { - $btnCancel.on('click', function() { - // should we stop this if the test is going? - app.layout.closeDialog('network-test') - return false; - }) - $btnClose.on('click', function(e) { if(!networkTest.isScoring()) { app.layout.closeDialog('network-test'); @@ -77,7 +73,6 @@ app.bindDialog('network-test', dialogBindings); $dialog = $('#network-test-dialog'); - $btnCancel = $dialog.find('.btn-cancel'); $btnHelp = $dialog.find('.btn-help'); $btnClose = $dialog.find('.btn-close'); diff --git a/web/app/assets/javascripts/everywhere/everywhere.js b/web/app/assets/javascripts/everywhere/everywhere.js index b3272d0c6..e90049966 100644 --- a/web/app/assets/javascripts/everywhere/everywhere.js +++ b/web/app/assets/javascripts/everywhere/everywhere.js @@ -46,6 +46,8 @@ initializeStun(app); operationalEvents(app); + + handleGettingStarted(app); }); function watchPreferencesEvent(app) { @@ -107,8 +109,11 @@ JK.UserDropdown = userDropdown; userDropdown.initialize(invitationDialog); - var whatsNextDialog = new JK.WhatsNextDialog(app); - whatsNextDialog.initialize(invitationDialog); + var gettingStartedDialog = new JK.GettingStartedDialog(app); + gettingStartedDialog.initialize(invitationDialog); + + var joinTestSessionDialog = new JK.JoinTestSessionDialog(app); + joinTestSessionDialog.initialize(); var videoDialog = new JK.VideoDialog(app); videoDialog.initialize(); @@ -167,7 +172,18 @@ JK.JamServer.registerMessageCallback(JK.MessageType.STOP_APPLICATION, function(header, payload) { context.jamClient.ShutdownApplication(); }); + } + function handleGettingStarted(app) { + app.user() + .done(function(userProfile) { + if (userProfile.show_whats_next && + window.location.pathname.indexOf(gon.client_path) == 0 && + !app.layout.isDialogShowing('getting-started')) + { + app.layout.showDialog('getting-started'); + } + }) } })(window, jQuery); \ No newline at end of file diff --git a/web/app/assets/javascripts/findMusician.js b/web/app/assets/javascripts/findMusician.js index b46e1aaaa..c900cbf2a 100644 --- a/web/app/assets/javascripts/findMusician.js +++ b/web/app/assets/javascripts/findMusician.js @@ -6,6 +6,7 @@ var logger = context.JK.logger; var rest = context.JK.Rest(); + var EVENTS = context.JK.EVENTS; var musicians = {}; var musicianList; var instrument_logo_map = context.JK.getInstrumentIconMap24(); @@ -16,60 +17,139 @@ var $results = null; var $spinner = null; var $endMusicianList = null; + var $templateAccountSessionLatency = null; var helpBubble = context.JK.HelpBubbleHelper; var sessionUtils = context.JK.SessionUtils; - + var $musicianSearchCity = null; + var $musicianFilterCity = null; + var $musicianChangeFilterCity = null; + var $musicianQueryScore = null; + var $musicianDistance = null; + var $musicianLatencyOrDistanceLabel = null; + var $refreshBtn = null; + var searchLocationOverride = null; + var currentRequest = null; function search() { - $spinner.show(); - did_show_musician_page = true; - var queryString = 'srch_m=1&page=' + page_num + '&'; + var options = { + srch_m: 1, + page: page_num + } // order by var orderby = $('#musician_order_by').val(); if (typeof orderby != 'undefined' && orderby.length > 0) { - queryString += "orderby=" + orderby + '&'; + options['orderby'] = orderby; } // instrument filter var instrument = $('#musician_instrument').val(); if (typeof instrument != 'undefined' && !(instrument === '')) { - queryString += "instrument=" + instrument + '&'; + options['instrument'] = instrument; } // score filter - var query_param = $('#musician_query_score').val(); + var query_param = $musicianQueryScore.val(); if (query_param !== null) { - queryString += "score_limit=" + query_param + '&'; + options['score_limit'] = query_param; } - rest.searchMusicians(queryString) + + var distance = $musicianDistance.val(); + if (distance !== null) { + options['distance'] = distance; + } + + if(searchLocationOverride) { + options['country'] = searchLocationOverride.countrycode; + options['region'] = searchLocationOverride.regioncode; + options['city'] = searchLocationOverride.city; + } + + $spinner.show(); + $refreshBtn.addClass('disabled') + currentRequest = rest.searchMusicians(options) .done(afterLoadMusicians) - .fail(app.ajaxError) - .always(function() {$spinner.hide()}) + .fail(function(jqXHR) { + if(jqXHR.status === 0 && jqXHR.statusText === 'abort') { + // do nothing + } + else { + app.ajaxError(arguments); + } + }) + .always(function() {currentRequest = null; $spinner.hide(); $refreshBtn.removeClass('disabled')}) + } + + function abortCurrentRequest() { + if(currentRequest) { + currentRequest.abort() + currentRequest = null; + $spinner.hide(); + $refreshBtn.removeClass('disabled') + } } function refreshDisplay() { + abortCurrentRequest(); clearResults(); + setupSearch(); search(); } + // user clicked refresh + function manualRefresh() { + if(!$refreshBtn.is('.disabled')) { + refreshDisplay(); + } + return false; + } + + function changeSearchLocation() { + app.layout.showDialog('change-search-location').one(EVENTS.DIALOG_CLOSED, function(e, data) { + if(data.canceled) { + // do nothing + } + else { + searchLocationOverride = data.result; + displayUserCity(); + refreshDisplay(); + } + }) + + return false; + } + + function displayUserCity() { + app.user().done(function(user) { + var location = searchLocationOverride || user.location; + var unknown = 'unknown location'; + var result = unknown; + if(location) { + var region = location['region'] ? location['region'] : location['regioncode'] + if(!region) { region = 'n/a'; } + var city = location['city']; + result = city + ', ' + region; + } + $musicianFilterCity.text(result); + }) + } + function afterLoadMusicians(mList) { + did_show_musician_page = true; + // display the 'no musicians' banner if appropriate var $noMusiciansFound = $('#musicians-none-found'); musicianList = mList; - - // @FIXME: This needs to pivot on musicianList.musicians.length - if (musicianList.length == 0) { + if (musicianList.musicians.length == 0) { $noMusiciansFound.show(); musicians = []; } else { $noMusiciansFound.hide(); - musicians = musicianList['musicians']; + musicians = musicianList.musicians; if (!(typeof musicians === 'undefined')) { - $('#musician-filter-city').text(musicianList['city']); - if (0 == page_count) { + if (-1 == page_count) { page_count = musicianList['page_count']; } renderMusicians(); @@ -102,7 +182,6 @@ function renderMusicians() { var ii, len; var mTemplate = $('#template-find-musician-row').html(); - var fTemplate = $('#template-musician-follow-info').html(); var aTemplate = $('#template-musician-action-btns').html(); var mVals, musician, renderings = ''; var instr_logos, instr; @@ -122,19 +201,6 @@ } instr_logos += ''; } - follows = ''; - followVals = {}; - for (var jj = 0, ilen = musician['followings'].length; jj < ilen; jj++) { - aFollow = musician['followings'][jj]; - followVals = { - user_id: aFollow.user_id, - musician_name: aFollow.name, - profile_url: '/client#/profile/' + aFollow.user_id, - avatar_url: context.JK.resolveAvatarUrl(aFollow.photo_url) - }; - follows += context.JK.fillTemplate(fTemplate, followVals); - if (2 == jj) break; - } var actionVals = { profile_url: "/client#/profile/" + musician.id, friend_class: 'button-' + (musician['is_friend'] ? 'grey' : 'orange'), @@ -147,9 +213,12 @@ }; var musician_actions = context.JK.fillTemplate(aTemplate, actionVals); - var full_score = musician['full_score']; + var latencyBadge = context._.template( + $templateAccountSessionLatency.html(), + $.extend(sessionUtils.createLatency(musician), musician), + {variable: 'data'} + ); - var scoreInfo = sessionUtils.scoreInfo(full_score, false) mVals = { avatar_url: context.JK.resolveAvatarUrl(musician.photo_url), profile_url: "/client#/profile/" + musician.id, @@ -162,12 +231,9 @@ recording_count: musician['recording_count'], session_count: musician['session_count'], musician_id: musician['id'], - musician_follow_template: follows, musician_action_template: musician_actions, - musician_one_way_score: score_to_text(full_score), - musician_score_color: scoreInfo.icon_name, - musician_score_color_alt: scoreInfo.description, - latency_style: scoreInfo.latency_style + latency_badge: latencyBadge, + musician_first_name: musician['first_name'] }; var $rendering = $(context.JK.fillTemplate(mTemplate, mVals)) @@ -178,9 +244,10 @@ context.JK.helpBubble($('.friend-count', $rendering), 'musician-friend-count', {}, options); context.JK.helpBubble($('.recording-count', $rendering), 'musician-recording-count', {}, options); context.JK.helpBubble($('.session-count', $rendering), 'musician-session-count', {}, options); - helpBubble.scoreBreakdown($('.score-count', $rendering), false, full_score, myAudioLatency, musician['audio_latency'], musician['score'], scoreOptions); + helpBubble.scoreBreakdown($('.latency', $rendering), false, musician['full_score'], myAudioLatency, musician['audio_latency'], musician['score'], scoreOptions); $results.append($rendering); + $rendering.find('.biography').dotdotdot(); } @@ -195,7 +262,10 @@ } function afterShow(data) { - refreshDisplay(); + if(!did_show_musician_page) { + // cache page because query is slow, and user may have paginated a bunch + refreshDisplay(); + } } function clearResults() { @@ -203,7 +273,23 @@ $('#musician-filter-results .musician-list-result').remove(); $endMusicianList.hide(); page_num = 1; - page_count = 0; + page_count = -1; + } + + function setupSearch() { + var orderby = $('#musician_order_by').val(); + if(orderby == 'distance') { + $musicianSearchCity.show(); + $musicianDistance.closest('.easydropdown-wrapper').show(); + $musicianQueryScore.closest('.easydropdown-wrapper').hide(); + $musicianLatencyOrDistanceLabel.text('Distance:') + } + else { + $musicianSearchCity.hide(); + $musicianDistance.closest('.easydropdown-wrapper').hide(); + $musicianQueryScore.closest('.easydropdown-wrapper').show(); + $musicianLatencyOrDistanceLabel.text('Latency:') + } } function friendMusician(evt) { @@ -267,11 +353,18 @@ function events() { - $('#musician_query_score').change(refreshDisplay); + $musicianQueryScore.change(refreshDisplay); $('#musician_instrument').change(refreshDisplay); $('#musician_order_by').change(refreshDisplay); + $musicianDistance.change(refreshDisplay); + $musicianChangeFilterCity.click(changeSearchLocation); + $refreshBtn.click(manualRefresh) $('#musician-filter-results').closest('.content-body-scroller').bind('scroll', function () { + + // do not check scroll when filling out initial results, which we can tell if page_count == -1 + if(page_count == -1) {return}; + if ($(this).scrollTop() + $(this).innerHeight() >= $(this)[0].scrollHeight) { if (page_num < page_count) { page_num += 1; @@ -298,8 +391,17 @@ $results = $screen.find('#musician-filter-results'); $spinner = $screen.find('.paginate-wait') $endMusicianList = $screen.find('#end-of-musician-list') - + $templateAccountSessionLatency = $("#template-account-session-latency") + $musicianSearchCity = $('#musician-search-city'); + $musicianFilterCity = $('#musician-filter-city'); + $musicianChangeFilterCity = $('#musician-change-filter-city'); + $musicianQueryScore = $('#musician_query_score'); + $musicianDistance = $('#musician_distance'); + $musicianLatencyOrDistanceLabel = $screen.find('.latency-or-distance') + $refreshBtn = $screen.find('.btn-refresh-entries'); events(); + setupSearch(); + displayUserCity(); } this.initialize = initialize; diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js index 936ecd116..08c153eb8 100644 --- a/web/app/assets/javascripts/jam_rest.js +++ b/web/app/assets/javascripts/jam_rest.js @@ -1189,13 +1189,10 @@ }); } - function searchMusicians(queryString) { - // squelch nulls and undefines - queryString = !!queryString ? queryString : ""; - + function searchMusicians(query) { return $.ajax({ type: "GET", - url: "/api/search.json?" + queryString + url: "/api/search.json?" + $.param(query) }); } diff --git a/web/app/assets/javascripts/jamkazam.js b/web/app/assets/javascripts/jamkazam.js index 046fffa2a..08d79d34a 100644 --- a/web/app/assets/javascripts/jamkazam.js +++ b/web/app/assets/javascripts/jamkazam.js @@ -294,6 +294,16 @@ context.location = url; } + this.updateUserModel = function(userUpdateData) { + + var update = rest.updateUser(userUpdateData) + update.done(function() { + logger.debug("updating user info") + userDeferred = update; // update the global user object if this succeeded + }) + return update; + } + // call .done/.fail on this to wait for safe user data this.user = function() { return userDeferred; diff --git a/web/app/assets/javascripts/landing/landing.js b/web/app/assets/javascripts/landing/landing.js index caf518ddb..fe9765444 100644 --- a/web/app/assets/javascripts/landing/landing.js +++ b/web/app/assets/javascripts/landing/landing.js @@ -1,3 +1,4 @@ +//= require bind-polyfill //= require jquery //= require jquery.monkeypatch //= require jquery_ujs diff --git a/web/app/assets/javascripts/layout.js b/web/app/assets/javascripts/layout.js index ea161f7e3..6abce7e89 100644 --- a/web/app/assets/javascripts/layout.js +++ b/web/app/assets/javascripts/layout.js @@ -427,15 +427,40 @@ return false; } - function closeDialog(dialog) { + function cancel(evt) { + var $target = $(evt.currentTarget).closest('[layout]'); + var layoutId = $target.attr('layout-id'); + var isDialog = ($target.attr('layout') === 'dialog'); + if (isDialog) { + cancelDialog(layoutId); + } else { + // ? + logger.warn("unable to handle cancel layout-action for %o", $target) + } + return false; + } + + function cancelDialog(dialog) { + logger.debug("cancelling dialog: " + dialog); + var $dialog = $('[layout-id="' + dialog + '"]'); + var result = dialogEvent(dialog, 'onCancel'); + if(result !== false) { + closeDialog(dialog, true); + } + else { + logger.debug("dialog refused cancel"); + } + } + + function closeDialog(dialog, canceled) { logger.debug("closing dialog: " + dialog); var $dialog = $('[layout-id="' + dialog + '"]'); dialogEvent(dialog, 'beforeHide'); var $overlay = $('.dialog-overlay'); unstackDialogs($overlay); $dialog.hide(); - $dialog.triggerHandler(EVENTS.DIALOG_CLOSED, {name: dialog, dialogCount: openDialogs.length, result: $dialog.data('result') }); - $(context).triggerHandler(EVENTS.DIALOG_CLOSED, {name: dialog, dialogCount: openDialogs.length, result: $dialog.data('result') }); + $dialog.triggerHandler(EVENTS.DIALOG_CLOSED, {name: dialog, dialogCount: openDialogs.length, result: $dialog.data('result'), canceled: canceled }); + $(context).triggerHandler(EVENTS.DIALOG_CLOSED, {name: dialog, dialogCount: openDialogs.length, result: $dialog.data('result'), canceled: canceled }); dialogEvent(dialog, 'afterHide'); } @@ -550,8 +575,23 @@ } } + // if no arguments passed, then see if any dialog is showing + // if string passed, see if dialog is showing (even if buried) of a given name function isDialogShowing() { - return openDialogs.length > 0; + if(arguments.length == 1) { + // user passed in dialog id + var dialogId = arguments[0]; + context._.each(openDialogs, function(dialog) { + if($(dialog).attr('layout-id') == dialogId) { + return true; + } + }) + return false; + } + else { + // user passed in nothing + return openDialogs.length > 0; + } } function currentDialog() { @@ -690,7 +730,7 @@ // var step = 0; //setWizardStep(step); //wizardShowFunctions[step](); - showDialog('gear-wizard'); + return showDialog('gear-wizard'); } function setWizardStep(targetStepId) { @@ -718,6 +758,22 @@ context.JK.GA.virtualPageView(location.pathname + location.search + location.hash); } + function onHandleKey(e) { + + if (e.keyCode == 27 /** esc */) + { + if(isDialogShowing()) { + var $dialog = currentDialog(); + if(!$dialog) { + logger.error("unable to find current dialog on ESC"); + return; + } + + cancelDialog($dialog.attr('layout-id')); + } + } + } + function handleDialogState() { var rawDialogState = $.cookie('dialog_state'); try { @@ -756,11 +812,13 @@ }); $('body').on('click', '[layout-link]', linkClicked); $('[layout-action="close"]').on('click', close); + $('[layout-action="cancel"]').on('click', cancel); $('[layout-sidebar-expander]').on('click', toggleSidebar); $('[layout-panel="expanded"] [layout-panel="header"]').on('click', panelHeaderClicked); $('[layout-wizard-link]').on('click', wizardLinkClicked); $('[tab-target]').on('click', tabClicked); $(context).on('hashchange', trackLocationChange); + $(document).keyup(onHandleKey); } // public functions @@ -960,6 +1018,7 @@ } this.closeDialog = closeDialog; + this.cancelDialog = cancelDialog; this.handleDialogState = handleDialogState; this.queueDialog = queueDialog; diff --git a/web/app/assets/javascripts/selectLocation.js b/web/app/assets/javascripts/selectLocation.js new file mode 100644 index 000000000..4d5b36711 --- /dev/null +++ b/web/app/assets/javascripts/selectLocation.js @@ -0,0 +1,271 @@ +(function (context, $) { + + "use strict"; + + context.JK = context.JK || {}; + + context.JK.SelectLocation = Class.extend({ + + init: function ($countries, $regions, $cities, app) { + this.api = context.JK.Rest(); + this.logger = context.JK.logger; + this.loadingCitiesData = false; + this.loadingRegionsData = false; + this.loadingCountriesData = false; + this.nilOptionStr = ''; + this.nilOptionText = 'n/a'; + this.$countries = $countries; + this.$regions = $regions; + this.$cities = $cities; + this.app = app; + + $countries.on('change', function (evt) { + evt.stopPropagation(); + this.handleCountryChanged(); + return false; + }.bind(this)); + $regions.on('change', function (evt) { + evt.stopPropagation(); + this.handleRegionChanged(); + return false; + }.bind(this)); + }, + load: function (country, region, city) { + + this.country = country; + this.region = region; + this.city = city; + + if (!country) { + // this case shouldn't happen because sign up makes you pick a location. This is just 'in case', so that the UI is more error-resilient + this.logger.debug("user has no specified country: " + country) + country = 'US'; + } + + this.loadingCountriesData = true; + this.loadingRegionsData = true; + this.loadingCitiesData = true; + + // make the 3 slower requests, which only matter if the user wants to affect their ISP or location + + this.api.getCountries() + .done(function (countriesx) { + this.populateCountriesx(countriesx["countriesx"], country); + }.bind(this)) + .fail(this.app.ajaxError) + .always(function () { + this.loadingCountriesData = false; + }.bind(this)) + + if (country) { + this.api.getRegions({ country: country }) + .done(function (regions) { + this.populateRegions(regions["regions"], region); + }.bind(this)) + .fail(this.regionListFailure.bind(this)) + .always(function () { + this.loadingRegionsData = false; + }.bind(this)) + + if (region) { + this.api.getCities({ country: country, region: region }) + .done(function (cities) { + this.populateCities(cities["cities"], this.city) + }.bind(this)) + .fail(this.cityListFailure.bind(this)) + .always(function () { + this.loadingCitiesData = false; + }.bind(this)) + } + } + }, + handleCountryChanged: function () { + var selectedCountry = this.$countries.val() + var selectedRegion = this.$regions.val() + var cityElement = this.$cities + + this.updateRegionList(selectedCountry, this.$regions); + this.updateCityList(selectedCountry, null, cityElement); + }, + handleRegionChanged: function () { + var selectedCountry = this.$countries.val() + var selectedRegion = this.$regions.val() + var cityElement = this.$cities; + + this.updateCityList(selectedCountry, selectedRegion, cityElement); + }, + updateRegionList: function (selectedCountry, regionElement) { + // only update region + if (selectedCountry) { + // set city disabled while updating + regionElement.attr('disabled', true).easyDropDown('disable'); + this.loadingRegionsData = true; + + regionElement.children().remove() + regionElement.append($(this.nilOptionStr).text('loading...')) + + this.api.getRegions({ country: selectedCountry }) + .done(this.getRegionsDone.bind(this)) + .error(function (err) { + regionElement.children().remove() + regionElement.append($(this.nilOptionStr).text(this.nilOptionText)) + }.bind(this)) + .always(function () { + console.log("regions load: this.loadingRegionsData; " + this.loadingRegionsData) + this.loadingRegionsData = false; + }.bind(this)) + } + else { + regionElement.children().remove() + regionElement.append($(this.nilOptionStr).text(this.nilOptionText)) + } + }, + updateCityList: function (selectedCountry, selectedRegion, cityElement) { + + // only update cities + if (selectedCountry && selectedRegion) { + // set city disabled while updating + cityElement.attr('disabled', true).easyDropDown('disable'); + this.loadingCitiesData = true; + + cityElement.children().remove() + cityElement.append($(this.nilOptionStr).text('loading...')) + + this.api.getCities({ country: selectedCountry, region: selectedRegion }) + .done(this.getCitiesDone.bind(this)) + .error(function (err) { + cityElement.children().remove() + cityElement.append($(this.nilOptionStr).text(this.nilOptionText)) + }.bind(this)) + .always(function () { + this.loadingCitiesData = false; + }.bind(this)) + } + else { + cityElement.children().remove(); + cityElement.append($(this.nilOptionStr).text(this.nilOptionText)); + context.JK.dropdown(cityElement); + } + }, + + getCitiesDone: function (data) { + this.populateCities(data['cities'], this.city); + }, + getRegionsDone: function (data) { + this.populateRegions(data['regions'], this.region); + this.updateCityList(this.$countries.val(), this.$regions.val(), this.$cities); + }, + writeCountry: function (index, countryx) { + if (!countryx.countrycode) return; + + var option = $(this.nilOptionStr); + option.text(countryx.countryname); + option.attr("value", countryx.countrycode); + + if (countryx.countrycode == this.country) { + this.foundCountry = true; + } + + this.$countries.append(option); + }, + populateCountriesx: function (countriesx) { + + // countriesx has the format [{countrycode: "US", countryname: "United States"}, ...] + + this.foundCountry = false; + this.$countries.children().remove(); + + var nilOption = $(this.nilOptionStr); + nilOption.text(this.nilOptionText); + this.$countries.append(nilOption); + + $.each(countriesx, this.writeCountry.bind(this)); + + if (!this.foundCountry) { + this.logger.warn("user has no country in the database. user's country:" + this.country) + // in this case, the user has a country that is not in the database + // this can happen in a development/test scenario, but let's assume it can + // happen in production too. + var option = $(this.nilOptionStr); + option.text(this.country); + option.attr("value", this.country); + this.$countries.append(option); + } + + this.$countries.val(this.country); + this.$countries.attr("disabled", null).easyDropDown('enable'); + + context.JK.dropdown(this.$countries); + }, + + writeRegion: function (index, region) { + if (!region) return; + + var option = $(this.nilOptionStr) + option.text(region['name']) + option.attr("value", region['region']) + + this.$regions.append(option) + }, + + populateRegions: function (regions, userRegion) { + this.$regions.children().remove() + + var nilOption = $(this.nilOptionStr); + nilOption.text(this.nilOptionText); + this.$regions.append(nilOption); + + $.each(regions, this.writeRegion.bind(this)) + + this.$regions.val(userRegion) + this.$regions.attr("disabled", null).easyDropDown('enable'); + + context.JK.dropdown(this.$regions); + }, + + writeCity: function (index, city) { + if (!city) return; + + var option = $(this.nilOptionStr) + option.text(city) + option.attr("value", city) + + this.$cities.append(option) + }, + + populateCities: function (cities, userCity) { + this.$cities.children().remove(); + + var nilOption = $(this.nilOptionStr); + nilOption.text(this.nilOptionText); + this.$cities.append(nilOption); + + $.each(cities, this.writeCity.bind(this)) + + this.$cities.val(userCity) + this.$cities.attr("disabled", null).easyDropDown('enable'); + + context.JK.dropdown(this.$cities); + }, + + regionListFailure: function (jqXHR, textStatus, errorThrown) { + if (jqXHR.status == 422) { + this.logger.debug("no regions found for country: " + this.country); + } + else { + this.app.ajaxError(arguments); + } + }, + + cityListFailure: function (jqXHR, textStatus, errorThrown) { + if (jqXHR.status == 422) { + this.logger.debug("no cities found for country/region: " + this.country + "/" + this.region); + } + else { + this.app.ajaxError(arguments); + } + } + }); + +})(window, jQuery); + diff --git a/web/app/assets/javascripts/sessionList.js b/web/app/assets/javascripts/sessionList.js index c2103a66d..fc4120ef9 100644 --- a/web/app/assets/javascripts/sessionList.js +++ b/web/app/assets/javascripts/sessionList.js @@ -90,13 +90,16 @@ var $row = $(context.JK.fillTemplate($activeSessionTemplate.html(), sessionVals)); var $offsetParent = $(tbGroup).closest('.content'); - var $latencyBadge = $row.find('.latency-value'); - var full_score = $latencyBadge.attr('data-full-score') || null; - var internet_score = $latencyBadge.attr('data-internet-score') || null; - var audio_latency = $latencyBadge.attr('data-audio-latency') || null; - var latencyBadgeUserId = $latencyBadge.attr('data-user-id'); - var scoreOptions = {offsetParent: $offsetParent}; - helpBubble.scoreBreakdown($latencyBadge, context.JK.currentUserId == latencyBadgeUserId, full_score, myAudioLatency, audio_latency, internet_score, scoreOptions); + var $latencyBadges = $row.find('.latency-value'); + context._.each($latencyBadges, function(latencyBadge) { + var $latencyBadge = $(latencyBadge); + var full_score = $latencyBadge.attr('data-full-score') || null; + var internet_score = $latencyBadge.attr('data-internet-score') || null; + var audio_latency = $latencyBadge.attr('data-audio-latency') || null; + var latencyBadgeUserId = $latencyBadge.attr('data-user-id'); + var scoreOptions = {offsetParent: $offsetParent}; + helpBubble.scoreBreakdown($latencyBadge, context.JK.currentUserId == latencyBadgeUserId, full_score, myAudioLatency, audio_latency, internet_score, scoreOptions); + }); $(tbGroup).append($row); @@ -200,13 +203,16 @@ var $row = $(context.JK.fillTemplate($inactiveSessionTemplate.html(), sessionVals)); var $offsetParent = $(tbGroup).closest('.content'); - var $latencyBadge = $row.find('.latency-value'); - var full_score = $latencyBadge.attr('data-full-score') || null; - var internet_score = $latencyBadge.attr('data-internet-score') || null; - var audio_latency = $latencyBadge.attr('data-audio-latency') || null; - var latencyBadgeUserId = $latencyBadge.attr('data-user-id'); - var scoreOptions = {offsetParent: $offsetParent}; - helpBubble.scoreBreakdown($latencyBadge, context.JK.currentUserId == latencyBadgeUserId, full_score, myAudioLatency, audio_latency, internet_score, scoreOptions); + var $latencyBadges = $row.find('.latency-value'); + context._.each($latencyBadges, function(latencyBadge) { + var $latencyBadge = $(latencyBadge); + var full_score = $latencyBadge.attr('data-full-score') || null; + var internet_score = $latencyBadge.attr('data-internet-score') || null; + var audio_latency = $latencyBadge.attr('data-audio-latency') || null; + var latencyBadgeUserId = $latencyBadge.attr('data-user-id'); + var scoreOptions = {offsetParent: $offsetParent}; + helpBubble.scoreBreakdown($latencyBadge, context.JK.currentUserId == latencyBadgeUserId, full_score, myAudioLatency, audio_latency, internet_score, scoreOptions); + }) // initial page load diff --git a/web/app/assets/javascripts/sessionModel.js b/web/app/assets/javascripts/sessionModel.js index 054f1953e..82a8724b0 100644 --- a/web/app/assets/javascripts/sessionModel.js +++ b/web/app/assets/javascripts/sessionModel.js @@ -515,6 +515,15 @@ } function onWindowBackgrounded(type, text) { + app.user() + .done(function(userProfile) { + if(userProfile.show_whats_next && + window.location.pathname.indexOf(gon.client_path) == 0 && + !app.layout.isDialogShowing('getting-started')) { + app.layout.showDialog('getting-started'); + } + }) + if(!inSession()) return; // the window was closed; just attempt to nav to home, which will cause all the right REST calls to happen diff --git a/web/app/assets/javascripts/user_dropdown.js b/web/app/assets/javascripts/user_dropdown.js index 3242ec824..342e3dc7e 100644 --- a/web/app/assets/javascripts/user_dropdown.js +++ b/web/app/assets/javascripts/user_dropdown.js @@ -11,7 +11,7 @@ var rest = new JK.Rest(); var userMe = null; var invitationDialog = null; - var notYetShownWhatsNext = true; + var nowYetShownGettingStarted = true; function menuHoverIn() { $('ul.shortcuts', this).show(); @@ -77,7 +77,6 @@ // TODO - Setting global variable for local user. context.JK.userMe = r; updateHeader(); - handleWhatsNext(userMe); }); } @@ -86,17 +85,6 @@ showAvatar(); } - function handleWhatsNext(userProfile) { - if (notYetShownWhatsNext && gon.isNativeClient && userProfile.show_whats_next) { - notYetShownWhatsNext = false; - console.log("window.location.pathname", window.location.pathname, gon.client_path, window.location.pathname.indexOf(gon.client_url)); - if(window.location.pathname.indexOf(gon.client_path) == 0) { - app.layout.showDialog('whatsNext'); - } - - } - } - // initially show avatar function showAvatar() { var photoUrl = context.JK.resolveAvatarUrl(userMe.photo_url); diff --git a/web/app/assets/javascripts/utils.js b/web/app/assets/javascripts/utils.js index f6a12d05a..904b12084 100644 --- a/web/app/assets/javascripts/utils.js +++ b/web/app/assets/javascripts/utils.js @@ -115,7 +115,9 @@ }) - var helpText = context._.template($('#template-help-' + templateName).html(), data, { variable: 'data' }); + var $template = $('#template-help-' + templateName); + if($template.length == 0) throw "no template by the name " + templateName; + var helpText = context._.template($template.html(), data, { variable: 'data' }); var holder = $('
'); holder.append(helpText); context.JK.hoverBubble($element, helpText, options); @@ -136,7 +138,7 @@ if(!options) options = {}; options['trigger'] = 'none'; options['clickAnywhereToClose'] = false - if(!options['duration']) options['duration'] = 4000; + if(!options['duration']) options['duration'] = 6000; var existingTimer = $element.data("prodTimer"); if(existingTimer) { diff --git a/web/app/assets/javascripts/voiceChatHelper.js b/web/app/assets/javascripts/voiceChatHelper.js index e78aa00cb..e4d744178 100644 --- a/web/app/assets/javascripts/voiceChatHelper.js +++ b/web/app/assets/javascripts/voiceChatHelper.js @@ -77,6 +77,7 @@ context.JK.onBackendEvent(ALERT_NAMES.AUDIO_DEVICE_NOT_PRESENT, 'voice_chat_helper', onInvalidAudioDevice); registerVuCallbacks(); } + function beforeHide() { context.JK.offBackendEvent(ALERT_NAMES.AUDIO_DEVICE_NOT_PRESENT, 'voice_chat_helper', onInvalidAudioDevice); jamClient.FTUERegisterVUCallbacks('', '', ''); diff --git a/web/app/assets/javascripts/web/web.js b/web/app/assets/javascripts/web/web.js index 62a9e8d40..8e84fa00c 100644 --- a/web/app/assets/javascripts/web/web.js +++ b/web/app/assets/javascripts/web/web.js @@ -1,3 +1,4 @@ +//= require bind-polyfill //= require jquery //= require jquery.monkeypatch //= require jquery_ujs diff --git a/web/app/assets/stylesheets/client/ftue.css.scss b/web/app/assets/stylesheets/client/ftue.css.scss index 494e12532..ef81f1a39 100644 --- a/web/app/assets/stylesheets/client/ftue.css.scss +++ b/web/app/assets/stylesheets/client/ftue.css.scss @@ -475,42 +475,6 @@ div[layout-id="ftue3"] { } -#whatsnext-dialog { - - height:auto; - - .ftue-inner h2 { - font-weight:normal; - color:#ed3618; - margin-bottom:6px; - font-size:1.7em; - } - - .ftue-inner table td.whatsnext { - font-size:12px; - padding:10px; - width:50%; - font-weight:300; - } - - .ftue-inner table td.whatsnext { - font-size:12px; - padding:10px; - width:50%; - font-weight:300; - } - - .ftue-inner table a { - text-decoration:none; - } - - .ftue-inner table { - border-collapse:separate; - border-spacing: 20px; - } -} - - .ftue-inner { width:750px; padding:25px; diff --git a/web/app/assets/stylesheets/client/musician.css.scss b/web/app/assets/stylesheets/client/musician.css.scss index f2b92f8f8..7ace07308 100644 --- a/web/app/assets/stylesheets/client/musician.css.scss +++ b/web/app/assets/stylesheets/client/musician.css.scss @@ -1,28 +1,4 @@ -.filter-element { - float:left; - margin-left: 5px; - - &.wrapper { - margin-top: 5px; - &.right { - float: right; - > a { - margin-top: 3px; - } - } - } - - // @FIXME labeel is overriding from #session-controls. - &.desc { - margin-top: 3px; - padding-top: 3px; - } - - + .easydropdown-wrapper { - float:left; - margin-left:2px; - } -} +@import 'common'; #musicians-screen { @@ -35,6 +11,11 @@ } } + .btn-refresh-holder { + float:right; + margin-right:10px; + } + .paginate-wait { display:none; margin:auto; @@ -46,26 +27,144 @@ vertical-align:top; } } -} + .musician-avatar { + width:63px; + } -#musician-filter-results { - margin: 0 10px 0px 10px; -} + .musician-profile, .musician-stats { + width:150px; + overflow:hidden; + } -.musician-wrapper { - -ms-overflow-style: none; - overflow: initial; - height: 100%; - width: 100%; -} + .musician-stats { + margin-top:10px; + } + .musician-info { + margin-top: 12px; + margin-right:0; + } -.musician-list-result { - padding-top: 5px; - padding-right: 5px; - padding-left: 5px; - - table.musicians { - margin-top:12px; + .button-row { + float:none; + } + + .latency-holder { + position:absolute; + top: 53px; + width:100%; + text-align:center; + } + + .latency { + min-width: 50px; + display:inline-block; + padding:4px; + font-family:Arial, Helvetica, sans-serif; + font-weight:200; + font-size:11px; + text-align:center; + @include border-radius(2px); + color:white; + } + + .latency-unknown { + background-color:$latencyBadgeUnknown; + } + + .latency-unacceptable { + background-color:$latencyBadgeUnacceptable; + } + + .latency-good { + background-color:$latencyBadgeGood; + } + + .latency-fair{ + background-color:$latencyBadgeFair; + } + + .latency-poor { + background-color:$latencyBadgePoor; + } + + /** + .latency { + font-size: 15px; + height: 16px; + padding: 3px; + width: 100%; + @include border-radius(2px); + width:130px; + position:absolute; + top: 63px; + } +*/ + .biography { + height:73px; + } + + .musician-wrapper { + -ms-overflow-style: none; + overflow: initial; + height: 100%; + width: 100%; + } + + .musician-latency { + width: 136px; + height:100px; + margin-right:35px; + text-align:center; + position:relative; + } + + .latency-help { + margin-top:8px; + line-height:14px; + } + + .musician-list-result { + padding-top: 5px; + padding-right: 5px; + padding-left: 5px; + height: 123px; + + table.musicians { + margin-top:12px; + } + } + + #musician-filter-results { + margin: 0 10px 0px 10px; } } + +.filter-element { + float:left; + margin-left: 5px; + + &.wrapper { + margin-top: 5px; + &.right { + float: right; + > a { + margin-top: 3px; + } + } + } + + // @FIXME labeel is overriding from #session-controls. + &.desc { + margin-top: 3px; + padding-top: 3px; + } + + + .easydropdown-wrapper { + float:left; + margin-left:2px; + } +} + + + diff --git a/web/app/assets/stylesheets/client/screen_common.css.scss b/web/app/assets/stylesheets/client/screen_common.css.scss index 0e7252a72..30cc32a06 100644 --- a/web/app/assets/stylesheets/client/screen_common.css.scss +++ b/web/app/assets/stylesheets/client/screen_common.css.scss @@ -185,6 +185,18 @@ small, .small {font-size:11px;} text-decoration:none; line-height:12px; text-align:center; + + &.disabled { + background-color: transparent; + border: solid 1px #868686; + outline: solid 2px transparent; + color:#ccc; + } + + &.disabled:hover { + background-color: #515151; + color:#ccc; + } } .button-grey:hover { @@ -215,8 +227,6 @@ small, .small {font-size:11px;} } &.disabled:hover { - //background-color:darken(#f16750, 20%); - //color:#FFF; background-color: #515151; color:#ccc; } diff --git a/web/app/assets/stylesheets/client/sessionList.css.scss b/web/app/assets/stylesheets/client/sessionList.css.scss index f6dc3c46d..1fb27a134 100644 --- a/web/app/assets/stylesheets/client/sessionList.css.scss +++ b/web/app/assets/stylesheets/client/sessionList.css.scss @@ -3,8 +3,6 @@ table.findsession-table, table.local-recordings, #account-session-detail { - - .latency-unacceptable { width: 50px; height: 10px; diff --git a/web/app/assets/stylesheets/dialogs/changeSearchLocationDialog.css.scss b/web/app/assets/stylesheets/dialogs/changeSearchLocationDialog.css.scss new file mode 100644 index 000000000..9412b9133 --- /dev/null +++ b/web/app/assets/stylesheets/dialogs/changeSearchLocationDialog.css.scss @@ -0,0 +1,49 @@ +@import "client/common"; + +#change-search-location-dialog { + + height:300px; + min-height:300px; + width:390px; + min-width:390px; + + form { + width:100%; + @include border_box_sizing; + + .column { + + float:left; + @include border_box_sizing; + + &:nth-of-type(1) { + width:100%; + } + } + } + + .field{ + margin-bottom:5px; + } + a.reset-location { + margin: 5px 0 0 130px; + font-size:12px; + } + + label { + display:inline-block; + width:130px; + line-height:26px; + vertical-align:top; + } + + .hint { + font-size:16px; + line-height:18px; + margin-bottom:20px; + } + + .easydropdown { + width:150px; + } +} diff --git a/web/app/assets/stylesheets/dialogs/gettingStartDialog.css.scss b/web/app/assets/stylesheets/dialogs/gettingStartDialog.css.scss new file mode 100644 index 000000000..9e768b3e2 --- /dev/null +++ b/web/app/assets/stylesheets/dialogs/gettingStartDialog.css.scss @@ -0,0 +1,141 @@ +@import "client/common"; + +#whatsnext-dialog, #getting-started-dialog { + + height:auto; + width:auto; + + .ftue-inner h2 { + font-weight:normal; + color:#ed3618; + margin-bottom:6px; + font-size:1.7em; + } + + .icheckbox_minimal { + display:inline-block; + position:relative; + top:3px; + margin-right:3px; + + } + + .show-getting-started { + position:absolute; + margin-left:-280px; + right:50%; + top:-2px; + width:280px; + color:rgb(170, 170, 170); + + span { + font-size:15px; + } + } + + .close-btn { + position:relative; + } + + .title { + width:97%; + margin:20px 1.5%; + + } + + .row { + font-size:12px; + font-weight:300; + margin:10px 0; + @include border_box_sizing; + + .column { + @include border_box_sizing; + width:47%; + float:left; + background-color:black; + padding: 10px; + height:140px; + margin: 0 1.5%; + } + + &.full { + .column { + width:97%; + margin:0 1.5%; + height:78px; + } + } + + &.find-connect { + .column { + height:128px; + } + } + + &.setup-gear { + .action-button { + float:right; + margin-top:2px; + } + } + + &.learn-more { + height:80px; + margin-bottom:20px; + + .blurb a { + margin-left:5px; + } + } + + &.dialog-buttons { + margin-top:35px; + } + + .action-button { + margin-top:10px; + text-align:center; + } + + .blurb { + line-height:1.3em; + } + .buttons { + width:100%; + margin:0 auto; + text-align:center; + } + + .social-buttons { + text-align:center; + width:100; + margin:0 auto; + height:24px; + line-height:24px; + vertical-align:middle; + + a { + vertical-align: middle; + line-height:24px; + height:24px; + margin:5px; + } + + span { + vertical-align: top; + line-height:24px; + height:24px; + } + } + } + + .column { + @include border_box_sizing; + } + + + .ftue-inner table a { + text-decoration:none; + } +} \ No newline at end of file diff --git a/web/app/assets/stylesheets/dialogs/joinTestSession.css.scss b/web/app/assets/stylesheets/dialogs/joinTestSession.css.scss new file mode 100644 index 000000000..9c7c8f3c2 --- /dev/null +++ b/web/app/assets/stylesheets/dialogs/joinTestSession.css.scss @@ -0,0 +1,26 @@ +@import "client/common"; + +#join-test-session-dialog{ + + width:600px; + min-height:inherit; + + em { + font-style:italic; + } + .title { + font-weight:normal; + color:#ed3618; + margin-bottom:6px; + font-size:1.7em; + } + + .hint { + margin: 20px 0; + line-height:1.3em; + } + + .buttons { + text-align:right; + } +} \ No newline at end of file diff --git a/web/app/assets/stylesheets/dialogs/launchAppDialog.css.scss b/web/app/assets/stylesheets/dialogs/launchAppDialog.css.scss index c05bb2313..a4dc90196 100644 --- a/web/app/assets/stylesheets/dialogs/launchAppDialog.css.scss +++ b/web/app/assets/stylesheets/dialogs/launchAppDialog.css.scss @@ -10,7 +10,7 @@ line-height:1em; } - .buttons { + .launch-buttons { margin:20px 0; } } \ No newline at end of file diff --git a/web/app/assets/stylesheets/dialogs/whatsNextDialog.css.scss b/web/app/assets/stylesheets/dialogs/whatsNextDialog.css.scss deleted file mode 100644 index 8ae323ed3..000000000 --- a/web/app/assets/stylesheets/dialogs/whatsNextDialog.css.scss +++ /dev/null @@ -1,8 +0,0 @@ -#whatsnext-dialog { - .icheckbox_minimal { - display:inline-block; - position:relative; - top:3px; - margin-right:3px; - } -} \ No newline at end of file diff --git a/web/app/controllers/api_search_controller.rb b/web/app/controllers/api_search_controller.rb index 3d57aeffc..dfe2fc322 100644 --- a/web/app/controllers/api_search_controller.rb +++ b/web/app/controllers/api_search_controller.rb @@ -7,15 +7,10 @@ class ApiSearchController < ApiController def index if 1 == params[Search::PARAM_MUSICIAN].to_i || 1 == params[Search::PARAM_BAND].to_i - # puts "================== params #{params.to_s}" query = params.clone query[:remote_ip] = request.remote_ip if 1 == query[Search::PARAM_MUSICIAN].to_i - clientid = query[:clientid] - conn = (clientid ? Connection.where(client_id: clientid, user_id: current_user.id).first : nil) - # puts "================== query #{query.inspect}" - @search = Search.musician_filter(query, current_user, conn) - # puts "================== search #{@search.inspect}" + @search = Search.musician_filter(query, current_user) else @search = Search.band_filter(query, current_user) end diff --git a/web/app/controllers/api_users_controller.rb b/web/app/controllers/api_users_controller.rb index d8435722e..ddfcf4630 100644 --- a/web/app/controllers/api_users_controller.rb +++ b/web/app/controllers/api_users_controller.rb @@ -668,7 +668,7 @@ class ApiUsersController < ApiController def udp_reachable Connection.transaction do - @connection = Connection.find_by_client_id(params[:client_id]) + @connection = Connection.find_by_client_id!(params[:client_id]) @connection.udp_reachable = params[:udp_reachable] @connection.save respond_with_model(@connection) diff --git a/web/app/views/api_search/index.rabl b/web/app/views/api_search/index.rabl index e5c8c446d..0f3479db6 100644 --- a/web/app/views/api_search/index.rabl +++ b/web/app/views/api_search/index.rabl @@ -34,9 +34,6 @@ if @search.musicians_text_search? end if @search.musicians_filter_search? - node :city do |user| - current_user.try(:location) - end node :page_count do |foo| @search.page_count diff --git a/web/app/views/api_users/show.rabl b/web/app/views/api_users/show.rabl index 5dc6f0d02..0c3433959 100644 --- a/web/app/views/api_users/show.rabl +++ b/web/app/views/api_users/show.rabl @@ -12,6 +12,10 @@ end if @user == current_user attributes :email, :original_fpfile, :cropped_fpfile, :crop_selection, :session_settings, :show_whats_next, :subscribe_email, :auth_twitter, :new_notifications + node :location do |user| + geoiplocation = current_user.geoiplocation + geoiplocation.info if geoiplocation + end elsif current_user node :is_friend do |uu| diff --git a/web/app/views/clients/_account_audio_profile.html.erb b/web/app/views/clients/_account_audio_profile.html.erb index c8aacd7bc..8bfaf2ba2 100644 --- a/web/app/views/clients/_account_audio_profile.html.erb +++ b/web/app/views/clients/_account_audio_profile.html.erb @@ -1,5 +1,5 @@ -
+
diff --git a/web/app/views/clients/_account_session_detail.html.haml b/web/app/views/clients/_account_session_detail.html.haml index fed767c60..7ed0bed4f 100644 --- a/web/app/views/clients/_account_session_detail.html.haml +++ b/web/app/views/clients/_account_session_detail.html.haml @@ -158,6 +158,7 @@ %a{href: "#", 'user-id' => "{{data.user_id}}", 'hoveraction' => "musician", class: 'avatar-tiny'} %img{src: "{{data.avatar_url}}"} +// also used by musicians page %script{type: 'text/template', id: 'template-account-session-latency'} .latency{class: "{{data.latency_style}}", 'data-user-id' => "{{data.id}}", 'data-audio-latency' => "{{data.audio_latency || ''}}", 'data-full-score' => "{{data.full_score || ''}}", 'data-internet-score' => "{{data.internet_score || ''}}"} {{data.latency_text}} diff --git a/web/app/views/clients/_help.html.erb b/web/app/views/clients/_help.html.erb index be06b79c0..b17acb042 100644 --- a/web/app/views/clients/_help.html.erb +++ b/web/app/views/clients/_help.html.erb @@ -113,3 +113,9 @@ + + diff --git a/web/app/views/clients/_musicians.html.erb b/web/app/views/clients/_musicians.html.erb index 12cbcfb77..60279346f 100644 --- a/web/app/views/clients/_musicians.html.erb +++ b/web/app/views/clients/_musicians.html.erb @@ -13,7 +13,9 @@ <%= content_tag(:div, :class => 'content-body-scroller') do -%> <%= content_tag(:div, :class => 'content-wrapper musician-wrapper') do -%> <%= content_tag(:div, '', :id => 'musician-filter-results', :class => 'filter-results') %> -
Fetching more results...
+
Fetching more results... +
+
<%= content_tag(:div, 'No more results.', :class => 'end-of-list', :id => 'end-of-musician-list') %> <% end -%> <% end -%> @@ -27,64 +29,55 @@ - - + \ No newline at end of file diff --git a/web/app/views/clients/_web_filter.html.erb b/web/app/views/clients/_web_filter.html.erb index c0726caee..efb275a08 100644 --- a/web/app/views/clients/_web_filter.html.erb +++ b/web/app/views/clients/_web_filter.html.erb @@ -49,13 +49,14 @@ <% elsif :musician == filter_label %> - <%= content_tag(:div, 'Latency:', :class => 'filter-element desc') %> + <%= content_tag(:div, 'Latency:', :class => 'filter-element desc latency-or-distance') %> <%= content_tag(:div, :class => 'query-distance-params') do -%> <%= select_tag("musician_query_score", options_for_select(Search::M_SCORE_OPTS, Search::M_SCORE_DEFAULT), {:class => 'easydropdown'}) %> + <%= select_tag("musician_distance", options_for_select(Search::M_DISTANCE_OPTS, Search::M_DISTANCE_DEFAULT), {:class => 'easydropdown'}) %> <% end -%> - <%= content_tag(:div, :class => 'filter-element desc') do -%> - to <%= content_tag(:span, current_user ? current_user.current_city(request.remote_ip) : '', :id => "musician-filter-city") %> - <% end -%> +
+ to +
<% else %> @@ -75,6 +76,10 @@ + <% elsif :musician == filter_label %> +
+ REFRESH +
<% end %> <% end -%> \ No newline at end of file diff --git a/web/app/views/clients/index.html.erb b/web/app/views/clients/index.html.erb index 482659c9e..f310fa583 100644 --- a/web/app/views/clients/index.html.erb +++ b/web/app/views/clients/index.html.erb @@ -247,6 +247,9 @@ var testBridgeScreen = new JK.TestBridgeScreen(JK.app); testBridgeScreen.initialize(); + var changeSearchLocationDialog = new JK.ChangeSearchLocationDialog(JK.app); + changeSearchLocationDialog.initialize(); + // do a client update early check upon initialization JK.ClientUpdateInstance.check() diff --git a/web/app/views/dialogs/_acceptFriendRequestDialog.html.haml b/web/app/views/dialogs/_acceptFriendRequestDialog.html.haml index bedd461ad..b1fb6d0f8 100644 --- a/web/app/views/dialogs/_acceptFriendRequestDialog.html.haml +++ b/web/app/views/dialogs/_acceptFriendRequestDialog.html.haml @@ -8,7 +8,7 @@ .right.action-buttons %a.button-grey.btn-close-dialog{href:'#', 'layout-action' => 'close'} CLOSE - %a.button-grey.btn-cancel-dialog{href:'#', 'layout-action' => 'close'} CANCEL + %a.button-grey.btn-cancel-dialog{href:'#', 'layout-action' => 'cancel'} CANCEL %a.button-orange.btn-accept-friend-request{href:'#'} ACCEPT %script{type: 'text/template', id: 'template-friend-request-not-friends'} diff --git a/web/app/views/dialogs/_audioProfileInvalidDialog.html.slim b/web/app/views/dialogs/_audioProfileInvalidDialog.html.slim index b4feb3753..6131637b4 100644 --- a/web/app/views/dialogs/_audioProfileInvalidDialog.html.slim +++ b/web/app/views/dialogs/_audioProfileInvalidDialog.html.slim @@ -35,7 +35,7 @@ li = 'Input device is not connected' .buttons .left - a.button-grey.btnCancel CANCEL + a.button-grey.btnCancel layout-action="cancel" CANCEL a.button-grey.btnConfigureGear GO TO AUDIO GEAR SCREEN a.button-grey.btnRestartApplication RESTART APPLICATION .right diff --git a/web/app/views/dialogs/_changeSearchLocationDialog.html.slim b/web/app/views/dialogs/_changeSearchLocationDialog.html.slim new file mode 100644 index 000000000..bc14591a6 --- /dev/null +++ b/web/app/views/dialogs/_changeSearchLocationDialog.html.slim @@ -0,0 +1,24 @@ +.dialog.dialog-overlay-sm layout='dialog' layout-id='change-search-location' id='change-search-location-dialog' + .content-head + h1 change search location + .dialog-inner + .hint + | Specify which location you want to search from. + form action='post' + .column + .field purpose="country" + label for="country" Country: + select name="country" + .field purpose="region" + label for="region" State/Province: + select name="region" disabled="disabled" + .field purpose="city" + label for="city" City: + select name="city" disabled="disabled" + a href="#" class="reset-location" Reset to my current location + br clear='all' + .buttons + .right + a.button-grey class='btnCancel' layout-action='cancel' CANCEL + a.button-orange class='btnSave' SAVE + diff --git a/web/app/views/dialogs/_clientPreferencesDialog.html.slim b/web/app/views/dialogs/_clientPreferencesDialog.html.slim index 00cfe538d..e7d2285c6 100644 --- a/web/app/views/dialogs/_clientPreferencesDialog.html.slim +++ b/web/app/views/dialogs/_clientPreferencesDialog.html.slim @@ -13,6 +13,6 @@ br clear='all' .buttons .right - a.button-grey class='btnCancel' layout-action='close' CANCEL + a.button-grey class='btnCancel' layout-action='cancel' CANCEL a.button-orange class='btnSave' SAVE diff --git a/web/app/views/dialogs/_dialogs.html.haml b/web/app/views/dialogs/_dialogs.html.haml index b241776b4..28a85f986 100644 --- a/web/app/views/dialogs/_dialogs.html.haml +++ b/web/app/views/dialogs/_dialogs.html.haml @@ -24,4 +24,7 @@ = render 'dialogs/videoDialog' = render 'dialogs/friendSelectorDialog' = render 'dialogs/clientPreferencesDialog' -= render 'dialogs/audioProfileInvalidDialog' \ No newline at end of file += render 'dialogs/audioProfileInvalidDialog' += render 'dialogs/gettingStartedDialog' += render 'dialogs/joinTestSessionDialog' += render 'dialogs/changeSearchLocationDialog' diff --git a/web/app/views/dialogs/_edit_recording_dialog.html.haml b/web/app/views/dialogs/_edit_recording_dialog.html.haml index b1b944c4d..a34457f2d 100644 --- a/web/app/views/dialogs/_edit_recording_dialog.html.haml +++ b/web/app/views/dialogs/_edit_recording_dialog.html.haml @@ -18,7 +18,7 @@ %label{for: 'is_public'} Public Recording .buttons - %a.button-grey.cancel-btn CANCEL + %a.button-grey.cancel-btn {'layout-action' => 'cancel'} CANCEL %a.button-orange.delete-btn DELETE %a.button-orange.save-btn UPDATE %br{clear: 'all'} \ No newline at end of file diff --git a/web/app/views/dialogs/_gettingStartedDialog.html.slim b/web/app/views/dialogs/_gettingStartedDialog.html.slim new file mode 100644 index 000000000..9b34a11be --- /dev/null +++ b/web/app/views/dialogs/_gettingStartedDialog.html.slim @@ -0,0 +1,81 @@ +.dialog.whatsnext-overlay.ftue-overlay.tall layout="dialog" layout-id="getting-started" id="getting-started-dialog" + .content-head + h1 getting started + + .ftue-inner + .title + | Welcome to JamKazam! Here are the top things you should do to get the + most out of this service. Be sure to get through all of these for the best experience, but you can spread + them out over multiple visits! + + .row.full.setup-gear + .column + h2 SET UP GEAR + .blurb + span + .action-button + a.button-orange.setup-gear-btn layout-link="account/audio" SET UP GEAR + | Click SET UP GEAR to configure and test your audio gear and network connection. + Once you’ve completed this step, we’ll drop you into your first session, and you can explore the session + interface. + br clear="both" + .row + .column + h2 INVITE YOUR FRIENDS + .blurb + | Invite others to join JamKazam. You’ll be connected as friends, which makes it easier to get into sessions + together. And it will grow our community, which helps us as a young company. Click the icons below to + invite! + .social-buttons + a href="#" class="facebook-invite" + = image_tag "content/icon_facebook.png", {:align=>"absmiddle", :height => 24, :width => 24} + span Facebook + a href="#" class="email-invite" + = image_tag "content/icon_gmail.png", {:align=>"absmiddle", :height => 24, :width => 24} + span E-mail + a href="#" class="google-invite" + = image_tag "content/icon_google.png", {:align=>"absmiddle", :height => 26, :width => 26 } + span Google+ + .column + h2 CREATE A "REAL" SESSION + .blurb + | You can create a session to start immediately and hope others join, but this doesn’t work well. It’s better + to schedule a session and invite friends or the community to join you. Watch a video to learn how, then + schedule your first session! + .action-button + a.button-orange rel="external" href="https://www.youtube.com/watch?v=EZZuGcDUoWk" WATCH VIDEO + br clear="both" + .row.find-connect + .column + h2 FIND SESSIONS TO JOIN + .blurb + | In addition to creating your own sessions, it’s awesome to join others’ sessions. Watch this tutorial video + to learn about how to find and select good sessions to join. + .action-button + a.button-orange.setup-gear rel="external" href="https://www.youtube.com/watch?v=xWponSJo-GU" WATCH VIDEO + + .column + h2 CONNECT WITH MUSICIANS + .blurb + | To play more music, tap into our growing + community to connect with other musicians. Watch this video for tips on how to do this. + .action-button + a.button-orange rel="external" href="https://www.youtube.com/watch?v=xWponSJo-GU" WATCH VIDEO + br clear="both" + .row.full.learn-more + .column + h2 LEARN MORE ABOUT JAMKAZAM + .blurb + | There is a lot you can do with JamKazam, and more great features available every week. Check the + following link for a list of videos and other resources you can use to take advantage of everything that’s + available: + a rel="external" purpose="youtube-tutorials" href="http://www.youtube.com/channel/UC38nc9MMZgExJAd7ca3rkUA" JamKazam Tutorials & Resources + br clear="both" + .row.dialog-buttons + .buttons + .show-getting-started + input type="checkbox" id="show_getting_started" + span Don't show this again + a.close-btn href="#" class="button-orange" layout-action="close" + | CLOSE + br clear="both" \ No newline at end of file diff --git a/web/app/views/dialogs/_joinTestSessionDialog.html.slim b/web/app/views/dialogs/_joinTestSessionDialog.html.slim new file mode 100644 index 000000000..5855de33b --- /dev/null +++ b/web/app/views/dialogs/_joinTestSessionDialog.html.slim @@ -0,0 +1,21 @@ +.dialog layout='dialog' layout-id='join-test-session' id='join-test-session-dialog' + .content-head + h1 join test session + + .dialog-inner + .title + | VERIFY YOUR SETUP WITH A TEST SESSION + .hint + | You have set up your gear and verified your network, but does it + em  really  + | work? + br + br + | The way to find out is by joining a test session. + br + br + | This will familiarize you with how sessions work in JamKazam, and if you are lucky, someone else might even join in. + .buttons + a.button-grey layout-action="cancel" CANCEL + a.button-orange.join-test-session JOIN TEST SESSION + br \ No newline at end of file diff --git a/web/app/views/dialogs/_launchAppDialog.html.haml b/web/app/views/dialogs/_launchAppDialog.html.haml index e6d4cbf9f..8a6a30836 100644 --- a/web/app/views/dialogs/_launchAppDialog.html.haml +++ b/web/app/views/dialogs/_launchAppDialog.html.haml @@ -7,14 +7,14 @@ %script{type: 'text/template', id: 'template-attempt-launch'} %p {{data.messagePrefix}}, you must use the JamKazam application. - .right.buttons + .right.launch-buttons %a.button-grey.btn-cancel{href:'#', 'layout-action' => 'close'} CANCEL %a.button-orange.btn-launch-app{href:'{{data.launchUrl}}'} LAUNCH APP %script{type: 'text/template', id: 'template-unsupported-launch'} %p {{data.messagePrefix}}, you must use the JamKazam application. Please download and install the application if you have not done so already. - .right.buttons + .right.launch-buttons %a.button-grey.btn-cancel{href:'#', 'layout-action' => 'close'} CANCEL %a.button-orange.btn-go-to-download-page{href:'/downloads'} GO TO APP DOWNLOAD PAGE @@ -31,7 +31,7 @@ If the application is not running, then please %a.download-application{href: '/downloads'} download and install the application if you have not done so already, and then start it manually rather than using this web launcher. - .right.buttons + .right.launch-buttons %a.button-grey.btn-done{href:'#', 'layout-action' => 'close'} DONE %script{type: 'text/template', id: 'template-launch-unsuccessful'} @@ -44,6 +44,6 @@ If the application is not running, then please %a.download-application{href: '/downloads'} download and install the application if you have not done so already, and then start it manually rather than using this web launcher. - .right.buttons + .right.launch-buttons %a.button-grey.btn-done{href:'#', 'layout-action' => 'close'} CLOSE %a.button-orange.btn-go-to-download-page{href:'/downloads'} GO TO APP DOWNLOAD PAGE diff --git a/web/app/views/dialogs/_networkTestDialog.html.haml b/web/app/views/dialogs/_networkTestDialog.html.haml index 9d7e11ef5..0e680d32c 100644 --- a/web/app/views/dialogs/_networkTestDialog.html.haml +++ b/web/app/views/dialogs/_networkTestDialog.html.haml @@ -6,7 +6,7 @@ .clearall .buttons .left - %a.button-grey.btn-cancel{href:'#'} CANCEL + %a.button-grey.btn-cancel{href:'#', 'layout-action' => 'cancel'} CANCEL %a.button-grey.btn-help{rel: 'external', href: 'https://jamkazam.desk.com/customer/portal/articles/1599969-first-time-setup---step-6---test-your-network'} HELP .right %a.button-orange.btn-close{href:'#'} CLOSE \ No newline at end of file diff --git a/web/config/environments/test.rb b/web/config/environments/test.rb index cbaab4cb2..d71e9d0b3 100644 --- a/web/config/environments/test.rb +++ b/web/config/environments/test.rb @@ -14,6 +14,9 @@ SampleApp::Application.configure do # Log error messages when you accidentally call methods on nil config.whiny_nils = true + # useful when debugging a javascript problem... + config.assets.compress = false + # Show full error reports and disable caching config.consider_all_requests_local = true config.action_controller.perform_caching = false diff --git a/web/spec/factories.rb b/web/spec/factories.rb index 0d069a2cb..898c94527 100644 --- a/web/spec/factories.rb +++ b/web/spec/factories.rb @@ -29,6 +29,52 @@ FactoryGirl.define do admin true end + factory :austin_user do + first_name 'Austin' + sequence(:last_name) { |n| "#{n}" } + state 'TX' + city 'Austin' + last_jam_locidispid { austin_geoip[:locidispid] } + last_jam_addr { austin_ip } + end + + factory :dallas_user do + first_name 'Dallas' + sequence(:last_name) { |n| "#{n}" } + state 'TX' + city 'Dallas' + last_jam_locidispid { dallas_geoip[:locidispid] } + last_jam_addr { dallas_ip } + end + + factory :houston_user do + first_name 'Houston' + sequence(:last_name) { |n| "#{n}" } + state 'TX' + city 'Houston' + last_jam_locidispid { houston_geoip[:locidispid] } + last_jam_addr { houston_ip } + end + + factory :miami_user do + first_name 'Miami' + sequence(:last_name) { |n| "#{n}" } + state 'FL' + city 'Miami' + last_jam_locidispid { miami_geoip[:locidispid] } + last_jam_addr { miami_ip } + end + + factory :seattle_user do + first_name 'Seattle' + sequence(:last_name) { |n| "#{n}" } + state 'WA' + city 'Seattle' + last_jam_locidispid { seattle_geoip[:locidispid] } + last_jam_addr { seattle_ip } + end + + factory :band_musician do after(:create) do |user| band = FactoryGirl.create(:band) diff --git a/web/spec/features/gear_wizard_spec.rb b/web/spec/features/gear_wizard_spec.rb index a87e2d6e4..6edc6bd59 100644 --- a/web/spec/features/gear_wizard_spec.rb +++ b/web/spec/features/gear_wizard_spec.rb @@ -14,46 +14,15 @@ describe "Gear Wizard", :js => true, :type => :feature, :capybara_feature => tru it "success path" do FactoryGirl.create(:latency_tester) fast_signin user, '/client#/account/audio' - # step 1 - intro find("div.account-audio a[data-purpose='add-profile']").trigger(:click) - find('.btn-next').trigger(:click) - - # step 2 - select gear - find('.ftue-step-title', text: 'Select & Test Audio Gear') - should_not have_selector('.resync-status') # when you enter this step, - jk_select('Built-in', 'div[layout-wizard-step="1"] select.select-audio-input-device') - find('.btn-next.button-orange:not(.disabled)').trigger(:click) - - # step 3 - configure tracks - find('.ftue-step-title', text: 'Configure Tracks') - - # drag one input over to tracks area http://rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Element#drag_to-instance_method - input = first('.ftue-input') - track_slot = first('.track-target') - input.drag_to(track_slot) - - find('.btn-next.button-orange:not(.disabled)').trigger(:click) - - # step 4 - configure voice chat - find('.ftue-step-title', text: 'Configure Voice Chat') - find('.btn-next.button-orange:not(.disabled)').trigger(:click) - - # step 5 - configure direct monitoring - find('.ftue-step-title', text: 'Turn Off Direct Monitoring') - find('.btn-next.button-orange:not(.disabled)').trigger(:click) - - # step 6 - Test Router & Network - find('.ftue-step-title', text: 'Test Router & Network') - find('.button-orange.start-network-test').trigger(:click) - find('.user-btn', text: 'RUN NETWORK TEST ANYWAY').trigger(:click) - find('.button-orange.start-network-test') - find('.btn-next.button-orange:not(.disabled)').trigger(:click) - - # step 7 - Success - find('.ftue-step-title', text: 'Success!') - find('.btn-close.button-orange').trigger(:click) + walk_gear_wizard + # should see prompt afterwards about joining a test session + find('h1', text: 'join test session') + find('.join-test-session').trigger(:click) + # and should now be in session + find('h2', text: 'my tracks') end end diff --git a/web/spec/features/getting_started_dialog_spec.rb b/web/spec/features/getting_started_dialog_spec.rb new file mode 100644 index 000000000..43ce13201 --- /dev/null +++ b/web/spec/features/getting_started_dialog_spec.rb @@ -0,0 +1,93 @@ +require 'spec_helper' + +describe "Home Screen", :js => true, :type => :feature, :capybara_feature => true do + + subject { page } + + before(:all) do + Capybara.javascript_driver = :poltergeist + Capybara.current_driver = Capybara.javascript_driver + Capybara.default_wait_time = 10 + end + + let(:user) { FactoryGirl.create(:user, :show_whats_next => true) } + + + describe "in normal browser" do + before(:each) do + sign_in_poltergeist user + visit "/client" + should have_selector('h1', text: 'getting started') + end + + it "should show launch app dialog if clicked setup gear" do + find('#getting-started-dialog .setup-gear-btn').trigger('click') + should have_selector('p', text: 'To configure your audio gear, you must use the JamKazam application.') + end + end + + describe "in native client" do + + before(:each) do + sign_in_poltergeist user + emulate_client + visit "/client" + should have_selector('h1', text: 'getting started') + end + + describe "new user in the native view should see the getting started dialog" do + + it "should show gear page if clicked setup gear" do + find('#getting-started-dialog .setup-gear-btn').trigger('click') + should have_selector('h2', text: 'audio profiles:') + end + + describe "open invitation dialog for email" do + before(:each) do + find('#getting-started-dialog .email-invite').trigger(:click) + end + + it {should have_selector('label', text: 'Enter email address(es). If multiple addresses, separate with commas.')} + end + + + describe "launches youtube tutorial site" do + + it { + find("#getting-started-dialog a[purpose='youtube-tutorials']").trigger(:click) + page.driver.window_handles.last + page.within_window page.driver.window_handles.last do + should have_title('JamKazam - YouTube') + end + } + end + + + describe "close hides the screen" do + + it { + find('#getting-started-dialog a[layout-action="close"]').trigger(:click) + should have_no_selector('h1', text: 'getting started') + } + end + + describe "user can make prompt go away forever" do + + it { + find('#getting-started-dialog ins.iCheck-helper').trigger(:click) + find('#getting-started-dialog a[layout-action="close"]').trigger(:click) + + # needed because we poke the server with an updateUser call, but their is no indication in the UI that it's done + wait_for_ajax + emulate_client + sleep 1 + visit "/client" + wait_until_curtain_gone + should_not have_selector('h1', text: 'getting started') + } + end + + end + end +end + diff --git a/web/spec/features/musician_search_spec.rb b/web/spec/features/musician_search_spec.rb index e10ddf438..95c50f860 100644 --- a/web/spec/features/musician_search_spec.rb +++ b/web/spec/features/musician_search_spec.rb @@ -6,22 +6,21 @@ describe "Musician Search", :js => true, :type => :feature, :capybara_feature => let(:austin) { austin_geoip } let(:dallas) { dallas_geoip } - let(:user) {FactoryGirl.create(:user, last_jam_locidispid: austin_geoip[:locidispid], last_jam_addr: austin_ip)} - let(:user2) {FactoryGirl.create(:user, last_jam_locidispid: dallas_geoip[:locidispid], last_jam_addr: dallas_ip)} + let(:austin_user) { FactoryGirl.create(:austin_user) } + let(:dallas_user) { FactoryGirl.create(:dallas_user) } + let(:miami_user) { FactoryGirl.create(:miami_user) } + let(:seattle_user) { FactoryGirl.create(:seattle_user) } before(:each) do MaxMindManager.create_phony_database + User.delete_all + austin_user.touch + dallas_user.touch Score.delete_all Score.createx(austin_geoip[:locidispid], 'a', 1, dallas_geoip[:locidispid], 'a', 1, 10) - ActiveRecord::Base.logger.debug '====================================== begin ======================================' - sign_in_poltergeist user - visit "/client#/musicians" - end - - after(:each) do - ActiveRecord::Base.logger.debug '====================================== done ======================================' + fast_signin(austin_user, "/client#/musicians") end it "shows the musician search page" do @@ -49,13 +48,47 @@ describe "Musician Search", :js => true, :type => :feature, :capybara_feature => it "shows latency information correctly" do # this will try to show 5 latency badges. unknown, good, fair, poor, unacceptable. 'me' does not happen on this screen - user.last_jam_locidispid = austin[:locidispid] - user.save! + austin_user.last_jam_locidispid = austin[:locidispid] + austin_user.save! - verify_find_musician_score(nil, user, user2) - verify_find_musician_score(3, user, user2) - verify_find_musician_score(40, user, user2) - verify_find_musician_score(80, user, user2) - verify_find_musician_score(110, user, user2) + verify_find_musician_score(nil, austin_user, dallas_user) + verify_find_musician_score(3, austin_user, dallas_user) + verify_find_musician_score(40, austin_user, dallas_user) + verify_find_musician_score(80, austin_user, dallas_user) + verify_find_musician_score(110, austin_user, dallas_user) end + + it "shows search by distance" do + + # this test does a distance search with the austin user, then opens up the 'change search location' dialog, + # and changes the search distance + + miami_user.touch # no scores, but should still show + seattle_user.touch # no scores, but should still show + + wait_for_easydropdown('#musician_order_by') + jk_select('Distance', '#musician_order_by') + + find(".musician-list-result[data-musician-id='#{dallas_user.id}']:nth-child(1)") # only dallas is within range + + find('#musician-change-filter-city').trigger(:click) + + find('h1', text: 'change search location') # dialog should be showing + + # wait for it to finish populating + wait_for_easydropdown('#change-search-location-dialog select[name="country"]') + wait_for_easydropdown('#change-search-location-dialog select[name="region"]') + wait_for_easydropdown('#change-search-location-dialog select[name="city"]') + + jk_select('FL', '#change-search-location-dialog select[name="region"]') # this should be 'Florida', but our test data + # wait for the city to not be disabled as it reloads + expect(page).to_not have_selector('#change-search-location-dialog .field[purpose="city"] .easydropdown-wrapper.disabled') + jk_select('Miami', '#change-search-location-dialog select[name="city"]') + find('#change-search-location-dialog .btnSave').trigger(:click) + find('#musician-filter-city', text: "Miami, FL") + + find(".musician-list-result[data-musician-id='#{miami_user.id}']:nth-child(1)") # only miami is within range + + end + end diff --git a/web/spec/features/whats_next_spec.rb b/web/spec/features/whats_next_spec.rb deleted file mode 100644 index 9073e09ce..000000000 --- a/web/spec/features/whats_next_spec.rb +++ /dev/null @@ -1,75 +0,0 @@ -require 'spec_helper' - -describe "Home Screen", :js => true, :type => :feature, :capybara_feature => true do - - subject { page } - - before(:all) do - Capybara.javascript_driver = :poltergeist - Capybara.current_driver = Capybara.javascript_driver - Capybara.default_wait_time = 10 - end - - before(:each) do - sign_in_poltergeist user - emulate_client - visit "/client" - - end - - let(:user) { FactoryGirl.create(:user, :show_whats_next => true) } - - describe "new user in the native view should see the whats next dialog" do - - it { - should have_selector('h1', text: 'what\'s next?') - } - - describe "open invitation dialog for email" do - before(:each) do - find('#whatsnext-dialog .email-invite').trigger(:click) - end - - it {should have_selector('label', text: 'Enter email address(es). If multiple addresses, separate with commas.')} - end - - - describe "launches youtube tutorial site" do - - it { - find("#whatsnext-dialog a.orange[purpose='youtube-tutorials']").trigger(:click) - page.driver.window_handles.last - page.within_window page.driver.window_handles.last do - should have_title('JamKazam - YouTube') - end - } - end - - - describe "close hides the screen" do - - it { - find('#whatsnext-dialog a[layout-action="close"]').trigger(:click) - should have_no_selector('h1', text: 'what\'s next?') - } - end - - describe "user can make prompt go away forever" do - - it { - find('#whatsnext-dialog ins.iCheck-helper').trigger(:click) - find('#whatsnext-dialog a[layout-action="close"]').trigger(:click) - - # needed because we poke the server with an updateUser call, but their is no indication in the UI that it's done - wait_for_ajax - emulate_client - sleep 1 - visit "/client" - wait_until_curtain_gone - should_not have_selector('h1', text: 'what\'s next?') - } - end - - end -end - diff --git a/web/spec/javascripts/fixtures/location_dropdowns.html b/web/spec/javascripts/fixtures/location_dropdowns.html new file mode 100644 index 000000000..fc76a0b8c --- /dev/null +++ b/web/spec/javascripts/fixtures/location_dropdowns.html @@ -0,0 +1,20 @@ +
+ + +
+ +
+ + +
+ +
+ + +
\ No newline at end of file diff --git a/web/spec/javascripts/spec_helper.js b/web/spec/javascripts/spec_helper.js new file mode 100644 index 000000000..e3a81ec65 --- /dev/null +++ b/web/spec/javascripts/spec_helper.js @@ -0,0 +1,31 @@ +// Teaspoon includes some support files, but you can use anything from your own support path too. +// require support/jasmine-jquery-1.7.0 +// require support/jasmine-jquery-2.0.0 +// require support/sinon +// require support/your-support-file +// +// PhantomJS (Teaspoons default driver) doesn't have support for Function.prototype.bind, which has caused confusion. +// Use this polyfill to avoid the confusion. +//= require support/bind-poly +// +// Deferring execution +// If you're using CommonJS, RequireJS or some other asynchronous library you can defer execution. Call +// Teaspoon.execute() after everything has been loaded. Simple example of a timeout: +// +// Teaspoon.defer = true +// setTimeout(Teaspoon.execute, 1000) +// +// Matching files +// By default Teaspoon will look for files that match _spec.{js,js.coffee,.coffee}. Add a filename_spec.js file in your +// spec path and it'll be included in the default suite automatically. If you want to customize suites, check out the +// configuration in config/initializers/teaspoon.rb +// +// Manifest +// If you'd rather require your spec files manually (to control order for instance) you can disable the suite matcher in +// the configuration and use this file as a manifest. +// +// For more information: http://github.com/modeset/teaspoon +// +// You can require your own javascript files here. By default this will include everything in application, however you +// may get better load performance if you require the specific files that are being used in the spec that tests them. +//= require application diff --git a/web/spec/support/client_interactions.rb b/web/spec/support/client_interactions.rb index 0b422e431..641d47be8 100644 --- a/web/spec/support/client_interactions.rb +++ b/web/spec/support/client_interactions.rb @@ -123,4 +123,45 @@ end def close_websocket page.evaluate_script("window.JK.JamServer.close(true)") +end + +# does not launch it; expects that to have just been done +def walk_gear_wizard + # step 1 - intro + find('.btn-next').trigger(:click) + + # step 2 - select gear + find('.ftue-step-title', text: 'Select & Test Audio Gear') + should_not have_selector('.resync-status') # when you enter this step, + jk_select('Built-in', 'div[layout-wizard-step="1"] select.select-audio-input-device') + find('.btn-next.button-orange:not(.disabled)').trigger(:click) + + # step 3 - configure tracks + find('.ftue-step-title', text: 'Configure Tracks') + + # drag one input over to tracks area http://rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Element#drag_to-instance_method + input = first('.ftue-input') + track_slot = first('.track-target') + input.drag_to(track_slot) + + find('.btn-next.button-orange:not(.disabled)').trigger(:click) + + # step 4 - configure voice chat + find('.ftue-step-title', text: 'Configure Voice Chat') + find('.btn-next.button-orange:not(.disabled)').trigger(:click) + + # step 5 - configure direct monitoring + find('.ftue-step-title', text: 'Turn Off Direct Monitoring') + find('.btn-next.button-orange:not(.disabled)').trigger(:click) + + # step 6 - Test Router & Network + find('.ftue-step-title', text: 'Test Router & Network') + find('.button-orange.start-network-test').trigger(:click) + find('.user-btn', text: 'RUN NETWORK TEST ANYWAY').trigger(:click) + find('.button-orange.start-network-test') + find('.btn-next.button-orange:not(.disabled)').trigger(:click) + + # step 7 - Success + find('.ftue-step-title', text: 'Success!') + find('.btn-close.button-orange').trigger(:click) end \ No newline at end of file diff --git a/web/spec/support/maxmind.rb b/web/spec/support/maxmind.rb index b0b9c85e7..f5d2f83f4 100644 --- a/web/spec/support/maxmind.rb +++ b/web/spec/support/maxmind.rb @@ -163,29 +163,78 @@ end def score_location(a_locidispid, b_locidispid, latency) Score.createx(a_locidispid, 'anodeid', 1, b_locidispid, 'bnodeid', 1, latency, nil) end +def ip_from_num(num) + IPAddr.new(num, Socket::AF_INET).to_s +end def austin_ip - IPAddr.new(0x0FFFFFFF, Socket::AF_INET).to_s + IPAddr.new(austin_ip_as_num, Socket::AF_INET).to_s +end + +def austin_ip_as_num + 0x0FFFFFFF end def dallas_ip - IPAddr.new(0x1FFFFFFF, Socket::AF_INET).to_s + IPAddr.new(dallas_ip_as_num, Socket::AF_INET).to_s end +def dallas_ip_as_num + 0x1FFFFFFF +end + +def houston_ip + IPAddr.new(houston_ip_as_num, Socket::AF_INET).to_s +end + +def houston_ip_as_num + 0x2FFFFFFF +end + +def miami_ip + IPAddr.new(miami_ip_as_num, Socket::AF_INET).to_s +end + +def miami_ip_as_num + 0x5FFFFFFF +end + +def seattle_ip + IPAddr.new(seattle_ip_as_num, Socket::AF_INET).to_s +end + +def seattle_ip_as_num + 0xAFFFFFFF +end + + +def create_geoip(locid) + geoiplocation = GeoIpLocations.find_by_locid(locid) + geoipblock = GeoIpBlocks.find_by_locid(locid) + jamisp = JamIsp.find_by_beginip(geoipblock.beginip) + {jamisp: jamisp, geoiplocation: geoiplocation, geoipblock: geoipblock, locidispid: Score.compute_locidispid(geoiplocation.locid, jamisp.coid)} +end # gets related models for an IP in the 1st block from the scores_better_test_data.sql def austin_geoip - geoiplocation = GeoIpLocations.find_by_locid(17192) - geoipblock = GeoIpBlocks.find_by_locid(17192) - jamisp = JamIsp.find_by_beginip(geoipblock.beginip) - {jamisp: jamisp, geoiplocation: geoiplocation, geoipblock: geoipblock, locidispid: Score.compute_locidispid(geoiplocation.locid, jamisp.coid) } + create_geoip(17192) end # gets related models for an IP in the 1st block from the scores_better_test_data.sql def dallas_geoip - geoiplocation = GeoIpLocations.find_by_locid(667) - geoipblock = GeoIpBlocks.find_by_locid(667) - jamisp = JamIsp.find_by_beginip(geoipblock.beginip) - {jamisp: jamisp, geoiplocation: geoiplocation, geoipblock: geoipblock, locidispid: Score.compute_locidispid(geoiplocation.locid, jamisp.coid)} + create_geoip(667) +end + +# gets related models for an IP in the 1st block from the scores_better_test_data.sql +def houston_geoip + create_geoip(30350) +end + +def miami_geoip + create_geoip(23565) +end + +def seattle_geoip + create_geoip(1539) end # attempts to make the creation of a score more straightforward. @@ -257,8 +306,7 @@ def verify_find_musician_score(score, current_user, target_user) end visit '/client#/musicians' - hoverable = find(".musician-list-result[data-musician-id='#{target_user.id}'] .score-count#{expected[:latency_badge_selector]} ") - hoverable.find('img')['src'].include?("icon_#{expected[:color]}_score.png").should be_true + hoverable = find(".musician-list-result[data-musician-id='#{target_user.id}'] .latency#{expected[:latency_badge_selector]} ", text: expected[:latency_badge_text]) verify_score_hover(score, current_user, target_user, hoverable) end diff --git a/web/spec/teaspoon_env.rb b/web/spec/teaspoon_env.rb new file mode 100644 index 000000000..73f10e1bd --- /dev/null +++ b/web/spec/teaspoon_env.rb @@ -0,0 +1,182 @@ +# Set RAILS_ROOT and load the environment if it's not already loaded. +unless defined?(Rails) + ENV["RAILS_ROOT"] = File.expand_path("../../", __FILE__) + require File.expand_path("../../config/environment", __FILE__) +end + +Teaspoon.configure do |config| + + # Determines where the Teaspoon routes will be mounted. Changing this to "/jasmine" would allow you to browse to + # `http://localhost:3000/jasmine` to run your tests. + #config.mount_at = "/teaspoon" + + # Specifies the root where Teaspoon will look for files. If you're testing an engine using a dummy application it can + # be useful to set this to your engines root (e.g. `Teaspoon::Engine.root`). + # Note: Defaults to `Rails.root` if nil. + #config.root = nil + + # Paths that will be appended to the Rails assets paths + # Note: Relative to `config.root`. + #config.asset_paths = ["spec/javascripts", "spec/javascripts/stylesheets"] + + # Fixtures are rendered through a controller, which allows using HAML, RABL/JBuilder, etc. Files in these paths will + # be rendered as fixtures. + #config.fixture_paths = ["spec/javascripts/fixtures"] + + # SUITES + # + # You can modify the default suite configuration and create new suites here. Suites are isolated from one another. + # + # When defining a suite you can provide a name and a block. If the name is left blank, :default is assumed. You can + # omit various directives and the ones defined in the default suite will be used. + # + # To run a specific suite + # - in the browser: http://localhost/teaspoon/[suite_name] + # - with the rake task: rake teaspoon suite=[suite_name] + # - with the cli: teaspoon --suite=[suite_name] + config.suite do |suite| + + # Specify the framework you would like to use. This allows you to select versions, and will do some basic setup for + # you -- which you can override with the directives below. This should be specified first, as it can override other + # directives. + # Note: If no version is specified, the latest is assumed. + # + # Available: jasmine[1.3.1, 2.0.0], mocha[1.10.0, 1.17.1] qunit[1.12.0, 1.14.0] + suite.use_framework :jasmine, "1.3.1" + + # Specify a file matcher as a regular expression and all matching files will be loaded when the suite is run. These + # files need to be within an asset path. You can add asset paths using the `config.asset_paths`. + #suite.matcher = "{spec/javascripts,app/assets}/**/*_spec.{js,js.coffee,coffee}" + + # This suites spec helper, which can require additional support files. This file is loaded before any of your test + # files are loaded. + #suite.helper = "spec_helper" + + # The core Teaspoon javascripts. It's recommended to include only the base files here, as you can require support + # libraries from your spec helper. + # Note: For CoffeeScript files use `"teaspoon/jasmine"` etc. + # + # Available: teaspoon-jasmine, teaspoon-mocha, teaspoon-qunit + #suite.javascripts = ["jasmine/1.3.1", "teaspoon-jasmine"] + + # You can include your own stylesheets if you want to change how Teaspoon looks. + # Note: Spec related CSS can and should be loaded using fixtures. + #suite.stylesheets = ["teaspoon"] + + # Partial to be rendered in the head tag of the runner. You can use the provided ones or define your own by creating + # a `_boot.html.erb` in your fixtures path, and adjust the config to `"/boot"` for instance. + # + # Available: boot, boot_require_js + #suite.boot_partial = "boot" + + # Partial to be rendered in the body tag of the runner. You can define your own to create a custom body structure. + #suite.body_partial = "body" + + # Assets to be ignored when generating coverage reports. Accepts an array of filenames or regular expressions. The + # default excludes assets from vendor, gems and support libraries.

+ #suite.no_coverage = [%r{/lib/ruby/gems/}, %r{/vendor/assets/}, %r{/support/}, %r{/(.+)_helper.}] + + # Hooks allow you to use `Teaspoon.hook("fixtures")` before, after, or during your spec run. This will make a + # synchronous Ajax request to the server that will call all of the blocks you've defined for that hook name. + #suite.hook :fixtures, proc{ } + + end + + # Example suite. Since we're just filtering to files already within the root test/javascripts, these files will also + # be run in the default suite -- but can be focused into a more specific suite. + #config.suite :targeted do |suite| + # suite.matcher = "test/javascripts/targeted/*_test.{js,js.coffee,coffee}" + #end + + # CONSOLE RUNNER SPECIFIC + # + # These configuration directives are applicable only when running via the rake task or command line interface. These + # directives can be overridden using the command line interface arguments or with ENV variables when using the rake + # task. + # + # Command Line Interface: + # teaspoon --driver=phantomjs --server-port=31337 --fail-fast=true --format=junit --suite=my_suite /spec/file_spec.js + # + # Rake: + # teaspoon DRIVER=phantomjs SERVER_PORT=31337 FAIL_FAST=true FORMATTERS=junit suite=my_suite + + # Specify which headless driver to use. Supports PhantomJS and Selenium Webdriver. + # + # Available: phantomjs, selenium + # PhantomJS: https://github.com/modeset/teaspoon/wiki/Using-PhantomJS + # Selenium Webdriver: https://github.com/modeset/teaspoon/wiki/Using-Selenium-WebDriver + #config.driver = "phantomjs" + + # Specify additional options for the driver. + # + # PhantomJS: https://github.com/modeset/teaspoon/wiki/Using-PhantomJS + # Selenium Webdriver: https://github.com/modeset/teaspoon/wiki/Using-Selenium-WebDriver + #config.driver_options = nil + + # Specify the timeout for the driver. Specs are expected to complete within this time frame or the run will be + # considered a failure. This is to avoid issues that can arise where tests stall. + #config.driver_timeout = 180 + + # Specify a server to use with Rack (e.g. thin, mongrel). If nil is provided Rack::Server is used. + #config.server = nil + + # Specify a port to run on a specific port, otherwise Teaspoon will use a random available port. + #config.server_port = nil + + # Timeout for starting the server in seconds. If your server is slow to start you may have to bump this, or you may + # want to lower this if you know it shouldn't take long to start. + #config.server_timeout = 20 + + # Force Teaspoon to fail immediately after a failing suite. Can be useful to make Teaspoon fail early if you have + # several suites, but in environments like CI this may not be desirable. + #config.fail_fast = true + + # Specify the formatters to use when outputting the results. + # Note: Output files can be specified by using `"junit>/path/to/output.xml"`. + # + # Available: dot, documentation, clean, json, junit, pride, snowday, swayze_or_oprah, tap, tap_y, teamcity + #config.formatters = ["dot"] + + # Specify if you want color output from the formatters. + #config.color = true + + # Teaspoon pipes all console[log/debug/error] to $stdout. This is useful to catch places where you've forgotten to + # remove them, but in verbose applications this may not be desirable. + #config.suppress_log = false + + # COVERAGE REPORTS / THRESHOLD ASSERTIONS + # + # Coverage reports requires Istanbul (https://github.com/gotwarlost/istanbul) to add instrumentation to your code and + # display coverage statistics. + # + # Coverage configurations are similar to suites. You can define several, and use different ones under different + # conditions. + # + # To run with a specific coverage configuration + # - with the rake task: rake teaspoon USE_COVERAGE=[coverage_name] + # - with the cli: teaspoon --coverage=[coverage_name] + + # Specify that you always want a coverage configuration to be used. + #config.use_coverage = nil + + config.coverage do |coverage| + + # Which coverage reports Instanbul should generate. Correlates directly to what Istanbul supports. + # + # Available: text-summary, text, html, lcov, lcovonly, cobertura, teamcity + #coverage.reports = ["text-summary", "html"] + + # The path that the coverage should be written to - when there's an artifact to write to disk. + # Note: Relative to `config.root`. + #coverage.output_dir = "coverage" + + # Various thresholds requirements can be defined, and those thresholds will be checked at the end of a run. If any + # aren't met the run will fail with a message. Thresholds can be defined as a percentage (0-100), or nil. + #coverage.statements = nil + #coverage.functions = nil + #coverage.branches = nil + #coverage.lines = nil + + end + +end diff --git a/web/vendor/assets/javascripts/bind-polyfill.js b/web/vendor/assets/javascripts/bind-polyfill.js new file mode 100644 index 000000000..406c207cc --- /dev/null +++ b/web/vendor/assets/javascripts/bind-polyfill.js @@ -0,0 +1,39 @@ +https://github.com/ariya/phantomjs/issues/10522#issuecomment-39248521 + +var isFunction = function(o) { + return typeof o == 'function'; +}; + + +var bind, + slice = [].slice, + proto = Function.prototype, + featureMap; + +featureMap = { + 'function-bind': 'bind' +}; + +function has(feature) { + var prop = featureMap[feature]; + return isFunction(proto[prop]); +} + +// check for missing features +if (!has('function-bind')) { + // adapted from Mozilla Developer Network example at + // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind + bind = function bind(obj) { + var args = slice.call(arguments, 1), + self = this, + nop = function() { + }, + bound = function() { + return self.apply(this instanceof nop ? this : (obj || {}), args.concat(slice.call(arguments))); + }; + nop.prototype = this.prototype || {}; // Firefox cries sometimes if prototype is undefined + bound.prototype = new nop(); + return bound; + }; + proto.bind = bind; +} \ No newline at end of file diff --git a/web/vendor/assets/javascripts/class.js b/web/vendor/assets/javascripts/class.js new file mode 100644 index 000000000..c71a33bef --- /dev/null +++ b/web/vendor/assets/javascripts/class.js @@ -0,0 +1,83 @@ +/* Simple JavaScript Inheritance for ES 5.1 ( includes polyfill for IE < 9 ) + * based on http://ejohn.org/blog/simple-javascript-inheritance/ + * (inspired by base2 and Prototype) + * MIT Licensed. + */ +(function (global) { + "use strict"; + + if (!Object.create) { + Object.create = (function () { + function F() { + } + + return function (o) { + if (arguments.length != 1) { + throw new Error("Object.create implementation only accepts one parameter."); + } + F.prototype = o; + return new F(); + }; + })(); + } + + var fnTest = /xyz/.test(function () { + xyz; + }) ? /\b_super\b/ : /.*/; + + // The base Class implementation (does nothing) + function BaseClass() { + } + + // Create a new Class that inherits from this class + BaseClass.extend = function (props) { + var _super = this.prototype; + + // Instantiate a base class (but only create the instance, + // don't run the init constructor) + var proto = Object.create(_super); + + // Copy the properties over onto the new prototype + for (var name in props) { + // Check if we're overwriting an existing function + proto[name] = typeof props[name] === "function" && + typeof _super[name] === "function" && fnTest.test(props[name]) ? + (function (name, fn) { + return function () { + var tmp = this._super; + + // Add a new ._super() method that is the same method + // but on the super-class + this._super = _super[name]; + + // The method only need to be bound temporarily, so we + // remove it when we're done executing + var ret = fn.apply(this, arguments); + this._super = tmp; + + return ret; + }; + })(name, props[name]) : + props[name]; + } + + // The new constructor + var newClass = typeof proto.init === "function" ? + proto.init : // All construction is actually done in the init method + function () {}; + + // Populate our constructed prototype object + newClass.prototype = proto; + + // Enforce the constructor to be what we expect + proto.constructor = newClass; + + // And make this class extendable + newClass.extend = BaseClass.extend; + + return newClass; + }; + + // export + global.Class = BaseClass; +})(this); \ No newline at end of file From 9279d88d8d866c03d628b3a8742a504296d1ba69 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Tue, 10 Mar 2015 22:10:22 -0500 Subject: [PATCH 15/65] * VRFS-2876 - pro licensing added and 20 second snippet --- .../views/admin/jam_tracks/_form.html.slim | 13 ++- admin/config/initializers/jam_tracks.rb | 99 +++++++++++++++++++ db/manifest | 3 +- db/up/jam_track_pro_licensing_update.sql | 12 +++ .../app/uploaders/jam_track_uploader.rb | 4 +- ruby/lib/jam_ruby/jam_track_importer.rb | 1 - ruby/lib/jam_ruby/models/jam_track.rb | 77 +++------------ ruby/lib/jam_ruby/models/jam_track_tap_in.rb | 1 - ruby/spec/factories.rb | 2 - ruby/spec/jam_ruby/jam_track_importer_spec.rb | 1 - .../jam_ruby/models/jam_track_right_spec.rb | 8 -- ruby/spec/jam_ruby/models/jam_track_spec.rb | 14 +-- .../controllers/api_jam_tracks_controller.rb | 9 -- web/config/routes.rb | 1 - web/spec/factories.rb | 2 - 15 files changed, 146 insertions(+), 101 deletions(-) create mode 100644 db/up/jam_track_pro_licensing_update.sql diff --git a/admin/app/views/admin/jam_tracks/_form.html.slim b/admin/app/views/admin/jam_tracks/_form.html.slim index 2eb08fd27..a95275b82 100644 --- a/admin/app/views/admin/jam_tracks/_form.html.slim +++ b/admin/app/views/admin/jam_tracks/_form.html.slim @@ -6,22 +6,25 @@ = f.input :plan_code, :label=>'Recurly Plan Code', :required=>true, :hint => 'Must match plan code in Recurly' = f.input :version, :label => 'Version', :hint => 'Increment this value whenever you invalidate (update) the media in the JamTrack. Changing JMEP does not count as a version change; changing anything about a track (audio, instrument, part) does.' //= f.input :initial_play_silence, :label => 'Initial Play Silence (seconds)' - = f.input :time_signature, collection: JamRuby::JamTrack::TIME_SIGNATURES, include_blank: false + = f.input :time_signature, collection: JamRuby::JamTrack::TIME_SIGNATURES, include_blank: true = f.input :status, collection: JamRuby::JamTrack::STATUS, include_blank: false, hint: 'Only set to Production when end users should be able to purchase this JamTrack' = f.input :recording_type, collection: JamRuby::JamTrack::RECORDING_TYPE, include_blank: false = f.input :original_artist, :input_html => { :rows=>1, :maxlength=>1000 } = f.input :songwriter, :input_html => { :rows=>1, :maxlength=>1000 } = f.input :publisher, :input_html => { :rows=>1, :maxlength=>1000 } - = f.input :licensor, collection: JamRuby::JamTrackLicensor.all, include_blank: false - = f.input :pro, collection: JamRuby::JamTrack::PRO, include_blank: false + = f.input :licensor, collection: JamRuby::JamTrackLicensor.all, include_blank: true = f.input :genre, collection: JamRuby::Genre.all, include_blank: false = f.input :sales_region, collection: JamRuby::JamTrack::SALES_REGION, include_blank: false - = f.input :price, :required=>true, :input_html=>{type:'numeric'} + = f.input :price, :required => true, :input_html => {type: 'numeric'} + = f.input :pro_ascap, :label => 'ASCAP royalties due?' + = f.input :pro_bmi, :label => 'BMI royalties due?' + = f.input :pro_sesac, :label => 'SESAC royalties due?' = f.input :reproduction_royalty, :label => 'Reproduction Royalty' = f.input :public_performance_royalty, :label => 'Public Performance Royalty' = f.input :reproduction_royalty_amount, :required=>true, :input_html=>{type:'numeric'} = f.input :licensor_royalty_amount, :required=>true, :input_html=>{type:'numeric'} - = f.input :pro_royalty_amount, :required=>true, :input_html=>{type:'numeric'} + = f.input :preview_start_time_raw, :label=>'Preview Start Time', :hint => 'MM:SS:MLS', :as => :string + //= f.input :url, :as => :file, :label => 'Audio File' = f.input :jmep_text, :as => :text, :label => "JMEP Text", :input_html => {:rows => 5 }, :hint => 'Tap-Ins & Lead Silence. Examples: https://jamkazam.atlassian.net/wiki/pages/viewpage.action?pageId=39289025#JamKazamMeta-EventProcessor(JMEP)-CommonExamples' = f.input :jmep_json, :as => :text, :label => "JMEP Json", :input_html => {:rows => 5, :readonly => true }, :hint => 'Readonly field. This is shown here just so you can see what your JMEP got converted to readily' diff --git a/admin/config/initializers/jam_tracks.rb b/admin/config/initializers/jam_tracks.rb index c88fc6a2c..c1c0063bf 100644 --- a/admin/config/initializers/jam_tracks.rb +++ b/admin/config/initializers/jam_tracks.rb @@ -2,8 +2,11 @@ class JamRuby::JamTrack # add a custom validation + attr_accessor :preview_generate_error + before_save :jmep_json_generate validate :jmep_text_validate + validate :preview def jmep_text_validate begin @@ -14,6 +17,11 @@ class JamRuby::JamTrack end def jmep_json_generate + self.genre_id = nil if self.genre_id == '' + self.licensor_id = nil if self.licensor_id == '' + self.jmep_json = nil if self.jmep_json == '' + self.time_signature = nil if self.time_signature == '' + begin self[:jmep_json] = JmepManager.execute(self.jmep_text) rescue ArgumentError => err @@ -21,5 +29,96 @@ class JamRuby::JamTrack end end + def preview + if preview_generate_error + errors.add(:preview_url, preview_generate_error) + end + end + + + + # this is used by active admin/jam-admin + def preview_start_time_raw + if self.preview_start_time.nil? || self.preview_start_time.nil? + '' + else + seconds = self.preview_start_time.to_f/1000 + time = Time.at(seconds) + time.strftime("%M:%S:#{(self.preview_start_time % 1000).to_s.rjust(3, '0')}") + end + end + + # this is used by active admin/jam-admin + def preview_start_time_raw=(new_value) + + value = nil + if new_value == nil || new_value == '' + value = nil + else + if new_value && new_value.kind_of?(String) && new_value.include?(':') + bits = new_value.split(':') + if bits.length != 3 + raise "format of preview start time must be MM:SS:MLS" + end + + value = (bits[0].to_i * 60000) + (bits[1].to_i * 1000) + (bits[2].to_i) + + else + raise "format of preview start time must be MM:SS:MLS" + end + end + + if !value.nil? && value != self.preview_start_time + self.preview_start_time = value + generate_preview + else + self.preview_start_time = value + end + end + + def generate_preview + + begin + Dir.mktmpdir do |tmp_dir| + + input = File.join(tmp_dir, 'in.ogg') + output = File.join(tmp_dir, 'out.ogg') + + start = self.preview_start_time.to_f / 1000 + stop = start + 20 + + master_track = self.master_track + + raise 'no master track' unless master_track + + s3_manager.download(master_track.url_by_sample_rate(44), input) + + command = "sox \"#{input}\" \"#{output}\" trim #{start} #{stop}" + + @@log.debug("trimming using: " + command) + + sox_output = `#{command}` + + result_code = $?.to_i + + if result_code != 0 + @preview_generate_error = "unable to execute cut command #{sox_output}" + else + @@log.debug("uploading preview to #{self.preview_filename}") + + s3_manager.upload(self.preview_filename, output) + + # and finally update the JamTrackTrack with the new info + self["preview_url"] = self.preview_filename + self["preview_md5"] = ::Digest::MD5.file(output).hexdigest + self["preview_length"] = File.new(output).size + self.save! + end + end + rescue Exception => e + @preview_generate_error = e.to_s + end + + end end diff --git a/db/manifest b/db/manifest index f744571eb..6d9f1f63d 100755 --- a/db/manifest +++ b/db/manifest @@ -258,4 +258,5 @@ jam_track_version.sql recorded_jam_track_tracks.sql jam_track_jmep_data.sql add_jam_track_bitrates.sql -jam_track_importer.sql \ No newline at end of file +jam_track_importer.sql +jam_track_pro_licensing_update.sql \ No newline at end of file diff --git a/db/up/jam_track_pro_licensing_update.sql b/db/up/jam_track_pro_licensing_update.sql new file mode 100644 index 000000000..006483f44 --- /dev/null +++ b/db/up/jam_track_pro_licensing_update.sql @@ -0,0 +1,12 @@ +ALTER TABLE jam_tracks ADD COLUMN pro_ascap BOOLEAN DEFAULT FALSE NOT NULL; +ALTER TABLE jam_tracks ADD COLUMN pro_bmi BOOLEAN DEFAULT FALSE NOT NULL; +ALTER TABLE jam_tracks ADD COLUMN pro_sesac BOOLEAN DEFAULT FALSE NOT NULL; +UPDATE jam_tracks SET pro_ascap = TRUE WHERE pro = 'ASCAP'; +UPDATE jam_tracks SET pro_bmi = TRUE WHERE pro = 'BMI'; +UPDATE jam_tracks SET pro_sesac = TRUE WHERE pro = 'SESAC'; +ALTER TABLE jam_tracks DROP COLUMN pro; +ALTER TABLE jam_tracks DROP Column pro_royalty_amount; +ALTER TABLE jam_tracks ADD COLUMN preview_start_time INTEGER; +ALTER TABLE jam_tracks RENAME COLUMN url TO preview_url; +ALTER TABLE jam_tracks RENAME COLUMN md5 TO preview_md5; +ALTER TABLE jam_tracks RENAME COLUMN length TO preview_length; diff --git a/ruby/lib/jam_ruby/app/uploaders/jam_track_uploader.rb b/ruby/lib/jam_ruby/app/uploaders/jam_track_uploader.rb index 98e555e3d..9ec9a97ec 100644 --- a/ruby/lib/jam_ruby/app/uploaders/jam_track_uploader.rb +++ b/ruby/lib/jam_ruby/app/uploaders/jam_track_uploader.rb @@ -11,7 +11,7 @@ class JamTrackUploader < CarrierWave::Uploader::Base # Add a white list of extensions which are allowed to be uploaded. def extension_white_list - %w(jkz) + %w(ogg) end def store_dir @@ -23,6 +23,6 @@ class JamTrackUploader < CarrierWave::Uploader::Base end def filename - "#{model.store_dir}/#{model.filename}" if model.id + "#{model.preview_filename}" if model.id && model.uploading_preview end end diff --git a/ruby/lib/jam_ruby/jam_track_importer.rb b/ruby/lib/jam_ruby/jam_track_importer.rb index 39da44bb0..8874a66fe 100644 --- a/ruby/lib/jam_ruby/jam_track_importer.rb +++ b/ruby/lib/jam_ruby/jam_track_importer.rb @@ -102,7 +102,6 @@ module JamRuby jam_track.price = 1.99 jam_track.reproduction_royalty_amount = 0 jam_track.licensor_royalty_amount = 0 - jam_track.pro_royalty_amount = 0 jam_track.sales_region = 'United States' jam_track.recording_type = 'Cover' jam_track.description = "This is a JamTrack audio file for use exclusively with the JamKazam service. This JamTrack is a high quality cover of the #{jam_track.original_artist} song \"#{jam_track.name}\"." diff --git a/ruby/lib/jam_ruby/models/jam_track.rb b/ruby/lib/jam_ruby/models/jam_track.rb index 73e1634c3..9660f7a4f 100644 --- a/ruby/lib/jam_ruby/models/jam_track.rb +++ b/ruby/lib/jam_ruby/models/jam_track.rb @@ -12,35 +12,37 @@ module JamRuby @@log = Logging.logger[JamTrack] - mount_uploader :url, JamTrackUploader + mount_uploader :preview_url, JamTrackUploader + attr_accessor :uploading_preview attr_accessible :name, :description, :bpm, :time_signature, :status, :recording_type, :original_artist, :songwriter, :publisher, :licensor, :licensor_id, :pro, :genre, :genre_id, :sales_region, :price, :reproduction_royalty, :public_performance_royalty, :reproduction_royalty_amount, :licensor_royalty_amount, :pro_royalty_amount, :plan_code, :initial_play_silence, :jam_track_tracks_attributes, - :jam_track_tap_ins_attributes, :version, :jmep_json, :jmep_text, as: :admin + :jam_track_tap_ins_attributes, :version, :jmep_json, :jmep_text, :pro_ascap, :pro_bmi, :pro_sesac, :preview_start_time_raw, as: :admin validates :name, presence: true, uniqueness: true, length: {maximum: 200} validates :plan_code, presence: true, uniqueness: true, length: {maximum: 50 } validates :description, length: {maximum: 1000} - validates :time_signature, inclusion: {in: [nil] + TIME_SIGNATURES} + validates :time_signature, inclusion: {in: [nil] + [''] + TIME_SIGNATURES} # the empty string is needed because of activeadmin validates :status, inclusion: {in: [nil] + STATUS} validates :recording_type, inclusion: {in: [nil] + RECORDING_TYPE} validates :original_artist, length: {maximum: 200} validates :songwriter, length: {maximum: 1000} validates :publisher, length: {maximum: 1000} - validates :pro, inclusion: {in: [nil] + PRO} validates :sales_region, inclusion: {in: [nil] + SALES_REGION} validates_format_of :price, with: /^\d+\.*\d{0,2}$/ validates :version, presence: true - + validates :preview_start_time, numericality: {only_integer: true}, length: {in: 1..1000}, :allow_nil => true + validates :pro_ascap, inclusion: {in: [true, false]} + validates :pro_bmi, inclusion: {in: [true, false]} + validates :pro_sesac, inclusion: {in: [true, false]} + validates :public_performance_royalty, inclusion: {in: [nil, true, false]} validates :reproduction_royalty, inclusion: {in: [nil, true, false]} validates :public_performance_royalty, inclusion: {in: [nil, true, false]} + validates_format_of :reproduction_royalty_amount, with: /^\d+\.*\d{0,3}$/ validates_format_of :licensor_royalty_amount, with: /^\d+\.*\d{0,3}$/ - validates_format_of :pro_royalty_amount, with: /^\d+\.*\d{0,3}$/ - - before_save :sanitize_active_admin belongs_to :genre, class_name: "JamRuby::Genre" belongs_to :licensor , class_name: 'JamRuby::JamTrackLicensor', foreign_key: 'licensor_id' @@ -91,15 +93,10 @@ module JamRuby end end end - - # create storage directory that will house this jam_track, as well as - def store_dir - "jam_tracks/#{id}" - end # create name of the file - def filename - "#{name}.jkz" + def preview_filename + "jam_track_previews/#{self.original_artist}/#{self.name}/preview-44100.ogg" end # creates a short-lived URL that has access to the object. @@ -107,9 +104,12 @@ module JamRuby # we would verify their rights (can_download?), and generates a URL in response to the click so that they can download # but the url is short lived enough so that it wouldn't be easily shared def sign_url(expiration_time = 120) - s3_manager.sign_url(self[:url], {:expires => expiration_time, :response_content_type => 'audio/jkz', :secure => false}) + s3_manager.sign_url(self[:preview_url], {:expires => expiration_time, :response_content_type => 'audio/ogg', :secure => false}) end + def master_track + JamTrackTrack.where(jam_track_id: self.id).where(track_type: 'Master').first + end def can_download?(user) owners.include?(user) @@ -118,50 +118,5 @@ module JamRuby def right_for_user(user) jam_track_rights.where("user_id=?", user).first end - - def self.list_downloads(user, limit = 100, since = 0, bitrate = 48) - since = 0 unless since || since == '' # guard against nil - downloads = [] - - user.jam_track_rights - .limit(limit) - .where('jam_track_rights.id > ?', since) - .each do |jam_track_right| - download = { - :type => "jam_track", - :id => jam_track_right.id.to_s, - :jam_track_id => jam_track_right.jam_track_id, - :created_at => jam_track_right.created_at, - :next => jam_track_right.id - } - if(bitrate==48) - download[:length] = jam_track_right.length_48 - download[:md5] = jam_track_right.md5_48 - download[:url] = jam_track_right.url_48 - else - download[:length] = jam_track_right.length_44 - download[:md5] = jam_track_right.md5_44 - download[:url] = jam_track_right.url_44 - end - downloads << download - end - - next_id = downloads[-1][:next] if downloads.length > 0 - next_id = since if next_id.nil? # echo back to the client the same value they passed in, if there are no results - - { - 'downloads' => downloads, - 'next' => next_id.to_s - } - end - - - private - - def sanitize_active_admin - self.genre_id = nil if self.genre_id == '' - self.licensor_id = nil if self.licensor_id == '' - self.jmep_json = nil if self.jmep_json = '' - end end end diff --git a/ruby/lib/jam_ruby/models/jam_track_tap_in.rb b/ruby/lib/jam_ruby/models/jam_track_tap_in.rb index 0bddeb8c6..fa0e63eda 100644 --- a/ruby/lib/jam_ruby/models/jam_track_tap_in.rb +++ b/ruby/lib/jam_ruby/models/jam_track_tap_in.rb @@ -35,7 +35,6 @@ module JamRuby else raise "format of offset time must be MM:SS:MLS" end - end end end \ No newline at end of file diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb index 322b206bd..18dbeb8bf 100644 --- a/ruby/spec/factories.rb +++ b/ruby/spec/factories.rb @@ -730,14 +730,12 @@ FactoryGirl.define do sequence(:original_artist) { |n| "original-artist-#{n}" } sequence(:songwriter) { |n| "songwriter-#{n}" } sequence(:publisher) { |n| "publisher-#{n}" } - pro 'ASCAP' sales_region 'United States' price 1.99 reproduction_royalty true public_performance_royalty true reproduction_royalty_amount 0.999 licensor_royalty_amount 0.999 - pro_royalty_amount 0.999 sequence(:plan_code) { |n| "jamtrack-#{n}" } genre JamRuby::Genre.first diff --git a/ruby/spec/jam_ruby/jam_track_importer_spec.rb b/ruby/spec/jam_ruby/jam_track_importer_spec.rb index eda890d1f..5a0a89cf4 100644 --- a/ruby/spec/jam_ruby/jam_track_importer_spec.rb +++ b/ruby/spec/jam_ruby/jam_track_importer_spec.rb @@ -57,7 +57,6 @@ describe JamTrackImporter do jam_track.original_artist.should eq('Artist 1') jam_track.songwriter.should be_nil jam_track.publisher.should be_nil - jam_track.pro.should be_nil jam_track.sales_region.should eq('United States') jam_track.price.should eq(1.99) end diff --git a/ruby/spec/jam_ruby/models/jam_track_right_spec.rb b/ruby/spec/jam_ruby/models/jam_track_right_spec.rb index ee7497f40..bd8e48196 100644 --- a/ruby/spec/jam_ruby/models/jam_track_right_spec.rb +++ b/ruby/spec/jam_ruby/models/jam_track_right_spec.rb @@ -17,14 +17,6 @@ describe JamTrackRight do end - it "lists" do - jam_track_right = FactoryGirl.create(:jam_track_right) - jam_tracks = JamTrack.list_downloads(jam_track_right.user) - jam_tracks.should have_key('downloads') - jam_tracks.should have_key('next') - jam_tracks['downloads'].should have(1).items - end - describe "validations" do it "one purchase per user/jam_track combo" do user = FactoryGirl.create(:user) diff --git a/ruby/spec/jam_ruby/models/jam_track_spec.rb b/ruby/spec/jam_ruby/models/jam_track_spec.rb index af4980f7b..0f41e5f25 100644 --- a/ruby/spec/jam_ruby/models/jam_track_spec.rb +++ b/ruby/spec/jam_ruby/models/jam_track_spec.rb @@ -119,9 +119,9 @@ describe JamTrack do end describe "upload/download" do - JKA_NAME = 'blah.jkz' + PREVIEW_NAME = 'blah.ogg' - in_directory_with_file(JKA_NAME) + in_directory_with_file(PREVIEW_NAME) before(:all) do original_storage = JamTrackUploader.storage = :fog @@ -137,17 +137,17 @@ describe JamTrack do it "uploads to s3 with correct name, and then downloads via signed URL" do jam_track = FactoryGirl.create(:jam_track) - uploader = JamTrackUploader.new(jam_track, :url) - uploader.store!(File.open(JKA_NAME)) # uploads file + uploader = JamTrackUploader.new(jam_track, :preview_url) + uploader.store!(File.open(PREVIEW_NAME)) # uploads file jam_track.save! # verify that the uploader stores the correct path - jam_track[:url].should == jam_track.store_dir + '/' + jam_track.filename + jam_track[:preview_url].should == jam_track.preview_filename # verify it's on S3 s3 = S3Manager.new(APP_CONFIG.aws_bucket, APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key) - s3.exists?(jam_track[:url]).should be_true - s3.length(jam_track[:url]).should == 'abc'.length + s3.exists?(jam_track[:preview_url]).should be_true + s3.length(jam_track[:preview_url]).should == 'abc'.length # download it via signed URL, and check contents url = jam_track.sign_url diff --git a/web/app/controllers/api_jam_tracks_controller.rb b/web/app/controllers/api_jam_tracks_controller.rb index b2322c4a5..eff17afa0 100644 --- a/web/app/controllers/api_jam_tracks_controller.rb +++ b/web/app/controllers/api_jam_tracks_controller.rb @@ -23,15 +23,6 @@ class ApiJamTracksController < ApiController render "api_jam_tracks/purchased", :layout => nil end - def downloads - sample_rate = params[:sample_rate].nil? ? nil : params[:sample_rate].to_i - begin - render :json => JamTrack.list_downloads(current_user, params[:limit], params[:since], sample_rate), :status => 200 - rescue - render :json => { :message => "could not produce list of files" }, :status => 403 - end - end - def download if @jam_track_right.valid? sample_rate = params[:sample_rate].nil? ? nil : params[:sample_rate].to_i diff --git a/web/config/routes.rb b/web/config/routes.rb index 00fd25f38..d21ed3861 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -206,7 +206,6 @@ SampleApp::Application.routes.draw do # Jamtracks match '/jamtracks' => 'api_jam_tracks#index', :via => :get, :as => 'api_jam_tracks_list' match '/jamtracks/purchased' => 'api_jam_tracks#purchased', :via => :get, :as => 'api_jam_tracks_purchased' - match '/jamtracks/downloads' => 'api_jam_tracks#downloads', :via => :get, :as => 'api_jam_tracks_downloads' match '/jamtracks/download/:id' => 'api_jam_tracks#download', :via => :get, :as => 'api_jam_tracks_download' match '/jamtracks/enqueue/:id' => 'api_jam_tracks#enqueue', :via => :post, :as => 'api_jam_tracks_enqueue' match '/jamtracks/rights/:id' => 'api_jam_tracks#show_jam_track_right', :via => :get, :as => 'api_jam_tracks_show_right' diff --git a/web/spec/factories.rb b/web/spec/factories.rb index 1912f434d..59a8089fa 100644 --- a/web/spec/factories.rb +++ b/web/spec/factories.rb @@ -717,14 +717,12 @@ FactoryGirl.define do sequence(:original_artist) { |n| "original-artist-#{n}" } sequence(:songwriter) { |n| "songwriter-#{n}" } sequence(:publisher) { |n| "publisher-#{n}" } - pro 'ASCAP' sales_region 'United States' price 1.99 reproduction_royalty true public_performance_royalty true reproduction_royalty_amount 0.999 licensor_royalty_amount 0.999 - pro_royalty_amount 0.999 sequence(:plan_code) { |n| "jamtrack-#{n}" } ignore do make_track true From 6c82e00d0036209c99c368dd6c887ff8e53c79e4 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 11 Mar 2015 09:20:45 -0500 Subject: [PATCH 16/65] * VRFS-2876 cleaning up preview downloadability in jam-admin --- admin/app/admin/jam_tracks.rb | 20 +++------- .../views/admin/jam_tracks/_form.html.slim | 4 ++ ruby/lib/jam_ruby/models/jam_track.rb | 5 ++- ruby/spec/jam_ruby/models/jam_track_spec.rb | 39 ------------------- 4 files changed, 12 insertions(+), 56 deletions(-) diff --git a/admin/app/admin/jam_tracks.rb b/admin/app/admin/jam_tracks.rb index 67d961f22..ac1897c4e 100644 --- a/admin/app/admin/jam_tracks.rb +++ b/admin/app/admin/jam_tracks.rb @@ -22,29 +22,19 @@ ActiveAdmin.register JamRuby::JamTrack, :as => 'JamTracks' do links end - - column :id - column :name - column :description - column :version - column :time_signature - column :status - column :recording_type column :original_artist - column :songwriter - column :publisher + column :name + column :status + column :preview do |jam_track| jam_track.has_preview? ? (link_to "Download", jam_track.sign_url(3600)) : 'None' end + column :master_track do |jam_track| jam_track.master_track.nil? ? 'None' : (link_to "Download", jam_track.master_track.url_by_sample_rate(44)) end column :licensor - column :pro column :genre - column :sales_region column :price column :reproduction_royalty column :public_performance_royalty column :reproduction_royalty_amount column :licensor_royalty_amount - column :pro_royalty_amount - column :url - column :created_at + column :id column :jam_track_tracks do |jam_track| table_for jam_track.jam_track_tracks.order('position ASC') do diff --git a/admin/app/views/admin/jam_tracks/_form.html.slim b/admin/app/views/admin/jam_tracks/_form.html.slim index a95275b82..b74dba9c1 100644 --- a/admin/app/views/admin/jam_tracks/_form.html.slim +++ b/admin/app/views/admin/jam_tracks/_form.html.slim @@ -24,6 +24,10 @@ = f.input :reproduction_royalty_amount, :required=>true, :input_html=>{type:'numeric'} = f.input :licensor_royalty_amount, :required=>true, :input_html=>{type:'numeric'} = f.input :preview_start_time_raw, :label=>'Preview Start Time', :hint => 'MM:SS:MLS', :as => :string + - unless f.object.nil? || f.object[:preview_url].nil? + .current_file_holder style='margin-bottom:10px' + a href=f.object.sign_url(3600) style='padding:0 0 0 20px' + | Download //= f.input :url, :as => :file, :label => 'Audio File' = f.input :jmep_text, :as => :text, :label => "JMEP Text", :input_html => {:rows => 5 }, :hint => 'Tap-Ins & Lead Silence. Examples: https://jamkazam.atlassian.net/wiki/pages/viewpage.action?pageId=39289025#JamKazamMeta-EventProcessor(JMEP)-CommonExamples' diff --git a/ruby/lib/jam_ruby/models/jam_track.rb b/ruby/lib/jam_ruby/models/jam_track.rb index 9660f7a4f..47efe3cdf 100644 --- a/ruby/lib/jam_ruby/models/jam_track.rb +++ b/ruby/lib/jam_ruby/models/jam_track.rb @@ -12,8 +12,6 @@ module JamRuby @@log = Logging.logger[JamTrack] - mount_uploader :preview_url, JamTrackUploader - attr_accessor :uploading_preview attr_accessible :name, :description, :bpm, :time_signature, :status, :recording_type, :original_artist, :songwriter, :publisher, :licensor, :licensor_id, :pro, :genre, :genre_id, :sales_region, :price, @@ -99,6 +97,9 @@ module JamRuby "jam_track_previews/#{self.original_artist}/#{self.name}/preview-44100.ogg" end + def has_preview? + !self["preview_url"].nil? + end # creates a short-lived URL that has access to the object. # the idea is that this is used when a user who has the rights to this tries to download this JamTrack # we would verify their rights (can_download?), and generates a URL in response to the click so that they can download diff --git a/ruby/spec/jam_ruby/models/jam_track_spec.rb b/ruby/spec/jam_ruby/models/jam_track_spec.rb index 0f41e5f25..063f3a029 100644 --- a/ruby/spec/jam_ruby/models/jam_track_spec.rb +++ b/ruby/spec/jam_ruby/models/jam_track_spec.rb @@ -117,44 +117,5 @@ describe JamTrack do end end end - - describe "upload/download" do - PREVIEW_NAME = 'blah.ogg' - - in_directory_with_file(PREVIEW_NAME) - - before(:all) do - original_storage = JamTrackUploader.storage = :fog - end - - after(:all) do - JamTrackUploader.storage = @original_storage - end - - before(:each) do - content_for_file('abc') - end - - it "uploads to s3 with correct name, and then downloads via signed URL" do - jam_track = FactoryGirl.create(:jam_track) - uploader = JamTrackUploader.new(jam_track, :preview_url) - uploader.store!(File.open(PREVIEW_NAME)) # uploads file - jam_track.save! - - # verify that the uploader stores the correct path - jam_track[:preview_url].should == jam_track.preview_filename - - # verify it's on S3 - s3 = S3Manager.new(APP_CONFIG.aws_bucket, APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key) - s3.exists?(jam_track[:preview_url]).should be_true - s3.length(jam_track[:preview_url]).should == 'abc'.length - - # download it via signed URL, and check contents - url = jam_track.sign_url - downloaded_contents = open(url).read - downloaded_contents.should == 'abc' - end - - end end From 0a869e91e3abd2aa6d2b90dc8000281465e59309 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 11 Mar 2015 16:44:22 -0500 Subject: [PATCH 17/65] * VRFS-2913 - allow /client to be accessed without being logged in --- web/app/assets/javascripts/accounts.js | 6 ++- .../javascripts/dialog/loginRequiredDialog.js | 45 +++++++++++++++++++ .../javascripts/everywhere/everywhere.js | 3 ++ web/app/assets/javascripts/findSession.js | 2 + web/app/assets/javascripts/homeScreen.js | 14 +++++- .../javascripts/jam_track_utils.js.coffee | 2 +- web/app/assets/javascripts/jamkazam.js | 32 +++++++++++-- web/app/assets/javascripts/layout.js | 18 +++++++- .../assets/javascripts/notificationPanel.js | 6 ++- .../javascripts/scheduled_session.js.erb | 2 +- web/app/assets/javascripts/sidebar.js | 28 +++++++++--- .../assets/stylesheets/client/home.css.scss | 4 ++ .../stylesheets/client/sidebar.css.scss | 4 ++ .../dialogs/loginRequiredDialog.css.scss | 12 +++++ web/app/controllers/api_genres_controller.rb | 3 -- .../controllers/api_instruments_controller.rb | 3 -- web/app/helpers/sessions_helper.rb | 4 ++ web/app/views/clients/_header.html.erb | 4 +- web/app/views/clients/_home.html.slim | 30 ++++++------- web/app/views/clients/_jamtrack.html.haml | 2 +- web/app/views/clients/index.html.erb | 28 +++++------- web/app/views/dialogs/_dialogs.html.haml | 1 + .../dialogs/_loginRequiredDialog.html.slim | 20 +++++++++ .../api_jam_tracks_controller_spec.rb | 13 ------ .../features/authentication_pages_spec.rb | 3 +- web/spec/features/in_session_spec.rb | 4 +- web/spec/features/profile_menu_spec.rb | 2 +- web/spec/features/signin_spec.rb | 21 ++++++++- web/spec/support/utilities.rb | 7 ++- 29 files changed, 243 insertions(+), 80 deletions(-) create mode 100644 web/app/assets/javascripts/dialog/loginRequiredDialog.js create mode 100644 web/app/assets/stylesheets/dialogs/loginRequiredDialog.css.scss create mode 100644 web/app/views/dialogs/_loginRequiredDialog.html.slim diff --git a/web/app/assets/javascripts/accounts.js b/web/app/assets/javascripts/accounts.js index 64501695d..90b0c4e5e 100644 --- a/web/app/assets/javascripts/accounts.js +++ b/web/app/assets/javascripts/accounts.js @@ -105,9 +105,11 @@ } function renderAccount() { + app.user().done(function() { rest.getUserDetail() - .done(populateAccount) - .error(app.ajaxError) + .done(populateAccount) + .error(app.ajaxError) + }) } function navToScheduledSessions() { diff --git a/web/app/assets/javascripts/dialog/loginRequiredDialog.js b/web/app/assets/javascripts/dialog/loginRequiredDialog.js new file mode 100644 index 000000000..6795614c1 --- /dev/null +++ b/web/app/assets/javascripts/dialog/loginRequiredDialog.js @@ -0,0 +1,45 @@ +(function(context,$) { + + "use strict"; + context.JK = context.JK || {}; + context.JK.LoginRequiredDialog = function(app) { + var logger = context.JK.logger; + var $dialog = null; + var dialogId = 'login-required-dialog'; + + function beforeShow(data) { + } + + function afterShow(data) { + } + + function afterHide() { + } + + function events() { + $dialog.find('.go-to-jamtracks').click(function() { + app.layout.closeDialog(dialogId) + context.location.href = $(this).attr('href') + }) + } + + function initialize() { + + var dialogBindings = { + 'beforeShow' : beforeShow, + 'afterShow' : afterShow, + 'afterHide': afterHide + }; + + app.bindDialog(dialogId, dialogBindings); + + $dialog = $('#' + dialogId); + + events(); + } + + this.initialize = initialize; + }; + + return this; +})(window,jQuery); \ No newline at end of file diff --git a/web/app/assets/javascripts/everywhere/everywhere.js b/web/app/assets/javascripts/everywhere/everywhere.js index 4564cd9b0..b8c0f62d5 100644 --- a/web/app/assets/javascripts/everywhere/everywhere.js +++ b/web/app/assets/javascripts/everywhere/everywhere.js @@ -127,6 +127,9 @@ var clientPreferencesDialog = new JK.ClientPreferencesDialog(app); clientPreferencesDialog.initialize(); + + var loginRequiredDialog = new JK.LoginRequiredDialog(app); + loginRequiredDialog.initialize(); } // wait 10 seconds diff --git a/web/app/assets/javascripts/findSession.js b/web/app/assets/javascripts/findSession.js index f54feb866..d3c4638c2 100644 --- a/web/app/assets/javascripts/findSession.js +++ b/web/app/assets/javascripts/findSession.js @@ -57,9 +57,11 @@ }) .fail(function(xhr, textStatus, errorMessage) { if (xhr.status === 404) { + logger.warn("unable to list active sessions (404)") // swallow 404 } else { + logger.warn("unable to list active sessions") app.ajaxError(xhr, textStatus, errorMessage); } }) diff --git a/web/app/assets/javascripts/homeScreen.js b/web/app/assets/javascripts/homeScreen.js index 997927dbd..ec7b86d50 100644 --- a/web/app/assets/javascripts/homeScreen.js +++ b/web/app/assets/javascripts/homeScreen.js @@ -6,6 +6,7 @@ context.JK.HomeScreen = function(app) { var logger = context.JK.logger; var isFtueComplete = false; + var $screen = null; function beforeShow(data) { } @@ -86,9 +87,20 @@ var screenBindings = { 'beforeShow': beforeShow }; app.bindScreen('home', screenBindings); events(); + $screen = $('.screen[layout-id="home"]') - $('.profile').on('click', function() { + $screen.find('.profile').on('click', function() { + var $destination = $('[layout-id="profile"]'); + if(!context.JK.currentUserId && !$destination.is('.no-login-required')) { + // if there is no user and login is required, then stop user from clicknig through + app.layout.showDialog('login-required-dialog') + } + else + { context.location = '/client#/profile/' + context.JK.currentUserId; + } + + }); }; diff --git a/web/app/assets/javascripts/jam_track_utils.js.coffee b/web/app/assets/javascripts/jam_track_utils.js.coffee index 2935665cc..8ba73f592 100644 --- a/web/app/assets/javascripts/jam_track_utils.js.coffee +++ b/web/app/assets/javascripts/jam_track_utils.js.coffee @@ -16,7 +16,7 @@ class JamTrackUtils @rest.getShoppingCarts().done(this.displayCartIcon) displayCartIcon: (carts) => - cartLink = $("a[href='" + "/client#/shoppingCart" + "']") + cartLink = $("#header-shopping-cart") if carts.length > 0 cartLink.removeClass("hidden") else diff --git a/web/app/assets/javascripts/jamkazam.js b/web/app/assets/javascripts/jamkazam.js index db0615510..2bc31b957 100644 --- a/web/app/assets/javascripts/jamkazam.js +++ b/web/app/assets/javascripts/jamkazam.js @@ -132,6 +132,9 @@ logger.error("Unexpected ajax error: " + textStatus + ", msg:" + errorMessage); app.notify({title: "Oops!", text: "What you were looking for is gone now."}); } + else if(jqXHR.status === 403) { + logger.debug("not logged in"); + } else if (jqXHR.status === 422) { logger.error("Unexpected ajax error: " + textStatus + ", msg: " + errorMessage + ", response: " + jqXHR.responseText); // present a nicer message @@ -282,20 +285,37 @@ var hash = context.location.hash; + var screen = 'home' try { - context.RouteMap.parse(hash); + var location = context.RouteMap.parse(hash); + screen = location.page.substring(1); // remove leading slash } catch (e) { logger.debug("ignoring bogus screen name: %o", hash) hash = null; } - var url = '/client#/home'; + + var $destination = $('[layout-id="' + screen + '"]'); + + if(!context.JK.currentUserId && !$destination.is('.no-login-required')) { + logger.debug("not logged in so redirected to login from screen: " + screen) + var redirectPath= '?redirect-to=' + encodeURIComponent(JK.locationPath()); + if(gon.isNativeClient) { + window.location.href = '/signin' + redirectPath; + } + else { + window.location.href = '/' + redirectPath; + } + return; + } + + var url = '/client#/' + screen; if (hash) { url = hash; } - logger.debug("Changing screen to " + url); + logger.debug("jamkazam: Changing screen to " + url + " (hash=" + hash + ")") ; context.location = url; } @@ -377,7 +397,11 @@ app.notify({title: "Unable to Load User", text: "You should reload the page."}) }); } - } // if userDeferred + } + else { + userDeferred = new $.Deferred(); + userDeferred.reject('not_logged_in'); + } $(document).triggerHandler('JAMKAZAM_READY', {app:app}) diff --git a/web/app/assets/javascripts/layout.js b/web/app/assets/javascripts/layout.js index 6c40f596d..1179f71b2 100644 --- a/web/app/assets/javascripts/layout.js +++ b/web/app/assets/javascripts/layout.js @@ -415,8 +415,15 @@ } var destination = $(evt.currentTarget).attr('layout-link'); - var destinationType = $('[layout-id="' + destination + '"]').attr("layout"); + var $destination = $('[layout-id="' + destination + '"]'); + + var destinationType = $destination.attr("layout"); if (destinationType === "screen") { + if(!context.JK.currentUserId && !$destination.is('.no-login-required')) { + // there is no user, and this item does not support 'no-login', so warn user + showDialog('login-required-dialog'); + return; + } context.location = '/client#/' + destination; } else if (destinationType === "dialog") { showDialog(destination); @@ -548,7 +555,7 @@ var accepted = screenEvent(previousScreen, 'beforeHide', data); if(accepted === false) return; - logger.debug("Changing screen to " + currentScreen); + logger.debug("layout: changing screen to " + currentScreen); $(document).triggerHandler(EVENTS.SCREEN_CHANGED, {previousScreen: previousScreen, newScreen: currentScreen}) @@ -695,6 +702,7 @@ return null; } logger.debug("opening dialog: " + dialog) + var $overlay = $('.dialog-overlay') if (opts.sizeOverlayToContent) { @@ -727,6 +735,12 @@ function panelHeaderClicked(evt) { evt.preventDefault(); + + if(!context.JK.currentUserId) { + showDialog('login-required-dialog'); + return false; + } + expandedPanel = $(evt.currentTarget).closest('[layout="panel"]').attr("layout-id"); layout(); return false; diff --git a/web/app/assets/javascripts/notificationPanel.js b/web/app/assets/javascripts/notificationPanel.js index 7c3e608de..c6e1f25bc 100644 --- a/web/app/assets/javascripts/notificationPanel.js +++ b/web/app/assets/javascripts/notificationPanel.js @@ -213,7 +213,7 @@ }) .fail(function() { isLoading = false; - app.ajaxError(); + app.ajaxError(arguments); }) } @@ -1375,7 +1375,9 @@ events(); - populate(); + app.user().done(function(){ + populate(); + }) }; this.initialize = initialize; diff --git a/web/app/assets/javascripts/scheduled_session.js.erb b/web/app/assets/javascripts/scheduled_session.js.erb index 969f9449d..cf75275e1 100644 --- a/web/app/assets/javascripts/scheduled_session.js.erb +++ b/web/app/assets/javascripts/scheduled_session.js.erb @@ -1054,7 +1054,7 @@ context.JK.GenreSelectorHelper.render('#create-session-genre'); - inviteMusiciansUtil.loadFriends(); + //inviteMusiciansUtil.loadFriends(); context.JK.dropdown($screen.find('#session-musician-access')); context.JK.dropdown($screen.find('#session-fans-access')); diff --git a/web/app/assets/javascripts/sidebar.js b/web/app/assets/javascripts/sidebar.js index d60d59868..cf733c451 100644 --- a/web/app/assets/javascripts/sidebar.js +++ b/web/app/assets/javascripts/sidebar.js @@ -13,6 +13,7 @@ var notificationPanel = null; var chatPanel = null; var me = null; + var $sidebar = null; function initializeSearchPanel() { $('#search_text_type').change(function() { @@ -39,7 +40,9 @@ function initializeFriendsPanel() { $('#sidebar-search-header').hide(); - refreshFriends(); + app.user().done(function() { + refreshFriends(); + }) return false; } @@ -406,11 +409,24 @@ me = this; invitationDialog = invitationDialogInstance; textMessageDialog = textMessageDialogInstance; - events(); - initializeSearchPanel(); - initializeFriendsPanel(); - initializeChatPanel(); - initializeNotificationsPanel(); + $sidebar = $('#sidebar-div') + app.user() + .done(function() { + events(); + initializeSearchPanel(); + initializeFriendsPanel(); + initializeChatPanel(); + initializeNotificationsPanel(); + }) + .fail(function(arg1) { + if(arg1 == "not_logged_in") { + $('#search-input').attr('disabled', 'disabled') + $('.sidebar .invite-friend-row').click(function() { + app.layout.showDialog('login-required-dialog') + }); + $sidebar.addClass('not-logged-in') + } + }) }; this.refreshFriends = refreshFriends; diff --git a/web/app/assets/stylesheets/client/home.css.scss b/web/app/assets/stylesheets/client/home.css.scss index d9b7d9d45..9263fa2d7 100644 --- a/web/app/assets/stylesheets/client/home.css.scss +++ b/web/app/assets/stylesheets/client/home.css.scss @@ -8,6 +8,10 @@ background-repeat: no-repeat; background-position: bottom left; border: 1px solid $translucent1; + + &.not-logged-in { + opacity:0.6; + } } .homecard.createsession { background-image: url(/assets/content/bkg_home_create.jpg); diff --git a/web/app/assets/stylesheets/client/sidebar.css.scss b/web/app/assets/stylesheets/client/sidebar.css.scss index 17c975616..59a3bdfb5 100644 --- a/web/app/assets/stylesheets/client/sidebar.css.scss +++ b/web/app/assets/stylesheets/client/sidebar.css.scss @@ -5,6 +5,10 @@ background-color: $ColorElementPrimary; + &.not-logged-in { + opacity:0.6; + } + .panel-header { margin:0px; padding:0px; diff --git a/web/app/assets/stylesheets/dialogs/loginRequiredDialog.css.scss b/web/app/assets/stylesheets/dialogs/loginRequiredDialog.css.scss new file mode 100644 index 000000000..bc27a60ed --- /dev/null +++ b/web/app/assets/stylesheets/dialogs/loginRequiredDialog.css.scss @@ -0,0 +1,12 @@ +#login-required-dialog { + + width:455px; + + p { + margin:0 0 20px 0; + } + + .buttons { + margin-top:20px; + } +} \ No newline at end of file diff --git a/web/app/controllers/api_genres_controller.rb b/web/app/controllers/api_genres_controller.rb index c413bf1f2..293552737 100644 --- a/web/app/controllers/api_genres_controller.rb +++ b/web/app/controllers/api_genres_controller.rb @@ -1,8 +1,5 @@ class ApiGenresController < ApiController - # have to be signed in currently to see this screen - before_filter :api_signed_in_user - respond_to :json def index diff --git a/web/app/controllers/api_instruments_controller.rb b/web/app/controllers/api_instruments_controller.rb index fc2c44fd9..c5f10bd49 100644 --- a/web/app/controllers/api_instruments_controller.rb +++ b/web/app/controllers/api_instruments_controller.rb @@ -1,8 +1,5 @@ class ApiInstrumentsController < ApiController - # have to be signed in currently to see this screen - before_filter :api_signed_in_user - respond_to :json def index diff --git a/web/app/helpers/sessions_helper.rb b/web/app/helpers/sessions_helper.rb index 3545c3d05..6665b8c00 100644 --- a/web/app/helpers/sessions_helper.rb +++ b/web/app/helpers/sessions_helper.rb @@ -26,6 +26,10 @@ module SessionsHelper !current_user.nil? end + def logged_in_not_logged_in_class + signed_in? ? "logged-in" : "not-logged-in" + end + def current_user=(user) @current_user = user end diff --git a/web/app/views/clients/_header.html.erb b/web/app/views/clients/_header.html.erb index 855f0e988..de9015e66 100644 --- a/web/app/views/clients/_header.html.erb +++ b/web/app/views/clients/_header.html.erb @@ -13,8 +13,8 @@ <% if Rails.application.config.jam_tracks_available %> - - + <% end %> diff --git a/web/app/views/clients/_home.html.slim b/web/app/views/clients/_home.html.slim index 8130e82cc..2c07e10b2 100644 --- a/web/app/views/clients/_home.html.slim +++ b/web/app/views/clients/_home.html.slim @@ -1,4 +1,4 @@ -.screen layout="screen" layout-id="home" +.screen.no-login-required layout="screen" layout-id="home" -if Rails.configuration.show_jamblaster_notice #jamblaster-notice a href='https://www.youtube.com/watch?v=gAJAIHMyois' rel="external" @@ -15,23 +15,23 @@ / individual spells span those spaces -if @nativeClient .grid layout-grid="2x12" - .homecard.createsession layout-grid-columns="4" layout-grid-position="0,0" layout-grid-rows="1" layout-link="createSession" type="createSession" + .homecard.createsession layout-grid-columns="4" layout-grid-position="0,0" layout-grid-rows="1" layout-link="createSession" type="createSession" class="#{logged_in_not_logged_in_class}" h2 create session .homebox-info /! 4 friends online, 2 currently in sessions - .homecard.findsession layout-grid-columns="4" layout-grid-position="4,0" layout-grid-rows="1" layout-link="findSession" type="findSession" + .homecard.findsession layout-grid-columns="4" layout-grid-position="4,0" layout-grid-rows="1" layout-link="findSession" type="findSession" class="#{logged_in_not_logged_in_class}" 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" class="#{logged_in_not_logged_in_class}" h2 feed .homebox-info /! 4 friends online, 2 currently in sessions - .homecard.musicians layout-grid-columns=small_tile_size layout-grid-position=column_positions[0] layout-grid-rows="1" layout-link="musicians" + .homecard.musicians layout-grid-columns=small_tile_size layout-grid-position=column_positions[0] layout-grid-rows="1" layout-link="musicians" class="#{logged_in_not_logged_in_class}" h2 musicians .homebox-info /! 5 followers, 3 following - .homecard.bands layout-grid-columns=small_tile_size layout-grid-position=column_positions[1] layout-grid-rows="1" layout-link="bands" + .homecard.bands layout-grid-columns=small_tile_size layout-grid-position=column_positions[1] layout-grid-rows="1" layout-link="bands" class="#{logged_in_not_logged_in_class}" h2 bands .homebox-info /! 1 session invitation, 19 public sessions active @@ -40,33 +40,33 @@ h2 jamtracks .homebox-info /! 5 followers, 3 following - .homecard.profile layout-grid-columns=small_tile_size layout-grid-position=column_positions[3] layout-grid-rows="1" + .homecard.profile layout-grid-columns=small_tile_size layout-grid-position=column_positions[3] layout-grid-rows="1" class="#{logged_in_not_logged_in_class}" h2 profile .homebox-info /! 5 followers, 3 following - .homecard.account layout-grid-columns=small_tile_size layout-grid-position=column_positions[4] layout-grid-rows="1" layout-link="account" + .homecard.account layout-grid-columns=small_tile_size layout-grid-position=column_positions[4] layout-grid-rows="1" layout-link="account" class="#{logged_in_not_logged_in_class}" h2 account .homebox-info /! free service level -else .grid layout-grid="2x12" - .homecard.createsession layout-grid-columns="4" layout-grid-position="0,0" layout-grid-rows="1" layout-link="createSession" type="createSession" + .homecard.createsession layout-grid-columns="4" layout-grid-position="0,0" layout-grid-rows="1" layout-link="createSession" type="createSession" class="#{logged_in_not_logged_in_class}" h2 create session .homebox-info /! 4 friends online, 2 currently in sessions - .homecard.findsession layout-grid-columns="4" layout-grid-position="4,0" layout-grid-rows="1" layout-link="findSession" type="findSession" + .homecard.findsession layout-grid-columns="4" layout-grid-position="4,0" layout-grid-rows="1" layout-link="findSession" type="findSession" class="#{logged_in_not_logged_in_class}" 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" class="#{logged_in_not_logged_in_class}" h2 feed .homebox-info /! 4 friends online, 2 currently in sessions - .homecard.musicians layout-grid-columns=small_tile_size layout-grid-position=column_positions[0] layout-grid-rows="1" layout-link="musicians" + .homecard.musicians layout-grid-columns=small_tile_size layout-grid-position=column_positions[0] layout-grid-rows="1" layout-link="musicians" class="#{logged_in_not_logged_in_class}" h2 musicians .homebox-info /! 5 followers, 3 following - .homecard.bands layout-grid-columns=small_tile_size layout-grid-position=column_positions[1] layout-grid-rows="1" layout-link="bands" + .homecard.bands layout-grid-columns=small_tile_size layout-grid-position=column_positions[1] layout-grid-rows="1" layout-link="bands" class="#{logged_in_not_logged_in_class}" h2 bands .homebox-info -if jamtracks @@ -75,11 +75,11 @@ h2 jamtracks .homebox-info /! 5 followers, 3 following - .homecard.profile layout-grid-columns=small_tile_size layout-grid-position=column_positions[3] layout-grid-rows="1" + .homecard.profile layout-grid-columns=small_tile_size layout-grid-position=column_positions[3] layout-grid-rows="1" class="#{logged_in_not_logged_in_class}" h2 profile .homebox-info /! 5 followers, 3 following - .homecard.account layout-grid-columns=small_tile_size layout-grid-position=column_positions[4] layout-grid-rows="1" layout-link="account" + .homecard.account layout-grid-columns=small_tile_size layout-grid-position=column_positions[4] layout-grid-rows="1" layout-link="account" class="#{logged_in_not_logged_in_class}" h2 account .homebox-info /! free service level diff --git a/web/app/views/clients/_jamtrack.html.haml b/web/app/views/clients/_jamtrack.html.haml index ba076ee19..bbb85a697 100644 --- a/web/app/views/clients/_jamtrack.html.haml +++ b/web/app/views/clients/_jamtrack.html.haml @@ -1,4 +1,4 @@ -%div{ layout: 'screen', :'layout-id' => 'jamtrack', id: 'jamtrackScreen', :class => 'screen secondary'} +%div{ layout: 'screen', :'layout-id' => 'jamtrack', id: 'jamtrackScreen', :class => 'screen secondary no-login-required'} .content .content-head .content-icon= image_tag("content/icon_jamtracks.png", {:height => 19, :width => 19}) diff --git a/web/app/views/clients/index.html.erb b/web/app/views/clients/index.html.erb index 3f1ad8a1b..ebba73cf6 100644 --- a/web/app/views/clients/index.html.erb +++ b/web/app/views/clients/index.html.erb @@ -109,20 +109,7 @@ JK.currentUserName = null; JK.currentUserMusician = null; JK.currentUserAdmin = false; - - // you need to be logged in to use this part of the interface. - // save original URL, and redirect to the home page - logger.debug("redirecting back to / because not logged in") - - var redirectPath= '?redirect-to=' + encodeURIComponent(JK.locationPath()); - if(gon.isNativeClient) { - window.location.href = '/signin' + redirectPath; - } - else { - window.location.href = '/' + redirectPath; - } - - <% end %> + <% end %> // Some things can't be initialized until we're connected. Put them here. @@ -289,6 +276,8 @@ JK.ClientUpdateInstance.check() JK.app.initialRouting(); + + JK.hideCurtain(300); } @@ -304,10 +293,10 @@ JK.RecordingUtils.init(); - // Let's get things rolling... - if (JK.currentUserId) { + JK.app.initialize(); - JK.app.initialize(); + // Let's get things rolling... + if (JK.currentUserId) { JK.JamServer.registerMessageCallback(JK.MessageType.CLIENT_UPDATE, function(header, payload) { // do a client update early check upon initialization @@ -315,7 +304,7 @@ }); - JK.TickDuration('.feed-entry.music-session-history-entry .inprogress .tick-duration'); + JK.TickDuration('.feed-entry.music-session-history-entry .inprogress .tick-duration'); JK.JamServer.connect() // singleton here defined in JamServer.js .done(function() { @@ -328,6 +317,9 @@ // this ensures that there is always a CurrentSessionModel, even if it's for a non-active session JK.CurrentSessionModel = new JK.SessionModel(JK.app, JK.JamServer, window.jamClient, null); } + else { + _initAfterConnect(false); + } JK.bindHoverEvents(); }) diff --git a/web/app/views/dialogs/_dialogs.html.haml b/web/app/views/dialogs/_dialogs.html.haml index 2b7ac1206..164eb93f8 100644 --- a/web/app/views/dialogs/_dialogs.html.haml +++ b/web/app/views/dialogs/_dialogs.html.haml @@ -34,3 +34,4 @@ = render 'dialogs/adjustGearSpeedDialog' = render 'dialogs/openJamTrackDialog' = render 'dialogs/openBackingTrackDialog' += render 'dialogs/loginRequiredDialog' diff --git a/web/app/views/dialogs/_loginRequiredDialog.html.slim b/web/app/views/dialogs/_loginRequiredDialog.html.slim new file mode 100644 index 000000000..4b979e68d --- /dev/null +++ b/web/app/views/dialogs/_loginRequiredDialog.html.slim @@ -0,0 +1,20 @@ +.dialog.dialog-overlay-sm layout='dialog' layout-id='login-required-dialog' id='login-required-dialog' + .content-head + = image_tag "content/icon_alert.png", {:width => 24, :height => 24, :class => 'content-icon' } + h1 Login Required + + .dialog-inner + p + a href="/signup" Sign Up + |  or  + a href="/signin" Sign In + |  to access most functionality on this page. + p + | However, you can browse for  + a class="go-to-jamtracks" href='/client#/jamtrack' JamTracks + |  without logging in. + br + .clearall + .buttons + .right + a.button-orange class='btnClose' layout-action='close' CLOSE \ No newline at end of file diff --git a/web/spec/controllers/api_jam_tracks_controller_spec.rb b/web/spec/controllers/api_jam_tracks_controller_spec.rb index 357cb8737..baeb7d1e3 100644 --- a/web/spec/controllers/api_jam_tracks_controller_spec.rb +++ b/web/spec/controllers/api_jam_tracks_controller_spec.rb @@ -60,19 +60,6 @@ describe ApiJamTracksController do json["next"].should be_nil json["jamtracks"].length.should == 2 end - - it "lists owned tracks" do - get :downloads - response.should be_success - json = JSON.parse(response.body) - json['downloads'].should have(0).items - - right = JamTrackRight.create(:user=>@user, :jam_track=>@jam_track) - get :downloads - response.should be_success - json = JSON.parse(response.body) - json['downloads'].should have(1).items - end it "finds a download" do #get "/download/#{right.id}/" diff --git a/web/spec/features/authentication_pages_spec.rb b/web/spec/features/authentication_pages_spec.rb index 7aea9cb9d..70aae8a2b 100644 --- a/web/spec/features/authentication_pages_spec.rb +++ b/web/spec/features/authentication_pages_spec.rb @@ -62,7 +62,8 @@ describe "Authentication", :js => true, :type => :feature, :capybara_feature => find('.userinfo .sign-out a').trigger(:click) end - it { find('h1', text: 'Play music together over the Internet as if in the same room') } + # after logging out, we keep you at /client + it { find('#profile a.signin', text: 'Sign Up') } end end end diff --git a/web/spec/features/in_session_spec.rb b/web/spec/features/in_session_spec.rb index ef96b1deb..a17f0d16a 100644 --- a/web/spec/features/in_session_spec.rb +++ b/web/spec/features/in_session_spec.rb @@ -28,7 +28,9 @@ describe "In a Session", :js => true, :type => :feature, :capybara_feature => tr sign_in_poltergeist finder visit "/client#/findSession" expect(page).to have_selector('#no-active-sessions') # verify private session is not found - sign_out_poltergeist(validate: true) + #sign_out_poltergeist(validate: true) + visit "/" + should_be_at_root end in_client(user) do set_session_access :public diff --git a/web/spec/features/profile_menu_spec.rb b/web/spec/features/profile_menu_spec.rb index 68ea06c8b..a4d0e15e2 100644 --- a/web/spec/features/profile_menu_spec.rb +++ b/web/spec/features/profile_menu_spec.rb @@ -53,7 +53,7 @@ describe "Profile Menu", :js => true, :type => :feature, :capybara_feature => tr click_link 'Sign Out' end - it { should_be_at_root } + it { should_be_at_logged_out_client } end describe "Download App link" do diff --git a/web/spec/features/signin_spec.rb b/web/spec/features/signin_spec.rb index 689b8251b..ad4f7dfec 100644 --- a/web/spec/features/signin_spec.rb +++ b/web/spec/features/signin_spec.rb @@ -175,7 +175,7 @@ describe "signin" do click_button "SIGN IN" end - find('h1', text: 'Play music together over the Internet as if in the same room') + should_be_at_logged_out_client end # if a cookie with the default domain is found with another, delete the one with the default domain @@ -201,7 +201,7 @@ describe "signin" do click_button "SIGN IN" end - find('h1', text: 'Play music together over the Internet as if in the same room') + should_be_at_logged_out_client delete_called.should be_true end @@ -211,6 +211,23 @@ describe "signin" do sign_in_poltergeist(user) sign_out_poltergeist + + wait_until_curtain_gone + + # musicians homecard should be disabled + find('.homecard.musicians.not-logged-in').trigger(:click) + find('h1', text: 'Login Required') + find('.btnClose').trigger(:click) + + # profile homecard should be disabled (this one is handled in homeScreen.js instead of in layout.js) + find('.homecard.profile.not-logged-in').trigger(:click) + find('h1', text: 'Login Required') + find('.btnClose').trigger(:click) + + # sidebar should be disabled + find('[layout-id="panelSearch"] [layout-panel="expanded"] [layout-panel="header"]').trigger(:click) + find('h1', text: 'Login Required') + find('.btnClose').trigger(:click) end diff --git a/web/spec/support/utilities.rb b/web/spec/support/utilities.rb index 8982fd5a1..7a89d9b23 100644 --- a/web/spec/support/utilities.rb +++ b/web/spec/support/utilities.rb @@ -200,7 +200,7 @@ end def sign_out_poltergeist(options = {}) open_user_dropdown click_link 'Sign Out' - should_be_at_signin if options[:validate] + should_be_at_logged_out_client if options[:validate] end def open_user_dropdown @@ -221,6 +221,11 @@ def should_be_at_signin find('h1', text: 'sign in or register') end +def should_be_at_logged_out_client + find('#profile a.signin', text: 'Sign Up') + find('.musicians.not-logged-in') +end + def leave_music_session_sleep_delay # add a buffer to ensure WSG has enough time to expire sleep_dur = (Rails.application.config.websocket_gateway_connect_time_stale_browser + From 477df086b0172cc7cc75d4b012076476e6d67bd5 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 11 Mar 2015 16:53:12 -0500 Subject: [PATCH 18/65] Revert "* VRFS-2913 - allow /client to be accessed without being logged in" This reverts commit 0a869e91e3abd2aa6d2b90dc8000281465e59309. --- web/app/assets/javascripts/accounts.js | 6 +-- .../javascripts/dialog/loginRequiredDialog.js | 45 ------------------- .../javascripts/everywhere/everywhere.js | 3 -- web/app/assets/javascripts/findSession.js | 2 - web/app/assets/javascripts/homeScreen.js | 14 +----- .../javascripts/jam_track_utils.js.coffee | 2 +- web/app/assets/javascripts/jamkazam.js | 32 ++----------- web/app/assets/javascripts/layout.js | 18 +------- .../assets/javascripts/notificationPanel.js | 6 +-- .../javascripts/scheduled_session.js.erb | 2 +- web/app/assets/javascripts/sidebar.js | 28 +++--------- .../assets/stylesheets/client/home.css.scss | 4 -- .../stylesheets/client/sidebar.css.scss | 4 -- .../dialogs/loginRequiredDialog.css.scss | 12 ----- web/app/controllers/api_genres_controller.rb | 3 ++ .../controllers/api_instruments_controller.rb | 3 ++ web/app/helpers/sessions_helper.rb | 4 -- web/app/views/clients/_header.html.erb | 4 +- web/app/views/clients/_home.html.slim | 30 ++++++------- web/app/views/clients/_jamtrack.html.haml | 2 +- web/app/views/clients/index.html.erb | 28 +++++++----- web/app/views/dialogs/_dialogs.html.haml | 1 - .../dialogs/_loginRequiredDialog.html.slim | 20 --------- .../api_jam_tracks_controller_spec.rb | 13 ++++++ .../features/authentication_pages_spec.rb | 3 +- web/spec/features/in_session_spec.rb | 4 +- web/spec/features/profile_menu_spec.rb | 2 +- web/spec/features/signin_spec.rb | 21 +-------- web/spec/support/utilities.rb | 7 +-- 29 files changed, 80 insertions(+), 243 deletions(-) delete mode 100644 web/app/assets/javascripts/dialog/loginRequiredDialog.js delete mode 100644 web/app/assets/stylesheets/dialogs/loginRequiredDialog.css.scss delete mode 100644 web/app/views/dialogs/_loginRequiredDialog.html.slim diff --git a/web/app/assets/javascripts/accounts.js b/web/app/assets/javascripts/accounts.js index 90b0c4e5e..64501695d 100644 --- a/web/app/assets/javascripts/accounts.js +++ b/web/app/assets/javascripts/accounts.js @@ -105,11 +105,9 @@ } function renderAccount() { - app.user().done(function() { rest.getUserDetail() - .done(populateAccount) - .error(app.ajaxError) - }) + .done(populateAccount) + .error(app.ajaxError) } function navToScheduledSessions() { diff --git a/web/app/assets/javascripts/dialog/loginRequiredDialog.js b/web/app/assets/javascripts/dialog/loginRequiredDialog.js deleted file mode 100644 index 6795614c1..000000000 --- a/web/app/assets/javascripts/dialog/loginRequiredDialog.js +++ /dev/null @@ -1,45 +0,0 @@ -(function(context,$) { - - "use strict"; - context.JK = context.JK || {}; - context.JK.LoginRequiredDialog = function(app) { - var logger = context.JK.logger; - var $dialog = null; - var dialogId = 'login-required-dialog'; - - function beforeShow(data) { - } - - function afterShow(data) { - } - - function afterHide() { - } - - function events() { - $dialog.find('.go-to-jamtracks').click(function() { - app.layout.closeDialog(dialogId) - context.location.href = $(this).attr('href') - }) - } - - function initialize() { - - var dialogBindings = { - 'beforeShow' : beforeShow, - 'afterShow' : afterShow, - 'afterHide': afterHide - }; - - app.bindDialog(dialogId, dialogBindings); - - $dialog = $('#' + dialogId); - - events(); - } - - this.initialize = initialize; - }; - - return this; -})(window,jQuery); \ No newline at end of file diff --git a/web/app/assets/javascripts/everywhere/everywhere.js b/web/app/assets/javascripts/everywhere/everywhere.js index b8c0f62d5..4564cd9b0 100644 --- a/web/app/assets/javascripts/everywhere/everywhere.js +++ b/web/app/assets/javascripts/everywhere/everywhere.js @@ -127,9 +127,6 @@ var clientPreferencesDialog = new JK.ClientPreferencesDialog(app); clientPreferencesDialog.initialize(); - - var loginRequiredDialog = new JK.LoginRequiredDialog(app); - loginRequiredDialog.initialize(); } // wait 10 seconds diff --git a/web/app/assets/javascripts/findSession.js b/web/app/assets/javascripts/findSession.js index d3c4638c2..f54feb866 100644 --- a/web/app/assets/javascripts/findSession.js +++ b/web/app/assets/javascripts/findSession.js @@ -57,11 +57,9 @@ }) .fail(function(xhr, textStatus, errorMessage) { if (xhr.status === 404) { - logger.warn("unable to list active sessions (404)") // swallow 404 } else { - logger.warn("unable to list active sessions") app.ajaxError(xhr, textStatus, errorMessage); } }) diff --git a/web/app/assets/javascripts/homeScreen.js b/web/app/assets/javascripts/homeScreen.js index ec7b86d50..997927dbd 100644 --- a/web/app/assets/javascripts/homeScreen.js +++ b/web/app/assets/javascripts/homeScreen.js @@ -6,7 +6,6 @@ context.JK.HomeScreen = function(app) { var logger = context.JK.logger; var isFtueComplete = false; - var $screen = null; function beforeShow(data) { } @@ -87,20 +86,9 @@ var screenBindings = { 'beforeShow': beforeShow }; app.bindScreen('home', screenBindings); events(); - $screen = $('.screen[layout-id="home"]') - $screen.find('.profile').on('click', function() { - var $destination = $('[layout-id="profile"]'); - if(!context.JK.currentUserId && !$destination.is('.no-login-required')) { - // if there is no user and login is required, then stop user from clicknig through - app.layout.showDialog('login-required-dialog') - } - else - { + $('.profile').on('click', function() { context.location = '/client#/profile/' + context.JK.currentUserId; - } - - }); }; diff --git a/web/app/assets/javascripts/jam_track_utils.js.coffee b/web/app/assets/javascripts/jam_track_utils.js.coffee index 8ba73f592..2935665cc 100644 --- a/web/app/assets/javascripts/jam_track_utils.js.coffee +++ b/web/app/assets/javascripts/jam_track_utils.js.coffee @@ -16,7 +16,7 @@ class JamTrackUtils @rest.getShoppingCarts().done(this.displayCartIcon) displayCartIcon: (carts) => - cartLink = $("#header-shopping-cart") + cartLink = $("a[href='" + "/client#/shoppingCart" + "']") if carts.length > 0 cartLink.removeClass("hidden") else diff --git a/web/app/assets/javascripts/jamkazam.js b/web/app/assets/javascripts/jamkazam.js index 2bc31b957..db0615510 100644 --- a/web/app/assets/javascripts/jamkazam.js +++ b/web/app/assets/javascripts/jamkazam.js @@ -132,9 +132,6 @@ logger.error("Unexpected ajax error: " + textStatus + ", msg:" + errorMessage); app.notify({title: "Oops!", text: "What you were looking for is gone now."}); } - else if(jqXHR.status === 403) { - logger.debug("not logged in"); - } else if (jqXHR.status === 422) { logger.error("Unexpected ajax error: " + textStatus + ", msg: " + errorMessage + ", response: " + jqXHR.responseText); // present a nicer message @@ -285,37 +282,20 @@ var hash = context.location.hash; - var screen = 'home' try { - var location = context.RouteMap.parse(hash); - screen = location.page.substring(1); // remove leading slash + context.RouteMap.parse(hash); } catch (e) { logger.debug("ignoring bogus screen name: %o", hash) hash = null; } - - var $destination = $('[layout-id="' + screen + '"]'); - - if(!context.JK.currentUserId && !$destination.is('.no-login-required')) { - logger.debug("not logged in so redirected to login from screen: " + screen) - var redirectPath= '?redirect-to=' + encodeURIComponent(JK.locationPath()); - if(gon.isNativeClient) { - window.location.href = '/signin' + redirectPath; - } - else { - window.location.href = '/' + redirectPath; - } - return; - } - - var url = '/client#/' + screen; + var url = '/client#/home'; if (hash) { url = hash; } - logger.debug("jamkazam: Changing screen to " + url + " (hash=" + hash + ")") ; + logger.debug("Changing screen to " + url); context.location = url; } @@ -397,11 +377,7 @@ app.notify({title: "Unable to Load User", text: "You should reload the page."}) }); } - } - else { - userDeferred = new $.Deferred(); - userDeferred.reject('not_logged_in'); - } + } // if userDeferred $(document).triggerHandler('JAMKAZAM_READY', {app:app}) diff --git a/web/app/assets/javascripts/layout.js b/web/app/assets/javascripts/layout.js index 1179f71b2..6c40f596d 100644 --- a/web/app/assets/javascripts/layout.js +++ b/web/app/assets/javascripts/layout.js @@ -415,15 +415,8 @@ } var destination = $(evt.currentTarget).attr('layout-link'); - var $destination = $('[layout-id="' + destination + '"]'); - - var destinationType = $destination.attr("layout"); + var destinationType = $('[layout-id="' + destination + '"]').attr("layout"); if (destinationType === "screen") { - if(!context.JK.currentUserId && !$destination.is('.no-login-required')) { - // there is no user, and this item does not support 'no-login', so warn user - showDialog('login-required-dialog'); - return; - } context.location = '/client#/' + destination; } else if (destinationType === "dialog") { showDialog(destination); @@ -555,7 +548,7 @@ var accepted = screenEvent(previousScreen, 'beforeHide', data); if(accepted === false) return; - logger.debug("layout: changing screen to " + currentScreen); + logger.debug("Changing screen to " + currentScreen); $(document).triggerHandler(EVENTS.SCREEN_CHANGED, {previousScreen: previousScreen, newScreen: currentScreen}) @@ -702,7 +695,6 @@ return null; } logger.debug("opening dialog: " + dialog) - var $overlay = $('.dialog-overlay') if (opts.sizeOverlayToContent) { @@ -735,12 +727,6 @@ function panelHeaderClicked(evt) { evt.preventDefault(); - - if(!context.JK.currentUserId) { - showDialog('login-required-dialog'); - return false; - } - expandedPanel = $(evt.currentTarget).closest('[layout="panel"]').attr("layout-id"); layout(); return false; diff --git a/web/app/assets/javascripts/notificationPanel.js b/web/app/assets/javascripts/notificationPanel.js index c6e1f25bc..7c3e608de 100644 --- a/web/app/assets/javascripts/notificationPanel.js +++ b/web/app/assets/javascripts/notificationPanel.js @@ -213,7 +213,7 @@ }) .fail(function() { isLoading = false; - app.ajaxError(arguments); + app.ajaxError(); }) } @@ -1375,9 +1375,7 @@ events(); - app.user().done(function(){ - populate(); - }) + populate(); }; this.initialize = initialize; diff --git a/web/app/assets/javascripts/scheduled_session.js.erb b/web/app/assets/javascripts/scheduled_session.js.erb index cf75275e1..969f9449d 100644 --- a/web/app/assets/javascripts/scheduled_session.js.erb +++ b/web/app/assets/javascripts/scheduled_session.js.erb @@ -1054,7 +1054,7 @@ context.JK.GenreSelectorHelper.render('#create-session-genre'); - //inviteMusiciansUtil.loadFriends(); + inviteMusiciansUtil.loadFriends(); context.JK.dropdown($screen.find('#session-musician-access')); context.JK.dropdown($screen.find('#session-fans-access')); diff --git a/web/app/assets/javascripts/sidebar.js b/web/app/assets/javascripts/sidebar.js index cf733c451..d60d59868 100644 --- a/web/app/assets/javascripts/sidebar.js +++ b/web/app/assets/javascripts/sidebar.js @@ -13,7 +13,6 @@ var notificationPanel = null; var chatPanel = null; var me = null; - var $sidebar = null; function initializeSearchPanel() { $('#search_text_type').change(function() { @@ -40,9 +39,7 @@ function initializeFriendsPanel() { $('#sidebar-search-header').hide(); - app.user().done(function() { - refreshFriends(); - }) + refreshFriends(); return false; } @@ -409,24 +406,11 @@ me = this; invitationDialog = invitationDialogInstance; textMessageDialog = textMessageDialogInstance; - $sidebar = $('#sidebar-div') - app.user() - .done(function() { - events(); - initializeSearchPanel(); - initializeFriendsPanel(); - initializeChatPanel(); - initializeNotificationsPanel(); - }) - .fail(function(arg1) { - if(arg1 == "not_logged_in") { - $('#search-input').attr('disabled', 'disabled') - $('.sidebar .invite-friend-row').click(function() { - app.layout.showDialog('login-required-dialog') - }); - $sidebar.addClass('not-logged-in') - } - }) + events(); + initializeSearchPanel(); + initializeFriendsPanel(); + initializeChatPanel(); + initializeNotificationsPanel(); }; this.refreshFriends = refreshFriends; diff --git a/web/app/assets/stylesheets/client/home.css.scss b/web/app/assets/stylesheets/client/home.css.scss index 9263fa2d7..d9b7d9d45 100644 --- a/web/app/assets/stylesheets/client/home.css.scss +++ b/web/app/assets/stylesheets/client/home.css.scss @@ -8,10 +8,6 @@ background-repeat: no-repeat; background-position: bottom left; border: 1px solid $translucent1; - - &.not-logged-in { - opacity:0.6; - } } .homecard.createsession { background-image: url(/assets/content/bkg_home_create.jpg); diff --git a/web/app/assets/stylesheets/client/sidebar.css.scss b/web/app/assets/stylesheets/client/sidebar.css.scss index 59a3bdfb5..17c975616 100644 --- a/web/app/assets/stylesheets/client/sidebar.css.scss +++ b/web/app/assets/stylesheets/client/sidebar.css.scss @@ -5,10 +5,6 @@ background-color: $ColorElementPrimary; - &.not-logged-in { - opacity:0.6; - } - .panel-header { margin:0px; padding:0px; diff --git a/web/app/assets/stylesheets/dialogs/loginRequiredDialog.css.scss b/web/app/assets/stylesheets/dialogs/loginRequiredDialog.css.scss deleted file mode 100644 index bc27a60ed..000000000 --- a/web/app/assets/stylesheets/dialogs/loginRequiredDialog.css.scss +++ /dev/null @@ -1,12 +0,0 @@ -#login-required-dialog { - - width:455px; - - p { - margin:0 0 20px 0; - } - - .buttons { - margin-top:20px; - } -} \ No newline at end of file diff --git a/web/app/controllers/api_genres_controller.rb b/web/app/controllers/api_genres_controller.rb index 293552737..c413bf1f2 100644 --- a/web/app/controllers/api_genres_controller.rb +++ b/web/app/controllers/api_genres_controller.rb @@ -1,5 +1,8 @@ class ApiGenresController < ApiController + # have to be signed in currently to see this screen + before_filter :api_signed_in_user + respond_to :json def index diff --git a/web/app/controllers/api_instruments_controller.rb b/web/app/controllers/api_instruments_controller.rb index c5f10bd49..fc2c44fd9 100644 --- a/web/app/controllers/api_instruments_controller.rb +++ b/web/app/controllers/api_instruments_controller.rb @@ -1,5 +1,8 @@ class ApiInstrumentsController < ApiController + # have to be signed in currently to see this screen + before_filter :api_signed_in_user + respond_to :json def index diff --git a/web/app/helpers/sessions_helper.rb b/web/app/helpers/sessions_helper.rb index 6665b8c00..3545c3d05 100644 --- a/web/app/helpers/sessions_helper.rb +++ b/web/app/helpers/sessions_helper.rb @@ -26,10 +26,6 @@ module SessionsHelper !current_user.nil? end - def logged_in_not_logged_in_class - signed_in? ? "logged-in" : "not-logged-in" - end - def current_user=(user) @current_user = user end diff --git a/web/app/views/clients/_header.html.erb b/web/app/views/clients/_header.html.erb index de9015e66..855f0e988 100644 --- a/web/app/views/clients/_header.html.erb +++ b/web/app/views/clients/_header.html.erb @@ -13,8 +13,8 @@ <% if Rails.application.config.jam_tracks_available %> - + <% end %> diff --git a/web/app/views/clients/_home.html.slim b/web/app/views/clients/_home.html.slim index 2c07e10b2..8130e82cc 100644 --- a/web/app/views/clients/_home.html.slim +++ b/web/app/views/clients/_home.html.slim @@ -1,4 +1,4 @@ -.screen.no-login-required 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" @@ -15,23 +15,23 @@ / individual spells span those spaces -if @nativeClient .grid layout-grid="2x12" - .homecard.createsession layout-grid-columns="4" layout-grid-position="0,0" layout-grid-rows="1" layout-link="createSession" type="createSession" class="#{logged_in_not_logged_in_class}" + .homecard.createsession layout-grid-columns="4" layout-grid-position="0,0" layout-grid-rows="1" layout-link="createSession" type="createSession" h2 create session .homebox-info /! 4 friends online, 2 currently in sessions - .homecard.findsession layout-grid-columns="4" layout-grid-position="4,0" layout-grid-rows="1" layout-link="findSession" type="findSession" class="#{logged_in_not_logged_in_class}" + .homecard.findsession layout-grid-columns="4" layout-grid-position="4,0" layout-grid-rows="1" layout-link="findSession" type="findSession" 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" class="#{logged_in_not_logged_in_class}" + .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 - .homecard.musicians layout-grid-columns=small_tile_size layout-grid-position=column_positions[0] layout-grid-rows="1" layout-link="musicians" class="#{logged_in_not_logged_in_class}" + .homecard.musicians layout-grid-columns=small_tile_size layout-grid-position=column_positions[0] layout-grid-rows="1" layout-link="musicians" h2 musicians .homebox-info /! 5 followers, 3 following - .homecard.bands layout-grid-columns=small_tile_size layout-grid-position=column_positions[1] layout-grid-rows="1" layout-link="bands" class="#{logged_in_not_logged_in_class}" + .homecard.bands layout-grid-columns=small_tile_size layout-grid-position=column_positions[1] layout-grid-rows="1" layout-link="bands" h2 bands .homebox-info /! 1 session invitation, 19 public sessions active @@ -40,33 +40,33 @@ h2 jamtracks .homebox-info /! 5 followers, 3 following - .homecard.profile layout-grid-columns=small_tile_size layout-grid-position=column_positions[3] layout-grid-rows="1" class="#{logged_in_not_logged_in_class}" + .homecard.profile layout-grid-columns=small_tile_size layout-grid-position=column_positions[3] layout-grid-rows="1" h2 profile .homebox-info /! 5 followers, 3 following - .homecard.account layout-grid-columns=small_tile_size layout-grid-position=column_positions[4] layout-grid-rows="1" layout-link="account" class="#{logged_in_not_logged_in_class}" + .homecard.account layout-grid-columns=small_tile_size layout-grid-position=column_positions[4] layout-grid-rows="1" layout-link="account" h2 account .homebox-info /! free service level -else .grid layout-grid="2x12" - .homecard.createsession layout-grid-columns="4" layout-grid-position="0,0" layout-grid-rows="1" layout-link="createSession" type="createSession" class="#{logged_in_not_logged_in_class}" + .homecard.createsession layout-grid-columns="4" layout-grid-position="0,0" layout-grid-rows="1" layout-link="createSession" type="createSession" h2 create session .homebox-info /! 4 friends online, 2 currently in sessions - .homecard.findsession layout-grid-columns="4" layout-grid-position="4,0" layout-grid-rows="1" layout-link="findSession" type="findSession" class="#{logged_in_not_logged_in_class}" + .homecard.findsession layout-grid-columns="4" layout-grid-position="4,0" layout-grid-rows="1" layout-link="findSession" type="findSession" 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" class="#{logged_in_not_logged_in_class}" + .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 - .homecard.musicians layout-grid-columns=small_tile_size layout-grid-position=column_positions[0] layout-grid-rows="1" layout-link="musicians" class="#{logged_in_not_logged_in_class}" + .homecard.musicians layout-grid-columns=small_tile_size layout-grid-position=column_positions[0] layout-grid-rows="1" layout-link="musicians" h2 musicians .homebox-info /! 5 followers, 3 following - .homecard.bands layout-grid-columns=small_tile_size layout-grid-position=column_positions[1] layout-grid-rows="1" layout-link="bands" class="#{logged_in_not_logged_in_class}" + .homecard.bands layout-grid-columns=small_tile_size layout-grid-position=column_positions[1] layout-grid-rows="1" layout-link="bands" h2 bands .homebox-info -if jamtracks @@ -75,11 +75,11 @@ h2 jamtracks .homebox-info /! 5 followers, 3 following - .homecard.profile layout-grid-columns=small_tile_size layout-grid-position=column_positions[3] layout-grid-rows="1" class="#{logged_in_not_logged_in_class}" + .homecard.profile layout-grid-columns=small_tile_size layout-grid-position=column_positions[3] layout-grid-rows="1" h2 profile .homebox-info /! 5 followers, 3 following - .homecard.account layout-grid-columns=small_tile_size layout-grid-position=column_positions[4] layout-grid-rows="1" layout-link="account" class="#{logged_in_not_logged_in_class}" + .homecard.account layout-grid-columns=small_tile_size layout-grid-position=column_positions[4] layout-grid-rows="1" layout-link="account" h2 account .homebox-info /! free service level diff --git a/web/app/views/clients/_jamtrack.html.haml b/web/app/views/clients/_jamtrack.html.haml index bbb85a697..ba076ee19 100644 --- a/web/app/views/clients/_jamtrack.html.haml +++ b/web/app/views/clients/_jamtrack.html.haml @@ -1,4 +1,4 @@ -%div{ layout: 'screen', :'layout-id' => 'jamtrack', id: 'jamtrackScreen', :class => 'screen secondary no-login-required'} +%div{ layout: 'screen', :'layout-id' => 'jamtrack', id: 'jamtrackScreen', :class => 'screen secondary'} .content .content-head .content-icon= image_tag("content/icon_jamtracks.png", {:height => 19, :width => 19}) diff --git a/web/app/views/clients/index.html.erb b/web/app/views/clients/index.html.erb index ebba73cf6..3f1ad8a1b 100644 --- a/web/app/views/clients/index.html.erb +++ b/web/app/views/clients/index.html.erb @@ -109,7 +109,20 @@ JK.currentUserName = null; JK.currentUserMusician = null; JK.currentUserAdmin = false; - <% end %> + + // you need to be logged in to use this part of the interface. + // save original URL, and redirect to the home page + logger.debug("redirecting back to / because not logged in") + + var redirectPath= '?redirect-to=' + encodeURIComponent(JK.locationPath()); + if(gon.isNativeClient) { + window.location.href = '/signin' + redirectPath; + } + else { + window.location.href = '/' + redirectPath; + } + + <% end %> // Some things can't be initialized until we're connected. Put them here. @@ -276,8 +289,6 @@ JK.ClientUpdateInstance.check() JK.app.initialRouting(); - - JK.hideCurtain(300); } @@ -293,18 +304,18 @@ JK.RecordingUtils.init(); - JK.app.initialize(); - - // Let's get things rolling... + // Let's get things rolling... if (JK.currentUserId) { + JK.app.initialize(); + JK.JamServer.registerMessageCallback(JK.MessageType.CLIENT_UPDATE, function(header, payload) { // do a client update early check upon initialization JK.ClientUpdateInstance.runCheck(payload.product, payload.version, payload.uri, payload.size) }); - JK.TickDuration('.feed-entry.music-session-history-entry .inprogress .tick-duration'); + JK.TickDuration('.feed-entry.music-session-history-entry .inprogress .tick-duration'); JK.JamServer.connect() // singleton here defined in JamServer.js .done(function() { @@ -317,9 +328,6 @@ // this ensures that there is always a CurrentSessionModel, even if it's for a non-active session JK.CurrentSessionModel = new JK.SessionModel(JK.app, JK.JamServer, window.jamClient, null); } - else { - _initAfterConnect(false); - } JK.bindHoverEvents(); }) diff --git a/web/app/views/dialogs/_dialogs.html.haml b/web/app/views/dialogs/_dialogs.html.haml index 164eb93f8..2b7ac1206 100644 --- a/web/app/views/dialogs/_dialogs.html.haml +++ b/web/app/views/dialogs/_dialogs.html.haml @@ -34,4 +34,3 @@ = render 'dialogs/adjustGearSpeedDialog' = render 'dialogs/openJamTrackDialog' = render 'dialogs/openBackingTrackDialog' -= render 'dialogs/loginRequiredDialog' diff --git a/web/app/views/dialogs/_loginRequiredDialog.html.slim b/web/app/views/dialogs/_loginRequiredDialog.html.slim deleted file mode 100644 index 4b979e68d..000000000 --- a/web/app/views/dialogs/_loginRequiredDialog.html.slim +++ /dev/null @@ -1,20 +0,0 @@ -.dialog.dialog-overlay-sm layout='dialog' layout-id='login-required-dialog' id='login-required-dialog' - .content-head - = image_tag "content/icon_alert.png", {:width => 24, :height => 24, :class => 'content-icon' } - h1 Login Required - - .dialog-inner - p - a href="/signup" Sign Up - |  or  - a href="/signin" Sign In - |  to access most functionality on this page. - p - | However, you can browse for  - a class="go-to-jamtracks" href='/client#/jamtrack' JamTracks - |  without logging in. - br - .clearall - .buttons - .right - a.button-orange class='btnClose' layout-action='close' CLOSE \ No newline at end of file diff --git a/web/spec/controllers/api_jam_tracks_controller_spec.rb b/web/spec/controllers/api_jam_tracks_controller_spec.rb index baeb7d1e3..357cb8737 100644 --- a/web/spec/controllers/api_jam_tracks_controller_spec.rb +++ b/web/spec/controllers/api_jam_tracks_controller_spec.rb @@ -60,6 +60,19 @@ describe ApiJamTracksController do json["next"].should be_nil json["jamtracks"].length.should == 2 end + + it "lists owned tracks" do + get :downloads + response.should be_success + json = JSON.parse(response.body) + json['downloads'].should have(0).items + + right = JamTrackRight.create(:user=>@user, :jam_track=>@jam_track) + get :downloads + response.should be_success + json = JSON.parse(response.body) + json['downloads'].should have(1).items + end it "finds a download" do #get "/download/#{right.id}/" diff --git a/web/spec/features/authentication_pages_spec.rb b/web/spec/features/authentication_pages_spec.rb index 70aae8a2b..7aea9cb9d 100644 --- a/web/spec/features/authentication_pages_spec.rb +++ b/web/spec/features/authentication_pages_spec.rb @@ -62,8 +62,7 @@ describe "Authentication", :js => true, :type => :feature, :capybara_feature => find('.userinfo .sign-out a').trigger(:click) end - # after logging out, we keep you at /client - it { find('#profile a.signin', text: 'Sign Up') } + it { find('h1', text: 'Play music together over the Internet as if in the same room') } end end end diff --git a/web/spec/features/in_session_spec.rb b/web/spec/features/in_session_spec.rb index a17f0d16a..ef96b1deb 100644 --- a/web/spec/features/in_session_spec.rb +++ b/web/spec/features/in_session_spec.rb @@ -28,9 +28,7 @@ describe "In a Session", :js => true, :type => :feature, :capybara_feature => tr sign_in_poltergeist finder visit "/client#/findSession" expect(page).to have_selector('#no-active-sessions') # verify private session is not found - #sign_out_poltergeist(validate: true) - visit "/" - should_be_at_root + sign_out_poltergeist(validate: true) end in_client(user) do set_session_access :public diff --git a/web/spec/features/profile_menu_spec.rb b/web/spec/features/profile_menu_spec.rb index a4d0e15e2..68ea06c8b 100644 --- a/web/spec/features/profile_menu_spec.rb +++ b/web/spec/features/profile_menu_spec.rb @@ -53,7 +53,7 @@ describe "Profile Menu", :js => true, :type => :feature, :capybara_feature => tr click_link 'Sign Out' end - it { should_be_at_logged_out_client } + it { should_be_at_root } end describe "Download App link" do diff --git a/web/spec/features/signin_spec.rb b/web/spec/features/signin_spec.rb index ad4f7dfec..689b8251b 100644 --- a/web/spec/features/signin_spec.rb +++ b/web/spec/features/signin_spec.rb @@ -175,7 +175,7 @@ describe "signin" do click_button "SIGN IN" end - should_be_at_logged_out_client + find('h1', text: 'Play music together over the Internet as if in the same room') end # if a cookie with the default domain is found with another, delete the one with the default domain @@ -201,7 +201,7 @@ describe "signin" do click_button "SIGN IN" end - should_be_at_logged_out_client + find('h1', text: 'Play music together over the Internet as if in the same room') delete_called.should be_true end @@ -211,23 +211,6 @@ describe "signin" do sign_in_poltergeist(user) sign_out_poltergeist - - wait_until_curtain_gone - - # musicians homecard should be disabled - find('.homecard.musicians.not-logged-in').trigger(:click) - find('h1', text: 'Login Required') - find('.btnClose').trigger(:click) - - # profile homecard should be disabled (this one is handled in homeScreen.js instead of in layout.js) - find('.homecard.profile.not-logged-in').trigger(:click) - find('h1', text: 'Login Required') - find('.btnClose').trigger(:click) - - # sidebar should be disabled - find('[layout-id="panelSearch"] [layout-panel="expanded"] [layout-panel="header"]').trigger(:click) - find('h1', text: 'Login Required') - find('.btnClose').trigger(:click) end diff --git a/web/spec/support/utilities.rb b/web/spec/support/utilities.rb index 7a89d9b23..8982fd5a1 100644 --- a/web/spec/support/utilities.rb +++ b/web/spec/support/utilities.rb @@ -200,7 +200,7 @@ end def sign_out_poltergeist(options = {}) open_user_dropdown click_link 'Sign Out' - should_be_at_logged_out_client if options[:validate] + should_be_at_signin if options[:validate] end def open_user_dropdown @@ -221,11 +221,6 @@ def should_be_at_signin find('h1', text: 'sign in or register') end -def should_be_at_logged_out_client - find('#profile a.signin', text: 'Sign Up') - find('.musicians.not-logged-in') -end - def leave_music_session_sleep_delay # add a buffer to ensure WSG has enough time to expire sleep_dur = (Rails.application.config.websocket_gateway_connect_time_stale_browser + From 5fa37abfb3b7546ab06480deb5927f2197c5cfd2 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 11 Mar 2015 17:02:03 -0500 Subject: [PATCH 19/65] * VRFS-2916 - adding redeemed boolean --- db/manifest | 3 ++- db/up/jam_track_redeemed.sql | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 db/up/jam_track_redeemed.sql diff --git a/db/manifest b/db/manifest index 6d9f1f63d..9cc4e1fac 100755 --- a/db/manifest +++ b/db/manifest @@ -259,4 +259,5 @@ recorded_jam_track_tracks.sql jam_track_jmep_data.sql add_jam_track_bitrates.sql jam_track_importer.sql -jam_track_pro_licensing_update.sql \ No newline at end of file +jam_track_pro_licensing_update.sql +jam_track_redeemed.sql \ No newline at end of file diff --git a/db/up/jam_track_redeemed.sql b/db/up/jam_track_redeemed.sql new file mode 100644 index 000000000..8a09cd56f --- /dev/null +++ b/db/up/jam_track_redeemed.sql @@ -0,0 +1 @@ +ALTER TABLE jam_track_rights ADD COLUMN redeemed BOOLEAN NOT NULL DEFAULT FALSE; \ No newline at end of file From 7c73e2ab5ca4579c8565cb45cbf264d27490d4e7 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 12 Mar 2015 21:53:23 -0500 Subject: [PATCH 20/65] * VRFS-2922 - show spinner for pending metronome --- db/manifest | 3 +- db/up/connection_metronome.sql | 1 + ruby/lib/jam_ruby/connection_manager.rb | 2 +- ruby/lib/jam_ruby/models/connection.rb | 1 + ruby/lib/jam_ruby/models/track.rb | 7 ++- ruby/spec/jam_ruby/models/track_spec.rb | 16 +++++++ web/app/assets/javascripts/session.js | 41 ++++++++++++---- web/app/assets/javascripts/sessionModel.js | 48 ++++++++++++++----- web/app/assets/javascripts/trackHelpers.js | 32 ++++++++++--- .../stylesheets/client/session.css.scss | 10 ++++ .../api_music_sessions_controller.rb | 2 +- web/app/views/api_music_sessions/show.rabl | 2 +- web/app/views/clients/_session.html.slim | 7 ++- web/lib/music_session_manager.rb | 4 +- 14 files changed, 143 insertions(+), 33 deletions(-) create mode 100644 db/up/connection_metronome.sql diff --git a/db/manifest b/db/manifest index 9cc4e1fac..267d3a46f 100755 --- a/db/manifest +++ b/db/manifest @@ -260,4 +260,5 @@ jam_track_jmep_data.sql add_jam_track_bitrates.sql jam_track_importer.sql jam_track_pro_licensing_update.sql -jam_track_redeemed.sql \ No newline at end of file +jam_track_redeemed.sql +connection_metronome.sql \ No newline at end of file diff --git a/db/up/connection_metronome.sql b/db/up/connection_metronome.sql new file mode 100644 index 000000000..78ed3219e --- /dev/null +++ b/db/up/connection_metronome.sql @@ -0,0 +1 @@ +ALTER TABLE connections ADD COLUMN metronome_open BOOLEAN NOT NULL DEFAULT FALSE; \ No newline at end of file diff --git a/ruby/lib/jam_ruby/connection_manager.rb b/ruby/lib/jam_ruby/connection_manager.rb index 3ab2f4a84..352048dc0 100644 --- a/ruby/lib/jam_ruby/connection_manager.rb +++ b/ruby/lib/jam_ruby/connection_manager.rb @@ -89,7 +89,7 @@ module JamRuby udp_reachable_value = udp_reachable.nil? ? 'udp_reachable' : udp_reachable sql =< "JamRuby::BackingTrack", :inverse_of => :connection, :foreign_key => 'connection_id', :dependent => :delete_all has_many :video_sources, :class_name => "JamRuby::VideoSource", :inverse_of => :connection, :foreign_key => 'connection_id', :dependent => :delete_all + validates :metronome_open, :inclusion => {:in => [true, false]} validates :as_musician, :inclusion => {:in => [true, false, nil]} validates :client_type, :inclusion => {:in => CLIENT_TYPES} validates_numericality_of :last_jam_audio_latency, greater_than:0, :allow_nil => true diff --git a/ruby/lib/jam_ruby/models/track.rb b/ruby/lib/jam_ruby/models/track.rb index c06ad2d7a..e430edb2d 100644 --- a/ruby/lib/jam_ruby/models/track.rb +++ b/ruby/lib/jam_ruby/models/track.rb @@ -109,7 +109,7 @@ module JamRuby # this is a bit different from a normal track synchronization in that the client just sends up all tracks, # ... some may already exist - def self.sync(clientId, tracks, backing_tracks = []) + def self.sync(clientId, tracks, backing_tracks = [], metronome_open = false) result = {} backing_tracks = [] unless backing_tracks @@ -117,6 +117,11 @@ module JamRuby Track.transaction do connection = Connection.find_by_client_id!(clientId) + # synchronize metronome_open on connection + if connection.metronome_open != metronome_open + Connection.where(:id => connection.id).update_all(:metronome_open => metronome_open) + end + # each time tracks are synced we have to update the entry in music_sessions_user_history msh = MusicSessionUserHistory.find_by_client_id!(clientId) instruments = [] diff --git a/ruby/spec/jam_ruby/models/track_spec.rb b/ruby/spec/jam_ruby/models/track_spec.rb index 4b27ad062..4db0f59a5 100644 --- a/ruby/spec/jam_ruby/models/track_spec.rb +++ b/ruby/spec/jam_ruby/models/track_spec.rb @@ -172,5 +172,21 @@ describe Track do expect(found.updated_at.to_i).to eq backing_track.updated_at.to_i end end + + describe "metronome_open" do + it "sets metronome_open to true" do + result = Track.sync(connection.client_id, [track_hash], [], true) + connection.reload + connection.metronome_open.should be_true + end + + it "sets metronome_open to false" do + connection.metronome_open = true + connection.save! + result = Track.sync(connection.client_id, [track_hash], [], false) + connection.reload + connection.metronome_open.should be_false + end + end end end \ No newline at end of file diff --git a/web/app/assets/javascripts/session.js b/web/app/assets/javascripts/session.js index f6cda4a51..2bedb654d 100644 --- a/web/app/assets/javascripts/session.js +++ b/web/app/assets/javascripts/session.js @@ -114,6 +114,7 @@ var $openBackingTrack = null; var $metronomePlaybackSelect = null; var $metronomePlaybackHelp = null; + var $templatePendingMetronome = null; var mediaTrackGroups = [ChannelGroupIds.MediaTrackGroup, ChannelGroupIds.JamTrackGroup, ChannelGroupIds.MetronomeGroup]; var muteBothMasterAndPersonalGroups = [ChannelGroupIds.MediaTrackGroup, ChannelGroupIds.JamTrackGroup, ChannelGroupIds.MetronomeGroup]; @@ -234,12 +235,8 @@ promptLeave = false; window.location = '/client#/home' }); - }) - }) - - } function notifyWithUserInfo(title , text, clientId) { @@ -495,7 +492,7 @@ } function checkJamTrackTransition(currentSession) { -// handle jam tracks + // handle jam tracks if (jamTrack == null && (currentSession && currentSession.jam_track != null)) { playbackControls.startMonitor(context.JK.PLAYBACK_MONITOR_MODE.JAMTRACK); } @@ -506,7 +503,7 @@ } function checkBackingTrackTransition(currentSession) { -// handle backing tracks + // handle backing tracks if (backing_track_path == null && (currentSession && currentSession.backing_track_path != null)) { playbackControls.startMonitor(); } @@ -517,7 +514,7 @@ } function checkRecordingTransition(currentSession) { -// handle claimed recordings + // handle claimed recordings if (claimedRecording == null && (currentSession && currentSession.claimed_recording != null)) { // this is a 'started with a claimed_recording' transition. // we need to start a timer to watch for the state of the play session @@ -601,10 +598,11 @@ } function resetOtherAudioContent() { - if ($('.session-recordings .track').length === 0 && $('.session-recordings .download-jamtrack').length === 0) { + if ($('.session-recordings .track').length === 0 && $('.session-recordings .download-jamtrack').length === 0 && $('.session-recordings .pending-metronome').length === 0) { $('.session-recordings .when-empty').show(); $('.session-recording-name-wrapper').hide(); $('.session-recordings .recording-controls').hide(); + $closePlaybackRecording.show(); $('.session-recordings .session-recording-name').text('(No audio loaded)') } } @@ -635,6 +633,7 @@ if ($('.session-livetracks .track').length === 0) { $('.session-livetracks .when-empty').show(); } + checkPendingMetronome(); resetOtherAudioContent(); /** @@ -1345,6 +1344,7 @@ setFormFromMetronome() metroCricket = context.jamClient.getMetronomeCricketTestState(); setMetronomePlaybackMode() + $closePlaybackRecording.show(); } @@ -2447,6 +2447,30 @@ .fail(app.ajaxError); } + function checkPendingMetronome() { + logger.debug("checkPendingMetronome", sessionModel.isMetronomeOpen(), getMetronomeMasterMixers().length) + if(sessionModel.isMetronomeOpen() && getMetronomeMasterMixers().length == 0) { + var pendingMetronome = $($templatePendingMetronome.html()) + + // hide the open options + otherAudioFilled(); + // fill out the 'media' name + $('.session-recordings .session-recording-name').text('Metronome') + // and hide the close button + $closePlaybackRecording.hide(); + + // avoid double addition of pending metronome + if($otherAudioContainer.find('.pending-metronome').length === 0) { + $otherAudioContainer.append(pendingMetronome) + } + + } + else { + $('.session-recordings .pending-metronome').remove() + } + + } + function openBackingTrack(e) { if($openBackingTrack.is('.disabled')) { @@ -2946,6 +2970,7 @@ $openBackingTrack = $('#open-a-backingtrack'); $metronomePlaybackSelect = $('#metronome-playback-select') $metronomePlaybackHelp = $('#metronome-playback-help') + $templatePendingMetronome = $('#template-pending-metronome'); events(); diff --git a/web/app/assets/javascripts/sessionModel.js b/web/app/assets/javascripts/sessionModel.js index b61292800..723ed08f1 100644 --- a/web/app/assets/javascripts/sessionModel.js +++ b/web/app/assets/javascripts/sessionModel.js @@ -34,7 +34,7 @@ var sessionPageEnterTimeout = null; var startTime = null; var joinDeferred = null; - var previousBackingTracks = []; + var previousAllTracks = {userTracks: [], backingTracks: [], metronomeTracks: []}; var openBackingTrack = null; var shownAudioMediaMixerHelp = false; var controlsLockedForJamTrackRecording = false; @@ -68,6 +68,20 @@ } } + // if any participant has the metronome open, then we say this session has the metronome open + function isMetronomeOpen() { + var metronomeOpen = false; + context._.each(participants(), function(participant) { + console.log("paritiparc.", participant.metronome_open) + if(participant.metronome_open) { + metronomeOpen = true; + return false; + } + }) + + return metronomeOpen; + } + function isPlayingRecording() { // this is the server's state; there is no guarantee that the local tracks // requested from the backend will have corresponding track information @@ -358,7 +372,7 @@ } currentSessionId = null; currentParticipants = {} - previousBackingTracks = [] + previousAllTracks = {userTracks: [], backingTracks: [], metronomeTracks: []} openBackingTrack = null shownAudioMediaMixerHelp = false controlsLockedForJamTrackRecording = false; @@ -603,26 +617,27 @@ return mixerMode; } - function syncTracks(backingTracks) { + function syncTracks(allTracks) { // double check that we are in session, since a bunch could have happened since then if(!inSession()) { logger.debug("dropping queued up sync tracks because no longer in session"); return null; } - // this is a local change to our tracks. we need to tell the server about our updated track information - var inputTracks = context.JK.TrackHelpers.getUserTracks(context.jamClient); - - // backingTracks can be passed in as an optimization, so that we don't hit the backend excessively - if(backingTracks === undefined ) { - backingTracks = context.JK.TrackHelpers.getBackingTracks(context.jamClient); + if(allTracks === undefined) { + allTracks = context.JK.TrackHelpers.getTrackInfo(context.jamClient); } + var inputTracks = allTracks.userTracks; + var backingTracks = allTracks.backingTracks; + var metronomeTracks = allTracks.metronomeTracks; + // create a trackSync request based on backend data var syncTrackRequest = {}; syncTrackRequest.client_id = app.clientId; syncTrackRequest.tracks = inputTracks; syncTrackRequest.backing_tracks = backingTracks; + syncTrackRequest.metronome_open = metronomeTracks.length > 0; syncTrackRequest.id = id(); return rest.putTrackSyncChange(syncTrackRequest) @@ -793,17 +808,27 @@ } else if(inSession() && (text == 'RebuildMediaControl' || text == 'RebuildRemoteUserControl')) { - var backingTracks = context.JK.TrackHelpers.getBackingTracks(context.jamClient); + var allTracks = context.JK.TrackHelpers.getTrackInfo(context.jamClient); + var backingTracks = allTracks.backingTracks; + var previousBackingTracks = previousAllTracks.backingTracks; + var metronomeTracks = allTracks.metronomeTracks; + var previousMetronomeTracks = previousAllTracks.metronomeTracks; // the way we know if backing tracks changes, or recordings are opened, is via this event. // but we want to report to the user when backing tracks change; so we need to detect change on our own if(!(previousBackingTracks.length == 0 && backingTracks.length == 0) && previousBackingTracks != backingTracks) { logger.debug("backing tracks changed", previousBackingTracks, backingTracks) - syncTracks(backingTracks); + syncTracks(allTracks); + } + else if(!(previousMetronomeTracks.length == 0 && metronomeTracks.length == 0) && previousMetronomeTracks != metronomeTracks) { + logger.debug("metronome state changed ", previousMetronomeTracks, metronomeTracks) + syncTracks(allTracks); } else { refreshCurrentSession(true); } + + previousAllTracks = allTracks; } else if(inSession() && (text == 'Global Peer Input Mixer Mode')) { setMixerMode(MIX_MODES.MASTER); @@ -840,6 +865,7 @@ this.isPersonalMixMode = isPersonalMixMode; this.getMixMode = getMixMode; this.selfOpenedJamTracks = selfOpenedJamTracks; + this.isMetronomeOpen = isMetronomeOpen; this.areControlsLockedForJamTrackRecording = areControlsLockedForJamTrackRecording; this.lockControlsforJamTrackRecording = lockControlsforJamTrackRecording; this.unlockControlsforJamTrackRecording = unlockControlsforJamTrackRecording; diff --git a/web/app/assets/javascripts/trackHelpers.js b/web/app/assets/javascripts/trackHelpers.js index a77792a1c..b434d80a9 100644 --- a/web/app/assets/javascripts/trackHelpers.js +++ b/web/app/assets/javascripts/trackHelpers.js @@ -14,9 +14,28 @@ // take all necessary arguments to complete its work. context.JK.TrackHelpers = { - getTracks: function(jamClient, groupId) { + getTrackInfo: function(jamClient) { + + var allTracks = context.jamClient.SessionGetAllControlState(true); + + var userTracks = context.JK.TrackHelpers.getUserTracks(jamClient, allTracks); + var backingTracks = context.JK.TrackHelpers.getBackingTracks(jamClient, allTracks); + var metronomeTracks = context.JK.TrackHelpers.getTracks(jamClient, 12); + + return { + userTracks: userTracks, + backingTracks: backingTracks, + metronomeTracks: metronomeTracks + } + }, + + // allTracks is the result of SessionGetAllControlState; as an optimization + getTracks: function(jamClient, groupId, allTracks) { var tracks = []; - var allTracks = context.jamClient.SessionGetAllControlState(true); + + if(!allTracks) { + allTracks = context.jamClient.SessionGetAllControlState(true); + } //var trackIds = jamClient.SessionGetIDs(); //var allTracks = jamClient.SessionGetControlState(trackIds, true); @@ -30,8 +49,9 @@ return tracks; }, - getBackingTracks: function(jamClient) { - var mediaTracks = context.JK.TrackHelpers.getTracks(jamClient, 4); + // allTracks is the result of SessionGetAllControlState; as an optimization + getBackingTracks: function(jamClient, allTracks) { + var mediaTracks = context.JK.TrackHelpers.getTracks(jamClient, 4, allTracks); var backingTracks = [] context._.each(mediaTracks, function(mediaTrack) { @@ -56,11 +76,11 @@ * from jamClient. If none exist there, the first instrument from the * user's profile is used. */ - getUserTracks: function(jamClient) { + getUserTracks: function(jamClient, allTracks) { var localMusicTracks = []; var i; - localMusicTracks = context.JK.TrackHelpers.getTracks(jamClient, 2); + localMusicTracks = context.JK.TrackHelpers.getTracks(jamClient, 2, allTracks); var trackObjects = []; diff --git a/web/app/assets/stylesheets/client/session.css.scss b/web/app/assets/stylesheets/client/session.css.scss index 116ba1e1a..42467d158 100644 --- a/web/app/assets/stylesheets/client/session.css.scss +++ b/web/app/assets/stylesheets/client/session.css.scss @@ -15,6 +15,16 @@ position:relative; } + .pending-metronome { + .spinner-large { + margin:20px auto 0; + text-align:center; + } + p { + text-align:center; + font-size:14px; + } + } .track { width:70px; diff --git a/web/app/controllers/api_music_sessions_controller.rb b/web/app/controllers/api_music_sessions_controller.rb index e5f6a7bd4..bb8760dc2 100644 --- a/web/app/controllers/api_music_sessions_controller.rb +++ b/web/app/controllers/api_music_sessions_controller.rb @@ -357,7 +357,7 @@ class ApiMusicSessionsController < ApiController end def track_sync - @tracks = MusicSessionManager.new.sync_tracks(@music_session, params[:client_id], params[:tracks], params[:backing_tracks]) + @tracks = MusicSessionManager.new.sync_tracks(@music_session, params[:client_id], params[:tracks], params[:backing_tracks], params[:metronome_open]) unless @tracks.kind_of? Array # we have to do this because api_session_detail_url will fail with a bad @tracks diff --git a/web/app/views/api_music_sessions/show.rabl b/web/app/views/api_music_sessions/show.rabl index c81cf9ff6..8149352d0 100644 --- a/web/app/views/api_music_sessions/show.rabl +++ b/web/app/views/api_music_sessions/show.rabl @@ -45,7 +45,7 @@ else child(:connections => :participants) { collection @music_sessions, :object_root => false - attributes :ip_address, :client_id, :joined_session_at, :audio_latency, :id + attributes :ip_address, :client_id, :joined_session_at, :audio_latency, :id, :metronome_open node :user do |connection| { :id => connection.user.id, :photo_url => connection.user.photo_url, :name => connection.user.name, :is_friend => connection.user.friends?(current_user), :connection_state => connection.aasm_state } diff --git a/web/app/views/clients/_session.html.slim b/web/app/views/clients/_session.html.slim index a873900fa..eb7541100 100644 --- a/web/app/views/clients/_session.html.slim +++ b/web/app/views/clients/_session.html.slim @@ -143,4 +143,9 @@ script#template-option type="text/template" script#template-genre-option type="text/template" option value="{value}" - ="{label}" \ No newline at end of file + ="{label}" + +script#template-pending-metronome type="text/template" + .pending-metronome + .spinner-large + p Your metronome is synchronizing. diff --git a/web/lib/music_session_manager.rb b/web/lib/music_session_manager.rb index 706b41d83..ac10e6d20 100644 --- a/web/lib/music_session_manager.rb +++ b/web/lib/music_session_manager.rb @@ -140,10 +140,10 @@ class MusicSessionManager < BaseManager Notification.send_session_depart(active_music_session, connection.client_id, user, recordingId) end - def sync_tracks(active_music_session, client_id, new_tracks, backing_tracks) + def sync_tracks(active_music_session, client_id, new_tracks, backing_tracks, metronome_open) tracks = nil active_music_session.with_lock do # VRFS-1297 - tracks = Track.sync(client_id, new_tracks, backing_tracks) + tracks = Track.sync(client_id, new_tracks, backing_tracks, metronome_open) active_music_session.tick_track_changes end Notification.send_tracks_changed(active_music_session) From 4e456bce41c1f1c0fcab07c94d2c5b2b69e93c46 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Fri, 13 Mar 2015 02:26:09 -0400 Subject: [PATCH 21/65] VRFS-2701 wip current interests editing --- .../accounts_profile_experience.js | 6 +- .../javascripts/accounts_profile_interests.js | 116 +++++++++ .../client/accountProfileInterests.css.scss | 43 ++++ .../_account_profile_experience.html.erb | 7 - .../_account_profile_interests.html.erb | 222 ++++++++++++++++++ web/app/views/clients/index.html.erb | 4 +- 6 files changed, 386 insertions(+), 12 deletions(-) diff --git a/web/app/assets/javascripts/accounts_profile_experience.js b/web/app/assets/javascripts/accounts_profile_experience.js index a85888b9f..826cf09aa 100644 --- a/web/app/assets/javascripts/accounts_profile_experience.js +++ b/web/app/assets/javascripts/accounts_profile_experience.js @@ -48,7 +48,7 @@ description : instrument.description, id : instrument.id }); - $instrumentSelector.append(template) + $instrumentSelector.append(template); }); // and fill in the proficiency for the instruments that the user can play @@ -137,7 +137,7 @@ function navigateTo(targetLocation) { resetForm(); - window.location = targetLocation; + context.location = targetLocation; } function handleUpdateProfile() { @@ -152,7 +152,7 @@ genres: genres, skill_level: $scroller.find('select[name=skill_level]').val(), concert_count: $scroller.find('select[name=concert_count]').val(), - studio_session_count: $scroller.find('select[name=studio_session_count]').val(), + studio_session_count: $scroller.find('select[name=studio_session_count]').val() }) .done(postUpdateProfileSuccess) .fail(postUpdateProfileFailure); diff --git a/web/app/assets/javascripts/accounts_profile_interests.js b/web/app/assets/javascripts/accounts_profile_interests.js index e69de29bb..a79fb5e1a 100644 --- a/web/app/assets/javascripts/accounts_profile_interests.js +++ b/web/app/assets/javascripts/accounts_profile_interests.js @@ -0,0 +1,116 @@ +(function(context,$) { + + "use strict"; + + context.JK = context.JK || {}; + context.JK.AccountProfileInterests= function(app) { + var $document = $(document); + var logger = context.JK.logger; + var EVENTS = context.JK.EVENTS; + var api = context.JK.Rest(); + var userId; + var user = {}; + var profileUtils = context.JK.ProfileUtils; + + var $screen = $('#account-profile-interests'); + var $scroller = $screen.find('#account-profile-content-scroller'); + var $btnCancel = $scroller.find('#account-edit-profile-cancel'); + var $btnBack = $scroller.find('#account-edit-profile-back'); + var $btnSubmit = $scroller.find('#account-edit-profile-submit'); + + function beforeShow(data) { + userId = data.id; + } + + function afterShow(data) { + renderInterests(); + } + + function resetForm() { + $scroller.find('form .error-text').remove(); + $scroller.find('form .error').removeClass("error"); + } + + function populateAccountProfile(userDetail) { + + + context.JK.dropdown($('select', $scroller)); + } + + function events() { + console.log("HERE2"); + $btnCancel.on('click', function(evt) { evt.stopPropagation(); navigateTo('/client#/account'); return false; } ); + $btnBack.on('click', function(evt) { evt.stopPropagation(); navigateTo('/client#/account/profile/experience'); return false; } ); + $btnSubmit.on('click', function(evt) { evt.stopPropagation(); handleUpdateProfile(); return false; } ); + } + + function renderInterests() { + $.when(api.getUserProfile()) + .done(function(userDetailResponse) { + var userDetail = userDetailResponse[0]; + populateAccountProfile(userDetail); + }); + + context.JK.dropdown($('select')); + } + + function navigateTo(targetLocation) { + console.log("HERE"); + context.location = targetLocation; + } + + function handleUpdateProfile() { + resetForm(); + + api.updateUser({ + instruments: instruments, + genres: genres, + skill_level: $scroller.find('select[name=skill_level]').val(), + concert_count: $scroller.find('select[name=concert_count]').val(), + studio_session_count: $scroller.find('select[name=studio_session_count]').val() + }) + .done(postUpdateProfileSuccess) + .fail(postUpdateProfileFailure); + } + + function postUpdateProfileSuccess(response) { + $document.triggerHandler(EVENTS.USER_UPDATED, response); + context.location = "/client#/account/profile/samples"; + } + + function postUpdateProfileFailure(xhr, textStatus, errorMessage) { + + var errors = JSON.parse(xhr.responseText) + + if(xhr.status == 422) { + + } + else { + app.ajaxError(xhr, textStatus, errorMessage) + } + } + + function initialize() { + var screenBindings = { + 'beforeShow': beforeShow, + 'afterShow': afterShow + }; + + app.bindScreen('account/profile/interests', screenBindings); + + events(); + + $screen.iCheck({ + checkboxClass: 'icheckbox_minimal', + radioClass: 'iradio_minimal', + inheritClass: true + }); + } + + this.initialize = initialize; + this.beforeShow = beforeShow; + this.afterShow = afterShow; + return this; + }; + +})(window,jQuery); \ No newline at end of file diff --git a/web/app/assets/stylesheets/client/accountProfileInterests.css.scss b/web/app/assets/stylesheets/client/accountProfileInterests.css.scss index e69de29bb..0ae312516 100644 --- a/web/app/assets/stylesheets/client/accountProfileInterests.css.scss +++ b/web/app/assets/stylesheets/client/accountProfileInterests.css.scss @@ -0,0 +1,43 @@ +@import "client/common.css.scss"; + + +#account-profile-interests { + .interest { + font-weight: 600; + font-size: 16px; + } + + a.help { + font-weight: normal; + font-size: 14px; + } + + div.genres { + width: 20%; + margin-bottom: 15px; + } + + a.select-genre { + text-decoration: underline; + font-size: 12px; + font-weight: normal !important; + } + + span.genre-list { + font-style: italic; + font-size: 12px; + } + + .interest-options { + width: 30%; + margin-bottom: 15px; + + label { + margin-bottom: 10px; + } + } + + input[type=text].rate { + width: 100px; + } +} \ No newline at end of file diff --git a/web/app/views/clients/_account_profile_experience.html.erb b/web/app/views/clients/_account_profile_experience.html.erb index d4091e602..a371bd594 100644 --- a/web/app/views/clients/_account_profile_experience.html.erb +++ b/web/app/views/clients/_account_profile_experience.html.erb @@ -1,24 +1,17 @@ -
-
-
<%= image_tag "content/icon_account.png", {:width => 27, :height => 20} %>
-

my account

<%= render "screen_navigation" %>
- -
-
diff --git a/web/app/views/layouts/landing.html.erb b/web/app/views/layouts/landing.html.erb index 7ae5a821c..df95fab81 100644 --- a/web/app/views/layouts/landing.html.erb +++ b/web/app/views/layouts/landing.html.erb @@ -75,6 +75,9 @@ <% end %> JK.app = JK.JamKazam(); + JK.getGenreList().done(function(genres) { + JK.genres = genres; + }); JK.app.initialize({inClient: false, layoutOpts: {layoutFooter: false, sizeOverlayToContent: true}}); }) diff --git a/web/app/views/layouts/web.html.erb b/web/app/views/layouts/web.html.erb index e3e828bc4..c816bf518 100644 --- a/web/app/views/layouts/web.html.erb +++ b/web/app/views/layouts/web.html.erb @@ -116,6 +116,10 @@ JK.bindHoverEvents(); + JK.getGenreList().done(function(genres) { + JK.genres = genres; + }); + JK.JamServer.connect() // singleton here defined in JamServer.js .done(function() { console.log("websocket connected") From cba86f4db46d45a260a5a52f8086c55488cb34a5 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Fri, 20 Mar 2015 08:06:02 +0000 Subject: [PATCH 50/65] VRFS-2916 added totals; limit month range to 1; filled out all unpopulated cols with 0; misc tweaks --- admin/app/admin/cohorts.rb | 24 +++--- admin/app/admin/cohorts_monthly.rb | 82 ++++++++++--------- admin/app/models/cohort.rb | 124 +++++++++++++++++++++-------- 3 files changed, 142 insertions(+), 88 deletions(-) diff --git a/admin/app/admin/cohorts.rb b/admin/app/admin/cohorts.rb index 7e9d4a90c..e9bd73fea 100644 --- a/admin/app/admin/cohorts.rb +++ b/admin/app/admin/cohorts.rb @@ -12,7 +12,7 @@ ActiveAdmin.register Cohort, :as => 'Cohorts' do def scoped_collection objs = super - Cohort.alltime_cohorts! if 0 == Cohort.where(all_time: true).count + Cohort.alltime_cohorts! objs.where(all_time: true).order('group_start DESC') end @@ -23,46 +23,46 @@ ActiveAdmin.register Cohort, :as => 'Cohorts' do div(class: :cohort_col) { cc.group_start_str } end - column Cohort::TOTAL_LABELS[:registered_users] do |cc| + column Cohort::ALLTIME_LABELS[:registered_users] do |cc| div(class: :cohort_col) { cc.data_val(:registered_users) } end - column Cohort::TOTAL_LABELS[:first_downloaded_client_at] do |cc| + column Cohort::ALLTIME_LABELS[:first_downloaded_client_at] do |cc| div(class: :cohort_col) { cc.data_val(:first_downloaded_client_at) } end column '%' do |cc| div(class: :cohort_col) { cc.data_val(:first_downloaded_client_at, true) } end - column Cohort::TOTAL_LABELS[:first_certified_gear_at] do |cc| + column Cohort::ALLTIME_LABELS[:first_certified_gear_at] do |cc| div(class: :cohort_col) { cc.data_val(:first_certified_gear_at) } end column '%' do |cc| div(class: :cohort_col) { cc.data_val(:first_certified_gear_at, true) } end - column Cohort::TOTAL_LABELS[:music_sessions_user_history] do |cc| + column Cohort::ALLTIME_LABELS[:music_sessions_user_history] do |cc| div(class: :cohort_col) { cc.data_val(:music_sessions_user_history) } end column '%' do |cc| div(class: :cohort_col) { cc.data_val(:music_sessions_user_history, true) } end - column Cohort::TOTAL_LABELS[:jam_track_played] do |cc| - div(class: :cohort_col) { cc.data_val(:jam_track_played) } + column Cohort::ALLTIME_LABELS[:jam_tracks_played] do |cc| + div(class: :cohort_col) { cc.data_val(:jam_tracks_played) } end - column '%' do |cc| div(class: :cohort_col) { cc.data_val(:jam_track_played, true) } end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:jam_tracks_played, true) } end - column Cohort::TOTAL_LABELS[:jam_track_rights] do |cc| + column Cohort::ALLTIME_LABELS[:jam_track_rights] do |cc| div(class: :cohort_col) { cc.data_val(:jam_track_rights) } end column '%' do |cc| div(class: :cohort_col) { cc.data_val(:jam_track_rights, true) } end - column Cohort::TOTAL_LABELS[:recorded_tracks] do |cc| + column Cohort::ALLTIME_LABELS[:recorded_tracks] do |cc| div(class: :cohort_col) { cc.data_val(:recorded_tracks) } end column '%' do |cc| div(class: :cohort_col) { cc.data_val(:recorded_tracks, true) } end - column Cohort::TOTAL_LABELS[:friendships] do |cc| + column Cohort::ALLTIME_LABELS[:friendships] do |cc| div(class: :cohort_col) { cc.data_val(:friendships) } end column '%' do |cc| div(class: :cohort_col) { cc.data_val(:friendships, true) } end - column Cohort::TOTAL_LABELS[:invited_users] do |cc| + column Cohort::ALLTIME_LABELS[:invited_users] do |cc| div(class: :cohort_col) { cc.data_val(:invited_users) } end column '%' do |cc| div(class: :cohort_col) { cc.data_val(:invited_users, true) } end diff --git a/admin/app/admin/cohorts_monthly.rb b/admin/app/admin/cohorts_monthly.rb index 942da3191..d062a8317 100644 --- a/admin/app/admin/cohorts_monthly.rb +++ b/admin/app/admin/cohorts_monthly.rb @@ -8,91 +8,89 @@ ActiveAdmin.register Cohort, :as => 'Cohorts Monthly' do config.per_page = 50 filter(:monthly_start, as: :select, collection: Cohort.monthly_starts) - filter(:monthly_end, as: :select, collection: Cohort.monthly_ends) controller do def scoped_collection - objs = super args = params[:q] || {} - if !(args[:monthly_start_eq].nil? || args[:monthly_end_eq].nil?) - mstart, mend = Time.parse(args[:monthly_start_eq]), Time.parse(args[:monthly_end_eq]) - if mstart < mend - Cohort.monthly_cohorts(mstart, mend) # populate monthlys - objs = objs.where(monthly_start: mstart, monthly_end: mend) - end - end - objs.where(all_time: false).order('group_start DESC') + Cohort.monthly_cohorts!(Time.parse(args[:monthly_start_eq])) if ! args[:monthly_start_eq].nil? + super.where(all_time: false).order('group_start DESC') end end - index :title => "Monthly Cohorts" do - column 'Cohort' do |cc| cc.group_start_str end - column Cohort::MONTHLY_LABELS[:registered_users] do |cc| cc.data_val(:registered_users) end + index :title => proc { "Monthly Cohorts #{params[:q] ? '('+Time.parse(params[:q][:monthly_start_eq]).strftime('%Y-%m')+')' : ''}" } do + + column 'Cohort' do |cc| + div(class: :cohort_col) { cc.group_start_str } + end + + column Cohort::MONTHLY_LABELS[:registered_users] do |cc| + div(class: :cohort_col) { cc.data_val(:registered_users) } + end column Cohort::MONTHLY_LABELS[:first_downloaded_client_at] do |cc| - cc.data_val(:first_downloaded_client_at) + div(class: :cohort_col) { cc.data_val(:first_downloaded_client_at) } end - column '%' do |cc| cc.data_val(:first_downloaded_client_at, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:first_downloaded_client_at, true) } end column Cohort::MONTHLY_LABELS[:first_certified_gear_at] do |cc| - cc.data_val(:first_certified_gear_at) + div(class: :cohort_col) { cc.data_val(:first_certified_gear_at) } end - column '%' do |cc| cc.data_val(:first_certified_gear_at, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:first_certified_gear_at, true) } end column Cohort::MONTHLY_LABELS[:music_sessions_user_history_1] do |cc| - cc.data_val(:music_sessions_user_history_1) + div(class: :cohort_col) { cc.data_val(:music_sessions_user_history_1) } end - column '%' do |cc| cc.data_val(:music_sessions_user_history_1, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:music_sessions_user_history_1, true) } end column Cohort::MONTHLY_LABELS[:music_sessions_user_history_2_5] do |cc| - cc.data_val(:music_sessions_user_history_2_5) + div(class: :cohort_col) { cc.data_val(:music_sessions_user_history_2_5) } end - column '%' do |cc| cc.data_val(:music_sessions_user_history_2_5, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:music_sessions_user_history_2_5, true) } end column Cohort::MONTHLY_LABELS[:music_sessions_user_history_6_] do |cc| - cc.data_val(:music_sessions_user_history_6_) + div(class: :cohort_col) { cc.data_val(:music_sessions_user_history_6_) } end - column '%' do |cc| cc.data_val(:music_sessions_user_history_6_, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:music_sessions_user_history_6_, true) } end - column Cohort::MONTHLY_LABELS[:jam_track_played_1] do |cc| - cc.data_val(:jam_track_played_1) + column Cohort::MONTHLY_LABELS[:jam_tracks_played_1] do |cc| + div(class: :cohort_col) { cc.data_val(:jam_tracks_played_1) } end - column '%' do |cc| cc.data_val(:jam_track_played_1, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:jam_tracks_played_1, true) } end - column Cohort::MONTHLY_LABELS[:jam_track_played_2_5] do |cc| - cc.data_val(:jam_track_played_2_5) + column Cohort::MONTHLY_LABELS[:jam_tracks_played_2_5] do |cc| + div(class: :cohort_col) { cc.data_val(:jam_tracks_played_2_5) } end - column '%' do |cc| cc.data_val(:jam_track_played_2_5, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:jam_tracks_played_2_5, true) } end - column Cohort::MONTHLY_LABELS[:jam_track_played_6_] do |cc| - cc.data_val(:jam_track_played_6_) + column Cohort::MONTHLY_LABELS[:jam_tracks_played_6_] do |cc| + div(class: :cohort_col) { cc.data_val(:jam_tracks_played_6_) } end - column '%' do |cc| cc.data_val(:jam_track_played_6_, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:jam_tracks_played_6_, true) } end column Cohort::MONTHLY_LABELS[:jam_track_rights_redeemed] do |cc| - cc.data_val(:jam_track_rights_redeemed) + div(class: :cohort_col) { cc.data_val(:jam_track_rights_redeemed) } end - column '%' do |cc| cc.data_val(:jam_track_rights_redeemed, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:jam_track_rights_redeemed, true) } end column Cohort::MONTHLY_LABELS[:jam_track_rights] do |cc| - cc.data_val(:jam_track_rights) + div(class: :cohort_col) { cc.data_val(:jam_track_rights) } end - column '%' do |cc| cc.data_val(:jam_track_rights, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:jam_track_rights, true) } end column Cohort::MONTHLY_LABELS[:recorded_tracks] do |cc| - cc.data_val(:recorded_tracks) + div(class: :cohort_col) { cc.data_val(:recorded_tracks) } end - column '%' do |cc| cc.data_val(:recorded_tracks, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:recorded_tracks, true) } end column Cohort::MONTHLY_LABELS[:friendships] do |cc| - cc.data_val(:friendships) + div(class: :cohort_col) { cc.data_val(:friendships) } end - column '%' do |cc| cc.data_val(:friendships, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:friendships, true) } end column Cohort::MONTHLY_LABELS[:invited_users] do |cc| - cc.data_val(:invited_users) + div(class: :cohort_col) { cc.data_val(:invited_users) } end - column '%' do |cc| cc.data_val(:invited_users, true) end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:invited_users, true) } end end end diff --git a/admin/app/models/cohort.rb b/admin/app/models/cohort.rb index 93173a619..29523a46d 100644 --- a/admin/app/models/cohort.rb +++ b/admin/app/models/cohort.rb @@ -3,36 +3,38 @@ require 'date' class Cohort < ActiveRecord::Base EARLIEST_DATE = Time.parse('2014-03-01') + TOTAL_COHORT_DATE = Time.at(0) - TOTAL_LABELS = { + ALLTIME_LABELS = { registered_users: 'Registered Users', - first_downloaded_client_at: 'Downloaded app', + first_downloaded_client_at: 'DL app', first_certified_gear_at: 'Certified Gear', music_sessions_user_history: 'Played Online', - jam_track_played: 'Played JamTrack', - jam_track_rights: 'Purchased JamTrack', + jam_tracks_played: 'Played JT', + jam_track_rights: 'Purchased JT', recorded_tracks: 'Made Recording', - friendships: 'Connected w/ Friend', + friendships: 'Friended', invited_users: 'Invite Others', } MONTHLY_LABELS = { registered_users: 'Registered Users', - first_downloaded_client_at: 'Downloaded app', + first_downloaded_client_at: 'DL app', first_certified_gear_at: 'Certified Gear', music_sessions_user_history_1: 'Played Online 1', music_sessions_user_history_2_5: 'Played Online 2-5', music_sessions_user_history_6_: 'Played Online 6+', - jam_track_played_1: 'Played Online 1', - jam_track_played_2_5: 'Played Online 2-5', - jam_track_played_6_: 'Played Online 6+', - jam_track_rights_redeemed: 'Redeemed JamTrack', - jam_track_rights: 'Purchased JamTrack', + jam_tracks_played_1: 'Played JT 1', + jam_tracks_played_2_5: 'Played JT 2-5', + jam_tracks_played_6_: 'Played JT 6+', + jam_track_rights_redeemed: 'Redeemed JT', + jam_track_rights: 'Purchased JT', recorded_tracks: 'Made Recording', - friendships: 'Connected w/ Friend', + friendships: 'Friended', invited_users: 'Invite Others', } + attr_accessible :all_time, :monthly_start serialize :data_set, JSON before_create do @@ -87,6 +89,7 @@ class Cohort < ActiveRecord::Base end def self.generate_all_time_cohorts + Cohort.delete_all("all_time = 't'") self.cohort_group_ranges.collect do |range| unless cc = Cohort.where(group_start: range.first).where(all_time: true).limit(1).first cc = Cohort.new @@ -121,7 +124,8 @@ class Cohort < ActiveRecord::Base SELECT played.user_id FROM (SELECT user_id, COUNT(*) cnt FROM music_sessions_user_history msuh1 WHERE - msuh1.created_at >= '#{start_date}' AND msuh1.created_at <= '#{end_date}' AND + msuh1.created_at >= '#{start_date}' AND + msuh1.created_at <= '#{end_date}' AND EXTRACT(EPOCH FROM (msuh1.session_removed_at - msuh1.created_at)) >= 900 AND (SELECT COUNT(*) FROM music_sessions_user_history msuh2 WHERE msuh1.music_session_id = msuh2.music_session_id @@ -132,8 +136,30 @@ WHERE #{where} SQL end + def _played_jamtrack_subquery(constraint) + where = if constraint.is_a?(Range) + "played.cnt >= #{constraint.first} AND played.cnt <= #{constraint.last}" + else + "played.cnt #{constraint}" + end + start_date = all_time ? self.group_start : self.monthly_start + end_date = all_time ? self.group_end : self.monthly_end + sql =<= '#{start_date}' AND + pp.created_at <= '#{end_date}' AND + pp.playable_type = 'JamRuby::JamTrack' + GROUP BY player_id + ) played +WHERE #{where} +SQL + end + def _subquery(assoc_key, num_user) assoc = User.reflections[assoc_key] + return 0 unless assoc start_date = all_time ? self.group_start : self.monthly_start end_date = all_time ? self.group_end : self.monthly_end sql =<= 6') - # count = self.class.cohort_users(self).where("users.id IN (#{sql})").count - # _put_data_set(:jam_track_played_6_, count, num_user) + sql = _played_jamtrack_subquery(' >= 6') + count = self.class.cohort_users(self).where("users.id IN (#{sql})").count + _put_data_set(:jam_tracks_played_6_, count, num_user) self.save! end @@ -247,8 +273,10 @@ SQL count = _subquery(assoc_key = :jam_track_rights, num_user) _put_data_set(assoc_key, count, num_user) - # count = _subquery(assoc_key = :jam_track_played, num_user) - # _put_data_set(assoc_key, count, num_user) + count = _subquery(assoc_key = :jam_tracks_played, num_user) do |subsql| + subsql += " AND tt.playable_type = 'JamRuby::JamTrack' " + end + _put_data_set(assoc_key, count, num_user) sql = _played_online_subquery(' >= 1') count = self.class.cohort_users(self).where("users.id IN (#{sql})").count @@ -261,20 +289,50 @@ SQL self.all_time ? _all_time! : _monthly! end - def self.alltime_cohorts! - Cohort.generate_all_time_cohorts.each do |cc| - cc._all_time! + def calculate_totals(cohorts) + self.group_start = self.group_end = TOTAL_COHORT_DATE + labels = all_time ? Cohort::ALLTIME_LABELS : Cohort::MONTHLY_LABELS + + self.data_set = labels.inject({}) do |hh, (kk,vv)| + hh[kk.to_s] = hh["#{kk}%"] = 0 + hh end + + labels = labels.keys.map(&:to_s) + cohorts.each do |cc| + labels.each do |key| + self.data_set[key] += cc.data_set[key].to_i + end + end + + user_total = self.data_set['registered_users'].to_f + labels.delete('registered_users') + labels.each do |key| + xx = (self.data_set[key].to_f / user_total) + self.data_set["#{key}%"] = 100.0 * xx.round(4) + end + + self.save! + cohorts << self end - def self.monthly_cohorts(monthly_start, monthly_end) - self.generate_monthly_cohorts(monthly_start, monthly_end).compact.each do |cc| + def self.alltime_cohorts! + cohorts = Cohort.generate_all_time_cohorts.each do |cc| + cc._all_time! + end + Cohort.new(all_time: true).calculate_totals(cohorts) + end + + def self.monthly_cohorts!(monthly_start, monthly_end=nil) + monthly_end ||= monthly_start + 1.month - 1.second + cohorts = self.generate_monthly_cohorts(monthly_start, monthly_end).compact.each do |cc| cc._monthly! end + Cohort.new(all_time: false, monthly_start: monthly_start).calculate_totals(cohorts) end def group_start_str - self.group_start.strftime('%Y-%m') + is_total_cohort? ? 'Total' : self.group_start.strftime('%Y-%m') end def group_end_str @@ -296,10 +354,8 @@ SQL end end - def self.monthly_ends - self.cohort_group_ranges.collect do |rr| - rr.last.to_s - end + def is_total_cohort? + self.group_start == TOTAL_COHORT_DATE end end From 10f25be8c4413a321976bcf85cb33fcddb312d5f Mon Sep 17 00:00:00 2001 From: Seth Call Date: Fri, 20 Mar 2015 08:53:34 -0500 Subject: [PATCH 51/65] * replace old style of volume changes VRFS-2964 --- ruby/lib/jam_ruby/models/mix.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ruby/lib/jam_ruby/models/mix.rb b/ruby/lib/jam_ruby/models/mix.rb index f441a441f..66f0dd38f 100644 --- a/ruby/lib/jam_ruby/models/mix.rb +++ b/ruby/lib/jam_ruby/models/mix.rb @@ -141,12 +141,14 @@ module JamRuby recording.recorded_tracks.each do |recorded_track| manifest["files"] << { "filename" => recorded_track.sign_url(one_day), "codec" => "vorbis", "offset" => 0 } - mix_params << { "level" => 1.0, "balance" => 0 } + mix_params << { "level" => 100, "balance" => 0 } + # change to 1.0 level later end recording.recorded_backing_tracks.each do |recorded_backing_track| manifest["files"] << { "filename" => recorded_backing_track.sign_url(one_day), "codec" => "vorbis", "offset" => 0 } - mix_params << { "level" => 1.0, "balance" => 0 } + mix_params << { "level" => 100, "balance" => 0 } + # change to 1.0 level later end recording.recorded_jam_track_tracks.each do |recorded_jam_track_track| From 301b365044d21c7876696f55f0dac054b30d1193 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Fri, 20 Mar 2015 09:44:25 -0500 Subject: [PATCH 52/65] * VRFS-2968 - escape strings passed to JamTrack builder --- ruby/lib/jam_ruby/jam_tracks_manager.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ruby/lib/jam_ruby/jam_tracks_manager.rb b/ruby/lib/jam_ruby/jam_tracks_manager.rb index 53e92e02f..68c788f87 100644 --- a/ruby/lib/jam_ruby/jam_tracks_manager.rb +++ b/ruby/lib/jam_ruby/jam_tracks_manager.rb @@ -34,9 +34,9 @@ module JamRuby nm = jam_track_track.id + File.extname(jam_track_track.url_by_sample_rate(sample_rate)) track_filename = File.join(tmp_dir, nm) track_url = jam_track_track.sign_url(120, sample_rate) + @@log.info("downloading #{track_url} to #{track_filename}") copy_url_to_file(track_url, track_filename) - copy_url_to_file(track_url, File.join(".", nm)) - jam_file_opts << " -i '#{track_filename}+#{jam_track_track.part}'" + jam_file_opts << " -i #{Shellwords.escape("#{track_filename}+#{jam_track_track.part}")}" end #puts "LS + " + `ls -la '#{tmp_dir}'` @@ -46,9 +46,9 @@ module JamRuby py_file = File.join(py_root, "jkcreate.py") version = jam_track.version @@log.info "Executing python source in #{py_file}, outputting to #{tmp_dir} (#{output_jkz})" - + # From http://stackoverflow.com/questions/690151/getting-output-of-system-calls-in-ruby/5970819#5970819: - cli = "python #{py_file} -D -k #{sku} -p #{tmp_dir}/pkey.pem -s #{tmp_dir}/skey.pem #{jam_file_opts} -o #{output_jkz} -t '#{title}' -V '#{version}'" + cli = "python #{py_file} -D -k #{sku} -p #{Shellwords.escape(tmp_dir)}/pkey.pem -s #{Shellwords.escape(tmp_dir)}/skey.pem #{jam_file_opts} -o #{Shellwords.escape(output_jkz)} -t #{Shellwords.escape(title)} -V #{Shellwords.escape(version)}" Open3.popen3(cli) do |stdin, stdout, stderr, wait_thr| pid = wait_thr.pid exit_status = wait_thr.value From 6df6165bb7aa3f8f9d6c2725127de0bd52c4ab15 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Fri, 20 Mar 2015 09:55:35 -0500 Subject: [PATCH 53/65] remove jamblaster notice from site --- web/app/assets/javascripts/layout.js | 8 -------- web/app/assets/stylesheets/client/jamkazam.css.scss | 12 ------------ web/app/views/clients/_home.html.slim | 4 ---- 3 files changed, 24 deletions(-) diff --git a/web/app/assets/javascripts/layout.js b/web/app/assets/javascripts/layout.js index 6c40f596d..723e75650 100644 --- a/web/app/assets/javascripts/layout.js +++ b/web/app/assets/javascripts/layout.js @@ -215,14 +215,6 @@ 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/stylesheets/client/jamkazam.css.scss b/web/app/assets/stylesheets/client/jamkazam.css.scss index ab1444769..d777d3f11 100644 --- a/web/app/assets/stylesheets/client/jamkazam.css.scss +++ b/web/app/assets/stylesheets/client/jamkazam.css.scss @@ -593,16 +593,4 @@ 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/views/clients/_home.html.slim b/web/app/views/clients/_home.html.slim index 8130e82cc..fb6347c84 100644 --- a/web/app/views/clients/_home.html.slim +++ b/web/app/views/clients/_home.html.slim @@ -1,8 +1,4 @@ .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) From d44236abc4e847dbc57529b3df7819263d0d406f Mon Sep 17 00:00:00 2001 From: Seth Call Date: Fri, 20 Mar 2015 09:58:58 -0500 Subject: [PATCH 54/65] remove jamblaster notice from site --- web/config/application.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/web/config/application.rb b/web/config/application.rb index d594b65ca..e0bf20211 100644 --- a/web/config/application.rb +++ b/web/config/application.rb @@ -314,7 +314,6 @@ if defined?(Bundler) config.influxdb_ignored_environments = ENV["INFLUXDB_ENABLED"] == '1' ? ['test', 'cucumber'] : ['test', 'cucumber', 'development'] config.allow_spikes = false - config.show_jamblaster_notice = true config.show_jamblaster_kickstarter_link = true config.metronome_available = true config.backing_tracks_available = true From 36b41794924d0e3581dd9144fc8a03d367b5b944 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Fri, 20 Mar 2015 17:26:56 -0500 Subject: [PATCH 55/65] * VRFS-2972 - adding test purchase feature to jam-admin for buying all JamTracks (admin users only) --- admin/app/admin/cohorts.rb | 2 +- admin/app/admin/cohorts_monthly.rb | 2 +- admin/app/admin/crash_dumps.rb | 2 +- admin/app/admin/email_batch.rb | 2 +- .../admin/email_daily_scheduled_session.rb | 2 +- admin/app/admin/event.rb | 2 +- admin/app/admin/event_session.rb | 2 +- admin/app/admin/fake_purchaser.rb | 54 +++++++++++++++++++ admin/app/admin/isp_scoring_data.rb | 2 +- admin/app/admin/jam_ruby_artifact_updates.rb | 2 +- admin/app/admin/jam_ruby_users.rb | 1 + admin/app/admin/promo_buzz.rb | 2 +- admin/app/admin/promo_latest.rb | 2 +- admin/app/admin/score_export.rb | 8 +-- admin/app/admin/score_history.rb | 2 + admin/app/admin/scoring_load.rb | 2 +- admin/spec/factories.rb | 1 + db/manifest | 1 + db/up/jam_track_right_admin_purchase.sql | 1 + ruby/lib/jam_ruby/models/jam_track_right.rb | 2 + web/app/controllers/api_recurly_controller.rb | 1 - 21 files changed, 79 insertions(+), 16 deletions(-) create mode 100644 admin/app/admin/fake_purchaser.rb create mode 100644 db/up/jam_track_right_admin_purchase.sql diff --git a/admin/app/admin/cohorts.rb b/admin/app/admin/cohorts.rb index e9bd73fea..c39ec8048 100644 --- a/admin/app/admin/cohorts.rb +++ b/admin/app/admin/cohorts.rb @@ -1,6 +1,6 @@ ActiveAdmin.register Cohort, :as => 'Cohorts' do - menu :label => 'Cohorts All-time', :parent => 'Analytics' + menu :label => 'Cohorts All-time', :parent => 'Reports' config.sort_order = 'group_start_desc' config.batch_actions = false diff --git a/admin/app/admin/cohorts_monthly.rb b/admin/app/admin/cohorts_monthly.rb index d062a8317..5c24179c7 100644 --- a/admin/app/admin/cohorts_monthly.rb +++ b/admin/app/admin/cohorts_monthly.rb @@ -1,6 +1,6 @@ ActiveAdmin.register Cohort, :as => 'Cohorts Monthly' do - menu :label => 'Cohorts Monthly', :parent => 'Analytics' + menu :label => 'Cohorts Monthly', :parent => 'Reports' config.sort_order = 'group_start_desc' config.batch_actions = false diff --git a/admin/app/admin/crash_dumps.rb b/admin/app/admin/crash_dumps.rb index 759f3e656..bcfcd9106 100644 --- a/admin/app/admin/crash_dumps.rb +++ b/admin/app/admin/crash_dumps.rb @@ -3,7 +3,7 @@ ActiveAdmin.register JamRuby::CrashDump, :as => 'Crash Dump' do filter :timestamp filter :user_email, :as => :string filter :client_id - menu :parent => 'Debug' + menu :parent => 'Misc' index do column "Timestamp" do |post| diff --git a/admin/app/admin/email_batch.rb b/admin/app/admin/email_batch.rb index e31ec5cce..0ceecec4a 100644 --- a/admin/app/admin/email_batch.rb +++ b/admin/app/admin/email_batch.rb @@ -1,6 +1,6 @@ ActiveAdmin.register JamRuby::EmailBatch, :as => 'Batch Emails' do - menu :label => 'Batch Emails', :parent => 'Email' + menu :label => 'Batch Emails', :parent => 'Misc' config.sort_order = 'updated_at DESC' config.batch_actions = false diff --git a/admin/app/admin/email_daily_scheduled_session.rb b/admin/app/admin/email_daily_scheduled_session.rb index 94bdfa16c..32e997bb6 100644 --- a/admin/app/admin/email_daily_scheduled_session.rb +++ b/admin/app/admin/email_daily_scheduled_session.rb @@ -1,6 +1,6 @@ ActiveAdmin.register JamRuby::EmailBatchScheduledSessions, :as => 'Daily Sessions' do - menu :label => 'Daily Sessions', :parent => 'Email' + menu :label => 'Daily Sessions', :parent => 'Misc' config.sort_order = 'updated_at DESC' config.filters = false diff --git a/admin/app/admin/event.rb b/admin/app/admin/event.rb index 186592d30..48981a4d7 100644 --- a/admin/app/admin/event.rb +++ b/admin/app/admin/event.rb @@ -1,3 +1,3 @@ ActiveAdmin.register JamRuby::Event, :as => 'Event' do - menu :parent => 'Events' + menu :parent => 'Misc' end diff --git a/admin/app/admin/event_session.rb b/admin/app/admin/event_session.rb index df540716d..560f7351f 100644 --- a/admin/app/admin/event_session.rb +++ b/admin/app/admin/event_session.rb @@ -1,3 +1,3 @@ ActiveAdmin.register JamRuby::EventSession, :as => 'Event Session' do - menu :parent => 'Events' + menu :parent => 'Misc' end diff --git a/admin/app/admin/fake_purchaser.rb b/admin/app/admin/fake_purchaser.rb new file mode 100644 index 000000000..75be6c653 --- /dev/null +++ b/admin/app/admin/fake_purchaser.rb @@ -0,0 +1,54 @@ +ActiveAdmin.register_page "Fake Purchaser" do + menu :parent => 'Misc' + + + page_action :bulk_jamtrack_purchase, :method => :post do + + puts params.inspect + + user_field = params[:jam_ruby_jam_track_right][:user] + + if user_field.blank? + redirect_to admin_fake_purchaser_path, :notice => "user not specified" + return + end + + bits = user_field.strip.split(' ') + + user = User.find_by_email(bits[0]) + if user.nil? + redirect_to admin_fake_purchaser_path, :notice =>"no user with email #{bits[0]}" + return + end + + if !user.admin + redirect_to admin_fake_purchaser_path, :notice =>"user is not admin" + return + end + + count = 0 + JamTrack.all.each do |jam_track| + unless jam_track.right_for_user(user) + + jam_track_right=JamTrackRight.new + jam_track_right.user = user + jam_track_right.jam_track = jam_track + jam_track_right.is_test_purchase = true + jam_track_right.save! + count = count + 1 + end + end + + redirect_to admin_fake_purchaser_path, :notice => "Bought #{count} jamtracks for #{user.email}" + end + + content do + + semantic_form_for JamTrackRight.new, :url => admin_fake_purchaser_bulk_jamtrack_purchase_path, :builder => ActiveAdmin::FormBuilder do |f| + f.inputs "Admin User to Fake JamTrack Purchases" do + f.input :user, :as => :autocomplete, :url => autocomplete_user_email_admin_users_path, :input_html => { :id_element => "#jam_trak_right_user_id" }, hint: 'All JamTracks in the system will be \'bought\' for this user. No Recurly interaction occurs with this feature.' + end + f.actions + end + end +end diff --git a/admin/app/admin/isp_scoring_data.rb b/admin/app/admin/isp_scoring_data.rb index 02756ffc0..ad93fed9a 100644 --- a/admin/app/admin/isp_scoring_data.rb +++ b/admin/app/admin/isp_scoring_data.rb @@ -2,6 +2,6 @@ ActiveAdmin.register JamRuby::IspScoreBatch, :as => 'Isp Score Data' do config.sort_order = 'created_at_desc' - menu :parent => 'Debug' + menu :parent => 'Misc' end diff --git a/admin/app/admin/jam_ruby_artifact_updates.rb b/admin/app/admin/jam_ruby_artifact_updates.rb index 62185042c..01c0379e1 100644 --- a/admin/app/admin/jam_ruby_artifact_updates.rb +++ b/admin/app/admin/jam_ruby_artifact_updates.rb @@ -1,5 +1,5 @@ ActiveAdmin.register JamRuby::ArtifactUpdate, :as => 'Artifacts' do - menu :label => 'Artifacts' + menu :label => 'Artifacts', :parent => 'Operations' config.sort_order = 'product,environment' #config.batch_actions = false diff --git a/admin/app/admin/jam_ruby_users.rb b/admin/app/admin/jam_ruby_users.rb index 3e8c1dd6b..0fe3c29d9 100644 --- a/admin/app/admin/jam_ruby_users.rb +++ b/admin/app/admin/jam_ruby_users.rb @@ -80,6 +80,7 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do User.select("email, first_name, last_name, id").where(["email ILIKE ? OR first_name ILIKE ? OR last_name ILIKE ?", "%#{parameters[:term]}%", "%#{parameters[:term]}%", "%#{parameters[:term]}%"]) end + def create @jam_ruby_user = JamRuby::User.new(params[:jam_ruby_user]) @jam_ruby_user.administratively_created = true diff --git a/admin/app/admin/promo_buzz.rb b/admin/app/admin/promo_buzz.rb index b3fc90514..98c76429c 100644 --- a/admin/app/admin/promo_buzz.rb +++ b/admin/app/admin/promo_buzz.rb @@ -1,6 +1,6 @@ ActiveAdmin.register JamRuby::PromoBuzz, :as => 'Buzz' do - menu :label => 'Buzz', :parent => 'Home Page' + menu :label => 'Promo Buzz', :parent => 'Misc' config.sort_order = 'position ASC aasm_state DESC updated_at DESC' config.batch_actions = false diff --git a/admin/app/admin/promo_latest.rb b/admin/app/admin/promo_latest.rb index ec0c593ba..9177bdda9 100644 --- a/admin/app/admin/promo_latest.rb +++ b/admin/app/admin/promo_latest.rb @@ -1,6 +1,6 @@ ActiveAdmin.register JamRuby::PromoLatest, :as => 'Latest' do - menu :label => 'Latest', :parent => 'Home Page' + menu :label => 'Promo Latest', :parent => 'Misc' config.batch_actions = false config.sort_order = '' diff --git a/admin/app/admin/score_export.rb b/admin/app/admin/score_export.rb index 1387cc17d..076928140 100644 --- a/admin/app/admin/score_export.rb +++ b/admin/app/admin/score_export.rb @@ -1,5 +1,6 @@ -ActiveAdmin.register_page "Download CSV" do - menu :parent => 'Score' +=begin +ActiveAdmin.register_page "Download Score CSV" do + menu :parent => 'Misc' page_action :create_csv, :method => :post do @@ -95,4 +96,5 @@ ActiveAdmin.register_page "Download CSV" do #end end -end \ No newline at end of file +end +=end diff --git a/admin/app/admin/score_history.rb b/admin/app/admin/score_history.rb index 85ed34393..7786b4c29 100644 --- a/admin/app/admin/score_history.rb +++ b/admin/app/admin/score_history.rb @@ -1,3 +1,4 @@ +=begin ActiveAdmin.register JamRuby::ScoreHistory, :as => 'Score History' do menu :parent => 'Score' @@ -80,3 +81,4 @@ ActiveAdmin.register JamRuby::ScoreHistory, :as => 'Score History' do column "To Client", :to_client_id end end +=end diff --git a/admin/app/admin/scoring_load.rb b/admin/app/admin/scoring_load.rb index f6f3e3b2c..3afcc7219 100644 --- a/admin/app/admin/scoring_load.rb +++ b/admin/app/admin/scoring_load.rb @@ -1,5 +1,5 @@ ActiveAdmin.register_page "Current Scoring Load" do - menu :parent => 'Score' + menu :parent => 'Misc' content :title => "Current Scoring Load" do table_for GetWork.summary do diff --git a/admin/spec/factories.rb b/admin/spec/factories.rb index 156fb1a6f..a1e906a48 100644 --- a/admin/spec/factories.rb +++ b/admin/spec/factories.rb @@ -40,6 +40,7 @@ FactoryGirl.define do scoring_timeout Time.now sequence(:channel_id) { |n| "Channel#{n}"} association :user, factory: :user + metronome_open false end factory :artifact_update, :class => JamRuby::ArtifactUpdate do diff --git a/db/manifest b/db/manifest index 791afb9d9..f33ec0e15 100755 --- a/db/manifest +++ b/db/manifest @@ -264,3 +264,4 @@ jam_track_redeemed.sql connection_metronome.sql preview_jam_track_tracks.sql cohorts.sql +jam_track_right_admin_purchase.sql \ No newline at end of file diff --git a/db/up/jam_track_right_admin_purchase.sql b/db/up/jam_track_right_admin_purchase.sql new file mode 100644 index 000000000..aec1d5cfa --- /dev/null +++ b/db/up/jam_track_right_admin_purchase.sql @@ -0,0 +1 @@ +ALTER TABLE jam_track_rights ADD COLUMN is_test_purchase BOOLEAN DEFAULT FALSE NOT NULL; \ No newline at end of file diff --git a/ruby/lib/jam_ruby/models/jam_track_right.rb b/ruby/lib/jam_ruby/models/jam_track_right.rb index a12347f70..4f8852341 100644 --- a/ruby/lib/jam_ruby/models/jam_track_right.rb +++ b/ruby/lib/jam_ruby/models/jam_track_right.rb @@ -11,6 +11,8 @@ module JamRuby validates :user, presence:true validates :jam_track, presence:true + validates :is_test_purchase, inclusion: {in: [true, false]} + validate :verify_download_count after_save :after_save diff --git a/web/app/controllers/api_recurly_controller.rb b/web/app/controllers/api_recurly_controller.rb index a7fedb452..50f4f703d 100644 --- a/web/app/controllers/api_recurly_controller.rb +++ b/web/app/controllers/api_recurly_controller.rb @@ -56,7 +56,6 @@ class ApiRecurlyController < ApiController def place_order error=nil - puts "PLACING ORDER #{params.inspect}" response = {jam_tracks:[]} # 1st confirm that all specified JamTracks exist From b137d7586f5d60a4c519d69021e5a2a0813d398f Mon Sep 17 00:00:00 2001 From: Seth Call Date: Fri, 20 Mar 2015 21:27:11 -0500 Subject: [PATCH 56/65] * VRFS-2973 - fix pagination in jam trackdialog --- ruby/lib/jam_ruby/models/jam_track.rb | 24 ++++++++++++++----- .../javascripts/dialog/openJamTrackDialog.js | 3 ++- web/app/assets/javascripts/sessionModel.js | 1 - .../dialogs/openJamTrackDialog.css.scss | 7 ++++++ 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/ruby/lib/jam_ruby/models/jam_track.rb b/ruby/lib/jam_ruby/models/jam_track.rb index 058e44d1e..646fd18ff 100644 --- a/ruby/lib/jam_ruby/models/jam_track.rb +++ b/ruby/lib/jam_ruby/models/jam_track.rb @@ -59,15 +59,27 @@ module JamRuby class << self def index(options, user) - limit = options[:limit] - limit ||= 20 - limit = limit.to_i + if options[:page] + page = options[:page].to_i + per_page = options[:per_page].to_i + + start = (page -1 )* per_page + limit = per_page + else + limit = options[:limit] + limit ||= 20 + limit = limit.to_i + + start = options[:start].presence + start = start.to_i || 0 + + page = 1 + start/limit + per_page = limit + end - start = options[:start].presence - start = start.to_i || 0 query = JamTrack.joins(:jam_track_tracks) - .paginate(page: 1 + start/limit, per_page: limit) + .paginate(page: page, per_page: per_page) if options[:show_purchased_only] query = query.joins(:jam_track_rights) diff --git a/web/app/assets/javascripts/dialog/openJamTrackDialog.js b/web/app/assets/javascripts/dialog/openJamTrackDialog.js index 349742ad7..9f0fc5ce6 100644 --- a/web/app/assets/javascripts/dialog/openJamTrackDialog.js +++ b/web/app/assets/javascripts/dialog/openJamTrackDialog.js @@ -58,7 +58,8 @@ options.jamTrackId = jamTrack.id; options.name = jamTrack.name; options.artist = jamTrack.original_artist; - options.downloaded = 'Yes' + var detail = context.jamClient.JamTrackGetTrackDetail(jamTrack.id) || {} + options.downloaded = detail.key_state == 'ready' ? 'Yes' : 'No' var $tr = $(context._.template($templateOpenJamTrackRow.html(), options, { variable: 'data' })); $tr.data('server-model', jamTrack); diff --git a/web/app/assets/javascripts/sessionModel.js b/web/app/assets/javascripts/sessionModel.js index 723ed08f1..c09f4e878 100644 --- a/web/app/assets/javascripts/sessionModel.js +++ b/web/app/assets/javascripts/sessionModel.js @@ -72,7 +72,6 @@ function isMetronomeOpen() { var metronomeOpen = false; context._.each(participants(), function(participant) { - console.log("paritiparc.", participant.metronome_open) if(participant.metronome_open) { metronomeOpen = true; return false; diff --git a/web/app/assets/stylesheets/dialogs/openJamTrackDialog.css.scss b/web/app/assets/stylesheets/dialogs/openJamTrackDialog.css.scss index 4ff94bb71..b13355afe 100644 --- a/web/app/assets/stylesheets/dialogs/openJamTrackDialog.css.scss +++ b/web/app/assets/stylesheets/dialogs/openJamTrackDialog.css.scss @@ -31,6 +31,7 @@ left: 15%; font-size: 12px; padding-top:5px; + z-index:-1; a { margin:0 10px; @@ -40,5 +41,11 @@ .paginator-holder { padding-top:3px; } + + .recording-wrapper { + height:290px; + overflow:auto; + + } } From 128a8119cf8bdfc37620e0e580c12c636a9939d8 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Fri, 20 Mar 2015 22:04:03 -0500 Subject: [PATCH 57/65] * VRFS-2942 - show correct instrument and show correct part info --- web/app/assets/javascripts/session.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/app/assets/javascripts/session.js b/web/app/assets/javascripts/session.js index bafcccc13..b62d2737d 100644 --- a/web/app/assets/javascripts/session.js +++ b/web/app/assets/javascripts/session.js @@ -1208,12 +1208,12 @@ }); var oneOfTheTracks = correspondingTracks[0]; - var instrumentIcon = context.JK.getInstrumentIcon45(oneOfTheTracks.instrument_id); + var instrumentIcon = context.JK.getInstrumentIcon45(oneOfTheTracks.instrument.id); var photoUrl = "/assets/content/icon_recording.png"; var name = oneOfTheTracks.part if (!name) { - name = oneOfTheTracks.instrument; + name = ''; } if(isOpener) { From 9acb235a6dd07c7f70979c31943cf4d15d00aaa5 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 21 Mar 2015 10:04:46 -0400 Subject: [PATCH 58/65] VRFS-2701 VRFS-2699 wip performance samples edit screen --- db/manifest | 1 + .../alter_genre_player_unique_constraint.sql | 2 + .../assets/javascripts/accounts_profile.js | 17 +- .../accounts_profile_experience.js | 23 +- .../javascripts/accounts_profile_interests.js | 26 +- .../javascripts/accounts_profile_samples.js | 70 ++++++ .../javascripts/dialog/genreSelectorDialog.js | 4 +- ...Selector.js => recordingSelectorDialog.js} | 0 web/app/assets/javascripts/ui_helper.js | 7 + ....scss => recordingSelectorDialog.css.scss} | 0 .../clients/_account_profile_samples.html.erb | 223 ++++++++++++++++++ web/app/views/clients/index.html.erb | 4 +- web/app/views/dialogs/_dialogs.html.haml | 3 +- ....erb => _recordingSelectorDialog.html.erb} | 0 14 files changed, 366 insertions(+), 14 deletions(-) create mode 100644 db/up/alter_genre_player_unique_constraint.sql rename web/app/assets/javascripts/dialog/{recordingSelector.js => recordingSelectorDialog.js} (100%) rename web/app/assets/stylesheets/dialogs/{recordingSelector.css.scss => recordingSelectorDialog.css.scss} (100%) rename web/app/views/dialogs/{_recordingSelector.html.erb => _recordingSelectorDialog.html.erb} (100%) diff --git a/db/manifest b/db/manifest index 761758fe2..4e3709a27 100755 --- a/db/manifest +++ b/db/manifest @@ -268,3 +268,4 @@ jam_track_redeemed.sql connection_metronome.sql preview_jam_track_tracks.sql cohorts.sql +alter_genre_player_unique_constraint.sql \ No newline at end of file diff --git a/db/up/alter_genre_player_unique_constraint.sql b/db/up/alter_genre_player_unique_constraint.sql new file mode 100644 index 000000000..c0159b59b --- /dev/null +++ b/db/up/alter_genre_player_unique_constraint.sql @@ -0,0 +1,2 @@ +ALTER TABLE genre_players DROP CONSTRAINT genre_player_uniqkey; +ALTER TABLE genre_players ADD CONSTRAINT genre_player_uniqkey UNIQUE (player_id, player_type, genre_id, genre_type); \ No newline at end of file diff --git a/web/app/assets/javascripts/accounts_profile.js b/web/app/assets/javascripts/accounts_profile.js index c418c213f..b7189774a 100644 --- a/web/app/assets/javascripts/accounts_profile.js +++ b/web/app/assets/javascripts/accounts_profile.js @@ -18,6 +18,9 @@ var nilOptionStr = ''; var nilOptionText = 'n/a'; var $screen = $('#account-profile-basics'); + var $btnCancel = $screen.find('#account-edit-profile-cancel'); + var $btnSubmit = $screen.find('#account-edit-profile-submit'); + var $biography = null; function beforeShow(data) { @@ -213,8 +216,18 @@ /****************** MAIN PORTION OF SCREEN *****************/ // events for main screen function events() { - $('#account-profile-content-scroller').on('click', '#account-edit-profile-cancel', function(evt) { evt.stopPropagation(); navToAccount(); return false; } ); - $('#account-profile-content-scroller').on('click', '#account-edit-profile-submit', function(evt) { evt.stopPropagation(); handleUpdateProfile(); return false; } ); + $btnCancel.click(function(evt) { + evt.stopPropagation(); + navToAccount(); + return false; + }); + + $btnSubmit.click(function(evt) { + evt.stopPropagation(); + handleUpdateProfile(); + return false; + }); + $('#account-profile-content-scroller').on('submit', '#account-edit-email-form', function(evt) { evt.stopPropagation(); handleUpdateProfile(); return false; } ); $('#account-profile-content-scroller').on('click', '#account-change-avatar', function(evt) { evt.stopPropagation(); navToAvatar(); return false; } ); } diff --git a/web/app/assets/javascripts/accounts_profile_experience.js b/web/app/assets/javascripts/accounts_profile_experience.js index 2a212acd5..3e0b1efb6 100644 --- a/web/app/assets/javascripts/accounts_profile_experience.js +++ b/web/app/assets/javascripts/accounts_profile_experience.js @@ -13,6 +13,9 @@ var $instrumentSelector = null; var $userGenres = null; var profileUtils = context.JK.ProfileUtils; + var $btnCancel = $screen.find('#account-edit-profile-cancel'); + var $btnBack = $screen.find('#account-edit-profile-back'); + var $btnSubmit = $screen.find('#account-edit-profile-submit'); function beforeShow(data) { } @@ -118,9 +121,23 @@ } function events() { - $screen.find('#account-edit-profile-cancel').on('click', function(evt) { evt.stopPropagation(); navigateTo('/client#/profile/' + context.JK.currentUserId); return false; } ); - $screen.find('#account-edit-profile-back').on('click', function(evt) { evt.stopPropagation(); navigateTo('/client#/account/profile/'); return false; } ); - $screen.find('#account-edit-profile-submit').on('click', function(evt) { evt.stopPropagation(); handleUpdateProfile(); return false; } ); + $btnCancel.click(function(evt) { + evt.stopPropagation(); + navigateTo('/client#/profile/' + context.JK.currentUserId); + return false; + }); + + $btnBack.click(function(evt) { + evt.stopPropagation(); + navigateTo('/client#/account/profile/'); + return false; + }); + + $btnSubmit.click(function(evt) { + evt.stopPropagation(); + handleUpdateProfile(); + return false; + }); } function renderExperience() { diff --git a/web/app/assets/javascripts/accounts_profile_interests.js b/web/app/assets/javascripts/accounts_profile_interests.js index 2edbf5f59..6d8ccd40b 100644 --- a/web/app/assets/javascripts/accounts_profile_interests.js +++ b/web/app/assets/javascripts/accounts_profile_interests.js @@ -3,7 +3,7 @@ "use strict"; context.JK = context.JK || {}; - context.JK.AccountProfileInterests= function(app) { + context.JK.AccountProfileInterests = function(app) { var $document = $(document); var logger = context.JK.logger; var EVENTS = context.JK.EVENTS; @@ -154,7 +154,7 @@ function bindGenreSelector(type, $btnSelect, $genreList) { $btnSelect.unbind('click').click(function(evt) { - evt.stopPropagation(); + evt.preventDefault(); var genreText = $genreList.html(); var genres = []; if (genres !== NONE_SPECIFIED) { @@ -164,6 +164,8 @@ ui.launchGenreSelectorDialog(type, genres, function(selectedGenres) { $genreList.html(selectedGenres && selectedGenres.length > 0 ? selectedGenres.join(GENRE_LIST_DELIMITER) : NONE_SPECIFIED); }); + + return false; }); } @@ -175,9 +177,23 @@ bindGenreSelector('free sessions', $btnFreeSessionsGenreSelect, $freeSessionsGenreList); bindGenreSelector('co-writing', $btnCowritingGenreSelect, $cowritingGenreList); - $btnCancel.on('click', function(evt) { evt.stopPropagation(); navigateTo('/client#/profile/' + context.JK.currentUserId); return false; } ); - $btnBack.on('click', function(evt) { evt.stopPropagation(); navigateTo('/client#/account/profile/experience'); return false; } ); - $btnSubmit.on('click', function(evt) { evt.stopPropagation(); handleUpdateProfile(); return false; } ); + $btnCancel.click(function(evt) { + evt.stopPropagation(); + navigateTo('/client#/profile/' + context.JK.currentUserId); + return false; + }); + + $btnBack.click(function(evt) { + evt.stopPropagation(); + navigateTo('/client#/account/profile/experience'); + return false; + }); + + $btnSubmit.click(function(evt) { + evt.stopPropagation(); + handleUpdateProfile(); + return false; + }); context.JK.dropdown($virtualBandCommitment); context.JK.dropdown($traditionalBandCommitment); diff --git a/web/app/assets/javascripts/accounts_profile_samples.js b/web/app/assets/javascripts/accounts_profile_samples.js index e69de29bb..0131f49e4 100644 --- a/web/app/assets/javascripts/accounts_profile_samples.js +++ b/web/app/assets/javascripts/accounts_profile_samples.js @@ -0,0 +1,70 @@ +(function(context,$) { + + "use strict"; + + context.JK = context.JK || {}; + context.JK.AccountProfileSamples = function(app) { + var $document = $(document); + var logger = context.JK.logger; + var EVENTS = context.JK.EVENTS; + var api = context.JK.Rest(); + var ui = new context.JK.UIHelper(JK.app); + var user = {}; + var profileUtils = context.JK.ProfileUtils; + + var $screen = $('#account-profile-samples'); + var $btnAddJkRecording = $screen.find('') + + function beforeShow(data) { + } + + function afterShow(data) { + renderSamples(); + } + + function renderSamples() { + + } + + function events() { + $btnAddJkRecording.click(function(evt) { + evt.preventDefault(); + ui.launchRecordingSelectorDialog('', function(selectedRecordings) { + + }); + + return false; + }); + + $btnAddSoundCloudRecording.click(function(evt) { + + }); + + $btnAddYouTubeVideo.click(function(evt) { + + }); + + $btnSaveAndFinish.click(function(evt) { + $document.triggerHandler(EVENTS.USER_UPDATED, response); + context.location = "/client#/profile/" + context.JK.currentUserId; + }); + } + + function initialize() { + var screenBindings = { + 'beforeShow': beforeShow, + 'afterShow': afterShow + }; + + app.bindScreen('account/profile/samples', screenBindings); + + events(); + } + + this.initialize = initialize; + this.beforeShow = beforeShow; + this.afterShow = afterShow; + return this; + }; + +})(window,jQuery); \ No newline at end of file diff --git a/web/app/assets/javascripts/dialog/genreSelectorDialog.js b/web/app/assets/javascripts/dialog/genreSelectorDialog.js index 78f8c08dc..f72634d0c 100644 --- a/web/app/assets/javascripts/dialog/genreSelectorDialog.js +++ b/web/app/assets/javascripts/dialog/genreSelectorDialog.js @@ -57,7 +57,7 @@ function events() { $btnSelect.click(function(evt) { - + evt.preventDefault(); var selectedGenres = []; $genres.find('input[type=checkbox]:checked').each(function(index) { selectedGenres.push($(this).val()); @@ -69,6 +69,8 @@ app.layout.closeDialog(dialogId); + return false; + }); } diff --git a/web/app/assets/javascripts/dialog/recordingSelector.js b/web/app/assets/javascripts/dialog/recordingSelectorDialog.js similarity index 100% rename from web/app/assets/javascripts/dialog/recordingSelector.js rename to web/app/assets/javascripts/dialog/recordingSelectorDialog.js diff --git a/web/app/assets/javascripts/ui_helper.js b/web/app/assets/javascripts/ui_helper.js index ed0621e8e..bdebf103d 100644 --- a/web/app/assets/javascripts/ui_helper.js +++ b/web/app/assets/javascripts/ui_helper.js @@ -68,6 +68,12 @@ return genreSelectorDialog.showDialog(); } + function launchRecordingSelectorDialog(recordings, callback) { + var recordingSelectorDialog = new JK.RecordingSelectorDialog(JK.app, recordings, callback); + recordingSelectorDialog.initialize(); + return recordingSelectorDialog.showDialog(); + } + this.addSessionLike = addSessionLike; this.addRecordingLike = addRecordingLike; this.launchCommentDialog = launchCommentDialog; @@ -77,6 +83,7 @@ this.launchRsvpCreateSlotDialog = launchRsvpCreateSlotDialog; this.launchSessionStartDialog = launchSessionStartDialog; this.launchGenreSelectorDialog = launchGenreSelectorDialog; + this.launchRecordingSelectorDialog = launchRecordingSelectorDialog; return this; }; diff --git a/web/app/assets/stylesheets/dialogs/recordingSelector.css.scss b/web/app/assets/stylesheets/dialogs/recordingSelectorDialog.css.scss similarity index 100% rename from web/app/assets/stylesheets/dialogs/recordingSelector.css.scss rename to web/app/assets/stylesheets/dialogs/recordingSelectorDialog.css.scss diff --git a/web/app/views/clients/_account_profile_samples.html.erb b/web/app/views/clients/_account_profile_samples.html.erb index e69de29bb..bb2d80636 100644 --- a/web/app/views/clients/_account_profile_samples.html.erb +++ b/web/app/views/clients/_account_profile_samples.html.erb @@ -0,0 +1,223 @@ +
+
+
+ <%= image_tag "content/icon_account.png", {:width => 27, :height => 20} %> +
+

my account

+ <%= render "screen_navigation" %> +
+ +
+ +
+
+ diff --git a/web/app/views/clients/index.html.erb b/web/app/views/clients/index.html.erb index c6e4bbea8..1757ddcf0 100644 --- a/web/app/views/clients/index.html.erb +++ b/web/app/views/clients/index.html.erb @@ -220,8 +220,8 @@ var accountProfileInterests = new JK.AccountProfileInterests(JK.app); accountProfileInterests.initialize(); - // var accountProfileSamples = new JK.AccountProfileSamples(JK.app); - // accountProfileSamples.initialize(); + var accountProfileSamples = new JK.AccountProfileSamples(JK.app); + accountProfileSamples.initialize(); var accountAudioProfile = new JK.AccountAudioProfile(JK.app); accountAudioProfile.initialize(); diff --git a/web/app/views/dialogs/_dialogs.html.haml b/web/app/views/dialogs/_dialogs.html.haml index 809181660..7383a4400 100644 --- a/web/app/views/dialogs/_dialogs.html.haml +++ b/web/app/views/dialogs/_dialogs.html.haml @@ -34,4 +34,5 @@ = render 'dialogs/adjustGearSpeedDialog' = render 'dialogs/openJamTrackDialog' = render 'dialogs/openBackingTrackDialog' -= render 'dialogs/genreSelectorDialog' \ No newline at end of file += render 'dialogs/genreSelectorDialog' += render 'dialogs/recordingSelectorDialog' \ No newline at end of file diff --git a/web/app/views/dialogs/_recordingSelector.html.erb b/web/app/views/dialogs/_recordingSelectorDialog.html.erb similarity index 100% rename from web/app/views/dialogs/_recordingSelector.html.erb rename to web/app/views/dialogs/_recordingSelectorDialog.html.erb From ae71c1b9e90c83c1fca24f8566b1962312f74129 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 21 Mar 2015 10:16:46 -0400 Subject: [PATCH 59/65] VRFS-2701 VRFS-2699 wip performance samples edit screen --- .../javascripts/accounts_profile_samples.js | 6 +- .../dialog/recordingSelectorDialog.js | 68 +++++++++++++++++++ web/app/assets/javascripts/ui_helper.js | 4 +- .../dialogs/recordingSelectorDialog.css.scss | 14 ++++ .../dialogs/_recordingSelectorDialog.html.erb | 14 ++++ .../_recordingSelectorDialog.html.haml | 0 6 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 web/app/views/dialogs/_recordingSelectorDialog.html.haml diff --git a/web/app/assets/javascripts/accounts_profile_samples.js b/web/app/assets/javascripts/accounts_profile_samples.js index 0131f49e4..6938b57fe 100644 --- a/web/app/assets/javascripts/accounts_profile_samples.js +++ b/web/app/assets/javascripts/accounts_profile_samples.js @@ -13,7 +13,11 @@ var profileUtils = context.JK.ProfileUtils; var $screen = $('#account-profile-samples'); - var $btnAddJkRecording = $screen.find('') + //var $btnAddJkRecording = $screen.find(''); + + var $btnCancel = $screen.find('#account-edit-profile-cancel'); + var $btnBack = $screen.find('#account-edit-profile-back'); + var $btnSubmit = $screen.find('#account-edit-profile-submit'); function beforeShow(data) { } diff --git a/web/app/assets/javascripts/dialog/recordingSelectorDialog.js b/web/app/assets/javascripts/dialog/recordingSelectorDialog.js index e69de29bb..cc485aa64 100644 --- a/web/app/assets/javascripts/dialog/recordingSelectorDialog.js +++ b/web/app/assets/javascripts/dialog/recordingSelectorDialog.js @@ -0,0 +1,68 @@ +(function(context,$) { + + "use strict"; + context.JK = context.JK || {}; + context.JK.RecordingSelectorDialog = function(app, recordings, selectedRecordings, callback) { + var logger = context.JK.logger; + var rest = context.JK.Rest(); + var $dialog = null; + var dialogId = 'recording-selector-dialog'; + var $screen = $('#' + dialogId); + var $btnSelect = $screen.find(".btn-select-recordings"); + var $instructions = $screen.find('#instructions'); + var $recordings = $screen.find('.recordings'); + + function beforeShow(data) { + } + + function afterShow(data) { + + $recordings.empty(); + } + + function afterHide() { + } + + function showDialog() { + return app.layout.showDialog(dialogId); + } + + function events() { + $btnSelect.click(function(evt) { + evt.preventDefault(); + var selectedRecordings = []; + $recordings.find('input[type=checkbox]:checked').each(function(index) { + selectedRecordings.push($(this).val()); + }); + + if (callback) { + callback(selectedRecordings); + } + + app.layout.closeDialog(dialogId); + + return false; + + }); + } + + function initialize() { + var dialogBindings = { + 'beforeShow' : beforeShow, + 'afterShow' : afterShow, + 'afterHide': afterHide + }; + + app.bindDialog(dialogId, dialogBindings); + + $instructions.html('Select one or more recordings.'); + + events(); + } + + this.initialize = initialize; + this.showDialog = showDialog; + } + + return this; +})(window,jQuery); \ No newline at end of file diff --git a/web/app/assets/javascripts/ui_helper.js b/web/app/assets/javascripts/ui_helper.js index bdebf103d..01d2aa403 100644 --- a/web/app/assets/javascripts/ui_helper.js +++ b/web/app/assets/javascripts/ui_helper.js @@ -68,8 +68,8 @@ return genreSelectorDialog.showDialog(); } - function launchRecordingSelectorDialog(recordings, callback) { - var recordingSelectorDialog = new JK.RecordingSelectorDialog(JK.app, recordings, callback); + function launchRecordingSelectorDialog(recordings, selectedRecordings, callback) { + var recordingSelectorDialog = new JK.RecordingSelectorDialog(JK.app, recordings, selectedRecordings, callback); recordingSelectorDialog.initialize(); return recordingSelectorDialog.showDialog(); } diff --git a/web/app/assets/stylesheets/dialogs/recordingSelectorDialog.css.scss b/web/app/assets/stylesheets/dialogs/recordingSelectorDialog.css.scss index e69de29bb..c23ea586f 100644 --- a/web/app/assets/stylesheets/dialogs/recordingSelectorDialog.css.scss +++ b/web/app/assets/stylesheets/dialogs/recordingSelectorDialog.css.scss @@ -0,0 +1,14 @@ +@import "client/common"; + +#genre-selector-dialog { + + min-height:initial; + + .dialog-inner { + color:white; + } + + .action-buttons { + margin-bottom:10px; + } +} \ No newline at end of file diff --git a/web/app/views/dialogs/_recordingSelectorDialog.html.erb b/web/app/views/dialogs/_recordingSelectorDialog.html.erb index e69de29bb..a3e1cc93b 100644 --- a/web/app/views/dialogs/_recordingSelectorDialog.html.erb +++ b/web/app/views/dialogs/_recordingSelectorDialog.html.erb @@ -0,0 +1,14 @@ +.dialog.dialog-overlay-sm{layout: 'dialog', 'layout-id' => 'recording-selector-dialog', id: 'recording-selector-dialog'} + .content-head + = image_tag "content/icon_checkmark_circle.png", {:width => 20, :height => 20, :class => 'content-icon' } + %h1 + = 'select recordings' + .dialog-inner + %span{id: 'instructions'} + %br{:clear => "all"}/ + %br{:clear => "all"}/ + .recordings + + .right.action-buttons + %a.button-grey.btn-cancel-dialog{'layout-action' => 'cancel'} CANCEL + %a.button-orange.btn-select-recordings SELECT \ No newline at end of file diff --git a/web/app/views/dialogs/_recordingSelectorDialog.html.haml b/web/app/views/dialogs/_recordingSelectorDialog.html.haml new file mode 100644 index 000000000..e69de29bb From 370ca1086ed5dad803d385a85d72273673a7f098 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sat, 21 Mar 2015 20:53:38 -0400 Subject: [PATCH 60/65] VRFS-2701 VRFS-2699 wip performance samples edit screen --- .../javascripts/accounts_profile_samples.js | 28 +++++++++---------- .../dialogs/_recordingSelectorDialog.html.erb | 14 ---------- .../_recordingSelectorDialog.html.haml | 14 ++++++++++ 3 files changed, 28 insertions(+), 28 deletions(-) delete mode 100644 web/app/views/dialogs/_recordingSelectorDialog.html.erb diff --git a/web/app/assets/javascripts/accounts_profile_samples.js b/web/app/assets/javascripts/accounts_profile_samples.js index 6938b57fe..5ef8e299f 100644 --- a/web/app/assets/javascripts/accounts_profile_samples.js +++ b/web/app/assets/javascripts/accounts_profile_samples.js @@ -31,27 +31,27 @@ } function events() { - $btnAddJkRecording.click(function(evt) { - evt.preventDefault(); - ui.launchRecordingSelectorDialog('', function(selectedRecordings) { + // $btnAddJkRecording.click(function(evt) { + // evt.preventDefault(); + // ui.launchRecordingSelectorDialog('', function(selectedRecordings) { - }); + // }); - return false; - }); + // return false; + // }); - $btnAddSoundCloudRecording.click(function(evt) { + // $btnAddSoundCloudRecording.click(function(evt) { - }); + // }); - $btnAddYouTubeVideo.click(function(evt) { + // $btnAddYouTubeVideo.click(function(evt) { - }); + // }); - $btnSaveAndFinish.click(function(evt) { - $document.triggerHandler(EVENTS.USER_UPDATED, response); - context.location = "/client#/profile/" + context.JK.currentUserId; - }); + // $btnSaveAndFinish.click(function(evt) { + // $document.triggerHandler(EVENTS.USER_UPDATED, response); + // context.location = "/client#/profile/" + context.JK.currentUserId; + // }); } function initialize() { diff --git a/web/app/views/dialogs/_recordingSelectorDialog.html.erb b/web/app/views/dialogs/_recordingSelectorDialog.html.erb deleted file mode 100644 index a3e1cc93b..000000000 --- a/web/app/views/dialogs/_recordingSelectorDialog.html.erb +++ /dev/null @@ -1,14 +0,0 @@ -.dialog.dialog-overlay-sm{layout: 'dialog', 'layout-id' => 'recording-selector-dialog', id: 'recording-selector-dialog'} - .content-head - = image_tag "content/icon_checkmark_circle.png", {:width => 20, :height => 20, :class => 'content-icon' } - %h1 - = 'select recordings' - .dialog-inner - %span{id: 'instructions'} - %br{:clear => "all"}/ - %br{:clear => "all"}/ - .recordings - - .right.action-buttons - %a.button-grey.btn-cancel-dialog{'layout-action' => 'cancel'} CANCEL - %a.button-orange.btn-select-recordings SELECT \ No newline at end of file diff --git a/web/app/views/dialogs/_recordingSelectorDialog.html.haml b/web/app/views/dialogs/_recordingSelectorDialog.html.haml index e69de29bb..a3e1cc93b 100644 --- a/web/app/views/dialogs/_recordingSelectorDialog.html.haml +++ b/web/app/views/dialogs/_recordingSelectorDialog.html.haml @@ -0,0 +1,14 @@ +.dialog.dialog-overlay-sm{layout: 'dialog', 'layout-id' => 'recording-selector-dialog', id: 'recording-selector-dialog'} + .content-head + = image_tag "content/icon_checkmark_circle.png", {:width => 20, :height => 20, :class => 'content-icon' } + %h1 + = 'select recordings' + .dialog-inner + %span{id: 'instructions'} + %br{:clear => "all"}/ + %br{:clear => "all"}/ + .recordings + + .right.action-buttons + %a.button-grey.btn-cancel-dialog{'layout-action' => 'cancel'} CANCEL + %a.button-orange.btn-select-recordings SELECT \ No newline at end of file From e4aa6609ec8ace1fbae3ae8f45c1cf6cfe273a8f Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Sun, 22 Mar 2015 21:13:53 -0400 Subject: [PATCH 61/65] VRFS-2974 fixed broadcast widget bug when session is private --- .../javascripts/jquery.listenbroadcast.js | 54 +++++++++++-------- .../api_music_sessions_controller.rb | 3 +- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/web/app/assets/javascripts/jquery.listenbroadcast.js b/web/app/assets/javascripts/jquery.listenbroadcast.js index 059b7c18b..f6964cd60 100644 --- a/web/app/assets/javascripts/jquery.listenbroadcast.js +++ b/web/app/assets/javascripts/jquery.listenbroadcast.js @@ -74,6 +74,9 @@ var playState = PlayStateNone; // tracks if the stream is actually playing + var CANNOT_BROADCAST_TITLE = 'Unable to Broadcast Session'; + var CANNOT_BROADCAST_MSG = 'This session cannot be broadcasted. The session organizer may have configured it to be private.'; + function play(e) { if(e) { e.preventDefault(); @@ -407,17 +410,21 @@ sessionInfo = response; }) .fail(function(jqXHR) { - if(jqXHR.status == 404 || jqXHR.status == 403) { + if(jqXHR.status == 404) { transition(PlayStateSessionOver); destroy(); } + else if (jqXHR.status == 403) { + logger.debug("session is private"); + context.JK.app.notify({"title": CANNOT_BROADCAST_TITLE, "text": CANNOT_BROADCAST_MSG}); + } else if(jqXHR.status >= 500 && jqXHR.status <= 599){ transition(PlayStateServerError); } else { transition(PlayStateNetworkError); } - }) + }); } function triggerStateChange() { @@ -702,28 +709,31 @@ } function openBubble() { - checkServer().done(function(response) { + checkServer() + .done(function(response) { - var mountId = sessionInfo.mount ? sessionInfo.mount.id : null; + var mountId = sessionInfo.mount ? sessionInfo.mount.id : null; - if(mountId) { - rest.getMount({id: mountId}) - .done(function (mount) { - mountInfo = mount; - $parent.data('mount-id', mountId); - context.JK.SubscriptionUtils.subscribe('mount', mountId).on(context.JK.EVENTS.SUBSCRIBE_NOTIFICATION, onDetailEvent); - $parent.btOn(); - }) - .fail(context.JK.app.ajaxError) - } - else { - mountInfo = null; - destroy(); - context.JK.app.notify({"title": "Unable to Broadcast Session", "text": "This session cannot be broadcasted. The session organizer may have configured it to be private."}); - } - }) - .fail(function() { - logger.debug("session is over") + if(mountId) { + rest.getMount({id: mountId}) + .done(function (mount) { + mountInfo = mount; + $parent.data('mount-id', mountId); + context.JK.SubscriptionUtils.subscribe('mount', mountId).on(context.JK.EVENTS.SUBSCRIBE_NOTIFICATION, onDetailEvent); + $parent.btOn(); + }) + .fail(context.JK.app.ajaxError) + } + else { + mountInfo = null; + destroy(); + context.JK.app.notify({"title": CANNOT_BROADCAST_TITLE, "text": CANNOT_BROADCAST_MSG}); + } + }) + .fail(function(response) { + if (response.status == 404) { + logger.debug("session is over"); + } }) } diff --git a/web/app/controllers/api_music_sessions_controller.rb b/web/app/controllers/api_music_sessions_controller.rb index bb8760dc2..6fb5df5cf 100644 --- a/web/app/controllers/api_music_sessions_controller.rb +++ b/web/app/controllers/api_music_sessions_controller.rb @@ -165,7 +165,8 @@ class ApiMusicSessionsController < ApiController def show unless @music_session.can_see? current_user - raise ActiveRecord::RecordNotFound + # render :json => { :message => ValidationMessages::PERMISSION_VALIDATION_ERROR }, :status => 403 + raise JamRuby::PermissionError end end From 231f7c69b51a3734ff5ed807284fb2115b9eabbb Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 23 Mar 2015 13:11:28 -0400 Subject: [PATCH 62/65] VRFS-2701 wip presence/sample edit --- .../javascripts/accounts_profile_samples.js | 120 +++++++-- .../client/accountProfileInterests.css.scss | 1 - .../client/accountProfileSamples.css.scss | 33 +++ .../dialogs/recordingSelectorDialog.css.scss | 2 +- .../api_music_sessions_controller.rb | 1 - .../clients/_account_profile_samples.html.erb | 245 ++++++------------ 6 files changed, 210 insertions(+), 192 deletions(-) diff --git a/web/app/assets/javascripts/accounts_profile_samples.js b/web/app/assets/javascripts/accounts_profile_samples.js index 5ef8e299f..3b4c6568e 100644 --- a/web/app/assets/javascripts/accounts_profile_samples.js +++ b/web/app/assets/javascripts/accounts_profile_samples.js @@ -13,7 +13,18 @@ var profileUtils = context.JK.ProfileUtils; var $screen = $('#account-profile-samples'); - //var $btnAddJkRecording = $screen.find(''); + var $url = $screen.find('#url'); + var $soundCloudUsername = $screen.find('#soundcloud-username'); + var $reverbNationUsername = $screen.find('#reverbnation-username'); + var $bandCampUsername = $screen.find('#bandcamp-username'); + var $fandalismUsername = $screen.find('#fandalism-username'); + var $youTubeUsername = $screen.find('#youtube-username'); + var $facebookUsername = $screen.find('#facebook-username'); + var $twitterUsername = $screen.find('#twitter-username'); + + var $btnAddJkRecording = $screen.find('#btn-add-jk-recording'); + var $btnAddSoundCloudRecording = $screen.find('#btn-add-soundcloud-recording'); + var $btnAddYouTubeVideo = $screen.find('#btn-add-youtube-video'); var $btnCancel = $screen.find('#account-edit-profile-cancel'); var $btnBack = $screen.find('#account-edit-profile-back'); @@ -23,35 +34,108 @@ } function afterShow(data) { - renderSamples(); + $.when(api.getUserProfile()) + .done(function(userDetail) { + renderPresence(userDetail); + renderSamples(userDetail); + }); } - function renderSamples() { + function renderPresence(user) { + $url.val(user.website); + // SoundCloud + var presences = profileUtils.soundCloudPresences(user.online_presences); + if (presences && presences.length > 0) { + $soundCloudUsername.val(presences[0].username); + } + + // ReverbNation + presences = profileUtils.reverbNationPresences(user.online_presences); + if (presences && presences.length > 0) { + $reverbNationUsername.val(presences[0].username); + } + + // Bandcamp + presences = profileUtils.bandCampPresences(user.online_presences); + if (presences && presences.length > 0) { + $bandCampUsername.val(presences[0].username); + } + + // Fandalism + presences = profileUtils.fandalismPresences(user.online_presences); + if (presences && presences.length > 0) { + $fandalismUsername.val(presences[0].username); + } + + // YouTube + presences = profileUtils.youTubePresences(user.online_presences); + if (presences && presences.length > 0) { + $youTubeUsername.val(presences[0].username); + } + + // Facebook + presences = profileUtils.facebookPresences(user.online_presences); + if (presences && presences.length > 0) { + $facebookUsername.val(presences[0].username); + } + + // Twitter + presences = profileUtils.twitterPresences(user.online_presences); + if (presences && presences.length > 0) { + $twitterUsername.val(presences[0].username); + } + } + + function renderSamples(user) { + + // JamKazam recordings + var samples = profileUtils.jamkazamSamples(user.performance_samples); + if (samples) { + $.each(samples, function(index, val) { + + }); + } + + // SoundCloud recordings + samples = profileUtils.soundCloudSamples(user.performance_samples); + if (samples) { + $.each(samples, function(index, val) { + + }); + } + + // YouTube videos + samples = profileUtils.youTubeSamples(user.performance_samples); + if (samples) { + $.each(samples, function(index, val) { + + }); + } } function events() { - // $btnAddJkRecording.click(function(evt) { - // evt.preventDefault(); - // ui.launchRecordingSelectorDialog('', function(selectedRecordings) { + $btnAddJkRecording.click(function(evt) { + evt.preventDefault(); + ui.launchRecordingSelectorDialog('', function(selectedRecordings) { - // }); + }); - // return false; - // }); + return false; + }); - // $btnAddSoundCloudRecording.click(function(evt) { + $btnAddSoundCloudRecording.click(function(evt) { + // add to list + }); - // }); + $btnAddYouTubeVideo.click(function(evt) { - // $btnAddYouTubeVideo.click(function(evt) { + }); - // }); - - // $btnSaveAndFinish.click(function(evt) { - // $document.triggerHandler(EVENTS.USER_UPDATED, response); - // context.location = "/client#/profile/" + context.JK.currentUserId; - // }); + $btnSubmit.click(function(evt) { + $document.triggerHandler(EVENTS.USER_UPDATED, response); + context.location = "/client#/profile/" + context.JK.currentUserId; + }); } function initialize() { diff --git a/web/app/assets/stylesheets/client/accountProfileInterests.css.scss b/web/app/assets/stylesheets/client/accountProfileInterests.css.scss index 230a1f4b0..7e11df987 100644 --- a/web/app/assets/stylesheets/client/accountProfileInterests.css.scss +++ b/web/app/assets/stylesheets/client/accountProfileInterests.css.scss @@ -1,6 +1,5 @@ @import "common.css.scss"; - #account-profile-interests { .interest { font-weight: 600; diff --git a/web/app/assets/stylesheets/client/accountProfileSamples.css.scss b/web/app/assets/stylesheets/client/accountProfileSamples.css.scss index e69de29bb..134b1cc3c 100644 --- a/web/app/assets/stylesheets/client/accountProfileSamples.css.scss +++ b/web/app/assets/stylesheets/client/accountProfileSamples.css.scss @@ -0,0 +1,33 @@ +@import "common.css.scss"; + +#account-profile-samples { + + .sample { + font-weight: 600; + font-size: 16px; + } + + .presence { + margin: 3px 30px 15px 0px; + + input { + width:200px; + } + } + + .samples { + width: 30%; + + input { + width: 200px; + } + + .sample { + margin: 3px 5px 10px 0px; + } + + .sample-list { + + } + } +} \ No newline at end of file diff --git a/web/app/assets/stylesheets/dialogs/recordingSelectorDialog.css.scss b/web/app/assets/stylesheets/dialogs/recordingSelectorDialog.css.scss index c23ea586f..c9994cff9 100644 --- a/web/app/assets/stylesheets/dialogs/recordingSelectorDialog.css.scss +++ b/web/app/assets/stylesheets/dialogs/recordingSelectorDialog.css.scss @@ -1,6 +1,6 @@ @import "client/common"; -#genre-selector-dialog { +#recording-selector-dialog { min-height:initial; diff --git a/web/app/controllers/api_music_sessions_controller.rb b/web/app/controllers/api_music_sessions_controller.rb index 6fb5df5cf..50100a0a2 100644 --- a/web/app/controllers/api_music_sessions_controller.rb +++ b/web/app/controllers/api_music_sessions_controller.rb @@ -165,7 +165,6 @@ class ApiMusicSessionsController < ApiController def show unless @music_session.can_see? current_user - # render :json => { :message => ValidationMessages::PERMISSION_VALIDATION_ERROR }, :status => 403 raise JamRuby::PermissionError end end diff --git a/web/app/views/clients/_account_profile_samples.html.erb b/web/app/views/clients/_account_profile_samples.html.erb index bb2d80636..708e41557 100644 --- a/web/app/views/clients/_account_profile_samples.html.erb +++ b/web/app/views/clients/_account_profile_samples.html.erb @@ -16,194 +16,97 @@

edit profile: online presence & performance samples

-
- -
-
- -
-
- -
-
-
-
- -
-
- -
+
+ +
+
- -
- - +
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+
+
-
- - +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
-
- -
-
- -
-
- -
+
+ +
+ BROWSE
-
-
- -
-
- -
+
+
scrollable
+
+
+ +
+
+
+ ADD +
+
+
scrollable
- -
- - -
- -
- - -
- -
- - +
+ +
+ +
+
+ ADD +
+
+
scrollable
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
-
- - - -
- - -
- -
- - -
-
- -
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
-
- -
- - -
-
- -
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
-
- -
- - -
- -
- - -
-
-

From 9face3b3f9dd5d1f639249801637de0cefada6dc Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 23 Mar 2015 16:27:36 -0400 Subject: [PATCH 63/65] VRFS-2701 wip presence/sample edit --- ruby/lib/jam_ruby/models/user.rb | 4 +- .../javascripts/accounts_profile_samples.js | 136 +++++++++++++++++- web/app/assets/javascripts/profile_utils.js | 3 +- web/app/views/api_users/profile_show.rabl | 2 +- .../clients/_account_profile_samples.html.erb | 29 +++- 5 files changed, 161 insertions(+), 13 deletions(-) diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index 0d6661b75..f2810d985 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -658,7 +658,7 @@ module JamRuby end online_presences.each do |op| - op = OnlinePresence.create(self.id, online_presences, false) + op = OnlinePresence.create(self, online_presences, false) self.online_presences << op end end @@ -669,7 +669,7 @@ module JamRuby end performance_samples.each do |ps| - ps = PerformanceSample.create(self.id, performance_samples, false) + ps = PerformanceSample.create(self, performance_samples, false) self.performance_samples << ps end end diff --git a/web/app/assets/javascripts/accounts_profile_samples.js b/web/app/assets/javascripts/accounts_profile_samples.js index 3b4c6568e..694039d5d 100644 --- a/web/app/assets/javascripts/accounts_profile_samples.js +++ b/web/app/assets/javascripts/accounts_profile_samples.js @@ -13,7 +13,9 @@ var profileUtils = context.JK.ProfileUtils; var $screen = $('#account-profile-samples'); - var $url = $screen.find('#url'); + + // online presences + var $website = $screen.find('#website'); var $soundCloudUsername = $screen.find('#soundcloud-username'); var $reverbNationUsername = $screen.find('#reverbnation-username'); var $bandCampUsername = $screen.find('#bandcamp-username'); @@ -22,6 +24,15 @@ var $facebookUsername = $screen.find('#facebook-username'); var $twitterUsername = $screen.find('#twitter-username'); + // performance samples + var $soundCloudRecordingUrl = $screen.find('#soundcloud-recording'); + var $youTubeVideoUrl = $screen.find('#youtube-video'); + + var $jamkazamSampleList = $screen.find('.samples.jamkazam'); + var $soundCloudSampleList = $screen.find('.samples.soundcloud'); + var $youTubeSampleList = $screen.find('.samples.youtube'); + + // buttons var $btnAddJkRecording = $screen.find('#btn-add-jk-recording'); var $btnAddSoundCloudRecording = $screen.find('#btn-add-soundcloud-recording'); var $btnAddYouTubeVideo = $screen.find('#btn-add-youtube-video'); @@ -42,7 +53,7 @@ } function renderPresence(user) { - $url.val(user.website); + $website.val(user.website); // SoundCloud var presences = profileUtils.soundCloudPresences(user.online_presences); @@ -115,6 +126,49 @@ } function events() { + + $website.blur(function(evt) { + evt.preventDefault(); + // TODO: validate website + }); + + $soundCloudUsername.blur(function(evt) { + evt.preventDefault(); + // TODO: validate SoundCloud username + }); + + $reverbNationUsername.blur(function(evt) { + evt.preventDefault(); + // TODO: validate ReverbNation username + }); + + $bandCampUsername.blur(function(evt) { + evt.preventDefault(); + // TODO: validate Bandcamp username + }); + + $fandalismUsername.blur(function(evt) { + evt.preventDefault(); + // TODO: validate Fandalism username + }); + + $youTubeUsername.blur(function(evt) { + evt.preventDefault(); + // TODO: validate YouTube username + }); + + $facebookUsername.blur(function(evt) { + evt.preventDefault(); + // TODO: validate Facebook username + }); + + $twitterUsername.blur(function(evt) { + evt.preventDefault(); + // TODO: validate Twitter username + }); + + + // buttons $btnAddJkRecording.click(function(evt) { evt.preventDefault(); ui.launchRecordingSelectorDialog('', function(selectedRecordings) { @@ -125,19 +179,91 @@ }); $btnAddSoundCloudRecording.click(function(evt) { - // add to list + var url = $soundCloudRecordingUrl.val(); + if (url.length > 0) { + if (extractSoundCloudUrlParts(url)) { + // add to list + } + } }); $btnAddYouTubeVideo.click(function(evt) { - + var url = $youTubeVideoUrl.val(); + if (url.length) { + if (extractYouTubeUrlParts(url)) { + // add to list + } + } }); $btnSubmit.click(function(evt) { $document.triggerHandler(EVENTS.USER_UPDATED, response); - context.location = "/client#/profile/" + context.JK.currentUserId; + + handleUpdateProfile(); + return false; }); } + function addOnlinePresence(presenceArray, username, type) { + if ($.trim($soundCloudUsername.val()).length > 0) { + presenceArray.push({ + service_type: type, + username: username + }); + } + } + + function addPerformanceSamples(sampleArray, $samplesSelector, type) { + var sampleTable = $samplesSelector.find('table'); + // loop over rows, extracting service id, description, and url + } + + function handleUpdateProfile() { + + // extract online presences + var op = []; + var presenceTypes = profileUtils.ONLINE_PRESENCE_TYPES; + addOnlinePresence(op, $soundCloudUsername.val(), presenceTypes.SOUNDCLOUD.description); + addOnlinePresence(op, $reverbNationUsername.val(), presenceTypes.REVERBNATION.description); + addOnlinePresence(op, $bandCampUsername.val(), presenceTypes.BANDCAMP.description); + addOnlinePresence(op, $fandalismUsername.val(), presenceTypes.FANDALISM.description); + addOnlinePresence(op, $youTubeUsername.val(), presenceTypes.YOUTUBE.description); + addOnlinePresence(op, $facebookUsername.val(), presenceTypes.FACEBOOK.description); + addOnlinePresence(op, $twitterUsername.val(), presenceTypes.TWITTER.description); + + // extract performance samples + var ps = []; + var performanceSampleTypes = profileUtils.SAMPLE_TYPES; + addPerformanceSamples(ps, $jamkazamSampleList, performanceSampleTypes.JAMKAZAM); + addPerformanceSamples(ps, $soundCloudSampleList, performanceSampleTypes.SOUNDCLOUD); + addPerformanceSamples(ps, $youTubeSampleList, performanceSampleTypes.YOUTUBE); + + // api.updateUser({ + // website: $website.val(), + // online_presences: op, + // performance_samples: ps + // }) + // .done(postUpdateProfileSuccess) + // .fail(postUpdateProfileFailure); + } + + function postUpdateProfileSuccess(response) { + $document.triggerHandler(EVENTS.USER_UPDATED, response); + context.location = "/client#/profile/" + context.JK.currentUserId; + } + + function postUpdateProfileFailure(xhr, textStatus, errorMessage) { + + var errors = JSON.parse(xhr.responseText) + + if(xhr.status == 422) { + + } + else { + app.ajaxError(xhr, textStatus, errorMessage) + } + } + function initialize() { var screenBindings = { 'beforeShow': beforeShow, diff --git a/web/app/assets/javascripts/profile_utils.js b/web/app/assets/javascripts/profile_utils.js index 126e60be8..edab5c367 100644 --- a/web/app/assets/javascripts/profile_utils.js +++ b/web/app/assets/javascripts/profile_utils.js @@ -17,13 +17,14 @@ var FREE_SESSION_GENRE_TYPE = 'free_session'; var COWRITING_GENRE_TYPE = 'cowriting'; - // performance samples + // performance sample types var SAMPLE_TYPES = { JAMKAZAM: {description: "jamkazam"}, SOUNDCLOUD: {description: "soundcloud"}, YOUTUBE: {description: "youtube"} }; + // online presence types var ONLINE_PRESENCE_TYPES = { SOUNDCLOUD: {description: "soundcloud"}, REVERBNATION: {description: "reverbnation"}, diff --git a/web/app/views/api_users/profile_show.rabl b/web/app/views/api_users/profile_show.rabl index 5be632329..56a822dfe 100644 --- a/web/app/views/api_users/profile_show.rabl +++ b/web/app/views/api_users/profile_show.rabl @@ -11,7 +11,7 @@ child :performance_samples => :performance_samples do attributes :id, :url, :service_type, :claimed_recording_id, :service_id, :description child :claimed_recording => :claimed_recording do - attributes :name + attributes :id, :name end end diff --git a/web/app/views/clients/_account_profile_samples.html.erb b/web/app/views/clients/_account_profile_samples.html.erb index 708e41557..51e6cd1be 100644 --- a/web/app/views/clients/_account_profile_samples.html.erb +++ b/web/app/views/clients/_account_profile_samples.html.erb @@ -19,7 +19,7 @@
- +
@@ -80,7 +80,14 @@ BROWSE
-
scrollable
+
+ + + + + +
Test RecordingX
+
@@ -91,7 +98,14 @@ ADD
-
scrollable
+
+ + + + + +
Test RecordingX
+
@@ -102,7 +116,14 @@ ADD
-
scrollable
+
+ + + + + +
Test RecordingX
+
From d3cf83db8507b32daa1cf9f013b95d48731abeb8 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 23 Mar 2015 22:26:36 -0400 Subject: [PATCH 64/65] VRFS-2701 remove use of template on experience screen --- .../accounts_profile_experience.js | 47 +++--- .../_account_profile_experience.html.erb | 134 +++++++++--------- 2 files changed, 85 insertions(+), 96 deletions(-) diff --git a/web/app/assets/javascripts/accounts_profile_experience.js b/web/app/assets/javascripts/accounts_profile_experience.js index 3e0b1efb6..53a9a2c78 100644 --- a/web/app/assets/javascripts/accounts_profile_experience.js +++ b/web/app/assets/javascripts/accounts_profile_experience.js @@ -9,14 +9,14 @@ var EVENTS = context.JK.EVENTS; var api = context.JK.Rest(); var $screen = $('#account-profile-experience'); - var $scroller = $screen.find('#account-profile-content-scroller'); - var $instrumentSelector = null; - var $userGenres = null; var profileUtils = context.JK.ProfileUtils; var $btnCancel = $screen.find('#account-edit-profile-cancel'); var $btnBack = $screen.find('#account-edit-profile-back'); var $btnSubmit = $screen.find('#account-edit-profile-submit'); + var $instrumentSelector = $screen.find('.instrument_selector'); + var $userGenres = $screen.find('#user-genres'); + function beforeShow(data) { } @@ -26,23 +26,15 @@ } function resetForm() { - $scroller.find('form .error-text').remove(); - $scroller.find('form .error').removeClass("error"); + $screen.find('form .error-text').remove(); + $screen.find('form .error').removeClass("error"); } function populateAccountProfile(userDetail, instruments) { - var template = context.JK.fillTemplate($('#template-account-profile-experience').html(), { - user_instruments: userDetail.instruments - }); - - $scroller.html(template); - - $instrumentSelector = $screen.find('.instrument_selector'); - $userGenres = $screen.find('#user-genres'); - events(); loadGenres(profileUtils.profileGenres(userDetail.genres)); + $instrumentSelector.empty(); $.each(instruments, function(index, instrument) { var template = context.JK.fillTemplate($('#account-profile-instrument').html(), { checked : isUserInstrument(instrument, userDetail.instruments) ? "checked=\"checked\"" :"", @@ -55,15 +47,15 @@ // and fill in the proficiency for the instruments that the user can play if(userDetail.instruments) { $.each(userDetail.instruments, function(index, userInstrument) { - $('tr[data-instrument-id="' + userInstrument.instrument_id + '"] select.proficiency_selector', $scroller).val(userInstrument.proficiency_level); + $('tr[data-instrument-id="' + userInstrument.instrument_id + '"] select.proficiency_selector', $screen).val(userInstrument.proficiency_level); }); } - $scroller.find('select[name=skill_level]').val(userDetail.skill_level); - $scroller.find('select[name=concert_count]').val(userDetail.concert_count); - $scroller.find('select[name=studio_session_count]').val(userDetail.studio_session_count); + $screen.find('select[name=skill_level]').val(userDetail.skill_level); + $screen.find('select[name=concert_count]').val(userDetail.concert_count); + $screen.find('select[name=studio_session_count]').val(userDetail.studio_session_count); - context.JK.dropdown($('select', $scroller)); + context.JK.dropdown($('select', $screen)); } function isUserInstrument(instrument, userInstruments) { @@ -122,6 +114,7 @@ function events() { $btnCancel.click(function(evt) { + console.log("HERE"); evt.stopPropagation(); navigateTo('/client#/profile/' + context.JK.currentUserId); return false; @@ -158,16 +151,15 @@ function handleUpdateProfile() { resetForm(); - var instruments = getInstrumentsValue(); + var instruments = getSelectedInstruments(); var genres = getSelectedGenres(); - var status = api.updateUser({ instruments: instruments, genres: genres, - skill_level: $scroller.find('select[name=skill_level]').val(), - concert_count: $scroller.find('select[name=concert_count]').val(), - studio_session_count: $scroller.find('select[name=studio_session_count]').val() + skill_level: $screen.find('select[name=skill_level]').val(), + concert_count: $screen.find('select[name=concert_count]').val(), + studio_session_count: $screen.find('select[name=studio_session_count]').val() }) .done(postUpdateProfileSuccess) .fail(postUpdateProfileFailure); @@ -186,7 +178,7 @@ var instruments = context.JK.format_errors("musician_instruments", errors); if(instruments != null) { - instrumentSelector.closest('div.field').addClass('error').append(instruments); + $instrumentSelector.closest('div.field').addClass('error').append(instruments); } } else { @@ -194,7 +186,7 @@ } } - function getInstrumentsValue() { + function getSelectedInstruments() { var instruments = []; $('input[type=checkbox]:checked', $instrumentSelector).each(function(i) { @@ -217,7 +209,10 @@ 'beforeShow': beforeShow, 'afterShow': afterShow }; + app.bindScreen('account/profile/experience', screenBindings); + + events(); } this.initialize = initialize; diff --git a/web/app/views/clients/_account_profile_experience.html.erb b/web/app/views/clients/_account_profile_experience.html.erb index 55a731ba7..5360c22b9 100644 --- a/web/app/views/clients/_account_profile_experience.html.erb +++ b/web/app/views/clients/_account_profile_experience.html.erb @@ -9,81 +9,75 @@
-