This commit is contained in:
Seth Call 2014-02-07 17:38:51 +00:00
commit b648c40587
105 changed files with 3545 additions and 642 deletions

View File

@ -1,5 +1,5 @@
GEM
remote: https://rubygems.org/
remote: http://rubygems.org/
specs:
little-plugger (1.1.3)
logging (1.7.2)

View File

@ -98,4 +98,7 @@ invited_users_facebook_support.sql
first_recording_at.sql
share_token.sql
facebook_signup.sql
audiomixer_mp3.sql
audiomixer_mp3.sql
share_token_2.sql
large_photo_url.sql
add_secret_to_user_authorization.sql

View File

@ -0,0 +1 @@
ALTER TABLE user_authorizations ADD COLUMN secret VARCHAR(255);

View File

@ -0,0 +1,7 @@
ALTER TABLE users ADD COLUMN large_photo_url VARCHAR(2048);
ALTER TABLE users ADD COLUMN cropped_large_s3_path VARCHAR(512);
ALTER TABLE users ADD COLUMN cropped_large_fpfile VARCHAR(8000);
ALTER TABLE bands ADD COLUMN large_photo_url VARCHAR(2048);
ALTER TABLE bands ADD COLUMN cropped_large_s3_path_photo VARCHAR(512);
ALTER TABLE bands ADD COLUMN cropped_large_fpfile_photo VARCHAR(8000);

14
db/up/share_token_2.sql Normal file
View File

@ -0,0 +1,14 @@
alter table music_sessions_history drop column share_token;
alter table claimed_recordings drop column share_token;
CREATE TABLE share_tokens
(
id character varying(64) NOT NULL DEFAULT uuid_generate_v4(),
token varchar(15) NOT NULL,
shareable_id varchar(64) NOT NULL,
shareable_type varchar(50) NOT NULL,
created_at timestamp without time zone NOT NULL DEFAULT now(),
updated_at timestamp without time zone NOT NULL DEFAULT now(),
CONSTRAINT token_uniqkey UNIQUE (token),
CONSTRAINT share_tokens_pkey PRIMARY KEY (id)
);

View File

@ -94,6 +94,7 @@ require "jam_ruby/models/recording_liker"
require "jam_ruby/models/recording_play"
require "jam_ruby/models/recorded_track"
require "jam_ruby/models/recorded_track_observer"
require "jam_ruby/models/share_token"
require "jam_ruby/models/mix"
require "jam_ruby/models/claimed_recording"
require "jam_ruby/models/crash_dump"

View File

@ -2,8 +2,8 @@ module JamRuby
class Band < ActiveRecord::Base
attr_accessible :name, :website, :biography, :city, :state,
:country, :original_fpfile_photo, :cropped_fpfile_photo,
:cropped_s3_path_photo, :crop_selection_photo, :photo_url
:country, :original_fpfile_photo, :cropped_fpfile_photo, :cropped_large_fpfile_photo,
:cropped_s3_path_photo, :cropped_large_s3_path_photo, :crop_selection_photo, :photo_url, :large_photo_url
attr_accessor :updating_photo
@ -88,6 +88,7 @@ module JamRuby
# we want to mak sure that original_fpfile and cropped_fpfile seems like real fpfile info objects (i.e, json objects from filepicker.io)
errors.add(:original_fpfile_photo, ValidationMessages::INVALID_FPFILE) if self.original_fpfile_photo.nil? || self.original_fpfile_photo["key"].nil? || self.original_fpfile_photo["url"].nil?
errors.add(:cropped_fpfile_photo, ValidationMessages::INVALID_FPFILE) if self.cropped_fpfile_photo.nil? || self.cropped_fpfile_photo["key"].nil? || self.cropped_fpfile_photo["url"].nil?
errors.add(:cropped_large_fpfile_photo, ValidationMessages::INVALID_FPFILE) if self.cropped_large_fpfile_photo.nil? || self.cropped_large_fpfile_photo["key"].nil? || self.cropped_large_fpfile_photo["url"].nil?
end
end
@ -196,18 +197,21 @@ module JamRuby
return band
end
def update_photo(original_fpfile, cropped_fpfile, crop_selection, aws_bucket)
def update_photo(original_fpfile, cropped_fpfile, cropped_large_fpfile, crop_selection, aws_bucket)
self.updating_photo = true
cropped_s3_path = cropped_fpfile["key"]
cropped_large_s3_path = cropped_large_fpfile["key"]
return self.update_attributes(
:original_fpfile_photo => original_fpfile,
:cropped_fpfile_photo => cropped_fpfile,
:cropped_large_fpfile_photo => cropped_large_fpfile,
:cropped_s3_path_photo => cropped_s3_path,
:cropped_large_s3_path_photo => cropped_large_s3_path,
:crop_selection_photo => crop_selection,
:photo_url => S3Util.url(aws_bucket, cropped_s3_path, :secure => false)
)
:photo_url => S3Util.url(aws_bucket, cropped_s3_path, :secure => false),
:large_photo_url => S3Util.url(aws_bucket, cropped_large_s3_path, :secure => false))
end
def delete_photo(aws_bucket)
@ -217,15 +221,18 @@ module JamRuby
unless self.cropped_s3_path_photo.nil?
S3Util.delete(aws_bucket, File.dirname(self.cropped_s3_path_photo) + '/cropped.jpg')
S3Util.delete(aws_bucket, self.cropped_s3_path_photo)
S3Util.delete(aws_bucket, self.cropped_large_s3_path_photo)
end
return self.update_attributes(
:original_fpfile_photo => nil,
:cropped_fpfile_photo => nil,
:cropped_large_fpfile_photo => nil,
:cropped_s3_path_photo => nil,
:cropped_large_s3_path_photo => nil,
:crop_selection_photo => nil,
:photo_url => nil
)
:photo_url => nil,
:large_photo_url => nil)
end
end

View File

@ -8,12 +8,12 @@ module JamRuby
validates :genre, presence: true
validates_uniqueness_of :recording_id, :scope => :user_id
belongs_to :recording, :class_name => "JamRuby::Recording", :inverse_of => :claimed_recordings
belongs_to :user, :class_name => "JamRuby::User", :inverse_of => :claimed_recordings
belongs_to :genre, :class_name => "JamRuby::Genre"
has_many :recorded_tracks, :through => :recording, :class_name => "JamRuby::RecordedTrack"
has_many :playing_sessions, :class_name => "JamRuby::MusicSession"
has_one :share_token, :class_name => "JamRuby::ShareToken", :inverse_of => :shareable, :foreign_key => 'shareable_id'
before_create :generate_share_token
@ -54,12 +54,16 @@ module JamRuby
private
def generate_share_token
self.share_token = loop do
token = loop do
token = SecureRandom.urlsafe_base64(SHARE_TOKEN_LENGTH, false)
token = remove_non_alpha_num(token)
token.upcase!
break token unless MusicSessionHistory.exists?(share_token: token)
break token unless ShareToken.exists?(token: token)
end
self.share_token = ShareToken.new
self.share_token.token = token
self.share_token.shareable_type = "recording"
end
end
end

View File

@ -40,6 +40,11 @@ module JamRuby
validate :creator_is_musician
validate :no_new_playback_while_playing
before_create :create_uuid
def create_uuid
#self.id = SecureRandom.uuid
end
def before_destroy
self.mount.destroy if self.mount
end

View File

@ -22,6 +22,7 @@ module JamRuby
has_many :music_session_user_histories, :class_name => "JamRuby::MusicSessionUserHistory", :foreign_key => "music_session_id"
has_many :comments, :class_name => "JamRuby::MusicSessionComment", :foreign_key => "music_session_id"
has_many :likes, :class_name => "JamRuby::MusicSessionLiker", :foreign_key => "music_session_id"
has_one :share_token, :class_name => "JamRuby::ShareToken", :inverse_of => :shareable, :foreign_key => 'shareable_id'
before_create :generate_share_token
@ -124,6 +125,10 @@ module JamRuby
session_history.save!
end
def is_over?
!session_removed_at.nil?
end
def end_history
self.update_attribute(:session_removed_at, Time.now)
@ -155,12 +160,18 @@ module JamRuby
private
def generate_share_token
self.share_token = loop do
self.id = music_session.id # unify music_session.id and music_session_history.id
token = loop do
token = SecureRandom.urlsafe_base64(SHARE_TOKEN_LENGTH, false)
token = remove_non_alpha_num(token)
token.upcase!
break token unless MusicSessionHistory.exists?(share_token: token)
break token unless ShareToken.exists?(token: token)
end
self.share_token = ShareToken.new
self.share_token.token = token
self.share_token.shareable_type = "session"
end
end

View File

@ -21,6 +21,7 @@ module JamRuby
validates :sound, :inclusion => {:in => SOUND}
validates :client_id, :presence => true # not a connection relation on purpose
validates :track_id, :presence => true # not a track relation on purpose
validates :client_track_id, :presence => true
validates :md5, :presence => true, :if => :upload_starting?
validates :length, length: {minimum: 1, maximum: 1024 * 1024 * 256 }, if: :upload_starting? # 256 megs max. is this reasonable? surely...

View File

@ -0,0 +1,7 @@
module JamRuby
class ShareToken < ActiveRecord::Base
belongs_to :shareable, :polymorphic => true
end
end

View File

@ -13,7 +13,7 @@ module JamRuby
after_save :check_lat_lng
attr_accessible :first_name, :last_name, :email, :city, :password, :password_confirmation, :state, :country, :birth_date, :subscribe_email, :terms_of_service, :original_fpfile, :cropped_fpfile, :cropped_s3_path, :photo_url, :crop_selection, :lat, :lng
attr_accessible :first_name, :last_name, :email, :city, :password, :password_confirmation, :state, :country, :birth_date, :subscribe_email, :terms_of_service, :original_fpfile, :cropped_fpfile, :cropped_large_fpfile, :cropped_s3_path, :cropped_large_s3_path, :photo_url, :large_photo_url, :crop_selection, :lat, :lng
# updating_password corresponds to a lost_password
attr_accessor :updating_password, :updating_email, :updated_email, :update_email_confirmation_url, :administratively_created, :current_password, :setting_password, :confirm_current_password, :updating_avatar, :updating_progression_field
@ -187,6 +187,7 @@ module JamRuby
# we want to mak sure that original_fpfile and cropped_fpfile seems like real fpfile info objects (i.e, json objects from filepicker.io)
errors.add(:original_fpfile, ValidationMessages::INVALID_FPFILE) if self.original_fpfile.nil? || self.original_fpfile["key"].nil? || self.original_fpfile["url"].nil?
errors.add(:cropped_fpfile, ValidationMessages::INVALID_FPFILE) if self.cropped_fpfile.nil? || self.cropped_fpfile["key"].nil? || self.cropped_fpfile["url"].nil?
errors.add(:cropped_large_fpfile, ValidationMessages::INVALID_FPFILE) if self.cropped_large_fpfile.nil? || self.cropped_large_fpfile["key"].nil? || self.cropped_large_fpfile["url"].nil?
end
end
@ -898,17 +899,21 @@ module JamRuby
self.save
end
def update_avatar(original_fpfile, cropped_fpfile, crop_selection, aws_bucket)
def update_avatar(original_fpfile, cropped_fpfile, cropped_large_fpfile, crop_selection, aws_bucket)
self.updating_avatar = true
cropped_s3_path = cropped_fpfile["key"]
cropped_large_s3_path = cropped_large_fpfile["key"]
return self.update_attributes(
:original_fpfile => original_fpfile,
:cropped_fpfile => cropped_fpfile,
:cropped_large_fpfile => cropped_large_fpfile,
:cropped_s3_path => cropped_s3_path,
:cropped_large_s3_path => cropped_large_s3_path,
:crop_selection => crop_selection,
:photo_url => S3Util.url(aws_bucket, cropped_s3_path, :secure => false)
:photo_url => S3Util.url(aws_bucket, cropped_s3_path, :secure => false),
:large_photo_url => S3Util.url(aws_bucket, cropped_large_s3_path, :secure => false)
)
end
@ -919,14 +924,18 @@ module JamRuby
unless self.cropped_s3_path.nil?
S3Util.delete(aws_bucket, File.dirname(self.cropped_s3_path) + '/cropped.jpg')
S3Util.delete(aws_bucket, self.cropped_s3_path)
S3Util.delete(aws_bucket, self.cropped_large_s3_path)
end
return self.update_attributes(
:original_fpfile => nil,
:cropped_fpfile => nil,
:cropped_large_fpfile => nil,
:cropped_s3_path => nil,
:cropped_large_s3_path => nil,
:photo_url => nil,
:crop_selection => nil
:crop_selection => nil,
:large_photo_url => nil
)
end
@ -965,6 +974,50 @@ module JamRuby
end
end
def invalidate_user_authorization(provider)
auth = user_authorization(provider)
auth.destroy if auth
end
def user_authorization(provider)
user_authorizations.where(provider: provider).first
end
def auth_twitter
!user_authorization('twitter').nil?
end
def build_twitter_authorization(auth_hash)
twitter_uid = auth_hash[:uid]
credentials = auth_hash[:credentials]
secret = credentials[:secret] if credentials
token = credentials[:token] if credentials
if twitter_uid && secret && token
user_authorization = nil
unless self.new_record?
# see if this user has an existing user_authorization for this provider
user_authorization = UserAuthorization.find_by_user_id_and_provider(self.id, 'twitter')
end
end
if user_authorization.nil?
user_authorization = UserAuthorization.new(provider: 'twitter',
uid: twitter_uid,
token: token,
secret: secret,
user: self)
else
user_authorization.uid = twitter_uid
user_authorization.token = token
user_authorization.secret = secret
end
user_authorization
end
# updates an existing user_authorization for facebook, or creates a new one if none exist
def update_fb_authorization(fb_signup)
if fb_signup.uid && fb_signup.token && fb_signup.token_expires_at
@ -980,11 +1033,13 @@ module JamRuby
self.user_authorizations.build provider: 'facebook',
uid: fb_signup.uid,
token: fb_signup.token,
token_expiration: fb_signup.token_expires_at
token_expiration: fb_signup.token_expires_at,
user: self
else
user_authorization.uid = fb_signup.uid
user_authorization.token = fb_signup.token
user_authorization.token_expiration = fb_signup.token_expires_at
user_authorization.save
end
end
end

View File

@ -1,18 +1,16 @@
module JamRuby
class UserAuthorization < ActiveRecord::Base
attr_accessible :provider, :uid, :token, :token_expiration
attr_accessible :provider, :uid, :token, :token_expiration, :secret, :user
self.table_name = "user_authorizations"
self.primary_key = 'id'
belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "user_id"
validates :provider, :uid, :presence => true
validates :provider, :uid, :user, :presence => true
validates_uniqueness_of :uid, scope: :provider
# token and token_expiration can be missing
# token, secret, token_expiration can be missing
end
end

View File

@ -32,7 +32,7 @@ FactoryGirl.define do
end
end
factory :music_session, :class => JamRuby::MusicSession do
factory :music_session_no_history, :class => JamRuby::MusicSession do
sequence(:description) { |n| "Music Session #{n}" }
fan_chat true
fan_access true
@ -42,8 +42,16 @@ FactoryGirl.define do
genres [JamRuby::Genre.first]
association :creator, :factory => :user
factory :music_session_with_mount do
factory :music_session do
after(:create) { |session|
MusicSessionHistory.save(session)
}
factory :music_session_with_mount do
association :mount, :factory => :icecast_mount
end
end
end
@ -109,14 +117,46 @@ FactoryGirl.define do
end
factory :recorded_track, :class => JamRuby::RecordedTrack do
instrument JamRuby::Instrument.first
sound 'stereo'
sequence(:client_id) { |n| "client_id-#{n}"}
sequence(:track_id) { |n| "track_id-#{n}"}
sequence(:client_track_id) { |n| "client_track_id-#{n}"}
md5 'abc'
length 1
fully_uploaded true
association :user, factory: :user
association :recording, factory: :recording
end
factory :instrument, :class => JamRuby::Instrument do
description { |n| "Instrument #{n}" }
end
factory :recording, :class => JamRuby::Recording do
association :owner, factory: :user
association :music_session, factory: :music_session
factory :recording_with_track do
before(:create) { |recording|
recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: recording.owner)
}
end
end
factory :claimed_recording, :class => JamRuby::ClaimedRecording do
sequence(:name) { |n| "name-#{n}" }
sequence(:description) { |n| "description-#{n}" }
is_public true
is_downloadable true
association :genre, factory: :genre
association :user, factory: :user
before(:create) { |claimed_recording|
claimed_recording.recording = FactoryGirl.create(:recording_with_track, owner: claimed_recording.user)
}
end
factory :musician_instrument, :class => JamRuby::MusicianInstrument do

View File

@ -120,7 +120,7 @@ describe 'Band search' do
it "by now playing" do
# should get 1 result with 1 active session
session = make_session(@band3)
FactoryGirl.create(:music_session_history, :music_session => session)
#FactoryGirl.create(:music_session_history, :music_session => session)
results = Search.band_filter({ :orderby => 'playing' })
expect(results.results.count).to be 1
@ -129,7 +129,7 @@ describe 'Band search' do
# should get 2 results with 2 active sessions
# sort order should be created_at DESC
session = make_session(@band4)
FactoryGirl.create(:music_session_history, :music_session => session)
#FactoryGirl.create(:music_session_history, :music_session => session)
results = Search.band_filter({ :orderby => 'playing' })
expect(results.results.count).to be 2
expect(results.results[0].id).to eq(@band4.id)

View File

@ -3,7 +3,7 @@ require 'spec_helper'
describe MusicSessionHistory do
let(:some_user) { FactoryGirl.create(:user) }
let(:music_session) { FactoryGirl.create(:music_session) }
let(:music_session) { FactoryGirl.create(:music_session_no_history) }
let(:history) { FactoryGirl.create(:music_session_history, :music_session => music_session) }
let(:user_history1) { FactoryGirl.create(:music_session_user_history, :history => history, :user => music_session.creator) }
let(:user_history2) { FactoryGirl.create(:music_session_user_history, :history => history, :user => some_user) }

