diff --git a/admin/Gemfile b/admin/Gemfile index 6f5c0b21e..2e1799c5d 100644 --- a/admin/Gemfile +++ b/admin/Gemfile @@ -41,7 +41,7 @@ gem 'uuidtools', '2.1.2' gem 'jquery-rails' # , '2.3.0' # pinned because jquery-ui-rails was split from jquery-rails, but activeadmin doesn't support this gem yet gem 'jquery-ui-rails', '4.2.1' gem 'rails3-jquery-autocomplete' -gem 'activeadmin', '0.6.2' +gem 'activeadmin' #, github: 'activeadmin', branch: '0-6-stable' gem 'mime-types', '1.25' gem 'meta_search' gem 'fog', "~> 1.18.0" diff --git a/admin/app/admin/cohorts.rb b/admin/app/admin/cohorts.rb new file mode 100644 index 000000000..c39ec8048 --- /dev/null +++ b/admin/app/admin/cohorts.rb @@ -0,0 +1,71 @@ +ActiveAdmin.register Cohort, :as => 'Cohorts' do + + menu :label => 'Cohorts All-time', :parent => 'Reports' + + config.sort_order = 'group_start_desc' + config.batch_actions = false + config.clear_action_items! + config.filters = false + config.per_page = 50 + + controller do + + def scoped_collection + objs = super + Cohort.alltime_cohorts! + objs.where(all_time: true).order('group_start DESC') + end + + end + + index :title => "All-Time Cohorts" do + column 'Cohort' do |cc| + div(class: :cohort_col) { cc.group_start_str } + end + + column Cohort::ALLTIME_LABELS[:registered_users] do |cc| + div(class: :cohort_col) { cc.data_val(:registered_users) } + end + + 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::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::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::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_tracks_played, true) } end + + 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::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::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::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 + end + +end diff --git a/admin/app/admin/cohorts_monthly.rb b/admin/app/admin/cohorts_monthly.rb new file mode 100644 index 000000000..5c24179c7 --- /dev/null +++ b/admin/app/admin/cohorts_monthly.rb @@ -0,0 +1,96 @@ +ActiveAdmin.register Cohort, :as => 'Cohorts Monthly' do + + menu :label => 'Cohorts Monthly', :parent => 'Reports' + + config.sort_order = 'group_start_desc' + config.batch_actions = false + config.clear_action_items! + config.per_page = 50 + + filter(:monthly_start, as: :select, collection: Cohort.monthly_starts) + + controller do + def scoped_collection + args = params[:q] || {} + 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 => 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| + 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::MONTHLY_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::MONTHLY_LABELS[:music_sessions_user_history_1] do |cc| + div(class: :cohort_col) { cc.data_val(:music_sessions_user_history_1) } + 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| + div(class: :cohort_col) { cc.data_val(:music_sessions_user_history_2_5) } + 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| + div(class: :cohort_col) { cc.data_val(:music_sessions_user_history_6_) } + end + column '%' do |cc| div(class: :cohort_col) { cc.data_val(:music_sessions_user_history_6_, true) } end + + 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| div(class: :cohort_col) { cc.data_val(:jam_tracks_played_1, true) } end + + 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| div(class: :cohort_col) { cc.data_val(:jam_tracks_played_2_5, true) } end + + 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| div(class: :cohort_col) { cc.data_val(:jam_tracks_played_6_, true) } end + + column Cohort::MONTHLY_LABELS[:jam_track_rights_redeemed] do |cc| + div(class: :cohort_col) { cc.data_val(:jam_track_rights_redeemed) } + 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| + 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::MONTHLY_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::MONTHLY_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::MONTHLY_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 + end + +end 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/jam_tracks.rb b/admin/app/admin/jam_tracks.rb index 67d961f22..96f341829 100644 --- a/admin/app/admin/jam_tracks.rb +++ b/admin/app/admin/jam_tracks.rb @@ -22,29 +22,18 @@ 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 :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/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/app/assets/stylesheets/active_admin.css.scss b/admin/app/assets/stylesheets/active_admin.css.scss index 48aa7bb98..b71df4545 100644 --- a/admin/app/assets/stylesheets/active_admin.css.scss +++ b/admin/app/assets/stylesheets/active_admin.css.scss @@ -31,3 +31,7 @@ // .active_admin applies to any Active Admin namespace // .admin_namespace applies to the admin namespace (eg: /admin) // .other_namespace applies to a custom namespace named other (eg: /other) + +.cohort_col { + text-align: center; +} diff --git a/admin/app/models/cohort.rb b/admin/app/models/cohort.rb new file mode 100644 index 000000000..29523a46d --- /dev/null +++ b/admin/app/models/cohort.rb @@ -0,0 +1,362 @@ +require 'date' + +class Cohort < ActiveRecord::Base + + EARLIEST_DATE = Time.parse('2014-03-01') + TOTAL_COHORT_DATE = Time.at(0) + + ALLTIME_LABELS = { + registered_users: 'Registered Users', + first_downloaded_client_at: 'DL app', + first_certified_gear_at: 'Certified Gear', + music_sessions_user_history: 'Played Online', + jam_tracks_played: 'Played JT', + jam_track_rights: 'Purchased JT', + recorded_tracks: 'Made Recording', + friendships: 'Friended', + invited_users: 'Invite Others', + } + + MONTHLY_LABELS = { + registered_users: 'Registered Users', + 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_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: 'Friended', + invited_users: 'Invite Others', + } + + attr_accessible :all_time, :monthly_start + serialize :data_set, JSON + + before_create do + self.data_set ||= {} + end + + def self.date_tuples(from,to) + prec = from.size + start = Date.new(*from) + finish = Date.new(*to) + + filter_on = [:day,:mon].first(3-prec) + filter = ->(d) { filter_on.all? {|attr| d.send(attr) == 1 } } + + (start..finish) + .select(&filter) + .map { |d| [d.year,d.mon,d.day].first(prec) } + end + + def self.earliest_cohort + starting = User.where(admin: false).order(:created_at).first.created_at + starting = EARLIEST_DATE if starting.nil? || starting < EARLIEST_DATE + starting # this is necessary to always return not null + end + + def self.cohort_group_ranges(starting=nil, ending=nil) + starting ||= self.earliest_cohort + ending ||= Time.now + dates = self.date_tuples([starting.year, starting.month], [ending.year, ending.month]) + ranges = [] + dates.each_with_index do |d1, idx| + d2 = dates[idx+1] || [Time.now.next_month.year,Time.now.next_month.month] + rr = Time.parse("#{d1[0]}-#{d1[1]}-1")..(Time.parse("#{d2[0]}-#{d2[1]}-1") - 1.second) + ranges << rr + end + ranges + end + + def self.generate_monthly_cohorts(monthly_start, monthly_end) + Cohort.delete_all(['all_time = ?',false]) + self.cohort_group_ranges.collect do |range| + next if range.first > monthly_end + cc = Cohort.new + cc.group_start = range.first + cc.group_end = range.last + cc.monthly_start = monthly_start + cc.monthly_end = monthly_end + cc.all_time = false + cc.save! + cc + end + 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 + cc.group_start = range.first + cc.group_end = range.last + cc.all_time = true + cc.save! + end + cc + end + end + + def _put_data_set(key, count, num_user) + self.data_set[key.to_s] = count + xx = (count.to_f / num_user.to_f) + self.data_set["#{key}%"] = 100.0 * xx.round(2) + end + + def self.cohort_users(cohort) + User.where(created_at: cohort.group_start..cohort.group_end) + end + + def _played_online_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 + 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 + ) > 1 + GROUP BY user_id + ) played +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 =<= '#{start_date}' AND + tt.created_at <= '#{end_date}' +SQL + yield(sql) if block_given? + self.class.cohort_users(self).where("users.id IN (#{sql})").count + end + + def _monthly! + unless 0 < num_user = self.class.cohort_users(self).count + self.update_attribute(:data_set, {}) + return + end + + self.data_set['registered_users'] = num_user + num_user = num_user.to_f + + qq = self.class.cohort_users(self) + .where(first_downloaded_client_at: self.monthly_start..self.monthly_end) + _put_data_set(:first_downloaded_client_at, qq.count, num_user) + + qq = self.class.cohort_users(self) + .where(first_certified_gear_at: self.monthly_start..self.monthly_end) + _put_data_set(:first_certified_gear_at, qq.count, num_user) + + count = _subquery(assoc_key = :invited_users, num_user) + _put_data_set(assoc_key, count, num_user) + + count = _subquery(assoc_key = :recorded_tracks, num_user) + _put_data_set(assoc_key, count, num_user) + + count = _subquery(assoc_key = :jam_track_rights, num_user) do |subsql| + subsql += " AND tt.redeemed = 'f' " + end + _put_data_set(assoc_key, count, num_user) + + count = _subquery(assoc_key = :jam_track_rights, num_user) do |subsql| + subsql += " AND tt.redeemed = 't' " + end + _put_data_set(:jam_track_rights_redeemed, count, num_user) + + count = _subquery(assoc_key = :friendships, num_user) + _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 + _put_data_set(:music_sessions_user_history_1, count, num_user) + + sql = _played_online_subquery(2..5) + count = self.class.cohort_users(self).where("users.id IN (#{sql})").count + _put_data_set(:music_sessions_user_history_2_5, count, num_user) + + sql = _played_online_subquery(' >= 6') + count = self.class.cohort_users(self).where("users.id IN (#{sql})").count + _put_data_set(:music_sessions_user_history_6_, count, num_user) + + sql = _played_jamtrack_subquery(' = 1 ') + count = self.class.cohort_users(self).where("users.id IN (#{sql})").count + _put_data_set(:jam_tracks_played_1, count, num_user) + + sql = _played_jamtrack_subquery(2..5) + count = self.class.cohort_users(self).where("users.id IN (#{sql})").count + _put_data_set(:jam_tracks_played_2_5, 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 + + def _join_user_all_time(assoc_ref) + assoc_ref.active_record + .joins("INNER JOIN users AS uu ON uu.id = #{assoc_ref.foreign_key}") + .where(created_at: self.group_start..self.group_end) + .where(['uu.created_at >= ? AND uu.created_at <= ?', self.group_start, self.group_end]) + end + + def _all_time! + unless 0 < num_user = self.class.cohort_users(self).count + self.update_attribute(:data_set, {}) + return + end + + self.data_set['registered_users'] = num_user + num_user = num_user.to_f + + count = self.class.cohort_users(self) + .where(['first_downloaded_client_at IS NOT NULL']) + .count + _put_data_set('first_downloaded_client_at', count, num_user) + + count = self.class.cohort_users(self) + .where(['first_certified_gear_at IS NOT NULL']) + .count + _put_data_set('first_certified_gear_at', count, num_user) + + count = _subquery(assoc_key = :invited_users, num_user) + _put_data_set(assoc_key, count, num_user) + + count = _subquery(assoc_key = :recorded_tracks, num_user) + _put_data_set(assoc_key, count, num_user) + + count = _subquery(assoc_key = :friendships, num_user) + _put_data_set(assoc_key, count, num_user) + + count = _subquery(assoc_key = :jam_track_rights, 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 + _put_data_set(:music_sessions_user_history, count, num_user) + + self.save! + end + + def populate! + self.all_time ? _all_time! : _monthly! + end + + 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.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 + is_total_cohort? ? 'Total' : self.group_start.strftime('%Y-%m') + end + + def group_end_str + self.group_end.strftime('%Y-%m-%d') + end + + def data_val(col, percent=false) + if percent + val = self.data_set["#{col}%"] + val ? "#{'%0.f' % val}%" : '' + else + self.data_set[col.to_s] + end + end + + def self.monthly_starts + self.cohort_group_ranges.collect do |rr| + rr.first.to_s + end + end + + def is_total_cohort? + self.group_start == TOTAL_COHORT_DATE + end + +end + diff --git a/admin/app/views/admin/jam_tracks/_form.html.slim b/admin/app/views/admin/jam_tracks/_form.html.slim index 2eb08fd27..fc0d9502a 100644 --- a/admin/app/views/admin/jam_tracks/_form.html.slim +++ b/admin/app/views/admin/jam_tracks/_form.html.slim @@ -4,27 +4,29 @@ = f.input :name, :input_html => { :rows=>1, :maxlength=>200 } = f.input :description, :input_html => { :rows=>5, :maxlength=>1000 } = 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 :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' + = 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.semantic_fields_for :jam_track_tracks do |track| = render 'jam_track_track_fields', f: track diff --git a/admin/app/views/admin/jam_tracks/_jam_track_track_fields.html.slim b/admin/app/views/admin/jam_tracks/_jam_track_track_fields.html.slim index 593d8ef7d..0ef6078ac 100644 --- a/admin/app/views/admin/jam_tracks/_jam_track_track_fields.html.slim +++ b/admin/app/views/admin/jam_tracks/_jam_track_track_fields.html.slim @@ -4,24 +4,29 @@ = f.input :track_type, :as => :select, collection: ['Track', 'Master'], include_blank: false = f.input :instrument, collection: Instrument.all, include_blank: false = f.input :part, :required=>true, :input_html => { :rows=>1, :maxlength=>20, :type=>'numeric' } - = f.input :position + = 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.preview_sign_url(3600) style='padding:0 0 0 20px' + | Download Preview + // temporarily disable - if f.object.new_record? - p style='margin-left:10px' - i - | before you can upload, you must select 'Update JamTrack' + //p style='margin-left:10px' + //i + // | before you can upload, you must select 'Update JamTrack' - else - = f.input :url_48, :as => :file, :label => 'Track file (48kHz)' + // = f.input :url_48, :as => :file, :label => 'Track file (48kHz)' - unless f.object.nil? || f.object[:url_48].nil? .current_file_holder style='margin-bottom:10px' a href=f.object.sign_url(3600) style='padding:0 0 0 20px' - | Download + | #{File.basename(f.object[:url_48])} - = f.input :url_44, :as => :file, :label => 'Track file (44kHz)' + // = f.input :url_44, :as => :file, :label => 'Track file (44kHz)' - unless f.object.nil? || f.object[:url_44].nil? .current_file_holder style='margin-bottom:10px' a href=f.object.sign_url(3600, 44) style='padding:0 0 0 20px' - | Download + | #{File.basename(f.object[:url_44])} = link_to_remove_association "Delete Track", f, class: 'button', style: 'margin-left:10px' \ No newline at end of file diff --git a/admin/config/initializers/jam_track_tracks.rb b/admin/config/initializers/jam_track_tracks.rb new file mode 100644 index 000000000..36f82d79e --- /dev/null +++ b/admin/config/initializers/jam_track_tracks.rb @@ -0,0 +1,102 @@ +class JamRuby::JamTrackTrack + + # add a custom validation + + attr_accessor :preview_generate_error + + validate :preview + + def preview + if preview_generate_error + errors.add(:preview_start_time, 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 + + raise 'no track' unless self["url_44"] + + s3_manager.download(self.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 + @@log.debug("fail #{result_code}") + @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) + + self.skip_uploader = true + # 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 + @@log.error("error in sox command #{e.to_s}") + @preview_generate_error = e.to_s + end + + end + +end diff --git a/admin/config/initializers/jam_tracks.rb b/admin/config/initializers/jam_tracks.rb index c88fc6a2c..cd02eccee 100644 --- a/admin/config/initializers/jam_tracks.rb +++ b/admin/config/initializers/jam_tracks.rb @@ -2,6 +2,8 @@ class JamRuby::JamTrack # add a custom validation + attr_accessor :preview_generate_error + before_save :jmep_json_generate validate :jmep_text_validate @@ -14,12 +16,15 @@ 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 #errors.add(:jmep_text, err.to_s) end end - - end 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 5463007c6..4a3c59b8b 100755 --- a/db/manifest +++ b/db/manifest @@ -263,4 +263,11 @@ 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 +jam_track_redeemed.sql +connection_metronome.sql +preview_jam_track_tracks.sql +cohorts.sql +jam_track_right_admin_purchase.sql +alter_genre_player_unique_constraint.sql musician_search.sql 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/db/up/cohorts.sql b/db/up/cohorts.sql new file mode 100644 index 000000000..3a7cbbb5b --- /dev/null +++ b/db/up/cohorts.sql @@ -0,0 +1,19 @@ +CREATE TABLE cohorts ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(), + + data_set JSON NOT NULL DEFAULT '{}', + + group_start TIMESTAMP NOT NULL, + group_end TIMESTAMP NOT NULL, + + all_time BOOLEAN NOT NULL DEFAULT FALSE, + monthly_start TIMESTAMP, + monthly_end TIMESTAMP, + + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX index_group_date ON cohorts USING btree (group_start); + +CREATE INDEX msuh_music_session_idx ON music_sessions_user_history USING btree(music_session_id); 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/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/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 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/db/up/preview_jam_track_tracks.sql b/db/up/preview_jam_track_tracks.sql new file mode 100644 index 000000000..a8c9ae224 --- /dev/null +++ b/db/up/preview_jam_track_tracks.sql @@ -0,0 +1,9 @@ +ALTER TABLE jam_tracks DROP COLUMN preview_url; +ALTER TABLE jam_tracks DROP COLUMN preview_md5; +ALTER TABLE jam_tracks DROP COLUMN preview_length; +ALTER TABLE jam_tracks DROP COLUMN preview_start_time; + +ALTER TABLE jam_track_tracks ADD COLUMN preview_url VARCHAR; +ALTER TABLE jam_track_tracks ADD COLUMN preview_md5 VARCHAR; +ALTER TABLE jam_track_tracks ADD COLUMN preview_length BIGINT; +ALTER TABLE jam_track_tracks ADD COLUMN preview_start_time INTEGER; \ No newline at end of file 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/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/jam_track.rb b/ruby/lib/jam_ruby/models/jam_track.rb index 73e1634c3..646fd18ff 100644 --- a/ruby/lib/jam_ruby/models/jam_track.rb +++ b/ruby/lib/jam_ruby/models/jam_track.rb @@ -12,35 +12,34 @@ module JamRuby @@log = Logging.logger[JamTrack] - mount_uploader :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, 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 :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' @@ -60,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) @@ -91,26 +102,12 @@ 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" - 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 - # 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}) + def master_track + JamTrackTrack.where(jam_track_id: self.id).where(track_type: 'Master').first end - def can_download?(user) owners.include?(user) end @@ -118,50 +115,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_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/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/lib/jam_ruby/models/jam_track_track.rb b/ruby/lib/jam_ruby/models/jam_track_track.rb index 255cd0337..419b49c63 100644 --- a/ruby/lib/jam_ruby/models/jam_track_track.rb +++ b/ruby/lib/jam_ruby/models/jam_track_track.rb @@ -7,17 +7,22 @@ module JamRuby # there should only be one Master per JamTrack, but there can be N Track per JamTrack TRACK_TYPE = %w{Track Master} - mount_uploader :url_48, JamTrackTrackUploader - mount_uploader :url_44, JamTrackTrackUploader + @@log = Logging.logger[JamTrackTrack] + + + # Because JamTrackImporter imports audio files now, and because also the mere presence of this causes serious issues when updating the model (because reset of url_44 to something bogus), I've removed these + #mount_uploader :url_48, JamTrackTrackUploader + #mount_uploader :url_44, JamTrackTrackUploader attr_accessible :jam_track_id, :track_type, :instrument, :instrument_id, :position, :part, as: :admin - attr_accessible :url_44, :url_48, :md5_44, :md5_48, :length_44, :length_48, as: :admin + attr_accessible :url_44, :url_48, :md5_44, :md5_48, :length_44, :length_48, :preview_start_time_raw, as: :admin attr_accessor :original_audio_s3_path, :skip_uploader validates :position, presence: true, numericality: {only_integer: true}, length: {in: 1..1000} validates :part, length: {maximum: 25} validates :track_type, inclusion: {in: TRACK_TYPE } + validates :preview_start_time, numericality: {only_integer: true}, length: {in: 1..1000}, :allow_nil => true validates_uniqueness_of :position, scope: :jam_track_id validates_uniqueness_of :part, scope: [:jam_track_id, :instrument_id] # validates :jam_track, presence: true @@ -37,6 +42,24 @@ module JamRuby "#{store_dir}/#{jam_track.original_artist}/#{jam_track.name}/#{original_name}" end + # create name of the file + def preview_filename + filename("#{File.basename(self["url_44"], ".ogg")}-preview.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 + # but the url is short lived enough so that it wouldn't be easily shared + def preview_sign_url(expiration_time = 120) + s3_manager.sign_url(self[:preview_url], {:expires => expiration_time, :response_content_type => 'audio/ogg', :secure => false}) + end + + def manually_uploaded_filename(mounted_as) if track_type == 'Master' filename("Master Mix-#{mounted_as == :url_48 ? '48000' : '44100'}.ogg") 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| 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/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index ef07016c8..37381a839 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -660,7 +660,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 @@ -671,7 +671,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/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..e1e99c3f2 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) @@ -66,15 +58,15 @@ describe JamTrackRight do user = FactoryGirl.create(:user) jam_track_track = FactoryGirl.create(:jam_track_track) jam_track = jam_track_track.jam_track - - uploader = JamTrackTrackUploader.new(jam_track_track, :url_48) - uploader.store!(File.open(ogg_path, 'rb')) + + s3 = S3Manager.new(APP_CONFIG.aws_bucket, APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key) + + s3.upload(jam_track_track.manually_uploaded_filename(:url_48), ogg_path) + jam_track_track[:url_48] = jam_track_track.manually_uploaded_filename(:url_48) jam_track_track.save! - jam_track_track[:url_48].should == jam_track_track.manually_uploaded_filename(:url_48) # 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_track[:url_48]).should be_true s3.length(jam_track_track[:url_48]).should == File.size?(ogg_path) diff --git a/ruby/spec/jam_ruby/models/jam_track_spec.rb b/ruby/spec/jam_ruby/models/jam_track_spec.rb index af4980f7b..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 - JKA_NAME = 'blah.jkz' - - in_directory_with_file(JKA_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, :url) - uploader.store!(File.open(JKA_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 - - # 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 - - # 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 diff --git a/ruby/spec/jam_ruby/models/jam_track_track_spec.rb b/ruby/spec/jam_ruby/models/jam_track_track_spec.rb index 5d2f934a4..bce440ad8 100644 --- a/ruby/spec/jam_ruby/models/jam_track_track_spec.rb +++ b/ruby/spec/jam_ruby/models/jam_track_track_spec.rb @@ -46,9 +46,11 @@ describe JamTrackTrack do end it "uploads to s3 with correct name, and then downloads via signed URL" do + s3 = S3Manager.new(APP_CONFIG.aws_bucket, APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key) + jam_track_track = FactoryGirl.create(:jam_track_track) - uploader = JamTrackTrackUploader.new(jam_track_track, :url_48) - uploader.store!(File.open(TRACK_NAME)) # uploads file + s3.upload(jam_track_track.manually_uploaded_filename(:url_48), TRACK_NAME) + jam_track_track[:url_48] = jam_track_track.manually_uploaded_filename(:url_48) jam_track_track.save! jam_track_track.reload @@ -56,7 +58,6 @@ describe JamTrackTrack do jam_track_track[:url_48].should == jam_track_track.manually_uploaded_filename(:url_48) # 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_track[:url_48]).should be_true s3.length(jam_track_track[:url_48]).should == 'abc'.length 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/ruby/spec/jam_ruby/resque/jam_tracks_builder_spec.rb b/ruby/spec/jam_ruby/resque/jam_tracks_builder_spec.rb index 8cceafcc7..b737f574e 100644 --- a/ruby/spec/jam_ruby/resque/jam_tracks_builder_spec.rb +++ b/ruby/spec/jam_ruby/resque/jam_tracks_builder_spec.rb @@ -27,8 +27,8 @@ describe JamTracksBuilder do jam_track_track = FactoryGirl.create(:jam_track_track) jam_track = jam_track_track.jam_track - #uploader = JamTrackTrackUploader.new(jam_track_track, :url_48) - jam_track_track.url_48.store!(File.open(ogg_path, 'rb')) + @s3.upload(jam_track_track.manually_uploaded_filename(:url_48), ogg_path) + jam_track_track[:url_48] = jam_track_track.manually_uploaded_filename(:url_48) jam_track_track.save! jam_track_track[:url_48].should == jam_track_track.manually_uploaded_filename(:url_48) @@ -36,7 +36,7 @@ describe JamTracksBuilder do # verify it's on S3 @s3.exists?(jam_track_track[:url_48]).should be_true @s3.length(jam_track_track[:url_48]).should == File.size?(ogg_path) - @s3.exists?(jam_track_track[:url_44]).should be_false + jam_track_track[:url_44].should be_nil # Check right jam_track_right = JamTrackRight.create(:user=>user, :jam_track=>jam_track) @@ -45,7 +45,7 @@ describe JamTracksBuilder do JamTracksBuilder.perform(jam_track_right.id, 48) jam_track_right.reload jam_track_right[:url_48].should == jam_track_right.store_dir + '/' + jam_track_right.filename - @s3.exists?(jam_track_track[:url_44]).should be_false + jam_track_track[:url_44].should be_nil end describe "with bitrate 44" do @@ -59,7 +59,8 @@ describe JamTracksBuilder do # uploader = JamTrackTrackUploader.new(jam_track_track, :url_44) # uploader.store!(File.open(ogg_path, 'rb')) - jam_track_track.url_44.store!(File.open(ogg_path, 'rb')) + @s3.upload(jam_track_track.manually_uploaded_filename(:url_44), ogg_path) + jam_track_track[:url_44] = jam_track_track.manually_uploaded_filename(:url_44) jam_track_track.save! jam_track_track[:url_44].should == jam_track_track.manually_uploaded_filename(:url_44) @@ -67,7 +68,7 @@ describe JamTracksBuilder do # verify it's on S3 @s3.exists?(jam_track_track[:url_44]).should be_true @s3.length(jam_track_track[:url_44]).should == File.size?(ogg_path) - @s3.exists?(jam_track_track[:url_48]).should be_false + jam_track_track[:url_48].should be_nil # Check right jam_track_right = JamTrackRight.create(:user=>user, :jam_track=>jam_track) @@ -77,7 +78,7 @@ describe JamTracksBuilder do jam_track_right.reload jam_track_right[:url_44].should == jam_track_right.store_dir + '/' + jam_track_right.filename jam_track_right.url_44.should_not be_nil - @s3.exists?(jam_track_track[:url_48]).should be_false + jam_track_track[:url_48].should be_nil end end end \ No newline at end of file diff --git a/web/README.md b/web/README.md index f2dccf0a1..4548ce76b 100644 --- a/web/README.md +++ b/web/README.md @@ -1,3 +1,4 @@ + Jasmine Javascript Unit Tests ============================= diff --git a/web/app/assets/images/content/bandcamp-logo.png b/web/app/assets/images/content/bandcamp-logo.png new file mode 100644 index 000000000..03eec06be Binary files /dev/null and b/web/app/assets/images/content/bandcamp-logo.png differ diff --git a/web/app/assets/images/content/facebook-logo.png b/web/app/assets/images/content/facebook-logo.png new file mode 100644 index 000000000..bb99485bc Binary files /dev/null and b/web/app/assets/images/content/facebook-logo.png differ diff --git a/web/app/assets/images/content/fandalism-logo.png b/web/app/assets/images/content/fandalism-logo.png new file mode 100644 index 000000000..9cf95bee1 Binary files /dev/null and b/web/app/assets/images/content/fandalism-logo.png differ diff --git a/web/app/assets/images/content/reverbnation-logo.png b/web/app/assets/images/content/reverbnation-logo.png new file mode 100644 index 000000000..152d59982 Binary files /dev/null and b/web/app/assets/images/content/reverbnation-logo.png differ diff --git a/web/app/assets/images/content/soundcloud-logo.png b/web/app/assets/images/content/soundcloud-logo.png new file mode 100644 index 000000000..2dee5398a Binary files /dev/null and b/web/app/assets/images/content/soundcloud-logo.png differ diff --git a/web/app/assets/images/content/twitter-logo.png b/web/app/assets/images/content/twitter-logo.png new file mode 100644 index 000000000..c2c63ca0c Binary files /dev/null and b/web/app/assets/images/content/twitter-logo.png differ diff --git a/web/app/assets/images/content/website-logo.png b/web/app/assets/images/content/website-logo.png new file mode 100644 index 000000000..fa0552330 Binary files /dev/null and b/web/app/assets/images/content/website-logo.png differ diff --git a/web/app/assets/images/content/youtube-logo.png b/web/app/assets/images/content/youtube-logo.png new file mode 100644 index 000000000..d5ca8f9c5 Binary files /dev/null and b/web/app/assets/images/content/youtube-logo.png differ diff --git a/web/app/assets/javascripts/accounts_profile.js b/web/app/assets/javascripts/accounts_profile.js index ca979abc5..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,14 +216,24 @@ /****************** 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; } ); } function renderAccountProfile() { - $.when(api.getUserDetail()) + $.when(api.getUserProfile()) .done(function(userDetail) { recentUserDetail = userDetail; populateAccountProfile(userDetail); @@ -234,7 +247,7 @@ function navToAccount() { resetForm(); - window.location = '/client#/account'; + window.location = '/client#/profile/' + context.JK.currentUserId; } function navToAvatar() { diff --git a/web/app/assets/javascripts/accounts_profile_experience.js b/web/app/assets/javascripts/accounts_profile_experience.js index aa7749d76..4e13c3d79 100644 --- a/web/app/assets/javascripts/accounts_profile_experience.js +++ b/web/app/assets/javascripts/accounts_profile_experience.js @@ -8,15 +8,16 @@ var logger = context.JK.logger; var EVENTS = context.JK.EVENTS; var api = context.JK.Rest(); - var userId; - var user = {}; 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) { - userId = data.id; } function afterShow(data) { @@ -25,40 +26,36 @@ } 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(userDetail.genres); + 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\"" :"", 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 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); }); } - context.JK.dropdown($('select', $scroller)); + $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', $screen)); } function isUserInstrument(instrument, userInstruments) { @@ -83,7 +80,7 @@ var selected = ''; if (selectedGenres) { var genreMatch = $.grep(selectedGenres, function (n, i) { - return n.id === genre.id; + return n.genre_id === genre.id; }); if (genreMatch.length > 0) { selected = "checked"; @@ -116,13 +113,27 @@ } function events() { - $screen.find('#account-edit-profile-cancel').on('click', function(evt) { evt.stopPropagation(); navigateTo('/client#/account'); 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() { - $.when(api.getUserDetail(), api.getInstruments()) + $.when(api.getUserProfile(), api.getInstruments()) .done(function(userDetailResponse, instrumentsResponse) { var userDetail = userDetailResponse[0]; populateAccountProfile(userDetail, instrumentsResponse[0]); @@ -133,22 +144,21 @@ function navigateTo(targetLocation) { resetForm(); - window.location = targetLocation; + context.location = targetLocation; } 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=gender]').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); @@ -167,7 +177,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 { @@ -175,7 +185,7 @@ } } - function getInstrumentsValue() { + function getSelectedInstruments() { var instruments = []; $('input[type=checkbox]:checked', $instrumentSelector).each(function(i) { @@ -198,7 +208,10 @@ 'beforeShow': beforeShow, 'afterShow': afterShow }; + app.bindScreen('account/profile/experience', screenBindings); + + events(); } this.initialize = initialize; diff --git a/web/app/assets/javascripts/accounts_profile_interests.js b/web/app/assets/javascripts/accounts_profile_interests.js index e69de29bb..6d8ccd40b 100644 --- a/web/app/assets/javascripts/accounts_profile_interests.js +++ b/web/app/assets/javascripts/accounts_profile_interests.js @@ -0,0 +1,283 @@ +(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 ui = new context.JK.UIHelper(JK.app); + var user = {}; + var profileUtils = context.JK.ProfileUtils; + var masterGenreList = []; + + var NONE_SPECIFIED = 'None specified'; + var GENRE_LIST_DELIMITER = ', '; + + var $screen = $('#account-profile-interests'); + + var SELECT_GENRE_SELECTOR = '.select-genre'; + var GENRE_LIST_SELECTOR = '.genre-list'; + + // virtual bands + var $virtualBandYes = $screen.find('#virtual-band-yes'); + var $virtualBandNo = $screen.find('#virtual-band-no'); + var $virtualBandGenres = $screen.find('#virtual-band-genres'); + var $btnVirtualBandGenreSelect = $virtualBandGenres.find(SELECT_GENRE_SELECTOR); + var $virtualBandGenreList = $virtualBandGenres.find(GENRE_LIST_SELECTOR); + var $virtualBandCommitment = $screen.find('#virtual-band-commitment'); + + // traditional bands + var $traditionalBandYes = $screen.find('#traditional-band-yes'); + var $traditionalBandNo = $screen.find('#traditional-band-no'); + var $traditionalBandGenres = $screen.find('#traditional-band-genres'); + var $btnTraditionalBandGenreSelect = $traditionalBandGenres.find(SELECT_GENRE_SELECTOR); + var $traditionalBandGenreList = $traditionalBandGenres.find(GENRE_LIST_SELECTOR); + var $traditionalBandCommitment = $screen.find('#traditional-band-commitment'); + var $traditionalTouringOption = $screen.find('#traditional-band-touring'); + + // paid sessions + var $paidSessionsYes = $screen.find('#paid-sessions-yes'); + var $paidSessionsNo = $screen.find('#paid-sessions-no'); + var $paidSessionsGenres = $screen.find('#paid-sessions-genres'); + var $btnPaidSessionsGenreSelect = $paidSessionsGenres.find(SELECT_GENRE_SELECTOR); + var $paidSessionsGenreList = $paidSessionsGenres.find(GENRE_LIST_SELECTOR); + var $hourlyRate = $screen.find('#hourly-rate'); + var $dailyRate = $screen.find('#daily-rate'); + + // free sessions + var $freeSessionsYes = $screen.find('#free-sessions-yes'); + var $freeSessionsNo = $screen.find('#free-sessions-no'); + var $freeSessionsGenres = $screen.find('#free-sessions-genres'); + var $btnFreeSessionsGenreSelect = $freeSessionsGenres.find(SELECT_GENRE_SELECTOR); + var $freeSessionsGenreList = $freeSessionsGenres.find(GENRE_LIST_SELECTOR); + + // cowriting + var $cowritingYes = $screen.find('#cowriting-yes'); + var $cowritingNo = $screen.find('#cowriting-no'); + var $cowritingGenres = $screen.find('#cowriting-genres'); + var $btnCowritingGenreSelect = $cowritingGenres.find(SELECT_GENRE_SELECTOR); + var $cowritingGenreList = $cowritingGenres.find(GENRE_LIST_SELECTOR); + var $cowritingPurpose = $screen.find('#cowriting-purpose'); + + 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) { + } + + function afterShow(data) { + renderInterests(); + } + + function resetForm() { + $screen.find('form .error-text').remove(); + $screen.find('form .error').removeClass("error"); + } + + function populateAccountProfile(userDetail) { + + // Column 1 - options + if (userDetail) { + + if (userDetail.virtual_band) { + $virtualBandYes.iCheck('check').attr('checked', 'checked'); + } + else { + $virtualBandNo.iCheck('check').attr('checked', 'checked'); + } + + if (userDetail.traditional_band) { + $traditionalBandYes.iCheck('check').attr('checked', 'checked'); + } + else { + $traditionalBandNo.iCheck('check').attr('checked', 'checked'); + } + + if (userDetail.paid_sessions) { + $paidSessionsYes.iCheck('check').attr('checked', 'checked'); + } + else { + $paidSessionsNo.iCheck('check').attr('checked', 'checked'); + } + + if (userDetail.free_sessions) { + $freeSessionsYes.iCheck('check').attr('checked', 'checked'); + } + else { + $freeSessionsNo.iCheck('check').attr('checked', 'checked'); + } + + if (userDetail.cowriting) { + $cowritingYes.iCheck('check').attr('checked', 'checked'); + } + else { + $cowritingNo.iCheck('check').attr('checked', 'checked'); + } + } + + // Column 2 - genres + var genres = profileUtils.virtualBandGenreList(userDetail.genres); + $virtualBandGenreList.html(genres && genres.length > 0 ? genres : NONE_SPECIFIED); + + genres = profileUtils.traditionalBandGenreList(userDetail.genres); + $traditionalBandGenreList.html(genres && genres.length > 0 ? genres : NONE_SPECIFIED); + + genres = profileUtils.paidSessionGenreList(userDetail.genres); + $paidSessionsGenreList.html(genres && genres.length > 0 ? genres : NONE_SPECIFIED); + + genres = profileUtils.freeSessionGenreList(userDetail.genres); + $freeSessionsGenreList.html(genres && genres.length > 0 ? genres : NONE_SPECIFIED); + + genres = profileUtils.cowritingGenreList(userDetail.genres); + $cowritingGenreList.html(genres && genres.length > 0 ? genres : NONE_SPECIFIED); + + // Column 3 - misc (play commitment, rates, cowriting purpose) + $virtualBandCommitment.val(userDetail.virtual_band_commitment); + context.JK.dropdown($virtualBandCommitment); + + $traditionalBandCommitment.val(userDetail.traditional_band_commitment); + context.JK.dropdown($traditionalBandCommitment); + + $traditionalTouringOption.val(userDetail.traditional_band_touring ? '1' : '0'); + context.JK.dropdown($traditionalTouringOption); + + $hourlyRate.val(userDetail.paid_sessions_hourly_rate); + $dailyRate.val(userDetail.paid_sessions_daily_rate); + + $cowritingPurpose.val(userDetail.cowriting_purpose); + context.JK.dropdown($cowritingPurpose); + } + + function bindGenreSelector(type, $btnSelect, $genreList) { + $btnSelect.unbind('click').click(function(evt) { + evt.preventDefault(); + var genreText = $genreList.html(); + var genres = []; + if (genres !== NONE_SPECIFIED) { + genres = genreText.split(GENRE_LIST_DELIMITER); + } + + ui.launchGenreSelectorDialog(type, genres, function(selectedGenres) { + $genreList.html(selectedGenres && selectedGenres.length > 0 ? selectedGenres.join(GENRE_LIST_DELIMITER) : NONE_SPECIFIED); + }); + + return false; + }); + } + + function events() { + + bindGenreSelector('virtual bands', $btnVirtualBandGenreSelect, $virtualBandGenreList); + bindGenreSelector('traditional bands', $btnTraditionalBandGenreSelect, $traditionalBandGenreList); + bindGenreSelector('paid sessions', $btnPaidSessionsGenreSelect, $paidSessionsGenreList); + bindGenreSelector('free sessions', $btnFreeSessionsGenreSelect, $freeSessionsGenreList); + bindGenreSelector('co-writing', $btnCowritingGenreSelect, $cowritingGenreList); + + $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); + context.JK.dropdown($cowritingPurpose); + } + + function renderInterests() { + $.when(api.getUserProfile()) + .done(function(userDetail) { + populateAccountProfile(userDetail); + }); + } + + function navigateTo(targetLocation) { + context.location = targetLocation; + } + + function handleUpdateProfile() { + resetForm(); + + api.updateUser({ + virtual_band: $screen.find('input[name=virtual_band]:checked').val(), + virtual_band_genres: $virtualBandGenreList.html() === NONE_SPECIFIED ? [] : $virtualBandGenreList.html().split(GENRE_LIST_DELIMITER), + virtual_band_commitment: $virtualBandCommitment.val(), + + traditional_band: $screen.find('input[name=traditional_band]:checked').val(), + traditional_band_genres: $traditionalBandGenreList.html() === NONE_SPECIFIED ? [] : $traditionalBandGenreList.html().split(GENRE_LIST_DELIMITER), + traditional_band_commitment: $traditionalBandCommitment.val(), + traditional_band_touring: $traditionalTouringOption.val(), + + paid_sessions: $screen.find('input[name=paid_sessions]:checked').val(), + paid_session_genres: $paidSessionsGenreList.html() === NONE_SPECIFIED ? [] : $paidSessionsGenreList.html().split(GENRE_LIST_DELIMITER), + paid_sessions_hourly_rate: $hourlyRate.val(), + paid_sessions_daily_rate: $dailyRate.val(), + + free_sessions: $screen.find('input[name=free_sessions]:checked').val(), + free_session_genre: $freeSessionsGenreList.html() === NONE_SPECIFIED ? [] : $freeSessionsGenreList.html().split(GENRE_LIST_DELIMITER), + + cowriting: $screen.find('input[name=cowriting]:checked').val(), + cowriting_genres: $cowritingGenreList.html() === NONE_SPECIFIED ? [] : $cowritingGenreList.html().split(GENRE_LIST_DELIMITER), + cowriting_purpose: $cowritingPurpose.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.find('.interest-options').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/javascripts/accounts_profile_samples.js b/web/app/assets/javascripts/accounts_profile_samples.js index e69de29bb..59d7e3e05 100644 --- a/web/app/assets/javascripts/accounts_profile_samples.js +++ b/web/app/assets/javascripts/accounts_profile_samples.js @@ -0,0 +1,300 @@ +(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'); + + // 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'); + var $fandalismUsername = $screen.find('#fandalism-username'); + var $youTubeUsername = $screen.find('#youtube-username'); + 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'); + + 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) { + } + + function afterShow(data) { + $.when(api.getUserProfile()) + .done(function(userDetail) { + renderPresence(userDetail); + renderSamples(userDetail); + }); + } + + function renderPresence(user) { + $website.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() { + + $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) { + + }); + + return false; + }); + + $btnAddSoundCloudRecording.click(function(evt) { + 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 + } + } + }); + + $btnCancel.click(function(evt) { + evt.stopPropagation(); + navigateTo('/client#/profile/' + context.JK.currentUserId); + return false; + }); + + $btnBack.click(function(evt) { + evt.stopPropagation(); + navigateTo('/client#/account/profile/interests'); + return false; + }); + + $btnSubmit.click(function(evt) { + $document.triggerHandler(EVENTS.USER_UPDATED, response); + + handleUpdateProfile(); + return false; + }); + } + + function navigateTo(targetLocation) { + context.location = targetLocation; + } + + 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, + '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/genreSelector.js b/web/app/assets/javascripts/dialog/genreSelector.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/web/app/assets/javascripts/dialog/genreSelectorDialog.js b/web/app/assets/javascripts/dialog/genreSelectorDialog.js new file mode 100644 index 000000000..f72634d0c --- /dev/null +++ b/web/app/assets/javascripts/dialog/genreSelectorDialog.js @@ -0,0 +1,96 @@ +(function(context,$) { + + "use strict"; + context.JK = context.JK || {}; + context.JK.GenreSelectorDialog = function(app, type, genres, callback) { + var logger = context.JK.logger; + var rest = context.JK.Rest(); + var $dialog = null; + var dialogId = 'genre-selector-dialog'; + var $screen = $('#' + dialogId); + var $btnSelect = $screen.find(".btn-select-genres"); + var $instructions = $screen.find('#instructions'); + var $genres = $screen.find('.genres'); + var GENRES_PER_COLUMN = 12; + + function beforeShow(data) { + } + + function afterShow(data) { + var genreList = context.JK.genres; + + $genres.empty(); + + if (genreList) { + var columns = genreList.length / GENRES_PER_COLUMN; + columns = Math.floor((genreList.length % GENRES_PER_COLUMN) === 0 ? columns : columns + 1); + + var columnWidthPct = 100/columns; + + $.each(genreList, function(index, val) { + if (index === 0 || index % GENRES_PER_COLUMN === 0) { + $genres.append('
'); + } + + $genres.append(''); + var checked = ''; + if (genres && $.inArray(val.id, genres) > -1) { + checked = 'checked'; + } + + $genres.append(''); + $genres.append(''); + + if (index === genreList.length-1 || (index+1) % GENRES_PER_COLUMN === 0) { + $genres.append('
' + val.description + '
') + } + }); + } + } + + function afterHide() { + } + + function showDialog() { + return app.layout.showDialog(dialogId); + } + + function events() { + $btnSelect.click(function(evt) { + evt.preventDefault(); + var selectedGenres = []; + $genres.find('input[type=checkbox]:checked').each(function(index) { + selectedGenres.push($(this).val()); + }); + + if (callback) { + callback(selectedGenres); + } + + 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 genres for ' + type + ':'); + + events(); + } + + this.initialize = initialize; + this.showDialog = showDialog; + } + + return this; +})(window,jQuery); \ No newline at end of file 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/dialog/recordingSelector.js b/web/app/assets/javascripts/dialog/recordingSelector.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/web/app/assets/javascripts/dialog/recordingSelectorDialog.js b/web/app/assets/javascripts/dialog/recordingSelectorDialog.js new file mode 100644 index 000000000..cc485aa64 --- /dev/null +++ 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/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/assets/javascripts/jquery.metronomePlaybackMode.js b/web/app/assets/javascripts/jquery.metronomePlaybackMode.js index 4bc53bbc6..720cea539 100644 --- a/web/app/assets/javascripts/jquery.metronomePlaybackMode.js +++ b/web/app/assets/javascripts/jquery.metronomePlaybackMode.js @@ -42,9 +42,16 @@ var $li = $(this); var playbackMode = $li.attr('data-playback-option'); - value = playbackMode; - close(); - $parent.triggerHandler(context.JK.EVENTS.METRONOME_PLAYBACK_MODE_SELECTED, {playbackMode: playbackMode}); + if(playbackMode) { + value = playbackMode; + close(); + $parent.triggerHandler(context.JK.EVENTS.METRONOME_PLAYBACK_MODE_SELECTED, {playbackMode: playbackMode}); + } + else { + // if no playback mode, then this must be an attempt to open metronome window + close(); + context.jamClient.SessionShowMetronomeGui(); + } return false; }; @@ -69,7 +76,7 @@ cssClass: 'metronome-playback-mode-selector-popup', spikeGirth:0, spikeLength:0, - width:160, + width:180, closeWhenOthersOpen: true, offsetParent: $parent.offsetParent(), positions:['top'], 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/javascripts/networkTestHelper.js b/web/app/assets/javascripts/networkTestHelper.js index c295f1123..61273a2db 100644 --- a/web/app/assets/javascripts/networkTestHelper.js +++ b/web/app/assets/javascripts/networkTestHelper.js @@ -26,6 +26,7 @@ var TEST_TIMEOUT_CALLBACK = 'JK.HandleNetworkTestTimeout'; var $startNetworkTestBtn = null; + var $foreverNetworkTestBtn = null; var $testResults = null; var $testScore = null; var $testText = null; @@ -51,6 +52,8 @@ var operatingSystem = null; var PRIME_PUMP_TIME = 1; + var forever = false; + // these try to make it such that we only pass a NetworkTest Pass/Failed one time in a new user flow var trackedPass = false; var lastNetworkFailure = null; @@ -313,6 +316,9 @@ if (success) { $self.triggerHandler(NETWORK_TEST_DONE) + if(forever) { + prepareNetworkTest(); + } } else { $self.triggerHandler(NETWORK_TEST_FAIL) @@ -875,6 +881,7 @@ inGearWizard = _inGearWizard; $startNetworkTestBtn = $step.find('.start-network-test'); + $foreverNetworkTestBtn = $step.find('.forever-network-test') if ($startNetworkTestBtn.length == 0) throw 'no start network test button found in network-test' @@ -889,8 +896,16 @@ $subscore = $step.find('.subscore'); $watchVideo = $step.find('.watch-video'); $startNetworkTestBtn.on('click', function () { + forever = false; prepareNetworkTest(); }); + if(context.JK.currentUserAdmin) { + $foreverNetworkTestBtn.on('click', function() { + forever = true; + prepareNetworkTest(); + }).show(); + } + operatingSystem = context.JK.GetOSAsString(); initializeVideoWatchButton(); diff --git a/web/app/assets/javascripts/profile.js b/web/app/assets/javascripts/profile.js index ac68b4622..916c6a8b4 100644 --- a/web/app/assets/javascripts/profile.js +++ b/web/app/assets/javascripts/profile.js @@ -680,13 +680,13 @@ $paidGigSection.show(); var genreList = profileUtils.paidSessionGenreList(user.genres); - $paidGigDetails.find("ul li:nth-child(1)").append(genreList.length > 0 ? genreList : NOT_SPECIFIED_TEXT); + $paidGigDetails.find("ul li:nth-child(1)").html('Genre(s): ' + (genreList.length > 0 ? genreList : NOT_SPECIFIED_TEXT)); var hourlyRate = user.paid_sessions_hourly_rate; - $paidGigDetails.find("ul li:nth-child(2)").append(hourlyRate ? hourlyRate : NOT_SPECIFIED_TEXT); + $paidGigDetails.find("ul li:nth-child(2)").html('Hourly rate = ' + (hourlyRate ? hourlyRate : NOT_SPECIFIED_TEXT)); var dailyRate = user.paid_sessions_daily_rate; - $paidGigDetails.find("ul li:nth-child(3)").append(dailyRate ? dailyRate : NOT_SPECIFIED_TEXT); + $paidGigDetails.find("ul li:nth-child(3)").html('Day rate = ' + (dailyRate ? dailyRate : NOT_SPECIFIED_TEXT)); } else { $paidGigSection.hide(); @@ -697,7 +697,7 @@ $freeGigSection.show(); var genreList = profileUtils.freeSessionGenreList(user.genres); - $freeGigDetails.find("ul li:nth-child(1)").append(genreList.length > 0 ? genreList : NOT_SPECIFIED_TEXT); + $freeGigDetails.find("ul li:nth-child(1)").html('Genre(s): ' + (genreList.length > 0 ? genreList : NOT_SPECIFIED_TEXT)); } else { $freeGigSection.hide(); @@ -708,10 +708,10 @@ $cowritingSection.show(); var genreList = profileUtils.cowritingGenreList(user.genres); - $cowritingDetails.find("ul li:nth-child(1)").append(genreList.length > 0 ? genreList : NOT_SPECIFIED_TEXT); + $cowritingDetails.find("ul li:nth-child(1)").html('Genre(s): ' + (genreList.length > 0 ? genreList : NOT_SPECIFIED_TEXT)); var purpose = user.cowriting_purpose; - $cowritingDetails.find("ul li:nth-child(2)").append(purpose ? profileUtils.cowritingPurposeMap[purpose] : NOT_SPECIFIED_TEXT); + $cowritingDetails.find("ul li:nth-child(2)").html('Purpose: ' + (purpose ? profileUtils.cowritingPurposeMap[purpose] : NOT_SPECIFIED_TEXT)); } else { $cowritingSection.hide(); @@ -722,14 +722,14 @@ $traditionalBandSection.show(); var genreList = profileUtils.traditionalBandGenreList(user.genres); - $traditionalBandDetails.find("ul li:nth-child(1)").append(genreList.length > 0 ? genreList : NOT_SPECIFIED_TEXT); + $traditionalBandDetails.find("ul li:nth-child(1)").html('Genre(s): ' + (genreList.length > 0 ? genreList : NOT_SPECIFIED_TEXT)); var commitment = user.traditional_band_commitment; - $traditionalBandDetails.find("ul li:nth-child(2)").append(commitment ? profileUtils.bandCommitmentMap[commitment] : NOT_SPECIFIED_TEXT); + $traditionalBandDetails.find("ul li:nth-child(2)").html('Commitment: ' + (commitment ? profileUtils.bandCommitmentMap[commitment] : NOT_SPECIFIED_TEXT)); var canTour = user.traditional_band_touring; var canTourResponse = canTour ? "Yes" : (canTour === false ? "No" : NOT_SPECIFIED_TEXT); - $traditionalBandDetails.find("ul li:nth-child(3)").append(canTourResponse); + $traditionalBandDetails.find("ul li:nth-child(3)").html('Touring: ' + canTourResponse); } else { $traditionalBandSection.hide(); @@ -740,10 +740,10 @@ $virtualBandSection.show(); var genreList = profileUtils.virtualBandGenreList(user.genres); - $virtualBandDetails.find("ul li:nth-child(1)").append(genreList.length > 0 ? genreList : NOT_SPECIFIED_TEXT); + $virtualBandDetails.find("ul li:nth-child(1)").html('Genre(s): ' + (genreList.length > 0 ? genreList : NOT_SPECIFIED_TEXT)); var commitment = user.virtual_band_commitment; - $virtualBandDetails.find("ul li:nth-child(2)").append(commitment ? profileUtils.bandCommitmentMap[commitment] : NOT_SPECIFIED_TEXT); + $virtualBandDetails.find("ul li:nth-child(2)").html('Commitment: ' + (commitment ? profileUtils.bandCommitmentMap[commitment] : NOT_SPECIFIED_TEXT)); } else { $virtualBandSection.hide(); diff --git a/web/app/assets/javascripts/profile_utils.js b/web/app/assets/javascripts/profile_utils.js index cdaf0d6de..d0935c698 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_sessions'; 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"}, @@ -79,7 +80,7 @@ } } - return list.length > 0; + return list; } // profile genres @@ -98,9 +99,12 @@ // virtual band genres profileUtils.virtualBandGenres = function(genres) { - var matches = $.grep(genres, function(g) { - return g.player_type === USER_TYPE && g.genre_type === VIRTUAL_BAND_GENRE_TYPE; - }); + var matches = []; + if (genres) { + matches = $.grep(genres, function(g) { + return g.player_type === USER_TYPE && g.genre_type === VIRTUAL_BAND_GENRE_TYPE; + }); + } return matches; } @@ -112,9 +116,12 @@ // traditional band genres profileUtils.traditionalBandGenres = function(genres) { - var matches = $.grep(genres, function(g) { - return g.player_type === USER_TYPE && g.genre_type === TRADITIONAL_BAND_GENRE_TYPE; - }); + var matches = []; + if (genres) { + matches = $.grep(genres, function(g) { + return g.player_type === USER_TYPE && g.genre_type === TRADITIONAL_BAND_GENRE_TYPE; + }); + } return matches; } @@ -126,9 +133,12 @@ // paid session genres profileUtils.paidSessionGenres = function(genres) { - var matches = $.grep(genres, function(g) { - return g.player_type === USER_TYPE && g.genre_type === PAID_SESSION_GENRE_TYPE; - }); + var matches = []; + if (genres) { + matches = $.grep(genres, function(g) { + return g.player_type === USER_TYPE && g.genre_type === PAID_SESSION_GENRE_TYPE; + }); + } return matches; } @@ -140,9 +150,12 @@ // free session genres profileUtils.freeSessionGenres = function(genres) { - var matches = $.grep(genres, function(g) { - return g.player_type === USER_TYPE && g.genre_type === FREE_SESSION_GENRE_TYPE; - }); + var matches = []; + if (genres) { + matches = $.grep(genres, function(g) { + return g.player_type === USER_TYPE && g.genre_type === FREE_SESSION_GENRE_TYPE; + }); + } return matches; } @@ -154,9 +167,12 @@ // cowriting genres profileUtils.cowritingGenres = function(genres) { - var matches = $.grep(genres, function(g) { - return g.player_type === USER_TYPE && g.genre_type === COWRITING_GENRE_TYPE; - }); + var matches = []; + if (genres) { + matches = $.grep(genres, function(g) { + return g.player_type === USER_TYPE && g.genre_type === COWRITING_GENRE_TYPE; + }); + } return matches; } diff --git a/web/app/assets/javascripts/session.js b/web/app/assets/javascripts/session.js index f6cda4a51..b62d2737d 100644 --- a/web/app/assets/javascripts/session.js +++ b/web/app/assets/javascripts/session.js @@ -114,8 +114,9 @@ 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]; + var muteBothMasterAndPersonalGroups = [ChannelGroupIds.AudioInputMusicGroup, ChannelGroupIds.MediaTrackGroup, ChannelGroupIds.JamTrackGroup, ChannelGroupIds.MetronomeGroup]; var rest = context.JK.Rest(); var RENDER_SESSION_DELAY = 750; // When I need to render a session, I have to wait a bit for the mixers to be there. @@ -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(); /** @@ -1209,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) { @@ -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..c09f4e878 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,19 @@ } } + // 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) { + 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 +371,7 @@ } currentSessionId = null; currentParticipants = {} - previousBackingTracks = [] + previousAllTracks = {userTracks: [], backingTracks: [], metronomeTracks: []} openBackingTrack = null shownAudioMediaMixerHelp = false controlsLockedForJamTrackRecording = false; @@ -603,26 +616,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 +807,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 +864,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/javascripts/ui_helper.js b/web/app/assets/javascripts/ui_helper.js index 01f204bfb..01d2aa403 100644 --- a/web/app/assets/javascripts/ui_helper.js +++ b/web/app/assets/javascripts/ui_helper.js @@ -62,6 +62,18 @@ }); } + function launchGenreSelectorDialog(type, genres, callback) { + var genreSelectorDialog = new JK.GenreSelectorDialog(JK.app, type, genres, callback); + genreSelectorDialog.initialize(); + return genreSelectorDialog.showDialog(); + } + + function launchRecordingSelectorDialog(recordings, selectedRecordings, callback) { + var recordingSelectorDialog = new JK.RecordingSelectorDialog(JK.app, recordings, selectedRecordings, callback); + recordingSelectorDialog.initialize(); + return recordingSelectorDialog.showDialog(); + } + this.addSessionLike = addSessionLike; this.addRecordingLike = addRecordingLike; this.launchCommentDialog = launchCommentDialog; @@ -70,6 +82,8 @@ this.launchRsvpCancelDialog = launchRsvpCancelDialog; this.launchRsvpCreateSlotDialog = launchRsvpCreateSlotDialog; this.launchSessionStartDialog = launchSessionStartDialog; + this.launchGenreSelectorDialog = launchGenreSelectorDialog; + this.launchRecordingSelectorDialog = launchRecordingSelectorDialog; return this; }; diff --git a/web/app/assets/javascripts/utils.js b/web/app/assets/javascripts/utils.js index fef57ccf5..aee15f5c6 100644 --- a/web/app/assets/javascripts/utils.js +++ b/web/app/assets/javascripts/utils.js @@ -7,6 +7,7 @@ context.JK = context.JK || {}; var logger = context.JK.logger; + var api = context.JK.Rest(); var AUDIO_DEVICE_BEHAVIOR = context.JK.AUDIO_DEVICE_BEHAVIOR; var ALERT_NAMES = context.JK.ALERT_NAMES; @@ -22,6 +23,10 @@ var os = null; + context.JK.getGenreList = function() { + return api.getGenres(); + } + context.JK.stringToBool = function (s) { switch (s.toLowerCase()) { case "true": diff --git a/web/app/assets/stylesheets/client/account.css.scss b/web/app/assets/stylesheets/client/account.css.scss index 69b8ef318..782f43065 100644 --- a/web/app/assets/stylesheets/client/account.css.scss +++ b/web/app/assets/stylesheets/client/account.css.scss @@ -28,7 +28,7 @@ } form { - padding-right:10%; + //padding-right:10%; } label { diff --git a/web/app/assets/stylesheets/client/accountProfileExperience.css.scss b/web/app/assets/stylesheets/client/accountProfileExperience.css.scss index 1f21ad04a..a5d9171c9 100644 --- a/web/app/assets/stylesheets/client/accountProfileExperience.css.scss +++ b/web/app/assets/stylesheets/client/accountProfileExperience.css.scss @@ -1,4 +1,4 @@ -@import "client/common.css.scss"; +@import "common.css.scss"; #account-profile-experience { diff --git a/web/app/assets/stylesheets/client/accountProfileInterests.css.scss b/web/app/assets/stylesheets/client/accountProfileInterests.css.scss index e69de29bb..7e11df987 100644 --- a/web/app/assets/stylesheets/client/accountProfileInterests.css.scss +++ b/web/app/assets/stylesheets/client/accountProfileInterests.css.scss @@ -0,0 +1,42 @@ +@import "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/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/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/assets/stylesheets/client/metronomePlaybackModeSelect.css.scss b/web/app/assets/stylesheets/client/metronomePlaybackModeSelect.css.scss index 776135e98..7700a57be 100644 --- a/web/app/assets/stylesheets/client/metronomePlaybackModeSelect.css.scss +++ b/web/app/assets/stylesheets/client/metronomePlaybackModeSelect.css.scss @@ -2,7 +2,7 @@ .metronome-playback-mode-selector-popup { .bt-content { - width:160px; + width:180px; background-color:#333; overflow:auto; border:1px solid #ED3618; diff --git a/web/app/assets/stylesheets/client/profile.css.scss b/web/app/assets/stylesheets/client/profile.css.scss index 94d057129..7a9ccbee5 100644 --- a/web/app/assets/stylesheets/client/profile.css.scss +++ b/web/app/assets/stylesheets/client/profile.css.scss @@ -9,6 +9,25 @@ padding:0; } } + + div.logo { + text-align: bottom; + } + + img.logo { + margin-right: 20px; + } + + ul { + margin:0px 0px 10px 0px; + padding:0px; + } + + li { + margin-left: 15px; + margin-bottom: 0px !important; + list-style: disc; + } } .profile-head { 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/assets/stylesheets/client/wizard/gearWizard.css.scss b/web/app/assets/stylesheets/client/wizard/gearWizard.css.scss index 29d498f69..6916f7c60 100644 --- a/web/app/assets/stylesheets/client/wizard/gearWizard.css.scss +++ b/web/app/assets/stylesheets/client/wizard/gearWizard.css.scss @@ -342,6 +342,11 @@ margin-top: 20px; } + a.forever-network-test { + margin-top:20px; + display:none; + } + .network-test-score { height: 24px; padding: 10px; diff --git a/web/app/assets/stylesheets/dialogs/genreSelector.css.scss b/web/app/assets/stylesheets/dialogs/genreSelector.css.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/web/app/assets/stylesheets/dialogs/genreSelectorDialog.css.scss b/web/app/assets/stylesheets/dialogs/genreSelectorDialog.css.scss new file mode 100644 index 000000000..c23ea586f --- /dev/null +++ b/web/app/assets/stylesheets/dialogs/genreSelectorDialog.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/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; + + } } diff --git a/web/app/assets/stylesheets/dialogs/recordingSelector.css.scss b/web/app/assets/stylesheets/dialogs/recordingSelector.css.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/web/app/assets/stylesheets/dialogs/recordingSelectorDialog.css.scss b/web/app/assets/stylesheets/dialogs/recordingSelectorDialog.css.scss new file mode 100644 index 000000000..c9994cff9 --- /dev/null +++ b/web/app/assets/stylesheets/dialogs/recordingSelectorDialog.css.scss @@ -0,0 +1,14 @@ +@import "client/common"; + +#recording-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/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/app/controllers/api_music_sessions_controller.rb b/web/app/controllers/api_music_sessions_controller.rb index e5f6a7bd4..50100a0a2 100644 --- a/web/app/controllers/api_music_sessions_controller.rb +++ b/web/app/controllers/api_music_sessions_controller.rb @@ -165,7 +165,7 @@ class ApiMusicSessionsController < ApiController def show unless @music_session.can_see? current_user - raise ActiveRecord::RecordNotFound + raise JamRuby::PermissionError end end @@ -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/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 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/api_users/profile_show.rabl b/web/app/views/api_users/profile_show.rabl index 0ad7585f1..56a822dfe 100644 --- a/web/app/views/api_users/profile_show.rabl +++ b/web/app/views/api_users/profile_show.rabl @@ -1,7 +1,7 @@ object @profile attributes :id, :first_name, :last_name, :name, :city, :state, :country, :location, :online, :photo_url, :musician, :gender, :birth_date, :internet_service_provider, :friend_count, :liker_count, :like_count, :follower_count, :following_count, :recording_count, :session_count, :biography, :favorite_count, :audio_latency, :upcoming_session_count, :age, :website, :skill_level, :concert_count, :studio_session_count, :virtual_band, :virtual_band_commitment, :traditional_band, :traditional_band_commitment, :traditional_band_touring, :paid_sessions, :paid_sessions_hourly_rate, -:paid_sessions_daily_rate, :free_sessions, :cowriting, :cowriting_purpose +:paid_sessions_daily_rate, :free_sessions, :cowriting, :cowriting_purpose, :subscribe_email child :online_presences => :online_presences do attributes :id, :service_type, :username @@ -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 @@ -30,8 +30,4 @@ end child :musician_instruments => :instruments do attributes :description, :proficiency_level, :priority, :instrument_id -end - -# child :genres do -# attributes :description, :id -# end \ No newline at end of file +end \ 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..5360c22b9 100644 --- a/web/app/views/clients/_account_profile_experience.html.erb +++ b/web/app/views/clients/_account_profile_experience.html.erb @@ -1,96 +1,83 @@ -
-
-
<%= 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") 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 diff --git a/web/config/routes.rb b/web/config/routes.rb index cc456384a..be93d6b68 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -207,7 +207,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/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) 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