Merge branch 'develop' into feature/new_session

Conflicts:
	web/app/assets/javascripts/fakeJamClient.js
	web/app/assets/javascripts/globals.js
	web/app/assets/javascripts/trackHelpers.js
	web/app/assets/javascripts/utils.js
	web/app/views/dialogs/_dialogs.html.haml
This commit is contained in:
Seth Call 2015-07-08 12:34:06 -05:00
commit 3eea6c89d5
147 changed files with 8895 additions and 3249 deletions

View File

@ -0,0 +1,20 @@
require 'csv'
class EmailController < ApplicationController
respond_to :csv
def dump_emailables
if params[:code] != Rails.application.config.email_dump_code
render :text => "", :status => 404
return
end
headers['Content-Disposition'] = "attachment; filename=\"user-list.csv\""
headers['Content-Type'] ||= 'text/csv'
@users = User.where(subscribe_email: true)
end
end

View File

@ -0,0 +1,2 @@
<%- headers = ['email', 'name', 'unsubscribe_token'] -%>
<%= CSV.generate_line headers %><%- @users.each do |user| -%><%= CSV.generate_line([user.email, user.first_name, user.unsubscribe_token]) %><%- end -%>

View File

@ -151,5 +151,7 @@ module JamAdmin
config.jamtracks_dir = ENV['JAMTRACKS_DIR'] || File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "jamtracks"))
config.jmep_dir = ENV['JMEP_DIR'] || File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "jmep"))
config.email_dump_code = 'rcAUyC3TZCbgGx4YQpznBRbNnQMXW5iKTzf9NSBfzMLsnw9dRQ'
end
end

View File

@ -4,4 +4,4 @@
# If you change this key, all old signed cookies will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
JamAdmin::Application.config.secret_token = 'e27a8deff5bc124d1c74cb86ebf38ac4a246091b859bcccf4f8076454e0ff3e04ffc87c9a0f4ddc801c4753e20b2a3cf06e5efc815cfe8e6377f912b737c5f77'
JamAdmin::Application.config.secret_token = 'ced345e01611593c1b783bae98e4e56dbaee787747e92a141565f7c61d0ab2c6f98f7396fb4b178258301e2713816e158461af58c14b695901692f91e72b6200'

View File

@ -34,6 +34,8 @@ JamAdmin::Application.routes.draw do
match '/api/mix/:id/enqueue' => 'admin/mixes#mix_again', :via => :post
match '/api/checks/latency_tester' => 'checks#check_latency_tester', :via => :get
match '/api/users/emailables/:code' => 'email#dump_emailables', :via => :get
mount Resque::Server.new, :at => "/resque"

View File

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

View File

@ -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,4 +292,6 @@ signing.sql
optimized_redeemption.sql
optimized_redemption_warn_mode.sql
affiliate_partners2.sql
broadcast_notifications.sql
enhance_band_profile.sql
broadcast_notifications.sql
broadcast_notifications_fk.sql

View File

@ -0,0 +1 @@
alter table performance_samples add column description varchar(256) NULL;

1
db/up/add_genre_type.sql Normal file
View File

@ -0,0 +1 @@
alter table genre_players add column genre_type varchar(20) default 'profile';

View File

@ -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);

View File

@ -0,0 +1,2 @@
ALTER TABLE user_presences RENAME COLUMN type to service_type;
ALTER TABLE performance_samples RENAME COLUMN type to service_type;

View File

@ -0,0 +1,2 @@
ALTER TABLE broadcast_notification_views DROP CONSTRAINT broadcast_notification_views_user_id_fkey;
ALTER TABLE broadcast_notification_views ADD CONSTRAINT broadcast_notification_views_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;

View File

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

10
db/up/musician_search.sql Normal file
View File

@ -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);

View File

@ -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
);

View File

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

1
ruby/jt_metadata.json Normal file
View File

@ -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"}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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'
@ -106,8 +107,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'

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 828 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -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);

View File

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

View File

