VRFS-3936 merging develop
This commit is contained in:
commit
81979ba4bb
|
|
@ -36,11 +36,11 @@ gem 'coffee-script-source' #, '~> 1.4.0' # ADD THIS LINE, 1.5.0 doesn't compile
|
||||||
gem 'devise' #, '3.3.0'
|
gem 'devise' #, '3.3.0'
|
||||||
gem 'will_paginate' #, '3.0.3'
|
gem 'will_paginate' #, '3.0.3'
|
||||||
gem 'bootstrap-will_paginate', '0.0.6'
|
gem 'bootstrap-will_paginate', '0.0.6'
|
||||||
gem 'carrierwave' #, '0.9.0'
|
gem 'carrierwave', '0.11.2' #, '0.9.0'
|
||||||
gem 'carrierwave_direct'
|
gem 'carrierwave_direct'
|
||||||
gem 'uuidtools', '2.1.2'
|
gem 'uuidtools', '2.1.2'
|
||||||
gem 'jquery-ui-rails' #, '4.2.1'
|
gem 'jquery-ui-rails', '5.0.5' #, '4.2.1'
|
||||||
gem 'jquery-rails'
|
gem 'jquery-rails', '4.1.1' # both this and jquery-ui-rails are pinned; if you unpin, jquery/autocomplete is missing during precomplie
|
||||||
gem 'rails-jquery-autocomplete' # This is the maintained version of rails3-jquery-autocomplete
|
gem 'rails-jquery-autocomplete' # This is the maintained version of rails3-jquery-autocomplete
|
||||||
gem 'activeadmin' , '1.0.0.pre4'# github: 'activeadmin', branch: 'master'
|
gem 'activeadmin' , '1.0.0.pre4'# github: 'activeadmin', branch: 'master'
|
||||||
gem 'mime-types', '1.25'
|
gem 'mime-types', '1.25'
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
ActiveAdmin.register JamRuby::Jamblaster, :as => 'Jamblaster' do
|
ActiveAdmin.register JamRuby::Jamblaster, :as => 'Jamblaster' do
|
||||||
|
|
||||||
|
|
||||||
|
config.filters = false
|
||||||
menu :label => 'JamBlasters', :parent => 'JamBlaster'
|
menu :label => 'JamBlasters', :parent => 'JamBlaster'
|
||||||
|
|
||||||
|
scope("Connected", default: true) { |scope| scope.where('client_id in (select client_id from connections)') }
|
||||||
|
scope("All") { |scope| scope.order('created_at desc') }
|
||||||
|
|
||||||
form do |f|
|
form do |f|
|
||||||
f.inputs 'New JamBlaster' do
|
f.inputs 'New JamBlaster' do
|
||||||
f.input :user, required: true, collection: User.all, include_blank: false
|
f.input :user, required: true, collection: User.all, include_blank: false
|
||||||
|
|
@ -12,4 +16,25 @@ ActiveAdmin.register JamRuby::Jamblaster, :as => 'Jamblaster' do
|
||||||
end
|
end
|
||||||
f.actions
|
f.actions
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
index do
|
||||||
|
|
||||||
|
# actions # use this for all view/edit/delete links
|
||||||
|
|
||||||
|
column 'Serial' do |oo|
|
||||||
|
oo.serial_no
|
||||||
|
end
|
||||||
|
|
||||||
|
column 'IPv4' do |oo|
|
||||||
|
oo.ipv4_link_local
|
||||||
|
end
|
||||||
|
|
||||||
|
column 'IPv6' do |oo|
|
||||||
|
oo.ipv6_link_local
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,17 @@ ActiveAdmin.register JamRuby::SaleLineItem, :as => 'Sale Line Items' do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
column 'Source' do |oo|
|
column 'Source' do |oo|
|
||||||
oo.sale.source
|
if oo.sale.source == JamRuby::Sale::SOURCE_PAYPAL
|
||||||
|
link_to(oo.sale.source, 'https://history.paypal.com/webscr?cmd=_history-details-from-hub&id=' + oo.sale.recurly_invoice_id)
|
||||||
|
else
|
||||||
|
oo.sale.source
|
||||||
|
end
|
||||||
end
|
end
|
||||||
column 'When' do |oo|
|
column 'When' do |oo|
|
||||||
oo.created_at
|
oo.created_at
|
||||||
end
|
end
|
||||||
|
column 'Link' do |oo|
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -368,4 +368,8 @@ retailers.sql
|
||||||
second_ed.sql
|
second_ed.sql
|
||||||
second_ed_v2.sql
|
second_ed_v2.sql
|
||||||
retailers_v2.sql
|
retailers_v2.sql
|
||||||
retailer_interest.sql
|
retailer_interest.sql
|
||||||
|
connection_role.sql
|
||||||
|
retailer_payment_split.sql
|
||||||
|
teacher_distribution_fields.sql
|
||||||
|
jam_track_download_rights.sql
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE connections ADD COLUMN client_role VARCHAR;
|
||||||
|
ALTER TABLE connections ADD COLUMN parent_client_id VARCHAR;
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
ALTER TABLE jam_tracks ADD COLUMN download_price numeric;
|
||||||
|
UPDATE jam_tracks SET download_price = 4.99;
|
||||||
|
ALTER TABLE jam_track_rights ADD COLUMN can_download BOOLEAN NOT NULL DEFAULT FALSE;
|
||||||
|
UPDATE jam_track_rights SET can_download = TRUE;
|
||||||
|
ALTER TABLE shopping_carts ADD COLUMN variant VARCHAR;
|
||||||
|
UPDATE shopping_carts set variant = 'stream' where cart_type = 'JamTrack';
|
||||||
|
ALTER TABLE sale_line_items ADD COLUMN variant VARCHAR;
|
||||||
|
UPDATE sale_line_items set variant = 'full';
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE retailers ADD COLUMN payment VARCHAR;
|
||||||
|
ALTER TABLE lesson_bookings ADD COLUMN payment VARCHAR;
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
ALTER TABLE teacher_distributions ADD COLUMN teacher_fee_in_cents INTEGER;
|
||||||
|
ALTER TABLE lesson_bookings ADD COLUMN same_retailer BOOLEAN DEFAULT FALSE NOT NULL;
|
||||||
|
ALTER TABLE lesson_sessions ADD COLUMN admin_marked BOOLEAN DEFAULT FALSE NOT NULL;
|
||||||
|
|
@ -64,6 +64,7 @@ gem 'icalendar'
|
||||||
gem 'email_validator'
|
gem 'email_validator'
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
|
gem 'pry'
|
||||||
gem 'simplecov', '~> 0.7.1'
|
gem 'simplecov', '~> 0.7.1'
|
||||||
gem 'simplecov-rcov'
|
gem 'simplecov-rcov'
|
||||||
gem 'factory_girl', '4.5.0'
|
gem 'factory_girl', '4.5.0'
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ require "jam_ruby/errors/state_error"
|
||||||
require "jam_ruby/errors/jam_argument_error"
|
require "jam_ruby/errors/jam_argument_error"
|
||||||
require "jam_ruby/errors/jam_record_not_found"
|
require "jam_ruby/errors/jam_record_not_found"
|
||||||
require "jam_ruby/errors/conflict_error"
|
require "jam_ruby/errors/conflict_error"
|
||||||
|
require "jam_ruby/errors/pay_pal_client_error"
|
||||||
require "jam_ruby/lib/app_config"
|
require "jam_ruby/lib/app_config"
|
||||||
require "jam_ruby/lib/s3_manager_mixin"
|
require "jam_ruby/lib/s3_manager_mixin"
|
||||||
require "jam_ruby/lib/s3_public_manager_mixin"
|
require "jam_ruby/lib/s3_public_manager_mixin"
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@
|
||||||
|
|
||||||
yourself playing along with the rest of the band in audio or video, and more. Get your first
|
yourself playing along with the rest of the band in audio or video, and more. Get your first
|
||||||
|
|
||||||
JamTrack free to try one out! After that they are just $1.99 each. <a href="https://jamkazam.desk.com/customer/en/portal/articles/2414618-playing-with-jamtracks" style="color:#fc0">Click here for more
|
JamTrack free to try one out! After that they are just $1.99/$4.99 each. <a href="https://jamkazam.desk.com/customer/en/portal/articles/2414618-playing-with-jamtracks" style="color:#fc0">Click here for more
|
||||||
|
|
||||||
information</a> on how you can use JamTracks in your browser, in our free Mac or Windows
|
information</a> on how you can use JamTracks in your browser, in our free Mac or Windows
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -402,7 +402,7 @@ SQL
|
||||||
music_session.creator
|
music_session.creator
|
||||||
end
|
end
|
||||||
|
|
||||||
def join_music_session(user, client_id, music_session, as_musician, tracks, audio_latency, video_sources=nil)
|
def join_music_session(user, client_id, music_session, as_musician, tracks, audio_latency, client_role = nil, parent_client_id = nil, video_sources=nil)
|
||||||
connection = nil
|
connection = nil
|
||||||
|
|
||||||
ConnectionManager.active_record_transaction do |connection_manager|
|
ConnectionManager.active_record_transaction do |connection_manager|
|
||||||
|
|
@ -418,7 +418,7 @@ SQL
|
||||||
raise JamPermissionError, "wrong user_id associated with connection #{client_id}"
|
raise JamPermissionError, "wrong user_id associated with connection #{client_id}"
|
||||||
end
|
end
|
||||||
|
|
||||||
connection.join_the_session(music_session, as_musician, tracks, user, audio_latency, video_sources)
|
connection.join_the_session(music_session, as_musician, tracks, user, audio_latency, client_role, parent_client_id, video_sources)
|
||||||
JamRuby::MusicSessionUserHistory.join_music_session(user.id, music_session.id, client_id)
|
JamRuby::MusicSessionUserHistory.join_music_session(user.id, music_session.id, client_id)
|
||||||
# connection.music_session_id = music_session.id
|
# connection.music_session_id = music_session.id
|
||||||
# connection.as_musician = as_musician
|
# connection.as_musician = as_musician
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
module JamRuby
|
||||||
|
class PayPalClientError < StandardError
|
||||||
|
|
||||||
|
attr_accessor :errors
|
||||||
|
def initialize(data)
|
||||||
|
if data.respond_to?('has_key?')
|
||||||
|
self.errors = data
|
||||||
|
else
|
||||||
|
self.errors = {:message=>data.to_s}
|
||||||
|
end
|
||||||
|
end # initialize
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
s=super
|
||||||
|
s << ", errors: #{errors.inspect}" if self.errors.any?
|
||||||
|
s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -868,6 +868,7 @@ module JamRuby
|
||||||
jam_track.genres = determine_genres(metadata)
|
jam_track.genres = determine_genres(metadata)
|
||||||
jam_track.language = determine_language(metadata)
|
jam_track.language = determine_language(metadata)
|
||||||
jam_track.price = 1.99
|
jam_track.price = 1.99
|
||||||
|
jam_track.download_price = 4.99
|
||||||
jam_track.reproduction_royalty_amount = nil
|
jam_track.reproduction_royalty_amount = nil
|
||||||
jam_track.reproduction_royalty = true
|
jam_track.reproduction_royalty = true
|
||||||
jam_track.public_performance_royalty = true
|
jam_track.public_performance_royalty = true
|
||||||
|
|
|
||||||
|
|
@ -501,7 +501,7 @@ module JamRuby
|
||||||
[music_sessions, user_scores]
|
[music_sessions, user_scores]
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.participant_create(user, music_session_id, client_id, as_musician, tracks, audio_latency, video_sources=nil)
|
def self.participant_create(user, music_session_id, client_id, as_musician, tracks, audio_latency, client_role = nil, parent_client_id = nil, video_sources=nil)
|
||||||
music_session = MusicSession.find(music_session_id)
|
music_session = MusicSession.find(music_session_id)
|
||||||
|
|
||||||
# USERS ARE ALREADY IN SESSION
|
# USERS ARE ALREADY IN SESSION
|
||||||
|
|
@ -514,7 +514,7 @@ module JamRuby
|
||||||
active_music_session.with_lock do # VRFS-1297
|
active_music_session.with_lock do # VRFS-1297
|
||||||
active_music_session.tick_track_changes
|
active_music_session.tick_track_changes
|
||||||
# VRFS-3986
|
# VRFS-3986
|
||||||
connection = ConnectionManager.new.join_music_session(user, client_id, active_music_session, as_musician, tracks, audio_latency, video_sources)
|
connection = ConnectionManager.new.join_music_session(user, client_id, active_music_session, as_musician, tracks, audio_latency, client_role, parent_client_id, video_sources)
|
||||||
|
|
||||||
if connection.errors.any?
|
if connection.errors.any?
|
||||||
# rollback the transaction to make sure nothing is disturbed in the database
|
# rollback the transaction to make sure nothing is disturbed in the database
|
||||||
|
|
@ -573,7 +573,7 @@ module JamRuby
|
||||||
|
|
||||||
# auto-join this user into the newly created session
|
# auto-join this user into the newly created session
|
||||||
as_musician = true
|
as_musician = true
|
||||||
connection = ConnectionManager.new.join_music_session(user, client_id, active_music_session, as_musician, tracks, audio_latency, video_sources)
|
connection = ConnectionManager.new.join_music_session(user, client_id, active_music_session, as_musician, tracks, audio_latency, client_role, parent_client_id, video_sources)
|
||||||
|
|
||||||
unless connection.errors.any?
|
unless connection.errors.any?
|
||||||
user.update_progression_field(:first_music_session_at)
|
user.update_progression_field(:first_music_session_at)
|
||||||
|
|
|
||||||
|
|
@ -176,11 +176,13 @@ module JamRuby
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def join_the_session(music_session, as_musician, tracks, user, audio_latency, videos=nil)
|
def join_the_session(music_session, as_musician, tracks, user, audio_latency, client_role = nil, parent_client_id = nil, videos=nil)
|
||||||
self.music_session_id = music_session.id
|
self.music_session_id = music_session.id
|
||||||
self.as_musician = as_musician == true # this is deliberate; otherwise we create a warning in one our tests that passes 'blarg' (rails warning about casting strings to false)
|
self.as_musician = as_musician == true # this is deliberate; otherwise we create a warning in one our tests that passes 'blarg' (rails warning about casting strings to false)
|
||||||
self.joining_session = true
|
self.joining_session = true
|
||||||
self.joined_session_at = Time.now
|
self.joined_session_at = Time.now
|
||||||
|
self.client_role = client_role
|
||||||
|
self.parent_client_id = parent_client_id
|
||||||
associate_tracks(tracks) unless tracks.nil?
|
associate_tracks(tracks) unless tracks.nil?
|
||||||
associate_videos(videos) unless videos.nil?
|
associate_videos(videos) unless videos.nil?
|
||||||
self.save
|
self.save
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,10 @@ module JamRuby
|
||||||
sale_display
|
sale_display
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def variant_price(variant = nil)
|
||||||
|
price
|
||||||
|
end
|
||||||
|
|
||||||
def price
|
def price
|
||||||
if card_type == JAM_TRACKS_5
|
if card_type == JAM_TRACKS_5
|
||||||
10.00
|
10.00
|
||||||
|
|
@ -39,7 +43,7 @@ module JamRuby
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def sale_display
|
def sale_display(variant = nil)
|
||||||
if card_type == JAM_TRACKS_5
|
if card_type == JAM_TRACKS_5
|
||||||
'JamTracks Gift Card (5)'
|
'JamTracks Gift Card (5)'
|
||||||
elsif card_type == JAM_TRACKS_10
|
elsif card_type == JAM_TRACKS_10
|
||||||
|
|
|
||||||
|
|
@ -161,9 +161,22 @@ module JamRuby
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def sale_display
|
def sale_display(variant = nil)
|
||||||
"JamTrack: " + name
|
if variant == ShoppingCart::JAMTRACK_FULL
|
||||||
|
variant_desc = 'FULL'
|
||||||
|
elsif variant == ShoppingCart::JAMTRACK_DOWNLOAD
|
||||||
|
variant_desc = 'UPRGADE'
|
||||||
|
elsif variant == ShoppingCart::JAMTRACK_STREAM
|
||||||
|
variant_desc = 'FOR USE ONLY WITHIN APP'
|
||||||
|
else
|
||||||
|
variant_desc = 'UNKNOWN'
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
"JamTrack: #{name} - #{variant_desc}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def duplicate_positions?
|
def duplicate_positions?
|
||||||
counter = {}
|
counter = {}
|
||||||
jam_track_tracks.each do |track|
|
jam_track_tracks.each do |track|
|
||||||
|
|
@ -504,14 +517,43 @@ module JamRuby
|
||||||
owners.include?(user)
|
owners.include?(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def right_for_user(user)
|
def right_for_user(user, variant = nil)
|
||||||
jam_track_rights.where("user_id=?", user).first
|
|
||||||
|
query = jam_track_rights.where("user_id=?", user)
|
||||||
|
|
||||||
|
if variant
|
||||||
|
if variant == ShoppingCart::JAMTRACK_DOWNLOAD
|
||||||
|
query = query.where('can_download', true)
|
||||||
|
elsif variant == ShoppingCart::JAMTRACK_FULL
|
||||||
|
query = query.where('can_download', true)
|
||||||
|
elsif variant == ShoppingCart::JAMTRACK_STREAM
|
||||||
|
|
||||||
|
else
|
||||||
|
throw 'unknown variant ' + variant
|
||||||
|
end
|
||||||
|
end
|
||||||
|
query.first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def mixdowns_for_user(user)
|
def mixdowns_for_user(user)
|
||||||
JamTrackMixdown.where(user_id: user.id).where(jam_track_id: self.id)
|
JamTrackMixdown.where(user_id: user.id).where(jam_track_id: self.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def upgrade_price
|
||||||
|
variant_price('download')
|
||||||
|
end
|
||||||
|
|
||||||
|
def variant_price(variant)
|
||||||
|
if variant == 'full'
|
||||||
|
download_price
|
||||||
|
elsif variant == 'download'
|
||||||
|
download_price - price
|
||||||
|
else
|
||||||
|
price
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def short_plan_code
|
def short_plan_code
|
||||||
prefix = 'jamtrack-'
|
prefix = 'jamtrack-'
|
||||||
plan_code[prefix.length..-1]
|
plan_code[prefix.length..-1]
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ module JamRuby
|
||||||
belongs_to :default_slot, class_name: "JamRuby::LessonBookingSlot", foreign_key: :default_slot_id, inverse_of: :defaulted_booking, :dependent => :destroy
|
belongs_to :default_slot, class_name: "JamRuby::LessonBookingSlot", foreign_key: :default_slot_id, inverse_of: :defaulted_booking, :dependent => :destroy
|
||||||
belongs_to :counter_slot, class_name: "JamRuby::LessonBookingSlot", foreign_key: :counter_slot_id, inverse_of: :countered_booking, :dependent => :destroy
|
belongs_to :counter_slot, class_name: "JamRuby::LessonBookingSlot", foreign_key: :counter_slot_id, inverse_of: :countered_booking, :dependent => :destroy
|
||||||
belongs_to :school, class_name: "JamRuby::School"
|
belongs_to :school, class_name: "JamRuby::School"
|
||||||
|
belongs_to :retailer, class_name: "JamRuby::Retailer"
|
||||||
belongs_to :test_drive_package_choice, class_name: "JamRuby::TestDrivePackageChoice"
|
belongs_to :test_drive_package_choice, class_name: "JamRuby::TestDrivePackageChoice"
|
||||||
belongs_to :posa_card, class_name: "JamRuby::PosaCard"
|
belongs_to :posa_card, class_name: "JamRuby::PosaCard"
|
||||||
has_many :lesson_booking_slots, class_name: "JamRuby::LessonBookingSlot", :dependent => :destroy
|
has_many :lesson_booking_slots, class_name: "JamRuby::LessonBookingSlot", :dependent => :destroy
|
||||||
|
|
@ -527,11 +528,15 @@ module JamRuby
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def distribution_price_in_cents(target, education)
|
def distribution_price_in_cents(target, education, split = nil, fee_rate = nil)
|
||||||
distribution = teacher_distribution_price_in_cents(target)
|
distribution = teacher_distribution_price_in_cents(target)
|
||||||
|
|
||||||
if education
|
if split
|
||||||
(distribution * 0.0625).round
|
(distribution * split).round
|
||||||
|
|
||||||
|
# when a split is provided, we also pin down the teacher_fee_in_cents, instead of assuming a bunch of stuff
|
||||||
|
elsif education
|
||||||
|
(distribution * 0.0625).round # 0.0625 is 1/4th of 25%
|
||||||
else
|
else
|
||||||
distribution
|
distribution
|
||||||
end
|
end
|
||||||
|
|
@ -799,6 +804,13 @@ module JamRuby
|
||||||
lesson_booking.school = lesson_booking.teacher.teacher.school
|
lesson_booking.school = lesson_booking.teacher.teacher.school
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# copy payment settings from retailer into lesson booking
|
||||||
|
if lesson_booking.teacher && lesson_booking.teacher.teacher.retailer
|
||||||
|
lesson_booking.retailer = lesson_booking.teacher.teacher.retailer
|
||||||
|
lesson_booking.payment = lesson_booking.teacher.teacher.retailer.payment_details.to_json
|
||||||
|
lesson_booking.same_retailer = lesson_booking.teacher.teacher.retailer.affiliate_partner == user.affiliate_referral
|
||||||
|
end
|
||||||
|
|
||||||
if user
|
if user
|
||||||
lesson_booking.same_school = !!(lesson_booking.school && user.school && (lesson_booking.school.id == user.school.id))
|
lesson_booking.same_school = !!(lesson_booking.school && user.school && (lesson_booking.school.id == user.school.id))
|
||||||
if lesson_booking.same_school
|
if lesson_booking.same_school
|
||||||
|
|
|
||||||
|
|
@ -53,11 +53,11 @@ module JamRuby
|
||||||
end
|
end
|
||||||
|
|
||||||
def teacher_distribution
|
def teacher_distribution
|
||||||
teacher_distributions.where(education:false).first
|
teacher_distributions.where(education: false).first
|
||||||
end
|
end
|
||||||
|
|
||||||
def education_distribution
|
def education_distribution
|
||||||
teacher_distributions.where(education:true).first
|
teacher_distributions.where(education: true).first
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_test_drives
|
def add_test_drives
|
||||||
|
|
@ -97,15 +97,46 @@ module JamRuby
|
||||||
purchase.month = month
|
purchase.month = month
|
||||||
purchase.recurring = true
|
purchase.recurring = true
|
||||||
|
|
||||||
|
|
||||||
# this is for monthly
|
# this is for monthly
|
||||||
if lesson_booking && lesson_booking.requires_teacher_distribution?(purchase)
|
if lesson_booking && lesson_booking.requires_teacher_distribution?(purchase)
|
||||||
teacher_dist = TeacherDistribution.create_for_lesson_package_purchase(purchase, false)
|
|
||||||
purchase.teacher_distributions << teacher_dist
|
|
||||||
# price should always match the teacher_distribution, if there is one
|
|
||||||
purchase.price = teacher_dist.amount_in_cents / 100
|
|
||||||
|
|
||||||
if lesson_booking.school_on_school_payment?
|
if lesson_booking.posa_card.nil? && lesson_booking.payment
|
||||||
purchase.teacher_distributions << TeacherDistribution.create_for_lesson_package_purchase(purchase, true)
|
|
||||||
|
# if there is a payment object, it will describe how everything gets doled out
|
||||||
|
payment = JSON.parse(lesson_booking.payment)
|
||||||
|
|
||||||
|
teacher_split = payment["teacher"]
|
||||||
|
retailer_split = payment["retailer"]
|
||||||
|
|
||||||
|
retailer_rate = teacher.teacher.retailer.jamkazam_rate + APP_CONFIG.stripe[:charge_fee] # add 0.03 to account for stripe deduction
|
||||||
|
|
||||||
|
|
||||||
|
if teacher_split && teacher_split > 0
|
||||||
|
teacher_dist = TeacherDistribution.create_for_lesson_package_purchase(purchase, false, (teacher_split / 100.0).round(2), retailer_rate)
|
||||||
|
purchase.teacher_distributions << teacher_dist
|
||||||
|
# price should always match the teacher_distribution, if there is one
|
||||||
|
purchase.price = teacher_dist.amount_in_cents / 100
|
||||||
|
end
|
||||||
|
|
||||||
|
if retailer_split && retailer_split > 0
|
||||||
|
teacher_dist = TeacherDistribution.create_for_lesson_package_purchase(purchase, false, (retailer_split / 100.0).round(2), retailer_rate)
|
||||||
|
teacher_dist.retailer = teacher.teacher.retailer
|
||||||
|
teacher_dist.save
|
||||||
|
purchase.teacher_distributions << teacher_dist
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
teacher_dist = TeacherDistribution.create_for_lesson_package_purchase(purchase, false)
|
||||||
|
purchase.teacher_distributions << teacher_dist
|
||||||
|
# price should always match the teacher_distribution, if there is one
|
||||||
|
purchase.price = teacher_dist.amount_in_cents / 100
|
||||||
|
|
||||||
|
if lesson_booking.school_on_school_payment?
|
||||||
|
teacher_dist = TeacherDistribution.create_for_lesson_package_purchase(purchase, true)
|
||||||
|
purchase.teacher_distributions << teacher_dist
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -102,10 +102,13 @@ module JamRuby
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def sale_display
|
def sale_display(variant = nil)
|
||||||
name
|
name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def variant_price(variant = nil)
|
||||||
|
price
|
||||||
|
end
|
||||||
def plan_code
|
def plan_code
|
||||||
if package_type == SINGLE_FREE
|
if package_type == SINGLE_FREE
|
||||||
"lesson-package-single-free"
|
"lesson-package-single-free"
|
||||||
|
|
|
||||||
|
|
@ -97,13 +97,17 @@ module JamRuby
|
||||||
end
|
end
|
||||||
|
|
||||||
def teacher_distribution
|
def teacher_distribution
|
||||||
teacher_distributions.where(education:false).first
|
teacher_distributions.where(education:false).where('retailer_id is null').first
|
||||||
end
|
end
|
||||||
|
|
||||||
def education_distribution
|
def education_distribution
|
||||||
teacher_distributions.where(education:true).first
|
teacher_distributions.where(education:true).first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def retailer_distribution
|
||||||
|
teacher_distributions.where('retailer_id is not null').first
|
||||||
|
end
|
||||||
|
|
||||||
def manage_slot_changes
|
def manage_slot_changes
|
||||||
# if this slot changed, we need to update the time. But LessonBooking does this for us, for requested/accepted .
|
# if this slot changed, we need to update the time. But LessonBooking does this for us, for requested/accepted .
|
||||||
# TODO: what to do, what to do.
|
# TODO: what to do, what to do.
|
||||||
|
|
@ -197,6 +201,59 @@ module JamRuby
|
||||||
counterer_id.nil? || counterer_id == student_id
|
counterer_id.nil? || counterer_id == student_id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def mark_lesson(success, administratively_marked = false)
|
||||||
|
self.success = success
|
||||||
|
self.analysed_at = Time.now
|
||||||
|
self.analysed = true
|
||||||
|
self.status = STATUS_COMPLETED
|
||||||
|
self.admin_marked = administratively_marked
|
||||||
|
|
||||||
|
|
||||||
|
if success && lesson_booking.requires_teacher_distribution?(self)
|
||||||
|
|
||||||
|
if lesson_booking.posa_card.nil? && lesson_booking.payment
|
||||||
|
# if there is a payment object, it will describe how everything gets doled out
|
||||||
|
payment = JSON.parse(lesson_booking.payment)
|
||||||
|
|
||||||
|
teacher_split = payment["teacher"]
|
||||||
|
retailer_split = payment["retailer"]
|
||||||
|
|
||||||
|
retailer_rate = teacher.teacher.retailer.jamkazam_rate + APP_CONFIG.stripe[:charge_fee] # add 0.03 to account for stripe deduction
|
||||||
|
|
||||||
|
# but we must also take the correct rate out of the
|
||||||
|
|
||||||
|
if is_test_drive?
|
||||||
|
# test drives do not get subject to split behavior, per quick convo with David
|
||||||
|
self.teacher_distributions << TeacherDistribution.create_for_lesson(self, false)
|
||||||
|
else
|
||||||
|
|
||||||
|
if teacher_split && teacher_split > 0
|
||||||
|
teacher_dist = TeacherDistribution.create_for_lesson(self, false, (teacher_split / 100.0).round(2), retailer_rate)
|
||||||
|
self.teacher_distributions << teacher_dist
|
||||||
|
end
|
||||||
|
|
||||||
|
if retailer_split && retailer_split > 0
|
||||||
|
teacher_dist = TeacherDistribution.create_for_lesson(self, false, (retailer_split / 100.0).round(2), retailer_rate)
|
||||||
|
teacher_dist.retailer = teacher.teacher.retailer
|
||||||
|
teacher_dist.save
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
is_education_school_on_school = lesson_booking.school_on_school_payment?
|
||||||
|
|
||||||
|
self.teacher_distributions << TeacherDistribution.create_for_lesson(self, false)
|
||||||
|
if is_education_school_on_school
|
||||||
|
self.teacher_distributions << TeacherDistribution.create_for_lesson(self, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.save
|
||||||
|
# send out emails appropriate for this type of session
|
||||||
|
session_completed
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def analyse
|
def analyse
|
||||||
if self.analysed
|
if self.analysed
|
||||||
return
|
return
|
||||||
|
|
@ -210,23 +267,8 @@ module JamRuby
|
||||||
# extra protection against bad code somewhere
|
# extra protection against bad code somewhere
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
self.success = analysis[:bill]
|
|
||||||
self.analysed_at = Time.now
|
|
||||||
self.analysed = true
|
|
||||||
|
|
||||||
self.status = STATUS_COMPLETED
|
mark_lesson(analysis[:bill])
|
||||||
|
|
||||||
if success && lesson_booking.requires_teacher_distribution?(self)
|
|
||||||
self.teacher_distributions << TeacherDistribution.create_for_lesson(self, false)
|
|
||||||
if lesson_booking.school_on_school_payment?
|
|
||||||
self.teacher_distributions << TeacherDistribution.create_for_lesson(self, true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.save
|
|
||||||
# send out emails appropriate for this type of session
|
|
||||||
session_completed
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def billed
|
def billed
|
||||||
|
|
|
||||||
|
|
@ -948,6 +948,9 @@ SQL
|
||||||
if scheduled_duration.class == String
|
if scheduled_duration.class == String
|
||||||
duration = scheduled_duration.to_i.seconds
|
duration = scheduled_duration.to_i.seconds
|
||||||
end
|
end
|
||||||
|
if duration == 0
|
||||||
|
duration = 30 * 60
|
||||||
|
end
|
||||||
duration
|
duration
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -996,7 +999,7 @@ SQL
|
||||||
"#{start_time.strftime("%A, %B %e")}, #{start_time.strftime("%l:%M").strip}-#{end_time.strftime("%l:%M %p").strip} #{timezone_display}"
|
"#{start_time.strftime("%A, %B %e")}, #{start_time.strftime("%l:%M").strip}-#{end_time.strftime("%l:%M %p").strip} #{timezone_display}"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
"#{start_time.strftime("%A, %B %e")} - #{start_time.strftime("%l:%M%P").strip}"
|
"#{start_time.strftime("%A, %B %e")} - #{end_time.strftime("%l:%M%P").strip}"
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,10 @@ module JamRuby
|
||||||
sale_display
|
sale_display
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def variant_price(variant = nil)
|
||||||
|
price
|
||||||
|
end
|
||||||
|
|
||||||
def price
|
def price
|
||||||
if card_type == JAM_TRACKS_5
|
if card_type == JAM_TRACKS_5
|
||||||
10.00
|
10.00
|
||||||
|
|
@ -49,7 +53,7 @@ module JamRuby
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def sale_display
|
def sale_display(variant = nil)
|
||||||
if card_type == JAM_TRACKS_5
|
if card_type == JAM_TRACKS_5
|
||||||
'JamTracks Card (5)'
|
'JamTracks Card (5)'
|
||||||
elsif card_type == JAM_TRACKS_10
|
elsif card_type == JAM_TRACKS_10
|
||||||
|
|
|
||||||
|
|
@ -272,7 +272,9 @@ module JamRuby
|
||||||
|
|
||||||
music_session.connections.each do |connection|
|
music_session.connections.each do |connection|
|
||||||
connection.tracks.each do |track|
|
connection.tracks.each do |track|
|
||||||
recording.recorded_tracks << RecordedTrack.create_from_track(track, recording)
|
if connection.client_role != 'child'
|
||||||
|
recording.recorded_tracks << RecordedTrack.create_from_track(track, recording)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
connection.video_sources.each do |video|
|
connection.video_sources.each do |video|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ module JamRuby
|
||||||
has_many :teacher_distributions, class_name: 'JamRuby::TeacherDistribution'
|
has_many :teacher_distributions, class_name: 'JamRuby::TeacherDistribution'
|
||||||
has_many :sales, class_name: 'JamRuby::Sale'
|
has_many :sales, class_name: 'JamRuby::Sale'
|
||||||
has_many :sale_line_items, class_name: 'JamRuby::SaleLineItem'
|
has_many :sale_line_items, class_name: 'JamRuby::SaleLineItem'
|
||||||
|
has_many :lesson_bookings, class_name: 'JamRuby::LessonBooking'
|
||||||
|
|
||||||
validates :user, presence: true
|
validates :user, presence: true
|
||||||
#validates :slug, presence: true
|
#validates :slug, presence: true
|
||||||
|
|
@ -60,11 +61,23 @@ module JamRuby
|
||||||
BCrypt::Password.new(self.encrypted_password) == password
|
BCrypt::Password.new(self.encrypted_password) == password
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update_payment(split)
|
||||||
|
if split[:teacher] && split[:teacher].is_a?(Integer) && split[:retailer] && split[:retailer].is_a?(Integer)
|
||||||
|
teacher_split = split[:teacher]
|
||||||
|
retailer_split = split[:retailer]
|
||||||
|
if (teacher_split >= 0 && teacher_split <= 100) && (retailer_split >= 0 && retailer_split <= 100) && (teacher_split + retailer_split == 100)
|
||||||
|
self.payment = split.to_json
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
def update_from_params(params)
|
def update_from_params(params)
|
||||||
self.name = params[:name] if params[:name].present?
|
self.name = params[:name] if params[:name].present?
|
||||||
self.city = params[:city]
|
self.city = params[:city]
|
||||||
self.state = params[:state]
|
self.state = params[:state]
|
||||||
self.slug = params[:slug] if params[:slug].present?
|
self.slug = params[:slug] if params[:slug].present?
|
||||||
|
if params[:split]
|
||||||
|
self.update_payment(params[:split])
|
||||||
|
end
|
||||||
|
|
||||||
if params[:password].present?
|
if params[:password].present?
|
||||||
self.should_validate_password = true
|
self.should_validate_password = true
|
||||||
|
|
@ -74,6 +87,15 @@ module JamRuby
|
||||||
self.save
|
self.save
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# should be of form {teacher: 0-100, retailer: 0-100}
|
||||||
|
def payment_details
|
||||||
|
if self.payment
|
||||||
|
JSON.parse(self.payment)
|
||||||
|
else
|
||||||
|
{"teacher" =>100, "retailer" => 0}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def owner
|
def owner
|
||||||
user
|
user
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ module JamRuby
|
||||||
|
|
||||||
SOURCE_RECURLY = 'recurly'
|
SOURCE_RECURLY = 'recurly'
|
||||||
SOURCE_IOS = 'ios'
|
SOURCE_IOS = 'ios'
|
||||||
|
SOURCE_PAYPAL = 'paypal'
|
||||||
|
|
||||||
belongs_to :retailer, class_name: 'JamRuby::Retailer'
|
belongs_to :retailer, class_name: 'JamRuby::Retailer'
|
||||||
belongs_to :user, class_name: 'JamRuby::User'
|
belongs_to :user, class_name: 'JamRuby::User'
|
||||||
|
|
@ -117,8 +118,24 @@ module JamRuby
|
||||||
price_info
|
price_info
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.ios_purchase(current_user, jam_track, receipt, price_data)
|
def self.ios_purchase(current_user, jam_track, receipt, price_data, variant)
|
||||||
jam_track_right = nil
|
|
||||||
|
if variant.nil?
|
||||||
|
variant = ShoppingCart::JAMTRACK_STREAM
|
||||||
|
end
|
||||||
|
|
||||||
|
# see if we should bail because we already own these rights
|
||||||
|
jam_track_right = jam_track.right_for_user(current_user)
|
||||||
|
|
||||||
|
if !jam_track_right.nil? && jam_track_right.can_download
|
||||||
|
# if the user already has full rights to the JamTrack, there is nothing else to do in this path
|
||||||
|
return jam_track_right
|
||||||
|
end
|
||||||
|
|
||||||
|
if !jam_track_right.nil? && (!jam_track_right.can_download && variant == ShoppingCart::JAMTRACK_STREAM)
|
||||||
|
# if the user does have the track, but isn't upgrading it, bail
|
||||||
|
return jam_track_right
|
||||||
|
end
|
||||||
|
|
||||||
# everything needs to go into a transaction! If anything goes wrong, we need to raise an exception to break it
|
# everything needs to go into a transaction! If anything goes wrong, we need to raise an exception to break it
|
||||||
Sale.transaction do
|
Sale.transaction do
|
||||||
|
|
@ -157,6 +174,11 @@ module JamRuby
|
||||||
jam_track_right.redeemed = using_free_credit
|
jam_track_right.redeemed = using_free_credit
|
||||||
jam_track_right.version = jam_track.version
|
jam_track_right.version = jam_track.version
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if variant == ShoppingCart::JAMTRACK_DOWNLOAD || variant == ShoppingCart::JAMTRACK_FULL
|
||||||
|
jam_track_right.can_download = true
|
||||||
|
jam_track_right.save
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
jam_track_right
|
jam_track_right
|
||||||
|
|
@ -166,7 +188,7 @@ module JamRuby
|
||||||
# individual subscriptions will end up create their own sale (you can't have N subscriptions in one sale--recurly limitation)
|
# individual subscriptions will end up create their own sale (you can't have N subscriptions in one sale--recurly limitation)
|
||||||
# jamtracks however can be piled onto the same sale as adjustments (VRFS-3028)
|
# jamtracks however can be piled onto the same sale as adjustments (VRFS-3028)
|
||||||
# so this method may create 1 or more sales, , where 2 or more sales can occur if there are more than one subscriptions or subscription + jamtrack
|
# so this method may create 1 or more sales, , where 2 or more sales can occur if there are more than one subscriptions or subscription + jamtrack
|
||||||
def self.place_order(current_user, shopping_carts)
|
def self.place_order(current_user, shopping_carts, paypal = false)
|
||||||
|
|
||||||
sales = []
|
sales = []
|
||||||
|
|
||||||
|
|
@ -176,7 +198,7 @@ module JamRuby
|
||||||
# return sales
|
# return sales
|
||||||
#end
|
#end
|
||||||
|
|
||||||
jam_track_sale = order_jam_tracks(current_user, shopping_carts)
|
jam_track_sale = order_jam_tracks(current_user, shopping_carts, paypal)
|
||||||
sales << jam_track_sale if jam_track_sale
|
sales << jam_track_sale if jam_track_sale
|
||||||
|
|
||||||
# TODO: process shopping_carts_subscriptions
|
# TODO: process shopping_carts_subscriptions
|
||||||
|
|
@ -377,7 +399,7 @@ module JamRuby
|
||||||
|
|
||||||
# this method will either return a valid sale, or throw a RecurlyClientError or ActiveRecord validation error (save! failed)
|
# this method will either return a valid sale, or throw a RecurlyClientError or ActiveRecord validation error (save! failed)
|
||||||
# it may return an nil sale if the JamTrack(s) specified by the shopping carts are already owned
|
# it may return an nil sale if the JamTrack(s) specified by the shopping carts are already owned
|
||||||
def self.order_jam_tracks(current_user, shopping_carts)
|
def self.order_jam_tracks(current_user, shopping_carts, is_paypal)
|
||||||
|
|
||||||
shopping_carts_jam_tracks = []
|
shopping_carts_jam_tracks = []
|
||||||
shopping_carts_subscriptions = []
|
shopping_carts_subscriptions = []
|
||||||
|
|
@ -395,11 +417,9 @@ module JamRuby
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
client = RecurlyClient.new
|
|
||||||
|
|
||||||
sale = nil
|
sale = nil
|
||||||
Sale.transaction do
|
Sale.transaction do
|
||||||
sale = create_jam_track_sale(current_user, SOURCE_RECURLY)
|
sale = create_jam_track_sale(current_user, is_paypal ? SOURCE_PAYPAL : SOURCE_RECURLY)
|
||||||
|
|
||||||
if sale.valid?
|
if sale.valid?
|
||||||
if is_only_freebie(shopping_carts)
|
if is_only_freebie(shopping_carts)
|
||||||
|
|
@ -429,61 +449,142 @@ module JamRuby
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
account = client.get_account(current_user)
|
if is_paypal
|
||||||
if account.present?
|
|
||||||
|
|
||||||
purge_pending_adjustments(account)
|
sale.process_shopping_carts(current_user, shopping_carts)
|
||||||
|
|
||||||
created_adjustments = sale.process_shopping_carts(current_user, shopping_carts, account)
|
paypal_auth = current_user.paypal_auth
|
||||||
|
|
||||||
# now invoice the sale ... almost done
|
@api = PayPal::SDK::Merchant::API.new
|
||||||
|
@get_express_checkout_details = @api.build_get_express_checkout_details({:Token => paypal_auth.token})
|
||||||
|
@response = @api.get_express_checkout_details(@get_express_checkout_details)
|
||||||
|
|
||||||
begin
|
@@log.info("User #{current_user.email}, GetExpressCheckout: #{@response.inspect}")
|
||||||
invoice = account.invoice!
|
|
||||||
sale.recurly_invoice_id = invoice.uuid
|
tax = false
|
||||||
sale.recurly_invoice_number = invoice.invoice_number
|
if @response.Ack == 'Success'
|
||||||
|
payerInfo = @response.GetExpressCheckoutDetailsResponseDetails.PayerInfo
|
||||||
|
if payerInfo.Address && ( payerInfo.Address.Country == 'US' && payerInfo.Address.StateOrProvince == 'TX')
|
||||||
|
# we need to ask for taxes
|
||||||
|
tax = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
tax_rate = tax ? 0.0825 : 0
|
||||||
|
total = current_user.shopping_cart_total.round(2)
|
||||||
|
tax_total = (total * tax_rate).round(2)
|
||||||
|
total = total + tax_total
|
||||||
|
total = total.round(2)
|
||||||
|
|
||||||
|
@do_express_checkout_payment = @api.build_do_express_checkout_payment({
|
||||||
|
:DoExpressCheckoutPaymentRequestDetails => {
|
||||||
|
:PaymentDetails =>
|
||||||
|
[
|
||||||
|
{
|
||||||
|
:OrderTotal => {
|
||||||
|
:currencyID => "USD",
|
||||||
|
:value => total
|
||||||
|
},
|
||||||
|
:PaymentAction => "Sale"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
:Token => paypal_auth.token,
|
||||||
|
:PayerID => paypal_auth.uid, }})
|
||||||
|
@pay_response = @api.do_express_checkout_payment(@do_express_checkout_payment)
|
||||||
|
|
||||||
|
@@log.info("User #{current_user.email}, DoExpressCheckoutPayment: #{@pay_response.inspect}")
|
||||||
|
|
||||||
|
# #<PayPal::SDK::Merchant::DataTypes::DoExpressCheckoutPaymentResponseType:0x007fe511dd9b88
|
||||||
|
# @Timestamp=Sun, 11 Dec 2016 02:09:31 +0000, @Ack="Success",
|
||||||
|
# @CorrelationID="b28faf6bd90d9", @Version="117.0", @Build="000000",
|
||||||
|
# @DoExpressCheckoutPaymentResponseDetails=#<PayPal::SDK::Merchant::DataTypes::DoExpressCheckoutPaymentResponseDetailsType:0x007fe511dd38c8
|
||||||
|
# @Token="EC-7A4606566T700564B",
|
||||||
|
# @PaymentInfo=[#<PayPal::SDK::Merchant::DataTypes::PaymentInfoType:0x007fe511dd3008 @TransactionID="63C410710F2619403", @ParentTransactionID=nil,
|
||||||
|
# @ReceiptID=nil, @TransactionType="express-checkout", @PaymentType="instant", @PaymentDate=Sun, 11 Dec 2016 02:09:31 +0000,
|
||||||
|
# @GrossAmount=#<PayPal::SDK::Merchant::DataTypes::BasicAmountType:0x007fe511dd0c90 @currencyID="USD", @value="1.99">,
|
||||||
|
# @FeeAmount=#<PayPal::SDK::Merchant::DataTypes::BasicAmountType:0x007fe511dd04e8 @currencyID="USD", @value="0.36">,
|
||||||
|
# @TaxAmount=#<PayPal::SDK::Merchant::DataTypes::BasicAmountType:0x007fe511dcbe98 @currencyID="USD", @value="0.00">,
|
||||||
|
# @ExchangeRate=nil, @PaymentStatus="Completed", @PendingReason="none", @ReasonCode="none", @ProtectionEligibility="Eligible",
|
||||||
|
# @ProtectionEligibilityType="ItemNotReceivedEligible,UnauthorizedPaymentEligible", @SellerDetails=#<PayPal::SDK::Merchant::DataTypes::SellerDetailsType:0x007fe511dcb358 @SecureMerchantAccountID="6MB486RSBRMJ2">>],
|
||||||
|
# @SuccessPageRedirectRequested="false", @CoupledPaymentInfo=[#<PayPal::SDK::Merchant::DataTypes::CoupledPaymentInfoType:0x007fe511dca7a0>]>>
|
||||||
|
|
||||||
|
if @pay_response.Ack == 'Success'
|
||||||
|
details = @pay_response.DoExpressCheckoutPaymentResponseDetails.PaymentInfo[0]
|
||||||
|
sale.recurly_invoice_id = details.TransactionID
|
||||||
|
sale.recurly_invoice_number = details.ReceiptID
|
||||||
|
|
||||||
# now slap in all the real tax/purchase totals
|
# now slap in all the real tax/purchase totals
|
||||||
sale.recurly_subtotal_in_cents = invoice.subtotal_in_cents
|
sale.recurly_subtotal_in_cents = ((details.GrossAmount.value.to_f - details.TaxAmount.value.to_f) * 100).to_i
|
||||||
sale.recurly_tax_in_cents = invoice.tax_in_cents
|
sale.recurly_tax_in_cents = (details.TaxAmount.value.to_f * 100).to_i
|
||||||
sale.recurly_total_in_cents = invoice.total_in_cents
|
sale.recurly_total_in_cents = (details.GrossAmount.value.to_f * 100).to_i
|
||||||
sale.recurly_currency = invoice.currency
|
sale.recurly_currency = details.GrossAmount.currencyID
|
||||||
|
|
||||||
# and resolve against sale_line_items
|
|
||||||
sale.sale_line_items.each do |sale_line_item|
|
|
||||||
found_line_item = false
|
|
||||||
invoice.line_items.each do |line_item|
|
|
||||||
if line_item.uuid == sale_line_item.recurly_adjustment_uuid
|
|
||||||
sale_line_item.recurly_tax_in_cents = line_item.tax_in_cents
|
|
||||||
sale_line_item.recurly_total_in_cents =line_item.total_in_cents
|
|
||||||
sale_line_item.recurly_currency = line_item.currency
|
|
||||||
sale_line_item.recurly_discount_in_cents = line_item.discount_in_cents
|
|
||||||
found_line_item = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
if !found_line_item
|
|
||||||
@@log.error("can't find line item #{sale_line_item.recurly_adjustment_uuid}")
|
|
||||||
puts "CANT FIND LINE ITEM"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
unless sale.save
|
unless sale.save
|
||||||
puts "WTF"
|
puts "Invalid sale (at end)."
|
||||||
raise RecurlyClientError, "Invalid sale (at end)."
|
raise PayPalClientError, "Invalid sale (at end)."
|
||||||
end
|
end
|
||||||
rescue Recurly::Resource::Invalid => e
|
else
|
||||||
# this exception is thrown by invoice! if the invoice is invalid
|
@@log.error("User #{current_user.email}, DoExpressCheckoutPayment: #{@pay_response.inspect}")
|
||||||
sale.rollback_adjustments(current_user, created_adjustments)
|
raise PayPalClientError, @pay_response.Errors[0].LongMessage
|
||||||
sale = nil
|
|
||||||
raise ActiveRecord::Rollback # kill all db activity, but don't break outside logic
|
|
||||||
rescue => e
|
|
||||||
puts "UNKNOWN E #{e}"
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
raise RecurlyClientError, "Could not find account to place order."
|
client = RecurlyClient.new
|
||||||
|
account = client.get_account(current_user)
|
||||||
|
if account.present?
|
||||||
|
|
||||||
|
purge_pending_adjustments(account)
|
||||||
|
|
||||||
|
created_adjustments = sale.process_shopping_carts(current_user, shopping_carts, account)
|
||||||
|
|
||||||
|
# now invoice the sale ... almost done
|
||||||
|
|
||||||
|
begin
|
||||||
|
invoice = account.invoice!
|
||||||
|
sale.recurly_invoice_id = invoice.uuid
|
||||||
|
sale.recurly_invoice_number = invoice.invoice_number
|
||||||
|
|
||||||
|
# now slap in all the real tax/purchase totals
|
||||||
|
sale.recurly_subtotal_in_cents = invoice.subtotal_in_cents
|
||||||
|
sale.recurly_tax_in_cents = invoice.tax_in_cents
|
||||||
|
sale.recurly_total_in_cents = invoice.total_in_cents
|
||||||
|
sale.recurly_currency = invoice.currency
|
||||||
|
|
||||||
|
# and resolve against sale_line_items
|
||||||
|
sale.sale_line_items.each do |sale_line_item|
|
||||||
|
found_line_item = false
|
||||||
|
invoice.line_items.each do |line_item|
|
||||||
|
if line_item.uuid == sale_line_item.recurly_adjustment_uuid
|
||||||
|
sale_line_item.recurly_tax_in_cents = line_item.tax_in_cents
|
||||||
|
sale_line_item.recurly_total_in_cents =line_item.total_in_cents
|
||||||
|
sale_line_item.recurly_currency = line_item.currency
|
||||||
|
sale_line_item.recurly_discount_in_cents = line_item.discount_in_cents
|
||||||
|
found_line_item = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
if !found_line_item
|
||||||
|
@@log.error("can't find line item #{sale_line_item.recurly_adjustment_uuid}")
|
||||||
|
puts "CANT FIND LINE ITEM"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
unless sale.save
|
||||||
|
puts "Invalid sale (at end)."
|
||||||
|
raise RecurlyClientError, "Invalid sale (at end)."
|
||||||
|
end
|
||||||
|
rescue Recurly::Resource::Invalid => e
|
||||||
|
# this exception is thrown by invoice! if the invoice is invalid
|
||||||
|
sale.rollback_adjustments(current_user, created_adjustments)
|
||||||
|
sale = nil
|
||||||
|
raise ActiveRecord::Rollback # kill all db activity, but don't break outside logic
|
||||||
|
rescue => e
|
||||||
|
puts "UNKNOWN E #{e}"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise RecurlyClientError, "Could not find account to place order."
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
|
@ -493,7 +594,7 @@ module JamRuby
|
||||||
sale
|
sale
|
||||||
end
|
end
|
||||||
|
|
||||||
def process_shopping_carts(current_user, shopping_carts, account)
|
def process_shopping_carts(current_user, shopping_carts, account = nil)
|
||||||
|
|
||||||
created_adjustments = []
|
created_adjustments = []
|
||||||
|
|
||||||
|
|
@ -515,7 +616,7 @@ module JamRuby
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def process_shopping_cart(current_user, shopping_cart, account, created_adjustments)
|
def process_shopping_cart(current_user, shopping_cart, recurly_account, created_adjustments)
|
||||||
recurly_adjustment_uuid = nil
|
recurly_adjustment_uuid = nil
|
||||||
recurly_adjustment_credit_uuid = nil
|
recurly_adjustment_credit_uuid = nil
|
||||||
|
|
||||||
|
|
@ -527,7 +628,8 @@ module JamRuby
|
||||||
|
|
||||||
if shopping_cart.is_jam_track?
|
if shopping_cart.is_jam_track?
|
||||||
jam_track = cart_product
|
jam_track = cart_product
|
||||||
if jam_track.right_for_user(current_user)
|
|
||||||
|
if jam_track.right_for_user(current_user, shopping_cart.variant)
|
||||||
# if the user already owns the JamTrack, we should just skip this cart item, and destroy it
|
# if the user already owns the JamTrack, we should just skip this cart item, and destroy it
|
||||||
# if this occurs, we have to reload every shopping_cart as we iterate. so, we do at the top of the loop
|
# if this occurs, we have to reload every shopping_cart as we iterate. so, we do at the top of the loop
|
||||||
ShoppingCart.remove_jam_track_from_cart(current_user, shopping_cart)
|
ShoppingCart.remove_jam_track_from_cart(current_user, shopping_cart)
|
||||||
|
|
@ -536,14 +638,14 @@ module JamRuby
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
if account
|
if recurly_account
|
||||||
# ask the shopping cart to create the correct Recurly adjustment attributes for a JamTrack
|
# ask the shopping cart to create the correct Recurly adjustment attributes for a JamTrack
|
||||||
adjustments = shopping_cart.create_adjustment_attributes(current_user)
|
adjustments = shopping_cart.create_adjustment_attributes(current_user)
|
||||||
|
|
||||||
adjustments.each do |adjustment|
|
adjustments.each do |adjustment|
|
||||||
|
|
||||||
# create the adjustment at Recurly (this may not look like it, but it is a REST API)
|
# create the adjustment at Recurly (this may not look like it, but it is a REST API)
|
||||||
created_adjustment = account.adjustments.new(adjustment)
|
created_adjustment = recurly_account.adjustments.new(adjustment)
|
||||||
created_adjustment.save
|
created_adjustment.save
|
||||||
|
|
||||||
# if the adjustment could not be made, bail
|
# if the adjustment could not be made, bail
|
||||||
|
|
@ -583,6 +685,13 @@ module JamRuby
|
||||||
jam_track_right.version = jam_track.version
|
jam_track_right.version = jam_track.version
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# deal with variant behavior
|
||||||
|
if shopping_cart.purchasing_downloadable_rights?
|
||||||
|
jam_track_right.can_download = true
|
||||||
|
jam_track_right.save
|
||||||
|
end
|
||||||
|
|
||||||
# also if the purchase was a free one, then:
|
# also if the purchase was a free one, then:
|
||||||
# first, mark the free has_redeemable_jamtrack field if that's still true
|
# first, mark the free has_redeemable_jamtrack field if that's still true
|
||||||
# and if still they have more free things, then redeem the giftable_jamtracks
|
# and if still they have more free things, then redeem the giftable_jamtracks
|
||||||
|
|
|
||||||
|
|
@ -175,6 +175,7 @@ module JamRuby
|
||||||
|
|
||||||
sale_line_item = SaleLineItem.new
|
sale_line_item = SaleLineItem.new
|
||||||
sale_line_item.product_type = shopping_cart.cart_type
|
sale_line_item.product_type = shopping_cart.cart_type
|
||||||
|
sale_line_item.variant = shopping_cart.variant
|
||||||
sale_line_item.unit_price = product_info[:price]
|
sale_line_item.unit_price = product_info[:price]
|
||||||
sale_line_item.quantity = product_info[:quantity]
|
sale_line_item.quantity = product_info[:quantity]
|
||||||
sale_line_item.free = product_info[:marked_for_redeem]
|
sale_line_item.free = product_info[:marked_for_redeem]
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,11 @@ module JamRuby
|
||||||
|
|
||||||
PURCHASE_REASONS = [PURCHASE_NORMAL, PURCHASE_FREE, PURCHASE_FREE_CREDIT]
|
PURCHASE_REASONS = [PURCHASE_NORMAL, PURCHASE_FREE, PURCHASE_FREE_CREDIT]
|
||||||
|
|
||||||
|
JAMTRACK_FULL = 'full'
|
||||||
|
JAMTRACK_STREAM = 'stream'
|
||||||
|
JAMTRACK_DOWNLOAD = 'download'
|
||||||
|
JAMTRACK_VARIANTS = ['full', 'stream', 'download']
|
||||||
|
|
||||||
attr_accessible :quantity, :cart_type, :product_info
|
attr_accessible :quantity, :cart_type, :product_info
|
||||||
|
|
||||||
attr_accessor :skip_mix_check
|
attr_accessor :skip_mix_check
|
||||||
|
|
@ -22,13 +27,15 @@ module JamRuby
|
||||||
validates :cart_type, presence: true
|
validates :cart_type, presence: true
|
||||||
validates :cart_class_name, presence: true
|
validates :cart_class_name, presence: true
|
||||||
validates :marked_for_redeem, numericality: {only_integer: true}
|
validates :marked_for_redeem, numericality: {only_integer: true}
|
||||||
|
validates :variant, inclusion: {in: [nil, JAMTRACK_FULL, JAMTRACK_STREAM, JAMTRACK_DOWNLOAD]}
|
||||||
|
|
||||||
#validate :not_mixed
|
#validate :not_mixed
|
||||||
|
|
||||||
default_scope { order('created_at DESC') }
|
default_scope { order('created_at DESC') }
|
||||||
|
|
||||||
def product_info(instance = nil)
|
def product_info(instance = nil)
|
||||||
product = self.cart_product
|
product = self.cart_product
|
||||||
data = {type: cart_type, name: product.name, price: product.price, product_id: cart_id, plan_code: product.plan_code, real_price: real_price(product), total_price: total_price(product), quantity: quantity, marked_for_redeem: marked_for_redeem, free: free?, sales_region: product.sales_region, sale_display:product.sale_display, allow_free: allow_free(product)} unless product.nil?
|
data = {type: cart_type, name: product.name, price: product.variant_price(variant), product_id: cart_id, plan_code: product.plan_code, real_price: real_price(product), total_price: total_price(product), quantity: quantity, marked_for_redeem: marked_for_redeem, free: free?, sales_region: product.sales_region, sale_display:product.sale_display(variant), allow_free: allow_free(product)} unless product.nil?
|
||||||
if data && instance
|
if data && instance
|
||||||
data.merge!(instance.product_info)
|
data.merge!(instance.product_info)
|
||||||
end
|
end
|
||||||
|
|
@ -37,12 +44,16 @@ module JamRuby
|
||||||
|
|
||||||
# multiply quantity by price
|
# multiply quantity by price
|
||||||
def total_price(product)
|
def total_price(product)
|
||||||
quantity * product.price
|
quantity * product.variant_price(variant)
|
||||||
|
end
|
||||||
|
|
||||||
|
def purchasing_downloadable_rights?
|
||||||
|
is_jam_track? && (variant == ShoppingCart::JAMTRACK_DOWNLOAD || variant == ShoppingCart::JAMTRACK_FULL)
|
||||||
end
|
end
|
||||||
|
|
||||||
# multiply (quantity - redeemable) by price
|
# multiply (quantity - redeemable) by price
|
||||||
def real_price(product)
|
def real_price(product)
|
||||||
(quantity - marked_for_redeem) * product.price
|
(quantity - marked_for_redeem) * product.variant_price(variant)
|
||||||
end
|
end
|
||||||
|
|
||||||
def allow_free(product)
|
def allow_free(product)
|
||||||
|
|
@ -101,7 +112,7 @@ module JamRuby
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.create user, product, quantity = 1, mark_redeem = false
|
def self.create(user, product, quantity = 1, mark_redeem = false, variant = nil)
|
||||||
|
|
||||||
cart = ShoppingCart.new
|
cart = ShoppingCart.new
|
||||||
if user.is_a?(User)
|
if user.is_a?(User)
|
||||||
|
|
@ -111,6 +122,14 @@ module JamRuby
|
||||||
end
|
end
|
||||||
|
|
||||||
cart.cart_type = product.class::PRODUCT_TYPE
|
cart.cart_type = product.class::PRODUCT_TYPE
|
||||||
|
|
||||||
|
if cart.cart_type == JamTrack::PRODUCT_TYPE && variant.nil?
|
||||||
|
cart.variant = JAMTRACK_STREAM # default to jamtrack 'stream'
|
||||||
|
else
|
||||||
|
cart.variant = variant
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
cart.cart_class_name = product.class.name
|
cart.cart_class_name = product.class.name
|
||||||
cart.cart_id = product.id
|
cart.cart_id = product.id
|
||||||
cart.quantity = quantity
|
cart.quantity = quantity
|
||||||
|
|
@ -158,9 +177,9 @@ module JamRuby
|
||||||
shopping_carts.each do |shopping_cart|
|
shopping_carts.each do |shopping_cart|
|
||||||
if shopping_cart.is_jam_track?
|
if shopping_cart.is_jam_track?
|
||||||
mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(user)
|
mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(user)
|
||||||
cart = ShoppingCart.create(user, shopping_cart.cart_product, shopping_cart.quantity, mark_redeem)
|
cart = ShoppingCart.create(user, shopping_cart.cart_product, shopping_cart.quantity, mark_redeem, shopping_cart.variant)
|
||||||
else
|
else
|
||||||
cart = ShoppingCart.create(user, shopping_cart.cart_product, shopping_cart.quantity, false)
|
cart = ShoppingCart.create(user, shopping_cart.cart_product, shopping_cart.quantity, false, shopping_cart.variant)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
@ -201,8 +220,13 @@ module JamRuby
|
||||||
end
|
end
|
||||||
|
|
||||||
# adds a jam_track to cart, checking for promotions
|
# adds a jam_track to cart, checking for promotions
|
||||||
def self.add_jam_track_to_cart(any_user, jam_track, clear:false)
|
def self.add_jam_track_to_cart(any_user, jam_track, variant = JAMTRACK_FULL)
|
||||||
cart = nil
|
cart = nil
|
||||||
|
|
||||||
|
if variant.nil?
|
||||||
|
variant = JAMTRACK_FULL
|
||||||
|
end
|
||||||
|
|
||||||
ShoppingCart.transaction do
|
ShoppingCart.transaction do
|
||||||
|
|
||||||
# if clear
|
# if clear
|
||||||
|
|
@ -213,7 +237,7 @@ module JamRuby
|
||||||
end
|
end
|
||||||
|
|
||||||
mark_redeem = jam_track.allow_free ? ShoppingCart.user_has_redeemable_jam_track?(any_user) : false
|
mark_redeem = jam_track.allow_free ? ShoppingCart.user_has_redeemable_jam_track?(any_user) : false
|
||||||
cart = ShoppingCart.create(any_user, jam_track, 1, mark_redeem)
|
cart = ShoppingCart.create(any_user, jam_track, 1, mark_redeem, variant)
|
||||||
end
|
end
|
||||||
any_user.reload
|
any_user.reload
|
||||||
cart
|
cart
|
||||||
|
|
|
||||||
|
|
@ -43,26 +43,35 @@ module JamRuby
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.create_for_lesson(lesson_session, for_education)
|
def self.create_for_lesson(lesson_session, for_education, split = nil, fee_rate = nil)
|
||||||
distribution = create(lesson_session, for_education)
|
distribution = create(lesson_session, for_education, split, fee_rate)
|
||||||
distribution.lesson_session = lesson_session
|
distribution.lesson_session = lesson_session
|
||||||
distribution.education = for_education
|
distribution.education = for_education
|
||||||
|
|
||||||
|
# lock down the teacher_fee_in_cents
|
||||||
|
distribution.teacher_fee_in_cents = distribution.calculate_teacher_fee(split, fee_rate)
|
||||||
|
|
||||||
distribution
|
distribution
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.create_for_lesson_package_purchase(lesson_package_purchase, for_education)
|
def self.create_for_lesson_package_purchase(lesson_package_purchase, for_education, split = nil, fee_rate = nil)
|
||||||
distribution = create(lesson_package_purchase, for_education)
|
distribution = create(lesson_package_purchase, for_education, split, fee_rate)
|
||||||
distribution.lesson_package_purchase = lesson_package_purchase
|
distribution.lesson_package_purchase = lesson_package_purchase
|
||||||
distribution.education = for_education
|
distribution.education = for_education
|
||||||
|
|
||||||
|
|
||||||
|
# lock down the teacher_fee_in_cents
|
||||||
|
distribution.teacher_fee_in_cents = distribution.calculate_teacher_fee(split, fee_rate)
|
||||||
|
|
||||||
distribution
|
distribution
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.create(target, education)
|
def self.create(target, education, split, fee_rate)
|
||||||
distribution = TeacherDistribution.new
|
distribution = TeacherDistribution.new
|
||||||
distribution.teacher = target.teacher
|
distribution.teacher = target.teacher
|
||||||
distribution.ready = false
|
distribution.ready = false
|
||||||
distribution.distributed = false
|
distribution.distributed = false
|
||||||
distribution.amount_in_cents = target.lesson_booking.distribution_price_in_cents(target, education)
|
distribution.amount_in_cents = target.lesson_booking.distribution_price_in_cents(target, education, split, fee_rate)
|
||||||
distribution.school = target.lesson_booking.school
|
distribution.school = target.lesson_booking.school
|
||||||
distribution
|
distribution
|
||||||
end
|
end
|
||||||
|
|
@ -111,23 +120,34 @@ module JamRuby
|
||||||
(jamkazam_margin_in_cents / 100).round(2)
|
(jamkazam_margin_in_cents / 100).round(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
def calculate_teacher_fee
|
def calculate_teacher_fee(split = nil, fee_rate = nil)
|
||||||
if education
|
if teacher_fee_in_cents
|
||||||
0
|
return teacher_fee_in_cents
|
||||||
else
|
else
|
||||||
if is_test_drive?
|
if education
|
||||||
0
|
0
|
||||||
else
|
else
|
||||||
if school
|
if is_test_drive?
|
||||||
# if school exists, use it's rate
|
0
|
||||||
rate = school.jamkazam_rate
|
|
||||||
else
|
else
|
||||||
# otherwise use the teacher's rate
|
|
||||||
rate = teacher.teacher.jamkazam_rate
|
if fee_rate
|
||||||
|
rate = (fee_rate * split) # charge_Fee is already handled elsewhere
|
||||||
|
else
|
||||||
|
if school
|
||||||
|
# if school exists, use it's rate
|
||||||
|
rate = school.jamkazam_rate + APP_CONFIG.stripe[:charge_fee]
|
||||||
|
else
|
||||||
|
# otherwise use the teacher's rate
|
||||||
|
rate = teacher.teacher.jamkazam_rate + APP_CONFIG.stripe[:charge_fee]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
(amount_in_cents * rate).round
|
||||||
end
|
end
|
||||||
(amount_in_cents * (rate + 0.03)).round # 0.03 is stripe fee that we include in cost of JK fee
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def student
|
def student
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ module JamRuby
|
||||||
payment.amount_in_cents = payment.teacher_distribution.amount_in_cents
|
payment.amount_in_cents = payment.teacher_distribution.amount_in_cents
|
||||||
payment.fee_in_cents = payment.teacher_distribution.calculate_teacher_fee
|
payment.fee_in_cents = payment.teacher_distribution.calculate_teacher_fee
|
||||||
|
|
||||||
effective_in_cents = payment.amount_in_cents - payment.fee_in_cents
|
effective_in_cents = payment.real_distribution_in_cents
|
||||||
|
|
||||||
if payment.teacher_payment_charge.nil?
|
if payment.teacher_payment_charge.nil?
|
||||||
charge = TeacherPaymentCharge.new
|
charge = TeacherPaymentCharge.new
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,8 @@ module JamRuby
|
||||||
:currency => "usd",
|
:currency => "usd",
|
||||||
:customer => APP_CONFIG.stripe[:source_customer],
|
:customer => APP_CONFIG.stripe[:source_customer],
|
||||||
:description => construct_description,
|
:description => construct_description,
|
||||||
:metadata => metadata
|
:metadata => metadata,
|
||||||
|
:destination => teacher.teacher.stripe_account_id
|
||||||
)
|
)
|
||||||
|
|
||||||
stripe_charge
|
stripe_charge
|
||||||
|
|
|
||||||
|
|
@ -1912,6 +1912,13 @@ module JamRuby
|
||||||
stats
|
stats
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def shopping_cart_total
|
||||||
|
total = 0
|
||||||
|
shopping_carts.each do |shopping_cart|
|
||||||
|
total += shopping_cart.product_info[:total_price]
|
||||||
|
end
|
||||||
|
total
|
||||||
|
end
|
||||||
def destroy_all_shopping_carts
|
def destroy_all_shopping_carts
|
||||||
ShoppingCart.where("user_id=?", self).destroy_all
|
ShoppingCart.where("user_id=?", self).destroy_all
|
||||||
end
|
end
|
||||||
|
|
@ -2053,6 +2060,15 @@ module JamRuby
|
||||||
user_authorizations.where(provider: "stripe_connect").first
|
user_authorizations.where(provider: "stripe_connect").first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def paypal_auth
|
||||||
|
user_authorizations.where(provider: 'paypal').first
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_paypal_auth?
|
||||||
|
auth = paypal_auth
|
||||||
|
auth && (!auth.token_expiration || auth.token_expiration > Time.now)
|
||||||
|
end
|
||||||
|
|
||||||
def has_stripe_connect?
|
def has_stripe_connect?
|
||||||
auth = stripe_auth
|
auth = stripe_auth
|
||||||
auth && (!auth.token_expiration || auth.token_expiration > Time.now)
|
auth && (!auth.token_expiration || auth.token_expiration > Time.now)
|
||||||
|
|
|
||||||
|
|
@ -784,6 +784,7 @@ FactoryGirl.define do
|
||||||
sequence(:publisher) { |n| "publisher-#{n}" }
|
sequence(:publisher) { |n| "publisher-#{n}" }
|
||||||
sales_region 'United States'
|
sales_region 'United States'
|
||||||
price 1.99
|
price 1.99
|
||||||
|
download_price 4.99
|
||||||
reproduction_royalty true
|
reproduction_royalty true
|
||||||
public_performance_royalty true
|
public_performance_royalty true
|
||||||
reproduction_royalty_amount 0.999
|
reproduction_royalty_amount 0.999
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ describe "Monthly Recurring Lesson Flow" do
|
||||||
teacher.stripe_account_id = stripe_account1_id
|
teacher.stripe_account_id = stripe_account1_id
|
||||||
teacher.save!
|
teacher.save!
|
||||||
}
|
}
|
||||||
it "works" do
|
it "works normal" do
|
||||||
|
|
||||||
# if it's later in the month, we'll make 2 lesson_package_purchases (prorated one, and next month's), which can throw off some assertions later on
|
# if it's later in the month, we'll make 2 lesson_package_purchases (prorated one, and next month's), which can throw off some assertions later on
|
||||||
Timecop.travel(Date.new(2016, 3, 20))
|
Timecop.travel(Date.new(2016, 3, 20))
|
||||||
|
|
@ -173,7 +173,7 @@ describe "Monthly Recurring Lesson Flow" do
|
||||||
|
|
||||||
booked_price = booking.booked_price
|
booked_price = booking.booked_price
|
||||||
prorated = booked_price / 2
|
prorated = booked_price / 2
|
||||||
prorated_cents = (booked_price * 100).to_i
|
prorated_cents = (prorated * 100).to_i
|
||||||
user.reload
|
user.reload
|
||||||
user.lesson_purchases.length.should eql 1
|
user.lesson_purchases.length.should eql 1
|
||||||
lesson_purchase = user.lesson_purchases[0]
|
lesson_purchase = user.lesson_purchases[0]
|
||||||
|
|
@ -205,10 +205,10 @@ describe "Monthly Recurring Lesson Flow" do
|
||||||
teacher_distribution.distributed.should be_true
|
teacher_distribution.distributed.should be_true
|
||||||
TeacherPayment.count.should eql 1
|
TeacherPayment.count.should eql 1
|
||||||
payment = TeacherPayment.first
|
payment = TeacherPayment.first
|
||||||
payment.amount_in_cents.should eql 3000
|
payment.amount_in_cents.should eql prorated_cents
|
||||||
payment.fee_in_cents.should eql (3000 * 0.28).round
|
payment.fee_in_cents.should eql (prorated_cents * 0.28).round
|
||||||
payment.teacher_payment_charge.amount_in_cents.should eql (3000 + 3000 * APP_CONFIG.stripe[:ach_pct]).round
|
payment.teacher_payment_charge.amount_in_cents.should eql (payment.real_distribution_in_cents + payment.real_distribution_in_cents * APP_CONFIG.stripe[:ach_pct]).round
|
||||||
payment.teacher_payment_charge.fee_in_cents.should eql (3000 * 0.28).round
|
payment.teacher_payment_charge.fee_in_cents.should eql (prorated_cents * 0.28).round
|
||||||
payment.teacher.should eql teacher_user
|
payment.teacher.should eql teacher_user
|
||||||
payment.teacher_distribution.should eql teacher_distribution
|
payment.teacher_distribution.should eql teacher_distribution
|
||||||
|
|
||||||
|
|
@ -405,7 +405,7 @@ describe "Monthly Recurring Lesson Flow" do
|
||||||
|
|
||||||
booked_price = booking.booked_price
|
booked_price = booking.booked_price
|
||||||
prorated = booked_price / 2
|
prorated = booked_price / 2
|
||||||
prorated_cents = (booked_price * 100).to_i
|
prorated_cents = (prorated * 100).to_i
|
||||||
user.reload
|
user.reload
|
||||||
user.lesson_purchases.length.should eql 1
|
user.lesson_purchases.length.should eql 1
|
||||||
lesson_purchase = user.lesson_purchases[0]
|
lesson_purchase = user.lesson_purchases[0]
|
||||||
|
|
@ -441,16 +441,16 @@ describe "Monthly Recurring Lesson Flow" do
|
||||||
teacher_distribution.distributed.should be_true
|
teacher_distribution.distributed.should be_true
|
||||||
TeacherPayment.count.should eql 2
|
TeacherPayment.count.should eql 2
|
||||||
payment = teacher_distribution.teacher_payment
|
payment = teacher_distribution.teacher_payment
|
||||||
payment.amount_in_cents.should eql 3000
|
payment.amount_in_cents.should eql prorated_cents
|
||||||
payment.fee_in_cents.should eql (3000 * 0.28).round
|
payment.fee_in_cents.should eql (prorated_cents * 0.28).round
|
||||||
payment.teacher_payment_charge.amount_in_cents.should eql (3000 + 3000 * APP_CONFIG.stripe[:ach_pct]).round
|
payment.teacher_payment_charge.amount_in_cents.should eql (payment.real_distribution_in_cents + payment.real_distribution_in_cents * APP_CONFIG.stripe[:ach_pct]).round
|
||||||
payment.teacher_payment_charge.fee_in_cents.should eql (3000 * 0.28).round
|
payment.teacher_payment_charge.fee_in_cents.should eql (prorated_cents * 0.28).round
|
||||||
payment.teacher.should eql teacher_user
|
payment.teacher.should eql teacher_user
|
||||||
payment.teacher_distribution.should eql teacher_distribution
|
payment.teacher_distribution.should eql teacher_distribution
|
||||||
education_distribution.reload
|
education_distribution.reload
|
||||||
education_distribution.distributed.should be_true
|
education_distribution.distributed.should be_true
|
||||||
|
|
||||||
education_amt = (3000 * 0.0625).round
|
education_amt = (prorated_cents * 0.0625).round
|
||||||
payment = education_distribution.teacher_payment
|
payment = education_distribution.teacher_payment
|
||||||
payment.amount_in_cents.should eql education_amt
|
payment.amount_in_cents.should eql education_amt
|
||||||
payment.fee_in_cents.should eql 0
|
payment.fee_in_cents.should eql 0
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ describe "Normal Lesson Flow" do
|
||||||
let(:affiliate_partner) { FactoryGirl.create(:affiliate_partner) }
|
let(:affiliate_partner) { FactoryGirl.create(:affiliate_partner) }
|
||||||
let(:affiliate_partner2) { FactoryGirl.create(:affiliate_partner, lesson_rate: 0.30) }
|
let(:affiliate_partner2) { FactoryGirl.create(:affiliate_partner, lesson_rate: 0.30) }
|
||||||
let(:school) {FactoryGirl.create(:school)}
|
let(:school) {FactoryGirl.create(:school)}
|
||||||
|
let(:retailer) {FactoryGirl.create(:retailer)}
|
||||||
|
|
||||||
after {Timecop.return}
|
after {Timecop.return}
|
||||||
|
|
||||||
|
|
@ -459,6 +460,7 @@ describe "Normal Lesson Flow" do
|
||||||
booking.card_presumed_ok.should be_false
|
booking.card_presumed_ok.should be_false
|
||||||
booking.user.should eql user
|
booking.user.should eql user
|
||||||
user.unprocessed_normal_lesson.should be_nil
|
user.unprocessed_normal_lesson.should be_nil
|
||||||
|
booking.same_school_free.should be_true
|
||||||
booking.sent_notices.should be_true
|
booking.sent_notices.should be_true
|
||||||
booking.booked_price.should eql 30.00
|
booking.booked_price.should eql 30.00
|
||||||
booking.is_requested?.should be_true
|
booking.is_requested?.should be_true
|
||||||
|
|
@ -609,8 +611,8 @@ describe "Normal Lesson Flow" do
|
||||||
booking.school.should be_true
|
booking.school.should be_true
|
||||||
booking.card_presumed_ok.should be_false
|
booking.card_presumed_ok.should be_false
|
||||||
booking.user.should eql user
|
booking.user.should eql user
|
||||||
booking.same_school_free.should be_true
|
booking.same_school_free.should be_false
|
||||||
user.unprocessed_normal_lesson.should be_nil
|
#user.unprocessed_normal_lesson.should be_nil
|
||||||
booking.sent_notices.should be_false
|
booking.sent_notices.should be_false
|
||||||
booking.booked_price.should eql 30.00
|
booking.booked_price.should eql 30.00
|
||||||
booking.is_requested?.should be_true
|
booking.is_requested?.should be_true
|
||||||
|
|
@ -793,15 +795,236 @@ describe "Normal Lesson Flow" do
|
||||||
payment = teacher_distribution.teacher_payment
|
payment = teacher_distribution.teacher_payment
|
||||||
payment.amount_in_cents.should eql 3000
|
payment.amount_in_cents.should eql 3000
|
||||||
payment.fee_in_cents.should eql (3000 * 0.28).round
|
payment.fee_in_cents.should eql (3000 * 0.28).round
|
||||||
payment.teacher_payment_charge.amount_in_cents.should eql (3000 + 3000 * APP_CONFIG.stripe[:ach_pct]).round
|
payment.teacher_payment_charge.amount_in_cents.should eql ((teacher_distribution.amount_in_cents - teacher_distribution.teacher_fee_in_cents) + (teacher_distribution.amount_in_cents - teacher_distribution.teacher_fee_in_cents) * APP_CONFIG.stripe[:ach_pct]).round
|
||||||
payment.teacher_payment_charge.fee_in_cents.should eql (3000 * 0.28).round
|
payment.teacher_payment_charge.fee_in_cents.should eql payment.teacher_payment_charge.fee_in_cents
|
||||||
payment.teacher.should eql teacher_user
|
payment.teacher.should eql teacher_user
|
||||||
payment.teacher_distribution.should eql teacher_distribution
|
payment.teacher_distribution.should eql teacher_distribution
|
||||||
lesson_session.lesson_booking.status.should eql LessonBooking::STATUS_COMPLETED
|
lesson_session.lesson_booking.status.should eql LessonBooking::STATUS_COMPLETED
|
||||||
lesson_session.lesson_booking.success.should be_true
|
lesson_session.lesson_booking.success.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "works (retailer on retailer)" do
|
||||||
|
|
||||||
|
# make sure teacher can get payments
|
||||||
|
teacher.stripe_account_id = stripe_account1_id
|
||||||
|
retailer.user.stripe_account_id = stripe_account2_id
|
||||||
|
|
||||||
|
# make sure can get stripe payments
|
||||||
|
|
||||||
|
# get user and teacher into same retailer
|
||||||
|
|
||||||
|
teacher_split = 70
|
||||||
|
retailer_split = 30
|
||||||
|
teacher_split_pct = (teacher_split / 100.0)
|
||||||
|
retailer_split_pct = (retailer_split / 100.0)
|
||||||
|
retailer.update_payment({teacher: teacher_split, retailer:retailer_split})
|
||||||
|
retailer.save!
|
||||||
|
|
||||||
|
user.affiliate_referral = retailer.affiliate_partner
|
||||||
|
user.save!
|
||||||
|
teacher.retailer = retailer
|
||||||
|
teacher.save!
|
||||||
|
|
||||||
|
# user has no test drives, no credit card on file, but attempts to book a lesson
|
||||||
|
booking = LessonBooking.book_normal(user, teacher_user, valid_single_slots, "Hey I've heard of you before.", false, LessonBooking::PAYMENT_STYLE_SINGLE, 60)
|
||||||
|
booking.errors.any?.should be_false
|
||||||
|
booking.retailer.should eql retailer
|
||||||
|
booking.card_presumed_ok.should be_false
|
||||||
|
booking.user.should eql user
|
||||||
|
booking.same_school_free.should be_false
|
||||||
|
booking.same_retailer.should be_true
|
||||||
|
#user.unprocessed_normal_lesson.should be_nil
|
||||||
|
booking.sent_notices.should be_false
|
||||||
|
booking.booked_price.should eql 30.00
|
||||||
|
booking.is_requested?.should be_true
|
||||||
|
booking.lesson_sessions[0].music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
|
||||||
|
LessonPaymentCharge.count.should eql 1
|
||||||
|
|
||||||
|
|
||||||
|
########## Need validate their credit card
|
||||||
|
token = create_stripe_token
|
||||||
|
result = user.payment_update({token: token, zip: '78759', normal: true, booking_id: booking.id})
|
||||||
|
booking = result[:lesson]
|
||||||
|
lesson = booking.lesson_sessions[0]
|
||||||
|
booking.errors.any?.should be_false
|
||||||
|
lesson.errors.any?.should be_false
|
||||||
|
booking.card_presumed_ok.should be_true
|
||||||
|
booking.sent_notices.should be_true
|
||||||
|
lesson.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
|
||||||
|
lesson.amount_charged.should eql 0.0
|
||||||
|
lesson.reload
|
||||||
|
|
||||||
|
user.reload
|
||||||
|
user.stripe_customer_id.should_not be nil
|
||||||
|
user.remaining_test_drives.should eql 0
|
||||||
|
user.lesson_purchases.length.should eql 0
|
||||||
|
|
||||||
|
customer = Stripe::Customer.retrieve(user.stripe_customer_id)
|
||||||
|
customer.email.should eql user.email
|
||||||
|
|
||||||
|
booking.lesson_sessions.length.should eql 1
|
||||||
|
lesson_session = booking.lesson_sessions[0]
|
||||||
|
lesson_session.status.should eql LessonBooking::STATUS_REQUESTED
|
||||||
|
booking.status.should eql LessonBooking::STATUS_REQUESTED
|
||||||
|
|
||||||
|
######### Teacher counters with new slot
|
||||||
|
teacher_countered_slot = FactoryGirl.build(:lesson_booking_slot_single, hour: 14)
|
||||||
|
UserMailer.deliveries.clear
|
||||||
|
lesson_session.counter({proposer: teacher_user, slot: teacher_countered_slot, message: 'Does this work?'})
|
||||||
|
booking.reload
|
||||||
|
booking.errors.any?.should be false
|
||||||
|
lesson_session.lesson_booking.errors.any?.should be false
|
||||||
|
lesson_session.lesson_booking_slots.length.should eql 1
|
||||||
|
lesson_session.lesson_booking_slots[0].proposer.should eql teacher_user
|
||||||
|
teacher_counter = lesson_session.lesson_booking_slots.order(:created_at).last
|
||||||
|
teacher_counter.should eql teacher_countered_slot
|
||||||
|
teacher_counter.proposer.should eql teacher_user
|
||||||
|
booking.lesson_booking_slots.length.should eql 3
|
||||||
|
UserMailer.deliveries.length.should eql 1
|
||||||
|
chat = ChatMessage.unscoped.order(:created_at).last
|
||||||
|
chat.channel.should eql ChatMessage::CHANNEL_LESSON
|
||||||
|
chat.message.should eql 'Does this work?'
|
||||||
|
chat.user.should eql teacher_user
|
||||||
|
chat.target_user.should eql user
|
||||||
|
notification = Notification.unscoped.order(:created_at).last
|
||||||
|
notification.session_id.should eql lesson_session.music_session.id
|
||||||
|
notification.student_directed.should eql true
|
||||||
|
notification.purpose.should eql 'counter'
|
||||||
|
notification.description.should eql NotificationTypes::LESSON_MESSAGE
|
||||||
|
|
||||||
|
######### Student counters with new slot
|
||||||
|
student_countered_slot = FactoryGirl.build(:lesson_booking_slot_single, hour: 16)
|
||||||
|
UserMailer.deliveries.clear
|
||||||
|
lesson_session.counter({proposer: user, slot: student_countered_slot, message: 'Does this work better?'})
|
||||||
|
lesson_session.errors.any?.should be false
|
||||||
|
lesson_session.lesson_booking.errors.any?.should be false
|
||||||
|
lesson_session.lesson_booking_slots.length.should eql 2
|
||||||
|
student_counter = booking.lesson_booking_slots.order(:created_at).last
|
||||||
|
student_counter.proposer.should eql user
|
||||||
|
booking.reload
|
||||||
|
booking.lesson_booking_slots.length.should eql 4
|
||||||
|
UserMailer.deliveries.length.should eql 1
|
||||||
|
chat = ChatMessage.unscoped.order(:created_at).last
|
||||||
|
chat.message.should eql 'Does this work better?'
|
||||||
|
chat.channel.should eql ChatMessage::CHANNEL_LESSON
|
||||||
|
chat.user.should eql user
|
||||||
|
chat.target_user.should eql teacher_user
|
||||||
|
notification = Notification.unscoped.order(:created_at).last
|
||||||
|
notification.session_id.should eql lesson_session.music_session.id
|
||||||
|
notification.student_directed.should eql false
|
||||||
|
notification.purpose.should eql 'counter'
|
||||||
|
notification.description.should eql NotificationTypes::LESSON_MESSAGE
|
||||||
|
|
||||||
|
######## Teacher accepts slot
|
||||||
|
UserMailer.deliveries.clear
|
||||||
|
|
||||||
|
lesson_session.accept({message: 'Yeah I got this', slot: student_counter.id, update_all: false, accepter: teacher_user})
|
||||||
|
lesson_session.errors.any?.should be_false
|
||||||
|
lesson_session.reload
|
||||||
|
lesson_session.slot.should eql student_counter
|
||||||
|
lesson_session.status.should eql LessonSession::STATUS_APPROVED
|
||||||
|
booking.reload
|
||||||
|
booking.default_slot.should eql student_counter
|
||||||
|
lesson_session.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
|
||||||
|
booking.status.should eql LessonBooking::STATUS_APPROVED
|
||||||
|
|
||||||
|
UserMailer.deliveries.length.should eql 2
|
||||||
|
chat = ChatMessage.unscoped.order(:created_at).last
|
||||||
|
chat.message.should eql 'Yeah I got this'
|
||||||
|
chat.purpose.should eql 'Lesson Approved'
|
||||||
|
chat.channel.should eql ChatMessage::CHANNEL_LESSON
|
||||||
|
chat.user.should eql teacher_user
|
||||||
|
chat.target_user.should eql user
|
||||||
|
notification = Notification.unscoped.order(:created_at).last
|
||||||
|
notification.session_id.should eql lesson_session.music_session.id
|
||||||
|
notification.student_directed.should eql true
|
||||||
|
notification.purpose.should eql 'accept'
|
||||||
|
notification.description.should eql NotificationTypes::LESSON_MESSAGE
|
||||||
|
|
||||||
|
# teacher & student get into session
|
||||||
|
start = lesson_session.scheduled_start
|
||||||
|
end_time = lesson_session.scheduled_start + (60 * lesson_session.duration)
|
||||||
|
uh2 = FactoryGirl.create(:music_session_user_history, user: teacher_user, history: lesson_session.music_session, created_at: start, session_removed_at: end_time)
|
||||||
|
# artificially end the session, which is covered by other background jobs
|
||||||
|
lesson_session.music_session.session_removed_at = end_time
|
||||||
|
lesson_session.music_session.save!
|
||||||
|
|
||||||
|
Timecop.travel(end_time + 1)
|
||||||
|
|
||||||
|
UserMailer.deliveries.clear
|
||||||
|
# background code comes around and analyses the session
|
||||||
|
LessonSession.hourly_check
|
||||||
|
lesson_session.reload
|
||||||
|
lesson_session.analysed.should be_true
|
||||||
|
analysis = lesson_session.analysis
|
||||||
|
analysis["reason"].should eql LessonSessionAnalyser::STUDENT_FAULT
|
||||||
|
analysis["student"].should eql LessonSessionAnalyser::NO_SHOW
|
||||||
|
lesson_session.billed.should be_true
|
||||||
|
if lesson_session.billing_error_detail
|
||||||
|
puts "testdrive flow #{lesson_session.billing_error_detail}" # this should not occur, but helps a great deal if a regression occurs and running all the tests
|
||||||
|
end
|
||||||
|
lesson_session.billing_attempts.should eql 1
|
||||||
|
user.reload
|
||||||
|
user.lesson_purchases.length.should eql 1
|
||||||
|
|
||||||
|
LessonBooking.hourly_check
|
||||||
|
|
||||||
|
lesson_session.reload
|
||||||
|
lesson_session.education_distribution.should be_nil
|
||||||
|
teacher_distribution = lesson_session.teacher_distribution
|
||||||
|
teacher_distribution.amount_in_cents.should eql (3000 * teacher_split_pct).round
|
||||||
|
teacher_distribution.teacher_fee_in_cents.should eql (teacher_distribution.amount_in_cents * (teacher_split_pct * (retailer.jamkazam_rate + APP_CONFIG.stripe[:charge_fee]))).round
|
||||||
|
teacher_distribution.ready.should be_true
|
||||||
|
teacher_distribution.distributed.should be_false
|
||||||
|
|
||||||
|
lesson_session.teacher_distributions.count.should eql 2
|
||||||
|
retailer_distribution = lesson_session.retailer_distribution
|
||||||
|
retailer_distribution.amount_in_cents.should eql (3000 * retailer_split_pct).round
|
||||||
|
retailer_distribution.teacher_fee_in_cents.should eql (retailer_distribution.amount_in_cents * (retailer_split_pct * (retailer.jamkazam_rate + APP_CONFIG.stripe[:charge_fee]))).round
|
||||||
|
retailer_distribution.ready.should be_true
|
||||||
|
retailer_distribution.distributed.should be_false
|
||||||
|
|
||||||
|
lesson_session.billed.should be true
|
||||||
|
user.reload
|
||||||
|
user.lesson_purchases.length.should eql 1
|
||||||
|
user.sales.length.should eql 1
|
||||||
|
lesson_session.amount_charged.should eql 32.48
|
||||||
|
lesson_session.billing_error_reason.should be_nil
|
||||||
|
lesson_session.sent_billing_notices.should be_true
|
||||||
|
user.reload
|
||||||
|
user.remaining_test_drives.should eql 0
|
||||||
|
UserMailer.deliveries.length.should eql 2 # one for student, one for teacher
|
||||||
|
|
||||||
|
TeacherPayment.count.should eql 0
|
||||||
|
TeacherPayment.hourly_check
|
||||||
|
TeacherPayment.count.should eql 2
|
||||||
|
|
||||||
|
LessonPaymentCharge.count.should eql 1
|
||||||
|
TeacherDistribution.count.should eql 2
|
||||||
|
|
||||||
|
|
||||||
|
teacher_distribution.reload
|
||||||
|
teacher_distribution.distributed.should be_true
|
||||||
|
retailer_distribution.reload
|
||||||
|
retailer_distribution.distributed.should be_true
|
||||||
|
|
||||||
|
retailer_amt = (3000 * retailer_split_pct).round
|
||||||
|
payment = retailer_distribution.teacher_payment
|
||||||
|
payment.amount_in_cents.should eql retailer_distribution.amount_in_cents
|
||||||
|
payment.fee_in_cents.should eql retailer_distribution.teacher_fee_in_cents
|
||||||
|
payment.teacher_payment_charge.amount_in_cents.should eql ((retailer_distribution.amount_in_cents - retailer_distribution.teacher_fee_in_cents) + (retailer_distribution.amount_in_cents - retailer_distribution.teacher_fee_in_cents) * APP_CONFIG.stripe[:ach_pct]).round
|
||||||
|
payment.teacher_payment_charge.fee_in_cents.should eql retailer_distribution.teacher_fee_in_cents
|
||||||
|
payment.teacher.should eql teacher_user
|
||||||
|
payment.teacher_distribution.should eql retailer_distribution
|
||||||
|
payment = teacher_distribution.teacher_payment
|
||||||
|
payment.amount_in_cents.should eql teacher_distribution.amount_in_cents
|
||||||
|
payment.fee_in_cents.should eql teacher_distribution.teacher_fee_in_cents
|
||||||
|
payment.teacher_payment_charge.amount_in_cents.should eql ((teacher_distribution.amount_in_cents - teacher_distribution.teacher_fee_in_cents) + (teacher_distribution.amount_in_cents - teacher_distribution.teacher_fee_in_cents) * APP_CONFIG.stripe[:ach_pct]).round
|
||||||
|
payment.teacher_payment_charge.fee_in_cents.should eql payment.teacher_payment_charge.fee_in_cents
|
||||||
|
payment.teacher.should eql teacher_user
|
||||||
|
payment.teacher_distribution.should eql teacher_distribution
|
||||||
|
lesson_session.lesson_booking.status.should eql LessonBooking::STATUS_COMPLETED
|
||||||
|
lesson_session.lesson_booking.success.should be_true
|
||||||
end
|
end
|
||||||
|
|
||||||
it "affiliate gets their cut" do
|
it "affiliate gets their cut" do
|
||||||
|
|
|
||||||
|
|
@ -338,7 +338,7 @@ describe LessonBooking do
|
||||||
slot.day_of_week = jan1.wday
|
slot.day_of_week = jan1.wday
|
||||||
|
|
||||||
booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_WEEKLY, 60)
|
booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_WEEKLY, 60)
|
||||||
times = booking.predicted_times_for_month(next_year, 1)
|
times = booking.predicted_times_for_month(next_year, 1)[:times]
|
||||||
times.length.should eql 5
|
times.length.should eql 5
|
||||||
times[0].to_date.should eql (jan1)
|
times[0].to_date.should eql (jan1)
|
||||||
times[1].to_date.should eql (Date.new(next_year, 1, 8))
|
times[1].to_date.should eql (Date.new(next_year, 1, 8))
|
||||||
|
|
@ -357,7 +357,7 @@ describe LessonBooking do
|
||||||
slot.day_of_week = jan1.wday
|
slot.day_of_week = jan1.wday
|
||||||
|
|
||||||
booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_WEEKLY, 60)
|
booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_WEEKLY, 60)
|
||||||
times = booking.predicted_times_for_month(next_year, 1)
|
times = booking.predicted_times_for_month(next_year, 1)[:times]
|
||||||
times.length.should eql 3
|
times.length.should eql 3
|
||||||
times[0].to_date.should eql (Date.new(next_year, 1, 15))
|
times[0].to_date.should eql (Date.new(next_year, 1, 15))
|
||||||
times[1].to_date.should eql (Date.new(next_year, 1, 22))
|
times[1].to_date.should eql (Date.new(next_year, 1, 22))
|
||||||
|
|
|
||||||
|
|
@ -890,29 +890,30 @@ describe MusicSession do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "purgeable sessions " do
|
describe "purgeable sessions" do
|
||||||
|
|
||||||
it 'selects unscheduled sessions past due date' do
|
it 'selects unscheduled sessions past due date' do
|
||||||
interval = MusicSession::UNSTARTED_INTERVAL_DAYS_PURGE
|
interval = MusicSession::UNSTARTED_INTERVAL_DAYS_PURGE
|
||||||
dd = Time.now - (interval.to_i + 1).days
|
|
||||||
Timecop.travel(dd)
|
|
||||||
msess1 = FactoryGirl.create(:music_session)
|
msess1 = FactoryGirl.create(:music_session)
|
||||||
|
dd = Time.now - (interval.to_i + 1).days
|
||||||
|
Timecop.travel(dd)
|
||||||
msess2 = FactoryGirl.create(:music_session)
|
msess2 = FactoryGirl.create(:music_session)
|
||||||
purging = MusicSession.purgeable_sessions
|
purging = MusicSession.purgeable_sessions
|
||||||
expect(purging.size).to be(1)
|
expect(purging.size).to be(1)
|
||||||
expect(purging[0].id).to eq(msess1.id)
|
expect(purging[0].id).to eq(msess2.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'selects recurring and non-recurring sessions past due date' do
|
it 'selects recurring and non-recurring sessions past due date' do
|
||||||
[MusicSession::UNSTARTED_INTERVAL_DAYS_PURGE,
|
[MusicSession::UNSTARTED_INTERVAL_DAYS_PURGE,
|
||||||
MusicSession::UNSTARTED_INTERVAL_DAYS_PURGE_RECUR].each do |interval|
|
MusicSession::UNSTARTED_INTERVAL_DAYS_PURGE_RECUR].each do |interval|
|
||||||
|
Timecop.return
|
||||||
|
msess1 = FactoryGirl.create(:music_session, scheduled_start: Time.now)
|
||||||
dd = Time.now - (interval.to_i + 1).days
|
dd = Time.now - (interval.to_i + 1).days
|
||||||
Timecop.travel(dd)
|
Timecop.travel(dd)
|
||||||
msess1 = FactoryGirl.create(:music_session, scheduled_start: Time.now)
|
|
||||||
msess2 = FactoryGirl.create(:music_session, scheduled_start: Time.now)
|
msess2 = FactoryGirl.create(:music_session, scheduled_start: Time.now)
|
||||||
purging = MusicSession.purgeable_sessions
|
purging = MusicSession.purgeable_sessions
|
||||||
expect(purging.size).to be(1)
|
expect(purging.size).to be(1)
|
||||||
expect(purging[0].id).to eq(msess1.id)
|
expect(purging[0].id).to eq(msess2.id)
|
||||||
MusicSession.delete_all
|
MusicSession.delete_all
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ describe Retailer do
|
||||||
end
|
end
|
||||||
it "has correct associations" do
|
it "has correct associations" do
|
||||||
retailer = FactoryGirl.create(:retailer)
|
retailer = FactoryGirl.create(:retailer)
|
||||||
retailer.slug.should eql retailer.id
|
|
||||||
|
|
||||||
retailer.should eql retailer.user.owned_retailer
|
retailer.should eql retailer.user.owned_retailer
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -486,7 +486,7 @@ describe Sale do
|
||||||
purchase= adjustments[0]
|
purchase= adjustments[0]
|
||||||
purchase.unit_amount_in_cents.should eq((jamtrack.price * 100).to_i)
|
purchase.unit_amount_in_cents.should eq((jamtrack.price * 100).to_i)
|
||||||
purchase.accounting_code.should eq(ShoppingCart::PURCHASE_NORMAL)
|
purchase.accounting_code.should eq(ShoppingCart::PURCHASE_NORMAL)
|
||||||
purchase.description.should eq("JamTrack: " + jamtrack.name)
|
purchase.description.should eq("JamTrack: " + jamtrack.name + '- FOR USE ONLY WITHIN APP')
|
||||||
purchase.state.should eq('invoiced')
|
purchase.state.should eq('invoiced')
|
||||||
purchase.uuid.should eq(sale_line_item.recurly_adjustment_uuid)
|
purchase.uuid.should eq(sale_line_item.recurly_adjustment_uuid)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,11 +32,11 @@ describe ShoppingCart do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "allows mix of free and not free stuff" do
|
it "allows mix of free and not free stuff" do
|
||||||
cart1 = ShoppingCart.add_jam_track_to_cart(user, jam_track, clear: true)
|
cart1 = ShoppingCart.add_jam_track_to_cart(user, jam_track)
|
||||||
cart1.should_not be_nil
|
cart1.should_not be_nil
|
||||||
cart1.errors.any?.should be_false
|
cart1.errors.any?.should be_false
|
||||||
user.reload
|
user.reload
|
||||||
cart2 = ShoppingCart.add_jam_track_to_cart(user, jam_track, clear: true)
|
cart2 = ShoppingCart.add_jam_track_to_cart(user, jam_track)
|
||||||
cart2.errors.any?.should be_false
|
cart2.errors.any?.should be_false
|
||||||
user.reload
|
user.reload
|
||||||
user.shopping_carts.length.should eq(1)
|
user.shopping_carts.length.should eq(1)
|
||||||
|
|
@ -44,7 +44,7 @@ describe ShoppingCart do
|
||||||
cart3.errors.any?.should be_false
|
cart3.errors.any?.should be_false
|
||||||
user.reload
|
user.reload
|
||||||
user.shopping_carts.length.should eq(2)
|
user.shopping_carts.length.should eq(2)
|
||||||
cart4 = ShoppingCart.add_jam_track_to_cart(user, jam_track2, clear: true)
|
cart4 = ShoppingCart.add_jam_track_to_cart(user, jam_track2)
|
||||||
cart4.errors.any?.should be_false
|
cart4.errors.any?.should be_false
|
||||||
user.reload
|
user.reload
|
||||||
user.shopping_carts.length.should eq(3)
|
user.shopping_carts.length.should eq(3)
|
||||||
|
|
@ -70,12 +70,12 @@ describe ShoppingCart do
|
||||||
it "removes redeemable item to shopping cart (maintains only one in cart)" do
|
it "removes redeemable item to shopping cart (maintains only one in cart)" do
|
||||||
|
|
||||||
user.has_redeemable_jamtrack.should be_true
|
user.has_redeemable_jamtrack.should be_true
|
||||||
cart1 = ShoppingCart.add_jam_track_to_cart(user, jam_track, clear: true)
|
cart1 = ShoppingCart.add_jam_track_to_cart(user, jam_track)
|
||||||
cart1.should_not be_nil
|
cart1.should_not be_nil
|
||||||
cart1.errors.any?.should be_false
|
cart1.errors.any?.should be_false
|
||||||
cart1.marked_for_redeem.should eq(1)
|
cart1.marked_for_redeem.should eq(1)
|
||||||
user.reload
|
user.reload
|
||||||
cart2 = ShoppingCart.add_jam_track_to_cart(user, jam_track2, clear: true)
|
cart2 = ShoppingCart.add_jam_track_to_cart(user, jam_track2)
|
||||||
cart2.should_not be_nil
|
cart2.should_not be_nil
|
||||||
cart2.errors.any?.should be_false
|
cart2.errors.any?.should be_false
|
||||||
cart2.marked_for_redeem.should eq(1)
|
cart2.marked_for_redeem.should eq(1)
|
||||||
|
|
|
||||||
|
|
@ -265,7 +265,7 @@ describe TeacherPayment do
|
||||||
teacher_distribution = payment.teacher_payment_charge.distribution
|
teacher_distribution = payment.teacher_payment_charge.distribution
|
||||||
teacher_distribution.amount_in_cents.should eql 1000
|
teacher_distribution.amount_in_cents.should eql 1000
|
||||||
charge = Stripe::Charge.retrieve(payment.teacher_payment_charge.stripe_charge_id)
|
charge = Stripe::Charge.retrieve(payment.teacher_payment_charge.stripe_charge_id)
|
||||||
charge.destination.should be_nil
|
charge.destination.should_not be_nil
|
||||||
charge.amount.should eql 726
|
charge.amount.should eql 726
|
||||||
charge.application_fee.should be_nil
|
charge.application_fee.should be_nil
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -306,7 +306,8 @@ def app_config
|
||||||
:publishable_key => 'pk_test_HLTvioRAxN3hr5fNfrztZeoX',
|
:publishable_key => 'pk_test_HLTvioRAxN3hr5fNfrztZeoX',
|
||||||
:secret_key => 'sk_test_OkjoIF7FmdjunyNsdVqJD02D',
|
:secret_key => 'sk_test_OkjoIF7FmdjunyNsdVqJD02D',
|
||||||
:source_customer => 'cus_88Vp44SLnBWMXq', # seth@jamkazam.com in JamKazam-test account
|
:source_customer => 'cus_88Vp44SLnBWMXq', # seth@jamkazam.com in JamKazam-test account
|
||||||
:ach_pct => 0.008
|
:ach_pct => 0.008,
|
||||||
|
:charge_fee => 0.03
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
def musician_count
|
def musician_count
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ gem 'sprockets-rails', '2.3.2'
|
||||||
gem 'non-stupid-digest-assets'
|
gem 'non-stupid-digest-assets'
|
||||||
#gem 'license_finder'
|
#gem 'license_finder'
|
||||||
gem 'pg_migrate', '0.1.14'
|
gem 'pg_migrate', '0.1.14'
|
||||||
|
#gem 'paypal-sdk-rest'
|
||||||
|
gem 'paypal-sdk-merchant-jk', '1.118.1'
|
||||||
gem 'kickbox'
|
gem 'kickbox'
|
||||||
gem 'oj', '2.10.2'
|
gem 'oj', '2.10.2'
|
||||||
gem 'builder'
|
gem 'builder'
|
||||||
|
|
@ -70,7 +72,7 @@ gem 'filepicker-rails', '0.1.0'
|
||||||
gem 'aws-sdk', '~> 1'
|
gem 'aws-sdk', '~> 1'
|
||||||
gem 'aasm' #, '3.0.16'
|
gem 'aasm' #, '3.0.16'
|
||||||
gem 'carmen'
|
gem 'carmen'
|
||||||
gem 'carrierwave' #, '0.9.0'
|
gem 'carrierwave', '0.11.2' #, '0.9.0'
|
||||||
gem 'carrierwave_direct'
|
gem 'carrierwave_direct'
|
||||||
gem 'fog'
|
gem 'fog'
|
||||||
#gem 'jquery-payment-rails', github: 'sethcall/jquery-payment-rails'
|
#gem 'jquery-payment-rails', github: 'sethcall/jquery-payment-rails'
|
||||||
|
|
@ -163,7 +165,7 @@ end
|
||||||
gem 'sass-rails'
|
gem 'sass-rails'
|
||||||
gem 'coffee-rails'
|
gem 'coffee-rails'
|
||||||
gem 'uglifier'
|
gem 'uglifier'
|
||||||
|
gem 'coffee-script-source', '1.11.1'
|
||||||
group :test, :cucumber do
|
group :test, :cucumber do
|
||||||
gem 'simplecov', '~> 0.7.1'
|
gem 'simplecov', '~> 0.7.1'
|
||||||
gem 'simplecov-rcov'
|
gem 'simplecov-rcov'
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
//= require jquery.Jcrop
|
//= require jquery.Jcrop
|
||||||
//= require jquery.naturalsize
|
//= require jquery.naturalsize
|
||||||
//= require jquery.queryparams
|
//= require jquery.queryparams
|
||||||
//= require jquery.clipboard
|
//= require clipboard
|
||||||
//= require jquery.timeago
|
//= require jquery.timeago
|
||||||
//= require jquery.easydropdown
|
//= require jquery.easydropdown
|
||||||
//= require jquery.scrollTo
|
//= require jquery.scrollTo
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,8 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("GetLocalRecordingState", localResults)
|
||||||
|
|
||||||
$.each(claimedRecordings, function(index, claimedRecording) {
|
$.each(claimedRecordings, function(index, claimedRecording) {
|
||||||
|
|
||||||
var options = {
|
var options = {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@
|
||||||
var $dialog = null;
|
var $dialog = null;
|
||||||
var $saveVideoCheckbox = null
|
var $saveVideoCheckbox = null
|
||||||
var $uploadToYoutube = null
|
var $uploadToYoutube = null
|
||||||
|
var timeout = null
|
||||||
|
var CLIENT_ROLE = context.JK.CLIENT_ROLE
|
||||||
|
|
||||||
function resetForm() {
|
function resetForm() {
|
||||||
// remove all display errors
|
// remove all display errors
|
||||||
|
|
@ -44,6 +46,47 @@
|
||||||
|
|
||||||
resetForm();
|
resetForm();
|
||||||
|
|
||||||
|
if(context.jamClient.getClientParentChildRole() == CLIENT_ROLE.CHILD) {
|
||||||
|
|
||||||
|
logger.debug("child client; launching preview after xfer");
|
||||||
|
$('#recording-finished-dialog span.nowait').addClass('hidden')
|
||||||
|
$('#recording-finished-dialog span.pleasewait').removeClass('hidden')
|
||||||
|
$('#recording-finished-dialog .preview-area').css('visibility', 'hidden')
|
||||||
|
$('#recording-finished-dialog form').css('visibility', 'hidden')
|
||||||
|
waitForMixTransfer()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log("normal client; launching preview immediately")
|
||||||
|
$('#recording-finished-dialog span.pleasewait').addClass('hidden')
|
||||||
|
$('#recording-finished-dialog span.nowait').removeClass('hidden')
|
||||||
|
$('#recording-finished-dialog .preview-area').css('visibility', 'visible')
|
||||||
|
$('#recording-finished-dialog form').css('visibility', 'visible')
|
||||||
|
launchPreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function waitForMixTransfer() {
|
||||||
|
timeout = setTimeout(function() {
|
||||||
|
console.log("checking for file transfer", window.RecordingStore.mixTransferred)
|
||||||
|
|
||||||
|
if(window.RecordingStore.mixTransferred) {
|
||||||
|
$('#recording-finished-dialog span.pleasewait').addClass('hidden')
|
||||||
|
$('#recording-finished-dialog span.nowait').removeClass('hidden')
|
||||||
|
$('#recording-finished-dialog .preview-area').css('visibility', 'visible')
|
||||||
|
$('#recording-finished-dialog form').css('visibility', 'visible')
|
||||||
|
timeout = null
|
||||||
|
launchPreview()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
waitForMixTransfer();
|
||||||
|
}
|
||||||
|
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
function launchPreview() {
|
||||||
var parentSelector = '#recording-finished-dialog div.genre-selector';
|
var parentSelector = '#recording-finished-dialog div.genre-selector';
|
||||||
context.JK.GenreSelectorHelper.render(parentSelector);
|
context.JK.GenreSelectorHelper.render(parentSelector);
|
||||||
|
|
||||||
|
|
@ -117,10 +160,12 @@
|
||||||
playbackControls.startMonitor();
|
playbackControls.startMonitor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function afterHide() {
|
function afterHide() {
|
||||||
|
if(timeout) {
|
||||||
|
clearTimeout(timeout)
|
||||||
|
timeout = null
|
||||||
|
}
|
||||||
if(recording && recording.video) {
|
if(recording && recording.video) {
|
||||||
var name = $('#recording-finished-dialog form input[name=name]').val();
|
var name = $('#recording-finished-dialog form input[name=name]').val();
|
||||||
name = name.replace(/[^A-Za-z0-9\-\ ]/g, '');
|
name = name.replace(/[^A-Za-z0-9\-\ ]/g, '');
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
var userDetail = null;
|
var userDetail = null;
|
||||||
var entity = null;
|
var entity = null;
|
||||||
var remainingCap = 140 - 22 - 1; // 140 tweet max, minus 22 for link size, minus 1 for space
|
var remainingCap = 140 - 22 - 1; // 140 tweet max, minus 22 for link size, minus 1 for space
|
||||||
|
var clipboard = null;
|
||||||
|
|
||||||
function showSpinner() {
|
function showSpinner() {
|
||||||
$(dialogId + ' .dialog-inner').hide();
|
$(dialogId + ' .dialog-inner').hide();
|
||||||
|
|
@ -444,27 +445,6 @@
|
||||||
|
|
||||||
function afterShow() {
|
function afterShow() {
|
||||||
$("#shareType").text(entityType);
|
$("#shareType").text(entityType);
|
||||||
if(context.JK.hasFlash()) {
|
|
||||||
$("#btn-share-copy").clipboard({
|
|
||||||
path: '/assets/jquery.clipboard.swf',
|
|
||||||
copy: function() {
|
|
||||||
// Return text in closest element (useful when you have multiple boxes that can be copied)
|
|
||||||
return $(".link-contents").text();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(context.jamClient) {
|
|
||||||
// uses bridge call to ultimately access QClipboard
|
|
||||||
$("#btn-share-copy").unbind('click').click(function() {
|
|
||||||
context.jamClient.SaveToClipboard($(".link-contents").text());
|
|
||||||
return false;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
logger.debug("no copy-to-clipboard capabilities")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function afterHide() {
|
function afterHide() {
|
||||||
|
|
@ -486,6 +466,20 @@
|
||||||
//initDialog();
|
//initDialog();
|
||||||
|
|
||||||
facebookHelper.deferredLoginStatus().done(function(response) { handleFbStateChange(response); });
|
facebookHelper.deferredLoginStatus().done(function(response) { handleFbStateChange(response); });
|
||||||
|
|
||||||
|
if(context.jamClient.IsNativeClient()) {
|
||||||
|
$("#btn-share-copy").unbind('click').click(function() {
|
||||||
|
context.jamClient.SaveToClipboard($("#link-contents").text());
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
clipboard = new Clipboard('#btn-share-copy', {
|
||||||
|
text: function(trigger) {
|
||||||
|
return $("#link-contents").text();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.initialize = initialize;
|
this.initialize = initialize;
|
||||||
|
|
|
||||||
|
|
@ -188,6 +188,37 @@
|
||||||
return context.JK.prodBubble($element, 'teacher-profile', {}, bigHelpDarkOptions({spikeGirth:0, spikeLength: 0, duration:10000, offsetParent:$offsetParent, width:385, positions:['top', 'right', 'bottom']}))
|
return context.JK.prodBubble($element, 'teacher-profile', {}, bigHelpDarkOptions({spikeGirth:0, spikeLength: 0, duration:10000, offsetParent:$offsetParent, width:385, positions:['top', 'right', 'bottom']}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
helpBubble.jamtrackVariants = function($element, $offsetParent) {
|
||||||
|
var offer = function() {
|
||||||
|
console.log("jamtrackVariant turn off")
|
||||||
|
$element.btOff()
|
||||||
|
$offsetParent.off('click', offer)
|
||||||
|
}
|
||||||
|
|
||||||
|
var bubble = context.JK.prodBubble($element, 'jamtrack-variants', {}, bigHelpDarkOptions({clickAnywhereToClose: true, spikeGirth:0, spikeLength: 0, duration:20000, positions:['bottom', 'right', 'left'], offsetParent: $offsetParent}))
|
||||||
|
setTimeout(function() {
|
||||||
|
$offsetParent.on('click', offer)
|
||||||
|
}, 1)
|
||||||
|
|
||||||
|
return bubble
|
||||||
|
}
|
||||||
|
|
||||||
|
helpBubble.jamtrackUpgrade = function($element, $offsetParent) {
|
||||||
|
var offer = function() {
|
||||||
|
console.log("jamtrackUpgrade turn off")
|
||||||
|
$element.btOff()
|
||||||
|
$offsetParent.off('click', offer)
|
||||||
|
}
|
||||||
|
|
||||||
|
var bubble = context.JK.prodBubble($element, 'jamtrack-upgrade', {}, bigHelpDarkOptions({clickAnywhereToClose: true, spikeGirth:0, spikeLength: 0, duration:20000, positions:['bottom', 'right', 'left'], offsetParent: $offsetParent}))
|
||||||
|
setTimeout(function() {
|
||||||
|
$offsetParent.on('click', offer)
|
||||||
|
}, 1)
|
||||||
|
|
||||||
|
return bubble
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
helpBubble.showUseRemainingTestDrives = function($element, $offsetParent, user, callback) {
|
helpBubble.showUseRemainingTestDrives = function($element, $offsetParent, user, callback) {
|
||||||
return context.JK.onceBubble($element, 'side-remaining-test-drives', user, {offsetParent:$offsetParent, width:260, positions:['right'], postShow: function(container) {
|
return context.JK.onceBubble($element, 'side-remaining-test-drives', user, {offsetParent:$offsetParent, width:260, positions:['right'], postShow: function(container) {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2734,6 +2734,27 @@
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function paypalDetail(options) {
|
||||||
|
options = options || {}
|
||||||
|
return $.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: '/api/paypal/checkout/detail',
|
||||||
|
dataType: 'json',
|
||||||
|
contentType: 'application/json',
|
||||||
|
data: JSON.stringify(options)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function paypalPlaceOrder(options) {
|
||||||
|
options = options || {}
|
||||||
|
return $.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: '/api/paypal/checkout/confirm',
|
||||||
|
dataType: 'json',
|
||||||
|
contentType: 'application/json',
|
||||||
|
data: JSON.stringify(options)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function initialize() {
|
function initialize() {
|
||||||
return self;
|
return self;
|
||||||
|
|
@ -2977,6 +2998,8 @@
|
||||||
this.posaActivate = posaActivate;
|
this.posaActivate = posaActivate;
|
||||||
this.posaClaim = posaClaim;
|
this.posaClaim = posaClaim;
|
||||||
this.sendRetailerCustomerEmail = sendRetailerCustomerEmail;
|
this.sendRetailerCustomerEmail = sendRetailerCustomerEmail;
|
||||||
|
this.paypalDetail = paypalDetail;
|
||||||
|
this.paypalPlaceOrder = paypalPlaceOrder;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
})(window,jQuery);
|
})(window,jQuery);
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
//= require jquery.queryparams
|
//= require jquery.queryparams
|
||||||
//= require jquery.hoverIntent
|
//= require jquery.hoverIntent
|
||||||
//= require jquery.cookie
|
//= require jquery.cookie
|
||||||
//= require jquery.clipboard
|
//= require clipboard
|
||||||
//= require jquery.easydropdown
|
//= require jquery.easydropdown
|
||||||
//= require jquery.carousel-1.1
|
//= require jquery.carousel-1.1
|
||||||
//= require jquery.mousewheel-3.1.9
|
//= require jquery.mousewheel-3.1.9
|
||||||
|
|
|
||||||
|
|
@ -496,7 +496,10 @@
|
||||||
monitoring = false;
|
monitoring = false;
|
||||||
logger.debug("playbackControl.stopMonitor")
|
logger.debug("playbackControl.stopMonitor")
|
||||||
if (monitorPlaybackTimeout != null) {
|
if (monitorPlaybackTimeout != null) {
|
||||||
clearTimeout(monitorPlaybackTimeout);
|
if(clearTimeout) {
|
||||||
|
clearTimeout(monitorPlaybackTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
monitorPlaybackTimeout = null;
|
monitorPlaybackTimeout = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,7 @@ profileUtils = context.JK.ProfileUtils
|
||||||
selected: 'account',
|
selected: 'account',
|
||||||
updateErrors: null,
|
updateErrors: null,
|
||||||
retailerName: null,
|
retailerName: null,
|
||||||
|
teacherSplit: null,
|
||||||
teacherInvitations: null,
|
teacherInvitations: null,
|
||||||
updating: false
|
updating: false
|
||||||
}
|
}
|
||||||
|
|
@ -121,18 +122,25 @@ profileUtils = context.JK.ProfileUtils
|
||||||
city = @root.find('select[name="cities"]').val()
|
city = @root.find('select[name="cities"]').val()
|
||||||
password = @root.find('input[type="password"]').val()
|
password = @root.find('input[type="password"]').val()
|
||||||
|
|
||||||
|
teacherSplit = @teacherSplit()
|
||||||
|
|
||||||
|
if teacherSplit
|
||||||
|
retailerSplit = Number((100 - teacherSplit).toFixed(2))
|
||||||
|
|
||||||
@setState(updating: true)
|
@setState(updating: true)
|
||||||
rest.updateRetailer({
|
rest.updateRetailer({
|
||||||
id: this.state.retailer.id,
|
id: this.state.retailer.id,
|
||||||
name: name,
|
name: name,
|
||||||
state: region,
|
state: region,
|
||||||
city: city,
|
city: city,
|
||||||
password:password
|
password:password,
|
||||||
|
split: {teacher: teacherSplit, retailer: retailerSplit}
|
||||||
|
|
||||||
}).done((response) => @onUpdateDone(response)).fail((jqXHR) => @onUpdateFail(jqXHR))
|
}).done((response) => @onUpdateDone(response)).fail((jqXHR) => @onUpdateFail(jqXHR))
|
||||||
|
|
||||||
|
|
||||||
onUpdateDone: (response) ->
|
onUpdateDone: (response) ->
|
||||||
@setState({retailer: response, retailerName: null, updateErrors: null, updating: false})
|
@setState({retailer: response, retailerName: null, teacherSplit: null, updateErrors: null, updating: false})
|
||||||
|
|
||||||
@app.layout.notify({title: "update success", text: "Your retailer information has been successfully updated"})
|
@app.layout.notify({title: "update success", text: "Your retailer information has been successfully updated"})
|
||||||
|
|
||||||
|
|
@ -262,6 +270,63 @@ profileUtils = context.JK.ProfileUtils
|
||||||
logger.debug("handleLocationChange #{country} #{region} ${city}")
|
logger.debug("handleLocationChange #{country} #{region} ${city}")
|
||||||
@setState({city: city, region: region})
|
@setState({city: city, region: region})
|
||||||
|
|
||||||
|
teacherSplitCurrent: () ->
|
||||||
|
if this.state.teacherSplit?
|
||||||
|
console.log("taking state for teacher split")
|
||||||
|
this.state.teacherSplit
|
||||||
|
else
|
||||||
|
this.state.retailer.payment_details.teacher
|
||||||
|
|
||||||
|
teacherSplitValue: () ->
|
||||||
|
@teacherSplitCurrent()
|
||||||
|
|
||||||
|
retailerSplitValue: () ->
|
||||||
|
teacherSplit = @teacherSplitCurrent()
|
||||||
|
return (100 - teacherSplit).toFixed(2)
|
||||||
|
|
||||||
|
onTeacherBlur: () ->
|
||||||
|
teacherSplit = @root.find('input[name="teacher-split"]').val()
|
||||||
|
teacherSplit = Number(teacherSplit)
|
||||||
|
if teacherSplit != teacherSplit #NaN?
|
||||||
|
@setState({teacherSplit: null})
|
||||||
|
|
||||||
|
|
||||||
|
teacherSplit: () ->
|
||||||
|
teacherSplit = @root.find('input[name="teacher-split"]').val()
|
||||||
|
if teacherSplit
|
||||||
|
teacherSplit = Number(teacherSplit)
|
||||||
|
if !teacherSplit
|
||||||
|
console.log("defaulting to 100 because teachersplit is empty")
|
||||||
|
teacherSplit = 100
|
||||||
|
teacherSplit
|
||||||
|
|
||||||
|
onTeacherSplitChange: (e) ->
|
||||||
|
$target = $(e.target)
|
||||||
|
|
||||||
|
# edge cases first
|
||||||
|
teacherSplit = @root.find('input[name="teacher-split"]').val()
|
||||||
|
|
||||||
|
if teacherSplit == null || teacherSplit == ''
|
||||||
|
@setState({teacherSplit: ''})
|
||||||
|
return
|
||||||
|
|
||||||
|
teacherSplit = Number(teacherSplit)
|
||||||
|
|
||||||
|
if teacherSplit != teacherSplit # NaN?
|
||||||
|
console.log("teacher split is NaN; ignoring")
|
||||||
|
# do nothing; this way junk doesn't start showing up in retail square. Onblur will fix
|
||||||
|
return
|
||||||
|
|
||||||
|
teacherSplit = @teacherSplit()
|
||||||
|
|
||||||
|
if teacherSplit > 100
|
||||||
|
console.log("teacher split is > 100. setting to 100")
|
||||||
|
return
|
||||||
|
if teacherSplit < 0
|
||||||
|
console.log("teacher split is < 0. setting to 0")
|
||||||
|
return
|
||||||
|
@setState({teacherSplit: teacherSplit})
|
||||||
|
|
||||||
account: () ->
|
account: () ->
|
||||||
|
|
||||||
nameErrors = context.JK.reactSingleFieldErrors('name', @state.updateErrors)
|
nameErrors = context.JK.reactSingleFieldErrors('name', @state.updateErrors)
|
||||||
|
|
@ -313,6 +378,19 @@ profileUtils = context.JK.ProfileUtils
|
||||||
<StripeConnect purpose='retailer' user={this.state.user}/>
|
<StripeConnect purpose='retailer' user={this.state.user}/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="field split">
|
||||||
|
<div className="teacher-split">
|
||||||
|
<label>Teacher % of Each Lesson:</label>
|
||||||
|
<input name="teacher-split" className="split-input teacher" type="number" defaultValue="" placeholder="please enter a value 0-100" value={this.teacherSplitValue()} onChange={this.onTeacherSplitChange} onBlur={this.onTeacherBlur}/>
|
||||||
|
<span className="usage-hint">Enter 0-100</span>
|
||||||
|
</div>
|
||||||
|
<div className="retailer-split">
|
||||||
|
<label>Retailer % of Each Lesson:</label>
|
||||||
|
<input name="retailer-split" className="split-input retailer" type="number" defaultValue="" value={this.retailerSplitValue()} readonly={true} disabled={true}/>
|
||||||
|
<span className="usage-hint">This is computed automatically based on the Teacher %</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="actions">
|
<div className="actions">
|
||||||
<a className={classNames(cancelClasses)} onClick={this.onCancel}>CANCEL</a>
|
<a className={classNames(cancelClasses)} onClick={this.onCancel}>CANCEL</a>
|
||||||
<a className={classNames(updateClasses)} onClick={this.onUpdate}>UPDATE</a>
|
<a className={classNames(updateClasses)} onClick={this.onUpdate}>UPDATE</a>
|
||||||
|
|
|
||||||
|
|
@ -249,7 +249,7 @@ JamBlasterActions = @JamBlasterActions
|
||||||
|
|
||||||
audio: () ->
|
audio: () ->
|
||||||
`<div className="audio-content">
|
`<div className="audio-content">
|
||||||
<JamBlasterTrackConfig />
|
To edit the JamBlaster audio settings, get into a session, and click the Settings link under My Tracks.
|
||||||
</div>`
|
</div>`
|
||||||
|
|
||||||
ipSettingsChanged: (key, e) ->
|
ipSettingsChanged: (key, e) ->
|
||||||
|
|
|
||||||
|
|
@ -194,13 +194,14 @@ LessonTimerActions = context.LessonTimerActions
|
||||||
@app.ajaxError(jqXHR)
|
@app.ajaxError(jqXHR)
|
||||||
|
|
||||||
cancelSelected: (lesson, recurring) ->
|
cancelSelected: (lesson, recurring) ->
|
||||||
rest.checkLessonCancel({id: lesson.id, update_all: recurring}).done((response) => (@issueCancelLesson(lesson,
|
rest.checkLessonCancel({id: lesson.id, update_all: recurring})
|
||||||
recurring))).fail((jqXHR) => (@cancelSelectedFail(jqXHR)))
|
.done((response) => @issueCancelLesson(lesson, recurring))
|
||||||
|
.fail((jqXHR) => @cancelSelectedFail(jqXHR, lesson))
|
||||||
|
|
||||||
cancelSelectedFailed: (jqXHR) ->
|
cancelSelectedFail: (jqXHR, lesson) ->
|
||||||
if jqXHR.status == 422
|
if jqXHR.status == 422
|
||||||
|
|
||||||
if recurring
|
if lesson.recurring
|
||||||
if @viewerStudent()
|
if @viewerStudent()
|
||||||
buttons = []
|
buttons = []
|
||||||
buttons.push({name: 'CLOSE', buttonStyle: 'button-grey'})
|
buttons.push({name: 'CLOSE', buttonStyle: 'button-grey'})
|
||||||
|
|
|
||||||
|
|
@ -73,13 +73,37 @@ MIX_MODES = context.JK.MIX_MODES
|
||||||
|
|
||||||
actionBtn = null
|
actionBtn = null
|
||||||
if jamtrack.purchased
|
if jamtrack.purchased
|
||||||
actionBtn = `<a className="jamtrack-add-cart-disabled button-grey button-disabled" href="javascript:void(0)">PURCHASED</a>`
|
if jamtrack.can_download
|
||||||
|
actionBtn = `<a className="jamtrack-add-cart-disabled button-grey button-disabled" href="javascript:void(0)">PURCHASED</a>`
|
||||||
|
else
|
||||||
|
priceNotice = `<div className={jamtrackPricesClasses}>$ {Number(jamtrack.upgrade_price).toFixed(2)}</div>`
|
||||||
|
actionBtn = `<div>
|
||||||
|
<div className="jamtrack-add-zone">
|
||||||
|
{priceNotice}
|
||||||
|
<a className="jamtrack-add-cart button-orange" href="#" data-jamtrack-id={jamtrack.id} data-variant="download">UPGRADE TO FULL</a>
|
||||||
|
</div>
|
||||||
|
<a className="jamtrack-upgrade-help" href='#'>HELP</a>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
|
||||||
else if jamtrack.is_free
|
else if jamtrack.is_free
|
||||||
actionBtn = `<a className="jamtrack-add-cart button-orange is_free" href="#" data-jamtrack-id={jamtrack.id}>GET IT FREE!</a>`
|
actionBtn = `<a className="jamtrack-add-cart button-orange is_free" href="#" data-jamtrack-id={jamtrack.id} data-variant="full">GET IT FREE!</a>`
|
||||||
else if jamtrack.added_cart
|
else if jamtrack.added_cart
|
||||||
actionBtn = `<a className="jamtrack-add-cart-disabled button-grey button-disabled" href="client#/shoppingCart">ALREADY IN CART</a>`
|
actionBtn = `<a className="jamtrack-add-cart-disabled button-grey button-disabled" href="client#/shoppingCart">ALREADY IN CART</a>`
|
||||||
else
|
else
|
||||||
actionBtn = `<a className="jamtrack-add-cart button-orange" href="#" data-jamtrack-id={jamtrack.id}>ADD TO CART</a>`
|
priceNotice = `<div className={jamtrackPricesClasses}>$ {jamtrack.price}</div>`
|
||||||
|
fullPriceNotice = `<div className={jamtrackPricesClasses}>$ {jamtrack.download_price}</div>`
|
||||||
|
actionBtn = `<div>
|
||||||
|
<div className="jamtrack-add-zone">
|
||||||
|
{priceNotice}
|
||||||
|
<a className="jamtrack-add-cart button-orange" href="#" data-jamtrack-id={jamtrack.id} data-variant="stream">ADD TO CART</a>
|
||||||
|
</div>
|
||||||
|
<div className="jamtrack-add-zone">
|
||||||
|
{fullPriceNotice}
|
||||||
|
<a className="jamtrack-add-cart button-orange" href="#" data-jamtrack-id={jamtrack.id} data-variant="full">ADD TO CART (FULL)</a>
|
||||||
|
</div>
|
||||||
|
<a className="jamtrack-variant-help" href='#'>HELP</a>
|
||||||
|
</div>`
|
||||||
|
|
||||||
availabilityNotice = null
|
availabilityNotice = null
|
||||||
if jamtrack.sales_region==context.JK.AVAILABILITY_US
|
if jamtrack.sales_region==context.JK.AVAILABILITY_US
|
||||||
|
|
@ -114,7 +138,6 @@ MIX_MODES = context.JK.MIX_MODES
|
||||||
<td className="jamtrack-action">
|
<td className="jamtrack-action">
|
||||||
<div className="jamtrack-action-container">
|
<div className="jamtrack-action-container">
|
||||||
<div className="jamtrack-actions">
|
<div className="jamtrack-actions">
|
||||||
<div className={jamtrackPricesClasses}>$ {jamtrack.price}</div>
|
|
||||||
{actionBtn}
|
{actionBtn}
|
||||||
{availabilityNotice}
|
{availabilityNotice}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -213,7 +236,7 @@ MIX_MODES = context.JK.MIX_MODES
|
||||||
.done((response) =>
|
.done((response) =>
|
||||||
@setState({jamtracks: response.jamtracks, next: response.next, searching: false, first_search: false, currentPage: 1, count: response.count})
|
@setState({jamtracks: response.jamtracks, next: response.next, searching: false, first_search: false, currentPage: 1, count: response.count})
|
||||||
)
|
)
|
||||||
.fail(() =>
|
.fail((jqXHR) =>
|
||||||
@app.notifyServerError jqXHR, 'Search Unavailable'
|
@app.notifyServerError jqXHR, 'Search Unavailable'
|
||||||
@setState({searching: false, first_search: false})
|
@setState({searching: false, first_search: false})
|
||||||
)
|
)
|
||||||
|
|
@ -300,6 +323,7 @@ MIX_MODES = context.JK.MIX_MODES
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
$target = $(e.target)
|
$target = $(e.target)
|
||||||
params = id: $target.attr('data-jamtrack-id')
|
params = id: $target.attr('data-jamtrack-id')
|
||||||
|
params.variant = $target.attr('data-variant')
|
||||||
isFree = $(e.target).is('.is_free')
|
isFree = $(e.target).is('.is_free')
|
||||||
|
|
||||||
@rest.addJamtrackToShoppingCart(params).done((response) =>
|
@rest.addJamtrackToShoppingCart(params).done((response) =>
|
||||||
|
|
@ -322,6 +346,18 @@ MIX_MODES = context.JK.MIX_MODES
|
||||||
$parent.find('.jamtrack-add-cart').on 'click', @addToCartJamtrack
|
$parent.find('.jamtrack-add-cart').on 'click', @addToCartJamtrack
|
||||||
$parent.find('.license-us-why').on 'click', @licenseUSWhy
|
$parent.find('.license-us-why').on 'click', @licenseUSWhy
|
||||||
$parent.find('.jamtrack-detail-btn').on 'click', @toggleExpanded
|
$parent.find('.jamtrack-detail-btn').on 'click', @toggleExpanded
|
||||||
|
$parent.find('.jamtrack-variant-help').on 'click', @showVariantHelp
|
||||||
|
$parent.find('.jamtrack-upgrade-help').on 'click', @showUpgradeHelp
|
||||||
|
|
||||||
|
showVariantHelp: (e) ->
|
||||||
|
$screen = $('#jamtrackFilter')
|
||||||
|
e.preventDefault()
|
||||||
|
context.JK.HelpBubbleHelper.jamtrackVariants($(e.target), $screen)
|
||||||
|
|
||||||
|
showUpgradeHelp: (e) ->
|
||||||
|
$screen = $('#jamtrackFilter')
|
||||||
|
e.preventDefault()
|
||||||
|
context.JK.HelpBubbleHelper.jamtrackUpgrade($(e.target), $screen)
|
||||||
|
|
||||||
toggleExpanded:(e) ->
|
toggleExpanded:(e) ->
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
|
||||||
|
|
@ -95,13 +95,37 @@ MIX_MODES = context.JK.MIX_MODES
|
||||||
|
|
||||||
actionBtn = null
|
actionBtn = null
|
||||||
if jamtrack.purchased
|
if jamtrack.purchased
|
||||||
actionBtn = `<a className="jamtrack-add-cart-disabled button-grey button-disabled" href="javascript:void(0)">PURCHASED</a>`
|
if jamtrack.can_download
|
||||||
|
actionBtn = `<a className="jamtrack-add-cart-disabled button-grey button-disabled" href="javascript:void(0)">PURCHASED</a>`
|
||||||
|
else
|
||||||
|
priceNotice = `<div className={jamtrackPricesClasses}>$ {Number(jamtrack.upgrade_price).toFixed(2)}</div>`
|
||||||
|
actionBtn = `<div>
|
||||||
|
<div className="jamtrack-add-zone">
|
||||||
|
{priceNotice}
|
||||||
|
<a className="jamtrack-add-cart button-orange" href="#" data-jamtrack-id={jamtrack.id} data-variant="download">UPGRADE TO FULL</a>
|
||||||
|
</div>
|
||||||
|
<a className="jamtrack-upgrade-help" href='#'>HELP</a>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
|
||||||
else if jamtrack.is_free
|
else if jamtrack.is_free
|
||||||
actionBtn = `<a className="jamtrack-add-cart button-orange is_free" href="#" data-jamtrack-id={jamtrack.id}>GET IT FREE!</a>`
|
actionBtn = `<a className="jamtrack-add-cart button-orange is_free" href="#" data-jamtrack-id={jamtrack.id} data-variant="full">GET IT FREE!</a>`
|
||||||
else if jamtrack.added_cart
|
else if jamtrack.added_cart
|
||||||
actionBtn = `<a className="jamtrack-add-cart-disabled button-grey button-disabled" href="client#/shoppingCart">ALREADY IN CART</a>`
|
actionBtn = `<a className="jamtrack-add-cart-disabled button-grey button-disabled" href="client#/shoppingCart">ALREADY IN CART</a>`
|
||||||
else
|
else
|
||||||
actionBtn = `<a className="jamtrack-add-cart button-orange" href="#" data-jamtrack-id={jamtrack.id}>ADD TO CART</a>`
|
priceNotice = `<div className={jamtrackPricesClasses}>$ {jamtrack.price}</div>`
|
||||||
|
fullPriceNotice = `<div className={jamtrackPricesClasses}>$ {jamtrack.download_price}</div>`
|
||||||
|
actionBtn = `<div>
|
||||||
|
<div className="jamtrack-add-zone">
|
||||||
|
{priceNotice}
|
||||||
|
<a className="jamtrack-add-cart button-orange" href="#" data-jamtrack-id={jamtrack.id} data-variant="stream">ADD TO CART</a>
|
||||||
|
</div>
|
||||||
|
<div className="jamtrack-add-zone">
|
||||||
|
{fullPriceNotice}
|
||||||
|
<a className="jamtrack-add-cart button-orange" href="#" data-jamtrack-id={jamtrack.id} data-variant="full">ADD TO CART (FULL)</a>
|
||||||
|
</div>
|
||||||
|
<a className="jamtrack-variant-help" href='#'>HELP</a>
|
||||||
|
</div>`
|
||||||
|
|
||||||
availabilityNotice = null
|
availabilityNotice = null
|
||||||
if jamtrack.sales_region==context.JK.AVAILABILITY_US
|
if jamtrack.sales_region==context.JK.AVAILABILITY_US
|
||||||
|
|
@ -136,7 +160,6 @@ MIX_MODES = context.JK.MIX_MODES
|
||||||
<td className="jamtrack-action">
|
<td className="jamtrack-action">
|
||||||
<div className="jamtrack-action-container">
|
<div className="jamtrack-action-container">
|
||||||
<div className="jamtrack-actions">
|
<div className="jamtrack-actions">
|
||||||
<div className={jamtrackPricesClasses}>$ {jamtrack.price}</div>
|
|
||||||
{actionBtn}
|
{actionBtn}
|
||||||
{availabilityNotice}
|
{availabilityNotice}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -326,12 +349,12 @@ MIX_MODES = context.JK.MIX_MODES
|
||||||
.done((response) =>
|
.done((response) =>
|
||||||
@setState({jamtracks: response.jamtracks, next: response.next, searching: false, first_search: false, currentPage: 1, count: response.count})
|
@setState({jamtracks: response.jamtracks, next: response.next, searching: false, first_search: false, currentPage: 1, count: response.count})
|
||||||
)
|
)
|
||||||
.fail(() =>
|
.fail((jqXHR) =>
|
||||||
@app.notifyServerError jqXHR, 'Search Unavailable'
|
@app.notifyServerError jqXHR, 'Search Unavailable'
|
||||||
@setState({searching: false, first_search: false})
|
@setState({searching: false, first_search: false})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.fail(() =>
|
.fail((jqXHR) =>
|
||||||
@app.notifyServerError jqXHR, 'Search Unavailable'
|
@app.notifyServerError jqXHR, 'Search Unavailable'
|
||||||
@setState({searching: false, first_search: false})
|
@setState({searching: false, first_search: false})
|
||||||
)
|
)
|
||||||
|
|
@ -440,6 +463,7 @@ MIX_MODES = context.JK.MIX_MODES
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
$target = $(e.target)
|
$target = $(e.target)
|
||||||
params = id: $target.attr('data-jamtrack-id')
|
params = id: $target.attr('data-jamtrack-id')
|
||||||
|
params.variant = $target.attr('data-variant')
|
||||||
isFree = $(e.target).is('.is_free')
|
isFree = $(e.target).is('.is_free')
|
||||||
|
|
||||||
@rest.addJamtrackToShoppingCart(params).done((response) =>
|
@rest.addJamtrackToShoppingCart(params).done((response) =>
|
||||||
|
|
@ -475,6 +499,18 @@ MIX_MODES = context.JK.MIX_MODES
|
||||||
$parent.find('.jamtrack-add-cart').on 'click', @addToCartJamtrack
|
$parent.find('.jamtrack-add-cart').on 'click', @addToCartJamtrack
|
||||||
$parent.find('.license-us-why').on 'click', @licenseUSWhy
|
$parent.find('.license-us-why').on 'click', @licenseUSWhy
|
||||||
$parent.find('.jamtrack-detail-btn').on 'click', @toggleExpanded
|
$parent.find('.jamtrack-detail-btn').on 'click', @toggleExpanded
|
||||||
|
$parent.find('.jamtrack-variant-help').on 'click', @showVariantHelp
|
||||||
|
$parent.find('.jamtrack-upgrade-help').on 'click', @showUpgradeHelp
|
||||||
|
|
||||||
|
showVariantHelp: (e) ->
|
||||||
|
$screen = $('#jamtrackSearch')
|
||||||
|
e.preventDefault()
|
||||||
|
context.JK.HelpBubbleHelper.jamtrackVariants($(e.target), $screen)
|
||||||
|
|
||||||
|
showUpgradeHelp: (e) ->
|
||||||
|
$screen = $('#jamtrackSearch')
|
||||||
|
e.preventDefault()
|
||||||
|
context.JK.HelpBubbleHelper.jamtrackUpgrade($(e.target), $screen)
|
||||||
|
|
||||||
toggleExpanded:(e) ->
|
toggleExpanded:(e) ->
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
|
||||||
|
|
@ -502,7 +502,7 @@ UserStore = context.UserStore
|
||||||
else if this.state.user.lesson_package_type_id == 'test-drive-2'
|
else if this.state.user.lesson_package_type_id == 'test-drive-2'
|
||||||
explanation =`<span>You are purchasing the TestDrive package of JamClass by JamKazam. This purchase entitles you to take 2 private online music lessons - 1 each from 2 different instructors in the JamClass instructor community. The price of this TestDrive package is $29.99.</span>`
|
explanation =`<span>You are purchasing the TestDrive package of JamClass by JamKazam. This purchase entitles you to take 2 private online music lessons - 1 each from 2 different instructors in the JamClass instructor community. The price of this TestDrive package is $29.99.</span>`
|
||||||
else
|
else
|
||||||
alert("You do not have a test drive package selected")
|
alert("You do not have a test drive package selected: " + this.state.user.lesson_package_type_id )
|
||||||
|
|
||||||
|
|
||||||
bookingDetail = `<p>{explanation}
|
bookingDetail = `<p>{explanation}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,160 @@
|
||||||
|
context = window
|
||||||
|
MIX_MODES = context.JK.MIX_MODES
|
||||||
|
|
||||||
|
@PayPalConfirmationScreen = React.createClass({
|
||||||
|
|
||||||
|
mixins: [Reflux.listenTo(@AppStore, "onAppInit"), Reflux.listenTo(@UserStore, "onUserChanged")]
|
||||||
|
|
||||||
|
render: () ->
|
||||||
|
content = null
|
||||||
|
|
||||||
|
|
||||||
|
if this.state.sold
|
||||||
|
|
||||||
|
if context.jamClient && context.jamClient.IsNativeClient()
|
||||||
|
platformMessage = `<div>
|
||||||
|
<p> To play your purchased JamTrack, start a session and then open the JamTrack</p>
|
||||||
|
<a className="download-jamkazam-wrapper" href="/client#/createSession">
|
||||||
|
<div className="download-jamkazam">
|
||||||
|
Click Here to Start a Session
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>`
|
||||||
|
else
|
||||||
|
platformMessage =
|
||||||
|
`<div>
|
||||||
|
<a href='/client#/jamtrack' className="download-jamkazam-wrapper jt-popup">
|
||||||
|
<div className="download-jamkazam">
|
||||||
|
Click Here to Start Using Your JamTrack
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href='/downloads' rel="external" className="download-jamkazam-wrapper" target="_blank">
|
||||||
|
<div>
|
||||||
|
Do More With Your JamTrack - Click Here to Download Our Application
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a className="back-to-browsing" href="/client#/jamtrack">
|
||||||
|
or click here to browse more jamtracks
|
||||||
|
</a>
|
||||||
|
</div>`
|
||||||
|
content = `<div className="sold-notice">
|
||||||
|
<h2>Thank you for your order!</h2>
|
||||||
|
{platformMessage}
|
||||||
|
</div>`
|
||||||
|
else
|
||||||
|
orderButtons = {"button-orange": true, "place-order-btn": true, disabled: this.state.ordering }
|
||||||
|
cancelButtons = {"button-grey": true, "cancel": true, disabled: this.state.ordering }
|
||||||
|
|
||||||
|
content = `<div>
|
||||||
|
<h2 className="confirm-header">Confirm PayPal Payment</h2>
|
||||||
|
|
||||||
|
<p>You have not yet made a payment via PayPal. Please review your purchase and confirm or cancel.</p>
|
||||||
|
|
||||||
|
<div className="controls">
|
||||||
|
<a href="#" className={classNames(orderButtons)} onClick={this.placeOrder}>CONFIRM PURCHASE WITH
|
||||||
|
PAYPAL</a>
|
||||||
|
<a href="#" className={classNames(cancelButtons)} onClick={this.cancel}>CANCEL</a>
|
||||||
|
|
||||||
|
<div className="clearall"/>
|
||||||
|
</div>
|
||||||
|
<ShoppingCartContents carts={this.state.carts}/>
|
||||||
|
|
||||||
|
<div className="controls bottom">
|
||||||
|
<a href="#" className={classNames(orderButtons)} onClick={this.placeOrder}>CONFIRM PURCHASE WITH
|
||||||
|
PAYPAL</a>
|
||||||
|
<a href="#" className={classNames(cancelButtons)} onClick={this.cancel}>CANCEL</a>
|
||||||
|
|
||||||
|
<div className="clearall"/>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
`<div className="PayPalConfirmationScreen">
|
||||||
|
<div className="content-body-scroller">
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
placeOrder: (e) ->
|
||||||
|
e.preventDefault()
|
||||||
|
if this.state.ordering
|
||||||
|
return
|
||||||
|
@setState({ordering: true})
|
||||||
|
|
||||||
|
console.log("placing order with paypal")
|
||||||
|
@rest.paypalPlaceOrder()
|
||||||
|
.done((response) =>
|
||||||
|
console.log("paypal detail obtained", response)
|
||||||
|
|
||||||
|
@setState({sold: true, ordering: false})
|
||||||
|
context.JK.JamTrackUtils.checkShoppingCart()
|
||||||
|
@app.refreshUser()
|
||||||
|
)
|
||||||
|
.fail((jqXHR) =>
|
||||||
|
@setState({ordering: false})
|
||||||
|
if jqXHR.status == 404
|
||||||
|
context.JK.Banner.showAlert('PayPal Session Over', 'Your PayPal authorization has expired. Please restart the PayPal confirmation process. <a href="/client#/checkoutPayment">Click Here to Checkout Again.</a>')
|
||||||
|
else if jqXHR.status == 422
|
||||||
|
response = JSON.parse(jqXHR.responseText)
|
||||||
|
context.JK.Banner.showAlert('PayPal Purchase Error', 'PayPal: ' + response.message)
|
||||||
|
else
|
||||||
|
context.JK.Banner.showAlert('PayPal/Sales Error', 'Please contact support@jamkazam.com')
|
||||||
|
)
|
||||||
|
|
||||||
|
cancelOrder: (e) ->
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
window.location = '/client#/jamtrack'
|
||||||
|
|
||||||
|
getInitialState: () ->
|
||||||
|
{}
|
||||||
|
|
||||||
|
componentDidMount: () ->
|
||||||
|
|
||||||
|
componentDidUpdate: () ->
|
||||||
|
|
||||||
|
afterShow: (data) ->
|
||||||
|
rest.getShoppingCarts()
|
||||||
|
.done((carts) =>
|
||||||
|
@setState({carts: carts})
|
||||||
|
if carts.length == 0
|
||||||
|
window.location = '/client#/jamtrack'
|
||||||
|
return
|
||||||
|
|
||||||
|
@rest.paypalDetail()
|
||||||
|
.done((response) =>
|
||||||
|
console.log("paypal detail obtained", response)
|
||||||
|
)
|
||||||
|
.fail((jqXHR) =>
|
||||||
|
if jqXHR.status == 404
|
||||||
|
context.JK.Banner.showAlert('PayPal Session Over', 'Your PayPal authorization has expired. Please restart the PayPal confirmation process. <a href="/client#/checkoutPayment">Click Here to Checkout Again.</a>')
|
||||||
|
else if jqXHR.status == 422
|
||||||
|
response = JSON.parse(jqXHR.responseText)
|
||||||
|
context.JK.Banner.showAlert('PayPal Purchase Error', 'PayPal: ' + response.message)
|
||||||
|
else
|
||||||
|
context.JK.Banner.showAlert('PayPal/Sales Error', 'Please contact support@jamkazam.com')
|
||||||
|
@app.notifyServerError jqXHR, 'PayPal Communication Error'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.fail((jqXHR) =>
|
||||||
|
@app.notifyServerError jqXHR, 'Unable to fetch carts'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
beforeShow: () ->
|
||||||
|
this.setState({sold: false})
|
||||||
|
|
||||||
|
onAppInit: (@app) ->
|
||||||
|
@EVENTS = context.JK.EVENTS
|
||||||
|
@rest = context.JK.Rest()
|
||||||
|
@logger = context.JK.logger
|
||||||
|
|
||||||
|
screenBindings =
|
||||||
|
'beforeShow': @beforeShow
|
||||||
|
'afterShow': @afterShow
|
||||||
|
|
||||||
|
@app.bindScreen('paypal/confirm', screenBindings)
|
||||||
|
|
||||||
|
onUserChanged: (userState) ->
|
||||||
|
@user = userState?.user
|
||||||
|
|
||||||
|
})
|
||||||
|
|
@ -69,7 +69,7 @@ JamTrackPlayerStore = reactContext.JamTrackPlayerStore
|
||||||
|
|
||||||
new window.Fingerprint2().get((result, components) => (
|
new window.Fingerprint2().get((result, components) => (
|
||||||
redirectTo = "/api/mixdowns/#{@state.mixdown.id}/download.mp3?file_type=mp3&sample_rate=#{@sampleRate}&download=1&mark=#{result}"
|
redirectTo = "/api/mixdowns/#{@state.mixdown.id}/download.mp3?file_type=mp3&sample_rate=#{@sampleRate}&download=1&mark=#{result}"
|
||||||
redirectTo = URI.escape(redirectTo)
|
redirectTo = encodeURIComponent(redirectTo)
|
||||||
AppActions.openExternalUrl(window.location.protocol + '//' + window.location.host + "/signin?redirect-to=#{redirectTo}")
|
AppActions.openExternalUrl(window.location.protocol + '//' + window.location.host + "/signin?redirect-to=#{redirectTo}")
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ rest = context.JK.Rest()
|
||||||
mixins = []
|
mixins = []
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# make sure this is actually us opening the window, not someone else (by checking for MixerStore)
|
# make sure this is actually us opening the window, not someone else (by checking for MixerStore)
|
||||||
# this check ensures we attempt to listen if this component is created in a popup
|
# this check ensures we attempt to listen if this component is created in a popup
|
||||||
reactContext = if window.opener? then window.opener else window
|
reactContext = if window.opener? then window.opener else window
|
||||||
|
|
@ -38,7 +37,8 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
weight = switch
|
weight = switch
|
||||||
when jam_track_track.track_type == 'Master' then 0
|
when jam_track_track.track_type == 'Master' then 0
|
||||||
when jam_track_track.track_type == 'Click' then 10000
|
when jam_track_track.track_type == 'Click' then 10000
|
||||||
else jam_track_track.position
|
else
|
||||||
|
jam_track_track.position
|
||||||
|
|
||||||
onJamTrackPlayerStoreChanged: (changes) ->
|
onJamTrackPlayerStoreChanged: (changes) ->
|
||||||
#logger.debug("PopupMediaControls: jamtrack changed", changes)
|
#logger.debug("PopupMediaControls: jamtrack changed", changes)
|
||||||
|
|
@ -69,7 +69,6 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
AppActions.openExternalUrl($(e.target).attr('href'))
|
AppActions.openExternalUrl($(e.target).attr('href'))
|
||||||
|
|
||||||
render: () ->
|
render: () ->
|
||||||
|
|
||||||
closeLinkText = null
|
closeLinkText = null
|
||||||
header = null
|
header = null
|
||||||
extraControls = null
|
extraControls = null
|
||||||
|
|
@ -90,17 +89,17 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
if selectedMixdown.client_state?
|
if selectedMixdown.client_state?
|
||||||
switch selectedMixdown.client_state
|
switch selectedMixdown.client_state
|
||||||
when 'download_fail'
|
when 'download_fail'
|
||||||
customMixName = `<h5>{selectedMixdown.name}<img src="/assets/content/icon-mix-fail@2X.png" /></h5>`
|
customMixName = `<h5>{selectedMixdown.name}<img src="/assets/content/icon-mix-fail@2X.png"/></h5>`
|
||||||
when 'downloading'
|
when 'downloading'
|
||||||
customMixName = `<h5>Loading selected mix... <img src="/assets/shared/spinner.gif" /></h5>`
|
customMixName = `<h5>Loading selected mix... <img src="/assets/shared/spinner.gif"/></h5>`
|
||||||
when 'ready'
|
when 'ready'
|
||||||
customMixName = `<h5>{selectedMixdown.name}</h5>`
|
customMixName = `<h5>{selectedMixdown.name}</h5>`
|
||||||
disabled = false
|
disabled = false
|
||||||
else
|
else
|
||||||
if selectedMixdown.myPackage
|
if selectedMixdown.myPackage
|
||||||
customMixName = `<h5>Creating mixdown... <img src="/assets/shared/spinner.gif" /></h5>`
|
customMixName = `<h5>Creating mixdown... <img src="/assets/shared/spinner.gif"/></h5>`
|
||||||
else
|
else
|
||||||
customMixName = `<h5>{selectedMixdown.name}<img src="/assets/content/icon-mix-fail@2X.png" /></h5>`
|
customMixName = `<h5>{selectedMixdown.name}<img src="/assets/content/icon-mix-fail@2X.png"/></h5>`
|
||||||
|
|
||||||
else if selectedStem?
|
else if selectedStem?
|
||||||
if selectedStem.instrument
|
if selectedStem.instrument
|
||||||
|
|
@ -117,20 +116,20 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
if selectedStem.client_state?
|
if selectedStem.client_state?
|
||||||
switch selectedStem.client_state
|
switch selectedStem.client_state
|
||||||
when 'downloading'
|
when 'downloading'
|
||||||
customMixName = `<h5>Loading {trackName}... <img src="/assets/shared/spinner.gif" /></h5>`
|
customMixName = `<h5>Loading {trackName}... <img src="/assets/shared/spinner.gif"/></h5>`
|
||||||
when 'download_fail'
|
when 'download_fail'
|
||||||
customMixName = `<h5>{trackName}<img src="/assets/content/icon-mix-fail@2X.png" /></h5>`
|
customMixName = `<h5>{trackName}<img src="/assets/content/icon-mix-fail@2X.png"/></h5>`
|
||||||
when 'ready'
|
when 'ready'
|
||||||
customMixName = `<h5>{trackName}</h5>`
|
customMixName = `<h5>{trackName}</h5>`
|
||||||
disabled = false
|
disabled = false
|
||||||
else
|
else
|
||||||
customMixName = `<h5>{trackName}<img src="/assets/content/icon-mix-fail@2X.png" /></h5>`
|
customMixName = `<h5>{trackName}<img src="/assets/content/icon-mix-fail@2X.png"/></h5>`
|
||||||
|
|
||||||
else
|
else
|
||||||
if jamTrack?.client_state == 'downloading'
|
if jamTrack?.client_state == 'downloading'
|
||||||
downloader = `<img src="/assets/shared/spinner.gif" />`
|
downloader = `<img src="/assets/shared/spinner.gif"/>`
|
||||||
else if jamTrack?.client_state == 'download_fail'
|
else if jamTrack?.client_state == 'download_fail'
|
||||||
downloader = `<img src="/assets/content/icon-mix-fail@2X.png" />`
|
downloader = `<img src="/assets/content/icon-mix-fail@2X.png"/>`
|
||||||
|
|
||||||
jamTrackTypeHeader = `<span>Full JamTrack {downloader}</span>`
|
jamTrackTypeHeader = `<span>Full JamTrack {downloader}</span>`
|
||||||
|
|
||||||
|
|
@ -153,15 +152,19 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
active = jamTrack.last_mixdown_id == null && jamTrack.last_stem_id == null
|
active = jamTrack.last_mixdown_id == null && jamTrack.last_stem_id == null
|
||||||
|
|
||||||
myMixdowns.push `
|
myMixdowns.push `
|
||||||
<div key="full-track" className={classNames({'full-track': true, 'mixdown-display': true, 'active' : active})}>
|
<div key="full-track"
|
||||||
|
className={classNames({'full-track': true, 'mixdown-display': true, 'active' : active})}>
|
||||||
<div className="mixdown-name">
|
<div className="mixdown-name">
|
||||||
Full JamTrack
|
Full JamTrack
|
||||||
</div>
|
</div>
|
||||||
<div className="mixdown-actions">
|
<div className="mixdown-actions">
|
||||||
<img src="/assets/content/icon_open@2X.png" className="mixdown-play" onClick={boundPlayClick}/>
|
<img src="/assets/content/icon_open@2X.png" className="mixdown-play" onClick={boundPlayClick}/>
|
||||||
<a href={this.createJamTrackUrl(jamTrack)} target="_blank" onClick={boundDownloadClick}><img src="/assets/content/icon_download@2X.png" className="mixdown-download"/></a>
|
<a href={this.createJamTrackUrl(jamTrack)} target="_blank" onClick={boundDownloadClick}><img
|
||||||
<img src ="/assets/content/icon-delete@2X.png" style={{visibility:'hidden'}} className="mixdown-edit" onClick={false} />
|
src="/assets/content/icon_download@2X.png" className="mixdown-download"/></a>
|
||||||
<img src ="/assets/content/icon-delete@2X.png" style={{visibility:'hidden'}} className="mixdown-delete" onClick={false} />
|
<img src="/assets/content/icon-delete@2X.png" style={{visibility:'hidden'}} className="mixdown-edit"
|
||||||
|
onClick={false}/>
|
||||||
|
<img src="/assets/content/icon-delete@2X.png" style={{visibility:'hidden'}} className="mixdown-delete"
|
||||||
|
onClick={false}/>
|
||||||
</div>
|
</div>
|
||||||
</div>`
|
</div>`
|
||||||
|
|
||||||
|
|
@ -186,26 +189,33 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
if mixdown_package
|
if mixdown_package
|
||||||
switch mixdown_package.signing_state
|
switch mixdown_package.signing_state
|
||||||
when 'QUIET_TIMEOUT'
|
when 'QUIET_TIMEOUT'
|
||||||
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play" onClick={boundErrorClick}/>`
|
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play"
|
||||||
|
onClick={boundErrorClick}/>`
|
||||||
when 'QUIET'
|
when 'QUIET'
|
||||||
action = `<img src="/assets/shared/spinner.gif" className="mixdown-play"/>`
|
action = `<img src="/assets/shared/spinner.gif" className="mixdown-play"/>`
|
||||||
when 'QUEUED'
|
when 'QUEUED'
|
||||||
action = `<img src="/assets/shared/spinner.gif" className="mixdown-play"/>`
|
action = `<img src="/assets/shared/spinner.gif" className="mixdown-play"/>`
|
||||||
when 'QUEUED_TIMEOUT'
|
when 'QUEUED_TIMEOUT'
|
||||||
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play" onClick={boundErrorClick}/>`
|
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play"
|
||||||
|
onClick={boundErrorClick}/>`
|
||||||
when 'SIGNING'
|
when 'SIGNING'
|
||||||
action = `<img src="/assets/shared/spinner.gif" className="mixdown-play"/>`
|
action = `<img src="/assets/shared/spinner.gif" className="mixdown-play"/>`
|
||||||
when 'SIGNING_TIMEOUT'
|
when 'SIGNING_TIMEOUT'
|
||||||
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play" onClick={boundErrorClick}/>`
|
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play"
|
||||||
|
onClick={boundErrorClick}/>`
|
||||||
when 'SIGNED'
|
when 'SIGNED'
|
||||||
action = `<img src="/assets/content/icon_open@2X.png" className="mixdown-play" onClick={boundPlayClick}/>`
|
action = `<img src="/assets/content/icon_open@2X.png" className="mixdown-play"
|
||||||
|
onClick={boundPlayClick}/>`
|
||||||
when 'ERROR'
|
when 'ERROR'
|
||||||
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play" onClick={boundErrorClick}/>`
|
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play"
|
||||||
|
onClick={boundErrorClick}/>`
|
||||||
else
|
else
|
||||||
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play" onClick={boundErrorClick}/>`
|
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play"
|
||||||
|
onClick={boundErrorClick}/>`
|
||||||
|
|
||||||
if editing
|
if editing
|
||||||
mixdownName = `<input className="edit-name" type="text" defaultValue={mixdown.name} onKeyDown={boundEditKeydown} />`
|
mixdownName = `<input className="edit-name" type="text" defaultValue={mixdown.name}
|
||||||
|
onKeyDown={boundEditKeydown}/>`
|
||||||
editIcon = `<img src="/assets/content/icon-save@2X.png" className="mixdown-edit" onClick={boundSaveClick}/>`
|
editIcon = `<img src="/assets/content/icon-save@2X.png" className="mixdown-edit" onClick={boundSaveClick}/>`
|
||||||
else
|
else
|
||||||
mixdownName = mixdown.name
|
mixdownName = mixdown.name
|
||||||
|
|
@ -213,9 +223,11 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
|
|
||||||
# create hidden objects to deal with alginment issues using a table
|
# create hidden objects to deal with alginment issues using a table
|
||||||
if !editIcon
|
if !editIcon
|
||||||
editIcon = `<img src ="/assets/content/icon-delete@2X.png" style={{visibility:'hidden'}} className="mixdown-edit" onClick={false} />`
|
editIcon = `<img src="/assets/content/icon-delete@2X.png" style={{visibility:'hidden'}}
|
||||||
|
className="mixdown-edit" onClick={false}/>`
|
||||||
|
|
||||||
download = `<a onClick={boundDownloadReadyClick} href={this.downloadMixdownUrl(mixdown)} target="_blank"><img src="/assets/content/icon_download@2X.png" className="mixdown-download" /></a>`
|
download = `<a onClick={boundDownloadReadyClick} href={this.downloadMixdownUrl(mixdown)} target="_blank"><img
|
||||||
|
src="/assets/content/icon_download@2X.png" className="mixdown-download"/></a>`
|
||||||
|
|
||||||
myMixdowns.push `
|
myMixdowns.push `
|
||||||
<div key={mixdown.id} className={classNames({'mixdown-display': true, 'active' : active})}>
|
<div key={mixdown.id} className={classNames({'mixdown-display': true, 'active' : active})}>
|
||||||
|
|
@ -227,7 +239,7 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
{download}
|
{download}
|
||||||
{editIcon}
|
{editIcon}
|
||||||
|
|
||||||
<img src ="/assets/content/icon-delete@2X.png" className="mixdown-delete" onClick={boundDeleteClick} />
|
<img src="/assets/content/icon-delete@2X.png" className="mixdown-delete" onClick={boundDeleteClick}/>
|
||||||
</div>
|
</div>
|
||||||
</div>`
|
</div>`
|
||||||
|
|
||||||
|
|
@ -260,16 +272,21 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
myMixdowns.push `
|
myMixdowns.push `
|
||||||
<div key={track.id} className={classNames({'stem-track' : true, 'mixdown-display': true, 'active' : active})}>
|
<div key={track.id} className={classNames({'stem-track' : true, 'mixdown-display': true, 'active' : active})}>
|
||||||
<div className="mixdown-name">
|
<div className="mixdown-name">
|
||||||
<select className="active-stem-select" name="active-stem-select" onChange={boundStemChange} defaultValue={jamTrack.last_stem_id}>
|
<select className="active-stem-select" name="active-stem-select" onChange={boundStemChange}
|
||||||
|
defaultValue={jamTrack.last_stem_id}>
|
||||||
<option key="null" value="">or select a track</option>
|
<option key="null" value="">or select a track</option>
|
||||||
{trackOptions}
|
{trackOptions}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div className="mixdown-actions">
|
<div className="mixdown-actions">
|
||||||
<img src="/assets/content/icon_open@2X.png" className="mixdown-play" onClick={boundStemActivateClick}/>
|
<img src="/assets/content/icon_open@2X.png" className="mixdown-play" onClick={boundStemActivateClick}/>
|
||||||
<a href={this.createStemUrl(jamTrack.id, jamTrack.last_stem_id)} target="_blank" onClick={boundStemPlayClick}><img src="/assets/content/icon_download@2X.png" className="mixdown-download" /></a>
|
<a href={this.createStemUrl(jamTrack.id, jamTrack.last_stem_id)} target="_blank"
|
||||||
<img src ="/assets/content/icon-delete@2X.png" style={{visibility:'hidden'}} className="mixdown-edit" onClick={false} />
|
onClick={boundStemPlayClick}><img src="/assets/content/icon_download@2X.png"
|
||||||
<img src ="/assets/content/icon-delete@2X.png" style={{visibility:'hidden'}} className="mixdown-delete" onClick={false} />
|
className="mixdown-download"/></a>
|
||||||
|
<img src="/assets/content/icon-delete@2X.png" style={{visibility:'hidden'}} className="mixdown-edit"
|
||||||
|
onClick={false}/>
|
||||||
|
<img src="/assets/content/icon-delete@2X.png" style={{visibility:'hidden'}} className="mixdown-delete"
|
||||||
|
onClick={false}/>
|
||||||
</div>
|
</div>
|
||||||
</div>`
|
</div>`
|
||||||
|
|
||||||
|
|
@ -300,23 +317,29 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
|
|
||||||
tracks.push(`
|
tracks.push(`
|
||||||
<tr className="stem">
|
<tr className="stem">
|
||||||
<td><img src={context.JK.getInstrumentIcon24(instrumentId)} className="instrument-icon" /> {instrumentDescription} {part}</td>
|
<td><img src={context.JK.getInstrumentIcon24(instrumentId)}
|
||||||
<td className="mute"><input type="checkbox" className="stem-mute" data-stem-id={track.id} defaultChecked={track.track_type == 'Click'}/></td>
|
className="instrument-icon"/> {instrumentDescription} {part}</td>
|
||||||
|
<td className="mute"><input type="checkbox" className="stem-mute" data-stem-id={track.id}
|
||||||
|
defaultChecked={track.track_type == 'Click'}/></td>
|
||||||
</tr>`)
|
</tr>`)
|
||||||
|
|
||||||
if jamTrack?.jmep?.Events? && jamTrack.jmep.Events[0].metronome?
|
if jamTrack?.jmep?.Events? && jamTrack.jmep.Events[0].metronome?
|
||||||
# tap-in detected; show user tap-in option
|
# tap-in detected; show user tap-in option
|
||||||
tracks.push(`
|
tracks.push(`
|
||||||
<tr className="stem">
|
<tr className="stem">
|
||||||
<td><img src={context.JK.getInstrumentIcon24('computer')} className="instrument-icon" /> Count-in</td>
|
<td><img src={context.JK.getInstrumentIcon24('computer')} className="instrument-icon"/> Count-in</td>
|
||||||
<td className="mute"><input type="checkbox" className="stem-mute" data-stem-id="count-in" /></td>
|
<td className="mute"><input type="checkbox" className="stem-mute" data-stem-id="count-in"/></td>
|
||||||
</tr>`)
|
</tr>`)
|
||||||
|
|
||||||
stems = `<div key="stems" className="stems">
|
stems = `<div key="stems" className="stems">
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<col align="left" /><col align="right"/>
|
<col align="left"/>
|
||||||
<tr><th>TRACKS</th><th className="mute">mute</th></tr>
|
<col align="right"/>
|
||||||
|
<tr>
|
||||||
|
<th>TRACKS</th>
|
||||||
|
<th className="mute">mute</th>
|
||||||
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{tracks}
|
{tracks}
|
||||||
|
|
@ -328,13 +351,15 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
nameClassData = {field: true}
|
nameClassData = {field: true}
|
||||||
if @state.createMixdownErrors?
|
if @state.createMixdownErrors?
|
||||||
|
|
||||||
errorHtml = context.JK.reactErrors(@state.createMixdownErrors, {name: 'Mix Name', settings: 'Settings', jam_track: 'JamTrack'})
|
errorHtml = context.JK.reactErrors(@state.createMixdownErrors,
|
||||||
|
{name: 'Mix Name', settings: 'Settings', jam_track: 'JamTrack'})
|
||||||
|
|
||||||
createMixClasses = classNames({'button-orange' : true, 'create-mix-btn' : true, 'disabled' : @state.creatingMixdown})
|
createMixClasses = classNames({'button-orange': true, 'create-mix-btn': true, 'disabled': @state.creatingMixdown})
|
||||||
|
|
||||||
mixControls = `
|
mixControls = `
|
||||||
<div key="create-mix" className="create-mix">
|
<div key="create-mix" className="create-mix">
|
||||||
<p>Mute or unmute any tracks you like. You can also use the controls below to adjust the tempo or pitch of the JamTrack. Then give your custom mix a name, and click the Create Mix button.</p>
|
<p>Mute or unmute any tracks you like. You can also use the controls below to adjust the tempo or pitch of the
|
||||||
|
JamTrack. Then give your custom mix a name, and click the Create Mix button.</p>
|
||||||
{stems}
|
{stems}
|
||||||
<div className="field">
|
<div className="field">
|
||||||
<label>TEMPO</label>
|
<label>TEMPO</label>
|
||||||
|
|
@ -410,14 +435,22 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
|
|
||||||
|
|
||||||
if @state.showMyMixes
|
if @state.showMyMixes
|
||||||
showMyMixesText = `<a className="show-hide-my-mixes" onClick={this.toggleMyMixes}>hide my mixes <div className="details-arrow arrow-up" /></a>`
|
showMyMixesText = `<a className="show-hide-my-mixes" onClick={this.toggleMyMixes}>hide my mixes
|
||||||
|
<div className="details-arrow arrow-up"/>
|
||||||
|
</a>`
|
||||||
else
|
else
|
||||||
showMyMixesText = `<a className="show-hide-my-mixes" onClick={this.toggleMyMixes}>show my mixes <div className="details-arrow arrow-down" /></a>`
|
showMyMixesText = `<a className="show-hide-my-mixes" onClick={this.toggleMyMixes}>show my mixes
|
||||||
|
<div className="details-arrow arrow-down"/>
|
||||||
|
</a>`
|
||||||
|
|
||||||
if @state.showCustomMixes
|
if @state.showCustomMixes
|
||||||
showMixControlsText = `<a className="show-hide-mix-controls" onClick={this.toggleCustomMixes}>hide mix controls <div className="details-arrow arrow-up" /></a>`
|
showMixControlsText = `<a className="show-hide-mix-controls" onClick={this.toggleCustomMixes}>hide mix controls
|
||||||
|
<div className="details-arrow arrow-up"/>
|
||||||
|
</a>`
|
||||||
else
|
else
|
||||||
showMixControlsText = `<a className="show-hide-mix-controls" onClick={this.toggleCustomMixes}>show mix controls <div className="details-arrow arrow-down" /></a>`
|
showMixControlsText = `<a className="show-hide-mix-controls" onClick={this.toggleCustomMixes}>show mix controls
|
||||||
|
<div className="details-arrow arrow-down"/>
|
||||||
|
</a>`
|
||||||
|
|
||||||
extraControls = `
|
extraControls = `
|
||||||
<div className="extra-controls">
|
<div className="extra-controls">
|
||||||
|
|
@ -432,20 +465,65 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
</div>`
|
</div>`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if helpLink?
|
if helpLink?
|
||||||
helpButton = `<a className="help-link button-grey" href={helpLink} target="_blank">HELP</a>`
|
helpButton = `<a className="help-link button-grey" href={helpLink} target="_blank">HELP</a>`
|
||||||
|
|
||||||
`<div className="media-controls-popup">
|
`<div className="jamtrack-player-main">
|
||||||
{header}
|
<div className="jamtrack-player-controls">
|
||||||
<BrowserMediaControls disabled={this.disableLoading}/>
|
<div className="media-header">
|
||||||
{extraControls}
|
jamtracks web player
|
||||||
<div className="actions">
|
</div>
|
||||||
{helpButton}
|
<div className="media-controls-popup">
|
||||||
|
{header}
|
||||||
|
<BrowserMediaControls disabled={this.disableLoading}/>
|
||||||
|
{extraControls}
|
||||||
|
<div className="actions">
|
||||||
|
{helpButton}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="helpful-resources">
|
||||||
|
<div className="helpful-header">
|
||||||
|
helpful resources
|
||||||
|
</div>
|
||||||
|
<div className="helpful-section">
|
||||||
|
<a href="https://jamkazam.desk.com/customer/portal/articles/2166273-playing-your-jamtracks-in-a-browser" target="_blank">read a web player help article</a><br/>
|
||||||
|
that explains how to use all the features of this web player
|
||||||
|
</div>
|
||||||
|
<div className="helpful-section">
|
||||||
|
<a href="/client#/jamtrack" target="_blank">go to JamTracks homepage</a><br/>
|
||||||
|
where you can access all of your JamTracks, search for more JamTracks, and more
|
||||||
|
</div>
|
||||||
|
<div className="helpful-section">
|
||||||
|
<a href="https://itunes.apple.com/us/app/jamtracks/id1060927816?mt=8" target="_blank">download our free iOS app</a><br/>
|
||||||
|
that you can use to play with your JamTracks on your iPhone, iPad, or iPod Touch
|
||||||
|
</div>
|
||||||
|
<div className="helpful-section">
|
||||||
|
<a href="/downloads" target="_blank">download our free Mac or Windows app</a><br/>
|
||||||
|
that gives you more powerful features to do more amazing things with your JamTracks
|
||||||
|
</div>
|
||||||
|
<div className="helpful-section">
|
||||||
|
<a href="https://jamkazam.desk.com/customer/en/portal/articles/2126113-playing-your-jamtracks-in-our-free-windows-or-mac-app" target="_blank">review a list of help articles on the Mac/Win app</a><br/>
|
||||||
|
to understand all the things you can do with our free desktop app
|
||||||
|
</div>
|
||||||
|
<div className="helpful-section">
|
||||||
|
<a onClick={this.seeAllByArtist} target="_blank">see more JamTracks by this artist</a><br/>
|
||||||
|
to check out other songs you might like
|
||||||
|
</div>
|
||||||
|
<div className="helpful-section">
|
||||||
|
<a href="https://s3.amazonaws.com/jamkazam-public/public/lists/all-jamkazam-jamtracks.pdf" target="_blank">download our complete JamTracks catalog</a><br/>
|
||||||
|
to browse or search through all the music we have in a convenient PDF file
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`
|
</div>`
|
||||||
|
|
||||||
# <a className="close-link button-orange" onClick={this.close}>{closeLinkText}</a>
|
seeAllByArtist: (e) ->
|
||||||
|
e.preventDefault()
|
||||||
|
jamTrack = @state.jamTrackState?.jamTrack
|
||||||
|
|
||||||
|
console.log("seeAllByArtist context", jamTrack)
|
||||||
|
window.open('/client?artist=' + encodeURIComponent(jamTrack.original_artist) + '#/jamtrack/search')
|
||||||
|
|
||||||
windowUnloaded: () ->
|
windowUnloaded: () ->
|
||||||
JamTrackPlayerActions.windowUnloaded() unless window.DontAutoCloseMedia
|
JamTrackPlayerActions.windowUnloaded() unless window.DontAutoCloseMedia
|
||||||
|
|
||||||
|
|
@ -464,14 +542,12 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
verificationCheck: () ->
|
verificationCheck: () ->
|
||||||
|
|
||||||
if @state.user?.email_needs_verification
|
if @state.user?.email_needs_verification
|
||||||
alert("Check your email and click the verification link. Refresh this page when done, and try again.")
|
alert("Check your email and click the verification link. Refresh this page when done, and try again.")
|
||||||
|
|
||||||
return @state.user?.email_needs_verification
|
return @state.user?.email_needs_verification
|
||||||
|
|
||||||
downloadMixdownReady: (mixdown, e) ->
|
downloadMixdownReady: (mixdown, e) ->
|
||||||
|
|
||||||
if @verificationCheck()
|
if @verificationCheck()
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
return
|
return
|
||||||
|
|
@ -494,7 +570,6 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
window.location.protocol + '//' + window.location.host + "/api/mixdowns/#{mixdown.id}/download.mp3?file_type=mp3&sample_rate=48&download=1&mark=#{result}"
|
window.location.protocol + '//' + window.location.host + "/api/mixdowns/#{mixdown.id}/download.mp3?file_type=mp3&sample_rate=48&download=1&mark=#{result}"
|
||||||
|
|
||||||
activateStem: (e) ->
|
activateStem: (e) ->
|
||||||
|
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
return if @verificationCheck()
|
return if @verificationCheck()
|
||||||
|
|
@ -506,7 +581,6 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
if !selectedTrackId? || selectedTrackId == ''
|
if !selectedTrackId? || selectedTrackId == ''
|
||||||
alert("You must pick a track from the dropdown in order to play it.")
|
alert("You must pick a track from the dropdown in order to play it.")
|
||||||
else
|
else
|
||||||
|
|
||||||
@setState({editingMixdownId: null})
|
@setState({editingMixdownId: null})
|
||||||
|
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
@ -518,8 +592,38 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
# make this package the active one
|
# make this package the active one
|
||||||
JamTrackPlayerActions.openStem(selectedTrackId)
|
JamTrackPlayerActions.openStem(selectedTrackId)
|
||||||
|
|
||||||
downloadStem: (e) ->
|
addUpgradeToCart: (jamtrack) ->
|
||||||
|
console.log("adding upgrade to cart")
|
||||||
|
|
||||||
|
params = {id: jamtrack.id, variant: 'download'}
|
||||||
|
|
||||||
|
rest.addJamtrackToShoppingCart(params).done((response) =>
|
||||||
|
console.log("added item to shopping cart. fast_redeem? " + response.fast_redeem)
|
||||||
|
if response.fast_reedem
|
||||||
|
if context.JK.currentUserId?
|
||||||
|
window.open('/client#/redeemComplete')
|
||||||
|
else
|
||||||
|
window.open('/client#/redeemSignup')
|
||||||
|
else
|
||||||
|
window.open('/client#/shoppingCart')
|
||||||
|
|
||||||
|
).fail(((jqxhr) =>
|
||||||
|
|
||||||
|
handled = false
|
||||||
|
if jqxhr.status == 422
|
||||||
|
body = JSON.parse(jqxhr.responseText)
|
||||||
|
if body.errors?.cart_id?[0] == 'has already been taken'
|
||||||
|
console.log("already taken, just show shopping cart")
|
||||||
|
window.open('/client#/shoppingCart')
|
||||||
|
return
|
||||||
|
else if body.errors && body.errors.base
|
||||||
|
handled = true
|
||||||
|
alert("You can not have a mix of free and non-free items in your shopping cart.\n\nIf you want to add this new item to your shopping cart, then clear out all current items by clicking on the shopping cart icon and clicking 'delete' next to each item.")
|
||||||
|
if !handled
|
||||||
|
alert("Error adding to shoppig cart. " + jqxhr.responseText)
|
||||||
|
))
|
||||||
|
|
||||||
|
downloadStem: (e) ->
|
||||||
if @verificationCheck()
|
if @verificationCheck()
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
return
|
return
|
||||||
|
|
@ -528,6 +632,12 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
|
|
||||||
selectedTrackId = $select.val()
|
selectedTrackId = $select.val()
|
||||||
|
|
||||||
|
if !@state.jamTrackState.jamTrack.can_download
|
||||||
|
e.preventDefault()
|
||||||
|
if confirm("You have not purchased the rights to download a JamTrack. Add them to your shopping cart?")
|
||||||
|
@addUpgradeToCart(@state.jamTrackState.jamTrack)
|
||||||
|
return
|
||||||
|
|
||||||
if !selectedTrackId? || selectedTrackId == ''
|
if !selectedTrackId? || selectedTrackId == ''
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
alert("You must select a track in order to download and also click the folder icon.")
|
alert("You must select a track in order to download and also click the folder icon.")
|
||||||
|
|
@ -581,11 +691,17 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
JamTrackPlayerActions.activateNoMixdown(jamtrack)
|
JamTrackPlayerActions.activateNoMixdown(jamtrack)
|
||||||
|
|
||||||
jamTrackDownload: (jamTrack, e) ->
|
jamTrackDownload: (jamTrack, e) ->
|
||||||
|
|
||||||
if @verificationCheck()
|
if @verificationCheck()
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if !@state.jamTrackState.jamTrack.can_download
|
||||||
|
e.preventDefault()
|
||||||
|
if confirm("You have not purchased the rights to download a JamTrack. Add them to your shopping cart?")
|
||||||
|
@addUpgradeToCart(@state.jamTrackState.jamTrack)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
if /iPhone|iPad|iPod/i.test(navigator.userAgent)
|
if /iPhone|iPad|iPod/i.test(navigator.userAgent)
|
||||||
# fall through
|
# fall through
|
||||||
|
|
||||||
|
|
@ -622,14 +738,13 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
|
|
||||||
mixdownDelete: (mixdown) ->
|
mixdownDelete: (mixdown) ->
|
||||||
if @state.editingMixdownId?
|
if @state.editingMixdownId?
|
||||||
@setState({editingMixdownId:null})
|
@setState({editingMixdownId: null})
|
||||||
return
|
return
|
||||||
|
|
||||||
if confirm("Delete this custom mix?")
|
if confirm("Delete this custom mix?")
|
||||||
JamTrackPlayerActions.deleteMixdown(mixdown)
|
JamTrackPlayerActions.deleteMixdown(mixdown)
|
||||||
|
|
||||||
mixdownError: (mixdown) ->
|
mixdownError: (mixdown) ->
|
||||||
|
|
||||||
myPackage = mixdown.myPackage
|
myPackage = mixdown.myPackage
|
||||||
|
|
||||||
if myPackage?
|
if myPackage?
|
||||||
|
|
@ -709,7 +824,11 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
tracks.push({id: stemId, mute: muted})
|
tracks.push({id: stemId, mute: muted})
|
||||||
)
|
)
|
||||||
|
|
||||||
mixdown = {jamTrackID: @state.jamTrackState.jamTrack.id, name: name, settings: {speed:speed, pitch: pitch, "count-in": count_in, tracks:tracks}}
|
mixdown = {
|
||||||
|
jamTrackID: @state.jamTrackState.jamTrack.id,
|
||||||
|
name: name,
|
||||||
|
settings: {speed: speed, pitch: pitch, "count-in": count_in, tracks: tracks}
|
||||||
|
}
|
||||||
|
|
||||||
JamTrackPlayerActions.createMixdown(mixdown, @createMixdownDone, @createMixdownFail)
|
JamTrackPlayerActions.createMixdown(mixdown, @createMixdownDone, @createMixdownFail)
|
||||||
|
|
||||||
|
|
@ -743,7 +862,6 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
fetchUserInfo: () ->
|
fetchUserInfo: () ->
|
||||||
rest.getUserDetail()
|
rest.getUserDetail()
|
||||||
.done((response) =>
|
.done((response) =>
|
||||||
|
|
||||||
rest.postUserEvent({name: 'jamtrack_web_player_open'})
|
rest.postUserEvent({name: 'jamtrack_web_player_open'})
|
||||||
context.stats.write('web.jamtrack_web_player.open', {
|
context.stats.write('web.jamtrack_web_player.open', {
|
||||||
value: 1,
|
value: 1,
|
||||||
|
|
@ -757,13 +875,12 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
rest.userOpenedJamTrackWebPlayer()
|
rest.userOpenedJamTrackWebPlayer()
|
||||||
$root = $(@getDOMNode())
|
$root = $(@getDOMNode())
|
||||||
#context.JK.prodBubble($root.find('.create-mix-btn'), 'first-time-jamtrack-web-player', {}, {positions:['left'], offsetParent: $root})
|
#context.JK.prodBubble($root.find('.create-mix-btn'), 'first-time-jamtrack-web-player', {}, {positions:['left'], offsetParent: $root})
|
||||||
), 1500)
|
), 1500)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
componentDidMount: () ->
|
componentDidMount: () ->
|
||||||
|
|
||||||
$(window).unload(@windowUnloaded)
|
$(window).unload(@windowUnloaded)
|
||||||
|
|
||||||
@root = jQuery(this.getDOMNode())
|
@root = jQuery(this.getDOMNode())
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
context = window
|
context = window
|
||||||
logger = context.JK.logger
|
logger = context.JK.logger
|
||||||
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
|
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
|
||||||
|
rest = context.JK.Rest()
|
||||||
|
|
||||||
mixins = []
|
mixins = []
|
||||||
|
|
||||||
|
|
@ -523,12 +523,49 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
|
|
||||||
return if @verificationCheck()
|
return if @verificationCheck()
|
||||||
|
|
||||||
|
if !@state.jamTrackState.jamTrack.can_download
|
||||||
|
e.preventDefault()
|
||||||
|
if confirm("You have not purchased the rights to download a JamTrack. Add them to your shopping cart?")
|
||||||
|
@addUpgradeToCart(@state.jamTrackState.jamTrack)
|
||||||
|
return
|
||||||
|
|
||||||
new window.Fingerprint2().get((result, components) => (
|
new window.Fingerprint2().get((result, components) => (
|
||||||
redirectTo = "/api/jamtracks/#{jamTrack.id}/stems/master/download.mp3?file_type=mp3&download=1&mark=#{result}"
|
redirectTo = "/api/jamtracks/#{jamTrack.id}/stems/master/download.mp3?file_type=mp3&download=1&mark=#{result}"
|
||||||
redirectTo = URI.escape(redirectTo)
|
redirectTo = encodeURIComponent(redirectTo)
|
||||||
AppActions.openExternalUrl(window.location.protocol + '//' + window.location.host + "/signin?redirect-to=#{redirectTo}")
|
AppActions.openExternalUrl(window.location.protocol + '//' + window.location.host + "/signin?redirect-to=#{redirectTo}")
|
||||||
))
|
))
|
||||||
|
|
||||||
|
addUpgradeToCart: (jamtrack) ->
|
||||||
|
console.log("adding upgrade to cart")
|
||||||
|
|
||||||
|
params = {id: jamtrack.id, variant: 'download'}
|
||||||
|
|
||||||
|
rest.addJamtrackToShoppingCart(params).done((response) =>
|
||||||
|
console.log("added item to shopping cart. fast_redeem? " + response.fast_redeem)
|
||||||
|
if response.fast_reedem
|
||||||
|
if context.JK.currentUserId?
|
||||||
|
AppActions.openExternalUrl(window.location.protocol + '//' + window.location.host + '/client#/redeemComplete')
|
||||||
|
else
|
||||||
|
AppActions.openExternalUrl(window.location.protocol + '//' + window.location.host + '/client#/redeemSignup')
|
||||||
|
else
|
||||||
|
AppActions.openExternalUrl(window.location.protocol + '//' + window.location.host + '/client#/shoppingCart')
|
||||||
|
|
||||||
|
).fail(((jqxhr) =>
|
||||||
|
|
||||||
|
handled = false
|
||||||
|
if jqxhr.status == 422
|
||||||
|
body = JSON.parse(jqxhr.responseText)
|
||||||
|
if body.errors?.cart_id?[0] == 'has already been taken'
|
||||||
|
console.log("already taken, just show shopping cart")
|
||||||
|
AppActions.openExternalUrl(window.location.protocol + '//' + window.location.host + '/client#/shoppingCart')
|
||||||
|
return
|
||||||
|
else if body.errors && body.errors.base
|
||||||
|
handled = true
|
||||||
|
alert("You can not have a mix of free and non-free items in your shopping cart.\n\nIf you want to add this new item to your shopping cart, then clear out all current items by clicking on the shopping cart icon and clicking 'delete' next to each item.")
|
||||||
|
if !handled
|
||||||
|
alert("Error adding to shoppig cart. " + jqxhr.responseText)
|
||||||
|
))
|
||||||
|
|
||||||
stemChanged:() ->
|
stemChanged:() ->
|
||||||
stemDownload: (e) ->
|
stemDownload: (e) ->
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
@ -537,6 +574,12 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
|
|
||||||
$select = $(this.getDOMNode()).find('.active-stem-select')
|
$select = $(this.getDOMNode()).find('.active-stem-select')
|
||||||
|
|
||||||
|
if !@state.jamTrackState.jamTrack.can_download
|
||||||
|
e.preventDefault()
|
||||||
|
if confirm("You have not purchased the rights to download a JamTrack. Add them to your shopping cart?")
|
||||||
|
@addUpgradeToCart(@state.jamTrackState.jamTrack)
|
||||||
|
return
|
||||||
|
|
||||||
selectedTrackId = $select.val()
|
selectedTrackId = $select.val()
|
||||||
|
|
||||||
if !selectedTrackId? || selectedTrackId == ''
|
if !selectedTrackId? || selectedTrackId == ''
|
||||||
|
|
@ -546,7 +589,7 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
|
|
||||||
new window.Fingerprint2().get((result, components) => (
|
new window.Fingerprint2().get((result, components) => (
|
||||||
redirectTo = "/api/jamtracks/#{@state.jamTrackState.jamTrack.id}/stems/#{selectedTrackId}/download.mp3?file_type=mp3&download=1&mark=#{result}"
|
redirectTo = "/api/jamtracks/#{@state.jamTrackState.jamTrack.id}/stems/#{selectedTrackId}/download.mp3?file_type=mp3&download=1&mark=#{result}"
|
||||||
redirectTo = URI.escape(redirectTo)
|
redirectTo = encodeURIComponent(redirectTo)
|
||||||
AppActions.openExternalUrl(window.location.protocol + '//' + window.location.host + "/signin?redirect-to=#{redirectTo}")
|
AppActions.openExternalUrl(window.location.protocol + '//' + window.location.host + "/signin?redirect-to=#{redirectTo}")
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
@ -591,7 +634,7 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
if browserPackage?.signing_state == 'SIGNED'
|
if browserPackage?.signing_state == 'SIGNED'
|
||||||
new window.Fingerprint2().get((result, components) => (
|
new window.Fingerprint2().get((result, components) => (
|
||||||
redirectTo = "/api/mixdowns/#{mixdown.id}/download.mp3?file_type=mp3&sample_rate=48&download=1&mark=#{result}"
|
redirectTo = "/api/mixdowns/#{mixdown.id}/download.mp3?file_type=mp3&sample_rate=48&download=1&mark=#{result}"
|
||||||
redirectTo = URI.escape(redirectTo)
|
redirectTo = encodeURIComponent(redirectTo)
|
||||||
AppActions.openExternalUrl(window.location.protocol + '//' + window.location.host + "/signin?redirect-to=#{redirectTo}")
|
AppActions.openExternalUrl(window.location.protocol + '//' + window.location.host + "/signin?redirect-to=#{redirectTo}")
|
||||||
))
|
))
|
||||||
else
|
else
|
||||||
|
|
@ -716,7 +759,6 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
||||||
setTimeout(@resizeWindow, 1000)
|
setTimeout(@resizeWindow, 1000)
|
||||||
|
|
||||||
shouldComponentUpdate: () ->
|
shouldComponentUpdate: () ->
|
||||||
console.log("THIS UNLOADED", @unloaded)
|
|
||||||
return !@unloaded
|
return !@unloaded
|
||||||
|
|
||||||
resizeWindow: () =>
|
resizeWindow: () =>
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,10 @@ if accessOpener
|
||||||
# this.setState(chatMixer: mixers.chatMixer)
|
# this.setState(chatMixer: mixers.chatMixer)
|
||||||
|
|
||||||
onRecordingStateChanged: (recordingState) ->
|
onRecordingStateChanged: (recordingState) ->
|
||||||
|
if @unloaded
|
||||||
|
#console.log("PopupMediaControls unloaded. ignore onMixersChnaged")
|
||||||
|
return
|
||||||
|
|
||||||
this.setState(isRecording: recordingState.isRecording, recordedOnce: this.state.recordedOnce || recordingState.isRecording)
|
this.setState(isRecording: recordingState.isRecording, recordedOnce: this.state.recordedOnce || recordingState.isRecording)
|
||||||
|
|
||||||
startStopRecording: () ->
|
startStopRecording: () ->
|
||||||
|
|
@ -165,6 +169,9 @@ if accessOpener
|
||||||
</div>`
|
</div>`
|
||||||
|
|
||||||
windowUnloaded: () ->
|
windowUnloaded: () ->
|
||||||
|
@unloaded = true
|
||||||
|
window.unloaded = true
|
||||||
|
|
||||||
window.opener.RecordingActions.recordingControlsClosed()
|
window.opener.RecordingActions.recordingControlsClosed()
|
||||||
|
|
||||||
onChatHelp: (e) ->
|
onChatHelp: (e) ->
|
||||||
|
|
@ -215,6 +222,9 @@ if accessOpener
|
||||||
$root = jQuery(this.getDOMNode())
|
$root = jQuery(this.getDOMNode())
|
||||||
$includeChat = $root.find('#include-chat')
|
$includeChat = $root.find('#include-chat')
|
||||||
|
|
||||||
|
shouldComponentUpdate: () ->
|
||||||
|
return !@unloaded
|
||||||
|
|
||||||
resizeWindow: () =>
|
resizeWindow: () =>
|
||||||
$container = $('#minimal-container')
|
$container = $('#minimal-container')
|
||||||
width = $container.width()
|
width = $container.width()
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ ConfigureTracksActions = @ConfigureTracksActions
|
||||||
$connectionState,
|
$connectionState,
|
||||||
'SessionStatsHover',
|
'SessionStatsHover',
|
||||||
() =>
|
() =>
|
||||||
{participant: {client_id: this.props.clientId, user: name: 'You', possessive: 'Your'}, }
|
{myTrack: true, participant: {client_id: this.props.connStatsClientId, user: name: 'You', possessive: 'Your'}, }
|
||||||
,
|
,
|
||||||
{width:385, positions:['right', 'left'], offsetParent:$root.closest('.screen'), extraClasses: 'self'})
|
{width:385, positions:['right', 'left'], offsetParent:$root.closest('.screen'), extraClasses: 'self'})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,15 +9,29 @@ MixerActions = context.MixerActions
|
||||||
session = sessionMixers.session
|
session = sessionMixers.session
|
||||||
mixers = sessionMixers.mixers
|
mixers = sessionMixers.mixers
|
||||||
noAudioUsers = mixers.noAudioUsers
|
noAudioUsers = mixers.noAudioUsers
|
||||||
|
clientsWithAudioOverride = mixers.clientsWithAudioOverride
|
||||||
|
|
||||||
participants = []
|
participants = []
|
||||||
|
|
||||||
if session.inSession()
|
if session.inSession()
|
||||||
|
|
||||||
|
self = session.getParticipant(@app.clientId)
|
||||||
|
|
||||||
|
myJamBlasterClientId = null
|
||||||
|
if self? && self.client_role == 'child' && self.parent_client_id?
|
||||||
|
myJamBlasterClientId = self.parent_client_id
|
||||||
|
|
||||||
for participant in session.otherParticipants()
|
for participant in session.otherParticipants()
|
||||||
|
|
||||||
#if participant.is_jamblaster
|
if myJamBlasterClientId? && participant.client_id == myJamBlasterClientId
|
||||||
#continue
|
# don't show my parent jamblaster in 'others'
|
||||||
|
continue
|
||||||
|
|
||||||
|
if participant.client_role == 'child' && participant.parent_client_id?
|
||||||
|
#participant.parent = session.getParticipant(participant.parent_client_id)
|
||||||
|
# don't show children nodes
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
if participant.client_id == @app.clientId
|
if participant.client_id == @app.clientId
|
||||||
participant.user.possessive = "Your"
|
participant.user.possessive = "Your"
|
||||||
|
|
@ -55,6 +69,10 @@ MixerActions = context.MixerActions
|
||||||
|
|
||||||
name = "#{name}: #{instrumentDescription}"
|
name = "#{name}: #{instrumentDescription}"
|
||||||
|
|
||||||
|
noAudio = false
|
||||||
|
if !clientsWithAudioOverride[participant.client_id]
|
||||||
|
noAudio = noAudioUsers[participant.client_id]
|
||||||
|
|
||||||
participantState = {
|
participantState = {
|
||||||
participant: participant,
|
participant: participant,
|
||||||
tracks: tracks,
|
tracks: tracks,
|
||||||
|
|
@ -63,7 +81,7 @@ MixerActions = context.MixerActions
|
||||||
instrumentIcon: instrumentIcon,
|
instrumentIcon: instrumentIcon,
|
||||||
photoUrl: photoUrl,
|
photoUrl: photoUrl,
|
||||||
hasMixer: hasMixer,
|
hasMixer: hasMixer,
|
||||||
noAudio: noAudioUsers[participant.client_id]
|
noAudio: noAudio
|
||||||
}
|
}
|
||||||
|
|
||||||
MixerActions.missingPeerMixer(participant.client_id) unless hasMixer
|
MixerActions.missingPeerMixer(participant.client_id) unless hasMixer
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ MixerActions = @MixerActions
|
||||||
<div>Volume</div>
|
<div>Volume</div>
|
||||||
<div>{monitorVolumeLeft}dB</div>
|
<div>{monitorVolumeLeft}dB</div>
|
||||||
</div>
|
</div>
|
||||||
<SessionTrackGain mixers={this.state.inputGroupMixers} gainType='music' />
|
<SessionTrackGain mixers={this.state.inputGroupMixers} gainType='music' controlGroup="music" />
|
||||||
<div className={monitorMuteClasses} data-control="mute" onClick={this.handleAudioInputMute}/>
|
<div className={monitorMuteClasses} data-control="mute" onClick={this.handleAudioInputMute}/>
|
||||||
|
|
||||||
<input type="checkbox" name="mute"/>
|
<input type="checkbox" name="mute"/>
|
||||||
|
|
@ -108,7 +108,7 @@ MixerActions = @MixerActions
|
||||||
<div>Volume</div>
|
<div>Volume</div>
|
||||||
<div>{chatVolumeLeft}dB</div>
|
<div>{chatVolumeLeft}dB</div>
|
||||||
</div>
|
</div>
|
||||||
<SessionTrackGain mixers={this.state.chatGroupMixers} />
|
<SessionTrackGain mixers={this.state.chatGroupMixers} controlGroup="chat" />
|
||||||
<div className={chatMuteClasses} data-control="mute" onClick={this.handleChatMute}/>
|
<div className={chatMuteClasses} data-control="mute" onClick={this.handleChatMute}/>
|
||||||
|
|
||||||
<input type="checkbox" name="mute"/>
|
<input type="checkbox" name="mute"/>
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@ StatsInfo = {
|
||||||
audio = @state.stats?.audio
|
audio = @state.stats?.audio
|
||||||
|
|
||||||
aggregateTag = null
|
aggregateTag = null
|
||||||
if aggregate?
|
if aggregate? && !this.props.myTrack
|
||||||
if aggregate.latency
|
if aggregate.latency
|
||||||
aggregateStats.push(@stat(aggregate, 'aggregate', 'Tot Latency', 'latency', Math.round(aggregate.latency)))
|
aggregateStats.push(@stat(aggregate, 'aggregate', 'Tot Latency', 'latency', Math.round(aggregate.latency)))
|
||||||
|
|
||||||
|
|
@ -173,10 +173,16 @@ StatsInfo = {
|
||||||
audio_type = 'WDM'
|
audio_type = 'WDM'
|
||||||
else if audio_long.indexOf('core') > -1
|
else if audio_long.indexOf('core') > -1
|
||||||
audio_type = 'CoreAudio'
|
audio_type = 'CoreAudio'
|
||||||
|
else if audio_long.indexOf('alsa') > -1
|
||||||
|
audio_type = 'JamBlaster'
|
||||||
audioStats.push(@stat(audio, 'audio', 'Gear Driver', 'audio_in_type', audio_type))
|
audioStats.push(@stat(audio, 'audio', 'Gear Driver', 'audio_in_type', audio_type))
|
||||||
if audio.framesize?
|
if audio.framesize?
|
||||||
framesize = '?'
|
framesize = '?'
|
||||||
if audio.framesize == 2.5
|
if audio.framesize == 1.0
|
||||||
|
framesize = '1 ms'
|
||||||
|
else if audio.framesize == 2.0
|
||||||
|
framesize = '1 ms'
|
||||||
|
else if audio.framesize == 2.5
|
||||||
framesize = '2.5 ms'
|
framesize = '2.5 ms'
|
||||||
else if audio.framesize == 5
|
else if audio.framesize == 5
|
||||||
framesize = '5 ms'
|
framesize = '5 ms'
|
||||||
|
|
@ -185,7 +191,7 @@ StatsInfo = {
|
||||||
audioStats.push(@stat(audio, 'audio', 'Frame Size', 'framesize', framesize))
|
audioStats.push(@stat(audio, 'audio', 'Frame Size', 'framesize', framesize))
|
||||||
|
|
||||||
networkTag = null
|
networkTag = null
|
||||||
if network?
|
if network? && !this.props.myTrack
|
||||||
if network.ping?
|
if network.ping?
|
||||||
networkStats.push(@stat(network, 'network', 'Latency', 'ping', (network.ping / 2).toFixed(1) + ' ms'))
|
networkStats.push(@stat(network, 'network', 'Latency', 'ping', (network.ping / 2).toFixed(1) + ' ms'))
|
||||||
if network.audiojq_median?
|
if network.audiojq_median?
|
||||||
|
|
@ -238,7 +244,11 @@ StatsInfo = {
|
||||||
onStatsChanged: (stats) ->
|
onStatsChanged: (stats) ->
|
||||||
stats = window.SessionStatsStore.stats
|
stats = window.SessionStatsStore.stats
|
||||||
if stats?
|
if stats?
|
||||||
clientStats = stats[@props.participant.client_id]
|
if stats.parent?
|
||||||
|
# if we have a parent, then use stats from the JamBlaster (parent), not ourselves. Otherwise we'll get bad stats (no Audio etc)
|
||||||
|
clientStats = stats.parent[@props.participant.client_id]
|
||||||
|
else
|
||||||
|
clientStats = stats[@props.participant.client_id]
|
||||||
else
|
else
|
||||||
clientStats = null
|
clientStats = null
|
||||||
@setState({stats: clientStats})
|
@setState({stats: clientStats})
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ MIX_MODES = context.JK.MIX_MODES
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
gainType: React.PropTypes.string
|
gainType: React.PropTypes.string
|
||||||
|
controlGroup: React.PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
getInitialState: () ->
|
getInitialState: () ->
|
||||||
|
|
@ -22,12 +23,11 @@ MIX_MODES = context.JK.MIX_MODES
|
||||||
mixers = @state.mixers.mixer
|
mixers = @state.mixers.mixer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# if this is a media track, jam track , or media category, affect volume of both mixer and opposing mixer
|
# if this is a media track, jam track , or media category, affect volume of both mixer and opposing mixer
|
||||||
if @state.mixers.mixer.group_id == ChannelGroupIds.MediaTrackGroup || @state.mixers.mixer.group_id == ChannelGroupIds.JamTrackGroup || ((@state.mixers.mixer.group_id == ChannelGroupIds.MonitorCatGroup || @state.mixers.mixer.group_id == ChannelGroupIds.MasterCatGroup) && @state.mixers.mixer.name == CategoryGroupIds.MediaTrack)
|
if @state.mixers.mixer.group_id == ChannelGroupIds.MediaTrackGroup || @state.mixers.mixer.group_id == ChannelGroupIds.JamTrackGroup || ((@state.mixers.mixer.group_id == ChannelGroupIds.MonitorCatGroup || @state.mixers.mixer.group_id == ChannelGroupIds.MasterCatGroup) && @state.mixers.mixer.name == CategoryGroupIds.MediaTrack)
|
||||||
MixerActions.faderChanged(data, [@state.mixers.mixer, @state.mixers.oppositeMixer], @props.gainType)
|
MixerActions.faderChanged(data, [@state.mixers.mixer, @state.mixers.oppositeMixer], @props.gainType, @props.controlGroup)
|
||||||
else
|
else
|
||||||
MixerActions.faderChanged(data, mixers, @props.gainType)
|
MixerActions.faderChanged(data, mixers, @props.gainType, @props.controlGroup)
|
||||||
|
|
||||||
render: () ->
|
render: () ->
|
||||||
# mixer can be a single item or array
|
# mixer can be a single item or array
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
context = window
|
||||||
|
MIX_MODES = context.JK.MIX_MODES
|
||||||
|
|
||||||
|
@ShoppingCartContents = React.createClass({
|
||||||
|
|
||||||
|
mixins: [Reflux.listenTo(@AppStore, "onAppInit"), Reflux.listenTo(@UserStore, "onUserChanged")]
|
||||||
|
|
||||||
|
render: () ->
|
||||||
|
|
||||||
|
carts = []
|
||||||
|
|
||||||
|
if this.props.carts?
|
||||||
|
if this.props.carts.length == 0
|
||||||
|
carts = `<div className="no-cart-items">You have nothing in your cart</div>`
|
||||||
|
else
|
||||||
|
taxRate = 0
|
||||||
|
if this.props.tax
|
||||||
|
taxRate = 0.0825
|
||||||
|
|
||||||
|
estimatedTax = 0
|
||||||
|
estimatedTotal = 0
|
||||||
|
|
||||||
|
for cart in this.props.carts
|
||||||
|
cart_quantity = cart.product_info.quantity - cart.product_info.marked_for_redeem
|
||||||
|
estimatedTax += cart.product_info.price * cart_quantity * taxRate
|
||||||
|
estimatedTotal += cart.product_info.price * cart_quantity
|
||||||
|
|
||||||
|
estimatedTax = Math.round(estimatedTax * 100) / 100
|
||||||
|
estimatedTotal = Math.round((estimatedTotal + estimatedTax) * 100) / 100
|
||||||
|
|
||||||
|
for cart in this.props.carts
|
||||||
|
console.log("CART", cart)
|
||||||
|
freeNotice = null
|
||||||
|
if cart.product_info.free
|
||||||
|
freeNotice = `<span className="first-one-free">| (first one free)</span>`
|
||||||
|
carts.push(`<div className="cart-item" key={cart.id}>
|
||||||
|
<div className="cart-item-caption">
|
||||||
|
<span>{cart.product_info.sale_display}</span>
|
||||||
|
{freeNotice}
|
||||||
|
</div>
|
||||||
|
<div className="cart-item-price">
|
||||||
|
$ {Number(cart.product_info.real_price).toFixed(2)}
|
||||||
|
</div>
|
||||||
|
<div className="cart-item-quantity">
|
||||||
|
{cart.quantity}
|
||||||
|
</div>
|
||||||
|
<div className="clearall"/>
|
||||||
|
</div>`)
|
||||||
|
|
||||||
|
carts.push(`<div className="cart-item tax-total" key={'tax'}>
|
||||||
|
<div className="cart-item-caption">
|
||||||
|
<span>Tax</span>
|
||||||
|
</div>
|
||||||
|
<div className="cart-item-price">
|
||||||
|
$ {estimatedTax.toFixed(2)}
|
||||||
|
</div>
|
||||||
|
<div className="cart-item-quantity">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div className="clearall"/>
|
||||||
|
</div>`)
|
||||||
|
|
||||||
|
carts.push(`<div className="cart-item total" key={'total'}>
|
||||||
|
<div className="cart-item-caption">
|
||||||
|
<span>Total</span>
|
||||||
|
</div>
|
||||||
|
<div className="cart-item-price">
|
||||||
|
$ {estimatedTotal.toFixed(2)}
|
||||||
|
</div>
|
||||||
|
<div className="cart-item-quantity">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div className="clearall"/>
|
||||||
|
</div>`)
|
||||||
|
else
|
||||||
|
carts = `<div className="loading-indicator">Loading...</div>`
|
||||||
|
|
||||||
|
`<div className="shopping-cart-contents">
|
||||||
|
<div className="order-items-page">
|
||||||
|
<div className="cart-items">
|
||||||
|
<div className="cart-item-caption">
|
||||||
|
<span>YOUR ORDER INCLUDES:</span>
|
||||||
|
</div>
|
||||||
|
<div className="cart-item-price">
|
||||||
|
<span>PRICE</span>
|
||||||
|
</div>
|
||||||
|
<div className="cart-item-quantity">
|
||||||
|
<span>QUANTITY</span>
|
||||||
|
</div>
|
||||||
|
<div className="clearall"></div>
|
||||||
|
{carts}
|
||||||
|
<div className="clearall"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
onAppInit: (@app) ->
|
||||||
|
@EVENTS = context.JK.EVENTS
|
||||||
|
@rest = context.JK.Rest()
|
||||||
|
@logger = context.JK.logger
|
||||||
|
|
||||||
|
|
||||||
|
onUserChanged: (userState) ->
|
||||||
|
@user = userState?.user
|
||||||
|
|
||||||
|
})
|
||||||
|
|
@ -43,6 +43,7 @@ proficiencyDescriptionMap = {
|
||||||
TILE_RATINGS: 'ratings'
|
TILE_RATINGS: 'ratings'
|
||||||
TILE_PRICES: 'prices'
|
TILE_PRICES: 'prices'
|
||||||
visible: false
|
visible: false
|
||||||
|
profileClipboard: null
|
||||||
|
|
||||||
TILES: ['about', 'experience', 'samples', 'ratings', 'prices']
|
TILES: ['about', 'experience', 'samples', 'ratings', 'prices']
|
||||||
|
|
||||||
|
|
@ -65,11 +66,26 @@ proficiencyDescriptionMap = {
|
||||||
@root = $(@getDOMNode())
|
@root = $(@getDOMNode())
|
||||||
@screen = $('#teacher-profile')
|
@screen = $('#teacher-profile')
|
||||||
@starbox()
|
@starbox()
|
||||||
|
@clipboard()
|
||||||
|
|
||||||
componentDidUpdate:() ->
|
componentDidUpdate:() ->
|
||||||
@starbox()
|
@starbox()
|
||||||
|
|
||||||
context.JK.popExternalLinks(@root)
|
context.JK.popExternalLinks(@root)
|
||||||
|
@clipboard()
|
||||||
|
|
||||||
|
clipboard: () ->
|
||||||
|
$profileLink = @root.find('.copy-profile-link')
|
||||||
|
|
||||||
|
if $profileLink.length > 0 && !@profileClipboard?
|
||||||
|
# mount it
|
||||||
|
@profileClipboard = new Clipboard($profileLink.get(0), {
|
||||||
|
text: =>
|
||||||
|
return context.JK.makeAbsolute('/client#/teacher/profile/' + @state.user.teacher?.id)
|
||||||
|
})
|
||||||
|
else if $profileLink.length == 0 && @profileClipboard?
|
||||||
|
@profileClipboard.destroy()
|
||||||
|
@profileClipboard = null
|
||||||
|
|
||||||
starbox:() ->
|
starbox:() ->
|
||||||
$ratings = @root.find('.ratings-box')
|
$ratings = @root.find('.ratings-box')
|
||||||
|
|
@ -230,7 +246,9 @@ proficiencyDescriptionMap = {
|
||||||
biography = biography.replace(/\n/g, "<br/>")
|
biography = biography.replace(/\n/g, "<br/>")
|
||||||
|
|
||||||
`<div className="section bio">
|
`<div className="section bio">
|
||||||
<h3>Teacher Profile {this.editProfileLink('edit profile', 'introduction')}</h3>
|
<a className="copy-profile-link button-orange" href='#' onClick={this.copyProfileLink}>
|
||||||
|
COPY PROFILE URL TO CLIPBOARD
|
||||||
|
</a><h3>Teacher Profile {this.editProfileLink('edit profile', 'introduction')}</h3>
|
||||||
<div className="section-content">
|
<div className="section-content">
|
||||||
<div dangerouslySetInnerHTML={{__html: biography}}></div>
|
<div dangerouslySetInnerHTML={{__html: biography}}></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -694,6 +712,16 @@ proficiencyDescriptionMap = {
|
||||||
</div>
|
</div>
|
||||||
</div>`
|
</div>`
|
||||||
|
|
||||||
|
copyProfileLink: (e) ->
|
||||||
|
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
@app.layout.notify({
|
||||||
|
title: 'Teacher Profile Link Copied',
|
||||||
|
text: "Your clipboard now has a link to this teacher that you can share with anyone."
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
selectionMade: (selection, e) ->
|
selectionMade: (selection, e) ->
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,4 +14,5 @@ context = window
|
||||||
metronomeChanged: {}
|
metronomeChanged: {}
|
||||||
deadUserRemove: {}
|
deadUserRemove: {}
|
||||||
missingPeerMixer: {}
|
missingPeerMixer: {}
|
||||||
|
clientsWithAudio: {}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -11,4 +11,5 @@ context = window
|
||||||
abortedRecording: {}
|
abortedRecording: {}
|
||||||
openRecordingControls: {}
|
openRecordingControls: {}
|
||||||
recordingControlsClosed: {}
|
recordingControlsClosed: {}
|
||||||
|
mixTransferred: {}
|
||||||
})
|
})
|
||||||
|
|
@ -8,7 +8,7 @@ MIX_MODES = context.JK.MIX_MODES;
|
||||||
|
|
||||||
@MixerHelper = class MixerHelper
|
@MixerHelper = class MixerHelper
|
||||||
|
|
||||||
constructor: (@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @mixMode) ->
|
constructor: (@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @clientsWithAudioOverride, @mixMode) ->
|
||||||
@mixMode = MIX_MODES.PERSONAL # TODO - remove mixMode from MixerHelper? Or at least stop using it in most functions
|
@mixMode = MIX_MODES.PERSONAL # TODO - remove mixMode from MixerHelper? Or at least stop using it in most functions
|
||||||
@app = @session.app
|
@app = @session.app
|
||||||
@mixersByResourceId = {}
|
@mixersByResourceId = {}
|
||||||
|
|
@ -622,13 +622,12 @@ MIX_MODES = context.JK.MIX_MODES;
|
||||||
return mixerPair.personal
|
return mixerPair.personal
|
||||||
|
|
||||||
|
|
||||||
findMixerForTrack: (client_id, track, myTrack, mode = MIX_MODES.PERSONAL) ->
|
findMixerForTrack: (client_id, track, myTrack, mode) ->
|
||||||
mixer = null # what is the best mixer for this track/client ID?
|
mixer = null # what is the best mixer for this track/client ID?
|
||||||
oppositeMixer = null # what is the corresponding mixer in the opposite mode?
|
oppositeMixer = null # what is the corresponding mixer in the opposite mode?
|
||||||
vuMixer = null
|
vuMixer = null
|
||||||
muteMixer = null
|
muteMixer = null
|
||||||
|
|
||||||
|
|
||||||
if myTrack
|
if myTrack
|
||||||
# when it's your track, look it up by the backend resource ID
|
# when it's your track, look it up by the backend resource ID
|
||||||
mixer = @getMixerByTrackId(track.client_track_id, mode)
|
mixer = @getMixerByTrackId(track.client_track_id, mode)
|
||||||
|
|
@ -674,7 +673,7 @@ MIX_MODES = context.JK.MIX_MODES;
|
||||||
oppositeMixer = oppositeMixers[ChannelGroupIds.UserMusicInputGroup][0]
|
oppositeMixer = oppositeMixers[ChannelGroupIds.UserMusicInputGroup][0]
|
||||||
|
|
||||||
if !oppositeMixer
|
if !oppositeMixer
|
||||||
logger.warn("unable to find UserMusicInputGroup corresponding to PeerAudioInputMusicGroup mixer", mixer, @personalMixers )
|
logger.warn("unable to find UserMusicInputGroup corresponding to PeerAudioInputMusicGroup mixer", mixer, @personalMixers)
|
||||||
|
|
||||||
when MIX_MODES.PERSONAL
|
when MIX_MODES.PERSONAL
|
||||||
mixers = @groupedMixersForClientId(client_id, [ ChannelGroupIds.UserMusicInputGroup], {}, MIX_MODES.PERSONAL)
|
mixers = @groupedMixersForClientId(client_id, [ ChannelGroupIds.UserMusicInputGroup], {}, MIX_MODES.PERSONAL)
|
||||||
|
|
@ -693,6 +692,8 @@ MIX_MODES = context.JK.MIX_MODES;
|
||||||
logger.error("personaol: found remote mixer that was not of groupID: PeerAudioInputMusicGroup", client_id, track.client_track_id, mixer)
|
logger.error("personaol: found remote mixer that was not of groupID: PeerAudioInputMusicGroup", client_id, track.client_track_id, mixer)
|
||||||
|
|
||||||
#vuMixer = oppositeMixer; # for personal mode, use the PeerAudioInputMusicGroup's VUs
|
#vuMixer = oppositeMixer; # for personal mode, use the PeerAudioInputMusicGroup's VUs
|
||||||
|
else
|
||||||
|
logger.error("no UserMusicInputGroup for client_id #{client_id} in PERSONAL mode", mixers)
|
||||||
|
|
||||||
{
|
{
|
||||||
mixer: mixer,
|
mixer: mixer,
|
||||||
|
|
@ -729,26 +730,43 @@ MIX_MODES = context.JK.MIX_MODES;
|
||||||
|
|
||||||
originalVolume
|
originalVolume
|
||||||
|
|
||||||
faderChanged: (data, mixers, gainType) ->
|
faderChanged: (data, mixers, gainType, controlGroup) ->
|
||||||
mixers = [mixers] unless $.isArray(mixers)
|
mixers = [mixers] unless $.isArray(mixers)
|
||||||
|
|
||||||
originalVolume = @getOriginalVolume(mixers, gainType)
|
originalVolume = @getOriginalVolume(mixers, gainType)
|
||||||
|
|
||||||
for mixer in mixers
|
if controlGroup?
|
||||||
broadcast = !(data.dragging) # If fader is still dragging, don't broadcast
|
mixers = [mixers[0]]
|
||||||
mixer = @fillTrackVolumeObject(mixer.id, mixer.mode, broadcast)
|
|
||||||
|
|
||||||
relative = gainType == 'music' && (mixer.name == CategoryGroupIds.UserMedia || mixer.name == CategoryGroupIds.MediaTrack)
|
for mixer in mixers
|
||||||
|
broadcast = !(data.dragging) # If fader is still dragging, don't broadcast
|
||||||
|
mixer = @fillTrackVolumeObject(mixer.id, mixer.mode, broadcast)
|
||||||
|
|
||||||
@setMixerVolume(mixer, data.percentage, relative, originalVolume)
|
relative = gainType == 'music' && (mixer.name == CategoryGroupIds.UserMedia || mixer.name == CategoryGroupIds.MediaTrack)
|
||||||
|
|
||||||
# keep state of mixer in sync with backend
|
@setMixerVolume(mixer, data.percentage, relative, originalVolume, controlGroup)
|
||||||
mixer = @getMixer(mixer.id, mixer.mode)
|
|
||||||
mixer.volume_left = context.trackVolumeObject.volL
|
|
||||||
|
|
||||||
#if groupId == ChannelGroupIds.UserMusicInputGroup
|
# keep state of mixer in sync with backend
|
||||||
# # there may be other mixers with this same ID in the case of a Peer Music Stream, so update them as well
|
mixer = @getMixer(mixer.id, mixer.mode)
|
||||||
# context.JK.FaderHelpers.setFaderValue(mixerId, data.percentage)
|
mixer.volume_left = context.trackVolumeObject.volL
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
for mixer in mixers
|
||||||
|
broadcast = !(data.dragging) # If fader is still dragging, don't broadcast
|
||||||
|
mixer = @fillTrackVolumeObject(mixer.id, mixer.mode, broadcast)
|
||||||
|
|
||||||
|
relative = gainType == 'music' && (mixer.name == CategoryGroupIds.UserMedia || mixer.name == CategoryGroupIds.MediaTrack)
|
||||||
|
|
||||||
|
@setMixerVolume(mixer, data.percentage, relative, originalVolume)
|
||||||
|
|
||||||
|
# keep state of mixer in sync with backend
|
||||||
|
mixer = @getMixer(mixer.id, mixer.mode)
|
||||||
|
mixer.volume_left = context.trackVolumeObject.volL
|
||||||
|
|
||||||
|
#if groupId == ChannelGroupIds.UserMusicInputGroup
|
||||||
|
# # there may be other mixers with this same ID in the case of a Peer Music Stream, so update them as well
|
||||||
|
# context.JK.FaderHelpers.setFaderValue(mixerId, data.percentage)
|
||||||
|
|
||||||
initGain: (mixer) ->
|
initGain: (mixer) ->
|
||||||
if $.isArray(mixer)
|
if $.isArray(mixer)
|
||||||
|
|
@ -791,7 +809,7 @@ MIX_MODES = context.JK.MIX_MODES;
|
||||||
mixer = @getMixer(mixer.id, mixer.mode)
|
mixer = @getMixer(mixer.id, mixer.mode)
|
||||||
mixer.loop = context.trackVolumeObject.loop
|
mixer.loop = context.trackVolumeObject.loop
|
||||||
|
|
||||||
setMixerVolume: (mixer, volumePercent, relative, originalVolume) ->
|
setMixerVolume: (mixer, volumePercent, relative, originalVolume, controlGroup) ->
|
||||||
###
|
###
|
||||||
// The context.trackVolumeObject has been filled with the mixer values
|
// The context.trackVolumeObject has been filled with the mixer values
|
||||||
// that go with mixerId, and the range of that mixer
|
// that go with mixerId, and the range of that mixer
|
||||||
|
|
@ -821,7 +839,15 @@ MIX_MODES = context.JK.MIX_MODES;
|
||||||
else
|
else
|
||||||
context.trackVolumeObject.volL = newVolume
|
context.trackVolumeObject.volL = newVolume
|
||||||
context.trackVolumeObject.volR = newVolume
|
context.trackVolumeObject.volR = newVolume
|
||||||
context.jamClient.SessionSetControlState(mixer.id, mixer.mode);
|
if controlGroup?
|
||||||
|
|
||||||
|
if mixer.mode == MIX_MODES.PERSONAL
|
||||||
|
controlGroupsArg = 0
|
||||||
|
else
|
||||||
|
controlGroupsArg = 1
|
||||||
|
context.jamClient.setSessionMixerCategoryPlayoutState(controlGroup == 'music', controlGroupsArg);
|
||||||
|
else
|
||||||
|
context.jamClient.SessionSetControlState(mixer.id, mixer.mode);
|
||||||
|
|
||||||
percentFromMixerValue: (min, max, value) ->
|
percentFromMixerValue: (min, max, value) ->
|
||||||
try
|
try
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ rest = context.JK.Rest()
|
||||||
<li>And more…</li>
|
<li>And more…</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>
|
<p>
|
||||||
JamTracks sell for $1.99 each. Musicians love to play with these, and typically buy a few at a
|
JamTracks sell for $1.99 each ($4.99 to include ability to download). Musicians love to play with these, and typically buy a few at a
|
||||||
|
|
||||||
time. Imagine that you are selling a set of guitar strings to an electric guitar player. As a
|
time. Imagine that you are selling a set of guitar strings to an electric guitar player. As a
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ rest = context.JK.Rest()
|
||||||
<a href="/client#/jamtrack/search" onClick={this.redeem} className="cta-free-jamtrack" alt="ClICK HERE TO PICK YOUR FIRST JAMTRACK FREE!">
|
<a href="/client#/jamtrack/search" onClick={this.redeem} className="cta-free-jamtrack" alt="ClICK HERE TO PICK YOUR FIRST JAMTRACK FREE!">
|
||||||
{img}
|
{img}
|
||||||
</a>
|
</a>
|
||||||
<span className="value-indicator">$1.99 value</span>
|
<span className="value-indicator">#{this.props.jam_track.download_price} value</span>
|
||||||
</div>
|
</div>
|
||||||
<br/>
|
<br/>
|
||||||
<div className="browse-band">
|
<div className="browse-band">
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,10 @@ rest = context.JK.Rest()
|
||||||
if loggedIn
|
if loggedIn
|
||||||
loggedInCtaButton = `<button className={classNames({'cta-button' : true, 'processing': this.state.processing})} onClick={this.ctaClick}>{ctaButtonText}</button>`
|
loggedInCtaButton = `<button className={classNames({'cta-button' : true, 'processing': this.state.processing})} onClick={this.ctaClick}>{ctaButtonText}</button>`
|
||||||
if !@isFree()
|
if !@isFree()
|
||||||
loggedInPriceAdvisory = `<div className="price-advisory">${this.props.jam_track.price}</div>`
|
loggedInPriceAdvisory = `<div className="price-advisory">${this.props.jam_track.download_price}</div>`
|
||||||
else
|
else
|
||||||
if !@isFree()
|
if !@isFree()
|
||||||
loggedOutPriceAdvisory = `<div className="price-advisory">${this.props.jam_track.price}</div>`
|
loggedOutPriceAdvisory = `<div className="price-advisory">${this.props.jam_track.download_price}</div>`
|
||||||
|
|
||||||
if this.state.loginErrors?
|
if this.state.loginErrors?
|
||||||
for key, value of this.state.loginErrors
|
for key, value of this.state.loginErrors
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ context = window
|
||||||
<iframe src="//www.youtube.com/embed/ysptXwFYDhQ" frameborder="0" allowfullscreen="allowfullscreen"/>
|
<iframe src="//www.youtube.com/embed/ysptXwFYDhQ" frameborder="0" allowfullscreen="allowfullscreen"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>Your first JamTrack is free, and after that JamTracks are just $1.99 each.</p>
|
<p>Your first JamTrack is free, and after that JamTracks are just $1.99/$4.99 each.</p>
|
||||||
<div className="clearall"/>
|
<div className="clearall"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ badCode = 'This is not a valid code. Please carefully re-enter the code and try
|
||||||
|
|
||||||
if errorText? && errorText.indexOf('already claimed') > -1
|
if errorText? && errorText.indexOf('already claimed') > -1
|
||||||
errorText = 'This card has already been claimed. If you believe this is in error, please email us at support@jamkazam.com to report this problem.'
|
errorText = 'This card has already been claimed. If you believe this is in error, please email us at support@jamkazam.com to report this problem.'
|
||||||
|
|
||||||
buttonClassnames = classNames({'redeem-giftcard': true, 'button-orange': true, disabled: @state.processing || @state.done })
|
buttonClassnames = classNames({'redeem-giftcard': true, 'button-orange': true, disabled: @state.processing || @state.done })
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
context = window
|
||||||
|
rest = context.JK.Rest()
|
||||||
|
|
||||||
|
@SimpleJamClassPage = React.createClass({
|
||||||
|
|
||||||
|
render: () ->
|
||||||
|
|
||||||
|
`<div className="container">
|
||||||
|
<div className="video-wrapper">
|
||||||
|
<div className="video-container">
|
||||||
|
<iframe src="//www.youtube.com/embed/6lICn4g5X-Q" frameborder="0" allowfullscreen="allowfullscreen"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
context = window
|
||||||
|
rest = context.JK.Rest()
|
||||||
|
|
||||||
|
@SimpleJamTracksPage = React.createClass({
|
||||||
|
|
||||||
|
render: () ->
|
||||||
|
|
||||||
|
`<div className="container">
|
||||||
|
<div className="video-wrapper">
|
||||||
|
<div className="video-container">
|
||||||
|
<iframe src="//www.youtube.com/embed/-rHfJggbgqk" frameborder="0" allowfullscreen="allowfullscreen"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
})
|
||||||
|
|
@ -28,6 +28,12 @@ MIDI_TRACK = context.JK.MIDI_TRACK
|
||||||
if session.inSession()
|
if session.inSession()
|
||||||
participant = session.getParticipant(@app.clientId)
|
participant = session.getParticipant(@app.clientId)
|
||||||
|
|
||||||
|
connStatsClientId = @app.clientId
|
||||||
|
|
||||||
|
if participant.client_role == 'child' && participant.parent_client_id?
|
||||||
|
participant.parent = session.getParticipant(participant.parent_client_id)
|
||||||
|
connStatsClientId = participant.parent_client_id
|
||||||
|
|
||||||
if participant
|
if participant
|
||||||
photoUrl = context.JK.resolveAvatarUrl(participant.user.photo_url);
|
photoUrl = context.JK.resolveAvatarUrl(participant.user.photo_url);
|
||||||
|
|
||||||
|
|
@ -79,7 +85,7 @@ MIDI_TRACK = context.JK.MIDI_TRACK
|
||||||
associatedVst = vst
|
associatedVst = vst
|
||||||
break
|
break
|
||||||
|
|
||||||
tracks.push({track: track, mixerFinder: mixerFinder, mixers: mixerData, hasMixer:hasMixer, name: name, trackName: trackName, instrumentIcon: instrumentIcon, photoUrl: photoUrl, clientId: participant.client_id, associatedVst: associatedVst})
|
tracks.push({track: track, mixerFinder: mixerFinder, mixers: mixerData, hasMixer:hasMixer, name: name, trackName: trackName, instrumentIcon: instrumentIcon, photoUrl: photoUrl, clientId: participant.client_id, associatedVst: associatedVst, connStatsClientId: connStatsClientId})
|
||||||
else
|
else
|
||||||
logger.warn("SessionMyTracks: unable to find participant")
|
logger.warn("SessionMyTracks: unable to find participant")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
$ = jQuery
|
$ = jQuery
|
||||||
context = window
|
context = window
|
||||||
logger = context.JK.logger
|
logger = context.JK.logger
|
||||||
|
RecordingActions = @RecordingActions
|
||||||
SessionActions = @SessionActions
|
SessionActions = @SessionActions
|
||||||
JamBlasterActions = @JamBlasterActions
|
JamBlasterActions = @JamBlasterActions
|
||||||
|
|
||||||
|
|
@ -27,6 +28,9 @@ JamBlasterActions = @JamBlasterActions
|
||||||
JamBlasterActions.pairState(map)
|
JamBlasterActions.pairState(map)
|
||||||
else if map.cmd == 'jamblaster_tracks_updated'
|
else if map.cmd == 'jamblaster_tracks_updated'
|
||||||
JamBlasterActions.jamblasterTracksUpdated()
|
JamBlasterActions.jamblasterTracksUpdated()
|
||||||
|
else if map.cmd == 'file_xfer_from_parent'
|
||||||
|
if map.filename && map.filename.indexOf('RT-mix.wav') > -1
|
||||||
|
RecordingActions.mixTransferred()
|
||||||
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ SessionStore = context.SessionStore
|
||||||
query.start = next
|
query.start = next
|
||||||
return query
|
return query
|
||||||
|
|
||||||
c: (lessonSessionId) ->
|
initializeLesson: (lessonSessionId) ->
|
||||||
@lessonSessionId = lessonSessionId
|
@lessonSessionId = lessonSessionId
|
||||||
@channelType = 'lesson'
|
@channelType = 'lesson'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ rest = context.JK.Rest()
|
||||||
checkingMissingPeers : {}
|
checkingMissingPeers : {}
|
||||||
missingMixerPeers : {}
|
missingMixerPeers : {}
|
||||||
recheckTimeout : null
|
recheckTimeout : null
|
||||||
|
clientsWithAudioOverride : {}
|
||||||
|
|
||||||
init: ->
|
init: ->
|
||||||
# Register with the app store to get @app
|
# Register with the app store to get @app
|
||||||
|
|
@ -38,6 +39,7 @@ rest = context.JK.Rest()
|
||||||
this.listenTo(context.MixerActions.metronomeChanged, this.onMetronomeChanged)
|
this.listenTo(context.MixerActions.metronomeChanged, this.onMetronomeChanged)
|
||||||
this.listenTo(context.MixerActions.deadUserRemove, this.onDeadUserRemove)
|
this.listenTo(context.MixerActions.deadUserRemove, this.onDeadUserRemove)
|
||||||
this.listenTo(context.MixerActions.missingPeerMixer, this.onMissingPeerMixer)
|
this.listenTo(context.MixerActions.missingPeerMixer, this.onMissingPeerMixer)
|
||||||
|
this.listenTo(context.MixerActions.clientsWithAudio, this.onClientsWithAudio)
|
||||||
|
|
||||||
context.JK.HandleVolumeChangeCallback2 = @handleVolumeChangeCallback
|
context.JK.HandleVolumeChangeCallback2 = @handleVolumeChangeCallback
|
||||||
context.JK.HandleMetronomeCallback2 = @handleMetronomeCallback
|
context.JK.HandleMetronomeCallback2 = @handleMetronomeCallback
|
||||||
|
|
@ -76,7 +78,7 @@ rest = context.JK.Rest()
|
||||||
# metroSound = args.sound
|
# metroSound = args.sound
|
||||||
SessionActions.syncWithServer()
|
SessionActions.syncWithServer()
|
||||||
|
|
||||||
@mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @mixers?.mixMode || MIX_MODES.PERSONAL)
|
@mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @clientsWithAudioOverride, @mixers?.mixMode || MIX_MODES.PERSONAL)
|
||||||
|
|
||||||
@issueChange()
|
@issueChange()
|
||||||
|
|
||||||
|
|
@ -132,7 +134,7 @@ rest = context.JK.Rest()
|
||||||
@masterMixers = context.jamClient.SessionGetAllControlState(true);
|
@masterMixers = context.jamClient.SessionGetAllControlState(true);
|
||||||
@personalMixers = context.jamClient.SessionGetAllControlState(false);
|
@personalMixers = context.jamClient.SessionGetAllControlState(false);
|
||||||
|
|
||||||
@mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @mixers?.mixMode || MIX_MODES.PERSONAL)
|
@mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @clientsWithAudioOverride, @mixers?.mixMode || MIX_MODES.PERSONAL)
|
||||||
|
|
||||||
@issueChange()
|
@issueChange()
|
||||||
|
|
||||||
|
|
@ -146,9 +148,9 @@ rest = context.JK.Rest()
|
||||||
# simulate a state change to cause a UI redraw
|
# simulate a state change to cause a UI redraw
|
||||||
@issueChange()
|
@issueChange()
|
||||||
|
|
||||||
onFaderChanged: (data, mixers, gainType) ->
|
onFaderChanged: (data, mixers, gainType, controlGroup) ->
|
||||||
|
|
||||||
@mixers.faderChanged(data, mixers, gainType)
|
@mixers.faderChanged(data, mixers, gainType, controlGroup)
|
||||||
|
|
||||||
@issueChange()
|
@issueChange()
|
||||||
|
|
||||||
|
|
@ -171,7 +173,7 @@ rest = context.JK.Rest()
|
||||||
@metro.sound = sound
|
@metro.sound = sound
|
||||||
context.jamClient.SessionSetMetronome(@metro.tempo, @metro.sound, 1, 0);
|
context.jamClient.SessionSetMetronome(@metro.tempo, @metro.sound, 1, 0);
|
||||||
|
|
||||||
@mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @mixers?.mixMode || MIX_MODES.PERSONAL)
|
@mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @clientsWithAudioOverride, @mixers?.mixMode || MIX_MODES.PERSONAL)
|
||||||
@issueChange()
|
@issueChange()
|
||||||
|
|
||||||
onDeadUserRemove: (clientId) ->
|
onDeadUserRemove: (clientId) ->
|
||||||
|
|
@ -193,6 +195,9 @@ rest = context.JK.Rest()
|
||||||
|
|
||||||
@issueChange()
|
@issueChange()
|
||||||
|
|
||||||
|
onClientsWithAudio: (clients) ->
|
||||||
|
@clientsWithAudioOverride = clients
|
||||||
|
|
||||||
onMissingPeerMixer: (clientId) ->
|
onMissingPeerMixer: (clientId) ->
|
||||||
|
|
||||||
missingPeerAttempts = @missingMixerPeers[clientId]
|
missingPeerAttempts = @missingMixerPeers[clientId]
|
||||||
|
|
@ -219,7 +224,7 @@ rest = context.JK.Rest()
|
||||||
@masterMixers = context.jamClient.SessionGetAllControlState(true);
|
@masterMixers = context.jamClient.SessionGetAllControlState(true);
|
||||||
@personalMixers = context.jamClient.SessionGetAllControlState(false);
|
@personalMixers = context.jamClient.SessionGetAllControlState(false);
|
||||||
logger.debug("MixerStore: recheckForMixers")
|
logger.debug("MixerStore: recheckForMixers")
|
||||||
@mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @mixers?.mixMode || MIX_MODES.PERSONAL)
|
@mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @clientsWithAudioOverride, @mixers?.mixMode || MIX_MODES.PERSONAL)
|
||||||
@issueChange()
|
@issueChange()
|
||||||
|
|
||||||
onInitGain: (mixer) ->
|
onInitGain: (mixer) ->
|
||||||
|
|
@ -234,7 +239,7 @@ rest = context.JK.Rest()
|
||||||
|
|
||||||
logger.debug("MixerStore: onMixersChanged")
|
logger.debug("MixerStore: onMixersChanged")
|
||||||
|
|
||||||
@mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @mixers?.mixMode || MIX_MODES.PERSONAL)
|
@mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @clientsWithAudioOverride, @mixers?.mixMode || MIX_MODES.PERSONAL)
|
||||||
|
|
||||||
SessionActions.mixersChanged.trigger(type, text, @mixers.getTrackInfo())
|
SessionActions.mixersChanged.trigger(type, text, @mixers.getTrackInfo())
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,10 +33,11 @@ BackendToFrontendFPS = {
|
||||||
this.trigger({isRecording: @recordingModel.isRecording()})
|
this.trigger({isRecording: @recordingModel.isRecording()})
|
||||||
|
|
||||||
onStartRecording: (recordVideo, recordChat) ->
|
onStartRecording: (recordVideo, recordChat) ->
|
||||||
|
frameRate = 0
|
||||||
frameRate = context.jamClient.GetCurrentVideoFrameRate() || 0;
|
if recordVideo
|
||||||
|
if context.jamClient.GetCurrentVideoFrameRate?
|
||||||
frameRate = BackendToFrontendFPS[frameRate]
|
frameRate = context.jamClient.GetCurrentVideoFrameRate() || 0;
|
||||||
|
frameRate = BackendToFrontendFPS[frameRate]
|
||||||
|
|
||||||
NoVideoRecordActive = 0
|
NoVideoRecordActive = 0
|
||||||
WebCamRecordActive = 1
|
WebCamRecordActive = 1
|
||||||
|
|
@ -49,12 +50,14 @@ BackendToFrontendFPS = {
|
||||||
|
|
||||||
onStartingRecording: (details) ->
|
onStartingRecording: (details) ->
|
||||||
details.cause = 'starting'
|
details.cause = 'starting'
|
||||||
|
@mixTransferred = false
|
||||||
this.trigger(details)
|
this.trigger(details)
|
||||||
|
|
||||||
@popupRecordingControls() unless @recordingWindow?
|
@popupRecordingControls() unless @recordingWindow?
|
||||||
|
|
||||||
onStartedRecording: (details) ->
|
onStartedRecording: (details) ->
|
||||||
details.cause = 'started'
|
details.cause = 'started'
|
||||||
|
@mixTransferred = false
|
||||||
this.trigger(details)
|
this.trigger(details)
|
||||||
|
|
||||||
@popupRecordingControls() unless @recordingWindow?
|
@popupRecordingControls() unless @recordingWindow?
|
||||||
|
|
@ -92,6 +95,9 @@ BackendToFrontendFPS = {
|
||||||
logger.debug("recording controls closed")
|
logger.debug("recording controls closed")
|
||||||
@recordingWindow = null
|
@recordingWindow = null
|
||||||
|
|
||||||
|
onMixTransferred: () ->
|
||||||
|
@mixTransferred = true
|
||||||
|
|
||||||
popupRecordingControls: () ->
|
popupRecordingControls: () ->
|
||||||
logger.debug("poupRecordingControls")
|
logger.debug("poupRecordingControls")
|
||||||
@recordingWindow = window.open("/popups/recording-controls", 'Recording', 'scrollbars=yes,toolbar=no,status=no,height=315,width=340')
|
@recordingWindow = window.open("/popups/recording-controls", 'Recording', 'scrollbars=yes,toolbar=no,status=no,height=315,width=340')
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ EVENTS = context.JK.EVENTS
|
||||||
MIX_MODES = context.JK.MIX_MODES
|
MIX_MODES = context.JK.MIX_MODES
|
||||||
|
|
||||||
SessionActions = @SessionActions
|
SessionActions = @SessionActions
|
||||||
|
MixerActions = @MixerActions
|
||||||
|
|
||||||
SessionStatThresholds = gon.session_stat_thresholds
|
SessionStatThresholds = gon.session_stat_thresholds
|
||||||
NetworkThresholds = SessionStatThresholds.network
|
NetworkThresholds = SessionStatThresholds.network
|
||||||
|
|
@ -16,7 +17,9 @@ AggregateThresholds = SessionStatThresholds.aggregate
|
||||||
@SessionStatsStore = Reflux.createStore(
|
@SessionStatsStore = Reflux.createStore(
|
||||||
{
|
{
|
||||||
listenables: @SessionStatsActions
|
listenables: @SessionStatsActions
|
||||||
rawStats: null
|
rawStats: null,
|
||||||
|
parentStats: null
|
||||||
|
clientsWithAudio: null
|
||||||
|
|
||||||
init: ->
|
init: ->
|
||||||
# Register with the app store to get @app
|
# Register with the app store to get @app
|
||||||
|
|
@ -24,8 +27,9 @@ AggregateThresholds = SessionStatThresholds.aggregate
|
||||||
|
|
||||||
onAppInit: (@app) ->
|
onAppInit: (@app) ->
|
||||||
|
|
||||||
onPushStats: (stats) ->
|
onPushStats: (stats, parentStats) ->
|
||||||
@rawStats = stats
|
@rawStats = stats
|
||||||
|
@parentStats = parentStats
|
||||||
@changed()
|
@changed()
|
||||||
|
|
||||||
classify: (holder, field, threshold) ->
|
classify: (holder, field, threshold) ->
|
||||||
|
|
@ -65,17 +69,15 @@ AggregateThresholds = SessionStatThresholds.aggregate
|
||||||
else
|
else
|
||||||
holder[fieldLevel] = 'good'
|
holder[fieldLevel] = 'good'
|
||||||
|
|
||||||
|
classifyStats: (statBag) ->
|
||||||
changed: () ->
|
container = {}
|
||||||
@stats = {}
|
|
||||||
|
|
||||||
self = null
|
self = null
|
||||||
for participant in @rawStats
|
for participant in statBag
|
||||||
if participant.id == @app.clientId
|
if participant.id == @app.clientId
|
||||||
self = participant
|
self = participant
|
||||||
break
|
break
|
||||||
|
|
||||||
for participant in @rawStats
|
for participant in statBag
|
||||||
|
|
||||||
aggregate = {}
|
aggregate = {}
|
||||||
|
|
||||||
|
|
@ -93,6 +95,9 @@ AggregateThresholds = SessionStatThresholds.aggregate
|
||||||
network = participant.network
|
network = participant.network
|
||||||
|
|
||||||
if network?
|
if network?
|
||||||
|
if network.audio_bitrate_rx > 0 && network.audio_bitrate_tx > 0
|
||||||
|
@clientsWithAudio[participant.id] = true
|
||||||
|
|
||||||
@classify(network, 'audiojq_median', NetworkThresholds)
|
@classify(network, 'audiojq_median', NetworkThresholds)
|
||||||
@classify(network, 'jitter_var', NetworkThresholds)
|
@classify(network, 'jitter_var', NetworkThresholds)
|
||||||
@classify(network, 'audio_bitrate_rx', NetworkThresholds)
|
@classify(network, 'audio_bitrate_rx', NetworkThresholds)
|
||||||
|
|
@ -151,8 +156,23 @@ AggregateThresholds = SessionStatThresholds.aggregate
|
||||||
else
|
else
|
||||||
participant.classification = 'unknown'
|
participant.classification = 'unknown'
|
||||||
|
|
||||||
@stats[participant.id] = participant
|
container[participant.id] = participant
|
||||||
|
|
||||||
|
return container
|
||||||
|
|
||||||
|
changed: () ->
|
||||||
|
@clientsWithAudio = {}
|
||||||
|
@stats = {}
|
||||||
|
|
||||||
|
@stats = @classifyStats(@rawStats)
|
||||||
|
if @parentStats
|
||||||
|
@stats.parent = @classifyStats(@parentStats)
|
||||||
|
else
|
||||||
|
@stats.parent = null
|
||||||
|
|
||||||
|
MixerActions.clientsWithAudio(@clientsWithAudio)
|
||||||
|
|
||||||
|
# see if we can reset noAudio
|
||||||
@trigger(@stats)
|
@trigger(@stats)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -728,8 +728,19 @@ ConfigureTracksActions = @ConfigureTracksActions
|
||||||
context.jamClient.SessionRegisterCallback("JK.HandleBridgeCallback2");
|
context.jamClient.SessionRegisterCallback("JK.HandleBridgeCallback2");
|
||||||
context.jamClient.RegisterRecordingCallbacks("JK.HandleRecordingStartResult", "JK.HandleRecordingStopResult", "JK.HandleRecordingStarted", "JK.HandleRecordingStopped", "JK.HandleRecordingAborted");
|
context.jamClient.RegisterRecordingCallbacks("JK.HandleRecordingStartResult", "JK.HandleRecordingStopResult", "JK.HandleRecordingStarted", "JK.HandleRecordingStopped", "JK.HandleRecordingAborted");
|
||||||
context.jamClient.SessionSetConnectionStatusRefreshRate(1000);
|
context.jamClient.SessionSetConnectionStatusRefreshRate(1000);
|
||||||
|
clientRole = context.jamClient.getClientParentChildRole();
|
||||||
|
parentClientId = context.jamClient.getParentClientId();
|
||||||
|
logger.debug("role when joining session: #{clientRole}, parent client id #{parentClientId}")
|
||||||
#context.JK.HelpBubbleHelper.jamtrackGuideSession($screen.find('li.open-a-jamtrack'), $screen)
|
#context.JK.HelpBubbleHelper.jamtrackGuideSession($screen.find('li.open-a-jamtrack'), $screen)
|
||||||
|
|
||||||
|
if clientRole == 0
|
||||||
|
clientRole = 'child'
|
||||||
|
else if clientRole == 1
|
||||||
|
clientRole = 'parent'
|
||||||
|
|
||||||
|
if clientRole == '' || !clientRole
|
||||||
|
clientRole = null
|
||||||
|
|
||||||
# subscribe to events from the recording model
|
# subscribe to events from the recording model
|
||||||
@recordingRegistration()
|
@recordingRegistration()
|
||||||
|
|
||||||
|
|
@ -741,6 +752,8 @@ ConfigureTracksActions = @ConfigureTracksActions
|
||||||
as_musician: true,
|
as_musician: true,
|
||||||
tracks: @userTracks,
|
tracks: @userTracks,
|
||||||
session_id: @currentSessionId,
|
session_id: @currentSessionId,
|
||||||
|
client_role: clientRole,
|
||||||
|
parent_client_id: parentClientId
|
||||||
audio_latency: context.jamClient.FTUEGetExpectedLatency().latency
|
audio_latency: context.jamClient.FTUEGetExpectedLatency().latency
|
||||||
})
|
})
|
||||||
.done((response) =>
|
.done((response) =>
|
||||||
|
|
@ -840,8 +853,11 @@ ConfigureTracksActions = @ConfigureTracksActions
|
||||||
@backendStatsInterval = window.setInterval((() => (@updateBackendStats())), 1000)
|
@backendStatsInterval = window.setInterval((() => (@updateBackendStats())), 1000)
|
||||||
|
|
||||||
updateBackendStats: () ->
|
updateBackendStats: () ->
|
||||||
connectionStats = window.jamClient.getConnectionDetail('')
|
connectionStats = window.jamClient.getConnectionDetail('', false)
|
||||||
SessionStatsActions.pushStats(connectionStats)
|
parentConnectionStats = window.jamClient.getConnectionDetail('', true)
|
||||||
|
#console.log("CONNECTION STATES", connectionStats)
|
||||||
|
#console.log("PARENT STATES", parentConnectionStats)
|
||||||
|
SessionStatsActions.pushStats(connectionStats, parentConnectionStats)
|
||||||
|
|
||||||
trackChanges: (header, payload) ->
|
trackChanges: (header, payload) ->
|
||||||
if @currentTrackChanges < payload.track_changes_counter
|
if @currentTrackChanges < payload.track_changes_counter
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@
|
||||||
var details = { clientId: app.clientId, reason: 'rest', detail: arguments, isRecording: false }
|
var details = { clientId: app.clientId, reason: 'rest', detail: arguments, isRecording: false }
|
||||||
$self.triggerHandler('startedRecording', details);
|
$self.triggerHandler('startedRecording', details);
|
||||||
currentlyRecording = false;
|
currentlyRecording = false;
|
||||||
context.RecordingActions.startedRecording(details);
|
context.RecordingActions.startedRecording(details);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
var $downloadApplicationLink = null;
|
var $downloadApplicationLink = null;
|
||||||
var $noPurchasesPrompt = null;
|
var $noPurchasesPrompt = null;
|
||||||
var shoppingCartItem = null;
|
var shoppingCartItem = null;
|
||||||
|
var $startUsingJtPopup = null;
|
||||||
|
|
||||||
|
|
||||||
function beforeShow() {
|
function beforeShow() {
|
||||||
|
|
@ -156,6 +157,12 @@
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
console.log("jamtracks", jamTracks)
|
||||||
|
$startUsingJtPopup.attr('href', '/popups/jamtrack-player/' + jamTracks[0].id)
|
||||||
|
$startUsingJtPopup.find('.download-jamkazam').html('Click Here to Start Using <b>' + jamTracks[0].name + '</b>')
|
||||||
|
context.JK.popExternalLinks($startUsingJtPopup.parent())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -246,6 +253,7 @@
|
||||||
$backBtn = $screen.find('.back');
|
$backBtn = $screen.find('.back');
|
||||||
$downloadApplicationLink = $screen.find('.download-jamkazam-wrapper');
|
$downloadApplicationLink = $screen.find('.download-jamkazam-wrapper');
|
||||||
$noPurchasesPrompt = $screen.find('.no-purchases-prompt')
|
$noPurchasesPrompt = $screen.find('.no-purchases-prompt')
|
||||||
|
$startUsingJtPopup = $screen.find('.jt-popup')
|
||||||
|
|
||||||
if ($screen.length == 0) throw "$screen must be specified";
|
if ($screen.length == 0) throw "$screen must be specified";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
//= require jquery.queryparams
|
//= require jquery.queryparams
|
||||||
//= require jquery.hoverIntent
|
//= require jquery.hoverIntent
|
||||||
//= require jquery.cookie
|
//= require jquery.cookie
|
||||||
//= require jquery.clipboard
|
//= require clipboard
|
||||||
//= require jquery.easydropdown
|
//= require jquery.easydropdown
|
||||||
//= require jquery.carousel-1.1
|
//= require jquery.carousel-1.1
|
||||||
//= require jquery.mousewheel-3.1.9
|
//= require jquery.mousewheel-3.1.9
|
||||||
|
|
|
||||||
|
|
@ -867,6 +867,8 @@
|
||||||
else {
|
else {
|
||||||
var inputSampleRate = 44100;
|
var inputSampleRate = 44100;
|
||||||
}
|
}
|
||||||
|
// ignore all this; just set it to
|
||||||
|
inputSampleRate = 'DEVICE_DEFAULT'
|
||||||
logger.debug("applying the sample rate based on input device: " + selectedDeviceInfo.input.id + " (" + inputSampleRate + ")");
|
logger.debug("applying the sample rate based on input device: " + selectedDeviceInfo.input.id + " (" + inputSampleRate + ")");
|
||||||
sampleRate.selectSampleRate(inputSampleRate);
|
sampleRate.selectSampleRate(inputSampleRate);
|
||||||
context.jamClient.FTUESetPreferredMixerSampleRate(sampleRate.selectedSampleRate());
|
context.jamClient.FTUESetPreferredMixerSampleRate(sampleRate.selectedSampleRate());
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,9 @@
|
||||||
else if(value == 96000) {
|
else if(value == 96000) {
|
||||||
setter = 'PREFER_96'
|
setter = 'PREFER_96'
|
||||||
}
|
}
|
||||||
|
else if (value == 'DEVICE_DEFAULT') {
|
||||||
|
setter = 'USE_DEVICE_DEFAULT_SR'
|
||||||
|
}
|
||||||
console.log("SELECT SAMPLE RATE" + value, setter);
|
console.log("SELECT SAMPLE RATE" + value, setter);
|
||||||
context.JK.dropdown($sampleRate.val(setter).easyDropDown('select', setter.toString(), true))
|
context.JK.dropdown($sampleRate.val(setter).easyDropDown('select', setter.toString(), true))
|
||||||
}
|
}
|
||||||
|
|
@ -63,7 +66,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetValues() {
|
function resetValues() {
|
||||||
$sampleRate.val('PREFER_44').easyDropDown('select', 'PREFER_44', true)
|
$sampleRate.val('USE_DEVICE_DEFAULT_SR').easyDropDown('select', 'USE_DEVICE_DEFAULT_SR', true)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,24 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.paypal-region {
|
||||||
|
text-align: center;
|
||||||
|
margin:10px auto 0;
|
||||||
|
/**margin: 10px auto 0;
|
||||||
|
padding: 10px 10px 5px;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
border-color: #ccc;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 3px;
|
||||||
|
width:145px;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.or-text {
|
||||||
|
margin: 60px auto 0;
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
color:white;
|
color:white;
|
||||||
background-color:#4d4d4d;
|
background-color:#4d4d4d;
|
||||||
|
|
|
||||||
|
|
@ -266,9 +266,19 @@
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.jamtrack-variant-help {
|
||||||
|
margin-bottom:20px;
|
||||||
|
}
|
||||||
|
.jamtrack-add-zone {
|
||||||
|
margin: 8px 0px;
|
||||||
|
position:relative;
|
||||||
|
}
|
||||||
.jamtrack-price {
|
.jamtrack-price {
|
||||||
margin-top: 5px;
|
width:100%;
|
||||||
|
margin: 0 auto 10px;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
color:white;
|
||||||
|
display:block;
|
||||||
|
|
||||||
&.free {
|
&.free {
|
||||||
margin-top:0;
|
margin-top:0;
|
||||||
|
|
@ -282,7 +292,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.jamtrack-add-cart, .jamtrack-add-cart-disabled {
|
.jamtrack-add-cart, .jamtrack-add-cart-disabled {
|
||||||
margin: 8px 0px;
|
position:relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jamtrack-license {
|
.jamtrack-license {
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,9 @@
|
||||||
.scooter {
|
.scooter {
|
||||||
margin-bottom:10px;
|
margin-bottom:10px;
|
||||||
}
|
}
|
||||||
|
div.retailer-split {
|
||||||
|
margin-top:10px;
|
||||||
|
}
|
||||||
.store-header {
|
.store-header {
|
||||||
float: left;
|
float: left;
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
|
|
@ -280,4 +283,10 @@
|
||||||
font-size:12px;
|
font-size:12px;
|
||||||
margin:0;
|
margin:0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.split-input {
|
||||||
|
:after {
|
||||||
|
content: '%';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
@import "client/common.scss";
|
||||||
|
|
||||||
|
[data-react-class="PayPalConfirmationScreen"] {
|
||||||
|
height: 100%;
|
||||||
|
overflow: scroll;
|
||||||
|
|
||||||
|
.content-body-scroller {
|
||||||
|
height: calc(100% - 30px) ! important; // 15px top and bottom padding, and 48px used by .controls
|
||||||
|
padding: 15px 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-header {
|
||||||
|
color: white;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls.bottom {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.place-order-btn {
|
||||||
|
text-align: center;
|
||||||
|
margin-right:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.or-holder {
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-order-btn {
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.shopping-cart-contents {
|
||||||
|
@include border-box_sizing;
|
||||||
|
width: 50%;
|
||||||
|
margin-top:20px;
|
||||||
|
}
|
||||||
|
.controls {
|
||||||
|
@include border-box_sizing;
|
||||||
|
width:50%;
|
||||||
|
a {
|
||||||
|
float:right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.loading-indicator {
|
||||||
|
margin-bottom:20px;
|
||||||
|
padding-bottom:20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sold-notice {
|
||||||
|
h2 {
|
||||||
|
font-size:30px;
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.download-jamkazam {
|
||||||
|
color:$ColorLink;
|
||||||
|
border-radius: 4px;
|
||||||
|
border-style:solid;
|
||||||
|
border-color:#AAA;
|
||||||
|
border-width:1px;
|
||||||
|
padding:10px;
|
||||||
|
margin-top:20px;
|
||||||
|
display:inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-jamkazam-wrapper, .back-to-browsing {
|
||||||
|
text-align:center;
|
||||||
|
display:block;
|
||||||
|
margin-top:35px;
|
||||||
|
|
||||||
|
&.hidden {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
@import "client/common.scss";
|
||||||
|
|
||||||
|
.shopping-cart-contents {
|
||||||
|
|
||||||
|
background-color:#262626;
|
||||||
|
border-width:0 1px 0 0;
|
||||||
|
border-style:solid;
|
||||||
|
border-color:#333;
|
||||||
|
padding:20px 20px 0;
|
||||||
|
.cart-item-caption {
|
||||||
|
width: 50%;
|
||||||
|
text-align: left;
|
||||||
|
float: left;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
@include border_box_sizing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.first-one-free {
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: italic;
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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 {
|
||||||
|
}
|
||||||
|
|
||||||
|
.tax-total {
|
||||||
|
margin-top:10px;
|
||||||
|
border-width:1px 0 0 0;
|
||||||
|
border-color:white;
|
||||||
|
border-style:solid;
|
||||||
|
padding-top:10px;
|
||||||
|
}
|
||||||
|
.cart-item.total {
|
||||||
|
margin-top:5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -199,6 +199,9 @@
|
||||||
position:absolute;
|
position:absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.copy-profile-link {
|
||||||
|
float:right;
|
||||||
|
}
|
||||||
|
|
||||||
.spinner-large {
|
.spinner-large {
|
||||||
width:200px;
|
width:200px;
|
||||||
|
|
|
||||||
|
|
@ -723,7 +723,7 @@
|
||||||
}
|
}
|
||||||
.easydropdown-wrapper {
|
.easydropdown-wrapper {
|
||||||
width:auto;
|
width:auto;
|
||||||
float:right;
|
float:left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,4 +12,6 @@
|
||||||
*= require dialogs/dialog
|
*= require dialogs/dialog
|
||||||
*= require icheck/minimal/minimal
|
*= require icheck/minimal/minimal
|
||||||
*= require landings/posa_activation
|
*= require landings/posa_activation
|
||||||
|
*= require landings/simple_jamtracks
|
||||||
|
*= require landings/simple_jamclass
|
||||||
*/
|
*/
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue