diff --git a/db/manifest b/db/manifest
index 36e365013..5bad3d1e0 100755
--- a/db/manifest
+++ b/db/manifest
@@ -182,3 +182,4 @@ rsvp_slots_prof_level.sql
add_file_name_music_notation.sql
change_scheduled_start_music_session.sql
music_sessions_iso_639_3.sql
+discardable_claimed_recordings.sql
diff --git a/db/up/discardable_claimed_recordings.sql b/db/up/discardable_claimed_recordings.sql
new file mode 100644
index 000000000..d082ff770
--- /dev/null
+++ b/db/up/discardable_claimed_recordings.sql
@@ -0,0 +1 @@
+ALTER TABLE claimed_recordings ADD COLUMN discarded BOOLEAN DEFAULT FALSE;
\ No newline at end of file
diff --git a/ruby/lib/jam_ruby/lib/nav.rb b/ruby/lib/jam_ruby/lib/nav.rb
index 56a7a09af..1bce0f133 100644
--- a/ruby/lib/jam_ruby/lib/nav.rb
+++ b/ruby/lib/jam_ruby/lib/nav.rb
@@ -1,9 +1,18 @@
module JamRuby
+ # To use this, be sure to add this in before block: stub_const("APP_CONFIG", web_config)
class Nav
def self.home(options ={})
- "#{APP_CONFIG.external_root_url}/client#/home#{dialog(options)}"
+ "#{base_url}/home#{dialog(options)}"
+ end
+
+ def self.profile(user)
+ "#{base_url}/profile/#{user.id}"
+ end
+
+ def self.feed
+ "#{base_url}/feed"
end
def self.accept_friend_request_dialog(friend_request_id)
@@ -12,6 +21,10 @@ module JamRuby
private
+ def self.base_url
+ "#{APP_CONFIG.external_root_url}/client#"
+ end
+
def self.dialog(options)
dialog = ''
if options[:dialog]
diff --git a/ruby/lib/jam_ruby/models/active_music_session.rb b/ruby/lib/jam_ruby/models/active_music_session.rb
index 9d0de3493..a4bf834ce 100644
--- a/ruby/lib/jam_ruby/models/active_music_session.rb
+++ b/ruby/lib/jam_ruby/models/active_music_session.rb
@@ -334,6 +334,7 @@ module JamRuby
# same, sorted by score. date seems irrelevant as these are active sessions. ams_init must be called
# first.
def self.ams_query(current_user, options = {})
+ session_id = options[:session_id]
client_id = options[:client_id]
genre = options[:genre]
lang = options[:lang]
@@ -392,6 +393,7 @@ module JamRuby
query = query.limit(limit)
query = query.where("music_sessions.genre_id = ?", genre) unless genre.blank?
query = query.where('music_sessions.language = ?', lang) unless lang.blank?
+ query = query.where('music_sessions.id = ?', session_id) unless session_id.blank?
query = query.where("(description_tsv @@ to_tsquery('jamenglish', ?))", keyword + ':*') unless keyword.blank?
if !day.blank? && !timezone_offset.blank?
@@ -399,6 +401,9 @@ module JamRuby
day = Date.parse(day)
next_day = day + 1
timezone_offset = timezone_offset.to_i
+ if timezone_offset > 0
+ timezone_offset = "+#{timezone_offset}"
+ end
query = query.where("scheduled_start BETWEEN TIMESTAMP WITH TIME ZONE '#{day} 00:00:00#{timezone_offset}'
AND TIMESTAMP WITH TIME ZONE '#{next_day} 00:00:00#{timezone_offset}'")
rescue Exception => e
@@ -678,6 +683,10 @@ module JamRuby
music_session.approval_required
end
+ def music_notations
+ music_session.music_notations
+ end
+
def tick_track_changes
self.track_changes_counter += 1
self.save!(:validate => false)
diff --git a/ruby/lib/jam_ruby/models/claimed_recording.rb b/ruby/lib/jam_ruby/models/claimed_recording.rb
index 573d5a390..d984d6a3c 100644
--- a/ruby/lib/jam_ruby/models/claimed_recording.rb
+++ b/ruby/lib/jam_ruby/models/claimed_recording.rb
@@ -8,9 +8,9 @@ module JamRuby
belongs_to :genre, :class_name => "JamRuby::Genre"
has_many :recorded_tracks, :through => :recording, :class_name => "JamRuby::RecordedTrack"
has_many :playing_sessions, :class_name => "JamRuby::ActiveMusicSession"
- has_many :likes, :class_name => "JamRuby::RecordingLiker", :foreign_key => "claimed_recording_id"
+ has_many :likes, :class_name => "JamRuby::RecordingLiker", :foreign_key => "claimed_recording_id", :dependent => :destroy
has_many :plays, :class_name => "JamRuby::PlayablePlay", :foreign_key => "claimed_recording_id", :dependent => :destroy
- has_one :share_token, :class_name => "JamRuby::ShareToken", :inverse_of => :shareable, :foreign_key => 'shareable_id'
+ has_one :share_token, :class_name => "JamRuby::ShareToken", :inverse_of => :shareable, :foreign_key => 'shareable_id', :dependent => :destroy
validates :name, no_profanity: true, length: {minimum: 3, maximum: 64}, presence: true
validates :description, no_profanity: true, length: {maximum: 8000}
@@ -39,9 +39,9 @@ module JamRuby
raise PermissionError, "user doesn't own claimed_recording"
end
- self.name = params[:name] unless params[:name].nil?
- self.description = params[:description] unless params[:description].nil?
- self.genre = Genre.find(params[:genre]) unless params[:genre].nil?
+ self.name = params[:name]
+ self.description = params[:description]
+ self.genre = Genre.find_by_id(params[:genre]) unless params[:genre].nil?
self.is_public = params[:is_public] unless params[:is_public].nil?
save
end
@@ -50,13 +50,10 @@ module JamRuby
if user != self.user
raise PermissionError, "user doesn't own claimed_recording"
end
-
- # If this is the only copy, destroy the entire recording. Otherwise, just destroy this claimed_recording
- if recording.claimed_recordings.count == 1
- recording.destroy
- else
- self.destroy
- end
+
+ ClaimedRecording.where(:id => id).update_all(:discarded => true )
+
+ recording.discard(user)
end
diff --git a/ruby/lib/jam_ruby/models/country.rb b/ruby/lib/jam_ruby/models/country.rb
index ca64b222c..0cf5486a6 100644
--- a/ruby/lib/jam_ruby/models/country.rb
+++ b/ruby/lib/jam_ruby/models/country.rb
@@ -8,46 +8,16 @@ module JamRuby
end
def self.import_from_iso3166(file)
-
- # File iso3166.csv
- # Format:
- # countrycode,countryname
-
- # what this does is not replace the contents of the table, but rather update the specified rows with the names.
- # any rows not specified have the countryname reset to be the same as the countrycode.
-
- self.transaction do
- self.connection.execute "update #{self.table_name} set countryname = countrycode"
-
- File.open(file, 'r:ISO-8859-1') do |io|
- saved_level = ActiveRecord::Base.logger ? ActiveRecord::Base.logger.level : 0
- count = 0
-
- ncols = 2
-
- csv = ::CSV.new(io, {encoding: 'ISO-8859-1', headers: false})
- csv.each do |row|
- raise "file does not have expected number of columns (#{ncols}): #{row.length}" unless row.length == ncols
-
- countrycode = row[0]
- countryname = row[1]
-
- stmt = "UPDATE #{self.table_name} SET countryname = #{MaxMindIsp.quote_value(countryname)} WHERE countrycode = #{MaxMindIsp.quote_value(countrycode)}"
- self.connection.execute stmt
- count += 1
-
- if ActiveRecord::Base.logger and ActiveRecord::Base.logger.level < Logger::INFO
- ActiveRecord::Base.logger.debug "... logging updates to #{self.table_name} suspended ..."
- ActiveRecord::Base.logger.level = Logger::INFO
- end
- end
-
- if ActiveRecord::Base.logger
- ActiveRecord::Base.logger.level = saved_level
- ActiveRecord::Base.logger.debug "updated #{count} records in #{self.table_name}"
- end
- end # file
- end # transaction
+ self.delete_all
+ File.open(file, 'r:ISO-8859-1') do |io|
+ csv = ::CSV.new(io, {encoding: 'ISO-8859-1', headers: false})
+ csv.each do |row|
+ cc = self.new
+ cc.countrycode = row[0]
+ cc.countryname = row[1]
+ cc.save
+ end
+ end # file
end
end
end
diff --git a/ruby/lib/jam_ruby/models/feed.rb b/ruby/lib/jam_ruby/models/feed.rb
index 7c118d364..0802e3085 100644
--- a/ruby/lib/jam_ruby/models/feed.rb
+++ b/ruby/lib/jam_ruby/models/feed.rb
@@ -43,6 +43,7 @@ module JamRuby
query = Feed.joins("LEFT OUTER JOIN recordings ON recordings.id = feeds.recording_id")
.joins("LEFT OUTER JOIN music_sessions ON music_sessions.id = feeds.music_session_id")
.limit(limit)
+ .where('recordings is NULL OR recordings.all_discarded = false') # remove any 'all_discarded recordings from the search results'
# handle sort
if sort == 'date'
@@ -72,14 +73,14 @@ module JamRuby
end
- if target_user
+ if target_user
if target_user != user.id
require_public_recordings = "claimed_recordings.is_public = TRUE AND"
require_public_sessions = "music_sessions.fan_access = TRUE AND"
end
- query = query.joins("LEFT OUTER JOIN claimed_recordings ON recordings.id = claimed_recordings.recording_id AND #{require_public_recordings} (claimed_recordings.user_id = '#{target_user}' OR (recordings.band_id IN (SELECT band_id FROM bands_musicians where user_id='#{target_user}')))")
+ query = query.joins("LEFT OUTER JOIN claimed_recordings ON recordings.id = claimed_recordings.recording_id AND claimed_recordings.discarded = FALSE AND #{require_public_recordings} (claimed_recordings.user_id = '#{target_user}' OR (recordings.band_id IN (SELECT band_id FROM bands_musicians where user_id='#{target_user}')))")
query = query.joins("LEFT OUTER JOIN music_sessions_user_history ON music_sessions.id = music_sessions_user_history.music_session_id AND #{require_public_sessions} music_sessions_user_history.user_id = '#{target_user}'")
query = query.group("feeds.id, feeds.recording_id, feeds.music_session_id, feeds.created_at, feeds.updated_at, recordings.id, music_sessions.id")
if sort == 'plays'
@@ -97,7 +98,7 @@ module JamRuby
require_public_sessions = "music_sessions.fan_access = TRUE AND"
end
- query = query.joins("LEFT OUTER JOIN claimed_recordings ON recordings.id = claimed_recordings.recording_id AND #{require_public_recordings} recordings.band_id = '#{target_band}'")
+ query = query.joins("LEFT OUTER JOIN claimed_recordings ON recordings.id = claimed_recordings.recording_id AND claimed_recordings.discarded = FALSE AND #{require_public_recordings} recordings.band_id = '#{target_band}'")
query = query.where("music_sessions IS NULL OR #{require_public_sessions} music_sessions.band_id = '#{target_band}'")
query = query.group("feeds.id, feeds.recording_id, feeds.music_session_id, feeds.created_at, feeds.updated_at, recordings.id, music_sessions.id")
if sort == 'plays'
@@ -108,7 +109,7 @@ module JamRuby
query = query.where('recordings.id is NULL OR claimed_recordings.id IS NOT NULL')
#query = query.where('music_sessions.id is NULL OR music_sessions_user_history.id IS NOT NULL')
else
- query = query.joins('LEFT OUTER JOIN claimed_recordings ON recordings.id = claimed_recordings.recording_id AND claimed_recordings.is_public = TRUE')
+ query = query.joins('LEFT OUTER JOIN claimed_recordings ON recordings.id = claimed_recordings.recording_id AND claimed_recordings.discarded = FALSE AND claimed_recordings.is_public = TRUE')
query = query.joins("LEFT OUTER JOIN music_sessions_user_history ON music_sessions.id = music_sessions_user_history.music_session_id AND music_sessions.fan_access = TRUE")
query = query.group("feeds.id, feeds.recording_id, feeds.music_session_id, feeds.created_at, feeds.updated_at, recordings.id, music_sessions.id")
if sort == 'plays'
diff --git a/ruby/lib/jam_ruby/models/music_session.rb b/ruby/lib/jam_ruby/models/music_session.rb
index 39bb9c932..46c4200b5 100644
--- a/ruby/lib/jam_ruby/models/music_session.rb
+++ b/ruby/lib/jam_ruby/models/music_session.rb
@@ -411,7 +411,7 @@ module JamRuby
def language_description
if self.language.blank?
- self.language = "en"
+ self.language = "eng" # iso-639-3
end
iso639Details = ISO_639.find_by_code(self.language)
@@ -605,6 +605,7 @@ module JamRuby
# same, sorted by score. date seems irrelevant as these are active sessions. sms_init must be called
# first.
def self.sms_query(current_user, options = {})
+ session_id = options[:session_id]
client_id = options[:client_id]
genre = options[:genre]
lang = options[:lang]
@@ -663,6 +664,7 @@ module JamRuby
query = query.limit(limit)
query = query.where("music_sessions.genre_id = ?", genre) unless genre.blank?
query = query.where('music_sessions.language = ?', lang) unless lang.blank?
+ query = query.where('music_sessions.id = ?', session_id) unless session_id.blank?
query = query.where("(description_tsv @@ to_tsquery('jamenglish', ?))", keyword + ':*') unless keyword.blank?
if !day.blank? && !timezone_offset.blank?
@@ -670,9 +672,7 @@ module JamRuby
day = Date.parse(day)
next_day = day + 1
timezone_offset = timezone_offset.to_i
- if timezone_offset == 0
- timezone_offset = '' # no offset to specify in this case
- elsif timezone_offset > 0
+ if timezone_offset > 0
timezone_offset = "+#{timezone_offset}"
end
query = query.where("scheduled_start BETWEEN TIMESTAMP WITH TIME ZONE '#{day} 00:00:00#{timezone_offset}'
diff --git a/ruby/lib/jam_ruby/models/recording.rb b/ruby/lib/jam_ruby/models/recording.rb
index 143aed8f6..c4d5fc08d 100644
--- a/ruby/lib/jam_ruby/models/recording.rb
+++ b/ruby/lib/jam_ruby/models/recording.rb
@@ -5,12 +5,12 @@ module JamRuby
attr_accessible :owner, :owner_id, :band, :band_id, :recorded_tracks_attributes, :mixes_attributes, :claimed_recordings_attributes, :name, :description, :genre, :is_public, :duration, as: :admin
- has_many :claimed_recordings, :class_name => "JamRuby::ClaimedRecording", :inverse_of => :recording, :foreign_key => 'recording_id', :dependent => :destroy
has_many :users, :through => :recorded_tracks, :class_name => "JamRuby::User"
+ has_many :claimed_recordings, :class_name => "JamRuby::ClaimedRecording", :inverse_of => :recording, :foreign_key => 'recording_id', :dependent => :destroy
has_many :mixes, :class_name => "JamRuby::Mix", :inverse_of => :recording, :foreign_key => 'recording_id', :dependent => :destroy
has_many :recorded_tracks, :class_name => "JamRuby::RecordedTrack", :foreign_key => :recording_id, :dependent => :destroy
- has_many :comments, :class_name => "JamRuby::RecordingComment", :foreign_key => "recording_id"
- has_many :likes, :class_name => "JamRuby::RecordingLiker", :foreign_key => "recording_id"
+ has_many :comments, :class_name => "JamRuby::RecordingComment", :foreign_key => "recording_id", :dependent => :destroy
+ has_many :likes, :class_name => "JamRuby::RecordingLiker", :foreign_key => "recording_id", :dependent => :destroy
has_many :plays, :class_name => "JamRuby::PlayablePlay", :as => :playable, :dependent => :destroy
has_one :feed, :class_name => "JamRuby::Feed", :inverse_of => :recording, :foreign_key => 'recording_id', :dependent => :destroy
@@ -128,7 +128,7 @@ module JamRuby
unless self.users.exists?(user)
raise PermissionError, "user was not in this session"
end
- recorded_tracks.where(:user_id=> user.id)
+ recorded_tracks.where(:user_id => user.id)
end
def has_access?(user)
@@ -211,7 +211,7 @@ module JamRuby
# check if all recorded_tracks for this recording are discarded
if recorded_tracks.where('discard = false or discard is NULL').length == 0
- self.all_discarded = true
+ self.all_discarded = true # the feed won't pick this up; also background cleanup will find these and whack them later
self.save(:validate => false)
end
@@ -238,7 +238,7 @@ module JamRuby
.order('recorded_tracks.id')
.where('recorded_tracks.fully_uploaded = TRUE')
.where('recorded_tracks.id > ?', since)
- .where('claimed_recordings.user_id = ?', user).limit(limit).each do |recorded_track|
+ .where('claimed_recordings.user_id = ? AND claimed_recordings.discarded = FALSE', user).limit(limit).each do |recorded_track|
downloads.push(
{
:type => "recorded_track",
@@ -258,7 +258,7 @@ module JamRuby
.order('mixes.id')
.where('mixes.completed_at IS NOT NULL')
.where('mixes.id > ?', since)
- .where('claimed_recordings.user_id = ?', user)
+ .where('claimed_recordings.user_id = ? AND claimed_recordings.discarded = FALSE', user)
.limit(limit).each do |mix|
downloads.push(
{
diff --git a/ruby/lib/jam_ruby/models/region.rb b/ruby/lib/jam_ruby/models/region.rb
index 8a44a4f6d..0aa27ea87 100644
--- a/ruby/lib/jam_ruby/models/region.rb
+++ b/ruby/lib/jam_ruby/models/region.rb
@@ -8,52 +8,17 @@ module JamRuby
end
def self.import_from_region_codes(file)
-
- # File region_codes.csv
- # Format:
- # countrycode,region,regionname
-
- # what this does is replace the contents of the table with the new data.
-
- self.transaction do
- self.connection.execute "delete from #{self.table_name}"
-
- File.open(file, 'r:ISO-8859-1') do |io|
- saved_level = ActiveRecord::Base.logger ? ActiveRecord::Base.logger.level : -1
- count = 0
- errors = 0
- ncols = 3
-
- csv = ::CSV.new(io, {encoding: 'ISO-8859-1', headers: false})
- csv.each do |row|
- raise "file does not have expected number of columns (#{ncols}): #{row.length}" unless row.length == ncols
-
- countrycode = row[0]
- region = row[1]
- regionname = row[2]
-
- if countrycode.length == 2 and region.length == 2 and regionname.length >= 2 and regionname.length <= 64
-
- stmt = "INSERT INTO #{self.table_name} (countrycode, region, regionname) VALUES (#{self.connection.quote(countrycode)}, #{self.connection.quote(region)}, #{self.connection.quote(regionname)})"
- self.connection.execute stmt
- count += 1
-
- if ActiveRecord::Base.logger and ActiveRecord::Base.logger.level < Logger::INFO
- ActiveRecord::Base.logger.debug "... logging updates to #{self.table_name} suspended ..."
- ActiveRecord::Base.logger.level = Logger::INFO
- end
- else
- ActiveRecord::Base.logger.warn("bogus region_codes record '#{countrycode}', '#{region}', '#{regionname}'") if ActiveRecord::Base.logger
- errors += 1
- end
- end
-
- if ActiveRecord::Base.logger
- ActiveRecord::Base.logger.level = saved_level
- ActiveRecord::Base.logger.debug "inserted #{count} records into #{self.table_name}, #{errors} errors"
- end
- end # file
- end # transaction
+ self.delete_all
+ File.open(file, 'r:ISO-8859-1') do |io|
+ csv = ::CSV.new(io, {encoding: 'ISO-8859-1', headers: false})
+ csv.each do |row|
+ rr = Region.new
+ rr.countrycode = row[0]
+ rr.region = row[1]
+ rr.regionname = row[2]
+ rr.save
+ end
+ end
end
end
end
diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb
index 0513e8f68..7ee7c6137 100644
--- a/ruby/spec/factories.rb
+++ b/ruby/spec/factories.rb
@@ -58,7 +58,7 @@ FactoryGirl.define do
legal_terms true
genre JamRuby::Genre.first
band nil
- language 'en'
+ language 'eng'
end
diff --git a/ruby/spec/jam_ruby/models/active_music_session_spec.rb b/ruby/spec/jam_ruby/models/active_music_session_spec.rb
index 79a0992d6..c7d4d276c 100644
--- a/ruby/spec/jam_ruby/models/active_music_session_spec.rb
+++ b/ruby/spec/jam_ruby/models/active_music_session_spec.rb
@@ -403,8 +403,8 @@ describe ActiveMusicSession do
let(:searcher_2) { FactoryGirl.create(:user, last_jam_locidispid: 3, last_jam_audio_latency: 14) }
let(:searcher_conn_2) { FactoryGirl.create(:connection, user: searcher_2, ip_address: '9.9.9.9', locidispid: 3, addr:3) }
- let!(:music_session_1) { FactoryGirl.create(:active_music_session, :creator => creator_1, genre: Genre.find('african'), language: 'en', description: "Bunny Jumps" ) }
- let!(:music_session_2) { FactoryGirl.create(:active_music_session, :creator => creator_2, genre: Genre.find('ambient'), language: 'es', description: "Play with us as we jam to beatles and bunnies") }
+ let!(:music_session_1) { FactoryGirl.create(:active_music_session, :creator => creator_1, genre: Genre.find('african'), language: 'eng', description: "Bunny Jumps" ) }
+ let!(:music_session_2) { FactoryGirl.create(:active_music_session, :creator => creator_2, genre: Genre.find('ambient'), language: 'spa', description: "Play with us as we jam to beatles and bunnies") }
let(:good_network_score) { 20 }
let(:fair_network_score) { 30 }
@@ -459,14 +459,14 @@ describe ActiveMusicSession do
music_sessions.length.should == 2
# get only english
- music_sessions, user_search = ams(searcher_1, client_id: searcher_conn_1.client_id, lang: 'en')
+ music_sessions, user_search = ams(searcher_1, client_id: searcher_conn_1.client_id, lang: 'eng')
music_sessions.length.should == 1
- music_sessions[0].language.should == 'en'
+ music_sessions[0].language.should == 'eng'
# get only ambient
- music_sessions, user_search = ams(searcher_1, client_id: searcher_conn_1.client_id, lang: 'es')
+ music_sessions, user_search = ams(searcher_1, client_id: searcher_conn_1.client_id, lang: 'spa')
music_sessions.length.should == 1
- music_sessions[0].language.should == 'es'
+ music_sessions[0].language.should == 'spa'
end
it "keyword" do
diff --git a/ruby/spec/jam_ruby/models/music_session_spec.rb b/ruby/spec/jam_ruby/models/music_session_spec.rb
index 89c379190..9176ed777 100644
--- a/ruby/spec/jam_ruby/models/music_session_spec.rb
+++ b/ruby/spec/jam_ruby/models/music_session_spec.rb
@@ -314,8 +314,8 @@ describe MusicSession do
let!(:searcher_2) { FactoryGirl.create(:user, last_jam_locidispid: 3, last_jam_audio_latency: 14) }
let!(:searcher_conn_2) { FactoryGirl.create(:connection, user: searcher_2, ip_address: '9.9.9.9', locidispid: 3, addr:3) }
- let!(:music_session_1) { FactoryGirl.create(:music_session, creator: creator_1, genre: Genre.find('african'), language: 'en', description: "Bunny Jumps") }
- let!(:music_session_2) { FactoryGirl.create(:music_session, creator: creator_2, genre: Genre.find('ambient'), language: 'es', description: "Play with us as we jam to beatles and bunnies") }
+ let!(:music_session_1) { FactoryGirl.create(:music_session, creator: creator_1, genre: Genre.find('african'), language: 'eng', description: "Bunny Jumps") }
+ let!(:music_session_2) { FactoryGirl.create(:music_session, creator: creator_2, genre: Genre.find('ambient'), language: 'spa', description: "Play with us as we jam to beatles and bunnies") }
let!(:music_session_3) { FactoryGirl.create(:music_session, creator: creator_3) }
let(:good_network_score) { 20 }
@@ -399,8 +399,8 @@ describe MusicSession do
let(:searcher_2) { FactoryGirl.create(:user, last_jam_locidispid: 3, last_jam_audio_latency: 14) }
let(:searcher_conn_2) { FactoryGirl.create(:connection, user: searcher_2, ip_address: '9.9.9.9', locidispid: 3, addr:3) }
- let!(:music_session_1) { FactoryGirl.create(:music_session, :creator => creator_1, genre: Genre.find('african'), language: 'en', description: "Bunny Jumps" ) }
- let!(:music_session_2) { FactoryGirl.create(:music_session, :creator => creator_2, genre: Genre.find('ambient'), language: 'es', description: "Play with us as we jam to beatles and bunnies") }
+ let!(:music_session_1) { FactoryGirl.create(:music_session, :creator => creator_1, genre: Genre.find('african'), language: 'eng', description: "Bunny Jumps" ) }
+ let!(:music_session_2) { FactoryGirl.create(:music_session, :creator => creator_2, genre: Genre.find('ambient'), language: 'spa', description: "Play with us as we jam to beatles and bunnies") }
let(:good_network_score) { 20 }
let(:fair_network_score) { 30 }
@@ -448,14 +448,14 @@ describe MusicSession do
music_sessions.length.should == 2
# get only english
- music_sessions, user_search = sms(searcher_1, client_id: searcher_conn_1.client_id, lang: 'en')
+ music_sessions, user_search = sms(searcher_1, client_id: searcher_conn_1.client_id, lang: 'eng')
music_sessions.length.should == 1
- music_sessions[0].language.should == 'en'
+ music_sessions[0].language.should == 'eng'
# get only ambient
- music_sessions, user_search = sms(searcher_1, client_id: searcher_conn_1.client_id, lang: 'es')
+ music_sessions, user_search = sms(searcher_1, client_id: searcher_conn_1.client_id, lang: 'spa')
music_sessions.length.should == 1
- music_sessions[0].language.should == 'es'
+ music_sessions[0].language.should == 'spa'
end
it "keyword" do
@@ -498,7 +498,6 @@ describe MusicSession do
music_sessions, user_search = sms(searcher_1, client_id: searcher_conn_1.client_id, day: (Date.today + 1).to_s, timezone_offset: DateTime.now.offset.numerator)
music_sessions.length.should == 1
music_sessions[0].should == music_session_1
-
end
end
end
diff --git a/ruby/spec/jam_ruby/models/recording_spec.rb b/ruby/spec/jam_ruby/models/recording_spec.rb
index d61a1eecd..0bc9702e0 100644
--- a/ruby/spec/jam_ruby/models/recording_spec.rb
+++ b/ruby/spec/jam_ruby/models/recording_spec.rb
@@ -188,8 +188,12 @@ describe Recording do
expect { @claimed_recordign.discard(@user2) }.to raise_error
@claimed_recording = @recording.claim(@user2, "name2", "description2", @genre, true)
@claimed_recording.discard(@user2)
+ @claimed_recording.reload
+ @claimed_recording.discarded.should == true
+ @recording.recorded_tracks_for_user(@user2)[0].discard.should == true
@recording.reload
- @recording.claimed_recordings.length.should == 1
+ @recording.claimed_recordings.length.should == 2
+ @recording.all_discarded.should == false
end
it "should destroy the entire recording if there was only one claimed_recording which is discarded" do
@@ -199,8 +203,10 @@ describe Recording do
@genre = FactoryGirl.create(:genre)
@claimed_recording = @recording.claim(@user, "name", "description", @genre, true)
@claimed_recording.discard(@user)
- expect { Recording.find(@recording.id) }.to raise_error
- expect { ClaimedRecording.find(@claimed_recording.id) }.to raise_error
+ @claimed_recording.reload
+ @claimed_recording.discarded.should == true
+ @claimed_recording.recording.all_discarded.should == true
+ @recording.recorded_tracks_for_user(@user)[0].discard.should == true
end
it "should use the since parameter when restricting uploads" do
diff --git a/web/app/assets/javascripts/accounts_profile.js b/web/app/assets/javascripts/accounts_profile.js
index 32b72de65..bb5a0f48b 100644
--- a/web/app/assets/javascripts/accounts_profile.js
+++ b/web/app/assets/javascripts/accounts_profile.js
@@ -206,8 +206,8 @@
if(!region) return;
var option = $(nilOptionStr)
- option.text(region)
- option.attr("value", region)
+ option.text(region['name'])
+ option.attr("value", region['region'])
regionSelect.append(option)
})
@@ -431,6 +431,7 @@
var cityElement = getCityElement();
updateRegionList(selectedCountry, getRegionElement());
+ updateCityList(selectedCountry, null, cityElement);
}
function updateRegionList(selectedCountry, regionElement) {
@@ -445,7 +446,10 @@
api.getRegions({ country: selectedCountry })
.done(getRegionsDone)
- .fail(app.ajaxError)
+ .error(function(err) {
+ regionElement.children().remove()
+ regionElement.append($(nilOptionStr).text(nilOptionText))
+ })
.always(function () {
loadingRegionsData = false;
})
@@ -470,14 +474,18 @@
api.getCities({ country: selectedCountry, region: selectedRegion })
.done(getCitiesDone)
- .fail(app.ajaxError)
+ .error(function(err) {
+ cityElement.children().remove()
+ cityElement.append($(nilOptionStr).text(nilOptionText))
+ })
.always(function () {
loadingCitiesData = false;
})
}
else {
- cityElement.children().remove()
- cityElement.append($(nilOptionStr).text(nilOptionText))
+ cityElement.children().remove();
+ cityElement.append($(nilOptionStr).text(nilOptionText));
+ context.JK.dropdown(cityElement);
}
}
diff --git a/web/app/assets/javascripts/band_setup.js b/web/app/assets/javascripts/band_setup.js
index 2860c3ed1..a5120f5cb 100644
--- a/web/app/assets/javascripts/band_setup.js
+++ b/web/app/assets/javascripts/band_setup.js
@@ -309,19 +309,22 @@
$.each(response["regions"], function (index, region) {
if (!region) return;
var option = $(nilOptionStr);
- option.text(region);
- option.attr("value", region);
+ option.text(region['name']);
+ option.attr("value", region['region']);
- if (initialRegion === region) {
+ if (initialRegion === region['region']) {
option.attr("selected", "selected");
}
$region.append(option);
});
-
context.JK.dropdown($region);
-
+ if (onRegionsLoaded) {
+ onRegionsLoaded();
+ }
+ }).error(function(err) {
+ context.JK.dropdown($region);
if (onRegionsLoaded) {
onRegionsLoaded();
}
@@ -338,6 +341,7 @@
var nilOption = $(nilOptionStr);
nilOption.text(nilOptionText);
$city.append(nilOption);
+ nilOption.attr('selected','selected');
if (selectedCountry && selectedRegion) {
rest.getCities({'country': selectedCountry, 'region': selectedRegion}).done(function (response) {
@@ -355,9 +359,13 @@
});
context.JK.dropdown($city);
+ }).error(function(err) {
+ context.JK.dropdown($city);
});
+ } else {
+ context.JK.dropdown($city);
}
- }
+ }
function addInvitation(value, data) {
if ($('#selected-band-invitees div[user-id=' + data + ']').length === 0) {
diff --git a/web/app/assets/javascripts/banner.js b/web/app/assets/javascripts/banner.js
index 7eda9fa0f..99e0ddfbd 100644
--- a/web/app/assets/javascripts/banner.js
+++ b/web/app/assets/javascripts/banner.js
@@ -9,6 +9,9 @@
var self = this;
var logger = context.JK.logger;
var $banner = null;
+ var $closeBtn = null;
+ var $yesBtn = null;
+ var $noBtn = null;
// you can also do
// * showAlert('title', 'text')
@@ -26,6 +29,19 @@
return show(options);
}
+ function showYesNo(options) {
+ if (typeof options == 'string' || options instanceof String) {
+ if(arguments.length == 2) {
+ options = {title: options, html:arguments[1]}
+ }
+ else {
+ options = {html:options};
+ }
+ }
+ options.type = 'yes_no'
+ return show(options);
+ }
+
// responsible for updating the contents of the update dialog
// as well as registering for any event handlers
function show(options) {
@@ -33,7 +49,12 @@
var html = options.html;
if(!options.title) {
- options.title = 'alert'
+ if(options.type == 'alert') {
+ options.title = 'alert'
+ }
+ else if(options.type == 'yes_no') {
+ options.title = 'please confirm';
+ }
}
var $h1 = $banner.find('h1');
@@ -50,11 +71,18 @@
throw "unable to show banner for empty message";
}
- var $closeBtn = $banner.find('.close-btn');
if((options.type == "alert" && !options.buttons) || options.close) {
- $closeBtn.show().click(function() {
+ var closeButtonText = 'CLOSE';
+ if(options.close !== null && typeof options.close == 'object') {
+ // extra styling options for close button
+ if(options.close.name) {
+ closeButtonText = options.close.name;
+ }
+ }
+
+ $closeBtn.show().text(closeButtonText).unbind('click').click(function() {
hide();
return false;
});
@@ -63,6 +91,27 @@
$closeBtn.hide();
}
+ if(options.type == "yes_no") {
+ $yesBtn.show().unbind('click').click(function() {
+ if(options.yes) {
+ options.yes();
+ }
+ hide();
+ return false;
+ })
+ $noBtn.show().unbind('click').click(function() {
+ if(options.no) {
+ options.no();
+ }
+ hide();
+ return false;
+ })
+ }
+ else {
+ $yesBtn.hide();
+ $noBtn.hide();
+ }
+
if(options.buttons) {
var $buttons = $banner.find('.buttons')
context._.each(options.buttons, function(button) {
@@ -86,6 +135,7 @@
return newContent;
}
+
function hide() {
$banner.hide();
$banner.find('.user-btn').remove();
@@ -96,6 +146,10 @@
function initialize() {
$banner = $('#banner');
+
+ $closeBtn = $banner.find('.close-btn');
+ $yesBtn = $banner.find('.yes-btn');
+ $noBtn = $banner.find('.no-btn');
return self;
}
@@ -104,6 +158,7 @@
initialize: initialize,
show: show,
showAlert: showAlert,
+ showYesNo: showYesNo,// shows Yes and Cancel button (confirmation dialog)
hide: hide
}
diff --git a/web/app/assets/javascripts/commentDialog.js b/web/app/assets/javascripts/commentDialog.js
index 263ea7d6d..794ee141b 100644
--- a/web/app/assets/javascripts/commentDialog.js
+++ b/web/app/assets/javascripts/commentDialog.js
@@ -122,7 +122,7 @@
}
function showDialog() {
- app.layout.showDialog('comment-dialog');
+ return app.layout.showDialog('comment-dialog');
}
function initialize() {
diff --git a/web/app/assets/javascripts/editRecordingDialog.js b/web/app/assets/javascripts/editRecordingDialog.js
new file mode 100644
index 000000000..23c1d4919
--- /dev/null
+++ b/web/app/assets/javascripts/editRecordingDialog.js
@@ -0,0 +1,170 @@
+(function (context, $) {
+
+ "use strict";
+ context.JK = context.JK || {};
+ context.JK.EditRecordingDialog = function (app) {
+ var logger = context.JK.logger;
+ var rest = context.JK.Rest();
+ var claimedRecordingId = null;
+ var $dialog = null;
+ var $form = null;
+ var $name = null;
+ var $description = null;
+ var $genre = null;
+ var $isPublic = null;
+ var $cancelBtn = null;
+ var $saveBtn = null;
+ var $deleteBtn = null;
+
+ var updating = false;
+ var deleting = false;
+
+ function resetForm() {
+
+ // remove all display errors
+ $dialog.find('.error-text').remove()
+ $dialog.find('.error').removeClass("error")
+ }
+
+ function beforeShow(args) {
+
+ claimedRecordingId = args.d1;
+
+ if(!claimedRecordingId) throw "claimedRecordingId must be specified";
+
+ resetForm();
+
+ rest.getClaimedRecording(claimedRecordingId)
+ .done(function(data) {
+ var name = data.name;
+ var description = data.description;
+ var is_public = data.is_public;
+ var genre_id = data.genre_id;
+
+ context.JK.GenreSelectorHelper.setSelectedGenres($genre.parent(), [genre_id]);
+ $name.val(name);
+ $description.val(description);
+ if(is_public) {
+ $isPublic.attr('checked', 'checked').iCheck('check')
+ }
+ else {
+ $isPublic.removeAttr('checked').iCheck('uncheck')
+ }
+ })
+ .fail(app.ajaxError)
+ }
+
+ function afterHide() {
+
+ }
+
+ function attemptUpdate() {
+ if(updating) return;
+
+ updating = true;
+ var name = $name.val();
+ var description = $description.val();
+ var genre = $genre.val();
+ var is_public = $isPublic.is(':checked');
+
+ rest.updateClaimedRecording({id: claimedRecordingId, name: name, description: description, is_public: is_public, genre: genre })
+ .done(function(updated) {
+ resetForm();
+ $dialog.triggerHandler('recording_updated', {id: claimedRecordingId, name: name, description: description, is_public: is_public, genre: genre})
+ app.layout.closeDialog('edit-recording');
+ })
+ .fail(function(jqXHR) {
+
+ if(jqXHR.status = 422) {
+ // highlight fields in error
+
+ resetForm();
+
+ var errors = JSON.parse(jqXHR.responseText);
+
+ var $name_errors = context.JK.format_errors('name', errors);
+ if ($name_errors) $name.closest('div.field').addClass('error').end().after($name_errors);
+
+ var $description_errors = context.JK.format_errors('description', errors);
+ if ($description_errors) $description.closest('div.field').addClass('error').end().after($description_errors);
+
+ var $genre_errors = context.JK.format_errors('genre', errors);
+ if ($genre_errors) $genre.closest('div.field').addClass('error').end().after($genre_errors);
+
+ var $is_public_errors = context.JK.format_errors('is_public', errors);
+ if ($is_public_errors) $isPublic.closest('div.field').addClass('error').end().after($is_public_errors);
+
+ }
+ else {
+ app.ajaxError(arguments);
+ }
+ })
+ .always(function() {
+ updating = false;
+ })
+ }
+
+ function attemptDelete() {
+ if(deleting) return;
+
+ deleting = true;
+ context.JK.Banner.showYesNo({
+ title: "Confirm Deletion",
+ html: "Are you sure you want to delete this recording?",
+ yes: function() {
+ rest.deleteClaimedRecording(claimedRecordingId)
+ .done(function() {
+ $dialog.triggerHandler('recording_deleted', {id: claimedRecordingId});
+ app.layout.closeDialog('edit-recording');
+ })
+ .fail(app.ajaxError)
+ .always(function() {
+ deleting = false;
+ })
+ },
+ no : function() {
+ context.JK.Banner.hide();
+ deleting = false;
+ }
+ })
+ }
+
+ function cancel() {
+ app.layout.closeDialog('edit-recording');
+ }
+
+ function events() {
+ $saveBtn.click(attemptUpdate);
+ $deleteBtn.click(attemptDelete);
+ $cancelBtn.click(cancel)
+ $form.submit(false);
+ }
+
+
+ function initialize() {
+ var dialogBindings = {
+ 'beforeShow': beforeShow,
+ 'afterHide': afterHide
+ };
+
+ app.bindDialog('edit-recording', dialogBindings);
+
+ $dialog = $('#edit-recording-dialog');
+ $form = $dialog.find('form');
+ $cancelBtn = $dialog.find('.cancel-btn');
+ $saveBtn = $dialog.find('.save-btn');
+ $deleteBtn = $dialog.find('.delete-btn');
+ $name = $dialog.find('input[name="name"]');
+ $description = $dialog.find('textarea[name="description"]');
+ $genre = $dialog.find('select[name=genre]');
+ $isPublic = $dialog.find('input[name=is_public]');
+
+ events();
+
+ context.JK.GenreSelectorHelper.render($genre.parent());
+ context.JK.checkbox($isPublic);
+ };
+
+ this.initialize = initialize;
+ }
+})(window, jQuery);
\ No newline at end of file
diff --git a/web/app/assets/javascripts/feed.js b/web/app/assets/javascripts/feed.js
index da4c107cb..41027b898 100644
--- a/web/app/assets/javascripts/feed.js
+++ b/web/app/assets/javascripts/feed.js
@@ -5,407 +5,15 @@
context.JK.FeedScreen = function(app) {
var logger = context.JK.logger;
- var rest = new context.JK.Rest();
- var ui = new context.JK.UIHelper(JK.app);
- var currentQuery = null;
- var currentPage = 0;
- var LIMIT = 20;
- var $screen = null;
- var $next = null;
- var $scroller = null;
- var $content = null;
- var $noMoreFeeds = null;
- var $refresh = null;
- var $sortFeedBy = null;
- var $includeDate = null;
- var $includeType = null;
- var next = null;
- function defaultQuery() {
- var query = { limit:LIMIT, page:currentPage};
-
- if(next) {
- query.since = next;
- }
-
- return query;
- }
-
- function buildQuery() {
- currentQuery = defaultQuery();
-
- // specify search criteria based on form
- currentQuery.sort = $sortFeedBy.val();
- currentQuery.time_range = $includeDate.val();
- currentQuery.type = $includeType.val();
-
- return currentQuery;
- }
+ var feed = null;
function beforeShow(data) {
}
function afterShow(data) {
- refresh();
- }
-
- function clearResults() {
- currentPage = 0;
- $content.empty(); // TODO: do we need to delete audio elements?
- $noMoreFeeds.hide();
- next = null;
- }
-
- function handleFeedResponse(response) {
- next = response.next;
-
- renderFeeds(response);
-
- if(response.next == null) {
- // if we less results than asked for, end searching
- $scroller.infinitescroll('pause');
- logger.debug("end of feeds")
-
- if(currentPage > 0) {
- $noMoreFeeds.show();
- // there are bugs with infinitescroll not removing the 'loading'.
- // it's most noticeable at the end of the list, so whack all such entries
- $('.infinite-scroll-loader').remove();
- }
- }
- else {
- currentPage++;
- buildQuery();
- registerInfiniteScroll();
- }
- }
-
- function refresh() {
-
- clearResults();
-
- currentQuery = buildQuery();
- rest.getFeeds(currentQuery)
- .done(function(response) {
- handleFeedResponse(response);
- })
- .fail(function(jqXHR) {
- app.notifyServerError(jqXHR, 'Feed Unavailable')
- })
- }
-
- function registerInfiniteScroll() {
-
- $scroller.infinitescroll({
- behavior: 'local',
- navSelector: '#feedScreen .btn-next-pager',
- nextSelector: '#feedScreen .btn-next-pager',
- binder: $scroller,
- dataType: 'json',
- appendCallback: false,
- prefill: false,
- bufferPx:100,
- loading: {
- msg: $('
Loading ...
'),
- img: '/assets/shared/spinner.gif'
- },
- path: function(page) {
- return '/api/feeds?' + $.param(buildQuery());
- }
- },function(json, opts) {
- handleFeedResponse(json);
- });
- $scroller.infinitescroll('resume');
- }
-
-
- function toggleSessionDetails() {
- var $detailsLink = $(this);
- var $feedItem = $detailsLink.closest('.feed-entry');
- var $musicians = $feedItem.find('.musician-detail');
- var $description = $feedItem.find('.description');
- var toggledOpen = $detailsLink.data('toggledOpen');
-
- if(toggledOpen) {
- $feedItem.css('height', $feedItem.height() + 'px')
- $feedItem.animate({'height': $feedItem.data('original-max-height')}).promise().done(function() {
- $feedItem.css('height', 'auto').css('max-height', $feedItem.data('original-max-height'));
-
- $musicians.hide();
- $description.css('height', $description.data('original-height'));
- $description.dotdotdot();
- });
- }
- else {
- $description.trigger('destroy.dot');
- $description.data('original-height', $description.css('height')).css('height', 'auto');
- $musicians.show();
- $feedItem.animate({'max-height': '1000px'});
- }
-
- toggledOpen = !toggledOpen;
- $detailsLink.data('toggledOpen', toggledOpen);
- return false;
- }
-
- function startSessionPlay($feedItem) {
- var img = $('.play-icon', $feedItem);
- var $controls = $feedItem.find('.session-controls');
- img.attr('src', '/assets/content/icon_pausebutton.png');
- $controls.trigger('play.listenBroadcast');
- $feedItem.data('playing', true);
- }
-
- function stopSessionPlay($feedItem) {
- var img = $('.play-icon', $feedItem);
- var $controls = $feedItem.find('.session-controls');
- img.attr('src', '/assets/content/icon_playbutton.png');
- $controls.trigger('pause.listenBroadcast');
- $feedItem.data('playing', false);
- }
-
- function toggleSessionPlay() {
- var $playLink = $(this);
- var $feedItem = $playLink.closest('.feed-entry');
-
- var $status = $feedItem.find('.session-status')
- var playing = $feedItem.data('playing');
-
- if(playing) {
- $status.text('SESSION IN PROGRESS');
- stopSessionPlay($feedItem);
- }
- else {
- startSessionPlay($feedItem);
- }
- return false;
- }
-
- function stateChangeSession(e, data) {
- var $controls = data.element;
- var $feedItem = $controls.closest('.feed-entry');
- var $status = $feedItem.find('.session-status');
-
- if(data.displayText) $status.text(data.displayText);
-
- if(data.isEnd) stopSessionPlay();
-
- if(data.isSessionOver) {
- $controls.removeClass('inprogress').addClass('ended')
- }
- }
-
- function startRecordingPlay($feedItem) {
- var img = $('.play-icon', $feedItem);
- var $controls = $feedItem.find('.recording-controls');
- img.attr('src', '/assets/content/icon_pausebutton.png');
- $controls.trigger('play.listenRecording');
- $feedItem.data('playing', true);
- }
-
- function stopRecordingPlay($feedItem) {
- var img = $('.play-icon', $feedItem);
- var $controls = $feedItem.find('.recording-controls');
- img.attr('src', '/assets/content/icon_playbutton.png');
- $controls.trigger('pause.listenRecording');
- $feedItem.data('playing', false);
- }
-
- function toggleRecordingPlay() {
-
- var $playLink = $(this);
- var $feedItem = $playLink.closest('.feed-entry');
- var playing = $feedItem.data('playing');
-
- if(playing) {
- stopRecordingPlay($feedItem);
- }
- else {
- startRecordingPlay($feedItem);
- }
- return false;
- }
-
-
- function stateChangeRecording(e, data) {
- var $controls = data.element;
- var $feedItem = $controls.closest('.feed-entry');
-
- var $sliderBar = $('.recording-position', $feedItem);
- var $statusBar = $('.recording-status', $feedItem);
- var $currentTime = $('.recording-current', $feedItem);
- var $status = $('.status-text', $feedItem);
- var $playButton = $('.play-button', $feedItem);
-
- if(data.isEnd) stopRecordingPlay($feedItem);
- if(data.isError) {
- $sliderBar.hide();
- $playButton.hide();
- $currentTime.hide();
- $statusBar.show();
- $status.text(data.displayText);
- }
- }
-
- function toggleRecordingDetails() {
- var $detailsLink = $(this);
- var $feedItem = $detailsLink.closest('.feed-entry');
- var $musicians = $feedItem.find('.musician-detail');
- var $description = $feedItem.find('.description');
- var $name = $feedItem.find('.name');
- var toggledOpen = $detailsLink.data('toggledOpen');
-
- if(toggledOpen) {
- $feedItem.css('height', $feedItem.height() + 'px')
- $feedItem.animate({'height': $feedItem.data('original-max-height')}).promise().done(function() {
- $feedItem.css('height', 'auto').css('max-height', $feedItem.data('original-max-height'));
-
- $musicians.hide();
- $description.css('height', $description.data('original-height'));
- $description.dotdotdot();
- $name.css('height', $name.data('original-height'));
- $name.dotdotdot();
- });
- }
- else {
- $description.trigger('destroy.dot');
- $description.data('original-height', $description.css('height')).css('height', 'auto');
- $name.trigger('destroy.dot');
- $name.data('original-height', $name.css('height')).css('height', 'auto');
- $musicians.show();
- $feedItem.animate({'max-height': '1000px'});
- }
-
- toggledOpen = !toggledOpen;
- $detailsLink.data('toggledOpen', toggledOpen);
-
- return false;
- }
-
- function renderFeeds(feeds) {
-
- $.each(feeds.entries, function(i, feed) {
- if(feed.type == 'music_session') {
- var options = {
- feed_item: feed,
- status_class: feed['is_over?'] ? 'ended' : 'inprogress',
- mount_class: feed['has_mount?'] ? 'has-mount' : 'no-mount'
- }
- var $feedItem = $(context._.template($('#template-feed-music-session').html(), options, {variable: 'data'}));
- var $controls = $feedItem.find('.session-controls');
-
- // do everything we can before we attach the item to the page
- $('.timeago', $feedItem).timeago();
- context.JK.prettyPrintElements($('.duration', $feedItem).show());
- context.JK.setInstrumentAssetPath($('.instrument-icon', $feedItem));
- $('.details', $feedItem).click(toggleSessionDetails);
- $('.details-arrow', $feedItem).click(toggleSessionDetails);
- $('.play-button', $feedItem).click(toggleSessionPlay);
-
- if (!feed.session_removed_at)
- {
- $('.btn-share', $feedItem).click(function() {
- ui.launchShareDialog(feed.id, 'session');
- });
- }
- else {
- $('.btn-share', $feedItem).hide();
- }
-
- $('.btn-comment', $feedItem).click(function() {
- ui.launchCommentDialog({
- session_id: feed.id,
- entity_type: 'session'
- });
- });
-
- $('.btn-like', $feedItem).click(function() {
- ui.addSessionLike(feed.id, JK.currentUserId, $('.likes', $feedItem), $('.btn-like', $feedItem))
- });
-
- // put the feed item on the page
- renderFeed($feedItem);
-
- // these routines need the item to have height to work (must be after renderFeed)
- $controls.listenBroadcast();
- $controls.bind('statechange.listenBroadcast', stateChangeSession);
- $('.dotdotdot', $feedItem).dotdotdot();
- $feedItem.data('original-max-height', $feedItem.css('height'));
- context.JK.bindHoverEvents($feedItem);
- context.JK.bindProfileClickEvents($feedItem);
- }
- else if(feed.type == 'recording') {
- if(feed.claimed_recordings.length == 0) {
- logger.error("a recording in the feed should always have one claimed_recording")
- return;
- }
- var options = {
- feed_item: feed,
- candidate_claimed_recording: feed.claimed_recordings[0],
- mix_class: feed['has_mix?'] ? 'has-mix' : 'no-mix',
- }
-
- var $feedItem = $(context._.template($('#template-feed-recording').html(), options, {variable: 'data'}));
- var $controls = $feedItem.find('.recording-controls');
-
- $('.timeago', $feedItem).timeago();
- context.JK.prettyPrintElements($('.duration', $feedItem));
- context.JK.setInstrumentAssetPath($('.instrument-icon', $feedItem));
- $('.details', $feedItem).click(toggleRecordingDetails);
- $('.details-arrow', $feedItem).click(toggleRecordingDetails);
- $('.play-button', $feedItem).click(toggleRecordingPlay);
-
- $('.btn-share', $feedItem).click(function() {
- ui.launchShareDialog(options.candidate_claimed_recording.id, 'recording');
- });
-
- $('.btn-comment', $feedItem).click(function() {
- ui.launchCommentDialog({
- recording_id: feed.id,
- claimed_recording_id: options.candidate_claimed_recording.id,
- entity_type: 'recording'
- });
- });
-
- $('.btn-like', $feedItem).click(function() {
- ui.addRecordingLike(feed.id, options.candidate_claimed_recording.id, JK.currentUserId, $('.likes', $feedItem), $('.btn-like', $feedItem));
- });
-
- // put the feed item on the page
- renderFeed($feedItem);
-
- // these routines need the item to have height to work (must be after renderFeed)
- $controls.listenRecording({recordingId: feed.id, claimedRecordingId: options.candidate_claimed_recording.id, sliderSelector:'.recording-slider', sliderBarSelector: '.recording-playback', currentTimeSelector:'.recording-current'});
- $controls.bind('statechange.listenRecording', stateChangeRecording);
- $('.dotdotdot', $feedItem).dotdotdot();
- $feedItem.data('original-max-height', $feedItem.css('height'));
- context.JK.bindHoverEvents($feedItem);
- context.JK.bindProfileClickEvents($feedItem);
- }
- else {
- logger.warn("skipping feed type: " + feed.type);
- }
-
- context.JK.bindProfileClickEvents();
- });
- }
-
- function renderFeed(feed) {
- $content.append(feed);
- }
-
- function search() {
- logger.debug("Searching for feeds...");
- refresh();
- return false;
- }
-
- function events() {
- $refresh.on("click", search);
- $sortFeedBy.on('change', search);
- $includeDate.on('change', search);
- $includeType.on('change', search);
+ feed.refresh();
}
function initialize() {
@@ -415,21 +23,17 @@
};
app.bindScreen('feed', screenBindings);
- $screen = $('[layout-id="feed"]');
- $scroller = $screen.find('.content-body-scroller');
- $content = $screen.find('.feed-content');
- $noMoreFeeds = $('#end-of-feeds-list');
- $refresh = $screen.find('#btn-refresh-feed');
- $sortFeedBy = $screen.find('#feed_order_by');
- $includeDate = $screen.find('#feed_date');
- $includeType = $screen.find('#feed_show');
+ var $screen = $('[layout-id="feed"]');
+ var $scroller = $screen.find('.content-body-scroller');
+ var $content = $screen.find('.feed-content');
+ var $noMoreFeeds = $('#end-of-feeds-list');
+ var $refresh = $screen.find('.btn-refresh-entries');
+ var $sortFeedBy = $screen.find('#feed_order_by');
+ var $includeDate = $screen.find('#feed_date');
+ var $includeType = $screen.find('#feed_show');
- // set default search criteria
- $sortFeedBy.val('date')
- $includeDate.val('month')
- $includeType.val('all')
-
- events();
+ feed = new context.JK.Feed(app);
+ feed.initialize($screen, $scroller, $content, $noMoreFeeds, $refresh, $sortFeedBy, $includeDate, $includeType);
}
this.initialize = initialize;
diff --git a/web/app/assets/javascripts/feedHelper.js b/web/app/assets/javascripts/feedHelper.js
new file mode 100644
index 000000000..fd7b3eb73
--- /dev/null
+++ b/web/app/assets/javascripts/feedHelper.js
@@ -0,0 +1,586 @@
+(function (context, $) {
+
+ "use strict";
+
+ context.JK = context.JK || {};
+ context.JK.Feed = function (app) {
+
+ var logger = context.JK.logger;
+ var rest = new context.JK.Rest();
+ var EVENTS = context.JK.EVENTS;
+ var ui = new context.JK.UIHelper(JK.app);
+ var userId = null;
+ var currentQuery = null;
+ var currentPage = 0;
+ var LIMIT = 20;
+ var $next = null;
+ var $screen = null;
+ var $scroller = null;
+ var $content = null;
+ var $noMoreFeeds = null;
+ var $refresh = null;
+ var $sortFeedBy = null;
+ var $includeDate = null;
+ var $includeType = null;
+ var next = null;
+
+ function defaultQuery() {
+ var query = { limit:LIMIT, page:currentPage};
+
+ if(next) {
+ query.since = next;
+ }
+
+ if(userId) {
+ query.user = userId;
+ }
+
+ return query;
+ }
+
+ function buildQuery() {
+ currentQuery = defaultQuery();
+
+ // specify search criteria based on form
+ currentQuery.sort = $sortFeedBy.val();
+ currentQuery.time_range = $includeDate.val();
+ currentQuery.type = $includeType.val();
+
+ return currentQuery;
+ }
+
+
+ function clearResults() {
+ currentPage = 0;
+ $content.empty(); // TODO: do we need to delete audio elements?
+ $noMoreFeeds.hide();
+ next = null;
+ }
+
+ function handleFeedResponse(response) {
+ next = response.next;
+
+ renderFeeds(response);
+
+ if(response.next == null) {
+ // if we less results than asked for, end searching
+ $scroller.infinitescroll('pause');
+ logger.debug("end of feeds")
+
+ if(currentPage == 0 && response.entries.length == 0) {
+ $content.append("
This user has no history.
") ;
+ }
+
+ if(currentPage > 0) {
+ $noMoreFeeds.show();
+ // there are bugs with infinitescroll not removing the 'loading'.
+ // it's most noticeable at the end of the list, so whack all such entries
+ $('.infinite-scroll-loader').remove();
+ }
+ }
+ else {
+ currentPage++;
+ buildQuery();
+ registerInfiniteScroll();
+ }
+ }
+
+ function setUser(_userId) {
+ userId = _userId;
+ }
+
+ function refresh() {
+
+ clearResults();
+
+ currentQuery = buildQuery();
+ rest.getFeeds(currentQuery)
+ .done(function(response) {
+ handleFeedResponse(response);
+ })
+ .fail(function(jqXHR) {
+ app.notifyServerError(jqXHR, 'Feed Unavailable')
+ })
+ }
+
+ function registerInfiniteScroll() {
+
+ $scroller.infinitescroll({
+ behavior: 'local',
+ navSelector: '#feedScreen .btn-next-pager',
+ nextSelector: '#feedScreen .btn-next-pager',
+ binder: $scroller,
+ dataType: 'json',
+ appendCallback: false,
+ prefill: false,
+ bufferPx:100,
+ loading: {
+ msg: $('
Loading ...
'),
+ img: '/assets/shared/spinner.gif'
+ },
+ path: function(page) {
+ return '/api/feeds?' + $.param(buildQuery());
+ }
+ },function(json, opts) {
+ handleFeedResponse(json);
+ });
+ $scroller.infinitescroll('resume');
+ }
+
+
+ function toggleSessionDetails() {
+ var $detailsLink = $(this);
+ var $feedItem = $detailsLink.closest('.feed-entry');
+ var $musicians = $feedItem.find('.musician-detail');
+ var $description = $feedItem.find('.description');
+ var toggledOpen = $detailsLink.data('toggledOpen');
+
+ if(toggledOpen) {
+ $feedItem.css('height', $feedItem.height() + 'px')
+ $feedItem.animate({'height': $feedItem.data('original-max-height')}).promise().done(function() {
+ $feedItem.css('height', 'auto').css('max-height', $feedItem.data('original-max-height'));
+
+ $musicians.hide();
+ $description.css('height', $description.data('original-height'));
+ $description.dotdotdot();
+ });
+ }
+ else {
+ $description.trigger('destroy.dot');
+ $description.data('original-height', $description.css('height')).css('height', 'auto');
+ $musicians.show();
+ $feedItem.animate({'max-height': '1000px'});
+ }
+
+ toggledOpen = !toggledOpen;
+ $detailsLink.data('toggledOpen', toggledOpen);
+ return false;
+ }
+
+ function startSessionPlay($feedItem) {
+ var img = $('.play-icon', $feedItem);
+ var $controls = $feedItem.find('.session-controls');
+ img.attr('src', '/assets/content/icon_pausebutton.png');
+ $controls.trigger('play.listenBroadcast');
+ $feedItem.data('playing', true);
+ }
+
+ function stopSessionPlay($feedItem) {
+ var img = $('.play-icon', $feedItem);
+ var $controls = $feedItem.find('.session-controls');
+ img.attr('src', '/assets/content/icon_playbutton.png');
+ $controls.trigger('pause.listenBroadcast');
+ $feedItem.data('playing', false);
+ }
+
+ function toggleSessionPlay() {
+ var $playLink = $(this);
+ var $feedItem = $playLink.closest('.feed-entry');
+
+ var $status = $feedItem.find('.session-status')
+ var playing = $feedItem.data('playing');
+
+ if(playing) {
+ $status.text('SESSION IN PROGRESS');
+ stopSessionPlay($feedItem);
+ }
+ else {
+ startSessionPlay($feedItem);
+ }
+ return false;
+ }
+
+ function stateChangeSession(e, data) {
+ var $controls = data.element;
+ var $feedItem = $controls.closest('.feed-entry');
+ var $status = $feedItem.find('.session-status');
+
+ if(data.displayText) $status.text(data.displayText);
+
+ if(data.isEnd) stopSessionPlay();
+
+ if(data.isSessionOver) {
+ $controls.removeClass('inprogress').addClass('ended')
+ }
+ }
+
+ function startRecordingPlay($feedItem) {
+ var img = $('.play-icon', $feedItem);
+ var $controls = $feedItem.find('.recording-controls');
+ img.attr('src', '/assets/content/icon_pausebutton.png');
+ $controls.trigger('play.listenRecording');
+ $feedItem.data('playing', true);
+ }
+
+ function stopRecordingPlay($feedItem) {
+ var img = $('.play-icon', $feedItem);
+ var $controls = $feedItem.find('.recording-controls');
+ img.attr('src', '/assets/content/icon_playbutton.png');
+ $controls.trigger('pause.listenRecording');
+ $feedItem.data('playing', false);
+ }
+
+ function toggleRecordingPlay() {
+
+ var $playLink = $(this);
+ var $feedItem = $playLink.closest('.feed-entry');
+ var playing = $feedItem.data('playing');
+
+ if(playing) {
+ stopRecordingPlay($feedItem);
+ }
+ else {
+ startRecordingPlay($feedItem);
+ }
+ return false;
+ }
+
+ function isOwner() {
+ return userId == context.JK.currentUserId;
+ }
+
+ function obtainCandidate(recording) {
+ if(isOwner()) {
+ var candidate = null;
+ context._.each(recording.claimed_recordings, function(claimedRecording) {
+ if(claimedRecording.user_id == context.JK.currentUserId) {
+ candidate = claimedRecording;
+ return false;
+ }
+ })
+
+ if(!candidate) throw "unable to find candidate claimed recording, yet we can see this recording. server error..."
+ return candidate;
+ }
+ else {
+ return recording.claimed_recordings[0]
+ }
+ }
+
+ function toggleOpen($feedItem, $name, $description, $musicians) {
+ $description.trigger('destroy.dot');
+ $description.data('original-height', $description.css('height')).css('height', 'auto');
+ $name.trigger('destroy.dot');
+ $name.data('original-height', $name.css('height')).css('height', 'auto');
+ $musicians.show();
+ $feedItem.animate({'max-height': '1000px'});
+ }
+
+ function toggleClose($feedItem, $name, $description, $musicians, immediate) {
+ $feedItem.css('height', $feedItem.height() + 'px')
+ $feedItem.animate({'height': $feedItem.data('original-max-height')}, immediate ? 0 : 400).promise().done(function() {
+ $feedItem.css('height', 'auto').css('max-height', $feedItem.data('original-max-height'));
+
+ $musicians.hide();
+ $description.css('height', $description.data('original-height'));
+ $description.dotdotdot();
+ $name.css('height', $name.data('original-height'));
+ $name.dotdotdot();
+ });
+ }
+
+ function stateChangeRecording(e, data) {
+ var $controls = data.element;
+ var $feedItem = $controls.closest('.feed-entry');
+
+ var $sliderBar = $('.recording-position', $feedItem);
+ var $statusBar = $('.recording-status', $feedItem);
+ var $currentTime = $('.recording-current', $feedItem);
+ var $status = $('.status-text', $feedItem);
+ var $playButton = $('.play-button', $feedItem);
+
+ if(data.isEnd) stopRecordingPlay($feedItem);
+ if(data.isError) {
+ $sliderBar.hide();
+ $playButton.hide();
+ $currentTime.hide();
+ $statusBar.show();
+ $status.text(data.displayText);
+ }
+ }
+
+ function toggleRecordingDetails() {
+ var $detailsLink = $(this);
+ var $feedItem = $detailsLink.closest('.feed-entry');
+ var $musicians = $feedItem.find('.musician-detail');
+ var $description = $feedItem.find('.description');
+ var $name = $feedItem.find('.name');
+ var toggledOpen = $detailsLink.data('toggledOpen');
+
+ if(toggledOpen) {
+ toggleClose($feedItem, $name, $description, $musicians)
+ }
+ else {
+ toggleOpen($feedItem, $name, $description, $musicians)
+ }
+
+ toggledOpen = !toggledOpen;
+ $detailsLink.data('toggledOpen', toggledOpen);
+
+ return false;
+ }
+
+ function updateRecordingName($feedEntry, name) {
+ $feedEntry.find('.name-text').text(name);
+ }
+
+ function updateRecordingDescription($feedEntry, description) {
+ $feedEntry.find('.description').text(description);
+ }
+
+ function updateIsPublic($feedEntry, isPublic) {
+ var $isPrivate = $feedEntry.find('.is_private')
+ if(isPublic) {
+ $isPrivate.removeClass('enabled')
+ }
+ else {
+ $isPrivate.addClass('enabled')
+ }
+ }
+
+ function updateComments($feedEntry, comments) {
+ $feedEntry.find('.comments').html(comments)
+ }
+
+ function updatePlays($feedEntry, plays) {
+ $feedEntry.find('.plays').html(plays);
+ }
+
+ function updateLikes($feedEntry, likes) {
+ $feedEntry.find('.likes').html(likes);
+ }
+
+ function updateStats($feedEntry) {
+ if($feedEntry.is('.recording-entry')) {
+ var id = $feedEntry.attr('data-claimed-recording-id');
+ rest.getClaimedRecording(id)
+ .done(function(claimedRecording) {
+ updateComments($feedEntry, claimedRecording.recording.comment_count);
+ updateLikes($feedEntry, claimedRecording.recording.like_count);
+ updatePlays($feedEntry, claimedRecording.recording.play_count);
+
+ })
+ .fail(app.ajaxError)
+ }
+ else {
+ var id = $feedEntry.attr('data-music-session');
+ rest.getSessionHistory(id)
+ .done(function(music_session) {
+ updateComments($feedEntry, music_session.comment_count);
+ updateLikes($feedEntry, music_session.like_count);
+ updatePlays($feedEntry, music_session.play_count);
+ })
+ .fail(app.ajaxError)
+ }
+ }
+
+ function updateGenre($feedEntry, genre) {
+ $feedEntry.find('.genre').text(context.JK.GenreSelectorHelper.getNameForId(genre));
+ }
+
+ function renderFeeds(feeds) {
+
+ $.each(feeds.entries, function(i, feed) {
+ if(feed.type == 'music_session') {
+ var options = {
+ feed_item: feed,
+ status_class: feed['is_over?'] ? 'ended' : 'inprogress',
+ mount_class: feed['has_mount?'] ? 'has-mount' : 'no-mount'
+ }
+ var $feedItem = $(context._.template($('#template-feed-music-session').html(), options, {variable: 'data'}));
+ var $controls = $feedItem.find('.session-controls');
+
+ // do everything we can before we attach the item to the page
+ $('.timeago', $feedItem).timeago();
+ context.JK.prettyPrintElements($('.duration', $feedItem).show());
+ context.JK.setInstrumentAssetPath($('.instrument-icon', $feedItem));
+ $('.details', $feedItem).click(toggleSessionDetails);
+ $('.details-arrow', $feedItem).click(toggleSessionDetails);
+ $('.play-button', $feedItem).click(toggleSessionPlay);
+
+ if (!feed.session_removed_at)
+ {
+ $('.btn-share', $feedItem).click(function() {
+ ui.launchShareDialog(feed.id, 'session');
+ });
+ }
+ else {
+ $('.btn-share', $feedItem).hide();
+ }
+
+ $('.btn-comment', $feedItem).click(function() {
+ var result = ui.launchCommentDialog({
+ session_id: feed.id,
+ entity_type: 'session'
+ }).one(EVENTS.DIALOG_CLOSED, function() {
+ updateStats($feedItem);
+ })
+ });
+
+ $('.btn-like', $feedItem).click(function() {
+ ui.addSessionLike(feed.id, JK.currentUserId, $('.likes', $feedItem), $('.btn-like', $feedItem))
+ });
+
+ // put the feed item on the page
+ renderFeed($feedItem);
+
+ // these routines need the item to have height to work (must be after renderFeed)
+ $controls.listenBroadcast();
+ $controls.bind('statechange.listenBroadcast', stateChangeSession);
+ $('.dotdotdot', $feedItem).dotdotdot();
+ $feedItem.data('original-max-height', $feedItem.css('height'));
+ context.JK.bindHoverEvents($feedItem);
+ context.JK.bindProfileClickEvents($feedItem);
+ }
+ else if(feed.type == 'recording') {
+ if(feed.claimed_recordings.length == 0) {
+ logger.error("a recording in the feed should always have one claimed_recording")
+ return;
+ }
+ var options = {
+ feed_item: feed,
+ candidate_claimed_recording: obtainCandidate(feed),
+ mix_class: feed['has_mix?'] ? 'has-mix' : 'no-mix',
+ }
+
+ var $feedItem = $(context._.template($('#template-feed-recording').html(), options, {variable: 'data'}));
+ var $controls = $feedItem.find('.recording-controls');
+
+ $('.timeago', $feedItem).timeago();
+ context.JK.prettyPrintElements($('.duration', $feedItem));
+ context.JK.setInstrumentAssetPath($('.instrument-icon', $feedItem));
+ $('.details', $feedItem).click(toggleRecordingDetails);
+ $('.details-arrow', $feedItem).click(toggleRecordingDetails);
+ $('.play-button', $feedItem).click(toggleRecordingPlay);
+ updateIsPublic($feedItem, options.candidate_claimed_recording.is_public);
+
+ $('.btn-share', $feedItem).click(function() {
+ ui.launchShareDialog(options.candidate_claimed_recording.id, 'recording');
+ });
+
+ $('.btn-comment', $feedItem).click(function() {
+ ui.launchCommentDialog({
+ recording_id: feed.id,
+ claimed_recording_id: options.candidate_claimed_recording.id,
+ entity_type: 'recording'
+ })
+ .one(EVENTS.DIALOG_CLOSED, function() {
+ updateStats($feedItem);
+ });
+ });
+
+ $('.btn-like', $feedItem).click(function() {
+ ui.addRecordingLike(feed.id, options.candidate_claimed_recording.id, JK.currentUserId, $('.likes', $feedItem), $('.btn-like', $feedItem));
+ });
+
+ if(isOwner()) {
+ $('.edit-recording-dialog', $feedItem).data('claimed_recording_id', options.candidate_claimed_recording.id).click(function() {
+ app.layout.showDialog('edit-recording', {d1: $(this).data('claimed_recording_id')})
+ .one(EVENTS.DIALOG_CLOSED, function() {
+ $(this).unbind('recording_updated').unbind('recording_deleted');
+ })
+ .one('recording_updated', function(e, data) {
+ // find recording by claimed recording id
+ var $feedEntry = $screen.find('.feed-entry.recording-entry[data-claimed-recording-id="'+ data.id +'"]');
+ var $musicians = $feedEntry.find('.musician-detail');
+ var $description = $feedEntry.find('.description');
+ var $name = $feedEntry.find('.name');
+ var $detailsLink = $feedEntry.find('.details');
+ var toggledOpen = $detailsLink.data('toggledOpen');
+
+ if(toggledOpen) {
+ toggleClose($feedEntry, $name, $description, $musicians, true);
+ }
+
+ $description.trigger('destroy.dot');
+ $name.trigger('destroy.dot');
+
+ updateRecordingName($feedEntry, data.name);
+ updateRecordingDescription($feedEntry, data.description);
+ updateIsPublic($feedEntry, data.is_public);
+ updateGenre($feedEntry, data.genre);
+
+ $name.dotdotdot();
+ $description.dotdotdot();
+ $feedItem.data('original-max-height', $feedEntry.css('height'));
+
+ $detailsLink.data('toggledOpen', false);
+ })
+ .one('recording_deleted', function(e, data) {
+ var $feedEntry = $screen.find('.feed-entry.recording-entry[data-claimed-recording-id="'+ data.id +'"]');
+ $feedEntry.remove();
+ })
+ return false;
+ }).show();
+ }
+
+ // put the feed item on the page
+ renderFeed($feedItem);
+
+ // these routines need the item to have height to work (must be after renderFeed)
+ $controls.listenRecording({recordingId: feed.id, claimedRecordingId: options.candidate_claimed_recording.id, sliderSelector:'.recording-slider', sliderBarSelector: '.recording-playback', currentTimeSelector:'.recording-current'});
+ $controls.bind('statechange.listenRecording', stateChangeRecording);
+ $('.dotdotdot', $feedItem).dotdotdot();
+ $feedItem.data('original-max-height', $feedItem.css('height'));
+ context.JK.bindHoverEvents($feedItem);
+ context.JK.bindProfileClickEvents($feedItem);
+ }
+ else {
+ logger.warn("skipping feed type: " + feed.type);
+ }
+
+ context.JK.bindProfileClickEvents();
+ });
+ }
+
+ function renderFeed(feed) {
+ $content.append(feed);
+ }
+
+ function search() {
+ logger.debug("Searching for feeds...");
+ refresh();
+ return false;
+ }
+
+ function events() {
+ $refresh.on("click", search);
+ $sortFeedBy.on('change', search);
+ $includeDate.on('change', search);
+ $includeType.on('change', search);
+ }
+
+ function initialize(_$parent, _$scroller, _$content, _$noMorefeeds, _$refresh, _$sortFeedBy, _$includeDate, _$includeType) {
+ $screen = _$parent;
+ $scroller = _$scroller;
+ $content = _$content;
+ $noMoreFeeds = _$noMorefeeds;
+ $refresh = _$refresh;
+ $sortFeedBy = _$sortFeedBy;
+ $includeDate = _$includeDate;
+ $includeType = _$includeType;
+
+ if($screen.length == 0) throw "$screen must be specified";
+ if($scroller.length == 0) throw "$scroller must be specified";
+ if($content.length == 0) throw "$content must be specified";
+ if($noMoreFeeds.length == 0) throw "$noMoreFeeds must be specified";
+ if($refresh.length == 0) throw "$refresh must be specified";
+ if($sortFeedBy.length == 0) throw "$sortFeedBy must be specified";
+ if($includeDate.length == 0) throw "$includeDate must be specified";
+ if($includeType.length ==0) throw "$includeType must be specified";
+
+ // set default search criteria
+ $sortFeedBy.val('date')
+ $includeDate.val('month')
+ $includeType.val('all')
+
+ events();
+ }
+
+ this.initialize = initialize;
+ this.refresh = refresh;
+ this.setUser = setUser;
+
+ return this;
+ }
+})(window, jQuery)
\ No newline at end of file
diff --git a/web/app/assets/javascripts/findSession.js b/web/app/assets/javascripts/findSession.js
index c246e5fd2..396c0b813 100644
--- a/web/app/assets/javascripts/findSession.js
+++ b/web/app/assets/javascripts/findSession.js
@@ -99,10 +99,8 @@
// date filter
var date = $('#session-date-filter').val();
if (date !== null && date.length > 0) {
- console.log(date);
currentQuery.day = context.JK.formatDateYYYYMMDD(date);
- // console.log("currentQuery.day=%o", currentQuery.day);
- currentQuery.timezone_offset = new Date().getTimezoneOffset();
+ currentQuery.timezone_offset = (new Date().getTimezoneOffset()) / 60;
}
// language filter
@@ -160,7 +158,6 @@
clearResults();
buildQuery();
loadSessions();
- context.JK.guardAgainstBrowser(app);
}
function clearResults() {
diff --git a/web/app/assets/javascripts/genreSelector.js b/web/app/assets/javascripts/genreSelector.js
index 7dabdf2b0..42c2ca442 100644
--- a/web/app/assets/javascripts/genreSelector.js
+++ b/web/app/assets/javascripts/genreSelector.js
@@ -7,21 +7,12 @@
"use strict";
context.JK = context.JK || {};
+ context.JK.GenreSelectorDeferred = null;
context.JK.GenreSelectorHelper = (function() {
var logger = context.JK.logger;
var _genres = []; // will be list of structs: [ {label:xxx, value:yyy}, {...}, ... ]
- function loadGenres() {
- var url = "/api/genres";
- $.ajax({
- type: "GET",
- url: url,
- async: false, // do this synchronously so the event handlers in events() can be wired up
- success: genresLoaded
- });
- }
-
function reset(parentSelector, defaultGenre) {
defaultGenre = typeof(defaultGenre) == 'undefined' ? '' : defaultGenre;
$('select', parentSelector).val(defaultGenre);
@@ -38,7 +29,7 @@
function render(parentSelector) {
$('select', parentSelector).empty();
- $('select', parentSelector).append('');
+ $('select', parentSelector).append('');
var template = $('#template-genre-option').html();
$.each(_genres, function(index, value) {
// value will be a dictionary entry from _genres:
@@ -67,6 +58,21 @@
return selectedGenres;
}
+ function getNameForId(genreId) {
+ var name = null;
+ context._.each(_genres, function(genre) {
+ if(genreId == genre.value) {
+ name = genre.label;
+ return false;
+ }
+ });
+
+ if(!name) {
+ logger.warn("no genre found for genreId: " + genreId);
+ }
+ return name;
+ }
+
function setSelectedGenres(parentSelector, genreList) {
if (!genreList) {
return;
@@ -79,18 +85,31 @@
$('select', parentSelector).val(values[0]);
}
- function initialize() {
- loadGenres();
+ function initialize(app) {
+ // XXX; _instruments should be populated in a template, rather than round-trip to server
+ if(!context.JK.GenreSelectorDeferred) {
+ // this dance is to make sure there is only one server request instead of InstrumentSelector instances *
+ context.JK.GenreSelectorDeferred = rest.getGenres()
+ }
+
+ context.JK.GenreSelectorDeferred
+ .done(function(response) {genresLoaded(response)})
+ .fail(app.ajaxError)
+
+ return this;
}
var me = { // This will be our singleton.
initialize: initialize,
getSelectedGenres: getSelectedGenres,
setSelectedGenres: setSelectedGenres,
+ getNameForId : getNameForId,
getSelectedGenresValues: getSelectedGenresValues,
reset: reset,
- render: render,
- loadGenres: loadGenres
+ render: function() {
+ var _args = arguments;
+ context.JK.GenreSelectorDeferred.done(function(){render.apply(self, _args)})
+ }
};
return me;
diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js
index d7749f353..8243324b5 100644
--- a/web/app/assets/javascripts/jam_rest.js
+++ b/web/app/assets/javascripts/jam_rest.js
@@ -139,7 +139,8 @@
}
function updateSession(id, newSession) {
- return $.ajax('/api/sessions/' + id, {
+ return $.ajax({
+ url: '/api/sessions/' + id,
type: "PUT",
data : newSession,
dataType : 'json'
@@ -954,6 +955,27 @@
});
}
+ function updateClaimedRecording(options) {
+ var claimedRecordingId = options["id"];
+ return $.ajax({
+ type: "PUT",
+ dataType: "json",
+ url: '/api/claimed_recordings/' + claimedRecordingId,
+ contentType: 'application/json',
+ processData: false,
+ data: JSON.stringify(options)
+ });
+ }
+
+ function deleteClaimedRecording(id) {
+ return $.ajax({
+ type: "DELETE",
+ dataType: "json",
+ contentType: 'application/json',
+ url: "/api/claimed_recordings/" + id
+ });
+ }
+
function claimRecording(options) {
var recordingId = options["id"];
@@ -1220,6 +1242,8 @@
this.getRecording = getRecording;
this.getClaimedRecordings = getClaimedRecordings;
this.getClaimedRecording = getClaimedRecording;
+ this.updateClaimedRecording = updateClaimedRecording;
+ this.deleteClaimedRecording = deleteClaimedRecording;
this.claimRecording = claimRecording;
this.startPlayClaimedRecording = startPlayClaimedRecording;
this.stopPlayClaimedRecording = stopPlayClaimedRecording;
diff --git a/web/app/assets/javascripts/layout.js b/web/app/assets/javascripts/layout.js
index e087a2820..43aaf0d7b 100644
--- a/web/app/assets/javascripts/layout.js
+++ b/web/app/assets/javascripts/layout.js
@@ -644,8 +644,9 @@
function showDialog(dialog, options) {
if (dialogEvent(dialog, 'beforeShow', options) === false) {
- return;
+ return null;
}
+ logger.debug("opening dialog: " + dialog)
var $overlay = $('.dialog-overlay')
if (opts.sizeOverlayToContent) {
diff --git a/web/app/assets/javascripts/networkTestHelper.js b/web/app/assets/javascripts/networkTestHelper.js
index c93b5dce4..098764b4e 100644
--- a/web/app/assets/javascripts/networkTestHelper.js
+++ b/web/app/assets/javascripts/networkTestHelper.js
@@ -17,6 +17,7 @@
var PAYLOAD_SIZE = 100;
var MINIMUM_ACCEPTABLE_SESSION_SIZE = 2;
+ var gearUtils = context.JK.GearUtils;
var rest = context.JK.Rest();
var logger = context.JK.logger;
var $step = null;
@@ -201,6 +202,7 @@
storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror);
}
else if(reason == "server_comm_timeout") {
+ gearUtils.skipNetworkTest();
context.JK.alertSupportedNeeded("Communication with the JamKazam network service has timed out." + appendContextualStatement());
renderStopTest('', '');
storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror);
@@ -211,11 +213,13 @@
storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror);
}
else if(reason == "invalid_response") {
+ gearUtils.skipNetworkTest();
context.JK.alertSupportedNeeded("The JamKazam client software had an unexpected problem while scoring your Internet connection.
Reason: " + attempt.backend_data.reason + '.');
renderStopTest('', '');
storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror);
}
else if(reason == 'no_servers') {
+ gearUtils.skipNetworkTest();
context.JK.alertSupportedNeeded("No network test servers are available." + appendContextualStatement());
renderStopTest('', '');
testedSuccessfully = true;
@@ -227,18 +231,21 @@
storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.noNetwork);
}
else if(reason == "rest_api_error") {
+ gearUtils.skipNetworkTest();
context.JK.alertSupportedNeeded("Unable to acquire a network test server." + appendContextualStatement());
testedSuccessfully = true;
renderStopTest('', '');
storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror);
}
else if(reason == "timeout") {
+ gearUtils.skipNetworkTest();
context.JK.alertSupportedNeeded("Communication with the JamKazam network service timed out." + appendContextualStatement());
testedSuccessfully = true;
renderStopTest('', '');
storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror);
}
else {
+ gearUtils.skipNetworkTest();
context.JK.alertSupportedNeeded("The JamKazam client software had a logic error while scoring your Internet connection.");
renderStopTest('', '');
storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror);
diff --git a/web/app/assets/javascripts/profile.js b/web/app/assets/javascripts/profile.js
index b8e2a5810..bc6277302 100644
--- a/web/app/assets/javascripts/profile.js
+++ b/web/app/assets/javascripts/profile.js
@@ -13,6 +13,7 @@
var sentFriendRequest = false;
var profileScreen = null;
var textMessageDialog = null;
+ var feed = null;
var instrument_logo_map = context.JK.getInstrumentIconMap24();
@@ -30,6 +31,7 @@
function beforeShow(data) {
userId = data.id;
+ feed.setUser(userId);
}
function afterShow(data) {
@@ -37,6 +39,10 @@
resetForm();
}
+ function beforeHide(data) {
+ feed.setUser(null);
+ }
+
function resetForm() {
$('#profile-instruments').empty();
@@ -571,7 +577,7 @@
}
function bindHistory() {
-
+ feed.refresh();
}
/****************** BANDS TAB *****************/
@@ -752,15 +758,31 @@
function bindFavorites() {
}
+ function initializeFeed() {
+
+ var $scroller = profileScreen.find('.content-body-scroller');
+ var $content = profileScreen.find('.feed-content');
+ var $noMoreFeeds = $('#end-of-feeds-list');
+ var $refresh = profileScreen.find('.btn-refresh-entries');
+ var $sortFeedBy = profileScreen.find('#feed_order_by');
+ var $includeDate = profileScreen.find('#feed_date');
+ var $includeType = profileScreen.find('#feed_show');
+
+ feed = new context.JK.Feed(app);
+ feed.initialize(profileScreen, $scroller, $content, $noMoreFeeds, $refresh, $sortFeedBy, $includeDate, $includeType);
+ }
+
function initialize(textMessageDialogInstance) {
textMessageDialog = textMessageDialogInstance;
var screenBindings = {
'beforeShow': beforeShow,
- 'afterShow': afterShow
+ 'afterShow': afterShow,
+ 'beforeHide' : beforeHide
};
app.bindScreen('profile', screenBindings);
profileScreen = $('#user-profile');
events();
+ initializeFeed();
}
this.initialize = initialize;
diff --git a/web/app/assets/javascripts/recordingFinishedDialog.js b/web/app/assets/javascripts/recordingFinishedDialog.js
index 90061b63e..972badf3e 100644
--- a/web/app/assets/javascripts/recordingFinishedDialog.js
+++ b/web/app/assets/javascripts/recordingFinishedDialog.js
@@ -98,10 +98,10 @@
id: recording.id
})
.done(function () {
- console.error("recording discarded by user. recordingId=%o", recording.id);
+ logger.debug("recording discarded by user. recordingId=%o", recording.id);
})
.fail(function (jqXHR) {
- console.error("recording discard by user failed. recordingId=%o. reason: %o", recording.id, jqXHR.responseText);
+ logger.error("recording discard by user failed. recordingId=%o. reason: %o", recording.id, jqXHR.responseText);
})
.always(function () {
app.layout.closeDialog('recordingFinished')
diff --git a/web/app/assets/javascripts/rsvpCancelDialog.js b/web/app/assets/javascripts/rsvpCancelDialog.js
index a0a610cb5..2156cec2e 100644
--- a/web/app/assets/javascripts/rsvpCancelDialog.js
+++ b/web/app/assets/javascripts/rsvpCancelDialog.js
@@ -34,7 +34,7 @@
}
function showDialog() {
- app.layout.showDialog('rsvp-cancel-dialog');
+ return app.layout.showDialog('rsvp-cancel-dialog');
}
function events() {
diff --git a/web/app/assets/javascripts/rsvpSubmitDialog.js b/web/app/assets/javascripts/rsvpSubmitDialog.js
index 281b8120b..9f7ea2e59 100644
--- a/web/app/assets/javascripts/rsvpSubmitDialog.js
+++ b/web/app/assets/javascripts/rsvpSubmitDialog.js
@@ -55,7 +55,7 @@
}
function showDialog() {
- app.layout.showDialog(dialogId);
+ return app.layout.showDialog(dialogId);
}
function events() {
diff --git a/web/app/assets/javascripts/scheduled_session.js b/web/app/assets/javascripts/scheduled_session.js
index 6b17e425a..6766b51cf 100644
--- a/web/app/assets/javascripts/scheduled_session.js
+++ b/web/app/assets/javascripts/scheduled_session.js
@@ -5,6 +5,7 @@
context.JK = context.JK || {};
context.JK.CreateScheduledSession = function(app) {
+ var gearUtils = context.JK.GearUtils;
var logger = context.JK.logger;
var rest = JK.Rest();
var invitationDialog = null;
@@ -188,9 +189,7 @@
function beforeShowStep5() {
var startType = null;
- if (createSessionSettings.createType == 'start-scheduled' ||
- createSessionSettings.createType == 'immediately' ||
- createSessionSettings.createType == 'quick-start') {
+ if (willOptionStartSession()) {
startType = 'Now!';
createSessionSettings.startType = "START SESSION";
}
@@ -305,6 +304,10 @@
if (createSessionSettings.createType == 'start-scheduled') {
var session = scheduledSessions[createSessionSettings.selectedSessionId];
+ if(session == null) {
+ // TODO: notify user they need to pick session? Or maybe it should be grayed out.
+ return false;
+ }
var moveToFinish = function() {
app.layout.closeDialog('confirm');
createSessionSettings.startDate = new Date(session.scheduled_start).toDateString();
@@ -488,7 +491,27 @@
function beforeMoveStep5() {
}
+ function startSessionClicked() {
+
+ if(willOptionStartSession()) {
+ gearUtils.guardAgainstInvalidConfiguration(app)
+ .fail(function() {
+ app.notify(
+ { title: "Unable to Start New Session",
+ text: "You can only start a session once you have working audio gear and a tested internet connection."
+ })
+ })
+ .done(function(){
+ startSession();
+ });
+ }
+ else {
+ startSession();
+ }
+ }
+
function startSession() {
+
var data = {};
if (createSessionSettings.createType == 'start-scheduled') {
@@ -563,22 +586,15 @@
});
}
- var tracks = context.JK.TrackHelpers.getUserTracks(context.jamClient);
- if(tracks.length == 0) {
- logger.error("we should never have 0 tracks and have gotten this far. Launch FTUE is the best we can do right now")
- // If user hasn't completed FTUE - do so now.
- app.afterFtue = function() { startSession(); };
- app.layout.startNewFtue();
- return false;
- }
-
var joinSession = function(sessionId) {
+ var tracks = context.JK.TrackHelpers.getUserTracks(context.jamClient);
+
var options = {};
options.client_id = app.clientId;
options.session_id = sessionId;
options.as_musician = true;
options.tracks = tracks;
- rest.joinSession(options)
+ rest.joinSession(options)
.done(function(response) {
var invitationCount = data.invitations.length;
@@ -600,16 +616,19 @@
app.notifyServerError(jqXHR, "Unable to Create Session");
}
})
+
};
if (createSessionSettings.createType == 'start-scheduled') {
joinSession(createSessionSettings.selectedSessionId);
+ $('#create-session-buttons .btn-next').off('click');
}
else {
rest.createScheduledSession(data)
.done(function(response) {
+ logger.debug("created session on server");
+ $('#create-session-buttons .btn-next').off('click');
var newSessionId = response.id;
- $(".btn-next").off('click');
if (createSessionSettings.createType == 'quick-start' || createSessionSettings.createType == "immediately") {
joinSession(newSessionId);
@@ -620,6 +639,7 @@
}
})
.fail(function(jqXHR){
+ logger.debug("unable to schedule a session")
app.notifyServerError(jqXHR, "Unable to schedule a session");
});
}
@@ -734,7 +754,7 @@
if (step == STEP_SELECT_CONFIRM) {
$btnNext.html(createSessionSettings.startType);
- $btnNext.on('click', startSession);
+ $btnNext.on('click', startSessionClicked);
}
else
$btnNext.on('click', next);
@@ -764,7 +784,20 @@
return false;
}
+ // will this option result in a session being started?
+ function willOptionStartSession() {
+ return createSessionSettings.createType == 'start-scheduled' ||
+ createSessionSettings.createType == 'immediately' ||
+ createSessionSettings.createType == 'quick-start';
+ }
+
function next(event) {
+ if(willOptionStartSession()) {
+ if(!context.JK.guardAgainstBrowser(app)) {
+ return false;
+ }
+ }
+
var valid = beforeMoveStep();
if (!valid) {
return false;
@@ -820,7 +853,7 @@
}
function afterShow() {
- context.JK.guardAgainstBrowser(app);
+
}
function getFormattedTime(date, change) {
diff --git a/web/app/assets/javascripts/session.js b/web/app/assets/javascripts/session.js
index fd5bc3bee..1a1b0bb77 100644
--- a/web/app/assets/javascripts/session.js
+++ b/web/app/assets/javascripts/session.js
@@ -4,6 +4,7 @@
context.JK = context.JK || {};
context.JK.SessionScreen = function(app) {
+ var gearUtils = context.JK.GearUtils;
var logger = context.JK.logger;
var self = this;
var sessionModel = null;
@@ -114,23 +115,25 @@
checkForCurrentUser();
}
+
+
function afterShow(data) {
- if(!context.JK.JamServer.connected) {
- promptLeave = false;
- app.notifyAlert("Not Connected", 'To create or join a session, you must be connected to the server.');
- window.location = '/client#/home'
- return;
- }
+ if(!context.JK.JamServer.connected) {
+ promptLeave = false;
+ app.notifyAlert("Not Connected", 'To create or join a session, you must be connected to the server.');
+ window.location = '/client#/home'
+ return;
+ }
- if (!context.JK.hasOneConfiguredDevice() || context.JK.TrackHelpers.getUserTracks(context.jamClient).length == 0) {
- app.afterFtue = function() { initializeSession(); };
- app.cancelFtue = function() { promptLeave = false; window.location = '/client#/home' };
- app.layout.startNewFtue();
- }
- else {
+ gearUtils.guardAgainstInvalidConfiguration(app)
+ .fail(function() {
+ promptLeave = false;
+ window.location = '/client#/home'
+ })
+ .done(function(){
initializeSession();
- }
+ })
}
function notifyWithUserInfo(title , text, clientId) {
diff --git a/web/app/assets/javascripts/sessionList.js b/web/app/assets/javascripts/sessionList.js
index c02313117..f0cb814c0 100644
--- a/web/app/assets/javascripts/sessionList.js
+++ b/web/app/assets/javascripts/sessionList.js
@@ -4,6 +4,7 @@
context.JK = context.JK || {};
context.JK.SessionList = function(app) {
+ var gearUtils = context.JK.GearUtils;
var logger = context.JK.logger;
var rest = context.JK.Rest();
var ui = new context.JK.UIHelper(app);
@@ -11,18 +12,17 @@
var $inactiveSessionTemplate = $('#template-inactive-session-row');
var $notationFileTemplate = $('#template-notation-files');
var $openSlotsTemplate = $('#template-open-slots');
- var $pendingInvitationsTemplate = $('#template-pending-invitations');
var $latencyTemplate = $('#template-latency');
var $musicianTemplate = $('#template-musician-info');
var showJoinLink = true;
var showRsvpLink = true;
var LATENCY = {
- GOOD : {description: "GOOD", style: "latency-green", min: 0.0, max: 20.0},
- MEDIUM : {description: "MEDIUM", style: "latency-yellow", min: 20.0, max: 40.0},
- POOR : {description: "POOR", style: "latency-red", min: 40.0, max: 10000000000.0},
- UNREACHABLE: {description: "UNREACHABLE", style: "latency-grey", min: -1, max: -1},
- UNKNOWN: {description: "UNKNOWN", style: "latency-grey", min: -2, max: -2}
+ GOOD : {description: "GOOD", style: "latency-green", min: 0.0, max: 20.0},
+ MEDIUM : {description: "MEDIUM", style: "latency-yellow", min: 20.0, max: 40.0},
+ POOR : {description: "POOR", style: "latency-red", min: 40.0, max: 10000000000.0},
+ UNREACHABLE: {description: "UNREACHABLE", style: "latency-grey", min: -1, max: -1},
+ UNKNOWN: {description: "UNKNOWN", style: "latency-grey", min: -2, max: -2}
};
var instrument_logo_map = context.JK.getInstrumentIconMap24();
@@ -41,7 +41,7 @@
showJoinLink = session.musician_access;
// render musicians who are already in the session
- if (session.active_music_session && "participants" in session.active_music_session) {
+ if (session.active_music_session && "participants" in session.active_music_session && session.active_music_session.participants.length > 0) {
for (i=0; i < session.active_music_session.participants.length; i++) {
inSessionUsers.push(session.active_music_session.participants[i].user.id);
var inSessionUserInfo = createInSessionUser(session.active_music_session.participants[i]);
@@ -49,11 +49,16 @@
latencyHtml += inSessionUserInfo[1];
}
}
+ // this provides a buffer at the top to shift the first latency tag down in the event there are NO in-session musicians
+ else {
+ latencyHtml += "
";
+ }
// render users who have approved RSVPs
if (session.approved_rsvps) {
for (i=0; i < session.approved_rsvps.length; i++) {
- if (!(session.approved_rsvps[i].id in inSessionUsers)) {
+ // do not show the user in this section if he is already in the session
+ if ($.inArray(session.approved_rsvps[i].id, inSessionUsers) === -1) {
if (session.approved_rsvps[i].id === context.JK.currentUserId) {
showJoinLink = true;
}
@@ -61,6 +66,9 @@
rsvpUsersHtml += rsvpUserInfo[0];
latencyHtml += rsvpUserInfo[1];
}
+ else {
+ showJoinLink = true;
+ }
}
}
@@ -89,18 +97,25 @@
// wire up the Join Link to the T&Cs dialog
var $parentRow = $('tr[id=' + session.id + ']', tbGroup);
$('.join-link', $parentRow).click(function(evt) {
+ if(!context.JK.guardAgainstBrowser(app)) {
+ return false;
+ }
+
if (!context.JK.JamServer.connected) {
app.notifyAlert("Not Connected", 'To create or join a session, you must be connected to the server.');
return false;
}
- // If no FTUE, show that first.
- if (!context.JK.hasOneConfiguredDevice() || context.JK.TrackHelpers.getUserTracks(context.jamClient).length == 0) {
- app.afterFtue = function() { joinClick(session.id); };
- app.layout.startNewFtue();
- }
- else {
- joinClick(session.id);
- }
+
+ gearUtils.guardAgainstInvalidConfiguration(app)
+ .fail(function() {
+ app.notify(
+ { title: "Unable to Join Session",
+ text: "You can only join a session once you have working audio gear and a tested internet connection."
+ })
+ })
+ .done(function(){
+ joinClick(session.id);
+ })
return false;
});
@@ -119,10 +134,10 @@
$('#actionHeader', tbGroup).html('RSVP');
var i = 0;
- var rsvpUsersHtml = '', openSlotsHtml = '', pendingInvitationsHtml = '', latencyHtml = '', notationFileHtml = '';
+ var rsvpUsersHtml = '', openSlotsHtml = '', latencyHtml = '', notationFileHtml = '';
// render users who have approved RSVPs
- if (session.approved_rsvps) {
+ if (session.approved_rsvps && session.approved_rsvps.length > 0) {
for (i=0; i < session.approved_rsvps.length; i++) {
if (session.approved_rsvps[i].id === context.JK.currentUserId) {
hasApprovedRsvp = true;
@@ -132,6 +147,10 @@
latencyHtml += rsvpUserInfo[1];
}
}
+ // this provides a buffer at the top to shift the first latency tag down in the event there are NO RSVP musicians
+ else {
+ latencyHtml += "
";
+ }
// render open slots
if (session.open_slots) {
@@ -147,7 +166,6 @@
if (session.pending_invitations[i].id === context.JK.currentUserId) {
hasInvitation = true;
}
- pendingInvitationsHtml += createPendingInvitation(session.pending_invitations[i]);
}
}
@@ -166,7 +184,6 @@
}
var sessionVals = buildSessionObject(session, notationFileHtml, rsvpUsersHtml, openSlotsHtml, latencyHtml);
- sessionVals.pending_invitations = pendingInvitationsHtml.length > 0 ? pendingInvitationsHtml : 'N/A';
sessionVals.rsvp_link_display_style = showRsvpLink ? "block" : "none";
var row = context.JK.fillTemplate($inactiveSessionTemplate.html(), sessionVals);
@@ -319,15 +336,6 @@
return context.JK.fillTemplate($openSlotsTemplate.html(), slot);
}
- function createPendingInvitation(user) {
-
- var invitationVals = {
- avatar_url: context.JK.resolveAvatarUrl(user.photo_url)
- };
-
- return context.JK.fillTemplate($pendingInvitationsTemplate.html(), invitationVals);
- }
-
function createNotationFile(notation) {
var notationVals = {
file_url: notation.file_url,
diff --git a/web/app/assets/javascripts/sessionSettingsDialog.js b/web/app/assets/javascripts/sessionSettingsDialog.js
index 603704484..041ee32dd 100644
--- a/web/app/assets/javascripts/sessionSettingsDialog.js
+++ b/web/app/assets/javascripts/sessionSettingsDialog.js
@@ -4,6 +4,12 @@
context.JK.SessionSettingsDialog = function(app, sessionScreen) {
var logger = context.JK.logger;
var $dialog;
+ var $screen = $('#session-settings');
+ var $selectedFilenames = $screen.find('#selected-filenames');
+ var $uploadSpinner = $screen.find($('.upload-spinner'));
+ var $selectedFilenames = $('#settings-selected-filenames');
+ var $inputFiles = $('#settings-select-files');
+ var $btnSelectFiles = $screen.find('.btn-select-files');
var rest = new JK.Rest();
function beforeShow(data) {
@@ -19,7 +25,6 @@
// genre
context.JK.GenreSelectorHelper.setSelectedGenres('#session-settings-genre', currentSession.genres);
-
// name
$('#session-settings-name').val(currentSession.name);
@@ -56,7 +61,12 @@
}
// notation files
-
+ $selectedFilenames.empty();
+ for (var i=0; i < currentSession.music_notations.length; i++) {
+ var notation = currentSession.music_notations[i];
+ console.log('notation.file_name %o', notation.file_name);
+ $selectedFilenames.append('' + notation.file_name + ' ');
+ }
context.JK.dropdown($('#session-settings-language'));
context.JK.dropdown($('#session-settings-musician-access'));
@@ -67,7 +77,7 @@
var data = {};
- data.genre = $('#session-settings-genre').val();
+ data.genre = context.JK.GenreSelectorHelper.getSelectedGenres('#session-settings-genre')[0];
data.name = $('#session-settings-name').val();
data.description = $('#session-settings-description').val();
data.language = $('#session-settings-language').val();
@@ -105,6 +115,78 @@
rest.updateSession($('#session-settings-id').val(), data).done(settingsSaved);
}
+ function changeSelectedFiles() {
+ var fileNames = [];
+ var files = $inputFiles.get(0).files;
+ var error = false;
+ for (var i = 0; i < files.length; ++i) {
+ var name = files.item(i).name;
+ var ext = name.split('.').pop();
+ if ($.inArray(ext, ["pdf", "png", "jpg", "jpeg", "gif", "xml", "mxl", "txt"]) == -1) {
+ error = true;
+ break;
+ }
+ fileNames.push(name);
+ }
+
+ if (error) {
+ app.notifyAlert("Error", "We're sorry, but we do not allow upload of that file type. Please upload only the file types listed in the Upload dialog box.");
+ $inputFiles.replaceWith($inputFiles.clone(true));
+ }
+ else {
+ }
+
+ // upload as soon as user picks their files.
+ uploadNotations($inputFiles.get(0).files)
+ .done(function() {
+ context._.each(fileNames, function(fileName) {
+ $selectedFilenames.append(fileName);
+ })
+ })
+ }
+
+ function uploadNotations(notations) {
+ var formData = new FormData();
+ $.each(notations, function(i, file) {
+ formData.append('files[]', file);
+ });
+
+ formData.append('client_id', app.clientId);
+
+ $btnSelectFiles.text('UPLOADING...').data('uploading', true)
+ $uploadSpinner.show();
+ return rest.uploadMusicNotations(formData)
+ .done(function(response) {
+ var error_files = [];
+ $.each(response, function(i, music_notation) {
+ if (music_notation.errors) {
+ //error_files.push(createSessionSettings.notations[i].name);
+ }
+ })
+ if (error_files.length > 0) {
+ app.notifyAlert("Failed to upload notations.", error_files.join(', '));
+ }
+ })
+ .fail(function(jqXHR) {
+ app.notifyServerError(jqXHR, "Unable to upload music notations");
+ })
+ .always(function() {
+ $btnSelectFiles.text('SELECT FILES...').data('uploading', null)
+ $uploadSpinner.hide();
+ })
+ }
+
+ function toggleSelectFiles(event) {
+ if($btnSelectFiles.data('uploading')) {
+ logger.debug("ignoring click of SELECT FILES... while uploading")
+ return false;
+ }
+
+ event.preventDefault();
+ $('#session-select-files').trigger('click');
+ return false;
+ }
+
function settingsSaved(response) {
// No response returned from this call. 204.
sessionScreen.refreshCurrentSession(true);
@@ -113,6 +195,9 @@
function events() {
$('#session-settings-dialog-submit').on('click', saveSettings);
+
+ $inputFiles.on('change', changeSelectedFiles);
+ $btnSelectFiles.on('click', toggleSelectFiles);
}
this.initialize = function() {
diff --git a/web/app/assets/javascripts/shareDialog.js b/web/app/assets/javascripts/shareDialog.js
index dc3bca311..0d3fbcfda 100644
--- a/web/app/assets/javascripts/shareDialog.js
+++ b/web/app/assets/javascripts/shareDialog.js
@@ -336,7 +336,7 @@
}
function showDialog() {
- app.layout.showDialog('share-dialog');
+ return app.layout.showDialog('share-dialog');
}
// function initDialog() {
diff --git a/web/app/assets/javascripts/ui_helper.js b/web/app/assets/javascripts/ui_helper.js
index 9bff51a7d..0ed51e8d0 100644
--- a/web/app/assets/javascripts/ui_helper.js
+++ b/web/app/assets/javascripts/ui_helper.js
@@ -26,25 +26,25 @@
function launchCommentDialog(options) {
var commentDialog = new JK.CommentDialog(JK.app, options);
commentDialog.initialize();
- commentDialog.showDialog();
+ return commentDialog.showDialog();
}
function launchShareDialog(entityId, entityType) {
var shareDialog = new JK.ShareDialog(JK.app, entityId, entityType);
shareDialog.initialize(JK.FacebookHelperInstance);
- shareDialog.showDialog();
+ return shareDialog.showDialog();
}
function launchRsvpSubmitDialog(sessionId) {
var rsvpDialog = new JK.RsvpSubmitDialog(JK.app, sessionId);
rsvpDialog.initialize();
- rsvpDialog.showDialog();
+ return rsvpDialog.showDialog();
}
function launchRsvpCancelDialog(sessionId, rsvpRequestId) {
var rsvpDialog = new JK.RsvpCancelDialog(JK.app, sessionId, rsvpRequestId);
rsvpDialog.initialize();
- rsvpDialog.showDialog();
+ return rsvpDialog.showDialog();
}
this.addSessionLike = addSessionLike;
diff --git a/web/app/assets/javascripts/utils.js b/web/app/assets/javascripts/utils.js
index 586186111..592c06b06 100644
--- a/web/app/assets/javascripts/utils.js
+++ b/web/app/assets/javascripts/utils.js
@@ -936,8 +936,11 @@
context.JK.guardAgainstBrowser = function(app, args) {
if(!gon.isNativeClient) {
+ logger.debug("guarding against normal browser on screen thaht requires native client")
app.layout.showDialog('launch-app-dialog', args)
+ return false;
}
+ return true;
}
/*
diff --git a/web/app/assets/javascripts/web/session_info.js b/web/app/assets/javascripts/web/session_info.js
index 0426fedf0..fba2b7d55 100644
--- a/web/app/assets/javascripts/web/session_info.js
+++ b/web/app/assets/javascripts/web/session_info.js
@@ -10,8 +10,15 @@
var ui = new context.JK.UIHelper(app);
var $btnAction = $("#btn-action");
+ var LATENCY = {
+ GOOD : {description: "GOOD", style: "latency-green", min: 0.0, max: 20.0},
+ MEDIUM : {description: "MEDIUM", style: "latency-yellow", min: 20.0, max: 40.0},
+ POOR : {description: "POOR", style: "latency-red", min: 40.0, max: 10000000000.0},
+ UNREACHABLE: {description: "UNREACHABLE", style: "latency-grey", min: -1, max: -1},
+ UNKNOWN: {description: "UNKNOWN", style: "latency-grey", min: -2, max: -2}
+ };
+
function addComment(musicSessionId) {
- console.log("here");
var comment = $("#txtSessionInfoComment").val();
if ($.trim(comment).length > 0) {
rest.addSessionInfoComment(musicSessionId, comment)
diff --git a/web/app/assets/javascripts/wizard/gear_utils.js b/web/app/assets/javascripts/wizard/gear_utils.js
index 30ba2e686..6eb7aa2eb 100644
--- a/web/app/assets/javascripts/wizard/gear_utils.js
+++ b/web/app/assets/javascripts/wizard/gear_utils.js
@@ -13,6 +13,11 @@
var ASSIGNMENT = context.JK.ASSIGNMENT;
var VOICE_CHAT = context.JK.VOICE_CHAT;
var AUDIO_DEVICE_BEHAVIOR = context.JK.AUDIO_DEVICE_BEHAVIOR;
+ var EVENTS = context.JK.EVENTS;
+
+ gearUtils.SKIPPED_NETWORK_TEST = -1; // we store a negative 1 to mean that we let the user skip.
+
+ gearUtils.skippedNetworkTest = false; // we allow someone to play in session (for one client run) if it's our fault they can't network test score
// checks if it's an assigned OUTPUT or ASSIGNED CHAT
gearUtils.isChannelAssigned = function (channel) {
@@ -271,4 +276,88 @@
app.notifyServerError(jqXHR, "Unable to sync audio latency")
});
}
+
+ // if the user has a good user network score, immediately returns with a resolved deferred object.
+ // if not, the user will have the network test dialog prompted... once it's closed, then you'll be told reject() if score is still bad, or resolve() if now good
+ gearUtils.guardAgainstBadNetworkScore = function(app) {
+ var deferred = new $.Deferred();
+
+ if (!gearUtils.validNetworkScore()) {
+ // invalid network test score. They have to score to move on
+ app.layout.showDialog('network-test').one(EVENTS.DIALOG_CLOSED, function() {
+ if(gearUtils.validNetworkScore()) {
+ deferred.resolve();
+ }
+ else {
+ deferred.reject();
+ }
+ });
+ }
+ else {
+ deferred.resolve();
+ }
+ return deferred;
+ }
+
+ // XXX this isn't quite right... it needs to check if a good device is *active*
+ // but seen too many problems so far with the backend not reporting any profile active
+ gearUtils.hasGoodActiveProfile = function(app) {
+ return context.JK.hasOneConfiguredDevice() && context.JK.TrackHelpers.getUserTracks(context.jamClient).length > 0
+ }
+
+ // if the user does not have a currently active, good profile, then they are made to deal with it
+ gearUtils.guardAgainstInvalidGearConfiguration = function(app) {
+ var deferred = new $.Deferred();
+
+ if (!gearUtils.hasGoodActiveProfile()) {
+ app.layout.showDialog('gear-wizard').one(EVENTS.DIALOG_CLOSED, function() {
+ if(gearUtils.hasGoodActiveProfile() && gearUtils.validNetworkScore()) {
+ deferred.resolve();
+ }
+ else {
+ deferred.reject();
+ }
+ });
+ }
+ else {
+ deferred.resolve();
+ }
+
+ return deferred;
+ }
+
+ // tests both device config, and network score
+ gearUtils.guardAgainstInvalidConfiguration = function(app) {
+ var deferred = new $.Deferred();
+ gearUtils.guardAgainstInvalidGearConfiguration(app)
+ .fail(function() {
+ deferred.reject();
+ })
+ .done(function() {
+ gearUtils.guardAgainstBadNetworkScore(app)
+ .fail(function() {
+ deferred.reject();
+ })
+ .done(function() {
+ deferred.resolve();
+ })
+ })
+
+ return deferred;
+ }
+
+ gearUtils.skipNetworkTest = function() {
+ context.jamClient.SetNetworkTestScore(gearUtils.SKIPPED_NETWORK_TEST);
+ gearUtils.skippedNetworkTest = true;
+ }
+
+ gearUtils.isNetworkTestSkipped = function() {
+ return gearUtils.skippedNetworkTest;
+ }
+
+ gearUtils.validNetworkScore = function() {
+ return gearUtils.skippedNetworkTest || context.jamClient.GetNetworkTestScore() >= 2;
+ }
+
+
})(window, jQuery);
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/client/client.css b/web/app/assets/stylesheets/client/client.css
index 7dba0e458..0fe1feba3 100644
--- a/web/app/assets/stylesheets/client/client.css
+++ b/web/app/assets/stylesheets/client/client.css
@@ -54,6 +54,7 @@
*= require ./textMessageDialog
*= require ./acceptFriendRequestDialog
*= require ./launchAppDialog
+ *= require ./editRecordingDialog
*= require ./iconInstrumentSelect
*= require ./terms
*= require ./createSession
diff --git a/web/app/assets/stylesheets/client/content.css.scss b/web/app/assets/stylesheets/client/content.css.scss
index b74d26068..d5e335f43 100644
--- a/web/app/assets/stylesheets/client/content.css.scss
+++ b/web/app/assets/stylesheets/client/content.css.scss
@@ -340,6 +340,41 @@ a.arrow-down {
width:200px;
}
+.btn-select-files {
+ margin-top: 10px;
+ margin-left:0;
+ width:110px;
+ @include border_box_sizing;
+}
+
+.spinner-small.upload-spinner {
+ display:none;
+ position: absolute;
+ left: 0px;
+ margin-top: 4px;
+}
+
+.select-files-section {
+ position:absolute;
+}
+
+#settings-selected-filenames {
+ font-size:12px;
+
+ span {
+ white-space:nowrap;
+ text-overflow:ellipsis;
+ overflow:hidden;
+ display:block;
+ }
+}
+
+.selected-files-section {
+ overflow: hidden;
+ width: 100%;
+ @include border_box_sizing;
+}
+
#session-controls {
width:100%;
padding:6px 0px 11px 0px;
diff --git a/web/app/assets/stylesheets/client/editRecordingDialog.css.scss b/web/app/assets/stylesheets/client/editRecordingDialog.css.scss
new file mode 100644
index 000000000..5a38282a4
--- /dev/null
+++ b/web/app/assets/stylesheets/client/editRecordingDialog.css.scss
@@ -0,0 +1,79 @@
+@import "client/common";
+
+#edit-recording-dialog {
+
+ min-height:330px;
+
+ input, textarea {
+ @include border_box_sizing;
+ }
+
+ .field {
+ margin-top:20px;
+
+ &:nth-of-type(1) {
+ margin-top:0;
+ }
+ }
+
+ .buttons {
+ float:right;
+ clear:both;
+ margin-top:20px;
+ }
+
+ label[for="name"] {
+
+ }
+ input[name="name"] {
+ margin-top:5px;
+ width:100%;
+ }
+
+ label[for="description"] {
+
+ }
+ textarea[name="description"] {
+ margin-top:5px;
+ width:100%;
+ }
+
+ label[for="genre"] {
+ display:inline;
+ float:left;
+ line-height:26px;
+ vertical-align:middle;
+ }
+ select[name="genre"] {
+ float:left;
+ margin-left:5px;
+ }
+
+ .genre-selector {
+ float:left;
+ .dropdown-wrapper {
+ margin-left:5px;
+ }
+ }
+
+ label[for="is_public"] {
+ display: inline;
+ float:right;
+ line-height: 26px;
+ padding-right: 5px;
+ vertical-align: middle;
+ }
+ select[name="is_public"] {
+ float:right;
+ }
+ div[purpose="is_public"] {
+ float:right;
+ .icheckbox_minimal {
+ float:right;
+ margin-top:4px;
+ }
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/client/feed.css.scss b/web/app/assets/stylesheets/client/feed.css.scss
index 4dbd60d4f..a69727e00 100644
--- a/web/app/assets/stylesheets/client/feed.css.scss
+++ b/web/app/assets/stylesheets/client/feed.css.scss
@@ -2,4 +2,9 @@
.recording-current {
position:absolute; // solves a problem with duration wrapping--only in firefox
}
+
+ .btn-refresh-holder {
+ float:right;
+ margin-right:10px;
+ }
}
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/client/jamServer.css.scss b/web/app/assets/stylesheets/client/jamServer.css.scss
index 44955e99f..6eab98e23 100644
--- a/web/app/assets/stylesheets/client/jamServer.css.scss
+++ b/web/app/assets/stylesheets/client/jamServer.css.scss
@@ -66,4 +66,3 @@
}
}
-
diff --git a/web/app/assets/stylesheets/client/jamkazam.css.scss b/web/app/assets/stylesheets/client/jamkazam.css.scss
index e4256229f..16d357c82 100644
--- a/web/app/assets/stylesheets/client/jamkazam.css.scss
+++ b/web/app/assets/stylesheets/client/jamkazam.css.scss
@@ -328,6 +328,7 @@ input[type="text"], input[type="password"]{
textarea {
font-size:15px;
+ padding:3px;
}
diff --git a/web/app/assets/stylesheets/client/profile.css.scss b/web/app/assets/stylesheets/client/profile.css.scss
index 746f783e3..ff45db852 100644
--- a/web/app/assets/stylesheets/client/profile.css.scss
+++ b/web/app/assets/stylesheets/client/profile.css.scss
@@ -317,4 +317,60 @@
#btn-add-friend {
display:none;
+}
+
+#profile-history {
+ padding:0 10px 0 20px;
+ width:100%;
+ position:relative;
+ height:100%;
+ @include border_box_sizing;
+
+ #user-feed-controls {
+ width:100%;
+ @include border_box_sizing;
+ position:relative;
+ display:none;
+ }
+
+ .btn-refresh-holder {
+ left: 95%; // 5 * 19% to right-align 5 user blocks (in conjunction with the margin-left
+ margin-left: -65px;
+ position: absolute;
+ }
+
+ .filter-body {
+ bottom: 0;
+ right: 0;
+ top: 0;
+ left: 20px;
+ position: absolute;
+ width: 95%;
+ padding-top:0;
+ margin-top:10px;
+ height:auto;
+ }
+
+ .profile-wrapper {
+ padding: 10px 0;
+ }
+
+ .feed-entry .feed-details {
+ margin-right:5px;
+ }
+
+ .recording-current {
+ position:absolute; // solves a problem with duration wrapping--only in firefox
+ }
+
+ .content-body-scroller {
+ height: 100%;
+ width: 100%;
+ position: absolute;
+ }
+
+
+ .no-feed-msg {
+ text-align:center
+ }
}
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/client/sessionList.css.scss b/web/app/assets/stylesheets/client/sessionList.css.scss
index db441a3ae..f6bf67216 100644
--- a/web/app/assets/stylesheets/client/sessionList.css.scss
+++ b/web/app/assets/stylesheets/client/sessionList.css.scss
@@ -22,9 +22,15 @@ table.findsession-table, table.local-recordings {
}
td.latency {
+ padding-top: 15px !important;
text-align:center !important;
}
+ td.latency div.center {
+ display: inline-block;
+ margin: auto;
+ }
+
.noborder {
border-right:none;
}
@@ -65,6 +71,7 @@ table.findsession-table, table.local-recordings {
font-weight:200;
font-size:11px;
background-color:#868686;
+ text-align:center;
}
.latency-green {
@@ -74,6 +81,7 @@ table.findsession-table, table.local-recordings {
font-weight:200;
font-size:11px;
background-color:#71a43b;
+ text-align:center;
}
.latency-yellow {
@@ -83,6 +91,7 @@ table.findsession-table, table.local-recordings {
font-weight:200;
font-size:11px;
background-color:#cc9900;
+ text-align:center;
}
.latency-red {
@@ -92,6 +101,7 @@ table.findsession-table, table.local-recordings {
font-weight:200;
font-size:11px;
background-color:#980006;
+ text-align:center;
}
.avatar-tiny {
diff --git a/web/app/assets/stylesheets/web/audioWidgets.css.scss b/web/app/assets/stylesheets/web/audioWidgets.css.scss
index da7a933f1..1fcd93f85 100644
--- a/web/app/assets/stylesheets/web/audioWidgets.css.scss
+++ b/web/app/assets/stylesheets/web/audioWidgets.css.scss
@@ -272,6 +272,20 @@
}
}
+ a.edit-recording-dialog {
+ font-size:12px;
+ display:none;
+ }
+
+ .is_private {
+ display:none;
+ font-size:12px;
+ font-style:italic;
+ &.enabled {
+ display:inline;
+ }
+ }
+
.play-count {
margin-right:10px;
}
diff --git a/web/app/assets/stylesheets/web/main.css.scss b/web/app/assets/stylesheets/web/main.css.scss
index 3cd310634..c0ec227e9 100644
--- a/web/app/assets/stylesheets/web/main.css.scss
+++ b/web/app/assets/stylesheets/web/main.css.scss
@@ -589,4 +589,8 @@ strong {
fieldset.login-error .login-error-msg {
display:block;
}
+}
+
+body.jam.web.welcome .no-websocket-connection {
+ display:none;
}
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/web/sessions.css.scss b/web/app/assets/stylesheets/web/sessions.css.scss
index ef513c304..f0a39eb7c 100644
--- a/web/app/assets/stylesheets/web/sessions.css.scss
+++ b/web/app/assets/stylesheets/web/sessions.css.scss
@@ -1,23 +1,54 @@
-/*.session-controls {
- background-color:#471f18;
-}
-
-.session-controls.inprogress {
- background-color:#4C742E;
-}
-
-.session-status-ended, .session-status {
- float:left;
- font-size:18px;
-}
-
-.session-status-inprogress {
- float:left;
- font-size:15px;
- color:#cccc00;
- margin-left:20px;
-}*/
-
#btnPlayPause {
position: relative;
+}
+
+table.musicians {
+ margin-top:-3px;
+}
+
+table.musicians td {
+ border-right:none;
+ border-top:none;
+ padding:2px;
+ vertical-align:middle !important;
+ }
+
+.latency-grey {
+ width: 50px;
+ height: 10px;
+ font-family:Arial, Helvetica, sans-serif;
+ font-weight:200;
+ font-size:11px;
+ background-color:#868686;
+ text-align:center;
+}
+
+.latency-green {
+ width: 50px;
+ height: 10px;
+ font-family:Arial, Helvetica, sans-serif;
+ font-weight:200;
+ font-size:11px;
+ background-color:#71a43b;
+ text-align:center;
+}
+
+.latency-yellow {
+ width: 50px;
+ height: 10px;
+ font-family:Arial, Helvetica, sans-serif;
+ font-weight:200;
+ font-size:11px;
+ background-color:#cc9900;
+ text-align:center;
+}
+
+.latency-red {
+ width: 40px;
+ height: 10px;
+ font-family:Arial, Helvetica, sans-serif;
+ font-weight:200;
+ font-size:11px;
+ background-color:#980006;
+ text-align:center;
}
\ No newline at end of file
diff --git a/web/app/controllers/api_claimed_recordings_controller.rb b/web/app/controllers/api_claimed_recordings_controller.rb
index 61eaa9de2..04cef8d3f 100644
--- a/web/app/controllers/api_claimed_recordings_controller.rb
+++ b/web/app/controllers/api_claimed_recordings_controller.rb
@@ -19,30 +19,20 @@ class ApiClaimedRecordingsController < ApiController
end
def update
-
if @claimed_recording.user_id != current_user.id
raise PermissionError, 'only owner of claimed_recording can update it'
end
- begin
- @claimed_recording.update_fields(current_user, params)
- respond_with responder: ApiResponder, :status => 204
- rescue
- render :json => { :message => "claimed_recording could not be updated" }, :status => 403
- end
+ @claimed_recording.update_fields(current_user, params)
+ respond_with @claimed_recording
end
def delete
if @claimed_recording.user_id != current_user.id
raise PermissionError, 'only owner of claimed_recording can update it'
end
- #begin
- #@claimed_recording.discard(current_user)
- #render :json => {}, :status => 204
-# respond_with responder: ApiResponder, :status => 204
- #rescue
- #render :json => { :message => "claimed_recording could not be deleted" }, :status => 403
- #end
+ @claimed_recording.discard(current_user)
+ render :json => {}, :status => 200
end
def download
diff --git a/web/app/controllers/api_music_sessions_controller.rb b/web/app/controllers/api_music_sessions_controller.rb
index aa2f37331..501edc025 100644
--- a/web/app/controllers/api_music_sessions_controller.rb
+++ b/web/app/controllers/api_music_sessions_controller.rb
@@ -82,7 +82,6 @@ class ApiMusicSessionsController < ApiController
end
end
-
def scheduled
@music_sessions = MusicSession.scheduled(current_user)
end
@@ -175,7 +174,7 @@ class ApiMusicSessionsController < ApiController
@music_session.music_session,
params[:name],
params[:description],
- params[:genre],
+ params[:genre] ? Genre.find(params[:genre]) : nil,
params[:language],
params[:musician_access],
params[:approval_required],
diff --git a/web/app/controllers/api_recordings_controller.rb b/web/app/controllers/api_recordings_controller.rb
index 478a3aeee..75e3ea6f4 100644
--- a/web/app/controllers/api_recordings_controller.rb
+++ b/web/app/controllers/api_recordings_controller.rb
@@ -205,6 +205,7 @@ class ApiRecordingsController < ApiController
end
end
+
private
def parse_filename
@recorded_track = RecordedTrack.find_by_recording_id_and_client_track_id!(params[:id], params[:track_id])
diff --git a/web/app/controllers/music_sessions_controller.rb b/web/app/controllers/music_sessions_controller.rb
index 226142a58..19f37c7c2 100644
--- a/web/app/controllers/music_sessions_controller.rb
+++ b/web/app/controllers/music_sessions_controller.rb
@@ -47,9 +47,15 @@ class MusicSessionsController < ApplicationController
# run these 3 queries only if the user has access to the page
if @can_view
- @approved_rsvps = @music_session.approved_rsvps
- @open_slots = @music_session.open_slots
- @pending_invitations = @music_session.pending_invitations
+ ActiveRecord::Base.transaction do
+ @music_sessions, @user_scores = MusicSession.sms_index(current_user, {:session_id => params[:id], :client_id => cookies[:client_id]})
+ unless @music_sessions.blank?
+ ms = @music_sessions[0]
+ @approved_rsvps = ms.approved_rsvps
+ @open_slots = ms.open_slots
+ @pending_invitations = ms.pending_invitations
+ end
+ end
end
render :layout => "web"
diff --git a/web/app/views/api_feeds/show.rabl b/web/app/views/api_feeds/show.rabl
index 03a8ad2c5..140828833 100644
--- a/web/app/views/api_feeds/show.rabl
+++ b/web/app/views/api_feeds/show.rabl
@@ -140,7 +140,7 @@ glue :recording do
child(:claimed_recordings => :claimed_recordings) {
- attributes :id, :name, :description, :is_public, :genre_id, :has_mix?
+ attributes :id, :name, :description, :is_public, :genre_id, :has_mix?, :user_id
child(:user => :creator) {
attributes :id, :first_name, :last_name, :photo_url
diff --git a/web/app/views/api_music_sessions/show.rabl b/web/app/views/api_music_sessions/show.rabl
index 1eebd1728..15e1ef234 100644
--- a/web/app/views/api_music_sessions/show.rabl
+++ b/web/app/views/api_music_sessions/show.rabl
@@ -19,6 +19,14 @@ else
[item.genre.description] # XXX: need to return single genre; not array
end
+ child(:music_notations => :music_notations) {
+ attributes :id, :file_name
+
+ node do |music_notation|
+ { file_url: music_notation["file_url"] }
+ end
+ }
+
if :is_recording?
node do |music_session|
{ :recording => partial("api_recordings/show", :object => music_session.recording) }
@@ -44,7 +52,6 @@ else
}
}
-
child({:invitations => :invitations}) {
attributes :id, :sender_id, :receiver_id
}
diff --git a/web/app/views/api_music_sessions/show_history.rabl b/web/app/views/api_music_sessions/show_history.rabl
index 1da5fc3ab..0ff523c36 100644
--- a/web/app/views/api_music_sessions/show_history.rabl
+++ b/web/app/views/api_music_sessions/show_history.rabl
@@ -17,7 +17,7 @@ if !current_user
else
attributes :id, :music_session_id, :name, :description, :musician_access, :approval_required, :fan_access, :fan_chat,
- :band_id, :user_id, :genre_id, :created_at, :like_count, :comment_count, :scheduled_start, :scheduled_duration,
+ :band_id, :user_id, :genre_id, :created_at, :like_count, :comment_count, :play_count, :scheduled_start, :scheduled_duration,
:language, :recurring_mode, :language_description, :scheduled_start_time, :access_description, :timezone, :timezone_description,
:musician_access_description, :fan_access_description, :session_removed_at, :legal_policy, :open_rsvps
diff --git a/web/app/views/api_recordings/show.rabl b/web/app/views/api_recordings/show.rabl
index 633816ba0..8b75f0384 100644
--- a/web/app/views/api_recordings/show.rabl
+++ b/web/app/views/api_recordings/show.rabl
@@ -28,7 +28,7 @@ child(:comments => :comments) {
child(:claimed_recordings => :claimed_recordings) {
- attributes :id, :name, :description, :is_public, :genre_id
+ attributes :id, :name, :description, :is_public, :genre_id, :has_mix?, :user_id
node :share_url do |claimed_recording|
unless claimed_recording.share_token.nil?
diff --git a/web/app/views/clients/_banner.html.erb b/web/app/views/clients/_banner.html.erb
deleted file mode 100644
index 1bcde7cbd..000000000
--- a/web/app/views/clients/_banner.html.erb
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/web/app/views/clients/_banner.html.haml b/web/app/views/clients/_banner.html.haml
new file mode 100644
index 000000000..9e53fb0e1
--- /dev/null
+++ b/web/app/views/clients/_banner.html.haml
@@ -0,0 +1,33 @@
+#banner_overlay.overlay
+#banner.dialog-overlay-sm{ 'data-type' => '' }
+ .content-head
+ = image_tag("content/icon_alert.png", :height => '24', :width => '24', :class => "content-icon")
+ %h1
+
+ .dialog-inner
+
+ %br.end-content{ clear: 'all'}
+
+ .right.buttons
+ %a.button-orange.close-btn CLOSE
+ %a.button-orange.yes-btn YES
+ %a.button-grey.no-btn CANCEL
+
+%script{type: 'text/template', id: 'template-app-in-read-only-volume'}
+ .template-app-in-read-only-volume
+ %p The JamKazam application is running in a read-only volume. This stops the automatic update feature from working, and may cause other issues because it is not a supported configuration.
+ %p So let's fix it. Don't worry--it's easy to do--please read on.
+ %p First, here's almost certainly what happened to cause this problem: after JamKazam.dmg was downloaded, it was then double-clicked and a window opened showing the contents of the dmg. The JamKazam application icon was double-clicked inside that opened window. Unfortunately, that isn't OK.
+ %p Instead, do this to move JamKazam to a good location, and run it from there:
+ %ol
+ %li.download-dmg
+ Download the latest mac installer from the
+ %a{href:"/downloads"}Downloads
+ page.
+ %br
+ %em (the download will have a filename ending in .dmg)
+ %li Double-click the downloaded dmg file to open it.
+ %li In the resulting screen, drag the JamKazam icon to the Applications folder. It will show a progress bar as it copies.
+ %li Double-click the Applications folder to go into it.
+ %li If you are still running the JamKazam application, you will need to stop it before executing the last step.
+ %li Find the JamKazam application in the Applications folder, and double-click the icon to launch it!
\ No newline at end of file
diff --git a/web/app/views/clients/_edit_recording_dialog.html.haml b/web/app/views/clients/_edit_recording_dialog.html.haml
new file mode 100644
index 000000000..b1b944c4d
--- /dev/null
+++ b/web/app/views/clients/_edit_recording_dialog.html.haml
@@ -0,0 +1,24 @@
+.dialog.configure-tracks{ layout: 'dialog', 'layout-id' => 'edit-recording', id: 'edit-recording-dialog'}
+ .content-head
+ = image_tag "content/icon_add.png", {:width => 19, :height => 19, :class => 'content-icon' }
+ %h1 Edit Recording
+ .dialog-inner
+ %form
+ .field
+ %label{for: 'name'} Recording name:
+ %input{type: 'text', name: 'name'}
+ .field
+ %label{for: 'description'} Description:
+ %textarea{name: 'description', rows: '4'}
+ .field.genre-selector
+ %label{for: 'genre'} Genre:
+ %select{name:'genre'}
+ .field{purpose: 'is_public'}
+ %input{type: 'checkbox', name: 'is_public'}
+ %label{for: 'is_public'} Public Recording
+
+ .buttons
+ %a.button-grey.cancel-btn CANCEL
+ %a.button-orange.delete-btn DELETE
+ %a.button-orange.save-btn UPDATE
+ %br{clear: 'all'}
\ No newline at end of file
diff --git a/web/app/views/clients/_findSession.html.erb b/web/app/views/clients/_findSession.html.erb
index 050f5658c..49c0110b1 100644
--- a/web/app/views/clients/_findSession.html.erb
+++ b/web/app/views/clients/_findSession.html.erb
@@ -81,14 +81,14 @@