@ -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('<div class="clearall recording-row left entry"' + recordingIdAttr + recordingUrlAttr + recordingTitleAttr + '>' + title + '</div>');
$sampleList.append('<div class="right close-button" data-recording-type="' + type + '"' + recordingIdAttr + '>X</div>');
});
}
}
}
function buildJamkazamEntry(recordingId, recordingName) {
var title = formatTitle(recordingName);
var recordingIdAttr = ' data-recording-id="' + recordingId + '" ';
$jamkazamSampleList.append('<div class="clearall recording-row left entry"' + recordingIdAttr + '>' + title + '</div>');
$jamkazamSampleList.append('<div class="right close-button" data-recording-type="jamkazam"' + recordingIdAttr + '>X</div>');
}
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("<span class='error-text'>Invalid username</span>").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('<div class="clearall recording-row left entry"' + recordingIdAttr + recordingUrlAttr + recordingTitleAttr + '>' + title + '</div>');
sampleList.append('<div class="right close-button" data-recording-type="' + type + '"' + recordingIdAttr + '>X</div>');
}
$inputDiv.find('input').val('');
}
function siteFailCallback($inputDiv) {
$inputDiv.addClass('error');
$inputDiv.find('.error-text').remove();
$inputDiv.append("<span class='error-text'>Invalid URL</span>").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);

File diff suppressed because it is too large Load Diff

View File