View File

@ -3,7 +3,7 @@ require 'spec_helper'
describe MusicSessionUserHistory do
let(:some_user) { FactoryGirl.create(:user) }
let(:music_session) { FactoryGirl.create(:music_session) }
let(:music_session) { FactoryGirl.create(:music_session_no_history) }
let(:history) { FactoryGirl.create(:music_session_history, :music_session => music_session) }
let(:user_history1) { FactoryGirl.create(:music_session_user_history, :history => history, :user => music_session.creator) }
let(:user_history2) { FactoryGirl.create(:music_session_user_history, :history => history, :user => some_user) }

View File

@ -0,0 +1,32 @@
require 'spec_helper'
describe ShareToken do
let(:user) { FactoryGirl.create(:user) }
let(:music_session) {FactoryGirl.create(:music_session) }
let(:claimed_recording) {FactoryGirl.create(:claimed_recording) }
before(:each) do
ShareToken.delete_all
end
it "can reference a music session" do
music_session.touch # should create a MSH, and a token, too
ShareToken.count.should == 1
music_session.music_session_history.share_token.should_not be_nil
token = ShareToken.find_by_shareable_id!(music_session.id)
token.should == music_session.music_session_history.share_token
token.shareable_id.should == music_session.id
token.shareable_type.should == 'session'
end
it "can reference a claimed recording" do
claimed_recording.touch # should create a share token
ShareToken.count.should == 2 # one for MSH, one for recording
claimed_recording.share_token.should_not be_nil
token = ShareToken.find_by_shareable_id!(claimed_recording.id)
claimed_recording.share_token.should == token
token.shareable_type.should == 'recording'
end
end

View File

@ -403,7 +403,8 @@ describe User do
@user.user_authorizations.build provider: 'facebook',
uid: '1',
token: '1',
token_expiration: Time.now
token_expiration: Time.now,
user: @user
@user.save!
end
@ -411,14 +412,16 @@ describe User do
@user.user_authorizations.build provider: 'facebook',
uid: '1',
token: '1',
token_expiration: Time.now
token_expiration: Time.now,
user: @user
@user.save!
@user2 = FactoryGirl.create(:user)
@user2.user_authorizations.build provider: 'facebook',
uid: '1',
token: '1',
token_expiration: Time.now
token_expiration: Time.now,
user: @user2
@user2.save.should be_false
@user2.errors[:user_authorizations].should == ['is invalid']
end

View File

@ -30,6 +30,7 @@ ActiveRecord::Base.add_observer UserObserver.instance
ActiveRecord::Base.add_observer FeedbackObserver.instance
ActiveRecord::Base.add_observer RecordedTrackObserver.instance
RecordedTrack.observers.disable :all # only a few tests want this observer active
# put ActionMailer into test mode
ActionMailer::Base.delivery_method = :test

View File

@ -40,7 +40,9 @@ gem 'amqp', '0.9.8'
gem 'logging-rails', :require => 'logging/rails'
gem 'omniauth', '1.1.1'
gem 'omniauth-facebook', '1.4.1'
gem 'omniauth-twitter'
gem 'omniauth-google-oauth2', '0.2.1'
gem 'twitter'
gem 'fb_graph', '2.5.9'
gem 'sendgrid', '1.1.0'
gem 'recaptcha', '0.3.4'

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -13,6 +13,7 @@
var avatar;
var selection = null;
var targetCropSize = 88;
var largerCropSize = 200;
var updatingAvatar = false;
var userDropdown;
@ -320,15 +321,32 @@
signature: filepickerPolicy.signature
}, { path: createStorePath(self.userDetail), access: 'public' },
function(scaled) {
logger.debug("converted and scaled final image %o", scaled);
rest.updateAvatar({
original_fpfile: determineCurrentFpfile(),
cropped_fpfile: scaled,
crop_selection: currentSelection
})
.done(updateAvatarSuccess)
.fail(app.ajaxError)
.always(function() { removeAvatarSpinner(); self.updatingAvatar = false;})
filepicker.convert(cropped, {
height: largerCropSize,
width: largerCropSize,
fit: 'scale',
format: 'jpg',
quality: 75,
policy: filepickerPolicy.policy,
signature: filepickerPolicy.signature
}, { path: createStorePath(self.userDetail) + 'large.jpg', access: 'public' },
function(scaledLarger) {
logger.debug("converted and scaled final image %o", scaled);
rest.updateAvatar({
original_fpfile: determineCurrentFpfile(),
cropped_fpfile: scaled,
cropped_large_fpfile: scaledLarger,
crop_selection: currentSelection
})
.done(updateAvatarSuccess)
.fail(app.ajaxError)
.always(function() { removeAvatarSpinner(); self.updatingAvatar = false;})
},
function(fperror) {
alert("unable to scale larger selection. error code: " + fperror.code);
removeAvatarSpinner();
self.updatingAvatar = false;
});
},
function(fperror) {
alert("unable to scale selection. error code: " + fperror.code);

View File

@ -20,6 +20,7 @@
//= require jquery.Jcrop
//= require jquery.naturalsize
//= require jquery.queryparams
//= require jquery.clipboard
//= require jquery.timeago
//= require globals
//= require_directory .

View File

@ -192,7 +192,7 @@
if (bandId.length === 0) {
rest.createBand(band).done(function(response) {
createBandInvitations(response.id, function() {
context.location = "#/bandProfile/" + response.id;
context.location = "/client#/bandProfile/" + response.id;
});
});
}
@ -200,7 +200,7 @@
band.id = bandId;
rest.updateBand(band).done(function(response) {
createBandInvitations(band.id, function() {
context.location = "#/bandProfile/" + band.id;
context.location = "/client#/bandProfile/" + band.id;
});
});
}
@ -473,7 +473,7 @@
$('#btn-band-setup-cancel').click(function() {
resetForm();
context.location = "#/home";
context.location = "/client#/home";
});
$('#btn-band-setup-next').click(function() {
@ -510,7 +510,7 @@
$('#band-change-photo').click(function(evt) {
evt.stopPropagation();
$("#hdn-band-id").val(bandId);
context.location = '#/band/setup/photo';
context.location = '/client#/band/setup/photo';
return false;
});

View File

@ -14,13 +14,14 @@
var bandPhoto;
var selection = null;
var targetCropSize = 88;
var largeCropSize = 200;
var updatingBandPhoto = false;
function beforeShow(data) {
bandId = $("#hdn-band-id").val();
logger.debug("bandId=" + bandId);
if (!bandId) {
context.location = '#/home';
context.location = '/client#/home';
}
}
@ -143,7 +144,7 @@
function navToEditProfile() {
resetForm();
$("#hdn-band-id").val(bandId);
context.location = '#/band/setup';
context.location = '/client#/band/setup';
}
function renderBandPhotoSpinner() {
@ -322,16 +323,34 @@
signature: filepickerPolicy.signature
}, { path: createStorePath(self.bandDetail), access: 'public' },
function(scaled) {
logger.debug("converted and scaled final image %o", scaled);
rest.updateBandPhoto({
original_fpfile: determineCurrentFpfile(),
cropped_fpfile: scaled,
crop_selection: currentSelection,
id: bandId
})
.done(updateBandPhotoSuccess)
.fail(app.ajaxError)
.always(function() { removeBandPhotoSpinner(); self.updatingBandPhoto = false;})
filepicker.convert(cropped, {
height: largeCropSize,
width: largeCropSize,
fit: 'scale',
format: 'jpg',
quality: 75,
policy: filepickerPolicy.policy,
signature: filepickerPolicy.signature
}, { path: createStorePath(self.bandDetail) + 'large.jpg', access: 'public' },
function(scaledLarger) {
logger.debug("converted and scaled final image %o", scaled);
rest.updateBandPhoto({
original_fpfile: determineCurrentFpfile(),
cropped_fpfile: scaled,
cropped_large_fpfile: scaledLarger,
crop_selection: currentSelection,
id: bandId
})
.done(updateBandPhotoSuccess)
.fail(app.ajaxError)
.always(function() { removeBandPhotoSpinner(); self.updatingBandPhoto = false;})
},
function(fperror) {
alert("unable to scale larger selection. error code: " + fperror.code);
removeBandPhotoSpinner();
self.updatingBandPhoto = false;
})
},
function(fperror) {
alert("unable to scale selection. error code: " + fperror.code);

View File

@ -165,7 +165,7 @@
success: function(response) {
var newSessionId = response.id;
var invitationCount = inviteMusiciansUtil.createInvitations(newSessionId, function() {
context.location = '#/session/' + newSessionId;
context.location = '/client#/session/' + newSessionId;
});
// Re-loading the session settings will cause the form to reset with the right stuff in it.
// This is an extra xhr call, but it keeps things to a single codepath

View File

@ -0,0 +1,74 @@
(function(context,$) {
"use strict";
context.JK = context.JK || {};
context.JK.FacebookHelper = function(app) {
var logger = context.JK.logger;
var loginStatusDeferred = null;
var $self = $(this);
var connected = false;
function promptLogin() {
if(connected) {
// instantly return previous login info
return loginStatusDeferred;
}
loginStatusDeferred = $.Deferred();
FB.login(function(response) {
handle_fblogin_response(response);
}, {scope:'publish_stream'});
return loginStatusDeferred;
}
function handle_fblogin_response(response) {
console.log("facebook login response: status=" + response.status)
if(response.status == "connected") {
connected = true;
}
$self.triggerHandler('fb.login_response', {response: response});
loginStatusDeferred.resolve(response);
}
function initialize(fbAppID) {
loginStatusDeferred = $.Deferred();
var fbAppID_ = fbAppID;
window.fbAsyncInit = function() {
FB.init({
appId : fbAppID_,
// channelUrl : '//WWW.YOUR_DOMAIN.COM/channel.html',
status : true, // check the login status upon init?
cookie : true, // set sessions cookies to allow server to access the session?
xfbml : true, // parse XFBML tags on this page?
oauth : true // enable OAuth 2.0
});
// listen to see if the user is known/logged in
FB.getLoginStatus(function(response) {
handle_fblogin_response(response);
});
};
// Load the SDK Asynchronously
(function(d){
var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;}
js = d.createElement('script'); js.id = id; js.async = true;
js.src = "//connect.facebook.net/en_US/all.js";
d.getElementsByTagName('head')[0].appendChild(js);
}(document));
}
this.initialize = initialize;
this.promptLogin = promptLogin;
};
})(window, jQuery);

View File

@ -0,0 +1,40 @@
(function(context,$) {
/**
* Javascript wrappers for the REST API
*/
"use strict";
context.JK = context.JK || {};
context.JK.FacebookRest = function() {
var self = this;
var logger = context.JK.logger;
// https://developers.facebook.com/docs/reference/api/post
function post(options) {
var d = $.Deferred();
FB.api(
'https://graph.facebook.com/me/feed',
'post',
options,
function(response) {
if (!response || response.error) {
d.reject(response)
} else {
d.resolve(response);
}
}
)
return d;
}
this.post = post;
return this;
};
})(window,jQuery);

View File

@ -94,7 +94,7 @@
function events() {
$('body').on('click', 'div[layout="header"] h1', function() {
context.location = '#/home';
context.location = '/client#/home';
});
$('#account-identity-form').submit(handleIdentitySubmit);

View File

@ -287,7 +287,7 @@
app.bindDialog('inviteUsers', dialogBindings);
callFB(fbAppID);
//callFB(fbAppID);
};
this.initialize = initialize;

View File

@ -43,6 +43,16 @@
});
}
function getSessionHistory(id) {
return $.ajax({
type: "GET",
dataType: "json",
url: '/api/sessions/' + id + '/history',
contentType: 'application/json',
processData: false
});
}
function addSessionComment(sessionId, userId, comment) {
return $.ajax({
url: '/api/sessions/' + sessionId + "/comments",
@ -274,11 +284,13 @@
var original_fpfile = options['original_fpfile'];
var cropped_fpfile = options['cropped_fpfile'];
var cropped_large_fpfile = options['cropped_large_fpfile'];
var crop_selection = options['crop_selection'];
logger.debug(JSON.stringify({
original_fpfile : original_fpfile,
cropped_fpfile : cropped_fpfile,
cropped_large_fpfile : cropped_large_fpfile,
crop_selection : crop_selection
}));
@ -292,6 +304,7 @@
data: JSON.stringify({
original_fpfile : original_fpfile,
cropped_fpfile : cropped_fpfile,
cropped_large_fpfile : cropped_large_fpfile,
crop_selection : crop_selection
})
});
@ -328,11 +341,13 @@
var original_fpfile = options['original_fpfile'];
var cropped_fpfile = options['cropped_fpfile'];
var cropped_large_fpfile = options['cropped_large_fpfile'];
var crop_selection = options['crop_selection'];
logger.debug(JSON.stringify({
original_fpfile : original_fpfile,
cropped_fpfile : cropped_fpfile,
cropped_large_fpfile : cropped_large_fpfile,
crop_selection : crop_selection
}));
@ -346,6 +361,7 @@
data: JSON.stringify({
original_fpfile : original_fpfile,
cropped_fpfile : cropped_fpfile,
cropped_large_fpfile : cropped_large_fpfile,
crop_selection : crop_selection
})
});
@ -665,7 +681,6 @@
}
function getClaimedRecordings(options) {
return $.ajax({
type: "GET",
dataType: "json",
@ -675,6 +690,15 @@
});
}
function getClaimedRecording(id) {
return $.ajax({
type: "GET",
dataType: "json",
contentType: 'application/json',
url: "/api/claimed_recordings/" + id
});
}
function claimRecording(options) {
var recordingId = options["id"];
@ -743,6 +767,44 @@
});
}
function getShareSession(options) {
var id = getId(options);
var provider = options['provider'];
delete options['provider']
return $.ajax({
type: "GET",
dataType: "json",
contentType: 'application/json',
url: "/api/users/" + id + "/share/session/" + provider,
data: options
})
}
function getShareRecording(options) {
var id = getId(options);
var provider = options['provider'];
delete options['provider']
return $.ajax({
type: "GET",
dataType: "json",
contentType: 'application/json',
url: "/api/users/" + id + "/share/recording/" + provider,
data: options
})
}
function tweet(options) {
return $.ajax({
type: "POST",
dataType: "json",
contentType: 'application/json',
url: "/api/twitter/tweet",
data: JSON.stringify(options)
})
}
function initialize() {
return self;
}
@ -770,6 +832,7 @@
this.getBandFollowing = getBandFollowing;
this.getBands = getBands;
this.updateSession = updateSession;
this.getSessionHistory = getSessionHistory;
this.addSessionComment = addSessionComment;
this.addSessionLike = addSessionLike;
this.addRecordingComment = addRecordingComment;
@ -792,6 +855,7 @@
this.stopRecording = stopRecording;
this.getRecording = getRecording;
this.getClaimedRecordings = getClaimedRecordings;
this.getClaimedRecording = getClaimedRecording;
this.claimRecording = claimRecording;
this.startPlayClaimedRecording = startPlayClaimedRecording;
this.stopPlayClaimedRecording = stopPlayClaimedRecording;
@ -807,6 +871,9 @@
this.updateBandInvitation = updateBandInvitation;
this.removeBandMember = removeBandMember;
this.login = login;
this.getShareSession = getShareSession;
this.getShareRecording = getShareRecording;
this.tweet = tweet;
return this;
};

View File

