diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb index 6b2d70a20..7d7ffd4e6 100755 --- a/ruby/lib/jam_ruby.rb +++ b/ruby/lib/jam_ruby.rb @@ -239,7 +239,9 @@ 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/base_search" require "jam_ruby/models/musician_search" +require "jam_ruby/models/band_search" require "jam_ruby/import/tency_stem_mapping" include Jampb diff --git a/ruby/lib/jam_ruby/models/band.rb b/ruby/lib/jam_ruby/models/band.rb index c3d4b471d..2ba858c6e 100644 --- a/ruby/lib/jam_ruby/models/band.rb +++ b/ruby/lib/jam_ruby/models/band.rb @@ -71,6 +71,7 @@ module JamRuby end def follower_count + # FIXME: this could be a lot of followers; calling size loads all the data into memory self.followers.size end diff --git a/ruby/lib/jam_ruby/models/band_search.rb b/ruby/lib/jam_ruby/models/band_search.rb new file mode 100644 index 000000000..976250935 --- /dev/null +++ b/ruby/lib/jam_ruby/models/band_search.rb @@ -0,0 +1,428 @@ +module JamRuby + class BandSearch < BaseSearch + + cattr_accessor :jschema, :search_meta + attr_accessor :user_counters + + serialize :data_blob, JSON + + KEY_BAND_SEARCH_TYPE = 'band_search_type' + KEY_BAND_TYPE = 'band_type' + KEY_BAND_STATUS = 'band_status' + KEY_PLAY_COMMIT = 'play_commitment' + KEY_TOUR_OPTION = 'touring_option' + KEY_PERF_SAMPLES = 'performance_samples' + KEY_HIRE_MAX_COST = 'max_cost' + KEY_HIRE_FREE = 'free_gigs' + + TO_JOIN = 'to_join' + TO_HIRE = 'to_hire' + + BAND_SEARCH_TYPE_VALS = [TO_JOIN, TO_HIRE] + BAND_SEARCH_TYPES = { + TO_JOIN => 'search bands', + TO_HIRE => 'search bands to hire', + } + + BAND_TYPE_VAL_STRS = [ANY_VAL_STR, 'amateur', 'professional'] + BAND_TYPES = { + BAND_TYPE_VAL_STRS[0] => BAND_TYPE_VAL_STRS[0].camelcase, + BAND_TYPE_VAL_STRS[1] => BAND_TYPE_VAL_STRS[1].camelcase, + BAND_TYPE_VAL_STRS[2] => BAND_TYPE_VAL_STRS[2].camelcase, + } + + SORT_VALS = %W{ distance } + SORT_ORDERS = { + SORT_VALS[0] => 'Distance to Me' + } + # SORT_VALS = %W{ distance latency } + # SORT_ORDERS = { + # SORT_VALS[0] => 'Distance to Me' + # SORT_VALS[1] => 'Latency to Me', + # } + + HIRE_SORT_VALS = %W{ distance price_asc price_desc } + HIRE_SORT_ORDERS = { + HIRE_SORT_VALS[0] => 'Distance to Me', + HIRE_SORT_VALS[1] => 'Gig Minimum Price (Low to High)', + HIRE_SORT_VALS[2] => 'Gig Minimum Price (High to Low)', + } + + BAND_STATUS_VALS = [ANY_VAL_STR, + GenrePlayer::VIRTUAL_BAND, + GenrePlayer::TRADITIONAL_BAND, + ] + BAND_STATUS = { + BAND_STATUS_VALS[0] => 'Any', + BAND_STATUS_VALS[1] => 'Virtual Band', + BAND_STATUS_VALS[2] => 'Traditional Band', + } + + PLAY_COMMIT_VALS = [ANY_VAL_STR, + '1', + '2', + '3', + '4', + ] + PLAY_COMMITS = { + PLAY_COMMIT_VALS[0] => 'Any', + PLAY_COMMIT_VALS[1] => 'Infrequent', + PLAY_COMMIT_VALS[2] => 'Once a Week', + PLAY_COMMIT_VALS[3] => '2-3 Times Per Week', + PLAY_COMMIT_VALS[4] => '4+ Times Per Week', + } + + TOUR_OPTION_VALS = [ANY_VAL_STR, + VAL_YES, + VAL_NO, + ] + TOUR_OPTIONS = { + TOUR_OPTION_VALS[0] => 'Any', + TOUR_OPTION_VALS[1] => VAL_YES, + TOUR_OPTION_VALS[2] => VAL_NO, + } + + PERF_SAMPLES_VALS = TOUR_OPTION_VALS.clone + PERF_SAMPLES = TOUR_OPTIONS.clone + + COUNT_FOLLOW = :count_follow + COUNT_RECORD = :count_record + COUNT_SESSION = :count_session + + def self.json_schema + return @@jschema if @@jschema + @@jschema = { + TO_JOIN => BaseSearch.json_schema.merge({ + KEY_SORT_ORDER => self::SORT_VALS[0], + KEY_BAND_TYPE => self::BAND_TYPE_VAL_STRS[0].to_s, + KEY_BAND_STATUS => BAND_STATUS_VALS[0], + KEY_PLAY_COMMIT => PLAY_COMMIT_VALS[0], + KEY_TOUR_OPTION => TOUR_OPTION_VALS[0], + }), + TO_HIRE => { + KEY_SORT_ORDER => self::HIRE_SORT_VALS[0], + KEY_GENRES => [], + KEY_GIGS => self::GIG_COUNTS[0].to_s, + KEY_BAND_STATUS => BAND_STATUS_VALS[0], + KEY_PERF_SAMPLES => self::PERF_SAMPLES_VALS[0], + KEY_HIRE_MAX_COST => 0, + KEY_HIRE_FREE => 0, + }, + } + end + + def self.search_filter_meta + return @@search_meta if @@search_meta + toJoinMeta = super(self.json_schema[TO_JOIN]) + toJoinMeta.merge!({ + KEY_BAND_TYPE => { keys: BAND_TYPE_VAL_STRS, map: BAND_TYPES }, + KEY_BAND_STATUS => { keys: BAND_STATUS_VALS, map: BAND_STATUS }, + KEY_PLAY_COMMIT => { keys: PLAY_COMMIT_VALS, map: PLAY_COMMITS }, + KEY_TOUR_OPTION => { keys: TOUR_OPTION_VALS, map: TOUR_OPTIONS } + }) + toHireMeta = super(self.json_schema[TO_HIRE], + { keys: HIRE_SORT_VALS, map: HIRE_SORT_ORDERS }) + toHireMeta.merge!({ + KEY_BAND_STATUS => { keys: BAND_STATUS_VALS, map: BAND_STATUS }, + KEY_PERF_SAMPLES => { keys: PERF_SAMPLES_VALS, map: PERF_SAMPLES }, + }) + @@search_meta = { + TO_JOIN => toJoinMeta, + TO_HIRE => toHireMeta, + } + end + + def self.search_target_class + Band + end + + def _genres(rel, filter) + super(rel, filter) + end + + def _concert_gigs(rel, filter) + gg = filter[KEY_GIGS].to_i + rel = rel.where(concert_count: gg) if 0 <= gg + rel + end + + def _band_status(rel, filter) + case filter[KEY_BAND_STATUS] + when GenrePlayer::VIRTUAL_BAND + rel.where(band_status: GenrePlayer::VIRTUAL_BAND.sub('_band','')) + when GenrePlayer::TRADITIONAL_BAND + rel.where(band_status: GenrePlayer::TRADITIONAL_BAND.sub('_band','')) + else + rel + end + end + + def _play_commitment(rel, filter) + unless ANY_VAL_STR == filter[KEY_PLAY_COMMIT] + rel = rel.where(play_commitment: filter[KEY_PLAY_COMMIT].to_i) + end + rel + end + + def _touring_option(rel, filter) + case filter[KEY_TOUR_OPTION] + when VAL_YES + rel.where(touring_option: true) + when VAL_NO + rel.where(touring_option: false) + else + rel + end + end + + def _performance_samples(rel, filter) + case filter[KEY_PERF_SAMPLES] + when VAL_YES + rel.joins("LEFT OUTER JOIN performance_samples AS ps ON ps.player_id = bands.id AND player_type = '#{Band.name}'").where(["ps.id IS NOT NULL"]) + when VAL_NO + rel.joins("LEFT OUTER JOIN performance_samples AS ps ON ps.player_id = bands.id AND player_type = '#{Band.name}'").where(["ps.id IS NULL"]) + else + rel + end + end + + def _max_cost(rel, filter) + if 0 < (max_cost = filter[KEY_HIRE_MAX_COST].to_i) + col = Band.arel_table[:gig_minimum] + rel = rel.where(col.lteq(max_cost)).where(col.gt(0)) + end + rel + end + + def _free_gigs(rel, filter) + case filter[KEY_HIRE_FREE] + when VAL_YES + rel.where(free_gigs: true) + when VAL_NO + rel.where(free_gigs: false) + else + rel + end + end + + def _band_type(rel, filter) + case filter[KEY_BAND_TYPE] + when BAND_TYPE_VAL_STRS[1] + rel.where(band_type: BAND_TYPE_VAL_STRS[1]) + when BAND_TYPE_VAL_STRS[2] + rel.where(band_type: BAND_TYPE_VAL_STRS[2]) + else + rel + end + end + + def _sort_order(rel, filter) + val = filter[KEY_SORT_ORDER] + if 'distance' == val || val.blank? + 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 other_geo.latitude = bands.lat AND other_geo.longitude = bands.lng") + rel = rel.group("bands.id, my_geo.geog, other_geo.geog") + rel = rel.order('st_distance(my_geo.geog, other_geo.geog)') + + elsif 'price_asc' == val + rel = rel.order('gig_minimum ASC') + + elsif 'price_desc' == val + rel = rel.order('gig_minimum DESC') + end + rel + end + + def do_search(filter) + rel = Band.unscoped + filter.keys.each do |fkey| + mname = "_#{fkey}" + if self.respond_to?(mname) + rel = self.send(mname.to_sym, rel, filter) + end + end + rel + end + + def search_includes(rel, subtype=TO_JOIN) + TO_JOIN == subtype ? rel.includes([:instruments]) : rel + end + + def _process_results_page(_results) + @results = _results + if user + @user_counters = @results.inject({}) { |hh,val| hh[val.id] = {}; hh } + + # this gets counts for each search result + @results.each do |bb| + counters = { + COUNT_FOLLOW => Follow.where(:followable_id => bb.id).count, + COUNT_RECORD => Recording.where(:band_id => bb.id).count, + COUNT_SESSION => MusicSession.where(:band_id => bb.id).count + } + @user_counters[bb.id] = counters + end + else + @user_counters = {} + end + self + end + + private + + def _count(band, key) + if mm = @user_counters[band.id] + return mm[key] + end if @user_counters + 0 + end + + public + + def follow_count(band) + _count(band, COUNT_FOLLOW) + end + + def record_count(band) + _count(band, COUNT_RECORD) + end + + def session_count(band) + _count(band, COUNT_SESSION) + end + + def is_follower?(band) + if mm = @user_counters[band.id] + return mm.include?(RESULT_FOLLOW) + end if @user_counters + false + end + + def search_type + self.class.to_s + end + + def is_blank?(subtype=TO_JOIN) + self.search_filter_for_subtype(subtype) == self.class.json_schema[subtype] + end + + def reset_filter(subtype, data=nil) + data ||= self.class.json_schema[subtype] + dblob = self.data_blob + dblob[subtype] = data + self.data_blob = dblob + self.save + end + + def reset_search_results(subtype=TO_JOIN) + reset_filter(subtype) + search_results_page(subtype) + end + + def self.search_filter_json(user, subtype=TO_JOIN) + self.user_search_filter(user).json[subtype] + end + + def search_filter_for_subtype(subtype) + self.data_blob[subtype] + end + + def search_results_page(subtype=TO_JOIN, filter=nil, page=1) + if filter + reset_filter(subtype, filter) + else + filter = self.search_filter_for_subtype(subtype) + end + rel = do_search(filter) + + @page_number = [page.to_i, 1].max + rel = rel.paginate(:page => @page_number, :per_page => self.class::PER_PAGE) + + rel = self.search_includes(rel, subtype) + @page_count = rel.total_pages + + _process_results_page(rel.all) + end + + def _add_description(descrip, add) + descrip += "; " if 0 < descrip.length + descrip + add + end + + def description(subtype=TO_JOIN) + return '' if self.is_blank?(subtype) + + filter = search_filter_for_subtype(subtype) + str = '' + + if filter.has_key?(KEY_SORT_ORDER) + str += 'Sort = ' + case sort = filter[KEY_SORT_ORDER] + when 'distance' + str += SORT_ORDERS[sort] + when 'latency' + str += SORT_ORDERS[sort] + when 'price_asc' + str += HIRE_SORT_ORDERS[sort] + when 'price_desc' + str += HIRE_SORT_ORDERS[sort] + end + end + if (val = filter[KEY_BAND_TYPE]) != ANY_VAL_STR + str = _add_description(str, "Band type = #{BAND_TYPES[val]}") + end if filter.has_key?(KEY_BAND_TYPE) + + if (val = filter[KEY_BAND_STATUS]) != ANY_VAL_STR + str = _add_description(str, "Band status = #{BAND_STATUS[val]}") + end if filter.has_key?(KEY_BAND_STATUS) + + if (val = filter[KEY_PLAY_COMMIT]) != ANY_VAL_STR + str = _add_description(str, "Play commitment = #{PLAY_COMMITS[val]}") + end if filter.has_key?(KEY_PLAY_COMMIT) + + if (val = filter[KEY_TOUR_OPTION]) != ANY_VAL_STR + str = _add_description(str, "Touring options = #{TOUR_OPTIONS[val]}") + end if filter.has_key?(KEY_TOUR_OPTION) + + if (val = filter[KEY_PERF_SAMPLES]) != ANY_VAL_STR + str = _add_description(str, "Performance samples = #{PERF_SAMPLES[val]}") + end if filter.has_key?(KEY_PERF_SAMPLES) + + if (val = filter[KEY_HIRE_MAX_COST].to_i) > 0 + str = _add_description(str, "Maximum gig cost = $#{val}") + end if filter.has_key?(KEY_HIRE_MAX_COST) + + if 0 < filter[KEY_HIRE_FREE] + str = _add_description(str, "Bands playing free gigs") + end if filter.has_key?(KEY_HIRE_FREE) + + if (val = filter[KEY_GIGS].to_i) != GIG_COUNTS[0] + str = _add_description(str, "Concert gigs = #{GIG_LABELS[val]}") + end if filter.has_key?(KEY_GIGS) + + if 0 < (val = filter[KEY_GENRES]).length + gstr = "Genres = " + genres = Genre.where(["id IN (?)", val]).order('description').pluck(:description) + gstr += genres.join(', ') + str = _add_description(str, gstr) + end if filter.has_key?(KEY_GENRES) + + if 0 < ((val = filter[KEY_INSTRUMENTS]) || '').length + istr = "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'] + istr += "#{ii.description} (#{INSTRUMENT_PROFICIENCY[proficiency.to_i]})" + istr += ', ' unless idx==(instrs.length-1) + end + str = _add_description(str, istr) + end if filter.has_key?(KEY_INSTRUMENTS) + str = "Current Search: #{str}" + str + end + + end +end diff --git a/ruby/lib/jam_ruby/models/base_search.rb b/ruby/lib/jam_ruby/models/base_search.rb new file mode 100644 index 000000000..f08c46a7d --- /dev/null +++ b/ruby/lib/jam_ruby/models/base_search.rb @@ -0,0 +1,196 @@ +module JamRuby + class BaseSearch < JsonStore + + attr_accessor :page_count, :results, :page_number + + ANY_VAL_STR = 'any' + ANY_VAL_INT = -1 + + VAL_YES = 'yes' + VAL_NO = 'no' + + PER_PAGE = 10 + PG_SMALLINT_MAX = 32767 + + KEY_SKILL = 'skill_level' + KEY_GENRES = 'genres' + KEY_INSTRUMENTS = 'instruments' + KEY_GIGS = 'concert_gigs' + 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' + } + + INSTRUMENT_PROFICIENCY = { + 1 => 'Beginner', + 2 => 'Intermediate', + 3 => 'Expert', + } + + def self.json_schema + { + KEY_SORT_ORDER => self::SORT_VALS[0], + KEY_INSTRUMENTS => [], + KEY_GENRES => [], + KEY_GIGS => self::GIG_COUNTS[0].to_s, + } + end + + def self.search_filter_meta(jschema=nil, sort_order=nil) + jschema ||= self.json_schema + schema_keys = jschema.keys + sort_order ||= { keys: self::SORT_VALS, map: self::SORT_ORDERS } + multi_keys = jschema.collect { |kk,vv| vv.is_a?(Array) ? kk : nil }.compact + { + per_page: self::PER_PAGE, + filter_keys: { + keys: schema_keys, + multi: multi_keys, + single: schema_keys - multi_keys, + }, + sort_order: sort_order + } + 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 self.user_search_filter(user) + unless ss = user.send(self.name.demodulize.tableize.singularize) + ss = self.create_search(user) + end + ss + 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 = self.json_schema + ms.save! + ms + end + + def self.search_target_class + end + + # FIXME: SQL INJECTION + def _genres(rel, query_data=json) + gids = query_data[KEY_GENRES] + unless gids.blank? + gidsql = gids.join("','") + gpsql = "SELECT player_id FROM genre_players WHERE (player_type = '#{self.class.search_target_class.name}' AND genre_id IN ('#{gidsql}'))" + rel = rel.where("#{self.class.search_target_class.table_name}.id IN (#{gpsql})") + end + rel + end + + # FIXME: SQL INJECTION + def _instruments(rel, query_data=json) + unless (instruments = query_data[KEY_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("#{self.class.search_target_class.table_name}.id IN (#{instsql})") + end + 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 _sort_order(rel) + end + + def do_search(params={}) + end + + def process_results_page(objs) + end + + def search_includes(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 => self.class::PER_PAGE) + + rel = self.search_includes(rel) + @page_count = rel.total_pages + + process_results_page(rel.all) + end + + def reset_filter + self.data_blob = self.class.json_schema + self.save + end + + def reset_search_results + reset_filter + search_results_page + end + + def search_type + self.class.to_s + end + + def is_blank? + self.data_blob == self.class.json_schema + end + + def description + end + + end +end diff --git a/ruby/lib/jam_ruby/models/musician_search.rb b/ruby/lib/jam_ruby/models/musician_search.rb index 9b780c53f..f11e36e20 100644 --- a/ruby/lib/jam_ruby/models/musician_search.rb +++ b/ruby/lib/jam_ruby/models/musician_search.rb @@ -1,22 +1,12 @@ module JamRuby - class MusicianSearch < JsonStore + class MusicianSearch < BaseSearch - attr_accessor :page_count, :results, :user_counters, :page_number + cattr_accessor :jschema, :search_meta + attr_accessor :user_counters - 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 = { @@ -24,22 +14,6 @@ module JamRuby 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', @@ -74,81 +48,28 @@ module JamRuby 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 + def self.json_schema + return @@jschema if @@jschema + @@jschema = BaseSearch.json_schema.merge({ + KEY_INTERESTS => INTEREST_VALS[0], + KEY_STUDIOS => STUDIO_COUNTS[0].to_s, + KEY_AGES => [], + KEY_SKILL => self::SKILL_VALS[0].to_s, + }) end - def self.search_filter_json(user) - self.user_search_filter(user).json + def self.search_filter_meta + return @@search_meta if @@search_meta + @@search_meta = super.merge({ + interests: { keys: INTEREST_VALS, map: INTERESTS }, + ages: { keys: AGE_COUNTS, map: AGES } + }) end - def self.create_search(user) - ms = self.new - ms.user = user - ms.data_blob = JSON_SCHEMA - ms.save! - ms + def self.search_target_class + User end - # XXX SQL INJECTION - 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 - - # XXX SQL INJECTION - def _instruments(rel) - unless (instruments = json['instruments']).blank? - instsql = "SELECT player_id FROM musicians_instruments WHERE ((" - instsql += instruments.collect do |inst| - "instrument_id = '#{inst['id']}' AND proficiency_level = #{inst['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 } @@ -193,7 +114,7 @@ module JamRuby def _skills(rel) if 0 < (val = json[KEY_SKILL].to_i) - rel = rel.where(['skill_level = ?', val]) + rel = rel.where(skill_level: val) end rel end @@ -207,8 +128,8 @@ module JamRuby end def _sort_order(rel) - val = json[KEY_SORT_ORDER] - if SORT_VALS[1] == val + val = json[self.class::KEY_SORT_ORDER] + if self.class::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}") @@ -235,45 +156,11 @@ module JamRuby 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 + def search_includes(rel) + rel.includes([:instruments, :followings, :friends]) 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) + def process_results_page(_results) @results = _results @user_counters = {} and return self unless user @@ -351,7 +238,7 @@ module JamRuby end def is_blank? - self.data_blob == JSON_SCHEMA + self.data_blob == self.class.json_schema end def description diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index 217c73339..691f03a30 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -172,6 +172,7 @@ module JamRuby has_many :performance_samples, :class_name => "JamRuby::PerformanceSample", :foreign_key=> 'player_id' has_one :musician_search, :class_name => 'JamRuby::MusicianSearch' + has_one :band_search, :class_name => 'JamRuby::BandSearch' before_save :create_remember_token, :if => :should_validate_password? before_save :stringify_avatar_info , :if => :updating_avatar diff --git a/ruby/spec/jam_ruby/models/band_filter_search_spec.rb b/ruby/spec/jam_ruby/models/band_filter_search_spec.rb index 1805a0873..bc2c0c206 100644 --- a/ruby/spec/jam_ruby/models/band_filter_search_spec.rb +++ b/ruby/spec/jam_ruby/models/band_filter_search_spec.rb @@ -1,6 +1,19 @@ require 'spec_helper' -describe 'Band search' do +describe 'Band Search Model' do + + let!(:searcher) { FactoryGirl.create(:austin_user) } + let!(:search) { BandSearch.user_search_filter(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] } + + let!(:to_join) { search.search_filter_for_subtype(BandSearch::TO_JOIN) } + let!(:to_hire) { search.search_filter_for_subtype(BandSearch::TO_HIRE) } before(:all) do Recording.delete_all @@ -20,213 +33,310 @@ describe 'Band search' do FactoryGirl.create(:band_musician, :band => bb, :user => FactoryGirl.create(:user)) end end + end + + 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.band_search).to eq(search) + end + + it "sets json" do + expect(search.search_filter_for_subtype(BandSearch::TO_JOIN)).to eq(BandSearch.json_schema[BandSearch::TO_JOIN]) + expect(search.search_filter_for_subtype(BandSearch::TO_HIRE)).to eq(BandSearch.json_schema[BandSearch::TO_HIRE]) + end + + it "loads all bands by default" do + search.search_results_page + expect(search.results.count).to eq(Band.count) + end + + it "has follower counts" do + Follow.create(user_id: searcher.id, followable: Band.first) + search.search_results_page + expect(search.follow_count(Band.first)).to eq(1) + end end - context 'default filter settings' do + describe "generates description" do - it "finds all bands" do - # expects all the bands - num = Band.count - results = Search.band_filter({ :per_page => num }) - expect(results.results.count).to eq(num) + def check_description(_filter, subtype, key, value, lookup, label) + _filter[key] = value + search.search_results_page(subtype, _filter) + expect(search.description(subtype)).to match(/Current Search: Sort = .*; #{label} = #{lookup[value]}$/) end - it "finds bands with proper ordering" 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 }) + it "renders no description for blank" do + search.search_results_page + expect(search.description).to eq('') + end + + it "renders description for sort order" do + to_join[BandSearch::KEY_TOUR_OPTION] = BandSearch::VAL_YES + search.search_results_page(BandSearch::TO_JOIN, to_join) + search.search_results_page + expect(search.description).to match(/Sort =/) + end + + context "to_join" do + + it "renders description for band type" do + check_description(to_join, + BandSearch::TO_JOIN, + BandSearch::KEY_BAND_TYPE, + BandSearch::BAND_TYPE_VAL_STRS[1], + BandSearch::BAND_TYPES, + 'Band type') + end + + it "renders description for band status" do + check_description(to_join, + BandSearch::TO_JOIN, + BandSearch::KEY_BAND_STATUS, + BandSearch::SKILL_VALS[1], + BandSearch::BAND_STATUS, + 'Band status') + end + + it "renders description for concert gigs" do + check_description(to_join, + BandSearch::TO_JOIN, + BandSearch::KEY_GIGS, + BandSearch::GIG_COUNTS[1], + BandSearch::GIG_LABELS, + 'Concert gigs') + end + + it "renders description for play commitment" do + check_description(to_join, + BandSearch::TO_JOIN, + BandSearch::KEY_PLAY_COMMIT, + BandSearch::PLAY_COMMIT_VALS[1], + BandSearch::PLAY_COMMITS, + 'Play commitment') + end + + it "renders description for tour option" do + check_description(to_join, + BandSearch::TO_JOIN, + BandSearch::KEY_TOUR_OPTION, + BandSearch::VAL_YES, + BandSearch::TOUR_OPTIONS, + 'Touring options') + end + + it "renders description for genres" do + to_join[BandSearch::KEY_GENRES] = [Genre.first.id] + search.search_results_page(BandSearch::TO_JOIN, to_join) + expect(search.description).to match(/Current Search: Sort = .*; Genres = #{Genre.first.description}$/) + end + + it "renders description for instruments" do + to_join[BandSearch::KEY_INSTRUMENTS] = [{ 'instrument_id' => Instrument.first.id, 'proficiency_level' => 1 }] + search.search_results_page(BandSearch::TO_JOIN, to_join) + expect(search.description).to match(/Current Search: Sort = .*; Instruments = #{Instrument.first.description} \(#{BandSearch::INSTRUMENT_PROFICIENCY[1]}\)$/) + end + + end + + context "to_hire" do + it "renders description for genres" do + to_hire[BandSearch::KEY_GENRES] = [Genre.first.id] + search.search_results_page(BandSearch::TO_HIRE, to_hire) + expect(search.description(BandSearch::TO_HIRE)).to match(/Current Search: Sort = .*; Genres = #{Genre.first.description}$/) + end + + it "renders description for band status" do + check_description(to_hire, + BandSearch::TO_HIRE, + BandSearch::KEY_BAND_STATUS, + BandSearch::BAND_STATUS_VALS[1], + BandSearch::BAND_STATUS, + 'Band status') + end + + it "renders description for concert gigs" do + check_description(to_hire, + BandSearch::TO_HIRE, + BandSearch::KEY_GIGS, + BandSearch::GIG_COUNTS[1], + BandSearch::GIG_LABELS, + 'Concert gigs') + end + + it "renders description for performance samples" do + check_description(to_hire, + BandSearch::TO_HIRE, + BandSearch::KEY_PERF_SAMPLES, + BandSearch::PERF_SAMPLES_VALS[1], + BandSearch::PERF_SAMPLES, + 'Performance samples') + end + + it "renders description max cost" do + to_hire[BandSearch::KEY_HIRE_MAX_COST] = 100 + search.search_results_page(BandSearch::TO_HIRE, to_hire) + expect(search.description(BandSearch::TO_HIRE)).to match(/Current Search: Sort = .*; Maximum gig cost = \$100$/) + end + + it "renders description free gigs" do + to_hire[BandSearch::KEY_HIRE_FREE] = 1 + search.search_results_page(BandSearch::TO_HIRE, to_hire) + expect(search.description(BandSearch::TO_HIRE)).to match(/Current Search: Sort = .*; Bands playing free gigs$/) + end + + end + + end + + describe "filtering by keys" do + + let!(:band) { Band.first } + + context 'all search keys' do + + let!(:filter) { to_join } + + it "filters by gigs" do + band.update_attribute(:concert_count, BandSearch::GIG_COUNTS[2]) + filter[BandSearch::KEY_GIGS] = BandSearch::GIG_COUNTS[2] + search.search_results_page(BandSearch::TO_JOIN, filter) + expect(search.results.count).to eq(1) + expect(search.results[0].id).to eq(band.id) + end + it "filters by genre" do + band_id = band.id + filter[BandSearch::KEY_GENRES] = [band_id] + search.search_results_page(BandSearch::TO_JOIN, filter) + expect(search.results.count).to eq(Band.all.map(&:genres).flatten.select { |bb| bb.id == band_id }.count) + end + + it "filters by band_type" do + band.update_attribute(:band_type, BandSearch::BAND_TYPE_VAL_STRS[1]) + filter[BandSearch::KEY_BAND_TYPE] = BandSearch::BAND_TYPE_VAL_STRS[1] + search.search_results_page(BandSearch::TO_JOIN, filter) + expect(search.results.count).to eq(1) + expect(search.results[0].id).to eq(band.id) + end + + it "filters by instruments" do + minst = FactoryGirl.create(:musician_instrument) + band.musician_instruments << minst + band.save + filter[BandSearch::KEY_INSTRUMENTS] = [{'instrument_id' => minst.instrument_id, + 'proficiency_level' => minst.proficiency_level + }] + search.search_results_page(BandSearch::TO_JOIN, filter) + expect(search.results.count).to eq(1) + expect(search.results[0].id).to eq(band.id) + + filter[BandSearch::KEY_INSTRUMENTS] = [{'instrument_id' => minst.instrument_id, + 'proficiency_level' => minst.proficiency_level + 1 + }] + search.search_results_page(BandSearch::TO_JOIN, filter) + expect(search.results.count).to eq(0) + end + + end + + context 'to_join' do + + let!(:filter) { to_join } + + it "sorts by distance" do + bands = Band.all.reverse + bb = bands.first + bb.lat, bb.lng = austin_geoip[:geoiplocation].latitude, austin_geoip[:geoiplocation].longitude + bb.save! + bb = bands.second + bb.lat, bb.lng = dallas_geoip[:geoiplocation].latitude, dallas_geoip[:geoiplocation].longitude + bb.save! + bb = bands.third + bb.lat, bb.lng = miami_geoip[:geoiplocation].latitude, miami_geoip[:geoiplocation].longitude + bb.save! + bb = bands.fourth + bb.lat, bb.lng = seattle_geoip[:geoiplocation].latitude, seattle_geoip[:geoiplocation].longitude + bb.save! + + filter[BandSearch::KEY_SORT_ORDER] = BandSearch::SORT_VALS[0] + search.search_results_page(BandSearch::TO_JOIN, filter) + expect(search.results.count).to eq(Band.count) + expect(search.results.first.id).to eq(bands.first.id) + expect(search.results.second.id).to eq(bands.second.id) + expect(search.results.third.id).to eq(bands.third.id) + expect(search.results.fourth.id).to eq(bands.fourth.id) + end + + it "filters by play commitment" do + band.update_attribute(BandSearch::KEY_PLAY_COMMIT, BandSearch::PLAY_COMMIT_VALS[1].to_i) + filter[BandSearch::KEY_PLAY_COMMIT] = BandSearch::PLAY_COMMIT_VALS[1] + search.search_results_page(BandSearch::TO_JOIN, filter) + expect(search.results.count).to eq(1) + expect(search.results[0].id).to eq(band.id) + end + + it "filters by tour option" do + band.update_attribute(BandSearch::KEY_TOUR_OPTION, true) + filter[BandSearch::KEY_TOUR_OPTION] = BandSearch::VAL_YES + search.search_results_page(BandSearch::TO_JOIN, filter) + expect(search.results.count).to eq(1) + expect(search.results[0].id).to eq(band.id) + end + + end + + + context 'to_hire' do - rbands = @bands.reverse - results.results.each_with_index do |uu, idx| - expect(uu.id).to eq(@bands.reverse[idx].id) - end - end + let!(:filter) { to_hire } - it "sorts bands by followers" do - users = [] - 4.downto(1) { |nn| users << FactoryGirl.create(:user) } - - users.each_with_index do |u, index| - if index != 0 - f1 = Follow.new - f1.user = u - f1.followable = @band4 - f1.save - end + it "filters by free gigs" do + band.update_attribute(BandSearch::KEY_HIRE_FREE, true) + filter[BandSearch::KEY_HIRE_FREE] = BandSearch::VAL_YES + search.search_results_page(BandSearch::TO_HIRE, filter) + expect(search.results.count).to eq(1) + expect(search.results[0].id).to eq(band.id) end - users.each_with_index do |u, index| - if index != 0 - f1 = Follow.new - f1.user = u - f1.followable = @band3 - f1.save - end + it "filters by max cost" do + band.update_attribute(:gig_minimum, 10) + filter[BandSearch::KEY_HIRE_MAX_COST] = 5 + search.search_results_page(BandSearch::TO_HIRE, filter) + expect(search.results.count).to eq(0) + + filter[BandSearch::KEY_HIRE_MAX_COST] = 15 + search.search_results_page(BandSearch::TO_HIRE, filter) + expect(search.results.count).to eq(1) end - f1 = Follow.new - f1.user = users.first - f1.followable = @band2 - f1.save + it "filters by perform samples" do + filter[BandSearch::KEY_PERF_SAMPLES] = BandSearch::VAL_YES + search.search_results_page(BandSearch::TO_HIRE, filter) + expect(search.results.count).to eq(0) - # establish sorting order - # @band4.followers.concat(users[1..-1]) - # @band3.followers.concat(users[1..3]) - # @band2.followers.concat(users[0]) - @bands.map(&:reload) + filter[BandSearch::KEY_PERF_SAMPLES] = BandSearch::VAL_NO + search.search_results_page(BandSearch::TO_HIRE, filter) + expect(search.results.count).to eq(Band.count) - expect(@band4.followers.count).to be 3 - expect(Follow.count).to be 7 - - # refresh the order to ensure it works right - users.each_with_index do |u, index| - if index != 0 - f1 = Follow.new - f1.user = u - f1.followable = @band2 - f1.save - end + ps = PerformanceSample.new + ps.player = band + ps.service_type = 'youtube' + ps.service_id = 'abc123' + ps.save + # PerformanceSample.create(player: band, service_type: 'youtube', service_id: 'abc123') + filter[BandSearch::KEY_PERF_SAMPLES] = BandSearch::VAL_YES + search.search_results_page(BandSearch::TO_HIRE, filter) + expect(search.results.count).to eq(1) + expect(search.results[0].id).to eq(band.id) end - # @band2.followers.concat(users[1..-1]) - results = Search.band_filter({ :per_page => @bands.size }, users[0]) - expect(results.results[0].id).to eq(@band2.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?(@band2)).to be true - end - - it 'paginates properly' do - # make sure pagination works right - params = { :per_page => 2, :page => 1 } - results = Search.band_filter(params) - expect(results.results.count).to be 2 - end - - end - - def make_session(band) - usr = band.users[0] - session = FactoryGirl.create(:active_music_session, :creator => usr, :description => "Session", :band => band) - FactoryGirl.create(:connection, :user => usr, :music_session => session) - user = FactoryGirl.create(:user) - session - end - - context 'band stat counters' do - - it "follow stat shows follower count" do - users = [] - 2.downto(1) { |nn| users << FactoryGirl.create(:user) } - - users.each do |u| - f1 = Follow.new - f1.user = u - f1.followable = @band1 - f1.save - end - - # establish sorting order - # @band1.followers.concat(users) - results = Search.band_filter({},@band1) - uu = results.results.detect { |mm| mm.id == @band1.id } - expect(uu).to_not be_nil - expect(results.follow_count(uu)).to eq(users.count) - end - - it "session stat shows session count" do - make_session(@band1) - @band1.reload - results = Search.band_filter({},@band1) - uu = results.results.detect { |mm| mm.id == @band1.id } - expect(uu).to_not be_nil - expect(results.session_count(uu)).to be 1 - end - - end - - context 'band sorting' do - - it "by plays" do - make_session(@band2) - make_session(@band2) - make_session(@band2) - make_session(@band1) - # order results by num recordings - results = Search.band_filter({ :orderby => 'plays' }) - expect(results.results[0].id).to eq(@band2.id) - expect(results.results[1].id).to eq(@band1.id) - end - - it "by now playing" do - # should get 1 result with 1 active session - session = make_session(@band3) - #FactoryGirl.create(:active_music_session, :music_session => session) - - results = Search.band_filter({ :orderby => 'playing' }) - expect(results.results.count).to be 1 - expect(results.results.first.id).to eq(@band3.id) - - # should get 2 results with 2 active sessions - # sort order should be created_at DESC - session = make_session(@band4) - #FactoryGirl.create(:active_music_session, :music_session => session) - results = Search.band_filter({ :orderby => 'playing' }) - expect(results.results.count).to be 2 - expect(results.results[0].id).to eq(@band4.id) - expect(results.results[1].id).to eq(@band3.id) - end - - end - - - context 'filter settings' do - it "searches bands for a genre" do - genre = FactoryGirl.create(:genre) - @band1.genres << genre - @band1.reload - ggg = @band1.genres.detect { |gg| gg.id == genre.id } - expect(ggg).to_not be_nil - results = Search.band_filter({ :genre => ggg.id }) - results.results.each do |rr| - expect(rr.genres.detect { |gg| gg.id==ggg.id }.id).to eq(genre.id) - end - expect(results.results.count).to be 1 - end - - it "finds bands within a given distance of given location" do - pending 'distance search changes' - num = Band.count - expect(@band1.lat).to_not be_nil - # short distance - results = Search.band_filter({ :per_page => num, - :distance => 10, - :city => 'Apex' }, @band1) - expect(results.results.count).to be num - # long distance - results = Search.band_filter({ :per_page => num, - :distance => 1000, - :city => 'Miami', - :state => 'FL' }, @band1) - expect(results.results.count).to be num - end - - it "finds bands within a given distance of bands location" do - pending 'distance search changes' - expect(@band1.lat).to_not be_nil - # uses the location of @band1 - results = Search.band_filter({ :distance => 10, :per_page => Band.count }, @band1) - expect(results.results.count).to be Band.count - end - - it "finds no bands within a given distance of location" do - pending 'distance search changes' - expect(@band1.lat).to_not be_nil - results = Search.band_filter({ :distance => 10, :city => 'San Francisco' }, @band1) - expect(results.results.count).to be 0 end end diff --git a/web/Gemfile b/web/Gemfile index 35c68d23c..924910f1b 100644 --- a/web/Gemfile +++ b/web/Gemfile @@ -115,7 +115,8 @@ group :development, :test do gem 'test-unit' # gem 'teaspoon' # gem 'teaspoon-jasmine' - # gem 'puma' + gem 'puma' + gem 'byebug' end group :unix do gem 'therubyracer' #, '0.11.0beta8' diff --git a/web/app/assets/javascripts/findBand.js b/web/app/assets/javascripts/findBand.js deleted file mode 100644 index e7c30f315..000000000 --- a/web/app/assets/javascripts/findBand.js +++ /dev/null @@ -1,257 +0,0 @@ -(function(context,$) { - "use strict"; - - context.JK = context.JK || {}; - context.JK.FindBandScreen = function(app) { - - var logger = context.JK.logger; - var bands = {}; - var bandList; - var instrument_logo_map = context.JK.getInstrumentIconMap24(); - var did_show_band_page = false; - var page_num=1, page_count=0; - var helpBubble = context.JK.HelpBubbleHelper; - var $screen = $('#bands-screen'); - var $results = $screen.find('#band-filter-results'); - - function loadBands(queryString) { - // squelch nulls and undefines - queryString = !!queryString ? queryString : ""; - - $.ajax({ - type: "GET", - url: "/api/search.json?" + queryString, - success: afterLoadBands, - error: app.ajaxError - }); - } - - function search() { - did_show_band_page = true; - var queryString = 'srch_b=1&page='+page_num+'&'; - - // order by - var orderby = $('#band_order_by').val(); - if (typeof orderby != 'undefined' && orderby.length > 0) { - queryString += "orderby=" + orderby + '&'; - } - // genre filter - var genre = $('#band_genre').val(); - if (typeof genre != 'undefined' && !(genre === '')) { - queryString += "genre=" + genre + '&'; - } - // distance filter - var query_param = $('#band_query_distance').val(); - if (query_param !== null && query_param.length > 0) { - var matches = query_param.match(/(\d+)/); - if (0 < matches.length) { - var distance = matches[0]; - queryString += "distance=" + distance + '&'; - } - } - loadBands(queryString); - } - - function refreshDisplay() { - clearResults(); - search(); - } - - function afterLoadBands(mList) { - // display the 'no bands' banner if appropriate - var $noBandsFound = $('#bands-none-found'); - bandList = mList; - - if(bandList.length == 0) { - $noBandsFound.show(); - bands = []; - } - else { - $noBandsFound.hide(); - bands = bandList['bands']; - if (!(typeof bands === 'undefined')) { - $('#band-filter-city').text(bandList['city']); - if (0 == page_count) { - page_count = bandList['page_count']; - } - renderBands(); - } - } - } - - function renderBands() { - var ii, len; - var mTemplate = $('#template-find-band-row').html(); - var pTemplate = $('#template-band-player-info').html(); - var aTemplate = $('#template-band-action-btns').html(); - var eTemplate = $('#template-band-edit-btns').html(); - var bVals, bb, renderings=''; - var instr_logos, instr; - var players, playerVals, aPlayer, isMember; - - for (ii=0, len=bands.length; ii < len; ii++) { - bb = bands[ii]; - instr_logos = ''; - players = ''; - playerVals = {}; - isMember = false; - for (var jj=0, ilen=bb['players'].length; jj'; - } - if (!isMember) { - isMember = aPlayer.user_id == context.JK.currentUserId; - } - playerVals = { - user_id: aPlayer.user_id, - player_name: aPlayer.name, - profile_url: '/client#/profile/' + aPlayer.user_id, - avatar_url: context.JK.resolveAvatarUrl(aPlayer.photo_url), - player_instruments: player_instrs - }; - - players += context.JK.fillTemplate(pTemplate, playerVals); - } - - var actionVals, band_actions; - if (isMember) { - actionVals = { - profile_url: "/client#/bandProfile/" + bb.id, - band_edit_url: "/client#/band/setup/" + bb.id + '/step1', - band_member_url: "/client#/band/setup/" + bb.id + '/step2' - }; - band_actions = context.JK.fillTemplate(eTemplate, actionVals); - } else { - actionVals = { - profile_url: "/client#/bandProfile/" + bb.id, - button_follow: bb['is_following'] ? '' : 'button-orange', - button_message: 'button-orange' - }; - band_actions = context.JK.fillTemplate(aTemplate, actionVals); - } - - var bgenres = ''; - for (jj=0, ilen=bb['genres'].length; jj'; - } - - bgenres += '
'; - - bVals = { - avatar_url: context.JK.resolveBandAvatarUrl(bb.photo_url), - profile_url: "/client#/bandProfile/" + bb.id, - band_name: bb.name, - band_location: bb.city + ', ' + bb.state, - genres: bgenres, - instruments: instr_logos, - biography: bb['biography'], - follow_count: bb['follow_count'], - recording_count: bb['recording_count'], - session_count: bb['session_count'], - band_id: bb['id'], - band_player_template: players, - band_action_template: band_actions - }; - - var $rendering = $(context.JK.fillTemplate(mTemplate, bVals)) - - var $offsetParent = $results.closest('.content'); - var data = {entity_type: 'band'}; - - var options = {positions: ['top', 'bottom', 'right', 'left'], offsetParent: $offsetParent}; - context.JK.helpBubble($('.follower-count', $rendering), 'follower-count', data, options); - context.JK.helpBubble($('.recording-count', $rendering), 'recording-count', data, options); - context.JK.helpBubble($('.session-count', $rendering), 'session-count', data, options); - - $results.append($rendering); - } - - $('.search-m-follow').on('click', followBand); - context.JK.bindHoverEvents(); - } - - function beforeShow(data) { - } - - function afterShow(data) { - if (!did_show_band_page) { - refreshDisplay(); - } - } - - function clearResults() { - bands = {}; - $('#band-filter-results').empty(); - page_num = 1; - page_count = 0; - } - - function followBand(evt) { - // if the band 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(function(ee) {ee.preventDefault();}); - - evt.stopPropagation(); - var newFollowing = {}; - newFollowing.band_id = $(this).parent().data('band-id'); - var url = "/api/users/" + context.JK.currentUserId + "/followings"; - $.ajax({ - type: "POST", - dataType: "json", - contentType: 'application/json', - url: url, - data: JSON.stringify(newFollowing), - processData: false, - success: function(response) { - // remove the orange look to indicate it's not selectable - // @FIXME -- this will need to be tweaked when we allow unfollowing - $('div[data-band-id='+newFollowing.band_id+'] .search-m-follow').removeClass('button-orange').addClass('button-grey'); - }, - error: app.ajaxError(arguments) - }); - } - - function events() { - $('#band_query_distance').change(refreshDisplay); - $('#band_genre').change(refreshDisplay); - $('#band_order_by').change(refreshDisplay); - - $('#band-filter-results').closest('.content-body-scroller').bind('scroll', function() { - if ($(this).scrollTop() + $(this).innerHeight() >= $(this)[0].scrollHeight) { - if (page_num < page_count) { - page_num += 1; - search(); - } - } - }); - } - - function initialize() { - var screenBindings = { - 'beforeShow': beforeShow, - 'afterShow': afterShow - }; - app.bindScreen('bands', screenBindings); - - events(); - } - - this.initialize = initialize; - this.renderBands = renderBands; - this.afterShow = afterShow; - this.clearResults = clearResults; - - return this; - } -})(window,jQuery); \ No newline at end of file diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js index 0f1445290..1a6c2c59a 100644 --- a/web/app/assets/javascripts/jam_rest.js +++ b/web/app/assets/javascripts/jam_rest.js @@ -1746,6 +1746,19 @@ }); } + function getBandSearchFilter(query) { + var qarg = query === undefined ? '' : query; + return $.get("/api/search/bands.json?"+qarg); + } + + function postBandSearchFilter(query) { + return $.ajax({ + type: "POST", + url: "/api/search/bands.json", + data: query + }); + } + function getMount(options) { var id = getId(options); return $.ajax({ @@ -1991,6 +2004,8 @@ this.addRecordingTimeline = addRecordingTimeline; this.getMusicianSearchFilter = getMusicianSearchFilter; this.postMusicianSearchFilter = postMusicianSearchFilter; + this.getBandSearchFilter = getBandSearchFilter; + this.postBandSearchFilter = postBandSearchFilter; this.playJamTrack = playJamTrack; this.createSignupHint = createSignupHint; this.createAlert = createAlert; diff --git a/web/app/assets/javascripts/member_search_filter.js.coffee b/web/app/assets/javascripts/member_search_filter.js.coffee new file mode 100644 index 000000000..357806ebc --- /dev/null +++ b/web/app/assets/javascripts/member_search_filter.js.coffee @@ -0,0 +1,858 @@ +$ = jQuery +context = window +context.JK ||= {}; + +context.JK.BaseSearchFilter = class BaseSearchFilter + + 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() + @searchType = '' + @searchTypeS = '' + @restGet = null + @restPost = null + @searchMeta = null + + init: (app) => + @app = app + @screenBindings = { 'afterShow': this.afterShow, 'afterHide': this.afterHide } + @app.bindScreen(@searchTypeS, @screenBindings) + + @screen = $('#'+@searchTypeS+'-screen') + @resultsListContainer = @screen.find('#'+@searchType+'-search-filter-results-list') + @spinner = @screen.find('.paginate-wait') + + this.registerResultsPagination() + + afterShow: () => + @screen.find('#'+@searchType+'-search-filter-results').show() + @screen.find('#'+@searchType+'-search-filter-builder').hide() + this.getUserFilterResults() + + showBuilder: () => + @screen.find('#'+@searchType+'-search-filter-results').hide() + @screen.find('#'+@searchType+'-search-filter-builder').show() + @resultsListContainer.empty() + + afterHide: () => + @resultsListContainer.empty() + + searchMetaData: () => + @searchMeta + + filterData: () => + @searchFilter.data_blob + + renderSearchFilter: () => + $.when(@restGet()).done (sFilter) => + this.loadSearchFilter(sFilter) + + loadSearchFilter: (sFilter) => + + _populateSelectWithKeys: (struct, selection, keys, element) => + element.children().remove() + $.each keys, (idx, value) => + label = struct[value] + blankOption = $ '' + blankOption.text label + blankOption.attr 'value', value + blankOption.attr 'selected', '' if value == selection + element.append(blankOption) + context.JK.dropdown(element) + + _populateSelectIdentifier: (identifier) => + elem = $ '#'+@searchType+'-search-filter-builder select[name='+identifier+']' + struct = this.searchMetaData()[identifier]['map'] + keys = this.searchMetaData()[identifier]['keys'] + this._populateSelectWithKeys(struct, this.filterData()[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') + + _populateGigs: () => + elem = $ '#'+@searchType+'-search-filter-builder select[name=concert_gigs]' + this._populateSelectWithInt(@profileUtils.gigMap, this.filterData().concert_gigs.toString(), elem) + + _populateGenres: () => + @screen.find('#search-filter-genres').empty() + @rest.getGenres().done (genres) => + genreTemplate = @screen.find('#template-search-filter-setup-genres').html() + filterGenres = this.filterData().genres + $.each genres, (index, genre) => + if 0 < filterGenres.length + genreMatch = $.grep(filterGenres, (n, i) -> + n == genre.id) + else + genreMatch = [] + if genreMatch.length > 0 then selected = 'checked' else selected = '' + 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 < this.filterData().instruments.length + instMatch = $.grep(this.filterData().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 = $ '#'+@searchType+'-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) -> + if $(this).prop('checked') + vals.push $(this).val() + vals + + willSearch: (reload) => + return false if @isSearching + @isSearching = true + if reload + @pageNumber = 1 + @screen.find('#'+@searchType+'-search-filter-spinner').show() + @resultsListContainer.empty() + @screen.find('#'+@searchType+'-search-filter-builder').hide() + @screen.find('#'+@searchType+'-search-filter-results').show() + true + + didSearch: (response) => + this.loadSearchFilter(response.filter_json) + @searchResults = response + @screen.find('#'+@searchType+'-search-filter-spinner').hide() + this.renderResultsPage() + @screen.find('.paginate-wait').hide() + @isSearching = false + + resetFilter: () => + if this.willSearch(true) + @restPost({ filter: 'reset' }).done(this.didSearch) + + cancelFilter: () => + this.resetFilter() + + getUserFilterResults: () => + if this.willSearch(true) + this.doRestGet() + + doRestGet: (query) => + query2 = 'results=true&' + unless (typeof query == "undefined") + query2 += query + @restGet(query2).done(this.didSearch) + + performSearch: () => + if this.willSearch(true) + $.each this.searchMetaData().filter_keys.single, (index, key) => + this.filterData()[key] = this._builderSelectValue(key) + $.each this.searchMetaData().filter_keys.multi, (index, key) => + this.filterData()[key] = this._builderSelectMultiValue(key) + @restPost({ filter: JSON.stringify(this.filterData()), page: @pageNumber }).done(this.didSearch) + + renderResultsHeader: () => + + renderResultsPage: () => + + _formatLocation: (band) -> + if band.city and band.state + band.city + ', ' + band.state + else if band.city + band.city + else if band.regionname + band.regionname + else + 'Location Unavailable' + + friendRequestCallback: (user_id)=> + # TODO: + + paginate: () => + if @pageNumber < @searchResults.page_count && this.willSearch(false) + @screen.find('.paginate-wait').show() + @pageNumber += 1 + @restPost({ filter: JSON.stringify(this.filterData()), page: @pageNumber }).done(this.didSearch) + return true + false + + registerResultsPagination: () => + _resultsListContainer = @resultsListContainer + _headerHeight = @screen.find('#'+@searchType+'-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() + + +context.JK.MusicianSearchFilter = class MusicianSearchFilter extends BaseSearchFilter + + constructor: () -> + super() + @searchType = 'musician' + @searchTypeS = @searchType+'s' + @restGet = @rest.getMusicianSearchFilter + @restPost = @rest.postMusicianSearchFilter + @searchMeta = gon.musician_search_meta + + init: (app) => + super(app) + @screen.find('#btn-'+@searchType+'-search-builder').on 'click', => + this.showBuilder() + @screen.find('#btn-'+@searchType+'-search-reset').on 'click', => + this.resetFilter() + + renderSearchFilter: () => + super() + + loadSearchFilter: (sFilter) => + super(sFilter) + + @searchFilter = JSON.parse(sFilter) + args = + interests: this.filterData().interests + skill_level: this.filterData().skill_level + studio_sessions: this.filterData().studio_sessions + concert_gigs: this.filterData().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() + + _populateSortOrder: () => + this._populateSelectIdentifier('sort_order') + + _populateInterests: () => + this._populateSelectIdentifier('interests') + + _populateStudio: () => + elem = $ '#musician-search-filter-builder select[name=studio_sessions]' + this._populateSelectWithInt(@profileUtils.studioMap, this.filterData().studio_sessions.toString(), elem) + + _populateAges: () => + @screen.find('#search-filter-ages').empty() + ages_map = this.searchMetaData()['ages']['map'] + $.each this.searchMetaData()['ages']['keys'], (index, key) => + ageTemplate = @screen.find('#template-search-filter-setup-ages').html() + selected = '' + ageLabel = ages_map[key] + if 0 < this.filterData().ages.length + key_val = key.toString() + ageMatch = $.grep(this.filterData().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: () => + super() + + _populateSkill: () => + elem = $ '#'+@searchType+'-search-filter-builder select[name=skill_level]' + this._populateSelectWithInt(@profileUtils.skillLevelMap, this.filterData().skill_level.toString(), elem) + + _populateInstruments: () => + super() + + willSearch: (reload) => + super(reload) + + didSearch: (response) => + super(response) + + resetFilter: () => + super() + + cancelFilter: () => + super() + + getUserFilterResults: () => + super() + + performSearch: () => + super() + + renderResultsHeader: () => + @screen.find('#'+@searchType+'-search-filter-description').html(@searchResults.description) + if @searchResults.is_blank_filter + @screen.find('#btn-'+@searchType+'-search-reset').hide() + else + @screen.find('#btn-'+@searchType+'-search-reset').show() + + renderResultsPage: () => + super() + this.renderResultsHeader() if @pageNumber == 1 + musicians = @searchResults.musicians + len = musicians.length + if 0 == len + @screen.find('#musician-search-filter-results-list-blank').show() + @screen.find('#musician-search-filter-results-list-blank').html('No results found') + return + else + @screen.find('#musician-search-filter-results-list-blank').hide() + + ii = 0 + mTemplate = @screen.find('#template-search-musician-row').html() + aTemplate = @screen.find('#template-search-musician-action-btns').html() + mVals = undefined + musician = undefined + renderings = '' + instr_logos = undefined + follows = undefined + followVals = undefined + aFollow = undefined + myAudioLatency = @searchResults.my_audio_latency + while ii < len + musician = musicians[ii] + if context.JK.currentUserId == musician.id + ii++ + continue + instr_logos = '' + jj = 0 + ilen = musician['instruments'].length + while jj < ilen + instr_id = musician['instruments'][jj].instrument_id + if instr_img = @instrument_logo_map[instr_id] + instr_logos += '' + jj++ + actionVals = + profile_url: '/client#/profile/' + musician.id + friend_class: 'button-' + (if musician['is_friend'] then 'grey' else 'orange') + friend_caption: (if musician.is_friend then 'DIS' else '') + 'CONNECT' + follow_class: 'button-' + (if musician['is_following'] then 'grey' else 'orange') + follow_caption: (if musician.is_following then 'UN' else '') + 'FOLLOW' + message_class: 'button-orange' + message_caption: 'MESSAGE' + button_message: 'button-orange' + musician_actions = context.JK.fillTemplate(aTemplate, actionVals) + latencyBadge = context._.template($("#template-account-session-latency").html(), $.extend(sessionUtils.createLatency(musician), musician), variable: 'data') + mVals = + avatar_url: context.JK.resolveAvatarUrl(musician.photo_url) + profile_url: '/client#/profile/' + musician.id + musician_name: musician.name + musician_location: this._formatLocation(musician) + instruments: instr_logos + biography: musician['biography'] + follow_count: musician['follow_count'] + friend_count: musician['friend_count'] + recording_count: musician['recording_count'] + session_count: musician['session_count'] + musician_id: musician['id'] + musician_action_template: musician_actions + latency_badge: latencyBadge + musician_first_name: musician['first_name'] + $rendering = $(context.JK.fillTemplate(mTemplate, mVals)) + $offsetParent = @resultsListContainer.closest('.content') + data = entity_type: 'musician' + options = + positions: [ + 'top' + 'bottom' + 'right' + 'left' + ] + offsetParent: $offsetParent + scoreOptions = offsetParent: $offsetParent + context.JK.helpBubble($('.follower-count', $rendering), 'follower-count', data, options); + context.JK.helpBubble($('.friend-count', $rendering), 'friend-count', data, options); + context.JK.helpBubble($('.recording-count', $rendering), 'recording-count', data, options); + context.JK.helpBubble($('.session-count', $rendering), 'session-count', data, options); + @helpBubble.scoreBreakdown $('.latency', $rendering), false, musician['full_score'], myAudioLatency, musician['audio_latency'], musician['score'], scoreOptions + @resultsListContainer.append $rendering + $rendering.find('.biography').dotdotdot() + ii++ + + this._bindMessageMusician() + this._bindFriendMusician() + this._bindFollowMusician() + + context.JK.bindHoverEvents() + return + + _bindMessageMusician: () => + objThis = this + @screen.find('.search-m-message').on 'click', (evt) -> + userId = $(this).parent().data('musician-id') + objThis.app.layout.showDialog 'text-message', d1: userId + + + _bindFriendMusician: () => + objThis = this + @screen.find('.search-m-friend').on 'click', (evt) -> + # if the musician is already a friend, remove the button-orange class, and prevent the link from working + if 0 == $(this).closest('.button-orange').size() + return false + $(this).click (ee) -> + ee.preventDefault() + return + evt.stopPropagation() + uid = $(this).parent().data('musician-id') + objThis.rest.sendFriendRequest objThis.app, uid, this.friendRequestCallback + + _bindFollowMusician: () => + objThis = this + @screen.find('.search-m-follow').on 'click', (evt) -> + # if the musician is already followed, remove the button-orange class, and prevent the link from working + if 0 == $(this).closest('.button-orange').size() + return false + $(this).click (ee) -> + ee.preventDefault() + return + evt.stopPropagation() + newFollowing = {} + newFollowing.user_id = $(this).parent().data('musician-id') + url = '/api/users/' + context.JK.currentUserId + '/followings' + $.ajax + type: 'POST' + dataType: 'json' + contentType: 'application/json' + url: url + data: JSON.stringify(newFollowing) + processData: false + success: (response) -> + # remove the orange look to indicate it's not selectable + # @FIXME -- this will need to be tweaked when we allow unfollowing + objThis.screen.find('div[data-musician-id=' + newFollowing.user_id + '] .search-m-follow').removeClass('button-orange').addClass 'button-grey' + return + error: objThis.app.ajaxError + + _formatLocation: (musician) -> + if musician.city and musician.state + musician.city + ', ' + musician.state + else if musician.city + musician.city + else if musician.regionname + musician.regionname + else + 'Location Unavailable' + + friendRequestCallback: (user_id)=> + # TODO: + + paginate: () => + super() + + registerResultsPagination: () => + super() + + +context.JK.BandSearchFilter = class BandSearchFilter extends BaseSearchFilter + + constructor: () -> + super() + @searchType = 'band' + @searchTypeS = @searchType+'s' + @restGet = @rest.getBandSearchFilter + @restPost = @rest.postBandSearchFilter + @searchMeta = gon.band_search_meta + @searchSubType = 'to_join' + + init: (app) => + super(app) + + @screen.find('#btn-'+@searchType+'-search-builder-to_join').on 'click', => + this.showBuilderToJoin() + @screen.find('#btn-'+@searchType+'-search-builder-to_hire').on 'click', => + this.showBuilderToHire() + @screen.find('#btn-'+@searchType+'-search-builder').on 'click', => + this.showBuilderActive() + @screen.find('#btn-'+@searchType+'-search-reset').on 'click', => + this.resetFilter() + + @resultsList = @screen.find('#band-search-filter-results-list') + @screen.find('#band-search-filter-results-filtered').hide() + + isToHire: () => + 'to_hire' == @searchSubType + + showBuilderActive: () => + if this.isToHire() + this.showBuilderToHire() + else + this.showBuilderToJoin() + + showBuilderToJoin: () => + @screen.find('.band-search-filter-builder-top-to_join').show() + @screen.find('.band-search-filter-builder-top-to_hire').hide() + @searchSubType = 'to_join' + this.showBuilder() + this._loadSearchFilter() if @searchFilter + # @screen.find('.band-search-filter-builder-top-to_join h2').html('search bands') + + showBuilderToHire: () => + @screen.find('.band-search-filter-builder-top-to_join').hide() + @screen.find('.band-search-filter-builder-top-to_hire').show() + @searchSubType = 'to_hire' + this.showBuilder() + this._loadSearchFilter() if @searchFilter + # @screen.find('.band-search-filter-builder-top-to_hire h2').html('search bands to hire') + + searchMetaData: () => + @searchMeta[@searchSubType] + + renderSearchFilter: () => + super() + + _searchFilterArgsToJoin: () => + args = + touring_option: this.filterData().touring_option + band_status: this.filterData().band_status + play_commitment: this.filterData().play_commitment + band_type: this.filterData().band_type + concert_gigs: this.filterData().concert_gigs + + _searchFilterArgsToHire: () => + if 0 < this.filterData().max_cost + has_max_cost = 'checked' + else + has_max_cost = '' + if 1==this.filterData().free_gigs + has_free_gigs = 'checked' + else + has_free_gigs = '' + + args = + band_status: this.filterData().band_status + concert_gigs: this.filterData().concert_gigs + performance_samples: this.filterData().performance_samples + has_max_cost: has_max_cost + max_cost: this.filterData().max_cost + has_free_gigs: has_free_gigs + + _populateSearchFilterToJoin: () => + this._populateInstruments() + this._populateSkill() + this._populateGigs() + this._populatePlayCommit() + this._populateTourOption() + this._populateBandStatus() + + _populateSearchFilterToHire: () => + this._populateBandStatus() + this._populateGigs() + this._populatePerformSamples() + + loadSearchFilter: (sFilter) => + super(sFilter) + @searchFilter = JSON.parse(sFilter) + this._loadSearchFilter() + + _loadSearchFilter: () => + switch @searchSubType + when 'to_join' then args = this._searchFilterArgsToJoin() + when 'to_hire' then args = this._searchFilterArgsToHire() + + template = context.JK.fillTemplate(@screen.find('#template-band-search-filter-'+@searchSubType).html(), args) + + content_root = @screen.find('#band-search-filter-builder') + content_root.html template + + @screen.find('#btn-perform-band-search').on 'click', => + this.performSearch() + + @screen.find('#btn-band-search-cancel').on 'click', => + this.cancelFilter() + + this._populateGenres() + this._populateSortOrder() if this.isToHire() + + switch @searchSubType + when 'to_join' then this._populateSearchFilterToJoin() + when 'to_hire' then this._populateSearchFilterToHire() + + _populateSkill: () => + this._populateSelectIdentifier('band_type') + + _populateBandStatus: () => + this._populateSelectIdentifier('band_status') + + _populatePlayCommit: () => + this._populateSelectIdentifier('play_commitment') + + _populateTourOption: () => + this._populateSelectIdentifier('touring_option') + + _populateSortOrder: () => + this._populateSelectIdentifier('sort_order') + + _populatePerformSamples: () => + this._populateSelectIdentifier('performance_samples') + + _populateGenres: () => + super() + + _populateInstruments: () => + super() + + willSearch: (reload) => + super(reload) + + didSearch: (response) => + super(response) + + resetFilter: () => + if this.willSearch(true) + @screen.find('#band-search-filter-results-blank').show() + @screen.find('#band-search-filter-results-filtered').hide() + @screen.find('#band-search-filter-description').html('') + @restPost({ filter: 'reset', subtype: @searchSubType }).done(this.didSearch) + + cancelFilter: () => + super() + + doRestGet: (query) => + super('subtype='+@searchSubType) + + performSearch: () => + if this.willSearch(true) + filterPost = this.filterData() + $.each this.searchMetaData().filter_keys.single, (index, key) => + filterPost[key] = this._builderSelectValue(key) + $.each this.searchMetaData().filter_keys.multi, (index, key) => + filterPost[key] = this._builderSelectMultiValue(key) + + if this.isToHire() + filterPost['max_cost'] = parseInt($('#max_cost_amount').val()) + filterPost['free_gigs'] = if $('#free_gigs').prop('checked') then 1 else 0 + + postData = { subtype: @searchSubType, filter: JSON.stringify(filterPost), page: @pageNumber } + @restPost(postData).done(this.didSearch) + + renderResultsHeader: () => + if @searchResults.is_blank_filter + @screen.find('#band-search-filter-results-blank').show() + @screen.find('#band-search-filter-results-filtered').hide() + else + @screen.find('#band-search-filter-results-blank').hide() + @screen.find('#band-search-filter-results-filtered').show() + @screen.find('#band-search-filter-description').html(@searchResults.description) + + renderResultsPage: () => + super() + this.renderResultsHeader() if @pageNumber == 1 + bands = @searchResults.bands + len = bands.length + if 0 == len + @screen.find('#band-search-filter-results-list').hide() + @screen.find('#band-search-filter-results-list-blank').show() + @screen.find('#band-search-filter-results-list-blank').html('No results found') + return + else + @screen.find('#band-search-filter-results-list-blank').hide() + @screen.find('#band-search-filter-results-list').show() + this.renderBands(bands) + + renderBands: (bands) => + toolTip = undefined + ii = undefined + len = undefined + mTemplate = $('#template-find-band-row').html() + pTemplate = $('#template-band-player-info').html() + aTemplate = $('#template-band-action-btns').html() + eTemplate = $('#template-band-edit-btns').html() + bVals = undefined + bb = undefined + renderings = '' + instr_logos = undefined + instr = undefined + players = undefined + playerVals = undefined + aPlayer = undefined + isMember = undefined + ii = 0 + len = bands.length + while ii < len + bb = bands[ii] + instr_logos = '' + players = '' + playerVals = {} + isMember = false + jj = 0 + ilen = bb['players'].length + while jj < ilen + toolTip = '' + aPlayer = bb['players'][jj] + player_instrs = '' + iter_pinstruments = aPlayer['instruments'].split(',') + kk = 0 + klen = iter_pinstruments.length + while kk < klen + pinstr = iter_pinstruments[kk] + toolTip = '' + if pinstr of @instrument_logo_map + instr = @instrument_logo_map[pinstr].asset + toolTip = pinstr + player_instrs += '' + kk++ + if !isMember + isMember = aPlayer.user_id == context.JK.currentUserId + playerVals = + user_id: aPlayer.user_id + player_name: aPlayer.name + profile_url: '/client#/profile/' + aPlayer.user_id + avatar_url: context.JK.resolveAvatarUrl(aPlayer.photo_url) + player_instruments: player_instrs + players += context.JK.fillTemplate(pTemplate, playerVals) + jj++ + actionVals = undefined + band_actions = undefined + if isMember + actionVals = + profile_url: '/client#/bandProfile/' + bb.id + band_edit_url: '/client#/band/setup/' + bb.id + '/step1' + band_member_url: '/client#/band/setup/' + bb.id + '/step2' + band_actions = context.JK.fillTemplate(eTemplate, actionVals) + else + actionVals = + profile_url: '/client#/bandProfile/' + bb.id + button_follow: if bb['is_following'] then '' else 'button-orange' + button_message: 'button-orange' + band_actions = context.JK.fillTemplate(aTemplate, actionVals) + bgenres = '' + jj = 0 + ilen = bb['genres'].length + while jj < ilen + bgenres += bb['genres'][jj]['description'] + '
' + jj++ + bgenres += '
' + bVals = + avatar_url: context.JK.resolveBandAvatarUrl(bb.photo_url) + profile_url: '/client#/bandProfile/' + bb.id + band_name: bb.name + band_location: bb.city + ', ' + bb.state + genres: bgenres + instruments: instr_logos + biography: bb['biography'] + follow_count: bb['follow_count'] + recording_count: bb['recording_count'] + session_count: bb['session_count'] + band_id: bb['id'] + band_player_template: players + band_action_template: band_actions + $rendering = $(context.JK.fillTemplate(mTemplate, bVals)) + $offsetParent = @resultsList.closest('.content') + data = entity_type: 'band' + options = + positions: [ + 'top' + 'bottom' + 'right' + 'left' + ] + offsetParent: $offsetParent + context.JK.helpBubble $('.follower-count', $rendering), 'follower-count', data, options + context.JK.helpBubble $('.recording-count', $rendering), 'recording-count', data, options + context.JK.helpBubble $('.session-count', $rendering), 'session-count', data, options + @resultsList.append $rendering + ii++ + + this._bindFollowBand() + + context.JK.bindHoverEvents() + return + + _bindFollowBand: () => + objThis = this + @screen.find('.search-m-follow').on 'click', (evt) -> + if 0 == $(this).closest('.button-orange').size() + return false + $(this).click (ee) -> + ee.preventDefault() + return + evt.stopPropagation() + newFollowing = {} + newFollowing.band_id = $(this).parent().data('band-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) -> + $('div[data-band-id=' + newFollowing.band_id + '] .search-m-follow').removeClass('button-orange').addClass 'button-grey' + return + error: objThis.app.ajaxError(arguments) + + _formatLocation: (band) -> + + friendRequestCallback: (user_id)=> + # TODO: + + paginate: () => + super() + + registerResultsPagination: () => + super() + + filterData: () => + super()[@searchSubType] + diff --git a/web/app/assets/javascripts/musician_search_filter.js.coffee b/web/app/assets/javascripts/musician_search_filter.js.coffee deleted file mode 100644 index 9586374b8..000000000 --- a/web/app/assets/javascripts/musician_search_filter.js.coffee +++ /dev/null @@ -1,412 +0,0 @@ -$ = 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() - @instrumentSelector = null - - 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') - @instrumentSelector = new context.JK.InstrumentSelector(JK.app) - @instrumentSelector.initialize(false, true) - - this.registerResultsPagination() - - @screen.find('#btn-musician-search-builder').on 'click', => - this.showBuilder() - false - - @screen.find('#btn-musician-search-reset').on 'click', => - this.resetFilter() - false - - 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() - false - - @screen.find('#btn-musician-search-cancel').on 'click', => - this.cancelFilter() - false - - this._populateSkill() - this._populateStudio() - this._populateGigs() - this._populateInterests() - this._populateAges() - this._populateGenres() - this._populateInstruments() - this._populateSortOrder() - - - - _populateSelectWithKeys: (struct, selection, keys, element) => - element.children().remove() - $.each keys, (idx, value) => - label = struct[value] - blankOption = $ '' - blankOption.text label - blankOption.attr 'value', value - element.append(blankOption) - element.val(selection) - 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'] - console.log("@searchFilter", @searchFilter, identifier) - this._populateSelectWithKeys(struct, @searchFilter.data_blob[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() - 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._.template(ageTemplate, - { id: key - description: ageLabel - checked: selected}, - {variable: 'data'}) - @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() - $.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._.template(genreTemplate, - { id: genre.id - description: genre.description - checked: selected }, - { variable: 'data' }) - @screen.find('#search-filter-genres').append genreHtml - - _populateInstruments: () => - - # TODO hydrate user's selection from json_store - @instrumentSelector.render(@screen.find('.session-instrumentlist'), []) - @instrumentSelector.setSelectedInstruments(@searchFilter.data_blob.instruments) - - - _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 - vals = @instrumentSelector.getSelectedInstruments() - 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) - filter = {} - $.each gon.musician_search_meta.filter_keys.single, (index, key) => - filter[key] = this._builderSelectValue(key) - $.each gon.musician_search_meta.filter_keys.multi, (index, key) => - filter[key] = this._builderSelectMultiValue(key) - @rest.postMusicianSearchFilter({ filter: JSON.stringify(filter), page: @pageNumber }).done(this.didSearch) - - renderResultsHeader: () => - if @searchResults.is_blank_filter - @screen.find('#btn-musician-search-reset').hide() - @screen.find('.musician-search-text').text('Click search button to look for musicians with similar interests, skill levels, etc.') - else - @screen.find('#btn-musician-search-reset').show() - @screen.find('.musician-search-text').text(@searchResults.summary) - - renderMusicians: () => - this.renderResultsHeader() if @pageNumber == 1 - musicians = @searchResults.musicians - len = musicians.length - if 0 == len - @screen.find('#musician-search-filter-results-list-blank').show() - @screen.find('#musician-search-filter-results-list-blank').html('No results found') - return - else - @screen.find('#musician-search-filter-results-list-blank').hide() - - ii = 0 - mTemplate = @screen.find('#template-search-musician-row').html() - aTemplate = @screen.find('#template-search-musician-action-btns').html() - mVals = undefined - musician = undefined - renderings = '' - instr_logos = undefined - follows = undefined - followVals = undefined - aFollow = undefined - myAudioLatency = @searchResults.my_audio_latency - while ii < len - musician = musicians[ii] - if context.JK.currentUserId == musician.id - ii++ - continue - instr_logos = '' - jj = 0 - ilen = musician['instruments'].length - while jj < ilen - instr_id = musician['instruments'][jj].instrument_id - if instr_img = @instrument_logo_map[instr_id] - instr_logos += '' - jj++ - actionVals = - profile_url: '/client#/profile/' + musician.id - friend_class: 'button-' + (if musician['is_friend'] then 'grey' else 'orange') - friend_caption: (if musician.is_friend then 'DIS' else '') + 'CONNECT' - follow_class: 'button-' + (if musician['is_following'] then 'grey' else 'orange') - follow_caption: (if musician.is_following then 'UN' else '') + 'FOLLOW' - message_class: 'button-orange' - message_caption: 'MESSAGE' - button_message: 'button-orange' - musician_actions = context.JK.fillTemplate(aTemplate, actionVals) - latencyBadge = context._.template($("#template-account-session-latency").html(), $.extend(sessionUtils.createLatency(musician), musician), variable: 'data') - mVals = - avatar_url: context.JK.resolveAvatarUrl(musician.photo_url) - profile_url: '/client#/profile/' + musician.id - musician_name: musician.name - musician_location: this._formatLocation(musician) - instruments: instr_logos - biography: musician['biography'] - follow_count: musician['follow_count'] - friend_count: musician['friend_count'] - recording_count: musician['recording_count'] - session_count: musician['session_count'] - musician_id: musician['id'] - musician_action_template: musician_actions - latency_badge: latencyBadge - musician_first_name: musician['first_name'] - $rendering = $(context.JK.fillTemplate(mTemplate, mVals)) - $offsetParent = @resultsListContainer.closest('.content') - data = entity_type: 'musician' - options = - positions: [ - 'top' - 'bottom' - 'right' - 'left' - ] - offsetParent: $offsetParent - scoreOptions = offsetParent: $offsetParent - context.JK.helpBubble($('.follower-count', $rendering), 'follower-count', data, options); - context.JK.helpBubble($('.friend-count', $rendering), 'friend-count', data, options); - context.JK.helpBubble($('.recording-count', $rendering), 'recording-count', data, options); - context.JK.helpBubble($('.session-count', $rendering), 'session-count', data, options); - @helpBubble.scoreBreakdown $('.latency', $rendering), false, musician['full_score'], myAudioLatency, musician['audio_latency'], musician['score'], scoreOptions - @resultsListContainer.append $rendering - $rendering.find('.biography').dotdotdot() - ii++ - - this._bindMessageMusician() - this._bindFriendMusician() - this._bindFollowMusician() - - context.JK.bindHoverEvents() - return - - _bindMessageMusician: () => - objThis = this - @screen.find('.search-m-message').on 'click', (evt) -> - userId = $(this).parent().data('musician-id') - objThis.app.layout.showDialog 'text-message', d1: userId - return false - - - _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 - $self = $(evt.target) - if 0 == $self.closest('.button-orange').size() - return false - logger.debug("evt.target", evt.target) - $self.click (ee) -> - ee.preventDefault() - return false - evt.stopPropagation() - uid = $self.parent().data('musician-id') - objThis.rest.sendFriendRequest objThis.app, uid, this.friendRequestCallback - @app.notify({text: 'A friend request has been sent.'}) - return false - - _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 false - 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 false - error: objThis.app.ajaxError - return false - - _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.data_blob), page: @pageNumber }).done(this.didSearch) - return true - false - - registerResultsPagination: () => - _resultsListContainer = @resultsListContainer - _headerHeight = @screen.find('#musician-search-filter-results-header').height() - _paginator = this.paginate - - @screen.find('.content-body-scroller').scroll -> - if _resultsListContainer.is(':visible') - jthis = $(this) - wintop = jthis.scrollTop() - winheight = jthis.innerHeight() - docheight = jthis[0].scrollHeight - _headerHeight - scrollTrigger = 0.98; - if ((wintop / (docheight - winheight)) >= scrollTrigger) - _paginator() diff --git a/web/app/assets/stylesheets/client/band.css.scss b/web/app/assets/stylesheets/client/band.css.scss index e3b33b7a3..2cbdc6b26 100644 --- a/web/app/assets/stylesheets/client/band.css.scss +++ b/web/app/assets/stylesheets/client/band.css.scss @@ -1,6 +1,6 @@ @import "client/common.css.scss"; -#band-setup, #band-profile { +#band-setup, #band-profile, #bands-filter-to_hire, #bands-filter-to_join { font-family: Raleway, Arial, Helvetica, verdana, arial, sans-serif; .band-field { input[type="text"], select, textarea { @@ -495,10 +495,178 @@ label { font-size: 1.05em; } +} - label.strong-label { - font-weight: bold; - font-size: 1.1em; +#bands-screen { + + .col-left { + float: left; + width: 50%; + margin-left: auto; + margin-right: auto; } + .col-right { + float: right; + width: 50%; + margin-left: auto; + margin-right: auto; + } + + #band-search-filter-spinner { + display: block; + margin-left: auto; + margin-right: auto; + margin-top: 40px; + } + + #band-search-filter-results-list { + .profile-band-list-result { + padding-top: 5px; + } + } + + .builder-section { + padding: 10px; + margin-bottom: 20px; + } + + .builder-sort-order { + .text-label { + float: right; + margin-right: 10px; + padding-top: 5px; + } + .sort-order-selector { + float: right; + .easydropdown-wrapper { + width: 140px; + } + } + } + + #band-search-filter-builder { + margin-left: 5px; + } + + .band-search-filter-builder-bottom { + text-align: right; + #btn-perform-band-search { + width: 120px; + margin-left: 20px; + } + } + + tr:nth-child(even) td { + padding-bottom: 0; + } + + #band-search-filter-results-header { + padding: 10px 10px 10px 10px; + } + + #band-search-filter-description { + padding: 5px 5px 5px 5px; + display: inline; + margin-left: auto; + margin-right: auto; + } + + #btn-band-search-builder { + float: left; + margin-right: 20px; + } + + #btn-band-search-reset { + float: right; + margin-left: 20px; + } + + #bands-filter-to_hire { + width: 100%; + + .builder-selector { + margin-top: 10px; + } + + #max_cost_amount { + width: 60px; + } + + .col-left { + float: left; + width: 45%; + margin-left: auto; + margin-right: auto; + padding-right: 20px; + + } + + .col-right { + float: right; + width: 45%; + margin-left: auto; + margin-right: auto; + + .easydropdown-wrapper { + width: 100%; + } + } + } + + #bands-filter-to_join { + width: 100%; + + .builder-selector { + margin-top: 10px; + } + .lhs { + float: left; + width: 40%; + .easydropdown-wrapper { + width: 100%; + } + } + .rhs { + float: right; + width: 40%; + .easydropdown-wrapper { + width: 100%; + } + } + .col-left { + float: left; + width: 30%; + margin-left: auto; + margin-right: auto; + padding-right: 20px; + + .easydropdown-wrapper { + width: 100%; + } + + } + + .col-right { + float: right; + width: 66%; + margin-left: auto; + margin-right: auto; + + } + + .search-filter-setup-genres { + } + + .band-setup-genres { + .easydropdown-wrapper { + width: 100%; + } + } + .builder-section { + padding: 10px; + margin-bottom: 20px; + } + + } } \ No newline at end of file diff --git a/web/app/assets/stylesheets/client/musician.css.scss b/web/app/assets/stylesheets/client/musician.css.scss index 7fcd81f54..69f9b82fe 100644 --- a/web/app/assets/stylesheets/client/musician.css.scss +++ b/web/app/assets/stylesheets/client/musician.css.scss @@ -254,15 +254,15 @@ .col-left { @include border_box_sizing; float: left; - width: 33%; + width: 45%; margin-left: auto; margin-right: auto; + padding-right: 20px; } .col-right { float: right; - width: 67%; - @include border_box_sizing; + width: 45%; margin-left: auto; margin-right: auto; @@ -306,6 +306,14 @@ } .builder-ages { width: 100%; + height: 100%; + 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; } .builder-instruments { width: 100%; @@ -321,6 +329,21 @@ } } + .musician-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; + .easydropdown-wrapper { + width: 100%; + } + } + } .filter-element { diff --git a/web/app/controllers/api_search_controller.rb b/web/app/controllers/api_search_controller.rb index 0663265ff..54e3ab5b0 100644 --- a/web/app/controllers/api_search_controller.rb +++ b/web/app/controllers/api_search_controller.rb @@ -7,7 +7,7 @@ class ApiSearchController < ApiController def index if 1 == params[Search::PARAM_MUSICIAN].to_i || 1 == params[Search::PARAM_BAND].to_i - query = params.clone + query = parasobj.clone query[:remote_ip] = request.remote_ip if 1 == query[Search::PARAM_MUSICIAN].to_i @search = Search.musician_filter(query, current_user) @@ -32,13 +32,35 @@ class ApiSearchController < ApiController end elsif request.post? - ms = MusicianSearch.user_search_filter(current_user) + sobj = MusicianSearch.user_search_filter(current_user) filter = params[:filter] if filter == 'reset' - @search = ms.reset_search_results + @search = sobj.reset_search_results else json = JSON.parse(filter, :create_additions => false) - @search = ms.search_results_page(json, [params[:page].to_i, 1].max) + @search = sobj.search_results_page(json, [params[:page].to_i, 1].max) + end + respond_with @search, responder: ApiResponder, status: 201, template: 'api_search/index' + end + end + + def bands + if request.get? + if params[:results] + @search = BandSearch.user_search_filter(current_user).search_results_page(params[:subtype]) + respond_with @search, responder: ApiResponder, status: 201, template: 'api_search/index' + else + render :json => BandSearch.search_filter_json(current_user, params[:subtype]), :status => 200 + end + + elsif request.post? + sobj = BandSearch.user_search_filter(current_user) + filter = params[:filter] + if filter == 'reset' + @search = sobj.reset_search_results(params[:subtype]) + else + json = JSON.parse(filter, :create_additions => false) + @search = sobj.search_results_page(params[:subtype], json, [params[:page].to_i, 1].max) end respond_with @search, responder: ApiResponder, status: 201, template: 'api_search/index' end diff --git a/web/app/controllers/spikes_controller.rb b/web/app/controllers/spikes_controller.rb index 0edce85ce..069a2d757 100644 --- a/web/app/controllers/spikes_controller.rb +++ b/web/app/controllers/spikes_controller.rb @@ -69,7 +69,12 @@ class SpikesController < ApplicationController end def musician_search_filter - # gon.musician_search_meta = MusicianSearch::SEARCH_FILTER_META + # gon.musician_search_meta = MusicianSearch.search_filter_meta + render :layout => 'web' + end + + def band_search_filter + gon.band_search_meta = BandSearch.search_filter_meta render :layout => 'web' end diff --git a/web/app/helpers/client_helper.rb b/web/app/helpers/client_helper.rb index c4977e130..f6febfb1d 100644 --- a/web/app/helpers/client_helper.rb +++ b/web/app/helpers/client_helper.rb @@ -67,7 +67,8 @@ 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 + gon.musician_search_meta = MusicianSearch.search_filter_meta + gon.band_search_meta = BandSearch.search_filter_meta # is this the native client or browser? @nativeClient = is_native_client? diff --git a/web/app/views/api_search/index.rabl b/web/app/views/api_search/index.rabl index 4296eb164..12e7fbf53 100644 --- a/web/app/views/api_search/index.rabl +++ b/web/app/views/api_search/index.rabl @@ -1,8 +1,6 @@ object @search -node :search_type do |ss| ss.search_type end - -if @search.is_a?(MusicianSearch) +if @search.is_a?(BaseSearch) node :page_count do |foo| @search.page_count @@ -12,18 +10,19 @@ if @search.is_a?(MusicianSearch) 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 - node :summary do |foo| - @search.description - end + if @search.is_a? MusicianSearch + node :description do |foo| + @search.description + end + node :is_blank_filter do |foo| + @search.is_blank? + end + child(:results => :musicians) { attributes :id, :first_name, :last_name, :name, :city, :state, :country, :online, :musician, :photo_url, :biography, :regionname, :score, :full_score @@ -53,8 +52,8 @@ if @search.is_a?(MusicianSearch) 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 :follow_count do |musician| @search.follow_count(musician) end node :recording_count do |musician| @search.record_count(musician) end node :session_count do |musician| @search.session_count(musician) end @@ -62,8 +61,49 @@ if @search.is_a?(MusicianSearch) last_jam_audio_latency(musician) end } + +elsif @search.is_a?(BandSearch) + + node :is_blank_filter do |foo| + @search.is_blank?(params[:subtype]) + end + + node :description do |foo| + @search.description(params[:subtype]) + end + + child(:results => :bands) { + attributes :id, :name, :city, :state, :country, :photo_url, :biography, :logo_url, :website + + node :is_following do |band| + @search.is_follower?(band) + end + + node :biography do |band| + band.biography.nil? ? "" : band.biography + end + + child :genres => :genres do + attributes :genre_id, :description + end + + child :users => :players do |pl| + node :user_id do |uu| uu.id end + node :photo_url do |uu| uu.photo_url end + node :name do |uu| uu.name end + node :instruments do |uu| uu.instruments.map(&:id).join(',') end + end + + node :follow_count do |band| @search.follow_count(band) end + node :recording_count do |band| @search.record_count(band) end + node :session_count do |band| @search.session_count(band) end + } +end + else +node :search_type do |ss| ss.search_type end + if @search.session_invite_search? child(:results => :suggestions) { node :value do |uu| uu.name end diff --git a/web/app/views/clients/_band_search_filter.html.slim b/web/app/views/clients/_band_search_filter.html.slim new file mode 100644 index 000000000..58340e5d1 --- /dev/null +++ b/web/app/views/clients/_band_search_filter.html.slim @@ -0,0 +1,216 @@ +.content-body-scroller + div#band-search-filter-builder.content-wrapper + + div#band-search-filter-results.content-wrapper + div#band-search-filter-results-header + #band-search-filter-results-blank + a.button-orange#btn-band-search-builder-to_join href="#" SEARCH FOR BAND TO JOIN + | - or -  + a.button-orange#btn-band-search-builder-to_hire href="#" SEARCH FOR BAND TO HIRE + #band-search-filter-results-filtered + a.button-orange#btn-band-search-builder href="#" SEARCH + a.button-grey#btn-band-search-reset href="#" RESET + div#band-search-filter-description + div.clearall + div#band-search-filter-spinner.spinner-large + + div#band-search-filter-results-wrapper + div#band-search-filter-results-list-blank + div.content-wrapper#band-search-filter-results-list + div.paginate-wait + Fetching more results... + div.spinner-small + +script#template-band-search-filter-to_join type="text/template" + #bands-filter-to_join + .band-search-filter-builder-top.builder-section + .col-left + h2 search bands + / .col-right.builder-sort-order + / .sort-order-selector + / select.easydropdown name="sort_order" + / option selected="selected" value="{sort_order}" {sort_order} + / .text-label Sort Results By: + .clearall + + .band-search-filter-builder-middle1.builder-section + .col-left + .field + label for="search-filter-genres" Genres: + .search-filter-setup-genres.band-setup-genres + table#search-filter-genres cellpadding="10" cellspacing="6" width="100%" + + .col-right + .field + label for="search-filter-instruments" + | Looking for New Members with These Skills: + .search-filter-setup-instruments.band-setup-genres.builder-instruments + table#search-filter-instruments cellpadding="10" cellspacing="6" width="100%" + + .clearall + + .band-search-filter-builder-middle2.builder-section + .col-left + .field.builder-selector + label Type: + select.easydropdown name="band_type" + option selected="selected" value="{band_type}" {band_type} + + .field.builder-selector + label Play Commitment: + select.easydropdown name="play_commitment" + option selected="selected" value="{play_commitment}" {play_commitment} + + .col-right + .field.builder-selector + .lhs + label Status: + select.easydropdown name="band_status" + option selected="selected" value="{band_status}" {band_status} + + .field.builder-selector + .rhs + label Concert Gigs Played: + select.easydropdown name="concert_gigs" + option selected="selected" value="{concert_gigs}" {concert_gigs} + + .clearall + + .field.builder-selector + .lhs + label Touring Option: + select.easydropdown name="touring_option" + option selected="selected" value="{touring_option}" {touring_option} + .clearall + + .clearall + .band-search-filter-builder-bottom.builder-section.builder-action-buttons + .col-right + a#btn-band-search-cancel.builder-button.button-grey href="#" CANCEL + a#btn-perform-band-search.builder-button.button-orange href="#" SEARCH + +script#template-band-search-filter-to_hire type="text/template" + #bands-filter-to_hire + .band-search-filter-builder-top-to_hire.builder-section + .col-left + h2 search bands to hire + .col-right.builder-sort-order + .sort-order-selector + select.easydropdown name="sort_order" + option selected="selected" value="{sort_order}" {sort_order} + .text-label Sort Results By: + .clearall + + .band-search-filter-builder-middle1.builder-section + .col-left + .field + label for="search-filter-genres" Genres: + .search-filter-setup-genres.band-setup-genres + table#search-filter-genres cellpadding="10" cellspacing="6" width="100%" + + .col-right + .field.builder-selector + label Status: + select.easydropdown name="band_status" + option selected="selected" value="{band_status}" {band_status} + + .field.builder-selector + label Concert Gigs Played: + select.easydropdown name="concert_gigs" + option selected="selected" value="{concert_gigs}" {concert_gigs} + + .field.builder-selector + label Performance Sample Available: + select.easydropdown name="performance_samples" + option selected="selected" value="{performance_samples}" {performance_samples} + + .clearall + + .band-search-filter-builder-middle2.builder-section + .field.builder-selector +  Find bands to play a paid gig at a cost not to exceed  $ + input type="text" id="max_cost_amount" name="max_cost" value="{max_cost}" + + .field.builder-selector +  Find bands that will play free gigs + + .clearall + + .clearall + .band-search-filter-builder-bottom.builder-section.builder-action-buttons + .col-right + a#btn-band-search-cancel.builder-button.button-grey href="#" CANCEL + a#btn-perform-band-search.builder-button.button-orange href="#" SEARCH + +script#template-search-filter-setup-instrument type="text/template" + tr data-instrument-id="{id}" + td {description} + td align="right" width="50%" + select.proficiency_selector name="proficiency" + option value="1" Beginner + option value="2" Intermediate + option value="3" Expert + +script#template-search-filter-setup-genres type="text/template" + tr + td {description} + +script#template-find-band-row type="text/template" + .profile-band-list-result.band-list-result + .f11 + .left style="width:63px;margin-top:-12px;" + .avatar-small + img src="{avatar_url}" / + .right.mband-players style="width:265px; margin-top:-4px;" + table.musicians cellpadding="0" cellspacing="5" + | {band_player_template} + div style="margin-left: 63px; margin-right: 275px;margin-top: 12px;" + .first-row data-hint="top-row" + .lcol.left + .result-name + | {band_name} + .result-location + | {band_location} + br + #result_genres.nowrap.mt10 + | {genres} + .whitespace + .biography + | {biography} + .clearleft + .button-row + .lcol.stats.left + span.follower-count + | {follow_count} + img src="../assets/content/icon_followers.png" width="22" height="12" align="absmiddle" style="margin-right:4px;" + span.recording-count + | {recording_count} + img src="../assets/content/icon_recordings.png" width="12" height="13" align="absmiddle" style="margin-right:4px;" + span.session-count + | {session_count} + img src="../assets/content/icon_session_tiny.png" width="12" height="12" align="absmiddle" + .clearall + .result-list-button-wrapper data-band-id="{band_id}" + | {band_action_template} + .clearall + +script#template-band-action-btns type="text/template" + a.button-orange.smallbutton href="{profile_url}" PROFILE + a.smallbutton.search-m-follow href="#" class="{button_follow}" FOLLOW + +script#template-band-edit-btns type="text/template" + a.button-orange.smallbutton href="{profile_url}" PROFILE + a.button-orange.smallbutton href="{band_edit_url}" EDIT BAND + a.button-orange.smallbutton href="{band_member_url}" INVITE + +script#template-band-player-info type="text/template" + tr + td + a.avatar-tiny href="{profile_url}" user-id="{user_id}" hoveraction="musician" + img src="{avatar_url}" + td style="padding: 0 4px;width:88px;" + a user-id="{user_id}" hoveraction="musician" href="{profile_url}" + strong + | {player_name} + td.instruments + | {player_instruments} diff --git a/web/app/views/clients/_bands.html.erb b/web/app/views/clients/_bands.html.erb deleted file mode 100644 index ae7bdbf5c..000000000 --- a/web/app/views/clients/_bands.html.erb +++ /dev/null @@ -1,86 +0,0 @@ - -<%= content_tag(:div, :layout => 'screen', 'layout-id' => 'bands', :class => "screen secondary", :id => "bands-screen") do -%> - <%= content_tag(:div, :class => :content) do -%> - <%= content_tag(:div, :class => 'content-head') do -%> - <%= content_tag(:div, image_tag("content/icon_bands.png", {:height => 19, :width => 19}), :class => 'content-icon') %> - <%= content_tag(:h1, 'bands') %> - <%= render "screen_navigation" %> - <% end -%> - <%= content_tag(:div, :class => 'content-body') do -%> - <%= form_tag('', {:id => 'find-band-form', :class => 'inner-content'}) do -%> - <%= render(:partial => "web_filter", :locals => {:search_type => Search::PARAM_BAND}) %> - <%= content_tag(:div, :class => 'filter-body') do %> - <%= content_tag(:div, :class => 'content-body-scroller') do -%> - <%= content_tag(:div, content_tag(:div, '', :id => 'band-filter-results', :class => 'filter-results'), :class => 'content-wrapper') %> - <% end -%> - <% end -%> - <% end -%> - <% end -%> - <% end -%> -<% end -%> - - - - - - - - - - diff --git a/web/app/views/clients/_bands.html.slim b/web/app/views/clients/_bands.html.slim new file mode 100644 index 000000000..c472fbe39 --- /dev/null +++ b/web/app/views/clients/_bands.html.slim @@ -0,0 +1,140 @@ +#bands-screen.screen.secondary layout="screen" layout-id="bands" + .content + .content-head + .content-icon + img alt="Icon_bands" height="19" src="/assets/content/icon_bands.png" width="19" / + h1 bands + = render "screen_navigation" + .content-body + = render "clients/band_search_filter" + +script#template-band-search-filter-to_join type="text/template" + #bands-filter-to_join + #band-search-filter-builder-top.builder-section + .col-left + h2 search bands + .col-right.builder-sort-order + .text-label Sort Results By: + select.easydropdown name="sort_order" + option selected="selected" value="{sort_order}" {sort_order} + .clearall + + #band-search-filter-builder-middle1.builder-section + .col-left + .field + label for="search-filter-genres" Genres: + .search-filter-setup-genres.band-setup-genres + table#search-filter-genres cellpadding="10" cellspacing="6" width="100%" + + .col-right + .field + label for="search-filter-instruments" + | Instruments & Skill Level: + .search-filter-setup-instruments.band-setup-genres.builder-instruments + table#search-filter-instruments cellpadding="10" cellspacing="6" width="100%" + .clearall + + #band-search-filter-builder-middle2.builder-section + .col-left + .field.builder-selector + label Type: + select.easydropdown name="band_type" + option selected="selected" value="{band_type}" {band_type} + + .field.builder-selector + label Play Commitment: + select.easydropdown name="play_commit" + option selected="selected" value="{play_commit}" {play_commit} + + .col-right + .field.builder-selector + label Status: + select.easydropdown name="skill_level" + option selected="selected" value="{skill_level}" {skill_level} + + .field.builder-selector + label Concert Gigs Played: + select.easydropdown name="concert_gigs" + option selected="selected" value="{concert_gigs}" {concert_gigs} + + .field.builder-selector + label Touring Option: + select.easydropdown name="tour_option" + option selected="selected" value="{tour_option}" {tour_option} + + .clearall + + .clearall + #band-search-filter-builder-bottom.builder-section.builder-action-buttons + .col-right + a#btn-perform-band-search.builder-button.button-orange href="#" SEARCH + a#btn-band-search-cancel.builder-button.button-grey href="#" CANCEL + +/! Session Row Template +script#template-search-band-row type="text/template" + .profile-band-list-result.band-list-result + .f11 + .left style="width:63px;margin-top:-12px;" + /! avatar + .avatar-small + img src="{avatar_url}" / + .right.mband-players style="width:265px; margin-top:-4px;" + table.musicians cellpadding="0" cellspacing="5" + | {band_player_template} + div style="margin-left: 63px; margin-right: 275px;margin-top: 12px;" + .first-row data-hint="top-row" + .lcol.left + /! name and location + .result-name + | {band_name} + .result-location + | {band_location} + br / + #result_genres.nowrap.mt10 + | {genres} + .whitespace + .biography + | {biography} + .clearleft + .button-row + .lcol.stats.left + span.follower-count + | {follow_count} + img src="../assets/content/icon_followers.png" width="22" height="12" align="absmiddle" style="margin-right:4px;" / + span.recording-count + | {recording_count} + img src="../assets/content/icon_recordings.png" width="12" height="13" align="absmiddle" style="margin-right:4px;" / + span.session-count + | {session_count} + img src="../assets/content/icon_session_tiny.png" width="12" height="12" align="absmiddle" / + .clearall + .result-list-button-wrapper data-band-id="{band_id}" + | {band_action_template} + .clearall + +script#template-search-band-action-btns type="text/template" + a.button-orange smallbutton href="{profile_url}" + PROFILE + a class="{button_follow} smallbutton search-m-follow" href="#" + FOLLOW + +script#template-search-band-edit-btns type="text/template" + a href="{profile_url}" class="button-orange smallbutton" + PROFILE + a href="{band_edit_url}" class="button-orange smallbutton" + EDIT BAND + a href="{band_member_url}" class="button-orange smallbutton" + INVITE + +script#template-search-band-player-info type="text/template" + tr + td + a.avatar-tiny href="{profile_url}" user-id="{user_id}" hoveraction="musician" + img src="{avatar_url}" / + td style="padding: 0 4px;width:88px" + a user-id="{user_id}" hoveraction="musician" href="{profile_url}" + strong + | {player_name} + td.instruments + | {player_instruments} + diff --git a/web/app/views/clients/_musician_search_filter.html.slim b/web/app/views/clients/_musician_search_filter.html.slim index bbd9a949a..b454bee83 100644 --- a/web/app/views/clients/_musician_search_filter.html.slim +++ b/web/app/views/clients/_musician_search_filter.html.slim @@ -4,7 +4,6 @@ div#musician-search-filter-results.content-wrapper div#musician-search-filter-results-header a#btn-musician-search-builder.button-orange href="#" SEARCH - span.musician-search-text Click search button to look for musicians with similar interests, skill levels, etc. a#btn-musician-search-reset.button-grey href="#" RESET div#musician-search-filter-description div.clearall @@ -22,51 +21,51 @@ script#template-musician-search-filter type="text/template" .col-left h2 search musicians .col-right.builder-sort-order - .text-label Sort Results By: - select.easydropdown name="sort_order" - option selected="selected" value="{sort_order}" {sort_order} + .text-label Sort Results By: + select.easydropdown name="sort_order" + option selected="selected" value="{sort_order}" {sort_order} .clearall #musician-search-filter-builder-middle.builder-section .col-left .field - label for="search-filter-genres" Genres - .search-filter-setup-genres.band-setup-genres - #search-filter-genres + label for="search-filter-genres" Genres: + .search-filter-setup-genres.musician-setup-genres + table#search-filter-genres cellpadding="10" cellspacing="6" width="100%" .field.builder-selector - label Interests + label Interests: select.easydropdown name="interests" option selected="selected" value="{interests}" {interests} .field.builder-selector - label Studio Sessions Played + label Studio Sessions Played: select.easydropdown name="studio_sessions" option selected="selected" value="{studio_sessions}" {studio_sessions} .col-right .field label for="search-filter-instruments" - | Instruments & Skill Level - .search-filter-setup-instruments.band-setup-genres.builder-instruments.session-instrumentlist + | Instruments & Skill Level: + .search-filter-setup-instruments.musician-setup-genres.builder-instruments table#search-filter-instruments cellpadding="10" cellspacing="6" width="100%" .col-left .field.builder-selector - label Status + label Status: select.easydropdown name="skill_level" option selected="selected" value="{skill_level}" {skill_label} .field.builder-selector - label Concert Gigs Played + label Concert Gigs Played: select.easydropdown name="concert_gigs" option selected="selected" value="{concert_gigs}" {concert_gigs} .col-right .field.builder-selector - label for="search-filter-ages" Ages - .search-filter-setup-ages.band-setup-genres.builder-ages - #search-filter-ages + label for="search-filter-ages" Ages: + .search-filter-setup-ages.builder-ages + table#search-filter-ages cellpadding="10" cellspacing="6" width="100%" .clearall .clearall @@ -85,25 +84,13 @@ script#template-search-filter-setup-instrument type="text/template" option value="2" Intermediate option value="3" Expert -script#template-search-filter-setup-genres type="text/template" - .genre-option - | {% if(data.checked) { %} - input value="{{data.id}}" checked="checked" type="checkbox" - | {% } else { %} - input value="{{data.id}}" type="checkbox" - | {% } %} - label - | {{data.description}} +script#template-search-filter-setup-genres type="text/template" + tr + td {description} -script#template-search-filter-setup-ages type="text/template" - .age-option - | {% if(data.checked) { %} - input value="{{data.id}}" checked="checked" type="checkbox" - | {% } else { %} - input value="{{data.id}}" type="checkbox" - | {% } %} - label - | {{data.description}} +script#template-search-filter-setup-ages type="text/template" + tr + td {description} /! Session Row Template script#template-search-musician-row type="text/template" diff --git a/web/app/views/clients/index.html.erb b/web/app/views/clients/index.html.erb index 5002f53ea..31eabb4bd 100644 --- a/web/app/views/clients/index.html.erb +++ b/web/app/views/clients/index.html.erb @@ -310,21 +310,17 @@ // var findMusicianScreen = new JK.FindMusicianScreen(JK.app); //findMusicianScreen.initialize(JK.TextMessageDialogInstance); + var findBandScreen = new JK.BandSearchFilter(); + findBandScreen.init(JK.app); + var redeemSignUpScreen = new JK.RedeemSignUpScreen(JK.app); redeemSignUpScreen.initialize(); var redeemCompleteScreen = new JK.RedeemCompleteScreen(JK.app); redeemCompleteScreen.initialize(); - var findBandScreen = new JK.FindBandScreen(JK.app); - findBandScreen.initialize(); - - //var sessionScreen = new JK.SessionScreen(JK.app); - //sessionScreen.initialize(localRecordingsDialog, recordingFinishedDialog, JK.FriendSelectorDialogInstance); - AppActions.appInit.trigger(JK.app) - var addNewGearDialog = new JK.AddNewGearDialog(JK.app); addNewGearDialog.initialize(); diff --git a/web/app/views/spikes/band_search_filter.html.slim b/web/app/views/spikes/band_search_filter.html.slim new file mode 100644 index 000000000..a98478205 --- /dev/null +++ b/web/app/views/spikes/band_search_filter.html.slim @@ -0,0 +1,15 @@ += javascript_include_tag "profile_utils" += javascript_include_tag "member_search_filter" += stylesheet_link_tag "client/band" +#bands-screen + = render "clients/band_search_filter" + +javascript: + var initialized = false; + $(document).on('JAMKAZAM_READY', function(e, data) { + setTimeout(function() { + window.band_search_filter = new JK.BandSearchFilter(); + band_search_filter.init(data.app); + band_search_filter.afterShow(); + }, 1) + }); diff --git a/web/app/views/spikes/musician_search_filter.html.slim b/web/app/views/spikes/musician_search_filter.html.slim index 777cf2636..d6ae1f1da 100644 --- a/web/app/views/spikes/musician_search_filter.html.slim +++ b/web/app/views/spikes/musician_search_filter.html.slim @@ -1,15 +1,15 @@ = javascript_include_tag "profile_utils" -= javascript_include_tag "musician_search_filter" += javascript_include_tag "member_search_filter" = stylesheet_link_tag "client/musician" -#musician_search_spike -= render "clients/musician_search_filter" +#musicians-screen + = render "clients/musician_search_filter" javascript: var initialized = false; $(document).on('JAMKAZAM_READY', function(e, data) { setTimeout(function() { window.musician_search_filter = new JK.MusicianSearchFilter(); - musician_search_filter.init(); + musician_search_filter.init(data.app); musician_search_filter.afterShow(); }, 1) }); diff --git a/web/config/routes.rb b/web/config/routes.rb index 2281f6937..22807e2f8 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -118,6 +118,7 @@ SampleApp::Application.routes.draw do match '/site_validate', to: 'spikes#site_validate' match '/recording_source', to: 'spikes#recording_source' match '/musician_search_filter', to: 'spikes#musician_search_filter' + match '/band_search_filter', to: 'spikes#band_search_filter' # junk pages match '/help', to: 'static_pages#help' @@ -478,6 +479,7 @@ SampleApp::Application.routes.draw do # search match '/search' => 'api_search#index', :via => :get match '/search/musicians' => 'api_search#musicians', :via => [:get, :post] + match '/search/bands' => 'api_search#bands', :via => [:get, :post] # join requests match '/join_requests/:id' => 'api_join_requests#show', :via => :get, :as => 'api_join_request_detail' diff --git a/web/lib/tasks/sample_data.rake b/web/lib/tasks/sample_data.rake index 8ba62eb5a..58d25782b 100644 --- a/web/lib/tasks/sample_data.rake +++ b/web/lib/tasks/sample_data.rake @@ -139,7 +139,7 @@ end def make_band_members Band.find_each do |bb| User.order('RANDOM()').limit(4).each do |uu| - BandMusician.create!({:user_id => uu.id, :band_id => bb.id}) + BandMusician.create({:user_id => uu.id, :band_id => bb.id}) end end end