@ -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 = '<option value=""></option>';
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 = '<option value=""></option>'
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<STEPS_COUNT-1) {
saveBand(function(band) {
currentStep++
renderCurrentPage()
})
} else {
saveBand(function(band) {
resetForm()
showProfile(band.id);
})
}
}
function renderCurrentPage() {
$screen.find($(".band-step")).addClass("hidden")
$("#band-setup-step-" + currentStep).removeClass("hidden")
if(currentStep==0) {
$("#btn-band-setup-back").addClass("hidden")
$("#btn-band-setup-next").removeClass("hidden").html("SAVE & NEXT")
} else if(currentStep<STEPS_COUNT-1) {
// if(currentStep==SAMPLE_STEP) {
// accountProfileSamples.renderPlayer(band)
// }
$("#btn-band-setup-back").removeClass("hidden")
$("#btn-band-setup-next").removeClass("hidden").html("SAVE & NEXT")
} else {
$("#btn-band-setup-back").removeClass("hidden")
$("#btn-band-setup-next").removeClass("hidden").html("SAVE & FINISH")
}
renderOptionalControls()
}
function renderOptionalControls(e) {
if(e){e.stopPropagation()}
// Is new member selected?
if ($screen.find($('input[name="add_new_members"]:checked')).val()=="yes") {
$screen.find($(".new-member-dependent")).removeClass("hidden")
} else {
$screen.find($(".new-member-dependent")).addClass("hidden")
}
// Is paid gigs selected?
if ($('input[name="paid_gigs"]:checked').val()=="yes") {
$screen.find($(".paid-gigs-dependent")).removeClass("hidden")
} else {
$screen.find($(".paid-gigs-dependent")).addClass("hidden")
}
return false;
}
function isNewBand() {
return bandId==null || typeof(bandId)=='undefined' || bandId.length == 0;
}
function removeErrors() {
@ -38,6 +121,7 @@
function resetForm() {
removeErrors();
accountProfileSamples.resetForm()
// name
$("#band-name").val('');
@ -60,10 +144,17 @@
// website
$('#band-website').val('');
resetGenres();
$("#new-member-no").iCheck('check').attr('checked', 'checked')
$("#paid-gigs-no").iCheck('check').attr('checked', 'checked')
$("#free-gigs-no").iCheck('check').attr('checked', 'checked')
$('#touring-option').val('no')
$("#band-setup-step-1").show();
$("#band-setup-step-2").hide();
$("#play-commitment").val('1')
$("#hourly-rate").val("0.0")
$("#gig-minimum").val("0.0")
resetGenres();
renderDesiredExperienceLabel([])
$(friendInput)
.unbind('blur')
@ -93,102 +184,144 @@
removeErrors();
var band = buildBand();
return rest.validateBand(band);
}
function renderErrors(errors) {
logger.debug("Band setup errors: ", errors)
var name = context.JK.format_errors("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 biography = context.JK.format_errors("biography", errors);
var genres = context.JK.format_errors("genres", errors);
var website = context.JK.format_errors("website", errors);
var genres = context.JK.format_errors("genres", errors);
if(name) $("#band-name").closest('div.field').addClass('error').end().after(name);
if(country) $("#band-country").closest('div.field').addClass('error').end().after(country);
if(state) $("#band-region").closest('div.field').addClass('error').end().after(state);
if(city) $("#band-city").closest('div.field').addClass('error').end().after(city);
if(genres) $(".band-setup-genres").closest('div.field').addClass('error').end().after(genres);
if(name) $("#band-name").closest('div.field').addClass('error').end().after(name);
if(country) $("#band-country").closest('div.field').addClass('error').end().after(country);
if(state) $("#band-region").closest('div.field').addClass('error').end().after(state);
if(city) $("#band-city").closest('div.field').addClass('error').end().after(city);
if(biography) $("#band-biography").closest('div.field').addClass('error').end().after(biography);
if(website) $("#band-website").closest('div.field').addClass('error').end().after(website);
if(website) $("#band-website").closest('div.field').addClass('error').end().after(website);
if(genres) $("#band-genres").closest('div.field').addClass('error').end().after(genres);
}
function buildBand() {
var band = {};
var band = {instruments:[]};
band.id = (isNewBand()) ? null : bandId;
band.name = $("#band-name").val();
band.website = $("#band-website").val();
band.biography = $("#band-biography").val();
band.city = $("#band-city").val();
band.state = $("#band-region").val();
band.country = $("#band-country").val();
band.genres = getSelectedGenres();
band.band_type = bandType.val();
band.band_status= bandStatus.val();
band.concert_count= concertCount.val();
band.add_new_members = $('input[name="add_new_members"]:checked').val()=="yes"
band.paid_gigs = $('input[name="paid_gigs"]:checked').val()=="yes"
band.free_gigs=$('input[name="free_gigs"]:checked').val()=="yes"
band.touring_option=$('#touring-option').val()=="yes"
band.play_commitment=$("#play-commitment").val()
band.hourly_rate=$("#hourly-rate").val()
band.gig_minimum=$("#gig-minimum").val()
if (currentStep==GENRE_STEP) {
band.genres = getSelectedGenres();
band.validate_genres = true
} else {
band.validate_genres = false
}
$.each($selectedInstruments, function(index, instrument) {
var h = {}
h.instrument_id = instrument.id
h.proficiency_level = instrument.level
band.instruments.push(h)
})
if(!isNewBand()) {
mergePerformanceSamples(band)
}
return band;
}
function mergePerformanceSamples(band) {
// Collect and merge data from this sub-widget:
var performanceSampleData = accountProfileSamples.buildPlayer()
band.website=performanceSampleData.website
band.online_presences=performanceSampleData.online_presences
band.performance_samples=performanceSampleData.performance_samples
// Change player id to that of band. Widget currently hardwires current user id:
if(band.online_presences) {
for (var i=0; i<band.online_presences.length; ++i) {
band.online_presences[i].player_id = band.id
}
}
// Change player id to that of band. Widget currently hardwires current user id:
if(band.performance_samples) {
for (var i=0; i<band.performance_samples.length; ++i) {
band.performance_samples[i].player_id = band.id
}
}
return band
}
function renderDesiredExperienceLabel(selectedInstruments) {
$selectedInstruments=selectedInstruments
var instrumentText=""
$.each($selectedInstruments, function(index, instrument) {
if (instrumentText.length!=0) {instrumentText += ", "}
instrumentText += instrument.name
})
$("#desired-experience-label").html(($selectedInstruments && $selectedInstruments.length > 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', "<div class='left w70'>If your bandmates are already on JamKazam, start typing their names in the box below, or click the Choose Friends button to select them.</div>");
invitationDialog = invitationDialogInstance;
events();
inviteMusiciansUtil = new JK.InviteMusiciansUtil(app)
inviteMusiciansUtil.initialize(friendSelectorDialog)
friendInput = inviteMusiciansUtil.inviteBandCreate('#band-setup-invite-musicians', "<div class='left w70'>If your bandmates are already on JamKazam, start typing their names in the box below, or click the Choose Friends button to select them.</div>")
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);

View File

@ -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('<li>');
var checked = '';
if (genres && $.inArray(val.id, genres) > -1) {
checked = 'checked';
}
$genres.append('<input type="checkbox" value="' + val.id + '" ' + checked + ' />' + val.description);
$genres.append('</li>');
});
}
}
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);

View File

@ -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('<li>');
// var checked = '';
// if (instruments && $.inArray(val.id, selectedInstruments) > -1) {
// checked = 'checked';
// }
// $instruments.append('<input type="checkbox" value="' + val.id + '" ' + checked + ' />' + val.description);
// $instruments.append('</li>');
// });
// }
}
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);

View File

@ -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("<div class='left'><input type='checkbox' " + checked + " data-recording-id='" + claimedRecording.id + "' data-recording-title='" + claimedRecording.name + "' />");
$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);