@ -54,7 +54,7 @@
if (targetArg) {
targetUrl += "/:" + targetArg;
}
rules[target] = {route: '/' + targetUrl + '/d:?', method: target};
rules[target] = {route: '/' + targetUrl + '/:d?', method: target};
routingContext[target] = fn;
});
routes.context(routingContext);
@ -328,7 +328,7 @@
hash = null;
}
var url = '#/home';
var url = '/client#/home';
if (hash) {
url = hash;
}
@ -337,6 +337,7 @@
context.location = url;
}
this.unloadFunction = function() {
logger.debug("window.unload function called.");
@ -359,6 +360,7 @@
this.layout = new context.JK.Layout();
this.layout.initialize(this.opts.layoutOpts);
events();
this.layout.handleDialogState();
if(opts.inClient) {
registerLoginAck();

View File

@ -41,7 +41,8 @@
gridOuterMargin: 6, // Outer margin on Grids (added to screenMargin if screen)
gridPadding: 8, // Padding around grid cells. Added to outer margin.
animationDuration: 400,
allowBodyOverflow: false // Allow tests to disable the body-no-scroll policy
allowBodyOverflow: false, // Allow tests to disable the body-no-scroll policy
sizeOverlayToContent: false // if true, use the size of <body> tag to decide overlay size everytime overlay is shown. should be used in non-client settings
};
var width = $(context).width();
@ -400,7 +401,7 @@
var destination = $(evt.currentTarget).attr('layout-link');
var destinationType = $('[layout-id="' + destination + '"]').attr("layout");
if (destinationType === "screen") {
context.location = '#/' + destination;
context.location = '/client#/' + destination;
} else if (destinationType === "dialog") {
showDialog(destination);
}
@ -522,6 +523,15 @@
function showDialog(dialog) {
if(!dialogEvent(dialog, 'beforeShow')) {return;}
var $overlay = $('.dialog-overlay')
if(opts.sizeOverlayToContent) {
var $body = $('body')
$('.dialog-overlay').css({
width: $body.width() + 'px',
height: $body.height() + 'px'
});
}
$overlay.show();
centerDialog(dialog);
var $dialog = $('[layout-id="' + dialog + '"]');
@ -532,6 +542,7 @@
function centerDialog(dialog) {
var $dialog = $('[layout-id="' + dialog + '"]');
console.log("$dialog.width, height", $dialog.width(), $dialog.height())
$dialog.css({
left: width/2 - ($dialog.width()/2) + "px",
top: height/2 - ($dialog.height()/2) + "px"
@ -578,6 +589,29 @@
context.JK.GA.virtualPageView(location.pathname + location.search + location.hash);
}
function handleDialogState() {
var rawDialogState = $.cookie('dialog_state');
try {
var dialogState = JSON.parse(rawDialogState);
if(!dialogState) { $.removeCookie('dialog_state'); return; }
}
catch (e) {$.removeCookie('dialog_state'); return; }
var dialogName = dialogState['name'];
if(dialogName) {
setTimeout(function() {
// TODO: we need a 'everything is initialized' event
showDialog(dialogName);
}, 0);
}
$.removeCookie('dialog_state');
}
// on next page load, a dialog of this name will show
function queueDialog(name) {
$.cookie('dialog_state', JSON.stringify({name:name}))
}
function events() {
$(context).resize(function() {
if (resizing) {
@ -725,6 +759,9 @@
this.closeDialog = closeDialog;
this.handleDialogState = handleDialogState;
this.queueDialog = queueDialog;
/**
* Given information on a grid, and a given card's grid settings, use the
* margin options and return a list of [top, left, width, height]

View File

@ -44,7 +44,7 @@
$('#search-results').empty();
var query = $('#search-input').val();
if (query) {
context.location = '#/searchResults/:' + query;
context.location = '/client#/searchResults/:' + query;
} else {
query = $('#query').html();
}

View File

@ -102,6 +102,9 @@
sessionId = data.id;
$('#session-mytracks-container').empty();
displayDoneRecording(); // assumption is that you can't join a recording session, so this should be safe
var shareDialog = new JK.ShareDialog(context.JK.app, sessionId, "session");
shareDialog.initialize(context.JK.FacebookHelperInstance);
}
function alertCallback(type, text) {

View File

@ -249,7 +249,7 @@
}
function onTermsAccepted(sessionId) {
context.location = '#/session/' + sessionId;
context.location = '/client#/session/' + sessionId;
}
function events() {

View File

@ -2,114 +2,483 @@
"use strict";
context.JK = context.JK || {};
context.JK.ShareDialog = function(app) {
context.JK.ShareDialog = function(app, entityId, entityType) {
var logger = context.JK.logger;
var rest = context.JK.Rest();
var dialogId = '#share-dialog'
var facebookRest = context.JK.FacebookRest();
var facebookHelper = null;
var dialogId = '#share-dialog';
var userDetail = null;
var entity = null;
var remainingCap = 140 - 22 - 1; // 140 tweet max, minus 22 for link size, minus 1 for space
function registerEvents(onOff) {
var textMap = {
LIVE_SESSION: "LIVE SESSION",
SESSION: "SESSION",
RECORDING: "RECORDING",
RECORDED: "RECORDED"
};
$(dialogId + ' .dialog-share-button').unbind('click').click(function(e) {
function showSpinner() {
$(dialogId + ' .dialog-inner').hide();
var spinner = $('<div class="spinner spinner-large"></div>')
$(dialogId + ' .content-head').after(spinner);
return false;
})
}
this.fb_login = function() {
FB.login(function(response) {
handle_fblogin_response(response);
}, {scope:'publish_stream'});
function hideSpinner() {
$(dialogId + ' .spinner').remove();
$(dialogId + ' .dialog-inner').show();
}
function handleRecordingShareWithGoogle(message) {
var defer = $.Deferred();
defer.resolve(); // remove when implemented
return defer;
}
function handleRecordingShareWithTwitter(message) {
var defer = $.Deferred();
rest.tweet({message: message + ' ' + entity.share_url})
.done(function() {
defer.resolve();
})
.fail(function(jqXHR) {
if(jqXHR.status == 422) {
// implies twitter token error.
app.notify({
title : "Failed to Tweet",
text : "You need to re-authorize JamKazam to access your Twitter account. Click (sign in) in the Share Dialog.",
"icon_url": "/assets/content/icon_alert_big.png"
});
disableTwitter();
}
else {
app.notifyServerError(jqXHR, "Unable to Share with Twitter");
}
defer.reject();
})
return defer;
}
function handleRecordingShareWithFacebook(message) {
var defer = $.Deferred();
rest.getShareRecording({ provider:'facebook', claimed_recording: entityId})
.done(function(data) {
facebookHelper.promptLogin()
.done(function(response) {
if(response.status == "connected") {
facebookRest.post({
access_token: response.authResponse.accessToken,
message: message,
description: data.description,
caption: data.caption,
name: data.title,
picture: data.photo_url
})
.done(function(response) {
defer.resolve();
})
.fail(function(response) {
app.notify({
title : "Unable to Share with Facebook",
text : "Error: " + response,
"icon_url": "/assets/content/icon_alert_big.png"
});
defer.reject();
})
}
else {
// user doesn't want to auth; this is a form of success
defer.resolve();
}
})
})
.fail(function(jqXHR) {
app.notifyServerError(jqXHR, "Unable to Populate Share Data");
defer.reject();
})
return defer;
}
function handleSessionShareWithGoogle(message) {
var defer = $.Deferred();
defer.resolve(); // remove when implemented
return defer;
}
// 116 characters
// abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdef
function handleSessionShareWithTwitter(message) {
var defer = $.Deferred();
rest.tweet({message: message + ' ' + entity.share_url})
.done(function() {
defer.resolve();
})
.fail(function(jqXHR) {
if(jqXHR.status == 422) {
// implies twitter token error.
app.notify({
title : "Failed to Tweet",
text : "You need to re-authorize JamKazam to access your Twitter account. Click (sign in) in the Share Dialog.",
"icon_url": "/assets/content/icon_alert_big.png"
});
disableTwitter();
}
else {
app.notifyServerError(jqXHR, "Unable to Share with Twitter");
}
defer.reject();
})
return defer;
}
function handleSessionShareWithFacebook(message) {
var defer = $.Deferred();
rest.getShareSession({ provider:'facebook', music_session: entityId})
.done(function(data) {
facebookHelper.promptLogin()
.done(function(response) {
if(response.status == "connected") {
facebookRest.post({
access_token: response.authResponse.accessToken,
message: message,
description: data.description,
caption: data.caption,
name: data.title,
picture: data.photo_url
})
.done(function(response) {
defer.resolve();
})
.fail(function(response) {
app.notify({
title : "Unable to Share with Facebook",
text : "Error: " + response,
"icon_url": "/assets/content/icon_alert_big.png"
});
defer.reject();
})
}
else {
// user doesn't want to auth; this is a form of success
defer.resolve();
}
})
})
.fail(function(jqXHR) {
app.notifyServerError(jqXHR, "Unable to Populate Share Data");
defer.reject();
})
return defer;
}
function messageTooLongForTwitter(message) {
return message && message.length > remainingCap;
}
function socialShare() {
var facebookCheckbox = $(dialogId + ' .share-with-facebook input');
var shareWithFacebook = facebookCheckbox.is(':checked') && !facebookCheckbox.is(':disabled');
var googleCheckbox = $(dialogId + ' .share-with-google input');
var shareWithGoogle = googleCheckbox.is(':checked') && !googleCheckbox.is(':disabled');
var twitterCheckbox = $(dialogId + ' .share-with-twitter input');
var shareWithTwitter = twitterCheckbox.is(':checked') && !twitterCheckbox.is(':disabled');
if(!shareWithFacebook && !shareWithGoogle && !shareWithTwitter) {
$(dialogId + ' .share-options').addClass('error')
return;
}
else {
$(dialogId + ' .share-options').removeClass('error')
}
var message = $(dialogId + ' .share-message').val();
if(!message) { message = undefined; }
if(shareWithTwitter && !message) {
$(dialogId + ' .share-message-holder').addClass('error')
$(dialogId + ' .share-message-holder .error-msg').text("You must specify a message for Twitter.");
return;
}
else
{
$(dialogId + ' .share-message-holder').removeClass('error')
$(dialogId + ' .share-message-holder .error-msg').text('');
}
// validate twitter message length
if(shareWithTwitter && messageTooLongForTwitter(message)) {
$(dialogId + ' .share-message-holder').addClass('error')
$(dialogId + ' .share-message-holder .error-msg').text("Your message must be less than " + (remainingCap + 1) + " characters in length for Twitter (currently " + message.length + ").");
return;
}
else
{
$(dialogId + ' .share-message-holder').removeClass('error')
$(dialogId + ' .share-message-holder .error-msg').text('');
}
showSpinner();
var chain = [];
if(entityType == 'session') {
if(shareWithFacebook) {
chain.push(handleSessionShareWithFacebook(message))
}
if(shareWithTwitter) {
chain.push(handleSessionShareWithTwitter(message))
}
if(shareWithGoogle) {
chain.push(handleSessionShareWithGoogle(message))
}
}
else {
if(shareWithFacebook) {
chain.push(handleRecordingShareWithFacebook(message))
}
if(shareWithTwitter) {
chain.push(handleRecordingShareWithTwitter(message))
}
if(shareWithGoogle) {
chain.push(handleRecordingShareWithGoogle(message))
}
}
$.when.apply($, chain)
.done(function() {
app.layout.closeDialog('share-dialog');
})
.fail(function() {
logger.error("share failed")
})
.always(function() {
hideSpinner();
});
}
function enableFacebook() {
$(dialogId + ' .share-with-facebook input').removeAttr('disabled')
$(dialogId + ' .share-with-facebook a').css('visibility', 'hidden');
}
function disableFacebook() {
$(dialogId + ' .share-with-facebook input').attr('disabled', 'disabled')
$(dialogId + ' .share-with-facebook a').css('visibility', 'visible');
}
function enableTwitter() {
$(dialogId + ' .share-with-twitter input').removeAttr('disabled')
$(dialogId + ' .share-with-twitter a').css('visibility', 'hidden');
}
function disableTwitter() {
$(dialogId + ' .share-with-twitter input').attr('disabled', 'disabled')
$(dialogId + ' .share-with-twitter a').css('visibility', 'visible');
}
function handleFbStateChange(response) {
if (response && response.status == "connected") {
enableFacebook();
}
else{
disableFacebook();
}
}
function registerEvents(onOff) {
$(dialogId + ' .dialog-share-button').unbind('click').click(function(e){
socialShare();
return false;
});
$(dialogId + ' .share-with-facebook a').unbind('click').click(function(e) {
facebookHelper.promptLogin();
return false;
});
$(dialogId + ' .share-with-twitter a').unbind('click').click(function(e) {
app.layout.queueDialog('share-dialog')
window.location = '/auth/twitter';
return false;
})
/*
$("#btn-share-copy").zclip({
path: 'zeroclipboard.swf',
copy: function() {
// console.log("copied " + $(".link-contents").text());
return "TEXT";
}
});*/
}
function showDialog() {
app.layout.showDialog('share-dialog');
}
/*function showEmailDialog() {
$('#invitation-dialog').show();
$('#invitation-textarea-container').show();
$('#invitation-checkbox-container').hide();
$('#btn-send-invitation').show();
$('#btn-next-invitation').hide();
clearTextFields();
app.layout.showDialog('inviteUsers')
}
function initDialog() {
var sessionText = textMap.SESSION;
var liveSessionText = textMap.LIVE_SESSION;
function showGoogleDialog() {
$('#invitation-dialog').show();
$('#invitation-textarea-container').hide();
$('#invitation-checkbox-container').show();
$('#btn-send-invitation').hide();
$('#btn-next-invitation').show();
clearTextFields();
var fill = entityType === sessionText.toLowerCase() ? "teal-fill" : "orange-fill";
app.layout.showDialog('inviteUsers')
$("#shareType").text(entityType);
$('#invitation-checkboxes').html('<div style="text-align: center; margin-top: 100px;">Loading your contacts...</div>');
window._oauth_callback = function() {
window._oauth_win.close();
window._oauth_win = null;
window._oauth_callback = null;
$.ajax({
type: "GET",
url: "/gmail_contacts",
success: function(response) {
$('#invitation-checkboxes').html('');
for (var i in response) {
$('#invitation-checkboxes').append("<label><input type='checkbox' class='invitation-checkbox' data-email='" + response[i] + "' /> " + response[i] + "</label>");
$("#divWidgetCodeHeader").addClass(fill);
$("#divWidgetPreviewHeader").addClass(fill);
$("#divWidgetPreview").addClass(entityType);
// SESSION
if (entityType === sessionText.toLowerCase()) {
$("#lblWidgetCodeType").html(sessionText.toLowerCase());
$("#lblWidgetPreviewType").html(sessionText.toLowerCase());
$("#spnWidgetCodeBranding").text(liveSessionText.toLowerCase());
$("#spnWidgetPreviewBranding").text(liveSessionText.toLowerCase());
rest.getSessionHistory(entityId)
.done(function(response) {
// var name, photoUrl;
$(".link-contents").html(response.share_url);
});
}
// RECORDING
else if (entityType === "recording") {
var recordedText = textMap.RECORDED.toLowerCase();
$("#lblWidgetCodeType").text(textMap.RECORDING);
$("#lblWidgetPreviewType").text(textMap.RECORDING);
$("#spnWidgetCodeBranding").text(recordedText);
$("#spnWidgetPreviewBranding").text(recordedText);
rest.getClaimedRecording(entityId)
.done(function(response) {
var name, photoUrl;
if (response.recording.band) {
name = response.recording.band.name;
photoUrl = context.JK.resolveBandAvatarUrl(response.recording.band.photo_url);
}
else {
name = response.recording.owner.name;
photoUrl = context.JK.resolveAvatarUrl(response.recording.owner.photo_url);
}
$('.invitation-checkbox').change(function() {
var checkedBoxes = $('.invitation-checkbox:checkbox:checked');
var emails = '';
for (var i = 0; i < checkedBoxes.length; i++) {
emails += $(checkedBoxes[i]).data('email') + ', ';
}
emails = emails.replace(/, $/, '');
// track how many of these came from google
$('#txt-emails').val(emails).data('google_invite_count', checkedBoxes.length);
$(".link-contents").html(response.share_url);
$("#imgWidgetCodeAvatar").attr('src', photoUrl);
$("#imgWidgetPreviewAvatar").attr('src', photoUrl);
$("#divWidgetPreviewTitle").html(response.recording.name);
$("#spnWidgetCodeArtistName").html(name);
$("#spnWidgetPreviewArtistName").html(name);
$.each(response.recording.recorded_tracks, function(index, val) {
$(".widget-members").append('<div class="widget-avatar-small">' + '<img src="' + context.JK.resolveAvatarUrl(val.user.photo_url) + '" alt="" />' + '</div>');
});
},
error: function() {
$('#invitation-checkboxes').html("Load failed!");
}
});
};
window._oauth_win = window.open("/auth/google_login", "_blank", "height=500,width=500,menubar=no,resizable=no,status=no");
});
}
}
function showFacebookDialog() {
window._oauth_callback = function() {
window._oauth_win.close();
window._oauth_win = null;
window._oauth_callback = null;
};
window._oauth_win = window.open("/auth/facebook_login", "_blank", "height=500,width=500,menubar=no,resizable=no,status=no");
}*/
function clearTextFields() {
}
function beforeShow() {
disableTwitter();
// no disableFacebook on purpose
if(entityType == 'recording') {
rest.getClaimedRecording(entityId)
.done(function(data) {
entity = data;
$(dialogId + ' .link-contents').text(entity.share_url)
})
.fail(function(jqXHR) {
app.notifyServerError(jqXHR, "Unable to Fetch Session Data");
})
}
else {
rest.getSession(entityId)
.done(function(data) {
entity = data;
$(dialogId + ' .link-contents').text(entity.share_url)
})
.fail(function(jqXHR) {
app.notifyServerError(jqXHR, "Unable to Fetch Session Data");
});
}
rest.getUserDetail()
.done(function(data) {
userDetail = data;
if(data.auth_twitter) {
enableTwitter();
}
else {
disableTwitter();
}
});
$(dialogId + ' .share-message-holder').removeClass('error')
$(dialogId + ' .share-message-holder .error-msg').text('');
$(dialogId + ' .share-options').removeClass('error');
registerEvents(true);
}
function afterShow() {
$("#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();
}
});
}
function afterHide() {
hideSpinner();
registerEvents(false);
}
function initialize(){
function initialize(_facebookHelper){
facebookHelper = _facebookHelper;
var dialogBindings = {
'beforeShow' : beforeShow,
'afterShow' : afterShow,
'afterHide': afterHide
};
app.bindDialog('shareRecording', dialogBindings);
app.bindDialog('share-dialog', dialogBindings);
// callFB(fbAppID);
initDialog();
$(facebookHelper).on('fb.login_response', function(e, data) {
handleFbStateChange(data.response);
})
};
this.initialize = initialize;
@ -117,4 +486,4 @@
}
return this;
})(window,jQuery);
})(window,jQuery)

View File

@ -585,7 +585,7 @@
function onTermsAccepted(args) {
deleteNotification(args.notification_id);
context.location = '#/session/' + args.session_id;
context.location = '/client#/session/' + args.session_id;
}
function registerSessionEnded() {

View File

@ -79,7 +79,6 @@
// initially show avatar
function showAvatar() {
var photoUrl = context.JK.resolveAvatarUrl(userMe.photo_url);
logger.debug("photoUrl=" + photoUrl);
$('#header-avatar').attr('src', photoUrl);
}

View File

@ -1,10 +1,75 @@
$(function() {
(function(context, $) {
function like() {
context.JK.ShowRecording = function(app) {
var logger = context.JK.logger;
var rest = new JK.Rest();
var recordingId = null;
var claimedRecordingId = null;
}
function like() {
rest.addRecordingLike(recordingId, JK.currentUserId)
.done(function(response) {
$("#spnLikeCount").html(parseInt($("#spnLikeCount").text()) + 1);
$("#btnLike").unbind("click");
});
}
// search click handler
$('#btnlike').click(like);
function play() {
rest.addRecordingPlay(recordingId, JK.currentUserId)
.done(function(response) {
$("#spnPlayCount").html(parseInt($("#spnPlayCount").text()) + 1);
});
}
});
function addComment() {
var comment = $("#txtRecordingComment").val();
if ($.trim(comment).length > 0) {
rest.addRecordingComment(recordingId, JK.currentUserId, comment)
.done(function(response) {
$("#spnCommentCount").html(parseInt($("#spnCommentCount").text()) + 1);
var template = $('#template-landing-comment').html();
var commentHtml = context.JK.fillTemplate(template, {
avatar_url: context.JK.currentUserAvatarUrl,
name: context.JK.currentUserName,
comment: comment
});
$(".landing-comment-scroller").prepend(commentHtml);
});
}
}
function initialize(_claimedRecordingId, _recordingId) {
recordingId = _recordingId;
claimedRecordingId = _claimedRecordingId;
if (JK.currentUserId) {
var shareDialog = new JK.ShareDialog(JK.app, claimedRecordingId, "recording");
shareDialog.initialize(context.JK.FacebookHelperInstance);
$("#btnShare").click(function(e) {
shareDialog.showDialog();
});
$("#txtRecordingComment").keypress(function(e) {
if (e.which === 13) {
addComment();
$(this).val('');
$(this).blur();
}
});
}
else {
$("#txtRecordingComment").attr("disabled", "disabled");
$("#txtRecordingComment").val("You must be logged in to add a comment.");
}
$("#btnLike").click(like);
$("#btnPlay").click(play);
}
this.initialize = initialize;
}
})(window, jQuery);

View File

@ -1,10 +1,69 @@
$(function() {
(function(context, $) {
function like() {
context.JK.ShowMusicSession = function(app) {
var logger = context.JK.logger;
var rest = new JK.Rest();
var sessionId = null;
}
function like() {
rest.addSessionLike(sessionId, JK.currentUserId)
.done(function(response) {
$("#spnLikeCount").html(parseInt($("#spnLikeCount").text()) + 1);
$("#btnLike").unbind("click");
});
}
// search click handler
$('#btnlike').click(like);
function addComment() {
var comment = $("#txtSessionComment").val();
if ($.trim(comment).length > 0) {
rest.addSessionComment(sessionId, JK.currentUserId, comment)
.done(function(response) {
$("#spnCommentCount").html(parseInt($("#spnCommentCount").text()) + 1);
var template = $('#template-landing-comment').html();
var commentHtml = context.JK.fillTemplate(template, {
avatar_url: context.JK.currentUserAvatarUrl,
name: context.JK.currentUserName,
comment: comment
});
});
$(".landing-comment-scroller").prepend(commentHtml);
});
}
}
function initialize(musicSessionId) {
sessionId = musicSessionId;
if (context.JK.currentUserId) {
var shareDialog = new JK.ShareDialog(context.JK.app, sessionId, "session");
shareDialog.initialize(context.JK.FacebookHelperInstance);
// shareDialog.showDialog();
$("#btnShare").click(function(e) {
shareDialog.showDialog();
});
$("#txtSessionComment").keypress(function(e) {
if (e.which === 13) {
addComment();
$(this).val('');
$(this).blur();
}
});
}
else {
$("#txtSessionComment").attr("disabled", "disabled");
$("#txtSessionComment").val("You must be logged in to add a comment.");
}
$("#btnLike").click(like);
}
this.initialize = initialize;
}
})(window, jQuery);

View File

@ -2,9 +2,12 @@
//= require jquery_ujs
//= require jquery.queryparams
//= require jquery.hoverIntent
//= require jquery.cookie
//= require jquery.clipboard
//= require AAA_Log
//= require AAC_underscore
//= require globals
//= require facebook_helper
//= require web/signupDialog
//= require web/signinDialog
//= require invitationDialog
@ -15,6 +18,7 @@
//= require utils
//= require ga
//= require jam_rest
//= require facebook_rest
//= require landing/init
//= require landing/signup
//= require web/downloads

View File

@ -33,6 +33,7 @@
*= require ./ftue
*= require ./invitationDialog
*= require ./shareDialog
*= require ./hoverBubble
*= require ./recordingFinishedDialog
*= require ./localRecordingsDialog
*= require ./createSession

View File

@ -1,194 +1,283 @@
body.widgets {
background:#fff;
padding:20px;
}
#share-dialog {
h3 {
font-size:20px;
font-weight:normal;
}
width:500px;
.share-overlay {
.button-orange {
margin:0 2px 0 0;
}
}
body.widgets {
background: #fff;
padding: 20px;
}
.widget {
width:430px;
height:180px;
background:#353535;
border:solid 1px;
text-align:left;
}
h3 {
font-size: 20px;
font-weight: normal;
}
.widget.session {
border-color:#0b6672;
}
.share-overlay {
.widget.recording {
border-color:#ed3618;
}
}
.widget-header {
color:#fff;
font-size:17px;
padding:8px;
}
.share-to-social-media {
margin-bottom: 20px;
padding-bottom: 20px;
}
.widget-content {
width:100%;
color:#ccc;
position:relative;
}
.widget {
width: 430px;
height: 180px;
background: #353535;
border: solid 1px;
text-align: left;
}
.widget-avatar {
top:15px;
left:15px;
position:absolute;
padding:2px;
width:110px;
height:110px;
background-color:#ed3618;
-webkit-border-radius:57px;
-moz-border-radius:57px;
border-radius:57px;
margin-bottom:10px;
}
.widget.session {
border-color: #0b6672;
}
.widget-avatar img {
width:110px;
height:110px;
-webkit-border-radius:55px;
-moz-border-radius:55px;
border-radius:55px;
}
.widget.recording {
border-color: #ed3618;
}
.widget-playbutton {
position:absolute;
top:55px;
left:55px;
width:35px;
height:31px;
background-image:url(../shared/play_button.png);
background-repeat:no-repeat;
}
.widget-header {
color: #fff;
font-size: 17px;
padding: 8px;
}
.widget-pausebutton {
position:absolute;
top:55px;
left:55px;
width:35px;
height:31px;
background-image:url(../shared/pause_button.png);
background-repeat:no-repeat;
}
.widget-content {
width: 100%;
color: #ccc;
position: relative;
}
.widget-title {
font-size:18px;
position:absolute;
top:20px;
left:153px;
width:260px;
height:22px;
overflow:hidden;
white-space:nowrap;
text-overflow:ellipsis;
}
.widget-avatar {
top: 15px;
left: 15px;
position: absolute;
padding: 2px;
width: 110px;
height: 110px;
background-color: #ed3618;
-webkit-border-radius: 57px;
-moz-border-radius: 57px;
border-radius: 57px;
margin-bottom: 10px;
}
.widget-description {
font-size:13px;
position:absolute;
top:15px;
left:153px;
width:260px;
height:32px;
overflow:hidden;
text-overflow:ellipsis;
}
.widget-avatar img {
width: 110px;
height: 110px;
-webkit-border-radius: 55px;
-moz-border-radius: 55px;
border-radius: 55px;
}
.widget-controls {
position:absolute;
top:25px;
left:153px;
width:270px;
height:32px;
}
.widget-playbutton {
position: absolute;
top: 55px;
left: 55px;
width: 35px;
height: 31px;
background-image: url(../shared/play_button.png);
background-repeat: no-repeat;
}
.widget-members {
position:absolute;
left:153px;
top:60px;
width:280px;
height:38px;
overflow:hidden;
}
.widget-pausebutton {
position: absolute;
top: 55px;
left: 55px;
width: 35px;
height: 31px;
background-image: url(../shared/pause_button.png);
background-repeat: no-repeat;
}
.widget-social {
position:absolute;
top:75px;
left:153px;
width:270px;
height:20px;
font-size:13px;
}
.widget-title {
font-size: 18px;
position: absolute;
top: 20px;
left: 153px;
width: 260px;
height: 22px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.widget-branding {
.widget-description {
font-size: 13px;
position: absolute;
top: 15px;
left: 153px;
width: 260px;
height: 32px;
overflow: hidden;
text-overflow: ellipsis;
}
.widget-controls {
position: absolute;
top: 25px;
left: 153px;
width: 270px;
height: 32px;
}
.widget-members {
position: absolute;
left: 153px;
top: 60px;
width: 280px;
height: 38px;
overflow: hidden;
}
.widget-social {
position: absolute;
top: 75px;
left: 153px;
width: 270px;
height: 20px;
font-size: 13px;
}
.widget-branding {
position: absolute;
top: 110px;
right: 8px;
width: 270px;
height: 34px;
font-size: 13px;
text-align:right;
}
text-align: right;
}
.widget .recording-controls {
margin-top:0px;
height:22px;
padding:8px 5px;
}
.widget .recording-controls {
margin-top: 0px;
height: 22px;
padding: 8px 5px;
}
.widget .recording-playback {
width:65%;
}
.widget .recording-playback {
width: 65%;
}
.widget .recording-position {
margin-left:-30px;
width:95%;
}
.widget .recording-position {
margin-left: -30px;
width: 95%;
}
.widget .recording-current {
top:8px;
}
.widget .recording-current {
top: 8px;
}
.widget a {
color:#ccc;
text-decoration:none;
}
.widget a {
color: #ccc;
text-decoration: none;
}
img.space {
margin-left:28px;
}
img.space {
margin-left: 28px;
}
.widget a:hover {
color:#fff;
}
.widget a:hover {
color: #fff;
}
.widget-avatar-small {
float:left;
padding:1px;
width: 36px;
height:36px;
background-color:#ed3618;
-webkit-border-radius:18px;
-moz-border-radius:18px;
border-radius:18px;
margin-right:15px;
}
.widget-avatar-small img {
.widget-avatar-small {
float: left;
padding: 1px;
width: 36px;
height: 36px;
-webkit-border-radius:18px;
-moz-border-radius:18px;
border-radius:18px;
}
background-color: #ed3618;
-webkit-border-radius: 18px;
-moz-border-radius: 18px;
border-radius: 18px;
margin-right: 15px;
}
.widget-avatar-small img {
width: 36px;
height: 36px;
-webkit-border-radius: 18px;
-moz-border-radius: 18px;
border-radius: 18px;
}
.share-button-holder {
float: right;
margin-top: 5px;
}
.share-message-holder {
margin:0 0 10px 0;
.share-message {
width: 100%;
padding:0;
}
.error-msg {
display:none;
margin-top: 10px;
text-align: center;
color: #F00;
font-size: 11px;
}
&.error {
background-color: #330000;
border: 1px solid #990000;
padding: 4px;
.error-msg {
display: block;
}
}
}
.share-options {
.error-msg {
display: none;
margin-top: 10px;
text-align: center;
color: #F00;
font-size: 11px;
}
img {
margin-right:1px;
}
a {
font-size:12px;
margin-right:15px;
}
}
.share-options.error {
background-color: #330000;
border: 1px solid #990000;
padding: 4px;
.error-msg {
display: block;
}
}
.share-link {
h3 {
margin-bottom:20px;
}
.link-contents {
margin-bottom:20px;
}
}
}

View File

@ -1,18 +1,20 @@
html {
height:100%;
min-height:100%;
}
p, div {
white-space: normal;
}
body.web {
background-repeat: repeat-x;
margin:0 !important;
padding:0 !important;
position:relative !important;
overflow: visible !important;
overflow: auto !important;
width:auto !important;
min-height:100%;
div.wrapper {
width:1100px;

View File

@ -8,6 +8,7 @@
*= require client/dialog
*= require client/invitationDialog
*= require client/shareDialog
*= require client/hoverBubble
*= require web/main
*= require web/footer
*= require web/recordings

View File

@ -191,10 +191,11 @@ class ApiBandsController < ApiController
def update_photo
original_fpfile = params[:original_fpfile]
cropped_fpfile = params[:cropped_fpfile]
cropped_large_fpfile = params[:cropped_large_fpfile]
crop_selection = params[:crop_selection]
# public bucket to allow images to be available to public
@band.update_photo(original_fpfile, cropped_fpfile, crop_selection, Rails.application.config.aws_bucket_public)
@band.update_photo(original_fpfile, cropped_fpfile, cropped_large_fpfile, crop_selection, Rails.application.config.aws_bucket_public)
if @band.errors.any?
render :json => { :message => "Unexpected error updating photo."}, :status => :unprocessable_entity

View File

@ -11,10 +11,19 @@ class ApiClaimedRecordingsController < ApiController
end
def show
if !@claimed_recording.is_public && @claimed_recording.user_id != current_user.id
raise PermissionError, 'this claimed recording is not public'
end
@claimed_recording
end
def update
if @claimed_recording.user_id != current_user.id
raise PermissionError, 'only owner of claimed_recording can update it'
end
begin
@claimed_recording.update_fields(current_user, params)
respond_with responder: ApiResponder, :status => 204
@ -24,6 +33,9 @@ class ApiClaimedRecordingsController < ApiController
end
def delete
if @claimed_recording.user_id != current_user.id
raise PermissionError, 'only owner of claimed_recording can update it'
end
#begin
#@claimed_recording.discard(current_user)
#render :json => {}, :status => 204
@ -37,7 +49,7 @@ class ApiClaimedRecordingsController < ApiController
def look_up_claimed_recording
@claimed_recording = ClaimedRecording.find(params[:id])
if @claimed_recording.nil? || @claimed_recording.user_id != current_user.id
if @claimed_recording.nil?
render :json => { :message => "claimed_recording not found" }, :status => 404
end
end

View File

@ -1,4 +1,4 @@
class ApiSearchController < ApiController
class ApiSessionsController < ApiController
def login
user = User.authenticate(params[:email], params[:password])

View File

@ -0,0 +1,34 @@
class ApiTwittersController < ApiController
before_filter :api_signed_in_user
respond_to :json
rescue_from 'Twitter::Error::Unauthorized' do |exception|
# invalidate current tokens
current_user.invalidate_user_authorization('twitter')
render :json => { :errors => { :token => ["is invalid"] } }, :status => 422
end
def tweet
twitter_auth = current_user.user_authorization('twitter')
raise JamRuby::PermissionError unless twitter_auth
client = Twitter::REST::Client.new do |config|
config.consumer_key = Rails.application.config.twitter_app_id
config.consumer_secret = Rails.application.config.twitter_app_secret
config.access_token = twitter_auth.token
config.access_token_secret = twitter_auth.secret
end
text = params[:message]
client.update(text)
render json: {}, status: :ok
end
end

View File

@ -10,7 +10,9 @@ class ApiUsersController < ApiController
:friend_show, :friend_destroy, # friends
: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]
:set_password, :begin_update_email, :update_avatar, :delete_avatar, :generate_filepicker_policy,
:share_session, :share_recording]
respond_to :json
def index
@ -411,10 +413,11 @@ class ApiUsersController < ApiController
def update_avatar
original_fpfile = params[:original_fpfile]
cropped_fpfile = params[:cropped_fpfile]
cropped_large_fpfile = params[:cropped_large_fpfile]
crop_selection = params[:crop_selection]
# public bucket to allow images to be available to public
@user.update_avatar(original_fpfile, cropped_fpfile, crop_selection, Rails.application.config.aws_bucket_public)
@user.update_avatar(original_fpfile, cropped_fpfile, cropped_large_fpfile, crop_selection, Rails.application.config.aws_bucket_public)
if @user.errors.any?
respond_with @user, status: :unprocessable_entity
@ -547,6 +550,59 @@ class ApiUsersController < ApiController
render :json => {}, :status => 200
end
# creates display-ready session data for sharing
def share_session
provider = params[:provider]
music_session_id = params[:music_session]
history = MusicSessionHistory.find_by_music_session_id!(music_session_id)
if provider == 'facebook'
render json: {
description: view_context.description_for_music_session_history(history),
title: view_context.title_for_music_session_history(history, current_user),
photo_url: view_context.facebook_image_for_music_session_history(history),
caption: 'www.jamkazam.com'
}, status: 200
elsif provider == 'twitter'
render json: {
message: view_context.title_for_music_session_history(history, current_user)
}, status: 200
else
render :json => { :errors => {:provider => ['not valid']} }, :status => 422
end
end
# creates display-ready recording data for sharing
def share_recording
provider = params[:provider]
claimed_recording_id = params[:claimed_recording]
claimed_recording = ClaimedRecording.find(claimed_recording_id)
if provider == 'facebook'
render json: {
description: view_context.description_for_claimed_recording(claimed_recording),
title: view_context.title_for_claimed_recording(claimed_recording, current_user),
photo_url: view_context.facebook_image_for_claimed_recording(claimed_recording),
caption: 'www.jamkazam.com'
}, status: 200
elsif provider == 'twitter'
render json: {
message: view_context.title_for_claimed_recording(history, current_user) + " at " + request.host_with_port
}, status: 200
else
render :json => { :errors => {:provider => ['not valid']} }, :status => 422
end
end
###################### RECORDINGS #######################
# def recording_index
# @recordings = User.recording_index(current_user, params[:id])

View File

@ -1,6 +1,8 @@
# this is not a jam session - this is an 'auth session'
class SessionsController < ApplicationController
layout "web"
def new
@login_error = false
render :layout => "landing"
@ -23,6 +25,7 @@ class SessionsController < ApplicationController
end
end
# OAuth docs
# http://net.tutsplus.com/tutorials/ruby/how-to-use-omniauth-to-authenticate-your-users/
def create_oauth
@ -39,7 +42,6 @@ class SessionsController < ApplicationController
# For debugging - to see what all is there:
# render :text => auth_hash.to_yaml
#FbGraph.debug!
#app = FbGraph::Application.new '468555793186398', :secret => '546a5b253972f3e2e8b36d9a3dd5a06e'
token = auth_hash[:credentials][:token]
# FIXME:
@ -65,14 +67,26 @@ class SessionsController < ApplicationController
end
end
# https://github.com/intridea/omniauth/wiki/Saving-User-Location
def oauth_callback
auth_hash = request.env['omniauth.auth']
provider = auth_hash[:provider]
if provider == 'facebook'
if provider == 'twitter'
@user_authorization = current_user.build_twitter_authorization(auth_hash)
if !@user_authorization.save
# this is a very poorly styled page, but it's better than a server error.
# the only reason this happens is because some other account has authed this twitter acct
render "twitter_oauth_failure"
return
end
redirect_to request.env['omniauth.origin'] || '/'
return
elsif provider == 'facebook'
fb_uid = auth_hash[:uid]
token = auth_hash[:credentials][:token]
token_expiration = Time.at(auth_hash[:credentials][:expires_at])
@ -144,7 +158,7 @@ class SessionsController < ApplicationController
end
def failure
redirect_to request.query_parameters['origin'] || '/'
end
def connection_state

View File

@ -0,0 +1,14 @@
class ShareTokensController < ApplicationController
respond_to :html
def shareable_resolver
share_token = ShareToken.find_by_token!(params[:id])
# TODO: clean this up later to truly use polymorphic associations
if share_token.shareable_type == "session"
redirect_to music_session_detail_url(share_token.shareable_id)
else
redirect_to recording_detail_url(share_token.shareable_id)
end
end
end

View File

@ -43,4 +43,5 @@ class SpikesController < ApplicationController
render :layout => 'web'
end
end

View File

@ -0,0 +1,48 @@
module MusicSessionHelper
def facebook_image_for_music_session_history(music_session)
if music_session.band
path = !music_session.band.large_photo_url.blank? ? music_session.band.large_photo_url : "/assets/web/logo-256.png"
else
path = "/assets/web/logo-256.png"
end
request.protocol + request.host_with_port + path
end
# careful; this mirrors logic of facebook_image_for_music_session_history
def facebook_image_size_for_music_session_history(music_session)
if music_session.band
!music_session.band.large_photo_url.blank? ? 200 : 256
else
256
end
end
def title_for_music_session_history(music_session, sharer = nil)
if music_session.band
"LIVE SESSION: #{music_session.band.name}"
else
unique_users = music_session.unique_users
if sharer && unique_users.exists?(sharer)
"LIVE SESSION: #{sharer.name}#{additional_member_count(unique_users)}"
else
"LIVE SESSION: #{music_session.user.name}#{additional_member_count(unique_users)}"
end
end
end
def additional_member_count(unique_users)
length = unique_users.length
if length < 2
""
else
" & #{length} OTHERS"
end
end
def description_for_music_session_history(music_session)
truncate(music_session.description, length:250)
end
end

View File

@ -0,0 +1,48 @@
module RecordingHelper
def facebook_image_for_claimed_recording(claimed_recording)
if claimed_recording.recording.band
path = !claimed_recording.recording.band.large_photo_url.blank? ? claimed_recording.recording.band.large_photo_url : "/assets/web/logo-256.png"
else
path = "/assets/web/logo-256.png"
end
request.protocol + request.host_with_port + path
end
# careful; this mirrors logic of facebook_image_for_music_session_history
def facebook_image_size_for_claimed_recording(claimed_recording)
if claimed_recording.recording.band
!claimed_recording.recording.band.large_photo_url.blank? ? 200 : 256
else
256
end
end
def title_for_claimed_recording(claimed_recording, sharer = nil)
if claimed_recording.recording.band
"RECORDING: #{claimed_recording.recording.band.name}"
else
unique_users = claimed_recording.recording.users
if sharer && unique_users.exists?(sharer)
"RECORDING: #{sharer.name}#{additional_member_count(unique_users)}"
else
"RECORDING: #{claimed_recording.user.name}#{additional_member_count(unique_users)}"
end
end
end
def additional_member_count(unique_users)
length = unique_users.length
if length < 2
""
else
" & #{length} OTHERS"
end
end
def description_for_claimed_recording(claimed_recording)
truncate(claimed_recording.name, length:250)
end
end

View File

@ -0,0 +1,4 @@
module ShareTokenHelper
end

View File

@ -6,11 +6,22 @@ object @claimed_recording
attributes :id, :name, :description, :is_public, :is_downloadable, :genre_id
node :share_url do |claimed_recording|
unless claimed_recording.share_token.nil?
share_token_url(claimed_recording.share_token.token)
end
end
child(:recording => :recording) {
attributes :id, :created_at, :duration, :comment_count, :like_count, :play_count
child(:band => :band) {
attributes :id, :name
}
attributes :id, :name, :photo_url
}
child(:owner => :owner) {
attributes :id, :name, :photo_url
}
child(:mixes => :mixes) {
attributes :id, :mp3_url, :ogg_url, :is_completed
@ -20,14 +31,10 @@ child(:recording => :recording) {
attributes :id, :fully_uploaded, :url, :client_track_id, :client_id, :instrument_id
child(:user => :user) {
attributes :id, :first_name, :last_name, :city, :state, :country, :photo_url
attributes :id, :first_name, :last_name, :name, :city, :state, :country, :photo_url
}
}
child(:band => :band) {
attributes :id, :name, :photo_url
}
child(:comments => :comments) {
attributes :comment, :created_at

View File

@ -2,18 +2,24 @@ object @history
attributes :music_session_id, :description, :genres
node :share_url do |history|
unless history.share_token.nil?
share_token_url(history.share_token.token)
end
end
child(:user => :creator) {
attributes :name, :photo_url
attributes :name, :photo_url
}
child(:band => :band) {
attributes :name, :photo_url
attributes :name, :photo_url
}
child(:music_session_user_histories => :users) {
attributes :instruments
attributes :instruments
child(:user => :user) {
attributes :name, :photo_url
}
child(:user => :user) {
attributes :name, :photo_url
}
}

View File

@ -12,6 +12,12 @@ if :is_recording?
end
end
node :share_url do |music_session|
unless music_session.music_session_history.share_token.nil?
share_token_url(music_session.music_session_history.share_token.token)
end
end
child(:connections => :participants) {
collection @music_sessions, :object_root => false
attributes :ip_address, :client_id

View File

@ -10,7 +10,8 @@ end
# give back more info if the user being fetched is yourself
if @user == current_user
attributes :email, :original_fpfile, :cropped_fpfile, :crop_selection, :session_settings, :show_whats_next, :subscribe_email
attributes :email, :original_fpfile, :cropped_fpfile, :crop_selection, :session_settings, :show_whats_next, :subscribe_email, :auth_twitter
elsif current_user
node :is_friend do |uu|
current_user.friends?(@user)

View File

@ -0,0 +1,52 @@
<!-- band hover -->
<div id="band-detail-1" class="hidden bubble">
<h2>Band Detail</h2>
<div class="bubble-inner">
<a href="#" class="avatar_large left mr20"><img src="images/content/avatar_band3.jpg" /></a>
<div class="left"><h3>Fox Force Five</h3>
<small>Richmond, VA<br />
<strong>Jazz</strong></small><br />
12 <img src="images/content/icon_like.png" width="12" height="12" align="absmiddle" />&nbsp;&nbsp;&nbsp;4 <img src="images/content/icon_followers.png" width="22" height="12" align="absmiddle" />&nbsp;&nbsp;&nbsp;17 <img src="images/content/icon_recordings.png" width="12" height="13" align="absmiddle" />&nbsp;&nbsp;&nbsp;64 <img src="images/content/icon_session_tiny.png" width="12" height="12" align="absmiddle" />
</div>
<br clear="all" /><br />
<div class="f11">Nulla facilisi. In vel sem. Morbi id urna in diam dignissim feugiat. Proin molestie tortor eu velit. Aliquam erat volutpat. Nullam ultrices, diam tempus vulputate egestas, eros pede varius leo, sed imperdiet lectus est ornare odio.</div>
<table class="musicians" cellpadding="0" cellspacing="5">
<tr>
<td width="24"><a href="#" class="avatar-tiny" id="musician1"><img src="images/shared/avatar_david.jpg" /></a></td>
<td><a href="#">John Doe</a></td>
<td><div class="nowrap"><img src="images/content/icon_instrument_guitar24.png" width="24" height="24" />&nbsp;<img src="images/content/icon_instrument_vocal24.png" width="24" height="24" /></div></td>
</tr>
<tr>
<td width="24"><a href="#" class="avatar-tiny"><img src="images/shared/avatar_creepyeye.jpg" /></a></td>
<td><a href="#">Cassandra Defrenza</a></td>
<td><img src="images/content/icon_instrument_keyboard24.png" width="24" height="24" /></td>
</tr>
<tr>
<td width="24"><a href="#" class="avatar-tiny"><img src="images/shared/avatar_silverfox.jpg" /></a></td>
<td><a href="#">Jimmy Stratham</a></td>
<td><img src="images/content/icon_instrument_guitar24.png" width="24" height="24" /></td>
</tr>
</table><br />
<div align="center">
<div class="left"><a href="#" class="button-orange">PROFILE</a></div>
<div class="left"><a href="#" class="button-orange">LIKE</a></div>
<div class="left"><a href="#" class="button-orange">FOLLOW</a></div>
</div>
<br /><br />
</div>
</div>
<script type="text/template" id="template-hover-bubble">
<div class="hover-bubble">
</div>
</script>

View File

@ -1,3 +1,232 @@
<!-- band hover -->
<div id="band-detail-1" class="hidden bubble">
<h2>Band Detail</h2>
<div class="bubble-inner">
<a href="#" class="avatar_large left mr20"><img src="images/content/avatar_band3.jpg" /></a>
<div class="left"><h3>Fox Force Five</h3>
<small>Richmond, VA<br />
<strong>Jazz</strong></small><br />
12 <img src="images/content/icon_like.png" width="12" height="12" align="absmiddle" />&nbsp;&nbsp;&nbsp;4 <img src="images/content/icon_followers.png" width="22" height="12" align="absmiddle" />&nbsp;&nbsp;&nbsp;17 <img src="images/content/icon_recordings.png" width="12" height="13" align="absmiddle" />&nbsp;&nbsp;&nbsp;64 <img src="images/content/icon_session_tiny.png" width="12" height="12" align="absmiddle" />
</div>
<br clear="all" /><br />
<div class="f11">Nulla facilisi. In vel sem. Morbi id urna in diam dignissim feugiat. Proin molestie tortor eu velit. Aliquam erat volutpat. Nullam ultrices, diam tempus vulputate egestas, eros pede varius leo, sed imperdiet lectus est ornare odio.</div>
<table class="musicians" cellpadding="0" cellspacing="5">
<tr>
<td width="24"><a href="#" class="avatar-tiny" id="musician1"><img src="images/shared/avatar_david.jpg" /></a></td>
<td><a href="#">John Doe</a></td>
<td><div class="nowrap"><img src="images/content/icon_instrument_guitar24.png" width="24" height="24" />&nbsp;<img src="images/content/icon_instrument_vocal24.png" width="24" height="24" /></div></td>
</tr>
<tr>
<td width="24"><a href="#" class="avatar-tiny"><img src="images/shared/avatar_creepyeye.jpg" /></a></td>
<td><a href="#">Cassandra Defrenza</a></td>
<td><img src="images/content/icon_instrument_keyboard24.png" width="24" height="24" /></td>
</tr>
<tr>
<td width="24"><a href="#" class="avatar-tiny"><img src="images/shared/avatar_silverfox.jpg" /></a></td>
<td><a href="#">Jimmy Stratham</a></td>
<td><img src="images/content/icon_instrument_guitar24.png" width="24" height="24" /></td>
</tr>
</table><br />
<div align="center">
<div class="left"><a href="#" class="button-orange">PROFILE</a></div>
<div class="left"><a href="#" class="button-orange">LIKE</a></div>
<div class="left"><a href="#" class="button-orange">FOLLOW</a></div>
</div>
<br /><br />
</div>
</div>
<!-- musician hover -->
<div id="musician-detail-1" class="hidden bubble">
<h2>Musician Detail</h2>
<div class="bubble-inner">
<a href="#" class="avatar_large left mr20"><img src="images/shared/avatar_david.jpg" /></a>
<div class="left ib"><h3>John Doe</h3>
<small>Austin, TX</small><br /><br />
<div class="left mr10 mb"><img src="images/content/icon_instrument_guitar24.png" width="24" height="24" /></div>
<div class="left mr10"><img src="images/content/icon_instrument_vocal24.png" width="24" height="24" /></div>
<div class="left mr10"><img src="images/content/icon_instrument_keyboard24.png" width="24" height="24" /></div>
<br clear="all" />
122 <img src="images/content/icon_friend.png" align="absmiddle" />&nbsp;&nbsp;&nbsp;4 <img src="images/content/icon_followers.png" width="22" height="12" align="absmiddle" />&nbsp;&nbsp;&nbsp;17 <img src="images/content/icon_recordings.png" width="12" height="13" align="absmiddle" />&nbsp;&nbsp;&nbsp;64 <img src="images/content/icon_session_tiny.png" width="12" height="12" align="absmiddle" /></div><br clear="all" /><br />
<div class="f12"><strong>IN SESSION &mdash; <a href="#">Click to Join</a></strong></div>
<br />
<div class="f11">Nulla facilisi. In vel sem. Morbi id urna in diam dignissim feugiat. Proin molestie tortor eu velit. Aliquam erat volutpat. Nullam ultrices, diam tempus vulputate egestas, eros pede varius leo, sed imperdiet lectus est ornare odio.</div><br />
<small><strong>FOLLOWING:</strong></small>
<table class="musicians" cellpadding="0" cellspacing="5">
<tr>
<td width="24"><a href="#" class="avatar-tiny"><img src="images/content/avatar_band3.jpg" /></a></td>
<td><a href="#"><strong>Fox Force Five</strong></a></td>
<td width="24"><a href="#" class="avatar-tiny"><img src="images/content/avatar_band1.jpg" /></a></td>
<td><a href="#"><strong>Tammany Hall</strong></a></td>
</tr>
<tr>
<td width="24"><a href="#" class="avatar-tiny"><img src="images/content/avatar_band2.jpg" /></a></td>
<td><a href="#"><strong>Bethany Grey</strong></a></td>
<td></td>
<td></td>
</tr>
</table><br />
<div align="center">
<div class="left"><a href="#" class="button-orange">PROFILE</a></div>
<div class="left"><a href="#" class="button-orange">LIKE</a></div>
<div class="left"><a href="#" class="button-orange">FRIEND</a></div>
<div class="left"><a href="#" class="button-orange">FOLLOW</a></div>
</div>
<br /><br />
</div>
</div>
<!-- fan hover -->
<div id="fan-detail-1" class="hidden bubble">
<h2>Fan Detail</h2>
<div class="bubble-inner">
<a href="#" class="avatar_large left mr20"><img src="images/shared/avatar_creepyeye.jpg" /></a>
<div class="left ib"><h3>Tomas Jones</h3>
<small>Austin, TX</small><br /><br />
122 <img src="images/content/icon_friend.png" align="absmiddle" />&nbsp;&nbsp;&nbsp;4 <img src="images/content/icon_followers.png" width="22" height="12" align="absmiddle" /></div><br clear="all" /><br />
<br />
<div class="f11">Nulla facilisi. In vel sem. Morbi id urna in diam dignissim feugiat. Proin molestie tortor eu velit. Aliquam erat volutpat. Nullam ultrices, diam tempus vulputate egestas, eros pede varius leo, sed imperdiet lectus est ornare odio.</div><br />
<small><strong>FOLLOWING:</strong></small>
<table class="musicians" cellpadding="0" cellspacing="5">
<tr>
<td width="24"><a href="#" class="avatar-tiny"><img src="images/content/avatar_band3.jpg" /></a></td>
<td><a href="#"><strong>Fox Force Five</strong></a></td>
<td width="24"><a href="#" class="avatar-tiny"><img src="images/shared/avatar_silverfox.jpg" /></a></td>
<td><a href="#"><strong>Bob Scrothers</strong></a></td>
</tr>
<tr>
<td width="24"><a href="#" class="avatar-tiny"><img src="images/content/avatar_band2.jpg" /></a></td>
<td><a href="#"><strong>Bethany Grey</strong></a></td>
<td></td>
<td></td>
</tr>
</table><br />
<div align="center">
<div class="left"><a href="#" class="button-orange">PROFILE</a></div>
<div class="left"><a href="#" class="button-orange">FOLLOW</a></div>
</div>
<br /><br />
</div>
</div>
<!-- session hover -->
<div id="session-detail-1" class="hidden bubble">
<h2>Session Detail</h2>
<div class="bubble-inner">
<div class="small left">JAZZ</div>
<div class="small right">01/25/14 - 11:23 pm</div>
<br clear="all" />
<div class="f11 mt5 mb5">Nulla facilisi. In vel sem. Morbi id urna in diam dignissim feugiat. Proin molestie tortor eu velit. Aliquam erat volutpat. Nullam ultrices, diam tempus vulputate egestas, eros pede varius leo, sed imperdiet lectus est ornare odio.</div>
<div class="small">12 <img src="images/content/icon_arrow.png" width="7" height="12" align="absmiddle" />&nbsp;&nbsp;&nbsp;&nbsp;4 <img src="images/content/icon_comment.png" width="13" height="12" align="absmiddle" />&nbsp;&nbsp;&nbsp;&nbsp;17 <img src="images/content/icon_like.png" width="12" height="12" align="absmiddle" /></div><br />
MUSICIANS:
<!-- sub-table of musicians -->
<table class="musicians" cellpadding="0" cellspacing="5">
<tr>
<td width="24"><a href="#" class="avatar-tiny" id="musician1"><img src="images/shared/avatar_david.jpg" /></a></td>
<td><a href="#">John Doe</a></td>
<td><div class="nowrap"><img src="images/content/icon_instrument_guitar24.png" width="24" height="24" />&nbsp;<img src="images/content/icon_instrument_vocal24.png" width="24" height="24" /></div></td>
</tr>
<tr>
<td width="24"><a href="#" class="avatar-tiny"><img src="images/shared/avatar_creepyeye.jpg" /></a></td>
<td><a href="#">Cassandra Defrenza</a></td>
<td><img src="images/content/icon_instrument_keyboard24.png" width="24" height="24" /></td>
</tr>
<tr>
<td width="24"><a href="#" class="avatar-tiny"><img src="images/shared/avatar_silverfox.jpg" /></a></td>
<td><a href="#">Jimmy Stratham</a></td>
<td><img src="images/content/icon_instrument_guitar24.png" width="24" height="24" /></td>
</tr>
</table><br /><br />
<div align="center">
<div class="left"><a href="#" class="button-orange">LIKE</a></div>
<div class="left"><a href="#" class="button-orange">COMMENT</a></div>
<div class="left"><a href="#" class="button-orange">SHARE</a></div>
</div>
<br /><br />
</div>
</div>
<!-- recording hover -->
<div id="recording-detail-1" class="hidden bubble">
<h2>Recording Detail</h2>
<div class="bubble-inner">
<h3>Twelve Weeks</h3>
<div class="small left">JAZZ</div>
<div class="small right">01/25/14 - 11:23 pm</div>
<br clear="all" />
<div class="f11 mt5 mb5">Nulla facilisi. In vel sem. Morbi id urna in diam dignissim feugiat. Proin molestie tortor eu velit. Aliquam erat volutpat. Nullam ultrices, diam tempus vulputate egestas, eros pede varius leo, sed imperdiet lectus est ornare odio.</div>
<div class="small">12 <img src="images/content/icon_arrow.png" width="7" height="12" align="absmiddle" />&nbsp;&nbsp;&nbsp;&nbsp;4 <img src="images/content/icon_comment.png" width="13" height="12" align="absmiddle" />&nbsp;&nbsp;&nbsp;&nbsp;17 <img src="images/content/icon_like.png" width="12" height="12" align="absmiddle" /><br />
<br />
<a href="#" class="avatar_large left mr20"><img src="images/content/avatar_band3.jpg" /></a>
<div class="left"><h3>Fox Force Five</h3>
<small>Richmond, VA</small></div>
<br clear="all" /><br />
MUSICIANS:
<!-- sub-table of musicians -->
<table class="musicians" cellpadding="0" cellspacing="5">
<tr>
<td width="24"><a href="#" class="avatar-tiny" id="musician1"><img src="images/shared/avatar_david.jpg" /></a></td>
<td><a href="#">John Doe</a></td>
<td><div class="nowrap"><img src="images/content/icon_instrument_guitar24.png" width="24" height="24" />&nbsp;<img src="images/content/icon_instrument_vocal24.png" width="24" height="24" /></div></td>
</tr>
<tr>
<td width="24"><a href="#" class="avatar-tiny"><img src="images/shared/avatar_creepyeye.jpg" /></a></td>
<td><a href="#">Cassandra Defrenza</a></td>
<td><img src="images/content/icon_instrument_keyboard24.png" width="24" height="24" /></td>
</tr>
<tr>
<td width="24"><a href="#" class="avatar-tiny"><img src="images/shared/avatar_silverfox.jpg" /></a></td>
<td><a href="#">Jimmy Stratham</a></td>
<td><img src="images/content/icon_instrument_guitar24.png" width="24" height="24" /></td>
</tr>
</table><br /><br />
<div align="center">
<div class="left"><a href="#" class="button-orange">LIKE</a></div>
<div class="left"><a href="#" class="button-orange">COMMENT</a></div>
<div class="left"><a href="#" class="button-orange">SHARE</a></div>
</div>
<br /><br />
</div>
</div>
</div>
<script type="text/template" id="template-hover-bubble">
<div class="hover-bubble">

View File

@ -0,0 +1,46 @@
<!-- fan hover -->
<div id="fan-detail-1" class="hidden bubble">
<h2>Fan Detail</h2>
<div class="bubble-inner">
<a href="#" class="avatar_large left mr20"><img src="images/shared/avatar_creepyeye.jpg" /></a>
<div class="left ib"><h3>Tomas Jones</h3>
<small>Austin, TX</small><br /><br />
122 <img src="images/content/icon_friend.png" align="absmiddle" />&nbsp;&nbsp;&nbsp;4 <img src="images/content/icon_followers.png" width="22" height="12" align="absmiddle" /></div><br clear="all" /><br />
<br />
<div class="f11">Nulla facilisi. In vel sem. Morbi id urna in diam dignissim feugiat. Proin molestie tortor eu velit. Aliquam erat volutpat. Nullam ultrices, diam tempus vulputate egestas, eros pede varius leo, sed imperdiet lectus est ornare odio.</div><br />
<small><strong>FOLLOWING:</strong></small>
<table class="musicians" cellpadding="0" cellspacing="5">
<tr>
<td width="24"><a href="#" class="avatar-tiny"><img src="images/content/avatar_band3.jpg" /></a></td>
<td><a href="#"><strong>Fox Force Five</strong></a></td>
<td width="24"><a href="#" class="avatar-tiny"><img src="images/shared/avatar_silverfox.jpg" /></a></td>
<td><a href="#"><strong>Bob Scrothers</strong></a></td>
</tr>
<tr>
<td width="24"><a href="#" class="avatar-tiny"><img src="images/content/avatar_band2.jpg" /></a></td>
<td><a href="#"><strong>Bethany Grey</strong></a></td>
<td></td>
<td></td>
</tr>
</table><br />
<div align="center">
<div class="left"><a href="#" class="button-orange">PROFILE</a></div>
<div class="left"><a href="#" class="button-orange">FOLLOW</a></div>
</div>
<br /><br />
</div>
</div>
<script type="text/template" id="template-hover-bubble">
<div class="hover-bubble">
</div>
</script>

View File

@ -0,0 +1,55 @@
<!-- recording hover -->
<div id="recording-detail-1" class="hidden bubble">
<h2>Recording Detail</h2>
<div class="bubble-inner">
<h3>Twelve Weeks</h3>
<div class="small left">JAZZ</div>
<div class="small right">01/25/14 - 11:23 pm</div>
<br clear="all" />
<div class="f11 mt5 mb5">Nulla facilisi. In vel sem. Morbi id urna in diam dignissim feugiat. Proin molestie tortor eu velit. Aliquam erat volutpat. Nullam ultrices, diam tempus vulputate egestas, eros pede varius leo, sed imperdiet lectus est ornare odio.</div>
<div class="small">12 <img src="images/content/icon_arrow.png" width="7" height="12" align="absmiddle" />&nbsp;&nbsp;&nbsp;&nbsp;4 <img src="images/content/icon_comment.png" width="13" height="12" align="absmiddle" />&nbsp;&nbsp;&nbsp;&nbsp;17 <img src="images/content/icon_like.png" width="12" height="12" align="absmiddle" /><br />
<br />
<a href="#" class="avatar_large left mr20"><img src="images/content/avatar_band3.jpg" /></a>
<div class="left"><h3>Fox Force Five</h3>
<small>Richmond, VA</small></div>
<br clear="all" /><br />
MUSICIANS:
<!-- sub-table of musicians -->
<table class="musicians" cellpadding="0" cellspacing="5">
<tr>
<td width="24"><a href="#" class="avatar-tiny" id="musician1"><img src="images/shared/avatar_david.jpg" /></a></td>
<td><a href="#">John Doe</a></td>
<td><div class="nowrap"><img src="images/content/icon_instrument_guitar24.png" width="24" height="24" />&nbsp;<img src="images/content/icon_instrument_vocal24.png" width="24" height="24" /></div></td>
</tr>
<tr>
<td width="24"><a href="#" class="avatar-tiny"><img src="images/shared/avatar_creepyeye.jpg" /></a></td>
<td><a href="#">Cassandra Defrenza</a></td>
<td><img src="images/content/icon_instrument_keyboard24.png" width="24" height="24" /></td>
</tr>
<tr>
<td width="24"><a href="#" class="avatar-tiny"><img src="images/shared/avatar_silverfox.jpg" /></a></td>
<td><a href="#">Jimmy Stratham</a></td>
<td><img src="images/content/icon_instrument_guitar24.png" width="24" height="24" /></td>
</tr>
</table><br /><br />
<div align="center">
<div class="left"><a href="#" class="button-orange">LIKE</a></div>
<div class="left"><a href="#" class="button-orange">COMMENT</a></div>
<div class="left"><a href="#" class="button-orange">SHARE</a></div>
</div>
<br /><br />
</div>
</div>
</div>
<script type="text/template" id="template-hover-bubble">
<div class="hover-bubble">
</div>
</script>

View File

@ -0,0 +1,49 @@
<!-- session hover -->
<div id="session-detail-1" class="hidden bubble">
<h2>Session Detail</h2>
<div class="bubble-inner">
<div class="small left">JAZZ</div>
<div class="small right">01/25/14 - 11:23 pm</div>
<br clear="all" />
<div class="f11 mt5 mb5">Nulla facilisi. In vel sem. Morbi id urna in diam dignissim feugiat. Proin molestie tortor eu velit. Aliquam erat volutpat. Nullam ultrices, diam tempus vulputate egestas, eros pede varius leo, sed imperdiet lectus est ornare odio.</div>
<div class="small">12 <img src="images/content/icon_arrow.png" width="7" height="12" align="absmiddle" />&nbsp;&nbsp;&nbsp;&nbsp;4 <img src="images/content/icon_comment.png" width="13" height="12" align="absmiddle" />&nbsp;&nbsp;&nbsp;&nbsp;17 <img src="images/content/icon_like.png" width="12" height="12" align="absmiddle" /></div><br />
MUSICIANS:
<!-- sub-table of musicians -->
<table class="musicians" cellpadding="0" cellspacing="5">
<tr>
<td width="24"><a href="#" class="avatar-tiny" id="musician1"><img src="images/shared/avatar_david.jpg" /></a></td>
<td><a href="#">John Doe</a></td>
<td><div class="nowrap"><img src="images/content/icon_instrument_guitar24.png" width="24" height="24" />&nbsp;<img src="images/content/icon_instrument_vocal24.png" width="24" height="24" /></div></td>
</tr>
<tr>
<td width="24"><a href="#" class="avatar-tiny"><img src="images/shared/avatar_creepyeye.jpg" /></a></td>
<td><a href="#">Cassandra Defrenza</a></td>
<td><img src="images/content/icon_instrument_keyboard24.png" width="24" height="24" /></td>
</tr>
<tr>
<td width="24"><a href="#" class="avatar-tiny"><img src="images/shared/avatar_silverfox.jpg" /></a></td>
<td><a href="#">Jimmy Stratham</a></td>
<td><img src="images/content/icon_instrument_guitar24.png" width="24" height="24" /></td>
</tr>
</table><br /><br />
<div align="center">
<div class="left"><a href="#" class="button-orange">LIKE</a></div>
<div class="left"><a href="#" class="button-orange">COMMENT</a></div>
<div class="left"><a href="#" class="button-orange">SHARE</a></div>
</div>
<br /><br />
</div>
</div>
<script type="text/template" id="template-hover-bubble">
<div class="hover-bubble">
</div>
</script>

View File

@ -1,123 +1,54 @@
<!-- Share dialog -->
<div class="dialog share-overlay" layout="dialog" layout-id="share-dialog" id="share-dialog" style="width:800px; height:auto;">
<div class="content-head"><h1>share this session</h1></div>
<div class="dialog-inner">
<div class="right"> <a class="button-orange" layout-action="close">X CLOSE</a></div>
<table class="w100">
<tr>
<td valign="top" width="45%" class="border-right"><h3 class="mb5">Share to Social Media:</h3>
<textarea class="w95" rows="3">Add a Message...</textarea><br />
<div class="left">
<input type="checkbox" />
<%= image_tag "content/icon_facebook.png", :size => "24x24", :align => "absmiddle", :alt => "", :style => "vertical-align:middle" %>&nbsp;
<input type="checkbox" />
<%= image_tag "content/icon_twitter.png", :size => "24x24", :align => "absmiddle", :alt => "", :style => "vertical-align:middle" %>&nbsp;
<input type="checkbox" />
<%= image_tag "content/icon_google.png", :size => "24x24", :align => "absmiddle", :alt => "", :style => "vertical-align:middle" %>&nbsp;
</div>
<div class=" right mr10 mt5"><a class="button-orange dialog-share-button">SHARE</a></div>
</td>
<td valign="top" width="48%">
<div class="ml10">
<h3>Share a Link:</h3><br />
<%= true ? '' : "#{root_url}#{share_token}" %>
<div class="right"><a class="button-orange">COPY LINK</a></div>
</div>
</td>
</tr>
<tr><td colspan=2 class="border-bottom"><br /><br /></td></tr>
</table><br /><br />
<table class="w100">
<tr>
<td valign="top" width="40%"><h3>Get a Widget:</h3>
<!-- ########## Javascript code for widget ########## -->
<textarea class="w95" rows="10">
<!-- session widget -->
<div class="widget session">
<!-- header -->
<div class="widget-header orange-fill"><strong>LIVE SESSION</strong> by Raven &amp; The Blackbirds</div>
<!-- start widget content -->
<div class="widget-content">
<!-- band avatar -->
<div class="widget-avatar"><img src="images/content/avatar_band4.jpg" alt=""/></div>
<a href="javascript:void(0)" class="widget-pausebutton" title="pause"></a>
<!-- song title -->
<div class="widget-controls">
<!-- timeline and controls -->
<div class="w100">
<!-- recording play controls -->
<div class="recording-controls">
<!-- playback position -->
<div class="recording-position">
<!-- start time -->
<div class="recording-time">0:00</div>
<!-- playback background & slider -->
<div class="recording-playback">
<div class="recording-slider"><img src="images/content/slider_playcontrols.png" width="5" height="16" /></div>
</div>
<!-- end time -->
<div class="recording-time">4:59</div>
</div>
<!-- end playback position -->
<!-- current playback time -->
<div class="recording-current">1:23</div>
</div>
<!-- end recording play controls -->
</div>
</div>
<!-- band member avatars -->
<div class="widget-social">&nbsp;
<a href="#"><img src="images/content/icon_like.png" width="12" height="12" alt=""/>&nbsp;&nbsp;LIKE</a>
<a href="#"><img class="space" src="images/content/icon_comment.png" width="12" height="12" alt=""/>&nbsp;&nbsp;COMMENT</a>
<a href="#"><img class="space" src="images/content/icon_share.png" width="13" height="15" alt=""/>&nbsp;&nbsp;SHARE</a>
</div>
<!-- jamkazam branding -->
<div class="widget-branding">recorded on
<a href="http://jamkazam.com"><img src="images/shared/jk_logo_small.png" alt="" width="142" height="26" align="absbottom"/></a>
</div>
</div>
<!-- end widget content -->
</div>
<!-- /widget -->
</textarea> <br /><br />
<div class="right"><a class="button-orange mr10">COPY WIDGET CODE</a></div>
</td>
<td>
<div class="ml10">Preview:
<!-- recording widget -->
<div class="widget session">
<!-- header -->
<div class="widget-header teal-fill"><strong>LIVE SESSION</strong> by Raven &amp; The Blackbirds</div>
<!-- start widget content -->
<div class="widget-content">
<!-- band avatar -->
<div class="widget-avatar"><%= image_tag "content/avatar_band4.jpg", :alt => "" %></div>
<a href="javascript:void(0)" class="widget-playbutton" title="play"></a>
<!-- song title -->
<div class="widget-title">You Hurt Me Bad</div>
<!-- avatars -->
<div class="widget-members">
<div class="widget-avatar-small"><%= image_tag "shared/avatar_david.jpg", :alt => "" %></div>
<div class="widget-avatar-small"><%= image_tag "shared/avatar_silverfox.jpg", :alt => "" %></div>
<div class="widget-avatar-small"><%= image_tag "shared/avatar_saltnpepper.jpg", :alt => "" %></div>
<div class="widget-avatar-small"><%= image_tag "shared/avatar_creepyeye.jpg", :alt => "" %></div>
<div class="widget-avatar-small"><%= image_tag "shared/avatar_silverfox.jpg", :alt => "" %></div>
<div class="widget-avatar-small"><%= image_tag "shared/avatar_saltnpepper.jpg", :alt => "" %></div>
</div>
<!-- jamkazam branding -->
<div class="widget-branding">live session on
<a href="http://www.jamkazam.com">
<%= image_tag "shared/jk_logo_small.png", :size => "142x26", :align => "absbottom", :alt => "" %>
</a>
</div>
</div>
<!-- end widget content -->
</div>
<!-- /widget -->
</div>
</td>
</tr>
</table>
<div class="dialog share-overlay" layout="dialog" layout-id="share-dialog" id="share-dialog">
<div class="content-head"><h1>share this <span id="shareType"></span></h1></div>
<div class="dialog-inner">
<div class="right"><a class="button-orange" layout-action="close">X CLOSE</a></div>
<br clear="both" />
<div class="share-to-social-media border-bottom">
<h3 class="mb5">Share to Social Media:</h3>
<div class="share-message-holder">
<textarea class="share-message" rows="4" placeholder="Add a Message..."></textarea><br/>
<span class="error-msg"></span>
</div>
<div class="share-options left">
<span class="share-with-facebook">
<input type="checkbox" disabled="disabled"/>
<%= image_tag "content/icon_facebook.png", :size => "24x24", :align => "absmiddle", :alt => "", :style => "vertical-align:middle" %>
<a href="#" class="authorize">(sign in)</a>
</span>
<span class="share-with-twitter">
<input type="checkbox" disabled="disabled"/>
<%= image_tag "content/icon_twitter.png", :size => "24x24", :align => "absmiddle", :alt => "", :style => "vertical-align:middle" %>
<a href="#" class="authorize">(sign in)</a>
</span>
<!--
<div class="share-with-google">
<input type="checkbox" disabled="disabled" />
<%= image_tag "content/icon_google.png", :size => "24x24", :align => "absmiddle", :alt => "", :style => "vertical-align:middle" %>
</div>-->
<div class="error-msg">Please select at least one</div>
</div>
<div class="share-button-holder"><a class="button-orange dialog-share-button">SHARE</a></div>
<br clear="both" />
</div>
<div class="share-link">
<h3>Share a Link:</h3>
<div class="link-contents">
</div>
<div class="right"><a id="btn-share-copy" class="button-orange">COPY LINK</a></div>
</div>
<!-- contains syndication widget code -->
<% #render 'shareDialogUnused' %>
</div>
</div>

View File

@ -0,0 +1,87 @@
<table class="w100">
<tr>
<td valign="top" width="40%"><h3>Get a Widget:</h3>
<!-- ########## Javascript code for widget ########## -->
<textarea class="w95" rows="10">
<!-- widget -->
<div id="divWidgetCode" class="widget">
<!-- header -->
<div id="divWidgetCodeHeader" class="widget-header"><strong><span id="lblWidgetCodeType"></span></strong> by <span id="spnWidgetCodeArtistName"></span></div>
<!-- start widget content -->
<div class="widget-content">
<!-- band avatar -->
<div class="widget-avatar"><img id="imgWidgetCodeAvatar" alt=""/></div>
<a href="javascript:void(0)" class="widget-pausebutton" title="pause"></a>
<!-- song title -->
<div class="widget-controls">
<!-- timeline and controls -->
<div class="w100">
<!-- recording play controls -->
<div class="recording-controls">
<!-- playback position -->
<div class="recording-position">
<!-- start time -->
<div class="recording-time">0:00</div>
<!-- playback background & slider -->
<div class="recording-playback">
<div class="recording-slider"><img src="images/content/slider_playcontrols.png" width="5" height="16" /></div>
</div>
<!-- end time -->
<div class="recording-time">4:59</div>
</div>
<!-- end playback position -->
<!-- current playback time -->
<div class="recording-current">1:23</div>
</div>
<!-- end recording play controls -->
</div>
</div>
<!-- band member avatars -->
<div class="widget-social">&nbsp;
<a href="#"><img src="images/content/icon_like.png" width="12" height="12" alt=""/>&nbsp;&nbsp;LIKE</a>
<a href="#"><img class="space" src="images/content/icon_comment.png" width="12" height="12" alt=""/>&nbsp;&nbsp;COMMENT</a>
<a href="#"><img class="space" src="images/content/icon_share.png" width="13" height="15" alt=""/>&nbsp;&nbsp;SHARE</a>
</div>
<!-- jamkazam branding -->
<div class="widget-branding"><span id="spnWidgetCodeBranding"></span> on
<a href="http://jamkazam.com"><img src="images/shared/jk_logo_small.png" alt="" width="142" height="26" align="absbottom"/></a>
</div>
</div>
<!-- end widget content -->
</div>
<!-- /widget -->
</textarea> <br /><br />
<div class="right"><a class="button-orange mr10">COPY WIDGET CODE</a></div>
</td>
<td>
<div class="ml10">Preview:
<!-- recording widget -->
<div id="divWidgetPreview" class="widget">
<!-- header -->
<div id="divWidgetPreviewHeader" class="widget-header"><strong><span id="lblWidgetPreviewType"></span></strong> by <span id="spnWidgetPreviewArtistName"></span></div>
<!-- start widget content -->
<div class="widget-content">
<!-- band avatar -->
<div class="widget-avatar"><img id="imgWidgetPreviewAvatar" src="" alt=""/></div>
<a href="javascript:void(0)" class="widget-playbutton" title="play"></a>
<!-- song title -->
<div id="divWidgetPreviewTitle" class="widget-title"></div>
<!-- avatars -->
<div class="widget-members"></div>
<!-- jamkazam branding -->
<div class="widget-branding"><span id="spnWidgetPreviewBranding"></span> on
<a href=<%= "#{root_url}" %>>
<%= image_tag "shared/jk_logo_small.png", :size => "142x26", :align => "absbottom", :alt => "" %>
</a>
</div>
</div>
<!-- end widget content -->
</div>
<!-- /widget -->
</div>
</td>
</tr>
</table>

View File

@ -52,6 +52,8 @@
JK = JK || {};
JK.root_url = "<%= root_url %>"
<% if Rails.env == "development" %>
// if in development mode, we assume you are running websocket-gateway
// on the same host as you hit your server.
@ -85,8 +87,12 @@
<% if current_user %>
JK.currentUserId = '<%= current_user.id %>';
JK.currentUserAvatarUrl = JK.resolveAvatarUrl('<%= current_user.photo_url %>');
JK.currentUserName = '<%= current_user.name %>';
<% else %>
JK.currentUserId = null;
JK.currentUserAvatarUrl = null;
JK.currentUserName = null;
<% end %>
@ -95,11 +101,11 @@
var recordingManager = new JK.RecordingManager();
var invitationDialog = new JK.InvitationDialog(JK.app);
invitationDialog.initialize('<%= SampleApp::Application.config.facebook_key %>');
var facebookHelper = new JK.FacebookHelper(JK.app);
facebookHelper.initialize(gon.global.facebook_app_id);
var shareDialog = new JK.ShareDialog(JK.app);
shareDialog.initialize();
var invitationDialog = new JK.InvitationDialog(JK.app);
invitationDialog.initialize(gon.global.facebook_app_id);
var localRecordingsDialog = new JK.LocalRecordingsDialog(JK.app);
localRecordingsDialog.initialize();

View File

@ -0,0 +1,13 @@
<meta property="fb:app_id" content="<%= Rails.application.config.facebook_app_id %>" />
<meta property="og:title" content="<%= "JamKazam" %>" />
<meta property="og:url" content="<%= request.original_url %>" />
<meta property="og:description" content="Play music together over the Internet as if in the same room." />
<meta property="og:image" content="<%= request.protocol + request.host_with_port + image_path("web/logo-256.png") %>" />
<meta property="og:image:width" content="256" />
<meta property="og:image:height" content="256" />
<meta property="og:type" content="website" />
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@jamkazam">
<meta name="twitter:title" content="JamKazam" />
<meta name="twitter:description" content="Play music together over the Internet as if in the same room." />

View File

@ -44,10 +44,16 @@
$(function () {
JK = JK || {};
JK.root_url = "<%= root_url %>"
<% if current_user %>
JK.currentUserId = '<%= current_user.id %>';
JK.currentUserAvatarUrl = JK.resolveAvatarUrl('<%= current_user.photo_url %>');
JK.currentUserName = '<%= current_user.name %>';
<% else %>
JK.currentUserId = null;
JK.currentUserAvatarUrl = null;
JK.currentUserName = null;
<% end %>
})
</script>

View File

@ -14,6 +14,11 @@
<%= include_gon %>
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
<% if content_for?(:social_meta) %>
<%= yield(:social_meta) %>
<% else %>
<%= render "layouts/social_meta" %>
<% end %>
</head>
<body>
<%= yield %>

View File

@ -12,6 +12,11 @@
<%= include_gon(:init => true) %>
<%= javascript_include_tag "corp/corporate" %>
<%= csrf_meta_tags %>
<% if content_for?(:social_meta) %>
<%= yield(:social_meta) %>
<% else %>
<%= render "layouts/social_meta" %>
<% end %>
</head>
<body class="corporate" data-purpose="<%= yield(:purpose) %>">
@ -59,10 +64,16 @@
$(function () {
JK = JK || {};
JK.root_url = "<%= root_url %>"
<% if current_user %>
JK.currentUserId = '<%= current_user.id %>';
JK.currentUserAvatarUrl = JK.resolveAvatarUrl('<%= current_user.photo_url %>');
JK.currentUserName = '<%= current_user.name %>';
<% else %>
JK.currentUserId = null;
JK.currentUserAvatarUrl = null;
JK.currentUserName = null;
<% end %>
})
</script>

View File

@ -14,6 +14,11 @@
<% end %>
<%= include_gon(:init => true) %>
<%= csrf_meta_tags %>
<% if content_for?(:social_meta) %>
<%= yield(:social_meta) %>
<% else %>
<%= render "layouts/social_meta" %>
<% end %>
</head>
<body>
<div id="landing-container">
@ -41,10 +46,16 @@
$(function () {
JK = JK || {};
JK.root_url = "<%= root_url %>"
<% if current_user %>
JK.currentUserId = '<%= current_user.id %>';
JK.currentUserAvatarUrl = JK.resolveAvatarUrl('<%= current_user.photo_url %>');
JK.currentUserName = '<%= current_user.name %>';
<% else %>
JK.currentUserId = null;
JK.currentUserAvatarUrl = null;
JK.currentUserName = null;
<% end %>
})
</script>

View File

@ -14,6 +14,11 @@
<% end %>
<%= include_gon(:init => true) %>
<%= csrf_meta_tags %>
<% if content_for?(:social_meta) %>
<%= yield(:social_meta) %>
<% else %>
<%= render "layouts/social_meta" %>
<% end %>
</head>
<body class="web">
<%= javascript_include_tag "web/web" %>
@ -51,23 +56,34 @@
<%= render "clients/invitationDialog" %>
<%= render "users/signupDialog" %>
<%= render "users/signinDialog" %>
<%= yield(:extra_dialogs) %>
<script type="text/javascript">
$(function () {
JK = JK || {};
JK.root_url = "<%= root_url %>"
<% if current_user %>
JK.currentUserId = '<%= current_user.id %>';
JK.currentUserAvatarUrl = JK.resolveAvatarUrl('<%= current_user.photo_url %>');
JK.currentUserName = '<%= current_user.name %>';
<% else %>
JK.currentUserId = null;
JK.currentUserAvatarUrl = null;
JK.currentUserName = null;
<% end %>
JK.app = JK.JamKazam();
JK.app.initialize({inClient: false, layoutOpts: {layoutFooter: false}});
JK.app.initialize({inClient: false, layoutOpts: {layoutFooter: false, sizeOverlayToContent: true}});
var facebookHelper = new JK.FacebookHelper(JK.app);
JK.FacebookHelperInstance = facebookHelper;
facebookHelper.initialize(gon.global.facebook_app_id);
var invitationDialog = new JK.InvitationDialog(JK.app);
invitationDialog.initialize();
invitationDialog.initialize(facebookHelper);
var userDropdown = new JK.UserDropdown(JK.app);
userDropdown.initialize(invitationDialog);
@ -75,11 +91,12 @@
var signupDialog = new JK.SignupDialog(JK.app);
signupDialog.initialize();
var signinDialog = new JK.SigninDialog(JK.app);
signinDialog.initialize();
})
</script>
<%= yield(:extra_js) %>
<%= render "shared/ga" %>
<!-- version info: <%= version %> -->
</body>

View File

@ -1,5 +1,21 @@
<% provide(:title, "#{@music_session.description}") %>
<% content_for :social_meta do %>
<meta property="fb:app_id" content="<%= Rails.application.config.facebook_app_id %>" />
<meta property="og:title" content="<%= title_for_music_session_history(@music_session) %>" />
<meta property="og:url" content="<%= request.original_url %>" />
<meta property="og:description" content="<%= description_for_music_session_history(@music_session) %>" />
<meta property="og:image" content="<%= facebook_image_for_music_session_history(@music_session) %>" />
<meta property="og:image:width" content="<%= facebook_image_size_for_music_session_history(@music_session) %>" />
<meta property="og:image:height" content="<%= facebook_image_size_for_music_session_history(@music_session) %>" />
<meta property="og:type" content="website" />
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@jamkazam">
<meta name="twitter:title" content="<%= title_for_music_session_history(@music_session) %>" />
<meta name="twitter:description" content="<%= description_for_music_session_history(@music_session) %>" />
<% end %>
<div class="landing-band">
<% unless @music_session.band.nil? %>
<div class="landing-avatar">
@ -78,64 +94,15 @@
<%= javascript_include_tag "web/sessions" %>
<%= render :partial => "clients/shareDialog", :locals => {:session => @music_session, :share_token => @music_session.share_token} %>
<% content_for :extra_dialogs do %>
<%= render :partial => "clients/shareDialog" %>
<% end %>
<script type="text/javascript">
$(function () {
JK = JK || {};
<% if current_user %>
JK.currentUserId = '<%= current_user.id %>';
<% else %>
JK.currentUserId = null;
<% end %>
if (JK.currentUserId) {
JK.app = JK.JamKazam();
JK.app.initialize({inClient: false, layoutOpts: {layoutFooter: false}});
var shareDialog = new JK.ShareDialog(JK.app);
shareDialog.initialize();
$("#btnShare").click(function(e) {
shareDialog.showDialog();
});
$("#txtSessionComment").keypress(function(e) {
if (e.which === 13) {
addComment();
}
});
}
else {
$("#txtSessionComment").attr("disabled", "disabled");
$("#txtSessionComment").val("You must be logged in to add a comment.");
}
JK.sessionId = "<%= @music_session.music_session_id %>";
var rest = new JK.Rest();
$("#btnLike").click(like);
function like() {
rest.addSessionLike(JK.sessionId, JK.currentUserId)
.done(function(response) {
$("#spnLikeCount").html(parseInt($("#spnLikeCount").text()) + 1);
$("#btnLike").unbind("click");
});
}
function addComment() {
var comment = $("#txtSessionComment").val();
if ($.trim(comment).length > 0) {
rest.addSessionComment(JK.sessionId, JK.currentUserId, comment)
.done(function(response) {
$("#spnCommentCount").html(parseInt($("#spnCommentCount").text()) + 1);
$(".landing-comment-scroller").prepend(comment);
});
}
}
})
</script>
<% content_for :extra_js do %>
<script type="text/javascript">
$(function () {
var showMusicSession = new JK.ShowMusicSession(JK.app);
showMusicSession.initialize("<%= @music_session.music_session_id %>");
})
</script>
<% end %>

View File

@ -1,5 +1,22 @@
<% provide(:title, "#{@claimed_recording.name}") %>
<% content_for :social_meta do %>
<meta property="fb:app_id" content="<%= Rails.application.config.facebook_app_id %>" />
<meta property="og:title" content="<%= title_for_claimed_recording(@claimed_recording) %>" />
<meta property="og:url" content="<%= request.original_url %>" />
<meta property="og:description" content="<%= description_for_claimed_recording(@claimed_recording) %>" />
<meta property="og:image" content="<%= facebook_image_for_claimed_recording(@claimed_recording) %>" />
<meta property="og:image:width" content="<%= facebook_image_size_for_claimed_recording(@claimed_recording) %>" />
<meta property="og:image:height" content="<%= facebook_image_size_for_claimed_recording(@claimed_recording) %>" />
<meta property="og:type" content="website" />
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@jamkazam">
<meta name="twitter:title" content="<%= title_for_claimed_recording(@claimed_recording) %>" />
<meta name="twitter:description" content="<%= description_for_claimed_recording(@claimed_recording) %>" />
<% end %>
<div class="landing-band">
<% unless @claimed_recording.recording.band.nil? %>
<div class="landing-avatar">
@ -77,72 +94,16 @@
<%= javascript_include_tag "web/recordings" %>
<%= render :partial => "clients/shareDialog", :locals => {:recording => @claimed_recording, :share_token => @claimed_recording.share_token} %>
<% content_for :extra_dialogs do %>
<%= render :partial => "clients/shareDialog" %>
<% end %>
<script type="text/javascript">
$(function () {
<% content_for :extra_js do %>
<script type="text/javascript">
$(function () {
var showRecording = new JK.ShowRecording(JK.app);
showRecording.initialize("<%= @claimed_recording.id %>", "<%= @claimed_recording.recording_id %>");
})
</script>
<% end %>
JK = JK || {};
<% if current_user %>
JK.currentUserId = '<%= current_user.id %>';
<% else %>
JK.currentUserId = null;
<% end %>
if (JK.currentUserId) {
JK.app = JK.JamKazam();
JK.app.initialize({inClient: false, layoutOpts: {layoutFooter: false}});
var shareDialog = new JK.ShareDialog(JK.app);
shareDialog.initialize();
$("#btnShare").click(function(e) {
shareDialog.showDialog();
});
$("#txtRecordingComment").keypress(function(e) {
if (e.which === 13) {
addComment();
}
});
}
else {
$("#txtRecordingComment").attr("disabled", "disabled");
$("#txtRecordingComment").val("You must be logged in to add a comment.");
}
JK.recordingId = "<%= @claimed_recording.recording.id %>";
var rest = new JK.Rest();
$("#btnLike").click(like);
$("#btnPlay").click(play);
function like() {
rest.addRecordingLike(JK.recordingId, JK.currentUserId)
.done(function(response) {
$("#spnLikeCount").html(parseInt($("#spnLikeCount").text()) + 1);
$("#btnLike").unbind("click");
});
}
function play() {
rest.addRecordingPlay(JK.recordingId, JK.currentUserId)
.done(function(response) {
$("#spnPlayCount").html(parseInt($("#spnPlayCount").text()) + 1);
});
}
function addComment() {
var comment = $("#txtRecordingComment").val();
if ($.trim(comment).length > 0) {
rest.addRecordingComment(JK.recordingId, JK.currentUserId, comment)
.done(function(response) {
$("#spnCommentCount").html(parseInt($("#spnCommentCount").text()) + 1);
$(".landing-comment-scroller").prepend(comment);
});
}
}
})
</script>

View File

@ -0,0 +1,3 @@
<script type="text/javascript">
</script>

View File

@ -0,0 +1,11 @@
Unable to authorize application. Reasons:
<ul>
<% @user_authorization.errors.each do |field, error| %>
<% if field == :uid && error.include?('has already been taken') %>
<li>This twitter account is already associated with someone else</li>
<% else %>
<li><%= @user_authorization.errors.full_message(field, error) %></li>
<% end %>
<% end %>
</ul>

View File

@ -4,7 +4,7 @@
<%= image_tag "shared/avatar_generic.png", {:alt => ""} %>
</div>
<div class="left w80 p10">
<textarea id="<%= id %>" class="w100 p5 f15" rows="2" onfocus="$(this).html('')" onblur="if($(this).html() == ''){$(this).html('Enter a comment...')}">Enter a comment...</textarea>
<textarea id="<%= id %>" class="w100 p5 f15" rows="2" placeholder="Enter a comment..."></textarea>
</div>
<br clear="all" />
@ -25,4 +25,16 @@
<br clear="all" />
<% end %>
</div>
</div>
</div>
<script type="text/template" id="template-landing-comment">
<div class="avatar-small mr10">
<img src="{avatar_url}" alt="" />
</div>
<div class="w80 left p10 lightgrey mt10">
<a href="#">{name}</a>&nbsp;{comment}
<br />
<div class="f12 grey mt5">Just now</div>
</div>
<br clear="all" />
</script>

View File

@ -29,7 +29,7 @@
window.fbAsyncInit = function() {
// init the FB JS SDK
FB.init({
appId : '<%= SampleApp::Application.config.facebook_key %>', // App ID from the App Dashboard
appId : gon.global.facebook_app_id, // App ID from the App Dashboard
// channelUrl : '//WWW.YOUR_DOMAIN.COM/channel.html', // Channel File for x-domain communication
status : true, // check the login status upon init?
cookie : true, // set sessions cookies to allow your server to access the session?

View File

@ -133,9 +133,6 @@ include JamRuby
# cloudfront host
config.cloudfront_host = "d34f55ppvvtgi3.cloudfront.net"
# facebook keys
config.facebook_key = '468555793186398'
# google api keys
config.google_client_id = '785931784279-gd0g8on6sc0tuesj7cu763pitaiv2la8.apps.googleusercontent.com'
config.google_secret = 'UwzIcvtErv9c2-GIsNfIo7bA'
@ -196,7 +193,10 @@ include JamRuby
config.email_smtp_password = 'jamjamblueberryjam'
config.email_smtp_starttls_auto = true
config.facebook_app_id = '468555793186398'
config.facebook_app_secret = '546a5b253972f3e2e8b36d9a3dd5a06e'
config.facebook_app_id = ENV['FACEBOOK_APP_ID'] || '468555793186398'
config.facebook_app_secret = ENV['FACEBOOK_APP_SECRET'] || '546a5b253972f3e2e8b36d9a3dd5a06e'
config.twitter_app_id = ENV['TWITTER_APP_ID'] || 'nQj2oEeoJZxECC33tiTuIg'
config.twitter_app_secret = ENV['TWITTER_APP_SECRET'] || 'Azcy3QqfzYzn2fsojFPYXcn72yfwa0vG6wWDrZ3KT8'
end
end

View File

@ -89,4 +89,7 @@ SampleApp::Application.configure do
config.facebook_app_id = '1412328362347190' # staging
config.facebook_app_secret = '8b1f20430356d44fb49c0a504a9ff401' # staging
config.twitter_app_id = 'RHv0NJod7NLCXH6Kv29LWw' # staging
config.twitter.app_secret = 'ZjLl7rtagTozYDuKKyZNtaTQ4aGFmZPVCO8EoUJmg' # staging
end

View File

@ -62,5 +62,8 @@ SampleApp::Application.configure do
config.facebook_app_id = '1441492266082868'
config.facebook_app_secret = '233bd040a07e47dcec1cff3e490bfce7'
config.twitter_app_id = 'e7hGc71gmcBgo6Wvdta6Sg'
config.twitter_app_secret = 'PfG1jAUMnyrimPcDooUVQaJrG1IuDjUyGg5KciOo'
end

View File

@ -0,0 +1 @@
Gon.global.facebook_app_id = Rails.application.config.facebook_app_id

View File

@ -1,5 +1,10 @@
Rails.application.config.middleware.use OmniAuth::Builder do
provider :facebook, Rails.application.config.facebook_app_id, Rails.application.config.facebook_app_secret, {name: "facebook", :scope => 'email,user_location'}
provider :google_oauth2, Rails.application.config.google_client_id, Rails.application.config.google_secret, {name: "google_login", approval_prompt: '', scope: 'userinfo.email, userinfo.profile, https://www.google.com/m8/feeds'}
provider :facebook, Rails.application.config.facebook_app_id, Rails.application.config.facebook_app_secret, {name: "facebook", :scope => 'email,user_location'}
provider :google_oauth2, Rails.application.config.google_client_id, Rails.application.config.google_secret, {name: "google_login", approval_prompt: '', scope: 'userinfo.email, userinfo.profile, https://www.google.com/m8/feeds'}
provider :twitter, Rails.application.config.twitter_app_id, Rails.application.config.twitter_app_secret, {x_auth_access_type: 'write' }
end
# https://github.com/intridea/omniauth/wiki/FAQ
OmniAuth.config.on_failure = Proc.new { |env|
OmniAuth::FailureEndpoint.new(env).redirect_to_failure
}

View File

@ -64,6 +64,9 @@ SampleApp::Application.routes.draw do
match '/gmail_contacts', to: 'spikes#gmail_contacts'
match '/listen_in', to: 'spikes#listen_in'
# share tokens
match '/s/:id', to: 'share_tokens#shareable_resolver', :as => 'share_token'
# password reset
match '/request_reset_password' => 'users#request_reset_password', :via => :get
match '/reset_password' => 'users#reset_password', :via => :post
@ -215,6 +218,10 @@ SampleApp::Application.routes.draw do
match '/users/progression/certified_gear' => 'api_users#qualified_gear', :via => :post
match '/users/progression/social_promoted' => 'api_users#social_promoted', :via => :post
# social
match '/users/:id/share/session/:provider' => 'api_users#share_session', :via => :get
match '/users/:id/share/recording/:provider' => 'api_users#share_recording', :via => :get
# user recordings
# match '/users/:id/recordings' => 'api_users#recording_index', :via => :get
# match '/users/:id/recordings/:recording_id' => 'api_users#recording_show', :via => :get, :as => 'api_recording_detail'
@ -336,6 +343,8 @@ SampleApp::Application.routes.draw do
match '/icecast/mount_remove' => 'api_icecast#mount_remove', :via => :post
match '/icecast/listener_add' => 'api_icecast#listener_add', :via => :post
match '/icecast/listener_remove' => 'api_icecast#listener_remove', :via => :post
match '/twitter/tweet' => 'api_twitters#tweet', :via => :post
end
end

View File

@ -10,4 +10,4 @@ pre-start script
chown jam-web:jam-web /var/run/jam-web
end script
exec start-stop-daemon --start --chuid jam-web:jam-web --chdir /var/lib/jam-web --exec /var/lib/jam-web/script/package/upstart-run.sh
exec start-stop-daemon --start --chuid jam-web:jam-web --chdir /var/lib/jam-web --exec /var/lib/jam-web/script/package/upstart-run.sh

View File

@ -2,7 +2,9 @@ require 'spec_helper'
describe SessionsController do
render_views
let(:user) { FactoryGirl.create(:user) }
describe "GET 'new'" do
it "should work" do
get :new
@ -36,40 +38,81 @@ describe SessionsController do
end
describe "create_oauth" do
before(:each) do
OmniAuth.config.mock_auth[:facebook] = OmniAuth::AuthHash.new({
'uid' => '100',
'provider' => 'facebook',
'info' => {
'first_name' => 'FirstName',
'last_name' => 'LastName',
'email' => 'test_oauth@example.com',
'location' => 'mylocation'
},
'credentials' => {
'token' => 'facebooktoken',
'expires_at' => 1000000000
}
})
describe "twitter" do
before(:each) do
OmniAuth.config.mock_auth[:twitter] = OmniAuth::AuthHash.new({
'uid' => '100',
'provider' => 'twitter',
'credentials' => {
'token' => 'twittertoken',
'secret' => 'twittersecret'
}
})
end
it "should update user_authorization for existing user" do
cookie_jar[:remember_token] = user.remember_token # controller.current_user is not working. i think because of omniauth
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:twitter]
visit '/auth/twitter'
user.reload
auth = user.user_authorization('twitter')
auth.uid.should == '100'
auth.token.should == 'twittertoken'
auth.secret.should == 'twittersecret'
# also verify that a second visit does *not* create another new user
visit '/auth/twitter'
user.reload
auth = user.user_authorization('twitter')
auth.uid.should == '100'
auth.token.should == 'twittertoken'
auth.secret.should == 'twittersecret'
end
end
it "should create a user when oauth comes in with a non-currently existing user" do
pending "needs this fixed: https://jamkazam.atlassian.net/browse/VRFS-271"
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:facebook]
lambda do
visit '/auth/facebook'
end.should change(User, :count).by(1)
user = User.find_by_email('test_oauth@example.com')
user.should_not be_nil
user.first_name.should == "FirstName"
response.should be_success
# also verify that a second visit does *not* create another new user
lambda do
visit '/auth/facebook'
end.should change(User, :count).by(0)
describe "facebook" do
before(:each) do
OmniAuth.config.mock_auth[:facebook] = OmniAuth::AuthHash.new({
'uid' => '100',
'provider' => 'facebook',
'info' => {
'first_name' => 'FirstName',
'last_name' => 'LastName',
'email' => 'test_oauth@example.com',
'location' => 'mylocation'
},
'credentials' => {
'token' => 'facebooktoken',
'expires_at' => 1000000000
}
})
end
it "should create a user when oauth comes in with a non-currently existing user" do
pending "needs this fixed: https://jamkazam.atlassian.net/browse/VRFS-271"
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:facebook]
lambda do
visit '/auth/facebook'
end.should change(User, :count).by(1)
user = User.find_by_email('test_oauth@example.com')
user.should_not be_nil
user.first_name.should == "FirstName"
response.should be_success
# also verify that a second visit does *not* create another new user
lambda do
visit '/auth/facebook'
end.should change(User, :count).by(0)
end
end
end

View File

@ -0,0 +1,28 @@
require 'spec_helper'
describe ShareTokensController do
render_views
let(:user) { FactoryGirl.create(:user) }
let(:music_session) {FactoryGirl.create(:music_session, creator: user) }
let(:claimed_recording) {FactoryGirl.create(:claimed_recording) }
it "resolves music session" do
music_session.touch
get :shareable_resolver, :id => music_session.music_session_history.share_token.token
location_header = response.headers["Location"]
location_header.should == music_session_detail_url(music_session.id)
end
it "resolves claimed recording" do
claimed_recording.touch
get :shareable_resolver, :id => claimed_recording.share_token.token
location_header = response.headers["Location"]
location_header.should == recording_detail_url(claimed_recording.id)
end
end

View File

@ -60,12 +60,14 @@ FactoryGirl.define do
musician_access true
legal_terms true
genres [JamRuby::Genre.first]
association :creator, :factory => :user
after(:create) { |session|
MusicSessionHistory.save(session)
}
end
factory :music_session_user_history, :class => JamRuby::MusicSessionUserHistory do
end
@ -119,20 +121,52 @@ FactoryGirl.define do
priority 0
end
factory :recording, :class => JamRuby::Recording do
end
factory :track, :class => JamRuby::Track do
sound "mono"
sequence(:client_track_id) { |n| "client_track_id_seq_#{n}"}
end
factory :recorded_track, :class => JamRuby::RecordedTrack do
instrument JamRuby::Instrument.first
sound 'stereo'
sequence(:client_id) { |n| "client_id-#{n}"}
sequence(:track_id) { |n| "track_id-#{n}"}
sequence(:client_track_id) { |n| "client_track_id-#{n}"}
md5 'abc'
length 1
fully_uploaded true
association :user, factory: :user
association :recording, factory: :recording
end
factory :recording, :class => JamRuby::Recording do
association :owner, factory: :user
association :music_session, factory: :music_session
factory :recording_with_track do
before(:create) { |recording|
recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: recording.owner)
}
end
end
factory :claimed_recording, :class => JamRuby::ClaimedRecording do
sequence(:name) { |n| "name-#{n}" }
sequence(:description) { |n| "description-#{n}" }
is_public true
is_downloadable true
association :genre, factory: :genre
association :user, factory: :user
before(:create) { |claimed_recording|
claimed_recording.recording = FactoryGirl.create(:recording_with_track, owner: claimed_recording.user)
}
end
factory :geocoder, :class => JamRuby::MaxMindGeo do
country 'US'
sequence(:region) { |n| ['NC', 'CA'][(n-1).modulo(2)] }

View File

@ -0,0 +1,115 @@
require 'spec_helper'
describe "social metadata" do
include MusicSessionHelper
include RecordingHelper
subject { page }
share_examples_for :has_default_metadata do
it "should have default metadata" do
page.find('meta[property="fb:app_id"]', :visible => false)['content'].should == Rails.application.config.facebook_app_id
page.find('meta[property="og:title"]', :visible => false)['content'].should == "JamKazam"
page.find('meta[property="og:description"]', :visible => false)['content'].should == "Play music together over the Internet as if in the same room."
page.find('meta[property="og:image"]', :visible => false)['content'].include?("/assets/web/logo-256.png").should be_true
page.find('meta[property="og:image:width"]', :visible => false)['content'].should == "256"
page.find('meta[property="og:image:height"]', :visible => false)['content'].should == "256"
page.find('meta[property="og:type"]', :visible => false)['content'].should == "website"
end
end
describe "default layout metadata" do
let(:user) {FactoryGirl.create(:user) }
describe "web layout" do
before(:each) do
visit '/'
end
it_behaves_like :has_default_metadata
end
describe "corp layout" do
before(:each) do
visit '/corp/about'
end
it_behaves_like :has_default_metadata
end
describe "client layout" do
before(:each) do
sign_in user
visit '/client'
end
it_behaves_like :has_default_metadata
end
describe "landing layout" do
before(:each) do
visit '/signin'
end
it_behaves_like :has_default_metadata
end
end
describe "music session metadata" do
let(:user) { FactoryGirl.create(:user) }
let(:connection) { FactoryGirl.create(:connection, :user => user) }
let(:instrument) { FactoryGirl.create(:instrument, :description => 'a great instrument') }
let(:track) { FactoryGirl.create(:track, :connection => connection, :instrument => instrument) }
let(:music_session) { ms = FactoryGirl.create(:music_session, :creator => user, :musician_access => true); ms.connections << connection; ms.save!; ms }
it "renders facebook metadata" do
visit "/sessions/#{music_session.id}"
page.find('meta[property="fb:app_id"]', :visible => false)['content'].should == Rails.application.config.facebook_app_id
page.find('meta[property="og:title"]', :visible => false)['content'].should == title_for_music_session_history(music_session.music_session_history)
page.find('meta[property="og:url"]', :visible => false)['content'].include?("/sessions/#{music_session.id}").should be_true
page.find('meta[property="og:description"]', :visible => false)['content'].should == music_session.music_session_history.description
page.find('meta[property="og:image"]', :visible => false)['content'].include?("/assets/web/logo-256.png").should be_true
page.find('meta[property="og:image:width"]', :visible => false)['content'].should == "256"
page.find('meta[property="og:image:height"]', :visible => false)['content'].should == "256"
page.find('meta[property="og:type"]', :visible => false)['content'].should == "website"
end
end
describe "recording metadata" do
before(:each) do
@user = FactoryGirl.create(:user)
@connection = FactoryGirl.create(:connection, :user => @user)
@instrument = FactoryGirl.create(:instrument, :description => 'a great instrument')
@track = FactoryGirl.create(:track, :connection => @connection, :instrument => @instrument)
@music_session = FactoryGirl.create(:music_session, :creator => @user, :musician_access => true)
@music_session.connections << @connection
@music_session.save
@recording = Recording.start(@music_session, @user)
@recording.stop
@recording.reload
@genre = FactoryGirl.create(:genre)
@recording.claim(@user, "name", "description", @genre, true, true)
@recording.reload
@claimed_recording = @recording.claimed_recordings.first
end
it "renders facebook metadata" do
visit "/recordings/#{@claimed_recording.id}"
page.find('meta[property="fb:app_id"]', :visible => false)['content'].should == Rails.application.config.facebook_app_id
page.find('meta[property="og:title"]', :visible => false)['content'].should == title_for_claimed_recording(@claimed_recording)
page.find('meta[property="og:url"]', :visible => false)['content'].include?("/recordings/#{@claimed_recording.id}").should be_true
page.find('meta[property="og:description"]', :visible => false)['content'].should == @claimed_recording.name
page.find('meta[property="og:image"]', :visible => false)['content'].include?("/assets/web/logo-256.png").should be_true
page.find('meta[property="og:image:width"]', :visible => false)['content'].should == "256"
page.find('meta[property="og:image:height"]', :visible => false)['content'].should == "256"
page.find('meta[property="og:type"]', :visible => false)['content'].should == "website"
end
end
end

View File

@ -0,0 +1,70 @@
require 'spec_helper'
describe "Welcome", :js => true, :type => :feature, :capybara_feature => true do
subject { page }
before(:all) do
Capybara.javascript_driver = :poltergeist
Capybara.current_driver = Capybara.javascript_driver
Capybara.default_wait_time = 10
end
let(:user) { FactoryGirl.create(:user, email: 'twitter_user1@jamkazam.com') }
let(:user2) { FactoryGirl.create(:user, email: 'twitter_user2@jamkazam.com') }
let(:twitter_auth) {
{ :provider => "twitter",
:uid => "1234",
:credentials => {:token => "twitter_token", :secret => 'twitter_secret'} }
}
before(:each) do
OmniAuth.config.mock_auth[:twitter] = OmniAuth::AuthHash.new(twitter_auth)
User.where(email: 'twitter_user1@jamkazam.com').delete_all
User.where(email: 'twitter_user2@jamkazam.com').delete_all
page.driver.headers = { 'User-Agent' => ' JamKazam ' }
sign_in_poltergeist user
visit "/"
find('h1', text: 'Play music together over the Internet as if in the same room')
end
it "redirects back when done, and updates user_auth" do
visit '/auth/twitter'
find('h1', text: 'Play music together over the Internet as if in the same room')
sleep 1
user.reload
auth = user.user_authorization('twitter')
auth.should_not be_nil
auth.uid.should == '1234'
auth.token.should == 'twitter_token'
auth.secret.should == 'twitter_secret'
visit '/auth/twitter'
find('h1', text: 'Play music together over the Internet as if in the same room')
user.reload
auth = user.user_authorization('twitter')
auth.uid.should == '1234'
auth.token.should == 'twitter_token'
auth.secret.should == 'twitter_secret'
end
it "shows error when two users try to auth same twitter account" do
visit '/auth/twitter'
find('h1', text: 'Play music together over the Internet as if in the same room')
sleep 1
user.reload
auth = user.user_authorization('twitter')
auth.uid.should == '1234'
sign_in_poltergeist user2
visit '/'
find('h1', text: 'Play music together over the Internet as if in the same room')
visit '/auth/twitter'
find('li', text: 'This twitter account is already associated with someone else')
end
end

Some files were not shown because too many files have changed in this diff Show More