VRFS-1503 added protocol buffer, chat message model & controller, rest api
This commit is contained in:
commit
3921324b32
|
|
@ -116,3 +116,7 @@ group :test do
|
|||
gem 'simplecov-rcov'
|
||||
end
|
||||
|
||||
gem 'pry'
|
||||
gem 'pry-remote'
|
||||
gem 'pry-stack_explorer'
|
||||
gem 'pry-debugger'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
ActiveAdmin.register JamRuby::User, :as => 'Referrals' do
|
||||
|
||||
menu :label => 'Referrals', :parent => 'Affiliates'
|
||||
|
||||
config.batch_actions = false
|
||||
config.clear_action_items!
|
||||
config.filters = false
|
||||
|
||||
index do
|
||||
column 'User' do |oo| link_to(oo.name, "http://www.jamkazam.com/client#/profile/#{oo.id}", {:title => oo.name}) end
|
||||
column 'Email' do |oo| oo.email end
|
||||
column 'Created' do |oo| oo.created_at end
|
||||
column 'Partner' do |oo| oo.affiliate_referral.partner_name end
|
||||
end
|
||||
|
||||
controller do
|
||||
|
||||
def scoped_collection
|
||||
rel = end_of_association_chain
|
||||
.includes([:affiliate_referral])
|
||||
.order('created_at DESC')
|
||||
if (ref_id = params[AffiliatePartner::PARAM_REFERRAL]).present?
|
||||
qq = ['affiliate_referral_id = ?', ref_id]
|
||||
else
|
||||
qq = ['affiliate_referral_id IS NOT NULL']
|
||||
end
|
||||
@users ||= rel.where(qq)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
ActiveAdmin.register JamRuby::AffiliatePartner, :as => 'Affiliates' do
|
||||
|
||||
menu :label => 'Partners', :parent => 'Affiliates'
|
||||
|
||||
config.sort_order = 'created_at DESC'
|
||||
config.batch_actions = false
|
||||
# config.clear_action_items!
|
||||
config.filters = false
|
||||
|
||||
form :partial => 'form'
|
||||
|
||||
index do
|
||||
column 'User' do |oo| link_to(oo.partner_user.name, "http://www.jamkazam.com/client#/profile/#{oo.partner_user.id}", {:title => oo.partner_user.name}) end
|
||||
column 'Email' do |oo| oo.partner_user.email end
|
||||
column 'Name' do |oo| oo.partner_name end
|
||||
column 'Code' do |oo| oo.partner_code end
|
||||
column 'Referral Count' do |oo| oo.referral_user_count end
|
||||
# column 'Referrals' do |oo| link_to('View', admin_referrals_path(AffiliatePartner::PARAM_REFERRAL => oo.id)) end
|
||||
default_actions
|
||||
end
|
||||
|
||||
controller do
|
||||
|
||||
def show
|
||||
redirect_to admin_referrals_path(AffiliatePartner::PARAM_REFERRAL => resource.id)
|
||||
end
|
||||
|
||||
def create
|
||||
obj = AffiliatePartner.create_with_params(params[:jam_ruby_affiliate_partner])
|
||||
if obj.errors.present?
|
||||
set_resource_ivar(obj)
|
||||
render active_admin_template('new')
|
||||
else
|
||||
redirect_to admin_affiliates_path
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
obj = resource
|
||||
vals = params[:jam_ruby_affiliate_partner]
|
||||
obj.partner_name = vals[:partner_name]
|
||||
obj.user_email = vals[:user_email] if vals[:user_email].present?
|
||||
obj.save!
|
||||
set_resource_ivar(obj)
|
||||
render active_admin_template('show')
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -97,12 +97,14 @@ ActiveAdmin.register JamRuby::EmailBatch, :as => 'Batch Emails' do
|
|||
|
||||
def create
|
||||
batch = EmailBatch.create_with_params(params[:jam_ruby_email_batch])
|
||||
redirect_to admin_batch_email_path(batch.id)
|
||||
set_resource_ivar(batch)
|
||||
render active_admin_template('show')
|
||||
end
|
||||
|
||||
def update
|
||||
resource.update_with_conflict_validation(params[:jam_ruby_email_batch])
|
||||
redirect_to admin_batch_email_path(resource.id)
|
||||
set_resource_ivar(resource)
|
||||
render active_admin_template('show')
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,7 +1,13 @@
|
|||
class AdminAuthorization < ActiveAdmin::AuthorizationAdapter
|
||||
|
||||
def authorized?(action, subject = nil)
|
||||
subject.is_a?(EmailBatch) && :update == action ? subject.can_run_batch? : true
|
||||
if subject.is_a?(EmailBatch) && :update == action
|
||||
subject.can_run_batch?
|
||||
elsif subject.is_a?(AffiliatePartner) && :destroy == action
|
||||
false
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
<%= semantic_form_for([:admin, resource], :url => resource.new_record? ? admin_affiliates_path : "/admin/affiliates/#{resource.id}") do |f| %>
|
||||
<%= f.semantic_errors *f.object.errors.keys %>
|
||||
<%= f.inputs do %>
|
||||
<%= f.input(:user_email, :input_html => {:maxlength => 255}) %>
|
||||
<%= f.input(:partner_name, :input_html => {:maxlength => 128}) %>
|
||||
<% if resource.new_record? %>
|
||||
<%= f.input(:partner_code, :input_html => {:maxlength => 128}) %>
|
||||
<% else %>
|
||||
<%= f.input(:partner_code, :input_html => {:maxlength => 128, :readonly => 'readonly'}) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<%= f.actions %>
|
||||
<% end %>
|
||||
4
build
4
build
|
|
@ -66,7 +66,7 @@ DEB_SERVER=http://localhost:9010/apt-`uname -p`
|
|||
GEM_SERVER=http://localhost:9000/gems
|
||||
|
||||
# if still going, then push all debs up
|
||||
if [[ "$GIT_BRANCH" == *develop* || "$GIT_BRANCH" == *master* ]]; then
|
||||
if [[ "$GIT_BRANCH" == *develop* || "$GIT_BRANCH" == *master* || "$GIT_BRANCH" == *release* ]]; then
|
||||
|
||||
echo ""
|
||||
echo "PUSHING DB ARTIFACTS"
|
||||
|
|
@ -129,7 +129,7 @@ GEM_SERVER=http://localhost:9000/gems
|
|||
popd > /dev/null
|
||||
|
||||
else
|
||||
echo "Skipping publish since branch is neither master or develop..."
|
||||
echo "Skipping publish since branch is not master, develop, or release/*. branch is $GIT_BRANCH"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -143,4 +143,6 @@ emails.sql
|
|||
email_batch.sql
|
||||
user_progress_tracking2.sql
|
||||
bands_did_session.sql
|
||||
email_change_default_sender.sql
|
||||
affiliate_partners.sql
|
||||
chat_messages.sql
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
CREATE TABLE affiliate_partners (
|
||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
partner_name VARCHAR(128) NOT NULL,
|
||||
partner_code VARCHAR(128) NOT NULL,
|
||||
partner_user_id VARCHAR(64) NOT NULL,
|
||||
user_email VARCHAR(255),
|
||||
referral_user_count INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX affiliate_partners_code_idx ON affiliate_partners(partner_code);
|
||||
CREATE INDEX affiliate_partners_user_idx ON affiliate_partners(partner_user_id);
|
||||
|
||||
ALTER TABLE users ADD COLUMN affiliate_referral_id VARCHAR(64) REFERENCES affiliate_partners(id);
|
||||
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE email_batches ALTER column from_email SET DEFAULT 'noreply@jamkazam.com'::character varying;
|
||||
|
|
@ -140,6 +140,7 @@ require "jam_ruby/models/email_batch_set"
|
|||
require "jam_ruby/models/email_error"
|
||||
require "jam_ruby/app/mailers/async_mailer"
|
||||
require "jam_ruby/app/mailers/batch_mailer"
|
||||
require "jam_ruby/models/affiliate_partner"
|
||||
|
||||
include Jampb
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ module JamRuby
|
|||
class InvitedUserMailer < ActionMailer::Base
|
||||
include SendGrid
|
||||
|
||||
DEFAULT_SENDER = "support@jamkazam.com"
|
||||
DEFAULT_SENDER = "noreply@jamkazam.com"
|
||||
|
||||
default :from => DEFAULT_SENDER
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
layout "user_mailer"
|
||||
|
||||
DEFAULT_SENDER = "support@jamkazam.com"
|
||||
DEFAULT_SENDER = "noreply@jamkazam.com"
|
||||
|
||||
default :from => DEFAULT_SENDER
|
||||
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ module JamWebEventMachine
|
|||
end
|
||||
|
||||
def self.run
|
||||
|
||||
return if defined?(Rails::Console)
|
||||
current = Thread.current
|
||||
Thread.new do
|
||||
run_em(current)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
class JamRuby::AffiliatePartner < ActiveRecord::Base
|
||||
belongs_to :partner_user, :class_name => "JamRuby::User", :foreign_key => :partner_user_id
|
||||
has_many :user_referrals, :class_name => "JamRuby::User", :foreign_key => :affiliate_referral_id
|
||||
|
||||
attr_accessible :partner_name, :partner_code, :partner_user_id
|
||||
|
||||
PARAM_REFERRAL = :ref
|
||||
PARAM_COOKIE = :affiliate_ref
|
||||
|
||||
PARTNER_CODE_REGEX = /^[#{Regexp.escape('abcdefghijklmnopqrstuvwxyz0123456789-._+,')}]+{2,128}$/i
|
||||
|
||||
validates :user_email, format: {with: JamRuby::User::VALID_EMAIL_REGEX}, :if => :user_email
|
||||
validates :partner_name, presence: true
|
||||
validates :partner_code, presence: true, format: { with: PARTNER_CODE_REGEX }
|
||||
validates :partner_user, presence: true
|
||||
|
||||
def self.create_with_params(params={})
|
||||
oo = self.new
|
||||
oo.partner_name = params[:partner_name].try(:strip)
|
||||
oo.partner_code = params[:partner_code].try(:strip).try(:downcase)
|
||||
oo.partner_user = User.where(:email => params[:user_email].try(:strip)).limit(1).first
|
||||
oo.partner_user_id = oo.partner_user.try(:id)
|
||||
oo.save
|
||||
oo
|
||||
end
|
||||
|
||||
def self.coded_id(code=nil)
|
||||
self.where(:partner_code => code).limit(1).pluck(:id).first if code.present?
|
||||
end
|
||||
|
||||
def self.is_code?(code)
|
||||
self.where(:partner_code => code).limit(1).pluck(:id).present?
|
||||
end
|
||||
|
||||
def referrals_by_date
|
||||
by_date = User.where(:affiliate_referral_id => self.id)
|
||||
.group('DATE(created_at)')
|
||||
.having("COUNT(*) > 0")
|
||||
.order('date_created_at DESC')
|
||||
.count
|
||||
block_given? ? yield(by_date) : by_date
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -12,7 +12,7 @@ module JamRuby
|
|||
VAR_FIRST_NAME = '@FIRSTNAME'
|
||||
VAR_LAST_NAME = '@LASTNAME'
|
||||
|
||||
DEFAULT_SENDER = "support@jamkazam.com"
|
||||
DEFAULT_SENDER = "noreply@jamkazam.com"
|
||||
BATCH_SIZE = 1000
|
||||
|
||||
BODY_TEMPLATE =<<FOO
|
||||
|
|
|
|||
|
|
@ -99,6 +99,10 @@ module JamRuby
|
|||
# events
|
||||
has_many :event_sessions, :class_name => "JamRuby::EventSession"
|
||||
|
||||
# affiliate_partner
|
||||
has_one :affiliate_partner, :class_name => "JamRuby::AffiliatePartner", :foreign_key => :partner_user_id
|
||||
belongs_to :affiliate_referral, :class_name => "JamRuby::AffiliatePartner", :foreign_key => :affiliate_referral_id, :counter_cache => :referral_user_count
|
||||
|
||||
# This causes the authenticate method to be generated (among other stuff)
|
||||
#has_secure_password
|
||||
|
||||
|
|
@ -735,6 +739,7 @@ module JamRuby
|
|||
invited_user = options[:invited_user]
|
||||
fb_signup = options[:fb_signup]
|
||||
signup_confirm_url = options[:signup_confirm_url]
|
||||
affiliate_referral_id = options[:affiliate_referral_id]
|
||||
|
||||
user = User.new
|
||||
|
||||
|
|
@ -836,6 +841,10 @@ module JamRuby
|
|||
if user.errors.any?
|
||||
raise ActiveRecord::Rollback
|
||||
else
|
||||
if user.affiliate_referral = AffiliatePartner.find_by_id(affiliate_referral_id)
|
||||
user.save
|
||||
end if affiliate_referral_id.present?
|
||||
|
||||
# don't send an signup email if email is already confirmed
|
||||
if user.email_confirmed
|
||||
UserMailer.welcome_message(user).deliver
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe AffiliatePartner do
|
||||
|
||||
let!(:user) { FactoryGirl.create(:user) }
|
||||
let!(:partner) {
|
||||
AffiliatePartner.create_with_params({:partner_name => Faker::Company.name,
|
||||
:partner_code => Faker::Lorem.word,
|
||||
:user_email => user.email})
|
||||
}
|
||||
|
||||
it 'validates required fields' do
|
||||
pending
|
||||
expect(partner.referral_user_count).to eq(0)
|
||||
expect(partner.partner_user).to eq(user)
|
||||
user.reload
|
||||
expect(user.affiliate_partner).to eq(partner)
|
||||
|
||||
oo = AffiliatePartner.create_with_params({:partner_name => Faker::Company.name,
|
||||
:partner_code => 'a',
|
||||
:user_email => user.email})
|
||||
expect(oo.errors.messages[:partner_code][0]).to eq('is invalid')
|
||||
oo = AffiliatePartner.create_with_params({:partner_name => Faker::Company.name,
|
||||
:partner_code => 'foo bar',
|
||||
:user_email => user.email})
|
||||
expect(oo.errors.messages[:partner_code][0]).to eq('is invalid')
|
||||
oo = AffiliatePartner.create_with_params({:partner_name => '',
|
||||
:partner_code => Faker::Lorem.word,
|
||||
:user_email => user.email})
|
||||
expect(oo.errors.messages[:partner_name][0]).to eq("can't be blank")
|
||||
oo = AffiliatePartner.create_with_params({:partner_name => '',
|
||||
:partner_code => Faker::Lorem.word,
|
||||
:user_email => Faker::Internet.email})
|
||||
expect(oo.errors.messages[:partner_user][0]).to eq("can't be blank")
|
||||
|
||||
code = Faker::Lorem.word.upcase
|
||||
oo = AffiliatePartner.create_with_params({:partner_name => Faker::Company.name,
|
||||
:partner_code => " #{code} ",
|
||||
:user_email => user.email})
|
||||
expect(oo.partner_code).to eq(code.downcase)
|
||||
end
|
||||
|
||||
it 'has user referrals' do
|
||||
pending
|
||||
expect(AffiliatePartner.coded_id(partner.partner_code)).to eq(partner.id)
|
||||
expect(partner.referral_user_count).to eq(0)
|
||||
uu = FactoryGirl.create(:user)
|
||||
uu.affiliate_referral = partner
|
||||
uu.save
|
||||
partner.reload
|
||||
expect(uu.affiliate_referral).to eq(partner)
|
||||
expect(partner.referral_user_count).to eq(1)
|
||||
expect(partner.user_referrals[0]).to eq(uu)
|
||||
end
|
||||
|
||||
it 'groups referrals properly' do
|
||||
FactoryGirl.create(:user, :created_at => Time.now - 7.days, :affiliate_referral_id => partner.id)
|
||||
FactoryGirl.create(:user, :created_at => Time.now - 7.days, :affiliate_referral_id => partner.id)
|
||||
FactoryGirl.create(:user, :created_at => Time.now - 6.days, :affiliate_referral_id => partner.id)
|
||||
FactoryGirl.create(:user, :created_at => Time.now - 6.days, :affiliate_referral_id => partner.id)
|
||||
FactoryGirl.create(:user, :created_at => Time.now - 3.days, :affiliate_referral_id => partner.id)
|
||||
FactoryGirl.create(:user, :created_at => Time.now - 2.days, :affiliate_referral_id => partner.id)
|
||||
partner.reload
|
||||
expect(partner.referral_user_count).to eq(6)
|
||||
|
||||
by_date = partner.referrals_by_date
|
||||
expect(by_date.count).to eq(4)
|
||||
keys = by_date.keys
|
||||
expect(Date.parse(keys.first)).to eq(Date.parse((Time.now - 2.days).to_s))
|
||||
expect(by_date[keys.first]).to eq(1)
|
||||
expect(Date.parse(keys.last)).to eq(Date.parse((Time.now - 7.days).to_s))
|
||||
expect(by_date[keys.last]).to eq(2)
|
||||
end
|
||||
|
||||
end
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 392 B |
|
|
@ -0,0 +1,65 @@
|
|||
(function(context,$) {
|
||||
|
||||
"use strict";
|
||||
|
||||
context.JK = context.JK || {};
|
||||
context.JK.AffiliateReportScreen = function(app) {
|
||||
var logger = context.JK.logger;
|
||||
var rest = context.JK.Rest();
|
||||
var user = {};
|
||||
|
||||
function beforeShow(data) {
|
||||
}
|
||||
|
||||
function afterShow(data) {
|
||||
renderAffiliateReport();
|
||||
}
|
||||
|
||||
function populateAffiliateReport(report) {
|
||||
console.log(report);
|
||||
var by_date = '';
|
||||
var ii=0, dates_len = report['by_date'].length;
|
||||
for (var ii=0; ii < dates_len; ii += 1) {
|
||||
var dd = report['by_date'][ii];
|
||||
by_date += '<div style="float:left; margin-left:50px">'+dd[0]+'</div>';
|
||||
by_date += '<div style="float:left; margin-left:20px;">'+dd[1].toString()+'</div>';
|
||||
by_date += '<br />';
|
||||
}
|
||||
var template = context.JK.fillTemplate($('#template-account-affiliate').html(), {
|
||||
total_count: report['total_count'],
|
||||
by_date: by_date
|
||||
});
|
||||
$('#account-affiliate-content-scroller').html(template);
|
||||
}
|
||||
|
||||
/****************** MAIN PORTION OF SCREEN *****************/
|
||||
// events for main screen
|
||||
function events() {
|
||||
}
|
||||
|
||||
function renderAffiliateReport() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: "/api/users/" + context.JK.currentUserId + "/affiliate",
|
||||
processData: false
|
||||
}).done(populateAffiliateReport)
|
||||
.error(app.ajaxError);
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
var screenBindings = {
|
||||
'beforeShow': beforeShow,
|
||||
'afterShow': afterShow
|
||||
};
|
||||
app.bindScreen('account/affiliate', screenBindings);
|
||||
events();
|
||||
}
|
||||
|
||||
this.initialize = initialize;
|
||||
this.beforeShow = beforeShow;
|
||||
this.afterShow = afterShow;
|
||||
return this;
|
||||
};
|
||||
|
||||
})(window,jQuery);
|
||||
|
|
@ -66,6 +66,7 @@
|
|||
var me = {
|
||||
initialize: initialize,
|
||||
show: show,
|
||||
showAlert: showAlert,
|
||||
hide: hide
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@
|
|||
.prop('disabled', true)
|
||||
}
|
||||
|
||||
function validateForm() {
|
||||
function validateForm() {
|
||||
//var errors = [];
|
||||
var isValid = true;
|
||||
var $form = $('#create-session-form');
|
||||
|
|
|
|||
|
|
@ -6,18 +6,22 @@
|
|||
context.JK = context.JK || {};
|
||||
context.JK.GearWizard = function (app) {
|
||||
|
||||
|
||||
var $dialog = null;
|
||||
var $wizardSteps = null;
|
||||
var $currentWizardStep = null;
|
||||
var step = 0;
|
||||
var $templateSteps = null;
|
||||
var $templateButtons = null;
|
||||
var $templateAudioPort = null;
|
||||
var $ftueButtons = null;
|
||||
var self = null;
|
||||
var operatingSystem = null;
|
||||
|
||||
// populated by loadDevices
|
||||
var deviceInformation = null;
|
||||
var musicInputPorts = null;
|
||||
var musicOutputPorts = null;
|
||||
|
||||
// SELECT DEVICE STATE
|
||||
var validScore = false;
|
||||
|
|
@ -33,28 +37,32 @@
|
|||
var STEP_ROUTER_NETWORK = 5;
|
||||
var STEP_SUCCESS = 6;
|
||||
|
||||
var PROFILE_DEV_SEP_TOKEN = '^';
|
||||
|
||||
var iCheckIgnore = false;
|
||||
|
||||
var audioDeviceBehavior = {
|
||||
MacOSX_builtin : {
|
||||
MacOSX_builtin: {
|
||||
display: 'MacOSX Built-In',
|
||||
videoURL: undefined
|
||||
},
|
||||
MACOSX_interface : {
|
||||
MACOSX_interface: {
|
||||
display: 'MacOSX external interface',
|
||||
videoURL: undefined
|
||||
},
|
||||
Win32_wdm : {
|
||||
Win32_wdm: {
|
||||
display: 'Windows WDM',
|
||||
videoURL: undefined
|
||||
},
|
||||
Win32_asio : {
|
||||
Win32_asio: {
|
||||
display: 'Windows ASIO',
|
||||
videoURL: undefined
|
||||
},
|
||||
Win32_asio4all : {
|
||||
Win32_asio4all: {
|
||||
display: 'Windows ASIO4ALL',
|
||||
videoURL: undefined
|
||||
},
|
||||
Linux : {
|
||||
Linux: {
|
||||
display: 'Linux',
|
||||
videoURL: undefined
|
||||
}
|
||||
|
|
@ -63,7 +71,7 @@
|
|||
function beforeShowIntro() {
|
||||
var $watchVideo = $currentWizardStep.find('.watch-video');
|
||||
var videoUrl = 'https://www.youtube.com/watch?v=VexH4834o9I';
|
||||
if(operatingSystem == "Win32") {
|
||||
if (operatingSystem == "Win32") {
|
||||
$watchVideo.attr('href', 'https://www.youtube.com/watch?v=VexH4834o9I');
|
||||
}
|
||||
$watchVideo.attr('href', videoUrl);
|
||||
|
|
@ -81,6 +89,9 @@
|
|||
var $inputPorts = $currentWizardStep.find('.input-ports');
|
||||
var $outputPorts = $currentWizardStep.find('.output-ports');
|
||||
var $scoreReport = $currentWizardStep.find('.results');
|
||||
var $latencyScore = $scoreReport.find('.latency-score');
|
||||
var $ioRateScore = $scoreReport.find('.io-rate-score');
|
||||
var $ioVarScore = $scoreReport.find('.io-var-score');
|
||||
var $nextButton = $ftueButtons.find('.btn-next');
|
||||
|
||||
// should return one of:
|
||||
|
|
@ -91,24 +102,24 @@
|
|||
// * Win32_asio4all
|
||||
// * Linux
|
||||
function determineDeviceType(deviceId, displayName) {
|
||||
if(operatingSystem == "MacOSX") {
|
||||
if(displayName.toLowerCase().trim() == "built-in") {
|
||||
if (operatingSystem == "MacOSX") {
|
||||
if (displayName.toLowerCase().trim() == "built-in") {
|
||||
return "MacOSX_builtin";
|
||||
}
|
||||
else {
|
||||
return "MacOSX_interface";
|
||||
}
|
||||
}
|
||||
else if(operatingSystem == "Win32") {
|
||||
if(context.jamClient.FTUEIsMusicDeviceWDM(deviceId)) {
|
||||
return "Win32_wdm";
|
||||
}
|
||||
else if(displayName.toLowerCase().indexOf("asio4all") > -1) {
|
||||
return "Win32_asio4all"
|
||||
}
|
||||
else {
|
||||
return "Win32_asio";
|
||||
}
|
||||
else if (operatingSystem == "Win32") {
|
||||
if (context.jamClient.FTUEIsMusicDeviceWDM(deviceId)) {
|
||||
return "Win32_wdm";
|
||||
}
|
||||
else if (displayName.toLowerCase().indexOf("asio4all") > -1) {
|
||||
return "Win32_asio4all"
|
||||
}
|
||||
else {
|
||||
return "Win32_asio";
|
||||
}
|
||||
}
|
||||
else {
|
||||
return "Linux";
|
||||
|
|
@ -122,25 +133,27 @@
|
|||
var loadedDevices = {};
|
||||
|
||||
// augment these devices by determining their type
|
||||
context._.each(devices, function(displayName, deviceId) {
|
||||
context._.each(devices, function (displayName, deviceId) {
|
||||
var deviceInfo = {};
|
||||
|
||||
deviceInfo.id = deviceId;
|
||||
deviceInfo.type = determineDeviceType(deviceId, displayName);
|
||||
deviceInfo.displayType = audioDeviceBehavior[deviceInfo.type].display;
|
||||
deviceInfo.displayName = deviceInfo.displayName;
|
||||
deviceInfo.displayName = displayName;
|
||||
|
||||
loadedDevices[deviceId] = deviceInfo;
|
||||
|
||||
logger.debug("loaded device: ", deviceInfo);
|
||||
})
|
||||
|
||||
deviceInformation = context._.keys(loadedDevices).sort();
|
||||
deviceInformation = loadedDevices;
|
||||
|
||||
logger.debug(context.JK.dlen(deviceInformation) + " devices loaded." , deviceInformation);
|
||||
logger.debug(context.JK.dlen(deviceInformation) + " devices loaded.", deviceInformation);
|
||||
}
|
||||
|
||||
// returns a deviceInfo hash for the device matching the deviceId, or null.
|
||||
// returns a deviceInfo hash for the device matching the deviceId, or undefined.
|
||||
function findDevice(deviceId) {
|
||||
return {};
|
||||
return deviceInformation[deviceId];
|
||||
}
|
||||
|
||||
function selectedAudioInput() {
|
||||
|
|
@ -164,22 +177,21 @@
|
|||
}
|
||||
|
||||
function initializeNextButtonState() {
|
||||
console.log(context.jamClient.FTUEGetDevices(false));
|
||||
console.log("chat devices", jamClient.FTUEGetChatInputs());
|
||||
$nextButton.removeClass('button-orange button-grey');
|
||||
|
||||
if(validScore) $nextButton.addClass('button-orange');
|
||||
if (validScore) $nextButton.addClass('button-orange');
|
||||
else $nextButton.addClass('button-grey');
|
||||
}
|
||||
|
||||
function initializeAudioInput() {
|
||||
var optionsHtml = '';
|
||||
optionsHtml = '<option selected="selected" value="">Choose...</option>';
|
||||
context._.each(deviceInformation, function(deviceInfo, deviceId) {
|
||||
context._.each(deviceInformation, function (deviceInfo, deviceId) {
|
||||
|
||||
console.log(arguments)
|
||||
optionsHtml += '<option title="' + deviceInfo.displayName + '" value="' + deviceId + '">' + deviceInfo.displayName + '</option>';
|
||||
});
|
||||
$audioInput.html(optionsHtml);
|
||||
alert(optionsHtml)
|
||||
context.JK.dropdown($audioInput);
|
||||
|
||||
initializeAudioInputChanged();
|
||||
|
|
@ -188,7 +200,7 @@
|
|||
function initializeAudioOutput() {
|
||||
var optionsHtml = '';
|
||||
optionsHtml = '<option selected="selected" value="">Same as Input...</option>';
|
||||
context._.each(deviceInformation, function(deviceInfo, deviceId) {
|
||||
context._.each(deviceInformation, function (deviceInfo, deviceId) {
|
||||
optionsHtml += '<option title="' + deviceInfo.displayName + '" value="' + deviceId + '">' + deviceInfo.displayName + '</option>';
|
||||
});
|
||||
$audioOutput.html(optionsHtml);
|
||||
|
|
@ -198,11 +210,146 @@
|
|||
}
|
||||
|
||||
function initializeFramesize() {
|
||||
context.JK.dropdown($frameSize);
|
||||
}
|
||||
|
||||
function initializeBuffers() {
|
||||
context.JK.dropdown($bufferIn);
|
||||
context.JK.dropdown($bufferOut);
|
||||
}
|
||||
|
||||
// finds out if the $port argument is from a different port pair than what's currently selected
|
||||
function isNewlySelectedPair($port) {
|
||||
var portId = $port.attr('data-id');
|
||||
// get all inputs currently selected except this one
|
||||
var $selectedInputs = $inputPorts.find('input[type="checkbox"]:checked').filter('[data-id="' + portId + '"]');
|
||||
|
||||
console.log("$selectedInputs", $selectedInputs);
|
||||
var isNewlySelected = true;
|
||||
context._.each($selectedInputs, function($current) {
|
||||
var testPairInfo = $($current).data('pair');
|
||||
|
||||
context._.each(testPairInfo.ports, function(port) {
|
||||
// if we can find the newly selected item in this pair, then it's not a different pair...
|
||||
if(port.id == portId) {
|
||||
isNewlySelected = false;
|
||||
return false; // break loop
|
||||
}
|
||||
});
|
||||
|
||||
if(isNewlySelected) return false; // break loop
|
||||
});
|
||||
|
||||
return isNewlySelected;
|
||||
}
|
||||
|
||||
// set checkbox state for all items in the pair
|
||||
function setCheckedForAllInPair($portBox, pairInfo, checked, signalBackend) {
|
||||
context._.each(pairInfo.ports, function(port) {
|
||||
var portId = port.id;
|
||||
var $input = $portBox.find('input[type="checkbox"][data-id="' + portId + '"]');
|
||||
if($input.is(':checked') != checked) {
|
||||
if(checked) {
|
||||
$input.iCheck('check').attr('checked', 'checked');
|
||||
//context.jamClient.FTUESetMusicInput2($input.id);
|
||||
}
|
||||
else {
|
||||
$input.iCheck('uncheck').removeAttr('checked');
|
||||
//context.jamClient.FTUEUnsetMusicInput2($input.id);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function inputPortChanged() {
|
||||
if(iCheckIgnore) return;
|
||||
|
||||
var $checkbox = $(this);
|
||||
var portId = $checkbox.data('data-id');
|
||||
var inputPortChecked = $checkbox.is(':checked');
|
||||
console.log('inputPortChecked: ' + inputPortChecked);
|
||||
|
||||
if(inputPortChecked) {
|
||||
if(isNewlySelectedPair($checkbox)) {
|
||||
setCheckedForAllInPair($inputPorts, $checkbox.data('pair'), true, true);
|
||||
}
|
||||
else {
|
||||
//context.jamClient.FTUESetMusicInput2($input.id);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// context.jamClient.FTUEUnsetMusicInput2($input.id);;
|
||||
}
|
||||
}
|
||||
|
||||
// should be called in a ifChanged callback if you want to cancel. bleh.
|
||||
function cancelICheckChange($checkbox) {
|
||||
iCheckIgnore = true;
|
||||
var checked = $checkbox.is(':checked');
|
||||
setTimeout(function() {
|
||||
if(checked) $checkbox.iCheck('uncheck').removeAttr('checked');
|
||||
else $checkbox.iCheck('check').attr('checked', 'checked');
|
||||
iCheckIgnore = false;
|
||||
}, 1);
|
||||
}
|
||||
|
||||
function outputPortChanged() {
|
||||
if(iCheckIgnore) return;
|
||||
|
||||
var $checkbox = $(this);
|
||||
var portId = $checkbox.data('data-id');
|
||||
var outputPortChecked = $checkbox.is(':checked');
|
||||
console.log('outputPortChecked: ' + outputPortChecked);
|
||||
|
||||
if(outputPortChecked) {
|
||||
var $selectedInputs = $outputPorts.find('input[type="checkbox"]:checked').filter('[data-id="' + portId + '"]');
|
||||
$selectedInputs.iCheck('uncheck').removeAttr('checked');
|
||||
var pairInfo = $checkbox.data('pair');
|
||||
setCheckedForAllInPair($outputPorts, pairInfo, true, false);
|
||||
console.log("Setting music output");
|
||||
context.jamClient.FTUESetMusicOutput(pairInfo.ports.map(function(i) {return i.id}).join(PROFILE_DEV_SEP_TOKEN));
|
||||
}
|
||||
else {
|
||||
context.JK.Banner.showAlert('You must have at least one output pair selected.');
|
||||
// can't allow uncheck of last output
|
||||
cancelICheckChange($checkbox);
|
||||
}
|
||||
}
|
||||
|
||||
function initializeInputPorts(inputPorts) {
|
||||
context._.each(inputPorts, function(inputPairs) {
|
||||
// there is no guarantee that a pair has two items.
|
||||
context._.each(inputPairs.ports, function(inputInPair) {
|
||||
var inputPort = $(context._.template($templateAudioPort.html(), inputInPair, { variable: 'data' }));
|
||||
var $checkbox = inputPort.find('input');
|
||||
$checkbox.data('pair', inputPairs); // so when it's selected, we can see what other ports, if any, are in the same pair
|
||||
context.JK.checkbox($checkbox);
|
||||
$checkbox.on('ifChanged', inputPortChanged);
|
||||
$inputPorts.append(inputPort);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function initializeOutputPorts(outputPorts) {
|
||||
var first = true;
|
||||
context._.each(outputPorts, function(outputPairs) {
|
||||
context._.each(outputPairs.ports, function(outputInPair) {
|
||||
var outputPort = $(context._.template($templateAudioPort.html(), outputInPair, { variable: 'data' }));
|
||||
var $checkbox = outputPort.find('input');
|
||||
$checkbox.data('pair', outputPairs); // so when it's selected, we can see what other ports, if any, are in the same pair
|
||||
context.JK.checkbox($checkbox);
|
||||
$checkbox.on('ifChanged', outputPortChanged);
|
||||
$outputPorts.append(outputPort);
|
||||
});
|
||||
if(first) {
|
||||
first = false;
|
||||
setCheckedForAllInPair($outputPorts, outputPairs, true, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initializeFormElements() {
|
||||
if(!deviceInformation) throw "devices are not initialized";
|
||||
if (!deviceInformation) throw "devices are not initialized";
|
||||
|
||||
initializeAudioInput();
|
||||
initializeAudioOutput();
|
||||
|
|
@ -225,7 +372,9 @@
|
|||
}
|
||||
|
||||
function resetScoreReport() {
|
||||
$scoreReport.empty();
|
||||
$ioRateScore.empty();
|
||||
$ioVarScore.empty();
|
||||
$latencyScore.empty();
|
||||
}
|
||||
|
||||
function updateScoreReport(latencyResult) {
|
||||
|
|
@ -233,9 +382,9 @@
|
|||
var latencyValue = 'N/A';
|
||||
var validLatency = false;
|
||||
if (latencyResult && latencyResult.latencyknown) {
|
||||
var latency = latencyResult.latency;
|
||||
var latencyValue = latencyResult.latency;
|
||||
latencyValue = Math.round(latencyValue * 100) / 100;
|
||||
if (latency.latency <= 10) {
|
||||
if (latencyValue <= 10) {
|
||||
latencyClass = "good";
|
||||
validLatency = true;
|
||||
} else if (latency.latency <= 20) {
|
||||
|
|
@ -248,57 +397,9 @@
|
|||
|
||||
validScore = validLatency; // validScore may become based on IO variance too
|
||||
|
||||
$scoreReport.html(latencyValue);
|
||||
$latencyScore.html(latencyValue + ' ms');
|
||||
}
|
||||
|
||||
function loadAudioDrivers() {
|
||||
var drivers = context.jamClient.FTUEGetDevices(false);
|
||||
var chatDrivers = jamClient.FTUEGetChatInputs();
|
||||
var optionsHtml = '<option selected="selected" value="">Choose...</option>';
|
||||
var chatOptionsHtml = '<option selected="selected" value="">Choose...</option>';
|
||||
|
||||
|
||||
var driverOptionFunc = function (driverKey, index, list) {
|
||||
if(!drivers[driverKey]) {
|
||||
logger.debug("skipping unknown device:", driverKey)
|
||||
}
|
||||
else {
|
||||
optionsHtml += '<option title="' + drivers[driverKey] + '" value="' + driverKey + '">' + drivers[driverKey] + '</option>';
|
||||
}
|
||||
};
|
||||
|
||||
var chatOptionFunc = function (driverKey, index, list) {
|
||||
chatOptionsHtml += '<option value="' + driverKey + '">' + chatDrivers[driverKey]+ '</option>';
|
||||
};
|
||||
|
||||
var selectors = [
|
||||
'[layout-wizard-step="0"] .settings-2-device select',
|
||||
'[layout-wizard-step="2"] .settings-driver select'
|
||||
];
|
||||
|
||||
// handle standard devices
|
||||
var sortedDeviceKeys = context._.keys(drivers).sort();
|
||||
context._.each(sortedDeviceKeys, driverOptionFunc);
|
||||
$.each(selectors, function (index, selector) {
|
||||
var $select = $(selector);
|
||||
$select.empty();
|
||||
$select.html(optionsHtml);
|
||||
context.JK.dropdown($select);
|
||||
});
|
||||
|
||||
selectors = ['[layout-wizard-step="0"] .settings-2-voice select'];
|
||||
var sortedVoiceDeviceKeys = context._.keys(chatDrivers).sort();
|
||||
|
||||
// handle voice inputs
|
||||
context._.each(sortedVoiceDeviceKeys, chatOptionFunc);
|
||||
$.each(selectors, function (index, selector) {
|
||||
var $select = $(selector);
|
||||
$select.empty();
|
||||
$select.html(chatOptionsHtml);
|
||||
context.JK.dropdown($select);
|
||||
});
|
||||
|
||||
}
|
||||
function audioInputDeviceUnselected() {
|
||||
validScore = false;
|
||||
initializeNextButtonState();
|
||||
|
|
@ -362,16 +463,16 @@
|
|||
}
|
||||
|
||||
function initializeWatchVideo() {
|
||||
$watchVideoInput.unbind('click').click(function() {
|
||||
$watchVideoInput.unbind('click').click(function () {
|
||||
|
||||
var audioDevice = findDevice(selectedAudioInput());
|
||||
if(!audioDevice) {
|
||||
if (!audioDevice) {
|
||||
context.JK.Banner.showAlert('You must first choose an Audio Input Device so that we can determine which video to show you.');
|
||||
}
|
||||
else {
|
||||
var videoURL = audioDeviceBehavior[audioDevice.type].videoURL;
|
||||
|
||||
if(videoURL) {
|
||||
if (videoURL) {
|
||||
$(this).attr('href', videoURL);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -383,16 +484,16 @@
|
|||
return false;
|
||||
});
|
||||
|
||||
$watchVideoOutput.unbind('click').click(function() {
|
||||
$watchVideoOutput.unbind('click').click(function () {
|
||||
|
||||
var audioDevice = findDevice(selectedAudioOutput());
|
||||
if(!audioDevice) {
|
||||
if (!audioDevice) {
|
||||
throw "this button should be hidden";
|
||||
}
|
||||
else {
|
||||
var videoURL = audioDeviceBehavior[audioDevice.type].videoURL;
|
||||
|
||||
if(videoURL) {
|
||||
if (videoURL) {
|
||||
$(this).attr('href', videoURL);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -406,35 +507,52 @@
|
|||
}
|
||||
|
||||
function initializeAudioInputChanged() {
|
||||
$audioInput.unbind('change').change(function(evt) {
|
||||
$audioInput.unbind('change').change(function (evt) {
|
||||
|
||||
var audioDeviceId = selectedAudioInput();
|
||||
if(!audioDeviceId) {
|
||||
audioInputDeviceUnselected();
|
||||
return false;
|
||||
}
|
||||
var audioDeviceId = selectedAudioInput();
|
||||
if (!audioDeviceId) {
|
||||
audioInputDeviceUnselected();
|
||||
return false;
|
||||
}
|
||||
|
||||
var audioDevice = findDevice(selectedAudioInput());
|
||||
if(!audioDevice) {
|
||||
context.JK.alertSupportedNeeded('Unable to find device information for: ' + audioDeviceId);
|
||||
return false;
|
||||
}
|
||||
var audioDevice = findDevice(selectedAudioInput());
|
||||
if (!audioDevice) {
|
||||
context.JK.alertSupportedNeeded('Unable to find device information for: ' + audioDeviceId);
|
||||
return false;
|
||||
}
|
||||
|
||||
renderScoringStarted();
|
||||
|
||||
jamClient.FTUESetMusicDevice(audioDeviceId);
|
||||
jamClient.FTUESetInputLatency(selectedAudioInput());
|
||||
jamClient.FTUESetOutputLatency(selectedAudioOutput());
|
||||
jamClient.FTUESetFrameSize(selectedFramesize());
|
||||
|
||||
logger.debug("Calling FTUESave(false)");
|
||||
jamClient.FTUESave(false);
|
||||
renderScoringStarted();
|
||||
|
||||
var latency = jamClient.FTUEGetExpectedLatency();
|
||||
console.log("FTUEGetExpectedLatency: %o", latency);
|
||||
jamClient.FTUESetMusicDevice(audioDeviceId);
|
||||
|
||||
renderScoringStopped();
|
||||
});
|
||||
// enumerate input and output ports
|
||||
musicInputPorts = jamClient.FTUEGetMusicInputs2();
|
||||
console.log(JSON.stringify(musicInputPorts));
|
||||
// [{"inputs":[{"id":"i~5~Built-in Microph~0~0","name":"Built-in Microph - Left"},{"id":"i~5~Built-in Microph~1~0","name":"Built-in Microph - Right"}]}]
|
||||
musicOutputPorts = jamClient.FTUEGetMusicOutputs2();
|
||||
console.log(JSON.stringify(musicOutputPorts));
|
||||
// [{"outputs":[{"id":"o~5~Built-in Output~0~0","name":"Built-in Output - Left"},{"id":"o~5~Built-in Output~1~0","name":"Built-in Output - Right"}]}]
|
||||
|
||||
|
||||
initializeInputPorts(musicInputPorts);
|
||||
initializeOutputPorts(musicOutputPorts);
|
||||
|
||||
|
||||
jamClient.FTUESetInputLatency(selectedAudioInput());
|
||||
jamClient.FTUESetOutputLatency(selectedAudioOutput());
|
||||
jamClient.FTUESetFrameSize(selectedFramesize());
|
||||
|
||||
logger.debug("Calling FTUESave(false)");
|
||||
jamClient.FTUESave(false);
|
||||
|
||||
var latency = jamClient.FTUEGetExpectedLatency();
|
||||
console.log("FTUEGetExpectedLatency: %o", latency);
|
||||
|
||||
updateScoreReport(latency);
|
||||
renderScoringStopped();
|
||||
});
|
||||
}
|
||||
|
||||
function initializeAudioOutputChanged() {
|
||||
|
|
@ -498,7 +616,9 @@
|
|||
function beforeShowStep($step) {
|
||||
var stepInfo = STEPS[step];
|
||||
|
||||
if(!stepInfo) {throw "unknown step: " + step;}
|
||||
if (!stepInfo) {
|
||||
throw "unknown step: " + step;
|
||||
}
|
||||
|
||||
stepInfo.beforeShow.call(self);
|
||||
}
|
||||
|
|
@ -511,7 +631,7 @@
|
|||
$currentWizardStep = $nextWizardStep;
|
||||
|
||||
var $ftueSteps = $(context._.template($templateSteps.html(), {}, { variable: 'data' }));
|
||||
var $activeStep = $ftueSteps.find('.ftue-stepnumber[data-step-number="'+ step +'"]');
|
||||
var $activeStep = $ftueSteps.find('.ftue-stepnumber[data-step-number="' + step + '"]');
|
||||
$activeStep.addClass('.active');
|
||||
$activeStep.next().show(); // show the .ftue-step-title
|
||||
$currentWizardStep.find('.ftuesteps').replaceWith($ftueSteps);
|
||||
|
|
@ -528,11 +648,11 @@
|
|||
var $btnCancel = $ftueButtonsContent.find('.btn-cancel');
|
||||
|
||||
// hide back button if 1st step or last step
|
||||
if(step == 0 && step == TOTAL_STEPS - 1) {
|
||||
if (step == 0 && step == TOTAL_STEPS - 1) {
|
||||
$btnBack.hide();
|
||||
}
|
||||
// hide next button if not on last step
|
||||
if (step == TOTAL_STEPS - 1 ) {
|
||||
if (step == TOTAL_STEPS - 1) {
|
||||
$btnNext.hide();
|
||||
}
|
||||
// hide close if on last step
|
||||
|
|
@ -559,7 +679,7 @@
|
|||
|
||||
function beforeShow(args) {
|
||||
step = args.d1;
|
||||
if(!step) step = 0;
|
||||
if (!step) step = 0;
|
||||
step = parseInt(step);
|
||||
moveToStep();
|
||||
}
|
||||
|
|
@ -573,14 +693,14 @@
|
|||
}
|
||||
|
||||
function back() {
|
||||
if($(this).is('.button-grey')) return;
|
||||
if ($(this).is('.button-grey')) return;
|
||||
step = step - 1;
|
||||
moveToStep();
|
||||
return false;
|
||||
}
|
||||
|
||||
function next() {
|
||||
if($(this).is('.button-grey')) return;
|
||||
if ($(this).is('.button-grey')) return;
|
||||
|
||||
step = step + 1;
|
||||
|
||||
|
|
@ -599,6 +719,7 @@
|
|||
function route() {
|
||||
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
|
||||
var dialogBindings = { beforeShow: beforeShow, afterShow: afterShow, afterHide: afterHide };
|
||||
|
|
@ -609,6 +730,7 @@
|
|||
$wizardSteps = $dialog.find('.wizard-step');
|
||||
$templateSteps = $('#template-ftuesteps');
|
||||
$templateButtons = $('#template-ftue-buttons');
|
||||
$templateAudioPort = $('#template-audio-port');
|
||||
$ftueButtons = $dialog.find('.ftue-buttons');
|
||||
|
||||
operatingSystem = context.jamClient.GetOSAsString();
|
||||
|
|
|
|||
|
|
@ -429,7 +429,7 @@ ul.shortcuts {
|
|||
padding:2px;
|
||||
}
|
||||
|
||||
.account-home, .band-setup, .audio, .get-help, .download-app, .community-forum, .invite-friends {
|
||||
.account-home, .band-setup, .account-menu-group, .get-help, .download-app, .community-forum, .invite-friends {
|
||||
border-bottom:1px;
|
||||
border-style:solid;
|
||||
border-color:#ED3618;
|
||||
|
|
|
|||
|
|
@ -184,8 +184,30 @@
|
|||
width:45%;
|
||||
}
|
||||
|
||||
|
||||
.buffers {
|
||||
.easydropdown-wrapper:nth-of-type(1) {
|
||||
left:5px;
|
||||
}
|
||||
.easydropdown-wrapper:nth-of-type(2) {
|
||||
left:35px;
|
||||
}
|
||||
.easydropdown, .easydropdown-wrapper {
|
||||
width:15px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.ftue-box.results {
|
||||
height: 230px !important;
|
||||
padding:0;
|
||||
|
||||
.scoring-section {
|
||||
font-size:15px;
|
||||
@include border_box_sizing;
|
||||
height:64px;
|
||||
}
|
||||
}
|
||||
|
||||
.audio-output {
|
||||
|
|
@ -510,8 +532,8 @@
|
|||
.subcolumn.third {
|
||||
right:0px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.settings-controls {
|
||||
|
||||
clear:both;
|
||||
|
|
|
|||
|
|
@ -558,4 +558,9 @@ hr {
|
|||
width:100%;
|
||||
height:20px;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
body.jam .icheckbox_minimal {
|
||||
display:inline-block;
|
||||
position:relative;
|
||||
}
|
||||
|
|
@ -11,7 +11,8 @@ class ApiUsersController < ApiController
|
|||
:notification_index, :notification_destroy, # notifications
|
||||
:band_invitation_index, :band_invitation_show, :band_invitation_update, # band invitations
|
||||
:set_password, :begin_update_email, :update_avatar, :delete_avatar, :generate_filepicker_policy,
|
||||
:share_session, :share_recording]
|
||||
:share_session, :share_recording,
|
||||
:affiliate_report]
|
||||
|
||||
respond_to :json
|
||||
|
||||
|
|
@ -605,6 +606,27 @@ class ApiUsersController < ApiController
|
|||
end
|
||||
end
|
||||
|
||||
def affiliate_report
|
||||
begin
|
||||
affiliate = User
|
||||
.where(:id => params[:id])
|
||||
.includes(:affiliate_partner)
|
||||
.limit(1)
|
||||
.first
|
||||
.affiliate_partner
|
||||
referrals_by_date = affiliate.referrals_by_date do |by_date|
|
||||
by_date.inject([]) { |rr, key| rr << key }
|
||||
end
|
||||
result = {
|
||||
:total_count => affiliate.referral_user_count,
|
||||
:by_date => referrals_by_date
|
||||
}
|
||||
render json: result.to_json, status: 200
|
||||
rescue
|
||||
render :json => { :message => $!.to_s }, :status => 400
|
||||
end
|
||||
end
|
||||
|
||||
def add_play
|
||||
if params[:id].blank?
|
||||
render :json => { :message => "Playable ID is required" }, :status => 400
|
||||
|
|
|
|||
|
|
@ -4,10 +4,22 @@ class ApplicationController < ActionController::Base
|
|||
include ApplicationHelper
|
||||
include SessionsHelper
|
||||
|
||||
|
||||
# inject username/email into bugsnag data
|
||||
before_bugsnag_notify :add_user_info_to_bugsnag
|
||||
|
||||
before_filter do
|
||||
if params[AffiliatePartner::PARAM_REFERRAL].present? && current_user.nil?
|
||||
if cookies[AffiliatePartner::PARAM_COOKIE].blank?
|
||||
code = params[AffiliatePartner::PARAM_REFERRAL].downcase
|
||||
cookies[AffiliatePartner::PARAM_COOKIE] = code if AffiliatePartner.is_code?(code)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def affiliate_code
|
||||
cookies[AffiliatePartner::PARAM_COOKIE]
|
||||
end
|
||||
|
||||
private
|
||||
def add_user_info_to_bugsnag(notif)
|
||||
# Add some app-specific data which will be displayed on a custom
|
||||
|
|
|
|||
|
|
@ -149,19 +149,20 @@ class UsersController < ApplicationController
|
|||
musician = params[:jam_ruby_user][:musician]
|
||||
|
||||
@user = UserManager.new.signup(remote_ip: request.remote_ip,
|
||||
first_name: params[:jam_ruby_user][:first_name],
|
||||
last_name: params[:jam_ruby_user][:last_name],
|
||||
email: params[:jam_ruby_user][:email],
|
||||
password: params[:jam_ruby_user][:password],
|
||||
password_confirmation: params[:jam_ruby_user][:password_confirmation],
|
||||
terms_of_service: terms_of_service,
|
||||
instruments: instruments,
|
||||
birth_date: birth_date,
|
||||
location: location,
|
||||
musician: musician,
|
||||
invited_user: @invited_user,
|
||||
fb_signup: @fb_signup,
|
||||
signup_confirm_url: ApplicationHelper.base_uri(request) + "/confirm")
|
||||
first_name: params[:jam_ruby_user][:first_name],
|
||||
last_name: params[:jam_ruby_user][:last_name],
|
||||
email: params[:jam_ruby_user][:email],
|
||||
password: params[:jam_ruby_user][:password],
|
||||
password_confirmation: params[:jam_ruby_user][:password_confirmation],
|
||||
terms_of_service: terms_of_service,
|
||||
instruments: instruments,
|
||||
birth_date: birth_date,
|
||||
location: location,
|
||||
musician: musician,
|
||||
invited_user: @invited_user,
|
||||
fb_signup: @fb_signup,
|
||||
signup_confirm_url: ApplicationHelper.base_uri(request) + "/confirm",
|
||||
affiliate_referral_id: AffiliatePartner.coded_id(self.affiliate_code))
|
||||
|
||||
# check for errors
|
||||
if @user.errors.any?
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
<!-- Account Summary Dialog -->
|
||||
<div layout="screen" layout-id="account/affiliate" class="screen secondary" id="account-affiliate">
|
||||
<!-- header -->
|
||||
<div class="content-head">
|
||||
<!-- icon -->
|
||||
<div class="content-icon">
|
||||
<%= image_tag "content/icon_dollar.png", {:width => 24, :height => 24} %>
|
||||
</div>
|
||||
<!-- section head text -->
|
||||
<h1>affiliate report</h1>
|
||||
<%= render "screen_navigation" %>
|
||||
</div>
|
||||
<!-- end header -->
|
||||
|
||||
<!-- profile scrolling area -->
|
||||
<div class="content-body">
|
||||
<div id="account-affiliate-content-scroller" class="content-body-scroller">
|
||||
</div>
|
||||
</div>
|
||||
<!-- end content scrolling area -->
|
||||
</div>
|
||||
|
||||
<script type="text/template" id="template-account-affiliate">
|
||||
<!-- content wrapper -->
|
||||
<div class="content-wrapper account-affiliate">
|
||||
<br />
|
||||
<div class="account-left">
|
||||
<div style='float:right; margin-right:20px;'><h2>Total Count: {total_count}</h2></div>
|
||||
<br />
|
||||
<div style="overflow:scroll; margin-left: 50px; margin-top: 20px">
|
||||
<div style="float:left;">Registration Date</div><div style="float:left; margin-left:20px">Number of Users</div>
|
||||
<br />
|
||||
{by_date}
|
||||
</div>
|
||||
</div>
|
||||
<br clear="all" />
|
||||
</div>
|
||||
<!-- end content wrapper -->
|
||||
|
||||
</script>
|
||||
|
||||
|
|
@ -83,6 +83,15 @@
|
|||
.wizard-step-column
|
||||
%h2 Test Results
|
||||
.ftue-box.results
|
||||
.left.w50.gold-fill.center.white.scoring-section
|
||||
.p5
|
||||
.latency LATENCY
|
||||
%span.latency-score
|
||||
.left.w50.green-fill.center.white.scoring-section
|
||||
.p5
|
||||
.io I/O
|
||||
%span.io-rate-score
|
||||
%span.io-var-score
|
||||
|
||||
.clearall
|
||||
|
||||
|
|
@ -195,5 +204,11 @@
|
|||
%a.button-orange.btn-next{href:'#'} NEXT
|
||||
%a.button-orange.btn-close{href:'#', 'layout-action' => 'close'} CLOSE
|
||||
|
||||
%script{type: 'text/template', id: 'template-audio-port'}
|
||||
.audio-port
|
||||
%input{ type: 'checkbox', 'data-id' => '{{data.id}}' }
|
||||
%span
|
||||
= '{{data.name}}'
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
<%= render "testBridge" %>
|
||||
<%= render "account" %>
|
||||
<%= render "account_identity" %>
|
||||
<%= render "affiliate_report" %>
|
||||
<%= render "account_profile" %>
|
||||
<%= render "friendSelector" %>
|
||||
<%= render "account_profile_avatar" %>
|
||||
|
|
@ -182,6 +183,9 @@
|
|||
var accountIdentityScreen = new JK.AccountIdentityScreen(JK.app);
|
||||
accountIdentityScreen.initialize();
|
||||
|
||||
var affiliateReportScreen = new JK.AffiliateReportScreen(JK.app);
|
||||
affiliateReportScreen.initialize();
|
||||
|
||||
var accountProfileScreen = new JK.AccountProfileScreen(JK.app);
|
||||
accountProfileScreen.initialize();
|
||||
|
||||
|
|
|
|||
|
|
@ -7,12 +7,14 @@
|
|||
<![endif]-->
|
||||
<script src="//ajax.googleapis.com/ajax/libs/webfont/1.4.7/webfont.js"></script>
|
||||
<script>
|
||||
WebFont.load({
|
||||
google: {
|
||||
families: ['Raleway:100,200,300,400,500,600,700']
|
||||
},
|
||||
timeout :5000
|
||||
});
|
||||
if (window.WebFont) {
|
||||
WebFont.load({
|
||||
google: {
|
||||
families: ['Raleway:100,200,300,400,500,600,700']
|
||||
},
|
||||
timeout :5000
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<!--<link href='http://fonts.googleapis.com/css?family=Raleway:100,200,300,400,500,600,700' rel='stylesheet' type='text/css'>-->
|
||||
<%= stylesheet_link_tag "client/client", media: "all" %>
|
||||
|
|
|
|||
|
|
@ -4,12 +4,14 @@
|
|||
<title><%= full_title(yield(:title)) %></title>
|
||||
<script src="//ajax.googleapis.com/ajax/libs/webfont/1.4.7/webfont.js"></script>
|
||||
<script>
|
||||
WebFont.load({
|
||||
google: {
|
||||
families: ['Raleway:100,200,300,400,500,600,700']
|
||||
},
|
||||
timeout :5000
|
||||
});
|
||||
if (window.WebFont) {
|
||||
WebFont.load({
|
||||
google: {
|
||||
families: ['Raleway:100,200,300,400,500,600,700']
|
||||
},
|
||||
timeout :5000
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<!--<link href='http://fonts.googleapis.com/css?family=Raleway:100,200,300,400,500,600,700' rel='stylesheet' type='text/css'>-->
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
|
|
|||
|
|
@ -8,12 +8,14 @@
|
|||
<![endif]-->
|
||||
<script src="//ajax.googleapis.com/ajax/libs/webfont/1.4.7/webfont.js"></script>
|
||||
<script>
|
||||
WebFont.load({
|
||||
google: {
|
||||
families: ['Raleway:100,200,300,400,500,600,700']
|
||||
},
|
||||
timeout :5000
|
||||
});
|
||||
if(window.WebFont){
|
||||
WebFont.load({
|
||||
google: {
|
||||
families: ['Raleway:100,200,300,400,500,600,700']
|
||||
},
|
||||
timeout :5000
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<!--<link href='http://fonts.googleapis.com/css?family=Raleway:100,200,300,400,500,600,700' rel='stylesheet' type='text/css'>-->
|
||||
<%= stylesheet_link_tag "landing/landing", media: "all" %>
|
||||
|
|
|
|||
|
|
@ -8,12 +8,14 @@
|
|||
<![endif]-->
|
||||
<script src="//ajax.googleapis.com/ajax/libs/webfont/1.4.7/webfont.js"></script>
|
||||
<script>
|
||||
WebFont.load({
|
||||
google: {
|
||||
families: ['Raleway:100,200,300,400,500,600,700']
|
||||
},
|
||||
timeout :5000
|
||||
});
|
||||
if (window.WebFont) {
|
||||
WebFont.load({
|
||||
google: {
|
||||
families: ['Raleway:100,200,300,400,500,600,700']
|
||||
},
|
||||
timeout :5000
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<!--<link href='http://fonts.googleapis.com/css?family=Raleway:100,200,300,400,500,600,700' rel='stylesheet' type='text/css'>-->
|
||||
<%= stylesheet_link_tag "minimal/minimal", media: "all" %>
|
||||
|
|
|
|||
|
|
@ -8,12 +8,14 @@
|
|||
<![endif]-->
|
||||
<script src="//ajax.googleapis.com/ajax/libs/webfont/1.4.7/webfont.js"></script>
|
||||
<script>
|
||||
WebFont.load({
|
||||
google: {
|
||||
families: ['Raleway:100,200,300,400,500,600,700']
|
||||
},
|
||||
timeout:5000
|
||||
});
|
||||
if (window.WebFont) {
|
||||
WebFont.load({
|
||||
google: {
|
||||
families: ['Raleway:100,200,300,400,500,600,700']
|
||||
},
|
||||
timeout:5000
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<!--<link href='http://fonts.googleapis.com/css?family=Raleway:100,200,300,400,500,600,700' rel='stylesheet' type='text/css'>-->
|
||||
<%= stylesheet_link_tag "web/web", media: "all" %>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@
|
|||
/ type and artist
|
||||
.left.ml20.w15
|
||||
.title{hoveraction: 'session', :'session-id' => '{{data.feed_item.id}}' } SESSION
|
||||
%a.artist{href: "#", :hoveraction => '{{data.feed_item.helpers.artist_hoveraction}}', :'{{data.feed_item.helpers.artist_datakey}}' => '{{data.feed_item.helpers.artist_id}}'}
|
||||
.artist
|
||||
%a.artist{href: "#", :hoveraction => '{{data.feed_item.helpers.artist_hoveraction}}', :'{{data.feed_item.helpers.artist_datakey}}' => '{{data.feed_item.helpers.artist_id}}'}
|
||||
= '{{data.feed_item.helpers.artist_name}}'
|
||||
%time.small.created_at.timeago{datetime: '{{data.feed_item.helpers.utc_created_at}}'}= '{{data.feed_item.created_at}}'
|
||||
/ name and description
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@
|
|||
/ type and artist
|
||||
.left.ml20.w15
|
||||
.title{hoveraction: 'recording', :'recording-id' => '{{data.candidate_claimed_recording.id}}' } RECORDING
|
||||
%a.artist{href: "#", :hoveraction => '{{data.feed_item.helpers.artist_hoveraction}}', :'{{data.feed_item.helpers.artist_datakey}}' => '{{data.feed_item.helpers.artist_id}}'}
|
||||
.artist
|
||||
%a.artist{href: "#", :hoveraction => '{{data.feed_item.helpers.artist_hoveraction}}', :'{{data.feed_item.helpers.artist_datakey}}' => '{{data.feed_item.helpers.artist_id}}'}
|
||||
= '{{data.feed_item.helpers.artist_name}}'
|
||||
%time.small.created_at.timeago{datetime: '{{data.feed_item.helpers.utc_created_at}}'}
|
||||
= '{{data.feed_item.created_at}}'
|
||||
|
|
|
|||
|
|
@ -20,7 +20,13 @@
|
|||
<!--<li class="subscriptions"><%= link_to "Subscriptions", '/client#/account/subscriptions' %></li> -->
|
||||
<!-- <li class="payments"><%= link_to "Payments", '/client#/account/payments' %></li> -->
|
||||
<% if current_user && current_user.musician? %>
|
||||
<li class="audio"><%= link_to "Audio Gear", '/client#/account/audio' %></li>
|
||||
<% class_val = current_user.affiliate_partner.present? ? 'audio' : 'audio account-menu-group' %>
|
||||
<li class="<%= class_val%>"><%= link_to "Audio Gear", '/client#/account/audio' %></li>
|
||||
<% end %>
|
||||
<% if current_user && current_user.affiliate_partner.present? %>
|
||||
<li class="affiliate account-menu-group"><%= link_to "Affiliate Report", '/client#/account/affiliate' %></li>
|
||||
<% end %>
|
||||
<% if current_user && current_user.musician? %>
|
||||
<li class="band-setup"><%= link_to "Band Setup", '/client#/band/setup/new' %></li>
|
||||
<% end %>
|
||||
<li class="invite-friends"><span class='menuheader'><span class="arrow-right"></span><%= link_to "Invite Friends", '#' %></span>
|
||||
|
|
|
|||
|
|
@ -271,6 +271,7 @@ SampleApp::Application.routes.draw do
|
|||
# match '/users/:id/recordings/:recording_id' => 'api_users#recording_destroy', :via => :delete
|
||||
|
||||
match '/users/:id/plays' => 'api_users#add_play', :via => :post, :as => 'api_users_add_play'
|
||||
match '/users/:id/affiliate' => 'api_users#affiliate_report', :via => :get, :as => 'api_users_affiliate'
|
||||
|
||||
# bands
|
||||
match '/bands' => 'api_bands#index', :via => :get
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ class UserManager < BaseManager
|
|||
invited_user = options[:invited_user]
|
||||
fb_signup = options[:fb_signup]
|
||||
signup_confirm_url = options[:signup_confirm_url]
|
||||
affiliate_referral_id = options[:affiliate_referral_id]
|
||||
|
||||
@user = User.new
|
||||
|
||||
|
|
@ -60,7 +61,8 @@ class UserManager < BaseManager
|
|||
photo_url: photo_url,
|
||||
invited_user: invited_user,
|
||||
fb_signup: fb_signup,
|
||||
signup_confirm_url: signup_confirm_url)
|
||||
signup_confirm_url: signup_confirm_url,
|
||||
affiliate_referral_id: affiliate_referral_id)
|
||||
|
||||
return @user
|
||||
#end
|
||||
|
|
|
|||
|
|
@ -11,6 +11,13 @@ describe UserManager do
|
|||
end
|
||||
|
||||
describe "signup" do
|
||||
let!(:user) { FactoryGirl.create(:user) }
|
||||
let!(:partner) {
|
||||
AffiliatePartner.create_with_params({:partner_name => Faker::Company.name,
|
||||
:partner_code => Faker::Lorem.words(1)[0],
|
||||
:user_email => user.email})
|
||||
}
|
||||
|
||||
it "signup successfully" do
|
||||
MaxMindIsp.delete_all # prove that city/state/country will remain nil if no maxmind data
|
||||
MaxMindGeo.delete_all
|
||||
|
|
@ -24,7 +31,8 @@ describe UserManager do
|
|||
terms_of_service: true,
|
||||
instruments: @instruments,
|
||||
musician:true,
|
||||
signup_confirm_url: "http://localhost:3000/confirm" )
|
||||
signup_confirm_url: "http://localhost:3000/confirm",
|
||||
affiliate_referral_id: AffiliatePartner.coded_id(partner.partner_code))
|
||||
|
||||
@user.errors.any?.should be_false
|
||||
@user.first_name.should == "bob"
|
||||
|
|
@ -38,6 +46,9 @@ describe UserManager do
|
|||
@user.subscribe_email.should be_true
|
||||
@user.signup_token.should_not be_nil
|
||||
|
||||
@user.reload
|
||||
expect(@user.affiliate_referral).to eq(partner)
|
||||
|
||||
UserMailer.deliveries.length.should == 1
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe "Affiliate Reports", :type => :api do
|
||||
|
||||
include Rack::Test::Methods
|
||||
|
||||
let!(:user) { FactoryGirl.create(:user) }
|
||||
let!(:partner) {
|
||||
AffiliatePartner.create_with_params({:partner_name => Faker::Company.name,
|
||||
:partner_code => Faker::Lorem.words[0],
|
||||
:user_email => user.email})
|
||||
}
|
||||
|
||||
it "valid score" do
|
||||
FactoryGirl.create(:user, :created_at => Time.now - 5.days, :affiliate_referral_id => partner.id)
|
||||
FactoryGirl.create(:user, :created_at => Time.now - 6.days, :affiliate_referral_id => partner.id)
|
||||
FactoryGirl.create(:user, :created_at => Time.now - 6.days, :affiliate_referral_id => partner.id)
|
||||
FactoryGirl.create(:user, :created_at => Time.now - 7.days, :affiliate_referral_id => partner.id)
|
||||
FactoryGirl.create(:user, :created_at => Time.now - 7.days, :affiliate_referral_id => partner.id)
|
||||
FactoryGirl.create(:user, :created_at => Time.now - 7.days, :affiliate_referral_id => partner.id)
|
||||
FactoryGirl.create(:user, :created_at => Time.now - 7.days, :affiliate_referral_id => partner.id)
|
||||
|
||||
post('/api/auth_session.json',
|
||||
{ :email => user.email, :password => user.password }.to_json,
|
||||
"CONTENT_TYPE" => 'application/json')
|
||||
last_response.status.should == 200
|
||||
expect(JSON.parse(last_response.body)).to eq({ "success" => true })
|
||||
|
||||
get "/api/users/#{user.id}/affiliate"
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
json = JSON.parse(last_response.body)
|
||||
|
||||
expect(json['total_count']).to eq(7)
|
||||
by_date = json['by_date']
|
||||
expect(by_date.count).to eq(3)
|
||||
expect(by_date.first.last).to eq(1)
|
||||
expect(by_date.last.last).to eq(4)
|
||||
end
|
||||
|
||||
end
|
||||
Loading…
Reference in New Issue