View File

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

View File

@ -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;
}

View File

@ -363,4 +363,4 @@
"Metronome" : "Metronome"
}
})(window,jQuery);
})(window,jQuery);

View File

@ -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);
}
});
}

View File

@ -196,7 +196,7 @@
if (includePending) {
includeFlag = 'true';
}
return $.ajax({
type: "GET",
dataType: "json",
@ -521,6 +521,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",
@ -1289,7 +1303,7 @@
function openBackingTrack(options) {
var musicSessionId = options["id"];
delete options["id"];
return $.ajax({
type: "POST",
dataType: "json",
@ -1352,7 +1366,7 @@
function openMetronome(options) {
var musicSessionId = options["id"];
delete options["id"];
return $.ajax({
type: "POST",
dataType: "json",
@ -1361,11 +1375,11 @@
data: JSON.stringify(options)
})
}
function closeMetronome(options) {
var musicSessionId = options["id"];
delete options["id"];
return $.ajax({
type: "POST",
dataType: "json",
@ -1374,7 +1388,7 @@
data: JSON.stringify(options)
})
}
function discardRecording(options) {
var recordingId = options["id"];
@ -1613,7 +1627,7 @@
url: '/api/recurly/payment_history',
dataType: "json",
contentType: 'application/json'
});
});
}
function getSalesHistory(options) {
@ -1709,7 +1723,7 @@
function placeOrder() {
return $.ajax({
type: "POST",
type: "POST",
url: '/api/recurly/place_order',
dataType: "json",
contentType: 'application/json'
@ -1723,6 +1737,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({
@ -1824,6 +1851,7 @@
this.cancelSession = cancelSession;
this.updateScheduledSession = updateScheduledSession;
this.getUserDetail = getUserDetail;
this.getUserProfile = getUserProfile;
this.getAffiliatePartnerData = getAffiliatePartnerData;
this.postAffiliatePartnerData = postAffiliatePartnerData;
this.createAffiliatePartner = createAffiliatePartner;
@ -1907,7 +1935,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;
@ -1965,6 +1993,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;
@ -1972,5 +2002,4 @@
this.portOverCarts = portOverCarts;
return this;
};
})(window,jQuery);

View File

@ -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 = $ '<option value=""></option>'
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 += '<img height="24" width="24" src="' + instr_img.asset + '" title="' + instr_id + '"/>'
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()

View File

@ -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 + '<br />');
}
$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('<span class="friend-count">' + user.friend_count + '</span>' + text);
$friendStats.html('<span class="friend-count">' + user.friend_count + '</span>' + text);
text = user.follower_count > 1 || user.follower_count === 0 ? " Followers" : " Follower";
$('#profile-follower-stats').html('<span class="follower-count">' + user.follower_count + '</span>' + text);
$followerStats.html('<span class="follower-count">' + user.follower_count + '</span>' + 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('<li>Genre(s): ' + (genreList.length > 0 ? genreList : NOT_SPECIFIED_TEXT) + '</li>');
var hourlyRate = user.paid_sessions_hourly_rate;
$paidGigDetails.find("ul").append('<li>Hourly rate = ' + (hourlyRate ? hourlyRate : NOT_SPECIFIED_TEXT) + '</li>');
var dailyRate = user.paid_sessions_daily_rate;
$paidGigDetails.find("ul").append('<li>Day rate = ' + (dailyRate ? dailyRate : NOT_SPECIFIED_TEXT) + '</li>');
} 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('<li>Genre(s): ' + (genreList.length > 0 ? genreList : NOT_SPECIFIED_TEXT) + '</li>');
} else {
$freeGigSection.hide();
}
// cowriting
if (user.cowriting) {
$cowritingSection.show();
$cowritingDetails.find("ul").html("");
var genreList = profileUtils.cowritingGenreList(user.genres);
$cowritingDetails.find("ul").append('<li>Genre(s): ' + (genreList.length > 0 ? genreList : NOT_SPECIFIED_TEXT) + '</li>');
var purpose = user.cowriting_purpose;
$cowritingDetails.find("ul").append('<li>Purpose: ' + (purpose ? profileUtils.cowritingPurposeMap[purpose] : NOT_SPECIFIED_TEXT) + '</li>');
} 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('<li>Genre(s): ' + (genreList.length > 0 ? genreList : NOT_SPECIFIED_TEXT) + '</li>');
var commitment = user.traditional_band_commitment;
$traditionalBandDetails.find("ul").append('<li>Commitment: ' + (commitment ? profileUtils.bandCommitmentMap[commitment] : NOT_SPECIFIED_TEXT) + '</li>');
var canTour = user.traditional_band_touring;
var canTourResponse = canTour ? "Yes" : (canTour === false ? "No" : NOT_SPECIFIED_TEXT);
$traditionalBandDetails.find("ul").append('<li>Touring: ' + canTourResponse + '</li>');
} 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('<li>Genre(s): ' + (genreList.length > 0 ? genreList : NOT_SPECIFIED_TEXT) + '</li>');
var commitment = user.virtual_band_commitment;
$virtualBandDetails.find("ul").append('<li>Commitment: ' + (commitment ? profileUtils.bandCommitmentMap[commitment] : NOT_SPECIFIED_TEXT) + '</li>');
} 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('&nbsp;');
$socialFriends.html('&nbsp;');
}
context.JK.bindHoverEvents();
})
@ -536,11 +647,11 @@
location: val.location
});
$('#profile-social-followings').append(followingHtml);
$socialFollowings.append(followingHtml);
});
}
else {
$('#profile-social-followings').html('&nbsp;');
$socialFollowings.html('&nbsp;');
}
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;

