diff --git a/admin/app/admin/jam_track_right.rb b/admin/app/admin/jam_track_right.rb
index 5e7c3687b..3c591234a 100644
--- a/admin/app/admin/jam_track_right.rb
+++ b/admin/app/admin/jam_track_right.rb
@@ -64,7 +64,7 @@ ActiveAdmin.register JamRuby::JamTrackRight, :as => 'JamTrackRights' do
begin
client.find_or_create_account(user, billing_info)
- client.place_order(user, jam_track)
+ client.place_order(user, jam_track, nil)
rescue RecurlyClientError=>x
redirect_to admin_jam_track_rights_path, notice: "Could not order #{jam_track} for #{user.to_s}: #{x.errors.inspect}"
else
diff --git a/admin/app/admin/jam_tracks.rb b/admin/app/admin/jam_tracks.rb
index 96f341829..6c90018e6 100644
--- a/admin/app/admin/jam_tracks.rb
+++ b/admin/app/admin/jam_tracks.rb
@@ -24,6 +24,7 @@ ActiveAdmin.register JamRuby::JamTrack, :as => 'JamTracks' do
column :original_artist
column :name
+ column :flags do |jam_track| jam_track.duplicate_positions? ? 'DUP POSITIONS' : '' end
column :status
column :master_track do |jam_track| jam_track.master_track.nil? ? 'None' : (link_to "Download", jam_track.master_track.url_by_sample_rate(44)) end
column :licensor
diff --git a/admin/app/models/cohort.rb b/admin/app/models/cohort.rb
index 29523a46d..2b72a3936 100644
--- a/admin/app/models/cohort.rb
+++ b/admin/app/models/cohort.rb
@@ -149,9 +149,9 @@ SELECT played.player_id FROM
(SELECT player_id, COUNT(*) cnt FROM playable_plays pp
WHERE
pp.created_at >= '#{start_date}' AND
- pp.created_at <= '#{end_date}' AND
- pp.playable_type = 'JamRuby::JamTrack'
- GROUP BY player_id
+ pp.created_at <= '#{end_date}' AND
+ pp.playable_type = 'JamRuby::JamTrack' /* VRFS-2916 jam_tracks.id is varchar: ADD */
+ GROUP BY player_id
) played
WHERE #{where}
SQL
@@ -168,7 +168,10 @@ WHERE
tt.created_at >= '#{start_date}' AND
tt.created_at <= '#{end_date}'
SQL
- yield(sql) if block_given?
+ if block_given?
+ yield_sql = yield(sql)
+ sql = yield_sql unless yield_sql.blank?
+ end
self.class.cohort_users(self).where("users.id IN (#{sql})").count
end
@@ -196,12 +199,12 @@ SQL
_put_data_set(assoc_key, count, num_user)
count = _subquery(assoc_key = :jam_track_rights, num_user) do |subsql|
- subsql += " AND tt.redeemed = 'f' "
+ subsql += " AND tt.is_test_purchase = 'f' AND tt.redeemed = 'f' "
end
_put_data_set(assoc_key, count, num_user)
count = _subquery(assoc_key = :jam_track_rights, num_user) do |subsql|
- subsql += " AND tt.redeemed = 't' "
+ subsql += " AND tt.is_test_purchase = 'f' AND tt.redeemed = 't' "
end
_put_data_set(:jam_track_rights_redeemed, count, num_user)
@@ -235,13 +238,6 @@ SQL
self.save!
end
- def _join_user_all_time(assoc_ref)
- assoc_ref.active_record
- .joins("INNER JOIN users AS uu ON uu.id = #{assoc_ref.foreign_key}")
- .where(created_at: self.group_start..self.group_end)
- .where(['uu.created_at >= ? AND uu.created_at <= ?', self.group_start, self.group_end])
- end
-
def _all_time!
unless 0 < num_user = self.class.cohort_users(self).count
self.update_attribute(:data_set, {})
@@ -270,10 +266,15 @@ SQL
count = _subquery(assoc_key = :friendships, num_user)
_put_data_set(assoc_key, count, num_user)
- count = _subquery(assoc_key = :jam_track_rights, num_user)
+ count = _subquery(assoc_key = :jam_track_rights, num_user) do |subsql|
+ subsql += " AND tt.is_test_purchase = 'f'"
+ end
_put_data_set(assoc_key, count, num_user)
-
+
count = _subquery(assoc_key = :jam_tracks_played, num_user) do |subsql|
+ # VRFS-2916 jam_tracks.id is varchar: REMOVE
+ # subsql += " AND tt.jam_track_id IS NOT NULL "
+ # VRFS-2916 jam_tracks.id is varchar: ADD
subsql += " AND tt.playable_type = 'JamRuby::JamTrack' "
end
_put_data_set(assoc_key, count, num_user)
diff --git a/admin/app/views/admin/jam_tracks/_jam_track_right_fields.html.slim b/admin/app/views/admin/jam_tracks/_jam_track_right_fields.html.slim
new file mode 100644
index 000000000..9bf130e56
--- /dev/null
+++ b/admin/app/views/admin/jam_tracks/_jam_track_right_fields.html.slim
@@ -0,0 +1,5 @@
+= f.inputs name: 'Jam Track Right fields' do
+
+ ol.nested-fields
+ = f.input :jam_track, :required=>true, collection: JamTrack.all, include_blank: false
+ = f.input :user, :required=>true, collection: User.all, include_blank: false
diff --git a/admin/config/initializers/jam_track_tracks.rb b/admin/config/initializers/jam_track_tracks.rb
index 36f82d79e..b6367cd58 100644
--- a/admin/config/initializers/jam_track_tracks.rb
+++ b/admin/config/initializers/jam_track_tracks.rb
@@ -68,7 +68,7 @@ class JamRuby::JamTrackTrack
s3_manager.download(self.url_by_sample_rate(44), input)
- command = "sox \"#{input}\" \"#{output}\" trim #{start} #{stop}"
+ command = "sox \"#{input}\" \"#{output}\" trim #{sprintf("%.3f", start)} =#{sprintf("%.3f", stop)}"
@@log.debug("trimming using: " + command)
diff --git a/admin/spec/factories.rb b/admin/spec/factories.rb
index a1e906a48..d6888a6bc 100644
--- a/admin/spec/factories.rb
+++ b/admin/spec/factories.rb
@@ -11,6 +11,7 @@ FactoryGirl.define do
state "NC"
country "US"
terms_of_service true
+ reuse_card true
factory :admin do
diff --git a/db/manifest b/db/manifest
index 7478b9f19..9a2e13ccd 100755
--- a/db/manifest
+++ b/db/manifest
@@ -269,4 +269,10 @@ connection_metronome.sql
preview_jam_track_tracks.sql
cohorts.sql
jam_track_right_admin_purchase.sql
-alter_genre_player_unique_constraint.sql
\ No newline at end of file
+alter_genre_player_unique_constraint.sql
+jam_track_playable_plays.sql
+shopping_cart_anonymous.sql
+user_reuse_card_and_reedem.sql
+jam_track_id_to_varchar.sql
+drop_position_unique_jam_track.sql
+recording_client_metadata.sql
\ No newline at end of file
diff --git a/db/up/drop_position_unique_jam_track.sql b/db/up/drop_position_unique_jam_track.sql
new file mode 100644
index 000000000..6055702d4
--- /dev/null
+++ b/db/up/drop_position_unique_jam_track.sql
@@ -0,0 +1 @@
+DROP INDEX jam_track_tracks_position_uniqkey;
\ No newline at end of file
diff --git a/db/up/jam_track_id_to_varchar.sql b/db/up/jam_track_id_to_varchar.sql
new file mode 100644
index 000000000..9a2c0cdfd
--- /dev/null
+++ b/db/up/jam_track_id_to_varchar.sql
@@ -0,0 +1,25 @@
+-- change jam_tracks PRIMARY KEY to VARCHAR(64)
+
+-- first, drop all constraints and change the types
+ALTER TABLE jam_track_tracks DROP CONSTRAINT jam_track_tracks_jam_track_id_fkey;
+ALTER TABLE jam_track_tracks ALTER COLUMN jam_track_id TYPE VARCHAR(64);
+ALTER TABLE jam_track_tap_ins DROP CONSTRAINT jam_track_tap_ins_jam_track_id_fkey;
+ALTER TABLE jam_track_tap_ins ALTER COLUMN jam_track_id TYPE VARCHAR(64);
+ALTER TABLE jam_track_rights DROP CONSTRAINT jam_track_rights_jam_track_id_fkey;
+ALTER TABLE jam_track_rights ALTER COLUMN jam_track_id TYPE VARCHAR(64);
+ALTER TABLE active_music_sessions ALTER COLUMN jam_track_id TYPE VARCHAR(64);
+ALTER TABLE recordings DROP CONSTRAINT recordings_jam_track_id_fkey;
+ALTER TABLE recordings ALTER COLUMN jam_track_id TYPE VARCHAR(64);
+ALTER TABLE playable_plays DROP COLUMN jam_track_id;
+
+
+-- then drop the jamtrack sequence, change it's type, and then set default to UUID
+-- DROP SEQUENCE jam_tracks_next_seq;
+ALTER TABLE jam_tracks ALTER COLUMN id TYPE VARCHAR(64);
+ALTER TABLE jam_tracks ALTER COLUMN id SET DEFAULT uuid_generate_v4();
+
+-- add back in all the constraints on the fk tables
+ALTER TABLE jam_track_tracks ADD CONSTRAINT jam_track_tracks_jam_track_id_fkey FOREIGN KEY (jam_track_id) REFERENCES jam_tracks(id) ON DELETE CASCADE;
+ALTER TABLE jam_track_tap_ins ADD CONSTRAINT jam_track_tap_ins_jam_track_id_fkey FOREIGN KEY (jam_track_id) REFERENCES jam_tracks(id) ON DELETE CASCADE;
+ALTER TABLE jam_track_rights ADD CONSTRAINT jam_track_rights_jam_track_id_fkey FOREIGN KEY (jam_track_id) REFERENCES jam_tracks(id);
+ALTER TABLE recordings ADD CONSTRAINT recordings_jam_track_id_fkey FOREIGN KEY (jam_track_id) REFERENCES jam_tracks(id);
diff --git a/db/up/jam_track_playable_plays.sql b/db/up/jam_track_playable_plays.sql
new file mode 100644
index 000000000..dfa95d07d
--- /dev/null
+++ b/db/up/jam_track_playable_plays.sql
@@ -0,0 +1,6 @@
+ALTER TABLE playable_plays ADD COLUMN jam_track_id bigint;
+ALTER TABLE playable_plays ALTER COLUMN playable_id DROP NOT NULL;
+ALTER TABLE playable_plays ALTER COLUMN playable_type DROP NOT NULL;
+
+
+
diff --git a/db/up/recording_client_metadata.sql b/db/up/recording_client_metadata.sql
new file mode 100644
index 000000000..f800f234a
--- /dev/null
+++ b/db/up/recording_client_metadata.sql
@@ -0,0 +1 @@
+ALTER TABLE recordings ADD COLUMN timeline JSON;
\ No newline at end of file
diff --git a/db/up/shopping_cart_anonymous.sql b/db/up/shopping_cart_anonymous.sql
new file mode 100644
index 000000000..e22cfeecd
--- /dev/null
+++ b/db/up/shopping_cart_anonymous.sql
@@ -0,0 +1,2 @@
+ALTER TABLE shopping_carts ALTER COLUMN user_id DROP NOT NULL;
+ALTER TABLE shopping_carts ADD COLUMN anonymous_user_id VARCHAR(1000);
\ No newline at end of file
diff --git a/db/up/user_reuse_card_and_reedem.sql b/db/up/user_reuse_card_and_reedem.sql
new file mode 100644
index 000000000..2f2b811ea
--- /dev/null
+++ b/db/up/user_reuse_card_and_reedem.sql
@@ -0,0 +1,3 @@
+ALTER TABLE users ADD COLUMN reuse_card BOOLEAN DEFAULT TRUE NOT NULL;
+ALTER TABLE users ADD COLUMN has_redeemable_jamtrack BOOLEAN DEFAULT TRUE NOT NULL;
+ALTER TABLE shopping_carts ADD COLUMN marked_for_redeem INTEGER DEFAULT 0 NOT NULL;
\ No newline at end of file
diff --git a/ruby/README.md b/ruby/README.md
index 302798539..4f85c80a6 100644
--- a/ruby/README.md
+++ b/ruby/README.md
@@ -7,5 +7,3 @@ Create development database 'jam_ruby'
Once you've created your database, migrate it:
`bundle exec jam_ruby up`
-
-
diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb
index 5d63b5d3e..31b5fafb3 100755
--- a/ruby/lib/jam_ruby.rb
+++ b/ruby/lib/jam_ruby.rb
@@ -97,6 +97,7 @@ require "jam_ruby/models/max_mind_release"
require "jam_ruby/models/genre_player"
require "jam_ruby/models/genre"
require "jam_ruby/models/user"
+require "jam_ruby/models/anonymous_user"
require "jam_ruby/models/rsvp_request"
require "jam_ruby/models/rsvp_slot"
require "jam_ruby/models/rsvp_request_rsvp_slot"
diff --git a/ruby/lib/jam_ruby/jam_tracks_manager.rb b/ruby/lib/jam_ruby/jam_tracks_manager.rb
index 68c788f87..9322c3dbd 100644
--- a/ruby/lib/jam_ruby/jam_tracks_manager.rb
+++ b/ruby/lib/jam_ruby/jam_tracks_manager.rb
@@ -15,6 +15,7 @@ module JamRuby
class << self
+
def save_jam_track_jkz(user, jam_track, sample_rate=48)
jam_track_right = jam_track.right_for_user(user)
raise ArgumentError if jam_track_right.nil?
diff --git a/ruby/lib/jam_ruby/models/anonymous_user.rb b/ruby/lib/jam_ruby/models/anonymous_user.rb
new file mode 100644
index 000000000..0a843185a
--- /dev/null
+++ b/ruby/lib/jam_ruby/models/anonymous_user.rb
@@ -0,0 +1,29 @@
+# this was added to support the idea of an anonymous user interacting with our site; needed by the ShoppingCart
+# over time it might make sense to beef this up and to use it conistently in anonymous interactions
+
+module JamRuby
+ class AnonymousUser
+
+ attr_accessor :id
+
+ def initialize(id)
+ @id = id
+ end
+
+ def shopping_carts
+ ShoppingCart.where(anonymous_user_id: @id)
+ end
+
+ def destroy_all_shopping_carts
+ ShoppingCart.destroy_all(anonymous_user_id: @id)
+ end
+
+ def admin
+ false
+ end
+
+ def has_redeemable_jamtrack
+ true
+ end
+ end
+end
diff --git a/ruby/lib/jam_ruby/models/jam_track.rb b/ruby/lib/jam_ruby/models/jam_track.rb
index 646fd18ff..56341ff26 100644
--- a/ruby/lib/jam_ruby/models/jam_track.rb
+++ b/ruby/lib/jam_ruby/models/jam_track.rb
@@ -44,25 +44,73 @@ module JamRuby
belongs_to :genre, class_name: "JamRuby::Genre"
belongs_to :licensor , class_name: 'JamRuby::JamTrackLicensor', foreign_key: 'licensor_id'
- has_many :jam_track_tracks, :class_name => "JamRuby::JamTrackTrack", order: 'position ASC'
+ has_many :jam_track_tracks, :class_name => "JamRuby::JamTrackTrack", order: 'position ASC, part ASC, instrument_id ASC'
has_many :jam_track_tap_ins, :class_name => "JamRuby::JamTrackTapIn", order: 'offset_time ASC'
- has_many :jam_track_rights, :class_name => "JamRuby::JamTrackRight" #, inverse_of: 'jam_track', :foreign_key => "jam_track_id"
+ has_many :jam_track_rights, :class_name => "JamRuby::JamTrackRight" #, inverse_of: 'jam_track', :foreign_key => "jam_track_id" # '
+
has_many :owners, :through => :jam_track_rights, :class_name => "JamRuby::User", :source => :user
has_many :playing_sessions, :class_name => "JamRuby::ActiveMusicSession"
has_many :recordings, :class_name => "JamRuby::Recording"
+ # VRFS-2916 jam_tracks.id is varchar: REMOVE
+ # has_many :plays, :class_name => "JamRuby::PlayablePlay", :foreign_key => :jam_track_id, :dependent => :destroy
+ # VRFS-2916 jam_tracks.id is varchar: ADD
+ has_many :plays, :class_name => "JamRuby::PlayablePlay", :as => :playable, :dependent => :destroy
+
accepts_nested_attributes_for :jam_track_tracks, allow_destroy: true
accepts_nested_attributes_for :jam_track_tap_ins, allow_destroy: true
+ def duplicate_positions?
+ counter = {}
+ jam_track_tracks.each do |track|
+ count = counter[track.position]
+ if count.nil?
+ count = 0
+ end
+ puts "count #{count}"
+ counter[track.position] = count + 1
+ end
+
+ duplicate = false
+ counter.each do|position, count|
+ if count > 1
+ duplicate = true
+ break
+ end
+ end
+ duplicate
+ end
+
class << self
+ # @return array[artist_name(string)]
+ def all_artists
+ JamTrack.select("original_artist").
+ group("original_artist").
+ collect{|jam_track|jam_track.original_artist}
+ end
+
+ # @return array[JamTrack] for given artist_name
+ def tracks_for_artist(artist_name)
+ JamTrack.where("original_artist=?", artist_name).all
+ end
+
def index(options, user)
if options[:page]
page = options[:page].to_i
per_page = options[:per_page].to_i
+ if per_page == 0
+ # try and see if limit was specified
+ limit = options[:limit]
+ limit ||= 20
+ limit = limit.to_i
+ else
+ limit = per_page
+ end
+
start = (page -1 )* per_page
limit = per_page
else
@@ -85,6 +133,14 @@ module JamRuby
query = query.joins(:jam_track_rights)
query = query.where("jam_track_rights.user_id = ?", user.id)
end
+
+ if options[:artist].present?
+ query = query.where("original_artist=?", options[:artist])
+ end
+
+ if options[:group_artist]
+ query = query.group("original_artist")
+ end
query = query.where("jam_tracks.status = ?", 'Production') unless user.admin
query = query.where("jam_tracks.genre_id = '#{options[:genre]}'") unless options[:genre].blank?
@@ -114,6 +170,6 @@ module JamRuby
def right_for_user(user)
jam_track_rights.where("user_id=?", user).first
- end
+ end
end
end
diff --git a/ruby/lib/jam_ruby/models/jam_track_right.rb b/ruby/lib/jam_ruby/models/jam_track_right.rb
index 4f8852341..f5abb1353 100644
--- a/ruby/lib/jam_ruby/models/jam_track_right.rb
+++ b/ruby/lib/jam_ruby/models/jam_track_right.rb
@@ -101,6 +101,7 @@ module JamRuby
remove_url_44!
end
+
def enqueue(sample_rate=48)
begin
JamTrackRight.where(:id => self.id).update_all(:signing_queued_at => Time.now, :signing_started_at => nil, :last_signed_at => nil)
@@ -124,6 +125,7 @@ module JamRuby
end
end
+
# @return true if signed && file exists for the sample_rate specifed:
def ready?(sample_rate=48)
if sample_rate==48
diff --git a/ruby/lib/jam_ruby/models/jam_track_track.rb b/ruby/lib/jam_ruby/models/jam_track_track.rb
index 419b49c63..78255d07c 100644
--- a/ruby/lib/jam_ruby/models/jam_track_track.rb
+++ b/ruby/lib/jam_ruby/models/jam_track_track.rb
@@ -23,7 +23,6 @@ module JamRuby
validates :part, length: {maximum: 25}
validates :track_type, inclusion: {in: TRACK_TYPE }
validates :preview_start_time, numericality: {only_integer: true}, length: {in: 1..1000}, :allow_nil => true
- validates_uniqueness_of :position, scope: :jam_track_id
validates_uniqueness_of :part, scope: [:jam_track_id, :instrument_id]
# validates :jam_track, presence: true
diff --git a/ruby/lib/jam_ruby/models/mix.rb b/ruby/lib/jam_ruby/models/mix.rb
index 66f0dd38f..d5c7bd364 100644
--- a/ruby/lib/jam_ruby/models/mix.rb
+++ b/ruby/lib/jam_ruby/models/mix.rb
@@ -136,23 +136,43 @@ module JamRuby
def manifest
one_day = 60 * 60 * 24
+ jam_track_offset = 0
+
+ if recording.timeline
+ recording_timeline_data = JSON.parse(recording.timeline)
+
+ recording_start_time = recording_timeline_data["recording_start_time"]
+ jam_track_play_start_time = recording_timeline_data["jam_track_play_start_time"]
+ jam_track_recording_start_play_offset = recording_timeline_data["jam_track_recording_start_play_offset"]
+
+ jam_track_offset = -jam_track_recording_start_play_offset
+ end
+
manifest = { "files" => [], "timeline" => [] }
mix_params = []
+
+ # this 'pick limiter' logic will ensure that we set a limiter on the 1st recorded_track we come across.
+ pick_limiter = false
+ if recording.is_jamtrack_recording?
+ # we only use the limiter feature if this is a JamTrack recording
+ # by setting this to true, the 1st recorded_track in the database will be the limiter
+ pick_limiter = true
+ end
+
recording.recorded_tracks.each do |recorded_track|
- manifest["files"] << { "filename" => recorded_track.sign_url(one_day), "codec" => "vorbis", "offset" => 0 }
- mix_params << { "level" => 100, "balance" => 0 }
- # change to 1.0 level later
+ manifest["files"] << { "filename" => recorded_track.sign_url(one_day), "codec" => "vorbis", "offset" => 0, limiter:pick_limiter }
+ pick_limiter = false
+ mix_params << { "level" => 1.0, "balance" => 0 }
end
recording.recorded_backing_tracks.each do |recorded_backing_track|
manifest["files"] << { "filename" => recorded_backing_track.sign_url(one_day), "codec" => "vorbis", "offset" => 0 }
- mix_params << { "level" => 100, "balance" => 0 }
- # change to 1.0 level later
+ mix_params << { "level" => 1.0, "balance" => 0 }
end
recording.recorded_jam_track_tracks.each do |recorded_jam_track_track|
- manifest["files"] << { "filename" => recorded_jam_track_track.jam_track_track.sign_url(one_day), "codec" => "vorbis", "offset" => 0 }
+ manifest["files"] << { "filename" => recorded_jam_track_track.jam_track_track.sign_url(one_day), "codec" => "vorbis", "offset" => jam_track_offset }
# let's look for level info from the client
level = 1.0 # default value - means no effect
if recorded_jam_track_track.timeline
diff --git a/ruby/lib/jam_ruby/models/music_session.rb b/ruby/lib/jam_ruby/models/music_session.rb
index a29d0b2f9..e2954d9f3 100644
--- a/ruby/lib/jam_ruby/models/music_session.rb
+++ b/ruby/lib/jam_ruby/models/music_session.rb
@@ -307,7 +307,7 @@ module JamRuby
filter_approved = only_approved ? 'AND rrrs.chosen = true' : ''
MusicSession.where(%Q{music_sessions.canceled = FALSE AND
- music_sessions.create_type != '#{CREATE_TYPE_QUICK_START}' AND
+ (music_sessions.create_type is NULL OR music_sessions.create_type != '#{CREATE_TYPE_QUICK_START}') AND
(music_sessions.scheduled_start is NULL OR music_sessions.scheduled_start > NOW() - '4 hour'::INTERVAL) AND
music_sessions.id in (
select distinct(rs.music_session_id)
diff --git a/ruby/lib/jam_ruby/models/playable_play.rb b/ruby/lib/jam_ruby/models/playable_play.rb
index 4631bc4db..a04018689 100644
--- a/ruby/lib/jam_ruby/models/playable_play.rb
+++ b/ruby/lib/jam_ruby/models/playable_play.rb
@@ -2,9 +2,27 @@ module JamRuby
class PlayablePlay < ActiveRecord::Base
self.table_name = "playable_plays"
- belongs_to :playable, :polymorphic => :true, :counter_cache => :play_count
+ belongs_to :playable, :polymorphic => :true
+ # VRFS-2916 jam_tracks.id is varchar: REMOVE
+ #belongs_to :jam_track, :foreign_key => :jam_track_id
belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "player_id"
belongs_to :claimed_recording, :class_name => "JamRuby::ClaimedRecording", :foreign_key => "claimed_recording_id"
+ validate do
+ # VRFS-2916 jam_tracks.id is varchar: REMOVE
+ #if !playable_id && !jam_track_id
+ # self.errors[:base] << 'No playable instance detected'
+ #end
+
+ # VRFS-2916 jam_tracks.id is varchar: ADD
+ if !playable_id
+ self.errors[:base] << 'No playable instance detected'
+ end
+
+ if !user
+ self.errors[:base] << 'No user detected'
+ end
+ end
+
end
end
diff --git a/ruby/lib/jam_ruby/models/recorded_jam_track_track.rb b/ruby/lib/jam_ruby/models/recorded_jam_track_track.rb
index f77615beb..53bad104c 100644
--- a/ruby/lib/jam_ruby/models/recorded_jam_track_track.rb
+++ b/ruby/lib/jam_ruby/models/recorded_jam_track_track.rb
@@ -9,10 +9,11 @@ module JamRuby
validates :user, presence: true
validates :jam_track_track, presence:true
- def self.create_from_jam_track_track(jam_track_track, recording)
+ def self.create_from_jam_track_track(jam_track_track, recording, user)
recorded_jam_track_track = self.new
recorded_jam_track_track.recording = recording
recorded_jam_track_track.jam_track_track = jam_track_track
+ recorded_jam_track_track.user = user
recorded_jam_track_track.save
recorded_jam_track_track
end
diff --git a/ruby/lib/jam_ruby/models/recording.rb b/ruby/lib/jam_ruby/models/recording.rb
index c4537c94d..cdb72816a 100644
--- a/ruby/lib/jam_ruby/models/recording.rb
+++ b/ruby/lib/jam_ruby/models/recording.rb
@@ -49,6 +49,10 @@ module JamRuby
self.comments.size
end
+ def is_jamtrack_recording?
+ !jam_track_id.nil?
+ end
+
def high_quality_mix?
has_final_mix
end
@@ -229,11 +233,13 @@ module JamRuby
if music_session.jam_track
music_session.jam_track.jam_track_tracks.each do |jam_track_track|
- recording.recorded_jam_track_tracks << RecordedJamTrackTrack.create_from_jam_track_track(jam_track_track, recording)
+ recording.recorded_jam_track_tracks << RecordedJamTrackTrack.create_from_jam_track_track(jam_track_track, recording, owner) if jam_track_track.track_type == 'Track'
end
recording.jam_track = music_session.jam_track
recording.jam_track_initiator = music_session.jam_track_initiator
end
+
+ recording.save
end
end
@@ -690,19 +696,17 @@ module JamRuby
end
def add_timeline(timeline)
- tracks = timeline["tracks"]
+ global = timeline["global"]
+ raise JamArgumentError, "global must be specified" unless global
+ tracks = timeline["tracks"]
raise JamArgumentError, "tracks must be specified" unless tracks
+ Recording.where(id: self.id).update_all(timeline: global.to_json)
+
jam_tracks = tracks.select {|track| track["type"] == "jam_track"}
jam_tracks.each do |client_jam_track|
- recorded_jam_track_track = RecordedJamTrackTrack.find_by_jam_track_track_id(client_jam_track["id"])
- if recorded_jam_track_track
- recorded_jam_track_track.timeline = client_jam_track["timeline"].to_json
- recorded_jam_track_track.save!
- else
- @@log.error("unable to find JamTrackTrack with id #{recorded_jam_track_track.id}")
- end
+ RecordedJamTrackTrack.where(recording_id: id, jam_track_track_id: client_jam_track["id"]).update_all(timeline: client_jam_track["timeline"].to_json)
end
end
diff --git a/ruby/lib/jam_ruby/models/shopping_cart.rb b/ruby/lib/jam_ruby/models/shopping_cart.rb
index da1e567f6..b2bbc13ce 100644
--- a/ruby/lib/jam_ruby/models/shopping_cart.rb
+++ b/ruby/lib/jam_ruby/models/shopping_cart.rb
@@ -8,27 +8,105 @@ module JamRuby
validates :cart_id, presence: true
validates :cart_type, presence: true
validates :cart_class_name, presence: true
+ validates :marked_for_redeem, numericality: {only_integer: true}
default_scope order('created_at DESC')
def product_info
product = self.cart_product
- {name: product.name, price: product.price, product_id: cart_id} unless product.nil?
+ {name: product.name, price: product.price, product_id: cart_id, plan_code: product.plan_code, total_price: total_price(product), quantity: quantity, marked_for_redeem: marked_for_redeem} unless product.nil?
+ end
+
+ # multiply (quantity - redeemable) by price
+ def total_price(product)
+ (quantity - marked_for_redeem) * product.price
end
def cart_product
- self.cart_class_name.classify.constantize.find_by_id self.cart_id unless self.cart_class_name.blank?
+ self.cart_class_name.classify.constantize.find_by_id(self.cart_id) unless self.cart_class_name.blank?
end
- def self.create user, product, quantity = 1
+ def redeem(mark_redeem)
+ self.marked_for_redeem = mark_redeem ? 1 : 0
+ end
+
+ def free?
+ marked_for_redeem == quantity
+ end
+
+ def self.create user, product, quantity = 1, mark_redeem = false
cart = ShoppingCart.new
- cart.user = user
+ if user.is_a?(User)
+ cart.user = user
+ else
+ cart.anonymous_user_id = user.id
+ end
+
cart.cart_type = product.class::PRODUCT_TYPE
cart.cart_class_name = product.class.name
cart.cart_id = product.id
cart.quantity = quantity
+ cart.redeem(mark_redeem)
cart.save
cart
end
+
+ # if the user has a redeemable jam_track still on their account, then also check if any shopping carts have already been marked.
+ # if no shpping carts have been marked, then mark it redeemable
+ # should be wrapped in a TRANSACTION
+ def self.user_has_redeemable_jam_track?(any_user)
+ mark_redeem = false
+ if APP_CONFIG.one_free_jamtrack_per_user && any_user.has_redeemable_jamtrack
+ mark_redeem = true # start out assuming we can redeem...
+ any_user.shopping_carts.each do |shopping_cart|
+ # but if we find any shopping cart item already marked for redeem, then back out of mark_redeem=true
+ if shopping_cart.cart_type == JamTrack::PRODUCT_TYPE && shopping_cart.marked_for_redeem > 0
+ mark_redeem = false
+ break
+ end
+ end
+ end
+ mark_redeem
+ end
+
+ # adds a jam_track to cart, checking for promotions
+ def self.add_jam_track_to_cart(any_user, jam_track)
+ cart = nil
+ ShoppingCart.transaction do
+ # does this user already have this JamTrack in their cart? If so, don't add it.
+
+ duplicate_found = false
+ any_user.shopping_carts.each do |shopping_cart|
+ if shopping_cart.cart_type == JamTrack::PRODUCT_TYPE && shopping_cart.cart_id == jam_track.id
+ duplicate_found = true
+ return
+ end
+ end
+
+ unless duplicate_found
+ mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(any_user)
+ cart = ShoppingCart.create(any_user, jam_track, 1, mark_redeem)
+ end
+ end
+ cart
+ end
+
+ # deletes a jam track from the shopping cart, updating redeem flag as necessary
+ def self.remove_jam_track_from_cart(any_user, cart)
+ ShoppingCart.transaction do
+ cart.destroy
+ # check if we should move the redemption
+ mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(any_user)
+
+ carts = any_user.shopping_carts
+
+ # if we find any carts on the account, mark one redeemable
+ if mark_redeem && carts.length > 0
+ carts[0].redeem(mark_redeem)
+ carts[0].save
+ end
+ end
+
+ end
end
end
\ No newline at end of file
diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb
index f2810d985..dec684737 100644
--- a/ruby/lib/jam_ruby/models/user.rb
+++ b/ruby/lib/jam_ruby/models/user.rb
@@ -71,6 +71,11 @@ module JamRuby
has_many :playing_claimed_recordings, :class_name => "JamRuby::ActiveMusicSession", :inverse_of => :claimed_recording_initiator
has_many :playing_jam_tracks, :class_name => "JamRuby::ActiveMusicSession", :inverse_of => :jam_track_initiator
+ # VRFS-2916 jam_tracks.id is varchar: REMOVE
+ # has_many :jam_tracks_played, :class_name => "JamRuby::PlayablePlay", :foreign_key => 'player_id', :conditions => "jam_track_id IS NOT NULL"
+ # VRFS-2916 jam_tracks.id is varchar: ADD
+ has_many :jam_tracks_played, :class_name => "JamRuby::PlayablePlay", :foreign_key => 'player_id', :conditions => ["playable_type = 'JamRuby::JamTrack'"]
+
# self.id = user_id in likes table
has_many :likings, :class_name => "JamRuby::Like", :inverse_of => :user, :dependent => :destroy
@@ -174,6 +179,8 @@ module JamRuby
validates_confirmation_of :password, :if => :should_validate_password?
validates :terms_of_service, :acceptance => {:accept => true, :on => :create, :allow_nil => false }
+ validates :reuse_card, :inclusion => {:in => [true, false]}
+ validates :has_redeemable_jamtrack, :inclusion => {:in => [true, false]}
validates :subscribe_email, :inclusion => {:in => [nil, true, false]}
validates :musician, :inclusion => {:in => [true, false]}
validates :show_whats_next, :inclusion => {:in => [nil, true, false]}
@@ -364,6 +371,10 @@ module JamRuby
MusicSession.scheduled_rsvp(self, true).length
end
+ def purchased_jamtracks_count
+ self.purchased_jam_tracks.count
+ end
+
def joined_score
return nil unless has_attribute?(:score)
a = read_attribute(:score)
@@ -959,6 +970,8 @@ module JamRuby
signup_confirm_url = options[:signup_confirm_url]
affiliate_referral_id = options[:affiliate_referral_id]
recaptcha_failed = options[:recaptcha_failed]
+ any_user = options[:any_user]
+ reuse_card = options[:reuse_card]
user = User.new
@@ -969,6 +982,7 @@ module JamRuby
user.subscribe_email = true
user.terms_of_service = terms_of_service
user.musician = musician
+ user.reuse_card unless reuse_card.nil?
# FIXME: Setting random password for social network logins. This
# is because we have validations all over the place on this.
@@ -1013,6 +1027,9 @@ module JamRuby
user.photo_url = photo_url
+ # copy over the shopping cart to the new user, if a shopping cart is provided
+ user.shopping_carts = any_user.shopping_carts if any_user
+
unless fb_signup.nil?
user.update_fb_authorization(fb_signup)
@@ -1534,6 +1551,11 @@ module JamRuby
stats['audio_latency_avg'] = result['last_jam_audio_latency_avg'].to_f
stats
end
+
+ def destroy_all_shopping_carts
+ ShoppingCart.where("user_id=?", self).destroy_all
+ end
+
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
diff --git a/ruby/lib/jam_ruby/recurly_client.rb b/ruby/lib/jam_ruby/recurly_client.rb
index 96c4df57e..6b12332a0 100644
--- a/ruby/lib/jam_ruby/recurly_client.rb
+++ b/ruby/lib/jam_ruby/recurly_client.rb
@@ -4,7 +4,7 @@ module JamRuby
def initialize()
end
- def create_account(current_user, billing_info=nil)
+ def create_account(current_user, billing_info)
options = account_hash(current_user, billing_info)
account = nil
begin
@@ -12,7 +12,7 @@ module JamRuby
account = Recurly::Account.create(options)
raise RecurlyClientError.new(account.errors) if account.errors.any?
rescue Recurly::Error, NoMethodError => x
- puts "Error: #{x} : #{Kernel.caller}"
+ #puts "Error: #{x} : #{Kernel.caller}"
raise RecurlyClientError, x.to_s
else
if account
@@ -37,7 +37,9 @@ module JamRuby
end
def get_account(current_user)
- (current_user && current_user.recurly_code) ? Recurly::Account.find(current_user.recurly_code) : nil
+ current_user && current_user.recurly_code ? Recurly::Account.find(current_user.recurly_code) : nil
+ rescue Recurly::Error => x
+ raise RecurlyClientError, x.to_s
end
def update_account(current_user, billing_info=nil)
@@ -53,12 +55,35 @@ module JamRuby
account
end
+ def payment_history(current_user)
+ payments = []
+ account = get_account(current_user)
+ if(account.present?)
+ begin
+ account.transactions.find_each do |transaction|
+ if transaction.amount_in_cents > 0 # Account creation adds a transaction record
+ payments << {
+ :created_at => transaction.created_at,
+ :amount_in_cents => transaction.amount_in_cents,
+ :status => transaction.status,
+ :payment_method => transaction.payment_method,
+ :reference => transaction.reference
+ }
+ end
+ end
+ rescue Recurly::Error, NoMethodError => x
+ raise RecurlyClientError, x.to_s
+ end
+ end
+ payments
+ end
+
def update_billing_info(current_user, billing_info=nil)
account = get_account(current_user)
if (account.present?)
begin
- account.billing_info=billing_info
- account.billing_info.save
+ account.billing_info = billing_info
+ account.billing_info.save
rescue Recurly::Error, NoMethodError => x
raise RecurlyClientError, x.to_s
end
@@ -145,7 +170,7 @@ module JamRuby
raise RecurlyClientError.new(plan.errors) if plan.errors.any?
end
- def place_order(current_user, jam_track)
+ def place_order(current_user, jam_track, shopping_cart)
jam_track_right = nil
account = get_account(current_user)
if (account.present?)
@@ -160,13 +185,22 @@ module JamRuby
end
end
+ free = false
+
# this means we already have a subscription, so don't try to create a new one for the same plan (Recurly would fail this anyway)
unless recurly_subscription_uuid
- subscription = Recurly::Subscription.create(:account=>account, :plan_code=>jam_track.plan_code)
+ # if the shopping cart was specified, see if the item should be free
+ free = shopping_cart.nil? ? false : shopping_cart.free?
+ # and if it's free, squish the charge to 0.
+ unit_amount_in_cents = free ? 0 : nil
+ subscription = Recurly::Subscription.create(:account=>account, :plan_code=>jam_track.plan_code, unit_amount_in_cents: unit_amount_in_cents)
raise RecurlyClientError.new(subscription.errors) if subscription.errors.any?
+ # delete from shopping cart the subscription
+ shopping_cart.destroy if shopping_cart
+
# Reload and make sure it went through:
account = get_account(current_user)
@@ -180,11 +214,19 @@ module JamRuby
raise RecurlyClientError, "Plan code '#{paid_subscription.plan_code}' doesn't match jam track: '#{jam_track.plan_code}'" unless recurly_subscription_uuid
- jam_track_right=JamRuby::JamTrackRight.find_or_create_by_user_id_and_jam_track_id(current_user.id, jam_track.id)
+ jam_track_right = JamRuby::JamTrackRight.find_or_create_by_user_id_and_jam_track_id(current_user.id, jam_track.id) do |jam_track_right|
+ jam_track_right.redeemed = free
+ end
+
+ User.where(id: current_user.id).update_all(has_redeemable_jamtrack: false) if free
+
+ # also if the purchase was a free one, then update the user record to no longer allow redeemed jamtracks
+ # this can't go in the block above, as it's here to fix bad subscription UUIDs in an update path
if jam_track_right.recurly_subscription_uuid != recurly_subscription_uuid
jam_track_right.recurly_subscription_uuid = recurly_subscription_uuid
jam_track_right.save
end
+
raise RecurlyClientError.new("Error creating jam_track_right for jam_track: #{jam_track.id}") if jam_track_right.nil?
raise RecurlyClientError.new(jam_track_right.errors) if jam_track_right.errors.any?
rescue Recurly::Error, NoMethodError => x
@@ -198,7 +240,7 @@ module JamRuby
jam_track_right
end
- def find_or_create_account(current_user, billing_info=nil)
+ def find_or_create_account(current_user, billing_info)
account = get_account(current_user)
if(account.nil?)
diff --git a/ruby/lib/jam_ruby/resque/audiomixer.rb b/ruby/lib/jam_ruby/resque/audiomixer.rb
index e0a3b065e..fa987b69f 100644
--- a/ruby/lib/jam_ruby/resque/audiomixer.rb
+++ b/ruby/lib/jam_ruby/resque/audiomixer.rb
@@ -260,6 +260,10 @@ module JamRuby
end
@manifest = symbolize_keys(mix.manifest)
+ @@log.debug("manifest")
+ @@log.debug("--------")
+ @@log.debug(JSON.pretty_generate(@manifest))
+
@manifest[:mix_id] = mix_id # slip in the mix_id so that the job can add it to the ogg comments
# sanity check the manifest
diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb
index 18dbeb8bf..74f0fb924 100644
--- a/ruby/spec/factories.rb
+++ b/ruby/spec/factories.rb
@@ -18,6 +18,7 @@ FactoryGirl.define do
musician true
terms_of_service true
last_jam_audio_latency 5
+ reuse_card true
#u.association :musician_instrument, factory: :musician_instrument, user: u
@@ -583,6 +584,7 @@ FactoryGirl.define do
end
factory :playable_play, :class => JamRuby::PlayablePlay do
+ association :user, factory: :user
end
factory :recording_like, :class => JamRuby::RecordingLiker do
diff --git a/ruby/spec/jam_ruby/models/jam_track_right_spec.rb b/ruby/spec/jam_ruby/models/jam_track_right_spec.rb
index e1e99c3f2..530535f8c 100644
--- a/ruby/spec/jam_ruby/models/jam_track_right_spec.rb
+++ b/ruby/spec/jam_ruby/models/jam_track_right_spec.rb
@@ -64,6 +64,7 @@ describe JamTrackRight do
s3.upload(jam_track_track.manually_uploaded_filename(:url_48), ogg_path)
jam_track_track[:url_48] = jam_track_track.manually_uploaded_filename(:url_48)
jam_track_track.save!
+
jam_track_track[:url_48].should == jam_track_track.manually_uploaded_filename(:url_48)
# verify it's on S3
@@ -97,7 +98,7 @@ describe JamTrackRight do
end
it "bogus key" do
- JamTrackRight.list_keys(user, [2112]).should eq([])
+ JamTrackRight.list_keys(user, ['2112']).should eq([])
end
it "valid track with no rights to it by querying user" do
diff --git a/ruby/spec/jam_ruby/models/jam_track_spec.rb b/ruby/spec/jam_ruby/models/jam_track_spec.rb
index 063f3a029..10cec40cd 100644
--- a/ruby/spec/jam_ruby/models/jam_track_spec.rb
+++ b/ruby/spec/jam_ruby/models/jam_track_spec.rb
@@ -14,6 +14,30 @@ describe JamTrack do
jam_track.licensor.jam_tracks.should == [jam_track]
end
+ describe 'plays' do
+ it "creates played instance properly" do
+ @jam_track = FactoryGirl.create(:jam_track)
+ play = PlayablePlay.new
+
+ # VRFS-2916 jam_tracks.id is varchar: REMOVE
+ # play.jam_track = @jam_track
+ # VRFS-2916 jam_tracks.id is varchar: ADD
+ play.playable = @jam_track
+
+ play.user = user
+ play.save!
+ expect(@jam_track.plays.count).to eq(1)
+ expect(@jam_track.plays[0].user.id).to eq(user.id)
+ expect(user.jam_tracks_played.count).to eq(1)
+ end
+ it "handles played errors" do
+ play = PlayablePlay.new
+ play.user = user
+ play.save
+ expect(play.errors.count).to eq(1)
+ end
+ end
+
describe "index" do
it "empty query" do
query, pager = JamTrack.index({}, user)
diff --git a/ruby/spec/jam_ruby/models/jam_track_track_spec.rb b/ruby/spec/jam_ruby/models/jam_track_track_spec.rb
index bce440ad8..6fb4343f6 100644
--- a/ruby/spec/jam_ruby/models/jam_track_track_spec.rb
+++ b/ruby/spec/jam_ruby/models/jam_track_track_spec.rb
@@ -16,7 +16,7 @@ describe JamTrackTrack do
jam_track_track_1 = FactoryGirl.create(:jam_track_track, position: 1, jam_track: jam_track)
jam_track_track_2 = FactoryGirl.build(:jam_track_track, position: 1, jam_track: jam_track)
jam_track_track_2.valid?.should == false
- jam_track_track_2.errors[:position].should == ['has already been taken']
+ #jam_track_track_2.errors[:position].should == ['has already been taken']
end
it "jam_track required" do
diff --git a/ruby/spec/jam_ruby/models/music_session_spec.rb b/ruby/spec/jam_ruby/models/music_session_spec.rb
index 6273855d9..e152fed55 100644
--- a/ruby/spec/jam_ruby/models/music_session_spec.rb
+++ b/ruby/spec/jam_ruby/models/music_session_spec.rb
@@ -854,6 +854,13 @@ describe MusicSession do
music_session_1.rsvp_slots[0].rsvp_requests_rsvp_slots[0].save!
MusicSession.scheduled_rsvp(creator_1, true).should == []
end
+
+ it "create_type = nil will still return RSVPs" do
+ music_session_1.create_type = nil
+ music_session_1.save!
+
+ MusicSession.scheduled_rsvp(creator_1, true).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 3d8515f29..7b64b2144 100644
--- a/ruby/spec/jam_ruby/models/recording_spec.rb
+++ b/ruby/spec/jam_ruby/models/recording_spec.rb
@@ -1080,6 +1080,7 @@ describe Recording do
let(:recording) {recorded_jam_track_track.recording}
let(:timeline_data) {{"sample" => "data"}}
let(:good_timeline) { {
+ "global" => {"recording_start_time" => 0, "jam_track_play_start_time" => 0, "jam_track_recording_start_play_offset" => 0},
"tracks" => [
{
"id" => recorded_jam_track_track.jam_track_track.id,
diff --git a/ruby/spec/jam_ruby/models/shopping_cart_spec.rb b/ruby/spec/jam_ruby/models/shopping_cart_spec.rb
index db3f4d75c..9daf3d449 100644
--- a/ruby/spec/jam_ruby/models/shopping_cart_spec.rb
+++ b/ruby/spec/jam_ruby/models/shopping_cart_spec.rb
@@ -4,6 +4,7 @@ describe ShoppingCart do
let(:user) { FactoryGirl.create(:user) }
let(:jam_track) {FactoryGirl.create(:jam_track) }
+ let(:jam_track2) {FactoryGirl.create(:jam_track) }
before(:each) do
ShoppingCart.delete_all
@@ -20,4 +21,48 @@ describe ShoppingCart do
user.shopping_carts[0].quantity.should == 1
end
+ it "should not add duplicate JamTrack to ShoppingCart" do
+ cart1 = ShoppingCart.add_jam_track_to_cart(user, jam_track)
+ cart1.should_not be_nil
+ user.reload
+ cart2 = ShoppingCart.add_jam_track_to_cart(user, jam_track)
+ cart2.should be_nil
+
+ end
+
+ describe "redeemable behavior" do
+ it "adds redeemable item to shopping cart" do
+
+ user.has_redeemable_jamtrack.should be_true
+
+ # first item added to shopping cart should be marked for redemption
+ cart = ShoppingCart.add_jam_track_to_cart(user, jam_track)
+ cart.marked_for_redeem.should eq(1)
+
+ # but the second item should not
+
+ user.reload
+
+ cart = ShoppingCart.add_jam_track_to_cart(user, jam_track2)
+ cart.marked_for_redeem.should eq(0)
+ end
+
+ it "removes redeemable item to shopping cart" do
+
+ user.has_redeemable_jamtrack.should be_true
+ cart1 = ShoppingCart.add_jam_track_to_cart(user, jam_track)
+ cart1.should_not be_nil
+ user.reload
+ cart2 = ShoppingCart.add_jam_track_to_cart(user, jam_track2)
+ cart2.should_not be_nil
+
+ cart1.marked_for_redeem.should eq(1)
+ cart2.marked_for_redeem.should eq(0)
+ ShoppingCart.remove_jam_track_from_cart(user, jam_track)
+
+ user.shopping_carts.length.should eq(1)
+ cart2.reload
+ cart1.marked_for_redeem.should eq(1)
+ end
+ end
end
diff --git a/ruby/spec/jam_ruby/recurly_client_spec.rb b/ruby/spec/jam_ruby/recurly_client_spec.rb
index 0245c91e9..5597f9894 100644
--- a/ruby/spec/jam_ruby/recurly_client_spec.rb
+++ b/ruby/spec/jam_ruby/recurly_client_spec.rb
@@ -88,21 +88,23 @@ describe RecurlyClient do
end
it "can place order" do
+ history_items = @client.payment_history(@user).length
@client.find_or_create_account(@user, @billing_info)
- expect{@client.place_order(@user, @jamtrack)}.not_to raise_error()
+ expect{@client.place_order(@user, @jamtrack, nil)}.not_to raise_error()
subs = @client.get_account(@user).subscriptions
subs.should_not be_nil
subs.should have(1).items
@user.jam_track_rights.should_not be_nil
@user.jam_track_rights.should have(1).items
@user.jam_track_rights.last.jam_track.id.should eq(@jamtrack.id)
+ @client.payment_history(@user).should have(history_items+1).items
end
it "can refund subscription" do
@client.find_or_create_account(@user, @billing_info)
# Place order:
- expect{@client.place_order(@user, @jamtrack)}.not_to raise_error()
+ expect{@client.place_order(@user, @jamtrack, nil)}.not_to raise_error()
active_subs=@client.get_account(@user).subscriptions.find_all{|t|t.state=='active'}
@jamtrack.reload
@jamtrack.jam_track_rights.should have(1).items
@@ -118,10 +120,10 @@ describe RecurlyClient do
it "detects error on double order" do
@client.find_or_create_account(@user, @billing_info)
- jam_track_right = @client.place_order(@user, @jamtrack)
+ jam_track_right = @client.place_order(@user, @jamtrack, nil)
jam_track_right.recurly_subscription_uuid.should_not be_nil
- jam_track_right2 = @client.place_order(@user, @jamtrack)
+ jam_track_right2 = @client.place_order(@user, @jamtrack, nil)
jam_track_right.should eq(jam_track_right2)
jam_track_right.recurly_subscription_uuid.should eq(jam_track_right.recurly_subscription_uuid)
end
diff --git a/ruby/spec/jam_ruby/resque/jam_tracks_builder_spec.rb b/ruby/spec/jam_ruby/resque/jam_tracks_builder_spec.rb
index b737f574e..7ead35e3a 100644
--- a/ruby/spec/jam_ruby/resque/jam_tracks_builder_spec.rb
+++ b/ruby/spec/jam_ruby/resque/jam_tracks_builder_spec.rb
@@ -32,7 +32,7 @@ describe JamTracksBuilder do
jam_track_track.save!
jam_track_track[:url_48].should == jam_track_track.manually_uploaded_filename(:url_48)
-
+
# verify it's on S3
@s3.exists?(jam_track_track[:url_48]).should be_true
@s3.length(jam_track_track[:url_48]).should == File.size?(ogg_path)
diff --git a/ruby/spec/support/utilities.rb b/ruby/spec/support/utilities.rb
index cab08fab2..94294b86c 100644
--- a/ruby/spec/support/utilities.rb
+++ b/ruby/spec/support/utilities.rb
@@ -166,6 +166,10 @@ def app_config
20 # 20 seconds
end
+ def one_free_jamtrack_per_user
+ true
+ end
+
private
diff --git a/web/Gemfile b/web/Gemfile
index 0405c352f..c7cb8debf 100644
--- a/web/Gemfile
+++ b/web/Gemfile
@@ -56,6 +56,7 @@ gem 'aasm', '3.0.16'
gem 'carrierwave', '0.9.0'
gem 'carrierwave_direct'
gem 'fog'
+gem 'jquery-payment-rails'
gem 'haml-rails'
gem 'unf' #optional fog dependency
gem 'devise', '3.3.0' #3.4.0 causes uninitialized constant ActionController::Metal (NameError)
diff --git a/web/README.md b/web/README.md
index 4548ce76b..975118c93 100644
--- a/web/README.md
+++ b/web/README.md
@@ -1,5 +1,5 @@
-
Jasmine Javascript Unit Tests
=============================
Open browser to localhost:3000/teaspoon
+
diff --git a/web/app/assets/images/content/bkg_home_guitar.jpg b/web/app/assets/images/content/bkg_home_guitar.jpg
new file mode 100644
index 000000000..b67797dd8
Binary files /dev/null and b/web/app/assets/images/content/bkg_home_guitar.jpg differ
diff --git a/web/app/assets/images/content/bkg_home_guitar_x.jpg b/web/app/assets/images/content/bkg_home_guitar_x.jpg
new file mode 100644
index 000000000..e76ec6a83
Binary files /dev/null and b/web/app/assets/images/content/bkg_home_guitar_x.jpg differ
diff --git a/web/app/assets/images/content/checkmark.png b/web/app/assets/images/content/checkmark.png
index 8ce5e42ee..628f05a1f 100644
Binary files a/web/app/assets/images/content/checkmark.png and b/web/app/assets/images/content/checkmark.png differ
diff --git a/web/app/assets/images/content/icon_shopping_cart.png b/web/app/assets/images/content/icon_shopping_cart.png
index 24bf9b09f..511fe7c77 100644
Binary files a/web/app/assets/images/content/icon_shopping_cart.png and b/web/app/assets/images/content/icon_shopping_cart.png differ
diff --git a/web/app/assets/images/content/shopping-cart.png b/web/app/assets/images/content/shopping-cart.png
index eef1a4c69..62e6cc2fb 100644
Binary files a/web/app/assets/images/content/shopping-cart.png and b/web/app/assets/images/content/shopping-cart.png differ
diff --git a/web/app/assets/images/up_arrow.png b/web/app/assets/images/up_arrow.png
new file mode 100644
index 000000000..6f3c95d68
Binary files /dev/null and b/web/app/assets/images/up_arrow.png differ
diff --git a/web/app/assets/javascripts/accounts.js b/web/app/assets/javascripts/accounts.js
index 64501695d..5df29a6bd 100644
--- a/web/app/assets/javascripts/accounts.js
+++ b/web/app/assets/javascripts/accounts.js
@@ -33,6 +33,10 @@
}
}
+ function licenseDetail(userDetail) {
+ return (userDetail.purchased_jamtracks_count==0) ? "You don't currently own any JamTracks" : 'You currently own a license to use ' + userDetail.purchased_jamtracks_count + " JamTracks"
+ }
+
function populateAccount(userDetail) {
var validProfiles = prettyPrintAudioProfiles(context.JK.getGoodConfigMap());
@@ -42,8 +46,10 @@
var $template = $(context._.template($('#template-account-main').html(), {
email: userDetail.email,
name: userDetail.name,
+ licenseDetail: licenseDetail(userDetail),
location : userDetail.location,
session : sessionSummary,
+ paymentMethod: "mastercard",
instruments : prettyPrintInstruments(userDetail.instruments),
photoUrl : context.JK.resolveAvatarUrl(userDetail.photo_url),
validProfiles : validProfiles,
@@ -94,20 +100,28 @@
// events for main screen
function events() {
- // wire up main panel clicks
+ // wire up main panel clicks:
$('#account-content-scroller').on('click', '#account-scheduled-sessions-link', function(evt) { evt.stopPropagation(); navToScheduledSessions(); return false; } );
+ $('#account-content-scroller').on('click', '#account-my-jamtracks-link', function(evt) { evt.stopPropagation(); navToMyJamTracks(); return false; } );
+
$('#account-content-scroller').on('click', '#account-edit-identity-link', function(evt) { evt.stopPropagation(); navToEditIdentity(); return false; } );
$('#account-content-scroller').on('click', '#account-edit-profile-link', function(evt) { evt.stopPropagation(); navToEditProfile(); return false; } );
$('#account-content-scroller').on('click', '#account-edit-subscriptions-link', function(evt) { evt.stopPropagation(); navToEditSubscriptions(); return false; } );
$('#account-content-scroller').on('click', '#account-edit-payments-link', function(evt) { evt.stopPropagation(); navToEditPayments(); return false; } );
$('#account-content-scroller').on('click', '#account-edit-audio-link', function(evt) { evt.stopPropagation(); navToEditAudio(); return false; } );
$('#account-content-scroller').on('avatar_changed', '#profile-avatar', function(evt, newAvatarUrl) { evt.stopPropagation(); updateAvatar(newAvatarUrl); return false; })
+
+ // License dialog:
+ $("#account-content-scroller").on('click', '#account-view-license-link', function(evt) {evt.stopPropagation(); app.layout.showDialog('jamtrack-license-dialog'); return false; } );
+ $("#account-content-scroller").on('click', '#account-payment-history-link', function(evt) {evt.stopPropagation(); app.layout.showDialog('jamtrack-payment-history-dialog'); return false; } );
}
function renderAccount() {
+ app.user().done(function() {
rest.getUserDetail()
- .done(populateAccount)
- .error(app.ajaxError)
+ .done(populateAccount)
+ .error(app.ajaxError)
+ })
}
function navToScheduledSessions() {
@@ -115,6 +129,11 @@
window.location = '/client#/account/sessions'
}
+ function navToMyJamTracks() {
+ resetForm();
+ window.location = '/client#/account/jamtracks'
+ }
+
function navToEditIdentity() {
resetForm()
window.location = '/client#/account/identity'
@@ -126,7 +145,7 @@
}
function navToEditSubscriptions() {
-
+ window.location = '/client#/account/profile'
}
function navToEditPayments() {
diff --git a/web/app/assets/javascripts/accounts_jamtracks.js.coffee b/web/app/assets/javascripts/accounts_jamtracks.js.coffee
new file mode 100644
index 000000000..43e1e51b9
--- /dev/null
+++ b/web/app/assets/javascripts/accounts_jamtracks.js.coffee
@@ -0,0 +1,103 @@
+$ = jQuery
+context = window
+context.JK ||= {}
+
+context.JK.AccountJamTracks = class AccountJamTracks
+ constructor: (@app) ->
+ @rest = context.JK.Rest()
+ @client = context.jamClient
+ @logger = context.JK.logger
+ @screen = null
+ @userId = context.JK.currentUserId;
+
+ initialize:() =>
+ screenBindings =
+ 'beforeShow': @beforeShow
+ 'afterShow': @afterShow
+ @app.bindScreen('account/jamtracks', screenBindings)
+ @screen = $('#account-jamtracks')
+
+ beforeShow:() =>
+ @logger.debug("beforeShow")
+ rest.getPurchasedJamTracks({})
+ .done(@populateJamTracks)
+ .fail(@app.ajaxError);
+
+ afterShow:() =>
+ @logger.debug("afterShow")
+
+ populateJamTracks:(data) =>
+ @logger.debug("populateJamTracks", data)
+ template = context._.template($('#template-account-jamtrack').html(), {jamtracks:data.jamtracks}, { variable: 'data' })
+
+ # template = context._.template($('#template-account-jamtrack').html(), {
+ # jamtracks: data.jamtracks
+ # current_user: @userId
+ # }, variable: 'data')
+ @logger.debug("TEMPLATE", template)
+ this.appendJamTracks template
+ @screen.find('.jamtrack-solo-session').on 'click', @soloSession
+ @screen.find('.jamtrack-group-session').on 'click', @groupSession
+
+ appendJamTracks:(template) =>
+ $('#account-my-jamtracks table tbody').replaceWith template
+
+ soloSession:(e) =>
+ #context.location="client#/createSession"
+ @logger.debug "BLEH", e
+ jamRow = $(e.target).parents("tr")
+ @logger.debug "BLEH2", e, jamRow.data()
+ @createSession(jamRow.data(), true)
+ #@logger.debug "BLEH", $(this), $(this).data()
+
+ groupSession:(e) =>
+ #context.location="client#/createSession"
+ jamRow = $(e.target).parents("tr")
+ @createSession(jamRow.data(), false)
+
+ createSession:(sessionData, solo) =>
+ tracks = context.JK.TrackHelpers.getUserTracks(context.jamClient)
+
+ if (context.JK.guardAgainstBrowser(@app))
+ @logger.debug("CRATING SESSION", sessionData.genre, solo)
+ data = {}
+ data.client_id = @app.clientId
+ #data.description = $('#description').val()
+ data.description = "Jam Track Session"
+ data.as_musician = true
+ data.legal_terms = true
+ data.intellectual_property = true
+ data.approval_required = false
+ data.musician_access = !solo
+ data.fan_access = false
+ data.fan_chat = false
+ data.genre = [sessionData.genre]
+ data.genres = [sessionData.genre]
+ # data.genres = context.JK.GenreSelectorHelper.getSelectedGenres('#create-session-genre')
+ # data.musician_access = if $('#musician-access option:selected').val() == 'true' then true else false
+ # data.approval_required = if $('input[name=\'musician-access-option\']:checked').val() == 'true' then true else false
+ # data.fan_access = if $('#fan-access option:selected').val() == 'true' then true else false
+ # data.fan_chat = if $('input[name=\'fan-chat-option\']:checked').val() == 'true' then true else false
+ # if $('#band-list option:selected').val() != ''
+ # data.band = $('#band-list option:selected').val()
+ data.audio_latency = context.jamClient.FTUEGetExpectedLatency().latency
+ data.tracks = tracks
+
+ rest.legacyCreateSession(data).done((response) =>
+ newSessionId = response.id
+ context.location = '/client#/session/' + newSessionId
+ # Re-loading the session settings will cause the form to reset with the right stuff in it.
+ # This is an extra xhr call, but it keeps things to a single codepath
+ loadSessionSettings()
+ context.JK.GA.trackSessionCount data.musician_access, data.fan_access, invitationCount
+ context.JK.GA.trackSessionMusicians context.JK.GA.SessionCreationTypes.create
+ ).fail (jqXHR) =>
+ handled = false
+ if jqXHR.status = 422
+ response = JSON.parse(jqXHR.responseText)
+ if response['errors'] and response['errors']['tracks'] and response['errors']['tracks'][0] == 'Please select at least one track'
+ @app.notifyAlert 'No Inputs Configured', $('You will need to reconfigure your audio device.')
+ handled = true
+ if !handled
+ @app.notifyServerError jqXHR, 'Unable to Create Session'
+
\ No newline at end of file
diff --git a/web/app/assets/javascripts/application.js b/web/app/assets/javascripts/application.js
index 037c2c1b7..73a57daad 100644
--- a/web/app/assets/javascripts/application.js
+++ b/web/app/assets/javascripts/application.js
@@ -35,6 +35,7 @@
//= require jquery.browser
//= require jquery.custom-protocol
//= require jquery.exists
+//= require jquery.payment
//= require jstz
//= require class
//= require AAC_underscore
diff --git a/web/app/assets/javascripts/checkout_order.js b/web/app/assets/javascripts/checkout_order.js
new file mode 100644
index 000000000..9024f5745
--- /dev/null
+++ b/web/app/assets/javascripts/checkout_order.js
@@ -0,0 +1,385 @@
+(function (context, $) {
+
+ "use strict";
+ context.JK = context.JK || {};
+ context.JK.CheckoutOrderScreen = function (app) {
+
+ var EVENTS = context.JK.EVENTS;
+ var logger = context.JK.logger;
+ var rest = context.JK.Rest();
+ var jamTrackUtils = context.JK.JamTrackUtils;
+
+ var $screen = null;
+ var $navigation = null;
+ var $templateOrderContent = null;
+ var $templatePurchasedJamTrack = null;
+ var $orderPanel = null;
+ var $thanksPanel = null;
+ var $jamTrackInBrowser = null;
+ var $purchasedJamTrack = null;
+ var $purchasedJamTrackHeader = null;
+ var $purchasedJamTracks = null;
+ var $orderContent = null;
+ var userDetail = null;
+ var step = null;
+ var downloadJamTracks = [];
+ var purchasedJamTracks = null;
+ var purchasedJamTrackIterator = 0;
+ var $backBtn = null;
+ var $orderPrompt = null;
+ var $emptyCartPrompt = null;
+ var $noAccountInfoPrompt = null;
+
+
+ function beforeShow() {
+ beforeShowOrder();
+ }
+
+
+ function afterShow(data) {
+
+ }
+
+
+ function beforeHide() {
+ if(downloadJamTracks) {
+ context._.each(downloadJamTracks, function(downloadJamTrack) {
+ downloadJamTrack.destroy();
+ downloadJamTrack.root.remove();
+ })
+
+ downloadJamTracks = [];
+ }
+ purchasedJamTracks = null;
+ purchasedJamTrackIterator = 0;
+ }
+
+ function beforeShowOrder() {
+ $orderPrompt.addClass('hidden')
+ $emptyCartPrompt.addClass('hidden')
+ $noAccountInfoPrompt.addClass('hidden')
+ $orderPanel.removeClass("hidden")
+ $thanksPanel.addClass("hidden")
+ $screen.find(".place-order").addClass('disabled').off('click', placeOrder)
+ $("#order_error").text('').addClass("hidden")
+ step = 3;
+ renderNavigation();
+ populateOrderPage();
+ }
+
+
+ function populateOrderPage() {
+ clearOrderPage();
+
+ rest.getShoppingCarts()
+ .done(function(carts) {
+ rest.getBillingInfo()
+ .done(function(billingInfo) {
+ renderOrderPage(carts, billingInfo)
+ })
+ .fail(function(jqXHR) {
+ if(jqXHR.status == 404) {
+ // no account for this user
+ $noAccountInfoPrompt.removeClass('hidden')
+ app.notify({ title: "No account information",
+ text: "Please restart the checkout process." },
+ null,
+ true);
+ }
+ })
+ })
+ .fail(app.ajaxError);
+ }
+
+
+ function renderOrderPage(carts, recurlyAccountInfo) {
+ logger.debug("rendering order page")
+ var data = {}
+
+ var sub_total = 0.0
+ var taxes = 0.0
+ $.each(carts, function(index, cart) {
+ sub_total += parseFloat(cart.product_info.total_price)
+ });
+ if(carts.length == 0) {
+ data.grand_total = '-.--'
+ data.sub_total = '-.--'
+ data.taxes = '-.--'
+ data.shipping_handling = '-.--'
+ }
+ else {
+ data.grand_total = 'Calculating...'
+ data.sub_total = '$' + sub_total.toFixed(2)
+ data.taxes = 'Calculating...'
+ data.shipping_handling = '$0.00'
+ }
+
+ data.carts = carts
+ data.billing_info = recurlyAccountInfo.billing_info
+ data.shipping_info = recurlyAccountInfo.address
+
+ data.shipping_as_billing = true; //jamTrackUtils.compareAddress(data.billing_info, data.shipping_info);
+
+ var orderContentHtml = $(
+ context._.template(
+ $templateOrderContent.html(),
+ data,
+ {variable: 'data'}
+ )
+ )
+
+ $orderContent.append(orderContentHtml)
+ $orderPanel.find(".change-payment-info").on('click', moveToPaymentInfo)
+
+ var $placeOrder = $screen.find(".place-order")
+ if(carts.length == 0) {
+ $orderPrompt.addClass('hidden')
+ $emptyCartPrompt.removeClass('hidden')
+ $noAccountInfoPrompt.addClass('hidden')
+ $placeOrder.addClass('disabled')
+ }
+ else {
+ logger.debug("cart has " + carts.length + " items in it")
+ $orderPrompt.removeClass('hidden')
+ $emptyCartPrompt.addClass('hidden')
+ $noAccountInfoPrompt.addClass('hidden')
+ $placeOrder.removeClass('disabled').on('click', placeOrder)
+
+ var planPricing = {}
+
+ context._.each(carts, function(cart) {
+ var priceElement = $screen.find('.order-right-page .plan[data-plan-code="' + cart.product_info.plan_code +'"]')
+
+ if(priceElement.length == 0) {
+ logger.error("unable to find price element for " + cart.product_info.plan_code, cart);
+ app.notify({title: "Error Encountered", text: "Unable to find plan info for " + cart.product_info.plan_code})
+ return false;
+ }
+
+ logger.debug("creating recurly pricing element for plan: " + cart.product_info.plan_code)
+ var pricing = context.recurly.Pricing();
+ pricing.plan_code = cart.product_info.plan_code;
+ pricing.resolved = false;
+ pricing.effective_quantity = cart.product_info.quantity - cart.product_info.marked_for_redeem
+ planPricing[pricing.plan_code] = pricing;
+
+ // this is called when the plan is resolved against Recurly. It will have tax info, which is the only way we can get it.
+ pricing.on('change', function(price) {
+
+ var resolvedPrice = planPricing[this.plan_code];
+ if(!resolvedPrice) {
+ logger.error("unable to find price info in storage")
+ app.notify({title: "Error Encountered", text: "Unable to find plan info in storage"})
+ return;
+ }
+ else {
+ logger.debug("pricing resolved for plan: " + this.plan_code)
+ }
+ resolvedPrice.resolved = true;
+
+ var allResolved = true;
+ var totalTax = 0;
+ var totalPrice = 0;
+
+ // let's see if all plans have been resolved via API; and add up total price and taxes for display
+ $.each(planPricing, function(plan_code, priceObject) {
+ logger.debug("resolved recurly priceObject", priceObject)
+
+ if(!priceObject.resolved) {
+ allResolved = false;
+ return false;
+ }
+ else {
+ var unitTax = Number(priceObject.price.now.tax) * priceObject.effective_quantity;
+ totalTax += unitTax;
+
+ var totalUnitPrice = Number(priceObject.price.now.total) * priceObject.effective_quantity;
+ totalPrice += totalUnitPrice;
+ }
+ })
+
+ if(allResolved) {
+ $screen.find('.order-right-page .order-items-value.taxes').text('$' + totalTax.toFixed(2))
+ $screen.find('.order-right-page .order-items-value.grand-total').text('$' + totalPrice.toFixed(2))
+ }
+ else
+ {
+ logger.debug("still waiting on more plans to resolve")
+ }
+ })
+ pricing.attach(priceElement.eq(0))
+ })
+ }
+ }
+
+ function moveToPaymentInfo() {
+ context.location = '/client#/checkoutPayment';
+ return false;
+ }
+
+ function placeOrder(e) {
+ e.preventDefault();
+ $screen.find(".place-order").off('click').addClass('disabled')
+ $("#order_error").text('').addClass("hidden")
+ rest.placeOrder()
+ .done(moveToThanks)
+ .fail(orderErrorHandling);
+ }
+
+
+ function orderErrorHandling(xhr, ajaxOptions, thrownError) {
+ if (xhr && xhr.responseJSON) {
+ var message = "Error submitting payment: "
+ $.each(xhr.responseJSON.errors, function (key, error) {
+ message += key + ": " + error
+ })
+ $("#order_error").text(message).removeClass("hidden")
+ }
+ else {
+ $("#order_error").text(xhr.responseText).removeClass("hidden")
+ }
+ $screen.find(".place-order").on('click', placeOrder).removeClass('disabled')
+ }
+
+ function moveToThanks(purchaseResponse) {
+ $("#order_error").addClass("hidden")
+ $orderPanel.addClass("hidden")
+ $thanksPanel.removeClass("hidden")
+ jamTrackUtils.checkShoppingCart()
+ handleJamTracksPurchased(purchaseResponse.jam_tracks)
+ }
+
+ function handleJamTracksPurchased(jamTracks) {
+ // were any JamTracks purchased?
+ var jamTracksPurchased = jamTracks && jamTracks.length > 0;
+ if(jamTracksPurchased) {
+ if(gon.isNativeClient) {
+ startDownloadJamTracks(jamTracks)
+ }
+ else {
+ $jamTrackInBrowser.removeClass('hidden');
+ }
+ }
+ }
+
+ function startDownloadJamTracks(jamTracks) {
+ // there can be multiple purchased JamTracks, so we cycle through them
+
+ purchasedJamTracks = jamTracks;
+
+ // populate list of jamtracks purchased, that we will iterate through graphically
+ context._.each(jamTracks, function(jamTrack) {
+ var downloadJamTrack = new context.JK.DownloadJamTrack(app, jamTrack, 'small');
+ var $purchasedJamTrack = $(context._.template(
+ $templatePurchasedJamTrack.html(),
+ jamTrack,
+ {variable: 'data'}
+ ));
+
+ $purchasedJamTracks.append($purchasedJamTrack)
+
+ // show it on the page
+ $purchasedJamTrack.append(downloadJamTrack.root)
+
+ downloadJamTracks.push(downloadJamTrack)
+ })
+
+ iteratePurchasedJamTracks();
+ }
+
+ function iteratePurchasedJamTracks() {
+ if(purchasedJamTrackIterator < purchasedJamTracks.length ) {
+ var downloadJamTrack = downloadJamTracks[purchasedJamTrackIterator++];
+
+ // make sure the 'purchasing JamTrack' section can be seen
+ $purchasedJamTrack.removeClass('hidden');
+
+ // the widget indicates when it gets to any transition; we can hide it once it reaches completion
+ $(downloadJamTrack).on(EVENTS.JAMTRACK_DOWNLOADER_STATE_CHANGED, function(e, data) {
+
+ if(data.state == downloadJamTrack.states.synchronized) {
+ logger.debug("jamtrack " + downloadJamTrack.jamTrack.name + " synchronized;")
+ //downloadJamTrack.root.remove();
+ downloadJamTrack.destroy();
+
+ // go to the next JamTrack
+ iteratePurchasedJamTracks()
+ }
+ })
+
+ logger.debug("jamtrack " + downloadJamTrack.jamTrack.name + " downloader initializing")
+
+ // kick off the download JamTrack process
+ downloadJamTrack.init()
+
+ // XXX style-test code
+ // downloadJamTrack.transitionError("package-error", "The server failed to create your package.")
+
+ }
+ else {
+ logger.debug("done iterating over purchased JamTracks")
+ $purchasedJamTrackHeader.text('All purchased JamTracks have been downloaded successfully! You can now play them in a session.')
+ }
+ }
+
+ function clearOrderPage() {
+ $orderContent.empty();
+ }
+
+ function renderNavigation() {
+ $navigation.html("");
+ var navigationHtml = $(
+ context._.template(
+ $('#template-checkout-navigation').html(),
+ {current: step},
+ {variable: 'data'}
+ )
+ );
+
+ $navigation.append(navigationHtml);
+ }
+
+ function events() {
+ $backBtn.on('click', function(e) {
+ e.preventDefault();
+
+ context.location = '/client#/checkoutPayment'
+ })
+ }
+
+ function initialize() {
+ var screenBindings = {
+ 'beforeShow': beforeShow,
+ 'afterShow': afterShow,
+ 'beforeHide': beforeHide
+ };
+ app.bindScreen('checkoutOrder', screenBindings);
+
+ $screen = $("#checkoutOrderScreen");
+ $navigation = $screen.find(".checkout-navigation-bar");
+ $templateOrderContent = $("#template-order-content");
+ $templatePurchasedJamTrack = $('#template-purchased-jam-track');
+ $orderPanel = $screen.find(".order-panel");
+ $thanksPanel = $screen.find(".thanks-panel");
+ $jamTrackInBrowser = $screen.find(".thanks-detail.jam-tracks-in-browser");
+ $purchasedJamTrack = $thanksPanel.find(".thanks-detail.purchased-jam-track");
+ $purchasedJamTrackHeader = $purchasedJamTrack.find(".purchased-jam-track-header");
+ $purchasedJamTracks = $purchasedJamTrack.find(".purchased-list")
+ $backBtn = $screen.find('.back');
+ $orderPrompt = $screen.find('.order-prompt');
+ $emptyCartPrompt = $screen.find('.empty-cart-prompt');
+ $noAccountInfoPrompt = $screen.find('.no-account-info-prompt');
+ $orderContent = $orderPanel.find(".order-content");
+
+ if ($screen.length == 0) throw "$screen must be specified";
+ if ($navigation.length == 0) throw "$navigation must be specified";
+
+ events();
+ }
+
+ this.initialize = initialize;
+
+ return this;
+ }
+})
+(window, jQuery);
\ No newline at end of file
diff --git a/web/app/assets/javascripts/checkout_payment.js b/web/app/assets/javascripts/checkout_payment.js
new file mode 100644
index 000000000..7c40ced26
--- /dev/null
+++ b/web/app/assets/javascripts/checkout_payment.js
@@ -0,0 +1,667 @@
+(function(context,$) {
+
+ "use strict";
+ context.JK = context.JK || {};
+ context.JK.CheckoutPaymentScreen = function(app) {
+
+ var EVENTS = context.JK.EVENTS;
+ var logger = context.JK.logger;
+ var jamTrackUtils = context.JK.JamTrackUtils;
+
+ var $screen = null;
+ var $navigation = null;
+ var $billingInfo = null;
+ var $shippingInfo = null;
+ var $paymentMethod = null;
+ var $shippingAddress = null;
+ var $shippingAsBilling = null;
+ var $paymentInfoPanel = null;
+ var $accountSignup = null;
+ var userDetail = null;
+ var step = null;
+ var billing_info = null;
+ var shipping_info = null;
+ var shipping_as_billing = null;
+ var $reuseExistingCard = null;
+ var $reuseExistingCardChk = null;
+ var $existingCardEndsWith = null;
+ var $newCardInfo = null;
+ var selectCountry = null;
+ var selectCountryLoaded = false;
+ var $freeJamTrackPrompt = null;
+ var $noFreeJamTrackPrompt = null;
+
+ function afterShow() {
+
+ beforeShowPaymentInfo();
+ }
+
+ function beforeShowPaymentInfo() {
+ step = 2;
+ renderNavigation();
+ renderAccountInfo();
+ }
+
+
+ function renderAccountInfo() {
+ $reuseExistingCard.addClass('hidden');
+ $newCardInfo.removeClass('hidden');
+ $freeJamTrackPrompt.addClass('hidden');
+ $noFreeJamTrackPrompt.addClass('hidden');
+ $("#payment_error").addClass('hidden').text('')
+
+ var selectCountryReady = selectCountry.ready();
+ if(!selectCountryReady) {
+ // one time init of country dropdown
+ selectCountryReady = selectCountry.load('US', null, null);
+ }
+
+ selectCountryReady.done(function() {
+ var user = rest.getUserDetail()
+ if(user) {
+ user.done(populateAccountInfo).error(app.ajaxError);
+ }
+ else {
+ $reuseExistingCardChk.iCheck('uncheck').attr('checked', false)
+ if(gon.global.one_free_jamtrack_per_user) {
+ $freeJamTrackPrompt.removeClass('hidden')
+ }
+ else {
+ $noFreeJamTrackPrompt.removeClass('hidden')
+ }
+ }
+ })
+ }
+
+ function populateAccountInfo(user) {
+ userDetail = user;
+
+ $reuseExistingCardChk.iCheck(userDetail.reuse_card && userDetail.has_recurly_account ? 'check' : 'uncheck').attr('checked', userDetail.reuse_card)
+
+ // show appropriate prompt text based on whether user has a free jamtrack
+ if(user.free_jamtrack) {
+ $freeJamTrackPrompt.removeClass('hidden')
+ }
+ else {
+ $noFreeJamTrackPrompt.removeClass('hidden')
+ }
+
+ if (userDetail.has_recurly_account) {
+
+ rest.getBillingInfo()
+ .done(function(response) {
+
+ if(userDetail.reuse_card) {
+ $reuseExistingCard.removeClass('hidden');
+ toggleReuseExistingCard.call($reuseExistingCardChk)
+ $existingCardEndsWith.text(response.billing_info.last_four);
+ }
+
+ var isSameAsShipping = true // jamTrackUtils.compareAddress(response.billing_info, response.address);
+
+ $shippingAsBilling.iCheck(isSameAsShipping ? 'check' : 'uncheck').attr('checked', isSameAsShipping)
+
+ $billingInfo.find("#billing-first-name").val(response.billing_info.first_name);
+ $billingInfo.find("#billing-last-name").val(response.billing_info.last_name);
+ $billingInfo.find("#billing-address1").val(response.billing_info.address1);
+ $billingInfo.find("#billing-address2").val(response.billing_info.address2);
+ $billingInfo.find("#billing-city").val(response.billing_info.city);
+ $billingInfo.find("#billing-state").val(response.billing_info.state);
+ $billingInfo.find("#billing-zip").val(response.billing_info.zip);
+ $billingInfo.find("#billing-country").val(response.billing_info.country);
+
+ //$shippingAddress.find("#shipping-first-name").val(response.billing_info.first_name);
+ //$shippingAddress.find("#shipping-last-name").val(response.billing_info.last_name);
+ //$shippingAddress.find("#shipping-address1").val(response.address.address1);
+ //$shippingAddress.find("#shipping-address2").val(response.address.address2);
+ //$shippingAddress.find("#shipping-city").val(response.address.city);
+ //$shippingAddress.find("#shipping-state").val(response.address.state);
+ //$shippingAddress.find("#shipping-zip").val(response.address.zip);
+ //$shippingAddress.find("#shipping-country").val(response.address.country);
+
+ })
+ .error(app.ajaxError);
+ }
+ else {
+ $billingInfo.find("#billing-first-name").val(userDetail.first_name);
+ $billingInfo.find("#billing-last-name").val(userDetail.last_name);
+ $billingInfo.find("#billing-city").val(userDetail.city);
+ $billingInfo.find("#billing-state").val(userDetail.state);
+ $billingInfo.find("#billing-country").val(userDetail.country);
+
+ $shippingAddress.find("#shipping-first-name").val(userDetail.first_name);
+ $shippingAddress.find("#shipping-last-name").val(userDetail.last_name);
+ $shippingAddress.find("#shipping-city").val(userDetail.city);
+ $shippingAddress.find("#shipping-state").val(userDetail.state);
+ $shippingAddress.find("#shipping-country").val(userDetail.country);
+ }
+ }
+
+ function beforeShow(data) {
+ // XXX : style-test code
+ // moveToThanks({jam_tracks: [{id: 14, jam_track_right_id: 11, name: 'Back in Black'}, {id: 15, jam_track_right_id: 11, name: 'In Bloom'}, {id: 16, jam_track_right_id: 11, name: 'Love Bird Supreme'}]});
+ }
+
+ function beforeHide() {
+
+ }
+
+ function beforeHide() {
+
+ }
+
+ // TODO: Refactor: this function is long and fraught with many return points.
+ function next(e) {
+ $paymentInfoPanel.find('.error-text').remove();
+ $paymentInfoPanel.find('.error').removeClass('error');
+ e.preventDefault();
+ $("#payment_error").addClass("hidden").text('')
+
+ var reuse_card_this_time = $reuseExistingCardChk.is(':checked');
+ var reuse_card_next_time = $paymentMethod.find('#save-card').is(':checked');
+
+ // validation
+ var billing_first_name = $billingInfo.find("#billing-first-name").val();
+ var billing_last_name = $billingInfo.find("#billing-last-name").val();
+ var billing_address1 = $billingInfo.find("#billing-address1").val();
+ var billing_address2 = $billingInfo.find("#billing-address2").val();
+ var billing_city = $billingInfo.find("#billing-city").val();
+ var billing_state = $billingInfo.find("#billing-state").val();
+ var billing_zip = $billingInfo.find("#billing-zip").val();
+ var billing_country = $billingInfo.find("#billing-country").val();
+
+ var billingInfoValid = true;
+ if (!billing_first_name) {
+ $billingInfo.find('#divBillingFirstName .error-text').remove();
+ $billingInfo.find('#divBillingFirstName').addClass("error").addClass("transparent");
+ $billingInfo.find('#billing-first-name').after("
");
+
+ logger.info("no billing first name");
+ billingInfoValid = false;
+ }
+ else {
+ $billingInfo.find('#divBillingFirstName').removeClass("error");
+ }
+
+ if (!billing_last_name) {
+ $billingInfo.find('#divBillingLastName .error-text').remove();
+ $billingInfo.find('#divBillingLastName').addClass("error").addClass("transparent");
+ $billingInfo.find('#billing-last-name').after("");
+
+ logger.info("no billing last name");
+ billingInfoValid = false;
+ }
+ else {
+ $billingInfo.find('#divBillingLastName').removeClass("error");
+ }
+
+ if (!billing_address1) {
+ $billingInfo.find('#divBillingAddress1 .error-text').remove();
+ $billingInfo.find('#divBillingAddress1').addClass("error").addClass("transparent");
+ $billingInfo.find('#billing-address1').after("");
+
+ logger.info("no billing address line 1");
+ billingInfoValid = false;
+ }
+ else {
+ $billingInfo.find('#divBillingAddress1').removeClass("error");
+ }
+
+ if (!billing_zip) {
+ $billingInfo.find('#divBillingZip .error-text').remove();
+ $billingInfo.find('#divBillingZip').addClass("error").addClass("transparent");
+ $billingInfo.find('#billing-zip').after("");
+
+ logger.info("no billing zip");
+ billingInfoValid = false;
+ }
+ else {
+ $billingInfo.find('#divBillingZip').removeClass("error");
+ }
+
+ if (!billing_state) {
+ $billingInfo.find('#divBillingState .error-text').remove();
+ $billingInfo.find('#divBillingState').addClass("error").addClass("transparent");
+ $billingInfo.find('#billing-state').after("");
+
+ logger.info("no billing state");
+ billingInfoValid = false;
+ }
+ else {
+ $billingInfo.find('#divBillingState').removeClass("error");
+ }
+
+ if (!billing_city) {
+ $billingInfo.find('#divBillingCity .error-text').remove();
+ $billingInfo.find('#divBillingCity').addClass("error").addClass("transparent");
+ $billingInfo.find('#billing-city').after("");
+
+ logger.info("no billing city");
+ billingInfoValid = false;
+ }
+ else {
+ $billingInfo.find('#divBillingCity').removeClass("error");
+ }
+
+ if (!billing_country) {
+ $billingInfo.find('#divBillingCountry .error-text').remove();
+ $billingInfo.find('#divBillingCountry').addClass("error").addClass("transparent");
+ $billingInfo.find('#billing-country').after("");
+
+ logger.info("no billing country");
+ billingInfoValid = false;
+ }
+ else {
+ $billingInfo.find('#divBillingCountry').removeClass("error");
+ }
+
+ /**
+ shipping_as_billing = $shippingAsBilling.is(":checked");
+ var shipping_first_name, shipping_last_name, shipping_address1, shipping_address2;
+ var shipping_city, shipping_state, shipping_zip, shipping_country;
+
+ if (!shipping_as_billing) {
+ shipping_first_name = $shippingAddress.find("#shipping-first-name").val();
+ shipping_last_name = $shippingAddress.find("#shipping-last-name").val();
+ shipping_address1 = $shippingAddress.find("#shipping-address1").val();
+ shipping_address2 = $shippingAddress.find("#shipping-address2").val();
+ shipping_city = $shippingAddress.find("#shipping-city").val();
+ shipping_state = $shippingAddress.find("#shipping-state").val();
+ shipping_zip = $shippingAddress.find("#shipping-zip").val();
+ shipping_country = $shippingAddress.find("#shipping-country").val();
+
+ if (!shipping_first_name) {
+ $shippingAddress.find('#divShippingFirstName .error-text').remove();
+ $shippingAddress.find('#divShippingFirstName').addClass("error").addClass("transparent");
+ $shippingAddress.find('#shipping-first-name').after("");
+
+ logger.info("no address first name");
+ return false;
+ }
+ else {
+ $shippingInfo.find('#divShippingFirstName').removeClass("error");
+ }
+
+ if (!shipping_last_name) {
+ $shippingAddress.find('#divShippingLastName .error-text').remove();
+ $shippingAddress.find('#divShippingLastName').addClass("error").addClass("transparent");
+ $shippingAddress.find('#shipping-last-name').after("");
+
+ logger.info("no last name");
+ return false;
+ }
+ else {
+ $shippingInfo.find('#divShippingLastName').removeClass("error");
+ }
+
+ if (!shipping_address1) {
+ $shippingAddress.find('#divShippingAddress1 .error-text').remove();
+ $shippingAddress.find('#divShippingAddress1').addClass("error").addClass("transparent");
+ $shippingAddress.find('#shipping-address1').after("");
+
+ logger.info("no shipping address 1");
+ return false;
+ }
+ else {
+ $shippingInfo.find('#divShippingAddress1').removeClass("error");
+ }
+
+ if (!shipping_zip) {
+ $shippingAddress.find('#divShippingZip .error-text').remove();
+ $shippingAddress.find('#divShippingZip').addClass("error").addClass("transparent");
+ $shippingAddress.find('#shipping-zip').after("");
+
+ logger.info("no shipping address 2");
+ return false;
+ }
+ else {
+ $shippingInfo.find('#divShippingZip').removeClass("error");
+ }
+
+ if (!shipping_state) {
+ $shippingAddress.find('#divShippingState .error-text').remove();
+ $shippingAddress.find('#divShippingState').addClass("error").addClass("transparent");
+ $shippingAddress.find('#shipping-zip').after("");
+
+ logger.info("no shipping state");
+ return false;
+ }
+ else {
+ $shippingInfo.find('#divShippingState').removeClass("error");
+ }
+
+ if (!shipping_city) {
+ $shippingAddress.find('#divShippingCity .error-text').remove();
+ $shippingAddress.find('#divShippingCity').addClass("error").addClass("transparent");
+ $shippingAddress.find('#shipping-city').after("");
+
+ logger.info("no shipping city");
+ return false;
+ }
+ else {
+ $shippingInfo.find('#divShippingCity').removeClass("error");
+ }
+
+ if (!shipping_country) {
+ $shippingAddress.find('#divShippingCountry .error-text').remove();
+ $shippingAddress.find('#divShippingCountry').addClass("error").addClass("transparent");
+ $shippingAddress.find('#shipping-country').after("");
+
+ logger.info("no shipping country");
+ return false;
+ }
+ else {
+ $shippingAddress.find('#divShippingCountry').removeClass("error");
+ }
+ }
+ */
+
+ var card_name = $paymentMethod.find("#card-name").val();
+ var card_number = $paymentMethod.find("#card-number").val();
+ var card_year = $paymentMethod.find("#card_expire-date_1i").val();
+ var card_month = $paymentMethod.find("#card_expire-date_2i").val();
+ var card_verify = $paymentMethod.find("#card-verify").val();
+
+ /**
+ if (!card_name) {
+ $paymentMethod.find('#divCardName .error-text').remove();
+ $paymentMethod.find('#divCardName').addClass("error").addClass("transparent");
+ $paymentMethod.find('#card-name').after("");
+ return false;
+ } else {
+ $paymentMethod.find('#divCardName').removeClass("error");
+ }*/
+
+ // don't valid card form fields when reuse card selected
+ if(!reuse_card_this_time) {
+ if (!card_number) {
+ $paymentMethod.find('#divCardNumber .error-text').remove();
+ $paymentMethod.find('#divCardNumber').addClass("error").addClass("transparent");
+ $paymentMethod.find('#card-number').after("");
+ logger.info("no card number");
+ billingInfoValid = false;
+ } else if (!$.payment.validateCardNumber(card_number)) {
+ $paymentMethod.find('#divCardNumber .error-text').remove();
+ $paymentMethod.find('#divCardNumber').addClass("error").addClass("transparent");
+ $paymentMethod.find('#card-number').after("");
+ logger.info("invalid card number");
+ billingInfoValid = false;
+ } else {
+ $paymentMethod.find('#divCardNumber').removeClass("error");
+ }
+
+ if (!$.payment.validateCardExpiry(card_month, card_year)) {
+ $paymentMethod.find('#divCardExpiry .error-text').remove();
+ $paymentMethod.find('#divCardExpiry').addClass("error").addClass("transparent");
+ $paymentMethod.find('#card-expiry').after("");
+ logger.info("invalid card expiry");
+ billingInfoValid = false;
+ } else {
+ $paymentMethod.find('#divCardExpiry').removeClass("error");
+ }
+
+ if (!card_verify) {
+ $paymentMethod.find('#divCardVerify .error-text').remove();
+ $paymentMethod.find('#divCardVerify').addClass("error").addClass("transparent");
+ $paymentMethod.find('#card-verify').after("- Card Verification Value is required
");
+
+ logger.info("no card verify");
+ billingInfoValid = false;
+ } else if (!$.payment.validateCardCVC(card_verify)) {
+ $paymentMethod.find('#divCardVerify .error-text').remove();
+ $paymentMethod.find('#divCardVerify').addClass("error").addClass("transparent");
+ $paymentMethod.find('#card-verify').after("- Card Verification Value is not valid.
");
+
+ logger.info("bad card CVC");
+ billingInfoValid = false;
+ } else {
+ $paymentMethod.find('#divCardVerify').removeClass("error");
+ }
+ }
+
+ if(!billingInfoValid) {
+ logger.debug("billing info is invalid. returning");
+ return false;
+ }
+
+ billing_info = {};
+ shipping_info = {};
+ billing_info.first_name = billing_first_name;
+ billing_info.last_name = billing_last_name;
+ billing_info.address1 = billing_address1;
+ billing_info.address2 = billing_address2;
+ billing_info.city = billing_city;
+ billing_info.state = billing_state;
+ billing_info.country = billing_country;
+ billing_info.zip = billing_zip;
+ billing_info.number = card_number;
+ billing_info.month = card_month;
+ billing_info.year = card_year;
+ billing_info.verification_value = card_verify;
+
+ /**
+ if (shipping_as_billing) {
+ shipping_info = $.extend({},billing_info);
+ delete shipping_info.number;
+ delete shipping_info.month;
+ delete shipping_info.year;
+ delete shipping_info.verification_value;
+ } else {
+ shipping_info.first_name = shipping_first_name;
+ shipping_info.last_name = shipping_last_name;
+ shipping_info.address1 = shipping_address1;
+ shipping_info.address2 = shipping_address2;
+ shipping_info.city = shipping_city;
+ shipping_info.state = shipping_state;
+ shipping_info.country = shipping_country;
+ shipping_info.zip = shipping_zip;
+ }*/
+
+ var email = null;
+ var password = null;
+ var terms = false;
+ var isLoggedIn = context.JK.currentUserId;
+ if(!isLoggedIn) {
+ email = $accountSignup.find('input[name="email"]').val()
+ password = $accountSignup.find('input[name="password"]').val()
+ terms = $accountSignup.find('input[name="terms-of-service"]').is(':checked');
+ }
+
+ $screen.find("#payment-info-next").addClass("disabled");
+ $screen.find("#payment-info-next").off("click");
+ rest.createRecurlyAccount({billing_info: billing_info, terms_of_service: terms, email: email, password: password, reuse_card_this_time: reuse_card_this_time, reuse_card_next_time: reuse_card_next_time})
+ .done(function() {
+ $screen.find("#payment-info-next").on("click", next);
+
+ if(isLoggedIn) {
+ context.location = '/client#/checkoutOrder'
+ }
+ else {
+ // this means the account was created; we need to reload the page for this to take effect
+ context.JK.currentUserId = 'something' // this is to trick layout.js from getting involved and redirecting to home screen
+ context.location = '/client#/checkoutOrder'
+ context.location.reload()
+ }
+ })
+ .fail(errorHandling)
+ .always(function(){
+ $screen.find("#payment-info-next").removeClass("disabled");
+ })
+ }
+
+ function errorHandling(xhr, ajaxOptions, thrownError) {
+ logger.debug("error handling", xhr.responseJSON)
+ if(xhr.responseJSON && xhr.responseJSON.errors) {
+ $.each(xhr.responseJSON.errors, function(key, error) {
+ if (key == 'number') {
+ $paymentMethod.find('#divCardNumber .error-text').remove();
+ $paymentMethod.find('#divCardNumber').addClass("error").addClass("transparent");
+ $paymentMethod.find('#card-number').after("");
+ }
+ else if (key == 'verification_value') {
+ $paymentMethod.find('#divCardVerify .error-text').remove();
+ $paymentMethod.find('#divCardVerify').addClass("error").addClass("transparent");
+ $paymentMethod.find('#card-verify').after("");
+ }
+ else if(key == 'email') {
+ var $email = $accountSignup.find('input[name="email"]')
+ var $field = $email.closest('.field')
+ $field.find('.error-text').remove()
+ $field.addClass("error").addClass("transparent");
+ $email.after("");
+ }
+ else if(key == 'password') {
+ var $password = $accountSignup.find('input[name="password"]')
+ var $field = $password.closest('.field')
+ $field.find('.error-text').remove()
+ $field.addClass("error").addClass("transparent");
+ $password.after("");
+ }
+ else if(key == 'terms_of_service') {
+ var $terms = $accountSignup.find('input[name="terms-of-service"]')
+ var $field = $terms.closest('.field')
+ $field.find('.error-text').remove()
+ $field.addClass("error").addClass("transparent");
+ $accountSignup.find('.terms-of-service-label-holder').append("");
+ }
+ else if(key == 'message') {
+ $("#payment_error").text(error).removeClass('hidden')
+ }
+ });
+ }
+ else {
+ $("#payment_error").text(xhr.responseText).removeClass('hidden')
+ }
+
+
+ $screen.find("#payment-info-next").on('click', next);
+ }
+
+ function beforeShowOrder() {
+ step = 3;
+ renderNavigation();
+ populateOrderPage();
+ }
+
+
+ function populateOrderPage() {
+
+ rest.getShoppingCarts()
+ .done(renderOrderPage)
+ .fail(app.ajaxError);
+ }
+
+ function toggleShippingAsBilling(e) {
+ e.preventDefault();
+
+ var shipping_as_billing = $(e.target).is(':checked');
+
+ if (!shipping_as_billing) {
+ $shippingAddress.removeClass("hidden");
+ }
+ else {
+ $shippingAddress.addClass("hidden");
+ }
+ }
+
+ function toggleReuseExistingCard(e) {
+ if(e) {
+ e.preventDefault();
+ }
+
+ logger.debug("toggle reuse existing card")
+
+ var reuse_existing = $(this).is(':checked');
+
+ $('#billing-first-name').prop('disabled', reuse_existing);
+ $('#billing-last-name').prop('disabled', reuse_existing);
+ $('#billing-address1').prop('disabled', reuse_existing);
+ $('#billing-address2').prop('disabled', reuse_existing);
+ $('#billing-city').prop('disabled', reuse_existing);
+ $('#billing-state').prop('disabled', reuse_existing);
+ $('#billing-zip').prop('disabled', reuse_existing);
+ $('#billing-country').prop('disabled', reuse_existing);
+
+ $('#card-name').prop('disabled', reuse_existing);
+ $('#card-number').prop('disabled', reuse_existing);
+ $('#card_expire-date_1i').prop('disabled', reuse_existing);
+ $('#card_expire-date_2i').prop('disabled', reuse_existing);
+ $('#card-verify').prop('disabled', reuse_existing);
+
+ }
+
+ function events() {
+ $screen.find("#payment-info-next").on('click', next);
+ $shippingAsBilling.on('ifChanged', toggleShippingAsBilling);
+ $reuseExistingCardChk.on('ifChanged', toggleReuseExistingCard);
+ }
+
+ function reset() {
+
+ }
+
+ function renderNavigation() {
+ $navigation.html("");
+ var navigationHtml = $(
+ context._.template(
+ $('#template-checkout-navigation').html(),
+ {current: step},
+ {variable: 'data'}
+ )
+ );
+
+ $navigation.append(navigationHtml);
+ }
+
+ function initializeControls() {
+ $("form.payment-info").addClass(context.JK.currentUserId ? 'signed-in' : 'not-signed-in').iCheck({
+ checkboxClass: 'icheckbox_minimal',
+ radioClass: 'iradio_minimal',
+ inheritClass: true
+ });
+
+ // Use jquery.payment to limit characters and length:
+ $paymentMethod.find("#card-number").payment('formatCardNumber');
+ $paymentMethod.find("#card-verify").payment('formatCardCVC');
+
+ selectCountry = new context.JK.SelectLocation($('#billing-country'), null, null, app, false)
+ }
+
+ function initialize() {
+ var screenBindings = {
+ 'beforeShow': beforeShow,
+ 'afterShow': afterShow,
+ 'beforeHide' : beforeHide
+ };
+ app.bindScreen('checkoutPayment', screenBindings);
+
+ $screen = $("#checkoutPaymentScreen");
+ $paymentInfoPanel = $screen.find("#checkout-payment-info");
+ $navigation = $screen.find(".checkout-navigation-bar");
+ $billingInfo = $paymentInfoPanel.find(".billing-address");
+ $shippingInfo = $paymentInfoPanel.find(".shipping-address");
+ $paymentMethod = $paymentInfoPanel.find(".payment-method");
+ $accountSignup = $paymentInfoPanel.find('.jamkazam-account-signup')
+ $shippingAddress = $paymentInfoPanel.find(".shipping-address-detail");
+ $shippingAsBilling = $paymentInfoPanel.find("#shipping-as-billing");
+ $reuseExistingCard = $paymentInfoPanel.find('.reuse-existing-card')
+ $reuseExistingCardChk = $paymentInfoPanel.find('#reuse-existing-card')
+ $existingCardEndsWith = $paymentInfoPanel.find('.existing-card-ends-with')
+ $newCardInfo = $paymentInfoPanel.find('.new-card-info')
+ $freeJamTrackPrompt = $screen.find('.payment-prompt.free-jamtrack')
+ $noFreeJamTrackPrompt = $screen.find('.payment-prompt.no-free-jamtrack')
+
+
+ if($screen.length == 0) throw "$screen must be specified";
+ if($navigation.length == 0) throw "$navigation must be specified";
+
+ initializeControls();
+
+ events();
+ }
+
+ this.initialize = initialize;
+
+ return this;
+ }
+})(window,jQuery);
\ No newline at end of file
diff --git a/web/app/assets/javascripts/checkout_signin.js b/web/app/assets/javascripts/checkout_signin.js
index beb536d3d..22ee88060 100644
--- a/web/app/assets/javascripts/checkout_signin.js
+++ b/web/app/assets/javascripts/checkout_signin.js
@@ -14,54 +14,77 @@
var $password = null;
var $signinBtn = null;
var $signupBtn = null;
+ var $inputElements = null;
+ var $contentHolder = null;
+ var $btnNext = null;
function beforeShow(data) {
renderNavigation();
+ renderLoggedInState();
}
function afterShow(data) {
}
- function events() {
- $signinBtn.on('click', login);
- $signupBtn.on('click', signup);
+
+ function renderLoggedInState(){
+ if(isLoggedIn()) {
+ $contentHolder.removeClass('not-signed-in').addClass('signed-in')
+ }
+ else {
+ $contentHolder.removeClass('signed-in').addClass('not-signed-in')
+ }
}
- function signup(e) {
- app.layout.showDialog('signup-dialog');
- return false;
+ function isLoggedIn() {
+ return !!context.JK.currentUserId;
+ }
+
+ function events() {
+ $signinForm.on('submit', login);
+ $signinBtn.on('click', login);
+ $btnNext.on('click', moveNext);
}
function reset() {
- $signinForm.removeClass('login-error');
-
- $email.val('');
- $password.val('');
+ $inputElements.removeClass('login-error');
}
+ function moveNext() {
+ window.location = '/client#/checkoutPayment';
+
+ return false;
+ }
function login() {
+ if($signinBtn.is('.disabled')) {
+ return false;
+ }
+
var email = $email.val();
var password = $password.val();
reset();
- $signinBtn.text('TRYING...');
+ $signinBtn.text('TRYING...').addClass('disabled')
- rest.login({email: email, password: password, remember_me: false})
+ rest.login({email: email, password: password, remember_me: true})
.done(function() {
- window.location = '/client#/order'
+ window.location = '/client#/checkoutPayment'
+ window.location.reload();
})
.fail(function(jqXHR) {
if(jqXHR.status == 422) {
- $signinForm.addClass('login-error')
+ $inputElements.addClass('login-error')
}
else {
app.notifyServerError(jqXHR, "Unable to log in")
}
})
.always(function() {
- $signinBtn.text('SIGN IN')
+ $signinBtn.text('SIGN IN').removeClass('disabled')
})
+
+ return false;
}
function renderNavigation() {
@@ -83,15 +106,17 @@
'beforeShow': beforeShow,
'afterShow': afterShow
};
- app.bindScreen('signin', screenBindings);
+ app.bindScreen('checkoutSignin', screenBindings);
- $screen = $("#signInScreen");
+ $screen = $("#checkoutSignInScreen");
$navigation = $screen.find(".checkout-navigation-bar");
$signinForm = $screen.find(".signin-form");
$signinBtn = $signinForm.find('.signin-submit');
- $email = $signinForm.find('input[name="session[email]"]');
- $password = $signinForm.find('input[name="session[password]"]');
- $signupBtn = $signinForm.find('.show-signup-dialog');
+ $email = $signinForm.find('input[name="email"]');
+ $password = $signinForm.find('input[name="password"]');
+ $inputElements = $signinForm.find('.input-elements');
+ $contentHolder = $screen.find('.content-holder');
+ $btnNext = $screen.find('.btnNext');
if($screen.length == 0) throw "$screen must be specified";
if($navigation.length == 0) throw "$navigation must be specified";
diff --git a/web/app/assets/javascripts/checkout_signin.js.coffee b/web/app/assets/javascripts/checkout_signin.js.coffee
new file mode 100644
index 000000000..fd8cc5288
--- /dev/null
+++ b/web/app/assets/javascripts/checkout_signin.js.coffee
@@ -0,0 +1,62 @@
+$ = jQuery
+context = window
+context.JK ||= {};
+
+# events emitted:
+# EVENTS.CHECKOUT_SIGNED_IN
+# EVENTS.CHECKOUT_SKIP_SIGN_IN
+
+context.JK.CheckoutSignin = class CheckoutSignin
+ constructor: (app, root) ->
+ @EVENTS = context.JK.EVENTS
+ @rest = context.JK.Rest()
+ @logger = context.JK.logger
+ @app = app
+ @root = root
+ @emailInput = @root.find('input[name="email"]')
+ @passwordInput = @root.find('input[name="password"]')
+ @signinBtn = @root.find('.signin-submit')
+ @skipSigninBtn = @root.find('.btnNext')
+ @signinForm = @root.find('form.signin-form')
+
+ # TODO: add Facebook login support
+
+ throw "no root element" if not @root.exists()
+ throw "no email element" if not @emailInput.exists()
+ throw "no password element" if not @passwordInput.exists()
+ throw "no signin btn" if not @signinBtn.exists()
+ throw "no skip signin btn" if not @skipSigninBtn.exists()
+
+ @signinForm.submit(@onSigninSubmit)
+ @skipSigninBtn.click(@onSkipSignin)
+
+ removeErrors: () =>
+ @signinForm.removeClass('login-error');
+
+ onSigninSubmit: () =>
+ @logger.debug("attempting to signin")
+
+ @removeErrors()
+
+ @signinBtn.addClass('disabled')
+
+ email = @emailInput.val()
+ password = @passwordInput.val()
+
+ @rest.login({email: email, password: password, remember_me: true})
+ .done(@onLoginDone)
+ .fail(@onLoginFail)
+
+ onLoginDone: () =>
+ @signinBtn.removeClass('disabled')
+ $(this).triggerHandler(@EVENTS.CHECKOUT_SIGNED_IN, {})
+
+ onLoginFail: (jqXHR) =>
+ @signinBtn.removeClass('disabled')
+ if jqXHR.status == 422
+ @signinForm.addClass('login-error')
+ else
+ @app.notifyServerError(jqXHR, "Unable to log in. Try again later.")
+
+ onSkipSignin: () =>
+ $(this).triggerHandler(@EVENTS.CHECKOUT_SKIP_SIGN_IN, {})
\ No newline at end of file
diff --git a/web/app/assets/javascripts/dialog/jamtrackLicenseDialog.js b/web/app/assets/javascripts/dialog/jamtrackLicenseDialog.js
new file mode 100644
index 000000000..c74d71e86
--- /dev/null
+++ b/web/app/assets/javascripts/dialog/jamtrackLicenseDialog.js
@@ -0,0 +1,46 @@
+(function(context,$) {
+
+ "use strict";
+ context.JK = context.JK || {};
+ context.JK.JamtrackLicenseDialog = function(app) {
+ var logger = context.JK.logger;
+ var $dialog = null;
+ var dialogId = 'jamtrack-license-dialog';
+
+ function beforeShow(data) {
+ }
+
+ function afterShow(data) {
+ }
+
+ function afterHide() {
+ }
+
+ function showDialog() {
+ return app.layout.showDialog(dialogId);
+ }
+
+ function events() {
+ }
+
+ function initialize() {
+
+ var dialogBindings = {
+ 'beforeShow' : beforeShow,
+ 'afterShow' : afterShow,
+ 'afterHide': afterHide
+ };
+
+ app.bindDialog(dialogId, dialogBindings);
+
+ $dialog = $('[layout-id="' + dialogId + '"]');
+
+ events();
+ }
+
+ this.initialize = initialize;
+ this.showDialog = showDialog;
+ };
+
+ return this;
+})(window,jQuery);
\ No newline at end of file
diff --git a/web/app/assets/javascripts/dialog/jamtrack_payment_history_dialog.js.coffee b/web/app/assets/javascripts/dialog/jamtrack_payment_history_dialog.js.coffee
new file mode 100644
index 000000000..a73460c58
--- /dev/null
+++ b/web/app/assets/javascripts/dialog/jamtrack_payment_history_dialog.js.coffee
@@ -0,0 +1,61 @@
+$ = jQuery
+context = window
+context.JK ||= {}
+
+context.JK.JamtrackPaymentHistoryDialog = class JamtrackPaymentHistoryDialog
+ constructor: (@app) ->
+ @rest = context.JK.Rest()
+ @client = context.jamClient
+ @logger = context.JK.logger
+ @screen = null
+ @dialogId = 'jamtrack-payment-history-dialog';
+ @dialog = null;
+
+ initialize:() =>
+ dialogBindings = {
+ 'beforeShow' : @beforeShow,
+ 'afterShow' : @afterShow
+ }
+
+ @dialog = $('[layout-id="' + @dialogId + '"]');
+ @app.bindDialog(@dialogId, dialogBindings);
+ @tbody = @dialog.find("table.payment-table tbody")
+ @rowTemplate = $('#template-payment-history-row').html()
+
+ beforeShow:() =>
+ # Get payment history from jamrest
+ payments = [
+ {date: new Date(2013, 4, 5), amount: 372.33},
+ {date: new Date(2014, 5, 5), amount: 338.44}
+ ]
+
+ @rest.getPaymentHistory()
+ .done(@showPaymentHistory)
+ .fail(@app.ajaxError)
+
+ showPaymentHistory:(data) =>
+ # Turn in to HTML rows and append:
+ @tbody.html("")
+ if data.payments? && data.payments.length > 0
+ for p in data.payments
+ amt = p.amount
+ amt = 0 if !amt?
+ payment = {
+ date: context.JK.formatDateYYYYMMDD(p.created_at)
+ amount: (amt * 100).toFixed(2)
+ status: p.status
+ payment_method: p.payment_method.replace("_", " ")
+ reference: p.reference
+ }
+ tr = $(context._.template(@rowTemplate, payment, { variable: 'data' }));
+ @tbody.append(tr);
+ else
+ tr = "| No payments found |
"
+ @tbody.append(tr);
+
+ afterShow:() =>
+
+ showDialog:() =>
+ @app.layout.showDialog(@dialogId)
+
+
\ No newline at end of file
diff --git a/web/app/assets/javascripts/dialog/loginRequiredDialog.js b/web/app/assets/javascripts/dialog/loginRequiredDialog.js
new file mode 100644
index 000000000..6795614c1
--- /dev/null
+++ b/web/app/assets/javascripts/dialog/loginRequiredDialog.js
@@ -0,0 +1,45 @@
+(function(context,$) {
+
+ "use strict";
+ context.JK = context.JK || {};
+ context.JK.LoginRequiredDialog = function(app) {
+ var logger = context.JK.logger;
+ var $dialog = null;
+ var dialogId = 'login-required-dialog';
+
+ function beforeShow(data) {
+ }
+
+ function afterShow(data) {
+ }
+
+ function afterHide() {
+ }
+
+ function events() {
+ $dialog.find('.go-to-jamtracks').click(function() {
+ app.layout.closeDialog(dialogId)
+ context.location.href = $(this).attr('href')
+ })
+ }
+
+ function initialize() {
+
+ var dialogBindings = {
+ 'beforeShow' : beforeShow,
+ 'afterShow' : afterShow,
+ 'afterHide': afterHide
+ };
+
+ app.bindDialog(dialogId, dialogBindings);
+
+ $dialog = $('#' + dialogId);
+
+ events();
+ }
+
+ this.initialize = initialize;
+ };
+
+ return this;
+})(window,jQuery);
\ No newline at end of file
diff --git a/web/app/assets/javascripts/everywhere/everywhere.js b/web/app/assets/javascripts/everywhere/everywhere.js
index 4564cd9b0..16adb8b3f 100644
--- a/web/app/assets/javascripts/everywhere/everywhere.js
+++ b/web/app/assets/javascripts/everywhere/everywhere.js
@@ -127,6 +127,9 @@
var clientPreferencesDialog = new JK.ClientPreferencesDialog(app);
clientPreferencesDialog.initialize();
+
+ var loginRequiredDialog = new JK.LoginRequiredDialog(app);
+ loginRequiredDialog.initialize();
}
// wait 10 seconds
@@ -201,8 +204,10 @@
var user = app.user()
if(user) {
user.done(function(userProfile) {
+ console.log("app.layout.getCurrentScreen() != 'checkoutOrderScreen'", app.layout.getCurrentScreen())
if (userProfile.show_whats_next &&
window.location.pathname.indexOf(gon.client_path) == 0 &&
+ window.location.pathname.indexOf('/checkout') == -1 &&
!app.layout.isDialogShowing('getting-started'))
{
app.layout.showDialog('getting-started');
@@ -212,13 +217,7 @@
}
function initShoppingCart(app) {
-
- var user = app.user()
- if(user) {
- user.done(function(userProfile) {
- context.JK.JamTrackUtils.checkShoppingCart();
- })
- }
+ context.JK.JamTrackUtils.checkShoppingCart();
}
})(window, jQuery);
diff --git a/web/app/assets/javascripts/fakeJamClient.js b/web/app/assets/javascripts/fakeJamClient.js
index 7a15ffbb1..d0b640b8d 100644
--- a/web/app/assets/javascripts/fakeJamClient.js
+++ b/web/app/assets/javascripts/fakeJamClient.js
@@ -739,7 +739,7 @@
}
function SessionOpenMetronome(bpm, click, meter, mode){
- console.log("Setting metronome BPM: ", bpm)
+ logger.debug("Setting metronome BPM: ", bpm)
metronomeActive =true
metronomeBPM = bpm
metronomeSound = click
diff --git a/web/app/assets/javascripts/findSession.js b/web/app/assets/javascripts/findSession.js
index f54feb866..d3c4638c2 100644
--- a/web/app/assets/javascripts/findSession.js
+++ b/web/app/assets/javascripts/findSession.js
@@ -57,9 +57,11 @@
})
.fail(function(xhr, textStatus, errorMessage) {
if (xhr.status === 404) {
+ logger.warn("unable to list active sessions (404)")
// swallow 404
}
else {
+ logger.warn("unable to list active sessions")
app.ajaxError(xhr, textStatus, errorMessage);
}
})
diff --git a/web/app/assets/javascripts/globals.js b/web/app/assets/javascripts/globals.js
index fbaeed1a8..175a96fd6 100644
--- a/web/app/assets/javascripts/globals.js
+++ b/web/app/assets/javascripts/globals.js
@@ -27,6 +27,9 @@
CHAT: "1"
};
+ context.JK.AVAILABILITY_US = "United States";
+ context.JK.MASTER_TRACK = "Master";
+
context.JK.EVENTS = {
DIALOG_CLOSED : 'dialog_closed',
SHOW_SIGNUP : 'show_signup',
@@ -47,7 +50,9 @@
CONNECTION_DOWN: 'connection_down',
SCREEN_CHANGED: 'screen_changed',
JAMTRACK_DOWNLOADER_STATE_CHANGED: 'jamtrack_downloader_state',
- METRONOME_PLAYBACK_MODE_SELECTED: 'metronome_playback_mode_selected'
+ METRONOME_PLAYBACK_MODE_SELECTED: 'metronome_playback_mode_selected',
+ CHECKOUT_SIGNED_IN: 'checkout_signed_in',
+ CHECKOUT_SKIP_SIGN_IN: 'checkout_skip_sign_in'
};
context.JK.PLAYBACK_MONITOR_MODE = {
diff --git a/web/app/assets/javascripts/homeScreen.js b/web/app/assets/javascripts/homeScreen.js
index 997927dbd..ec7b86d50 100644
--- a/web/app/assets/javascripts/homeScreen.js
+++ b/web/app/assets/javascripts/homeScreen.js
@@ -6,6 +6,7 @@
context.JK.HomeScreen = function(app) {
var logger = context.JK.logger;
var isFtueComplete = false;
+ var $screen = null;
function beforeShow(data) {
}
@@ -86,9 +87,20 @@
var screenBindings = { 'beforeShow': beforeShow };
app.bindScreen('home', screenBindings);
events();
+ $screen = $('.screen[layout-id="home"]')
- $('.profile').on('click', function() {
+ $screen.find('.profile').on('click', function() {
+ var $destination = $('[layout-id="profile"]');
+ if(!context.JK.currentUserId && !$destination.is('.no-login-required')) {
+ // if there is no user and login is required, then stop user from clicknig through
+ app.layout.showDialog('login-required-dialog')
+ }
+ else
+ {
context.location = '/client#/profile/' + context.JK.currentUserId;
+ }
+
+
});
};
diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js
index e66ec9897..5e7b3e07c 100644
--- a/web/app/assets/javascripts/jam_rest.js
+++ b/web/app/assets/javascripts/jam_rest.js
@@ -473,7 +473,7 @@
processData: false,
contentType: 'application/json',
data: JSON.stringify(options)
- });
+ })
}
function getUserDetail(options) {
@@ -1236,6 +1236,15 @@
})
}
+ function playJamTrack(jamTrackId) {
+ return $.ajax({
+ type: "POST",
+ url: '/api/jamtracks/played/' + jamTrackId,
+ dataType: "json",
+ contentType: 'application/json'
+ });
+ }
+
function closeJamTrack(options) {
var musicSessionId = options["id"];
delete options["id"];
@@ -1479,6 +1488,15 @@
});
}
+ function getPaymentHistory(options) {
+ return $.ajax({
+ type: "GET",
+ url: '/api/recurly/payment_history',
+ dataType: "json",
+ contentType: 'application/json'
+ });
+ }
+
function getBackingTracks(options) {
return $.ajax({
type: "GET",
@@ -1561,10 +1579,10 @@
});
}
- function placeOrder(options) {
+ function placeOrder() {
return $.ajax({
type: "POST",
- url: '/api/recurly/place_order?' + $.param(options),
+ url: '/api/recurly/place_order',
dataType: "json",
contentType: 'application/json'
});
@@ -1741,6 +1759,7 @@
this.updateAudioLatency = updateAudioLatency;
this.getJamtracks = getJamtracks;
this.getPurchasedJamTracks = getPurchasedJamTracks;
+ this.getPaymentHistory = getPaymentHistory;
this.getJamTrackRight = getJamTrackRight;
this.enqueueJamTrack = enqueueJamTrack;
this.getBackingTracks = getBackingTracks;
@@ -1760,6 +1779,7 @@
this.validateUrlSite = validateUrlSite;
this.markRecordedBackingTrackSilent = markRecordedBackingTrackSilent;
this.addRecordingTimeline = addRecordingTimeline;
+ this.playJamTrack = playJamTrack;
return this;
};
diff --git a/web/app/assets/javascripts/jam_track_screen.js.coffee b/web/app/assets/javascripts/jam_track_screen.js.coffee
new file mode 100644
index 000000000..d2f1f21a0
--- /dev/null
+++ b/web/app/assets/javascripts/jam_track_screen.js.coffee
@@ -0,0 +1,242 @@
+$ = jQuery
+context = window
+context.JK ||= {}
+
+context.JK.JamTrackScreen=class JamTrackScreen
+ LIMIT = 10
+ instrument_logo_map = context.JK.getInstrumentIconMap24()
+
+ constructor: (@app) ->
+ @logger = context.JK.logger
+ @screen = null
+ @content = null
+ @scroller = null
+ @genre = null
+ @artist = null
+ @instrument = null
+ @availability = null
+ @nextPager = null
+ @noMoreJamtracks = null
+ @currentPage = 0
+ @next = null
+ @currentQuery = this.defaultQuery()
+ @expanded = false
+
+ beforeShow:(data) =>
+ this.setFilterFromURL()
+ this.refresh()
+
+ afterShow:(data) =>
+
+ events:() =>
+ @genre.on 'change', this.search
+ @artist.on 'change', this.search
+ @instrument.on 'change', this.search
+ @availability.on 'change', this.search
+
+ clearResults:() =>
+ @currentPage = 0
+ @content.empty()
+ @noMoreJamtracks.hide()
+ @next = null
+
+ setFilterFromURL:() =>
+ # Grab parms from URL for artist, instrument, and availability
+ parms=this.getParams()
+
+ if(parms.artist?)
+ @artist.val(parms.artist)
+ if(parms.instrument?)
+ @instrument.val(parms.instrument)
+ if(parms.availability?)
+ @availability.val(parms.availability)
+ window.history.replaceState({}, "", "/client#/jamtrack")
+
+ getParams:() =>
+ params = {}
+ q = window.location.href.split("?")[1]
+ if q?
+ q = q.split('#')[0]
+ raw_vars = q.split("&")
+ for v in raw_vars
+ [key, val] = v.split("=")
+ params[key] = decodeURIComponent(val)
+ ms
+ params
+
+ refresh:() =>
+ @currentQuery = this.buildQuery()
+ that = this
+ rest.getJamtracks(@currentQuery).done((response) ->
+ that.clearResults()
+ that.handleJamtrackResponse(response)
+ ).fail (jqXHR) ->
+ that.clearResults()
+ that.noMoreJamtracks.show()
+ that.app.notifyServerError jqXHR, 'Jamtrack Unavailable'
+
+ search:() =>
+ this.refresh()
+ false
+
+ defaultQuery:() =>
+ query =
+ per_page: LIMIT
+ page: @currentPage+1
+ if @next
+ query.since = @next
+ query
+
+ buildQuery:() =>
+ @currentQuery = this.defaultQuery()
+ # genre filter
+ # var genres = @screen.find('#jamtrack_genre').val()
+ # if (genres !== undefined) {
+ # @currentQuery.genre = genres
+ # }
+ # instrument filter
+
+ instrument = @instrument.val()
+ if instrument?
+ @currentQuery.instrument = instrument
+
+ # artist filter
+ art = @artist.val()
+ if art?
+ @currentQuery.artist = art
+
+ # availability filter
+ availability = @availability.val()
+ if availability?
+ @currentQuery.availability = availability
+ @currentQuery
+
+ handleJamtrackResponse:(response) =>
+ @next = response.next
+ this.renderJamtracks(response)
+ if response.next == null
+ # if we less results than asked for, end searching
+ @scroller.infinitescroll 'pause'
+ if @currentPage == 0 and response.jamtracks.length == 0
+ @content.append 'There\'s no jamtracks.
'
+ if @currentPage > 0
+ @noMoreJamtracks.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++
+ this.buildQuery()
+ this.registerInfiniteScroll()
+
+
+ registerInfiniteScroll:() =>
+ @scroller.infinitescroll {
+ behavior: 'local'
+ navSelector: '#jamtrackScreen .btn-next-pager'
+ nextSelector: '#jamtrackScreen .btn-next-pager'
+ binder: @scroller
+ dataType: 'json'
+ appendCallback: false
+ prefill: false
+ bufferPx: 100
+ loading:
+ msg: $('Loading ...
')
+ img: '/assets/shared/spinner.gif'
+ path: (page) ->
+ '/api/jamtracks?' + $.param(this.buildQuery())
+
+ }, (json, opts) ->
+ this.handleJamtrackResponse(json)
+ @scroller.infinitescroll 'resume'
+
+ playJamtrack:(e) =>
+ e.preventDefault()
+
+ addToCartJamtrack:(e) =>
+ e.preventDefault()
+ params = id: $(e.target).attr('data-jamtrack-id')
+ rest.addJamtrackToShoppingCart(params).done((response) ->
+ context.location = '/client#/shoppingCart'
+ ).fail @app.ajaxError
+
+ licenseUSWhy:(e) =>
+ e.preventDefault()
+ @app.layout.showDialog 'jamtrack-availability-dialog'
+
+ registerEvents:() =>
+ @screen.find('.jamtrack-detail-btn').on 'click', this.showJamtrackDescription
+ @screen.find('.play-button').on 'click', this.playJamtrack
+ @screen.find('.jamtrack-add-cart').on 'click', this.addToCartJamtrack
+ @screen.find('.license-us-why').on 'click', this.licenseUSWhy
+ @screen.find('.jamtrack-detail-btn').on 'click', this.toggleExpanded
+
+ renderJamtracks:(data) =>
+ that = this
+ for jamtrack in data.jamtracks
+ for track in jamtrack.tracks
+ continue if track.track_type=='Master'
+ inst = '../assets/content/icon_instrument_default24.png'
+ if track.instrument.id in instrument_logo_map
+ inst = instrument_logo_map[track.instrument.id].asset
+ track.instrument_url = inst
+ track.instrument_desc = track.instrument.description
+ if track.part != ''
+ track.instrument_desc += ' (' + track.part + ')'
+
+ options =
+ jamtrack: jamtrack
+ expanded: that.expanded
+
+ @jamtrackItem = $(context._.template($('#template-jamtrack').html(), options, variable: 'data'))
+ that.renderJamtrack(@jamtrackItem)
+ this.registerEvents()
+
+ showJamtrackDescription:(e) =>
+ e.preventDefault()
+ @description = $(e.target).parent('.detail-arrow').next()
+ if @description.css('display') == 'none'
+ @description.show()
+ else
+ @description.hide()
+
+ toggleExpanded:() =>
+ this.expanded = !this.expanded
+ this.refresh()
+
+ renderJamtrack:(jamtrack) =>
+ @content.append jamtrack
+
+ initialize:() =>
+ screenBindings =
+ 'beforeShow': this.beforeShow
+ 'afterShow': this.afterShow
+ @app.bindScreen 'jamtrack', screenBindings
+ @screen = $('#jamtrack-find-form')
+ @scroller = @screen.find('.content-body-scroller')
+ @content = @screen.find('.jamtrack-content')
+ @genre = @screen.find('#jamtrack_genre')
+ @artist = @screen.find('#jamtrack_artist')
+ @instrument = @screen.find('#jamtrack_instrument')
+ @availability = @screen.find('#jamtrack_availability')
+ @nextPager = @screen.find('a.btn-next-pager')
+ @noMoreJamtracks = @screen.find('.end-of-jamtrack-list')
+ if @screen.length == 0
+ throw new Error('@screen must be specified')
+ if @scroller.length == 0
+ throw new Error('@scroller must be specified')
+ if @content.length == 0
+ throw new Error('@content must be specified')
+ if @noMoreJamtracks.length == 0
+ throw new Error('@noMoreJamtracks must be specified')
+ #if(@genre.length == 0) throw new Error("@genre must be specified")
+
+ if @artist.length == 0
+ throw new Error('@artist must be specified')
+ if @instrument.length == 0
+ throw new Error('@instrument must be specified')
+ if @availability.length == 0
+ throw new Error('@availability must be specified')
+ this.events()
+
+
diff --git a/web/app/assets/javascripts/jam_track_utils.js.coffee b/web/app/assets/javascripts/jam_track_utils.js.coffee
index 2935665cc..d3da3180a 100644
--- a/web/app/assets/javascripts/jam_track_utils.js.coffee
+++ b/web/app/assets/javascripts/jam_track_utils.js.coffee
@@ -16,13 +16,18 @@ class JamTrackUtils
@rest.getShoppingCarts().done(this.displayCartIcon)
displayCartIcon: (carts) =>
- cartLink = $("a[href='" + "/client#/shoppingCart" + "']")
+ cartLink = $("#header-shopping-cart")
if carts.length > 0
cartLink.removeClass("hidden")
else
cartLink.addClass("hidden")
-
+ compareAddress: (billing, shipping) =>
+ billing.address1 == shipping.address1 &&
+ billing.address2 == shipping.address2 &&
+ billing.zip == shipping.zip &&
+ billing.city == shipping.city &&
+ billing.country == shipping.country;
# global instance
diff --git a/web/app/assets/javascripts/jamkazam.js b/web/app/assets/javascripts/jamkazam.js
index db0615510..2bc31b957 100644
--- a/web/app/assets/javascripts/jamkazam.js
+++ b/web/app/assets/javascripts/jamkazam.js
@@ -132,6 +132,9 @@
logger.error("Unexpected ajax error: " + textStatus + ", msg:" + errorMessage);
app.notify({title: "Oops!", text: "What you were looking for is gone now."});
}
+ else if(jqXHR.status === 403) {
+ logger.debug("not logged in");
+ }
else if (jqXHR.status === 422) {
logger.error("Unexpected ajax error: " + textStatus + ", msg: " + errorMessage + ", response: " + jqXHR.responseText);
// present a nicer message
@@ -282,20 +285,37 @@
var hash = context.location.hash;
+ var screen = 'home'
try {
- context.RouteMap.parse(hash);
+ var location = context.RouteMap.parse(hash);
+ screen = location.page.substring(1); // remove leading slash
}
catch (e) {
logger.debug("ignoring bogus screen name: %o", hash)
hash = null;
}
- var url = '/client#/home';
+
+ var $destination = $('[layout-id="' + screen + '"]');
+
+ if(!context.JK.currentUserId && !$destination.is('.no-login-required')) {
+ logger.debug("not logged in so redirected to login from screen: " + screen)
+ var redirectPath= '?redirect-to=' + encodeURIComponent(JK.locationPath());
+ if(gon.isNativeClient) {
+ window.location.href = '/signin' + redirectPath;
+ }
+ else {
+ window.location.href = '/' + redirectPath;
+ }
+ return;
+ }
+
+ var url = '/client#/' + screen;
if (hash) {
url = hash;
}
- logger.debug("Changing screen to " + url);
+ logger.debug("jamkazam: Changing screen to " + url + " (hash=" + hash + ")") ;
context.location = url;
}
@@ -377,7 +397,11 @@
app.notify({title: "Unable to Load User", text: "You should reload the page."})
});
}
- } // if userDeferred
+ }
+ else {
+ userDeferred = new $.Deferred();
+ userDeferred.reject('not_logged_in');
+ }
$(document).triggerHandler('JAMKAZAM_READY', {app:app})
diff --git a/web/app/assets/javascripts/jamtrack.js b/web/app/assets/javascripts/jamtrack.js
deleted file mode 100644
index 57371b79e..000000000
--- a/web/app/assets/javascripts/jamtrack.js
+++ /dev/null
@@ -1,263 +0,0 @@
-(function(context,$) {
-
- "use strict";
- context.JK = context.JK || {};
- context.JK.JamTrackScreen = function(app) {
-
- var logger = context.JK.logger;
-
- var $screen = null;
- var $content = null;
- var $scroller = null;
- var $genre = null;
- var $instrument = null;
- var $availability = null;
- var $nextPager = null;
- var $noMoreJamtracks = null;
-
- var currentQuery = defaultQuery();
- var currentPage = 0;
- var LIMIT = 10;
- var next = null;
- var instrument_logo_map = context.JK.getInstrumentIconMap24();
-
- function beforeShow(data) {
- refresh();
- }
-
- function afterShow(data) {
- }
-
- function events() {
- $genre.on("change", search);
- $instrument.on("change", search);
- $availability.on("change", search);
- }
-
- function clearResults() {
- //logger.debug("CLEARING CONTENT")
- currentPage = 0;
- $content.empty();
- $noMoreJamtracks.hide();
- next = null;
- }
-
- function refresh() {
- currentQuery = buildQuery();
- rest.getJamtracks(currentQuery)
- .done(function(response) {
- clearResults();
- handleJamtrackResponse(response);
- })
- .fail(function(jqXHR) {
- clearResults();
- $noMoreJamtracks.show();
- app.notifyServerError(jqXHR, 'Jamtrack Unavailable')
- })
- }
-
- function search() {
- logger.debug("Searching for jamtracks...");
- refresh();
- return false;
- }
-
- function defaultQuery() {
- var query = { limit:LIMIT, page:currentPage};
-
- if(next) {
- query.since = next;
- }
-
- return query;
- }
-
- function buildQuery() {
- currentQuery = defaultQuery();
-
- // genre filter
- var genres = $screen.find('#jamtrack_genre').val();
- if (genres !== undefined) {
- currentQuery.genre = genres;
- }
-
- // instrument filter
- var instrument = $instrument.val();
- if (instrument !== undefined) {
- currentQuery.instrument = instrument;
- }
-
- // availability filter
- var availability = $availability.val();
- if (availability !== undefined) {
- currentQuery.availability = availability;
- }
-
- return currentQuery;
- }
-
- function handleJamtrackResponse(response) {
- //logger.debug("Handling response", JSON.stringify(response))
- next = response.next;
-
- renderJamtracks(response);
-
- if(response.next == null) {
- // if we less results than asked for, end searching
- $scroller.infinitescroll('pause');
- logger.debug("end of jamtracks");
-
- if(currentPage == 0 && response.jamtracks.length == 0) {
- $content.append("There's no jamtracks.
") ;
- }
-
- if(currentPage > 0) {
- $noMoreJamtracks.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 registerInfiniteScroll() {
- $scroller.infinitescroll({
- behavior: 'local',
- navSelector: '#jamtrackScreen .btn-next-pager',
- nextSelector: '#jamtrackScreen .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/jamtracks?' + $.param(buildQuery());
- }
- },function(json, opts) {
- handleJamtrackResponse(json);
- });
- $scroller.infinitescroll('resume');
- }
-
- function playJamtrack(e) {
- e.preventDefault();
- }
-
- function addToCartJamtrack(e) {
- e.preventDefault();
-
- var params = {id: $(e.target).attr("data-jamtrack-id")};
- rest.addJamtrackToShoppingCart(params)
- .done(function(response) {
- context.location = "/client#/shoppingCart";
- })
- .fail(app.ajaxError);
- }
-
- function licenseUSWhy(e) {
- e.preventDefault();
-
- app.layout.showDialog('jamtrack-availability-dialog');
- }
-
- function registerEvents() {
- $screen.find('.jamtrack-detail-btn').on("click", showJamtrackDescription);
- $screen.find('.play-button').on('click', playJamtrack);
- $screen.find('.jamtrack-add-cart').on('click', addToCartJamtrack);
- $screen.find('.license-us-why').on('click', licenseUSWhy);
- }
-
- function renderJamtracks(data) {
- $.each(data.jamtracks, function(i, jamtrack) {
- $.each(jamtrack.tracks, function (index, track) {
-
- if(track.track_type == 'Master') {
- return; // continue
- }
-
- var inst = '../assets/content/icon_instrument_default24.png';
- if (track.instrument.id in instrument_logo_map) {
- inst = instrument_logo_map[track.instrument.id].asset;
- }
- track.instrument_url = inst;
-
- track.instrument_desc = track.instrument.description;
- if (track.part != "") {
- track.instrument_desc += " ( " + track.part + " )";
- }
- });
-
- var options = {
- jamtrack: jamtrack
- };
-
- var $jamtrackItem = $(
- context._.template(
- $('#template-jamtrack').html(),
- options,
- {variable: 'data'}
- )
- );
- renderJamtrack($jamtrackItem );
- });
-
- registerEvents();
- }
-
- function showJamtrackDescription(e) {
- e.preventDefault();
-
- var $description = $(e.target).parent(".detail-arrow").next();
- if ($description.css("display") == "none") {
- $description.show();
- }
- else {
- $description.hide();
- }
- }
-
- function renderJamtrack(jamtrack) {
- $content.append(jamtrack);
- }
-
- function initialize() {
- var screenBindings = {
- 'beforeShow': beforeShow,
- 'afterShow': afterShow
- };
- app.bindScreen('jamtrack', screenBindings);
-
- $screen = $("#jamtrack-find-form");
- $scroller = $screen.find('.content-body-scroller');
- $content = $screen.find(".jamtrack-content");
- $genre = $screen.find("#jamtrack_genre");
- $instrument = $screen.find("#jamtrack_instrument");
- $availability = $screen.find("#jamtrack_availability");
- $nextPager = $screen.find("a.btn-next-pager");
- $noMoreJamtracks = $screen.find("#end-of-jamtrack-list");
-
- 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($noMoreJamtracks.length == 0) throw "$noMoreJamtracks must be specified";
- if($genre.length == 0) throw "$genre must be specified";
- if($instrument.length == 0) throw "$instrument must be specified";
- if($availability.length ==0) throw "$availability must be specified";
-
- events();
- }
-
- this.initialize = initialize;
-
- return this;
- }
-})(window,jQuery);
\ No newline at end of file
diff --git a/web/app/assets/javascripts/jamtrack.js.coffee b/web/app/assets/javascripts/jamtrack.js.coffee
new file mode 100644
index 000000000..95a1c89f0
--- /dev/null
+++ b/web/app/assets/javascripts/jamtrack.js.coffee
@@ -0,0 +1,243 @@
+$ = jQuery
+context = window
+context.JK ||= {}
+
+context.JK.JamTrackScreen=class JamTrackScreen
+ LIMIT = 10
+ instrument_logo_map = context.JK.getInstrumentIconMap24()
+
+ constructor: (@app) ->
+ @logger = context.JK.logger
+ @screen = null
+ @content = null
+ @scroller = null
+ @genre = null
+ @artist = null
+ @instrument = null
+ @availability = null
+ @nextPager = null
+ @noMoreJamtracks = null
+ @currentPage = 0
+ @next = null
+ @currentQuery = this.defaultQuery()
+ @expanded = false
+
+ beforeShow:(data) =>
+ this.setFilterFromURL()
+ this.refresh()
+
+ afterShow:(data) =>
+
+ events:() =>
+ @genre.on 'change', this.search
+ @artist.on 'change', this.search
+ @instrument.on 'change', this.search
+ @availability.on 'change', this.search
+
+ clearResults:() =>
+ #$logger.debug("CLEARING CONTENT")
+ @currentPage = 0
+ @content.empty()
+ @noMoreJamtracks.hide()
+ @next = null
+
+ setFilterFromURL:() =>
+ # Grab parms from URL for artist, instrument, and availability
+ parms=this.getParams()
+ this.logger.debug("parms", parms)
+ if(parms.artist?)
+ @artist.val(parms.artist)
+ if(parms.instrument?)
+ @instrument.val(parms.instrument)
+ if(parms.availability?)
+ @availability.val(parms.availability)
+ window.history.replaceState({}, "", "/client#/jamtrack")
+
+ getParams:() =>
+ params = {}
+ q = window.location.href.split("?")[1]
+ if q?
+ q = q.split('#')[0]
+ raw_vars = q.split("&")
+ for v in raw_vars
+ [key, val] = v.split("=")
+ params[key] = decodeURIComponent(val)
+ params
+
+ refresh:() =>
+ @currentQuery = this.buildQuery()
+ that = this
+ rest.getJamtracks(@currentQuery).done((response) ->
+ that.clearResults()
+ that.handleJamtrackResponse(response)
+ ).fail (jqXHR) ->
+ that.clearResults()
+ that.noMoreJamtracks.show()
+ that.app.notifyServerError jqXHR, 'Jamtrack Unavailable'
+
+ search:() =>
+ this.refresh()
+ false
+
+ defaultQuery:() =>
+ query =
+ per_page: LIMIT
+ page: @currentPage+1
+ if @next
+ query.since = @next
+ query
+
+ buildQuery:() =>
+ @currentQuery = this.defaultQuery()
+ # genre filter
+ # var genres = @screen.find('#jamtrack_genre').val()
+ # if (genres !== undefined) {
+ # @currentQuery.genre = genres
+ # }
+ # instrument filter
+
+ instrument = @instrument.val()
+ if instrument?
+ @currentQuery.instrument = instrument
+
+ # artist filter
+ art = @artist.val()
+ if art?
+ @currentQuery.artist = art
+
+ # availability filter
+ availability = @availability.val()
+ if availability?
+ @currentQuery.availability = availability
+ @currentQuery
+
+ handleJamtrackResponse:(response) =>
+ #logger.debug("Handling response", JSON.stringify(response))
+ @next = response.next
+ this.renderJamtracks(response)
+ if response.next == null
+ # if we less results than asked for, end searching
+ @scroller.infinitescroll 'pause'
+ if @currentPage == 0 and response.jamtracks.length == 0
+ @content.append 'There\'s no jamtracks.
'
+ if @currentPage > 0
+ @noMoreJamtracks.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++
+ this.buildQuery()
+ this.registerInfiniteScroll()
+
+
+ registerInfiniteScroll:() =>
+ @scroller.infinitescroll {
+ behavior: 'local'
+ navSelector: '#jamtrackScreen .btn-next-pager'
+ nextSelector: '#jamtrackScreen .btn-next-pager'
+ binder: @scroller
+ dataType: 'json'
+ appendCallback: false
+ prefill: false
+ bufferPx: 100
+ loading:
+ msg: $('Loading ...
')
+ img: '/assets/shared/spinner.gif'
+ path: (page) ->
+ '/api/jamtracks?' + $.param(this.buildQuery())
+
+ }, (json, opts) ->
+ this.handleJamtrackResponse(json)
+ @scroller.infinitescroll 'resume'
+
+ playJamtrack:(e) =>
+ e.preventDefault()
+
+ addToCartJamtrack:(e) =>
+ e.preventDefault()
+ params = id: $(e.target).attr('data-jamtrack-id')
+ rest.addJamtrackToShoppingCart(params).done((response) ->
+ context.location = '/client#/shoppingCart'
+ ).fail @app.ajaxError
+
+ licenseUSWhy:(e) =>
+ e.preventDefault()
+ @app.layout.showDialog 'jamtrack-availability-dialog'
+
+ registerEvents:() =>
+ @screen.find('.jamtrack-detail-btn').on 'click', this.showJamtrackDescription
+ @screen.find('.play-button').on 'click', this.playJamtrack
+ @screen.find('.jamtrack-add-cart').on 'click', this.addToCartJamtrack
+ @screen.find('.license-us-why').on 'click', this.licenseUSWhy
+ @screen.find('.jamtrack-detail-btn').on 'click', this.toggleExpanded
+
+ renderJamtracks:(data) =>
+ that = this
+ for jamtrack in data.jamtracks
+ for track in jamtrack.tracks
+ continue if track.track_type=='Master'
+ inst = '../assets/content/icon_instrument_default24.png'
+ if track.instrument.id in instrument_logo_map
+ inst = instrument_logo_map[track.instrument.id].asset
+ track.instrument_url = inst
+ track.instrument_desc = track.instrument.description
+ if track.part != ''
+ track.instrument_desc += ' (' + track.part + ')'
+
+ options =
+ jamtrack: jamtrack
+ expanded: that.expanded
+
+ @jamtrackItem = $(context._.template($('#template-jamtrack').html(), options, variable: 'data'))
+ that.renderJamtrack(@jamtrackItem)
+ this.registerEvents()
+
+ showJamtrackDescription:(e) =>
+ e.preventDefault()
+ @description = $(e.target).parent('.detail-arrow').next()
+ if @description.css('display') == 'none'
+ @description.show()
+ else
+ @description.hide()
+
+ toggleExpanded:() =>
+ this.expanded = !this.expanded
+ this.refresh()
+
+ renderJamtrack:(jamtrack) =>
+ @content.append jamtrack
+
+ initialize:() =>
+ screenBindings =
+ 'beforeShow': this.beforeShow
+ 'afterShow': this.afterShow
+ @app.bindScreen 'jamtrack', screenBindings
+ @screen = $('#jamtrack-find-form')
+ @scroller = @screen.find('.content-body-scroller')
+ @content = @screen.find('.jamtrack-content')
+ @genre = @screen.find('#jamtrack_genre')
+ @artist = @screen.find('#jamtrack_artist')
+ @instrument = @screen.find('#jamtrack_instrument')
+ @availability = @screen.find('#jamtrack_availability')
+ @nextPager = @screen.find('a.btn-next-pager')
+ @noMoreJamtracks = @screen.find('.end-of-jamtrack-list')
+ if @screen.length == 0
+ throw new Error('@screen must be specified')
+ if @scroller.length == 0
+ throw new Error('@scroller must be specified')
+ if @content.length == 0
+ throw new Error('@content must be specified')
+ if @noMoreJamtracks.length == 0
+ throw new Error('@noMoreJamtracks must be specified')
+ #if(@genre.length == 0) throw new Error("@genre must be specified")
+
+ if @artist.length == 0
+ throw new Error('@artist must be specified')
+ if @instrument.length == 0
+ throw new Error('@instrument must be specified')
+ if @availability.length == 0
+ throw new Error('@availability must be specified')
+ this.events()
+
+
diff --git a/web/app/assets/javascripts/jamtrack_landing.js.coffee b/web/app/assets/javascripts/jamtrack_landing.js.coffee
new file mode 100644
index 000000000..760744e9f
--- /dev/null
+++ b/web/app/assets/javascripts/jamtrack_landing.js.coffee
@@ -0,0 +1,53 @@
+$ = jQuery
+context = window
+context.JK ||= {}
+
+context.JK.JamTrackLanding = class JamTrackLanding
+ constructor: (@app) ->
+ @rest = context.JK.Rest()
+ @client = context.jamClient
+ @logger = context.JK.logger
+ @screen = null
+
+ initialize:() =>
+ screenBindings =
+ 'beforeShow': @beforeShow
+ 'afterShow': @afterShow
+ @app.bindScreen('jamtrackLanding', screenBindings)
+ @screen = $('#jamtrackLanding')
+
+ beforeShow:() =>
+ # Get artist names and build links
+ @rest.getJamtracks({group_artist: true})
+ .done(this.buildArtistLinks)
+ .fail(this.handleFailure)
+
+ # Bind links to action that will open the jam_tracks list view filtered to given artist_name:
+ # artist_name
+ this.bindArtistLinks()
+ afterShow:() =>
+
+ buildArtistLinks:(response) =>
+ # Get artist names and build links
+ jamtracks = response.jamtracks
+ $("#band_list>li:not('#no_bands_found')").remove()
+ if jamtracks.length==0
+ $("#no_bands_found").removeClass("hidden")
+ else
+ $("#no_bands_found").addClass("hidden")
+
+ # client#/jamtrack
+ for jamtrack in jamtracks
+ artistLink = "#{jamtrack.original_artist}"
+ $("#band_list").append("#{artistLink}")
+
+ # We don't want to do a full page load if this is clicked on here:
+ bindArtistLinks:() =>
+ band_list=$("ul#band_list")
+ that=this
+ band_list.on "click", "a.artist-link", (event)->
+ context.location="client#/jamtrack"
+ window.history.replaceState({}, "", this.href)
+ event.preventDefault()
+
+ handleFailure:(error) =>
diff --git a/web/app/assets/javascripts/layout.js b/web/app/assets/javascripts/layout.js
index 723e75650..ac49684e6 100644
--- a/web/app/assets/javascripts/layout.js
+++ b/web/app/assets/javascripts/layout.js
@@ -95,6 +95,8 @@
layoutHeader(width, height);
layoutNotify(width, height);
layoutFooter(width, height);
+
+ $(document).triggerHandler('layout_resized');
}
function layoutCurtain(screenWidth, screenHeight) {
@@ -407,8 +409,15 @@
}
var destination = $(evt.currentTarget).attr('layout-link');
- var destinationType = $('[layout-id="' + destination + '"]').attr("layout");
+ var $destination = $('[layout-id="' + destination + '"]');
+
+ var destinationType = $destination.attr("layout");
if (destinationType === "screen") {
+ if(!context.JK.currentUserId && !$destination.is('.no-login-required')) {
+ // there is no user, and this item does not support 'no-login', so warn user
+ showDialog('login-required-dialog');
+ return;
+ }
context.location = '/client#/' + destination;
} else if (destinationType === "dialog") {
showDialog(destination);
@@ -540,7 +549,7 @@
var accepted = screenEvent(previousScreen, 'beforeHide', data);
if(accepted === false) return;
- logger.debug("Changing screen to " + currentScreen);
+ logger.debug("layout: changing screen to " + currentScreen);
$(document).triggerHandler(EVENTS.SCREEN_CHANGED, {previousScreen: previousScreen, newScreen: currentScreen})
@@ -687,6 +696,7 @@
return null;
}
logger.debug("opening dialog: " + dialog)
+
var $overlay = $('.dialog-overlay')
if (opts.sizeOverlayToContent) {
@@ -719,6 +729,12 @@
function panelHeaderClicked(evt) {
evt.preventDefault();
+
+ if(!context.JK.currentUserId) {
+ showDialog('login-required-dialog');
+ return false;
+ }
+
expandedPanel = $(evt.currentTarget).closest('[layout="panel"]').attr("layout-id");
layout();
return false;
diff --git a/web/app/assets/javascripts/notificationPanel.js b/web/app/assets/javascripts/notificationPanel.js
index 7c3e608de..c6e1f25bc 100644
--- a/web/app/assets/javascripts/notificationPanel.js
+++ b/web/app/assets/javascripts/notificationPanel.js
@@ -213,7 +213,7 @@
})
.fail(function() {
isLoading = false;
- app.ajaxError();
+ app.ajaxError(arguments);
})
}
@@ -1375,7 +1375,9 @@
events();
- populate();
+ app.user().done(function(){
+ populate();
+ })
};
this.initialize = initialize;
diff --git a/web/app/assets/javascripts/order.js b/web/app/assets/javascripts/order.js
index ed5035826..23360c626 100644
--- a/web/app/assets/javascripts/order.js
+++ b/web/app/assets/javascripts/order.js
@@ -42,6 +42,7 @@
step = 2;
renderNavigation();
renderAccountInfo();
+ $("#order_error").addClass("hidden")
}
function resetJamTrackDownloadInfo() {
@@ -115,6 +116,7 @@
purchasedJamTrackIterator = 0;
}
+ // TODO: Refactor: this function is long and fraught with many return points.
function next(e) {
e.preventDefault();
$("#order_error").addClass("hidden")
@@ -308,10 +310,8 @@
$paymentMethod.find('#divCardName .error-text').remove();
$paymentMethod.find('#divCardName').addClass("error");
$paymentMethod.find('#card-name').after("");
-
return false;
- }
- else {
+ } else {
$paymentMethod.find('#divCardName').removeClass("error");
}
@@ -319,21 +319,37 @@
$paymentMethod.find('#divCardNumber .error-text').remove();
$paymentMethod.find('#divCardNumber').addClass("error");
$paymentMethod.find('#card-number').after("");
-
return false;
- }
- else {
+ } else if (!$.payment.validateCardNumber(card_number)) {
+ $paymentMethod.find('#divCardNumber .error-text').remove();
+ $paymentMethod.find('#divCardNumber').addClass("error");
+ $paymentMethod.find('#card-number').after("");
+ return false;
+ } else {
$paymentMethod.find('#divCardNumber').removeClass("error");
}
+ if (!$.payment.validateCardExpiry(card_month, card_year)) {
+ $paymentMethod.find('#divCardExpiry .error-text').remove();
+ $paymentMethod.find('#divCardExpiry').addClass("error");
+ $paymentMethod.find('#card-expiry').after("");
+ } else {
+ $paymentMethod.find('#divCardExpiry').removeClass("error");
+ }
+
if (!card_verify) {
$paymentMethod.find('#divCardVerify .error-text').remove();
$paymentMethod.find('#divCardVerify').addClass("error");
- $paymentMethod.find('#card_verify').after("- Card Verification Value is required
");
+ $paymentMethod.find('#card-verify').after("- Card Verification Value is required
");
return false;
- }
- else {
+ } else if(!$.payment.validateCardCVC(card_verify)) {
+ $paymentMethod.find('#divCardVerify .error-text').remove();
+ $paymentMethod.find('#divCardVerify').addClass("error");
+ $paymentMethod.find('#card-verify').after("- Card Verification Value is not valid.
");
+
+ return false;
+ } else {
$paymentMethod.find('#divCardVerify').removeClass("error");
}
@@ -607,6 +623,10 @@
radioClass: 'iradio_minimal',
inheritClass: true
});
+
+ // Use jquery.payment to limit characters and length:
+ $paymentMethod.find("#card-number").payment('formatCardNumber');
+ $paymentMethod.find("#card-verify").payment('formatCardCVC');
}
function initialize() {
diff --git a/web/app/assets/javascripts/playbackControls.js b/web/app/assets/javascripts/playbackControls.js
index ce7d74075..d56a1e214 100644
--- a/web/app/assets/javascripts/playbackControls.js
+++ b/web/app/assets/javascripts/playbackControls.js
@@ -104,10 +104,10 @@
$playButton.on('click', function(e) {
var sessionModel = context.JK.CurrentSessionModel || null;
- if(sessionModel && sessionModel.areControlsLockedForJamTrackRecording() && $parentElement.closest('.session-track').data('track_data').type == 'jam_track') {
- context.JK.prodBubble($fader, 'jamtrack-controls-disabled', {}, {positions:['top'], offsetParent: $playButton})
- return false;
- }
+ //if(sessionModel && sessionModel.areControlsLockedForJamTrackRecording() && $parentElement.closest('.session-track').data('track_data').type == 'jam_track') {
+ // context.JK.prodBubble($fader, 'jamtrack-controls-disabled', {}, {positions:['top'], offsetParent: $playButton})
+ // return false;
+ //}
startPlay();
return false;
@@ -115,10 +115,10 @@
$pauseButton.on('click', function(e) {
var sessionModel = context.JK.CurrentSessionModel || null;
- if(sessionModel && sessionModel.areControlsLockedForJamTrackRecording() && $parentElement.closest('.session-track').data('track_data').type == 'jam_track') {
- context.JK.prodBubble($pauseButton, 'jamtrack-controls-disabled', {}, {positions:['top'], offsetParent: $pauseButton})
- return false;
- }
+ //if(sessionModel && sessionModel.areControlsLockedForJamTrackRecording() && $parentElement.closest('.session-track').data('track_data').type == 'jam_track') {
+ // context.JK.prodBubble($pauseButton, 'jamtrack-controls-disabled', {}, {positions:['top'], offsetParent: $pauseButton})
+ // return false;
+ //}
stopPlay();
return false;
diff --git a/web/app/assets/javascripts/scheduled_session.js.erb b/web/app/assets/javascripts/scheduled_session.js.erb
index 969f9449d..cf75275e1 100644
--- a/web/app/assets/javascripts/scheduled_session.js.erb
+++ b/web/app/assets/javascripts/scheduled_session.js.erb
@@ -1054,7 +1054,7 @@
context.JK.GenreSelectorHelper.render('#create-session-genre');
- inviteMusiciansUtil.loadFriends();
+ //inviteMusiciansUtil.loadFriends();
context.JK.dropdown($screen.find('#session-musician-access'));
context.JK.dropdown($screen.find('#session-fans-access'));
diff --git a/web/app/assets/javascripts/selectLocation.js b/web/app/assets/javascripts/selectLocation.js
index 4d5b36711..93cde1b12 100644
--- a/web/app/assets/javascripts/selectLocation.js
+++ b/web/app/assets/javascripts/selectLocation.js
@@ -6,7 +6,7 @@
context.JK.SelectLocation = Class.extend({
- init: function ($countries, $regions, $cities, app) {
+ init: function ($countries, $regions, $cities, app, useEasyDropdown) {
this.api = context.JK.Rest();
this.logger = context.JK.logger;
this.loadingCitiesData = false;
@@ -14,9 +14,12 @@
this.loadingCountriesData = false;
this.nilOptionStr = '';
this.nilOptionText = 'n/a';
+ this.countriesLoaded = false;
this.$countries = $countries;
this.$regions = $regions;
this.$cities = $cities;
+ this.$deferred = null;
+ this.useEasyDropdown = useEasyDropdown === undefined ? true : useEasyDropdown;
this.app = app;
$countries.on('change', function (evt) {
@@ -24,11 +27,24 @@
this.handleCountryChanged();
return false;
}.bind(this));
- $regions.on('change', function (evt) {
- evt.stopPropagation();
- this.handleRegionChanged();
- return false;
- }.bind(this));
+ if($regions) {
+ $regions.on('change', function (evt) {
+ evt.stopPropagation();
+ this.handleRegionChanged();
+ return false;
+ }.bind(this));
+ }
+ },
+ selectCountry: function (country) {
+ if(this.useEasyDropdown) {
+ this.$countries.easyDropDown('select', country, true)
+ }
+ else {
+ this.$countries.val(country)
+ }
+ },
+ ready: function() {
+ return this.$deferred;
},
load: function (country, region, city) {
@@ -42,13 +58,9 @@
country = 'US';
}
- this.loadingCountriesData = true;
- this.loadingRegionsData = true;
- this.loadingCitiesData = true;
-
// make the 3 slower requests, which only matter if the user wants to affect their ISP or location
-
- this.api.getCountries()
+ this.loadingCountriesData = true;
+ this.$deferred = this.api.getCountries()
.done(function (countriesx) {
this.populateCountriesx(countriesx["countriesx"], country);
}.bind(this))
@@ -57,7 +69,9 @@
this.loadingCountriesData = false;
}.bind(this))
- if (country) {
+
+ if (country && this.$regions) {
+ this.loadingRegionsData = true;
this.api.getRegions({ country: country })
.done(function (regions) {
this.populateRegions(regions["regions"], region);
@@ -67,7 +81,8 @@
this.loadingRegionsData = false;
}.bind(this))
- if (region) {
+ if (region && this.$cities) {
+ this.loadingCitiesData = true;
this.api.getCities({ country: country, region: region })
.done(function (cities) {
this.populateCities(cities["cities"], this.city)
@@ -78,9 +93,15 @@
}.bind(this))
}
}
+ return this.$deferred;
},
handleCountryChanged: function () {
var selectedCountry = this.$countries.val()
+
+ if(!this.$regions) {
+ return;
+ }
+
var selectedRegion = this.$regions.val()
var cityElement = this.$cities
@@ -144,7 +165,9 @@
else {
cityElement.children().remove();
cityElement.append($(this.nilOptionStr).text(this.nilOptionText));
- context.JK.dropdown(cityElement);
+ if(this.useEasyDropdown) {
+ context.JK.dropdown(cityElement);
+ }
}
},
@@ -159,7 +182,7 @@
if (!countryx.countrycode) return;
var option = $(this.nilOptionStr);
- option.text(countryx.countryname);
+ option.text(countryx.countryname ? countryx.countryname : countryx.countrycode);
option.attr("value", countryx.countrycode);
if (countryx.countrycode == this.country) {
@@ -170,6 +193,8 @@
},
populateCountriesx: function (countriesx) {
+ this.countriesLoaded = true;
+
// countriesx has the format [{countrycode: "US", countryname: "United States"}, ...]
this.foundCountry = false;
@@ -194,8 +219,9 @@
this.$countries.val(this.country);
this.$countries.attr("disabled", null).easyDropDown('enable');
-
- context.JK.dropdown(this.$countries);
+ if(this.useEasyDropdown) {
+ context.JK.dropdown(this.$countries);
+ }
},
writeRegion: function (index, region) {
@@ -220,7 +246,9 @@
this.$regions.val(userRegion)
this.$regions.attr("disabled", null).easyDropDown('enable');
- context.JK.dropdown(this.$regions);
+ if(this.useEasyDropdown) {
+ context.JK.dropdown(this.$regions);
+ }
},
writeCity: function (index, city) {
@@ -245,7 +273,9 @@
this.$cities.val(userCity)
this.$cities.attr("disabled", null).easyDropDown('enable');
- context.JK.dropdown(this.$cities);
+ if(this.useEasyDropdown) {
+ context.JK.dropdown(this.$cities);
+ }
},
regionListFailure: function (jqXHR, textStatus, errorThrown) {
diff --git a/web/app/assets/javascripts/session.js b/web/app/assets/javascripts/session.js
index b62d2737d..db44a05fa 100644
--- a/web/app/assets/javascripts/session.js
+++ b/web/app/assets/javascripts/session.js
@@ -93,6 +93,7 @@
var claimedRecording = null;
var backing_track_path = null;
var jamTrack = null;
+
var metronomeMixer = null;
var playbackControls = null;
var promptLeave = false;
@@ -115,6 +116,11 @@
var $metronomePlaybackSelect = null;
var $metronomePlaybackHelp = null;
var $templatePendingMetronome = null;
+ var $myTracks = null;
+ var $liveTracks = null;
+ var $audioTracks = null;
+ var $fluidTracks = null;
+
var mediaTrackGroups = [ChannelGroupIds.MediaTrackGroup, ChannelGroupIds.JamTrackGroup, ChannelGroupIds.MetronomeGroup];
var muteBothMasterAndPersonalGroups = [ChannelGroupIds.AudioInputMusicGroup, ChannelGroupIds.MediaTrackGroup, ChannelGroupIds.JamTrackGroup, ChannelGroupIds.MetronomeGroup];
@@ -480,6 +486,12 @@
function checkMetronomeTransition() {
// trust backend over server
+ if(sessionModel.jamTracks() !== null || sessionModel.recordedJamTracks() !== null) {
+ // ignore all metronome events when jamtracks are open, because backend opens metronome mixer to play jamtrack tap-ins
+ logger.debug("ignore checkMetronomeTransition because JamTrack is open")
+ return;
+ }
+
var metronomeMasterMixers = getMetronomeMasterMixers();
if (metronomeMixer == null && metronomeMasterMixers.length > 0) {
@@ -513,6 +525,7 @@
backing_track_path = currentSession == null ? null : currentSession.backing_track_path;
}
+
function checkRecordingTransition(currentSession) {
// handle claimed recordings
if (claimedRecording == null && (currentSession && currentSession.claimed_recording != null)) {
@@ -604,6 +617,8 @@
$('.session-recordings .recording-controls').hide();
$closePlaybackRecording.show();
$('.session-recordings .session-recording-name').text('(No audio loaded)')
+ $('.session-recordings').attr('media-state', 'closed')
+ $('.session-livetracks').attr('media-state', 'closed')
}
}
function didSelfOpenMedia() {
@@ -635,7 +650,7 @@
}
checkPendingMetronome();
resetOtherAudioContent();
-
+ resizeFluid();
/**
if ($('.session-recordings .track').length === 0) {
$('.session-recordings .when-empty').show();
@@ -947,6 +962,11 @@
var recordedBackingTracks = sessionModel.recordedBackingTracks();
var backingTracks = sessionModel.backingTracks();
+ var recordedJamTracks = sessionModel.recordedJamTracks();
+ var jamTracks = sessionModel.jamTracks();
+
+ //logger.debug("localMediaMixers", localMediaMixers)
+ //logger.debug("peerMediaMixers", peerLocalMediaMixers)
// with mixer info, we use these to decide what kind of tracks are open in the backend
@@ -981,29 +1001,57 @@
// additional check; if we can match an id in backing tracks or recorded backing track,
// we need to remove it from the recorded track set, but move it to the backing track set
- var isBackingTrack = false
- if(recordedBackingTracks) {
- context._.each(recordedBackingTracks, function (recordedBackingTrack) {
- if (mixer.id == 'L' + recordedBackingTrack.client_track_id) {
- isBackingTrack = true;
- return false; // break
- }
- })
- }
- if(backingTracks) {
- context._.each(backingTracks, function (backingTrack) {
- if (mixer.id == 'L' + backingTrack.client_track_id) {
- isBackingTrack = true;
+ var isJamTrack = false;
+
+ if(jamTracks) {
+ // check if the ID matches that of an open jam track
+ context._.each(jamTracks, function (jamTrack) {
+ if (mixer.id == jamTrack.id) {
+ isJamTrack = true;
return false; // break
}
})
}
- if(isBackingTrack) {
- backingTrackMixers.push(mixer)
+ if(!isJamTrack && recordedJamTracks) {
+ // then check if the ID matches that of a open, recorded jam track
+ context._.each(recordedJamTracks, function (recordedJamTrack) {
+ if (mixer.id == recordedJamTrack.id) {
+ isJamTrack = true;
+ return false; // break
+ }
+ })
+ }
+
+ if(isJamTrack) {
+ jamTrackMixers.push(mixer)
}
else {
- recordingTrackMixers.push(mixer);
+ var isBackingTrack = false
+ if (recordedBackingTracks) {
+ context._.each(recordedBackingTracks, function (recordedBackingTrack) {
+ if (mixer.id == 'L' + recordedBackingTrack.client_track_id) {
+ isBackingTrack = true;
+ return false; // break
+ }
+ })
+ }
+ if (backingTracks) {
+ context._.each(backingTracks, function (backingTrack) {
+ if (mixer.id == 'L' + backingTrack.client_track_id) {
+ isBackingTrack = true;
+ return false; // break
+ }
+ })
+ }
+
+ if (isBackingTrack) {
+ backingTrackMixers.push(mixer)
+ }
+ else {
+ // couldn't resolve this as a JamTrack or Backing track, must be a normal recorded file
+ recordingTrackMixers.push(mixer);
+ }
}
} else if(mediaType == 'PeerMediaTrack' || mediaType == 'BackingTrack') {
// BackingTrack
@@ -1033,7 +1081,7 @@
if(jamTrackMixers.length > 0) {
renderJamTracks(jamTrackMixers);
}
- if(metronomeTrackMixers.length > 0) {
+ if(metronomeTrackMixers.length > 0 && sessionModel.jamTracks() === null && sessionModel.recordedJamTracks() == null) {
renderMetronomeTracks(metronomeTrackMixers);
}
if(adhocTrackMixers.length > 0) {
@@ -1170,7 +1218,20 @@
function renderJamTracks(jamTrackMixers) {
logger.debug("rendering jam tracks")
- var jamTracks = sessionModel.jamTracks();
+
+ var jamTracks = []
+ var jamTrackName = 'JamTrack';
+ if(sessionModel.isPlayingRecording()) {
+ // only return managed mixers for recorded backing tracks
+ jamTracks = sessionModel.recordedJamTracks();
+ jamTrackName = sessionModel.recordedJamTrackName();
+ }
+ else {
+ // only return un-managed (ad-hoc) mixers for normal backing tracks
+ jamTracks = sessionModel.jamTracks();
+ jamTrackName = sessionModel.jamTrackName();
+ }
+
// pluck the 1st mixer, and assume that all other mixers in this group are of the same type (between JamTrack vs Peer)
@@ -1179,7 +1240,7 @@
// using the server's info in conjuction with the client's, draw the recording tracks
if(jamTracks) {
- $('.session-recording-name').text(sessionModel.getCurrentSession().jam_track.name);
+ $('.session-recording-name').text(jamTrackName);
var noCorrespondingTracks = false;
$.each(jamTrackMixers, function(index, mixer) {
@@ -1870,6 +1931,39 @@
function otherAudioFilled() {
$('.session-recordings .when-empty').hide();
$('.session-recording-name-wrapper').show();
+ $('.session-recordings').attr('media-state', 'open');
+ $('.session-livetracks').attr('media-state', 'open');
+ }
+
+
+ function resizeFluid() {
+ var trackWidth = 78; // 70 width + 8 margin
+ var trackPadding = 30; // 15px left and right
+
+ var numLiveTracks = $liveTracks.find('.track').length;
+ var numAudioTracks = $audioTracks.find('.track').length;
+ var totalWidth = $fluidTracks.width();
+
+
+ // calculate desired audio tracks width
+ var minimumLiveTrackWidth = numLiveTracks * trackWidth + trackPadding;
+ var otherAudioWidth = numAudioTracks * trackWidth + trackPadding;
+ var liveTrackWidth = totalWidth - otherAudioWidth;
+
+ // live tracks get precedence over audio tracks, if there is a content over width usage
+ if(liveTrackWidth < minimumLiveTrackWidth) {
+ logger.debug("live track width trumping mode")
+ liveTrackWidth = minimumLiveTrackWidth;
+ otherAudioWidth = totalWidth - liveTrackWidth;
+ }
+
+ var otherAudioWidthPct = Math.floor(100 * otherAudioWidth/totalWidth);
+ var liveTrackWidthPct = Math.ceil(100 * liveTrackWidth/totalWidth);
+
+ logger.debug("resizeFluid: ", minimumLiveTrackWidth, otherAudioWidth, otherAudioWidthPct, liveTrackWidthPct, liveTrackWidthPct)
+
+ $audioTracks.css('width', otherAudioWidthPct + '%');
+ $liveTracks.css('width', liveTrackWidthPct + '%');
}
function _addRecordingTrack(trackData, mixer, oppositeMixer) {
@@ -2169,6 +2263,8 @@
}
}
+
+
$.each(mixerIds, function(i,v) {
var mixerId = v;
// behavior: if this is the user's track in personal mode, then we mute the track globally
@@ -2553,6 +2649,8 @@
{ title: "JamTrack Can Not Open",
text: "Unable to open your JamTrack. Please contact support@jamkazam.com"
}, null, true);
+ } else {
+ rest.playJamTrack(jamTrack.id);
}
}
})
@@ -2806,20 +2904,32 @@
}
function onChangePlayPosition(e, data){
- logger.debug("calling jamClient.SessionTrackSeekMs(" + data.positionMs + ")");
+
+ var seek = data.positionMs;
if(data.playbackMonitorMode == context.JK.PLAYBACK_MONITOR_MODE.JAMTRACK) {
- context.jamClient.SessionJamTrackSeekMs(data.positionMs);
+ // if positionMs == 0, then seek it back to whatever the earliest play start is to catch all the prelude
+
+ if(seek == 0) {
+ var duration = context.jamClient.SessionGetJamTracksPlayDurationMs();
+ seek = duration.start;
+ }
+ }
+
+ logger.debug("calling jamClient.SessionTrackSeekMs(" + seek + ")");
+
+ if(data.playbackMonitorMode == context.JK.PLAYBACK_MONITOR_MODE.JAMTRACK) {
+ context.jamClient.SessionJamTrackSeekMs(seek);
}
else {
- context.jamClient.SessionTrackSeekMs(data.positionMs);
+ context.jamClient.SessionTrackSeekMs(seek);
}
}
function startStopRecording() {
// check first if a jamtrack is loaded, and playing; if so, tell user to stop the play
- if(sessionModel.jamTracks() && context.jamClient.isSessionTrackPlaying()) {
+ /**if(sessionModel.jamTracks() && context.jamClient.isSessionTrackPlaying()) {
app.notify(
{ title: "Can't Recording a Play JamTrack",
text: "Stop the JamTrack before trying to recording." },
@@ -2827,7 +2937,7 @@
true);
return;
- }
+ }*/
if(sessionModel.recordingModel.isRecording()) {
sessionModel.recordingModel.stopRecording();
@@ -2938,6 +3048,9 @@
$(document).on("change", ".metronome-select", onMetronomeChanged)
$metronomePlaybackSelect.metronomePlaybackMode().on(EVENTS.METRONOME_PLAYBACK_MODE_SELECTED, metronomePlaybackModeChanged)
context.JK.helpBubble($metronomePlaybackHelp, 'metromone-playback-modes', {} , {offsetParent: $screen, width:'400px'});
+ $(document).on('layout_resized', function() {
+ resizeFluid();
+ });
}
this.initialize = function(localRecordingsDialogInstance, recordingFinishedDialogInstance, friendSelectorDialog) {
@@ -2971,6 +3084,10 @@
$metronomePlaybackSelect = $('#metronome-playback-select')
$metronomePlaybackHelp = $('#metronome-playback-help')
$templatePendingMetronome = $('#template-pending-metronome');
+ $myTracks = $screen.find('.session-mytracks');
+ $liveTracks = $screen.find('.session-livetracks');
+ $audioTracks = $screen.find('.session-recordings');
+ $fluidTracks = $screen.find('.session-fluidtracks')
events();
diff --git a/web/app/assets/javascripts/sessionModel.js b/web/app/assets/javascripts/sessionModel.js
index c09f4e878..05f726557 100644
--- a/web/app/assets/javascripts/sessionModel.js
+++ b/web/app/assets/javascripts/sessionModel.js
@@ -127,6 +127,32 @@
}
}
+ function recordedJamTracks() {
+ if(currentSession && currentSession.claimed_recording) {
+ return currentSession.claimed_recording.recording.recorded_jam_track_tracks
+ }
+ else {
+ return null;
+ }
+ }
+
+ function jamTrackName() {
+ if (currentSession && currentSession.jam_track) {
+ return currentSession.jam_track.name;
+ }
+ else {
+ return null;
+ }
+ }
+
+ function recordedJamTrackName() {
+ if(currentSession && currentSession.claimed_recording && currentSession.claimed_recording.recording.jam_track) {
+ return currentSession.claimed_recording.recording.jam_track.name;
+ }
+ else {
+ return null;
+ }
+ }
// did I open up the current JamTrack?
function selfOpenedJamTracks() {
return currentSession && (currentSession.jam_track_initiator_id == context.JK.currentUserId)
@@ -843,8 +869,11 @@
this.backingTrack = backingTrack;
this.backingTracks = backingTracks;
this.recordedBackingTracks = recordedBackingTracks;
+ this.recordedJamTracks = recordedJamTracks;
this.setUserTracks = setUserTracks;
this.recordedTracks = recordedTracks;
+ this.jamTrackName = jamTrackName;
+ this.recordedJamTrackName = recordedJamTrackName;
this.jamTracks = jamTracks;
this.participants = participants;
this.joinSession = joinSession;
diff --git a/web/app/assets/javascripts/shopping_cart.js b/web/app/assets/javascripts/shopping_cart.js
index 4a7d2bf7e..86157b96d 100644
--- a/web/app/assets/javascripts/shopping_cart.js
+++ b/web/app/assets/javascripts/shopping_cart.js
@@ -30,10 +30,17 @@
e.preventDefault();
if (!context.JK.currentUserId) {
- window.location = '/client#/signin';
+ window.location = '/client#/checkoutSignin';
}
else {
- window.location = '/client#/order';
+ app.user().done(function(user) {
+ if(user.has_recurly_account && user.reuse_card) {
+ window.location = '/client#/checkoutOrder';
+ }
+ else {
+ window.location = '/client#/checkoutPayment';
+ }
+ })
}
}
diff --git a/web/app/assets/javascripts/sidebar.js b/web/app/assets/javascripts/sidebar.js
index d60d59868..cf733c451 100644
--- a/web/app/assets/javascripts/sidebar.js
+++ b/web/app/assets/javascripts/sidebar.js
@@ -13,6 +13,7 @@
var notificationPanel = null;
var chatPanel = null;
var me = null;
+ var $sidebar = null;
function initializeSearchPanel() {
$('#search_text_type').change(function() {
@@ -39,7 +40,9 @@
function initializeFriendsPanel() {
$('#sidebar-search-header').hide();
- refreshFriends();
+ app.user().done(function() {
+ refreshFriends();
+ })
return false;
}
@@ -406,11 +409,24 @@
me = this;
invitationDialog = invitationDialogInstance;
textMessageDialog = textMessageDialogInstance;
- events();
- initializeSearchPanel();
- initializeFriendsPanel();
- initializeChatPanel();
- initializeNotificationsPanel();
+ $sidebar = $('#sidebar-div')
+ app.user()
+ .done(function() {
+ events();
+ initializeSearchPanel();
+ initializeFriendsPanel();
+ initializeChatPanel();
+ initializeNotificationsPanel();
+ })
+ .fail(function(arg1) {
+ if(arg1 == "not_logged_in") {
+ $('#search-input').attr('disabled', 'disabled')
+ $('.sidebar .invite-friend-row').click(function() {
+ app.layout.showDialog('login-required-dialog')
+ });
+ $sidebar.addClass('not-logged-in')
+ }
+ })
};
this.refreshFriends = refreshFriends;
diff --git a/web/app/assets/javascripts/utils.js b/web/app/assets/javascripts/utils.js
index aee15f5c6..d4d91e6cb 100644
--- a/web/app/assets/javascripts/utils.js
+++ b/web/app/assets/javascripts/utils.js
@@ -981,6 +981,19 @@
})
}
+
+ context.JK.flash = function(msg, options) {
+ options = options || {}
+
+ var $flash = $(context._.template($('#template-flash-notice').html(), {}, { variable: 'data' }));
+ $flash.find('.flash-content').html(msg);
+ $('body').prepend($flash)
+ if(options.hide) {
+ // slide down (take 1 sec to do it), sit for 5, then leave over 1 second
+ setTimeout(function() {$flash.slideUp(1000, 'swing') }, options.hide * 1000)
+ }
+ }
+
context.JK.hasFlash = function () {
var hasFlash = false;
diff --git a/web/app/assets/javascripts/web/downloads.js b/web/app/assets/javascripts/web/downloads.js
index 82ef0a1da..6f01037e5 100644
--- a/web/app/assets/javascripts/web/downloads.js
+++ b/web/app/assets/javascripts/web/downloads.js
@@ -13,7 +13,7 @@
var platformName; // mac, windows, linux
var platformDisplay; // Mac, Windows, Linux
var platform = selectedPlatform; //MacOSX, Win32, Unix
- var platformName1, platformName2, platform1, platform2;
+ var platformName1, platformName2, platform1, platform2, platformDisplay1, platformDisplay2;
var uri = downloadUris[selectedPlatform];
// prepare template varaibles
@@ -21,21 +21,27 @@
platformName = "linux";
platformDisplay = "Linux"
platformName1 = "mac";
+ platformDisplay1 = "Mac";
platformName2 = "windows";
+ platformDisplay2 = "Windows";
platform1 = "MacOSX";
platform2 = "Win32"
} else if(selectedPlatform == "Win32") {
platformName = "windows";
platformDisplay = "Windows";
platformName1 = "mac";
+ platformDisplay1 = "Mac";
platformName2 = "linux"
+ platformDisplay2 = "Linux";
platform1 = "MacOSX";
platform2 = "Unix";
} else if(selectedPlatform == "MacOSX") {
platformName = "mac";
platformDisplay = "Mac";
platformName1 = "windows";
+ platformDisplay1 = "Windows";
platformName2 = "linux";
+ platformDisplay2 = "Linux";
platform1 = "Win32";
platform2 = "Unix";
}
@@ -48,7 +54,9 @@
platformName : platformName,
platformDisplay : platformDisplay,
platformName1 : platformName1,
+ platformDisplay1 : platformDisplay1,
platformName2 : platformName2,
+ platformDisplay2 : platformDisplay2,
platform1: platform1,
platform2: platform2,
uri : uri ? uri : '#',
@@ -105,9 +113,20 @@
function removeSpinner() {
$('body.web .spinner-large').remove();
}
+
+ function flashCongratulations() {
+ context.JK.flash('Congratulations!
Your account is ready.', {hide:10})
+ }
function listClients(congratulations) {
isCongratulations = congratulations;
+ if(isCongratulations) {
+ flashCongratulations();
+ }
+ else {
+ //flashCongratulations();
+ }
+
var rest = context.JK.Rest();
var currentOS = context.JK.detectOS();
var downloads = $('.downloads');
diff --git a/web/app/assets/stylesheets/client/account.css.scss b/web/app/assets/stylesheets/client/account.css.scss
index 782f43065..49bbb7c33 100644
--- a/web/app/assets/stylesheets/client/account.css.scss
+++ b/web/app/assets/stylesheets/client/account.css.scss
@@ -458,8 +458,8 @@
}
/** account sessions */
- .account-sessions {
- div#account-scheduled-sessions {
+ .account-sessions, .account-jamtracks {
+ div#account-scheduled-sessions, #account-my-jamtracks {
position: relative;
display: block;
overflow: auto;
diff --git a/web/app/assets/stylesheets/client/checkout.css.scss b/web/app/assets/stylesheets/client/checkout.css.scss
index efe918e22..7d5190ad8 100644
--- a/web/app/assets/stylesheets/client/checkout.css.scss
+++ b/web/app/assets/stylesheets/client/checkout.css.scss
@@ -1,25 +1,24 @@
+@import "client/common.css.scss";
+
.checkout-navigation {
padding: 20px 0px;
.nav-signin, .nav-payment-info, .nav-place-order {
- width: 30%;
+ margin-right:50px;
float: left;
}
.nav-signin {
- margin-left: 5%;
- }
-
- .nav-place-order {
- margin-right: 5%;
+ margin-left: 30px;
}
.nav-text {
font-size: 17px;
float: left;
+ color:$ColorTextDisabled;
}
.nav-text.selected {
- font-weight: bold;
+ color:$ColorTextHighlight;
}
.nav-arrow {
@@ -28,144 +27,6 @@
}
}
-.checkout-signin, .checkout-payment-info, .checkout-place-order {
- padding: 30px;
-
- .signin-form {
- padding: 10px;
-
- strong {
- font-weight: bold;
- }
-
- label {
- display: inline;
- }
-
- .signin-password {
- margin-left: 33px;
- }
-
- .login-error {
- background-color: #330000;
- border: 1px solid #990000;
- padding:4px;
-
- div.actions {
- margin-top:10px;
- }
- }
-
- .login-error-msg {
- display:none;
- margin-top:10px;
- text-align:center;
- color:#F00;
- font-size:11px;
- }
-
- .login-error .login-error-msg {
- display:block;
- }
- }
-
- form.payment-info {
- width: 100%;
-
- input[type="text"] {
- width: 90%;
- }
-
- .billing-address {
- float: left;
- width: 50%;
-
- h2.billing-caption {
- margin: 20px 5px;
- font-size: 16px;
- }
-
- .billing-label {
- padding-top: 8px;
- width: 30%;
- float: left;
- text-align: right;
- margin-right: 5px;
- }
-
- .billing-value {
- width: 65%;
- text-align: left;
- float: left;
- }
- }
-
- .payment-method {
- float: left;
- width: 50%;
-
- h2.payment-method-caption {
- margin: 20px 5px;
- font-size: 16px;
- }
-
- .card-label {
- padding-top: 8px;
- width: 35%;
- float: left;
- text-align: right;
- margin-right: 5px;
- }
-
- .card-value {
- width: 60%;
- text-align: left;
- float: left;
- }
-
- .save-card-checkbox {
- float:left;
- display:block;
- margin-right:5px;
- }
- }
-
- .shipping-address {
- float: left;
- width: 50%;
-
- h2.shipping-address-label {
- margin: 20px 5px;
- font-size: 16px;
- }
-
- .shipping-as-billing {
- float:left;
- display:block;
- margin-right:5px;
- }
-
- .divBillingHelper {
- padding-top: 2px;
- }
-
- .shipping-label {
- padding-top: 8px;
- width: 30%;
- float: left;
- text-align: right;
- margin-right: 5px;
- }
-
- .shipping-value {
- width: 65%;
- text-align: left;
- float: left;
- }
- }
- }
-}
-
.thanks-panel {
padding: 30px;
@@ -203,88 +64,3 @@
}
}
}
-
-.order-panel {
- padding: 30px;
-
- .order-header {
- h2 {
- font-size: 16px;
- }
- }
-
- .order-content {
- margin-top: 20px;
- }
-
- .order-left-page {
- float: left;
- width: 60%;
-
- .payment-info-page {
- padding: 5px;
-
- .info-caption-link {
- .caption-text {
- float: left;
- }
- .caption-link {
- float: left;
- margin-left: 5px;
- }
- }
-
- .address-info {
- width: 50%;
- float: left;
- }
-
- .payment-method-info {
- width: 50%;
- float: left;
- }
- }
- .order-items-page {
- padding: 5px;
-
- .cart-item-caption {
- width: 50%;
- text-align: left;
- float: left;
- }
-
- .cart-item-caption#header {
- font-weight: bold;
- }
-
- .cart-item-price {
- width: 25%;
- text-align: right;
- float: left;
- }
-
- .cart-item-quantity {
- width: 25%;
- text-align: right;
- float: left;
- }
-
- .cart-items {
- margin-top: 10px;
- }
-
- .cart-item {
- margin-top: 10px;
- }
- }
- }
- .order-right-page {
- float: right;
- width: 35%;
- text-align: center;
-
- .order-total {
- color: #ed3618;
- }
- }
-}
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/client/checkout_order.css.scss b/web/app/assets/stylesheets/client/checkout_order.css.scss
new file mode 100644
index 000000000..92db40291
--- /dev/null
+++ b/web/app/assets/stylesheets/client/checkout_order.css.scss
@@ -0,0 +1,204 @@
+@import "client/common.css.scss";
+#checkoutOrderScreen {
+
+
+ p {
+ font-size:12px;
+ margin:0;
+ }
+
+ .order-prompt {
+ color:white;
+ line-height:125%;
+ }
+
+ h2 {
+ color:white;
+ background-color:#4d4d4d;
+ font-weight:normal;
+ margin: 0 0 10px 0;
+ font-size:14px;
+ padding: 3px 0 3px 10px;
+ height: 14px;
+ line-height: 14px;
+ vertical-align: middle;
+ text-align:left;
+ }
+
+
+
+ .action-bar {
+ margin-top:20px;
+ }
+
+ .line {
+ margin:10px 0 10px;
+ border-width:0 0 1px 0;
+ border-color:#ccc;
+ border-style:solid;
+ }
+
+ #checkout-info-help {
+ margin-right:1px;
+ }
+
+ .billing-info-item {
+ margin-bottom:3px;
+ }
+
+ .country {
+ margin-left:15px;
+ }
+ .billing-address {
+ margin-bottom:20px;
+ }
+ .order-panel {
+ padding: 30px;
+ min-width:730px;
+
+ .place-order {
+ font-size: 14px;
+ padding: 1px 3px;
+ line-height: 15px;
+ }
+
+ .place-order-center {
+ text-align:center;
+ margin:20px 0 20px;
+ }
+
+ .change-payment-info {
+ position:absolute;
+ font-size:12px;
+ left:180px;
+ }
+
+ .billing-caption {
+ margin-bottom:5px;
+ float:left;
+ position:relative;
+ }
+ .order-header {
+ h2 {
+ font-size: 16px;
+ }
+ }
+
+ .shipping-address {
+ display:none;
+ }
+
+ .order-help {
+ margin:20px 0 30px;
+ }
+ .order-summary {
+ padding:0 20px;
+
+ .billing-caption {
+ float:none;
+ margin-bottom:10px;
+ }
+ }
+ .order-items-header {
+ float:left;
+ margin-bottom:5px;
+ }
+
+ .order-items-value {
+ float:right;
+ }
+
+ .order-content {
+ margin-top: 20px;
+ background-color:#262626;
+ }
+
+ .order-left-page {
+ float: left;
+ width: 65%;
+ background-color:#262626;
+ border-width:0 1px 0 0;
+ border-style:solid;
+ border-color:#333;
+ @include border_box_sizing;
+
+ .payment-info-page {
+
+ .info-caption-link {
+ .caption-text {
+ float: left;
+ }
+ .caption-link {
+ float: left;
+ margin-left: 5px;
+ }
+ }
+
+ .address-info {
+ width: 50%;
+ float: left;
+ padding:0 10px;
+ @include border_box_sizing;
+ margin-bottom:30px;
+ }
+
+ .payment-method-info {
+ width: 50%;
+ float: left;
+ padding:0 10px;
+ @include border_box_sizing;
+ }
+ }
+ .order-items-page {
+ .cart-item-caption {
+ width: 50%;
+ text-align: left;
+ float: left;
+ margin-bottom:10px;
+ @include border_box_sizing;
+ }
+
+ .cart-item-price {
+ width: 25%;
+ text-align: right;
+ float: left;
+ padding:0 10px;
+ margin-bottom:10px;
+ @include border_box_sizing;
+ }
+
+ .cart-item-quantity {
+ width: 10%;
+ text-align: right;
+ float: left;
+ padding:0 10px;
+ margin-bottom:10px;
+ @include border_box_sizing;
+ }
+
+ .cart-items {
+ margin-top: 10px;
+ padding-left:10px;
+ }
+
+ .cart-item {
+ margin-top: 10px;
+ }
+
+ .no-cart-items {
+ }
+ }
+ }
+ .order-right-page {
+ float: left;
+ width: 35%;
+ text-align: left;
+ background-color:#262626;
+ @include border_box_sizing;
+
+ .grand-total {
+ color: #ed3618;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/client/checkout_payment.css.scss b/web/app/assets/stylesheets/client/checkout_payment.css.scss
new file mode 100644
index 000000000..39ddaccbf
--- /dev/null
+++ b/web/app/assets/stylesheets/client/checkout_payment.css.scss
@@ -0,0 +1,239 @@
+@import "client/common.css.scss";
+#checkoutPaymentScreen {
+
+ .payment-wrapper {
+ padding:10px 30px;
+ min-width:600px;
+ }
+
+ p {
+ font-size:12px;
+ margin:0;
+ }
+
+ .payment-prompt {
+ color:white;
+ line-height:125%;
+ }
+
+ .field.error {
+ background-color: transparent !important;
+ padding: 0 !important;
+ border-width:0 !important;
+
+ li {
+ list-style:none;
+ }
+ }
+
+ h2 {
+ color:white;
+ background-color:#4d4d4d;
+ font-weight:normal;
+ margin: 0 0 30px 0;
+ font-size:14px;
+ padding-left:10px;
+
+ &.shipping-address-label {
+ //margin-top:10px;
+ }
+ }
+
+ .field {
+ margin-bottom:5px;
+ }
+
+ #payment_error {
+ @include border_box_sizing;
+ background-color: #300;
+ padding: 5px;
+ border: solid 1px #900;
+ }
+
+ form.payment-info {
+ width: 100%;
+ display:block;
+ background-color:#262626;
+ padding-bottom: 10px;
+ margin-bottom: 20px;
+
+ input[type="text"], input[type="password"] {
+ width: 90%;
+ @include border_box_sizing;
+ }
+
+ select#billing-country {
+ width:90%;
+ @include border_box_sizing;
+ }
+
+ &.signed-in {
+ .row.second {
+ .left-side {
+ width:100%;
+ }
+ .right-side {
+ display:none;
+ }
+ }
+ }
+ &.not-signed-in {
+ .row.second {
+ .left-side {
+ display:none;
+ }
+ .right-side {
+ width:100%;
+ }
+ }
+ }
+
+ #divShippingFirstName, #divShippingLastName {
+ display:none;
+ }
+
+ .row {
+
+ margin-top:20px;
+ width:100%;
+ clear:both;
+ }
+ .left-side, .right-side {
+ float: left;
+ width: 50%;
+ @include border_box_sizing;
+ }
+
+ .left-side {
+ border-width:0 1px 0 0;
+ border-style:solid;
+ border-color:#333;
+ }
+
+ .jamkazam-account-signup {
+ .account-label {
+ padding-top: 4px;
+ width: 35%;
+ float: left;
+ text-align: left;
+ padding-left: 5px;
+ @include border_box_sizing;
+ }
+
+ .account-value {
+ width: 65%;
+ text-align: left;
+ float: left;
+ @include border_box_sizing;
+ }
+
+ div.terms-of-service.ichecbuttons {
+ margin-left:5px;
+ .icheckbox_minimal {
+
+ float: left;
+ display: block;
+ margin: 5px 5px 0 0;
+ }
+ }
+ .terms-of-service-label-holder {
+ font-size:12px;
+ line-height:18px;
+ top:4px;
+ position:relative;
+ float:left;
+ }
+ }
+
+ .hint {
+ font-size:12px;
+ }
+ .billing-address {
+
+ .billing-label {
+ padding-top: 4px;
+ width: 35%;
+ float: left;
+ text-align: left;
+ padding-left: 5px;
+ @include border_box_sizing;
+ }
+
+ .billing-value {
+ width: 65%;
+ text-align: left;
+ float: left;
+ @include border_box_sizing;
+ }
+ }
+
+ .payment-method {
+
+
+ .card-label {
+ padding-top: 4px;
+ width: 35%;
+ float: left;
+ text-align: left;
+ padding-left: 5px;
+ @include border_box_sizing;
+ position:relative;
+ }
+
+ .card-value {
+ width: 65%;
+ text-align: left;
+ float: left;
+ @include border_box_sizing;
+ }
+
+ .save-card-checkbox, .reuse-existing-card-checkbox {
+ float:left;
+ display:block;
+ margin-right:5px;
+ }
+
+ label[for="reuse-existing-card"], label[for="save-card"] {
+ line-height: 18px;
+ vertical-align: middle;
+ }
+
+ .reuse-existing-card-helper {
+ margin-bottom:10px;
+ }
+ }
+
+ .shipping-address {
+
+ .shipping-as-billing {
+ float:left;
+ display:block;
+ margin: 0 5px 10px;
+ }
+
+ .divBillingHelper {
+ padding-top: 2px;
+
+ label {
+
+ }
+ }
+
+ .shipping-label {
+ padding-top: 4px;
+ width: 35%;
+ float: left;
+ text-align: left;
+ padding-left: 5px;
+ @include border_box_sizing;
+ }
+
+ .shipping-value {
+ width: 65%;
+ text-align: left;
+ float: left;
+ @include border_box_sizing;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/client/checkout_signin.css.scss b/web/app/assets/stylesheets/client/checkout_signin.css.scss
new file mode 100644
index 000000000..d436c5c58
--- /dev/null
+++ b/web/app/assets/stylesheets/client/checkout_signin.css.scss
@@ -0,0 +1,167 @@
+@import "client/common.css.scss";
+
+#checkoutSignInScreen {
+
+ .content-holder {
+
+ margin-top:40px;
+ color:$ColorTextTypical;
+
+ &.signed-in {
+ .left-side, .right-side {
+ display:none;
+ }
+ }
+ &.not-signed-in {
+ .already-signed-in {
+ display:none;
+ }
+ }
+ }
+ .already-signed-in {
+ text-align:center;
+ }
+
+ a.forgot-password {
+ position:absolute;
+ top:0;
+ left:0;
+ font-size:10px;
+ }
+
+ .signin-submit {
+ margin-right:2px;
+ }
+
+
+ .signup-later-prompt {
+ margin:30px 0 30px 0;
+ text-align:center;
+ }
+
+ .signin-prompt {
+ float:right;
+ text-align:left;
+ margin:30px 0 15px 0;
+ width:60%;
+ max-width:300px;
+ @include border_box_sizing;
+
+ span {
+ text-align:left;
+ }
+ }
+
+ p {
+ font-size:14px;
+ line-height:125%;
+ }
+
+ label {
+ display:inline-block;
+ width:80px;
+ text-align:left;
+ }
+
+ .login-error {
+
+ div.actions {
+ margin-top:10px;
+ }
+
+ .field {
+ background-color: #330000;
+ border: 1px solid #990000;
+ padding:8px;
+
+ }
+ }
+
+ .field {
+ display:inline;
+ @include border_box_sizing;
+ }
+
+ .login-error-msg {
+ display:none;
+ margin-top:10px;
+ text-align:center;
+ color:#F00;
+ font-size:11px;
+ width:60%;
+ max-width:300px;
+ @include border_box_sizing;
+ float:right;
+ margin-bottom:10px;
+ }
+
+
+ .login-error .login-error-msg {
+ display:block;
+ }
+
+ h3
+ {
+ color:$ColorTextHighlight;
+ text-align:center;
+ }
+
+ input {
+ width:60%;
+ max-width:300px;
+ @include border_box_sizing;
+ }
+
+ input[name="email"] {
+ margin-bottom:13px;
+ }
+
+ .btnNext {
+ margin:0 auto;
+ }
+
+ .left-side {
+ float:left;
+ width:50%;
+ @include border_box_sizing;
+ border-width:0 1px 0 0;
+ border-color:white;
+ border-style:solid;
+ text-align:right;
+ padding: 0 50px;
+ margin-bottom:20px;
+
+ .actions {
+ width:60%;
+ max-width:300px;
+ @include border_box_sizing;
+ position:relative;
+ float:right;
+ }
+ }
+
+ .left-side-content {
+ @include border_box_sizing;
+ width:100%;
+ }
+
+ .right-side {
+ float:left;
+ width:50%;
+ @include border_box_sizing;
+ padding: 0 50px;
+
+ .actions {
+ width:100%;
+ text-align:center;
+ }
+ }
+
+ .facebook-prompt {
+ margin:40px 0 10px 0;
+ float:right;
+ text-align:left;
+ width:249px;
+ @include border_box_sizing;
+ }
+}
\ 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 c953408ec..28049797e 100644
--- a/web/app/assets/stylesheets/client/client.css
+++ b/web/app/assets/stylesheets/client/client.css
@@ -56,6 +56,10 @@
*= require ./jamtrack
*= require ./shoppingCart
*= require ./checkout
+ *= require ./checkout_signin
+ *= require ./checkout_payment
+ *= require ./checkout_order
+ *= require ./genreSelector
*= require ./sessionList
*= require ./searchResults
*= require ./clientUpdate
diff --git a/web/app/assets/stylesheets/client/common.css.scss b/web/app/assets/stylesheets/client/common.css.scss
index a349729f5..69f430b7f 100644
--- a/web/app/assets/stylesheets/client/common.css.scss
+++ b/web/app/assets/stylesheets/client/common.css.scss
@@ -12,8 +12,13 @@ $ColorLinkHover: #82AEAF;
$ColorSidebarText: #a0b9bd;
$ColorScreenBackground: lighten($ColorUIBackground, 10%);
$ColorTextBoxBackground: #c5c5c5;
+$ColorTextBoxDisabledBackground: #999;
$ColorRecordingBackground: #471f18;
+$ColorTextHighlight: white;
+$ColorTextTypical: #CCCCCC;
+$ColorTextDisabled: #AAAAAA;
+
$color1: #006AB6; /* mid blue */
$color2: #9A9084; /* warm gray */
$color3: #B11254; /* magenta */
@@ -292,4 +297,24 @@ $fair: #cc9900;
}
}
+.badge-number {
+ font-size:25px;
+ color:white;
+ background-color:$ColorScreenPrimary;
+ width:30px;
+ height:30px;
+ -webkit-border-radius:50%;
+ -moz-border-radius:50%;
+ border-radius:50%;
+ text-align:center;
+ display:inline-block;
+ border:2px solid white;
+ margin-right:10px;
+
+ &.disabled {
+ color:$ColorTextDisabled;
+ border-color:$ColorTextDisabled;
+ background-color:transparent;
+ }
+}
diff --git a/web/app/assets/stylesheets/client/content.css.scss b/web/app/assets/stylesheets/client/content.css.scss
index 9bee28a72..f5bad0e14 100644
--- a/web/app/assets/stylesheets/client/content.css.scss
+++ b/web/app/assets/stylesheets/client/content.css.scss
@@ -222,6 +222,10 @@
border:none;
-webkit-box-shadow: inset 2px 2px 3px 0px #888;
box-shadow: inset 2px 2px 3px 0px #888;
+
+ &:disabled {
+ background-color: $ColorTextBoxDisabledBackground;
+ }
}
}
@@ -383,6 +387,10 @@ a.arrow-down {
select {
font-size:11px;
margin-top:4px;
+
+ }
+ .dropdown-wrapper {
+ margin-right: 4px;
}
}
diff --git a/web/app/assets/stylesheets/client/flash.css.scss b/web/app/assets/stylesheets/client/flash.css.scss
new file mode 100644
index 000000000..918f3b014
--- /dev/null
+++ b/web/app/assets/stylesheets/client/flash.css.scss
@@ -0,0 +1,24 @@
+body > .flash-notice {
+
+ background-color:#006673;
+ position:absolute;
+ width:300px;
+ height:100px;
+ line-height: 100px;
+ margin:0 auto;
+ border-width:0 1px 1px;
+ border-style:solid;
+ border-color:white;
+ text-align:center;
+ left:50%;
+ margin-left:-150px;
+
+ .flash-content {
+ color:white;
+ font-size:20px;
+ display: inline-block;
+ vertical-align: middle;
+ line-height: normal;
+ }
+
+}
diff --git a/web/app/assets/stylesheets/client/home.css.scss b/web/app/assets/stylesheets/client/home.css.scss
index d9b7d9d45..c4e7c07f7 100644
--- a/web/app/assets/stylesheets/client/home.css.scss
+++ b/web/app/assets/stylesheets/client/home.css.scss
@@ -8,6 +8,10 @@
background-repeat: no-repeat;
background-position: bottom left;
border: 1px solid $translucent1;
+
+ &.not-logged-in {
+ opacity:0.6;
+ }
}
.homecard.createsession {
background-image: url(/assets/content/bkg_home_create.jpg);
@@ -38,7 +42,7 @@
background-image: url(/assets/content/bkg_home_bands.jpg);
}
.homecard.musicians {
- background-image: url(/assets/content/bkg_home_musicians.jpg);
+ background-image: url(/assets/content/bkg_home_guitar.jpg);
}
.homecard.jamtrack {
background-image: url(/assets/content/bkg_home_jamtracks.jpg);
@@ -94,7 +98,7 @@
background-image: url(/assets/content/bkg_home_bands_x.jpg);
}
.homecard.musicians.hover {
- background-image: url(/assets/content/bkg_home_musicians_x.jpg);
+ background-image: url(/assets/content/bkg_home_guitar_x.jpg);
}
.homecard.jamtrack.hover {
background-image: url(/assets/content/bkg_home_jamtracks_x.jpg);
diff --git a/web/app/assets/stylesheets/client/jamkazam.css.scss b/web/app/assets/stylesheets/client/jamkazam.css.scss
index d777d3f11..d1e2b4192 100644
--- a/web/app/assets/stylesheets/client/jamkazam.css.scss
+++ b/web/app/assets/stylesheets/client/jamkazam.css.scss
@@ -322,6 +322,10 @@ input[type="text"], input[type="password"]{
border:none;
padding:3px;
font-size:15px;
+
+ &:disabled {
+ background-color: $ColorTextBoxDisabledBackground;
+ }
}
textarea {
@@ -388,6 +392,24 @@ textarea {
padding:5px;
border: solid 1px #900;
+ .error-text {
+ display:block;
+ font-size:11px;
+ color:#F00;
+ margin:10px 0 0;
+ }
+
+ &.transparent {
+ background-color:transparent;
+ padding:0;
+ border-width:0px;
+
+ .error-text {
+ margin:5px 0 0;
+ font-size:14px;
+ font-weight:bold;
+ }
+ }
}
.error input {
@@ -399,12 +421,6 @@ textarea {
display:none;
}
-.error .error-text {
- display:block;
- font-size:11px;
- color:#F00;
- margin:10px 0 0;
-}
.grey {
color:#999;
}
diff --git a/web/app/assets/stylesheets/client/jamtrack.css.scss b/web/app/assets/stylesheets/client/jamtrack.css.scss
index 7d69b1b6f..d63839387 100644
--- a/web/app/assets/stylesheets/client/jamtrack.css.scss
+++ b/web/app/assets/stylesheets/client/jamtrack.css.scss
@@ -1,4 +1,74 @@
+@import 'common';
+
+#jamtrackLanding {
+ ul {
+ li {
+ margin: 1px 4px 1px 4em;
+ font-size:9px;
+ }
+ }
+
+ .list-columns {
+ h2 {
+ font-size: 16pt;
+ font-weight:300;
+ font-style: bolder;
+ font-family: verdana;
+ text-transform: lowercase;
+ margin-bottom: 2em;
+ }
+
+ .free-jamtrack {
+ font-size: 11pt;
+ padding: 3px;
+ @include border-radius(7px);
+ background-color:$ColorScreenPrimary;
+ text-align: center;
+ vertical-align: center;
+ }
+
+ .what, .howto {
+ margin-bottom: 2em;
+ }
+ p {
+ font-size: 12pt !important;
+ font-weight: normal;
+ line-height: 16px;
+ color: #dddddd;
+ * {
+ font-size: 10pt !important;
+ font-weight: normal;
+ line-height: 16px;
+ }
+ }
+ .about {
+ @include border_box_sizing;
+ float: left;
+ width: 50%;
+ > * {
+ margin: 4px;
+ }
+ }
+ .browse {
+ @include border_box_sizing;
+ float: left;
+ width: 50%;
+ > * {
+ margin: 4px;
+ }
+ }
+ }
+}
+
#jamtrackScreen {
+ .jamtrack-header {
+ background-color: #4c4c4c;
+ font-weight: bold;
+ text-transform: uppercase;
+ height: 2em;
+ padding: 4px;
+ }
+
a.jamtrack_help {
color: #fff;
text-decoration: none;
@@ -11,6 +81,8 @@
.jamtrack-content {
text-align: center;
+ border: 1px solid #222222;
+ padding: 2px
}
.no-jamtracks-msg {
@@ -23,14 +95,16 @@
}
.jamtrack-detail {
+ @include border_box_sizing;
float: left;
- width: 50%;
+ width: 30%;
padding: 10px 0px;
-
.detail-label {
width: 40%;
float: left;
margin-top: 5px;
+ font-weight: 400;
+ font-size: 11pt;
}
.detail-value {
@@ -56,15 +130,17 @@
.jamtrack-detail-btn {
cursor: pointer;
- margin-top: 5px;
+ margin-top: 7px;
margin-right: 5px;
padding-top: 5px;
+ vertical-align: bottom;
}
}
.jamtrack-tracks {
+ @include border_box_sizing;
float: left;
- width: 25%;
+ width: 50%;
padding: 10px 0px;
.tracks-caption {
@@ -78,6 +154,7 @@
.instrument-image {
float: left;
+ margin-right: 2px;
}
.instrument-desc {
@@ -88,8 +165,9 @@
}
.jamtrack-action {
+ @include border_box_sizing;
float: left;
- width: 25%;
+ width: 20%;
padding: 10px 0px;
text-align: center;
@@ -113,4 +191,30 @@
width: 60%;
}
}
+}
+
+#jamtrack-license-dialog {
+ .dialog-inner {
+ height: auto;
+ .content-body {
+ max-height: auto;
+ .content-body-scroller {
+ height: 350px;
+ .paragraph {
+ margin-bottom: 1em;
+ }
+ overflow: hidden;
+ }
+ border: 1px solid #222;
+ margin: 4px 4px 8px 4px;
+ }
+ }
+}
+
+.jamtrack_buttons {
+ margin: 8px 4px 12px 4px;
+}
+
+.capitalize {
+ text-transform: capitalize
}
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/client/session.css.scss b/web/app/assets/stylesheets/client/session.css.scss
index 42467d158..00fde3ac1 100644
--- a/web/app/assets/stylesheets/client/session.css.scss
+++ b/web/app/assets/stylesheets/client/session.css.scss
@@ -13,6 +13,7 @@
background-color:#4c4c4c;
min-height:20px;
position:relative;
+ min-width:690px;
}
.pending-metronome {
@@ -55,7 +56,11 @@
.recording-position {
display:inline-block;
- width:80%;
+ width:calc(100% - 27px - 47px); // 27 accounts for play arrow, 47px acconts for the total time (0:00)
+
+ position:absolute;
+ left:0;
+ margin-left:27px;
font-family:Arial, Helvetica, sans-serif;
font-size:11px;
@@ -150,6 +155,7 @@
.session-add {
margin-top:9px;
+ margin-right:10px;
}
.session-add a {
vertical-align:top;
@@ -193,6 +199,139 @@
color:#ffcc00;
cursor:help;
}
+
+
+ .leave {
+ margin-right:25px;
+ }
+
+ $mytracks-minwidth: 180px;
+ $livetracks-minwidth:180px;
+ $otheraudio-minwidth:195px;
+ $otheraudio-open-minwidth:230px;
+
+ .session-mytracks {
+ padding-left:15px;
+ float:left;
+ display:inline-block;
+ width:$mytracks-minwidth;
+ border-right:solid 1px #666;
+ @include border_box_sizing;
+ }
+
+ .session-fluidtracks {
+ width:calc(100% - #{$mytracks-minwidth});
+ position:relative;
+ float:left;
+ @include border_box_sizing;
+ }
+
+ .session-livetracks {
+ padding-left:15px;
+ padding-right:15px;
+ float:left;
+ display:inline-block;
+ min-width:$livetracks-minwidth;
+ max-width:calc(100% - #{$otheraudio-minwidth});
+ width:calc(100% - #{$otheraudio-minwidth});
+ border-right:solid 1px #666;
+ @include border_box_sizing;
+
+ &[media-state="open"] {
+ width:calc(100% - #{$otheraudio-open-minwidth});
+ max-width:calc(100% - #{$otheraudio-open-minwidth});
+ min-width:#{$livetracks-minwidth};
+ }
+
+ .recording {
+ left: 50%;
+ margin-left: -67px;
+ }
+
+ .recording-controls {
+ min-width:230px;
+ }
+ }
+
+ .session-recordings {
+ padding-left:15px;
+ padding-right:15px;
+ display:inline-block;
+ max-width:calc(100% - #{$livetracks-minwidth});
+ min-width:#{$otheraudio-minwidth};
+ width:#{$otheraudio-minwidth};
+ float:left;
+ @include border_box_sizing;
+
+ &[media-state="open"] {
+ width:$otheraudio-open-minwidth;
+ min-width:$otheraudio-open-minwidth;
+ max-width:calc(100% - #{$livetracks-minwidth});
+ }
+
+
+ .recording.metronome-mode {
+ margin-left:-100px;
+ }
+ }
+
+ #tracks .when-empty.livetracks {
+ margin: 0px;
+ padding:0px;
+ display:block;
+ padding-top: 125px;
+ vertical-align:middle;
+ text-align:center;
+ }
+ #tracks .when-empty.recordings {
+ //padding-top: 137px;
+ text-align:left;
+ padding-top:6px;
+ margin:0;
+ }
+
+ #tracks .when-empty a {
+ text-decoration: underline;
+ color: inherit;
+ }
+
+ .session-add {
+ margin-top:9px;
+ margin-bottom:8px;
+ font-size:16px;
+ height: 22px;
+ min-height: 22px;
+ max-height: 22px;
+ }
+
+ .session-add a {
+ color:#ccc;
+ text-decoration: none;
+ }
+
+ .session-add a img {
+ vertical-align:bottom;
+ }
+
+ .session-tracks-scroller {
+ position:relative;
+ overflow-x:auto;
+ overflow-y:hidden;
+ width:100%;
+ height:370px;
+ float:left;
+ white-space:nowrap;
+ }
+
+ .play-controls-holder {
+ width:100%;
+ text-align:center;
+
+ .recording {
+ left:50%;
+ margin-left:-46.5%;
+ }
+ }
}
@@ -334,84 +473,6 @@ table.vu td {
margin-left:15px;
}
-.leave {
- margin-right:25px;
-}
-
-.session-mytracks {
- margin-left:15px;
- float:left;
- display:inline-block;
- width:19%;
- min-width:165px;
- border-right:solid 1px #666;
-}
-
-.session-livetracks {
- margin-left:15px;
- float:left;
- display:inline-block;
- width:35%;
- min-width:220px;
- border-right:solid 1px #666;
-}
-
-.session-recordings {
- margin-left:15px;
- display:inline-block;
- width:35%;
- min-width:220px;
-}
-
-#tracks .when-empty.livetracks {
- margin: 0px;
- padding:0px;
- display:block;
- padding-top: 125px;
- vertical-align:middle;
- text-align:center;
-}
-#tracks .when-empty.recordings {
- //padding-top: 137px;
- text-align:left;
- padding-top:6px;
- margin:0;
-}
-
-#tracks .when-empty a {
- text-decoration: underline;
- color: inherit;
-}
-
-.session-add {
- margin-top:9px;
- margin-bottom:8px;
- font-size:16px;
- height: 22px;
- min-height: 22px;
- max-height: 22px;
-}
-
-.session-add a {
- color:#ccc;
- text-decoration: none;
-}
-
-.session-add a img {
- vertical-align:bottom;
-}
-
-
-.session-tracks-scroller {
- position:relative;
- overflow-x:auto;
- overflow-y:hidden;
- width:100%;
- height:370px;
- float:left;
- white-space:nowrap;
-}
-
@@ -750,7 +811,6 @@ table.vu td {
margin-top:15px;
padding:3px;
height:19px;
- width:95%;
background-color:#242323;
position:absolute;
text-align:center;
diff --git a/web/app/assets/stylesheets/client/sessionList.css.scss b/web/app/assets/stylesheets/client/sessionList.css.scss
index b0df3def5..ce326ef9c 100644
--- a/web/app/assets/stylesheets/client/sessionList.css.scss
+++ b/web/app/assets/stylesheets/client/sessionList.css.scss
@@ -1,7 +1,7 @@
@import "client/common";
-table.findsession-table, table.local-recordings, table.open-jam-tracks, table.open-backing-tracks, #account-session-detail {
+table.findsession-table, table.local-recordings, table.open-jam-tracks, table.open-backing-tracks, table.cart-items, #account-session-detail, table.payment-table {
.latency-unacceptable {
width: 50px;
@@ -64,7 +64,7 @@ table.findsession-table, table.local-recordings, table.open-jam-tracks, table.op
text-align:center;
}
}
-table.findsession-table, table.local-recordings, table.open-jam-tracks, table.open-backing-tracks {
+table.findsession-table, table.local-recordings, table.open-jam-tracks, table.open-backing-tracks, table.cart-items, table.payment-table {
width:98%;
height:10%;
font-size:11px;
diff --git a/web/app/assets/stylesheets/client/shoppingCart.css.scss b/web/app/assets/stylesheets/client/shoppingCart.css.scss
index f8a8c6fdb..ba3b31146 100644
--- a/web/app/assets/stylesheets/client/shoppingCart.css.scss
+++ b/web/app/assets/stylesheets/client/shoppingCart.css.scss
@@ -29,9 +29,7 @@
}
.cart-item-caption {
- width: 50%;
- text-align: left;
- float: left;
+ text-align: left;
}
.cart-item-caption#header {
@@ -39,21 +37,15 @@
}
.cart-item-price {
- width: 15%;
- text-align: right;
- float: left;
+ text-align: right;
}
.cart-item-quantity {
- width: 15%;
- text-align: right;
- float: left;
+ text-align: right;
}
.cart-item-actions {
- width: 20%;
- text-align: center;
- float: left;
+ //text-align: center;
}
.cart-items {
diff --git a/web/app/assets/stylesheets/client/sidebar.css.scss b/web/app/assets/stylesheets/client/sidebar.css.scss
index 17c975616..59a3bdfb5 100644
--- a/web/app/assets/stylesheets/client/sidebar.css.scss
+++ b/web/app/assets/stylesheets/client/sidebar.css.scss
@@ -5,6 +5,10 @@
background-color: $ColorElementPrimary;
+ &.not-logged-in {
+ opacity:0.6;
+ }
+
.panel-header {
margin:0px;
padding:0px;
diff --git a/web/app/assets/stylesheets/dialogs/loginRequiredDialog.css.scss b/web/app/assets/stylesheets/dialogs/loginRequiredDialog.css.scss
new file mode 100644
index 000000000..bc27a60ed
--- /dev/null
+++ b/web/app/assets/stylesheets/dialogs/loginRequiredDialog.css.scss
@@ -0,0 +1,12 @@
+#login-required-dialog {
+
+ width:455px;
+
+ p {
+ margin:0 0 20px 0;
+ }
+
+ .buttons {
+ margin-top:20px;
+ }
+}
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/landings/landing_page.css.scss b/web/app/assets/stylesheets/landings/landing_page.css.scss
index ce9f7496b..56d1ad837 100644
--- a/web/app/assets/stylesheets/landings/landing_page.css.scss
+++ b/web/app/assets/stylesheets/landings/landing_page.css.scss
@@ -9,24 +9,6 @@ body.web.landing_page {
display:none;
}
- .badge-number {
- font-size:125%;
- color:white;
- background-color:$ColorScreenPrimary;
- width:30px;
- height:30px;
- //position:absolute;
- //left:-10px;
- //top:4px;
- margin-right:10px;
- -webkit-border-radius:50%;
- -moz-border-radius:50%;
- border-radius:50%;
- text-align:center;
- display:inline-block;
- border:2px solid white;
- }
-
&.kick {
h1 {
diff --git a/web/app/assets/stylesheets/web/audioWidgets.css.scss b/web/app/assets/stylesheets/web/audioWidgets.css.scss
index 5a6fb3303..4c044f8b7 100644
--- a/web/app/assets/stylesheets/web/audioWidgets.css.scss
+++ b/web/app/assets/stylesheets/web/audioWidgets.css.scss
@@ -30,6 +30,8 @@
&.metronome-mode {
width:200px;
+ margin-left:-100px;
+ left:50%;
.recording-position, .recording-current, .playback-mode-buttons {
display:none;
}
diff --git a/web/app/assets/stylesheets/web/downloads.css.scss b/web/app/assets/stylesheets/web/downloads.css.scss
new file mode 100644
index 000000000..4634d2f04
--- /dev/null
+++ b/web/app/assets/stylesheets/web/downloads.css.scss
@@ -0,0 +1,131 @@
+@import "client/common.css.scss";
+
+body.downloads {
+
+
+ h2 {
+ font-size:22px;
+ color:white !important;
+ }
+
+ ul {
+ width:auto !important;
+ }
+
+ ul li {
+ color:#CCC;
+ font-size:14px;
+ line-height:125%;
+ text-indent:-5px;
+ margin-left:40px;
+
+ &:before
+ { content:"-";
+ position:relative;
+ left:-5px;
+ }
+ }
+
+ p {
+ margin:20px 0 10px;
+ line-height:125% !important;
+ width:100% !important;
+ }
+
+ .download-app {
+
+ padding-top:20px;
+ width:55%;
+ float:left;
+ @include border_box_sizing;
+ }
+
+ .jamtracks {
+
+ padding-top:20px;
+ float:left;
+ width:45%;
+ @include border_box_sizing;
+ padding-left:5%;
+
+ .shop-jamtracks {
+
+ }
+
+ .special-value {
+ font-size:16px;
+ }
+ }
+
+ .video-container {
+ position:relative;
+ width:320px;
+ height:180px;
+ }
+
+ .video-container iframe,
+ .video-container object,
+ .video-container embed {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ }
+
+ .download-content {
+ border-style:solid;
+ border-width:0 1px 0 0;
+ border-color:#ccc;
+ }
+ .download-entreaty {
+
+
+ p.click-to-download {
+ margin-bottom:5px;
+ }
+
+ }
+
+ .downloads-container {
+ position:relative;
+ display:inline-block;
+ height:92px;
+ top:-25px;
+ left:20px;
+ }
+ .downloads-blurb {
+
+ h5 {
+ color:#ccc;
+ margin-bottom:5px;
+ margin-top:-15px;
+ }
+
+ ul li {
+ font-size:12px;
+ margin-left:15px;
+ }
+
+ .choose-other-platform {line-height:125%;}
+
+ }
+
+ .downloads {
+ position:relative;
+ }
+
+ .download-box {
+ color:#fff;
+ text-align:center;
+ }
+
+ .go-jamtrack-shopping {
+ line-height: 125%;
+ text-align: center;
+ display: block;
+ position: absolute;
+ right: 0;
+ bottom: 90px;
+ }
+}
diff --git a/web/app/assets/stylesheets/web/main.css.scss b/web/app/assets/stylesheets/web/main.css.scss
index 82c5160fd..2a2891d60 100644
--- a/web/app/assets/stylesheets/web/main.css.scss
+++ b/web/app/assets/stylesheets/web/main.css.scss
@@ -48,6 +48,16 @@ body.web {
margin-left:70px;
width:400px;
+ &.no-user-dropdown {
+ margin-left:0;
+ margin-top:30px;
+ position:absolute;
+ top:0;
+ right:0;
+ text-align:center;
+ width:350px;
+ }
+
h1 {
color:#ed3618;
font-size:26px;
@@ -310,15 +320,29 @@ body.web {
.register-page {
.register-container {
- padding:10px;
+ padding:0 10px 10px;
}
input.register-musician {
}
+ .create-account-header {
+ padding-top:10px;
+ margin-left:10px;
+ }
+
.actions {
margin-top:20px;
+ input, a {
+ font-size:18px;
+ }
+ input {
+ margin-left:2px;
+ }
+ a {
+ margin-left:10px;
+ }
}
.error {
@@ -327,6 +351,10 @@ body.web {
margin-right:-12px;
}
+ div.register-as {
+ margin-left:45px;
+ }
+
input.register-fan {
margin-left:20px;
}
@@ -415,39 +443,11 @@ body.web {
font-weight:normal;
}
- .download-box {
- padding:15px;
- color:#fff;
- text-align:center;
- font-size:20px;
- -webkit-border-radius: 8px;
- border-radius: 8px;
- background:#383838;
- margin-top:100px;
- }
-
- .system-requirements {
- margin-top:35px;
- display:none;
-
- ul {
- display:none;
- list-style:disc outside none;
- padding-left:40px;
- }
- }
div.proceed {
margin:40px 0 250px 5px;
}
- .downloads {
- ul {
- list-style:disc outside none;
- padding-left:40px;
- }
- }
-
.congratulations {
padding-top:1px;
}
diff --git a/web/app/assets/stylesheets/web/web.css b/web/app/assets/stylesheets/web/web.css
index d91e79de2..93d52b7c2 100644
--- a/web/app/assets/stylesheets/web/web.css
+++ b/web/app/assets/stylesheets/web/web.css
@@ -13,6 +13,7 @@
*= require client/hoverBubble
*= require client/help
*= require client/listenBroadcast
+*= require client/flash
*= require web/main
*= require web/footer
*= require web/recordings
@@ -20,6 +21,7 @@
#= require web/sessions
*= require web/events
*= require web/session_info
+*= require web/downloads
*= require users/signinCommon
*= require dialogs/dialog
*= require landings/landing_page
diff --git a/web/app/controllers/api_genres_controller.rb b/web/app/controllers/api_genres_controller.rb
index c413bf1f2..293552737 100644
--- a/web/app/controllers/api_genres_controller.rb
+++ b/web/app/controllers/api_genres_controller.rb
@@ -1,8 +1,5 @@
class ApiGenresController < ApiController
- # have to be signed in currently to see this screen
- before_filter :api_signed_in_user
-
respond_to :json
def index
diff --git a/web/app/controllers/api_instruments_controller.rb b/web/app/controllers/api_instruments_controller.rb
index fc2c44fd9..c5f10bd49 100644
--- a/web/app/controllers/api_instruments_controller.rb
+++ b/web/app/controllers/api_instruments_controller.rb
@@ -1,8 +1,5 @@
class ApiInstrumentsController < ApiController
- # have to be signed in currently to see this screen
- before_filter :api_signed_in_user
-
respond_to :json
def index
diff --git a/web/app/controllers/api_jam_tracks_controller.rb b/web/app/controllers/api_jam_tracks_controller.rb
index eff17afa0..f1b4264fa 100644
--- a/web/app/controllers/api_jam_tracks_controller.rb
+++ b/web/app/controllers/api_jam_tracks_controller.rb
@@ -1,18 +1,41 @@
class ApiJamTracksController < ApiController
# have to be signed in currently to see this screen
- before_filter :api_signed_in_user
+ before_filter :api_signed_in_user, :except => [:index]
+ before_filter :api_any_user, :only => [:index]
before_filter :lookup_jam_track_right, :only => [:download,:enqueue, :show_jam_track_right]
respond_to :json
def index
- data = JamTrack.index(params, current_user)
+ data = JamTrack.index(params, any_user)
@jam_tracks, @next = data[0], data[1]
render "api_jam_tracks/index", :layout => nil
end
+ def played
+ if params[:id].blank?
+ render(:json => { :message => "JamTrack ID required" }, :status => 400) and return
+ end
+ play = PlayablePlay.new
+ play.player_id = current_user.id
+ play.ip_address = request.remote_ip
+
+ # VRFS-2916 jam_tracks.id is varchar: REMOVE
+ # play.jam_track = JamTrack.where(id: params[:id].to_i).first
+ # VRFS-2916 jam_tracks.id is varchar: ADD
+ play.playable = JamTrack.where(id: params[:id]).first
+
+ play.save
+
+ if play.errors.any?
+ render :json => { :message => "Unexpected error occurred" }, :status => 500
+ else
+ render :json => {}, :status => 201
+ end
+ end
+
def purchased
params[:show_purchased_only] = true
data = JamTrack.index(params, current_user)
@@ -23,7 +46,8 @@ class ApiJamTracksController < ApiController
render "api_jam_tracks/purchased", :layout => nil
end
- def download
+
+ def download
if @jam_track_right.valid?
sample_rate = params[:sample_rate].nil? ? nil : params[:sample_rate].to_i
if (@jam_track_right && @jam_track_right.ready?(sample_rate))
diff --git a/web/app/controllers/api_recordings_controller.rb b/web/app/controllers/api_recordings_controller.rb
index b6f246e72..27eb6c856 100644
--- a/web/app/controllers/api_recordings_controller.rb
+++ b/web/app/controllers/api_recordings_controller.rb
@@ -1,7 +1,7 @@
class ApiRecordingsController < ApiController
before_filter :api_signed_in_user, :except => [ :add_like ]
- before_filter :lookup_recording, :only => [ :show, :stop, :claim, :discard, :keep, :delete_claim, :add_timeline_data ]
+ before_filter :lookup_recording, :only => [ :show, :stop, :claim, :discard, :keep, :delete_claim, :add_timeline ]
before_filter :lookup_recorded_track, :only => [ :download, :upload_next_part, :upload_sign, :upload_part_complete, :upload_complete ]
before_filter :lookup_recorded_backing_track, :only => [ :backing_track_download, :backing_track_upload_next_part, :backing_track_upload_sign, :backing_track_upload_part_complete, :backing_track_upload_complete ]
before_filter :lookup_recorded_video, :only => [ :video_upload_sign, :video_upload_start, :video_upload_complete ]
@@ -379,7 +379,7 @@ class ApiRecordingsController < ApiController
# metadata
def add_timeline
- @recording.add_timeline(params[:metadata])
+ @recording.add_timeline(params)
render :json => {}, :status => 200
end
diff --git a/web/app/controllers/api_recurly_controller.rb b/web/app/controllers/api_recurly_controller.rb
index 50f4f703d..480e33ee8 100644
--- a/web/app/controllers/api_recurly_controller.rb
+++ b/web/app/controllers/api_recurly_controller.rb
@@ -1,15 +1,70 @@
require 'jam_ruby/recurly_client'
class ApiRecurlyController < ApiController
- before_filter :api_signed_in_user
+ before_filter :api_signed_in_user, :except => [:create_account]
before_filter :create_client
respond_to :json
# create Recurly account
def create_account
- @account = @client.find_or_create_account(current_user, params[:billing_info])
- render :json=>account_json(@account)
- rescue RecurlyClientError => x
- render json: { :message => x.inspect, errors: x.errors }, :status => 404
+
+ billing_info = params[:billing_info]
+ shipping_info = params[:shipping_info]
+ # should we let the user reuse this card next time?
+ reuse_card_next_time = params[:reuse_card_next_time] == "true"
+ # should we update the card info, or use what's on file this time?
+ reuse_card_this_time = params[:reuse_card_this_time] == "true"
+ # terms of service accepted?
+ terms_of_service = params[:terms_of_service] == "true"
+
+
+ if current_user
+ # keep reuse card up-to-date
+ User.where(id: current_user.id).update_all(reuse_card: params[:reuse_card_next_time])
+ else
+ options = {
+ remote_ip: request.remote_ip,
+ first_name: billing_info[:first_name],
+ last_name: billing_info[:last_name],
+ email: params[:email],
+ password: params[:password],
+ password_confirmation: params[:password],
+ terms_of_service: terms_of_service,
+ instruments: [{ :instrument_id => 'other', :proficiency_level => 1, :priority => 1 }],
+ birth_date: nil,
+ location: { :country => billing_info[:country], :state => billing_info[:state], :city => billing_info[:city]},
+ musician: true,
+ skip_recaptcha: true,
+ invited_user: nil,
+ fb_signup: nil,
+ signup_confirm_url: ApplicationHelper.base_uri(request) + "/confirm",
+ any_user: any_user,
+ reuse_card: reuse_card_next_time
+ }
+
+ user = UserManager.new.signup(options)
+
+ if user.errors.any?
+ # render any @user.errors on error
+ respond_with_model(user)
+ return
+ else
+ sign_in user
+ end
+ end
+
+ begin
+ billing_info[:ip_address] = request.remote_ip if billing_info
+ if reuse_card_this_time
+ # do not attempt to update any billing/shipping info unless the user re-inputs their info!
+ @account = @client.get_account(current_user)
+ else
+ @account = @client.find_or_create_account(current_user, billing_info)
+ end
+
+ render :json=>account_json(@account)
+ rescue RecurlyClientError => x
+ render json: { :message => x.inspect, errors: x.errors }, :status => 404
+ end
end
def delete_account
@@ -21,13 +76,21 @@ class ApiRecurlyController < ApiController
# get Recurly account
def get_account
- @account=@client.get_account(current_user)
+ @account = @client.get_account(current_user)
render :json=>account_json(@account)
rescue RecurlyClientError => e
render json: { message: x.inspect, errors: x.errors}, :status => 404
end
+ # get Recurly payment history
+ def payment_history
+ @payments=@client.payment_history(current_user)
+ render :json=>{payments: @payments}
+ rescue RecurlyClientError => x
+ render json: { message: x.inspect, errors: x.errors}, :status => 404
+ end
+
# update Recurly account
def update_account
@account=@client.update_account(current_user, params[:billing_info])
@@ -39,9 +102,11 @@ class ApiRecurlyController < ApiController
# get Billing Information
def billing_info
@account = @client.get_account(current_user)
- # @billing = @account.billing_info
- # @billing ||= @account
- render :json=> account_json(@account)
+ if @account
+ render :json=> account_json(@account)
+ else
+ render :json=> {}, :status => 404
+ end
rescue RecurlyClientError => x
render json: { message: x.inspect, errors: x.errors}, :status => 404
end
@@ -58,30 +123,18 @@ class ApiRecurlyController < ApiController
error=nil
response = {jam_tracks:[]}
- # 1st confirm that all specified JamTracks exist
- jam_tracks = []
+ current_user.shopping_carts.each do |shopping_cart|
+ jam_track = shopping_cart.cart_product
- params[:jam_tracks].each do |jam_track_id|
- jam_track = JamTrack.where("id=?", jam_track_id).first
- if jam_track
- jam_tracks << jam_track
- else
- error="JamTrack not found for '#{jam_track_id}'"
- break
- end
+ # if shopping_cart has any marked_for_redeem, then we zero out the price by passing in 'free'
+ # NOTE: shopping_carts have the idea of quantity, but you should only be able to buy at most one JamTrack. So anything > 0 is considered free for a JamTrack
+
+ jam_track_right = @client.place_order(current_user, jam_track, shopping_cart)
+ # build up the response object with JamTracks that were purchased.
+ # if this gets more complicated, we should switch to RABL
+ response[:jam_tracks] << {name: jam_track.name, id: jam_track.id, jam_track_right_id: jam_track_right.id, version: jam_track.version}
end
- # then buy each
- unless error
- jam_tracks.each do |jam_track|
- jam_track_right = @client.place_order(current_user, jam_track)
- # build up the response object with JamTracks that were purchased.
- # if this gets more complicated, we should switch to RABL
- response[:jam_tracks] << {name: jam_track.name, id: jam_track.id, jam_track_right_id: jam_track_right.id, version: jam_track.version}
- end
- end
-
-
if error
render json: { errors: {message:error}}, :status => 404
else
@@ -97,16 +150,21 @@ private
end
def account_json(account)
+
+ billing_info = account.billing_info.nil? ? nil : {
+ :first_name => account.billing_info.first_name,
+ :last_name => account.billing_info.last_name,
+ :address1 => account.billing_info.address1,
+ :address2 => account.billing_info.address2,
+ :city => account.billing_info.city,
+ :state => account.billing_info.state,
+ :zip => account.billing_info.zip,
+ :country => account.billing_info.country,
+ :last_four => account.billing_info.last_four
+ }
+
{
- :first_name => account.first_name,
- :last_name => account.last_name,
- :email => account.email,
- :address1 => account.billing_info ? account.billing_info.address1 : nil,
- :address2 => account.billing_info ? account.billing_info.address2 : nil,
- :city => account.billing_info ? account.billing_info.city : nil,
- :state => account.billing_info ? account.billing_info.state : nil,
- :zip => account.billing_info ? account.billing_info.zip : nil,
- :country => account.billing_info ? account.billing_info.country : nil
+ billing_info: billing_info
}
end
diff --git a/web/app/controllers/api_shopping_carts_controller.rb b/web/app/controllers/api_shopping_carts_controller.rb
index 5cff85b97..761c3dc81 100644
--- a/web/app/controllers/api_shopping_carts_controller.rb
+++ b/web/app/controllers/api_shopping_carts_controller.rb
@@ -1,11 +1,11 @@
class ApiShoppingCartsController < ApiController
- before_filter :api_signed_in_user
+ before_filter :api_any_user
respond_to :json
def index
- @carts = current_user.shopping_carts
+ @carts = any_user.shopping_carts
end
def add_jamtrack
@@ -16,7 +16,7 @@ class ApiShoppingCartsController < ApiController
raise StateError, "Invalid JamTrack."
end
- @cart = ShoppingCart.create current_user, jam_track
+ @cart = ShoppingCart.add_jam_track_to_cart(any_user, jam_track)
if @cart.errors.any?
response.status = :unprocessable_entity
@@ -43,15 +43,16 @@ class ApiShoppingCartsController < ApiController
end
def remove_cart
- @cart = current_user.shopping_carts.find_by_id(params[:id])
+ @cart = any_user.shopping_carts.find_by_id(params[:id])
raise StateError, "Invalid Cart." if @cart.nil?
- @cart.destroy
+ ShoppingCart.remove_jam_track_from_cart(any_user, @cart)
+
respond_with responder: ApiResponder, :status => 204
end
def clear_all
- ShoppingCart.where("user_id=?", current_user).destroy_all
+ any_user.destroy_all_shopping_carts
render :json=>{}, :status=>200
end
diff --git a/web/app/controllers/api_users_controller.rb b/web/app/controllers/api_users_controller.rb
index 244dbc6af..a6ebb81c3 100644
--- a/web/app/controllers/api_users_controller.rb
+++ b/web/app/controllers/api_users_controller.rb
@@ -26,7 +26,7 @@ class ApiUsersController < ApiController
@user = User.includes([{musician_instruments: :instrument},
{band_musicians: :user},
{genre_players: :genre},
- :bands, :instruments, :genres])
+ :bands, :instruments, :genres, :jam_track_rights])
.find(params[:id])
respond_with @user, responder: ApiResponder, :status => 200
diff --git a/web/app/controllers/application_controller.rb b/web/app/controllers/application_controller.rb
index 29e294ac5..4a2ae2397 100644
--- a/web/app/controllers/application_controller.rb
+++ b/web/app/controllers/application_controller.rb
@@ -12,6 +12,8 @@ class ApplicationController < ActionController::Base
gon_setup
end
+ before_filter :set_tracking_cookie
+
before_filter do
if params[AffiliatePartner::PARAM_REFERRAL].present? && current_user.nil?
if cookies[AffiliatePartner::PARAM_COOKIE].blank?
@@ -25,6 +27,12 @@ class ApplicationController < ActionController::Base
cookies[AffiliatePartner::PARAM_COOKIE]
end
+
+ # http://stackoverflow.com/questions/15807214/where-to-set-a-tracking-permanent-cookie-in-rails
+ def set_tracking_cookie
+ cookies.permanent[:user_uuid] = SecureRandom.uuid unless cookies[:user_uuid]
+ end
+
private
def add_user_info_to_bugsnag(notif)
# Add some app-specific data which will be displayed on a custom
diff --git a/web/app/controllers/users_controller.rb b/web/app/controllers/users_controller.rb
index 61c42fe28..bcac17d3e 100644
--- a/web/app/controllers/users_controller.rb
+++ b/web/app/controllers/users_controller.rb
@@ -37,6 +37,7 @@ class UsersController < ApplicationController
end
def new
+ @no_user_dropdown = true
if current_user
redirect_to client_url
return
@@ -175,14 +176,17 @@ class UsersController < ApplicationController
end
def congratulations_fan
+ @no_user_dropdown = true
render :layout => "web"
end
def congratulations_musician
+ @no_user_dropdown = true
render :layout => "web"
end
def downloads
+ @no_user_dropdown = true
render :layout => "web"
end
diff --git a/web/app/helpers/sessions_helper.rb b/web/app/helpers/sessions_helper.rb
index 3545c3d05..5232fc24c 100644
--- a/web/app/helpers/sessions_helper.rb
+++ b/web/app/helpers/sessions_helper.rb
@@ -26,6 +26,10 @@ module SessionsHelper
!current_user.nil?
end
+ def has_anonymous_user?
+ !anonymous_user.nil?
+ end
+
def current_user=(user)
@current_user = user
end
@@ -34,6 +38,27 @@ module SessionsHelper
@current_user ||= User.find_by_remember_token(cookies[:remember_token])
end
+ def anonymous_user=(anonymous_user)
+ @anonymous_user = anonymous_user
+ end
+
+ def anonymous_user
+ if anon_cookie
+ @anonymous_user ||= AnonymousUser.new(anon_cookie)
+ else
+ nil
+ end
+ end
+
+ # tries current_user over anonymous_user
+ def any_user
+ current_user || anonymous_user
+ end
+
+ def anon_cookie
+ @anon_cookie ||= cookies[:user_uuid]
+ end
+
def current_user?(user)
user == current_user
end
@@ -45,13 +70,19 @@ module SessionsHelper
end
end
-
def api_signed_in_user
unless signed_in?
render :json => { :message => "not logged in"}, :status => 403
end
end
+ # take either the signed in user, or if that fails, try the anonymous user
+ def api_any_user
+ unless signed_in? || has_anonymous_user?
+ render :json => { :message => "not logged in"}, :status => 403
+ end
+ end
+
def sign_out
current_user = nil
cookies.delete(:remember_token, domain: Rails.application.config.session_cookie_domain)
@@ -92,6 +123,11 @@ module SessionsHelper
end
end
+ def logged_in_not_logged_in_class
+ signed_in? ? "logged-in" : "not-logged-in"
+ end
+
+
def metronome_tempos
[
40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 63, 66, 69, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 126, 132, 138, 144, 152, 160, 168, 176, 184, 192, 200, 208
diff --git a/web/app/views/api_claimed_recordings/show.rabl b/web/app/views/api_claimed_recordings/show.rabl
index 91174f87b..c22a49ad3 100644
--- a/web/app/views/api_claimed_recordings/show.rabl
+++ b/web/app/views/api_claimed_recordings/show.rabl
@@ -22,6 +22,10 @@ end
child(:recording => :recording) {
attributes :id, :created_at, :duration, :comment_count, :like_count, :play_count, :jam_track_id, :jam_track_initiator_id
+ node :timeline do |recording|
+ recording.timeline ? JSON.parse(recording.timeline) : {}
+ end
+
child(:jam_track => :jam_track) {
attributes :id
diff --git a/web/app/views/api_jam_tracks/show.rabl b/web/app/views/api_jam_tracks/show.rabl
index 184f44c89..e4226b553 100644
--- a/web/app/views/api_jam_tracks/show.rabl
+++ b/web/app/views/api_jam_tracks/show.rabl
@@ -7,7 +7,11 @@ node :genres do |item|
end
node :added_cart do |item|
- current_user.shopping_carts.where("cart_id='?'",item.id).count != 0
+ any_user.shopping_carts.where(cart_id: item.id).count != 0
+end
+
+node :purchased do |item|
+ !!item.right_for_user(current_user)
end
child(:jam_track_tracks => :tracks) {
diff --git a/web/app/views/api_jam_tracks/show_for_client.rabl b/web/app/views/api_jam_tracks/show_for_client.rabl
index faf84c144..bc212f46d 100644
--- a/web/app/views/api_jam_tracks/show_for_client.rabl
+++ b/web/app/views/api_jam_tracks/show_for_client.rabl
@@ -1,6 +1,10 @@
object @jam_track
-attributes :id, :name, :description, :initial_play_silence, :original_artist, :version
+attributes :id, :name, :description, :initial_play_silence, :original_artist, :version, :genre
+
+node :genre do |jam_track|
+ jam_track.genre.present? ? jam_track.genre.id : nil
+end
node :jmep do |jam_track|
jam_track.jmep_json ? JSON.parse(jam_track.jmep_json) : nil
diff --git a/web/app/views/api_music_sessions/show.rabl b/web/app/views/api_music_sessions/show.rabl
index 8149352d0..093e47077 100644
--- a/web/app/views/api_music_sessions/show.rabl
+++ b/web/app/views/api_music_sessions/show.rabl
@@ -79,10 +79,6 @@ else
child(:jam_track_tracks => :tracks) {
attributes :id, :part, :instrument, :track_type
}
-
- child(:jam_track_tap_ins => :tap_ins) {
- attributes :offset_time, :bpm, :tap_in_count
- }
}
# only show currently playing recording data if the current_user is in the session
@@ -107,6 +103,10 @@ else
end
}
+ child({:jam_track => :jam_track}) {
+ attributes :id, :name, :description
+ }
+
child(:recorded_tracks => :recorded_tracks) {
attributes :id, :fully_uploaded, :client_track_id, :client_id, :instrument_id
@@ -126,6 +126,19 @@ else
attributes :id, :first_name, :last_name, :city, :state, :country, :photo_url
}
}
+
+ child(:recorded_jam_track_tracks => :recorded_jam_track_tracks) {
+ node do |recorded_jam_track_track|
+ {
+ id: recorded_jam_track_track.jam_track_track.id,
+ user_id: recorded_jam_track_track.user_id,
+ part: recorded_jam_track_track.jam_track_track.part,
+ instrument: recorded_jam_track_track.jam_track_track.instrument,
+ track_type: recorded_jam_track_track.jam_track_track.track_type,
+ timeline: recorded_jam_track_track.timeline ? JSON.parse(recorded_jam_track_track.timeline) : []
+ }
+ end
+ }
}
}
diff --git a/web/app/views/api_recordings/show.rabl b/web/app/views/api_recordings/show.rabl
index a717a6bfb..330f12f7b 100644
--- a/web/app/views/api_recordings/show.rabl
+++ b/web/app/views/api_recordings/show.rabl
@@ -12,6 +12,10 @@ node :mix do |recording|
end
end
+node :timeline do |recording|
+ recording.timeline ? JSON.parse(recording.timeline) : {}
+end
+
child(:jam_track => :jam_track) {
attributes :id
diff --git a/web/app/views/api_users/show.rabl b/web/app/views/api_users/show.rabl
index 03e38f321..26dc5df11 100644
--- a/web/app/views/api_users/show.rabl
+++ b/web/app/views/api_users/show.rabl
@@ -1,6 +1,6 @@
object @user
-attributes :id, :first_name, :last_name, :name, :city, :state, :country, :location, :online, :photo_url, :musician, :gender, :birth_date, :internet_service_provider, :friend_count, :liker_count, :like_count, :follower_count, :following_count, :recording_count, :session_count, :biography, :favorite_count, :audio_latency, :upcoming_session_count, :age, :website, :skill_level
+attributes :id, :first_name, :last_name, :name, :city, :state, :country, :location, :online, :photo_url, :musician, :gender, :birth_date, :internet_service_provider, :friend_count, :liker_count, :like_count, :follower_count, :following_count, :recording_count, :session_count, :biography, :favorite_count, :audio_latency, :upcoming_session_count, :age, :website, :skill_level, :reuse_card, :purchased_jamtracks_count
if @user.musician?
node :location do @user.location end
@@ -17,6 +17,10 @@ if @user == current_user
geoiplocation.info if geoiplocation
end
+ node :free_jamtrack do |user|
+ Rails.application.config.one_free_jamtrack_per_user && user.has_redeemable_jamtrack
+ end
+
node :mods do |user|
user.mods_json
end
diff --git a/web/app/views/clients/_account.html.erb b/web/app/views/clients/_account.html.erb
index d675679a5..a630ef918 100644
--- a/web/app/views/clients/_account.html.erb
+++ b/web/app/views/clients/_account.html.erb
@@ -94,18 +94,19 @@
-
subscriptions:
+ jamtracks:
-
-
N/A
-
This feature not yet implemented
+
+ {{data.licenseDetail}}
+
+
+
JamTracks License
@@ -117,14 +118,11 @@
diff --git a/web/app/views/clients/_account_jamtracks.html.slim b/web/app/views/clients/_account_jamtracks.html.slim
new file mode 100644
index 000000000..966523768
--- /dev/null
+++ b/web/app/views/clients/_account_jamtracks.html.slim
@@ -0,0 +1,39 @@
+/! Account jamtracks Dialog
+#account-jamtracks.screen.secondary layout='screen' layout-id='account/jamtracks'
+ .content-head
+ .content-icon
+ = image_tag "content/icon_account.png", :width => 27, :height => 20
+ h1 my account
+ = render "screen_navigation"
+
+ /! jamtracks scrolling area
+ .content-body
+ .content-body-scroller.account-content-scroller#account-jamtracks-content-scroller
+ .content-wrapper.account-jamtracks
+ .jamtracks-header
+ .left.jamtracks-caption
+ h2 my jamtracks:
+ .clearall
+
+ #account-my-jamtracks
+ table.generaltable
+ thead
+ th TITLE
+ th ORIGINAL ARTIST
+ th ACTIONS
+ tbody
+ .right
+ a.button-grey href="javascript:history.go(-1)" BACK
+
+script#template-account-jamtrack type='text/template'
+ tbody
+ = "{% _.each(data.jamtracks, function(jamtrack) { %}"
+ tr data-id="{{jamtrack.id}}" data-genre="{{jamtrack.genre}}"
+ td
+ | {{jamtrack.name}}
+ td
+ | {{jamtrack.original_artist}}
+ td
+ .table-link: a.jamtrack-solo-session href= '#' jamtrack-id="{{jamtrack.id}}" Get into solo session
+ .table-link: a.jamtrack-group-session href= '#' jamtrack-id="{{jamtrack.id}}" Get into session others can join
+ = "{% }); %}"
diff --git a/web/app/views/clients/_checkout_order.html.slim b/web/app/views/clients/_checkout_order.html.slim
new file mode 100644
index 000000000..76db4d7ff
--- /dev/null
+++ b/web/app/views/clients/_checkout_order.html.slim
@@ -0,0 +1,157 @@
+div layout="screen" layout-id="checkoutOrder" id="checkoutOrderScreen" class="screen secondary"
+ .content
+ .content-head
+ .content-icon= image_tag("content/icon_shopping_cart.png", {:height => 19, :width => 19})
+ h1 check out
+ = render "screen_navigation"
+ .content-body
+ .content-body-scroller
+ .content-wrapper
+ .checkout-navigation-bar
+ .order-panel
+ .payment-wrapper
+ p.order-prompt.hidden
+ | Please review your order, and if everything looks correct, click the PLACE YOUR ORDER button. Thank you!
+ p.empty-cart-prompt.hidden
+ | You have nothing in your cart. You can go browse for JamTracks
+ a href="/client#/jamtrack" here
+ | .
+ p.no-account-info-prompt.hidden
+ | You have no billing info. Please go back to the
+ a href="/client#/checkoutPayment" Payment
+ | page to enter your billing info.
+ .order-content
+
+ #order_error.error.hidden
+ .clearall
+ .action-bar
+
+ .right
+ a.button-grey href="#" id="checkout-info-help" HELP
+ a.button-grey.back href="#" BACK
+ a.button-orange.place-order href="#" PLACE YOUR ORDER
+ .clearall
+ .thanks-panel
+ h2 Thank you for your order!
+ br
+ .thanks-detail We'll send you an email confirming your order shortly.
+ br
+ .thanks-detail.jam-tracks-in-browser.hidden
+ | To play your purchased JamTrack, launch the JamKazam application and open the JamTrack while in a session.
+ .thanks-detail.purchased-jam-track.hidden
+ h2.purchased-jam-track-header Downloading Your Purchased JamTracks
+ span Each JamTrack will be downloaded sequentially.
+ br
+ span.notice Note that you do not have to wait for this to complete in order to use your JamTrack later.
+ br.clear
+ ul.purchased-list
+
+
+
+script type='text/template' id='template-order-content'
+ .order-left-page
+ .payment-info-page
+ h2 ADDRESS & PAYMENT
+ .address-info
+ .billing-address
+ .billing-caption
+ | BILLING ADDRESS:
+ a.change-payment-info href="#" change
+ .clearall
+ .billing-info-item= "{{data.billing_info.address1}}"
+ .billing-info-item= "{{data.billing_info.address2}}"
+ .billing-info-item
+ | {{data.billing_info.city}}, {{data.billing_info.state}} {{data.billing_info.zip}}
+ span.country= "{{data.billing_info.country}}"
+ .shipping-address
+ .billing-caption
+ | SHIPPING ADDRESS:
+ a.change-payment-info href="#" change
+ .clearall
+ = "{% if (data.shipping_as_billing) { %}"
+ .billing-info-item Same as billing address
+ = "{% } else { %}"
+ .billing-info-item= "{{data.shipping_info.address1}}"
+ .billing-info-item= "{{data.shipping_info.address2}}"
+ .billing-info-item
+ | {{data.shipping_info.city}}, {{data.shipping_info.state}} {{data.shipping_info.zip}}
+ span.country= "{{data.shipping_info.country}}"
+ = "{% } %}"
+ br
+ .payment-method-info
+ .billing-caption
+ | PAYMENT METHOD:
+ a.change-payment-info href="#" change
+ .clearall
+
+ /= image_tag ''
+ ="Credit card ending {{data.billing_info.last_four}}"
+
+ .clearall
+ .order-items-page
+ h2 ORDER DETAILS
+ .cart-items
+ .cart-item-caption
+ span YOUR ORDER INCLUDES:
+ .cart-item-price
+ span PRICE
+ .cart-item-quantity
+ span QUANTITY
+ .clearall
+ = "{% if (data.carts.length == 0) { %}"
+ .no-cart-items You have no orders now.
+ = "{% } %}"
+ = "{% _.each(data.carts, function(cart) { %}"
+ .cart-item cart-id="{{cart.id}}"
+ .cart-item-caption
+ = "{{cart.cart_type}}: {{cart.product_info.name}}"
+ .cart-item-price
+ = "$ {{Number(cart.product_info.total_price).toFixed(2)}}"
+ .cart-item-quantity
+ = "{{cart.quantity}}"
+ .clearall
+ = "{% }); %}"
+ .clearall
+ .order-right-page
+ h2 PLACE ORDER
+ .recurly-data.hidden
+ = "{% _.each(data.carts, function(cart) { %}"
+ .plan data-plan-code="{{cart.product_info.plan_code}}"
+ input data-recurly="plan" type="text" value="{{cart.product_info.plan_code}}"
+ = "{% }); %}"
+ .order-summary
+ .place-order-center
+ a.button-orange.place-order href="#" PLACE YOUR ORDER
+ .clearall
+
+ .billing-caption ORDER SUMMARY:
+ .order-items-header.order-total Order items:
+ .order-items-value.order-total= "{{data.sub_total}}"
+ .clearall
+ .order-items-header.shipping-handling Shipping & handling:
+ .order-items-value.shipping-handling= "{{data.shipping_handling}}"
+ .clearall
+ .line
+ .order-items-header.sub-total Total before tax:
+ .order-items-value.sub-total= "{{data.sub_total}}"
+ .clearall
+ .order-items-header.taxes Taxes:
+ .order-items-value.taxes= "{{data.taxes}}"
+ .clearall
+ .line
+ .order-items-header.grand-total Order total:
+ .order-items-value.grand-total= "{{data.grand_total}}"
+ .clearall
+ .order-help
+ span By placing your order, you agree to JamKazam's
+ '
+ a href="http://www.jamkazam.com/corp/terms" rel="external" terms of service
+ '
+ span and
+ '
+ a href="http://www.jamkazam.com/corp/returns" rel="external" returns policy
+ span .
+ .clearall
+
+script type='text/template' id='template-purchased-jam-track'
+ li data-jam-track-id="{{data.jam_track_id}}"
\ No newline at end of file
diff --git a/web/app/views/clients/_checkout_payment.html.slim b/web/app/views/clients/_checkout_payment.html.slim
new file mode 100644
index 000000000..e937ef434
--- /dev/null
+++ b/web/app/views/clients/_checkout_payment.html.slim
@@ -0,0 +1,214 @@
+div layout="screen" layout-id="checkoutPayment" id="checkoutPaymentScreen" class="screen secondary no-login-required"
+ .content
+ .content-head
+ .content-icon= image_tag("content/icon_shopping_cart.png", {:height => 19, :width => 19})
+ h1 check out
+ = render "screen_navigation"
+ .content-body
+ .content-body-scroller
+ .content-wrapper
+ .checkout-navigation-bar
+ .payment-wrapper
+ p.payment-prompt.free-jamtrack.hidden
+ | Please enter your billing address and payment information below. You will not be billed for your first JamTrack, which is 100% free.
+ | But we need this data to prevent fraud/abuse of those who would create multiple accounts to collect multiple free JamTracks.
+ | You will not be billed for any charges of any kind without your explicit authorization.
+ | There are no "hidden" charges or fees, thank you!
+ p.payment-prompt.no-free-jamtrack.hidden
+ | Please enter your billing address and payment information below.
+
+ form class="payment-info" id="checkout-payment-info"
+ .row.first
+ .left-side
+ .billing-address
+ h2.billing-caption BILLING ADDRESS
+ #divBillingFirstName.field
+ .billing-label
+ label for="billing-first-name" First Name: *
+ .billing-value
+ input type="text" id="billing-first-name"
+ .clearall
+ #divBillingLastName.field
+ .billing-label
+ label for="billing-last-name" Last Name: *
+ .billing-value
+ input type="text" id="billing-last-name"
+ .clearall
+ #divBillingAddress1.field
+ .billing-label
+ label for="billing-address1" Address 1: *
+ .billing-value
+ input type="text" id="billing-address1"
+ .clearall
+ #divBillingAddress2.field
+ .billing-label
+ label for="billing-address2" Address 2:
+ .billing-value
+ input type="text" id="billing-address2"
+ .clearall
+ #divBillingCity.field
+ .billing-label
+ label for="billing-city" City: *
+ .billing-value
+ input type="text" id="billing-city"
+ .clearall
+ #divBillingState.field
+ .billing-label
+ label for="billing-state" State/Region: *
+ .billing-value
+ input type="text" id="billing-state"
+ .clearall
+ #divBillingZip.field
+ .billing-label
+ label for="billing-zip" Zip: *
+ .billing-value
+ input type="text" id="billing-zip"
+ .clearall
+ #divBillingCountry.field
+ .billing-label
+ label for="billing-country" Country: *
+ .billing-value
+ select id="billing-country"
+ option value="US" US
+ .clearall
+ .right-side
+ .payment-method
+ h2.payment-method-caption PAYMENT METHOD
+ .new-card-info
+ #divCardName.field.hidden
+ .card-label
+ label for="card-name" Name of Card: *
+ .card-value
+ input type="text" id="card-name"
+ .clearall
+ #divCardNumber.field
+ .card-label
+ label for="card-number" Card Number: *
+ .card-value
+ input type="text" id="card-number"
+ .clearall
+ #divCardExpiry.field
+ .card-label Expiration Date: *
+ .card-value
+ =date_select("card", "expire-date", use_two_digit_numbers: true, discard_day: true, :start_year => Time.now.year, :end_year => Time.now.year + 18, :order => [:month, :day, :year], :default => -25.years.from_now, :html => {:class => "account-profile-birthdate", :id => "card-expiry"})
+ .clearall
+ #divCardVerify.field
+ .card-label
+ label for="card-verify"
+ | CVV Code: *
+ .hint.cvv
+ | (back of card)
+ .card-value
+ input type="text" id="card-verify"
+ .clearall
+ .reuse-existing-card
+ .card-label
+ .card-value
+ .reuse-existing-card-checkbox.ichecbuttons
+ input type="checkbox" id="reuse-existing-card" name="reuse-existing-card" checked="checked"
+ .reuse-existing-card-helper
+ label for="reuse-existing-card"
+ | Use card ending in
+ span.existing-card-ends-with
+ .clearall
+ .card-label
+ .card-value
+ .save-card-checkbox.ichecbuttons
+ input type="checkbox" id="save-card" name="save-card" checked="checked"
+ .divSaveCardHelper
+ label for="save-card" Save card for future use
+ .clearall
+ .clearall
+ .clearall
+ .row.second
+ left-side.hidden
+ .shipping-address
+ h2.shipping-address-label SHIPPING ADDRESS
+ .shipping-as-billing.ichecbuttons
+ input type="checkbox" id="shipping-as-billing" name="shipping-as-billing" checked="checked"
+ .divBillingHelper
+ label for="shipping-as-billing" Same as billing address
+ .clearall
+ .shipping-address-detail.hidden
+ #divShippingFirstName.field
+ .shipping-label
+ label for="shipping-first-name" First Name:
+ .shipping-value
+ input type="text" id="shipping-first-name"
+ .clearall
+ #divShippingLastName.field
+ .shipping-label
+ label for="shipping-last-name" Last Name:
+ .shipping-value
+ input type="text" id="shipping-last-name"
+ .clearall
+ #divShippingAddress1.field
+ .shipping-label
+ label for="shipping-address1" Address 1:
+ .shipping-value
+ input type="text" id="shipping-address1"
+ .clearall
+ #divShippingAddress2.field
+ .shipping-label
+ label for="shipping-address2" Address 2:
+ .shipping-value
+ input type="text" id="shipping-address2"
+ .clearall
+ #divShippingCity.field
+ .shipping-label
+ label for="shipping-city" City:
+ .shipping-value
+ input type="text" id="shipping-city"
+ .clearall
+ #divShippingState.field
+ .shipping-label
+ label for="shipping-state" State/Region:
+ .shipping-value
+ input type="text" id="shipping-state"
+ .clearall
+ #divShippingZip.field
+ .shipping-label
+ label for="shipping-zip" Zip:
+ .shipping-value
+ input type="text" id="shipping-zip"
+ .clearall
+ #divShippingCountry.field
+ .shipping-label
+ label for="shipping-country" Country:
+ .shipping-value
+ input type="text" id="shipping-country"
+ .clearall
+ .right-side
+ .jamkazam-account-signup
+ h2.jamkazam-account-caption JAMKAZAM ACCOUNT
+ #divJamKazamEmail.field
+ .account-label
+ label for="email" Email Address: *
+ .account-value
+ input name="email" id="checkout-signup-email" type="text"
+ .clearall
+ #divJamKazamPassword.field
+ .account-label
+ label for="password" Password: *
+ .account-value
+ input name="password" id="checkout-signup-password" type="password"
+ .clearall
+ #divJamKazamTos.field
+ .terms-of-service.ichecbuttons
+ input type="checkbox" name="terms-of-service"
+ .terms-of-service-label-holder
+ label for="terms-of-service"
+ | I have read and agree to the JamKazam
+ a rel="external" href=corp_terms_path terms of service
+ .clearall
+ .clearall
+ .row.third.hidden#payment_error
+
+
+ .clearall
+ .action-bar
+
+ .right
+ a href="#" id="payment-info-help" class="button-grey" HELP
+ a href="#" id="payment-info-next" class="button-orange" NEXT
+ .clearall
diff --git a/web/app/views/clients/_checkout_signin.html.slim b/web/app/views/clients/_checkout_signin.html.slim
new file mode 100644
index 000000000..d6dd4ccdb
--- /dev/null
+++ b/web/app/views/clients/_checkout_signin.html.slim
@@ -0,0 +1,92 @@
+div layout="screen" layout-id="checkoutSignin" id="checkoutSignInScreen" class="screen secondary no-login-required"
+ .content
+ .content-head
+ .content-icon= image_tag("content/icon_shopping_cart.png", {:height => 19, :width => 19})
+ h1 check out
+ = render "screen_navigation"
+ .content-body
+ .content-body-scroller
+ .checkout-signin
+ .checkout-navigation-bar
+
+ .content-holder
+ .already-signed-in
+ h3 YOU ARE ALREADY LOGGED IN
+ p.carry-on-prompt
+ | You can move on to the next step of checkout.
+ .actions
+ a.btnNext.button-orange NEXT
+
+ .left-side
+ h3 ALREADY A MEMBER OF THE JAMKAZAM COMMUNITY?
+ .left-side-content
+ .signin-form
+ .signin-prompt Sign in using your email address:
+ .clearall
+ form.signin-form
+ .input-elements
+ .out
+ label.inline Email:
+ .email.field
+ input name='email' autofocus="true" type="text"
+ .out
+ label.inline Password:
+ .password.field
+ input name='password' autofocus="true" type="password"
+ .login-error-msg Invalid login
+
+
+ br clear='all'
+
+ .actions
+ small
+ a.forgot-password href='/request_reset_password' Forgot Password?
+ = link_to "SIGN IN", '#', class: 'button-orange signin-submit'
+
+
+ p.facebook-prompt Or sign in using Facebook:
+ = link_to image_tag("content/button_facebook_signin.png", {:width => 249, :height => 46}), '/auth/facebook', class: "signin-facebook"
+ .right-side
+ h3 NOT A MEMBER YET?
+
+ p.signup-later-prompt
+ | Thousands of musicians are now registered members of JamKazam. Click the NEXT button below to join us, and welcome!
+
+ .actions
+ a.btnNext.button-orange NEXT
+
+script type='text/template' id='template-checkout-navigation'
+ .checkout-navigation
+ .nav-signin
+ = "{% if (data.current == 1) { %}"
+ .nav-text.selected
+ .badge-number 1
+ | Sign In
+ = "{% } else { %}"
+ .nav-text
+ .badge-number.disabled 1
+ | Sign In
+ = "{% } %}"
+ .clearall
+ .nav-payment-info
+ = "{% if (data.current == 2) { %}"
+ .nav-text.selected
+ .badge-number 2
+ | Address & Payment
+ = "{% } else { %}"
+ .nav-text
+ .badge-number.disabled 2
+ | Address & Payment
+ = "{% } %}"
+ .clearall
+ .nav-place-order
+ = "{% if (data.current == 3) { %}"
+ .nav-text.selected
+ .badge-number 3
+ | Place Order
+ = "{% } else { %}"
+ .nav-text
+ .badge-number.disabled 3
+ | Place Order
+ = "{% } %}"
+ .clearall
diff --git a/web/app/views/clients/_flash.html.slim b/web/app/views/clients/_flash.html.slim
new file mode 100644
index 000000000..ec6b293ce
--- /dev/null
+++ b/web/app/views/clients/_flash.html.slim
@@ -0,0 +1,3 @@
+script type="text/template" id="template-flash-notice"
+ .flash-notice
+ .flash-content
\ No newline at end of file
diff --git a/web/app/views/clients/_header.html.erb b/web/app/views/clients/_header.html.erb
index 855f0e988..de9015e66 100644
--- a/web/app/views/clients/_header.html.erb
+++ b/web/app/views/clients/_header.html.erb
@@ -13,8 +13,8 @@
<% if Rails.application.config.jam_tracks_available %>
-
<% end %>
diff --git a/web/app/views/clients/_home.html.slim b/web/app/views/clients/_home.html.slim
index fb6347c84..f904459bb 100644
--- a/web/app/views/clients/_home.html.slim
+++ b/web/app/views/clients/_home.html.slim
@@ -1,6 +1,6 @@
-.screen layout="screen" layout-id="home"
+.screen.no-login-required layout="screen" layout-id="home"
/ Layout is different if jam_tracks tile available:
- -jamtracks=Rails.configuration.jam_tracks_available
+ -jamtracks=Rails.configuration.jam_tracks_available || (current_user && current_user.admin)
-if (jamtracks)
-small_tile_size="2.4"
-column_positions=["0.0,1", "2.4,1", "4.8,1", "7.2,1", "9.6,1"]
@@ -11,23 +11,23 @@
/ individual spells span those spaces
-if @nativeClient
.grid layout-grid="2x12"
- .homecard.createsession layout-grid-columns="4" layout-grid-position="0,0" layout-grid-rows="1" layout-link="createSession" type="createSession"
+ .homecard.createsession layout-grid-columns="4" layout-grid-position="0,0" layout-grid-rows="1" layout-link="createSession" type="createSession" class="#{logged_in_not_logged_in_class}"
h2 create session
.homebox-info
/! 4 friends online, 2 currently in sessions
- .homecard.findsession layout-grid-columns="4" layout-grid-position="4,0" layout-grid-rows="1" layout-link="findSession" type="findSession"
+ .homecard.findsession layout-grid-columns="4" layout-grid-position="4,0" layout-grid-rows="1" layout-link="findSession" type="findSession" class="#{logged_in_not_logged_in_class}"
h2 find session
.homebox-info
/! 1 session invitation, 19 public sessions active
- .homecard.feed layout-grid-columns="4" layout-grid-position="8,0" layout-grid-rows="1" layout-link="feed"
+ .homecard.feed layout-grid-columns="4" layout-grid-position="8,0" layout-grid-rows="1" layout-link="feed" class="#{logged_in_not_logged_in_class}"
h2 feed
.homebox-info
/! 4 friends online, 2 currently in sessions
- .homecard.musicians layout-grid-columns=small_tile_size layout-grid-position=column_positions[0] layout-grid-rows="1" layout-link="musicians"
+ .homecard.musicians layout-grid-columns=small_tile_size layout-grid-position=column_positions[0] layout-grid-rows="1" layout-link="musicians" class="#{logged_in_not_logged_in_class}"
h2 musicians
.homebox-info
/! 5 followers, 3 following
- .homecard.bands layout-grid-columns=small_tile_size layout-grid-position=column_positions[1] layout-grid-rows="1" layout-link="bands"
+ .homecard.bands layout-grid-columns=small_tile_size layout-grid-position=column_positions[1] layout-grid-rows="1" layout-link="bands" class="#{logged_in_not_logged_in_class}"
h2 bands
.homebox-info
/! 1 session invitation, 19 public sessions active
@@ -36,46 +36,46 @@
h2 jamtracks
.homebox-info
/! 5 followers, 3 following
- .homecard.profile layout-grid-columns=small_tile_size layout-grid-position=column_positions[3] layout-grid-rows="1"
+ .homecard.profile layout-grid-columns=small_tile_size layout-grid-position=column_positions[3] layout-grid-rows="1" class="#{logged_in_not_logged_in_class}"
h2 profile
.homebox-info
/! 5 followers, 3 following
- .homecard.account layout-grid-columns=small_tile_size layout-grid-position=column_positions[4] layout-grid-rows="1" layout-link="account"
+ .homecard.account layout-grid-columns=small_tile_size layout-grid-position=column_positions[4] layout-grid-rows="1" layout-link="account" class="#{logged_in_not_logged_in_class}"
h2 account
.homebox-info
/! free service level
-else
.grid layout-grid="2x12"
- .homecard.createsession layout-grid-columns="4" layout-grid-position="0,0" layout-grid-rows="1" layout-link="createSession" type="createSession"
+ .homecard.createsession layout-grid-columns="4" layout-grid-position="0,0" layout-grid-rows="1" layout-link="createSession" type="createSession" class="#{logged_in_not_logged_in_class}"
h2 create session
.homebox-info
/! 4 friends online, 2 currently in sessions
- .homecard.findsession layout-grid-columns="4" layout-grid-position="4,0" layout-grid-rows="1" layout-link="findSession" type="findSession"
+ .homecard.findsession layout-grid-columns="4" layout-grid-position="4,0" layout-grid-rows="1" layout-link="findSession" type="findSession" class="#{logged_in_not_logged_in_class}"
h2 find session
.homebox-info
/! 1 session invitation, 19 public sessions active
- .homecard.feed layout-grid-columns="4" layout-grid-position="8,0" layout-grid-rows="1" layout-link="feed"
+ .homecard.feed layout-grid-columns="4" layout-grid-position="8,0" layout-grid-rows="1" layout-link="feed" class="#{logged_in_not_logged_in_class}"
h2 feed
.homebox-info
/! 4 friends online, 2 currently in sessions
- .homecard.musicians layout-grid-columns=small_tile_size layout-grid-position=column_positions[0] layout-grid-rows="1" layout-link="musicians"
+ .homecard.musicians layout-grid-columns=small_tile_size layout-grid-position=column_positions[0] layout-grid-rows="1" layout-link="musicians" class="#{logged_in_not_logged_in_class}"
h2 musicians
.homebox-info
/! 5 followers, 3 following
- .homecard.bands layout-grid-columns=small_tile_size layout-grid-position=column_positions[1] layout-grid-rows="1" layout-link="bands"
+ .homecard.bands layout-grid-columns=small_tile_size layout-grid-position=column_positions[1] layout-grid-rows="1" layout-link="bands" class="#{logged_in_not_logged_in_class}"
h2 bands
.homebox-info
-if jamtracks
/! 1 session invitation, 19 public sessions active
- .homecard.jamtrack layout-grid-columns=small_tile_size layout-grid-position=column_positions[2] layout-grid-rows="1" layout-link="jamtrack"
+ .homecard.jamtrack layout-grid-columns=small_tile_size layout-grid-position=column_positions[2] layout-grid-rows="1" layout-link="jamtrackLanding"
h2 jamtracks
.homebox-info
/! 5 followers, 3 following
- .homecard.profile layout-grid-columns=small_tile_size layout-grid-position=column_positions[3] layout-grid-rows="1"
+ .homecard.profile layout-grid-columns=small_tile_size layout-grid-position=column_positions[3] layout-grid-rows="1" class="#{logged_in_not_logged_in_class}"
h2 profile
.homebox-info
/! 5 followers, 3 following
- .homecard.account layout-grid-columns=small_tile_size layout-grid-position=column_positions[4] layout-grid-rows="1" layout-link="account"
+ .homecard.account layout-grid-columns=small_tile_size layout-grid-position=column_positions[4] layout-grid-rows="1" layout-link="account" class="#{logged_in_not_logged_in_class}"
h2 account
.homebox-info
/! free service level
diff --git a/web/app/views/clients/_jamtrack.html.haml b/web/app/views/clients/_jamtrack.html.haml
deleted file mode 100644
index ba076ee19..000000000
--- a/web/app/views/clients/_jamtrack.html.haml
+++ /dev/null
@@ -1,83 +0,0 @@
-%div{ layout: 'screen', :'layout-id' => 'jamtrack', id: 'jamtrackScreen', :class => 'screen secondary'}
- .content
- .content-head
- .content-icon= image_tag("content/icon_jamtracks.png", {:height => 19, :width => 19})
- %h1 jamtracks
- %a{href: "#", class: "jamtrack_help"} What is a JamTrack?
- = render "screen_navigation"
- .content-body
- = form_tag('', {:id => 'jamtrack-find-form', :class => 'inner-content'}) do
- = render(:partial => "web_filter", :locals => {:search_type => Search::PARAM_JAMTRACK})
- .filter-body
- .content-body-scroller
- .profile-wrapper
- .jamtrack-content
- %a{href: "/api/jamtracks?page=1", class: "btn-next-pager"}= 'Next'
- %div{id: 'end-of-jamtrack-list', class: 'end-of-list'}= 'No more Jamtracks'
-
-%script{type: 'text/template', id: 'template-jamtrack'}
- .jamtrack-record{"jamtrack-id" => "{{data.jamtrack.id}}"}
- .jamtrack-detail
- .detail-label
- Name:
- .detail-value
- {{data.jamtrack.name}}
- .clearall.detail-label
- Type:
- .detail-value
- {{data.jamtrack.recording_type}}
- .clearall.detail-label
- Original Artist:
- .detail-value
- {{data.jamtrack.original_artist}}
- .clearall.detail-label
- Genre:
- .detail-value
- {{data.jamtrack.genres[0]}}
- .clearall.detail-label
- Writer/Composer:
- .detail-value
- {{[data.jamtrack.songwriter, data.jamtrack.publisher].join(", ")}}
- .clearall.detail-label
- Copyright:
- .copyright-value
- = "{% if (data.jamtrack.licensor != null) { %}"
- {{data.jamtrack.licensor.name}}
- ="{% }; %}"
- .detail-arrow
- = image_tag 'down_arrow.png', class: 'jamtrack-detail-btn'
- .clearall.jamtrack-description
- .detail-label
- Description
- .detail-value
- {{data.jamtrack.description}}
- .clearall
- .jamtrack-tracks
- .tracks-caption
- Tracks in This Recording:
- = "{% _.each(data.jamtrack.tracks, function(track) { %}"
-
- = "{% if(track.track_type == 'Master') return; %}"
- .track-instrument
- .instrument-image
- %img{src: "{{track.instrument_url}}", width: 24, height: 24}
- .instrument-desc
- {{track.instrument_desc}}
- .clearall
- = "{% }); %}"
- .jamtrack-action
- %a{href: "#", class: 'play-button', "data-jamtrack-id" => "{{data.jamtrack.id}}"}
- = image_tag 'shared/play_button.png'
- .jamtrack-price
- {{"$ " + data.jamtrack.price}}
- = "{% if (data.jamtrack.added_cart) { %}"
- %a.jamtrack-add-cart-disabled.button-grey.button-disabled{href: "javascript:void(0)"} Purchased
- = "{% } else { %}"
- %a.jamtrack-add-cart.button-orange{href: "#", "data-jamtrack-id" => "{{data.jamtrack.id}}"} Add to Cart
- = "{% }; %}"
- = "{% if (data.jamtrack.sales_region == 'United States') { %}"
- .jamtrack-license
- This JamTrack available only to US customers.
- %a{href: "#", class: 'license-us-why'} why?
- = "{% }; %}"
- .clearall
\ No newline at end of file
diff --git a/web/app/views/clients/_jamtrack.html.slim b/web/app/views/clients/_jamtrack.html.slim
new file mode 100644
index 000000000..79e2cf17e
--- /dev/null
+++ b/web/app/views/clients/_jamtrack.html.slim
@@ -0,0 +1,114 @@
+#jamtrackScreen.screen.secondary.no-login-required layout='screen' layout-id='jamtrack'
+ .content
+ .content-head
+ .content-icon=image_tag("content/icon_jamtracks.png", height:19, width:19 )
+ h1 jamtracks
+ =render "screen_navigation"
+ .content-body
+ =form_tag('', {:id => 'jamtrack-find-form', :class => 'inner-content'}) do
+ =render(:partial => "web_filter", :locals => {:search_type => Search::PARAM_JAMTRACK})
+ .filter-body
+ .content-body-scroller
+ .profile-wrapper
+ .jamtrack-content
+ a.btn-next-pager href="/api/jamtracks?page=1" Next
+ .end-of-jamtrack-list.end-of-list="No more Jamtracks"
+
+script#template-jamtrack type='text/template'
+ .jamtrack-record jamtrack-id="{{data.jamtrack.id}}"
+ .top_bar
+ .jamtrack-detail.jamtrack-header JAMTRACK
+ .jamtrack-tracks.jamtrack-header TRACKS INCLUDED/PREVIEW
+ .jamtrack-action.jamtrack-header SHOP
+ .jamtrack-detail
+ .detail-label
+ | Title:
+ .detail-value
+ | {{data.jamtrack.name}}
+ / .clearall.detail-label
+ / | Type:
+ / .detail-value
+ / | {{data.jamtrack.recording_type}}
+ / .clearall.detail-label
+ / | Original Artist:
+ / .detail-value
+ / | {{data.jamtrack.original_artist}}
+ .clearall.detail-label
+ | Original Artist:
+ .detail-value
+ | {{data.jamtrack.original_artist}}
+ .clearall.detail-label
+ | Genre:
+ .detail-value
+ | {{data.jamtrack.genres[0]}}
+ ="{% if (data.expanded) { %}"
+ .clearall.detail-label
+ | Writer(s):
+ .detail-value
+ | {{data.jamtrack.songwriter}}
+ .clearall.detail-label
+ | Publisher:
+ .detail-value
+ | {{data.jamtrack.publisher}}
+ .clearall.detail-label
+ | Description:
+ .detail-value
+ | {{data.jamtrack.description}}
+ ="{% } %}"
+
+
+ / / / .clearall.detail-label
+ / | Copyright:
+ / .copyright-value
+ / ="{% if (data.jamtrack.licensor !=null) { %}"
+ / | {{data.jamtrack.licensor.name}}
+ / ="{% }; %}"
+ / .clearall.jamtrack-description
+ / .detail-label
+ / | Description
+ / .detail-value
+ / | {{data.jamtrack.description}}
+ / .clearall
+ .jamtrack-tracks
+ / .tracks-caption
+ / | Tracks in This Recording:
+ ="{% counter = 0 %}"
+ ="{% _.each(data.jamtrack.tracks, function(track) { %}"
+ ="{% if(track.track_type == JK.MASTER_TRACK) return; %}"
+ .track-instrument href="{{track.url_44}}"
+ .instrument-image
+ img src="/assets/shared/play_button.png" width=24 height=24
+ .instrument-image
+ img src="{{track.instrument_url}}" width=24 height=24
+ .instrument-desc
+ | {{track.instrument_desc}}
+ .clearall
+ ="{% }); %}"
+ .detail-arrow
+ .jamtrack-detail-btn
+ ="{% if (data.expanded) { %}"
+ | hide tracks
+ =image_tag("up_arrow.png")
+ ="{% } else { %}"
+ | preview all tracks
+ =image_tag("down_arrow.png")
+ ="{% } %}"
+
+ .jamtrack-action
+ / a.play-button href="#" data-jamtrack-id="{{data.jamtrack.id}}"
+ / =image_tag "shared/play_button.png"
+ .jamtrack-price
+ | {{"$ " + data.jamtrack.price}}
+ ="{% if (data.jamtrack.purchased) { %}"
+ a.jamtrack-add-cart-disabled.button-grey.button-disabled href="javascript:void(0)" Purchased
+ ="{% } else if (data.jamtrack.added_cart) { %}"
+ a.jamtrack-add-cart-disabled.button-grey.button-disabled href="client#/shoppingCart" Already In Cart
+ ="{% } else { %}"
+ a.jamtrack-add-cart.button-orange href="#" data-jamtrack-id="{{data.jamtrack.id}}" Add to Cart
+ ="{% }; %}"
+ ="{% if (data.jamtrack.sales_region==JK.AVAILABILITY_US) { %}"
+ .jamtrack-license
+ | This JamTrack available only to US customers.
+ a.license-us-why href="#", why?
+ ="{% }; %}"
+ .clearall
diff --git a/web/app/views/clients/_jamtrack_landing.html.slim b/web/app/views/clients/_jamtrack_landing.html.slim
new file mode 100644
index 000000000..853ee05fe
--- /dev/null
+++ b/web/app/views/clients/_jamtrack_landing.html.slim
@@ -0,0 +1,35 @@
+#jamtrackLanding.screen.secondary layout='screen' layout-id='jamtrackLanding'
+ .content
+ .content-head
+ .content-icon=image_tag("content/icon_jamtracks.png", height:19, width:19)
+ h1 jamtracks
+ = render "screen_navigation"
+ .content-body
+ .list-columns
+ .about
+ h2 what are jamtracks?
+ p.what
+ .details JamTracks are the best way to play along with your favorite music! Unlike traditional backing tracks, JamTracks are professionally mastered, complete multitrack recordings, with fully isolated tracks for each and every part of the master mix. Used with the free JamKazam app & Internet service, you can:
+ ul
+ li Solo just the part you want to play in order to hear and learn it
+ li Mute just the part you want to play and play along with the rest
+ li Make audio recordings and share them via Facebook or URL
+ li Make video recordings and share them via YouTube
+ li And even go online to play with others in real time -- for example, you can play the electric guitar lead, while someone else plays the bass, and all other parts play from the recorded tracks in your session
+ / TODO: put in video thumbnail when available:
+ .browse
+ h2 my jamtracks
+ p.howto
+ .details
+ span="To play with your JamTracks, open a JamTrack while in a session in the JamKazam app. Or "
+ a href="client#/jamtrack" visit the JamTracks Section of your account.
+ .free-jamtrack.orange-fill
+ | For a limited time, get one JamTrack free. Browse JamTracks below, add one to your shopping cart, and we'll make it free during the checkout process.
+ h2 browse jamtracks
+ .browse-header
+ span="browse by band "
+ a href="client#/jamtrack" or browse all jamtracks
+ .band-browse
+ ul#band_list
+ li#no_bands_found.hidden No bands found
+
diff --git a/web/app/views/clients/_order.html.slim b/web/app/views/clients/_order.html.slim
index ffab91d7a..f5ea0cc91 100644
--- a/web/app/views/clients/_order.html.slim
+++ b/web/app/views/clients/_order.html.slim
@@ -71,19 +71,20 @@ div layout="screen" layout-id="order" id="orderScreen" class="screen secondary"
input type="text" id="card-name"
.clearall
#divCardNumber
- .card-label
+ .card-label.mt10
label for="card-number" Card Number:
- .card-value
+ .card-value.mt10
input type="text" id="card-number"
.clearall
- .card-label Expiration Date:
- .card-value
- = date_select("card", "expire-date", use_two_digit_numbers: true, discard_day: true, :start_year => Time.now.year, :end_year => Time.now.year + 18, :order => [:month, :day, :year], :default => -25.years.from_now, :html=>{:class => "account-profile-birthdate"} )
- .clearall
+ #divCardExpiry
+ .card-label.mt10 Expiration Date:
+ .card-value.mt10
+ =date_select("card", "expire-date", use_two_digit_numbers: true, discard_day: true, :start_year => Time.now.year, :end_year => Time.now.year + 18, :order => [:month, :day, :year], :default => -25.years.from_now, :html=>{:class => "account-profile-birthdate", :id=>"card-expiry"} )
+ .clearall
#divCardVerify
- .card-label
+ .card-label.mt10
label for="card-verify" Verification Value:
- .card-value
+ .card-value.mt10
input type="text" id="card-verify"
.clearall
.card-label.mt15
diff --git a/web/app/views/clients/_session.html.slim b/web/app/views/clients/_session.html.slim
index eb7541100..ae422b6b7 100644
--- a/web/app/views/clients/_session.html.slim
+++ b/web/app/views/clients/_session.html.slim
@@ -46,61 +46,63 @@
| CHAT
.voicechat-gain
.voicechat-mute.enabled[control="mute" mixer-id=""]
- .session-livetracks
- h2
- | live tracks
- .session-add[layout-link="select-invites"]
- a#session-invite-musicians[href="#"]
- = image_tag "content/icon_add.png", {:width => 19, :height => 19, :align => "texttop"}
- | Â Â Invite Musicians
- .session-tracks-scroller
- #session-livetracks-container
- .when-empty.livetracks
- | No other musicians
- br
- | are in your session
- br[clear="all"]
- #recording-start-stop.recording
- a
- = image_tag "content/recordbutton-off.png", {:width => 20, :height => 20, :align => "absmiddle"}
- | Â Â
- span#recording-status
- | Make a Recording
- .session-recordings
- h2
- | other audio
- .session-recording-name-wrapper
- .session-recording-name.left
- | (No audio loaded)
- .session-add.right
- a#close-playback-recording[href="#"]
- = image_tag "content/icon_close.png", {:width => 18, :height => 20, :align => "texttop"}
- | Â Â Close
- .session-tracks-scroller
- #session-recordedtracks-container
- .when-empty.recordings
- span.open-media-file-header
- = image_tag "content/icon_folder.png", {width:22, height:20}
- | Open:
- ul.open-media-file-options
- li
- a#open-a-recording[href="#"]
- | Recording
- - if Rails.application.config.jam_tracks_available
+ .session-fluidtracks
+ .session-livetracks
+ h2
+ | live tracks
+ .session-add[layout-link="select-invites"]
+ a#session-invite-musicians[href="#"]
+ = image_tag "content/icon_add.png", {:width => 19, :height => 19, :align => "texttop"}
+ | Â Â Invite Musicians
+ .session-tracks-scroller
+ #session-livetracks-container
+ .when-empty.livetracks
+ | No other musicians
+ br
+ | are in your session
+ br[clear="all"]
+ #recording-start-stop.recording
+ a
+ = image_tag "content/recordbutton-off.png", {:width => 20, :height => 20, :align => "absmiddle"}
+ | Â Â
+ span#recording-status
+ | Make a Recording
+ .session-recordings
+ h2
+ | other audio
+ .session-recording-name-wrapper
+ .session-recording-name.left
+ | (No audio loaded)
+ .session-add.right
+ a#close-playback-recording[href="#"]
+ = image_tag "content/icon_close.png", {:width => 18, :height => 20, :align => "texttop"}
+ | Â Â Close
+ .session-tracks-scroller
+ #session-recordedtracks-container
+ .when-empty.recordings
+ span.open-media-file-header
+ = image_tag "content/icon_folder.png", {width:22, height:20}
+ | Open:
+ ul.open-media-file-options
li
- a#open-a-jamtrack[href="#"]
- | JamTrack
- - if Rails.application.config.backing_tracks_available
- li
- a#open-a-backingtrack[href="#"]
- | Audio File
- .when-empty.use-metronome-header
- - if Rails.application.config.metronome_available
- = image_tag "content/icon_metronome.png", {width:22, height:20}
- a#open-a-metronome[href="#"]
- | Use Metronome
- br[clear="all"]
- = render "play_controls"
+ a#open-a-recording[href="#"]
+ | Recording
+ - if Rails.application.config.jam_tracks_available || (current_user && current_user.admin)
+ li
+ a#open-a-jamtrack[href="#"]
+ | JamTrack
+ - if Rails.application.config.backing_tracks_available
+ li
+ a#open-a-backingtrack[href="#"]
+ | Audio File
+ .when-empty.use-metronome-header
+ - if Rails.application.config.metronome_available
+ = image_tag "content/icon_metronome.png", {width:22, height:20}
+ a#open-a-metronome[href="#"]
+ | Use Metronome
+ br[clear="all"]
+ .play-controls-holder
+ = render "play_controls"
= render "configureTrack"
= render "addTrack"
= render "addNewGear"
diff --git a/web/app/views/clients/_shopping_cart.html.haml b/web/app/views/clients/_shopping_cart.html.haml
index df3be13f9..36fb6221e 100644
--- a/web/app/views/clients/_shopping_cart.html.haml
+++ b/web/app/views/clients/_shopping_cart.html.haml
@@ -1,4 +1,4 @@
-%div{ layout: 'screen', :'layout-id' => 'shoppingCart', id: 'shoppingCartScreen', :class => 'screen secondary'}
+%div{ layout: 'screen', :'layout-id' => 'shoppingCart', id: 'shoppingCartScreen', :class => 'screen secondary no-login-required'}
.content
.content-head
.content-icon= image_tag("content/icon_shopping_cart.png", {:height => 19, :width => 19})
@@ -19,35 +19,37 @@
.clearall
%script{type: 'text/template', id: 'template-shopping-cart-body'}
- .cart-items
- .cart-item-caption#header
- Your shopping cart now contains:
- .cart-item-price
- %span{style: "text-decoration: underline;"} Price
- .cart-item-quantity
- %span{style: "text-decoration: underline;"} Quantity
- .clearall
+ %table.cart-items
+ %tr
+ %th.cart-item-caption
+ YOUR SHOPPING CART NOW CONTAINS:
+ %th.cart-item-price
+ Price
+ %th.cart-item-quantity
+ Quantity
+ %th
= "{% if (data.carts.length == 0) { %}"
- .no-cart-items Nothing in cart
+ %tr
+ %td.no-cart-items colspan=4 Nothing in cart
= "{% } %}"
+
= "{% _.each(data.carts, function(cart) { %}"
- .cart-item{"cart-id" => "{{cart.id}}"}
- .cart-item-caption
+ %tr.cart-item{"cart-id" => "{{cart.id}}"}
+ %td.cart-item-caption
{{cart.cart_type}}: {{cart.product_info.name}}
- .cart-item-price
+ %td.cart-item-price
$ {{cart.product_info.price}}
- .cart-item-quantity
+ %td.cart-item-quantity
{{cart.quantity}}
- .cart-item-actions
- %a.button-grey.remove-cart{href: "#", "cart-id" => "{{cart.id}}"} DELETE
- .clearall
+ %td.cart-item-actions
+ %a.button-grey.remove-cart{href: "#", "cart-id" => "{{cart.id}}"}
+ DELETE
= "{% }); %}"
.shopping-sub-total
Subtotal: $ {{data.sub_total}}
.clearall
- .left
- %a.button-grey{href: "#"} HELP
.right
+ %a.button-grey{href: "#"} HELP
%a.button-orange{href: "/client#/jamtrack"} CONTINUE SHOPPING
%a.button-orange.proceed-checkout{href: "#"} PROCEED TO CHECKOUT
.clearall
\ No newline at end of file
diff --git a/web/app/views/clients/_signin.html.slim b/web/app/views/clients/_signin.html.slim
deleted file mode 100644
index 0310decb1..000000000
--- a/web/app/views/clients/_signin.html.slim
+++ /dev/null
@@ -1,68 +0,0 @@
-div layout="screen" layout-id="signin" id="signInScreen" class="screen secondary signin-common"
- .content
- .content-head
- .content-icon= image_tag("content/icon_shopping_cart.png", {:height => 19, :width => 19})
- h1 check out
- = render "screen_navigation"
- .content-body
- .checkout-signin
- .checkout-navigation-bar
-
- .signin-form
- = link_to image_tag("content/button_facebook_signin.png", {:width => 249, :height => 46}), '/auth/facebook', class: "signin-facebook"
-
- br
- br
- br
- strong.white Or sign in with JamKazam Account
- br
- br
-
- = form_for(:session, url: signin_path('redirect-to' => '/client#/payment_config') + (request.query_string.blank? ? '' : '?' + request.query_string), html: {class:"signin-form #{'login-error' if @login_error}"}) do |f|
- .email
- = f.label :email, "Email Address:", class: 'inline'
- = f.text_field :email, autofocus: true
-
- .password
- = f.label :password, "Password:", class: 'inline'
- = f.password_field :password, autofocus: true, class: 'signin-password'
- .login-error-msg Invalid login
-
- br clear='all'
-
- .actions align='center'
- = link_to "SIGN IN", '#', class: 'button-orange signin-submit'
- | or
- '
- a.show-signup-dialog href='#' Sign Up
- br
- br
- small
- a.forgot-password href='/request_reset_password' Forgot Password?
-
-
-script type='text/template' id='template-checkout-navigation'
- .checkout-navigation
- .nav-signin
- = "{% if (data.current == 1) { %}"
- .nav-text.selected Sign In
- = "{% } else { %}"
- .nav-text Sign In
- = "{% } %}"
- .nav-arrow ->
- .clearall
- .nav-payment-info
- = "{% if (data.current == 2) { %}"
- .nav-text.selected Address & Payment
- = "{% } else { %}"
- .nav-text Address & Payment
- = "{% } %}"
- .nav-arrow ->
- .clearall
- .nav-signin
- = "{% if (data.current == 3) { %}"
- .nav-text.selected Place Order
- = "{% } else { %}"
- .nav-text Place Order
- = "{% } %}"
- .clearall
diff --git a/web/app/views/clients/_web_filter.html.haml b/web/app/views/clients/_web_filter.html.haml
index 1c64bfd59..45efa625a 100644
--- a/web/app/views/clients/_web_filter.html.haml
+++ b/web/app/views/clients/_web_filter.html.haml
@@ -11,41 +11,31 @@
=content_tag(:div, :id => defined?(id) ? id : 'session-controls', :class => "#{filter_label}-filter filter-head") do
=content_tag(:div, :class => "filter-element wrapper") do
-if :feed==filter_label
- / @begin sort filter
=content_tag(:div, 'Sort Feed by:', :class => 'filter-element desc')
- =select_tag("#{filter_label}_order_by", options_for_select(Search::F_SORT_OPTS), {:class => "#{filter_label}-order-by easydropdown" } )
- / @end sort filter
+ =select_tag("#{filter_label}_order_by", options_for_select(Search::F_SORT_OPTS), {:class => "#{filter_label}-order-by easydropdown" } )
-elsif :jamtrack !=filter_label
- / @begin order by filter
=content_tag(:div, 'Order By:', :class => 'filter-element desc')
- =select_tag("#{filter_label}_order_by", options_for_select(Search::M_ORDERINGS), {:class => "#{filter_label}-order-by easydropdown"} )
- / @end order by filter
+ =select_tag("#{filter_label}_order_by", options_for_select(Search::M_ORDERINGS), {:class => "#{filter_label}-order-by easydropdown"} )
+ =content_tag(:div, :class => 'filter-element wrapper') do
+ / -if (:band==filter_label || :jamtrack==filter_label)
+ / =content_tag(:div, 'Genre:', :class => 'filter-element desc')
+ / =select_tag("#{filter_label}_genre", |
+ / options_for_select([['Any', '']].concat(JamRuby::Genre.all.collect { |ii| [ii.description, ii.id] })), {:class => 'easydropdown'}) |
+ -if :jamtrack==filter_label
+ =content_tag(:div, 'Filter JamTracks:', :class => 'filter-element desc')
+ =select_tag("#{filter_label}_artist", options_for_select([['Any Band', '']].concat(JamTrack.all_artists.collect { |ii| [ii, ii] })), {:class => 'easydropdown'})
=content_tag(:div, :class => 'filter-element wrapper') do
- -if :band==filter_label || :jamtrack==filter_label
- / @begin genre filter
- =content_tag(:div, 'Genre:', :class => 'filter-element desc')
- =select_tag("#{filter_label}_genre", |
- options_for_select([['Any', '']].concat(JamRuby::Genre.all.collect { |ii| [ii.description, ii.id] })), {:class => 'easydropdown'}) |
- / @end genre filter
-if :musician==filter_label || :jamtrack==filter_label
- / @begin instrument filter
- =content_tag(:div, 'Instrument:', :class => 'filter-element desc instrument-selector')
- =select_tag("#{filter_label}_instrument", |
- options_for_select([['Any', '']].concat(JamRuby::Instrument.all.collect { |ii| [ii.description, ii.id] })), {:class=> "easydropdown"}) |
- / @end instrument filter
+ / =content_tag(:div, 'Instrument:', :class => 'filter-element desc instrument-selector')
+ =select_tag("#{filter_label}_instrument", options_for_select([['Any Instrument', '']].concat(JamRuby::Instrument.all.collect { |ii| [ii.description, ii.id] })), {:class=> "easydropdown"}) |
-if :feed==filter_label
- / @begin date filter
- =content_tag(:div, 'Include Dates:', :class => 'filter-element desc')
- =select_tag("#{filter_label}_date", options_for_select(Search::DATE_OPTS), {:class => "easydropdown"})
- / @end date filter
+ / =content_tag(:div, 'Include Dates:', :class => 'filter-element desc')
+ =select_tag("#{filter_label}_date", options_for_select(Search::DATE_OPTS), {:class => "easydropdown"})
=content_tag(:div, :class => 'filter-element wrapper') do
-if :feed==filter_label
- / @begin show filter
=content_tag(:div, 'Show:', :class => 'filter-element desc')
- =select_tag("#{filter_label}_show", options_for_select(Search::SHOW_OPTS), {:class => "easydropdown"})
- / @end show filter
+ =select_tag("#{filter_label}_show", options_for_select(Search::SHOW_OPTS), {:class => "easydropdown"})
-elsif :musician==filter_label
- / @begin score filter
=content_tag(:div, 'Latency:', :class => 'filter-element desc latency-or-distance')
=content_tag(:div, :class => 'query-distance-params') do
=select_tag("musician_query_score", options_for_select(Search::M_SCORE_OPTS, Search::M_SCORE_DEFAULT), {:class => 'easydropdown'})
@@ -53,26 +43,20 @@
#musician-search-city.filter-element.desc
to
%a#musician-change-filter-city{:href => "#"}
- %span#musician-filter-city
- / @end score filter
+ %span#musician-filter-city
-elsif :jamtrack==filter_label
- / @begin availability filter
- =content_tag(:div, 'Availability:', :class => 'filter-element desc')
- =select_tag("#{filter_label}_availability", options_for_select([['Any', '']].concat(JamRuby::JamTrack::SALES_REGION), 'United States'), {:class => "easydropdown"})
- / @end availability filter
+ / =content_tag(:div, 'Availability:', :class => 'filter-element desc')
+ =select_tag("#{filter_label}_availability", options_for_select([['Any Availability', '']].concat(JamRuby::JamTrack::SALES_REGION), 'United States'), {:class => "easydropdown"})
-else
- / @begin distance filter
=content_tag(:div, 'Within', :class => 'filter-element desc')
=content_tag(:div, :class => 'query-distance-params') do
-default_distance =:musician==filter_label ? Search::M_MILES_DEFAULT : Search::B_MILES_DEFAULT
=select_tag("#{filter_label}_query_distance", options_for_select(Search::DISTANCE_OPTS, default_distance), {:class => 'easydropdown'})
=content_tag(:div, :class => 'filter-element desc') do
- miles of #{content_tag(:span, current_user ? current_user.current_city(request.remote_ip) : '', :id => "#{filter_label}-filter-city")}
- / @end distance filter
+ miles of #{content_tag(:span, current_user ? current_user.current_city(request.remote_ip) : '', :id => "#{filter_label}-filter-city")}
-if :feed==filter_label
.btn-refresh-holder
%a.button-grey.btn-refresh-entries{:href => "/client#/feed"} REFRESH
-elsif :musician==filter_label
.btn-refresh-holder
- %a.button-grey.btn-refresh-entries{:href => "/client#/musicians"} REFRESH
-/ @end web_filter
\ No newline at end of file
+ %a.button-grey.btn-refresh-entries{:href => "/client#/musicians"} REFRESH
\ No newline at end of file
diff --git a/web/app/views/clients/index.html.erb b/web/app/views/clients/index.html.erb
index 1757ddcf0..46205b9c7 100644
--- a/web/app/views/clients/index.html.erb
+++ b/web/app/views/clients/index.html.erb
@@ -38,8 +38,11 @@
<%= render "users/feed_music_session_ajax" %>
<%= render "users/feed_recording_ajax" %>
<%= render "jamtrack" %>
+<%= render "jamtrack_landing" %>
<%= render "shopping_cart" %>
-<%= render "signin" %>
+<%= render "checkout_signin" %>
+<%= render "checkout_payment" %>
+<%= render "checkout_order" %>
<%= render "order" %>
<%= render "feed" %>
<%= render "bands" %>
@@ -55,6 +58,7 @@
<%= render "account_profile_avatar" %>
<%= render "account_audio_profile" %>
<%= render "account_sessions" %>
+<%= render "account_jamtracks" %>
<%= render "account_session_detail" %>
<%= render "account_session_properties" %>
<%= render "inviteMusicians" %>
@@ -112,20 +116,7 @@
JK.currentUserName = null;
JK.currentUserMusician = null;
JK.currentUserAdmin = false;
-
- // you need to be logged in to use this part of the interface.
- // save original URL, and redirect to the home page
- logger.debug("redirecting back to / because not logged in")
-
- var redirectPath= '?redirect-to=' + encodeURIComponent(JK.locationPath());
- if(gon.isNativeClient) {
- window.location.href = '/signin' + redirectPath;
- }
- else {
- window.location.href = '/' + redirectPath;
- }
-
- <% end %>
+ <% end %>
// Some things can't be initialized until we're connected. Put them here.
@@ -174,6 +165,14 @@
var jamtrackAvailabilityDialog = new JK.JamtrackAvailabilityDialog(JK.app);
jamtrackAvailabilityDialog.initialize();
+
+ var jamtrackLicenseDialog = new JK.JamtrackLicenseDialog(JK.app);
+ jamtrackLicenseDialog.initialize();
+
+ var jamtrackPaymentHistoryDialog = new JK.JamtrackPaymentHistoryDialog(JK.app);
+ jamtrackPaymentHistoryDialog.initialize();
+
+
var audioProfileInvalidDialog = new JK.AudioProfileInvalidDialog(JK.app);
audioProfileInvalidDialog.initialize();
@@ -199,6 +198,9 @@
var accountSessionsScreen = new JK.AccountSessions(JK.app);
accountSessionsScreen.initialize();
+ var accountJamTracksScreen = new JK.AccountJamTracks(JK.app);
+ accountJamTracksScreen.initialize();
+
var accountSessionDetailScreen = new JK.AccountSessionDetail(JK.app);
accountSessionDetailScreen.initialize(JK.InvitationDialogInstance);
@@ -261,14 +263,25 @@
var jamtrackScreen = new JK.JamTrackScreen(JK.app);
jamtrackScreen.initialize();
+
+
+ var jamtrackLanding = new JK.JamTrackLanding(JK.app);
+ jamtrackLanding.initialize();
+
var shoppingCartScreen = new JK.ShoppingCartScreen(JK.app);
shoppingCartScreen.initialize();
var checkoutSignInScreen = new JK.CheckoutSignInScreen(JK.app);
checkoutSignInScreen.initialize();
- var OrderScreen = new JK.OrderScreen(JK.app);
- OrderScreen.initialize();
+ var checkoutPaymentScreen = new JK.CheckoutPaymentScreen(JK.app);
+ checkoutPaymentScreen.initialize();
+
+ var checkoutOrderScreen = new JK.CheckoutOrderScreen(JK.app);
+ checkoutOrderScreen.initialize();
+
+ // var OrderScreen = new JK.OrderScreen(JK.app);
+ // OrderScreen.initialize();
var findMusicianScreen = new JK.FindMusicianScreen(JK.app);
findMusicianScreen.initialize(JK.TextMessageDialogInstance);
@@ -304,6 +317,8 @@
JK.ClientUpdateInstance.check()
JK.app.initialRouting();
+
+
JK.hideCurtain(300);
}
@@ -319,10 +334,10 @@
JK.RecordingUtils.init();
- // Let's get things rolling...
- if (JK.currentUserId) {
+ JK.app.initialize();
- JK.app.initialize();
+ // Let's get things rolling...
+ if (JK.currentUserId) {
JK.JamServer.registerMessageCallback(JK.MessageType.CLIENT_UPDATE, function(header, payload) {
// do a client update early check upon initialization
@@ -330,7 +345,7 @@
});
- JK.TickDuration('.feed-entry.music-session-history-entry .inprogress .tick-duration');
+ JK.TickDuration('.feed-entry.music-session-history-entry .inprogress .tick-duration');
JK.JamServer.connect() // singleton here defined in JamServer.js
.done(function() {
@@ -343,6 +358,9 @@
// this ensures that there is always a CurrentSessionModel, even if it's for a non-active session
JK.CurrentSessionModel = new JK.SessionModel(JK.app, JK.JamServer, window.jamClient, null);
}
+ else {
+ _initAfterConnect(false);
+ }
JK.bindHoverEvents();
})
diff --git a/web/app/views/dialogs/_dialogs.html.haml b/web/app/views/dialogs/_dialogs.html.haml
index 7383a4400..990240721 100644
--- a/web/app/views/dialogs/_dialogs.html.haml
+++ b/web/app/views/dialogs/_dialogs.html.haml
@@ -25,6 +25,7 @@
= render 'dialogs/videoDialog'
= render 'dialogs/friendSelectorDialog'
= render 'dialogs/jamtrackAvailabilityDialog'
+= render 'dialogs/jamtrackLicenseDialog'
= render 'dialogs/clientPreferencesDialog'
= render 'dialogs/audioProfileInvalidDialog'
= render 'dialogs/gettingStartedDialog'
@@ -34,5 +35,7 @@
= render 'dialogs/adjustGearSpeedDialog'
= render 'dialogs/openJamTrackDialog'
= render 'dialogs/openBackingTrackDialog'
+= render 'dialogs/loginRequiredDialog'
+= render 'dialogs/jamtrackPaymentHistoryDialog'
= render 'dialogs/genreSelectorDialog'
-= render 'dialogs/recordingSelectorDialog'
\ No newline at end of file
+= render 'dialogs/recordingSelectorDialog'
diff --git a/web/app/views/dialogs/_jamtrackLicenseDialog.html.slim b/web/app/views/dialogs/_jamtrackLicenseDialog.html.slim
new file mode 100644
index 000000000..a72c01c06
--- /dev/null
+++ b/web/app/views/dialogs/_jamtrackLicenseDialog.html.slim
@@ -0,0 +1,18 @@
+#jamtrack-license-dialog.dialog.dialog-overlay-sm layout='dialog' layout-id = 'jamtrack-license-dialog'
+ .content-head
+ h1 JamTrack License:
+ .dialog-inner
+ .content-body
+ .content-body-scroller
+ .paragraph
+ | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam dignissim ut nunc at hendrerit. Vestibulum semper risus a libero fermentum, molestie convallis risus faucibus. Ut molestie hendrerit orci, id laoreet turpis malesuada nec. Cras sem urna, commodo finibus sodales eu, scelerisque et ligula. Vivamus congue urna lobortis, volutpat ex non, facilisis ante. Maecenas laoreet lacus sit amet justo tempus sagittis. Proin eget libero est. Nullam vulputate finibus nibh nec malesuada. Proin at odio dui. Cras venenatis pharetra ipsum sit amet mollis. Vivamus enim lectus, venenatis sit amet velit at, condimentum euismod dolor. Sed ut tellus in lacus finibus maximus quis ac tortor. Nullam ac purus tincidunt, vestibulum magna vel, hendrerit nunc. Nam tincidunt velit ut est congue ultrices. Integer id magna vulputate, consequat ante et, gravida nibh.
+ .paragraph
+ | Etiam ac neque vel ex sagittis cursus ut a nulla. Praesent id pretium metus. Duis rhoncus egestas magna ut fringilla. Aenean et lobortis sem. Duis at turpis luctus, auctor lectus vitae, consectetur ante. Donec feugiat ullamcorper lacus eu ultricies. Sed vitae turpis arcu. Nam faucibus facilisis sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Quisque vel felis rutrum, elementum lacus euismod, ultricies leo.
+ .paragraph
+ | Etiam ac neque vel ex sagittis cursus ut a nulla. Praesent id pretium metus. Duis rhoncus egestas magna ut fringilla. Aenean et lobortis sem. Duis at turpis luctus, auctor lectus vitae, consectetur ante. Donec feugiat ullamcorper lacus eu ultricies. Sed vitae turpis arcu. Nam faucibus facilisis sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Quisque vel felis rutrum, elementum lacus euismod, ultricies leo.
+ .paragraph
+ | Etiam non nisi magna. Sed diam sem, vulputate sit amet odio quis, vulputate pharetra nunc. Morbi commodo lacus in leo semper semper. Sed nulla felis, consequat dignissim dictum eu, elementum eget massa. Nulla luctus condimentum magna. Vestibulum in interdum erat. In porttitor fermentum mi, ac tincidunt nisi interdum vulputate.
+
+ .jamtrack_buttons
+ .right
+ a.button-grey class='btnCancel' layout-action='cancel' OK
diff --git a/web/app/views/dialogs/_jamtrackPaymentHistoryDialog.html.slim b/web/app/views/dialogs/_jamtrackPaymentHistoryDialog.html.slim
new file mode 100644
index 000000000..663d0f939
--- /dev/null
+++ b/web/app/views/dialogs/_jamtrackPaymentHistoryDialog.html.slim
@@ -0,0 +1,33 @@
+#jamtrack-payment-history-dialog.dialog.dialog-overlay-sm layout='dialog' layout-id = 'jamtrack-payment-history-dialog'
+ .content-head
+ h1 Payment History:
+ .dialog-inner
+ .content-body
+ .content-body-scroller
+ table.payment-table
+ thead
+ tr
+ th DATE
+ th AMOUNT
+ th STATUS
+ th PAYMENT_METHOD
+ th REFERENCE
+ tbody
+ tr: td colspan="5" Loading payment history...
+
+ .jamtrack_buttons
+ .right
+ a.button-orange class='btnCancel' layout-action='cancel' OK
+
+ script#template-payment-history-row type="text/template"
+ tr
+ td
+ | {{data.date}}
+ td
+ | ${{data.amount}}
+ td.capitalize
+ | {{data.status}}
+ td.capitalize
+ | {{data.payment_method}}
+ td
+ | {{data.reference}}
diff --git a/web/app/views/dialogs/_loginRequiredDialog.html.slim b/web/app/views/dialogs/_loginRequiredDialog.html.slim
new file mode 100644
index 000000000..4b979e68d
--- /dev/null
+++ b/web/app/views/dialogs/_loginRequiredDialog.html.slim
@@ -0,0 +1,20 @@
+.dialog.dialog-overlay-sm layout='dialog' layout-id='login-required-dialog' id='login-required-dialog'
+ .content-head
+ = image_tag "content/icon_alert.png", {:width => 24, :height => 24, :class => 'content-icon' }
+ h1 Login Required
+
+ .dialog-inner
+ p
+ a href="/signup" Sign Up
+ | or
+ a href="/signin" Sign In
+ | to access most functionality on this page.
+ p
+ | However, you can browse for
+ a class="go-to-jamtracks" href='/client#/jamtrack' JamTracks
+ | without logging in.
+ br
+ .clearall
+ .buttons
+ .right
+ a.button-orange class='btnClose' layout-action='close' CLOSE
\ No newline at end of file
diff --git a/web/app/views/layouts/client.html.erb b/web/app/views/layouts/client.html.erb
index 2c4238803..adee2a9d7 100644
--- a/web/app/views/layouts/client.html.erb
+++ b/web/app/views/layouts/client.html.erb
@@ -35,5 +35,7 @@
<%= yield %>
<%= render "shared/ga" %>
+ <%= render "shared/recurly" %>
+