diff --git a/admin/spec/factories.rb b/admin/spec/factories.rb
index d6888a6bc..ad1c0bf69 100644
--- a/admin/spec/factories.rb
+++ b/admin/spec/factories.rb
@@ -19,7 +19,7 @@ FactoryGirl.define do
end
before(:create) do |user|
- user.musician_instruments << FactoryGirl.build(:musician_instrument, user: user)
+ user.musician_instruments << FactoryGirl.build(:musician_instrument, player: user)
end
factory :single_user_session do
diff --git a/db/manifest b/db/manifest
index cf1750345..a3bddb189 100755
--- a/db/manifest
+++ b/db/manifest
@@ -276,6 +276,12 @@ jam_track_duration.sql
sales.sql
show_whats_next_count.sql
recurly_adjustments.sql
+alter_type_columns.sql
+user_presences_rename.sql
+add_genre_type.sql
+add_description_to_perf_samples.sql
+alter_genre_player_unique_constraint.sql
+musician_search.sql
signup_hints.sql
packaging_notices.sql
first_played_jamtrack_at.sql
@@ -286,5 +292,6 @@ signing.sql
optimized_redeemption.sql
optimized_redemption_warn_mode.sql
affiliate_partners2.sql
+enhance_band_profile.sql
broadcast_notifications.sql
-broadcast_notifications_fk.sql
\ No newline at end of file
+broadcast_notifications_fk.sql
diff --git a/db/up/add_description_to_perf_samples.sql b/db/up/add_description_to_perf_samples.sql
new file mode 100644
index 000000000..2c1ee462e
--- /dev/null
+++ b/db/up/add_description_to_perf_samples.sql
@@ -0,0 +1 @@
+alter table performance_samples add column description varchar(256) NULL;
\ No newline at end of file
diff --git a/db/up/add_genre_type.sql b/db/up/add_genre_type.sql
new file mode 100644
index 000000000..9c8312ada
--- /dev/null
+++ b/db/up/add_genre_type.sql
@@ -0,0 +1 @@
+alter table genre_players add column genre_type varchar(20) default 'profile';
\ No newline at end of file
diff --git a/db/up/alter_genre_player_unique_constraint.sql b/db/up/alter_genre_player_unique_constraint.sql
new file mode 100644
index 000000000..c0159b59b
--- /dev/null
+++ b/db/up/alter_genre_player_unique_constraint.sql
@@ -0,0 +1,2 @@
+ALTER TABLE genre_players DROP CONSTRAINT genre_player_uniqkey;
+ALTER TABLE genre_players ADD CONSTRAINT genre_player_uniqkey UNIQUE (player_id, player_type, genre_id, genre_type);
\ No newline at end of file
diff --git a/db/up/alter_type_columns.sql b/db/up/alter_type_columns.sql
new file mode 100644
index 000000000..690edec52
--- /dev/null
+++ b/db/up/alter_type_columns.sql
@@ -0,0 +1,2 @@
+ALTER TABLE user_presences RENAME COLUMN type to service_type;
+ALTER TABLE performance_samples RENAME COLUMN type to service_type;
\ No newline at end of file
diff --git a/db/up/enhance_band_profile.sql b/db/up/enhance_band_profile.sql
new file mode 100644
index 000000000..ed8fc29dd
--- /dev/null
+++ b/db/up/enhance_band_profile.sql
@@ -0,0 +1,25 @@
+ALTER TABLE bands ADD COLUMN band_type VARCHAR(16) DEFAULT '';
+ALTER TABLE bands ADD COLUMN band_status VARCHAR(16) DEFAULT '';
+ALTER TABLE bands ADD COLUMN concert_count SMALLINT DEFAULT 0;
+ALTER TABLE bands ADD COLUMN add_new_members BOOLEAN DEFAULT FALSE;
+ALTER TABLE bands ADD COLUMN play_commitment SMALLINT DEFAULT 0;
+ALTER TABLE bands ADD COLUMN touring_option BOOLEAN DEFAULT FALSE;
+ALTER TABLE bands ADD COLUMN paid_gigs BOOLEAN DEFAULT FALSE;
+ALTER TABLE bands ADD COLUMN free_gigs BOOLEAN DEFAULT FALSE;
+ALTER TABLE bands ADD COLUMN hourly_rate INT DEFAULT 0;
+ALTER TABLE bands ADD COLUMN gig_minimum INT DEFAULT 0;
+
+ALTER TABLE musicians_instruments ALTER COLUMN user_id DROP NOT NULL;
+ALTER TABLE musicians_instruments RENAME COLUMN user_id TO player_id;
+ALTER TABLE musicians_instruments ADD COLUMN player_type VARCHAR(32) DEFAULT 'user';
+ALTER TABLE musicians_instruments DROP CONSTRAINT musicians_instruments_user_id_fkey;
+
+ALTER TABLE performance_samples ALTER COLUMN user_id DROP NOT NULL;
+ALTER TABLE performance_samples RENAME COLUMN user_id TO player_id;
+ALTER TABLE performance_samples ADD COLUMN player_type VARCHAR(32) DEFAULT 'user';
+ALTER TABLE performance_samples DROP CONSTRAINT performance_samples_user_id_fkey;
+
+ALTER TABLE online_presences ALTER COLUMN user_id DROP NOT NULL;
+ALTER TABLE online_presences RENAME COLUMN user_id TO player_id;
+ALTER TABLE online_presences ADD COLUMN player_type VARCHAR(32) DEFAULT 'user';
+ALTER TABLE online_presences DROP CONSTRAINT online_presences_user_id_fkey;
\ No newline at end of file
diff --git a/db/up/musician_search.sql b/db/up/musician_search.sql
new file mode 100644
index 000000000..380ddb0a6
--- /dev/null
+++ b/db/up/musician_search.sql
@@ -0,0 +1,10 @@
+CREATE TABLE json_stores (
+ id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
+ user_id VARCHAR(64) REFERENCES users(id) ON DELETE CASCADE,
+ foreign_key1_id VARCHAR(64),
+ data_blob JSON NOT NULL DEFAULT '{}',
+ type VARCHAR(128)
+);
+
+CREATE INDEX user_idx ON json_stores(user_id);
+CREATE INDEX foreign_key1_idx ON json_stores(foreign_key1_id);
diff --git a/db/up/user_presences_rename.sql b/db/up/user_presences_rename.sql
new file mode 100644
index 000000000..92cb4e4ca
--- /dev/null
+++ b/db/up/user_presences_rename.sql
@@ -0,0 +1,10 @@
+drop table user_presences;
+
+CREATE TABLE online_presences (
+ id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
+ user_id VARCHAR(64) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
+ service_type VARCHAR(100) NOT NULL,
+ username VARCHAR(100) NOT NULL,
+ created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
\ No newline at end of file
diff --git a/ruby/Gemfile b/ruby/Gemfile
index 621108eea..d46e44e82 100644
--- a/ruby/Gemfile
+++ b/ruby/Gemfile
@@ -24,7 +24,7 @@ gem 'uuidtools', '2.1.2'
gem 'bcrypt-ruby', '3.0.1'
gem 'ruby-protocol-buffers', '1.2.2'
gem 'eventmachine', '1.0.3'
-gem 'amqp', '1.0.2'
+gem 'amqp', '1.0.2'
gem 'will_paginate'
gem 'actionmailer', '3.2.13'
gem 'sendgrid', '1.2.0'
@@ -62,6 +62,7 @@ group :test do
gem 'resque_spec' #, :path => "/home/jam/src/resque_spec/"
gem 'timecop'
gem 'rspec-prof'
+ gem 'time_difference'
gem 'byebug'
end
diff --git a/ruby/jt_metadata.json b/ruby/jt_metadata.json
new file mode 100644
index 000000000..cc85875b4
--- /dev/null
+++ b/ruby/jt_metadata.json
@@ -0,0 +1 @@
+{"container_file": "/var/folders/fk/0ckzmddd4tq28kxbb09vckbr0000gn/T/d20150519-97259-1h1tbhj/jam-track-35.jkz", "version": "0", "coverart": null, "rsa_priv_file": "/var/folders/fk/0ckzmddd4tq28kxbb09vckbr0000gn/T/d20150519-97259-1h1tbhj/skey.pem", "tracks": [{"name": "/var/folders/fk/0ckzmddd4tq28kxbb09vckbr0000gn/T/d20150519-97259-1h1tbhj/7452fa4a-0c55-4cb2-948e-221475d7299c.ogg", "trackName": "track_00"}], "rsa_pub_file": "/var/folders/fk/0ckzmddd4tq28kxbb09vckbr0000gn/T/d20150519-97259-1h1tbhj/pkey.pem", "jamktrack_info": "/var/folders/fk/0ckzmddd4tq28kxbb09vckbr0000gn/T/tmpGdncJS"}
\ No newline at end of file
diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb
index 8c1a51a58..f132f7b10 100755
--- a/ruby/lib/jam_ruby.rb
+++ b/ruby/lib/jam_ruby.rb
@@ -230,6 +230,10 @@ require "jam_ruby/models/broadcast_notification_view"
require "jam_ruby/jam_tracks_manager"
require "jam_ruby/jam_track_importer"
require "jam_ruby/jmep_manager"
+require "jam_ruby/models/performance_sample"
+require "jam_ruby/models/online_presence"
+require "jam_ruby/models/json_store"
+require "jam_ruby/models/musician_search"
include Jampb
diff --git a/ruby/lib/jam_ruby/constants/limits.rb b/ruby/lib/jam_ruby/constants/limits.rb
index bbbe44fd7..ab4403b76 100644
--- a/ruby/lib/jam_ruby/constants/limits.rb
+++ b/ruby/lib/jam_ruby/constants/limits.rb
@@ -14,7 +14,7 @@ module Limits
# instruments
MIN_INSTRUMENTS_PER_MUSICIAN = 1
- MAX_INSTRUMENTS_PER_MUSICIAN = 5
+ MAX_INSTRUMENTS_PER_MUSICIAN = 1000
# users
USERS_CAN_INVITE = true
diff --git a/ruby/lib/jam_ruby/models/band.rb b/ruby/lib/jam_ruby/models/band.rb
index f64bc47d2..c98f4a532 100644
--- a/ruby/lib/jam_ruby/models/band.rb
+++ b/ruby/lib/jam_ruby/models/band.rb
@@ -5,9 +5,11 @@ module JamRuby
attr_accessible :name, :website, :biography, :city, :state,
:country, :original_fpfile_photo, :cropped_fpfile_photo, :cropped_large_fpfile_photo,
- :cropped_s3_path_photo, :cropped_large_s3_path_photo, :crop_selection_photo, :photo_url, :large_photo_url
+ :cropped_s3_path_photo, :cropped_large_s3_path_photo, :crop_selection_photo, :photo_url, :large_photo_url,
+ :band_type, :band_status, :concert_count, :add_new_members, :play_commitment, :touring_option, :paid_gigs,
+ :free_gigs, :hourly_rate, :gig_minimum
- attr_accessor :updating_photo, :skip_location_validation
+ attr_accessor :updating_photo, :skip_location_validation, :skip_genre_validation
self.primary_key = 'id'
@@ -19,12 +21,20 @@ module JamRuby
validates :city, presence: true, :unless => :skip_location_validation
validate :validate_photo_info
- validate :require_at_least_one_genre
+ validate :require_at_least_one_genre, :unless => :skip_genre_validation
validate :limit_max_genres
before_save :check_lat_lng
before_save :check_website_url
+ # instruments
+ has_many :musician_instruments, :class_name => "JamRuby::MusicianInstrument", :foreign_key=> 'player_id'
+ has_many :instruments, :through => :musician_instruments, :class_name => "JamRuby::Instrument", :foreign_key=> 'player_id'
+
+ has_many :online_presences, :class_name => "JamRuby::OnlinePresence", :foreign_key=> 'player_id'
+ has_many :performance_samples, :class_name => "JamRuby::PerformanceSample", :foreign_key=> 'player_id'
+
+
# musicians
has_many :band_musicians, :class_name => "JamRuby::BandMusician", dependent: :destroy
has_many :users, :through => :band_musicians, :class_name => "JamRuby::User"
@@ -169,7 +179,7 @@ module JamRuby
band = id.blank? ? Band.new : Band.find(id)
# ensure user updating Band details is a Band member
- unless band.new_record? || band.users.exists?(user)
+ unless band.new_record? || band.users.exists?(user)
raise JamPermissionError, ValidationMessages::USER_NOT_BAND_MEMBER_VALIDATION_ERROR
end
@@ -189,12 +199,33 @@ module JamRuby
band.genres = genres
end
+ band.skip_genre_validation = true unless params[:validate_genres]
+
+ unless band.new_record?
+ OnlinePresence.delete_all(["player_id = ?", band.id])
+ PerformanceSample.delete_all(["player_id = ?", band.id])
+ end
+ online_presences = params[:online_presences]
+ if online_presences.present?
+ online_presences.each do |op|
+ new_presence = OnlinePresence.create(band, op, false)
+ band.online_presences << new_presence
+ end
+ end
+
+ performance_samples = params[:performance_samples]
+ if performance_samples.present?
+ performance_samples.each do |ps|
+ band.performance_samples << PerformanceSample.create(band, ps, false)
+ end
+ end
+
band
end
# helper method for creating / updating a Band
- def self.save(user, params)
+ def self.save(user, params)
band = build_band(user, params)
if band.save
diff --git a/ruby/lib/jam_ruby/models/claimed_recording.rb b/ruby/lib/jam_ruby/models/claimed_recording.rb
index 18dd11898..462ce6b58 100644
--- a/ruby/lib/jam_ruby/models/claimed_recording.rb
+++ b/ruby/lib/jam_ruby/models/claimed_recording.rb
@@ -12,6 +12,7 @@ module JamRuby
has_many :playing_sessions, :class_name => "JamRuby::ActiveMusicSession"
has_many :likes, :class_name => "JamRuby::RecordingLiker", :foreign_key => "claimed_recording_id", :dependent => :destroy
has_many :plays, :class_name => "JamRuby::PlayablePlay", :foreign_key => "claimed_recording_id", :dependent => :destroy
+ has_many :performance_samples, :class_name => "JamRuby::PerformanceSample", :foreign_key => "claimed_recording_id", :dependent => :destroy
has_one :share_token, :class_name => "JamRuby::ShareToken", :inverse_of => :shareable, :foreign_key => 'shareable_id', :dependent => :destroy
validates :name, no_profanity: true, length: {minimum: 3, maximum: 64}, presence: true
diff --git a/ruby/lib/jam_ruby/models/genre_player.rb b/ruby/lib/jam_ruby/models/genre_player.rb
index 76bdb4ccf..2d125ea9e 100644
--- a/ruby/lib/jam_ruby/models/genre_player.rb
+++ b/ruby/lib/jam_ruby/models/genre_player.rb
@@ -1,6 +1,13 @@
module JamRuby
class GenrePlayer < ActiveRecord::Base
+ PROFILE = 'profile'
+ VIRTUAL_BAND = 'virtual_band'
+ TRADITIONAL_BAND = 'traditional_band'
+ PAID_SESSION = 'paid_sessions'
+ FREE_SESSION = 'free_sessions'
+ COWRITING = 'cowriting'
+
self.table_name = "genre_players"
belongs_to :player, polymorphic: true
diff --git a/ruby/lib/jam_ruby/models/instrument.rb b/ruby/lib/jam_ruby/models/instrument.rb
index e7f0185f5..1a3fa8df7 100644
--- a/ruby/lib/jam_ruby/models/instrument.rb
+++ b/ruby/lib/jam_ruby/models/instrument.rb
@@ -36,7 +36,7 @@ module JamRuby
# users
has_many :musician_instruments, :class_name => "JamRuby::MusicianInstrument"
- has_many :users, :through => :musician_instruments, :class_name => "JamRuby::User"
+ has_many :players, :through => :musician_instruments
has_many :tracks, :class_name => "JamRuby::Track", :inverse_of => :instrument
has_many :recorded_tracks, :class_name => "JamRuby::RecordedTrack", :inverse_of => :instrument
diff --git a/ruby/lib/jam_ruby/models/json_store.rb b/ruby/lib/jam_ruby/models/json_store.rb
new file mode 100644
index 000000000..ca6aeb087
--- /dev/null
+++ b/ruby/lib/jam_ruby/models/json_store.rb
@@ -0,0 +1,33 @@
+module JamRuby
+ class JsonStore < ActiveRecord::Base
+ self.table_name = 'json_stores'
+
+ serialize :data_blob, JSON
+
+ before_create do
+ self.data_blob ||= {}
+ end
+
+ after_save do
+ @json = nil
+ end
+
+ attr_accessible :user_id
+
+ belongs_to :user, class_name: "JamRuby::User"
+
+ def json
+ @json ||= self.data_blob
+ end
+
+ def update_json_value(key, val)
+ self.json[key] = val
+ self.update_attribute(:data_blob, self.json)
+ end
+
+ def json_value(key)
+ self.json[key]
+ end
+
+ end
+end
diff --git a/ruby/lib/jam_ruby/models/musician_instrument.rb b/ruby/lib/jam_ruby/models/musician_instrument.rb
index 376b99a3c..445070da6 100644
--- a/ruby/lib/jam_ruby/models/musician_instrument.rb
+++ b/ruby/lib/jam_ruby/models/musician_instrument.rb
@@ -10,7 +10,7 @@ module JamRuby
# proficiency is 1 = Beginner, 2 = Intermediate, 3 = Expert
- belongs_to :user, :class_name => "JamRuby::User"
+ belongs_to :player, polymorphic: true
belongs_to :instrument, :class_name => "JamRuby::Instrument"
def description
diff --git a/ruby/lib/jam_ruby/models/musician_search.rb b/ruby/lib/jam_ruby/models/musician_search.rb
new file mode 100644
index 000000000..22d3d6221
--- /dev/null
+++ b/ruby/lib/jam_ruby/models/musician_search.rb
@@ -0,0 +1,405 @@
+module JamRuby
+ class MusicianSearch < JsonStore
+
+ attr_accessor :page_count, :results, :user_counters, :page_number
+
+ ANY_VAL_STR = 'any'
+ ANY_VAL_INT = -1
+
+ PER_PAGE = 10
+ PG_SMALLINT_MAX = 32767
+
+ KEY_GIGS = 'concert_gigs'
+ KEY_STUDIOS = 'studio_sessions'
+ KEY_AGES = 'ages'
+ KEY_SKILL = 'skill_level'
+ KEY_GENRES = 'genres'
+ KEY_INSTRUMENTS = 'instruments'
+ KEY_INTERESTS = 'interests'
+ KEY_SORT_ORDER = 'sort_order'
+
+ SORT_VALS = %W{ latency distance }
+ SORT_ORDERS = {
+ SORT_VALS[0] => 'Latency to Me',
+ SORT_VALS[1] => 'Distance to Me'
+ }
+
+ SKILL_VALS = [ANY_VAL_INT, 1, 2]
+ SKILL_LEVELS = {
+ SKILL_VALS[0] => 'Any',
+ SKILL_VALS[1] => 'Amateur',
+ SKILL_VALS[2] => 'Pro',
+ }
+
+ GIG_COUNTS = [ANY_VAL_INT, 0, 1, 2, 3, 4]
+ GIG_LABELS = {
+ GIG_COUNTS[0] => 'Any',
+ GIG_COUNTS[1] => 'under 10',
+ GIG_COUNTS[2] => '10 to 50',
+ GIG_COUNTS[3] => '50 to 100',
+ GIG_COUNTS[4] => 'over 100'
+ }
+
+ STUDIO_COUNTS = [ANY_VAL_INT, 0, 1, 2, 3, 4]
+ STUDIOS_LABELS = {
+ STUDIO_COUNTS[0] => 'Any',
+ STUDIO_COUNTS[1] => 'under 10',
+ STUDIO_COUNTS[2] => '10 to 50',
+ STUDIO_COUNTS[3] => '50 to 100',
+ STUDIO_COUNTS[4] => 'over 100'
+ }
+
+ AGE_COUNTS = [10, 20, 30, 40, 50]
+ AGES = {
+ AGE_COUNTS[0] => 'Teens',
+ AGE_COUNTS[1] => "20's",
+ AGE_COUNTS[2] => "30's",
+ AGE_COUNTS[3] => "40's",
+ AGE_COUNTS[4] => "50+"
+ }
+
+ INTEREST_VALS = [ANY_VAL_STR,
+ GenrePlayer::VIRTUAL_BAND,
+ GenrePlayer::TRADITIONAL_BAND,
+ GenrePlayer::PAID_SESSION,
+ GenrePlayer::FREE_SESSION,
+ GenrePlayer::COWRITING,
+ ]
+ INTERESTS = {
+ INTEREST_VALS[0] => 'Any',
+ INTEREST_VALS[1] => 'Virtual Band',
+ INTEREST_VALS[2] => 'Traditional Band',
+ INTEREST_VALS[3] => 'Paid Sessions',
+ INTEREST_VALS[4] => 'Free Sessions',
+ INTEREST_VALS[5] => 'Co-Writing'
+ }
+
+ INSTRUMENT_PROFICIENCY = {
+ 1 => 'Beginner',
+ 2 => 'Intermediate',
+ 3 => 'Expert',
+ }
+
+ JSON_SCHEMA = {
+ KEY_SORT_ORDER => SORT_VALS[0],
+ KEY_INSTRUMENTS => [],
+ KEY_INTERESTS => INTEREST_VALS[0],
+ KEY_GENRES => [],
+ KEY_GIGS => GIG_COUNTS[0].to_s,
+ KEY_STUDIOS => STUDIO_COUNTS[0].to_s,
+ KEY_SKILL => SKILL_VALS[0].to_s,
+ KEY_AGES => []
+ }
+ JSON_SCHEMA_KEYS = JSON_SCHEMA.keys
+ MULTI_VALUE_KEYS = JSON_SCHEMA.collect { |kk,vv| vv.is_a?(Array) ? kk : nil }.compact
+ SINGLE_VALUE_KEYS = JSON_SCHEMA.keys - MULTI_VALUE_KEYS
+
+ SEARCH_FILTER_META = {
+ per_page: PER_PAGE,
+ filter_keys: {
+ keys: JSON_SCHEMA_KEYS,
+ multi: MULTI_VALUE_KEYS,
+ single: SINGLE_VALUE_KEYS,
+ },
+ sort_order: { keys: SORT_VALS, map: SORT_ORDERS },
+ interests: { keys: INTEREST_VALS, map: INTERESTS },
+ ages: { keys: AGE_COUNTS, map: AGES }
+ }
+
+ def self.user_search_filter(user)
+ unless ms = user.musician_search
+ ms = self.create_search(user)
+ end
+ ms
+ end
+
+ def self.search_filter_json(user)
+ self.user_search_filter(user).json
+ end
+
+ def self.create_search(user)
+ ms = self.new
+ ms.user = user
+ ms.data_blob = JSON_SCHEMA
+ ms.save!
+ ms
+ end
+
+ def _genres(rel)
+ gids = json[KEY_GENRES]
+ unless gids.blank?
+ gidsql = gids.join("','")
+ gpsql = "SELECT player_id FROM genre_players WHERE (player_type = 'JamRuby::User' AND genre_id IN ('#{gidsql}'))"
+ rel = rel.where("users.id IN (#{gpsql})")
+ end
+ rel
+ end
+
+ def _instruments(rel)
+ unless (instruments = json['instruments']).blank?
+ instsql = "SELECT player_id FROM musicians_instruments WHERE (("
+ instsql += instruments.collect do |inst|
+ "instrument_id = '#{inst['instrument_id']}' AND proficiency_level = #{inst['proficiency_level']}"
+ end.join(") OR (")
+ instsql += "))"
+ rel = rel.where("users.id IN (#{instsql})")
+ end
+ rel
+ end
+
+ def _ages(rel)
+ unless (vals = json[KEY_AGES]).blank?
+ return rel if vals.detect { |vv| ANY_VAL_INT == vv }
+ arels = []
+ vals.each do |val|
+ today = Date.today
+ case val.to_i
+ when 10
+ arels << User.where("birth_date >= ? AND birth_date < ?",
+ today - 20.years, today - 10.years)
+ when 20
+ arels << User.where("birth_date >= ? AND birth_date < ?",
+ today - 30.years, today - 20.years)
+ when 30
+ arels << User.where("birth_date >= ? AND birth_date < ?",
+ today - 40.years, today - 50.years)
+ when 40
+ arels << User.where("birth_date >= ? AND birth_date < ?",
+ today - 50.years, today - 40.years)
+ when 50
+ arels << User.where("birth_date <= ?", today - 50.years)
+ end
+ end
+ rel = rel.where("birth_date IS NOT NULL")
+ sql = "(#{arels.map(&:where_values).flatten.join(') OR (')})"
+ rel = rel.where(sql)
+ end
+ rel
+ end
+
+ def _studios(rel)
+ ss = json[KEY_STUDIOS].to_i
+ rel = rel.where('studio_session_count = ?', ss) if 0 <= ss
+ rel
+ end
+
+ def _gigs(rel)
+ gg = json[KEY_GIGS].to_i
+ rel = rel.where('concert_count = ?',gg) if 0 <= gg
+ rel
+ end
+
+ def _skills(rel)
+ if 0 < (val = json[KEY_SKILL].to_i)
+ rel = rel.where(['skill_level = ?', val])
+ end
+ rel
+ end
+
+ def _interests(rel)
+ val = json[KEY_INTERESTS]
+ if val.present? && ANY_VAL_STR != val
+ rel = rel.where("#{val} = ?", true)
+ end
+ rel
+ end
+
+ def _sort_order(rel)
+ val = json[KEY_SORT_ORDER]
+ if SORT_VALS[1] == val
+ locidispid = self.user.last_jam_locidispid || 0
+ my_locid = locidispid / 1000000
+ rel = rel.joins("LEFT JOIN geoiplocations AS my_geo ON my_geo.locid = #{my_locid}")
+ rel = rel.joins("LEFT JOIN geoiplocations AS other_geo ON users.last_jam_locidispid/1000000 = other_geo.locid")
+ rel = rel.group("users.id, my_geo.geog, other_geo.geog")
+ rel = rel.order('st_distance(my_geo.geog, other_geo.geog)')
+ else
+ rel = rel.joins("LEFT JOIN current_scores ON current_scores.a_userid = users.id AND current_scores.b_userid = '#{self.user.id}'")
+ rel = rel.order('current_scores.full_score ASC')
+ end
+ rel
+ end
+
+ def do_search(params={})
+ rel = User.musicians.where('users.id <> ?', self.user.id)
+ rel = self._genres(rel)
+ rel = self._ages(rel)
+ rel = self._studios(rel)
+ rel = self._gigs(rel)
+ rel = self._skills(rel)
+ rel = self._instruments(rel)
+ rel = self._interests(rel)
+ rel = self._sort_order(rel)
+ rel
+ end
+
+ def search_results_page(filter=nil, page=1)
+ if filter
+ self.data_blob = filter
+ self.save
+ else
+ filter = self.data_blob
+ end
+
+ rel = do_search(filter)
+
+ @page_number = [page.to_i, 1].max
+ rel = rel.paginate(:page => @page_number, :per_page => PER_PAGE)
+
+ rel = rel.includes([:instruments, :followings, :friends])
+ @page_count = rel.total_pages
+
+ musician_results(rel.all)
+ end
+
+ def reset_filter
+ self.data_blob = JSON_SCHEMA
+ self.save
+ end
+
+ def reset_search_results
+ reset_filter
+ search_results_page
+ end
+
+ RESULT_FOLLOW = :follows
+ RESULT_FRIEND = :friends
+
+ COUNT_FRIEND = :count_friend
+ COUNT_FOLLOW = :count_follow
+ COUNT_RECORD = :count_record
+ COUNT_SESSION = :count_session
+ COUNTERS = [COUNT_FRIEND, COUNT_FOLLOW, COUNT_RECORD, COUNT_SESSION]
+
+ def musician_results(_results)
+ @results = _results
+ @user_counters = {} and return self unless user
+
+ @user_counters = @results.inject({}) { |hh,val| hh[val.id] = []; hh }
+ mids = "'#{@results.map(&:id).join("','")}'"
+
+ # this gets counts for each search result on friends/follows/records/sessions
+ @results.each do |uu|
+ counters = { }
+ counters[COUNT_FRIEND] = Friendship.where(:user_id => uu.id).count
+ counters[COUNT_FOLLOW] = Follow.where(:followable_id => uu.id).count
+ counters[COUNT_RECORD] = ClaimedRecording.where(:user_id => uu.id).count
+ counters[COUNT_SESSION] = MusicSession.where(:user_id => uu.id).count
+ @user_counters[uu.id] << counters
+ end
+
+ # this section determines follow/like/friend status for each search result
+ # so that action links can be activated or not
+ rel = User.select("users.id AS uid")
+ rel = rel.joins("LEFT JOIN follows ON follows.user_id = '#{user.id}'")
+ rel = rel.where(["users.id IN (#{mids}) AND follows.followable_id = users.id"])
+ rel.all.each { |val| @user_counters[val.uid] << RESULT_FOLLOW }
+
+ rel = User.select("users.id AS uid")
+ rel = rel.joins("LEFT JOIN friendships AS friends ON friends.friend_id = '#{user.id}'")
+ rel = rel.where(["users.id IN (#{mids}) AND friends.user_id = users.id"])
+ rel.all.each { |val| @user_counters[val.uid] << RESULT_FRIEND }
+
+ self
+ end
+
+ private
+
+ def _count(musician, key)
+ if mm = @user_counters[musician.id]
+ return mm.detect { |ii| ii.is_a?(Hash) }[key]
+ end if @user_counters
+ 0
+ end
+
+ public
+
+ def follow_count(musician)
+ _count(musician, COUNT_FOLLOW)
+ end
+
+ def friend_count(musician)
+ _count(musician, COUNT_FRIEND)
+ end
+
+ def record_count(musician)
+ _count(musician, COUNT_RECORD)
+ end
+
+ def session_count(musician)
+ _count(musician, COUNT_SESSION)
+ end
+
+ def is_friend?(musician)
+ if mm = @user_counters[musician.id]
+ return mm.include?(RESULT_FRIEND)
+ end if @user_counters
+ false
+ end
+
+ def is_follower?(musician)
+ if mm = @user_counters[musician.id]
+ return mm.include?(RESULT_FOLLOW)
+ end if @user_counters
+ false
+ end
+
+ def search_type
+ self.class.to_s
+ end
+
+ def is_blank?
+ self.data_blob == JSON_SCHEMA
+ end
+
+ def description
+ if self.is_blank?
+ return 'Click search button to look for musicians with similar interests, skill levels, etc.'
+ end
+ jj = self.json
+ str = 'Current Search: '
+ str += "Sort = #{SORT_ORDERS[json_value(MusicianSearch::KEY_SORT_ORDER)]}"
+ if (val = jj[KEY_INTERESTS]) != INTEREST_VALS[0]
+ str += "; Interest = #{INTERESTS[val]}"
+ end
+ if (val = jj[KEY_SKILL].to_i) != SKILL_VALS[0]
+ str += "; Skill = #{SKILL_LEVELS[val]}"
+ end
+ if (val = jj[KEY_STUDIOS].to_i) != STUDIO_COUNTS[0]
+ str += "; Studio Sessions = #{STUDIOS_LABELS[val]}"
+ end
+ if (val = jj[KEY_GIGS].to_i) != GIG_COUNTS[0]
+ str += "; Concert Gigs = #{GIG_LABELS[val]}"
+ end
+ val = jj[KEY_AGES].map(&:to_i)
+ val.sort!
+ if !val.blank?
+ str += "; Ages = "
+ val.each_with_index do |vv, idx|
+ str += "#{AGES[vv]}"
+ str += ', ' unless idx==(val.length-1)
+ end
+ end
+ if 0 < (val = jj[KEY_GENRES]).length
+ str += "; Genres = "
+ genres = Genre.where(["id IN (?)", val]).order('description').pluck(:description)
+ genres.each_with_index do |gg, idx|
+ str += "#{gg}"
+ str += ', ' unless idx==(genres.length-1)
+ end
+ end
+ if 0 < (val = jj[KEY_INSTRUMENTS]).length
+ str += "; Instruments = "
+ instr_ids = val.collect { |vv| vv['instrument_id'] }
+ instrs = Instrument.where(["id IN (?)", instr_ids]).order(:description)
+ instrs.each_with_index do |ii, idx|
+ proficiency = val.detect { |vv| vv['instrument_id'] == ii.id }['proficiency_level']
+ str += "#{ii.description} (#{INSTRUMENT_PROFICIENCY[proficiency.to_i]})"
+ str += ', ' unless idx==(instrs.length-1)
+ end
+ end
+ str
+ end
+
+ end
+end
diff --git a/ruby/lib/jam_ruby/models/notification.rb b/ruby/lib/jam_ruby/models/notification.rb
index 33ac67145..db4018ab8 100644
--- a/ruby/lib/jam_ruby/models/notification.rb
+++ b/ruby/lib/jam_ruby/models/notification.rb
@@ -57,7 +57,7 @@ module JamRuby
# remove all notifications related to this session if it's not found
if session.nil?
- Notification.delete_all "(session_id = '#{session_id}')"
+ Notification.delete_all "(session_id = '#{self.session_id}')"
end
end
diff --git a/ruby/lib/jam_ruby/models/online_presence.rb b/ruby/lib/jam_ruby/models/online_presence.rb
new file mode 100644
index 000000000..ed4f498bd
--- /dev/null
+++ b/ruby/lib/jam_ruby/models/online_presence.rb
@@ -0,0 +1,70 @@
+module JamRuby
+ class OnlinePresence < ActiveRecord::Base
+
+ PERMISSION_MSG = "You do not have permission to perform this operation."
+
+ attr_accessible :player_id, :service_type, :username
+ belongs_to :player, polymorphic: true
+
+ validates :service_type, presence:true, length: {maximum: 100}
+ validates :username, presence:true, length: {maximum: 100}
+
+ validate :username_service_type_unique
+
+ def username_service_type_unique
+ match = OnlinePresence.exists?(:username => self.username, :service_type => self.service_type)
+ raise ConflictError, "Username #{self.username} is already associated with a #{self.service_type} account" if match
+ end
+
+ def self.index(options = {})
+ raise StateError, "The user is not specified." if options[:id].blank?
+ OnlinePresence.where("player_id = ?", options[:id])
+ end
+
+ # Create with target_player (target_player is either user or band):
+ def self.create(target_player, options = {}, save = true)
+ auth_player(target_player, options)
+ raise StateError, "Missing required information" if options[:service_type].blank? || options[:username].blank?
+
+ up = OnlinePresence.new({
+ :player_id => target_player.id,
+ :service_type => options[:service_type],
+ :username => options[:username]
+ })
+
+ up.save! if save
+ up
+ end
+
+ def self.update(target_player, options = {})
+ auth_player(target_player, options)
+ raise StateError, "Missing required information" if options[:service_type].blank? || options[:username].blank? || options[:id].blank?
+
+ up = OnlinePresence.find(options[:id])
+ up.service_type = options[:service_type]
+ up.username = options[:username]
+ up.save!
+ end
+
+ def self.delete(target_player, options = {})
+ id = options[:id]
+ raise StateError, "Missing required information" if id.blank?
+ online_presence = OnlinePresence.find(id)
+
+ if online_presence.player_id != target_player.id
+ raise JamPermissionError, PERMISSION_MSG
+ end
+
+ unless online_presence.nil?
+ OnlinePresence.destroy(id)
+ end
+ end
+
+ private
+ def self.auth_player(target_player, options={})
+ if target_player.nil? || options[:player_id] != target_player.id
+ raise JamPermissionError, PERMISSION_MSG
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/ruby/lib/jam_ruby/models/performance_sample.rb b/ruby/lib/jam_ruby/models/performance_sample.rb
new file mode 100644
index 000000000..38ed618bb
--- /dev/null
+++ b/ruby/lib/jam_ruby/models/performance_sample.rb
@@ -0,0 +1,73 @@
+module JamRuby
+ class PerformanceSample < ActiveRecord::Base
+
+ PERMISSION_MSG = "You do not have permission to perform this operation."
+
+ attr_accessible :player_id, :service_type, :claimed_recording_id, :service_id, :url, :description
+
+ belongs_to :player, polymorphic: true
+ belongs_to :claimed_recording, :class_name => "JamRuby::ClaimedRecording", :foreign_key => "claimed_recording_id"
+
+ validates :service_type, presence:true, length: {maximum: 100}
+
+ # JamKazam validators
+ validate :claimed_recording_id_present, :if => lambda { |p| p.service_type == "jamkazam" }
+ validate :user_type_recording_unique, :if => lambda { |p| p.service_type == "jamkazam" }
+
+ # Non-JamKazam validators
+ validate :service_id_present, :if => lambda { |p| p.service_type != "jamkazam" }
+ validate :user_type_service_unique, :if => lambda { |p| p.service_type != "jamkazam" }
+
+ def claimed_recording_id_present
+ raise StateError, "Claimed recording is required for JamKazam performance samples" if self.claimed_recording_id.blank?
+ end
+
+ def user_type_recording_unique
+ match = PerformanceSample.exists?(:player_id => self.player_id, :claimed_recording_id => self.claimed_recording_id, :service_type => self.service_type)
+ raise ConflictError, "You already have this JamKazam recording listed as a sample" if match
+ end
+
+ def service_id_present
+ raise StateError, "Service ID is required for non-JamKazam performance samples" if self.service_id.blank?
+ end
+
+ def user_type_service_unique
+ match = PerformanceSample.exists?(:player_id => self.player_id, :service_id => self.service_id, :service_type => self.service_type)
+ raise ConflictError, "You already have this #{self.service_type} sample listed (#{self.service_id})." if match
+ end
+
+ def self.index(options = {})
+ raise JamArgumentError, "The user is not specified." if options[:id].blank?
+ PerformanceSample.where("player_id = ?", options[:id])
+ end
+
+ # Create with target_player (target_player is either user or band):
+ def self.create(target_player, options = {}, save = true)
+ auth_user(target_player, options)
+ raise StateError, "Missing required information" if options[:service_type].blank?
+
+ ps = PerformanceSample.new({
+ :player_id => target_player.id,
+ :service_type => options[:service_type],
+ :claimed_recording_id => options[:claimed_recording_id],
+ :service_id => options[:service_id],
+ :url => options[:url],
+ :description => options[:description]
+ })
+
+ ps.save! if save
+ ps
+ end
+
+ def self.delete(target_player, options = {})
+ raise JamPermissionError, "You do not have permission to perform this operation" if target_player.nil? || options[:player_id] != target_player.id
+ raise StateError, "The performance sample ID is missing." if options[:id].blank?
+ PerformanceSample.destroy(options[:id])
+ end
+
+ private
+ def self.auth_user(target_player, options={})
+ raise JamPermissionError, PERMISSION_MSG if target_player.nil? || options[:player_id] != target_player.id
+ end
+ end
+end
\ No newline at end of file
diff --git a/ruby/lib/jam_ruby/models/search.rb b/ruby/lib/jam_ruby/models/search.rb
index ae0ba396e..d1a4d13ea 100644
--- a/ruby/lib/jam_ruby/models/search.rb
+++ b/ruby/lib/jam_ruby/models/search.rb
@@ -2,7 +2,7 @@ module JamRuby
# not a active_record model; just a search result container
class Search
- attr_accessor :results, :search_type
+ attr_accessor :results, :search_type, :query
attr_accessor :user_counters, :page_num, :page_count
LIMIT = 10
@@ -10,82 +10,6 @@ module JamRuby
SEARCH_TEXT_TYPES = [:musicians, :bands, :fans]
SEARCH_TEXT_TYPE_ID = :search_text_type
- def self.band_search(txt, user = nil)
- self.text_search({ SEARCH_TEXT_TYPE_ID => :bands, :query => txt }, user)
- end
-
- def self.fan_search(txt, user = nil)
- self.text_search({ SEARCH_TEXT_TYPE_ID => :fans, :query => txt }, user)
- end
-
- def self.musician_search(txt, user = nil)
- self.text_search({ SEARCH_TEXT_TYPE_ID => :musicians, :query => txt }, user)
- end
-
- def self.session_invite_search(query, user)
- srch = Search.new
- srch.search_type = :session_invite
- like_str = "%#{query.downcase}%"
- rel = User
- .musicians
- .where(["users.id IN (SELECT friend_id FROM friendships WHERE user_id = '#{user.id}')"])
- .where(["first_name ILIKE ? OR last_name ILIKE ?", like_str, like_str])
- .limit(10)
- .order([:last_name, :first_name])
- srch.results = rel.all
- srch
- end
-
- def self.text_search(params, user = nil)
- srch = Search.new
- unless (params.blank? || params[:query].blank? || 2 > params[:query].length)
- srch.text_search(params, user)
- end
- srch
- end
-
- def text_search(params, user = nil)
- tsquery = Search.create_tsquery(params[:query])
- return [] if tsquery.blank?
-
- rel = case params[SEARCH_TEXT_TYPE_ID].to_s
- when 'bands'
- @search_type = :bands
- Band.scoped
- when 'fans'
- @search_type = :fans
- User.fans
- else
- @search_type = :musicians
- User.musicians
- end
- @results = rel.where("(name_tsv @@ to_tsquery('jamenglish', ?))", tsquery).limit(10)
- @results
- end
-
- def initialize(search_results=nil)
- @results = []
- self
- end
-
- def self.create_tsquery(query)
- return nil if query.blank?
-
- search_terms = query.split
- return nil if search_terms.length == 0
-
- args = nil
- search_terms.each do |search_term|
- if args == nil
- args = search_term
- else
- args = args + " & " + search_term
- end
- end
- args = args + ":*"
- args
- end
-
PARAM_SESSION_INVITE = :srch_sessinv
PARAM_MUSICIAN = :srch_m
PARAM_BAND = :srch_b
@@ -133,164 +57,322 @@ module JamRuby
DATE_OPTS = [['Today', 'today'], ['This Week', 'week'], ['This Month', 'month'], ['All Time', 'all']]
- def self.order_param(params, keys=M_ORDERING_KEYS)
- ordering = params[:orderby]
- ordering.blank? ? keys[0] : keys.detect { |oo| oo.to_s == ordering }
+
+ def initialize(search_results=nil)
+ @results = []
+ self
+ end
+
+ def is_blank?
+ !!@query && @query.empty?
end
- # produce a list of musicians (users where musician is true)
- # params:
- # instrument - instrument to search for or blank
- # score_limit - a range specification for score, see M_SCORE_OPTS above.
- # handled by relation_pagination:
- # page - page number to fetch (origin 1)
- # per_page - number of entries per page
- # handled by order_param:
- # orderby - what sort of search, also defines order (followed, plays, playing)
- # previously handled by where_latlng:
- # distance - defunct!
- # city - defunct!
- # remote_ip - defunct!
- def self.musician_filter(params={}, user=nil)
+ def text_search(params, user = nil)
+ @query = params[:query]
+ tsquery = Search.create_tsquery(params[:query])
+ return [] if tsquery.blank?
- rel = User.musicians # not musicians_geocoded on purpose; we allow 'unknowns' to surface in the search page
- rel = rel.select('users.*')
- rel = rel.group('users.id')
+ rel = case params[SEARCH_TEXT_TYPE_ID].to_s
+ when 'bands'
+ @search_type = :bands
+ Band.scoped
+ when 'fans'
+ @search_type = :fans
+ User.fans
+ else
+ @search_type = :musicians
+ User.musicians
+ end
+ @results = rel.where("(name_tsv @@ to_tsquery('jamenglish', ?))", tsquery).limit(10)
+ @results
+ end
- unless (instrument = params[:instrument]).blank?
- rel = rel.joins("inner JOIN musicians_instruments AS minst ON minst.user_id = users.id")
- .where(['minst.instrument_id = ?', instrument])
+ class << self
+ def band_search(txt, user = nil)
+ self.text_search({ SEARCH_TEXT_TYPE_ID => :bands, :query => txt }, user)
end
- # to find appropriate musicians we need to join users with scores to get to those with no scores or bad scores
- # weeded out
-
- # filter on scores using selections from params
- # see M_SCORE_OPTS
- score_limit = ANY_SCORE
- l = params[:score_limit]
- unless l.nil?
- score_limit = l
+ def fan_search(txt, user = nil)
+ self.text_search({ SEARCH_TEXT_TYPE_ID => :fans, :query => txt }, user)
end
- locidispid = user.nil? ? 0 : (user.last_jam_locidispid || 0)
-
- # user can override their location with these 3 values
- country = params[:country]
- region = params[:region]
- city = params[:city]
-
- my_locid = nil # this is used for distance searches only
-
- if country && region && city
- geoiplocation = GeoIpLocations.where(countrycode: country, region: region, city: city).first
- my_locid = geoiplocation.locid
+ def musician_search(txt, user = nil)
+ self.text_search({ SEARCH_TEXT_TYPE_ID => :musicians, :query => txt }, user)
end
- unless my_locid
- my_locid = locidispid/1000000 # if the user didn't specify a location to search on, user their account locidispid
+ def session_invite_search(query, user)
+ srch = Search.new
+ srch.search_type = :session_invite
+ like_str = "%#{query.downcase}%"
+ rel = User
+ .musicians
+ .where(["users.id IN (SELECT friend_id FROM friendships WHERE user_id = '#{user.id}')"])
+ .where(["first_name ILIKE ? OR last_name ILIKE ?", like_str, like_str])
+ .limit(10)
+ .order([:last_name, :first_name])
+ srch.results = rel.all
+ srch
end
- if !locidispid.nil? && !user.nil?
- # score_join of left allows for null scores, whereas score_join of inner requires a score however good or bad
- # this is ANY_SCORE:
- score_join = 'left outer' # or 'inner'
- score_min = nil
- score_max = nil
- # these score_min, score_max come from here (doubled): https://jamkazam.atlassian.net/browse/VRFS-1962
- case score_limit
- when GOOD_SCORE
- score_join = 'inner'
- score_min = nil
- score_max = 40
- when MODERATE_SCORE
- score_join = 'inner'
- score_min = 40
- score_max = 70
- when POOR_SCORE
- score_join = 'inner'
- score_min = 80
- score_max = 100
- when UNACCEPTABLE_SCORE
- score_join = 'inner'
- score_min = 100
- score_max = nil
- when SCORED_SCORE
- score_join = 'inner'
- score_min = nil
- score_max = nil
- when TEST_SCORE
- score_join = 'inner'
- score_min = nil
- score_max = 60
- when ANY_SCORE
- # the default of ANY setup above applies
+ def text_search(params, user = nil)
+ srch = Search.new
+ unless (params.blank? || params[:query].blank? || 2 > params[:query].length)
+ srch.text_search(params, user)
+ end
+ srch
+ end
+
+ def create_tsquery(query)
+ return nil if query.blank?
+
+ search_terms = query.split
+ return nil if search_terms.length == 0
+
+ args = nil
+ search_terms.each do |search_term|
+ if args == nil
+ args = search_term
else
- # the default of ANY setup above applies
+ args = args + " & " + search_term
+ end
+ end
+ args = args + ":*"
+ args
+ end
+ def order_param(params, keys=M_ORDERING_KEYS)
+ ordering = params[:orderby]
+ ordering.blank? ? keys[0] : keys.detect { |oo| oo.to_s == ordering }
+ end
+
+ # produce a list of musicians (users where musician is true)
+ # params:
+ # instrument - instrument to search for or blank
+ # score_limit - a range specification for score, see M_SCORE_OPTS above.
+ # handled by relation_pagination:
+ # page - page number to fetch (origin 1)
+ # per_page - number of entries per page
+ # handled by order_param:
+ # orderby - what sort of search, also defines order (followed, plays, playing)
+ # previously handled by where_latlng:
+ # distance - defunct!
+ # city - defunct!
+ # remote_ip - defunct!
+ def musician_filter(params={}, user=nil)
+
+ rel = User.musicians # not musicians_geocoded on purpose; we allow 'unknowns' to surface in the search page
+ rel = rel.select('users.*')
+ rel = rel.group('users.id')
+
+ unless (instrument = params[:instrument]).blank?
+ rel = rel.joins("inner JOIN musicians_instruments AS minst ON minst.player_id = users.id")
+ .where(['minst.instrument_id = ?', instrument])
end
- rel = rel.joins("LEFT JOIN current_scores ON current_scores.a_userid = users.id AND current_scores.b_userid = '#{user.id}'")
+ # to find appropriate musicians we need to join users with scores to get to those with no scores or bad scores
+ # weeded out
- rel = rel.joins('LEFT JOIN regions ON regions.countrycode = users.country AND regions.region = users.state')
+ # filter on scores using selections from params
+ # see M_SCORE_OPTS
+ score_limit = ANY_SCORE
+ l = params[:score_limit]
+ unless l.nil?
+ score_limit = l
+ end
- rel = rel.where(['current_scores.full_score > ?', score_min]) unless score_min.nil?
- rel = rel.where(['current_scores.full_score <= ?', score_max]) unless score_max.nil?
+ locidispid = user.nil? ? 0 : (user.last_jam_locidispid || 0)
- rel = rel.select('current_scores.full_score, current_scores.score, regions.regionname')
- rel = rel.group('current_scores.full_score, current_scores.score, regions.regionname')
+ # user can override their location with these 3 values
+ country = params[:country]
+ region = params[:region]
+ city = params[:city]
+
+ my_locid = nil # this is used for distance searches only
+
+ if country && region && city
+ geoiplocation = GeoIpLocations.where(countrycode: country, region: region, city: city).first
+ my_locid = geoiplocation.locid
+ end
+
+ unless my_locid
+ my_locid = locidispid/1000000 # if the user didn't specify a location to search on, user their account locidispid
+ end
+
+ if !locidispid.nil? && !user.nil?
+ # score_join of left allows for null scores, whereas score_join of inner requires a score however good or bad
+ # this is ANY_SCORE:
+ score_join = 'left outer' # or 'inner'
+ score_min = nil
+ score_max = nil
+ # these score_min, score_max come from here (doubled): https://jamkazam.atlassian.net/browse/VRFS-1962
+ case score_limit
+ when GOOD_SCORE
+ score_join = 'inner'
+ score_min = nil
+ score_max = 40
+ when MODERATE_SCORE
+ score_join = 'inner'
+ score_min = 40
+ score_max = 70
+ when POOR_SCORE
+ score_join = 'inner'
+ score_min = 80
+ score_max = 100
+ when UNACCEPTABLE_SCORE
+ score_join = 'inner'
+ score_min = 100
+ score_max = nil
+ when SCORED_SCORE
+ score_join = 'inner'
+ score_min = nil
+ score_max = nil
+ when TEST_SCORE
+ score_join = 'inner'
+ score_min = nil
+ score_max = 60
+ when ANY_SCORE
+ # the default of ANY setup above applies
+ else
+ # the default of ANY setup above applies
+ end
+
+ rel = rel.joins("LEFT JOIN current_scores ON current_scores.a_userid = users.id AND current_scores.b_userid = '#{user.id}'")
+
+ rel = rel.joins('LEFT JOIN regions ON regions.countrycode = users.country AND regions.region = users.state')
+
+ rel = rel.where(['current_scores.full_score > ?', score_min]) unless score_min.nil?
+ rel = rel.where(['current_scores.full_score <= ?', score_max]) unless score_max.nil?
+
+ rel = rel.select('current_scores.full_score, current_scores.score, regions.regionname')
+ rel = rel.group('current_scores.full_score, current_scores.score, regions.regionname')
+ end
+
+ ordering = self.order_param(params)
+ case ordering
+ when :latency
+ # nothing to do. the sort added below 'current_scores.score ASC NULLS LAST' handles this
+ when :distance
+ # convert miles to meters for PostGIS functions
+ miles = params[:distance].blank? ? 500 : params[:distance].to_i
+ meters = miles * 1609.34
+ rel = rel.joins("INNER JOIN geoiplocations AS my_geo ON #{my_locid} = my_geo.locid")
+ rel = rel.joins("INNER JOIN geoiplocations AS other_geo ON users.last_jam_locidispid/1000000 = other_geo.locid")
+ rel = rel.where("users.last_jam_locidispid/1000000 IN (SELECT locid FROM geoiplocations WHERE geog && st_buffer((SELECT geog FROM geoiplocations WHERE locid = #{my_locid}), #{meters}))")
+ rel = rel.group("my_geo.geog, other_geo.geog")
+ rel = rel.order('st_distance(my_geo.geog, other_geo.geog)')
+ when :plays # FIXME: double counting?
+ # sel_str = "COUNT(records)+COUNT(sessions) AS play_count, #{sel_str}"
+ rel = rel.select('COUNT(records.id)+COUNT(sessions.id) AS search_play_count')
+ rel = rel.joins("LEFT JOIN music_sessions AS sessions ON sessions.user_id = users.id")
+ rel = rel.joins("LEFT JOIN recordings AS records ON records.owner_id = users.id")
+ rel = rel.order("search_play_count DESC")
+ when :followed
+ rel = rel.joins('left outer join follows on follows.followable_id = users.id')
+ rel = rel.select('count(follows.user_id) as search_follow_count')
+ rel = rel.order('search_follow_count DESC')
+ when :playing
+ rel = rel.joins("inner JOIN connections ON connections.user_id = users.id")
+ rel = rel.where(['connections.aasm_state != ?', 'expired'])
+ end
+
+ if !locidispid.nil? && !user.nil?
+ rel = rel.order('current_scores.full_score ASC NULLS LAST')
+ end
+
+ rel = rel.order('users.created_at DESC')
+
+ rel, page = self.relation_pagination(rel, params)
+ rel = rel.includes([:instruments, :followings, :friends])
+
+ # XXX: DOES THIS MEAN ALL MATCHING USERS ARE RETURNED?
+ objs = rel.all
+
+ srch = Search.new
+ srch.search_type = :musicians_filter
+ srch.page_num, srch.page_count = page, objs.total_pages
+ srch.musician_results_for_user(objs, user)
end
- ordering = self.order_param(params)
- case ordering
- when :latency
- # nothing to do. the sort added below 'current_scores.score ASC NULLS LAST' handles this
- when :distance
- # convert miles to meters for PostGIS functions
- miles = params[:distance].blank? ? 500 : params[:distance].to_i
- meters = miles * 1609.34
- rel = rel.joins("INNER JOIN geoiplocations AS my_geo ON #{my_locid} = my_geo.locid")
- rel = rel.joins("INNER JOIN geoiplocations AS other_geo ON users.last_jam_locidispid/1000000 = other_geo.locid")
- rel = rel.where("users.last_jam_locidispid/1000000 IN (SELECT locid FROM geoiplocations WHERE geog && st_buffer((SELECT geog FROM geoiplocations WHERE locid = #{my_locid}), #{meters}))")
- rel = rel.group("my_geo.geog, other_geo.geog")
- rel = rel.order('st_distance(my_geo.geog, other_geo.geog)')
+ def relation_pagination(rel, params)
+ perpage = [(params[:per_page] || M_PER_PAGE).to_i, 100].min
+ page = [params[:page].to_i, 1].max
+ [rel.paginate(:page => page, :per_page => perpage), page]
+ end
+
+ def new_musicians(usr, since_date)
+ # this attempts to find interesting musicians to tell another musician about where interesting
+ # is "has a good score and was created recently"
+ # we're sort of depending upon usr being a musicians_geocoded as well...
+ # this appears to only be called from EmailBatchNewMusician#deliver_batch_sets! which is
+ # an offline process and thus uses the last jam location as "home base"
+
+ locidispid = usr.last_jam_locidispid
+ score_limit = 70
+ limit = 50
+
+ rel = User.musicians_geocoded
+ .where(['users.created_at >= ? AND users.id != ?', since_date, usr.id])
+ .joins('inner join current_scores on users.id = current_scores.a_userid')
+ .where(['current_scores.b_userid = ?', usr.id])
+ .where(['current_scores.full_score <= ?', score_limit])
+ .order('current_scores.full_score') # best scores first
+ .order('users.created_at DESC') # then most recent
+ .limit(limit)
+
+ objs = rel.all.to_a
+
+ if block_given?
+ yield(objs) if 0 < objs.count
+ else
+ return objs
+ end
+ end
+
+ def band_filter(params={}, current_user=nil)
+ rel = Band.scoped
+
+ unless (genre = params[:genre]).blank?
+ rel = Band.joins("RIGHT JOIN genre_players AS bgenres ON bgenres.player_id = bands.id AND bgenres.player_type = 'JamRuby::Band'")
+ .where(['bgenres.genre_id = ? AND bands.id IS NOT NULL', genre])
+ end
+
+ rel = GeoIpLocations.where_latlng(rel, params, current_user)
+
+ sel_str = 'bands.*'
+ case ordering = self.order_param(params, B_ORDERING_KEYS)
when :plays # FIXME: double counting?
- # sel_str = "COUNT(records)+COUNT(sessions) AS play_count, #{sel_str}"
- rel = rel.select('COUNT(records.id)+COUNT(sessions.id) AS search_play_count')
- rel = rel.joins("LEFT JOIN music_sessions AS sessions ON sessions.user_id = users.id")
- rel = rel.joins("LEFT JOIN recordings AS records ON records.owner_id = users.id")
- rel = rel.order("search_play_count DESC")
+ sel_str = "COUNT(records)+COUNT(msh) AS play_count, #{sel_str}"
+ rel = rel.joins("LEFT JOIN music_sessions AS msh ON msh.band_id = bands.id")
+ .joins("LEFT JOIN recordings AS records ON records.band_id = bands.id")
+ .group("bands.id")
+ .order("play_count DESC, bands.created_at DESC")
when :followed
- rel = rel.joins('left outer join follows on follows.followable_id = users.id')
- rel = rel.select('count(follows.user_id) as search_follow_count')
- rel = rel.order('search_follow_count DESC')
+ sel_str = "COUNT(follows) AS search_follow_count, #{sel_str}"
+ rel = rel.joins("LEFT JOIN follows ON follows.followable_id = bands.id")
+ .group("bands.id")
+ .order("COUNT(follows) DESC, bands.created_at DESC")
when :playing
- rel = rel.joins("inner JOIN connections ON connections.user_id = users.id")
- rel = rel.where(['connections.aasm_state != ?', 'expired'])
+ rel = rel.joins("LEFT JOIN music_sessions AS msh ON msh.band_id = bands.id")
+ .where('msh.music_session_id IS NOT NULL AND msh.session_removed_at IS NULL')
+ .order("bands.created_at DESC")
+ end
+
+ rel = rel.select(sel_str)
+ rel, page = self.relation_pagination(rel, params)
+ rel = rel.includes([{ :users => :instruments }, :genres ])
+
+ objs = rel.all
+ srch = Search.new
+ srch.search_type = :band_filter
+ srch.page_num, srch.page_count = page, objs.total_pages
+ if 1 == page && current_user.bands.present?
+ current_user.bands.order('created_at DESC').each { |bb| objs.unshift(bb) }
+ end if current_user && current_user.is_a?(User)
+ srch.band_results_for_user(objs, current_user)
end
- if !locidispid.nil? && !user.nil?
- rel = rel.order('current_scores.full_score ASC NULLS LAST')
- end
-
- rel = rel.order('users.created_at DESC')
-
- rel, page = self.relation_pagination(rel, params)
- rel = rel.includes([:instruments, :followings, :friends])
-
- # XXX: DOES THIS MEAN ALL MATCHING USERS ARE RETURNED?
- objs = rel.all
-
- srch = Search.new
- srch.search_type = :musicians_filter
- srch.page_num, srch.page_count = page, objs.total_pages
- srch.musician_results_for_user(objs, user)
end
- def self.relation_pagination(rel, params)
- perpage = [(params[:per_page] || M_PER_PAGE).to_i, 100].min
- page = [params[:page].to_i, 1].max
- [rel.paginate(:page => page, :per_page => perpage), page]
- end
+
RESULT_FOLLOW = :follows
RESULT_FRIEND = :friends
@@ -399,77 +481,7 @@ module JamRuby
false
end
- def self.new_musicians(usr, since_date)
- # this attempts to find interesting musicians to tell another musician about where interesting
- # is "has a good score and was created recently"
- # we're sort of depending upon usr being a musicians_geocoded as well...
- # this appears to only be called from EmailBatchNewMusician#deliver_batch_sets! which is
- # an offline process and thus uses the last jam location as "home base"
-
- locidispid = usr.last_jam_locidispid
- score_limit = 70
- limit = 50
-
- rel = User.musicians_geocoded
- .where(['users.created_at >= ? AND users.id != ?', since_date, usr.id])
- .joins('inner join current_scores on users.id = current_scores.a_userid')
- .where(['current_scores.b_userid = ?', usr.id])
- .where(['current_scores.full_score <= ?', score_limit])
- .order('current_scores.full_score') # best scores first
- .order('users.created_at DESC') # then most recent
- .limit(limit)
-
- objs = rel.all.to_a
- if block_given?
- yield(objs) if 0 < objs.count
- else
- return objs
- end
- end
-
- def self.band_filter(params={}, current_user=nil)
- rel = Band.scoped
-
- unless (genre = params[:genre]).blank?
- rel = Band.joins("RIGHT JOIN genre_players AS bgenres ON bgenres.player_id = bands.id AND bgenres.player_type = 'JamRuby::Band'")
- .where(['bgenres.genre_id = ? AND bands.id IS NOT NULL', genre])
- end
-
- rel = GeoIpLocations.where_latlng(rel, params, current_user)
-
- sel_str = 'bands.*'
- case ordering = self.order_param(params, B_ORDERING_KEYS)
- when :plays # FIXME: double counting?
- sel_str = "COUNT(records)+COUNT(msh) AS play_count, #{sel_str}"
- rel = rel.joins("LEFT JOIN music_sessions AS msh ON msh.band_id = bands.id")
- .joins("LEFT JOIN recordings AS records ON records.band_id = bands.id")
- .group("bands.id")
- .order("play_count DESC, bands.created_at DESC")
- when :followed
- sel_str = "COUNT(follows) AS search_follow_count, #{sel_str}"
- rel = rel.joins("LEFT JOIN follows ON follows.followable_id = bands.id")
- .group("bands.id")
- .order("COUNT(follows) DESC, bands.created_at DESC")
- when :playing
- rel = rel.joins("LEFT JOIN music_sessions AS msh ON msh.band_id = bands.id")
- .where('msh.music_session_id IS NOT NULL AND msh.session_removed_at IS NULL')
- .order("bands.created_at DESC")
- end
-
- rel = rel.select(sel_str)
- rel, page = self.relation_pagination(rel, params)
- rel = rel.includes([{ :users => :instruments }, :genres ])
-
- objs = rel.all
- srch = Search.new
- srch.search_type = :band_filter
- srch.page_num, srch.page_count = page, objs.total_pages
- if 1 == page && current_user.bands.present?
- current_user.bands.order('created_at DESC').each { |bb| objs.unshift(bb) }
- end if current_user && current_user.is_a?(User)
- srch.band_results_for_user(objs, current_user)
- end
def band_results_for_user(_results, user)
@results = _results
diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb
index f91973626..ecd0a3501 100644
--- a/ruby/lib/jam_ruby/models/user.rb
+++ b/ruby/lib/jam_ruby/models/user.rb
@@ -53,7 +53,7 @@ module JamRuby
has_many :received_friend_requests, :class_name => "JamRuby::FriendRequest", :foreign_key => 'friend_id'
# instruments
- has_many :musician_instruments, :class_name => "JamRuby::MusicianInstrument"
+ has_many :musician_instruments, :class_name => "JamRuby::MusicianInstrument", :foreign_key=> 'player_id'
has_many :instruments, :through => :musician_instruments, :class_name => "JamRuby::Instrument"
# bands
@@ -165,6 +165,11 @@ module JamRuby
# This causes the authenticate method to be generated (among other stuff)
#has_secure_password
+ has_many :online_presences, :class_name => "JamRuby::OnlinePresence", :foreign_key=> 'player_id'
+ has_many :performance_samples, :class_name => "JamRuby::PerformanceSample", :foreign_key=> 'player_id'
+
+ has_one :musician_search, :class_name => 'JamRuby::MusicianSearch'
+
before_save :create_remember_token, :if => :should_validate_password?
before_save :stringify_avatar_info , :if => :updating_avatar
@@ -359,7 +364,7 @@ module JamRuby
def age
now = Time.now.utc.to_date
- self.birth_date.nil? ? "unspecified" : now.year - self.birth_date.year - (self.birth_date.to_date.change(:year => now.year) > now ? 1 : 0)
+ self.birth_date.nil? ? "" : now.year - self.birth_date.year - (self.birth_date.to_date.change(:year => now.year) > now ? 1 : 0)
end
def session_count
@@ -651,13 +656,45 @@ module JamRuby
return recordings
end
- def update_genres(gids)
+ def update_genres(gids, genre_type)
unless self.new_record?
- GenrePlayer.delete_all(["player_id = ? AND player_type = ?",
- self.id, self.class.name])
+ GenrePlayer.delete_all(["player_id = ? AND player_type = ? AND genre_type = ?",
+ self.id, self.class.name, genre_type])
end
+
gids.each do |gid|
- self.genres << Genre.find_by_id(gid)
+ genre_player = GenrePlayer.new
+ genre_player.player_id = self.id
+ genre_player.player_type = self.class.name
+ genre_player.genre_id = gid
+ genre_player.genre_type = genre_type
+ self.genre_players << genre_player
+ end
+ end
+
+ def update_online_presences(online_presences)
+ unless self.new_record?
+ OnlinePresence.delete_all(["player_id = ?", self.id])
+ end
+
+ unless online_presences.nil?
+ online_presences.each do |op|
+ new_presence = OnlinePresence.create(self, op, false)
+ self.online_presences << new_presence
+ end
+ end
+ end
+
+ def update_performance_samples(performance_samples)
+ unless self.new_record?
+ PerformanceSample.delete_all(["player_id = ?", self.id])
+ end
+
+ unless performance_samples.nil?
+ performance_samples.each do |ps|
+ new_sample = PerformanceSample.create(self, ps, false)
+ self.performance_samples << new_sample
+ end
end
end
@@ -665,14 +702,14 @@ module JamRuby
def update_instruments(instruments)
# delete all instruments for this user first
unless self.new_record?
- MusicianInstrument.delete_all(["user_id = ?", self.id])
+ MusicianInstrument.delete_all(["player_id = ?", self.id])
end
# loop through each instrument in the array and save to the db
instruments.each do |musician_instrument_param|
instrument = Instrument.find(musician_instrument_param[:instrument_id])
musician_instrument = MusicianInstrument.new
- musician_instrument.user = self
+ musician_instrument.player = self
musician_instrument.instrument = instrument
musician_instrument.proficiency_level = musician_instrument_param[:proficiency_level]
musician_instrument.priority = musician_instrument_param[:priority]
@@ -999,7 +1036,7 @@ module JamRuby
instruments.each do |musician_instrument_param|
instrument = Instrument.find(musician_instrument_param[:instrument_id])
musician_instrument = MusicianInstrument.new
- musician_instrument.user = user
+ musician_instrument.player = user
musician_instrument.instrument = instrument
musician_instrument.proficiency_level = musician_instrument_param[:proficiency_level]
musician_instrument.priority = musician_instrument_param[:priority]
@@ -1150,13 +1187,13 @@ module JamRuby
end
unless user.new_record?
- MusicianInstrument.delete_all(["user_id = ?", user.id])
+ MusicianInstrument.delete_all(["player_id = ?", user.id])
end
instruments.each do |musician_instrument_param|
instrument = Instrument.find(musician_instrument_param[:instrument_id])
musician_instrument = MusicianInstrument.new
- musician_instrument.user = user
+ musician_instrument.player = user
musician_instrument.instrument = instrument
musician_instrument.proficiency_level = musician_instrument_param[:proficiency_level]
musician_instrument.priority = musician_instrument_param[:priority]
diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb
index 4538efbd6..90cd48c44 100644
--- a/ruby/spec/factories.rb
+++ b/ruby/spec/factories.rb
@@ -25,10 +25,10 @@ FactoryGirl.define do
before(:create) do |user, evaluator|
if evaluator.specific_instruments
evaluator.specific_instruments.each do |instrument|
- user.musician_instruments << FactoryGirl.build(:musician_instrument, user: user, instrument: instrument)
+ user.musician_instruments << FactoryGirl.build(:musician_instrument, player: user, instrument: instrument)
end
else
- user.musician_instruments << FactoryGirl.build(:musician_instrument, user: user)
+ user.musician_instruments << FactoryGirl.build(:musician_instrument, player: user)
end
end
@@ -193,7 +193,7 @@ FactoryGirl.define do
end
factory :invitation, :class => JamRuby::Invitation do
-
+
end
factory :friendship, :class => JamRuby::Friendship do
@@ -218,7 +218,7 @@ FactoryGirl.define do
band.genres << Genre.first
}
end
-
+
factory :genre, :class => JamRuby::Genre do
description { |n| "Genre #{n}" }
end
@@ -239,7 +239,7 @@ FactoryGirl.define do
end
factory :video_source, :class => JamRuby::VideoSource do
- #client_video_source_id "test_source_id"
+ #client_video_source_id "test_source_id"
sequence(:client_video_source_id) { |n| "client_video_source_id#{n}"}
end
@@ -269,7 +269,7 @@ FactoryGirl.define do
association :recording, factory: :recording
end
- factory :recorded_video, :class => JamRuby::RecordedVideo do
+ factory :recorded_video, :class => JamRuby::RecordedVideo do
sequence(:client_video_source_id) { |n| "client_video_source_id-#{n}"}
fully_uploaded true
length 1
@@ -832,4 +832,5 @@ FactoryGirl.define do
factory :affiliate_legalese, class: 'JamRuby::AffiliateLegalese' do
legalese Faker::Lorem.paragraphs(6).join("\n\n")
end
+
end
diff --git a/ruby/spec/jam_ruby/connection_manager_spec.rb b/ruby/spec/jam_ruby/connection_manager_spec.rb
index 6fce625cb..502039c1f 100644
--- a/ruby/spec/jam_ruby/connection_manager_spec.rb
+++ b/ruby/spec/jam_ruby/connection_manager_spec.rb
@@ -46,7 +46,7 @@ describe ConnectionManager, no_transaction: true do
client_id = "client_id1"
user_id = create_user("test", "user1", "user1@jamkazam.com")
user = User.find(user_id)
- user.musician_instruments << FactoryGirl.build(:musician_instrument, user: user)
+ user.musician_instruments << FactoryGirl.build(:musician_instrument, player: user)
user.save!
user = nil
diff --git a/ruby/spec/jam_ruby/models/band_filter_search_spec.rb b/ruby/spec/jam_ruby/models/band_filter_search_spec.rb
index 305cb83f0..d2114f13c 100644
--- a/ruby/spec/jam_ruby/models/band_filter_search_spec.rb
+++ b/ruby/spec/jam_ruby/models/band_filter_search_spec.rb
@@ -3,6 +3,7 @@ require 'spec_helper'
describe 'Band search' do
before(:each) do
+ Band.delete_all
@bands = []
@bands << @band1 = FactoryGirl.create(:band)
@bands << @band2 = FactoryGirl.create(:band)
@@ -31,6 +32,8 @@ describe 'Band search' do
# the ordering should be create_at since no followers exist
expect(Follow.count).to eq(0)
results = Search.band_filter({ :per_page => Band.count })
+
+ rbands = @bands.reverse
results.results.each_with_index do |uu, idx|
expect(uu.id).to eq(@bands.reverse[idx].id)
end
diff --git a/ruby/spec/jam_ruby/models/band_spec.rb b/ruby/spec/jam_ruby/models/band_spec.rb
index a74ec1bb6..706f16691 100644
--- a/ruby/spec/jam_ruby/models/band_spec.rb
+++ b/ruby/spec/jam_ruby/models/band_spec.rb
@@ -19,6 +19,21 @@ describe Band do
}
}
+ describe 'with instruments' do
+ it 'builds with instruments' do
+ band.musician_instruments << FactoryGirl.build(:musician_instrument, player: band)
+ band.musician_instruments.should have(1).items
+ band.instruments.should have(1).items
+ end
+
+ it 'creates with instruments' do
+ FactoryGirl.create(:musician_instrument, player: band)
+ band.reload
+ band.musician_instruments.should have(1).items
+ band.instruments.should have(1).items
+ end
+ end
+
describe 'website update' do
it 'should have http prefix on website url' do
band.website = 'example.com'
diff --git a/ruby/spec/jam_ruby/models/email_batch_spec_scheduled_session.rb b/ruby/spec/jam_ruby/models/email_batch_spec_scheduled_session.rb
index e93133a61..805799df0 100644
--- a/ruby/spec/jam_ruby/models/email_batch_spec_scheduled_session.rb
+++ b/ruby/spec/jam_ruby/models/email_batch_spec_scheduled_session.rb
@@ -299,7 +299,7 @@ describe EmailBatchScheduledSessions do
4.downto(1) do |nn|
uu = FactoryGirl.create(:user, :last_jam_locidispid => 6, :last_jam_addr => 6)
uu.musician_instruments << FactoryGirl.build(:musician_instrument,
- user: uu,
+ player: uu,
instrument: instruments.sample,
proficiency_level: 2)
end
diff --git a/ruby/spec/jam_ruby/models/musician_search_spec.rb b/ruby/spec/jam_ruby/models/musician_search_spec.rb
index 117222d82..da97f5eaa 100644
--- a/ruby/spec/jam_ruby/models/musician_search_spec.rb
+++ b/ruby/spec/jam_ruby/models/musician_search_spec.rb
@@ -1,79 +1,206 @@
require 'spec_helper'
+require 'time_difference'
-describe 'Musician search' do
- before(:all) do
- User.delete_all
+describe 'Musician Search Model' do
+
+ let!(:searcher) { FactoryGirl.create(:austin_user) }
+ let!(:search) { MusicianSearch.create_search(searcher) }
+
+ let!(:austin_user) { FactoryGirl.create(:austin_user) }
+ let!(:dallas_user) { FactoryGirl.create(:dallas_user) }
+ let!(:miami_user) { FactoryGirl.create(:miami_user) }
+ let!(:seattle_user) { FactoryGirl.create(:seattle_user) }
+
+ let!(:user_types) { [:austin_user, :dallas_user, :miami_user, :seattle_user] }
+
+ describe "creates search obj" do
+ before(:all) do
+ User.delete_all
+ end
+
+ it "associates to user" do
+ expect(search.user).to eq(searcher)
+ searcher.reload
+ expect(searcher.musician_search).to eq(search)
+ end
+
+ it "sets json" do
+ search.update_json_value(MusicianSearch::KEY_GIGS, MusicianSearch::GIG_COUNTS[1])
+ expect(search.json[MusicianSearch::KEY_GIGS]).to eq(MusicianSearch::GIG_COUNTS[1])
+ end
+ end
+
+ describe "filtering criteria" do
+ before(:all) do
+ User.delete_all
+ end
+ # it "filters musicians" do
+ # expect(search.do_search(per_page: User.musicians.count).count).to eq(User.musicians.count)
+ # end
+
+ describe "filters by age" do
+ before(:all) do
+ @users = []
+ today = Date.today
+ MusicianSearch::AGE_COUNTS.each_with_index do |age, idx|
+ dd = today - age.years - 1.day
+ @users << FactoryGirl.create(:austin_user, :birth_date => dd)
+ @users << FactoryGirl.create(:dallas_user, :birth_date => dd)
+ @users << FactoryGirl.create(:miami_user, :birth_date => dd)
+ @users << FactoryGirl.create(:seattle_user, :birth_date => dd)
+ end
+ end
+
+ it "filters by one age" do
+ age = MusicianSearch::AGE_COUNTS[0]
+ search.update_json_value(MusicianSearch::KEY_AGES, [age])
+ today = Date.today.to_time
+ search.do_search.all.each do |uu|
+ diff = TimeDifference.between(uu.birth_date.to_time, today).in_years
+ expect(diff).to be >= age
+ expect(diff).to be < MusicianSearch::AGE_COUNTS[1]
+ end
+ end
+
+ it "filters by multiple ages" do
+ ages = MusicianSearch::AGE_COUNTS[0..2]
+ search.update_json_value(MusicianSearch::KEY_AGES, ages[0..1])
+ today = Date.today.to_time
+ search.do_search.all.each do |uu|
+ diff = TimeDifference.between(uu.birth_date.to_time, today).in_years
+ expect(diff).to be >= ages.first
+ expect(diff).to be < ages.last
+ end
+ end
+
+ it "skips filtering by ages" do
+ search.update_json_value(MusicianSearch::KEY_AGES, [MusicianSearch::ANY_VAL_INT])
+ search.do_search.to_sql =~ /(birth_date)/
+ expect($1).to eq(nil)
+ end
+ end
+
+ describe "filtering by gig" do
+ before(:all) do
+ user_types.each do |utype|
+ FactoryGirl.create(utype, :concert_count => MusicianSearch::GIG_COUNTS[1])
+ end
+ end
+
+ it "ignores gig count if any selected" do
+ search.update_json_value(MusicianSearch::KEY_GIGS, MusicianSearch::GIG_COUNTS[0])
+ expect(search.do_search.count).to eq(User.musicians.count - 1) # searcher is musician
+ end
+
+ it "filters by gig count" do
+ search.update_json_value(MusicianSearch::KEY_GIGS, MusicianSearch::GIG_COUNTS[1])
+ expect(search.do_search.count).to eq(user_types.count)
+ end
+ end
+
+ describe "filtering by studio" do
+ before(:all) do
+ user_types.each do |utype|
+ FactoryGirl.create(utype, :studio_session_count => MusicianSearch::STUDIO_COUNTS[1])
+ end
+ end
+
+ it "ignores studio count if any selected" do
+ search.update_json_value(MusicianSearch::KEY_STUDIOS, MusicianSearch::STUDIO_COUNTS[0])
+ expect(search.do_search.count).to eq(User.musicians.count - 1)
+ end
+
+ it "filters by studio count" do
+ search.update_json_value(MusicianSearch::KEY_STUDIOS, MusicianSearch::STUDIO_COUNTS[1])
+ expect(search.do_search.count).to eq(user_types.count)
+ end
+ end
+
+ describe "filters skills" do
+ before(:all) do
+ MusicianSearch::SKILL_VALS.each do |val|
+ user_types.each { |utype| FactoryGirl.create(utype, :skill_level => val) }
+ end
+ end
+
+ it "get expected number per skill" do
+ search.update_json_value(MusicianSearch::KEY_SKILL, MusicianSearch::SKILL_VALS[1])
+ expect(search.do_search.count).to eq(user_types.count)
+ end
+ end
+
+ describe "filters interests" do
+ before(:all) do
+ MusicianSearch::INTEREST_VALS[1..-1].each do |val|
+ user_types.each { |utype| FactoryGirl.create(utype, val => true) }
+ end
+ end
+
+ it "get expected number per interest" do
+ search.update_json_value(MusicianSearch::KEY_INTERESTS, MusicianSearch::INTEREST_VALS[1])
+ expect(search.do_search.count).to eq(user_types.count)
+ end
+ end
+
+ describe "filters genres" do
+ before(:all) do
+ user_types.each do |utype|
+ uu = FactoryGirl.create(utype)
+ uu.update_genres([Genre.first.id, Genre.last.id], GenrePlayer::PROFILE)
+ end
+ end
+
+ it "gets expected number of users" do
+ search.update_json_value(MusicianSearch::KEY_GENRES, [Genre.first.id, Genre.last.id])
+ expect(search.do_search.count).to eq(user_types.count)
+ end
+ end
+
+ describe "filters instruments" do
+ before(:all) do
+ instruments = Instrument.first(user_types.count).collect do |inst|
+ { instrument_id: inst.id, proficiency_level: 2, priority: 1 }
+ end
+ user_types[0..2].each do |utype|
+ uu = FactoryGirl.create(utype)
+ uu.update_instruments(instruments)
+ end
+ end
+
+ it "gets expected number of users" do
+ instjson = [{ instrument_id: Instrument.first.id, proficiency_level: 2 },
+ { instrument_id: Instrument.first(2)[1].id, proficiency_level: 2 }
+ ]
+ search.update_json_value(MusicianSearch::KEY_INSTRUMENTS, instjson)
+ expect(search.do_search.count).to eq(3)
+ end
+ end
end
- # need a data set with actual distances
- describe "test set A" do
+ describe "sort order by distance" do
+ before(:all) do
+ User.delete_all
+ end
- before(:each) do
+ before(:each) do
create_phony_database
end
- let!(:austin_user) { FactoryGirl.create(:austin_user) }
- let!(:dallas_user) { FactoryGirl.create(:dallas_user) }
- let!(:miami_user) { FactoryGirl.create(:miami_user) }
- let!(:seattle_user) { FactoryGirl.create(:seattle_user) }
-
- describe "search on distance" do
-
- it "finds self when very local search" do
- Search.musician_filter({distance: 1, orderby: 'distance'}, austin_user).results.should == [austin_user] # just to see that distance is 0 to self
- Search.musician_filter({distance: 1, orderby: 'distance'}, dallas_user).results.should == [dallas_user] # just to see that distance is 0 to self
- Search.musician_filter({distance: 1, orderby: 'distance'}, miami_user).results.should == [miami_user] # just to see that distance is 0 to self
- Search.musician_filter({distance: 1, orderby: 'distance'}, seattle_user).results.should == [seattle_user] # just to see that distance is 0 to self
- end
-
- it "finds dallas when in range of austin" do
- expected_results = [austin_user, dallas_user]
-
- Search.musician_filter({distance: 500, orderby: 'distance'}, austin_user).results.should == expected_results
- Search.musician_filter({distance: 100, orderby: 'distance'}, austin_user).results.should == [austin_user]
- end
-
- it "finds miami when in range of austin" do
- expected_results = [austin_user, dallas_user, miami_user]
-
- Search.musician_filter({distance: 1500, orderby: 'distance'}, austin_user).results.should == expected_results
- Search.musician_filter({distance: 300, orderby: 'distance'}, austin_user).results.should == [austin_user, dallas_user]
- Search.musician_filter({distance: 100, orderby: 'distance'}, austin_user).results.should == [austin_user]
- end
-
- it "finds seattle when in range of austin" do
- expected_results = [austin_user, dallas_user, miami_user, seattle_user]
-
- Search.musician_filter({distance: 2000, orderby: 'distance'}, austin_user).results.should == expected_results
- Search.musician_filter({distance: 1500, orderby: 'distance'}, austin_user).results.should == [austin_user, dallas_user, miami_user]
- Search.musician_filter({distance: 300, orderby: 'distance'}, austin_user).results.should == [austin_user, dallas_user]
- Search.musician_filter({distance: 100, orderby: 'distance'}, austin_user).results.should == [austin_user]
- end
-
- it "finds austin & dallas by user-specified location when in range" do
- Search.musician_filter({distance: 500, orderby: 'distance', city: 'Austin', region: 'TX', country: 'US'}, austin_user).results.should == [austin_user, dallas_user]
- end
-
- it "finds dallas & austin by user-specified location when in range" do
- Search.musician_filter({distance: 500, orderby: 'distance', city: 'Dallas', region: 'TX', country: 'US'}, austin_user).results.should == [dallas_user, austin_user]
- end
-
- it "finds miami user-specified location when in range" do
- Search.musician_filter({distance: 300, orderby: 'distance', city: 'Tampa', region: 'FL', country: 'US'}, austin_user).results.should == [miami_user]
- end
-
- it "finds all users with user-specified location when in range" do
- Search.musician_filter({distance: 2500, orderby: 'distance', city: 'Tampa', region: 'FL', country: 'US'}, austin_user).results.should == [miami_user, dallas_user, austin_user, seattle_user]
- end
+ it "sorts by distance" do
+ musician_search = MusicianSearch.create_search(searcher)
+ musician_search.update_json_value(MusicianSearch::KEY_SORT_ORDER, MusicianSearch::SORT_VALS[1])
+ results = musician_search.do_search
+ expect(results[0].id).to eq(austin_user.id)
+ expect(results[1].id).to eq(dallas_user.id)
+ expect(results[2].id).to eq(miami_user.id)
+ expect(results[3].id).to eq(seattle_user.id)
end
end
- describe "test set B" do
-
+ describe "sort order by latency" do
before(:each) do
- # @geocode1 = FactoryGirl.create(:geocoder)
- # @geocode2 = FactoryGirl.create(:geocoder)
+ User.delete_all
t = Time.now - 10.minute
@user1 = FactoryGirl.create(:user, created_at: t+1.minute, last_jam_locidispid: 1)
@@ -92,13 +219,6 @@ describe 'Musician search' do
@musicians << @user5
@musicians << @user6
- @geomusicians = []
- @geomusicians << @user1
- @geomusicians << @user2
- @geomusicians << @user3
- @geomusicians << @user4
- @geomusicians << @user5
-
# from these scores:
# user1 has scores other users in user1 location, and with user2, user3, user4
# user2 has scores with users in user3 and user4 location
@@ -112,318 +232,83 @@ describe 'Musician search' do
Score.createx(2, 'b', 2, 4, 'd', 4, 70)
end
-
- context 'default filter settings' do
-
- it "finds all musicians" do
- # expects all the musicians (geocoded)
- results = Search.musician_filter({score_limit: Search::TEST_SCORE})
- results.search_type.should == :musicians_filter
- results.results.count.should == @musicians.length
- results.results.should eq @musicians.reverse
- end
-
- it "finds all musicians page 1" do
- # expects all the musicians
- results = Search.musician_filter({page: 1, score_limit: Search::TEST_SCORE})
- results.search_type.should == :musicians_filter
- results.results.count.should == @musicians.length
- results.results.should eq @musicians.reverse
- end
-
- it "finds all musicians page 2" do
- # expects no musicians (all fit on page 1)
- results = Search.musician_filter({page: 2, score_limit: Search::TEST_SCORE})
- results.search_type.should == :musicians_filter
- results.results.count.should == 0
- end
-
- it "finds all musicians page 1 per_page 3" do
- # expects three of the musicians
- results = Search.musician_filter({per_page: 3, score_limit: Search::TEST_SCORE})
- results.search_type.should == :musicians_filter
- results.results.count.should == 3
- results.results.should eq @musicians.reverse.slice(0, 3)
- end
-
- it "finds all musicians page 2 per_page 3" do
- # expects two of the musicians
- results = Search.musician_filter({page: 2, per_page: 3, score_limit: Search::TEST_SCORE})
- results.search_type.should == :musicians_filter
- results.results.count.should == 3
- results.results.should eq @musicians.reverse.slice(3, 3)
- end
-
- it "finds all musicians page 3 per_page 3" do
- # expects two of the musicians
- results = Search.musician_filter({page: 3, per_page: 3, score_limit: Search::TEST_SCORE})
- results.search_type.should == :musicians_filter
- results.results.count.should == 0
- end
-
- it "sorts musicians by followers" do
- # establish sorting order
-
- # @user4
- f1 = Follow.new
- f1.user = @user2
- f1.followable = @user4
- f1.save
-
- f2 = Follow.new
- f2.user = @user3
- f2.followable = @user4
- f2.save
-
- f3 = Follow.new
- f3.user = @user4
- f3.followable = @user4
- f3.save
-
- # @user3
- f4 = Follow.new
- f4.user = @user3
- f4.followable = @user3
- f4.save
-
- f5 = Follow.new
- f5.user = @user4
- f5.followable = @user3
- f5.save
-
- # @user2
- f6 = Follow.new
- f6.user = @user1
- f6.followable = @user2
- f6.save
-
- # @user4.followers.concat([@user2, @user3, @user4])
- # @user3.followers.concat([@user3, @user4])
- # @user2.followers.concat([@user1])
- expect(@user4.followers.count).to be 3
- expect(Follow.count).to be 6
-
- # refresh the order to ensure it works right
- f1 = Follow.new
- f1.user = @user3
- f1.followable = @user2
- f1.save
-
- f2 = Follow.new
- f2.user = @user4
- f2.followable = @user2
- f2.save
-
- f3 = Follow.new
- f3.user = @user2
- f3.followable = @user2
- f3.save
-
- # @user2.followers.concat([@user3, @user4, @user2])
- results = Search.musician_filter({:per_page => @musicians.size, score_limit: Search::TEST_SCORE, orderby: 'followed'}, @user3)
- expect(results.results[0].id).to eq(@user2.id)
-
- # check the follower count for given entry
- expect(results.results[0].search_follow_count.to_i).not_to eq(0)
- # check the follow relationship between current_user and result
- expect(results.is_follower?(@user2)).to be true
- end
-
- it 'paginates properly' do
- # make sure pagination works right
- params = {:per_page => 2, :page => 1, score_limit: Search::TEST_SCORE}
- results = Search.musician_filter(params)
- expect(results.results.count).to be 2
- end
-
+ it "sorts by latency" do
+ search.update_json_value(MusicianSearch::KEY_SORT_ORDER, MusicianSearch::SORT_VALS[0])
+ results = search.do_search
+ expect(results[0].id).to eq(@user1.id)
+ expect(results[1].id).to eq(@user2.id)
+ expect(results[2].id).to eq(@user3.id)
+ expect(results[3].id).to eq(@user4.id)
end
- def make_recording(usr)
- connection = FactoryGirl.create(:connection, :user => usr, locidispid: usr.last_jam_locidispid)
- instrument = FactoryGirl.create(:instrument, :description => 'a great instrument')
- track = FactoryGirl.create(:track, :connection => connection, :instrument => instrument)
- music_session = FactoryGirl.create(:active_music_session, :creator => usr, :musician_access => true)
- # music_session.connections << connection
- # music_session.save
- connection.join_the_session(music_session, true, nil, usr, 10)
- recording = Recording.start(music_session, usr)
- recording.stop
- recording.reload
- genre = FactoryGirl.create(:genre)
- recording.claim(usr, "name", "description", genre, true)
- recording.reload
- recording
- end
-
- def make_session(usr)
- connection = FactoryGirl.create(:connection, :user => usr, locidispid: usr.last_jam_locidispid)
- music_session = FactoryGirl.create(:active_music_session, :creator => usr, :musician_access => true)
- # music_session.connections << connection
- # music_session.save
- connection.join_the_session(music_session, true, nil, usr, 10)
- end
-
- context 'musician stat counters' do
-
- it "displays musicians top followings" do
- f1 = Follow.new
- f1.user = @user4
- f1.followable = @user4
- f1.save
-
- f2 = Follow.new
- f2.user = @user4
- f2.followable = @user3
- f2.save
-
- f3 = Follow.new
- f3.user = @user4
- f3.followable = @user2
- f3.save
-
- # @user4.followers.concat([@user4])
- # @user3.followers.concat([@user4])
- # @user2.followers.concat([@user4])
- expect(@user4.top_followings.count).to eq 3
- expect(@user4.top_followings.map(&:id)).to match_array((@musicians - [@user1, @user5, @user6]).map(&:id))
- end
-
- it "friends stat shows friend count" do
- # create friendship record
- Friendship.save(@user1.id, @user2.id)
- # search on user2
- results = Search.musician_filter({score_limit: Search::TEST_SCORE}, @user2)
- friend = results.results.detect { |mm| mm.id == @user1.id }
- expect(friend).to_not be_nil
- expect(results.friend_count(friend)).to be 1
- @user1.reload
- expect(friend.friends?(@user2)).to be true
- expect(results.is_friend?(@user1)).to be true
- end
-
- it "recording stat shows recording count" do
- Recording.delete_all
-
- recording = make_recording(@user1)
- expect(recording.users.length).to be 1
- expect(recording.users.first).to eq(@user1)
- @user1.reload
- expect(@user1.recordings.length).to be 1
- expect(@user1.recordings.first).to eq(recording)
- expect(recording.claimed_recordings.length).to be 1
- expect(@user1.recordings.detect { |rr| rr == recording }).to_not be_nil
-
- results = Search.musician_filter({score_limit: Search::TEST_SCORE}, @user1)
- # puts "====================== results #{results.inspect}"
- uu = results.results.detect { |mm| mm.id == @user1.id }
- expect(uu).to_not be_nil
-
- expect(results.record_count(uu)).to be 1
- expect(results.session_count(uu)).to be 1
- end
-
- end
-
- context 'musician sorting' do
-
- it "by plays" do
- Recording.delete_all
-
- make_recording(@user1)
- # order results by num recordings
- results = Search.musician_filter({orderby: 'plays', score_limit: Search::TEST_SCORE}, @user2)
- # puts "========= results #{results.inspect}"
- expect(results.results.length).to eq(2)
- expect(results.results[0].id).to eq(@user1.id)
- expect(results.results[1].id).to eq(@user3.id)
-
- # add more data and make sure order still correct
- make_recording(@user3)
- make_recording(@user3)
- results = Search.musician_filter({:orderby => 'plays', score_limit: Search::TEST_SCORE}, @user2)
- expect(results.results.length).to eq(2)
- expect(results.results[0].id).to eq(@user3.id)
- expect(results.results[1].id).to eq(@user1.id)
- end
-
- it "by now playing" do
- pending "these tests worked, so leaving them in, but we don't currently have 'Now Playing' in the find musicians screen"
- # should get 1 result with 1 active session
- make_session(@user1)
- results = Search.musician_filter({:orderby => 'playing', score_limit: Search::TEST_SCORE}, @user2)
- expect(results.results.count).to be 1
- expect(results.results.first.id).to eq(@user1.id)
-
- # should get 2 results with 2 active sessions
- # sort order should be created_at DESC
- make_session(@user3)
- results = Search.musician_filter({:orderby => 'playing', score_limit: Search::TEST_SCORE}, @user2)
- expect(results.results.count).to be 2
- expect(results.results[0].id).to eq(@user3.id)
- expect(results.results[1].id).to eq(@user1.id)
- end
-
- end
-
- context 'filter settings' do
- it "searches musicians for an instrument" do
- minst = FactoryGirl.create(:musician_instrument, {
- :user => @user1,
- :instrument => Instrument.find('tuba')})
- @user1.musician_instruments << minst
- @user1.reload
- ii = @user1.instruments.detect { |inst| inst.id == 'tuba' }
- expect(ii).to_not be_nil
- results = Search.musician_filter({:instrument => ii.id, score_limit: Search::TEST_SCORE}, @user2)
- results.results.each do |rr|
- expect(rr.instruments.detect { |inst| inst.id=='tuba' }.id).to eq(ii.id)
- end
- expect(results.results.count).to be 1
- end
- end
-
- context 'new users' do
-
- it "find three for user1" do
- # user2..4 are scored against user1
- ms = Search.new_musicians(@user1, Time.now - 1.week)
- ms.should_not be_nil
- ms.length.should == 3
- ms.should eq [@user2, @user3, @user4]
- end
-
- it "find two for user2" do
- # user1,3,4 are scored against user1, but user4 is bad
- ms = Search.new_musicians(@user2, Time.now - 1.week)
- ms.should_not be_nil
- ms.length.should == 2
- ms.should eq [@user3, @user1]
- end
-
- it "find two for user3" do
- # user1..2 are scored against user3
- ms = Search.new_musicians(@user3, Time.now - 1.week)
- ms.should_not be_nil
- ms.length.should == 2
- ms.should eq [@user2, @user1]
- end
-
- it "find one for user4" do
- # user1..2 are scored against user4, but user2 is bad
- ms = Search.new_musicians(@user4, Time.now - 1.week)
- ms.should_not be_nil
- ms.length.should == 1
- ms.should eq [@user1]
- end
-
- it "find none for user5" do
- # user1..4 are not scored against user5
- ms = Search.new_musicians(@user5, Time.now - 1.week)
- ms.should_not be_nil
- ms.length.should == 0
- end
-
- end
end
+
+ describe "produces accurate query description" do
+ before(:all) do
+ User.delete_all
+ end
+
+ it 'has default description' do
+ expect(search.description).to match(/^Click search button to look for musicians/)
+ end
+ it 'has correct sort order description' do
+ search.update_json_value(MusicianSearch::KEY_SORT_ORDER, MusicianSearch::SORT_VALS[1])
+ str = MusicianSearch::SORT_ORDERS[search.json_value(MusicianSearch::KEY_SORT_ORDER)]
+ expect(search.description).to match(/^Current Search: Sort = #{str}$/)
+ end
+
+ it 'has correct description for single-valued selections' do
+ selections = [{
+ key: MusicianSearch::KEY_INTERESTS,
+ value: MusicianSearch::INTEREST_VALS[1],
+ lookup: MusicianSearch::INTERESTS,
+ description: 'Interest'
+ },
+ {
+ key: MusicianSearch::KEY_SKILL,
+ value: MusicianSearch::SKILL_VALS[1],
+ lookup: MusicianSearch::SKILL_LEVELS,
+ description: 'Skill'
+ },
+ {
+ key: MusicianSearch::KEY_STUDIOS,
+ value: MusicianSearch::STUDIO_COUNTS[1],
+ lookup: MusicianSearch::STUDIOS_LABELS,
+ description: 'Studio Sessions'
+ },
+ {
+ key: MusicianSearch::KEY_GIGS,
+ value: MusicianSearch::GIG_COUNTS[1],
+ lookup: MusicianSearch::GIG_LABELS,
+ description: 'Concert Gigs'
+ }]
+ selections.each do |hash|
+ search.update_json_value(hash[:key], hash[:value])
+ json_val = search.json_value(hash[:key])
+ expect(search.description).to match(/; #{hash[:description]} = #{hash[:lookup][json_val]}/)
+ end
+ end
+
+ it 'has correct description for genres' do
+ search.update_json_value(MusicianSearch::KEY_GENRES, [Genre.first.id, Genre.last.id])
+ expect(search.description).to match(/; Genres = #{Genre.first.description}, #{Genre.last.description}/)
+ end
+
+ it 'has correct description for ages' do
+ search.update_json_value(MusicianSearch::KEY_AGES, [MusicianSearch::AGE_COUNTS[0],MusicianSearch::AGE_COUNTS[1]])
+ expect(search.description).to match(/; Ages = #{MusicianSearch::AGES[MusicianSearch::AGE_COUNTS[0]]}, #{MusicianSearch::AGES[MusicianSearch::AGE_COUNTS[1]]}/)
+ end
+
+ it 'has correct description for instruments' do
+ instrs = Instrument.limit(2).order(:description)
+ instjson = [{ instrument_id: instrs[0].id, proficiency_level: 2 },
+ { instrument_id: instrs[1].id, proficiency_level: 1 }
+ ]
+ search.update_json_value(MusicianSearch::KEY_INSTRUMENTS, instjson)
+ instr_descrip = "#{instrs[0].description} (#{MusicianSearch::INSTRUMENT_PROFICIENCY[2]}), #{instrs[1].description} (#{MusicianSearch::INSTRUMENT_PROFICIENCY[1]})"
+ expect(search.description).to match(/; Instruments = #{Regexp.escape(instr_descrip)}/)
+ end
+
+ end
end
diff --git a/ruby/spec/jam_ruby/models/online_presence_spec.rb b/ruby/spec/jam_ruby/models/online_presence_spec.rb
new file mode 100644
index 000000000..883917f25
--- /dev/null
+++ b/ruby/spec/jam_ruby/models/online_presence_spec.rb
@@ -0,0 +1,173 @@
+require 'spec_helper'
+
+describe OnlinePresence do
+
+ shared_examples_for :online_presence_specs do
+ describe "index" do
+
+ before(:all) do
+ OnlinePresence.delete_all
+
+ player1_presence1 = OnlinePresence.new({:player_id => player1.id, :username => "myonlineusername", :service_type => "facebook"})
+ player1_presence1.save!
+
+ player1_presence2 = OnlinePresence.new({:player_id => player1.id, :username => "myonlineusername", :service_type => "twitter"})
+ player1_presence2.save!
+
+ player2_presence1 = OnlinePresence.new({:player_id => player2.id, :username => "myonlineusername", :service_type => "soundcloud"})
+ player2_presence1.save!
+ end
+
+ context "when request is valid" do
+ it "should return all records for user" do
+ presence = OnlinePresence.index({:id => player1.id})
+ presence.count.should == 2
+
+ presence = OnlinePresence.index({:id => player2.id})
+ presence.count.should == 1
+ end
+ end
+
+ context "when request is invalid" do
+ it "should raise error when options are missing" do
+ lambda{OnlinePresence.index}.should raise_error(StateError)
+ end
+
+ it "should raise error when user id is missing" do
+ lambda{OnlinePresence.index({:id => ""})}.should raise_error(StateError)
+ end
+ end
+ end
+
+ describe "create" do
+
+ before(:all) do
+ OnlinePresence.delete_all
+ end
+
+ context "when request is valid" do
+ it "should save successfully" do
+ OnlinePresence.create(player1, {:player_id => player1.id, :service_type => "soundcloud", :username => "soundcloudplayer1"})
+
+ # make sure we can save a second OnlinePresence for same user and type
+ OnlinePresence.create(player1, {:player_id => player1.id, :service_type => "soundcloud", :username => "soundcloudplayer2"})
+
+ OnlinePresence.index({:id => player1.id}).count.should == 2
+ end
+ end
+
+ context "when request is not valid" do
+ it "should raise JamPermissionError if requester id does not match id in request" do
+ lambda{OnlinePresence.create(player1, {:player_id => player2.id, :service_type => "soundcloud", :username => "soundcloudplayer2"})}.should raise_error(JamPermissionError)
+ end
+
+ it "should raise error if service type is missing" do
+ lambda{OnlinePresence.create(player1, {:player_id => player1.id, :username => "soundcloudplayer2"})}.should raise_error(StateError)
+ end
+
+ it "should raise error if username is missing" do
+ lambda{OnlinePresence.create(player1, {:player_id => player1.id, :service_type => "soundcloud"})}.should raise_error(StateError)
+ end
+
+ it "should not allow duplicates of the same username / service type combination" do
+ OnlinePresence.create(player1, {:player_id => player1.id, :service_type => "soundcloud", :username => "soundcloudplayer1"})
+ OnlinePresence.index({:id => player1.id}).count.should == 1
+
+ lambda{OnlinePresence.create(player1, {:player_id => player1.id, :service_type => "soundcloud", :username => "soundcloudplayer1"})}.should raise_error(ConflictError)
+ OnlinePresence.index({:id => player1.id}).count.should == 1
+ end
+ end
+ end
+
+ describe "update" do
+
+ before(:all) do
+ OnlinePresence.delete_all
+ @online_presence = OnlinePresence.new(:player_id => player1.id, :service_type => "soundcloud", :username => "soundcloudplayer1")
+ @online_presence.save!
+ end
+
+ context "when request is valid" do
+ it "should save successfully" do
+
+ up_list = OnlinePresence.index({:id => player1.id})
+ up_list.count.should == 1
+ up_list.first.service_type.should == "soundcloud"
+ up_list.first.username.should == "soundcloudplayer1"
+
+ OnlinePresence.update(player1, {:id => @online_presence.id, :player_id => player1.id, :service_type => "soundcloud", :username => "soundcloudplayer2"})
+
+ up_list = OnlinePresence.index({:id => player1.id})
+ up_list.count.should == 1
+ up_list.first.service_type.should == "soundcloud"
+ up_list.first.username.should == "soundcloudplayer2"
+ end
+ end
+
+ context "when request is not valid" do
+ it "should raise JamPermissionError if requester id does not match id in request" do
+ lambda{OnlinePresence.update(player1, {:player_id => player2.id, :id => @online_presence.id, :service_type => "soundcloud", :username => "soundcloudplayer2"})}.should raise_error(JamPermissionError)
+ end
+
+ it "should raise error if type is missing" do
+ lambda{OnlinePresence.update(player1, {:player_id => player1.id, :id => @online_presence.id, :username => "soundcloudplayer2"})}.should raise_error(StateError)
+ end
+
+ it "should raise error if username is missing" do
+ lambda{OnlinePresence.update(player1, {:player_id => player1.id, :id => @online_presence.id, :service_type => "soundcloud"})}.should raise_error(StateError)
+ end
+
+ it "should raise error if player presence id is missing" do
+ lambda{OnlinePresence.update(player1, {:player_id => player1.id, :username => "soundcloudplayer2", :service_type => "soundcloud"})}.should raise_error(StateError)
+ end
+ end
+ end
+
+ describe "destroy" do
+
+ before(:all) do
+ OnlinePresence.delete_all
+ @online_presence = OnlinePresence.new(:player_id => player1.id, :service_type => "soundcloud", :username => "soundcloudplayer1")
+ @online_presence.save!
+ end
+
+ context "when request is valid" do
+ it "should destroy successfully" do
+ up_list = OnlinePresence.index({:id => player1.id})
+ up_list.count.should == 1
+ up_list.first.service_type.should == "soundcloud"
+ up_list.first.username.should == "soundcloudplayer1"
+
+ OnlinePresence.delete(player1, {:player_id => player1.id, :id => @online_presence.id})
+
+ up_list = OnlinePresence.index({:id => player1.id})
+ up_list.count.should == 0
+ end
+ end
+
+ context "when request is not valid" do
+ it "should raise JamPermissionError if requester id does not match id in request" do
+ lambda{OnlinePresence.delete(player2, {:player_id => player1.id, :id => @online_presence.id})}.should raise_error(JamPermissionError)
+ end
+
+ it "should raise error if player presence id is missing" do
+ lambda{OnlinePresence.delete(player1, {:player_id => player1.id})}.should raise_error(StateError)
+ end
+ end
+ end
+ end # shared
+
+ describe "with a user" do
+ it_should_behave_like :online_presence_specs do
+ let(:player1) { FactoryGirl.create(:user) }
+ let(:player2) { FactoryGirl.create(:user) }
+ end
+ end
+
+ describe "with a band" do
+ it_should_behave_like :online_presence_specs do
+ let(:player1) { FactoryGirl.create(:band) }
+ let(:player2) { FactoryGirl.create(:band) }
+ end
+ end
+end
\ No newline at end of file
diff --git a/ruby/spec/jam_ruby/models/performance_sample_spec.rb b/ruby/spec/jam_ruby/models/performance_sample_spec.rb
new file mode 100644
index 000000000..5cf592a2b
--- /dev/null
+++ b/ruby/spec/jam_ruby/models/performance_sample_spec.rb
@@ -0,0 +1,136 @@
+require 'spec_helper'
+
+describe PerformanceSample do
+
+ shared_examples_for :performance_sample_specs do
+ let(:claimed_recording) { FactoryGirl.create(:claimed_recording) }
+
+ describe "index" do
+
+ before(:all) do
+ PerformanceSample.delete_all
+
+ @player1_sample1 = PerformanceSample.new(:player_id => player1.id, :service_type => "jamkazam", :claimed_recording_id => claimed_recording.id)
+ @player1_sample1.save!
+
+ @player1_sample2 = PerformanceSample.new(:player_id => player1.id, :service_type => "youtube", :service_id => "12345")
+ @player1_sample2.save!
+
+ @player2_sample1 = PerformanceSample.new(:player_id => player2.id, :service_type => "soundcloud", :service_id => "67890")
+ @player2_sample1.save!
+ end
+
+ context "when request is valid" do
+ it "should return all records for user" do
+ sample = PerformanceSample.index({:id => player1.id})
+ sample.count.should == 2
+
+ sample = PerformanceSample.index({:id => player2.id})
+ sample.count.should == 1
+ end
+ end
+
+ context "when request is invalid" do
+ it "should raise error when options are missing" do
+ lambda{PerformanceSample.index}.should raise_error(JamArgumentError)
+ end
+
+ it "should raise error when user id is missing" do
+ lambda{PerformanceSample.index({:id => ""})}.should raise_error(JamArgumentError)
+ end
+ end
+ end
+
+ describe "create" do
+
+ before(:all) do
+ PerformanceSample.delete_all
+ end
+
+ context "when request is valid" do
+ it "should save successfully" do
+ PerformanceSample.create(player1, {:player_id => player1.id, :service_type => "youtube", :service_id => "12345"})
+
+ # make sure we can save a second PerformanceSample for same user and type
+ PerformanceSample.create(player1, {:player_id => player1.id, :service_type => "youtube", :service_id => "67890"})
+ end
+ end
+
+ context "when request is not valid" do
+ it "should raise JamPermissionError if requester id does not match id in request" do
+ lambda{PerformanceSample.create(player1, {:player_id => player2.id, :service_type => "soundcloud", :service_id => "12345"})}.should raise_error(JamPermissionError)
+ end
+
+ it "should raise error if service type is missing" do
+ lambda{PerformanceSample.create(player1, {:player_id => player1.id, :service_id => "12345"})}.should raise_error(StateError)
+ end
+
+ it "should raise error if service id is missing for non-JamKazam sample" do
+ lambda{PerformanceSample.create(player1, {:player_id => player1.id, :service_type => "youtube"})}.should raise_error(StateError)
+ end
+
+ it "should raise error if recording id is missing for JamKazam sample" do
+ lambda{PerformanceSample.create(player1, {:player_id => player1.id, :service_type => "jamkazam"})}.should raise_error(StateError)
+ end
+
+ it "should not allow duplicate type/service id combination for non-JamKazam sample" do
+ PerformanceSample.create(player1, {:player_id => player1.id, :service_type => "youtube", :service_id => "12345"})
+ lambda{PerformanceSample.create(player1, {:player_id => player1.id, :service_type => "youtube", :service_id => "12345"})}.should raise_error(ConflictError)
+ end
+
+ it "should not allow duplicate type/recording id combination for JamKazam sample" do
+ PerformanceSample.create(player1, {:player_id => player1.id, :service_type => "jamkazam", :claimed_recording_id => claimed_recording.id})
+ lambda{PerformanceSample.create(player1, {:player_id => player1.id, :service_type => "jamkazam", :claimed_recording_id => claimed_recording.id})}.should raise_error(ConflictError)
+ end
+ end
+ end
+
+
+ describe "destroy" do
+
+ before(:all) do
+ PerformanceSample.delete_all
+ @user_sample = PerformanceSample.new(:player_id => player1.id, :service_type => "soundcloud", :service_id => "12345")
+ @user_sample.save!
+ end
+
+ context "when request is valid" do
+ it "should destroy successfully" do
+ ps_list = PerformanceSample.index({:id => player1.id})
+ ps_list.count.should == 1
+ ps_list.first.service_type.should == "soundcloud"
+ ps_list.first.service_id.should == "12345"
+
+ PerformanceSample.delete(player1, {:player_id => player1.id, :id => @user_sample.id})
+
+ ps_list = PerformanceSample.index({:id => player1.id})
+ ps_list.count.should == 0
+ end
+ end
+
+ context "when request is not valid" do
+ it "should raise JamPermissionError if requester id does not match id in request" do
+ lambda{PerformanceSample.delete(player2, {:player_id => player1.id, :id => @user_sample.id})}.should raise_error(JamPermissionError)
+ end
+
+ it "should raise error if user sample id is missing" do
+ lambda{PerformanceSample.delete(player1, {:player_id => player1.id})}.should raise_error(StateError)
+ end
+ end
+ end
+ end
+
+ describe "with a user" do
+ it_should_behave_like :performance_sample_specs do
+ let(:player1) { FactoryGirl.create(:user) }
+ let(:player2) { FactoryGirl.create(:user) }
+ end
+ end
+
+ describe "with a band" do
+ it_should_behave_like :performance_sample_specs do
+ let(:player1) { FactoryGirl.create(:band) }
+ let(:player2) { FactoryGirl.create(:band) }
+ end
+ end
+end
\ No newline at end of file
diff --git a/ruby/spec/jam_ruby/models/user_spec.rb b/ruby/spec/jam_ruby/models/user_spec.rb
index 1d6b0f40f..0fb434442 100644
--- a/ruby/spec/jam_ruby/models/user_spec.rb
+++ b/ruby/spec/jam_ruby/models/user_spec.rb
@@ -9,7 +9,7 @@ describe User do
User.delete_all
@user = User.new(first_name: "Example", last_name: "User", email: "user@example.com",
password: "foobar", password_confirmation: "foobar", city: "Apex", state: "NC", country: "US", terms_of_service: true, musician: true)
- @user.musician_instruments << FactoryGirl.build(:musician_instrument, user: @user)
+ @user.musician_instruments << FactoryGirl.build(:musician_instrument, player: @user)
@recurly = RecurlyClient.new
end
@@ -701,13 +701,16 @@ describe User do
describe "age" do
let(:user) {FactoryGirl.create(:user)}
-
+
it "should calculate age based on birth_date" do
user.birth_date = Time.now - 10.years
user.age.should == 10
user.birth_date = Time.now - 10.years + 3.months
user.age.should == 9
+
+ user.birth_date = nil
+ user.age.should == ""
end
end
diff --git a/ruby/spec/mailers/render_emails_spec.rb b/ruby/spec/mailers/render_emails_spec.rb
index 4439671e6..dfc84cef3 100644
--- a/ruby/spec/mailers/render_emails_spec.rb
+++ b/ruby/spec/mailers/render_emails_spec.rb
@@ -98,13 +98,13 @@ describe "RenderMailers", :slow => true do
BatchMailer.deliveries.clear
scheduled_batch.reset!
- drummer.musician_instruments << FactoryGirl.build(:musician_instrument, user: drummer, instrument: drums, proficiency_level: 2)
- drummer.musician_instruments << FactoryGirl.build(:musician_instrument, user: drummer, instrument: guitar, proficiency_level: 2)
+ drummer.musician_instruments << FactoryGirl.build(:musician_instrument, player: drummer, instrument: drums, proficiency_level: 2)
+ drummer.musician_instruments << FactoryGirl.build(:musician_instrument, player: drummer, instrument: guitar, proficiency_level: 2)
- guitarist.musician_instruments << FactoryGirl.build(:musician_instrument, user: guitarist, instrument: guitar, proficiency_level: 2)
- guitarist.musician_instruments << FactoryGirl.build(:musician_instrument, user: guitarist, instrument: bass, proficiency_level: 2)
+ guitarist.musician_instruments << FactoryGirl.build(:musician_instrument, player: guitarist, instrument: guitar, proficiency_level: 2)
+ guitarist.musician_instruments << FactoryGirl.build(:musician_instrument, player: guitarist, instrument: bass, proficiency_level: 2)
- vocalist.musician_instruments << FactoryGirl.build(:musician_instrument, user: vocalist, instrument: vocals, proficiency_level: 2)
+ vocalist.musician_instruments << FactoryGirl.build(:musician_instrument, player: vocalist, instrument: vocals, proficiency_level: 2)
FactoryGirl.create(:rsvp_slot, :instrument => drums, :music_session => session1)
FactoryGirl.create(:rsvp_slot, :instrument => guitar, :music_session => session1)
diff --git a/web/Gemfile b/web/Gemfile
index d7975e309..00e74d2a6 100644
--- a/web/Gemfile
+++ b/web/Gemfile
@@ -53,6 +53,7 @@ gem 'sendgrid', '1.2.0'
gem 'filepicker-rails', '0.1.0'
gem 'aws-sdk', '~> 1'
gem 'aasm', '3.0.16'
+gem 'carmen'
gem 'carrierwave', '0.9.0'
gem 'carrierwave_direct'
gem 'fog'
@@ -105,8 +106,10 @@ group :development, :test do
gem 'execjs', '1.4.0'
gem 'factory_girl_rails', '4.1.0' # in dev because in use by rake task
gem 'database_cleaner', '1.3.0' #in dev because in use by rake task
-# gem 'teaspoon'
-# gem 'teaspoon-jasmine'
+
+ # gem 'teaspoon'
+ # gem 'teaspoon-jasmine'
+ # gem 'puma'
end
group :unix do
gem 'therubyracer' #, '0.11.0beta8'
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 79b8389fe..9938d4cf0 100644
--- a/web/app/assets/javascripts/accounts_profile.js
+++ b/web/app/assets/javascripts/accounts_profile.js
@@ -1,595 +1,509 @@
(function(context,$) {
- "use strict";
+ "use strict";
- context.JK = context.JK || {};
- context.JK.AccountProfileScreen = function(app) {
- var $document = $(document);
- var logger = context.JK.logger;
- var EVENTS = context.JK.EVENTS;
- var api = context.JK.Rest();
- var userId;
- var user = {};
- var selectLocation = null;
- var recentUserDetail = null;
- var loadingCitiesData = false;
- var loadingRegionsData = false;
- var loadingCountriesData = false;
- var nilOptionStr = '';
- var nilOptionText = 'n/a';
+ context.JK = context.JK || {};
+ context.JK.AccountProfileScreen = function(app) {
+ var $document = $(document);
+ var logger = context.JK.logger;
+ var EVENTS = context.JK.EVENTS;
+ var api = context.JK.Rest();
+ var userId;
+ var user = {};
+ var selectLocation = null;
+ var recentUserDetail = null;
+ var loadingCitiesData = false;
+ var loadingRegionsData = false;
+ var loadingCountriesData = false;
+ var nilOptionStr = '';
+ var nilOptionText = 'n/a';
+ var $screen = $('#account-profile-basics');
+ var $avatar = $screen.find('#avatar');
+ var $country = $screen.find('#country');
+ var $region = $screen.find('#region');
+ var $city = $screen.find('#city');
+ var $firstName = $screen.find('#first-name');
+ var $lastName = $screen.find('#last-name');
+ var $gender = $screen.find('#gender');
+ var $biography = $screen.find('#biography');
+ var $subscribe = $screen.find('#subscribe');
- function beforeShow(data) {
- userId = data.id;
+ var $btnCancel = $screen.find('.account-edit-profile-cancel');
+ var $btnSubmit = $screen.find('.account-edit-profile-submit');
+
+ function beforeShow(data) {
+ userId = data.id;
+ }
+
+ function afterShow(data) {
+ resetForm();
+ renderAccountProfile();
+ }
+
+ function resetForm() {
+ // remove all display errors
+ $('#account-profile-content-scroller form .error-text').remove()
+ $('#account-profile-content-scroller form .error').removeClass("error")
+ }
+
+ function populateAccountProfile(userDetail) {
+
+ $avatar.attr('src', context.JK.resolveAvatarUrl(userDetail.photo_url));
+ $country.val(userDetail.country);
+ $region.val(userDetail.state);
+ $city.val(userDetail.city);
+ $firstName.val(userDetail.first_name);
+ $lastName.val(userDetail.last_name);
+ $gender.val(userDetail.gender);
+ $biography.val(userDetail.biography);
+
+ if (userDetail.subscribe_email) {
+ $subscribe.attr('checked', 'checked');
+ }
+
+ var content_root = $('#account-profile-content-scroller');
+
+ // set birth_date
+ if(userDetail.birth_date) {
+ var birthDateFields = userDetail.birth_date.split('-')
+ var birthDateYear = birthDateFields[0];
+ var birthDateMonth = birthDateFields[1];
+ var birthDateDay = birthDateFields[2];
+
+ $('select#user_birth_date_1i', content_root).val(parseInt(birthDateYear));
+ $('select#user_birth_date_2i', content_root).val(parseInt(birthDateMonth));
+ $('select#user_birth_date_3i', content_root).val(parseInt(birthDateDay));
+ }
+
+ context.JK.dropdown($('select', content_root));
+ }
+
+ function populateAccountProfileLocation(userDetail, regions, cities) {
+ populateRegions(regions, userDetail.state);
+ populateCities(cities, userDetail.city);
+ }
+
+ function populateCountries(countries, userCountry) {
+
+ // countries has the format ["US", ...]
+ var foundCountry = false;
+ var countrySelect = getCountryElement();
+ countrySelect.children().remove();
+
+ var nilOption = $(nilOptionStr);
+ nilOption.text(nilOptionText);
+ countrySelect.append(nilOption);
+
+ $.each(countries, function(index, country) {
+ if(!country) return;
+
+ var option = $(nilOptionStr);
+ option.text(country);
+ option.attr("value", country);
+
+ if(country == userCountry) {
+ foundCountry = true;
}
- function afterShow(data) {
- resetForm();
- renderAccountProfile();
+ countrySelect.append(option);
+ });
+
+ if(!foundCountry) {
+ // in this case, the user has a country that is not in the database
+ // this can happen in a development/test scenario, but let's assume it can
+ // happen in production too.
+ var option = $(nilOptionStr);
+ option.text(userCountry);
+ option.attr("value", userCountry);
+ countrySelect.append(option);
+ }
+
+ countrySelect.val(userCountry);
+ countrySelect.attr("disabled", null)
+
+ context.JK.dropdown(countrySelect);
+ }
+
+
+ function populateCountriesx(countriesx, userCountry) {
+
+ // countriesx has the format [{countrycode: "US", countryname: "United States"}, ...]
+
+ var foundCountry = false;
+ var countrySelect = getCountryElement();
+ countrySelect.children().remove();
+
+ var nilOption = $(nilOptionStr);
+ nilOption.text(nilOptionText);
+ countrySelect.append(nilOption);
+
+ $.each(countriesx, function(index, countryx) {
+ if (!countryx.countrycode) return;
+
+ var option = $(nilOptionStr);
+ option.text(countryx.countryname);
+ option.attr("value", countryx.countrycode);
+
+ if(countryx.countrycode == userCountry) {
+ foundCountry = true;
}
- function resetForm() {
- // remove all display errors
- $('#account-profile-content-scroller form .error-text').remove()
- $('#account-profile-content-scroller form .error').removeClass("error")
- }
+ countrySelect.append(option);
+ });
- function populateAccountProfile(userDetail, instruments) {
- var template = context.JK.fillTemplate($('#template-account-profile').html(), {
- country: userDetail.country,
- region: userDetail.state,
- city: userDetail.city,
- first_name: userDetail.first_name,
- last_name: userDetail.last_name,
- photoUrl: context.JK.resolveAvatarUrl(userDetail.photo_url),
- user_instruments: userDetail.instruments,
- birth_date : userDetail.birth_date,
- gender: userDetail.gender,
- subscribe_email: userDetail.subscribe_email ? "checked=checked" : ""
- });
+ if(!foundCountry) {
+ // in this case, the user has a country that is not in the database
+ // this can happen in a development/test scenario, but let's assume it can
+ // happen in production too.
+ var option = $(nilOptionStr);
+ option.text(userCountry);
+ option.attr("value", userCountry);
+ countrySelect.append(option);
+ }
- var content_root = $('#account-profile-content-scroller')
- content_root.html(template);
+ countrySelect.val(userCountry);
+ countrySelect.attr("disabled", null);
- // now use javascript to fix up values too hard to do with templating
- // set gender
- $('select[name=gender]', content_root).val(userDetail.gender)
+ context.JK.dropdown(countrySelect);
+ }
- // set birth_date
- if(userDetail.birth_date) {
- var birthDateFields = userDetail.birth_date.split('-')
- var birthDateYear = birthDateFields[0];
- var birthDateMonth = birthDateFields[1];
- var birthDateDay = birthDateFields[2];
+ function populateRegions(regions, userRegion) {
+ var regionSelect = getRegionElement()
+ regionSelect.children().remove()
- $('select#user_birth_date_1i', content_root).val(parseInt(birthDateYear));
- $('select#user_birth_date_2i', content_root).val(parseInt(birthDateMonth));
- $('select#user_birth_date_3i', content_root).val(parseInt(birthDateDay));
- }
+ var nilOption = $(nilOptionStr);
+ nilOption.text(nilOptionText);
+ regionSelect.append(nilOption);
- loadGenres(userDetail.genres);
+ $.each(regions, function(index, region) {
+ if(!region) return;
- // update instruments
- $.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
- })
- $('.instrument_selector', content_root).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', content_root).val(userInstrument.proficiency_level)
- })
- }
+ var option = $(nilOptionStr);
+ option.text(region['name']);
+ option.attr("value", region['region']);
- context.JK.dropdown($('select', content_root));
- }
+ regionSelect.append(option);
+ })
- function isUserInstrument(instrument, userInstruments) {
- var isUserInstrument = false;
- if(!userInstruments) return false;
+ regionSelect.val(userRegion)
+ regionSelect.attr("disabled", null)
- $.each(userInstruments, function(index, userInstrument) {
- if(instrument.id == userInstrument.instrument_id) {
- isUserInstrument = true;
- return false;
- }
- })
- return isUserInstrument;
- }
+ context.JK.dropdown(regionSelect);
+ }
- function loadGenres(selectedGenres) {
- $("#user-genres").empty();
+ function populateCities(cities, userCity) {
+ var citySelect = getCityElement();
+ citySelect.children().remove();
- rest.getGenres().done(function (genres) {
- $.each(genres, function (index, genre) {
- var genreTemplate = $('#template-user-setup-genres').html();
- var selected = '';
- if (selectedGenres) {
- var genreMatch = $.grep(selectedGenres, function (n, i) {
- return n.id === genre.id;
- });
- if (genreMatch.length > 0) {
- selected = "checked";
- }
- }
- var genreHtml = context.JK.fillTemplate(genreTemplate, {
- id: genre.id,
- description: genre.description,
- checked: selected
- });
+ var nilOption = $(nilOptionStr);
+ nilOption.text(nilOptionText);
+ citySelect.append(nilOption);
- $('#user-genres').append(genreHtml);
+ $.each(cities, function(index, city) {
+ if(!city) return;
+
+ var option = $(nilOptionStr);
+ option.text(city);
+ option.attr("value", city);
+
+ citySelect.append(option);
+ });
+
+ citySelect.val(userCity);
+ citySelect.attr("disabled", null);
+
+ context.JK.dropdown(citySelect);
+ }
+
+ /****************** MAIN PORTION OF SCREEN *****************/
+ // events for main screen
+ function events() {
+ $btnCancel.click(function(evt) {
+ evt.stopPropagation();
+ navToAccount();
+ return false;
+ });
+
+ $('#account-profile-content-scroller').on('click', '#account-change-avatar', function(evt) { evt.stopPropagation(); navToAvatar(); return false; } );
+
+ enableSubmits();
+ }
+
+ function renderAccountProfile() {
+ $.when(api.getUserProfile())
+ .done(function(userDetail) {
+ recentUserDetail = userDetail;
+ populateAccountProfile(userDetail);
+
+ selectLocation = new context.JK.SelectLocation(getCountryElement(), getRegionElement(), getCityElement(), app);
+ selectLocation.load(userDetail.country, userDetail.state, userDetail.city)
});
- });
- }
- function resetGenres() {
- $('input[type=checkbox]:checked', '#user-genres').each(function (i) {
- $(this).removeAttr("checked");
- });
- var $tdGenres = $("#tdBandGenres");
+
+ context.JK.dropdown($('select'));
}
- function getSelectedGenres() {
- var genres = [];
- $('input[type=checkbox]:checked', '#user-genres').each(function (i) {
- var genre = $(this).val();
- genres.push(genre);
- });
- return genres;
+ function navToAccount() {
+ resetForm();
+ window.location = '/client#/profile/' + context.JK.currentUserId;
}
- function populateAccountProfileLocation(userDetail, regions, cities) {
- populateRegions(regions, userDetail.state);
- populateCities(cities, userDetail.city);
+ function navToAvatar() {
+ resetForm();
+ window.location = '/client#/account/profile/avatar';
+ }
+
+ function handleUpdateProfile() {
+ disableSubmits();
+ resetForm();
+
+ var country = getCountryElement().val();
+ var region = getRegionElement().val();
+ var city = getCityElement().val();
+ var firstName = getFirstNameElement().val();
+ var lastName = getLastNameElement().val();
+ var gender = getGenderElement().val();
+ var subscribeEmail = getSubscribeEmail().is(':checked');
+ var birthDate = getBirthDate();
+ var biography = $biography.val();
+
+ api.updateUser({
+ country: country,
+ state: region,
+ city: city,
+ first_name: firstName,
+ last_name: lastName,
+ gender: gender,
+ birth_date: birthDate,
+ biography: biography,
+ subscribe_email: subscribeEmail
+ })
+ .done(postUpdateProfileSuccess)
+ .fail(postUpdateProfileFailure)
+ .always(enableSubmits)
+ }
+
+ function enableSubmits() {
+ $btnSubmit.click(function(evt) {
+ evt.stopPropagation();
+ handleUpdateProfile();
+ return false;
+ });
+ $btnSubmit.removeClass("disabled");
+ $('#account-profile-content-scroller').on('submit', '#account-edit-email-form', function(evt) { evt.stopPropagation(); handleUpdateProfile(); return false; } );
+ $("#account-edit-email-form").removeClass("disabled");
+ }
+
+ function disableSubmits() {
+ $("#account-edit-email-form").addClass("disabled")
+ $("#account-edit-email-form").off("click")
+ $btnSubmit.addClass("disabled")
+ $btnSubmit.off("click")
+ }
+
+ function postUpdateProfileSuccess(response) {
+ $document.triggerHandler(EVENTS.USER_UPDATED, response);
+ context.location = "/client#/account/profile/experience";
+ }
+
+ function postUpdateProfileFailure(xhr, textStatus, errorMessage) {
+
+ var errors = JSON.parse(xhr.responseText)
+
+ if(xhr.status == 422) {
+ var first_name = context.JK.format_errors("first_name", errors);
+ var last_name = context.JK.format_errors("last_name", errors);
+ var country = context.JK.format_errors("country", errors);
+ var state = context.JK.format_errors("state", errors);
+ var city = context.JK.format_errors("city", errors);
+ var birth_date = context.JK.format_errors("birth_date", errors);
+ var gender = context.JK.format_errors("birth_date", errors);
+ var subscribeEmail = context.JK.format_errors("subscribe_email", errors);
+ var biography = context.JK.format_errors("biography", errors);
+
+ if(first_name != null) {
+ getFirstNameElement().closest('div.field').addClass('error').end().after(first_name);
}
-
- function populateCountries(countries, userCountry) {
-
- // countries has the format ["US", ...]
-
- var foundCountry = false;
- var countrySelect = getCountryElement();
- countrySelect.children().remove();
-
- var nilOption = $(nilOptionStr);
- nilOption.text(nilOptionText);
- countrySelect.append(nilOption);
-
- $.each(countries, function(index, country) {
- if(!country) return;
-
- var option = $(nilOptionStr);
- option.text(country);
- option.attr("value", country);
-
- if(country == userCountry) {
- foundCountry = true;
- }
-
- countrySelect.append(option);
- });
-
- if(!foundCountry) {
- // in this case, the user has a country that is not in the database
- // this can happen in a development/test scenario, but let's assume it can
- // happen in production too.
- var option = $(nilOptionStr);
- option.text(userCountry);
- option.attr("value", userCountry);
- countrySelect.append(option);
- }
-
- countrySelect.val(userCountry);
- countrySelect.attr("disabled", null)
-
- context.JK.dropdown(countrySelect);
+ if(last_name != null) {
+ getLastNameElement().closest('div.field').addClass('error').end().after(last_name);
}
-
- function populateCountriesx(countriesx, userCountry) {
-
- // countriesx has the format [{countrycode: "US", countryname: "United States"}, ...]
-
- var foundCountry = false;
- var countrySelect = getCountryElement();
- countrySelect.children().remove();
-
- var nilOption = $(nilOptionStr);
- nilOption.text(nilOptionText);
- countrySelect.append(nilOption);
-
- $.each(countriesx, function(index, countryx) {
- if(!countryx.countrycode) return;
-
- var option = $(nilOptionStr);
- option.text(countryx.countryname);
- option.attr("value", countryx.countrycode);
-
- if(countryx.countrycode == userCountry) {
- foundCountry = true;
- }
-
- countrySelect.append(option);
- });
-
- if(!foundCountry) {
- // in this case, the user has a country that is not in the database
- // this can happen in a development/test scenario, but let's assume it can
- // happen in production too.
- var option = $(nilOptionStr);
- option.text(userCountry);
- option.attr("value", userCountry);
- countrySelect.append(option);
- }
-
- countrySelect.val(userCountry);
- countrySelect.attr("disabled", null)
-
- context.JK.dropdown(countrySelect);
+ if(country != null) {
+ getCountryElement().closest('div.field').addClass('error').end().after(country);
}
-
- function populateRegions(regions, userRegion) {
- var regionSelect = getRegionElement()
- regionSelect.children().remove()
-
- var nilOption = $(nilOptionStr);
- nilOption.text(nilOptionText);
- regionSelect.append(nilOption);
-
- $.each(regions, function(index, region) {
- if(!region) return;
-
- var option = $(nilOptionStr)
- option.text(region['name'])
- option.attr("value", region['region'])
-
- regionSelect.append(option)
- })
-
- regionSelect.val(userRegion)
- regionSelect.attr("disabled", null)
-
- context.JK.dropdown(regionSelect);
+ if(state != null) {
+ getRegionElement().closest('div.field').addClass('error').end().after(state);
}
- function populateCities(cities, userCity) {
- var citySelect = getCityElement();
- citySelect.children().remove();
-
- var nilOption = $(nilOptionStr);
- nilOption.text(nilOptionText);
- citySelect.append(nilOption);
-
- $.each(cities, function(index, city) {
- if(!city) return;
-
- var option = $(nilOptionStr)
- option.text(city)
- option.attr("value", city)
-
- citySelect.append(option)
- })
-
- citySelect.val(userCity)
- citySelect.attr("disabled", null)
-
- context.JK.dropdown(citySelect);
+ if(city != null) {
+ getCityElement().closest('div.field').addClass('error').end().after(city);
}
- /****************** 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; } );
- $('#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(),
- api.getInstruments())
- .done(function(userDetailResponse, instrumentsResponse) {
- var userDetail = userDetailResponse[0];
- recentUserDetail = userDetail // store userDetail for later
- // show page; which can be done quickly at this point
- populateAccountProfile(userDetail,
- instrumentsResponse[0]);
-
- selectLocation = new context.JK.SelectLocation(getCountryElement(), getRegionElement(), getCityElement(), app);
- selectLocation.load(userDetail.country, userDetail.state, userDetail.city)
- })
- context.JK.dropdown($('select'));
+ if(birth_date != null) {
+ getYearElement().closest('div.field').addClass('error').end().after(birth_date);
}
- function navToAccount() {
- resetForm();
- window.location = '/client#/account';
+ if(subscribeEmail != null) {
+ getSubscribeEmail().closest('div.field').addClass('error').end().after(subscribeEmail);
}
- function navToAvatar() {
- resetForm();
- window.location = '/client#/account/profile/avatar';
+ if(gender != null) {
+ getGenderElement().closest('div.field').addClass('error').end().after(gender);
}
+ }
+ else {
+ app.ajaxError(xhr, textStatus, errorMessage)
+ }
+ }
- function handleUpdateProfile() {
+ function handleCountryChanged() {
+ var selectedCountry = getCountryElement().val()
+ var selectedRegion = getRegionElement().val()
+ var cityElement = getCityElement();
- resetForm();
+ updateRegionList(selectedCountry, getRegionElement());
+ updateCityList(selectedCountry, null, cityElement);
+ }
- var country = getCountryElement().val();
- var region = getRegionElement().val();
- var city = getCityElement().val();
- var firstName = getFirstNameElement().val();
- var lastName = getLastNameElement().val();
- var gender = getGenderElement().val();
- var subscribeEmail = getSubscribeEmail().is(':checked');
- var birthDate = getBirthDate();
- var instruments = getInstrumentsValue();
- var genres = getSelectedGenres();
+ function updateRegionList(selectedCountry, regionElement) {
+ // only update region
+ if (selectedCountry) {
+ // set city disabled while updating
+ regionElement.attr('disabled', true);
+ loadingRegionsData = true;
- api.updateUser({
- country: country,
- state: region,
- city: city,
- first_name: firstName,
- last_name: lastName,
- gender: gender,
- birth_date: birthDate,
- instruments: instruments,
- genres: genres,
- subscribe_email: subscribeEmail
- })
- .done(postUpdateProfileSuccess)
- .fail(postUpdateProfileFailure)
- }
+ regionElement.children().remove()
+ regionElement.append($(nilOptionStr).text('loading...'))
- function postUpdateProfileSuccess(response) {
- app.notify(
- { title: "Profile Changed",
- text: "You have updated your profile successfully."
- },
- null,
- true);
-
- $document.triggerHandler(EVENTS.USER_UPDATED, response);
+ api.getRegions({ country: selectedCountry })
+ .done(getRegionsDone)
+ .error(function(err) {
+ regionElement.children().remove()
+ regionElement.append($(nilOptionStr).text(nilOptionText))
+ })
+ .always(function () {
+ loadingRegionsData = false;
+ });
+ }
+ else {
+ regionElement.children().remove()
+ regionElement.append($(nilOptionStr).text(nilOptionText))
+ }
+ }
- }
+ function updateCityList(selectedCountry, selectedRegion, cityElement) {
+ logger.debug("updating city list: selectedCountry %o, selectedRegion %o", selectedCountry, selectedRegion);
- function postUpdateProfileFailure(xhr, textStatus, errorMessage) {
+ // only update cities
+ if (selectedCountry && selectedRegion) {
+ // set city disabled while updating
+ cityElement.attr('disabled', true);
+ loadingCitiesData = true;
- var errors = JSON.parse(xhr.responseText)
+ cityElement.children().remove();
+ cityElement.append($(nilOptionStr).text('loading...'));
- if(xhr.status == 422) {
+ api.getCities({ country: selectedCountry, region: selectedRegion })
+ .done(getCitiesDone)
+ .error(function(err) {
+ cityElement.children().remove();
+ cityElement.append($(nilOptionStr).text(nilOptionText));
+ })
+ .always(function () {
+ loadingCitiesData = false;
+ });
+ }
+ else {
+ cityElement.children().remove();
+ cityElement.append($(nilOptionStr).text(nilOptionText));
+ context.JK.dropdown(cityElement);
+ }
+ }
- var first_name = context.JK.format_errors("first_name", errors);
- var last_name = context.JK.format_errors("last_name", errors);
- var country = context.JK.format_errors("country", errors);
- var state = context.JK.format_errors("state", errors);
- var city = context.JK.format_errors("city", errors);
- var birth_date = context.JK.format_errors("birth_date", errors);
- var gender = context.JK.format_errors("birth_date", errors);
- var subscribeEmail = context.JK.format_errors("subscribe_email", errors);
- var instruments = context.JK.format_errors("musician_instruments", errors)
+ function handleRegionChanged() {
+ var selectedCountry = getCountryElement().val()
+ var selectedRegion = getRegionElement().val()
+ var cityElement = getCityElement();
- if(first_name != null) {
- getFirstNameElement().closest('div.field').addClass('error').end().after(first_name);
- }
+ updateCityList(selectedCountry, selectedRegion, cityElement);
+ }
- if(last_name != null) {
- getLastNameElement().closest('div.field').addClass('error').end().after(last_name);
- }
+ function getCitiesDone(data) {
+ populateCities(data['cities'], recentUserDetail.city);
+ }
- if(country != null) {
- getCountryElement().closest('div.field').addClass('error').end().after(country);
- }
+ function getRegionsDone(data) {
+ populateRegions(data['regions'], recentUserDetail.state);
+ updateCityList(getCountryElement().val(), getRegionElement().val(), getCityElement());
+ }
- if(state != null) {
- getRegionElement().closest('div.field').addClass('error').end().after(state);
- }
+ function getCountryElement() {
+ return $('#account-profile-content-scroller select[name=country]');
+ }
- if(city != null) {
- getCityElement().closest('div.field').addClass('error').end().after(city);
- }
+ function getRegionElement() {
+ return $('#account-profile-content-scroller select[name=region]');
+ }
- if(birth_date != null) {
- getYearElement().closest('div.field').addClass('error').end().after(birth_date);
- }
+ function getCityElement() {
+ return $('#account-profile-content-scroller select[name=city]');
+ }
- if(subscribeEmail != null) {
- getSubscribeEmail().closest('div.field').addClass('error').end().after(subscribeEmail);
- }
+ function getFirstNameElement() {
+ return $('#account-profile-content-scroller input[name=first_name]');
+ }
- if(gender != null) {
- getGenderElement().closest('div.field').addClass('error').end().after(gender);
- }
+ function getLastNameElement() {
+ return $('#account-profile-content-scroller input[name=last_name]');
+ }
- if(instruments != null) {
- getInstrumentsElement().closest('div.field').addClass('error').append(instruments);
- }
- }
- else {
- app.ajaxError(xhr, textStatus, errorMessage)
- }
- }
+ function getGenderElement() {
+ return $('#account-profile-content-scroller select[name=gender]');
+ }
- function handleCountryChanged() {
- var selectedCountry = getCountryElement().val()
- var selectedRegion = getRegionElement().val()
- var cityElement = getCityElement();
+ function getMonthElement() {
+ return $('#account-profile-content-scroller select#user_birth_date_2i');
+ }
- updateRegionList(selectedCountry, getRegionElement());
- updateCityList(selectedCountry, null, cityElement);
- }
+ function getDayElement() {
+ return $('#account-profile-content-scroller select#user_birth_date_3i');
+ }
- function updateRegionList(selectedCountry, regionElement) {
- // only update region
- if (selectedCountry) {
- // set city disabled while updating
- regionElement.attr('disabled', true);
- loadingRegionsData = true;
+ function getYearElement() {
+ return $('#account-profile-content-scroller select#user_birth_date_1i');
+ }
- regionElement.children().remove()
- regionElement.append($(nilOptionStr).text('loading...'))
+ function getSubscribeEmail() {
+ return $('#account-profile-content-scroller input[name=subscribe_email]');
+ }
- api.getRegions({ country: selectedCountry })
- .done(getRegionsDone)
- .error(function(err) {
- regionElement.children().remove()
- regionElement.append($(nilOptionStr).text(nilOptionText))
- })
- .always(function () {
- loadingRegionsData = false;
- })
- }
- else {
- regionElement.children().remove()
- regionElement.append($(nilOptionStr).text(nilOptionText))
- }
- }
+ function getBirthDate() {
+ var month = getMonthElement().val()
+ var day = getDayElement().val()
+ var year = getYearElement().val()
- function updateCityList(selectedCountry, selectedRegion, cityElement) {
- logger.debug("updating city list: selectedCountry %o, selectedRegion %o", selectedCountry, selectedRegion);
+ if(month != null && month.length > 0 && day != null && day.length > 0 && year != null && year.length > 0) {
+ return month + "-" + day + "-" + year;
+ }
+ else {
+ return null;
+ }
+ }
- // only update cities
- if (selectedCountry && selectedRegion) {
- // set city disabled while updating
- cityElement.attr('disabled', true);
- loadingCitiesData = true;
+ function initialize() {
+ var screenBindings = {
+ 'beforeShow': beforeShow,
+ 'afterShow': afterShow
+ };
+ app.bindScreen('account/profile', screenBindings);
+ events();
+ }
- cityElement.children().remove()
- cityElement.append($(nilOptionStr).text('loading...'))
-
- api.getCities({ country: selectedCountry, region: selectedRegion })
- .done(getCitiesDone)
- .error(function(err) {
- cityElement.children().remove()
- cityElement.append($(nilOptionStr).text(nilOptionText))
- })
- .always(function () {
- loadingCitiesData = false;
- })
- }
- else {
- cityElement.children().remove();
- cityElement.append($(nilOptionStr).text(nilOptionText));
- context.JK.dropdown(cityElement);
- }
- }
-
- function handleRegionChanged() {
- var selectedCountry = getCountryElement().val()
- var selectedRegion = getRegionElement().val()
- var cityElement = getCityElement();
-
- updateCityList(selectedCountry, selectedRegion, cityElement);
- }
-
- function getCitiesDone(data) {
- populateCities(data['cities'], recentUserDetail.city);
- }
-
- function getRegionsDone(data) {
- populateRegions(data['regions'], recentUserDetail.state);
- updateCityList(getCountryElement().val(), getRegionElement().val(), getCityElement());
- }
-
- function getCountryElement() {
- return $('#account-profile-content-scroller select[name=country]');
- }
-
- function getRegionElement() {
- return $('#account-profile-content-scroller select[name=region]');
- }
-
- function getCityElement() {
- return $('#account-profile-content-scroller select[name=city]');
- }
-
- function getFirstNameElement() {
- return $('#account-profile-content-scroller input[name=first_name]');
- }
-
- function getLastNameElement() {
- return $('#account-profile-content-scroller input[name=last_name]');
- }
-
- function getGenderElement() {
- return $('#account-profile-content-scroller select[name=gender]');
- }
-
- function getMonthElement() {
- return $('#account-profile-content-scroller select#user_birth_date_2i');
- }
-
- function getDayElement() {
- return $('#account-profile-content-scroller select#user_birth_date_3i');
- }
-
- function getYearElement() {
- return $('#account-profile-content-scroller select#user_birth_date_1i');
- }
-
- function getSubscribeEmail() {
- return $('#account-profile-content-scroller input[name=subscribe_email]');
- }
-
- function getInstrumentsElement() {
- return $('#account-profile-content-scroller .instrument_selector');
- }
-
-
- function getBirthDate() {
- var month = getMonthElement().val()
- var day = getDayElement().val()
- var year = getYearElement().val()
-
- if(month != null && month.length > 0 && day != null && day.length > 0 && year != null && year.length > 0) {
- return month + "-" + day + "-" + year;
- }
- else {
- return null;
- }
- }
-
- // looks in instrument_selector parent element, and gathers up all
- // selected elements, and the proficiency level declared
- function getInstrumentsValue() {
- var instrumentsParentElement = getInstrumentsElement();
-
- var instruments = []
- $('input[type=checkbox]:checked', instrumentsParentElement).each(function(i) {
-
- var instrumentElement = $(this).closest('tr');
- // traverse up to common parent of this instrument, and pick out proficiency selector
- var proficiency = $('select.proficiency_selector', instrumentElement).val()
-
- instruments.push({
- instrument_id: instrumentElement.attr('data-instrument-id'),
- proficiency_level: proficiency,
- priority : i
- })
- });
-
- return instruments;
- }
-
- function initialize() {
- var screenBindings = {
- 'beforeShow': beforeShow,
- 'afterShow': afterShow
- };
- app.bindScreen('account/profile', screenBindings);
- events();
- }
-
- this.initialize = initialize;
- this.beforeShow = beforeShow;
- this.afterShow = afterShow;
- return this;
- };
+ 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_avatar.js b/web/app/assets/javascripts/accounts_profile_avatar.js
index c89039d97..63d1aca1f 100644
--- a/web/app/assets/javascripts/accounts_profile_avatar.js
+++ b/web/app/assets/javascripts/accounts_profile_avatar.js
@@ -59,11 +59,21 @@
// wire up main panel clicks
$('#account-profile-avatar-content-scroller').on('click', '#account-edit-avatar-upload', function(evt) { evt.stopPropagation(); handleFilePick(); return false; } );
$('#account-profile-avatar-content-scroller').on('click', '#account-edit-avatar-delete', function(evt) { evt.stopPropagation(); handleDeleteAvatar(); return false; } );
- $('#account-profile-avatar-content-scroller').on('click', '#account-edit-avatar-cancel', function(evt) { evt.stopPropagation(); navToEditProfile(); return false; } );
- $('#account-profile-avatar-content-scroller').on('click', '#account-edit-avatar-submit', function(evt) { evt.stopPropagation(); handleUpdateAvatar(); return false; } );
+ $('#account-profile-avatar-content-scroller').on('click', '#account-edit-avatar-cancel', function(evt) { evt.stopPropagation(); navToEditProfile(); return false; } );
+ enableSubmits()
//$('#account-profile-avatar-content-scroller').on('change', 'input[type=filepicker-dragdrop]', function(evt) { evt.stopPropagation(); afterImageUpload(evt.originalEvent.fpfile); return false; } );
}
+ function enableSubmits() {
+ $('#account-profile-avatar-content-scroller').on('click', '#account-edit-avatar-submit', function(evt) { evt.stopPropagation(); handleUpdateAvatar(); return false; } );
+ $("#account-edit-avatar-submit").removeClass("disabled")
+ }
+
+ function disableSubmits() {
+ $("#account-edit-avatar-submit").addClass("disabled")
+ $("#account-edit-avatar-submit").off("click")
+ }
+
function handleDeleteAvatar() {
if(self.updatingAvatar) {
@@ -277,7 +287,7 @@
}
function handleUpdateAvatar(event) {
-
+ disableSubmits()
if(self.updatingAvatar) {
// protect against concurrent update attempts
return;
@@ -373,10 +383,11 @@
},
null,
true);
- }
+ }
+ enableSubmits()
}
- function updateAvatarSuccess(response) {
+ function updateAvatarSuccess(response) {
$.cookie('original_fpfile', null);
self.userDetail = response;
diff --git a/web/app/assets/javascripts/accounts_profile_experience.js b/web/app/assets/javascripts/accounts_profile_experience.js
new file mode 100644
index 000000000..de57e8695
--- /dev/null
+++ b/web/app/assets/javascripts/accounts_profile_experience.js
@@ -0,0 +1,236 @@
+(function(context,$) {
+
+ "use strict";
+
+ context.JK = context.JK || {};
+ context.JK.AccountProfileExperience = function(app) {
+ var $document = $(document);
+ var logger = context.JK.logger;
+ var EVENTS = context.JK.EVENTS;
+ var api = context.JK.Rest();
+ var $screen = $('#account-profile-experience');
+ 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) {
+ }
+
+ function afterShow(data) {
+ resetForm();
+ renderExperience();
+ }
+
+ function resetForm() {
+ $screen.find('form .error-text').remove();
+ $screen.find('form .error').removeClass("error");
+ }
+
+ function populateAccountProfile(userDetail, instruments) {
+
+ 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);
+ });
+
+ // 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', $screen).val(userInstrument.proficiency_level);
+ });
+ }
+
+ $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) {
+ var isUserInstrument = false;
+ if(!userInstruments) return false;
+
+ $.each(userInstruments, function(index, userInstrument) {
+ if(instrument.id == userInstrument.instrument_id) {
+ isUserInstrument = true;
+ return false;
+ }
+ });
+ return isUserInstrument;
+ }
+
+ function loadGenres(selectedGenres) {
+ $userGenres.empty();
+
+ rest.getGenres().done(function (genres) {
+ $.each(genres, function (index, genre) {
+ var genreTemplate = $('#template-user-setup-genres').html();
+ var selected = '';
+ if (selectedGenres) {
+ var genreMatch = $.grep(selectedGenres, function (n, i) {
+ return n.genre_id === genre.id;
+ });
+ if (genreMatch.length > 0) {
+ selected = "checked";
+ }
+ }
+ var genreHtml = context.JK.fillTemplate(genreTemplate, {
+ id: genre.id,
+ description: genre.description,
+ checked: selected
+ });
+ $userGenres.append(genreHtml);
+ });
+ });
+ }
+
+ function resetGenres() {
+ $('input[type=checkbox]:checked', $userGenres).each(function (i) {
+ $(this).removeAttr("checked");
+ });
+ var $tdGenres = $("#tdBandGenres");
+ }
+
+ function getSelectedGenres() {
+ var genres = [];
+ $('input[type=checkbox]:checked', $userGenres).each(function (i) {
+ var genre = $(this).val();
+ genres.push(genre);
+ });
+ return genres;
+ }
+
+ function events() {
+ $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;
+ });
+
+ enableSubmits()
+ }
+
+ function enableSubmits() {
+ $btnSubmit.on("click", function(evt) {
+ evt.stopPropagation();
+ handleUpdateProfile();
+ return false;
+ });
+
+ $btnSubmit.removeClass("disabled")
+ }
+
+ function disableSubmits() {
+ $btnSubmit.addClass("disabled")
+ $btnSubmit.off("click")
+ }
+
+ function renderExperience() {
+ $.when(api.getUserProfile(), api.getInstruments())
+ .done(function(userDetailResponse, instrumentsResponse) {
+ var userDetail = userDetailResponse[0];
+ populateAccountProfile(userDetail, instrumentsResponse[0]);
+ });
+
+ context.JK.dropdown($('select'));
+ }
+
+ function navigateTo(targetLocation) {
+ resetForm();
+ context.location = targetLocation;
+ }
+
+ function handleUpdateProfile() {
+ disableSubmits()
+ resetForm();
+
+ var instruments = getSelectedInstruments();
+ var genres = getSelectedGenres();
+
+ api.updateUser({
+ instruments: instruments,
+ genres: genres,
+ 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)
+ .always(enableSubmits)
+ }
+
+ function postUpdateProfileSuccess(response) {
+ $document.triggerHandler(EVENTS.USER_UPDATED, response);
+ context.location = "/client#/account/profile/interests";
+ }
+
+ function postUpdateProfileFailure(xhr, textStatus, errorMessage) {
+
+ var errors = JSON.parse(xhr.responseText)
+
+ if(xhr.status == 422) {
+ var instruments = context.JK.format_errors("musician_instruments", errors);
+
+ if(instruments != null) {
+ $instrumentSelector.closest('div.field').addClass('error').append(instruments);
+ }
+ }
+ else {
+ app.ajaxError(xhr, textStatus, errorMessage)
+ }
+ }
+
+ function getSelectedInstruments() {
+ var instruments = [];
+ $('input[type=checkbox]:checked', $instrumentSelector).each(function(i) {
+
+ var instrumentElement = $(this).closest('tr');
+ // traverse up to common parent of this instrument, and pick out proficiency selector
+ var proficiency = $('select.proficiency_selector', instrumentElement).val();
+
+ instruments.push({
+ instrument_id: instrumentElement.attr('data-instrument-id'),
+ proficiency_level: proficiency,
+ priority : i
+ });
+ });
+
+ return instruments;
+ }
+
+ function initialize() {
+ var screenBindings = {
+ 'beforeShow': beforeShow,
+ 'afterShow': afterShow
+ };
+
+ app.bindScreen('account/profile/experience', 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/accounts_profile_interests.js b/web/app/assets/javascripts/accounts_profile_interests.js
new file mode 100644
index 000000000..6a4a65e2c
--- /dev/null
+++ b/web/app/assets/javascripts/accounts_profile_interests.js
@@ -0,0 +1,298 @@
+(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').bind('click', function(e) {
+ e.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(e) {
+ e.stopPropagation()
+ navigateTo('/client#/profile/' + context.JK.currentUserId)
+ return false
+ })
+
+ $btnBack.click(function(e) {
+ e.stopPropagation()
+ navigateTo('/client#/account/profile/experience')
+ return false
+ })
+
+ enableSubmits()
+
+ context.JK.dropdown($virtualBandCommitment)
+ context.JK.dropdown($traditionalBandCommitment)
+ context.JK.dropdown($cowritingPurpose)
+ }
+
+ function enableSubmits() {
+ $btnSubmit.on("click", function(e) {
+ e.stopPropagation()
+ handleUpdateProfile()
+ return false
+ })
+
+ $btnSubmit.removeClass("disabled")
+ }
+
+ function disableSubmits() {
+ $btnSubmit.addClass("disabled")
+ $btnSubmit.off("click")
+ }
+
+ function renderInterests() {
+ $.when(api.getUserProfile())
+ .done(function(userDetail) {
+ populateAccountProfile(userDetail)
+ })
+ }
+
+ function navigateTo(targetLocation) {
+ context.location = targetLocation
+ }
+
+ function handleUpdateProfile() {
+ disableSubmits()
+ 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)
+ .always(enableSubmits)
+ }
+
+ 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
+ })
+
+ profileUtils.initializeHelpBubbles($screen)
+ }
+
+ 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
new file mode 100644
index 000000000..6cce9de0e
--- /dev/null
+++ b/web/app/assets/javascripts/accounts_profile_samples.js
@@ -0,0 +1,487 @@
+(function(context,$) {
+
+ "use strict";
+
+ context.JK = context.JK || {};
+
+ // TODO: Add a target type, which can be band or user -- call the
+ // appropriate API methods.
+ context.JK.AccountProfileSamples = function(app, parent, loadFn, updateFn) {
+ var $document = $(document)
+
+ // used to initialize RecordingSourceValidator in site_validator.js.coffee
+ var jamkazamRecordingSources = [];
+ var soundCloudRecordingSources = [];
+ var youTubeRecordingSources = [];
+
+ var logger = context.JK.logger;
+ var EVENTS = context.JK.EVENTS;
+ var api = context.JK.Rest();
+ var ui = new context.JK.UIHelper(JK.app);
+ var target = {};
+ var profileUtils = context.JK.ProfileUtils;
+ var $screen = $('.profile-online-sample-controls', parent);
+ // 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 $jamkazamSampleList = $screen.find(".sample-list[source-type='jamkazam']")
+ var $soundCloudSampleList = $screen.find(".sample-list[source-type='soundcloud']")
+ var $youTubeSampleList = $screen.find(".sample-list[source-type='youtube']")
+
+ // buttons
+ var $btnAddJkRecording = $screen.find('.btn-add-jk-recording')
+ var $btnCancel = parent.find('.account-edit-profile-cancel')
+ var $btnBack = parent.find('.account-edit-profile-back')
+ var $btnSubmit = parent.find('.account-edit-profile-submit')
+
+
+ var urlValidator=null
+ var soundCloudValidator=null
+ var reverbNationValidator=null
+ var bandCampValidator=null
+ var fandalismValidator=null
+ var youTubeValidator=null
+ var facebookValidator=null
+ var twitterValidator=null
+ var soundCloudRecordingValidator=null
+ var youTubeRecordingValidator=null
+
+ function beforeShow(data) {
+ }
+
+ function afterShow(data) {
+ $.when(loadFn())
+ .done(function(targetPlayer) {
+ if (targetPlayer && targetPlayer.keys && targetPlayer.keys.length > 0) {
+ renderPlayer(targetPlayer)
+ }
+ })
+ }
+
+ function renderPlayer(targetPlayer) {
+ renderPresence(targetPlayer);
+ renderSamples(targetPlayer);
+ }
+
+ function renderPresence(targetPlayer) {
+ $website.val(targetPlayer.website);
+
+ // SoundCloud
+ var presences = profileUtils.soundCloudPresences(targetPlayer.online_presences);
+ if (presences && presences.length > 0) {
+ $soundCloudUsername.val(presences[0].username);
+ }
+
+ // ReverbNation
+ presences = profileUtils.reverbNationPresences(targetPlayer.online_presences);
+ if (presences && presences.length > 0) {
+ $reverbNationUsername.val(presences[0].username);
+ }
+
+ // Bandcamp
+ presences = profileUtils.bandCampPresences(targetPlayer.online_presences);
+ if (presences && presences.length > 0) {
+ $bandCampUsername.val(presences[0].username);
+ }
+
+ // Fandalism
+ presences = profileUtils.fandalismPresences(targetPlayer.online_presences);
+ if (presences && presences.length > 0) {
+ $fandalismUsername.val(presences[0].username);
+ }
+
+ // YouTube
+ presences = profileUtils.youTubePresences(targetPlayer.online_presences);
+ if (presences && presences.length > 0) {
+ $youTubeUsername.val(presences[0].username);
+ }
+
+ // Facebook
+ presences = profileUtils.facebookPresences(targetPlayer.online_presences);
+ if (presences && presences.length > 0) {
+ $facebookUsername.val(presences[0].username);
+ }
+
+ // Twitter
+ presences = profileUtils.twitterPresences(targetPlayer.online_presences);
+ if (presences && presences.length > 0) {
+ $twitterUsername.val(presences[0].username);
+ }
+ }
+
+ function renderSamples(targetPlayer) {
+ // JamKazam recordings
+ var samples = profileUtils.jamkazamSamples(targetPlayer.performance_samples);
+ loadSamples(samples, 'jamkazam', $jamkazamSampleList, jamkazamRecordingSources);
+
+ // SoundCloud recordings
+ samples = profileUtils.soundCloudSamples(targetPlayer.performance_samples);
+ loadSamples(samples, 'soundcloud', $soundCloudSampleList, soundCloudRecordingSources);
+
+ // YouTube videos
+ samples = profileUtils.youTubeSamples(targetPlayer.performance_samples);
+ loadSamples(samples, 'youtube', $youTubeSampleList, youTubeRecordingSources);
+ }
+
+ function loadSamples(samples, type, $sampleList, recordingSources) {
+ $sampleList.find(":not(.empty)").remove();
+
+ if (type === 'jamkazam') {
+ $.each(samples, function(index, val) {
+ recordingSources.push({
+ 'claimed_recording_id': val.claimed_recording.id,
+ 'description': val.claimed_recording.name
+ });
+
+ buildJamkazamEntry(val.claimed_recording.id, val.claimed_recording.name);
+ });
+ } else {
+ if (samples && samples.length > 0) {
+ $.each(samples, function(index, val) {
+
+ recordingSources.push({
+ 'url': val.url,
+ 'recording_id': val.service_id,
+ 'recording_title': val.description
+ });
+
+ // TODO: this code is repeated in HTML file
+ var recordingIdAttr = ' data-recording-id="' + val.service_id + '" ';
+ var recordingUrlAttr = ' data-recording-url="' + val.url + '" ';
+ var recordingTitleAttr = ' data-recording-title="' + val.description + '"';
+ var title = formatTitle(val.description);
+ $sampleList.append('
' + title + '
');
+ $sampleList.append('X
');
+ });
+ }
+ }
+ }
+
+ function buildJamkazamEntry(recordingId, recordingName) {
+ var title = formatTitle(recordingName);
+
+ var recordingIdAttr = ' data-recording-id="' + recordingId + '" ';
+ $jamkazamSampleList.append('' + title + '
');
+ $jamkazamSampleList.append('X
');
+ }
+
+ function events() {
+
+ // buttons
+ $btnAddJkRecording.click(function(evt) {
+ evt.preventDefault();
+
+ // retrieve recordings and pass to modal dialog
+ api.getClaimedRecordings()
+ .done(function(response) {
+ ui.launchRecordingSelectorDialog(response, jamkazamRecordingSources, function(selectedRecordings) {
+ $jamkazamSampleList.empty();
+
+ jamkazamRecordingSources = [];
+
+ // update the list with the selected recordings
+ $.each(selectedRecordings, function(index, val) {
+ jamkazamRecordingSources.push({
+ 'claimed_recording_id': val.id,
+ 'description': val.name
+ });
+
+ buildJamkazamEntry(val.id, val.name);
+ });
+ });
+ });
+
+ 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/interests');
+ return false;
+ });
+
+ enableSubmits();
+
+ $screen.find(".sample-list").off("click").on("click", ".close-button", function(e) {
+ removeRow($(this).data("recording-id"), $(this).data("recording-type"))
+ })
+ }
+
+ function enableSubmits() {
+ $btnSubmit.off("click").on("click", function(e) {
+ e.stopPropagation();
+ handleUpdateProfile();
+ return false;
+ })
+
+ $btnSubmit.removeClass("disabled")
+ }
+
+ function disableSubmits() {
+ $btnSubmit.addClass("disabled")
+ $btnSubmit.off("click")
+ }
+
+ function validate() {
+ var errors = $screen.find('.site_validator.error');
+ return !(errors && errors.length > 0);
+ }
+
+ function navigateTo(targetLocation) {
+ context.location = targetLocation;
+ }
+
+ function addOnlinePresence(presenceArray, username, type) {
+ if ($.trim(username).length > 0) {
+ presenceArray.push({
+ 'player_id': context.JK.currentUserId,
+ 'service_type': type,
+ 'username': username
+ });
+ }
+ }
+
+ function addPerformanceSamples(sampleArray, $samplesSelector, type) {
+ var rows = $samplesSelector.find('.recording-row');
+
+ // loop over rows, extracting service id, description, and url
+ rows.each(function(index) {
+ var id = $(this).attr('data-recording-id');
+
+ if (type === 'jamkazam') {
+ sampleArray.push({
+ 'player_id': context.JK.currentUserId,
+ 'service_type': type,
+ 'claimed_recording_id': id,
+ });
+ } else {
+ var url = $(this).attr('data-recording-url');
+ var title = $(this).attr('data-recording-title');
+
+ sampleArray.push({
+ 'player_id': context.JK.currentUserId,
+ 'service_type': type,
+ 'service_id': id,
+ 'url': url,
+ 'description': title
+ });
+ }
+ });
+ }
+
+ function handleUpdateProfile() {
+ disableSubmits()
+
+ var player = buildPlayer()
+ updateFn({
+ website: player.website,
+ online_presences: player.online_presences,
+ performance_samples: player.performance_samples
+ })
+ .done(postUpdateProfileSuccess)
+ .fail(postUpdateProfileFailure)
+ .always(enableSubmits);
+ }
+
+ function buildPlayer() {
+ // 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.description);
+ addPerformanceSamples(ps, $soundCloudSampleList, performanceSampleTypes.SOUNDCLOUD.description);
+ addPerformanceSamples(ps, $youTubeSampleList, performanceSampleTypes.YOUTUBE.description);
+
+ return {
+ website: $website.val(),
+ online_presences: op,
+ performance_samples: ps
+ }
+ }
+
+ 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 removeRow(recordingId, type) {
+ $('div[data-recording-id=' + recordingId + ']').remove();
+ var sampleList = $('.sample-list[source-type="' + type + '"]')
+ var rowCnt = sampleList.find('.recording-row').length
+ if (0==parseInt(rowCnt)) {
+ sampleList.find(".empty").removeClass("hidden")
+ }
+
+ if (type === 'soundcloud') {
+ soundCloudRecordingValidator.removeRecordingId(recordingId);
+ } else if (type === 'youtube') {
+ youTubeRecordingValidator.removeRecordingId(recordingId);
+ }
+ }
+
+ function formatTitle(title) {
+ return title && title.length > 30 ? title.substring(0, 30) + "..." : title;
+ }
+
+ // This function is a bit of a mess. It was pulled
+ // from the html.erb file verbatim, and could use a
+ // refactor:
+ function initializeValidators() {
+ var initialized = false;
+ //$document.on('JAMKAZAM_READY', function(e, data) {
+ JK.JamServer.get$Server().on(JK.EVENTS.CONNECTION_UP, function() {
+ if(initialized) {
+ return;
+ }
+ initialized = true;
+
+ //var $screen = $('#account-profile-samples');
+ var $btnAddSoundCloudRecording = $screen.find('.btn-add-soundcloud-recording');
+ var $btnAddYouTubeVideo = $screen.find('.btn-add-youtube-video');
+ // var $soundCloudSampleList = $screen.find('.samples.soundcloud');
+ // var $youTubeSampleList = $screen.find('.samples.youtube');
+
+
+ setTimeout(function() {
+ urlValidator = new JK.SiteValidator('url', userNameSuccessCallback, userNameFailCallback, parent)
+ urlValidator.init()
+
+ soundCloudValidator = new JK.SiteValidator('soundcloud', userNameSuccessCallback, userNameFailCallback, parent)
+ soundCloudValidator.init()
+
+ reverbNationValidator = new JK.SiteValidator('reverbnation', userNameSuccessCallback, userNameFailCallback, parent)
+ reverbNationValidator.init()
+
+ bandCampValidator = new JK.SiteValidator('bandcamp', userNameSuccessCallback, userNameFailCallback, parent)
+ bandCampValidator.init()
+
+ fandalismValidator = new JK.SiteValidator('fandalism', userNameSuccessCallback, userNameFailCallback, parent)
+ fandalismValidator.init()
+
+ youTubeValidator = new JK.SiteValidator('youtube', userNameSuccessCallback, userNameFailCallback, parent)
+ youTubeValidator.init()
+
+ facebookValidator = new JK.SiteValidator('facebook', userNameSuccessCallback, userNameFailCallback, parent)
+ facebookValidator.init()
+
+ twitterValidator = new JK.SiteValidator('twitter', userNameSuccessCallback, userNameFailCallback, parent)
+ twitterValidator.init()
+
+ soundCloudRecordingValidator = new JK.RecordingSourceValidator('rec_soundcloud', soundCloudSuccessCallback, siteFailCallback, parent)
+ youTubeRecordingValidator = new JK.RecordingSourceValidator('rec_youtube', youTubeSuccessCallback, siteFailCallback, parent)
+
+ soundCloudRecordingValidator.init(soundCloudRecordingSources)
+ youTubeRecordingValidator.init(youTubeRecordingSources)
+ }, 1)
+
+ function userNameSuccessCallback($inputDiv) {
+ $inputDiv.removeClass('error');
+ $inputDiv.find('.error-text').remove();
+ }
+
+ function userNameFailCallback($inputDiv) {
+ $inputDiv.addClass('error');
+ $inputDiv.find('.error-text').remove();
+ $inputDiv.append("Invalid username").show();
+ }
+
+ function soundCloudSuccessCallback($inputDiv) {
+ siteSuccessCallback($inputDiv, soundCloudRecordingValidator, $soundCloudSampleList, 'soundcloud');
+ }
+
+ function youTubeSuccessCallback($inputDiv) {
+ siteSuccessCallback($inputDiv, youTubeRecordingValidator, $youTubeSampleList, 'youtube');
+ }
+
+ function siteSuccessCallback($inputDiv, recordingSiteValidator, sampleList, type) {
+ sampleList.find(".empty").addClass("hidden")
+ $inputDiv.removeClass('error');
+ $inputDiv.find('.error-text').remove();
+
+ var recordingSources = recordingSiteValidator.recordingSources();
+ if (recordingSources && recordingSources.length > 0) {
+ var addedRecording = recordingSources[recordingSources.length-1];
+
+ // TODO: this code is repeated in elsewhere in this JS file:
+ var recordingIdAttr = ' data-recording-id="' + addedRecording.recording_id + '" ';
+ var recordingUrlAttr = ' data-recording-url="' + addedRecording.url + '" ';
+ var recordingTitleAttr = ' data-recording-title="' + addedRecording.recording_title + '"';
+ var title = formatTitle(addedRecording.recording_title);
+ sampleList.append('' + title + '
');
+ sampleList.append('X
');
+ }
+
+ $inputDiv.find('input').val('');
+ }
+
+ function siteFailCallback($inputDiv) {
+ $inputDiv.addClass('error');
+ $inputDiv.find('.error-text').remove();
+ $inputDiv.append("Invalid URL").show();
+ }
+ });
+ //});
+
+
+
+ } // end initializeValidators.
+
+ function resetForm() {
+ $("input", $screen).val("")
+ }
+
+ function initialize() {
+ var screenBindings = {
+ 'beforeShow': beforeShow,
+ 'afterShow': afterShow
+ };
+
+ app.bindScreen('account/profile/samples', screenBindings);
+ initializeValidators();
+ events();
+ }
+
+ this.initialize = initialize;
+ this.beforeShow = beforeShow;
+ this.afterShow = afterShow;
+ this.buildPlayer = buildPlayer;
+ this.renderPlayer = renderPlayer
+ this.resetForm = resetForm;
+ return this;
+ };
+})(window,jQuery);
\ No newline at end of file
diff --git a/web/app/assets/javascripts/bandProfile.js b/web/app/assets/javascripts/bandProfile.js
index c140d2ea2..b7501373b 100644
--- a/web/app/assets/javascripts/bandProfile.js
+++ b/web/app/assets/javascripts/bandProfile.js
@@ -4,526 +4,623 @@
context.JK = context.JK || {};
context.JK.BandProfileScreen = function(app) {
- var logger = context.JK.logger;
- var rest = context.JK.Rest();
- var bandId;
- var isMember = false;
- var isAdmin = false;
- var band = {};
- var instrument_logo_map = context.JK.getInstrumentIconMap24();
+ var NOT_SPECIFIED_TEXT = 'Not specified';
+ var logger = context.JK.logger;
+ var rest = context.JK.Rest();
+ var bandId;
+ var isMember = false;
+ var isAdmin = false;
+ var band = {};
+ var instrument_logo_map = context.JK.getInstrumentIconMap24();
+ var profileUtils = context.JK.ProfileUtils;
+ var feed = new context.JK.Feed(app)
+ var $root = $("#band-profile")
+ var $history = $root.find("#band-profile-history")
- function beforeShow(data) {
- bandId = data.id;
+
+
+
+ function beforeShow(data) {
+ bandId = data.id;
+ feed.setBand(bandId);
+ }
+
+ function beforeHide(data) {
+ feed.setBand(null);
+ }
+
+
+ function afterShow(data) {
+ // hide until we know if 'isMember'
+ $("#btn-follow-band").hide();
+ $("#btn-edit-band-profile").hide();
+ $("#btn-edit-band-bio").hide();
+ $("#btn-edit-band-info").hide();
+ $("#btn-edit-band-members").hide();
+ $("#btn-edit-band-delete").hide();
+
+ resetForm();
+ events();
+ determineMembership()
+ .done(function() {
+ renderActive();
+ })
+ .fail(function(jqXHR) {
+ app.notifyServerError(jqXHR, "Unable to talk to server")
+ })
+ }
+
+ function resetForm() {
+ $('#band-profile-instruments').empty();
+
+ $('#band-profile-about').show();
+ $('#band-profile-history').hide();
+ $('#band-profile-members').hide();
+ $('#band-profile-social').hide();
+
+ $('.band-profile-nav a.active').removeClass('active');
+ $('.band-profile-nav a#band-profile-about-link').addClass('active');
+ }
+
+ /****************** MAIN PORTION OF SCREEN *****************/
+
+ function addFollowing(isBand, id) {
+ var newFollowing = {};
+
+ if (!isBand) {
+ newFollowing.user_id = id;
+ }
+ else {
+ newFollowing.band_id = id;
}
- function afterShow(data) {
-
- // hide until we know if 'isMember'
- $("#btn-follow-band").hide();
- $("#btn-edit-band-profile").hide();
- $("#btn-edit-band-info").hide();
- $("#btn-edit-band-members").hide();
- $("#btn-edit-band-delete").hide();
-
- resetForm();
- events();
- determineMembership()
- .done(function() {
- renderActive();
- })
- .fail(function(jqXHR) {
- app.notifyServerError(jqXHR, "Unable to talk to server")
- })
- }
-
- function resetForm() {
- $('#band-profile-instruments').empty();
-
- $('#band-profile-about').show();
- $('#band-profile-history').hide();
- $('#band-profile-members').hide();
- $('#band-profile-social').hide();
-
- $('.band-profile-nav a.active').removeClass('active');
- $('.band-profile-nav a#band-profile-about-link').addClass('active');
- }
-
- /****************** MAIN PORTION OF SCREEN *****************/
-
- function addFollowing(isBand, id) {
- var newFollowing = {};
-
- if (!isBand) {
- newFollowing.user_id = id;
- }
- else {
- newFollowing.band_id = id;
- }
-
- rest.addFollowing(newFollowing)
- .done(function() {
- if (isBand) {
- var newCount = parseInt($("#band-profile-follower-stats").text()) + 1;
- var text = newCount > 1 || newCount == 0 ? " Followers" : " Follower";
- $('#band-profile-follower-stats').html(newCount + text);
- configureBandFollowingButton(true);
- }
- else {
- configureMemberFollowingButton(true, id);
- }
- renderActive();
- })
- .fail(app.ajaxError);
- }
-
- function removeFollowing(isBand, id) {
- rest.removeFollowing(id)
- .done(function() {
- if (isBand) {
- var newCount = parseInt($("#band-profile-follower-stats").text()) - 1;
- var text = newCount > 1 || newCount == 0 ? " Followers" : " Follower";
- $('#band-profile-follower-stats').html(newCount + text);
- configureBandFollowingButton(false);
- }
- else {
- configureMemberFollowingButton(false, id);
- }
- renderActive();
- })
- .fail(app.ajaxError);
- }
-
- function configureBandFollowingButton(following) {
- $('#btn-follow-band').unbind("click");
-
- if (following) {
- $('#btn-follow-band').text('UNFOLLOW');
- $('#btn-follow-band').click(function() {
- removeFollowing(true, bandId);
- return false;
- });
+ rest.addFollowing(newFollowing)
+ .done(function() {
+ if (isBand) {
+ var newCount = parseInt($("#band-profile-follower-stats").text()) + 1;
+ var text = newCount > 1 || newCount == 0 ? " Followers" : " Follower";
+ $('#band-profile-follower-stats').html(newCount + text);
+ configureBandFollowingButton(true);
}
else {
- $('#btn-follow-band').text('FOLLOW');
- $('#btn-follow-band').click(function() {
- addFollowing(true, bandId);
- return false;
- });
+ configureMemberFollowingButton(true, id);
}
- }
+ renderActive();
+ })
+ .fail(app.ajaxError);
+ }
- function configureMemberFollowingButton(following, userId) {
-
- var $btnFollowMember = $('div[user-id=' + userId + ']', '#band-profile-members').find('#btn-follow-member');
-
- if (context.JK.currentUserId === userId) {
- $btnFollowMember.hide();
+ function removeFollowing(isBand, id) {
+ rest.removeFollowing(id)
+ .done(function() {
+ if (isBand) {
+ var newCount = parseInt($("#band-profile-follower-stats").text()) - 1;
+ var text = newCount > 1 || newCount == 0 ? " Followers" : " Follower";
+ $('#band-profile-follower-stats').html(newCount + text);
+ configureBandFollowingButton(false);
}
else {
- $btnFollowMember.unbind("click");
-
- if (following) {
- $btnFollowMember.text('UNFOLLOW');
- $btnFollowMember.click(function() {
- removeFollowing(false, userId);
- return false;
- });
- }
- else {
- $btnFollowMember.text('FOLLOW');
- $btnFollowMember.click(function() {
- addFollowing(false, userId);
- return false;
- });
- }
+ configureMemberFollowingButton(false, id);
}
- }
+ renderActive();
+ })
+ .fail(app.ajaxError);
+ }
- // refreshes the currently active tab
- function renderActive() {
+ function configureBandFollowingButton(following) {
+ $('#btn-follow-band').unbind("click");
- if (isMember) {
- $("#btn-follow-band").hide();
- $("#btn-edit-band-profile").show();
- $("#btn-edit-band-info").show();
- $("#btn-edit-band-members").show();
- if (isAdmin)
- $("#btn-edit-band-delete").show();
- }
- else {
- $("#btn-follow-band").show();
- $("#btn-edit-band-profile").hide();
- $("#btn-edit-band-info").hide();
- $("#btn-edit-band-members").hide();
- $("#btn-edit-band-delete").hide();
- }
-
- if ($('#band-profile-about-link').hasClass('active')) {
- renderAbout();
- }
- else if ($('#band-profile-history-link').hasClass('active')) {
- renderHistory();
- }
- else if ($('#band-profile-members-link').hasClass('active')) {
- renderMembers();
- }
- else if ($('#band-profile-social-link').hasClass('active')) {
- renderSocial();
- }
- }
-
- /****************** ABOUT TAB *****************/
- function renderAbout() {
-
- $('#band-profile-about').show();
- $('#band-profile-history').hide();
- $('#band-profile-members').hide();
- $('#band-profile-social').hide();
-
- $('.band-profile-nav a.active').removeClass('active');
- $('.band-profile-nav a#band-profile-about-link').addClass('active');
-
- bindAbout();
- }
-
- function bindAbout() {
-
- rest.getBand(bandId)
- .done(function(response) {
- band = response;
- if (band) {
- // name
- $('#band-profile-name').text(band.name);
-
- // avatar
- $('#band-profile-avatar').attr('src', context.JK.resolveAvatarUrl(band.photo_url));
-
- // location
- $('#band-profile-location').html(band.location);
-
- // stats
- var text = band.follower_count > 1 || band.follower_count == 0 ? " Followers" : " Follower";
- $('#band-profile-follower-stats').html(band.follower_count + text);
-
- text = band.session_count > 1 || band.session_count == 0 ? " Sessions" : " Session";
- $('#band-profile-session-stats').html(band.session_count + text);
-
- text = band.recording_count > 1 || band.recording_count == 0 ? " Recordings" : " Recording";
- $('#band-profile-recording-stats').html(band.recording_count + text);
-
- $('#band-profile-biography').text(band.biography);
-
- if (band.website) {
- $('#band-profile-website').attr('href', band.website);
- $('#band-profile-website').text(band.website);
- }
-
- // wire up Follow click
- configureBandFollowingButton(band.is_following);
- }
- else {
- logger.debug("No band found with bandId = " + bandId);
- }
- })
- .fail(function(xhr) {
- if(xhr.status >= 500) {
- context.JK.fetchUserNetworkOrServerFailure();
- }
- else if(xhr.status == 404) {
- context.JK.entityNotFound("Band");
- }
- else {
- context.JK.app.ajaxError(arguments);
- }
- });
- }
-
- /****************** SOCIAL TAB *****************/
- function renderSocial() {
- $('#band-profile-social-followers').empty();
-
- $('#band-profile-about').hide();
- $('#band-profile-history').hide();
- $('#band-profile-members').hide();
- $('#band-profile-social').show();
-
- $('.band-profile-nav a.active').removeClass('active');
- $('.band-profile-nav a#band-profile-social-link').addClass('active');
-
- bindSocial();
- }
-
- function bindSocial() {
-
- rest.getBandFollowers(bandId)
- .done(function(response) {
- $.each(response, function(index, val) {
- // var template = $('#template-profile-social').html();
- var template = $('#template-band-profile-social').html();
- var followerHtml = context.JK.fillTemplate(template, {
- userId: val.id,
- hoverAction: val.musician ? "musician" : "fan",
- avatar_url: context.JK.resolveAvatarUrl(val.photo_url),
- userName: val.name,
- location: val.location
- });
-
- $('#band-profile-social-followers').append(followerHtml);
-
- if (index === response.length-1) {
- context.JK.bindHoverEvents();
- }
- })
- })
- .fail(function(xhr) {
- if(xhr.status >= 500) {
- context.JK.fetchUserNetworkOrServerFailure();
- }
- else {
- context.JK.app.ajaxError(arguments);
- }
- });
- }
-
- /****************** HISTORY TAB *****************/
- function renderHistory() {
- $('#band-profile-about').hide();
- $('#band-profile-history').show();
- $('#band-profile-members').hide();
- $('#band-profile-social').hide();
-
- $('.band-profile-nav a.active').removeClass('active');
- $('.band-profile-nav a#band-profile-history-link').addClass('active');
-
- bindHistory();
- }
-
- function bindHistory() {
-
- }
-
- /****************** BANDS TAB *****************/
- function renderMembers() {
- $('#band-profile-members').empty();
-
- $('#band-profile-about').hide();
- $('#band-profile-history').hide();
- $('#band-profile-members').show();
- $('#band-profile-social').hide();
-
- $('.band-profile-nav a.active').removeClass('active');
- $('.band-profile-nav a#band-profile-members-link').addClass('active');
-
- bindMembers();
- }
-
- function bindMembers() {
- rest.getBandMembers(bandId, false)
- .done(function(response) {
- bindMusicians(response, false);
- if (isMember) {
- bindPendingMembers();
- }
- })
- .fail(function(xhr) {
- if(xhr.status >= 500) {
- context.JK.fetchUserNetworkOrServerFailure();
- }
- else {
- context.JK.app.ajaxError(arguments);
- }
- });
- }
-
- function bindPendingMembers() {
- rest.getBandMembers(bandId, true)
- .done(function(response) {
- if (response && response.length > 0) {
- $("#band-profile-members").append("
Pending Band Invitations
");
- bindMusicians(response, true);
- }
- })
- .fail(function(xhr) {
- if(xhr.status >= 500) {
- context.JK.fetchUserNetworkOrServerFailure();
- }
- else {
- context.JK.app.ajaxError(arguments);
- }
- });
- }
-
- function bindMusicians(musicians, isPending) {
- $.each(musicians, function(index, musician) {
- var instrumentLogoHtml = '';
- if ("instruments" in musician && musician.instruments != null) {
- for (var j=0; j < musician.instruments.length; j++) {
- var instrument = musician.instruments[j];
- var inst = '../assets/content/icon_instrument_default24.png';
- if (instrument.instrument_id in instrument_logo_map) {
- inst = instrument_logo_map[instrument.instrument_id].asset;
- }
- instrumentLogoHtml += '
';
- }
- }
- var bandAdmin = musician.band_admin;
- var template = $('#template-band-profile-members').html();
- var memberHtml = context.JK.fillTemplate(template, {
- userId: musician.id,
- band_admin: bandAdmin,
- is_pending: isPending,
- invitation_id: isPending ? musician.invitation_id : '',
- profile_url: "/client#/profile/" + musician.id,
- avatar_url: context.JK.resolveAvatarUrl(musician.photo_url),
- name: musician.name,
- location: musician.location,
- biography: musician.biography,
- friend_count: musician.friend_count,
- follower_count: musician.follower_count,
- recording_count: musician.recording_count,
- session_count: musician.session_count,
- instruments: instrumentLogoHtml
- });
-
- $('#band-profile-members').append(memberHtml);
-
- // wire up Follow button click handler
- configureMemberFollowingButton(musician.is_following, musician.id);
- configureRemoveMemberButton(musician.id, isPending, bandAdmin);
-
- // TODO: wire up Friend button click handler
- // var friend = isFriend(musician.id);
- // configureMemberFriendButton(friend, musician.id);
- });
- if (isPending) {
- $('div[pending-member=true] .btn-reinvite-member').each(function() {
- var btn = $(this);
- btn.show();
- btn.unbind('click');
- btn.click(function() {
- var inviteid = $(this).closest('.band-profile-members').attr('invitation-id');
- rest.resendBandInvitation(bandId, inviteid)
- .done(function (response) {
- app.notifyAlert('Band Invitation', 'Your invitation has been re-sent');
- }).fail(app.ajaxError);
- });
- });
- }
- }
-
- function configureRemoveMemberButton(userId, isPending, bandAdmin) {
- var $divMember = $('div[user-id=' + userId + ']', '#band-profile-members');
- var $btnRemoveMember = $divMember.find('.btn-remove-member');
- if (isMember && !isPending && !bandAdmin) {
- $btnRemoveMember.show();
- $btnRemoveMember.unbind("click");
- $btnRemoveMember.click(function() {
- var confirmMemberDeleteTxt;
- if (userId == context.JK.currentUserId)
- confirmMemberDeleteTxt = 'Are you sure you want to delete yourself from the band?';
- else
- confirmMemberDeleteTxt = 'Are you sure you want to delete this member from the band?';
- var confirmDialog = new context.JK.ConfirmDialog(app, "DELETE", confirmMemberDeleteTxt, "Edit Band Membership",
- function() {
- app.layout.closeDialog('confirm');
- rest.removeBandMember(bandId, userId)
- .done(function() {
- $divMember.remove();
- if (userId == context.JK.currentUserId) {
- $('#btn-edit-band-profile').hide();
- $('#btn-edit-band-info').hide();
- $('#btn-edit-band-members').hide();
- $('.btn-remove-member').each(function(idx) { $(this).hide(); });
- $('.btn-reinvite-member').each(function(idx) { $(this).hide(); });
- }
- })
- .fail(app.ajaxError);
- });
- confirmDialog.initialize();
- context.JK.app.layout.showDialog('confirm');
+ if (following) {
+ $('#btn-follow-band').text('UNFOLLOW');
+ $('#btn-follow-band').click(function() {
+ removeFollowing(true, bandId);
return false;
- });
+ });
+ } else {
+ $('#btn-follow-band').text('FOLLOW');
+ $('#btn-follow-band').click(function() {
+ addFollowing(true, bandId);
+ return false;
+ });
+ }
+ }
+
+ function configureMemberFollowingButton(following, userId) {
+
+ var $btnFollowMember = $('div[user-id=' + userId + ']', '#band-profile-members').find('#btn-follow-member');
+
+ if (context.JK.currentUserId === userId) {
+ $btnFollowMember.hide();
+ } else {
+ $btnFollowMember.unbind("click");
+
+ if (following) {
+ $btnFollowMember.text('UNFOLLOW');
+ $btnFollowMember.click(function() {
+ removeFollowing(false, userId);
+ return false;
+ });
+ }
+ else {
+ $btnFollowMember.text('FOLLOW');
+ $btnFollowMember.click(function() {
+ addFollowing(false, userId);
+ return false;
+ });
+ }
+ }
+ }
+
+ // refreshes the currently active tab
+ function renderActive() {
+
+ if (isMember) {
+ $("#btn-follow-band").hide();
+ $("#btn-edit-band-profile").show();
+ $("#btn-edit-band-bio").show();
+ $("#btn-edit-band-info").show();
+ $("#btn-edit-band-members").show();
+ if (isAdmin) {
+ $("#btn-edit-band-delete").show();
+ }
+ } else {
+ $("#btn-follow-band").show();
+ $("#btn-edit-band-bio").hide();
+ $("#btn-edit-band-profile").hide();
+ $("#btn-edit-band-info").hide();
+ $("#btn-edit-band-members").hide();
+ $("#btn-edit-band-delete").hide();
+ }
+
+ if ($('#band-profile-about-link').hasClass('active')) {
+ renderAbout();
+ } else if ($('#band-profile-history-link').hasClass('active')) {
+ renderHistory();
+ } else if ($('#band-profile-members-link').hasClass('active')) {
+ renderMembers();
+ } else if ($('#band-profile-social-link').hasClass('active')) {
+ renderSocial();
+ }
+ }
+
+ /****************** ABOUT TAB *****************/
+ function renderAbout() {
+
+ $('#band-profile-about').show();
+ $('#band-profile-history').hide();
+ $('#band-profile-members').hide();
+ $('#band-profile-social').hide();
+
+ $('.band-profile-nav a.active').removeClass('active');
+ $('.band-profile-nav a#band-profile-about-link').addClass('active');
+
+
+
+ bindAbout();
+ }
+
+ function bindAbout() {
+
+ rest.getBand(bandId)
+ .done(function(response) {
+ band = response;
+ if (band) {
+ // name
+ $('#band-profile-name').text(band.name);
+
+ // avatar
+ $('#band-profile-avatar').attr('src', context.JK.resolveAvatarUrl(band.photo_url));
+
+ // location
+ $('#band-profile-location').html(band.location);
+
+ // stats
+ var text = band.follower_count > 1 || band.follower_count == 0 ? " Followers" : " Follower";
+ $('#band-profile-follower-stats').html(band.follower_count + text);
+
+ text = band.session_count > 1 || band.session_count == 0 ? " Sessions" : " Session";
+ $('#band-profile-session-stats').html(band.session_count + text);
+
+ text = band.recording_count > 1 || band.recording_count == 0 ? " Recordings" : " Recording";
+ $('#band-profile-recording-stats').html(band.recording_count + text);
+
+ $('#band-profile-biography').text(band.biography);
+
+ renderMusicalExperience()
+ profileUtils.renderPerformanceSamples(band, $root, isAdmin)
+ profileUtils.renderOnlinePresence(band, $root, isAdmin)
+ renderCurrentInterests()
+
+ // wire up Follow click
+ configureBandFollowingButton(band.is_following);
+ }
+ else {
+ logger.debug("No band found with bandId = " + bandId);
+ }
+ })
+ .fail(function(xhr) {
+ if(xhr.status >= 500) {
+ context.JK.fetchUserNetworkOrServerFailure();
+ }
+ else if(xhr.status == 404) {
+ context.JK.entityNotFound("Band");
+ }
+ else {
+ context.JK.app.ajaxError(arguments);
+ }
+ });
+ }
+
+ function renderMusicalExperience() {
+ var genres = buildGenreList(band.genres)
+ var gigs = (band.concert_count > 0) ? 'Has played ' + profileUtils.gigMap[band.concert_count] + ' live concert gigs' : NOT_SPECIFIED_TEXT;
+ var bandType ;
+ if (!band.band_type || typeof(band.band_type)=="undefined" || band.band_type==="") {
+ bandType = "Not specified";
+ } else if (band.band_type.toLowerCase()==="physical") {
+ bandType = "Physical";
+ } else if (band.band_type.toLowerCase()==="virtual") {
+ bandType = "Virtual";
+ } else {
+ bandType = "Not specified";
+ }
+
+ var bandStatus ;
+ if (!band.band_status || typeof(band.band_status)=="undefined" || band.band_status==="") {
+ bandStatus = "Not specified";
+ } else if (band.band_status.toLowerCase()==="amateur") {
+ bandStatus = "Amateur Band";
+ } else if (band.band_status.toLowerCase()==="professional") {
+ bandStatus = "Professional Band";
+ } else {
+ bandStatus = "Not specified";
+ }
+
+ $root.find(".experience-genres").html(genres)
+ $root.find(".experience-gigs").html(gigs)
+ $root.find(".experience-status").html(bandStatus)
+ $root.find(".experience-type").html(bandType)
+
+ }
+
+ function renderCurrentInterests() {
+ if (band.add_new_members) {
+ $root.find(".new-member-details").html(profileUtils.renderBandInstruments(band))
+ $root.find(".interests-new-members").removeClass("hidden")
+ } else {
+ $root.find(".interests-new-members").addClass("hidden")
+ }
+
+ if (band.paid_gigs) {
+ $root.find(".paid-gig-rate").html(band.hourly_rate)
+ $root.find(".paid-gig-minimum").html(band.gig_minimum)
+ $root.find(".interests-paid-gigs").removeClass("hidden")
+ } else {
+ $root.find(".interests-paid-gigs").addClass("hidden")
+ }
+
+ if (band.free_gigs) {
+ $root.find(".interests-free-gigs").removeClass("hidden")
+ } else {
+ $root.find(".interests-free-gigs").addClass("hidden")
+ }
+
+ }
+
+
+ /****************** SOCIAL TAB *****************/
+ function renderSocial() {
+ $('#band-profile-social-followers').empty();
+
+ $('#band-profile-about').hide();
+ $('#band-profile-history').hide();
+ $('#band-profile-members').hide();
+ $('#band-profile-social').show();
+
+ $('.band-profile-nav a.active').removeClass('active');
+ $('.band-profile-nav a#band-profile-social-link').addClass('active');
+
+ bindSocial();
+ }
+
+ function bindSocial() {
+
+ rest.getBandFollowers(bandId)
+ .done(function(response) {
+ $.each(response, function(index, val) {
+ // var template = $('#template-profile-social').html();
+ var template = $('#template-band-profile-social').html();
+ var followerHtml = context.JK.fillTemplate(template, {
+ userId: val.id,
+ hoverAction: val.musician ? "musician" : "fan",
+ avatar_url: context.JK.resolveAvatarUrl(val.photo_url),
+ userName: val.name,
+ location: val.location
+ });
+
+ $('#band-profile-social-followers').append(followerHtml);
+
+ if (index === response.length-1) {
+ context.JK.bindHoverEvents();
+ }
+ })
+ })
+ .fail(function(xhr) {
+ if(xhr.status >= 500) {
+ context.JK.fetchUserNetworkOrServerFailure();
}
else {
- $btnRemoveMember.hide();
+ context.JK.app.ajaxError(arguments);
}
+ });
+ }
+
+ /****************** HISTORY TAB *****************/
+ function renderHistory() {
+ $('#band-profile-about').hide();
+ $('#band-profile-history').show();
+ $('#band-profile-members').hide();
+ $('#band-profile-social').hide();
+
+ $('.band-profile-nav a.active').removeClass('active');
+ $('.band-profile-nav a#band-profile-history-link').addClass('active');
+
+ bindHistory();
+ }
+
+ function bindHistory() {
+ feed.refresh();
+ }
+
+ function buildGenreList(genres) {
+ var list = '';
+
+ for (var i=0; i < genres.length; i++) {
+ list = list.concat(genres[i].id);
+ if (i !== genres.length - 1) {
+ list = list.concat(', ');
+ }
}
- // checks if person viewing the profile is also a band member
- function determineMembership() {
- var url = "/api/bands/" + bandId + "/musicians";
- return $.ajax({
- type: "GET",
- dataType: "json",
- url: url,
- processData:false,
- error: app.ajaxError
- })
- .done(function(response) {
- isAdmin = isMember = false;
- $.each(response, function(index, val) {
- if (val.id === context.JK.currentUserId) {
- isMember = true;
- if (val.band_admin) {
- isAdmin = true;
- }
+ return list;
+ }
+
+ /****************** BANDS TAB *****************/
+ function renderMembers() {
+ $('#band-profile-members').empty();
+
+ $('#band-profile-about').hide();
+ $('#band-profile-history').hide();
+ $('#band-profile-members').show();
+ $('#band-profile-social').hide();
+
+ $('.band-profile-nav a.active').removeClass('active');
+ $('.band-profile-nav a#band-profile-members-link').addClass('active');
+
+ bindMembers();
+ }
+
+ function bindMembers() {
+ rest.getBandMembers(bandId, false)
+ .done(function(response) {
+ bindMusicians(response, false);
+ if (isMember) {
+ bindPendingMembers();
+ }
+ })
+ .fail(function(xhr) {
+ if(xhr.status >= 500) {
+ context.JK.fetchUserNetworkOrServerFailure();
+ }
+ else {
+ context.JK.app.ajaxError(arguments);
+ }
+ });
+ }
+
+ function bindPendingMembers() {
+ rest.getBandMembers(bandId, true)
+ .done(function(response) {
+ if (response && response.length > 0) {
+ $("#band-profile-members").append("
Pending Band Invitations
");
+ bindMusicians(response, true);
+ }
+ })
+ .fail(function(xhr) {
+ if(xhr.status >= 500) {
+ context.JK.fetchUserNetworkOrServerFailure();
+ }
+ else {
+ context.JK.app.ajaxError(arguments);
+ }
+ });
+ }
+
+ function bindMusicians(musicians, isPending) {
+ $.each(musicians, function(index, musician) {
+ var instrumentLogoHtml = '';
+ if ("instruments" in musician && musician.instruments != null) {
+ for (var j=0; j < musician.instruments.length; j++) {
+ var instrument = musician.instruments[j];
+ var inst = '../assets/content/icon_instrument_default24.png';
+ if (instrument.instrument_id in instrument_logo_map) {
+ inst = instrument_logo_map[instrument.instrument_id].asset;
+ }
+ instrumentLogoHtml += '
';
}
- });
- })
+ }
+ var bandAdmin = musician.band_admin;
+ var template = $('#template-band-profile-members').html();
+ var memberHtml = context.JK.fillTemplate(template, {
+ userId: musician.id,
+ band_admin: bandAdmin,
+ is_pending: isPending,
+ invitation_id: isPending ? musician.invitation_id : '',
+ profile_url: "/client#/profile/" + musician.id,
+ avatar_url: context.JK.resolveAvatarUrl(musician.photo_url),
+ name: musician.name,
+ location: musician.location,
+ biography: musician.biography,
+ friend_count: musician.friend_count,
+ follower_count: musician.follower_count,
+ recording_count: musician.recording_count,
+ session_count: musician.session_count,
+ instruments: instrumentLogoHtml
+ });
+
+ $('#band-profile-members').append(memberHtml);
+
+ // wire up Follow button click handler
+ configureMemberFollowingButton(musician.is_following, musician.id);
+ configureRemoveMemberButton(musician.id, isPending, bandAdmin);
+
+ // TODO: wire up Friend button click handler
+ // var friend = isFriend(musician.id);
+ // configureMemberFriendButton(friend, musician.id);
+ });
+ if (isPending) {
+ $('div[pending-member=true] .btn-reinvite-member').each(function() {
+ var btn = $(this);
+ btn.show();
+ btn.unbind('click');
+ btn.click(function() {
+ var inviteid = $(this).closest('.band-profile-members').attr('invitation-id');
+ rest.resendBandInvitation(bandId, inviteid)
+ .done(function (response) {
+ app.notifyAlert('Band Invitation', 'Your invitation has been re-sent');
+ }).fail(app.ajaxError);
+ });
+ });
}
+ }
- // events for main screen
- function events() {
- // wire up panel clicks
- $('#band-profile-about-link').unbind('click').click(renderAbout);
- $('#band-profile-history-link').unbind('click').click(renderHistory);
- $('#band-profile-members-link').unbind('click').click(renderMembers);
- $('#band-profile-social-link').unbind('click').click(renderSocial);
+ function configureRemoveMemberButton(userId, isPending, bandAdmin) {
+ var $divMember = $('div[user-id=' + userId + ']', '#band-profile-members');
+ var $btnRemoveMember = $divMember.find('.btn-remove-member');
+ if (isMember && !isPending && !bandAdmin) {
+ $btnRemoveMember.show();
+ $btnRemoveMember.unbind("click");
+ $btnRemoveMember.click(function() {
+ var confirmMemberDeleteTxt;
+ if (userId == context.JK.currentUserId)
+ confirmMemberDeleteTxt = 'Are you sure you want to delete yourself from the band?';
+ else
+ confirmMemberDeleteTxt = 'Are you sure you want to delete this member from the band?';
+ var confirmDialog = new context.JK.ConfirmDialog(app, "DELETE", confirmMemberDeleteTxt, "Edit Band Membership",
+ function() {
+ app.layout.closeDialog('confirm');
+ rest.removeBandMember(bandId, userId)
+ .done(function() {
+ $divMember.remove();
+ if (userId == context.JK.currentUserId) {
+ $('#btn-edit-band-profile').hide();
+ $('#btn-edit-band-bio').hide();
+ $('#btn-edit-band-info').hide();
+ $('#btn-edit-band-members').hide();
+ $('.btn-remove-member').each(function(idx) { $(this).hide(); });
+ $('.btn-reinvite-member').each(function(idx) { $(this).hide(); });
+ }
+ })
+ .fail(app.ajaxError);
+ });
+ confirmDialog.initialize();
+ context.JK.app.layout.showDialog('confirm');
+ return false;
+ });
+ } else {
+ $btnRemoveMember.hide();
+ }
+ }
- $("#btn-edit-band-profile").unbind('click').click(function() {
- context.location = "/client#/band/setup/" + bandId + '/step1';
- return false;
- });
- $("#btn-edit-band-info").unbind('click').click(function() {
- context.location = "/client#/band/setup/" + bandId + '/step1';
- return false;
- });
- $("#btn-edit-band-members").unbind('click').click(function() {
- context.location = "/client#/band/setup/" + bandId + '/step2';
- return false;
- });
- $("#btn-edit-band-delete").unbind('click').click(function() {
- var confirmDialog = new context.JK.ConfirmDialog(app,
- "DELETE",
- "Are you sure you want to delete this band? This is a permanent action which cannot be undone.",
- "Delete Band",
- function() {
- app.layout.closeDialog('confirm');
- rest.deleteBand(bandId)
- .done(function() {
- context.location = "/client#/profile/"+context.JK.currentUserId;
- })
- .fail(app.ajaxError);
- });
- confirmDialog.initialize();
- context.JK.app.layout.showDialog('confirm');
+ // checks if person viewing the profile is also a band member
+ function determineMembership() {
+ var url = "/api/bands/" + bandId + "/musicians";
+ return $.ajax({
+ type: "GET",
+ dataType: "json",
+ url: url,
+ processData:false,
+ error: app.ajaxError
+ })
+ .done(function(response) {
+ isAdmin = isMember = false;
+ $.each(response, function(index, val) {
+ if (val.id === context.JK.currentUserId) {
+ isMember = true;
+ if (val.band_admin) {
+ isAdmin = true;
+ }
+ }
+ });
+ })
+ }
+
+ // events for main screen
+ function events() {
+ // wire up panel clicks
+ $('#band-profile-about-link').unbind('click').click(renderAbout);
+ $('#band-profile-history-link').unbind('click').click(renderHistory);
+ $('#band-profile-members-link').unbind('click').click(renderMembers);
+ $('#band-profile-social-link').unbind('click').click(renderSocial);
+
+ $("#btn-edit-band-profile").unbind('click').click(function() {
+ context.location = "/client#/band/setup/" + bandId + '/step0';
return false;
- });
- }
+ });
+ $("#btn-edit-band-info").unbind('click').click(function() {
+ context.location = "/client#/band/setup/" + bandId + '/step1';
+ return false;
+ });
+ $("#btn-edit-band-bio").unbind('click').click(function() {
+ context.location = "/client#/band/setup/" + bandId + '/step0';
+ return false;
+ });
+ $("#btn-edit-band-members").unbind('click').click(function() {
+ context.location = "/client#/band/setup/" + bandId + '/step2';
+ return false;
+ });
+ $("#btn-edit-band-delete").unbind('click').click(function() {
+ var confirmDialog = new context.JK.ConfirmDialog(app,
+ "DELETE",
+ "Are you sure you want to delete this band? This is a permanent action which cannot be undone.",
+ "Delete Band",
+ function() {
+ app.layout.closeDialog('confirm');
+ rest.deleteBand(bandId)
+ .done(function() {
+ context.location = "/client#/profile/"+context.JK.currentUserId;
+ })
+ .fail(app.ajaxError);
+ });
+ confirmDialog.initialize();
+ context.JK.app.layout.showDialog('confirm');
+ return false;
+ });
+ }
- function initialize() {
- var screenBindings = {
- 'beforeShow': beforeShow,
- 'afterShow': afterShow
- };
- app.bindScreen('bandProfile', screenBindings);
- }
+ function initialize() {
+ var screenBindings = {
+ 'beforeShow': beforeShow,
+ 'afterShow': afterShow
+ };
+ app.bindScreen('bandProfile', screenBindings);
+
+ var $feedScroller = $history.find(".content-body-scroller")
+ var $feedContent = $feedScroller.find(".feed-content")
+ var $noMoreFeeds = $feedScroller.find('.end-of-list')
+ var $refresh = $history.find('.btn-refresh-entries');
+ var $sortFeedBy = $history.find('#feed_order_by');
+ var $includeDate = $history.find('#feed_date');
+ var $includeType = $history.find('#feed_show');
- this.initialize = initialize;
- this.beforeShow = beforeShow;
- this.afterShow = afterShow;
- return this;
- };
+ feed.initialize($history, $feedScroller, $feedContent, $noMoreFeeds, $refresh, $sortFeedBy, $includeDate, $includeType, {time_range: 'all'})
+ }
+
+ 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/band_setup.js b/web/app/assets/javascripts/band_setup.js
index 44324bc78..4e09a8e47 100644
--- a/web/app/assets/javascripts/band_setup.js
+++ b/web/app/assets/javascripts/band_setup.js
@@ -9,24 +9,107 @@
// accounts_profiles.js
context.JK.BandSetupScreen = function (app) {
- var logger = context.JK.logger;
- var rest = context.JK.Rest();
- var inviteMusiciansUtil = null;
- var invitationDialog = null;
- var autoComplete = null;
- var userNames = [];
- var userIds = [];
- var userPhotoUrls = [];
- var selectedFriendIds = {};
- var nilOptionStr = '';
- var nilOptionText = 'n/a';
- var bandId = '';
- var friendInput=null;
- var step1, step2;
- var isSaving = false;
+ var NONE_SPECIFIED = 'None specified'
+ var GENRE_STEP = 1
+ var SAMPLE_STEP = 3
+ var STEPS_COUNT = 5
+ var currentStep = 0
+ var ui = new context.JK.UIHelper(JK.app)
+ var logger = context.JK.logger
+ var profileUtils = context.JK.ProfileUtils
+ var rest = context.JK.Rest()
+ var inviteMusiciansUtil = null
+ var invitationDialog = null
+ var autoComplete = null
+ var userNames = []
+ var userIds = []
+ var userPhotoUrls = []
+ var selectedFriendIds = {}
+ var nilOptionStr = ''
+ var nilOptionText = 'n/a'
+ var bandId = ''
+ var friendInput=null
+ var bandType=null
+ var bandStatus=null
+ var concertCount=null
- function is_new_record() {
- return bandId.length == 0;
+
+ var $screen=$("#band-setup")
+ var $samples = $screen.find(".account-profile-samples")
+ var $selectedInstruments=[]
+
+ var accountProfileSamples = new JK.AccountProfileSamples(app, $screen, loadBandCallback, rest.updateBand)
+ accountProfileSamples.initialize()
+
+ function navBack() {
+ if (currentStep>0) {
+ saveBand(function() {
+ currentStep--
+ renderCurrentPage()
+ })
+ }
+ }
+
+ function navCancel() {
+ resetForm()
+ window.history.go(-1)
+ return false
+ }
+
+ function navNext() {
+ if (currentStep 0) ? instrumentText : NONE_SPECIFIED)
+ }
+
function showProfile(band_id) {
context.location = "/client#/bandProfile/" + band_id;
}
- function saveBand() {
- if (isSaving) return;
- isSaving = true;
+ function saveInvitations(response) {
+ if (0 < $('#selected-friends-band .invitation').length) {
+ createBandInvitations(response.id, function () {
+ showProfile(response.id);
+ });
+ }
+ }
+ function saveBand(saveBandSuccessFn) {
+
+ unbindNavButtons()
+ removeErrors()
var band = buildBand()
- if (is_new_record()) {
- rest.createBand(band)
- .done(function (response) {
- isSaving = false;
- if (0 < $('#selected-friends-band .invitation').length) {
- createBandInvitations(response.id, function () {
- showProfile(response.id);
- });
- } else
- showProfile(response.id);
- })
- .fail(function (jqXHR) {
- isSaving = false;
- app.notifyServerError(jqXHR, "Unable to create band")
- });
- ;
- }
- else {
- band.id = bandId;
- if (!step1 && !step2){
- rest.updateBand(band)
- .done(function (response) {
- isSaving = false;
- createBandInvitations(band.id, function () {
- showProfile(band.id);
- });
- }).fail(function (jqXHR) {
- isSaving = false;
- app.notifyServerError(jqXHR, "Unable to create band")
- });
- } else {
- if (step1) {
- rest.updateBand(band)
- .done(function (response) {
- isSaving = false;
- app.notifyAlert('Band Information', 'Your changes have been saved');
- }).fail(function (jqXHR) {
- isSaving = false;
- app.notifyServerError(jqXHR, "Unable to update band")
- });
- } else if (step2) {
- isSaving = false;
- if (0 < $('#selected-friends-band .invitation').length) {
- createBandInvitations(bandId, function () {
- app.notifyAlert('Band Members', 'Your invitations have been sent');
- showProfile(bandId);
- });
- } else
- showProfile(bandId);
+ var saveBandFn = (isNewBand()) ? rest.createBand : rest.updateBand
+ saveBandFn(band)
+ .done(function (response) {
+ bandId = response.id
+ saveInvitations(response)
+ if(saveBandSuccessFn) {
+ saveBandSuccessFn(band)
}
- }
- }
+ })
+ .fail(function (jqXHR) {
+ if(jqXHR.status == 422) {
+ renderErrors(JSON.parse(jqXHR.responseText))
+ } else {
+ app.notifyServerError(jqXHR, "Unable to create band")
+ }
+ })
+ .always(function (jqXHR) {
+ bindNavButtons()
+ })
}
function createBandInvitations(bandId, onComplete) {
@@ -219,14 +352,13 @@
function beforeShow(data) {
inviteMusiciansUtil.clearSelections();
bandId = data.id == 'new' ? '' : data.id;
-
- step1 = step2 = false;
- if ('step2'==data['d']) {
- step2 = true;
- delete data['d'];
- } else if ('step1'==data['d']){
- step1 = true;
+ currentStep=0
+ if (data['d']) {
+ var stepNum = data['d'].substring(4)
+ if(stepNum) {
+ currentStep=stepNum
delete data['d'];
+ }
}
resetForm();
}
@@ -234,61 +366,68 @@
function afterShow(data) {
inviteMusiciansUtil.loadFriends();
- if (!is_new_record()) {
- $("#band-setup-title").html("edit band");
- $("#btn-band-setup-save").html("SAVE CHANGES");
+ if (!isNewBand()) {
$("#band-change-photo").html('Upload band photo.');
- $('#tdBandPhoto').css('visibility', 'visible');
+ $('.band-photo').removeClass("hidden")
// retrieve and initialize band profile data points
loadBandDetails();
-
- if (step2) {
- $("#band-setup-step-2").show();
- $("#band-setup-step-1").hide();
- $('.band-setup-text-step2').each(function(idx) { $(this).hide(); });
- $('#btn-band-setup-back').text('CANCEL');
- $('#btn-band-setup-save').text('SEND INVITATIONS');
-
- } else if (step1) {
- $("#band-setup-step-1").show();
- $("#band-setup-step-2").hide();
- $('.band-setup-text-step1').each(function(idx) { $(this).hide(); });
- $('#btn-band-setup-next').text('SAVE');
- }
- if (! step1 && ! step2) {
- $('#btn-band-setup-next').text('NEXT');
- $('#btn-band-setup-back').text('CANCEL');
- $('#btn-band-setup-save').text('CREATE BAND');
- $('.band-setup-text-step1').each(function(idx) { $(this).show(); });
- $('.band-setup-text-step2').each(function(idx) { $(this).show(); });
- }
- }
- else {
+ } else {
loadGenres();
- rest.getResolvedLocation()
- .done(function (location) {
- loadCountries(location.country, function () {
- loadRegions(location.region, function () {
- loadCities(location.city);
- });
+ // Load geo settings:
+ rest.getResolvedLocation().done(function (location) {
+ loadCountries(location.country, function () {
+ loadRegions(location.region, function () {
+ loadCities(location.city);
});
});
+ });
-
- $("#band-setup-title").html("set up band");
- $("#btn-band-setup-save").html("CREATE BAND");
- $('#tdBandPhoto').css('visibility', 'hidden'); // can't upload photo when going through initial setup
+ $('.band-photo').addClass("hidden")
}
+ renderCurrentPage()
}
+ function loadDesiredExperience() {
+
+ }
+
+
function loadBandDetails() {
rest.getBand(bandId).done(function (band) {
$("#band-name").val(band.name);
$("#band-website").val(band.website);
$("#band-biography").val(band.biography);
+ bandType.val(band.band_type)
+ bandStatus.val(band.band_status)
+ concertCount.val(band.concert_count)
+
+ if (band.add_new_members){
+ $("#new-member-no").iCheck('check').attr('checked', 'checked')
+ } else {
+ $("#new-member-yes").iCheck('check').attr('checked', 'checked')
+ }
+
+ if (band.paid_gigs) {
+ $("#paid-gigs-no").iCheck('check').attr('checked', 'checked')
+ } else {
+ $("#paid-gigs-yes").iCheck('check').attr('checked', 'checked')
+ }
+
+ if (band.free_gigs) {
+ $("#free-gigs-no").iCheck('check').attr('checked', 'checked')
+ } else {
+ $("#free-gigs-yes").iCheck('check').attr('checked', 'checked')
+ }
+
+ $('#touring-option').val(band.touring_option ? 'yes' : 'no')
+ $("#play-commitment").val(band.play_commitment)
+ $("#hourly-rate").val(band.hourly_rate)
+ $("#gig-minimum").val(band.gig_minimum)
+
+ // Initialize avatar
if (band.photo_url) {
$("#band-avatar").attr('src', band.photo_url);
}
@@ -301,7 +440,20 @@
});
});
- // TODO: initialize avatar
+ renderOptionalControls();
+
+ $.each(band.instruments, function(index, instrument) {
+ var h = {}
+ h.id = instrument.instrument_id
+ h.level = instrument.proficiency_level
+ h.approve = true
+ $selectedInstruments.push(h)
+ })
+
+ renderDesiredExperienceLabel($selectedInstruments)
+
+ accountProfileSamples.renderPlayer(band)
+
});
}
@@ -432,7 +584,7 @@
} else {
context.JK.dropdown($city);
}
- }
+ }
function addInvitation(value, data) {
if ($('#selected-band-invitees div[user-id=' + data + ']').length === 0) {
@@ -441,8 +593,7 @@
$('#selected-band-invitees').append(invitationHtml);
$('#band-invitee-input').select();
selectedFriendIds[data] = true;
- }
- else {
+ } else {
$('#band-invitee-input').select();
context.alert('Invitation already exists for this musician.');
}
@@ -453,58 +604,55 @@
context.location = '/client#/band/setup/photo/' + bandId;
return false;
}
-
+
function removeInvitation(evt) {
delete selectedFriendIds[$(evt.currentTarget).parent().attr('user-id')];
$(evt.currentTarget).closest('.invitation').remove();
}
+ function bindNavButtons() {
+ $('#btn-band-setup-back').on("click", function (e) {
+ e.stopPropagation()
+ navBack()
+ return false
+ })
+
+ $('#btn-band-setup-cancel').on("click", function (e) {
+ e.stopPropagation()
+ navCancel()
+ return false
+ })
+
+ $('#btn-band-setup-next').on("click", function (e) {
+ e.stopPropagation()
+ navNext()
+ return false
+ })
+
+ $('#btn-band-setup-back').removeClass("disabled")
+ $('#btn-band-setup-cancel').removeClass("disabled")
+ $('#btn-band-setup-next').removeClass("disabled")
+ }
+
+ function unbindNavButtons() {
+ $('#btn-band-setup-back').off("click")
+ $('#btn-band-setup-cancel').off("click")
+ $('#btn-band-setup-next').off("click")
+ $('#btn-band-setup-back').addClass("disabled")
+ $('#btn-band-setup-cancel').addClass("disabled")
+ $('#btn-band-setup-next').addClass("disabled")
+ }
+
function events() {
$('#selected-band-invitees').on("click", ".invitation a", removeInvitation);
+ bindNavButtons();
+
// friend input focus
$('#band-invitee-input').focus(function () {
$(this).val('');
});
- $('#btn-band-setup-cancel').click(function () {
- resetForm();
- window.history.go(-1);
- return false;
- });
-
- $('#btn-band-setup-next').click(function () {
- validateGeneralInfo()
- .done(function (response) {
- if (!step1 && !step2) {
- $("#band-setup-step-2").show();
- $("#band-setup-step-1").hide();
- } else if (step1) {
- saveBand();
- }
- })
- .fail(function (jqXHR) {
- if(jqXHR.status == 422) {
- renderErrors(JSON.parse(jqXHR.responseText))
- }
- else {
- app.notifyServerError(jqXHR, "Unable to validate band")
- }
- });
- });
-
- $('#btn-band-setup-back').click(function () {
- if (!step2) {
- $("#band-setup-step-1").show();
- $("#band-setup-step-2").hide();
- } else {
- showProfile(bandId);
- return false;
- }
- });
-
- $('#btn-band-setup-save').click(saveBand);
-
$('#band-country').on('change', function (evt) {
evt.stopPropagation();
loadRegions();
@@ -519,7 +667,7 @@
});
$('#band-change-photo').click(navigateToBandPhoto);
- $('#band-setup .avatar-profile').click(navigateToBandPhoto);
+ $('#band-setup .band-avatar-profile').click(navigateToBandPhoto);
$('div[layout-id="band/setup"] .btn-email-invitation').click(function () {
invitationDialog.showEmailDialog();
@@ -533,27 +681,57 @@
invitationDialog.showFacebookDialog();
});
+ $('a#choose-desired-experience').on("click", chooseExperience)
+
+ $('#band-setup').on('ifToggled', 'input[type="radio"].dependent-master', renderOptionalControls);
+
$(friendInput).focus(function() { $(this).val(''); })
}
+ function chooseExperience(e) {
+ e.stopPropagation()
+ ui.launchInstrumentSelectorDialog("new member(s)", $selectedInstruments, function(selectedInstruments) {
+ $selectedInstruments = selectedInstruments
+ renderDesiredExperienceLabel($selectedInstruments)
+ return false
+ })
+ return false
+ }
+
+ function loadBandCallback() {
+ return (isNewBand()) ? {} : rest.getBand(bandId)
+ }
+
function initialize(invitationDialogInstance, friendSelectorDialog) {
- inviteMusiciansUtil = new JK.InviteMusiciansUtil(app);
- inviteMusiciansUtil.initialize(friendSelectorDialog);
- friendInput = inviteMusiciansUtil.inviteBandCreate('#band-setup-invite-musicians', "If your bandmates are already on JamKazam, start typing their names in the box below, or click the Choose Friends button to select them.
");
- invitationDialog = invitationDialogInstance;
- events();
+ inviteMusiciansUtil = new JK.InviteMusiciansUtil(app)
+ inviteMusiciansUtil.initialize(friendSelectorDialog)
+
+ friendInput = inviteMusiciansUtil.inviteBandCreate('#band-setup-invite-musicians', "If your bandmates are already on JamKazam, start typing their names in the box below, or click the Choose Friends button to select them.
")
+ invitationDialog = invitationDialogInstance
+ events()
var screenBindings = {
'beforeShow': beforeShow,
'afterShow': afterShow
- };
+ }
- app.bindScreen('band/setup', screenBindings);
+ bandType=$("#band-type")
+ bandStatus=$("#band-status")
+ concertCount=$("#concert-count")
+
+ app.bindScreen('band/setup', screenBindings)
+
+ $screen.find('input[type=radio]').iCheck({
+ checkboxClass: 'icheckbox_minimal',
+ radioClass: 'iradio_minimal',
+ inheritClass: true
+ })
+
+ profileUtils.initializeHelpBubbles()
}
this.initialize = initialize;
this.afterShow = afterShow;
return this;
};
-
})(window, jQuery);
\ No newline at end of file
diff --git a/web/app/assets/javascripts/dialog/genreSelectorDialog.js b/web/app/assets/javascripts/dialog/genreSelectorDialog.js
new file mode 100644
index 000000000..3a9bf4c4f
--- /dev/null
+++ b/web/app/assets/javascripts/dialog/genreSelectorDialog.js
@@ -0,0 +1,83 @@
+(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');
+
+ function beforeShow(data) {
+ }
+
+ function afterShow(data) {
+ var genreList = context.JK.genres;
+
+ $genres.empty();
+
+ if (genreList) {
+ $.each(genreList, function(index, val) {
+ $genres.append('');
+ var checked = '';
+ if (genres && $.inArray(val.id, genres) > -1) {
+ checked = 'checked';
+ }
+
+ $genres.append('' + val.description);
+ $genres.append('');
+ });
+ }
+ }
+
+ function afterHide() {
+ $btnSelect.unbind("click")
+ }
+
+ function showDialog() {
+ return app.layout.showDialog(dialogId);
+ }
+
+ function events() {
+ $btnSelect.unbind("click").bind("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/instrumentSelectorDialog.js b/web/app/assets/javascripts/dialog/instrumentSelectorDialog.js
new file mode 100644
index 000000000..3346eef65
--- /dev/null
+++ b/web/app/assets/javascripts/dialog/instrumentSelectorDialog.js
@@ -0,0 +1,82 @@
+(function(context,$) {
+
+ "use strict";
+ context.JK = context.JK || {};
+ context.JK.InstrumentSelectorDialog = function(app, type, instruments, callback) {
+ var logger = context.JK.logger;
+ var rest = context.JK.Rest();
+ var $dialog = null;
+ var dialogId = 'instrument-selector-dialog';
+ var $screen = $('#' + dialogId);
+ var $btnSelect = $screen.find(".btn-select-instruments");
+ var $instructions = $screen.find('.instructions');
+ var $instruments = $screen.find('.instruments');
+ var $instrumentSelectorContainer = $screen.find('.instrument-selector-container')
+ var instrumentSelector = new JK.InstrumentSelector(app, $instrumentSelectorContainer);
+ var $callback = callback
+ var selectedInstruments = instruments
+ function beforeShow(data) {
+ instrumentSelector.initialize(false)
+ instrumentSelector.render($instrumentSelectorContainer)
+ instrumentSelector.setSelectedInstruments(selectedInstruments)
+ }
+
+ function afterShow(data) {
+ // var instrumentList = context.JK.instruments;
+
+ // $instruments.empty();
+
+ // if (instrumentList) {
+ // $.each(instrumentList, function(index, val) {
+ // $instruments.append('');
+ // var checked = '';
+ // if (instruments && $.inArray(val.id, selectedInstruments) > -1) {
+ // checked = 'checked';
+ // }
+
+ // $instruments.append('' + val.description);
+ // $instruments.append('');
+ // });
+ // }
+ }
+
+ function afterHide() {
+ $btnSelect.unbind("click")
+ }
+
+ function showDialog() {
+ return app.layout.showDialog(dialogId);
+ }
+
+ function events() {
+ $btnSelect.unbind("click").bind("click", function(evt) {
+ evt.preventDefault();
+
+ selectedInstruments = instrumentSelector.getSelectedInstruments()
+ $callback(selectedInstruments)
+ app.layout.closeDialog(dialogId);
+ return false;
+
+ });
+ }
+
+ function initialize() {
+ var dialogBindings = {
+ 'beforeShow' : beforeShow,
+ 'afterShow' : afterShow,
+ 'afterHide': afterHide
+ };
+
+ app.bindDialog(dialogId, dialogBindings);
+
+ $instructions.html('Select the instruments and expertise you need 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/recordingSelectorDialog.js b/web/app/assets/javascripts/dialog/recordingSelectorDialog.js
new file mode 100644
index 000000000..cc9e5dcf9
--- /dev/null
+++ b/web/app/assets/javascripts/dialog/recordingSelectorDialog.js
@@ -0,0 +1,230 @@
+(function(context,$) {
+
+ "use strict";
+ context.JK = context.JK || {};
+ context.JK.RecordingSelectorDialog = function(app, recordings, selectedRecordings, selectCallback) {
+ var logger = context.JK.logger;
+ var rest = context.JK.Rest();
+ var recordingUtils = context.JK.RecordingUtils;
+ 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');
+ var feedHelper = new context.JK.Feed(app);
+
+ function beforeShow(data) {
+ }
+
+ function afterShow(data) {
+
+ $recordings.empty();
+
+ $.each(recordings, function(index, val) {
+ bindRecordingItem(val);
+ });
+
+ // hide the avatars
+ $screen.find('.avatar-small.ib').hide();
+ }
+
+ /********* THE FOLLOWING BLOCK IS REPEATED IN feedHelper.js **********/
+ function startRecordingPlay($feedItem) {
+ var img = $('.play-icon', $feedItem);
+ var $controls = $feedItem.find('.recording-controls');
+ img.attr('src', '/assets/content/icon_pausebutton.png');
+ $controls.trigger('play.listenRecording');
+ $feedItem.data('playing', true);
+ }
+
+ function stopRecordingPlay($feedItem) {
+ var img = $('.play-icon', $feedItem);
+ var $controls = $feedItem.find('.recording-controls');
+ img.attr('src', '/assets/content/icon_playbutton.png');
+ $controls.trigger('pause.listenRecording');
+ $feedItem.data('playing', false);
+ }
+
+ function toggleRecordingPlay() {
+
+ var $playLink = $(this);
+ var $feedItem = $playLink.closest('.feed-entry');
+ var playing = $feedItem.data('playing');
+
+ if(playing) {
+ stopRecordingPlay($feedItem);
+ }
+ else {
+ startRecordingPlay($feedItem);
+ }
+ return false;
+ }
+
+ function toggleRecordingDetails() {
+ var $detailsLink = $(this);
+ var $feedItem = $detailsLink.closest('.feed-entry');
+ var $musicians = $feedItem.find('.musician-detail');
+ var $description = $feedItem.find('.description');
+ var $name = $feedItem.find('.name');
+ var toggledOpen = $detailsLink.data('toggledOpen');
+
+ if(toggledOpen) {
+ toggleClose($feedItem, $name, $description, $musicians)
+ }
+ else {
+ toggleOpen($feedItem, $name, $description, $musicians)
+ }
+
+ toggledOpen = !toggledOpen;
+ $detailsLink.data('toggledOpen', toggledOpen);
+
+ return false;
+ }
+
+ function stateChangeRecording(e, data) {
+ var $controls = data.element;
+ var $feedItem = $controls.closest('.feed-entry');
+
+ var $sliderBar = $('.recording-position', $feedItem);
+ var $statusBar = $('.recording-status', $feedItem);
+ var $currentTime = $('.recording-current', $feedItem);
+ var $status = $('.status-text', $feedItem);
+ var $playButton = $('.play-button', $feedItem);
+
+ if(data.isEnd) stopRecordingPlay($feedItem);
+ if(data.isError) {
+ $sliderBar.hide();
+ $playButton.hide();
+ $currentTime.hide();
+ $statusBar.show();
+ $status.text(data.displayText);
+ }
+ }
+
+ function toggleOpen($feedItem, $name, $description, $musicians) {
+ $description.trigger('destroy.dot');
+ $description.data('original-height', $description.css('height')).css('height', 'auto');
+ $name.trigger('destroy.dot');
+ $name.data('original-height', $name.css('height')).css('height', 'auto');
+ $musicians.show();
+ $feedItem.animate({'max-height': '1000px'});
+ }
+
+ function toggleClose($feedItem, $name, $description, $musicians, immediate) {
+ $feedItem.css('height', $feedItem.height() + 'px')
+ $feedItem.animate({'height': $feedItem.data('original-max-height')}, immediate ? 0 : 400).promise().done(function() {
+ $feedItem.css('height', 'auto').css('max-height', $feedItem.data('original-max-height'));
+
+ $musicians.hide();
+ $description.css('height', $description.data('original-height'));
+ $description.dotdotdot();
+ $name.css('height', $name.data('original-height'));
+ $name.dotdotdot();
+ });
+ }
+ /**********************************************************/
+
+ function bindRecordingItem(claimedRecording) {
+ claimedRecording.recording.mix_info = recordingUtils.createMixInfo({state: claimedRecording.recording.mix_state});
+ var options = {
+ feed_item: claimedRecording.recording,
+ candidate_claimed_recording: claimedRecording,
+ mix_class: claimedRecording['has_mix?'] ? 'has-mix' : 'no-mix',
+ };
+
+ var $feedItem = $(context._.template($('#template-feed-recording').html(), options, {variable: 'data'}));
+ var $controls = $feedItem.find('.recording-controls');
+
+ var $titleText = $feedItem.find('.title .title-text');
+
+ // if this item will be discarded, tack on a * to the RECORDING NAME
+ var discardTime = claimedRecording.recording['when_will_be_discarded?'];
+ if(discardTime) {
+ context.JK.helpBubble($titleText, 'recording-discarded-soon', {discardTime: discardTime}, {});
+ $titleText.text($titleText.text() + '*');
+ }
+
+ $controls.data('mix-state', claimedRecording.recording.mix_info); // for recordingUtils helper methods
+ $controls.data('server-info', claimedRecording.recording.mix); // for recordingUtils helper methods
+ $controls.data('view-context', 'feed');
+
+ $('.timeago', $feedItem).timeago();
+ context.JK.prettyPrintElements($('.duration', $feedItem));
+ context.JK.setInstrumentAssetPath($('.instrument-icon', $feedItem));
+ $('.details', $feedItem).click(toggleRecordingDetails);
+ $('.details-arrow', $feedItem).click(toggleRecordingDetails);
+ $('.play-button', $feedItem).click(toggleRecordingPlay);
+
+ var checked = '';
+
+ var match = $.grep(selectedRecordings, function(obj, index) {
+ return obj.claimed_recording_id === claimedRecording.id;
+ });
+
+ if (match && match.length > 0) {
+ checked = 'checked';
+ }
+
+ // put the item on the page
+ $recordings.append("");
+ $recordings.append($feedItem);
+
+ // these routines need the item to have height to work (must be after renderFeed)
+ $controls.listenRecording({recordingId: claimedRecording.recording.id, claimedRecordingId: options.candidate_claimed_recording.id, sliderSelector:'.recording-slider', sliderBarSelector: '.recording-playback', currentTimeSelector:'.recording-current'});
+ $controls.bind('statechange.listenRecording', stateChangeRecording);
+ $('.dotdotdot', $feedItem).dotdotdot();
+ $feedItem.data('original-max-height', $feedItem.css('height'));
+ context.JK.bindHoverEvents($feedItem);
+ context.JK.bindProfileClickEvents($feedItem);
+ }
+
+ function afterHide() {
+ }
+
+ function showDialog() {
+ return app.layout.showDialog(dialogId);
+ }
+
+ function events() {
+ $btnSelect.click(function(evt) {
+ evt.preventDefault();
+ var preSelectedRecordings = [];
+ $recordings.find('input[type=checkbox]:checked').each(function(index) {
+ preSelectedRecordings.push({
+ "id": $(this).attr('data-recording-id'),
+ "name": $(this).attr('data-recording-title')
+ });
+ });
+
+ if (selectCallback) {
+ selectCallback(preSelectedRecordings);
+ }
+
+ 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 and click ADD to add JamKazam recordings to your performance samples.');
+
+ 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/soundCloudPlayerDialog.js.coffee b/web/app/assets/javascripts/dialog/soundCloudPlayerDialog.js.coffee
new file mode 100644
index 000000000..29d2670bf
--- /dev/null
+++ b/web/app/assets/javascripts/dialog/soundCloudPlayerDialog.js.coffee
@@ -0,0 +1,40 @@
+$ = jQuery
+context = window
+context.JK ||= {}
+
+context.JK.SoundCloudPlayerDialog = class SoundCloudPlayerDialog
+ constructor: (@app) ->
+ @rest = context.JK.Rest()
+ @client = context.jamClient
+ @logger = context.JK.logger
+ @screen = null
+ @dialogId = 'sound-cloud-player-dialog'
+ @dialog = null
+ @player = null
+
+ initialize:(@url, @caption) =>
+ dialogBindings = {
+ 'beforeShow' : @beforeShow,
+ 'afterShow' : @afterShow
+ }
+
+ @dialog = $('[layout-id="' + @dialogId + '"]')
+ @app.bindDialog(@dialogId, dialogBindings)
+ @player = @dialog.find(".sound-cloud-player")
+ @dialog.find(".caption").text("'#{@caption}'")
+ @player.addClass("hidden")
+
+ beforeShow:() =>
+ @player.addClass("hidden")
+ @player.attr("src", "")
+ u = encodeURIComponent(@url)
+ src = "https://w.soundcloud.com/player/?url=#{u}&auto_play=true&hide_related=false&show_comments=true&show_user=true&show_reposts=false&visual=true&loop=true"
+ @player.attr("src", src)
+
+ afterShow:() =>
+ @player.removeClass("hidden")
+
+ showDialog:() =>
+ @app.layout.showDialog(@dialogId)
+
+
\ No newline at end of file
diff --git a/web/app/assets/javascripts/feedHelper.js b/web/app/assets/javascripts/feedHelper.js
index 9ae0b54db..9faf87f33 100644
--- a/web/app/assets/javascripts/feedHelper.js
+++ b/web/app/assets/javascripts/feedHelper.js
@@ -11,6 +11,7 @@
var ui = new context.JK.UIHelper(JK.app);
var recordingUtils = context.JK.RecordingUtils;
var userId = null;
+ var bandId = null;
var currentFeedPage = 0;
var feedBatchSize = 10;
var $screen = null;
@@ -35,6 +36,10 @@
query.user = userId;
}
+ if(bandId) {
+ query.band = bandId;
+ }
+
return query;
}
@@ -83,6 +88,10 @@
userId = _userId;
}
+ function setBand(_bandId) {
+ bandId = _bandId;
+ }
+
function refresh() {
clearResults();
populate();
@@ -99,9 +108,9 @@
function populate() {
if (isLoading || didLoadAllFeeds) return;
-
+
setLoading(true);
-
+
rest.getFeeds(buildQuery())
.done(function(response) {
handleFeedResponse(response);
@@ -242,7 +251,7 @@
return candidate;
}
else {
- return recording.claimed_recordings[0]
+ return recording.claimed_recordings[0];
}
}
@@ -566,7 +575,6 @@
var winheight = $(scrollerID).height();
var docheight = $('#'+screenID()+'-feed-entry-list').height();
var scrollTrigger = 0.90;
- //console.log("feed scroll: wintop="+wintop+" docheight="+docheight+" winheight="+winheight+" ratio="+(wintop / (docheight - winheight)));
if ((wintop / (docheight - winheight)) > scrollTrigger) {
populate();
}
@@ -606,6 +614,7 @@
this.initialize = initialize;
this.refresh = refresh;
this.setUser = setUser;
+ this.setBand = setBand;
return this;
}
diff --git a/web/app/assets/javascripts/instrumentSelector.js b/web/app/assets/javascripts/instrumentSelector.js
index 57d6d6e51..51e740b75 100644
--- a/web/app/assets/javascripts/instrumentSelector.js
+++ b/web/app/assets/javascripts/instrumentSelector.js
@@ -4,18 +4,19 @@
context.JK = context.JK || {};
context.JK.InstrumentSelectorDeferred = null;
- context.JK.InstrumentSelector = (function(app) {
+ context.JK.InstrumentSelector = (function(app, parentSelector) {
var logger = context.JK.logger;
var rest = new context.JK.Rest();
var _instruments = []; // will be list of structs: [ {label:xxx, value:yyy}, {...}, ... ]
var _rsvp = false;
- var _parentSelector = null;
+ if (typeof(_parentSelector)=="undefined") {_parentSelector=null}
+ var _parentSelector = parentSelector;
var deferredInstruments = null;
var self = this;
function reset() {
- $('input[type=checkbox]', _parentSelector).attr('checked', '');
+ $('input[type="checkbox"]', _parentSelector).attr('checked', '');
if (_rsvp) {
$('select.rsvp_count option', _parentSelector).eq(0).prop('selected', true);
$('select.rsvp_level option', _parentSelector).eq(0).prop('selected', true);
@@ -88,7 +89,7 @@
var $selectedVal = $('input[type="checkbox"]:checked', _parentSelector);
$.each($selectedVal, function (index, value) {
var id = $(value).attr('session-instrument-id');
- var name = $('label[for="' + $(value).attr('id') + '"]', _parentSelector).text();
+ var name = $('label[for="' + $(value).attr('id') + '"]', _parentSelector).text().trim();
if (_rsvp) {
var count = $('select[session-instrument-id="' + id + '"].rsvp-count', _parentSelector).val();
var rsvp_level = $('select[session-instrument-id="' + id + '"].rsvp-level', _parentSelector).val();
@@ -99,16 +100,16 @@
selectedInstruments.push({id: id, name: name, level: level});
}
});
+
return selectedInstruments;
}
function setSelectedInstruments(instrumentList) {
if (!instrumentList) {
return;
- }
-
- $.each(instrumentList, function (index, value) {
- $('input[type=checkbox][id="' + value.id + '"]')
+ }
+ $.each(instrumentList, function (index, value) {
+ $('input[type="checkbox"][session-instrument-id="' + value.id + '"]')
.attr('checked', 'checked')
.iCheck({
checkboxClass: 'icheckbox_minimal',
@@ -116,11 +117,11 @@
inheritClass: true
});
if (_rsvp) {
- $('select[session-instrument-id="' + value.value + '"].rsvp-count', _parentSelector).val(value.count);
- $('select[session-instrument-id="' + value.value + '"].rsvp-level', _parentSelector).val(value.level);
+ $('select[session-instrument-id="' + value.id + '"].rsvp-count', _parentSelector).val(value.count);
+ $('select[session-instrument-id="' + value.id + '"].rsvp-level', _parentSelector).val(value.level);
}
else {
- $('select[session-instrument-id="' + value.value + '"]').val(value.level);
+ $('select[session-instrument-id="' + value.id + '"]').val(value.level);
}
});
}
diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js
index 82773d503..a05a24827 100644
--- a/web/app/assets/javascripts/jam_rest.js
+++ b/web/app/assets/javascripts/jam_rest.js
@@ -188,7 +188,7 @@
if (includePending) {
includeFlag = 'true';
}
-
+
return $.ajax({
type: "GET",
dataType: "json",
@@ -505,6 +505,20 @@
return detail;
}
+ function getUserProfile(options) {
+ var id = getId(options);
+ var profile = null;
+ if (id != null && typeof(id) != 'undefined') {
+ profile = $.ajax({
+ type: "GET",
+ dataType: "json",
+ url: "/api/users/" + id + "/profile",
+ processData: false
+ });
+ }
+ return profile;
+ }
+
function createAffiliatePartner(options) {
return $.ajax({
type: "POST",
@@ -1273,7 +1287,7 @@
function openBackingTrack(options) {
var musicSessionId = options["id"];
delete options["id"];
-
+
return $.ajax({
type: "POST",
dataType: "json",
@@ -1336,7 +1350,7 @@
function openMetronome(options) {
var musicSessionId = options["id"];
delete options["id"];
-
+
return $.ajax({
type: "POST",
dataType: "json",
@@ -1345,11 +1359,11 @@
data: JSON.stringify(options)
})
}
-
+
function closeMetronome(options) {
var musicSessionId = options["id"];
delete options["id"];
-
+
return $.ajax({
type: "POST",
dataType: "json",
@@ -1358,7 +1372,7 @@
data: JSON.stringify(options)
})
}
-
+
function discardRecording(options) {
var recordingId = options["id"];
@@ -1597,7 +1611,7 @@
url: '/api/recurly/payment_history',
dataType: "json",
contentType: 'application/json'
- });
+ });
}
function getSalesHistory(options) {
@@ -1693,7 +1707,7 @@
function placeOrder() {
return $.ajax({
- type: "POST",
+ type: "POST",
url: '/api/recurly/place_order',
dataType: "json",
contentType: 'application/json'
@@ -1707,6 +1721,19 @@
});
}
+ function getMusicianSearchFilter(query) {
+ var qarg = query === undefined ? '' : query;
+ return $.get("/api/search/musicians.json?"+qarg);
+ }
+
+ function postMusicianSearchFilter(query) {
+ return $.ajax({
+ type: "POST",
+ url: "/api/search/musicians.json",
+ data: query
+ });
+ }
+
function getMount(options) {
var id = getId(options);
return $.ajax({
@@ -1807,6 +1834,7 @@
this.cancelSession = cancelSession;
this.updateScheduledSession = updateScheduledSession;
this.getUserDetail = getUserDetail;
+ this.getUserProfile = getUserProfile;
this.getAffiliatePartnerData = getAffiliatePartnerData;
this.postAffiliatePartnerData = postAffiliatePartnerData;
this.createAffiliatePartner = createAffiliatePartner;
@@ -1889,7 +1917,7 @@
this.openJamTrack = openJamTrack
this.openBackingTrack = openBackingTrack
this.closeBackingTrack = closeBackingTrack
- this.closeMetronome = closeMetronome;
+ this.closeMetronome = closeMetronome;
this.closeJamTrack = closeJamTrack;
this.openMetronome = openMetronome;
this.closeMetronome = closeMetronome;
@@ -1947,6 +1975,8 @@
this.validateUrlSite = validateUrlSite;
this.markRecordedBackingTrackSilent = markRecordedBackingTrackSilent;
this.addRecordingTimeline = addRecordingTimeline;
+ this.getMusicianSearchFilter = getMusicianSearchFilter;
+ this.postMusicianSearchFilter = postMusicianSearchFilter;
this.playJamTrack = playJamTrack;
this.createSignupHint = createSignupHint;
this.createAlert = createAlert;
@@ -1954,5 +1984,4 @@
this.portOverCarts = portOverCarts;
return this;
};
-
})(window,jQuery);
diff --git a/web/app/assets/javascripts/musician_search_filter.js.coffee b/web/app/assets/javascripts/musician_search_filter.js.coffee
new file mode 100644
index 000000000..4187d6acd
--- /dev/null
+++ b/web/app/assets/javascripts/musician_search_filter.js.coffee
@@ -0,0 +1,417 @@
+$ = jQuery
+context = window
+context.JK ||= {};
+
+context.JK.MusicianSearchFilter = class MusicianSearchFilter
+
+ constructor: () ->
+ @rest = context.JK.Rest()
+ @logger = context.JK.logger
+ @searchFilter = null
+ @profileUtils = context.JK.ProfileUtils
+ @helpBubble = context.JK.HelpBubbleHelper
+ @searchResults = null
+ @isSearching = false
+ @pageNumber = 1
+ @instrument_logo_map = context.JK.getInstrumentIconMap24()
+
+ init: (app) =>
+ @app = app
+ @screenBindings = { 'afterShow': this.afterShow, 'afterHide': this.afterHide }
+ @app.bindScreen('musicians', @screenBindings)
+
+ @screen = $('#musicians-screen')
+ @resultsListContainer = @screen.find('#musician-search-filter-results-list')
+ @spinner = @screen.find('.paginate-wait')
+
+ this.registerResultsPagination()
+
+ @screen.find('#btn-musician-search-builder').on 'click', =>
+ this.showBuilder()
+
+ @screen.find('#btn-musician-search-reset').on 'click', =>
+ this.resetFilter()
+
+ afterShow: () =>
+ @screen.find('#musician-search-filter-results').show()
+ @screen.find('#musician-search-filter-builder').hide()
+ this.getUserFilterResults()
+
+ showBuilder: () =>
+ @screen.find('#musician-search-filter-results').hide()
+ @screen.find('#musician-search-filter-builder').show()
+ @resultsListContainer.empty()
+
+ afterHide: () =>
+ @resultsListContainer.empty()
+
+ renderSearchFilter: () =>
+ $.when(this.rest.getMusicianSearchFilter()).done (sFilter) =>
+ this.loadSearchFilter(sFilter)
+
+ loadSearchFilter: (sFilter) =>
+ @searchFilter = JSON.parse(sFilter)
+ args =
+ interests: @searchFilter.data_blob.interests
+ skill_level: @searchFilter.data_blob.skill_level
+ studio_sessions: @searchFilter.data_blob.studio_sessions
+ concert_gigs: @searchFilter.data_blob.concert_gigs
+
+ template = context.JK.fillTemplate(@screen.find('#template-musician-search-filter').html(), args)
+
+ content_root = @screen.find('#musician-search-filter-builder')
+ content_root.html template
+
+ @screen.find('#btn-perform-musician-search').on 'click', =>
+ this.performSearch()
+
+ @screen.find('#btn-musician-search-cancel').on 'click', =>
+ this.cancelFilter()
+
+ this._populateSkill()
+ this._populateStudio()
+ this._populateGigs()
+ this._populateInterests()
+ this._populateAges()
+ this._populateGenres()
+ this._populateInstruments()
+ this._populateSortOrder()
+
+
+
+ _populateSelectWithKeys: (struct, selection, keys, element) =>
+ element.children().remove()
+ $.each keys, (idx, value) =>
+ label = struct[value]
+ blankOption = $ '
'
+ blankOption.text label
+ blankOption.attr 'value', value
+ blankOption.attr 'selected', '' if value == selection
+ element.append(blankOption)
+ context.JK.dropdown(element)
+
+ _populateSelectIdentifier: (identifier) =>
+ elem = $ '#musician-search-filter-builder select[name='+identifier+']'
+ struct = gon.musician_search_meta[identifier]['map']
+ keys = gon.musician_search_meta[identifier]['keys']
+ this._populateSelectWithKeys(struct, @searchFilter[identifier], keys, elem)
+
+ _populateSelectWithInt: (sourceStruct, selection, element) =>
+ struct =
+ '-1': 'Any'
+ $.extend(struct, sourceStruct)
+ this._populateSelectWithKeys(struct, selection, Object.keys(struct).sort(), element)
+
+ _populateSortOrder: () =>
+ this._populateSelectIdentifier('sort_order')
+
+ _populateInterests: () =>
+ this._populateSelectIdentifier('interests')
+
+ _populateStudio: () =>
+ elem = $ '#musician-search-filter-builder select[name=studio_sessions]'
+ this._populateSelectWithInt(@profileUtils.studioMap, @searchFilter.data_blob.studio_sessions.toString(), elem)
+
+ _populateGigs: () =>
+ elem = $ '#musician-search-filter-builder select[name=concert_gigs]'
+ this._populateSelectWithInt(@profileUtils.gigMap, @searchFilter.data_blob.concert_gigs.toString(), elem)
+
+ _populateSkill: () =>
+ elem = $ '#musician-search-filter-builder select[name=skill_level]'
+ this._populateSelectWithInt(@profileUtils.skillLevelMap, @searchFilter.data_blob.skill_level.toString(), elem)
+
+ _populateAges: () =>
+ @screen.find('#search-filter-ages').empty()
+ ages_map = gon.musician_search_meta['ages']['map']
+ $.each gon.musician_search_meta['ages']['keys'], (index, key) =>
+ ageTemplate = @screen.find('#template-search-filter-setup-ages').html()
+ selected = ''
+ ageLabel = ages_map[key]
+ if 0 < @searchFilter.data_blob.ages.length
+ key_val = key.toString()
+ ageMatch = $.grep(@searchFilter.data_blob.ages, (n, i) ->
+ n == key_val)
+ selected = 'checked' if ageMatch.length > 0
+ ageHtml = context.JK.fillTemplate(ageTemplate,
+ id: key
+ description: ageLabel
+ checked: selected)
+ @screen.find('#search-filter-ages').append ageHtml
+
+ _populateGenres: () =>
+ @screen.find('#search-filter-genres').empty()
+ @rest.getGenres().done (genres) =>
+ genreTemplate = @screen.find('#template-search-filter-setup-genres').html()
+ selected = ''
+ $.each genres, (index, genre) =>
+ if 0 < @searchFilter.data_blob.genres.length
+ genreMatch = $.grep(@searchFilter.data_blob.genres, (n, i) ->
+ n == genre.id)
+ else
+ genreMatch = []
+ selected = 'checked' if genreMatch.length > 0
+ genreHtml = context.JK.fillTemplate(genreTemplate,
+ id: genre.id
+ description: genre.description
+ checked: selected)
+ @screen.find('#search-filter-genres').append genreHtml
+
+ _populateInstruments: () =>
+ @screen.find('#search-filter-instruments').empty()
+ @rest.getInstruments().done (instruments) =>
+ $.each instruments, (index, instrument) =>
+ instrumentTemplate = @screen.find('#template-search-filter-setup-instrument').html()
+ selected = ''
+ proficiency = '1'
+ if 0 < @searchFilter.data_blob.instruments.length
+ instMatch = $.grep(@searchFilter.data_blob.instruments, (inst, i) ->
+ yn = inst.instrument_id == instrument.id
+ proficiency = inst.proficiency_level if yn
+ yn)
+ selected = 'checked' if instMatch.length > 0
+ instrumentHtml = context.JK.fillTemplate(instrumentTemplate,
+ id: instrument.id
+ description: instrument.description
+ checked: selected)
+ @screen.find('#search-filter-instruments').append instrumentHtml
+ profsel = '#search-filter-instruments tr[data-instrument-id="'+instrument.id+'"] select'
+ jprofsel = @screen.find(profsel)
+ jprofsel.val(proficiency)
+ context.JK.dropdown(jprofsel)
+ return true
+
+ _builderSelectValue: (identifier) =>
+ elem = $ '#musician-search-filter-builder select[name='+identifier+']'
+ elem.val()
+
+ _builderSelectMultiValue: (identifier) =>
+ vals = []
+ elem = $ '#search-filter-'+identifier+' input[type=checkbox]:checked'
+ if 'instruments' == identifier
+ elem.each (idx) ->
+ row = $(this).parent().parent()
+ instrument =
+ instrument_id: row.data('instrument-id')
+ proficiency_level: row.find('select').val()
+ vals.push instrument
+ else
+ elem.each (idx) ->
+ vals.push $(this).val()
+ vals
+
+ willSearch: (reload) =>
+ return false if @isSearching
+ @isSearching = true
+ if reload
+ @pageNumber = 1
+ @screen.find('#musician-search-filter-spinner').show()
+ @resultsListContainer.empty()
+ @screen.find('#musician-search-filter-builder').hide()
+ @screen.find('#musician-search-filter-results').show()
+ true
+
+ didSearch: (response) =>
+ this.loadSearchFilter(response.filter_json)
+ @searchResults = response
+ @screen.find('#musician-search-filter-spinner').hide()
+ this.renderMusicians()
+ @screen.find('.paginate-wait').hide()
+ @isSearching = false
+
+ resetFilter: () =>
+ if this.willSearch(true)
+ @rest.postMusicianSearchFilter({ filter: 'reset' }).done(this.didSearch)
+
+ cancelFilter: () =>
+ this.resetFilter()
+
+ getUserFilterResults: () =>
+ if this.willSearch(true)
+ @rest.getMusicianSearchFilter('results=true').done(this.didSearch)
+
+ performSearch: () =>
+ if this.willSearch(true)
+ $.each gon.musician_search_meta.filter_keys.single, (index, key) =>
+ @searchFilter[key] = this._builderSelectValue(key)
+ $.each gon.musician_search_meta.filter_keys.multi, (index, key) =>
+ @searchFilter[key] = this._builderSelectMultiValue(key)
+ @rest.postMusicianSearchFilter({ filter: JSON.stringify(@searchFilter), page: @pageNumber }).done(this.didSearch)
+
+ renderResultsHeader: () =>
+ @screen.find('#musician-search-filter-description').html(@searchResults.description)
+ if @searchResults.is_blank_filter
+ @screen.find('#btn-musician-search-reset').hide()
+ else
+ @screen.find('#btn-musician-search-reset').show()
+
+ renderMusicians: () =>
+ this.renderResultsHeader() if @pageNumber == 1
+ musicians = @searchResults.musicians
+ len = musicians.length
+ if 0 == len
+ @screen.find('#musician-search-filter-results-list-blank').show()
+ @screen.find('#musician-search-filter-results-list-blank').html('No results found')
+ return
+ else
+ @screen.find('#musician-search-filter-results-list-blank').hide()
+
+ ii = 0
+ mTemplate = @screen.find('#template-search-musician-row').html()
+ aTemplate = @screen.find('#template-search-musician-action-btns').html()
+ mVals = undefined
+ musician = undefined
+ renderings = ''
+ instr_logos = undefined
+ follows = undefined
+ followVals = undefined
+ aFollow = undefined
+ myAudioLatency = @searchResults.my_audio_latency
+ while ii < len
+ musician = musicians[ii]
+ if context.JK.currentUserId == musician.id
+ ii++
+ continue
+ instr_logos = ''
+ jj = 0
+ ilen = musician['instruments'].length
+ while jj < ilen
+ instr_id = musician['instruments'][jj].instrument_id
+ if instr_img = @instrument_logo_map[instr_id]
+ instr_logos += '

'
+ jj++
+ actionVals =
+ profile_url: '/client#/profile/' + musician.id
+ friend_class: 'button-' + (if musician['is_friend'] then 'grey' else 'orange')
+ friend_caption: (if musician.is_friend then 'DIS' else '') + 'CONNECT'
+ follow_class: 'button-' + (if musician['is_following'] then 'grey' else 'orange')
+ follow_caption: (if musician.is_following then 'UN' else '') + 'FOLLOW'
+ message_class: 'button-orange'
+ message_caption: 'MESSAGE'
+ button_message: 'button-orange'
+ musician_actions = context.JK.fillTemplate(aTemplate, actionVals)
+ latencyBadge = context._.template($("#template-account-session-latency").html(), $.extend(sessionUtils.createLatency(musician), musician), variable: 'data')
+ mVals =
+ avatar_url: context.JK.resolveAvatarUrl(musician.photo_url)
+ profile_url: '/client#/profile/' + musician.id
+ musician_name: musician.name
+ musician_location: this._formatLocation(musician)
+ instruments: instr_logos
+ biography: musician['biography']
+ follow_count: musician['follow_count']
+ friend_count: musician['friend_count']
+ recording_count: musician['recording_count']
+ session_count: musician['session_count']
+ musician_id: musician['id']
+ musician_action_template: musician_actions
+ latency_badge: latencyBadge
+ musician_first_name: musician['first_name']
+ $rendering = $(context.JK.fillTemplate(mTemplate, mVals))
+ $offsetParent = @resultsListContainer.closest('.content')
+ data = entity_type: 'musician'
+ options =
+ positions: [
+ 'top'
+ 'bottom'
+ 'right'
+ 'left'
+ ]
+ offsetParent: $offsetParent
+ scoreOptions = offsetParent: $offsetParent
+ context.JK.helpBubble($('.follower-count', $rendering), 'follower-count', data, options);
+ context.JK.helpBubble($('.friend-count', $rendering), 'friend-count', data, options);
+ context.JK.helpBubble($('.recording-count', $rendering), 'recording-count', data, options);
+ context.JK.helpBubble($('.session-count', $rendering), 'session-count', data, options);
+ @helpBubble.scoreBreakdown $('.latency', $rendering), false, musician['full_score'], myAudioLatency, musician['audio_latency'], musician['score'], scoreOptions
+ @resultsListContainer.append $rendering
+ $rendering.find('.biography').dotdotdot()
+ ii++
+
+ this._bindMessageMusician()
+ this._bindFriendMusician()
+ this._bindFollowMusician()
+
+ context.JK.bindHoverEvents()
+ return
+
+ _bindMessageMusician: () =>
+ objThis = this
+ @screen.find('.search-m-message').on 'click', (evt) ->
+ userId = $(this).parent().data('musician-id')
+ objThis.app.layout.showDialog 'text-message', d1: userId
+
+
+ _bindFriendMusician: () =>
+ objThis = this
+ @screen.find('.search-m-friend').on 'click', (evt) ->
+ # if the musician is already a friend, remove the button-orange class, and prevent the link from working
+ if 0 == $(this).closest('.button-orange').size()
+ return false
+ $(this).click (ee) ->
+ ee.preventDefault()
+ return
+ evt.stopPropagation()
+ uid = $(this).parent().data('musician-id')
+ objThis.rest.sendFriendRequest objThis.app, uid, this.friendRequestCallback
+
+ _bindFollowMusician: () =>
+ objThis = this
+ @screen.find('.search-m-follow').on 'click', (evt) ->
+ # if the musician is already followed, remove the button-orange class, and prevent the link from working
+ if 0 == $(this).closest('.button-orange').size()
+ return false
+ $(this).click (ee) ->
+ ee.preventDefault()
+ return
+ evt.stopPropagation()
+ newFollowing = {}
+ newFollowing.user_id = $(this).parent().data('musician-id')
+ url = '/api/users/' + context.JK.currentUserId + '/followings'
+ $.ajax
+ type: 'POST'
+ dataType: 'json'
+ contentType: 'application/json'
+ url: url
+ data: JSON.stringify(newFollowing)
+ processData: false
+ success: (response) ->
+ # remove the orange look to indicate it's not selectable
+ # @FIXME -- this will need to be tweaked when we allow unfollowing
+ objThis.screen.find('div[data-musician-id=' + newFollowing.user_id + '] .search-m-follow').removeClass('button-orange').addClass 'button-grey'
+ return
+ error: objThis.app.ajaxError
+
+ _formatLocation: (musician) ->
+ if musician.city and musician.state
+ musician.city + ', ' + musician.state
+ else if musician.city
+ musician.city
+ else if musician.regionname
+ musician.regionname
+ else
+ 'Location Unavailable'
+
+ friendRequestCallback: (user_id)=>
+ # TODO:
+
+ paginate: () =>
+ if @pageNumber < @searchResults.page_count && this.willSearch(false)
+ @screen.find('.paginate-wait').show()
+ @pageNumber += 1
+ @rest.postMusicianSearchFilter({ filter: JSON.stringify(@searchFilter), page: @pageNumber }).done(this.didSearch)
+ return true
+ false
+
+ registerResultsPagination: () =>
+ _resultsListContainer = @resultsListContainer
+ _headerHeight = @screen.find('#musician-search-filter-results-header').height()
+ _paginator = this.paginate
+
+ @screen.find('.content-body-scroller').scroll ->
+ if _resultsListContainer.is(':visible')
+ jthis = $(this)
+ wintop = jthis.scrollTop()
+ winheight = jthis.innerHeight()
+ docheight = jthis[0].scrollHeight - _headerHeight
+ scrollTrigger = 0.98;
+ if ((wintop / (docheight - winheight)) >= scrollTrigger)
+ _paginator()
diff --git a/web/app/assets/javascripts/profile.js b/web/app/assets/javascripts/profile.js
index 49543492d..fe616b7d7 100644
--- a/web/app/assets/javascripts/profile.js
+++ b/web/app/assets/javascripts/profile.js
@@ -11,24 +11,107 @@
var rest = context.JK.Rest();
var decrementedFriendCountOnce = false;
var sentFriendRequest = false;
- var profileScreen = null;
var textMessageDialog = null;
var feed = null;
+ var player = null;
+ var profileUtils = context.JK.ProfileUtils;
+
+ var NOT_SPECIFIED_TEXT = 'Not specified';
+
+ var $screen = $('#user-profile');
+
+ var $biography = $screen.find('#biography');
+
+ // musical experience
+ var $instruments = $screen.find('#instruments');
+ var $musicianStatus = $screen.find('#musician-status');
+ var $genres = $screen.find('#genres');
+ var $concertCount = $screen.find('#concert-count');
+ var $studioCount = $screen.find('#studio-count');
+
+ // performance samples
+ var $noSamples = $screen.find('.no-samples');
+ var $jamkazamSamples = $screen.find('.jamkazam-samples');
+ var $soundCloudSamples = $screen.find('.soundcloud-samples');
+ var $youTubeSamples = $screen.find('.youtube-samples');
+
+ // online presence
+ var $noOnlinePresence = $screen.find('.no-online-presence');
+ var $userWebsite = $screen.find('.user-website');
+ var $soundCloudPresence = $screen.find('.soundcloud-presence');
+ var $reverbNationPresence = $screen.find('.reverbnation-presence');
+ var $bandCampPresence = $screen.find('.bandcamp-presence');
+ var $fandalismPresence = $screen.find('.fandalism-presence');
+ var $youTubePresence = $screen.find('.youtube-presence');
+ var $facebookPresence = $screen.find('.facebook-presence');
+ var $twitterPresence = $screen.find('.twitter-presence');
+
+ // current interests
+ var $noInterests = $screen.find('#no-interests');
+ var $paidGigSection = $screen.find('#paid-gigs');
+ var $paidGigDetails = $screen.find('#paid-gig-details');
+
+ var $freeGigSection = $screen.find('#free-gigs');
+ var $freeGigDetails = $screen.find('#free-gig-details');
+
+ var $cowritingSection = $screen.find('#cowriting');
+ var $cowritingDetails = $screen.find('#cowriting-details');
+
+ var $traditionalBandSection = $screen.find('#traditional-band');
+ var $traditionalBandDetails = $screen.find('#traditional-band-details');
+
+ var $virtualBandSection = $screen.find('#virtual-band');
+ var $virtualBandDetails = $screen.find('#virtual-band-details');
+
+ // tabs
+ var $aboutLink = $screen.find('#about-link');
+ var $aboutContent = $screen.find('#about-content');
+
+ var $historyLink = $screen.find('#history-link');
+ var $historyContent = $screen.find('#history-content');
+
+ var $bandsLink = $screen.find('#bands-link');
+ var $bandsContent = $screen.find('#bands-content');
+
+ var $socialLink = $screen.find('#social-link');
+ var $socialContent = $screen.find('#social-content');
+
+ var $favoritesLink = $screen.find('#favorites-link');
+ var $favoritesContent = $screen.find('#favorites-content');
+
+ // stats
+ var $friendStats = $screen.find('#friend-stats');
+ var $followerStats = $screen.find('#follower-stats');
+ var $sessionStats = $screen.find('#session-stats');
+ var $recordingStats = $screen.find('#recording-stats');
+ var $followingStats = $screen.find('#following-stats');
+ var $favoriteStats = $screen.find('#favorite-stats');
+
+ // miscellaneous
+ var $userName = $screen.find('#username');
+ var $avatar = $screen.find('#avatar');
+ var $typeLabel = $screen.find('#type-label');
+ var $location = $screen.find('#location');
+ var $age = $screen.find('#age');
+
+ // buttons
+ var $btnEdit = $screen.find('#btn-edit');
+ var $btnAddFriend = $screen.find('#btn-add-friend');
+ var $btnFollowUser = $screen.find('#btn-follow-user');
+ var $btnMessageUser = $screen.find('#btn-message-user');
+ var $btnEditBio = $screen.find(".add-bio");
+ var $btnAddRecordings = $screen.find('.add-recordings');
+ var $btnAddSites = $screen.find('.add-sites');
+ var $btnAddInterests = $screen.find('.add-interests');
+
+ // social
+ var $socialLeft = $screen.find('.profile-social-left');
+ var $socialFriends = $screen.find('#social-friends');
+ var $socialFollowings = $screen.find('#social-followings');
+ var $socialFollowers = $screen.find('#social-followers');
var instrument_logo_map = context.JK.getInstrumentIconMap24();
- var proficiencyDescriptionMap = {
- "1": "BEGINNER",
- "2": "INTERMEDIATE",
- "3": "EXPERT"
- };
-
- var proficiencyCssMap = {
- "1": "proficiency-beginner",
- "2": "proficiency-intermediate",
- "3": "proficiency-expert"
- };
-
function beforeShow(data) {
userId = data.id;
feed.setUser(userId);
@@ -37,6 +120,7 @@
function afterShow(data) {
initUser();
resetForm();
+ renderAllStats();
}
function beforeHide(data) {
@@ -44,27 +128,28 @@
}
function resetForm() {
- $('#profile-instruments').empty();
+ $instruments.empty();
- $('#profile-about').show();
- $('#profile-history').hide();
- $('#profile-bands').hide();
- $('#profile-social').hide();
- $('#profile-favorites').hide();
+ $aboutContent.show();
+ $historyContent.hide();
+ $bandsContent.hide();
+ $socialContent.hide();
+ $favoritesContent.hide();
$('.profile-nav a.active').removeClass('active');
- $('.profile-nav a#profile-about-link').addClass('active');
+ $aboutLink.addClass('active');
}
function initUser() {
user = null;
decrementedFriendCountOnce = false;
sentFriendRequest = false;
- userDefer = rest.getUserDetail({id: userId})
+ userDefer = rest.getUserProfile({id: userId})
.done(function (response) {
user = response;
configureUserType();
renderActive();
+ renderAllStats();
})
.fail(function (jqXHR) {
if (jqXHR.status >= 500) {
@@ -89,43 +174,41 @@
function configureUserType() {
if (isMusician()) {
- $('#profile-history-link').show();
- $('#profile-bands-link').show();
- $('#profile-instruments').show();
- $('#profile-session-stats').show();
- $('#profile-recording-stats').show();
- // $('#profile-following-stats').hide();
- // $('#profile-favorites-stats').hide();
- $('.profile-social-left').show();
- $('#profile-type-label').text('musician');
- $('#profile-location-label').text('Location');
- }
- else {
- $('#profile-history-link').hide();
- $('#profile-bands-link').hide();
- $('#profile-instruments').hide();
- $('#profile-session-stats').hide();
- $('#profile-recording-stats').hide();
- // $('#profile-following-stats').show();
- // $('#profile-favorites-stats').show();
- $('.profile-social-left').hide();
- $('#profile-type-label').text('fan');
- $('#profile-location-label').text('Presence');
+ $historyLink.show();
+ $bandsLink.show();
+ $instruments.show();
+ $sessionStats.show();
+ $recordingStats.show();
+ // $followingStats.hide();
+ // $favoriteStats.hide();
+ $socialLeft.show();
+ $typeLabel.text('musician');
+ $location.text('Location');
+ } else {
+ $historyLink.hide();
+ $bandsLink.hide();
+ $instruments.hide();
+ $sessionStats.hide();
+ $recordingStats.hide();
+ // $followingStats.show();
+ // $favoriteStats.show();
+ $socialLeft.hide();
+ $typeLabel.text('fan');
+ $location.text('Presence');
}
if (isCurrentUser()) {
- $('#btn-profile-edit').show();
- $('#btn-add-friend').hide();
- $('#btn-follow-user').hide();
- $('#btn-message-user').hide();
- }
- else {
+ $btnEdit.show();
+ $btnAddFriend.hide();
+ $btnFollowUser.hide();
+ $btnMessageUser.hide();
+ } else {
configureFriendFollowersControls();
- $('#btn-profile-edit').hide();
- $('#btn-add-friend').show();
- $('#btn-follow-user').show();
- $('#btn-message-user').show();
+ $btnEdit.hide();
+ $btnAddFriend.show();
+ $btnFollowUser.show();
+ $btnMessageUser.show();
}
}
@@ -141,33 +224,48 @@
// events for main screen
function events() {
// wire up panel clicks -- these need to check deferred because they can't be hidden when in an invalid state
- $('#profile-about-link').click(function () {
+ $aboutLink.click(function () {
renderTabDeferred(renderAbout)
});
- $('#profile-history-link').click(function () {
+
+ $historyLink.click(function () {
renderTabDeferred(renderHistory)
});
- $('#profile-bands-link').click(function () {
+
+ $bandsLink.click(function () {
renderTabDeferred(renderBands)
});
- $('#profile-social-link').click(function () {
+
+ $socialLink.click(function () {
renderTabDeferred(renderSocial)
});
- $('#profile-favorites-link').click(function () {
+
+ $favoritesLink.click(function () {
renderTabDeferred(renderFavorites)
});
// this doesn't need deferred because it's only shown when valid
- $('#btn-add-friend').click(handleFriendChange);
- $('#btn-follow-user').click(handleFollowingChange);
- $('#btn-message-user').click(handleMessageMusician);
+ $btnAddFriend.click(handleFriendChange);
+ $btnFollowUser.click(handleFollowingChange);
+ $btnMessageUser.click(handleMessageMusician);
+
+ // Hook up soundcloud player:
+ $soundCloudSamples.off("click", "a.sound-cloud-playable") .on("click", "a.sound-cloud-playable", playSoundCloudFile)
+ }
+
+ function playSoundCloudFile(e) {
+ e.preventDefault();
+ var url = $(this).attr("soundcloud_url")
+ var cap = $(this).text()
+ player.initialize(url, cap);
+ app.layout.showDialog('sound-cloud-player-dialog');
+ return false;
}
function handleFriendChange(evt) {
if (isFriend()) {
removeFriend(evt);
- }
- else {
+ } else {
sendFriendRequest(evt);
}
return false;
@@ -176,8 +274,7 @@
function handleFollowingChange(evt) {
if (isFollowing()) {
removeFollowing(false, userId);
- }
- else {
+ } else {
addFollowing();
}
return false;
@@ -221,10 +318,9 @@
function configureFriendButton() {
if (isFriend()) {
- $('#btn-add-friend').text('DISCONNECT');
- }
- else {
- $('#btn-add-friend').text('CONNECT');
+ $btnAddFriend.text('DISCONNECT');
+ } else {
+ $btnAddFriend.text('CONNECT');
}
}
@@ -267,32 +363,31 @@
function configureFollowingButton() {
if (isFollowing()) {
- $('#btn-follow-user').text('UNFOLLOW');
- }
- else {
- $('#btn-follow-user').text('FOLLOW');
+ $btnFollowUser.text('UNFOLLOW');
+ } else {
+ $btnFollowUser.text('FOLLOW');
}
}
function configureEditProfileButton() {
- $('#btn-follow-user').click(addFollowing);
+ $btnFollowUser.click(addFollowing);
}
// refreshes the currently active tab
function renderActive() {
- if ($('#profile-about-link').hasClass('active')) {
+ if ($aboutLink.hasClass('active')) {
renderAbout();
}
- else if ($('#profile-history-link').hasClass('active')) {
+ else if ($historyLink.hasClass('active')) {
renderHistory();
}
- else if ($('#profile-bands-link').hasClass('active')) {
+ else if ($bandsLink.hasClass('active')) {
renderBands();
}
- else if ($('#profile-social-link').hasClass('active')) {
+ else if ($socialLink.hasClass('active')) {
renderSocial();
}
- else if ($('#profile-favorites-link').hasClass('active')) {
+ else if ($favoritesLink.hasClass('active')) {
renderFavorites();
}
}
@@ -308,187 +403,203 @@
})
}
- /****************** ABOUT TAB *****************/
- function renderAbout() {
- $('#profile-instruments').empty();
-
- $('#profile-about').show();
- $('#profile-history').hide();
- $('#profile-bands').hide();
- $('#profile-social').hide();
- $('#profile-favorites').hide();
-
- $('.profile-nav a.active').removeClass('active');
- $('.profile-nav a#profile-about-link').addClass('active');
-
- bindAbout();
+ function hideElements(elements) {
+ $.each(elements, function(index, val) {
+ val.hide();
+ });
}
- function bindAbout() {
+ /****************** ABOUT TAB *****************/
+ function renderAbout() {
+ $instruments.empty();
- $('#profile-instruments').empty();
+ $aboutContent.show();
+ $historyContent.hide();
+ $bandsContent.hide();
+ $socialContent.hide();
+ $favoritesContent.hide();
+ $('.profile-nav a.active').removeClass('active');
+ $aboutLink.addClass('active');
+ }
+
+ function renderAllStats() {
+
+ if (!isCurrentUser()) {
+ $btnEditBio.hide();
+ $btnAddRecordings.hide();
+ $btnAddSites.hide();
+ $btnAddInterests.hide();
+ }
+
+ if (user && $userName) {
+ renderNameLocationStats();
+ renderBio();
+ renderMusicalExperience();
+ renderPerformanceSamples();
+ renderOnlinePresence();
+ renderInterests();
+ }
+ }
+
+ function renderNameLocationStats() {
// name
- $('#profile-username').html(user.name);
+ $userName.html(user.name);
// avatar
- $('#profile-avatar').attr('src', context.JK.resolveAvatarUrl(user.photo_url));
-
- // instruments
- if (user.instruments) {
- for (var i = 0; i < user.instruments.length; i++) {
- var instrument = user.instruments[i];
- var description = instrument.instrument_id;
- var proficiency = instrument.proficiency_level;
- var instrument_icon_url = context.JK.getInstrumentIcon256(description);
-
- // add instrument info to layout
- var template = $('#template-profile-instruments').html();
- var instrumentHtml = context.JK.fillTemplate(template, {
- instrument_logo_url: instrument_icon_url,
- instrument_description: description,
- proficiency_level: proficiencyDescriptionMap[proficiency],
- proficiency_level_css: proficiencyCssMap[proficiency]
- });
-
- $('#profile-instruments').append(instrumentHtml);
- }
- }
- $('#profile-genres').empty();
- for (var i=0; i< user.genres.length; i++) {
- $('#profile-genres').append(user.genres[i].description + '
');
- }
+ $avatar.attr('src', context.JK.resolveAvatarUrl(user.photo_url));
// location
- $('#profile-location').html(user.location);
+ $location.html(user.location);
+
+ $age.html(user.age ? user.age + " years old" : "");
// stats
var text = user.friend_count > 1 || user.friend_count === 0 ? " Friends" : " Friend";
- $('#profile-friend-stats').html('
' + user.friend_count + '' + text);
+ $friendStats.html('
' + user.friend_count + '' + text);
text = user.follower_count > 1 || user.follower_count === 0 ? " Followers" : " Follower";
- $('#profile-follower-stats').html('
' + user.follower_count + '' + text);
+ $followerStats.html('
' + user.follower_count + '' + text);
if (isMusician()) {
text = user.session_count > 1 || user.session_count === 0 ? " Sessions" : " Session";
- $('#profile-session-stats').html(user.session_count + text);
+ $sessionStats.html(user.session_count + text);
text = user.recording_count > 1 || user.recording_count === 0 ? " Recordings" : " Recording";
- $('#profile-recording-stats').html(user.recording_count + text);
+ $recordingStats.html(user.recording_count + text);
} else {
text = " Following";
- $('#profile-following-stats').html(user.following_count + text);
+ $followingStats.html(user.following_count + text);
text = user.favorite_count > 1 || user.favorite_count === 0 ? " Favorites" : " Favorite";
- $('#profile-favorite-stats').html(user.favorite_count + text);
+ $favoriteStats.html(user.favorite_count + text);
}
-
- renderBio();
}
- /** The biography show/edit functionality */
function renderBio() {
+ $biography.html(user.biography ? user.biography : NOT_SPECIFIED_TEXT);
+ if (isCurrentUser() && !user.biography) {
+ $btnEditBio.show();
+ } else {
+ $btnEditBio.hide();
+ }
+ }
+ function renderMusicalExperience() {
+ profileUtils.renderMusicalExperience(user, $screen)
+ }
- function initializeBioVisibility() {
+ function renderPerformanceSamples() {
+ profileUtils.renderPerformanceSamples(user, $screen)
+ }
- $showBio.hide();
- $noBio.hide();
- $biographyEditor.hide();
+ function renderOnlinePresence() {
+ profileUtils.renderOnlinePresence(user, $screen)
+ }
- $bioTextArea.val(user.biography);
+ function renderInterests() {
+ // current interests
+ var noInterests = !user.paid_sessions && !user.free_sessions && !user.cowriting && !user.virtual_band && !user.traditional_band;
+ if (noInterests) {
+ $noInterests.show();
+ $paidGigSection.hide();
+ $freeGigSection.hide();
+ $cowritingSection.hide();
+ $traditionalBandSection.hide();
+ $virtualBandSection.hide();
- if(user.biography) {
-
- $showBio.show();
- if(isCurrentUser()) {
- $editBiographyButton.show();
- }
- else {
- $editBiographyButton.hide();
- }
- $biographyText.text(user.biography).show();
+ if (isCurrentUser()) {
+ $btnAddInterests.show();
}
- else {
- if(isCurrentUser()) {
- $noBio.show();
- }
+ } else {
+ $btnAddInterests.hide();
+ $noInterests.hide();
+
+ // paid sessions
+ if (user.paid_sessions) {
+ $paidGigSection.show();
+
+ var genreList = profileUtils.paidSessionGenreList(user.genres);
+ $paidGigDetails.find("ul").html("");
+ $paidGigDetails.find("ul").append('
Genre(s): ' + (genreList.length > 0 ? genreList : NOT_SPECIFIED_TEXT) + '');
+
+ var hourlyRate = user.paid_sessions_hourly_rate;
+ $paidGigDetails.find("ul").append('
Hourly rate = ' + (hourlyRate ? hourlyRate : NOT_SPECIFIED_TEXT) + '');
+
+ var dailyRate = user.paid_sessions_daily_rate;
+ $paidGigDetails.find("ul").append('
Day rate = ' + (dailyRate ? dailyRate : NOT_SPECIFIED_TEXT) + '');
+ } else {
+ $paidGigSection.hide();
+ }
+
+ // free sessions
+ if (user.free_sessions) {
+ $freeGigSection.show();
+ $freeGigDetails.find("ul").html("");
+ var genreList = profileUtils.freeSessionGenreList(user.genres);
+ $freeGigDetails.find("ul").append('
Genre(s): ' + (genreList.length > 0 ? genreList : NOT_SPECIFIED_TEXT) + '');
+ } else {
+ $freeGigSection.hide();
+ }
+
+ // cowriting
+ if (user.cowriting) {
+ $cowritingSection.show();
+ $cowritingDetails.find("ul").html("");
+ var genreList = profileUtils.cowritingGenreList(user.genres);
+ $cowritingDetails.find("ul").append('
Genre(s): ' + (genreList.length > 0 ? genreList : NOT_SPECIFIED_TEXT) + '');
+
+ var purpose = user.cowriting_purpose;
+ $cowritingDetails.find("ul").append('
Purpose: ' + (purpose ? profileUtils.cowritingPurposeMap[purpose] : NOT_SPECIFIED_TEXT) + '');
+ } else {
+ $cowritingSection.hide();
+ }
+
+ // traditional bands
+ if (user.traditional_band) {
+ $traditionalBandSection.show();
+ $traditionalBandDetails.find("ul").html("");
+ var genreList = profileUtils.traditionalBandGenreList(user.genres);
+ $traditionalBandDetails.find("ul").append('
Genre(s): ' + (genreList.length > 0 ? genreList : NOT_SPECIFIED_TEXT) + '');
+
+ var commitment = user.traditional_band_commitment;
+ $traditionalBandDetails.find("ul").append('
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").append('
Touring: ' + canTourResponse + '');
+ } else {
+ $traditionalBandSection.hide();
+ }
+
+ // virtual band
+ if (user.virtual_band) {
+ $virtualBandSection.show();
+ $virtualBandDetails.find("ul").html("");
+ var genreList = profileUtils.virtualBandGenreList(user.genres);
+ $virtualBandDetails.find("ul").append('
Genre(s): ' + (genreList.length > 0 ? genreList : NOT_SPECIFIED_TEXT) + '');
+
+ var commitment = user.virtual_band_commitment;
+ $virtualBandDetails.find("ul").append('
Commitment: ' + (commitment ? profileUtils.bandCommitmentMap[commitment] : NOT_SPECIFIED_TEXT) + '');
+ } else {
+ $virtualBandSection.hide();
}
}
-
- var $bioTextArea = $('.user-biography', profileScreen);
- var $showBio = $('.have-bio', profileScreen);
- var $noBio = $('.no-bio', profileScreen);
- var $biographyEditor = $('.update-biography', profileScreen);
- var $addBiographyButton = $('a.enter-bio', profileScreen);
- var $editBiographyButton = $('#profile-edit-biography', profileScreen);
- var $submitBiographyButton = $('#btn-update-user-biography', profileScreen);
- var $cancelBiographyButton = $('#btn-cancel-user-biography', profileScreen);
- var $biographyText = $('#profile-biography', profileScreen);
-
- initializeBioVisibility();
-
- $addBiographyButton.unbind('click').click(function() {
- $biographyEditor.val(user.biography).show();
- return false;
- });
-
- $editBiographyButton.unbind('click').click(function() {
- $editBiographyButton.hide();
- $biographyText.hide();
- $bioTextArea.val(user.biography);
- $biographyEditor.show();
- return false;
- })
-
- $submitBiographyButton.unbind('click').click(function() {
- var bio = $bioTextArea.val();
- $bioTextArea.closest('div.field').removeClass('error');
- $('.error-text', $bioTextArea.closest('div.field')).remove();
- userDefer = rest.updateUser({
- biography: bio
- })
- .done(function(response) {
- user = response;
- initializeBioVisibility();
- })
- .fail(function(jqXHR) {
- if(jqXHR.status == 422) {
- var errors = JSON.parse(jqXHR.responseText)
- var biography = context.JK.format_errors("biography", errors);
- if(biography != null) {
- $bioTextArea.closest('div.field').addClass('error').end().after(biography);
- }
- else {
- app.notifyServerError(jqXHR, "Unable to update biography")
- }
- }
- else {
- app.notifyServerError(jqXHR, "Unable to update biography")
- }
- })
- return false;
- })
-
- $cancelBiographyButton.unbind('click').click(function() {
- initializeBioVisibility();
- return false;
- })
}
/****************** SOCIAL TAB *****************/
function renderSocial() {
- $('#profile-social-friends').empty();
- $('#profile-social-followings').empty();
- $('#profile-social-followers').empty();
+ $socialFriends.empty();
+ $socialFollowings.empty();
+ $socialFollowers.empty();
- $('#profile-about').hide();
- $('#profile-history').hide();
- $('#profile-bands').hide();
- $('#profile-social').show();
- $('#profile-favorites').hide();
+ $aboutContent.hide();
+ $historyContent.hide();
+ $bandsContent.hide();
+ $socialContent.show();
+ $favoritesContent.hide();
$('.profile-nav a.active').removeClass('active');
- $('.profile-nav a#profile-social-link').addClass('active');
+ $socialLink.addClass('active');
bindSocial();
}
@@ -511,11 +622,11 @@
type: "Friends"
});
- $('#profile-social-friends').append(friendHtml);
+ $socialFriends.append(friendHtml);
});
}
else {
- $('#profile-social-friends').html(' ');
+ $socialFriends.html(' ');
}
context.JK.bindHoverEvents();
})
@@ -536,11 +647,11 @@
location: val.location
});
- $('#profile-social-followings').append(followingHtml);
+ $socialFollowings.append(followingHtml);
});
}
else {
- $('#profile-social-followings').html(' ');
+ $socialFollowings.html(' ');
}
context.JK.bindHoverEvents();
})
@@ -559,7 +670,7 @@
location: val.location
});
- $('#profile-social-followers').append(followerHtml);
+ $socialFollowers.append(followerHtml);
});
context.JK.bindHoverEvents();
})
@@ -568,14 +679,14 @@
/****************** HISTORY TAB *****************/
function renderHistory() {
- $('#profile-about').hide();
- $('#profile-history').show();
- $('#profile-bands').hide();
- $('#profile-social').hide();
- $('#profile-favorites').hide();
+ $aboutContent.hide();
+ $historyContent.show();
+ $bandsContent.hide();
+ $socialContent.hide();
+ $favoritesContent.hide();
$('.profile-nav a.active').removeClass('active');
- $('.profile-nav a#profile-history-link').addClass('active');
+ $historyLink.addClass('active');
bindHistory();
}
@@ -586,16 +697,16 @@
/****************** BANDS TAB *****************/
function renderBands() {
- $('#profile-bands').empty();
+ $bandsContent.empty();
- $('#profile-about').hide();
- $('#profile-history').hide();
- $('#profile-bands').show();
- $('#profile-social').hide();
- $('#profile-favorites').hide();
+ $aboutContent.hide();
+ $historyContent.hide();
+ $bandsContent.show();
+ $socialContent.hide();
+ $favoritesContent.hide();
$('.profile-nav a.active').removeClass('active');
- $('.profile-nav a#profile-bands-link').addClass('active');
+ $bandsLink.addClass('active');
bindBands();
}
@@ -606,7 +717,7 @@
.done(function (response) {
if ((!response || response.length === 0) && isCurrentUser()) {
var noBandHtml = $('#template-no-bands').html();
- $("#profile-bands").html(noBandHtml);
+ $bandsContent.html(noBandHtml);
}
else {
addMoreBandsLink();
@@ -652,8 +763,8 @@
bandId: val.id,
biography: val.biography,
profile_url: "/client#/bandProfile/" + val.id,
- band_edit_url: "/client#/band/setup/" + val.id + '/step1',
- band_member_url: "/client#/band/setup/" + val.id + '/step2',
+ band_edit_url: "/client#/band/setup/" + val.id + '/step0',
+ band_member_url: "/client#/band/setup/" + val.id + '/step4',
avatar_url: context.JK.resolveBandAvatarUrl(val.photo_url),
name: val.name,
location: val.location,
@@ -664,12 +775,12 @@
musicians: musicianHtml
});
- $('#profile-bands').append(bandHtml);
-
- $('.profile-band-link-member-true').each(function(idx) {
+ $bandsContent.append(bandHtml);
+
+ $('.profile-band-link-member-true').each(function(idx) {
isBandMember ? $(this).show() : $(this).hide();
});
- $('.profile-band-link-member-false').each(function(idx) {
+ $('.profile-band-link-member-false').each(function(idx) {
isBandMember ? $(this).hide() : $(this).show();
});
@@ -689,7 +800,7 @@
function addMoreBandsLink() {
if (isCurrentUser()) {
var moreBandsHtml = $('#template-more-bands').html();
- $("#profile-bands").append(moreBandsHtml);
+ $bandsContent.append(moreBandsHtml);
}
}
@@ -716,13 +827,13 @@
}
function updateFollowingCount(value) {
- var followingCount = $('#profile-follower-stats span.follower-count');
- followingCount.text(value + parseInt(followingCount.text()));
+ var $followingCount = $('#follower-stats span.follower-count');
+ $followingCount.text(value + parseInt($followingCount.text()));
}
function updateBandFollowingCount(bandId, value) {
- var bandFollowing = $('div[band-id="' + bandId + '"].profile-bands span.follower-count');
- bandFollowing.text(value + parseInt(bandFollowing.text()));
+ var $bandFollowing = $('div[band-id="' + bandId + '"].profile-bands span.follower-count');
+ $bandFollowing.text(value + parseInt($bandFollowing.text()));
}
function addBandFollowing(evt) {
@@ -734,7 +845,6 @@
rest.addFollowing(newFollowing)
.done(function (response) {
- logger.debug("following band " + bandId);
updateBandFollowingCount(bandId, 1); // increase counter
configureBandFollowingButton(true, bandId);
context.JK.GA.trackJKSocial(context.JK.GA.Categories.jkFollow, context.JK.GA.JKSocialTargets.band);
@@ -743,7 +853,7 @@
}
function configureBandFollowingButton(following, bandId) {
- var $btnFollowBand = $('div[band-id=' + bandId + ']', '#profile-bands').find('#btn-follow-band-2');
+ var $btnFollowBand = $('div[band-id=' + bandId + ']', '#bands-content').find('#btn-follow-band');
$btnFollowBand.unbind("click");
if (following) {
@@ -753,8 +863,7 @@
evt.stopPropagation();
return false;
});
- }
- else {
+ } else {
$btnFollowBand.text('FOLLOW');
$btnFollowBand.click(addBandFollowing);
}
@@ -762,14 +871,14 @@
/****************** FAVORITES TAB *****************/
function renderFavorites() {
- $('#profile-about').hide();
- $('#profile-history').hide();
- $('#profile-bands').hide();
- $('#profile-social').hide();
- $('#profile-favorites').show();
+ $aboutContent.hide();
+ $historyContent.hide();
+ $bandsContent.hide();
+ $socialContent.hide();
+ $favoritesContent.show();
$('.profile-nav a.active').removeClass('active');
- $('.profile-nav a#profile-favorites-link').addClass('active');
+ $favoritesLink.addClass('active');
bindFavorites();
}
@@ -780,16 +889,16 @@
function initializeFeed() {
- var $scroller = profileScreen.find('.content-body-scroller#user-profile-feed-scroller');
- var $content = profileScreen.find('.feed-content#user-profile-feed-entry-list');
- var $noMoreFeeds = $('#user-profile-end-of-feeds-list');
- var $refresh = profileScreen.find('.btn-refresh-entries');
- var $sortFeedBy = profileScreen.find('#feed_order_by');
- var $includeDate = profileScreen.find('#feed_date');
- var $includeType = profileScreen.find('#feed_show');
+ var $scroller = $screen.find('.content-body-scroller#user-profile-feed-scroller');
+ var $content = $screen.find('.feed-content#user-profile-feed-entry-list');
+ var $noMoreFeeds = $screen.find('#user-profile-end-of-feeds-list');
+ var $refresh = $screen.find('.btn-refresh-entries');
+ var $sortFeedBy = $screen.find('#feed_order_by');
+ var $includeDate = $screen.find('#feed_date');
+ var $includeType = $screen.find('#feed_show');
feed = new context.JK.Feed(app);
- feed.initialize(profileScreen, $scroller, $content, $noMoreFeeds, $refresh, $sortFeedBy, $includeDate, $includeType, {time_range: 'all'});
+ feed.initialize($screen, $scroller, $content, $noMoreFeeds, $refresh, $sortFeedBy, $includeDate, $includeType, {time_range: 'all'});
}
function initialize(textMessageDialogInstance) {
@@ -800,9 +909,9 @@
'beforeHide' : beforeHide
};
app.bindScreen('profile', screenBindings);
- profileScreen = $('#user-profile');
events();
initializeFeed();
+ player = new context.JK.SoundCloudPlayerDialog(app);
}
this.initialize = initialize;
diff --git a/web/app/assets/javascripts/profile_utils.js b/web/app/assets/javascripts/profile_utils.js
new file mode 100644
index 000000000..6c226b9e8
--- /dev/null
+++ b/web/app/assets/javascripts/profile_utils.js
@@ -0,0 +1,515 @@
+/**
+ * Common utility functions.
+ */
+(function (context, $) {
+
+ "use strict";
+
+ context.JK = context.JK || {};
+ var profileUtils = {};
+ context.JK.ProfileUtils = profileUtils;
+
+ // genre types
+ var PROFILE_GENRE_TYPE = 'profile';
+ var VIRTUAL_BAND_GENRE_TYPE = 'virtual_band';
+ var TRADITIONAL_BAND_GENRE_TYPE = 'traditional_band';
+ var PAID_SESSION_GENRE_TYPE = 'paid_sessions';
+ var FREE_SESSION_GENRE_TYPE = 'free_sessions';
+ var COWRITING_GENRE_TYPE = 'cowriting';
+
+ var NOT_SPECIFIED_TEXT = 'Not specified';
+
+ var proficiencyDescriptionMap = {
+ "1": "BEGINNER",
+ "2": "INTERMEDIATE",
+ "3": "EXPERT"
+ };
+
+ var proficiencyCssMap = {
+ "1": "proficiency-beginner",
+ "2": "proficiency-intermediate",
+ "3": "proficiency-expert"
+ };
+
+
+ // performance sample types
+ profileUtils.SAMPLE_TYPES = {
+ JAMKAZAM: {description: "jamkazam"},
+ SOUNDCLOUD: {description: "soundcloud"},
+ YOUTUBE: {description: "youtube"}
+ };
+
+ // online presence types
+ profileUtils.ONLINE_PRESENCE_TYPES = {
+ SOUNDCLOUD: {description: "soundcloud"},
+ REVERBNATION: {description: "reverbnation"},
+ BANDCAMP: {description: "bandcamp"},
+ FANDALISM: {description: "fandalism"},
+ YOUTUBE: {description: "youtube"},
+ FACEBOOK: {description: "facebook"},
+ TWITTER: {description: "twitter"}
+ };
+
+ var USER_TYPE = 'JamRuby::User';
+
+ profileUtils.skillLevelMap = {
+ "1": "Amateur",
+ "2": "Professional"
+ };
+
+ profileUtils.gigMap = {
+ "": "not specified",
+ "0": "zero",
+ "1": "under 10",
+ "2": "10 to 50",
+ "3": "50 to 100",
+ "4": "over 100"
+ };
+
+ profileUtils.studioMap = {
+ "0": "zero",
+ "1": "under 10",
+ "2": "10 to 50",
+ "3": "50 to 100",
+ "4": "over 100"
+ };
+
+ profileUtils.cowritingPurposeMap = {
+ "1": "just for fun",
+ "2": "sell music"
+ };
+
+ profileUtils.bandCommitmentMap = {
+ "1": "infrequent",
+ "2": "once a week",
+ "3": "2-3 times a week",
+ "4": "4+ times a week"
+ }
+
+ function buildGenreList(genres) {
+ var list = '';
+
+ for (var i=0; i < genres.length; i++) {
+ list = list.concat(genres[i].genre_id);
+ if (i !== genres.length - 1) {
+ list = list.concat(', ');
+ }
+ }
+
+ return list;
+ }
+
+ // Initialize standard profile help bubbles (topics stored as attributes on element):
+ profileUtils.initializeHelpBubbles = function(parentElement) {
+ $(".help", parentElement).each(function( index ) {
+ context.JK.helpBubble($(this), $(this).attr("help-topic"), {}, {})
+ })
+ }
+
+ // profile genres
+ profileUtils.profileGenres = function(genres) {
+ var matches = $.grep(genres, function(g) {
+ return g.player_type === USER_TYPE && g.genre_type === PROFILE_GENRE_TYPE;
+ });
+
+ return matches;
+ }
+
+ profileUtils.profileGenreList = function(genres) {
+ var matches = profileUtils.profileGenres(genres);
+ return buildGenreList(matches);
+ }
+
+ // virtual band genres
+ profileUtils.virtualBandGenres = function(genres) {
+ var matches = [];
+ if (genres) {
+ matches = $.grep(genres, function(g) {
+ return g.player_type === USER_TYPE && g.genre_type === VIRTUAL_BAND_GENRE_TYPE;
+ });
+ }
+
+ return matches;
+ }
+
+ profileUtils.virtualBandGenreList = function(genres) {
+ var matches = profileUtils.virtualBandGenres(genres);
+ return buildGenreList(matches);
+ }
+
+ // traditional band genres
+ profileUtils.traditionalBandGenres = function(genres) {
+ var matches = [];
+ if (genres) {
+ matches = $.grep(genres, function(g) {
+ return g.player_type === USER_TYPE && g.genre_type === TRADITIONAL_BAND_GENRE_TYPE;
+ });
+ }
+
+ return matches;
+ }
+
+ profileUtils.traditionalBandGenreList = function(genres) {
+ var matches = profileUtils.traditionalBandGenres(genres);
+ return buildGenreList(matches);
+ }
+
+ // paid session genres
+ profileUtils.paidSessionGenres = function(genres) {
+ var matches = [];
+ if (genres) {
+ matches = $.grep(genres, function(g) {
+ return g.player_type === USER_TYPE && g.genre_type === PAID_SESSION_GENRE_TYPE;
+ });
+ }
+
+ return matches;
+ }
+
+ profileUtils.paidSessionGenreList = function(genres) {
+ var matches = profileUtils.paidSessionGenres(genres);
+ return buildGenreList(matches);
+ }
+
+ // free session genres
+ profileUtils.freeSessionGenres = function(genres) {
+ var matches = [];
+ if (genres) {
+ matches = $.grep(genres, function(g) {
+ return g.player_type === USER_TYPE && g.genre_type === FREE_SESSION_GENRE_TYPE;
+ });
+ }
+
+ return matches;
+ }
+
+ profileUtils.freeSessionGenreList = function(genres) {
+ var matches = profileUtils.freeSessionGenres(genres);
+ return buildGenreList(matches);
+ }
+
+ // cowriting genres
+ profileUtils.cowritingGenres = function(genres) {
+ var matches = [];
+ if (genres) {
+ matches = $.grep(genres, function(g) {
+ return g.player_type === USER_TYPE && g.genre_type === COWRITING_GENRE_TYPE;
+ });
+ }
+
+ return matches;
+ }
+
+ profileUtils.cowritingGenreList = function(genres) {
+ var matches = profileUtils.cowritingGenres(genres);
+ return buildGenreList(matches);
+ }
+
+ profileUtils.jamkazamSamples = function(samples) {
+ var matches = $.grep(samples, function(s) {
+ return s.service_type === profileUtils.SAMPLE_TYPES.JAMKAZAM.description;
+ });
+
+ return matches;
+ }
+
+ profileUtils.soundCloudSamples = function(samples) {
+ var matches = $.grep(samples, function(s) {
+ return s.service_type === profileUtils.SAMPLE_TYPES.SOUNDCLOUD.description;
+ });
+
+ return matches;
+ }
+
+ profileUtils.youTubeSamples = function(samples) {
+ var matches = $.grep(samples, function(s) {
+ return s.service_type === profileUtils.SAMPLE_TYPES.YOUTUBE.description;
+ });
+
+ return matches;
+ }
+
+ profileUtils.soundCloudPresences = function(presences) {
+ var matches = $.grep(presences, function(p) {
+ return p.service_type === profileUtils.ONLINE_PRESENCE_TYPES.SOUNDCLOUD.description;
+ });
+
+ return matches;
+ }
+
+ profileUtils.reverbNationPresences = function(presences) {
+ var matches = $.grep(presences, function(p) {
+ return p.service_type === profileUtils.ONLINE_PRESENCE_TYPES.REVERBNATION.description;
+ });
+
+ return matches;
+ }
+
+ profileUtils.bandCampPresences = function(presences) {
+ var matches = $.grep(presences, function(p) {
+ return p.service_type === profileUtils.ONLINE_PRESENCE_TYPES.BANDCAMP.description;
+ });
+
+ return matches;
+ }
+
+ profileUtils.fandalismPresences = function(presences) {
+ var matches = $.grep(presences, function(p) {
+ return p.service_type === profileUtils.ONLINE_PRESENCE_TYPES.FANDALISM.description;
+ });
+
+ return matches;
+ }
+
+ profileUtils.youTubePresences = function(presences) {
+ var matches = $.grep(presences, function(p) {
+ return p.service_type === profileUtils.ONLINE_PRESENCE_TYPES.YOUTUBE.description;
+ });
+
+ return matches;
+ }
+
+ profileUtils.facebookPresences = function(presences) {
+ var matches = $.grep(presences, function(p) {
+ return p.service_type === profileUtils.ONLINE_PRESENCE_TYPES.FACEBOOK.description;
+ });
+
+ return matches;
+ }
+
+ profileUtils.twitterPresences = function(presences) {
+ var matches = $.grep(presences, function(p) {
+ return p.service_type === profileUtils.ONLINE_PRESENCE_TYPES.TWITTER.description;
+ });
+
+ return matches;
+ }
+
+ // Render band instruments to a string:
+ profileUtils.renderBandInstruments = function (band) {
+ var msg = ""
+ if (band.instruments) {
+ for (var i = 0; i < band.instruments.length; i++) {
+ var instrument = band.instruments[i]
+ var description = instrument.instrument_id
+
+ if (msg.length > 0) {
+ msg += ", "
+ }
+ msg += instrument
+ msg += "(" + proficiencyDescriptionMap[instrument.proficiency_level] + ")"
+ }
+ }
+ if (msg.length==0) {
+ msg = "None specified"
+ }
+ return msg
+ }
+
+ function formatTitle(title) {
+ return title && title.length > 30 ? title.substring(0, 30) + "..." : title;
+ }
+
+ profileUtils.renderMusicalExperience = function(player, $root) {
+ var $instruments = $root.find('#instruments');
+ var $musicianStatus = $root.find('#musician-status');
+ var $genres = $root.find('#genres');
+ var $concertCount = $root.find('#concert-count');
+ var $studioCount = $root.find('#studio-count');
+
+ $instruments.empty();
+
+ if (player.instruments) {
+ for (var i = 0; i < player.instruments.length; i++) {
+ var instrument = player.instruments[i];
+ var description = instrument.instrument_id;
+ var proficiency = instrument.proficiency_level;
+ var instrument_icon_url = context.JK.getInstrumentIcon256(description);
+
+ // add instrument info to layout
+ var template = $('#template-profile-instruments').html();
+ var instrumentHtml = context.JK.fillTemplate(template, {
+ instrument_logo_url: instrument_icon_url,
+ instrument_description: description,
+ proficiency_level: proficiencyDescriptionMap[proficiency],
+ proficiency_level_css: proficiencyCssMap[proficiency]
+ });
+
+ $instruments.append(instrumentHtml);
+ }
+ }
+
+ // status
+ var status = player.skill_level;
+ $musicianStatus.html(status ? profileUtils.skillLevelMap[status] + ' musician' : NOT_SPECIFIED_TEXT)
+
+ // genres
+ $genres.empty();
+ var profileGenres = profileUtils.profileGenreList(player.genres);
+ $genres.append(profileGenres.length > 0 ? profileGenres : NOT_SPECIFIED_TEXT);
+
+ // concert gigs
+ var concertCount = player.concert_count;
+ $concertCount.html(concertCount > 0 ? 'Has played ' + profileUtils.gigMap[concertCount] + ' live concert gigs' : NOT_SPECIFIED_TEXT);
+
+ // studio gigs
+ var studioCount = player.studio_session_count;
+ $studioCount.html(studioCount > 0 ? 'Has played ' + profileUtils.gigMap[studioCount] + ' studio session gigs' : NOT_SPECIFIED_TEXT);
+
+ }// function renderMusicalExperience
+
+ profileUtils.renderPerformanceSamples = function(player, $root, isOwner) {
+ // performance samples
+ var performanceSamples = player.performance_samples;
+ var $noSamples = $root.find('.no-samples');
+ var $jamkazamSamples = $root.find('.jamkazam-samples');
+ var $soundCloudSamples = $root.find('.soundcloud-samples');
+ var $youTubeSamples = $root.find('.youtube-samples');
+ var $btnAddRecordings = $root.find('.add-recordings');
+
+ if (!performanceSamples || performanceSamples.length === 0) {
+ $noSamples.show()
+ $jamkazamSamples.hide()
+ $soundCloudSamples.hide()
+ $youTubeSamples.hide()
+ if (isOwner) {
+ $btnAddRecordings.show();
+ }
+ } else {
+ $btnAddRecordings.hide();
+ $noSamples.hide();
+
+ // show samples section
+ var jamkazamSamples = profileUtils.jamkazamSamples(player.performance_samples);
+ if (!jamkazamSamples || jamkazamSamples.length === 0) {
+ $jamkazamSamples.hide()
+ } else {
+ $jamkazamSamples.show()
+ }
+
+ var soundCloudSamples = profileUtils.soundCloudSamples(player.performance_samples);
+ if (!soundCloudSamples || soundCloudSamples.length === 0) {
+ $soundCloudSamples.hide()
+ } else {
+ $soundCloudSamples.show()
+ }
+
+ var youTubeSamples = profileUtils.youTubeSamples(player.performance_samples);
+ if (!youTubeSamples || youTubeSamples.length === 0) {
+ $youTubeSamples.hide()
+ } else {
+ $youTubeSamples.show()
+ }
+
+ $.each(jamkazamSamples, function(index, sample) {
+ $jamkazamSamples.append("
" + formatTitle(sample.claimed_recording.name) + "");
+ });
+
+ $.each(soundCloudSamples, function(index, sample) {
+ $soundCloudSamples.append("
" + formatTitle(sample.description) + "");
+ });
+
+ $.each(youTubeSamples, function(index, sample) {
+ $youTubeSamples.append("
" + formatTitle(sample.description) + "");
+ });
+ }
+ }// function renderPerformanceSamples
+
+ profileUtils.renderOnlinePresence = function(player, $root, isOwner) {
+ var $noOnlinePresence = $root.find('.no-online-presence');
+ var $userWebsite = $root.find('.user-website');
+ var $soundCloudPresence = $root.find('.soundcloud-presence');
+ var $reverbNationPresence = $root.find('.reverbnation-presence');
+ var $bandCampPresence = $root.find('.bandcamp-presence');
+ var $fandalismPresence = $root.find('.fandalism-presence');
+ var $youTubePresence = $root.find('.youtube-presence');
+ var $facebookPresence = $root.find('.facebook-presence');
+ var $twitterPresence = $root.find('.twitter-presence');
+ var $btnAddSites = $root.find('.add-sites');
+
+
+
+ // online presences
+ var onlinePresences = player.online_presences;
+ if ((!onlinePresences || onlinePresences.length === 0) && !player.website) {
+ $noOnlinePresence.show()
+ $userWebsite.show()
+ $soundCloudPresence.show()
+ $reverbNationPresence.show()
+ $bandCampPresence.show()
+ $fandalismPresence.show()
+ $youTubePresence.show()
+ $facebookPresence.show()
+ $twitterPresence.show()
+
+ if (isOwner) {
+ $btnAddSites.show();
+ } else {
+ $btnAddSites.hide();
+ }
+ } else {
+ $btnAddSites.hide();
+ $noOnlinePresence.hide();
+
+ if (player.website) {
+ $userWebsite.find('a').attr('href', player.website);
+ }
+
+ var soundCloudPresences = profileUtils.soundCloudPresences(onlinePresences);
+ if (soundCloudPresences && soundCloudPresences.length > 0) {
+ $soundCloudPresence.find('a').attr('href', 'http://www.soundcloud.com/' + soundCloudPresences[0].username);
+ $soundCloudPresence.show();
+ } else {
+ $soundCloudPresence.hide();
+ }
+
+ var reverbNationPresences = profileUtils.reverbNationPresences(onlinePresences);
+ if (reverbNationPresences && reverbNationPresences.length > 0) {
+ $reverbNationPresence.find('a').attr('href', 'http://www.reverbnation.com/' + reverbNationPresences[0].username);
+ $reverbNationPresence.show();
+ } else {
+ $reverbNationPresence.hide();
+ }
+
+ var bandCampPresences = profileUtils.bandCampPresences(onlinePresences);
+ if (bandCampPresences && bandCampPresences.length > 0) {
+ $bandCampPresence.find('a').attr('href', 'http://' + bandCampPresences[0].username + '.bandcamp.com/');
+ $bandCampPresence.show();
+ } else {
+ $bandCampPresence.hide();
+ }
+
+ var fandalismPresences = profileUtils.fandalismPresences(onlinePresences);
+ if (fandalismPresences && fandalismPresences.length > 0) {
+ $fandalismPresence.find('a').attr('href', 'http://www.fandalism.com/' + fandalismPresences[0].username);
+ $fandalismPresence.show();
+ } else {
+ $fandalismPresence.hide();
+ }
+
+ var youTubePresences = profileUtils.youTubePresences(onlinePresences);
+ if (youTubePresences && youTubePresences.length > 0) {
+ $youTubePresence.find('a').attr('href', 'http://www.youtube.com/' + youTubePresences[0].username);
+ $youTubePresence.show();
+ } else {
+ $youTubePresence.hide();
+ }
+
+ var facebookPresences = profileUtils.facebookPresences(onlinePresences);
+ if (facebookPresences && facebookPresences.length > 0) {
+ $facebookPresence.find('a').attr('href', 'http://www.facebook.com/' + facebookPresences[0].username);
+ $facebookPresence.show();
+ } else {
+ $facebookPresence.hide();
+ }
+
+ var twitterPresences = profileUtils.twitterPresences(onlinePresences);
+ if (twitterPresences && twitterPresences.length > 0) {
+ $twitterPresence.find('a').attr('href', 'http://www.twitter.com/' + twitterPresences[0].username);
+ $twitterPresence.show();
+ } else {
+ $twitterPresence.hide();
+ }
+ }
+ }// function renderOnlinePresence
+})(window, jQuery);
\ No newline at end of file
diff --git a/web/app/assets/javascripts/site_validator.js.coffee b/web/app/assets/javascripts/site_validator.js.coffee
index 268d87f0f..111457aff 100644
--- a/web/app/assets/javascripts/site_validator.js.coffee
+++ b/web/app/assets/javascripts/site_validator.js.coffee
@@ -4,12 +4,13 @@ context.JK ||= {};
context.JK.SiteValidator = class SiteValidator
- constructor: (site_type) ->
+ constructor: (site_type, success_callback, fail_callback, parent) ->
@EVENTS = context.JK.EVENTS
@rest = context.JK.Rest()
@site_type = site_type
- @input_div = $(".site_validator#"+site_type+"_validator")
+ @input_div = $(".site_validator."+site_type+"_validator", parent)
@data_input = @input_div.find('input')
+
@logger = context.JK.logger
@spinner = @input_div.find('span.spinner-small')
@checkmark = @input_div.find('.validate-checkmark')
@@ -18,6 +19,8 @@ context.JK.SiteValidator = class SiteValidator
@is_rec_src = false
@deferred_status_check = null
@is_validating = false
+ @success_callback = success_callback
+ @fail_callback = fail_callback
init: () =>
this.renderErrors({})
@@ -32,7 +35,7 @@ context.JK.SiteValidator = class SiteValidator
dataToValidate: () =>
url = @data_input.val()
- if 0 < url.length
+ if url && 0 < url.length
url.substring(0,2000)
else
null
@@ -47,12 +50,14 @@ context.JK.SiteValidator = class SiteValidator
@checkmark.hide()
yn
- didBlur: () =>
+ didBlur: () =>
if this.showFormatStatus()
this.validateSite()
validateSite: () =>
unless data = this.dataToValidate()
+ if @success_callback
+ @success_callback(@input_div)
return null
this.setSiteStatus(null)
@spinner.show()
@@ -68,13 +73,18 @@ context.JK.SiteValidator = class SiteValidator
this.renderErrors({})
if @deferred_status_check
@deferred_status_check.resolve()
+ if @success_callback
+ @success_callback(@input_div)
else
this.setSiteStatus(false)
this.renderErrors(response)
if @deferred_status_check
@deferred_status_check.reject()
+ if @fail_callback
+ @fail_callback(@input_div)
+
@deferred_status_check = null
- @logger.debug("site_status = "+@site_status)
+ @logger.debug("site_status = " + @site_status)
processSiteCheckFail: (response) =>
@logger.error("site check error")
@@ -90,14 +100,14 @@ context.JK.SiteValidator = class SiteValidator
@checkmark.show()
else
@checkmark.hide()
-
- siteIsValid: () =>
+
+ siteIsValid: () =>
this.setSiteStatus(true)
-
- siteIsInvalid: () =>
+
+ siteIsInvalid: () =>
this.setSiteStatus(false)
-
- renderErrors: (errors) =>
+
+ renderErrors: (errors) =>
errdiv = @input_div.find('.error')
if errmsg = context.JK.format_errors("site", errors)
errdiv.show()
@@ -110,7 +120,7 @@ context.JK.SiteValidator = class SiteValidator
dfr = $.Deferred()
if null == @site_status
@deferred_status_check = dfr
- this.validateSite()
+ this.validateSite()
else
if true == @site_status
dfr.resolve()
@@ -118,31 +128,42 @@ context.JK.SiteValidator = class SiteValidator
dfr.reject()
return dfr.promise()
-
-context.JK.RecordingSourceValidator = class RecordingSourceValidator extends SiteValidator
- constructor: (site_type) ->
- super(site_type)
+
+context.JK.RecordingSourceValidator = class RecordingSourceValidator extends SiteValidator
+ constructor: (site_type, success_callback, fail_callback, parent) ->
+ super(site_type, success_callback, fail_callback, parent)
@recording_sources = []
@is_rec_src = true
@add_btn = @input_div.find('a.add-recording-source')
+ @site_success_callback = success_callback
+ @site_fail_callback = fail_callback
init: (sources) =>
super()
if sources
@recording_sources = sources
- @add_btn.on 'click', =>
+ @add_btn.off('click').on 'click', =>
this.attemptAdd()
processSiteCheckSucceed: (response) =>
super(response)
@add_btn.removeClass('disabled')
- @recording_sources.push({ url: response.data, recording_id: response.recording_id })
+
+ if @site_status
+ @recording_sources.push({ url: response.data, recording_id: response.recording_id, recording_title: response.recording_title })
+ if @site_success_callback
+ @site_success_callback(@input_div)
+ else
+ if @site_fail_callback
+ @site_fail_callback(@input_div)
processSiteCheckFail: (response) =>
super(response)
@add_btn.removeClass('disabled')
+ if @site_fail_callback
+ @site_fail_callback(@input_div)
- didBlur: () =>
+ didBlur: () =>
# do nothing, validate on add only
validateSite: () =>
@@ -156,12 +177,16 @@ context.JK.RecordingSourceValidator = class RecordingSourceValidator extends Sit
removeRecordingId: (recording_id) =>
start_len = @recording_sources.length
- @recording_sources = $.grep @recording_sources, (src_data) ->
- src_data['recording_id'] != recording_id
+ @recording_sources = @recording_sources.filter (src) ->
+ src["recording_id"] isnt recording_id.toString()
start_len != @recording_sources.length
containsRecordingUrl: (url) =>
vals = $.grep @recording_sources, (src_data) ->
- src_data['url'] == url
+ src_data['url'] is url
+
0 < vals.length
+ recordingSources: () =>
+ @recording_sources
+
diff --git a/web/app/assets/javascripts/ui_helper.js b/web/app/assets/javascripts/ui_helper.js
index 01f204bfb..11fb08af1 100644
--- a/web/app/assets/javascripts/ui_helper.js
+++ b/web/app/assets/javascripts/ui_helper.js
@@ -62,6 +62,24 @@
});
}
+ function launchInstrumentSelectorDialog(type, instruments, callback) {
+ var instrumentSelectorDialog = new JK.InstrumentSelectorDialog(JK.app, type, instruments, callback);
+ instrumentSelectorDialog.initialize();
+ return instrumentSelectorDialog.showDialog();
+ }
+
+ 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 +88,9 @@
this.launchRsvpCancelDialog = launchRsvpCancelDialog;
this.launchRsvpCreateSlotDialog = launchRsvpCreateSlotDialog;
this.launchSessionStartDialog = launchSessionStartDialog;
+ this.launchGenreSelectorDialog = launchGenreSelectorDialog;
+ this.launchInstrumentSelectorDialog = launchInstrumentSelectorDialog;
+ this.launchRecordingSelectorDialog = launchRecordingSelectorDialog;
return this;
};
diff --git a/web/app/assets/javascripts/utils.js b/web/app/assets/javascripts/utils.js
index cd44c1a76..8af882715 100644
--- a/web/app/assets/javascripts/utils.js
+++ b/web/app/assets/javascripts/utils.js
@@ -4,7 +4,6 @@
(function (context, $) {
"use strict";
-
context.JK = context.JK || {};
var logger = context.JK.logger;
var AUDIO_DEVICE_BEHAVIOR = context.JK.AUDIO_DEVICE_BEHAVIOR;
@@ -22,6 +21,10 @@
var os = null;
+ context.JK.getGenreList = function() {
+ return context.JK.Rest().getGenres();
+ }
+
context.JK.stringToBool = function (s) {
switch (s.toLowerCase()) {
case "true":
diff --git a/web/app/assets/javascripts/web/recordings.js b/web/app/assets/javascripts/web/recordings.js
index 783ca9356..c5611d48b 100644
--- a/web/app/assets/javascripts/web/recordings.js
+++ b/web/app/assets/javascripts/web/recordings.js
@@ -3,7 +3,7 @@
context.JK.ShowRecording = function(app) {
var logger = context.JK.logger;
var rest = JK.Rest();
- var ui = context.JK.UIHelper();
+ var ui = new context.JK.UIHelper(app);
var recordingId = null;
var claimedRecordingId = null;
var $scope = $(".landing-details");
diff --git a/web/app/assets/javascripts/web/sessions.js b/web/app/assets/javascripts/web/sessions.js
index bfad4c1e5..d6bfbc53f 100644
--- a/web/app/assets/javascripts/web/sessions.js
+++ b/web/app/assets/javascripts/web/sessions.js
@@ -3,7 +3,7 @@
context.JK.ShowMusicSession = function(app) {
var logger = context.JK.logger;
var rest = JK.Rest();
- var ui = context.JK.UIHelper();
+ var ui = new context.JK.UIHelper(app);
var sessionId = null;
var $scope = $(".landing-details");
var $controls = null;
diff --git a/web/app/assets/stylesheets/client/account.css.scss b/web/app/assets/stylesheets/client/account.css.scss
index db80c12b4..fae2ccd46 100644
--- a/web/app/assets/stylesheets/client/account.css.scss
+++ b/web/app/assets/stylesheets/client/account.css.scss
@@ -35,7 +35,7 @@
}
form {
- padding-right:10%;
+ //padding-right:10%;
}
label {
@@ -107,6 +107,14 @@
margin-right:17%;
}
+ textarea.biography {
+ width: 80%;
+ height: 90px;
+ }
+
+ .actions {
+ margin-right: 125px;
+ }
.location {
position:relative;
@@ -134,25 +142,6 @@
white-space: normal;
}
- .profile-instrumentlist {
- background-color: #C5C5C5;
- border: medium none;
- box-shadow: 2px 2px 3px 0 #888888 inset;
- color: #000;
- font-size: 14px;
- height: 100px;
- overflow: auto;
- width: 100%;
-
- .easydropdown-wrapper {
- width:100%;
- }
-
- select, .easydropdown {
- @include flat_dropdown;
- }
- }
-
.account-sub-description {
display: block;
white-space: normal;
@@ -212,11 +201,6 @@
}
}
- div.profile-instrumentlist table {
- border-collapse: separate;
- border-spacing: 6px;
- }
-
.account-edit-email, .account-edit-password {
width:35%;
}
diff --git a/web/app/assets/stylesheets/client/accountProfileExperience.css.scss b/web/app/assets/stylesheets/client/accountProfileExperience.css.scss
new file mode 100644
index 000000000..a5d9171c9
--- /dev/null
+++ b/web/app/assets/stylesheets/client/accountProfileExperience.css.scss
@@ -0,0 +1,44 @@
+@import "common.css.scss";
+
+
+#account-profile-experience {
+ .genres {
+ width:100%;
+ height:200px;
+ background-color:#c5c5c5;
+ border:none;
+ -webkit-box-shadow: inset 2px 2px 3px 0px #888;
+ box-shadow: inset 2px 2px 3px 0px #888;
+ color:#000;
+ overflow:auto;
+ font-size:14px;
+ }
+
+ .instruments {
+ background-color: #C5C5C5;
+ border: medium none;
+ box-shadow: 2px 2px 3px 0 #888888 inset;
+ color: #000;
+ font-size: 14px;
+ height: 200px;
+ overflow: auto;
+ width: 85%;
+
+ .easydropdown-wrapper {
+ width:100%;
+ }
+
+ select, .easydropdown {
+ @include flat_dropdown;
+ }
+ }
+
+ div.instruments table {
+ border-collapse: separate;
+ border-spacing: 6px;
+ }
+
+ div.actions {
+ margin-right: 100px;
+ }
+}
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/client/accountProfileInterests.css.scss b/web/app/assets/stylesheets/client/accountProfileInterests.css.scss
new file mode 100644
index 000000000..7e11df987
--- /dev/null
+++ 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
new file mode 100644
index 000000000..76c76ebc8
--- /dev/null
+++ b/web/app/assets/stylesheets/client/accountProfileSamples.css.scss
@@ -0,0 +1,54 @@
+@import "common.css.scss";
+@import "site_validator.css.scss";
+
+.profile-online-sample-controls {
+ table.profile-table {
+ width: 100%;
+ tr:nth-child(even) td {
+ padding: 0.25em 0.25em 1em 0.25em;
+ vertical-align: top;
+ }
+ tr:nth-child(odd) td {
+ padding: 0.25em;
+ vertical-align: top;
+ }
+ }
+
+ .sample-list {
+ border: 1px inset #cfcfcf;
+ padding: 0.5em;
+ .empty {
+ font-style: italic;
+ }
+ min-height: 150px;
+ overflow: scroll;
+ .close-button {
+ cursor:pointer;
+ }
+ }
+
+ table.control-table {
+ width: 100%;
+ }
+
+ .sample-row {
+ position: relative;
+ clear: both;
+ }
+
+ .sample {
+ font-weight: 600;
+ font-size: 16px;
+ }
+
+ .presence {
+ margin: 3px 30px 15px 0px;
+ }
+
+ .site_validator {
+ a, .spinner-small {
+ margin: 1px 1px 2px 2px;
+ vertical-align: top;
+ }
+ }
+}
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/client/band.css.scss b/web/app/assets/stylesheets/client/band.css.scss
index 4a9ff616d..a9645cd01 100644
--- a/web/app/assets/stylesheets/client/band.css.scss
+++ b/web/app/assets/stylesheets/client/band.css.scss
@@ -1,353 +1,466 @@
@import "client/common.css.scss";
+#band-setup, #band-profile {
+ font-family: Raleway, Arial, Helvetica, verdana, arial, sans-serif;
+ .band-field {
+ input[type="text"], select, textarea {
+ @include border_box_sizing;
+ width: 100%;
+ padding: 2px 4px 2px 2px;
+ overflow: hidden;
+ }
-.band-setup-bio {
- height:90px;
- overflow:auto;
-}
+ textarea {
+ overflow:hidden;
+ }
-.band-setup-genres {
- width:100%;
- height:90px;
- background-color:#c5c5c5;
- border:none;
- -webkit-box-shadow: inset 2px 2px 3px 0px #888;
- box-shadow: inset 2px 2px 3px 0px #888;
- color:#000;
- overflow:auto;
- font-size:14px;
-}
+ }
-.band-setup-photo {
- .avatar-space {
- color: $color2;
- margin: 20px;
- position:relative;
- min-height:300px;
-
- img.preview_profile_avatar {
+ table.summary-table {
+ tr {
+ td {
+ padding-right: 1em;
}
}
-
-
- .spinner-large {
- width:300px;
- height:300px;
- line-height: 300px;
- position:absolute;
- top:0;
- left:0;
- z-index: 2000; // to win over jcrop
- }
-
- .no-avatar-space {
- border:1px dotted $color2;
-
- color: $color2;
- width:300px;
- height:300px;
- line-height: 300px;
- text-align: center;
- vertical-align: middle;
- background-color:$ColorTextBoxBackground;
-
- }
}
-.band-profile-header {
- padding:20px;
- height:120px;
-}
+ // Mimic style of easydropdown selects:
+ input[type="number"] {
+ border-radius: 6px;
+ background-color: #c5c5c5;
+ border-right-style: outset;
+ border-bottom-style: outset;
+ height: 15px;
+ padding: 5px;
+ }
-.band-profile-header h2 {
- font-weight:200;
- font-size: 28px;
- float:left;
- margin: 0px 150px 0px 0px;
-}
+ .content-body-scroller {
+
+ }
-.band-profile-status {
- font-size:12px;
- float:left;
- display:inline-block;
- vertical-align:middle;
- line-height:30px;
-}
-
-.band-profile-photo {
- height: 95px;
- width: 15%;
- float:left;
-}
-
-.band-profile-nav {
- width:85%;
- position:relative;
- float:right;
- margin-right:-10px;
-}
-
-.band-profile-nav a {
- width:24%;
- text-align:center;
- height: 27px;
- display: block;
- float:left;
- margin-right:5px;
- vertical-align:bottom;
- padding-top:65px;
- background-color:#535353;
- color:#ccc;
- font-size:17px;
- text-decoration:none;
-}
-
-.band-profile-nav a:hover {
- background-color:#666;
- color:#fff;
-}
-
-.band-profile-nav a.active {
- background-color:#ed3618;
- color:#fff;
-}
-
-.band-profile-nav a.active:hover {
- background-color:#ed3618;
- cursor:default;
-}
-
-.band-profile-nav a.last {
- margin-right:0px !important;
-}
-
-.avatar-profile {
- float:left;
- padding:2px;
- width:88px;
- height:88px;
- background-color:#ed3618;
- -webkit-border-radius:44px;
- -moz-border-radius:44px;
- border-radius:44px;
-}
-
-.avatar-profile img {
- width:88px;
- height:88px;
- -webkit-border-radius:44px;
- -moz-border-radius:44px;
- border-radius:44px;
-}
-
-.band-profile-wrapper {
- padding:10px 25px 10px 25px;
- font-size:15px;
- color:#ccc;
- border-bottom: dotted 1px #444;
- position:relative;
- display:block;
-
- .result-name {
- font-size: 12px;
- font-weight: bold;
- margin-bottom: 2px;
- padding-right:4px;
+ .radio-field {
+ display: inline;
+ padding: 2px;
+ margin: 0.5em 2em 0.5em 0.25em;
+ label {
+ display: inline;
+ padding: 2px;
}
- .stats {
- margin-top: 4px;
- img {
- vertical-align: middle;
- }
- }
- .lcol {
- width: 200px;
- }
- .whitespace {
- // equal to lcol width.
- padding-left: 200px;
- }
- .instruments {
- width:128px;
- img {
- height:24px;
- width:24px;
- margin-right:2px;
+ .iradio-inline {
+ display: inline-block;
+ //padding: 2px;
+ }
+ }
- &:last-child {
- margin-right:0px;
+
+
+ .band-setup-genres {
+ width:100%;
+ height:200px;
+ background-color:#c5c5c5;
+ border:none;
+ -webkit-box-shadow: inset 2px 2px 3px 0px #888;
+ box-shadow: inset 2px 2px 3px 0px #888;
+ color:#000;
+ overflow:auto;
+ font-size:12px;
+ }
+
+ .band-setup-photo {
+
+ .avatar-space {
+ color: $color2;
+ margin: 20px;
+ position:relative;
+ min-height:300px;
+
+ img.preview_profile_avatar {
}
+ }
+
+
+ .spinner-large {
+ width:300px;
+ height:300px;
+ line-height: 300px;
+ position:absolute;
+ top:0;
+ left:0;
+ z-index: 2000; // to win over jcrop
+ }
+
+ .no-avatar-space {
+ border:1px dotted $color2;
+
+ color: $color2;
+ width:300px;
+ height:300px;
+ line-height: 300px;
+ text-align: center;
+ vertical-align: middle;
+ background-color:$ColorTextBoxBackground;
}
}
- .button-row {
- margin-top:10px;
- margin-bottom:5px;
- .result-list-button-wrapper {
- margin:0;
- }
+
+ .band-profile-header {
+ padding:20px;
+ height:120px;
+ }
+
+ .band-profile-header h2 {
+ font-weight:200;
+ font-size: 28px;
+ float:left;
+ margin: 0 0 1em 0;
+ }
+
+ .band-profile-status {
+ font-size:12px;
+ float:left;
+ display:inline-block;
+ vertical-align:middle;
+ line-height:30px;
+ }
+
+ .band-profile-photo {
+ height: 95px;
+ width: 15%;
+ float:left;
+ }
+
+ .band-profile-nav {
+ width:85%;
+ position:relative;
+ float:right;
+ margin-right:-10px;
+ }
+
+ .band-profile-nav a {
+ width:24%;
+ text-align:center;
+ height: 27px;
+ display: block;
+ float:left;
+ margin-right:5px;
+ vertical-align:bottom;
+ padding-top:65px;
+ background-color:#535353;
+ color:#ccc;
+ font-size:16px;
+ text-decoration:none;
+ }
+
+ .band-profile-nav a:hover {
+ background-color:#666;
+ color:#fff;
+ }
+
+ .band-profile-nav a.active {
+ background-color:#ed3618;
+ color:#fff;
+ }
+
+ .band-profile-nav a.active:hover {
+ background-color:#ed3618;
+ cursor:default;
+ }
+
+ .band-profile-nav a.last {
+ margin-right:0px !important;
+ }
+
+ .band-avatar-profile {
+ padding:2px;
+ width:88px;
+ height:88px;
+ background-color:#ed3618;
+ -webkit-border-radius:44px;
+ -moz-border-radius:44px;
+ border-radius:44px;
+ }
+
+ .band-avatar-profile img {
+ width:88px;
+ height:88px;
+ -webkit-border-radius:44px;
+ -moz-border-radius:44px;
+ border-radius:44px;
+ }
+
+
+ .band-entry {
+ .item-caption {
+ font-size: 1.4em;
+ font-weight: bold;
+ margin: 0.25em 0em 0.25em 0em;
}
-}
-
-.band-profile-about-left {
- width:16%;
- float:left;
- font-size:13px;
- line-height:140%;
- display:block;
-}
-
-.band-profile-about-left h3 {
- color:#fff;
- margin-bottom:0px;
- font-size:13px;
- font-weight:bold;
- display:inline;
-}
-
-.band-profile-about-right {
- float:right;
- font-size:13px;
- width:84%;
- line-height:140%;
- display:block;
-}
-
-.band-profile-about-right .band-profile-instrument {
- text-align:center;
- margin-right:15px;
- float:left;
-}
-
-.proficiency-beginner {
- font-size:10px;
- color:#8ea415;
- font-weight:600;
-}
-
-.proficiency-intermediate {
- font-size:10px;
- color:#0b6672;
- font-weight:600;
-}
-
-.proficiency-expert {
- font-size:10px;
- color:#ed3618;
- font-weight:600;
-}
-
-.band-profile-members {
- width:100%;
- min-height:90px;
- background-color:#242323;
- position:relative;
- float:left;
- margin:10px 20px 10px 0px;
- padding-bottom:5px;
-}
-
-.band-profile-member-name {
- float:left;
- font-size:12px;
- margin-top:12px;
- font-weight:bold;
-}
-
-.band-profile-member-location {
- font-size:12px;
- font-weight:200;
-}
-
-.band-profile-member-genres {
- float:left;
- width:40%;
- font-size:10px;
- margin-left:10px;
- padding-right:5px;
-}
-
-.band-profile-social-left {
- float:left;
- width:32%;
- margin-right:12px;
- border-right:solid 1px #666;
-}
-
-.band-profile-social-mid {
- float:left;
- width:31%;
- margin-right:12px;
- border-right:solid 1px #666;
-}
-
-.band-profile-social-right {
- float:left;
- width:31%;
-}
-
-.band-profile-social-left h2, .band-profile-social-mid h2, .band-profile-social-right h2 {
- font-weight:200;
- color:#fff;
- font-size:20px;
-}
-
-.band-profile-block {
- clear:left;
- white-space:nowrap;
- display:block;
- margin-bottom:10px;
-}
-
-.band-profile-outer-block {
- float: left;
- width: 25%;
-}
-
-.band-profile-block-name {
- display:inline-block;
- margin-top:13px;
- font-size:14px;
- color:#fff;
- font-weight:bold;
-}
-
-.band-profile-block-city {
- font-size:12px;
-}
-
-#band-filter-results {
- margin: 0 10px 5px 10px;
-}
-
-.band-list-result {
- padding-top: 5px;
- padding-right: 5px;
- padding-left: 5px;
-}
-
-.band-wrapper {
- overflow: auto;
- height: 480px;
- width: 100%;
-}
-
-#band-setup-form {
- .easydropdown {
- padding: 0 3px;
- width:100%;
+ .item-content {
+ font-size: 1.1em;
+ margin: 0.25em 0em 0.25em 0em;
+ }
+ margin: 0em 0em 1.5em 0em;
}
- .easydropdown-wrapper {
- width:80%;
+ // .band-name, .band-photo {
+ // display: inline;
+ // }
+
+ .band-profile-wrapper {
+ padding:10px 25px 10px 25px;
+ font-size:15px;
+ color:#ccc;
+ border-bottom: dotted 1px #444;
+ position:relative;
+ display:block;
+
+ .result-name {
+ font-size: 12px;
+ font-weight: bold;
+ margin-bottom: 2px;
+ padding-right:4px;
+ }
+ .stats {
+ margin-top: 4px;
+ img {
+ vertical-align: middle;
+ }
+ }
+ .lcol {
+ width: 200px;
+ }
+ .whitespace {
+ // equal to lcol width.
+ padding-left: 200px;
+ }
+ .instruments {
+ width:128px;
+ img {
+ height:24px;
+ width:24px;
+ margin-right:2px;
+
+ &:last-child {
+ margin-right:0px;
+ }
+
+ }
+ }
+ .button-row {
+ margin-top:10px;
+ margin-bottom:5px;
+ .result-list-button-wrapper {
+ margin:0;
+ }
+ }
}
- div.field {
- margin: 10px;
+ .band-profile-about-left {
+ width:16%;
+ float:left;
+ font-size:13px;
+ line-height:140%;
+ display:block;
+ }
+
+ .band-profile-about-left h3 {
+ color:#fff;
+ margin-bottom:0px;
+ font-size:13px;
+ font-weight:bold;
+ display:inline;
+ }
+
+ .band-profile-about-right {
+ float:right;
+ font-size:13px;
+ width:84%;
+ line-height:140%;
+ display:block;
+ }
+
+ .band-profile-about-right .band-profile-instrument {
+ text-align:center;
+ margin-right:15px;
+ float:left;
+ }
+
+ .proficiency-beginner {
+ font-size:10px;
+ color:#8ea415;
+ font-weight:600;
+ }
+
+ .proficiency-intermediate {
+ font-size:10px;
+ color:#0b6672;
+ font-weight:600;
+ }
+
+ .proficiency-expert {
+ font-size:10px;
+ color:#ed3618;
+ font-weight:600;
+ }
+
+ .band-profile-members {
+ width:100%;
+ min-height:90px;
+ background-color:#242323;
+ position:relative;
+ float:left;
+ margin:10px 20px 10px 0px;
+ padding-bottom:5px;
+ }
+
+ .band-profile-member-name {
+ float:left;
+ font-size:12px;
+ margin-top:12px;
+ font-weight:bold;
+ }
+
+ .band-profile-member-location {
+ font-size:12px;
+ font-weight:200;
+ }
+
+ .band-profile-member-genres {
+ float:left;
+ width:40%;
+ font-size:10px;
+ margin-left:10px;
+ padding-right:5px;
+ }
+
+ .band-profile-social-left {
+ float:left;
+ width:32%;
+ margin-right:12px;
+ border-right:solid 1px #666;
+ }
+
+ .band-profile-social-mid {
+ float:left;
+ width:31%;
+ margin-right:12px;
+ border-right:solid 1px #666;
+ }
+
+ .band-profile-social-right {
+ float:left;
+ width:31%;
+ }
+
+ .band-profile-social-left h2, .band-profile-social-mid h2, .band-profile-social-right h2 {
+ font-weight:200;
+ color:#fff;
+ font-size:20px;
+ }
+
+ .band-profile-block {
+ clear:left;
+ white-space:nowrap;
+ display:block;
+ margin-bottom:10px;
+ }
+
+ .band-profile-outer-block {
+ float: left;
+ width: 25%;
+ }
+
+ .band-profile-block-name {
+ display:inline-block;
+ margin-top:13px;
+ font-size:14px;
+ color:#fff;
+ font-weight:bold;
+ }
+
+ .band-profile-block-city {
+ font-size:12px;
+ }
+
+ #band-filter-results {
+ margin: 0 10px 5px 10px;
+ }
+
+ .band-list-result {
+ padding-top: 5px;
+ padding-right: 5px;
+ padding-left: 5px;
+ }
+
+ .band-wrapper {
+ overflow: auto;
+ height: 480px;
+ width: 100%;
+ }
+
+
+ #band-setup-form {
+ margin: 0.25em 0.5em 1.25em 0.25em;
+ table.band-form-table {
+ width: 100%;
+ margin: 1em;
+
+ tr:nth-child(even) td {
+ padding-bottom: 1em;
+ }
+
+ td.band-biography, td.tdBandGenres {
+ height:100%;
+ vertical-align: top;
+ #band-biography {
+
+ }
+ }
+
+ }
+
+ .easydropdown {
+ padding: 0 3px;
+ width:100%;
+ }
+
+ .easydropdown-wrapper {
+ width:80%;
+ }
+
+ div.field {
+ margin: 10px;
+ }
+
+ label {
+ margin-bottom:2px;
+ }
+
+ #desired-experience-label {
+ font-style: italic;
+ }
+ }
+
+
+ .band-setup-genre {
+ input {
+ display: inline;
+ width: auto !important;
+ }
+ label {
+ display: inline;
+ width: auto;
+ }
}
label {
- margin-bottom:2px;
+ font-size: 1.05em;
}
+
+ label.strong-label {
+ font-weight: bold;
+ font-size: 1.1em;
+ }
+
}
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/client/client.css b/web/app/assets/stylesheets/client/client.css
index 276bfb66a..c26547f68 100644
--- a/web/app/assets/stylesheets/client/client.css
+++ b/web/app/assets/stylesheets/client/client.css
@@ -32,6 +32,9 @@
*= require ./findSession
*= require ./session
*= require ./account
+ *= require ./accountProfileExperience
+ *= require ./accountProfileInterests
+ *= require ./accountProfileSamples
*= require ./accountPaymentHistory
*= require ./account_affiliate
*= require ./search
diff --git a/web/app/assets/stylesheets/client/musician.css.scss b/web/app/assets/stylesheets/client/musician.css.scss
index 7ace07308..e075a212e 100644
--- a/web/app/assets/stylesheets/client/musician.css.scss
+++ b/web/app/assets/stylesheets/client/musician.css.scss
@@ -47,6 +47,9 @@
.button-row {
float:none;
+ .result-list-button-wrapper {
+ margin:3px;
+ }
}
.latency-holder {
@@ -138,6 +141,95 @@
#musician-filter-results {
margin: 0 10px 0px 10px;
}
+
+ #musician-search-filter-results-header {
+ padding: 10px 10px 10px 10px;
+ }
+
+ #btn-musician-search-builder {
+ float: left;
+ }
+
+ #musician-search-filter-description {
+ padding: 5px 5px 5px 5px;
+ display: inline;
+ margin-left: auto;
+ margin-right: auto;
+ }
+
+ #btn-musician-search-reset {
+ float: right;
+ }
+
+ #musician-search-filter-results-list-blank {
+ font-weight: bold;
+ text-align: center;
+ margin: 30px 10px 10px 10px;
+ }
+
+ #musician-search-filter-spinner {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+ margin-top: 40px;
+ }
+
+ .col-left {
+ float: left;
+ width: 50%;
+ margin-left: auto;
+ margin-right: auto;
+ }
+
+ .col-right {
+ float: right;
+ width: 50%;
+ margin-left: auto;
+ margin-right: auto;
+ }
+
+ .builder-section {
+ padding: 10px;
+ margin-bottom: 20px;
+ }
+ .builder-button {
+ float: right;
+ }
+ .band-setup-genres {
+ width: 80%;
+ }
+ .easydropdown-wrapper {
+ width: 80%;
+ }
+ .builder-sort-order {
+ text-align: right;
+ .easydropdown-wrapper {
+ width: 140px;
+ }
+ .text-label {
+ vertical-align: top;
+ margin-right: 5px;
+ display: inline;
+ line-height: 2em;
+ }
+ .select {
+ float: right;
+ }
+ }
+ .builder-ages {
+ width: 100%;
+ }
+ .builder-instruments {
+ width: 100%;
+ height: 150px;
+ }
+ .builder-selector {
+ margin-top: 10px;
+ }
+ .builder-action-buttons {
+ margin-top: 20px;
+ }
+
}
.filter-element {
diff --git a/web/app/assets/stylesheets/client/profile.css.scss b/web/app/assets/stylesheets/client/profile.css.scss
index b19b16f9e..6e5665b5e 100644
--- a/web/app/assets/stylesheets/client/profile.css.scss
+++ b/web/app/assets/stylesheets/client/profile.css.scss
@@ -1,6 +1,6 @@
@import "client/common.css.scss";
-#user-profile {
+#user-profile, #band-profile {
.profile-about-right {
textarea {
@@ -9,32 +9,69 @@
padding:0;
}
}
+
+ div.logo, div.item {
+ text-align: bottom;
+ }
+
+ .online-presence-option, .performance-sample-option {
+ margin-right: 1em;
+ }
+
+ 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 {
}
.profile-body {
-
+
}
.profile-header {
padding:10px 20px;
-// height:120px;
+ position: relative;
}
.profile-header h2 {
font-weight:200;
font-size: 28px;
float:left;
- margin: 0px 150px 0px 0px;
+ margin: 0px 0px 0px 0px;
}
-.profile-status {
+.profile-about-right {
+ .section-header {
+ font-weight:600;
+ font-size:18px;
+ float:left;
+ margin: 0px 0px 10px 0px;
+ }
+
+ .section-content {
+ font-weight:normal;
+ font-size:1.2em;
+ float:left;
+ margin: 0px 0px 10px 0px;
+ }
+}
+
+.profile-details {
font-size:12px;
float:left;
- display:inline-block;
- vertical-align:middle;
- line-height:30px;
+ width: 80px;
}
.profile-photo {
@@ -159,7 +196,7 @@
font-weight:600;
}
-#profile-bands .when-empty {
+#bands-content .when-empty {
margin: 0px;
padding:0px;
display:block;
@@ -170,7 +207,7 @@
line-height: 150%;
}
-#profile-bands .when-empty a {
+#bands-content .when-empty a {
text-decoration: underline;
color: inherit;
}
@@ -205,18 +242,6 @@
padding-right:5px;
}
-.user-setup-genres {
- width:40%;
- height:90px;
- background-color:#c5c5c5;
- border:none;
- -webkit-box-shadow: inset 2px 2px 3px 0px #888;
- box-shadow: inset 2px 2px 3px 0px #888;
- color:#000;
- overflow:auto;
- font-size:14px;
-}
-
.profile-band-list-result {
width:100%;
min-height:85px;
@@ -228,7 +253,7 @@
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing:border-box;
-
+
.result-name {
font-size: 12px;
font-weight: bold;
@@ -254,11 +279,11 @@
height:24px;
width:24px;
margin-right:2px;
-
+
&:last-child {
margin-right:0px;
}
-
+
}
}
.button-row {
@@ -330,14 +355,14 @@
display:none;
}
-#profile-history {
+#history-content, #band-profile-history {
padding:0 10px 0 20px;
width:100%;
position:relative;
height:100%;
@include border_box_sizing;
- #user-feed-controls {
+ #user-feed-controls, #band-feed-controls {
width:100%;
@include border_box_sizing;
position:relative;
diff --git a/web/app/assets/stylesheets/client/site_validator.css.scss b/web/app/assets/stylesheets/client/site_validator.css.scss
index 4534ebd8c..09853cdae 100644
--- a/web/app/assets/stylesheets/client/site_validator.css.scss
+++ b/web/app/assets/stylesheets/client/site_validator.css.scss
@@ -11,7 +11,7 @@
input {
width: 100%;
- padding: 5px 5px 5px 30px;
+
float: left;
}
.validate-checkmark {
@@ -24,7 +24,7 @@
display:inline-block;
vertical-align: middle;
position: relative;
- margin-top: -40px;
+ margin-top: -10px;
left: 0px;
}
.error {
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..b6ab94944
--- /dev/null
+++ b/web/app/assets/stylesheets/dialogs/genreSelectorDialog.css.scss
@@ -0,0 +1,39 @@
+@import "client/common";
+
+#genre-selector-dialog {
+
+ min-height:initial;
+
+ .dialog-inner {
+ display:block;
+ overflow:auto;
+ max-height:300px;
+ color:white;
+ }
+
+ .action-buttons {
+ margin-bottom:10px;
+ }
+
+ .three-column-list-container {
+ -moz-column-count: 3;
+ -moz-column-gap: 10px;
+ -webkit-column-count: 3;
+ -webkit-column-gap: 10px;
+ column-count: 3;
+ column-gap: 10px;
+ margin-left: 0;
+ ul {
+ list-style-type: none;
+ li {
+ margin: 1px 4px 1px 0;
+ font-size:12px;
+ line-height:14px;
+ }
+ }
+ }
+
+ .genres {
+
+ }
+}
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/dialogs/instrumentSelectorDialog.css.scss b/web/app/assets/stylesheets/dialogs/instrumentSelectorDialog.css.scss
new file mode 100644
index 000000000..59760670d
--- /dev/null
+++ b/web/app/assets/stylesheets/dialogs/instrumentSelectorDialog.css.scss
@@ -0,0 +1,44 @@
+@import "client/common";
+
+#instrument-selector-dialog {
+
+ min-height:initial;
+
+
+ .dialog-inner {
+ .content-body {
+ max-height: auto;
+ .content-body-scroller {
+ height: 350px;
+ overflow: scroll;
+ background-color:#c5c5c5;
+ border: 1px inset;
+ }
+ border: 1px solid #222;
+ margin: 4px 4px 8px 4px;
+ }
+
+ .instructions {
+ font-size:16px;
+ }
+
+ .dropdown {
+ box-shadow:0 0 0 0;
+ }
+ }
+
+ .action-buttons {
+ margin-bottom:10px;
+ }
+
+ .instruments {
+
+ }
+
+ label {
+ display:inline;
+ color: black;
+ font-size:16px;
+ }
+
+}
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/dialogs/recordingSelectorDialog.css.scss b/web/app/assets/stylesheets/dialogs/recordingSelectorDialog.css.scss
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/assets/stylesheets/dialogs/soundCloudPlayer.css.scss b/web/app/assets/stylesheets/dialogs/soundCloudPlayer.css.scss
new file mode 100644
index 000000000..60cd88492
--- /dev/null
+++ b/web/app/assets/stylesheets/dialogs/soundCloudPlayer.css.scss
@@ -0,0 +1,6 @@
+#sound-cloud-player-dialog {
+ height:auto;
+ .caption {
+ margin: 0.1em 0.1em 0.5em 0em
+ }
+}
diff --git a/web/app/controllers/api_bands_controller.rb b/web/app/controllers/api_bands_controller.rb
index 8b0c83ccd..879cb09f2 100644
--- a/web/app/controllers/api_bands_controller.rb
+++ b/web/app/controllers/api_bands_controller.rb
@@ -250,6 +250,7 @@ class ApiBandsController < ApiController
def auth_band_member
@band = Band.find(params[:id])
unless @band.users.exists? current_user
+ Rails.logger.info("Could not find #{current_user} in #{@band.users.inspect}")
raise JamPermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR
end
end
@@ -257,6 +258,7 @@ class ApiBandsController < ApiController
uid = current_user.id
@band = Band.find(params[:id])
unless @band.band_musicians.detect { |bm| bm.user_id == uid && bm.admin? }
+ Rails.logger.info("Could not find #{current_user} in #{@band.band_musicians.inspect}")
raise JamPermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR
end
end
diff --git a/web/app/controllers/api_search_controller.rb b/web/app/controllers/api_search_controller.rb
index dfe2fc322..0663265ff 100644
--- a/web/app/controllers/api_search_controller.rb
+++ b/web/app/controllers/api_search_controller.rb
@@ -21,4 +21,27 @@ class ApiSearchController < ApiController
@search = Search.text_search(params, current_user)
end
end
+
+ def musicians
+ if request.get?
+ if params[:results]
+ @search = MusicianSearch.user_search_filter(current_user).search_results_page
+ respond_with @search, responder: ApiResponder, status: 201, template: 'api_search/index'
+ else
+ render :json => MusicianSearch.search_filter_json(current_user), :status => 200
+ end
+
+ elsif request.post?
+ ms = MusicianSearch.user_search_filter(current_user)
+ filter = params[:filter]
+ if filter == 'reset'
+ @search = ms.reset_search_results
+ else
+ json = JSON.parse(filter, :create_additions => false)
+ @search = ms.search_results_page(json, [params[:page].to_i, 1].max)
+ end
+ respond_with @search, responder: ApiResponder, status: 201, template: 'api_search/index'
+ end
+ end
+
end
diff --git a/web/app/controllers/api_users_controller.rb b/web/app/controllers/api_users_controller.rb
index 5d8e1cd00..ebe68290a 100644
--- a/web/app/controllers/api_users_controller.rb
+++ b/web/app/controllers/api_users_controller.rb
@@ -32,6 +32,17 @@ class ApiUsersController < ApiController
respond_with @user, responder: ApiResponder, :status => 200
end
+ def profile_show
+ @profile = User.includes([{musician_instruments: :instrument},
+ {band_musicians: :user},
+ {genre_players: :genre},
+ :bands, :instruments, :genres,
+ :online_presences, :performance_samples])
+ .find(params[:id])
+
+ respond_with @profile, responder: ApiResponder, :status => 200
+ end
+
# in other words, a minimal signup
def create
# today, this only accepts a minimal registration; it could be made to take in more if we wanted
@@ -69,7 +80,10 @@ class ApiUsersController < ApiController
respond_with_model(@user, new: true, location: lambda { return api_user_detail_url(@user.id) })
end
end
-
+
+ def profile_save
+ end
+
def update
@user = User.find(params[:id])
@@ -82,11 +96,46 @@ class ApiUsersController < ApiController
@user.country = params[:country] if params.has_key?(:country)
@user.musician = params[:musician] if params.has_key?(:musician)
@user.update_instruments(params[:instruments].nil? ? [] : params[:instruments]) if params.has_key?(:instruments)
- @user.update_genres(params[:genres].nil? ? [] : params[:genres]) if params.has_key?(:genres)
+
+ # genres
+ @user.update_genres(params[:genres].nil? ? [] : params[:genres], GenrePlayer::PROFILE) if params.has_key?(:genres)
+ @user.update_genres(params[:virtual_band_genres].nil? ? [] : params[:virtual_band_genres], GenrePlayer::VIRTUAL_BAND) if params.has_key?(:virtual_band_genres)
+ @user.update_genres(params[:traditional_band_genres].nil? ? [] : params[:traditional_band_genres], GenrePlayer::TRADITIONAL_BAND) if params.has_key?(:traditional_band_genres)
+ @user.update_genres(params[:paid_session_genres].nil? ? [] : params[:paid_session_genres], GenrePlayer::PAID_SESSION) if params.has_key?(:paid_session_genres)
+ @user.update_genres(params[:free_session_genres].nil? ? [] : params[:free_session_genres], GenrePlayer::FREE_SESSION) if params.has_key?(:free_session_genres)
+ @user.update_genres(params[:cowriting_genres].nil? ? [] : params[:cowriting_genres], GenrePlayer::COWRITING) if params.has_key?(:cowriting_genres)
+
@user.show_whats_next = params[:show_whats_next] if params.has_key?(:show_whats_next)
@user.show_whats_next_count = params[:show_whats_next_count] if params.has_key?(:show_whats_next_count)
@user.subscribe_email = params[:subscribe_email] if params.has_key?(:subscribe_email)
@user.biography = params[:biography] if params.has_key?(:biography)
+
+ @user.website = params[:website] if params.has_key?(:website)
+ @user.skill_level = params[:skill_level] if params.has_key?(:skill_level)
+ @user.concert_count = params[:concert_count] if params.has_key?(:concert_count)
+ @user.studio_session_count = params[:studio_session_count] if params.has_key?(:studio_session_count)
+
+ # virtual band
+ @user.virtual_band = params[:virtual_band] if params.has_key?(:virtual_band)
+ @user.virtual_band_commitment = params[:virtual_band_commitment] if params.has_key?(:virtual_band_commitment)
+
+ # traditional band
+ @user.traditional_band = params[:traditional_band] if params.has_key?(:traditional_band)
+ @user.traditional_band_commitment = params[:traditional_band_commitment] if params.has_key?(:traditional_band_commitment)
+ @user.traditional_band_touring = params[:traditional_band_touring] if params.has_key?(:traditional_band_touring)
+
+ # paid sessions
+ @user.paid_sessions = params[:paid_sessions] if params.has_key?(:paid_sessions)
+ @user.paid_sessions_hourly_rate = params[:paid_sessions_hourly_rate] if params.has_key?(:paid_sessions_hourly_rate)
+ @user.paid_sessions_daily_rate = params[:paid_sessions_daily_rate] if params.has_key?(:paid_sessions_daily_rate)
+
+ # free sessions
+ @user.free_sessions = params[:free_sessions] if params.has_key?(:free_sessions)
+
+ # co-writing
+ @user.cowriting = params[:cowriting] if params.has_key?(:cowriting)
+ @user.cowriting_purpose = params[:cowriting_purpose] if params.has_key?(:cowriting_purpose)
+
@user.want_jamblaster = params[:want_jamblaster] if params.has_key?(:want_jamblaster)
@user.mod_merge(params[:mods]) if params[:mods]
@@ -95,6 +144,9 @@ class ApiUsersController < ApiController
@user.update_notification_seen_at params[:notification_seen_at]
end
+ @user.update_online_presences(params[:online_presences]) if params.has_key?(:online_presences)
+ @user.update_performance_samples(params[:performance_samples]) if params.has_key?(:performance_samples)
+
@user.save
if @user.errors.any?
@@ -782,9 +834,9 @@ class ApiUsersController < ApiController
if site.blank? || 'url'==site
url = data
elsif Utils.recording_source?(site)
- rec_id = Utils.extract_recording_id(site, data)
- if rec_id
- render json: { message: 'Valid Site', recording_id: rec_id, data: data }, status: 200
+ rec_data = Utils.extract_recording_data(site, data)
+ if rec_data
+ render json: { message: 'Valid Site', recording_id: rec_data["id"], recording_title: rec_data["title"], data: data }, status: 200
return
else
render json: { message: 'Invalid Site', data: data, errors: { site: ["Could not detect recording identifier"] } }, status: 200
diff --git a/web/app/controllers/spikes_controller.rb b/web/app/controllers/spikes_controller.rb
index 52573388b..0edce85ce 100644
--- a/web/app/controllers/spikes_controller.rb
+++ b/web/app/controllers/spikes_controller.rb
@@ -67,4 +67,10 @@ class SpikesController < ApplicationController
def recording_source
render :layout => 'web'
end
+
+ def musician_search_filter
+ # gon.musician_search_meta = MusicianSearch::SEARCH_FILTER_META
+ render :layout => 'web'
+ end
+
end
diff --git a/web/app/helpers/client_helper.rb b/web/app/helpers/client_helper.rb
index 31364bd0b..c4977e130 100644
--- a/web/app/helpers/client_helper.rb
+++ b/web/app/helpers/client_helper.rb
@@ -67,6 +67,7 @@ module ClientHelper
gon.ftue_network_test_duration = Rails.application.config.ftue_network_test_duration
gon.ftue_network_test_max_clients = Rails.application.config.ftue_network_test_max_clients
gon.ftue_maximum_gear_latency = Rails.application.config.ftue_maximum_gear_latency
+ gon.musician_search_meta = MusicianSearch::SEARCH_FILTER_META
# is this the native client or browser?
@nativeClient = is_native_client?
@@ -77,4 +78,4 @@ module ClientHelper
gon.use_cached_session_scores = Rails.application.config.use_cached_session_scores
gon.allow_both_find_algos = Rails.application.config.allow_both_find_algos
end
-end
\ No newline at end of file
+end
diff --git a/web/app/views/api_bands/show.rabl b/web/app/views/api_bands/show.rabl
index 74d8dd08d..7c855b180 100644
--- a/web/app/views/api_bands/show.rabl
+++ b/web/app/views/api_bands/show.rabl
@@ -1,7 +1,9 @@
object @band
attributes :id, :name, :city, :state, :country, :location, :website, :biography, :photo_url, :logo_url, :liker_count, :follower_count, :recording_count, :session_count,
-:original_fpfile_photo, :cropped_fpfile_photo, :crop_selection_photo
+:original_fpfile_photo, :cropped_fpfile_photo, :crop_selection_photo,
+:band_type, :band_status, :concert_count, :add_new_members, :play_commitment, :touring_option, :paid_gigs,
+:free_gigs, :hourly_rate, :gig_minimum
child :users => :musicians do
attributes :id, :first_name, :last_name, :name, :photo_url
@@ -16,11 +18,27 @@ child :users => :musicians do
end
end
+child :instruments => :instruments do
+ attributes :id, :instrument_id, :proficiency_level
+end
+
child :genres => :genres do
attributes :id, :description
#partial('api_genres/index', :object => @band.genres)
end
+child :performance_samples => :performance_samples do
+ attributes :id, :url, :service_type, :claimed_recording_id, :service_id, :description
+
+ child :claimed_recording => :claimed_recording do
+ attributes :id, :name
+ end
+end
+
+child :online_presences => :online_presences do
+ attributes :id, :service_type, :username
+end
+
if current_user
node :is_following do |uu|
current_user.following?(@band)
diff --git a/web/app/views/api_claimed_recordings/show.rabl b/web/app/views/api_claimed_recordings/show.rabl
index c22a49ad3..379d1cb96 100644
--- a/web/app/views/api_claimed_recordings/show.rabl
+++ b/web/app/views/api_claimed_recordings/show.rabl
@@ -6,7 +6,7 @@
object @claimed_recording
-attributes :id, :name, :description, :is_public, :genre_id, :discarded
+attributes :id, :user_id, :name, :description, :is_public, :genre_id, :discarded
node :share_url do |claimed_recording|
unless claimed_recording.share_token.nil?
@@ -20,7 +20,7 @@ node :mix do |claimed_recording|
end
child(:recording => :recording) {
- attributes :id, :created_at, :duration, :comment_count, :like_count, :play_count, :jam_track_id, :jam_track_initiator_id
+ attributes :id, :band, :created_at, :duration, :comment_count, :like_count, :play_count, :has_mix?, :mix_state, :when_will_be_discarded?, :jam_track_id, :jam_track_initiator_id
node :timeline do |recording|
recording.timeline ? JSON.parse(recording.timeline) : {}
@@ -78,4 +78,20 @@ child(:recording => :recording) {
attributes :id, :first_name, :last_name, :name, :photo_url, :musician
}
}
+
+ node do |recording|
+ {
+ helpers: {
+ avatar: asset_path(resolve_avatarables(recording.band, recording.owner)),
+ artist_name: recording_artist_name(recording),
+ artist_id: recording_artist_id(recording),
+ artist_hoveraction: recording_artist_hoveraction(recording),
+ artist_datakey: recording_artist_datakey(recording),
+ utc_created_at: recording.created_at.getutc.iso8601,
+ name: recording_name(recording, current_user),
+ description: recording_description(recording, current_user),
+ genre: recording_genre(recording)
+ }
+ }
+ end
}
diff --git a/web/app/views/api_feeds/show.rabl b/web/app/views/api_feeds/show.rabl
index 31edd3497..1bbe751cd 100644
--- a/web/app/views/api_feeds/show.rabl
+++ b/web/app/views/api_feeds/show.rabl
@@ -78,7 +78,7 @@ glue :recording do
'recording'
end
- attributes :id, :band, :created_at, :duration, :comment_count, :like_count, :play_count, :has_mix?, :mix_state, :when_will_be_discarded?
+ attributes :id, :band, :created_at, :duration, :comment_count, :like_count, :play_count, :has_mix?, :mix_state, :when_will_be_discarded?
node do |recording|
{
diff --git a/web/app/views/api_search/index.rabl b/web/app/views/api_search/index.rabl
index 0f3479db6..ea256bf52 100644
--- a/web/app/views/api_search/index.rabl
+++ b/web/app/views/api_search/index.rabl
@@ -2,6 +2,64 @@ object @search
node :search_type do |ss| ss.search_type end
+if @search.is_a?(MusicianSearch)
+
+ node :page_count do |foo|
+ @search.page_count
+ end
+
+ node :my_audio_latency do |user|
+ current_user.last_jam_audio_latency.round if current_user.last_jam_audio_latency
+ end
+
+ node :is_blank_filter do |foo|
+ @search.is_blank?
+ end
+
+ node :filter_json do |foo|
+ @search.to_json
+ end
+
+ child(:results => :musicians) {
+ attributes :id, :first_name, :last_name, :name, :city, :state, :country, :online, :musician, :photo_url, :biography, :regionname, :score, :full_score
+
+ node :is_friend do |musician|
+ @search.is_friend?(musician)
+ end
+
+ node :is_following do |musician|
+ @search.is_follower?(musician)
+ end
+
+ node :pending_friend_request do |musician|
+ musician.pending_friend_request?(current_user)
+ end
+
+ node :biography do |musician|
+ musician.biography.nil? ? "" : musician.biography
+ end
+
+ child :musician_instruments => :instruments do
+ attributes :instrument_id, :description, :proficiency_level, :priority
+ end
+
+ child :top_followings => :followings do |uf|
+ node :user_id do |uu| uu.id end
+ node :photo_url do |uu| uu.photo_url end
+ node :name do |uu| uu.name end
+ end
+
+ node :follow_count do |musician| @search.follow_count(musician) end
+ node :friend_count do |musician| @search.friend_count(musician) end
+ node :recording_count do |musician| @search.record_count(musician) end
+ node :session_count do |musician| @search.session_count(musician) end
+
+ node :audio_latency do |musician|
+ last_jam_audio_latency(musician)
+ end
+ }
+else
+
if @search.session_invite_search?
child(:results => :suggestions) {
node :value do |uu| uu.name end
@@ -43,6 +101,14 @@ if @search.musicians_filter_search?
current_user.last_jam_audio_latency.round if current_user.last_jam_audio_latency
end
+ node :is_blank_filter do |foo|
+ @search.is_blank?
+ end
+
+ node :filter_json do |foo|
+ @search.to_json
+ end
+
child(:results => :musicians) {
attributes :id, :first_name, :last_name, :name, :city, :state, :country, :online, :musician, :photo_url, :biography, :regionname, :score, :full_score
@@ -131,3 +197,4 @@ if @search.fans_text_search?
}
end
+end
diff --git a/web/app/views/api_users/index.rabl b/web/app/views/api_users/index.rabl
index 27eb79ce0..00ecf01a6 100644
--- a/web/app/views/api_users/index.rabl
+++ b/web/app/views/api_users/index.rabl
@@ -1,4 +1,5 @@
collection @users
# do not retrieve all child collections when showing a list of users
-attributes :id, :first_name, :last_name, :name, :city, :state, :country, :email, :online, :musician, :photo_url, :biography
+attributes :id, :first_name, :last_name, :name, :city, :state, :country, :email, :online, :musician, :photo_url, :biography, :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
diff --git a/web/app/views/api_users/profile_show.rabl b/web/app/views/api_users/profile_show.rabl
new file mode 100644
index 000000000..56a822dfe
--- /dev/null
+++ b/web/app/views/api_users/profile_show.rabl
@@ -0,0 +1,33 @@
+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, :subscribe_email
+
+child :online_presences => :online_presences do
+ attributes :id, :service_type, :username
+end
+
+child :performance_samples => :performance_samples do
+ attributes :id, :url, :service_type, :claimed_recording_id, :service_id, :description
+
+ child :claimed_recording => :claimed_recording do
+ attributes :id, :name
+ end
+end
+
+child :genre_players => :genres do
+ attributes :genre_id, :player_type, :genre_type
+end
+
+child :band_musicians => :bands do
+ attributes :id, :name, :admin, :photo_url, :logo_url
+
+ child :genres => :genres do
+ attributes :id, :description
+ #partial('api_genres/index', :object => @user.bands.genres)
+ end
+end
+
+child :musician_instruments => :instruments do
+ attributes :description, :proficiency_level, :priority, :instrument_id
+end
\ No newline at end of file
diff --git a/web/app/views/api_users/show.rabl b/web/app/views/api_users/show.rabl
index 40cc96a77..412b38a6f 100644
--- a/web/app/views/api_users/show.rabl
+++ b/web/app/views/api_users/show.rabl
@@ -1,6 +1,7 @@
object @user
-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
+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, :reuse_card, :purchased_jamtracks_count
if @user.musician?
node :location do @user.location end
diff --git a/web/app/views/clients/_account.html.erb b/web/app/views/clients/_account.html.erb
index 29d806a94..2aa45e28f 100644
--- a/web/app/views/clients/_account.html.erb
+++ b/web/app/views/clients/_account.html.erb
@@ -105,19 +105,19 @@
-
jamtracks:
+ jamtracks:
- {{data.licenseDetail}}
+ {{data.licenseDetail}}
JamTracks License
diff --git a/web/app/views/clients/_account_profile.html.erb b/web/app/views/clients/_account_profile.html.erb
index af76ee2e7..7d7f08256 100644
--- a/web/app/views/clients/_account_profile.html.erb
+++ b/web/app/views/clients/_account_profile.html.erb
@@ -1,135 +1,98 @@
-
-
+
+
-
+
<%= image_tag "content/icon_account.png", {:width => 27, :height => 20} %>
-
+
my account
<%= render "screen_navigation" %>
-
-
-
-
-
-
-
-
-
diff --git a/web/app/views/clients/_account_profile_avatar.html.erb b/web/app/views/clients/_account_profile_avatar.html.erb
index 010ee663e..c9dc2eb49 100644
--- a/web/app/views/clients/_account_profile_avatar.html.erb
+++ b/web/app/views/clients/_account_profile_avatar.html.erb
@@ -46,10 +46,3 @@
-
-
diff --git a/web/app/views/clients/_account_profile_experience.html.erb b/web/app/views/clients/_account_profile_experience.html.erb
new file mode 100644
index 000000000..6485b118f
--- /dev/null
+++ b/web/app/views/clients/_account_profile_experience.html.erb
@@ -0,0 +1,97 @@
+
+
+
+ <%= image_tag "content/icon_account.png", {:width => 27, :height => 20} %>
+
+
my account
+ <%= render "screen_navigation" %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/app/views/clients/_account_profile_interests.html.erb b/web/app/views/clients/_account_profile_interests.html.erb
new file mode 100644
index 000000000..82aca53b9
--- /dev/null
+++ b/web/app/views/clients/_account_profile_interests.html.erb
@@ -0,0 +1,223 @@
+
+
+
+ <%= image_tag "content/icon_account.png", {:width => 27, :height => 20} %>
+
+
my account
+ <%= render "screen_navigation" %>
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/app/views/clients/_account_profile_samples.html.erb b/web/app/views/clients/_account_profile_samples.html.erb
new file mode 100644
index 000000000..22825b9f2
--- /dev/null
+++ b/web/app/views/clients/_account_profile_samples.html.erb
@@ -0,0 +1,33 @@
+
+
+
+ <%= image_tag "content/icon_account.png", {:width => 27, :height => 20} %>
+
+
my account
+ <%= render "screen_navigation" %>
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/app/views/clients/_bandProfile.html.erb b/web/app/views/clients/_bandProfile.html.erb
index 3f58378ee..8b0f819ce 100644
--- a/web/app/views/clients/_bandProfile.html.erb
+++ b/web/app/views/clients/_bandProfile.html.erb
@@ -1,5 +1,5 @@
-
+
<%= image_tag "content/icon_bands.png", :size => "19x19" %>
@@ -14,12 +14,17 @@