View File

@ -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("<a class='jamkazam-playable' href='/recordings/" + sample.claimed_recording.id + "' rel='external'>" + formatTitle(sample.claimed_recording.name) + "</a><br/>");
});
$.each(soundCloudSamples, function(index, sample) {
$soundCloudSamples.append("<a class='sound-cloud-playable' href='' soundcloud_url='" + sample.url + "'>" + formatTitle(sample.description) + "</a><br/>");
});
$.each(youTubeSamples, function(index, sample) {
$youTubeSamples.append("<a class='youtube-playable' href='" + sample.url + "' rel='external'>" + formatTitle(sample.description) + "</a><br/>");
});
}
}// 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);

View File

@ -14,6 +14,7 @@
var logger = context.JK.logger;
var self = this;
var webcamViewer = new context.JK.WebcamViewer()
var ChannelGroupIds = context.JK.ChannelGroupIds;
var defaultParticipant = {
tracks: [{
@ -39,23 +40,6 @@
height: 83
};
// Recreate ChannelGroupIDs ENUM from C++
var ChannelGroupIds = {
"MasterGroup": 0,
"MonitorGroup": 1,
"AudioInputMusicGroup": 2,
"AudioInputChatGroup": 3,
"MediaTrackGroup": 4,
"StreamOutMusicGroup": 5,
"StreamOutChatGroup": 6,
"UserMusicInputGroup": 7,
"UserChatInputGroup": 8,
"PeerAudioInputMusicGroup": 9,
"PeerMediaTrackGroup": 10,
"JamTrackGroup": 11,
"MetronomeGroup": 12
};
var METRO_SOUND_LOOKUP = {
0 : "BuiltIn",
1 : "SineWave",

View File

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

View File

@ -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;
};

View File

@ -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;
@ -23,6 +22,9 @@
var os = null;
var reactHovers = []
context.JK.getGenreList = function() {
return context.JK.Rest().getGenres();
}
context.JK.stringToBool = function (s) {
switch (s.toLowerCase()) {

View File

@ -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");

View File

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

View File

@ -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%;
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -32,6 +32,9 @@
*= require ./findSession
*= require ./session
*= require ./account
*= require ./accountProfileExperience
*= require ./accountProfileInterests
*= require ./accountProfileSamples
*= require ./accountPaymentHistory
*= require ./account_affiliate
*= require ./search

View File

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

View File

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

View File

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

View File

@ -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 {
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,14 @@
@import "client/common";
#recording-selector-dialog {
min-height:initial;
.dialog-inner {
color:white;
}
.action-buttons {
margin-bottom:10px;
}
}

View File

@ -0,0 +1,6 @@
#sound-cloud-player-dialog {
height:auto;
.caption {
margin: 0.1em 0.1em 0.5em 0em
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -402,12 +402,14 @@ JS
redirect_to '/'
end if params[:user_token].present?
if request.get?
#if request.get?
elsif request.post?
#elsif request.post?
@user.subscribe_email = false
@user.save!
end
#end
render text: 'You have been unsubscribed.'
end
private

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More