merge conflict
This commit is contained in:
commit
b4aae340f7
|
|
@ -121,3 +121,7 @@ scores_mod_connections.sql
|
|||
scores_create_schemas_and_extensions.sql
|
||||
scores_create_tables.sql
|
||||
remove_is_downloadable.sql
|
||||
scores_mod_connections2.sql
|
||||
track_download_counts.sql
|
||||
scores_mod_users2.sql
|
||||
user_bio.sql
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
-- fix locidispid should be bigint
|
||||
|
||||
ALTER TABLE connections DROP COLUMN locidispid;
|
||||
ALTER TABLE connections ADD COLUMN locidispid BIGINT;
|
||||
ALTER TABLE connections ALTER COLUMN locidispid SET NOT NULL;
|
||||
CREATE INDEX connections_locidispid_ndx ON connections (locidispid);
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
-- locidispid must be bigint
|
||||
|
||||
ALTER TABLE users DROP COLUMN locidispid;
|
||||
ALTER TABLE users ADD COLUMN locidispid BIGINT;
|
||||
ALTER TABLE users ALTER COLUMN locidispid SET DEFAULT 0;
|
||||
UPDATE users SET locidispid = 0;
|
||||
ALTER TABLE users ALTER COLUMN locidispid SET NOT NULL;
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
ALTER TABLE recorded_tracks ADD COLUMN download_count INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE recorded_tracks ADD COLUMN last_downloaded_at TIMESTAMP;
|
||||
|
||||
ALTER TABLE mixes ADD COLUMN download_count INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE mixes ADD COLUMN last_downloaded_at TIMESTAMP;
|
||||
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE users ALTER COLUMN biography TYPE TEXT;
|
||||
|
|
@ -33,6 +33,7 @@ gem 'carrierwave'
|
|||
gem 'aasm', '3.0.16'
|
||||
gem 'devise', '>= 1.1.2'
|
||||
gem 'postgres-copy'
|
||||
gem 'geokit'
|
||||
gem 'geokit-rails'
|
||||
gem 'postgres_ext'
|
||||
gem 'resque'
|
||||
|
|
@ -41,6 +42,7 @@ gem 'resque-failed-job-mailer' #, :path => "/Users/seth/workspace/resque_failed_
|
|||
gem 'resque-lonely_job', '~> 1.0.0'
|
||||
gem 'oj'
|
||||
gem 'builder'
|
||||
gem 'fog'
|
||||
|
||||
group :test do
|
||||
gem 'simplecov', '~> 0.7.1'
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ require "action_mailer"
|
|||
require "devise"
|
||||
require "sendgrid"
|
||||
require "postgres-copy"
|
||||
require "geokit"
|
||||
require "geokit-rails"
|
||||
require "postgres_ext"
|
||||
require 'builder'
|
||||
|
|
@ -124,6 +125,9 @@ require "jam_ruby/models/recording_play"
|
|||
require "jam_ruby/models/feed"
|
||||
require "jam_ruby/models/jam_isp"
|
||||
require "jam_ruby/models/geo_ip_blocks"
|
||||
require "jam_ruby/models/geo_ip_locations"
|
||||
require "jam_ruby/models/score"
|
||||
require "jam_ruby/models/get_work"
|
||||
|
||||
include Jampb
|
||||
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@
|
|||
@body = msg
|
||||
sendgrid_category "Notification"
|
||||
sendgrid_unique_args :type => unique_args[:type]
|
||||
mail(:to => email, :subject => subject) do |format|
|
||||
mail(:bcc => email, :subject => subject) do |format|
|
||||
format.text
|
||||
format.html
|
||||
end
|
||||
|
|
@ -154,53 +154,54 @@
|
|||
end
|
||||
end
|
||||
|
||||
def musician_session_join(email, msg)
|
||||
def musician_session_join(email, msg, session_id)
|
||||
subject = "Someone you know is in a session on JamKazam"
|
||||
unique_args = {:type => "musician_session_join"}
|
||||
|
||||
@body = msg
|
||||
@session_url = "#{APP_CONFIG.external_root_url}/sessions/#{session_id}"
|
||||
sendgrid_category "Notification"
|
||||
sendgrid_unique_args :type => unique_args[:type]
|
||||
mail(:to => email, :subject => subject) do |format|
|
||||
mail(:bcc => email, :subject => subject) do |format|
|
||||
format.text
|
||||
format.html
|
||||
end
|
||||
end
|
||||
|
||||
def band_session_join(email, msg)
|
||||
def band_session_join(email, msg, session_id)
|
||||
subject = "A band that you follow has joined a session"
|
||||
unique_args = {:type => "band_session_join"}
|
||||
|
||||
@body = msg
|
||||
@session_url = "#{APP_CONFIG.external_root_url}/sessions/#{session_id}"
|
||||
sendgrid_category "Notification"
|
||||
sendgrid_unique_args :type => unique_args[:type]
|
||||
mail(:to => email, :subject => subject) do |format|
|
||||
mail(:bcc => email, :subject => subject) do |format|
|
||||
format.text
|
||||
format.html
|
||||
end
|
||||
end
|
||||
|
||||
def musician_recording_saved(email, msg)
|
||||
subject = msg
|
||||
subject = "A musician has saved a new recording on JamKazam"
|
||||
unique_args = {:type => "musician_recording_saved"}
|
||||
|
||||
@body = msg
|
||||
sendgrid_category "Notification"
|
||||
sendgrid_unique_args :type => unique_args[:type]
|
||||
mail(:to => email, :subject => subject) do |format|
|
||||
mail(:bcc => email, :subject => subject) do |format|
|
||||
format.text
|
||||
format.html
|
||||
end
|
||||
end
|
||||
|
||||
def band_recording_saved(email, msg)
|
||||
subject = msg
|
||||
subject = "A band has saved a new recording on JamKazam"
|
||||
unique_args = {:type => "band_recording_saved"}
|
||||
|
||||
@body = msg
|
||||
sendgrid_category "Notification"
|
||||
sendgrid_unique_args :type => unique_args[:type]
|
||||
mail(:to => email, :subject => subject) do |format|
|
||||
mail(:bcc => email, :subject => subject) do |format|
|
||||
format.text
|
||||
format.html
|
||||
end
|
||||
|
|
@ -236,7 +237,7 @@
|
|||
# @body = msg
|
||||
# sendgrid_category "Notification"
|
||||
# sendgrid_unique_args :type => unique_args[:type]
|
||||
# mail(:to => email, :subject => subject) do |format|
|
||||
# mail(:bcc => email, :subject => subject) do |format|
|
||||
# format.text
|
||||
# format.html
|
||||
# end
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
<% provide(:title, 'New Band Session') %>
|
||||
|
||||
<p><%= @body %></p>
|
||||
<p><%= @body %> <a href="<%= @session_url %>">Listen in.</a></p>
|
||||
|
|
@ -1 +1 @@
|
|||
<%= @body %>
|
||||
<%= @body %> Listen at <%= @session_url %>.
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
<% provide(:title, 'Musician in Session') %>
|
||||
|
||||
<p><%= @body %></p>
|
||||
<p><%= @body %> <a href="<%= @session_url %>">Listen in.</a></p>
|
||||
|
|
@ -1 +1 @@
|
|||
<%= @body %>
|
||||
<%= @body %> Listen at <%= @session_url %>.
|
||||
|
|
@ -56,27 +56,44 @@ module JamRuby
|
|||
end
|
||||
|
||||
if ip_address
|
||||
# todo turn ip_address string into a number, then fetch the locid and ispid and the other stuff...
|
||||
# turn ip_address string into a number, then fetch the isp and block records and update location info
|
||||
|
||||
addr = JamIsp.ip_to_num(ip_address)
|
||||
puts("============= JamIsp.ip_to_num returns #{addr} for #{ip_address} =============")
|
||||
#puts("============= JamIsp.ip_to_num returns #{addr} for #{ip_address} =============")
|
||||
|
||||
isp = JamIsp.lookup(addr)
|
||||
#puts("============= JamIsp.lookup returns #{isp.inspect} for #{addr} =============")
|
||||
if isp.nil? then ispid = 0 else ispid = isp.coid end
|
||||
puts("============= JamIsp.lookup returns #{ispid} for #{addr} =============")
|
||||
|
||||
block = GeoIpBlocks.lookup(addr)
|
||||
#puts("============= GeoIpBlocks.lookup returns #{block.inspect} for #{addr} =============")
|
||||
if block.nil? then locid = 0 else locid = block.locid end
|
||||
puts("============= GeoIpBlocks.lookup returns #{locid} for #{addr} =============")
|
||||
|
||||
locidispid = 0
|
||||
latitude = 0.0
|
||||
longitude = 0.0
|
||||
countrycode = 'US'
|
||||
region = 'TX'
|
||||
city = 'Austin'
|
||||
location = GeoIpLocations.lookup(locid)
|
||||
if location.nil?
|
||||
locidispid = 0
|
||||
latitude = 0.0
|
||||
longitude = 0.0
|
||||
countrycode = 'US'
|
||||
region = 'TX'
|
||||
city = 'Austin'
|
||||
else
|
||||
locidispid = locid*1000000+ispid
|
||||
latitude = location.latitude
|
||||
longitude = location.longitude
|
||||
countrycode = location.countrycode
|
||||
region = location.region
|
||||
city = location.city
|
||||
end
|
||||
|
||||
# todo stuff this stuff into the connection records
|
||||
conn.ip_address = ip_address
|
||||
conn.locidispid = locidispid
|
||||
conn.latitude = latitude
|
||||
conn.longitude = longitude
|
||||
conn.countrycode = countrycode
|
||||
conn.region = region
|
||||
conn.city = city
|
||||
conn.save!(validate: false)
|
||||
end
|
||||
|
||||
sql =<<SQL
|
||||
|
|
@ -188,30 +205,40 @@ SQL
|
|||
ConnectionManager.active_record_transaction do |connection_manager|
|
||||
conn = connection_manager.pg_conn
|
||||
|
||||
# todo turn ip_address string into a number, then fetch the locid and ispid and the other stuff...
|
||||
# turn ip_address string into a number, then fetch the isp and block records
|
||||
|
||||
addr = JamIsp.ip_to_num(ip_address)
|
||||
puts("============= JamIsp.ip_to_num returns #{addr} for #{ip_address} =============")
|
||||
#puts("============= JamIsp.ip_to_num returns #{addr} for #{ip_address} =============")
|
||||
|
||||
isp = JamIsp.lookup(addr)
|
||||
#puts("============= JamIsp.lookup returns #{isp.inspect} for #{addr} =============")
|
||||
if isp.nil? then ispid = 0 else ispid = isp.coid end
|
||||
puts("============= JamIsp.lookup returns #{ispid} for #{addr} =============")
|
||||
|
||||
block = GeoIpBlocks.lookup(addr)
|
||||
#puts("============= GeoIpBlocks.lookup returns #{block.inspect} for #{addr} =============")
|
||||
if block.nil? then locid = 0 else locid = block.locid end
|
||||
puts("============= GeoIpBlocks.lookup returns #{locid} for #{addr} =============")
|
||||
|
||||
locidispid = 0
|
||||
latitude = 0.0
|
||||
longitude = 0.0
|
||||
countrycode = 'US'
|
||||
region = 'TX'
|
||||
city = 'Austin'
|
||||
location = GeoIpLocations.lookup(locid)
|
||||
if location.nil?
|
||||
locidispid = 0
|
||||
latitude = 0.0
|
||||
longitude = 0.0
|
||||
countrycode = 'US'
|
||||
region = 'TX'
|
||||
city = 'Austin'
|
||||
else
|
||||
locidispid = locid*1000000+ispid
|
||||
latitude = location.latitude
|
||||
longitude = location.longitude
|
||||
countrycode = location.countrycode
|
||||
region = location.region
|
||||
city = location.city
|
||||
end
|
||||
|
||||
lock_connections(conn)
|
||||
|
||||
conn.exec("INSERT INTO connections (user_id, client_id, addr, locidispid, latitude, longitude, countrycode, region, city, aasm_state) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
|
||||
[user_id, client_id, addr, locidispid, latitude, longitude, countrycode, region, city, Connection::CONNECT_STATE.to_s]).clear
|
||||
conn.exec("INSERT INTO connections (user_id, client_id, addr, locidispid, latitude, longitude, countrycode, region, city, aasm_state, ip_address) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)",
|
||||
[user_id, client_id, addr, locidispid, latitude, longitude, countrycode, region, city, Connection::CONNECT_STATE.to_s, ip_address]).clear
|
||||
|
||||
# we just created a new connection-if this is the first time the user has shown up, we need to send out a message to his friends
|
||||
conn.exec("SELECT count(user_id) FROM connections WHERE user_id = $1", [user_id]) do |result|
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ module JamRuby
|
|||
validates_uniqueness_of :user_id, :scope => :recording_id
|
||||
validate :user_belongs_to_recording
|
||||
|
||||
|
||||
before_create :generate_share_token
|
||||
|
||||
SHARE_TOKEN_LENGTH = 8
|
||||
|
|
@ -67,7 +68,6 @@ module JamRuby
|
|||
!ClaimedRecording.find_by_user_id_and_recording_id(some_user.id, recording_id).nil?
|
||||
end
|
||||
|
||||
|
||||
def remove_non_alpha_num(token)
|
||||
token.gsub(/[^0-9A-Za-z]/, '')
|
||||
end
|
||||
|
|
|
|||
|
|
@ -102,15 +102,15 @@ module JamRuby
|
|||
errors.add(:music_session, ValidationMessages::CANT_JOIN_RECORDING_SESSION)
|
||||
end
|
||||
|
||||
unless user.admin?
|
||||
num_sessions = Connection.where(:user_id => user_id)
|
||||
.where(["(music_session_id IS NOT NULL) AND (aasm_state != ?)",EXPIRED_STATE.to_s])
|
||||
.count
|
||||
if 0 < num_sessions
|
||||
errors.add(:music_session, ValidationMessages::CANT_JOIN_MULTIPLE_SESSIONS)
|
||||
return false;
|
||||
end
|
||||
end
|
||||
# unless user.admin?
|
||||
# num_sessions = Connection.where(:user_id => user_id)
|
||||
# .where(["(music_session_id IS NOT NULL) AND (aasm_state != ?)",EXPIRED_STATE.to_s])
|
||||
# .count
|
||||
# if 0 < num_sessions
|
||||
# errors.add(:music_session, ValidationMessages::CANT_JOIN_MULTIPLE_SESSIONS)
|
||||
# return false;
|
||||
# end
|
||||
# end
|
||||
|
||||
return true
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,17 +4,14 @@ module JamRuby
|
|||
self.table_name = 'geoipblocks'
|
||||
|
||||
def self.lookup(ipnum)
|
||||
GeoIpBlocks.select(:locid)
|
||||
.where('geom && ST_MakePoint(?, 0) AND ? BETWEEN beginip AND endip', ipnum, ipnum)
|
||||
GeoIpBlocks.where('geom && ST_MakePoint(?, 0) AND ? BETWEEN beginip AND endip', ipnum, ipnum)
|
||||
.limit(1)
|
||||
.first
|
||||
end
|
||||
|
||||
def self.make_row(beginip, endip, locid)
|
||||
c = ActiveRecord::Base.connection.raw_connection
|
||||
c.prepare('blah', 'insert into geoipblocks (beginip, endip, locid, geom) values($1::bigint, $2::bigint, $3, ST_MakeEnvelope($1::bigint, -1, $2::bigint, 1))')
|
||||
c.exec_prepared('blah', [beginip, endip, locid])
|
||||
c.exec("deallocate blah")
|
||||
def self.createx(beginip, endip, locid)
|
||||
c = connection.raw_connection
|
||||
c.exec_params('insert into geoipblocks (beginip, endip, locid, geom) values($1::bigint, $2::bigint, $3, ST_MakeEnvelope($1::bigint, -1, $2::bigint, 1))', [beginip, endip, locid])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
module JamRuby
|
||||
class GeoIpLocations < ActiveRecord::Base
|
||||
|
||||
self.table_name = 'geoiplocations'
|
||||
|
||||
def self.lookup(locid)
|
||||
GeoIpLocations.where(locid: locid)
|
||||
.limit(1)
|
||||
.first
|
||||
end
|
||||
|
||||
def self.createx(locid, countrycode, region, city, postalcode, latitude, longitude, metrocode, areacode)
|
||||
c = connection.raw_connection
|
||||
c.exec_params('insert into geoiplocations (locid, countrycode, region, city, postalcode, latitude, longitude, metrocode, areacode, geog) values($1, $2, $3, $4, $5, $6, $7, $8, $9, ST_SetSRID(ST_MakePoint($7, $6), 4326)::geography)',
|
||||
[locid, countrycode, region, city, postalcode, latitude, longitude, metrocode, areacode])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
module JamRuby
|
||||
class GetWork < ActiveRecord::Base
|
||||
|
||||
self.table_name = "connections"
|
||||
|
||||
def self.get_work(mylocidispid)
|
||||
list = self.get_work_list(mylocidispid)
|
||||
return nil if list.nil?
|
||||
return nil if list.length == 0
|
||||
return list[0]
|
||||
end
|
||||
|
||||
def self.get_work_list(mylocidispid)
|
||||
r = GetWork.select(:client_id).find_by_sql("select get_work(#{mylocidispid}) as client_id")
|
||||
#puts("r = #{r}")
|
||||
a = r.map {|i| i.client_id}
|
||||
#puts("a = #{a}")
|
||||
a
|
||||
#return ["blah1", "blah2", "blah3", "blah4", "blah5"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -110,7 +110,7 @@ module JamRuby
|
|||
|
||||
def listener_add
|
||||
with_lock do
|
||||
sourced_needs_changing_at = Time.now if listeners == 0
|
||||
self.sourced_needs_changing_at = Time.now if listeners == 0
|
||||
|
||||
# this is completely unsafe without that 'with_lock' statement above
|
||||
self.listeners = self.listeners + 1
|
||||
|
|
@ -126,7 +126,7 @@ module JamRuby
|
|||
end
|
||||
|
||||
with_lock do
|
||||
sourced_needs_changing_at = Time.now if listeners == 1
|
||||
self.sourced_needs_changing_at = Time.now if listeners == 1
|
||||
|
||||
# this is completely unsafe without that 'with_lock' statement above
|
||||
self.listeners = self.listeners - 1
|
||||
|
|
|
|||
|
|
@ -18,11 +18,10 @@ module JamRuby
|
|||
.first
|
||||
end
|
||||
|
||||
def self.make_row(beginip, endip, coid)
|
||||
c = ActiveRecord::Base.connection.raw_connection
|
||||
c.prepare('blah', 'insert into jamisp (beginip, endip, coid, geom) values($1::bigint, $2::bigint, $3, ST_MakeEnvelope($1::bigint, -1, $2::bigint, 1))')
|
||||
c.exec_prepared('blah', [beginip, endip, coid])
|
||||
c.exec("deallocate blah")
|
||||
def self.createx(beginip, endip, coid)
|
||||
c = connection.raw_connection
|
||||
c.exec_params('insert into jamisp (beginip, endip, coid, geom) values($1::bigint, $2::bigint, $3, ST_MakeEnvelope($1::bigint, -1, $2::bigint, 1))',
|
||||
[beginip, endip, coid])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,14 +10,24 @@ module JamRuby
|
|||
|
||||
attr_accessible :ogg_url, :should_retry, as: :admin
|
||||
attr_accessor :is_skip_mount_uploader
|
||||
attr_writer :current_user
|
||||
|
||||
belongs_to :recording, :class_name => "JamRuby::Recording", :inverse_of => :mixes, :foreign_key => 'recording_id'
|
||||
|
||||
validates :download_count, presence: true
|
||||
validate :verify_download_count
|
||||
|
||||
skip_callback :save, :before, :store_picture!, if: :is_skip_mount_uploader
|
||||
|
||||
mount_uploader :ogg_url, MixUploader
|
||||
|
||||
|
||||
def verify_download_count
|
||||
if (self.download_count < 0 || self.download_count > APP_CONFIG.max_audio_downloads) && !@current_user.admin
|
||||
errors.add(:download_count, "must be less than or equal to 100")
|
||||
end
|
||||
end
|
||||
|
||||
before_validation do
|
||||
# this should be an activeadmin only path, because it's using the mount_uploader (whereas the client does something completely different)
|
||||
if !is_skip_mount_uploader && ogg_url.present? && ogg_url.respond_to?(:file) && ogg_url_changed?
|
||||
|
|
@ -67,7 +77,6 @@ module JamRuby
|
|||
!ClaimedRecording.find_by_user_id_and_recording_id(some_user.id, recording_id).nil?
|
||||
end
|
||||
|
||||
|
||||
def errored(reason, detail)
|
||||
self.error_reason = reason
|
||||
self.error_detail = detail
|
||||
|
|
@ -148,6 +157,11 @@ module JamRuby
|
|||
Mix.construct_filename(self.created_at, self.recording_id, self.id, type)
|
||||
end
|
||||
|
||||
def update_download_count(count=1)
|
||||
self.download_count = self.download_count + count
|
||||
self.last_downloaded_at = Time.now
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def delete_s3_files
|
||||
|
|
|
|||
|
|
@ -43,19 +43,23 @@ module JamRuby
|
|||
self.comments.size
|
||||
end
|
||||
|
||||
def tracks
|
||||
def grouped_tracks
|
||||
tracks = []
|
||||
self.music_session_user_histories.each do |msuh|
|
||||
user = User.find(msuh.user_id)
|
||||
t = Track.new
|
||||
t.musician = user
|
||||
t.instrument_ids = []
|
||||
# this treats each track as a "user", which has 1 or more instruments in the session
|
||||
unless msuh.instruments.blank?
|
||||
instruments = msuh.instruments.split(SEPARATOR)
|
||||
instruments.each do |instrument|
|
||||
t = Track.new
|
||||
t.musician = user
|
||||
t.instrument_id = instrument
|
||||
tracks << t
|
||||
if !t.instrument_ids.include? instrument
|
||||
t.instrument_ids << instrument
|
||||
end
|
||||
end
|
||||
end
|
||||
tracks << t
|
||||
end
|
||||
tracks
|
||||
end
|
||||
|
|
@ -146,7 +150,7 @@ module JamRuby
|
|||
end
|
||||
|
||||
def is_over?
|
||||
!session_removed_at.nil?
|
||||
music_session.nil? || !session_removed_at.nil?
|
||||
end
|
||||
|
||||
def end_history
|
||||
|
|
@ -173,8 +177,6 @@ module JamRuby
|
|||
|
||||
hist.end_history if hist
|
||||
|
||||
puts "**************NOTIFICATION SESSION ENDED**************"
|
||||
|
||||
Notification.send_session_ended(session_id)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
module JamRuby
|
||||
class Notification < ActiveRecord::Base
|
||||
|
||||
@@log = Logging.logger[Notification]
|
||||
|
||||
self.primary_key = 'id'
|
||||
|
||||
default_scope order('created_at DESC')
|
||||
|
|
@ -357,7 +359,6 @@ module JamRuby
|
|||
# publish to all users who have a notification for this session
|
||||
# TODO: do this in BULK or in async block
|
||||
notifications.each do |n|
|
||||
puts "*************SENDING SESSION_ENDED TO #{n.target_user_id}***************"
|
||||
msg = @@message_factory.session_ended(n.target_user_id, session_id)
|
||||
@@mq_router.publish_to_user(n.target_user_id, msg)
|
||||
end
|
||||
|
|
@ -505,7 +506,11 @@ module JamRuby
|
|||
|
||||
# send email notifications
|
||||
unless offline_ff.empty?
|
||||
UserMailer.musician_session_join(offline_ff.map! {|f| f.email}, notification_msg).deliver
|
||||
begin
|
||||
UserMailer.musician_session_join(offline_ff.map! {|f| f.email}, notification_msg, music_session.id).deliver if APP_CONFIG.send_join_session_email_notifications
|
||||
rescue => e
|
||||
@@log.error("unable to send email to offline participants #{e}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -547,7 +552,7 @@ module JamRuby
|
|||
|
||||
# send email notifications
|
||||
unless offline_followers.empty?
|
||||
UserMailer.band_session_join(offline_followers.map! {|f| f.email}, notification_msg).deliver
|
||||
UserMailer.band_session_join(offline_followers.map! {|f| f.email}, notification_msg, music_session.id).deliver if APP_CONFIG.send_join_session_email_notifications
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -81,6 +81,14 @@ class JamRuby::PromoLatest < JamRuby::Promotional
|
|||
|
||||
attr_accessible :latest
|
||||
|
||||
def music_session_history
|
||||
@music_session_history ||= MusicSessionHistory.find_by_id(latest_id)
|
||||
end
|
||||
|
||||
def recording
|
||||
@recording ||= Recording.find_by_id(latest_id)
|
||||
end
|
||||
|
||||
def self.latest_candidates
|
||||
recordings = Recording
|
||||
.where('music_session_id IS NOT NULL')
|
||||
|
|
|
|||
|
|
@ -8,10 +8,13 @@ module JamRuby
|
|||
self.table_name = "recorded_tracks"
|
||||
self.primary_key = 'id'
|
||||
|
||||
default_scope order('user_id ASC')
|
||||
|
||||
attr_accessor :marking_complete
|
||||
attr_writer :is_skip_mount_uploader
|
||||
|
||||
attr_accessible :discard, :user, :user_id, :instrument_id, :sound, :client_id, :track_id, :client_track_id, :url, as: :admin
|
||||
attr_writer :current_user
|
||||
|
||||
SOUND = %w(mono stereo)
|
||||
MAX_PART_FAILURES = 3
|
||||
|
|
@ -31,11 +34,13 @@ module JamRuby
|
|||
validates :length, length: {minimum: 1, maximum: 1024 * 1024 * 256 }, if: :upload_starting? # 256 megs max. is this reasonable? surely...
|
||||
validates :user, presence: true
|
||||
validates :instrument, presence: true
|
||||
validates :download_count, presence: true
|
||||
|
||||
before_destroy :delete_s3_files
|
||||
validate :validate_fully_uploaded
|
||||
validate :validate_part_complete
|
||||
validate :validate_too_many_upload_failures
|
||||
validate :verify_download_count
|
||||
|
||||
before_save :sanitize_active_admin
|
||||
skip_callback :save, :before, :store_picture!, if: :is_skip_mount_uploader?
|
||||
|
|
@ -97,6 +102,12 @@ module JamRuby
|
|||
end
|
||||
end
|
||||
|
||||
def verify_download_count
|
||||
if (self.download_count < 0 || self.download_count > APP_CONFIG.max_audio_downloads) && !@current_user.admin
|
||||
errors.add(:download_count, "must be less than or equal to 100")
|
||||
end
|
||||
end
|
||||
|
||||
def sanitize_active_admin
|
||||
self.user_id = nil if self.user_id == ''
|
||||
end
|
||||
|
|
@ -187,6 +198,11 @@ module JamRuby
|
|||
RecordedTrack.construct_filename(self.created_at, self.recording.id, self.client_track_id)
|
||||
end
|
||||
|
||||
def update_download_count(count=1)
|
||||
self.download_count = self.download_count + count
|
||||
self.last_downloaded_at = Time.now
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def delete_s3_files
|
||||
|
|
|
|||
|
|
@ -43,6 +43,35 @@ module JamRuby
|
|||
self.comments.size
|
||||
end
|
||||
|
||||
# this can probably be done more efficiently, but David needs this asap for a video
|
||||
def grouped_tracks
|
||||
tracks = []
|
||||
sorted_tracks = self.recorded_tracks.sort { |a,b| a.user.id <=> b.user.id }
|
||||
|
||||
t = Track.new
|
||||
t.instrument_ids = []
|
||||
sorted_tracks.each_with_index do |track, index|
|
||||
if index > 0
|
||||
if sorted_tracks[index-1].user.id != sorted_tracks[index].user.id
|
||||
t = Track.new
|
||||
t.instrument_ids = []
|
||||
t.instrument_ids << track.instrument.id
|
||||
t.musician = track.user
|
||||
tracks << t
|
||||
else
|
||||
if !t.instrument_ids.include? track.instrument.id
|
||||
t.instrument_ids << track.instrument.id
|
||||
end
|
||||
end
|
||||
else
|
||||
t.musician = track.user
|
||||
t.instrument_ids << track.instrument.id
|
||||
tracks << t
|
||||
end
|
||||
end
|
||||
tracks
|
||||
end
|
||||
|
||||
def not_already_recording
|
||||
if music_session && music_session.is_recording?
|
||||
errors.add(:music_session, ValidationMessages::ALREADY_BEING_RECORDED)
|
||||
|
|
@ -309,6 +338,12 @@ module JamRuby
|
|||
save
|
||||
end
|
||||
|
||||
|
||||
# meant to be used as a way to 'pluck' a claimed_recording appropriate for user.
|
||||
def candidate_claimed_recording
|
||||
claimed_recordings.where(is_public: true).first
|
||||
end
|
||||
|
||||
private
|
||||
def self.validate_user_is_band_member(user, band)
|
||||
unless band.users.exists? user
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
require 'ipaddr'
|
||||
|
||||
module JamRuby
|
||||
class Score < ActiveRecord::Base
|
||||
|
||||
self.table_name = 'scores'
|
||||
|
||||
attr_accessible :alocidispid, :anodeid, :aaddr, :blocidispid, :bnodeid, :baddr, :score, :score_dt, :scorer
|
||||
|
||||
default_scope order('score_dt desc')
|
||||
|
||||
def self.createx(alocidispid, anodeid, aaddr, blocidispid, bnodeid, baddr, score, score_dt)
|
||||
score_dt = Time.new.utc if score_dt.nil?
|
||||
Score.create(alocidispid: alocidispid, anodeid: anodeid, aaddr: aaddr, blocidispid: blocidispid, bnodeid: bnodeid, baddr: baddr, score: score, scorer: 0, score_dt: score_dt)
|
||||
Score.create(alocidispid: blocidispid, anodeid: bnodeid, aaddr: baddr, blocidispid: alocidispid, bnodeid: anodeid, baddr: aaddr, score: score, scorer: 1, score_dt: score_dt) if alocidispid != blocidispid
|
||||
end
|
||||
|
||||
def self.deletex(alocidispid, blocidispid)
|
||||
Score.where(alocidispid: alocidispid, blocidispid: blocidispid).delete_all
|
||||
Score.where(alocidispid: blocidispid, blocidispid: alocidispid).delete_all if alocidispid != blocidispid
|
||||
end
|
||||
|
||||
def self.findx(alocidispid, blocidispid)
|
||||
s = Score.where(alocidispid: alocidispid, blocidispid: blocidispid).first
|
||||
return -1 if s.nil?
|
||||
return s.score
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -7,7 +7,7 @@ module JamRuby
|
|||
|
||||
default_scope order('created_at ASC')
|
||||
|
||||
attr_accessor :musician
|
||||
attr_accessor :musician, :instrument_ids
|
||||
|
||||
SOUND = %w(mono stereo)
|
||||
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ module JamRuby
|
|||
|
||||
validates :first_name, presence: true, length: {maximum: 50}, no_profanity: true
|
||||
validates :last_name, presence: true, length: {maximum: 50}, no_profanity: true
|
||||
validates :biography, length: {maximum: 4000}, no_profanity: true
|
||||
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
|
||||
validates :email, presence: true, format: {with: VALID_EMAIL_REGEX}
|
||||
validates :update_email, presence: true, format: {with: VALID_EMAIL_REGEX}, :if => :updating_email
|
||||
|
|
|
|||
|
|
@ -171,10 +171,28 @@ FactoryGirl.define do
|
|||
association :user, factory: :user
|
||||
|
||||
before(:create) { |claimed_recording|
|
||||
|
||||
claimed_recording.recording = FactoryGirl.create(:recording_with_track, owner: claimed_recording.user) unless claimed_recording.recording
|
||||
}
|
||||
|
||||
|
||||
end
|
||||
|
||||
factory :mix, :class => JamRuby::Mix do
|
||||
started_at Time.now
|
||||
completed_at Time.now
|
||||
ogg_md5 'abc'
|
||||
ogg_length 1
|
||||
sequence(:ogg_url) { |n| "recordings/ogg/#{n}" }
|
||||
mp3_md5 'abc'
|
||||
mp3_length 1
|
||||
sequence(:mp3_url) { |n| "recordings/mp3/#{n}" }
|
||||
completed true
|
||||
|
||||
before(:create) {|mix|
|
||||
user = FactoryGirl.create(:user)
|
||||
mix.recording = FactoryGirl.create(:recording_with_track, owner: user)
|
||||
mix.recording.claimed_recordings << FactoryGirl.create(:claimed_recording, user: user, recording: mix.recording)
|
||||
}
|
||||
end
|
||||
|
||||
factory :musician_instrument, :class => JamRuby::MusicianInstrument do
|
||||
|
|
|
|||
|
|
@ -426,6 +426,7 @@ describe ConnectionManager do
|
|||
end
|
||||
|
||||
it "join_music_session fails if user has music_session already active" do
|
||||
pending
|
||||
user_id = create_user("test", "user11", "user11@jamkazam.com")
|
||||
|
||||
user = User.find(user_id)
|
||||
|
|
|
|||
|
|
@ -4,16 +4,22 @@ describe GeoIpBlocks do
|
|||
|
||||
before do
|
||||
GeoIpBlocks.delete_all
|
||||
GeoIpBlocks.make_row(0x00000000, 0xffffffff, 17192)
|
||||
GeoIpBlocks.createx(0x01020300, 0x010203ff, 1)
|
||||
GeoIpBlocks.createx(0x02030400, 0x020304ff, 2)
|
||||
end
|
||||
|
||||
it "count" do GeoIpBlocks.count.should == 1 end
|
||||
after do
|
||||
GeoIpBlocks.delete_all
|
||||
GeoIpBlocks.createx(0x00000000, 0xffffffff, 17192)
|
||||
end
|
||||
|
||||
it "count" do GeoIpBlocks.count.should == 2 end
|
||||
|
||||
let(:first) { GeoIpBlocks.lookup(0x01020304) }
|
||||
let(:second) { GeoIpBlocks.lookup(0x02030405) }
|
||||
let(:seventh) { GeoIpBlocks.lookup(9999999999) } # bogus
|
||||
let(:third) { GeoIpBlocks.lookup(9999999999) } # bogus
|
||||
|
||||
it "first.locid" do first.locid.should == 17192 end
|
||||
it "second.locid" do second.locid.should == 17192 end
|
||||
it "seventh" do seventh.should be_nil end
|
||||
it "first.locid" do first.locid.should == 1 end
|
||||
it "second.locid" do second.locid.should == 2 end
|
||||
it "third" do third.should be_nil end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe GeoIpLocations do
|
||||
|
||||
before do
|
||||
GeoIpLocations.delete_all
|
||||
GeoIpLocations.createx(17192, 'US', 'TX', 'Austin', '78749', 30.2076, -97.8587, 635, '512')
|
||||
GeoIpLocations.createx(48086, 'MX', '28', 'Matamoros', '', 25.8833, -97.5000, nil, '')
|
||||
end
|
||||
|
||||
it "count" do GeoIpLocations.count.should == 2 end
|
||||
|
||||
let(:first) { GeoIpLocations.lookup(17192) }
|
||||
let(:second) { GeoIpLocations.lookup(48086) }
|
||||
let(:third) { GeoIpLocations.lookup(999999) } # bogus
|
||||
|
||||
it "first" do
|
||||
first.locid.should == 17192
|
||||
first.countrycode.should eql('US')
|
||||
first.region.should eql('TX')
|
||||
first.city.should eql('Austin')
|
||||
first.latitude.should == 30.2076
|
||||
first.longitude.should == -97.8587
|
||||
end
|
||||
|
||||
it "second" do
|
||||
second.locid.should == 48086
|
||||
second.countrycode.should eql('MX')
|
||||
second.region.should eql('28')
|
||||
second.city.should eql('Matamoros')
|
||||
second.latitude.should == 25.8833
|
||||
second.longitude.should == -97.5000
|
||||
end
|
||||
|
||||
it "third" do third.should be_nil end
|
||||
end
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe GetWork do
|
||||
|
||||
before(:each) do
|
||||
|
||||
end
|
||||
|
||||
it "get_work_1" do
|
||||
x = GetWork.get_work(1)
|
||||
puts x.inspect
|
||||
x.should be_nil
|
||||
end
|
||||
|
||||
it "get_work_list_1" do
|
||||
x = GetWork.get_work_list(1)
|
||||
puts x.inspect
|
||||
x.should eql([])
|
||||
end
|
||||
end
|
||||
|
|
@ -4,17 +4,17 @@ describe JamIsp do
|
|||
|
||||
before do
|
||||
JamIsp.delete_all
|
||||
JamIsp.make_row(0x01020300, 0x010203ff, 1)
|
||||
JamIsp.make_row(0x02030400, 0x020304ff, 2)
|
||||
JamIsp.make_row(0x03040500, 0x030405ff, 3)
|
||||
JamIsp.make_row(0x04050600, 0x040506ff, 4)
|
||||
JamIsp.make_row(0xc0A80100, 0xc0A801ff, 5)
|
||||
JamIsp.make_row(0xfffefd00, 0xfffefdff, 6)
|
||||
JamIsp.createx(0x01020300, 0x010203ff, 1)
|
||||
JamIsp.createx(0x02030400, 0x020304ff, 2)
|
||||
JamIsp.createx(0x03040500, 0x030405ff, 3)
|
||||
JamIsp.createx(0x04050600, 0x040506ff, 4)
|
||||
JamIsp.createx(0xc0A80100, 0xc0A801ff, 5)
|
||||
JamIsp.createx(0xfffefd00, 0xfffefdff, 6)
|
||||
end
|
||||
|
||||
after do
|
||||
JamIsp.delete_all
|
||||
JamIsp.make_row(0x00000000, 0xffffffff, 1)
|
||||
JamIsp.createx(0x00000000, 0xffffffff, 1)
|
||||
end
|
||||
|
||||
it "count" do JamIsp.count.should == 6 end
|
||||
|
|
|
|||
|
|
@ -63,6 +63,16 @@ describe Mix do
|
|||
recordings.length.should == 0
|
||||
end
|
||||
|
||||
|
||||
describe "download count" do
|
||||
it "will fail if too high" do
|
||||
mix = FactoryGirl.create(:mix)
|
||||
mix.current_user = mix.recording.owner
|
||||
mix.update_download_count(APP_CONFIG.max_audio_downloads + 1)
|
||||
mix.save
|
||||
mix.errors[:download_count].should == ["must be less than or equal to 100"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Score do
|
||||
|
||||
before do
|
||||
Score.delete_all
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil)
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 3456, 'cnodeid', 0x03040506, 30, nil)
|
||||
Score.createx(1234, 'anodeid', 0x01020304, 3456, 'cnodeid', 0x03040506, 40, Time.new.utc-3600)
|
||||
end
|
||||
|
||||
it "count" do
|
||||
Score.count.should == 6
|
||||
end
|
||||
|
||||
it 'a to b' do
|
||||
s = Score.where(alocidispid: 1234, blocidispid: 2345).limit(1).first
|
||||
s.should_not be_nil
|
||||
s.alocidispid.should == 1234
|
||||
s.anodeid.should eql('anodeid')
|
||||
s.aaddr.should == 0x01020304
|
||||
s.blocidispid.should == 2345
|
||||
s.bnodeid.should eql('bnodeid')
|
||||
s.baddr.should == 0x02030405
|
||||
s.score.should == 20
|
||||
s.scorer.should == 0
|
||||
s.score_dt.should_not be_nil
|
||||
end
|
||||
|
||||
it 'b to a' do
|
||||
s = Score.where(alocidispid: 2345, blocidispid: 1234).limit(1).first
|
||||
s.should_not be_nil
|
||||
s.alocidispid.should == 2345
|
||||
s.anodeid.should eql('bnodeid')
|
||||
s.aaddr.should == 0x02030405
|
||||
s.blocidispid.should == 1234
|
||||
s.bnodeid.should eql('anodeid')
|
||||
s.baddr.should == 0x01020304
|
||||
s.score.should == 20
|
||||
s.scorer.should == 1
|
||||
s.score_dt.should_not be_nil
|
||||
end
|
||||
|
||||
it 'a to c' do
|
||||
s = Score.where(alocidispid: 1234, blocidispid: 3456).limit(1).first
|
||||
s.should_not be_nil
|
||||
s.alocidispid.should == 1234
|
||||
s.anodeid.should eql('anodeid')
|
||||
s.aaddr.should == 0x01020304
|
||||
s.blocidispid.should == 3456
|
||||
s.bnodeid.should eql('cnodeid')
|
||||
s.baddr.should == 0x03040506
|
||||
s.score.should == 30
|
||||
s.scorer.should == 0
|
||||
s.score_dt.should_not be_nil
|
||||
end
|
||||
|
||||
it 'c to a' do
|
||||
s = Score.where(alocidispid: 3456, blocidispid: 1234).limit(1).first
|
||||
s.should_not be_nil
|
||||
s.alocidispid.should == 3456
|
||||
s.anodeid.should eql('cnodeid')
|
||||
s.aaddr.should == 0x03040506
|
||||
s.blocidispid.should == 1234
|
||||
s.bnodeid.should eql('anodeid')
|
||||
s.baddr.should == 0x01020304
|
||||
s.score.should == 30
|
||||
s.scorer.should == 1
|
||||
s.score_dt.should_not be_nil
|
||||
end
|
||||
|
||||
it 'delete a to c' do
|
||||
Score.deletex(1234, 3456)
|
||||
Score.count.should == 2
|
||||
Score.where(alocidispid: 1234, blocidispid: 3456).limit(1).first.should be_nil
|
||||
Score.where(alocidispid: 3456, blocidispid: 1234).limit(1).first.should be_nil
|
||||
Score.where(alocidispid: 1234, blocidispid: 2345).limit(1).first.should_not be_nil
|
||||
Score.where(alocidispid: 2345, blocidispid: 1234).limit(1).first.should_not be_nil
|
||||
end
|
||||
|
||||
it 'findx' do
|
||||
Score.findx(1234, 1234).should == -1
|
||||
Score.findx(1234, 2345).should == 20
|
||||
Score.findx(1234, 3456).should == 30
|
||||
|
||||
Score.findx(2345, 1234).should == 20
|
||||
Score.findx(2345, 2345).should == -1
|
||||
Score.findx(2345, 3456).should == -1
|
||||
|
||||
Score.findx(3456, 1234).should == 30
|
||||
Score.findx(3456, 2345).should == -1
|
||||
Score.findx(3456, 3456).should == -1
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -13,6 +13,9 @@ SpecDb::recreate_database
|
|||
# initialize ActiveRecord's db connection
|
||||
ActiveRecord::Base.establish_connection(YAML::load(File.open('config/database.yml'))["test"])
|
||||
|
||||
# so jam_ruby models that use APP_CONFIG in metadata will load. this is later stubbed pre test run
|
||||
APP_CONFIG = app_config
|
||||
|
||||
require 'jam_ruby'
|
||||
require 'factory_girl'
|
||||
require 'rubygems'
|
||||
|
|
|
|||
|
|
@ -101,6 +101,14 @@ def app_config
|
|||
'315576000'
|
||||
end
|
||||
|
||||
def max_audio_downloads
|
||||
100
|
||||
end
|
||||
|
||||
def send_join_session_email_notifications
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def audiomixer_workspace_path
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@
|
|||
var password_confirmation_errors = context.JK.format_errors("password_confirmation", errors)
|
||||
|
||||
if(current_password_errors != null) {
|
||||
$('#account-edit-password-form #account-forgot-password').closest('div.field').addClass('error').end().after(current_password_errors);
|
||||
$('#account-edit-password-form input[name=current_password]').closest('div.field').addClass('error').end().after(current_password_errors);
|
||||
}
|
||||
|
||||
if(password_errors != null) {
|
||||
|
|
|
|||
|
|
@ -47,55 +47,53 @@
|
|||
/****************** MAIN PORTION OF SCREEN *****************/
|
||||
|
||||
function addFollowing(isBand, id) {
|
||||
var newFollowing = {};
|
||||
var newFollowing = {};
|
||||
|
||||
if (!isBand) {
|
||||
newFollowing.user_id = id;
|
||||
}
|
||||
else {
|
||||
newFollowing.band_id = id;
|
||||
}
|
||||
if (!isBand) {
|
||||
newFollowing.user_id = id;
|
||||
}
|
||||
else {
|
||||
newFollowing.band_id = id;
|
||||
}
|
||||
|
||||
rest.addFollowing(newFollowing)
|
||||
.done(function() {
|
||||
if (isBand) {
|
||||
var newCount = parseInt($("#band-profile-follower-stats").text()) + 1;
|
||||
var text = newCount > 1 || newCount == 0 ? " Followers" : " Follower";
|
||||
$('#band-profile-follower-stats').html(newCount + text);
|
||||
configureBandFollowingButton(true);
|
||||
}
|
||||
else {
|
||||
configureMemberFollowingButton(true, id);
|
||||
}
|
||||
})
|
||||
.fail(app.ajaxError);
|
||||
rest.addFollowing(newFollowing)
|
||||
.done(function() {
|
||||
if (isBand) {
|
||||
var newCount = parseInt($("#band-profile-follower-stats").text()) + 1;
|
||||
var text = newCount > 1 || newCount == 0 ? " Followers" : " Follower";
|
||||
$('#band-profile-follower-stats').html(newCount + text);
|
||||
configureBandFollowingButton(true);
|
||||
}
|
||||
else {
|
||||
configureMemberFollowingButton(true, id);
|
||||
}
|
||||
renderActive();
|
||||
})
|
||||
.fail(app.ajaxError);
|
||||
}
|
||||
|
||||
function removeFollowing(isBand, id) {
|
||||
var following = {};
|
||||
following.target_entity_id = id;
|
||||
|
||||
rest.removeFollowing(following)
|
||||
.done(function() {
|
||||
renderActive(); // refresh stats
|
||||
if (isBand) {
|
||||
var newCount = parseInt($("#band-profile-follower-stats").text()) - 1;
|
||||
var text = newCount > 1 || newCount == 0 ? " Followers" : " Follower";
|
||||
$('#band-profile-follower-stats').html(newCount + text);
|
||||
configureBandFollowingButton(false);
|
||||
}
|
||||
else {
|
||||
configureMemberFollowingButton(false, id);
|
||||
}
|
||||
})
|
||||
.fail(app.ajaxError);
|
||||
rest.removeFollowing(id)
|
||||
.done(function() {
|
||||
if (isBand) {
|
||||
var newCount = parseInt($("#band-profile-follower-stats").text()) - 1;
|
||||
var text = newCount > 1 || newCount == 0 ? " Followers" : " Follower";
|
||||
$('#band-profile-follower-stats').html(newCount + text);
|
||||
configureBandFollowingButton(false);
|
||||
}
|
||||
else {
|
||||
configureMemberFollowingButton(false, id);
|
||||
}
|
||||
renderActive();
|
||||
})
|
||||
.fail(app.ajaxError);
|
||||
}
|
||||
|
||||
function configureBandFollowingButton(following) {
|
||||
$('#btn-follow-band').unbind("click");
|
||||
|
||||
if (following) {
|
||||
$('#btn-follow-band').text('STOP FOLLOWING');
|
||||
$('#btn-follow-band').text('UNFOLLOW');
|
||||
$('#btn-follow-band').click(function() {
|
||||
removeFollowing(true, bandId);
|
||||
return false;
|
||||
|
|
@ -121,7 +119,7 @@
|
|||
$btnFollowMember.unbind("click");
|
||||
|
||||
if (following) {
|
||||
$btnFollowMember.text('UN-FOLLOW');
|
||||
$btnFollowMember.text('UNFOLLOW');
|
||||
$btnFollowMember.click(function() {
|
||||
removeFollowing(false, userId);
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
(function(context, $) {
|
||||
|
||||
"use strict";
|
||||
|
||||
context.JK = context.JK || {};
|
||||
context.JK.FeedItemRecording = function($parentElement, options){
|
||||
|
||||
var $feedItem = $parentElement;
|
||||
var $description = $('.description', $feedItem)
|
||||
var $musicians = $('.musician-detail', $feedItem)
|
||||
|
||||
var toggledOpen = false;
|
||||
if(!$feedItem.is('.feed-entry')) {
|
||||
throw "$parentElement must be a .feed-entry"
|
||||
}
|
||||
|
||||
function toggleDetails() {
|
||||
if(toggledOpen) {
|
||||
$feedItem.css('height', $feedItem.height() + 'px')
|
||||
$feedItem.animate({'height': $feedItem.data('original-max-height')}).promise().done(function() {
|
||||
$feedItem.css('height', 'auto').css('max-height', $feedItem.data('original-max-height'));
|
||||
|
||||
$musicians.hide();
|
||||
$description.css('height', $description.data('original-height'));
|
||||
$description.dotdotdot();
|
||||
});
|
||||
}
|
||||
else {
|
||||
$description.trigger('destroy.dot');
|
||||
$description.data('original-height', $description.css('height')).css('height', 'auto');
|
||||
$musicians.show();
|
||||
$feedItem.animate({'max-height': '1000px'});
|
||||
}
|
||||
|
||||
toggledOpen = !toggledOpen;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function events() {
|
||||
$('.details', $feedItem).click(toggleDetails);
|
||||
$('.details-arrow', $feedItem).click(toggleDetails);
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
$('.timeago', $feedItem).timeago();
|
||||
$('.dotdotdot', $feedItem).dotdotdot();
|
||||
context.JK.prettyPrintElements($('time.duration', $feedItem));
|
||||
context.JK.setInstrumentAssetPath($('.instrument-icon', $feedItem));
|
||||
|
||||
$feedItem.data('original-max-height', $feedItem.css('height'));
|
||||
|
||||
events();
|
||||
}
|
||||
|
||||
initialize();
|
||||
|
||||
return this;
|
||||
}
|
||||
})(window, jQuery);
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
(function(context, $) {
|
||||
|
||||
"use strict";
|
||||
|
||||
context.JK = context.JK || {};
|
||||
context.JK.FeedItemSessionTimer = null;
|
||||
context.JK.FeedItemSession = function($parentElement, options){
|
||||
|
||||
var logger = context.JK.logger;
|
||||
var rest = new context.JK.Rest();
|
||||
|
||||
var $feedItem = $parentElement;
|
||||
var $description = $('.description', $feedItem)
|
||||
var $musicians = $('.musician-detail', $feedItem)
|
||||
var $controls = $('.session-controls', $feedItem);
|
||||
var $status = $('.session-status', $feedItem);
|
||||
var playing = false;
|
||||
var toggledOpen = false;
|
||||
var musicSessionId = $feedItem.attr('data-music-session');
|
||||
|
||||
if(!$feedItem.is('.feed-entry')) {
|
||||
throw "$parentElement must be a .feed-entry"
|
||||
}
|
||||
|
||||
function startPlay() {
|
||||
var img = $('.play-icon', $feedItem);
|
||||
img.attr('src', '/assets/content/icon_pausebutton.png');
|
||||
$controls.trigger('play.listenBroadcast');
|
||||
playing = true;
|
||||
}
|
||||
|
||||
function stopPlay() {
|
||||
var img = $('.play-icon', $feedItem);
|
||||
img.attr('src', '/assets/content/icon_playbutton.png');
|
||||
$controls.trigger('pause.listenBroadcast');
|
||||
playing = false;
|
||||
}
|
||||
function togglePlay() {
|
||||
if(playing) {
|
||||
$status.text('SESSION IN PROGRESS');
|
||||
stopPlay();
|
||||
}
|
||||
else {
|
||||
startPlay();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function toggleDetails() {
|
||||
if(toggledOpen) {
|
||||
$feedItem.css('height', $feedItem.height() + 'px')
|
||||
$feedItem.animate({'height': $feedItem.data('original-max-height')}).promise().done(function() {
|
||||
$feedItem.css('height', 'auto').css('max-height', $feedItem.data('original-max-height'));
|
||||
|
||||
$musicians.hide();
|
||||
$description.css('height', $description.data('original-height'));
|
||||
$description.dotdotdot();
|
||||
});
|
||||
}
|
||||
else {
|
||||
$description.trigger('destroy.dot');
|
||||
$description.data('original-height', $description.css('height')).css('height', 'auto');
|
||||
$musicians.show();
|
||||
$feedItem.animate({'max-height': '1000px'});
|
||||
}
|
||||
|
||||
toggledOpen = !toggledOpen;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function stateChange(e, data) {
|
||||
if(data.displayText) $status.text(data.displayText);
|
||||
|
||||
if(data.isEnd) stopPlay();
|
||||
|
||||
if(data.isSessionOver) {
|
||||
$controls.removeClass('inprogress').addClass('ended')
|
||||
}
|
||||
}
|
||||
|
||||
function events() {
|
||||
$('.details', $feedItem).click(toggleDetails);
|
||||
$('.details-arrow', $feedItem).click(toggleDetails);
|
||||
$('.play-button', $feedItem).click(togglePlay);
|
||||
|
||||
$controls.bind('statechange.listenBroadcast', stateChange);
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
$('.timeago', $feedItem).timeago();
|
||||
$('.dotdotdot', $feedItem).dotdotdot();
|
||||
$controls.listenBroadcast();
|
||||
|
||||
context.JK.prettyPrintElements($('time.duration', $feedItem));
|
||||
context.JK.setInstrumentAssetPath($('.instrument-icon', $feedItem));
|
||||
|
||||
$feedItem.data('original-max-height', $feedItem.css('height'));
|
||||
|
||||
events();
|
||||
|
||||
|
||||
// this is a bit lame, because this is a singleton.
|
||||
// the idea is, if there are any session widgets on the page, then we start ticking time
|
||||
if(!context.JK.FeedItemSessionTimer) {
|
||||
context.JK.FeedItemSessionTimer = setInterval(function() {
|
||||
$.each($('.feed-entry.music-session-history-entry .inprogress .session-duration'), function(index, item) {
|
||||
var $duration = $(item);
|
||||
|
||||
var createdAt = new Date(Number($duration.attr('data-created-at')) * 1000)
|
||||
var millisElapsed = (new Date()).getTime() - createdAt.getTime();
|
||||
console.log("createdAt, millis", createdAt, millisElapsed);
|
||||
$duration.text(context.JK.prettyPrintSeconds( parseInt(millisElapsed / 1000)));
|
||||
});
|
||||
}, 333);
|
||||
}
|
||||
}
|
||||
|
||||
initialize();
|
||||
|
||||
return this;
|
||||
}
|
||||
})(window, jQuery);
|
||||
|
|
@ -108,9 +108,9 @@
|
|||
for (var jj=0, ilen=mm['followings'].length; jj<ilen; jj++) {
|
||||
aFollow = mm['followings'][jj];
|
||||
followVals = {
|
||||
user_id: aFollow.id,
|
||||
user_id: aFollow.user_id,
|
||||
musician_name: aFollow.name,
|
||||
profile_url: '/client#/profile/' + aFollow.id,
|
||||
profile_url: '/client#/profile/' + aFollow.user_id,
|
||||
avatar_url: context.JK.resolveAvatarUrl(aFollow.photo_url),
|
||||
};
|
||||
follows += context.JK.fillTemplate(fTemplate, followVals);
|
||||
|
|
|
|||
|
|
@ -487,7 +487,7 @@
|
|||
* Load available drivers and populate the driver select box.
|
||||
*/
|
||||
function loadAudioDrivers() {
|
||||
var drivers = jamClient.FTUEGetDevices();
|
||||
var drivers = jamClient.FTUEGetDevices(false);
|
||||
|
||||
var driverOptionFunc = function (driverKey, index, list) {
|
||||
optionsHtml += '<option title="' + drivers[driverKey] + '"value="' + driverKey + '">' +
|
||||
|
|
|
|||
|
|
@ -49,20 +49,23 @@
|
|||
});
|
||||
|
||||
var bandHtml = context.JK.fillTemplate(template, {
|
||||
avatar_url: context.JK.resolveBandAvatarUrl(response.photo_url),
|
||||
name: response.name,
|
||||
location: response.location,
|
||||
genres: genres.join(', '),
|
||||
musicians: musicianHtml,
|
||||
like_count: response.liker_count,
|
||||
follower_count: response.follower_count,
|
||||
recording_count: response.recording_count,
|
||||
session_count: response.session_count,
|
||||
biography: response.biography,
|
||||
profile_url: "/client#/bandProfile/" + response.id
|
||||
bandId: response.id,
|
||||
avatar_url: context.JK.resolveBandAvatarUrl(response.photo_url),
|
||||
name: response.name,
|
||||
location: response.location,
|
||||
genres: genres.join(', '),
|
||||
musicians: musicianHtml,
|
||||
like_count: response.liker_count,
|
||||
follower_count: response.follower_count,
|
||||
recording_count: response.recording_count,
|
||||
session_count: response.session_count,
|
||||
biography: response.biography,
|
||||
followAction: response.is_following ? "removeBandFollowing" : "addBandFollowing",
|
||||
profile_url: "/client#/bandProfile/" + response.id
|
||||
});
|
||||
|
||||
$(hoverSelector).append('<h2>Band Detail</h2>' + bandHtml);
|
||||
configureActionButtons(response);
|
||||
})
|
||||
.fail(function(xhr) {
|
||||
if(xhr.status >= 500) {
|
||||
|
|
@ -77,6 +80,20 @@
|
|||
});
|
||||
};
|
||||
|
||||
function configureActionButtons(band) {
|
||||
var btnFollowSelector = "#btnFollow";
|
||||
|
||||
// if unauthenticated or authenticated user is viewing his own profile
|
||||
if (!context.JK.currentUserId) {
|
||||
$(btnFollowSelector, hoverSelector).hide();
|
||||
}
|
||||
else {
|
||||
if (band.is_following) {
|
||||
$(btnFollowSelector, hoverSelector).html('UNFOLLOW');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.hideBubble = function() {
|
||||
$(hoverSelector).hide();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -56,12 +56,15 @@
|
|||
location: response.location,
|
||||
friend_count: response.friend_count,
|
||||
follower_count: response.follower_count,
|
||||
friendAction: response.is_friend ? "removeFanFriend" : (response.pending_friend_request ? "" : "sendFanFriendRequest"),
|
||||
followAction: response.is_following ? "removeFanFollowing" : "addFanFollowing",
|
||||
biography: response.biography,
|
||||
followings: response.followings && response.followings.length > 0 ? followingHtml : "<tr><td>N/A</td></tr>",
|
||||
profile_url: "/client#/profile/" + response.id
|
||||
});
|
||||
|
||||
$(hoverSelector).append('<h2>Fan Detail</h2>' + fanHtml);
|
||||
configureActionButtons(response);
|
||||
})
|
||||
.fail(function(xhr) {
|
||||
if(xhr.status >= 500) {
|
||||
|
|
@ -76,6 +79,33 @@
|
|||
});
|
||||
};
|
||||
|
||||
function configureActionButtons(user) {
|
||||
var btnFriendSelector = "#btnFriend";
|
||||
var btnFollowSelector = "#btnFollow";
|
||||
|
||||
if (!context.JK.currentUserId || context.JK.currentUserId === user.id) {
|
||||
$(btnFriendSelector, hoverSelector).hide();
|
||||
$(btnFollowSelector, hoverSelector).hide();
|
||||
}
|
||||
else {
|
||||
if (user.is_friend) {
|
||||
$(btnFriendSelector, hoverSelector).html('DISCONNECT');
|
||||
}
|
||||
|
||||
if (user.is_following) {
|
||||
$(btnFollowSelector, hoverSelector).html('UNFOLLOW');
|
||||
|
||||
$(btnFollowSelector, hoverSelector).click(function(evt) {
|
||||
rest.removeFollowing(user.id);
|
||||
});
|
||||
}
|
||||
|
||||
if (user.pending_friend_request) {
|
||||
$(btnFriendSelector, hoverSelector).hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.hideBubble = function() {
|
||||
$(hoverSelector).hide();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -58,9 +58,15 @@
|
|||
|
||||
var sessionDisplayStyle = 'none';
|
||||
var sessionId = '';
|
||||
var joinDisplayStyle = 'none';
|
||||
if (response.sessions !== undefined && response.sessions.length > 0) {
|
||||
sessionDisplayStyle = 'block';
|
||||
sessionId = response.sessions[0].id;
|
||||
var session = response.sessions[0];
|
||||
sessionId = session.id;
|
||||
|
||||
if (context.jamClient && session.musician_access) {
|
||||
joinDisplayStyle = 'inline';
|
||||
}
|
||||
}
|
||||
|
||||
var musicianHtml = context.JK.fillTemplate(template, {
|
||||
|
|
@ -74,13 +80,17 @@
|
|||
recording_count: response.recording_count,
|
||||
session_count: response.session_count,
|
||||
session_display: sessionDisplayStyle,
|
||||
session_id: sessionId,
|
||||
join_display: joinDisplayStyle,
|
||||
sessionId: sessionId,
|
||||
friendAction: response.is_friend ? "removeMusicianFriend" : (response.pending_friend_request ? "" : "sendMusicianFriendRequest"),
|
||||
followAction: response.is_following ? "removeMusicianFollowing" : "addMusicianFollowing",
|
||||
biography: response.biography,
|
||||
followings: response.followings && response.followings.length > 0 ? followingHtml : "<tr><td>N/A</td></tr>",
|
||||
profile_url: "/client#/profile/" + response.id
|
||||
});
|
||||
|
||||
$(hoverSelector).append('<h2>Musician Detail</h2>' + musicianHtml);
|
||||
configureActionButtons(response);
|
||||
})
|
||||
.fail(function(xhr) {
|
||||
if(xhr.status >= 500) {
|
||||
|
|
@ -95,6 +105,28 @@
|
|||
});
|
||||
};
|
||||
|
||||
function configureActionButtons(user) {
|
||||
var btnFriendSelector = "#btnFriend";
|
||||
var btnFollowSelector = "#btnFollow";
|
||||
|
||||
// if unauthenticated or authenticated user is viewing his own profile
|
||||
if (!context.JK.currentUserId || context.JK.currentUserId === user.id) {
|
||||
$(btnFriendSelector, hoverSelector).hide();
|
||||
$(btnFollowSelector, hoverSelector).hide();
|
||||
}
|
||||
else {
|
||||
if (user.is_friend) {
|
||||
$(btnFriendSelector, hoverSelector).html('DISCONNECT');
|
||||
}
|
||||
if (user.is_following) {
|
||||
$(btnFollowSelector, hoverSelector).html('UNFOLLOW');
|
||||
}
|
||||
if (user.pending_friend_request) {
|
||||
$(btnFriendSelector, hoverSelector).hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.hideBubble = function() {
|
||||
$(hoverSelector).hide();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,6 +8,36 @@
|
|||
var instrumentLogoMap = context.JK.getInstrumentIconMap24();
|
||||
var hoverSelector = "#recording-hover";
|
||||
|
||||
function deDupTracks(recordedTracks) {
|
||||
var tracks = [];
|
||||
|
||||
// this is replicated in recording.rb model
|
||||
var t = {};
|
||||
t.instrument_ids = []
|
||||
$.each(recordedTracks, function(index, track) {
|
||||
if (index > 0) {
|
||||
if (recordedTracks[index-1].user.id !== recordedTracks[index].user.id) {
|
||||
t = {};
|
||||
t.instrument_ids = [];
|
||||
t.instrument_ids.push(track.instrument_id);
|
||||
t.user = track.user;
|
||||
tracks.push(t);
|
||||
}
|
||||
else {
|
||||
if ($.inArray(track.instrument_id, t.instrument_ids)) {
|
||||
t.instrument_ids.push(track.instrument_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
t.user = track.user;
|
||||
t.instrument_ids.push(track.instrument_id);
|
||||
tracks.push(t);
|
||||
}
|
||||
});
|
||||
return tracks;
|
||||
}
|
||||
|
||||
this.showBubble = function() {
|
||||
$(hoverSelector).css({left: position.left-100, top: position.top+20});
|
||||
$(hoverSelector).fadeIn(500);
|
||||
|
|
@ -18,9 +48,11 @@
|
|||
var recording = response.recording;
|
||||
$(hoverSelector).html('');
|
||||
|
||||
var deDupedTracks = deDupTracks(recording.recorded_tracks);
|
||||
|
||||
// musicians
|
||||
var musicianHtml = '';
|
||||
$.each(recording.recorded_tracks, function(index, val) {
|
||||
$.each(deDupedTracks, function(index, val) {
|
||||
var instrumentHtml = '';
|
||||
var musician = val.user;
|
||||
|
||||
|
|
@ -28,7 +60,9 @@
|
|||
musicianHtml += '<td width="75"><a href="#">' + musician.name + '</a></td>';
|
||||
|
||||
instrumentHtml = '<td><div class="nowrap">';
|
||||
instrumentHtml += '<img src="' + instrumentLogoMap[val.instrument_id] + '" width="24" height="24" /> ';
|
||||
$.each(val.instrument_ids, function(index, val) {
|
||||
instrumentHtml += '<img src="' + instrumentLogoMap[val] + '" width="24" height="24" /> ';
|
||||
})
|
||||
instrumentHtml += '</div></td>';
|
||||
|
||||
musicianHtml += instrumentHtml;
|
||||
|
|
@ -44,7 +78,7 @@
|
|||
name: claimedRecording.name,
|
||||
genre: claimedRecording.genre_id.toUpperCase(),
|
||||
created_at: context.JK.formatDateTime(recording.created_at),
|
||||
description: response.description,
|
||||
description: response.description ? response.description : "",
|
||||
play_count: recording.play_count,
|
||||
comment_count: recording.comment_count,
|
||||
like_count: recording.like_count,
|
||||
|
|
@ -55,6 +89,7 @@
|
|||
});
|
||||
|
||||
$(hoverSelector).append('<h2>Recording Detail</h2>' + recordingHtml);
|
||||
toggleActionButtons();
|
||||
})
|
||||
.fail(function(xhr) {
|
||||
if(xhr.status >= 500) {
|
||||
|
|
@ -69,6 +104,13 @@
|
|||
});
|
||||
};
|
||||
|
||||
function toggleActionButtons() {
|
||||
if (!context.JK.currentUserId) {
|
||||
$("#btnLike", hoverSelector).hide();
|
||||
$("#btnShare", hoverSelector).hide();
|
||||
}
|
||||
}
|
||||
|
||||
this.hideBubble = function() {
|
||||
$(hoverSelector).hide();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@
|
|||
});
|
||||
|
||||
$(hoverSelector).append('<h2>Session Detail</h2>' + sessionHtml);
|
||||
toggleActionButtons();
|
||||
})
|
||||
.fail(function(xhr) {
|
||||
if(xhr.status >= 500) {
|
||||
|
|
@ -64,6 +65,13 @@
|
|||
});
|
||||
};
|
||||
|
||||
function toggleActionButtons() {
|
||||
if (!context.JK.currentUserId) {
|
||||
$("#btnLike", hoverSelector).hide();
|
||||
$("#btnShare", hoverSelector).hide();
|
||||
}
|
||||
}
|
||||
|
||||
this.hideBubble = function() {
|
||||
$(hoverSelector).hide();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -451,7 +451,7 @@
|
|||
});
|
||||
}
|
||||
|
||||
function removeLike(options) {
|
||||
function removeLike(likableId, options) {
|
||||
var id = getId(options);
|
||||
return $.ajax({
|
||||
type: "DELETE",
|
||||
|
|
@ -476,15 +476,13 @@
|
|||
});
|
||||
}
|
||||
|
||||
function removeFollowing(options) {
|
||||
function removeFollowing(followableId, options) {
|
||||
var id = getId(options);
|
||||
|
||||
return $.ajax({
|
||||
type: "DELETE",
|
||||
dataType: "json",
|
||||
contentType: 'application/json',
|
||||
url: "/api/users/" + id + "/followings",
|
||||
data: JSON.stringify(options),
|
||||
url: "/api/users/" + id + "/followings/" + followableId,
|
||||
processData: false
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -198,12 +198,13 @@
|
|||
* Generic error handler for Ajax calls.
|
||||
*/
|
||||
function ajaxError(jqXHR, textStatus, errorMessage) {
|
||||
logger.error("Unexpected ajax error: " + textStatus);
|
||||
|
||||
if (jqXHR.status == 404) {
|
||||
logger.error("Unexpected ajax error: " + textStatus + ", msg:" + errorMessage);
|
||||
app.notify({title: "Oops!", text: "What you were looking for is gone now."});
|
||||
}
|
||||
else if (jqXHR.status = 422) {
|
||||
logger.error("Unexpected ajax error: " + textStatus + ", msg: " + errorMessage + ", response: " + jqXHR.responseText);
|
||||
// present a nicer message
|
||||
try {
|
||||
var text = "<ul>";
|
||||
|
|
@ -231,6 +232,7 @@
|
|||
}
|
||||
}
|
||||
else {
|
||||
|
||||
app.notify({title: textStatus, text: errorMessage, detail: jqXHR.responseText});
|
||||
}
|
||||
}
|
||||
|
|
@ -291,6 +293,7 @@
|
|||
if (jqXHR.status == 422) {
|
||||
var errors = JSON.parse(jqXHR.responseText);
|
||||
var $errors = context.JK.format_all_errors(errors);
|
||||
console.log("$errors", $errors)
|
||||
this.notify({title: title, text: $errors, icon_url: "/assets/content/icon_alert_big.png"})
|
||||
}
|
||||
else {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,416 @@
|
|||
(function(context, $) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* when session ends
|
||||
* - in chrome:
|
||||
* onsuspend
|
||||
* onpause
|
||||
* onended
|
||||
* - in firefox
|
||||
* onstalled
|
||||
* onplaying
|
||||
* onsuspend
|
||||
* onpause
|
||||
* onended
|
||||
*/
|
||||
context.JK = context.JK || {};
|
||||
|
||||
// the purpose of this code is to simply the interaction between user and server
|
||||
// it provides methods to call on user events (primarily play/pause), and will fire
|
||||
// events (such as 'stream_gone'). This element does nothing with UI; only fires events for you to handle
|
||||
|
||||
// $parentElement: the parent element needs to contain one audio element; this will take control of it.
|
||||
// pass in music_session as a 'data-music-session' attribute on the $parentElement
|
||||
|
||||
// this will also interact with any REST APIs needed to
|
||||
context.JK.ListenBroadcast = function($parentElement, options){
|
||||
|
||||
var WAIT_FOR_BUFFER_TIMEOUT = 5000;
|
||||
var RETRY_ATTEMPTS = 5; // we try 4 times, so the user will wait up until RETRY_ATTEMPTS * WAIT_FOR_BUFFER_TIMEOUTS
|
||||
|
||||
|
||||
var logger = context.JK.logger;
|
||||
var rest = context.JK.Rest();
|
||||
var $parent = $parentElement;
|
||||
var $audio = null;
|
||||
var audioDomElement = null;
|
||||
var musicSessionId = null;
|
||||
var waitForBufferingTimeout = null;
|
||||
var retryAttempts = 0;
|
||||
var self = this;
|
||||
|
||||
var destroyed = false;
|
||||
|
||||
var PlayStateNone = 'none';
|
||||
var PlayStateInitializing = 'initializing'; // user clicked play--nothing has happened yet
|
||||
var PlayStateBuffering = 'buffering'; // user clicked play--the stream is being read, buffer is being built
|
||||
var PlayStatePlaying = 'playing'; // we are playing
|
||||
var PlayStateStalled = 'stalled'; // we were playing, but the stream is stalled
|
||||
var PlayStateEnded = 'ended'; // we were playing, but the stream ended
|
||||
var PlayStateRetrying = 'retrying_play'; // we are retrying to play.
|
||||
var PlayStateFailedStart = 'failed_start'; // we could not start the stream. no more events are coming
|
||||
var PlayStateFailedPlaying = 'failed_playing'; // failed while playing.
|
||||
var PlayStateSessionOver = 'session_over'; // session is done
|
||||
var PlayStateNetworkError = 'network_error'; // network error
|
||||
var PlayStateServerError = 'server_error'; // server error
|
||||
var PlayStateConcurrentStop = 'concurrent_stop'; // stopped by another play attempt
|
||||
|
||||
var playState = PlayStateNone; // tracks if the stream is actually playing
|
||||
|
||||
function play(e) {
|
||||
if(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
if(destroyed) return;
|
||||
|
||||
if(!audioDomElement) throw "no audio element supplied; the user should not be able to attempt a play"
|
||||
|
||||
if(context.JK.ListenBroadcastCurrentlyPlaying) {
|
||||
context.JK.ListenBroadcastCurrentlyPlaying.forcedPause();
|
||||
}
|
||||
|
||||
context.JK.ListenBroadcastCurrentlyPlaying = self;
|
||||
|
||||
checkServer()
|
||||
.done(function(response) {
|
||||
audioDomElement.play();
|
||||
|
||||
retryAttempts = 0;
|
||||
|
||||
transition(PlayStateInitializing);
|
||||
|
||||
// keep this after transition, because any transition clears this timer
|
||||
waitForBufferingTimeout = setTimeout(noBuffer, WAIT_FOR_BUFFER_TIMEOUT);
|
||||
})
|
||||
}
|
||||
|
||||
function forcedPause() {
|
||||
transition(PlayStateConcurrentStop);
|
||||
pause();
|
||||
}
|
||||
|
||||
function pause(e) {
|
||||
if(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
if(destroyed) return;
|
||||
|
||||
if(!audioDomElement) throw "no audio element supplied; the user should not be able to attempt a pause"
|
||||
|
||||
transition(PlayStateNone);
|
||||
|
||||
recreateAudioElement();
|
||||
}
|
||||
|
||||
function destroy() {
|
||||
if(!destroyed) {
|
||||
$audio.remove();
|
||||
$audio = null;
|
||||
audioDomElement = null;
|
||||
destroyed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// this is the only way to make audio stop buffering after the user hits pause
|
||||
function recreateAudioElement() {
|
||||
// jeez: http://stackoverflow.com/questions/4071872/html5-video-force-abort-of-buffering/13302599#13302599
|
||||
var originalSource = $audio.html()
|
||||
audioDomElement.pause();
|
||||
audioDomElement.src = '';
|
||||
audioDomElement.load();
|
||||
var $parent = $audio.parent();
|
||||
$audio.remove();
|
||||
$parent.append('<audio preload="none"></audio>');
|
||||
$audio = $('audio', $parent);
|
||||
$audio.append(originalSource);
|
||||
audioDomElement = $audio.get(0);
|
||||
audioBind();
|
||||
}
|
||||
|
||||
function clearBufferTimeout() {
|
||||
if(waitForBufferingTimeout) {
|
||||
clearTimeout (waitForBufferingTimeout);
|
||||
waitForBufferingTimeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
function transition(newState) {
|
||||
logger.debug("transitioning from " + playState + " to " + newState);
|
||||
|
||||
playState = newState;
|
||||
|
||||
clearBufferTimeout();
|
||||
|
||||
if( playState == PlayStateNone ||
|
||||
playState == PlayStateEnded ||
|
||||
playState == PlayStateFailedStart ||
|
||||
playState == PlayStateFailedPlaying ||
|
||||
playState == PlayStateSessionOver ||
|
||||
playState == PlayStateNetworkError ||
|
||||
playState == PlayStateServerError ) {
|
||||
context.JK.ListenBroadcastCurrentlyPlaying = null;
|
||||
}
|
||||
|
||||
triggerStateChange();
|
||||
}
|
||||
|
||||
function noBuffer() {
|
||||
|
||||
if(retryAttempts >= RETRY_ATTEMPTS) {
|
||||
logger.debug("never received indication of buffering or playing");
|
||||
transition(PlayStateFailedStart);
|
||||
}
|
||||
else {
|
||||
retryAttempts++;
|
||||
|
||||
clearBufferTimeout();
|
||||
|
||||
// tell audio to stop/start, in attempt to retry
|
||||
//audioDomElement.stop();
|
||||
audioDomElement.load();
|
||||
audioDomElement.play();
|
||||
|
||||
transition(PlayStateRetrying);
|
||||
|
||||
waitForBufferingTimeout = setTimeout(noBuffer, WAIT_FOR_BUFFER_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
function isNoisyEvent(eventName) {
|
||||
if(playState == PlayStateNone) {
|
||||
console.log("ignoring: " + eventName)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function onPlay() {
|
||||
// this just confirms that the user tried to play
|
||||
}
|
||||
|
||||
function onPlaying() {
|
||||
if(isNoisyEvent('playing')) return;
|
||||
logger.debug("playing", arguments);
|
||||
|
||||
transition(PlayStatePlaying);
|
||||
}
|
||||
|
||||
function onPause() {
|
||||
if(isNoisyEvent('pause')) return;
|
||||
logger.debug("pause", arguments);
|
||||
|
||||
transition(PlayStateStalled);
|
||||
}
|
||||
|
||||
function onError() {
|
||||
if(isNoisyEvent('error')) return;
|
||||
logger.debug("error", arguments);
|
||||
|
||||
if(playState == PlayStatePlaying || playState == PlayStateStalled) {
|
||||
transition(PlayStateFailedPlaying);
|
||||
}
|
||||
else {
|
||||
transition(PlayStateFailedStart);
|
||||
}
|
||||
}
|
||||
|
||||
function onEnded() {
|
||||
if(isNoisyEvent('ended')) return;
|
||||
logger.debug("ended", arguments);
|
||||
|
||||
transition(PlayStateEnded);
|
||||
}
|
||||
|
||||
function onEmptied() {
|
||||
if(isNoisyEvent('emptied')) return;
|
||||
logger.debug("emptied", arguments);
|
||||
}
|
||||
|
||||
function onAbort() {
|
||||
if(isNoisyEvent('abort')) return;
|
||||
logger.debug("abort", arguments);
|
||||
}
|
||||
|
||||
function onStalled() {
|
||||
if(isNoisyEvent('stalled')) return;
|
||||
logger.debug("stalled", arguments);
|
||||
|
||||
// fires in Chrome on page load
|
||||
|
||||
if(playState == PlayStateBuffering || playState == PlayStatePlaying) {
|
||||
transition(PlayStateStalled);
|
||||
}
|
||||
}
|
||||
|
||||
function onSuspend() {
|
||||
if(isNoisyEvent('suspend')) return;
|
||||
logger.debug("onsuspend", arguments);
|
||||
|
||||
// fires in FF on page load
|
||||
|
||||
transition(PlayStateStalled);
|
||||
}
|
||||
|
||||
function onTimeUpdate() {
|
||||
//logger.debug("ontimeupdate", arguments);
|
||||
}
|
||||
|
||||
function onProgress() {
|
||||
if(isNoisyEvent('progress')) return;
|
||||
|
||||
if(playState == PlayStateInitializing) {
|
||||
transition(PlayStateBuffering);
|
||||
}
|
||||
}
|
||||
|
||||
function checkServer() {
|
||||
return rest.getSession(musicSessionId)
|
||||
.fail(function(jqXHR) {
|
||||
if(jqXHR.status == 404 || jqXHR.status == 403) {
|
||||
transition(PlayStateSessionOver);
|
||||
destroy();
|
||||
}
|
||||
else if(jqXHR.status >= 500 && jqXHR.status <= 599){
|
||||
transition(PlayStateServerError);
|
||||
}
|
||||
else {
|
||||
transition(PlayStateNetworkError);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function triggerStateChange() {
|
||||
|
||||
var isEnd = false;
|
||||
var isSessionOver = false;
|
||||
var displayText = null;
|
||||
var refresh = false;
|
||||
|
||||
if(playState == 'none') {
|
||||
//$status.text('SESSION IN PROGRESS');
|
||||
}
|
||||
else if(playState == 'initializing') {
|
||||
displayText = 'PREPARING AUDIO';
|
||||
}
|
||||
else if(playState == 'buffering') {
|
||||
|
||||
}
|
||||
else if(playState == 'playing') {
|
||||
displayText = 'SESSION IN PROGRESS';
|
||||
}
|
||||
else if(playState == 'stalled') {
|
||||
displayText = 'RECONNECTING';
|
||||
}
|
||||
else if(playState == 'ended') {
|
||||
displayText = 'STREAM DISCONNECTED';
|
||||
isEnd = true;
|
||||
refresh = true;
|
||||
}
|
||||
else if(playState == 'session_over') {
|
||||
displayText = 'SESSION ENDED';
|
||||
isEnd = true;
|
||||
isSessionOver = true;
|
||||
}
|
||||
else if(playState == 'retrying_play') {
|
||||
if(retryAttempts == 2) {
|
||||
displayText = 'STILL TRYING, HANG ON';
|
||||
}
|
||||
}
|
||||
else if(playState == 'failed_start') {
|
||||
displayText = 'AUDIO DID NOT START';
|
||||
isEnd = true;
|
||||
}
|
||||
else if(playState == 'failed_playing') {
|
||||
displayText = 'AUDIO FAILED';
|
||||
isEnd = true;
|
||||
refresh = true;
|
||||
}
|
||||
else if(playState == 'network_error') {
|
||||
displayText = 'STREAM DISCONNECTED';
|
||||
isEnd = true;
|
||||
}
|
||||
else if(playState == 'server_error') {
|
||||
displayText = 'SERVER ERROR';
|
||||
isEnd = true;
|
||||
}
|
||||
else if(playState == 'concurrent_stop') {
|
||||
displayText = 'SESSION IN PROGRESS';
|
||||
isEnd = true;
|
||||
}
|
||||
else {
|
||||
logger.error("unknown state: " + playState)
|
||||
}
|
||||
|
||||
$parent.triggerHandler('statechange.listenBroadcast',
|
||||
{
|
||||
state: playState,
|
||||
displayText: displayText,
|
||||
isEnd: isEnd,
|
||||
isSessionOver: isSessionOver
|
||||
})
|
||||
|
||||
// we have cause to believe the session is done; check against the server
|
||||
if(refresh) {
|
||||
checkServer();
|
||||
}
|
||||
}
|
||||
|
||||
function audioBind() {
|
||||
$audio.bind('play', onPlay);
|
||||
$audio.bind('playing', onPlaying);
|
||||
$audio.bind('error', onError);
|
||||
$audio.bind('emptied', onEmptied);
|
||||
$audio.bind('abort', onAbort);
|
||||
$audio.bind('ended', onEnded);
|
||||
$audio.bind('pause', onPause);
|
||||
$audio.bind('suspend', onSuspend);
|
||||
$audio.bind('stalled', onStalled);
|
||||
$audio.bind('timeupdate', onTimeUpdate);
|
||||
$audio.bind('progress', onProgress);
|
||||
|
||||
}
|
||||
function initialize() {
|
||||
|
||||
musicSessionId = $parent.attr('data-music-session');
|
||||
if(!musicSessionId) throw "data-music-session must be specified on $parentElement";
|
||||
|
||||
$audio = $('audio', $parent);
|
||||
|
||||
if($audio.length == 0) {
|
||||
logger.debug("listen_broadcast: no audio element. deactivating")
|
||||
return;
|
||||
}
|
||||
if($audio.length > 1) {
|
||||
throw "more than one <audio> element found";
|
||||
}
|
||||
|
||||
audioDomElement = $audio.get(0);
|
||||
audioBind();
|
||||
|
||||
$parent.bind('play.listenBroadcast', play);
|
||||
$parent.bind('pause.listenBroadcast', pause);
|
||||
$parent.bind('destroy.listenBroadcast', destroy);
|
||||
}
|
||||
|
||||
initialize();
|
||||
|
||||
this.play = play;
|
||||
this.pause = pause;
|
||||
this.forcedPause = forcedPause; // meant only be called by this code; not from external
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
context.JK.ListenBroadcastCurrentlyPlaying = null;
|
||||
|
||||
$.fn.listenBroadcast = function(options) {
|
||||
new context.JK.ListenBroadcast(this, options);
|
||||
}
|
||||
})(window, jQuery);
|
||||
|
|
@ -456,6 +456,8 @@
|
|||
|
||||
function onHashChange(e, postFunction) {
|
||||
|
||||
if(currentHash == context.location.hash) { return }
|
||||
|
||||
if(resettingHash) {
|
||||
resettingHash = false;
|
||||
e.preventDefault();
|
||||
|
|
@ -474,7 +476,7 @@
|
|||
var accepted = screenEvent(currentScreen, 'beforeLeave', {screen:screen, hash: context.location.hash});
|
||||
if(accepted === false) {
|
||||
console.log("navigation to " + context.location.hash + " rejected by " + currentScreen);
|
||||
resettingHash = true;
|
||||
//resettingHash = true;
|
||||
// reset the hash to where it just was
|
||||
context.location.hash = currentHash;
|
||||
}
|
||||
|
|
@ -500,6 +502,8 @@
|
|||
var accepted = screenEvent(previousScreen, 'beforeHide', data);
|
||||
if(accepted === false) return;
|
||||
|
||||
logger.debug("Changing screen to " + currentScreen);
|
||||
|
||||
screenEvent(currentScreen, 'beforeShow', data);
|
||||
|
||||
// For now -- it seems we want it open always.
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -117,23 +117,23 @@
|
|||
selector = isSidebar ? '#sidebar-search-results' : '#search-results';
|
||||
$(selector).append(invitationSentHtml);
|
||||
|
||||
// wire up button click handler if search result is not a friend or the current use
|
||||
// wire up button click handler if search result is not a friend or the current user
|
||||
if (isSidebar) {
|
||||
var $sidebar = $('div[layout=sidebar] div[user-id=' + val.id + ']');
|
||||
if (!val.is_friend && val.id !== context.JK.currentUserId) {
|
||||
$sidebar.find('.btn-connect-friend').click(sendFriendRequest);
|
||||
if (val.is_friend || val.pending_friend_request || val.id === context.JK.currentUserId) {
|
||||
// hide the button if the search result is already a friend
|
||||
$sidebar.find('.btn-connect-friend').hide();
|
||||
}
|
||||
else {
|
||||
// hide the button if the search result is already a friend
|
||||
$sidebar.find('.btn-connect-friend').hide();
|
||||
$sidebar.find('.btn-connect-friend').click(sendFriendRequest);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!val.is_friend && val.id !== context.JK.currentUserId) {
|
||||
$('div[user-id=' + val.id + ']').find('.btn-connect-friend').click(sendFriendRequest);
|
||||
if (val.is_friend || val.pending_friend_request || val.id === context.JK.currentUserId) {
|
||||
$('div[user-id=' + val.id + ']').find('.btn-connect-friend').hide();
|
||||
}
|
||||
else {
|
||||
$('div[user-id=' + val.id + ']').find('.btn-connect-friend').hide();
|
||||
$('div[user-id=' + val.id + ']').find('.btn-connect-friend').click(sendFriendRequest);
|
||||
}
|
||||
}
|
||||
resultDivVisibility(val, isSidebar);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
var playbackControls = null;
|
||||
var promptLeave = false;
|
||||
|
||||
var rest = JK.Rest();
|
||||
var rest = context.JK.Rest();
|
||||
|
||||
var RENDER_SESSION_DELAY = 750; // When I need to render a session, I have to wait a bit for the mixers to be there.
|
||||
|
||||
|
|
@ -279,6 +279,10 @@
|
|||
// a client can only be in one session at a time,
|
||||
// and other parts of the code want to know at any certain times
|
||||
// about the current session, if any (for example, reconnect logic)
|
||||
if(context.JK.CurrentSessionModel) {
|
||||
context.JK.CurrentSessionModel.unsubscribe();
|
||||
}
|
||||
|
||||
context.JK.CurrentSessionModel = sessionModel = new context.JK.SessionModel(
|
||||
context.JK.app,
|
||||
context.JK.JamServer,
|
||||
|
|
@ -432,6 +436,7 @@
|
|||
});
|
||||
}
|
||||
|
||||
// not leave session but leave screen
|
||||
function beforeLeave(data) {
|
||||
if(promptLeave) {
|
||||
var leaveSessionWarningDialog = new context.JK.LeaveSessionWarningDialog(context.JK.app,
|
||||
|
|
@ -441,6 +446,7 @@
|
|||
app.layout.showDialog('leave-session-warning');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function beforeHide(data) {
|
||||
|
|
@ -1209,6 +1215,15 @@
|
|||
}
|
||||
}
|
||||
|
||||
function sessionLeave(evt) {
|
||||
evt.preventDefault();
|
||||
|
||||
promptLeave = false;
|
||||
context.window.location = '/client#/home';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function sessionResync(evt) {
|
||||
evt.preventDefault();
|
||||
var response = context.jamClient.SessionAudioResync();
|
||||
|
|
@ -1388,7 +1403,8 @@
|
|||
}
|
||||
|
||||
function events() {
|
||||
$('#session-resync').on('click', sessionResync);
|
||||
$('#session-leave').on('click', sessionLeave);
|
||||
$('#session-resync').on('click', sessionResync);
|
||||
$('#session-contents').on("click", '[action="delete"]', deleteSession);
|
||||
$('#tracks').on('click', 'div[control="mute"]', toggleMute);
|
||||
$('#recording-start-stop').on('click', startStopRecording);
|
||||
|
|
|
|||
|
|
@ -74,7 +74,6 @@
|
|||
deferred
|
||||
.done(function(){
|
||||
logger.debug("calling jamClient.JoinSession");
|
||||
|
||||
if(!alreadyInSession()) {
|
||||
// on temporary disconnect scenarios, a user may already be in a session when they enter this path
|
||||
// so we avoid double counting
|
||||
|
|
@ -101,12 +100,17 @@
|
|||
logger.debug("SessionModel.leaveCurrentSession()");
|
||||
// TODO - sessionChanged will be called with currentSession = null
|
||||
server.unregisterMessageCallback(context.JK.MessageType.MUSICIAN_SESSION_JOIN, refreshCurrentSession);
|
||||
server.unregisterMessageCallback(context.JK.MessageType.MUSICIAN_SESSION_DEPART, refreshCurrentSession);
|
||||
server.unregisterMessageCallback(context.JK.MessageType.SESSION_JOIN, refreshCurrentSession);
|
||||
server.unregisterMessageCallback(context.JK.MessageType.SESSION_DEPART, refreshCurrentSession);
|
||||
|
||||
//server.unregisterMessageCallback(context.JK.MessageType.MUSICIAN_SESSION_DEPART, refreshCurrentSession);
|
||||
// leave the session right away without waiting on REST. Why? If you can't contact the server, or if it takes a long
|
||||
// time, for that entire duration you'll still be sending voice data to the other users.
|
||||
// this may be bad if someone decides to badmouth others in the left-session during this time
|
||||
logger.debug("calling jamClient.LeaveSession for clientId=" + clientId);
|
||||
console.time('jamClient.LeaveSession');
|
||||
client.LeaveSession({ sessionID: currentSessionId });
|
||||
console.timeEnd('jamClient.LeaveSession');
|
||||
leaveSessionRest(currentSessionId)
|
||||
.done(function() {
|
||||
sessionChanged();
|
||||
|
|
@ -144,7 +148,7 @@
|
|||
*/
|
||||
function refreshCurrentSession() {
|
||||
// XXX use backend instead: https://jamkazam.atlassian.net/browse/VRFS-854
|
||||
logger.debug("SessionModel.refreshCurrentSession()");
|
||||
logger.debug("SessionModel.refreshCurrentSession(" + currentSessionId +")");
|
||||
refreshCurrentSessionRest(sessionChanged);
|
||||
}
|
||||
|
||||
|
|
@ -172,6 +176,7 @@
|
|||
if(sessionData != null) {
|
||||
currentOrLastSession = sessionData;
|
||||
}
|
||||
|
||||
currentSession = sessionData;
|
||||
}
|
||||
|
||||
|
|
@ -190,7 +195,6 @@
|
|||
$.ajax({
|
||||
type: "GET",
|
||||
url: url,
|
||||
async: false,
|
||||
success: function(response) {
|
||||
sendClientParticipantChanges(currentSession, response);
|
||||
updateCurrentSession(response);
|
||||
|
|
@ -198,7 +202,7 @@
|
|||
callback();
|
||||
}
|
||||
},
|
||||
error: ajaxError,
|
||||
error: function(jqXHR) { app.notifyServerError(jqXHR, "Unable to refresh session data") },
|
||||
complete: function() {
|
||||
requestingSessionRefresh = false;
|
||||
if(pendingSessionRefresh) {
|
||||
|
|
@ -301,7 +305,7 @@
|
|||
logger.debug("successfully updated tracks on the server");
|
||||
//refreshCurrentSession();
|
||||
},
|
||||
error: ajaxError
|
||||
error: function(jqXHR) { app.notifyServerError(jqXHR, "Unable to refresh session data") }
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -318,7 +322,7 @@
|
|||
success: function(response) {
|
||||
logger.debug("Successfully updated track info (" + JSON.stringify(data) + ")");
|
||||
},
|
||||
error: ajaxError
|
||||
error: function(jqXHR) { app.notifyServerError(jqXHR, "Unable to refresh session data") }
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -379,8 +383,7 @@
|
|||
var url = "/api/participants/" + clientId;
|
||||
return $.ajax({
|
||||
type: "DELETE",
|
||||
url: url,
|
||||
async: true
|
||||
url: url
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -447,10 +450,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
function ajaxError(jqXHR, textStatus, errorMessage) {
|
||||
logger.error("Unexpected ajax error: " + textStatus);
|
||||
}
|
||||
|
||||
// returns a deferred object
|
||||
function findUserBy(finder) {
|
||||
if(finder.clientId) {
|
||||
|
|
@ -494,6 +493,12 @@
|
|||
this.getCurrentOrLastSession = function() {
|
||||
return currentOrLastSession;
|
||||
};
|
||||
this.unsubscribe = function() {
|
||||
server.unregisterMessageCallback(context.JK.MessageType.MUSICIAN_SESSION_JOIN, refreshCurrentSession);
|
||||
server.unregisterMessageCallback(context.JK.MessageType.SESSION_JOIN, refreshCurrentSession);
|
||||
server.unregisterMessageCallback(context.JK.MessageType.SESSION_DEPART, refreshCurrentSession);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
})(window,jQuery);
|
||||
|
|
@ -12,13 +12,6 @@
|
|||
var entity = null;
|
||||
var remainingCap = 140 - 22 - 1; // 140 tweet max, minus 22 for link size, minus 1 for space
|
||||
|
||||
var textMap = {
|
||||
LIVE_SESSION: "LIVE SESSION",
|
||||
SESSION: "SESSION",
|
||||
RECORDING: "RECORDING",
|
||||
RECORDED: "RECORDED"
|
||||
};
|
||||
|
||||
function showSpinner() {
|
||||
$(dialogId + ' .dialog-inner').hide();
|
||||
var spinner = $('<div class="spinner spinner-large"></div>')
|
||||
|
|
|
|||
|
|
@ -754,7 +754,7 @@
|
|||
|
||||
function listenToSession(args) {
|
||||
deleteNotification(args.notification_id);
|
||||
context.JK.popExternalLink('/recordings/' + args.session_id);
|
||||
context.JK.popExternalLink('/sessions/' + args.session_id);
|
||||
}
|
||||
|
||||
function registerMusicianRecordingSaved() {
|
||||
|
|
|
|||
|
|
@ -76,8 +76,8 @@
|
|||
|
||||
$.each(context._.keys(icon_map_base), function(index, instrumentId) {
|
||||
var icon = icon_map_base[instrumentId];
|
||||
instrumentIconMap24[instrumentId] = "../assets/content/icon_instrument_" + icon + "24.png";
|
||||
instrumentIconMap45[instrumentId] = "../assets/content/icon_instrument_" + icon + "45.png";
|
||||
instrumentIconMap24[instrumentId] = "/assets/content/icon_instrument_" + icon + "24.png";
|
||||
instrumentIconMap45[instrumentId] = "/assets/content/icon_instrument_" + icon + "45.png";
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
@ -224,7 +224,7 @@
|
|||
}
|
||||
|
||||
context.JK.fetchUserNetworkOrServerFailure = function() {
|
||||
app.notify({
|
||||
JK.app.notify({
|
||||
title: "Unable to communicate with server",
|
||||
text: "Please try again later",
|
||||
icon_url: "/assets/content/icon_alert_big.png"
|
||||
|
|
@ -232,19 +232,23 @@
|
|||
}
|
||||
|
||||
context.JK.entityNotFound = function(type) {
|
||||
app.notify({
|
||||
JK.app.notify({
|
||||
title: type + " Deleted",
|
||||
text: "The " + type + " no longer exists.",
|
||||
icon_url: "/assets/content/icon_alert_big.png"
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Uber-simple templating
|
||||
// var template = "Hey {name}";
|
||||
// var vals = { name: "Jon" };
|
||||
// _fillTemplate(template, vals);
|
||||
// --> "Hey Jon"
|
||||
//
|
||||
// use context._.template for something more powerful
|
||||
context.JK.fillTemplate = function(template, vals) {
|
||||
if(template == null) throw 'null template in fillTemplate'
|
||||
for(var val in vals)
|
||||
template=template.replace(new RegExp('{'+val+'}','g'), vals[val]);
|
||||
return template;
|
||||
|
|
@ -282,6 +286,19 @@
|
|||
return instrumentIconMap45["default"];
|
||||
};
|
||||
|
||||
// meant to pass in a bunch of images with an instrument-id attribute on them.
|
||||
|
||||
context.JK.setInstrumentAssetPath = function($elements) {
|
||||
$.each($elements, function(index, item) {
|
||||
var $element = $(this);
|
||||
if(!$element.is('img')) { throw "expected to receive an <img> in setInstrumentAssetPath" }
|
||||
|
||||
var instrument = $element.attr('instrument-id');
|
||||
|
||||
$element.attr('src', context.JK.getInstrumentIcon24(instrument))
|
||||
})
|
||||
}
|
||||
|
||||
context.JK.listInstruments = function(app, callback) {
|
||||
var url = "/api/instruments";
|
||||
$.ajax({
|
||||
|
|
@ -361,6 +378,13 @@
|
|||
return date.toLocaleTimeString();
|
||||
}
|
||||
|
||||
context.JK.prettyPrintElements = function($elements) {
|
||||
$.each($elements, function(index, item) {
|
||||
var $item = $(item);
|
||||
$item.text(context.JK.prettyPrintSeconds(parseInt($item.attr('duration'))))
|
||||
});
|
||||
}
|
||||
|
||||
context.JK.prettyPrintSeconds = function(seconds) {
|
||||
// from: http://stackoverflow.com/questions/3733227/javascript-seconds-to-minutes-and-seconds
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
var rest = new JK.Rest();
|
||||
var sessionId = null;
|
||||
|
||||
|
||||
function like() {
|
||||
rest.addSessionLike(sessionId, JK.currentUserId)
|
||||
.done(function(response) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@
|
|||
//= require jquery.easydropdown
|
||||
//= require jquery.carousel-1.1
|
||||
//= require jquery.mousewheel-3.1.9
|
||||
//= require jquery.timeago
|
||||
//= require jquery.dotdotdot
|
||||
//= require jquery.listenbroadcast
|
||||
//= require AAA_Log
|
||||
//= require AAC_underscore
|
||||
//= require globals
|
||||
|
|
@ -19,6 +22,8 @@
|
|||
//= require web/videoDialog
|
||||
//= require invitationDialog
|
||||
//= require hoverMusician
|
||||
//= require feed_item_recording
|
||||
//= require feed_item_session
|
||||
//= require hoverFan
|
||||
//= require hoverBand
|
||||
//= require hoverSession
|
||||
|
|
|
|||
|
|
@ -4,7 +4,12 @@
|
|||
|
||||
context.JK = context.JK || {};
|
||||
|
||||
var welcomeRoot;
|
||||
|
||||
function initialize() {
|
||||
|
||||
welcomeRoot = $('.landing-content .wrapper .welcome')
|
||||
|
||||
$('#signup').click(function(e) {
|
||||
context.JK.app.layout.showDialog('signup-dialog');
|
||||
e.preventDefault();
|
||||
|
|
@ -32,8 +37,19 @@
|
|||
backOpacity:2
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
$.each($('.feed-entry'), function(index, feedEntry) {
|
||||
var $feedEntry = $(this);
|
||||
if($feedEntry.is('.recording-entry')) {
|
||||
new context.JK.FeedItemRecording($feedEntry);
|
||||
}
|
||||
else {
|
||||
new context.JK.FeedItemSession($feedEntry);
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
context.JK.WelcomePage = initialize;
|
||||
|
||||
|
|
|
|||
|
|
@ -37,5 +37,18 @@ $border: hsl(210, 50%, 45%);
|
|||
$narrow-screen: 1000px; // 990 ? 1000 ?
|
||||
$short-screen: 600px; // toolbars / chrome for x768
|
||||
|
||||
@mixin border_box_sizing {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@mixin content_box_sizing {
|
||||
-webkit-box-sizing: content-box;
|
||||
-moz-box-sizing: content-box;
|
||||
-ms-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ a.arrow-down {
|
|||
}
|
||||
|
||||
.ftue-background {
|
||||
background-image:url(../images/content/bkg_ftue.jpg);
|
||||
background-image:url(../content/bkg_ftue.jpg);
|
||||
background-repeat:no-repeat;
|
||||
background-size:cover;
|
||||
min-height:475px;
|
||||
|
|
|
|||
|
|
@ -380,7 +380,7 @@ a.arrow-down {
|
|||
}
|
||||
|
||||
.ftue-background {
|
||||
background-image:url(../images/content/bkg_ftue.jpg);
|
||||
background-image:url(../content/bkg_ftue.jpg);
|
||||
background-repeat:no-repeat;
|
||||
background-size:cover;
|
||||
min-height:475px;
|
||||
|
|
@ -476,6 +476,17 @@ ul.shortcuts {
|
|||
padding:2px 8px !important;
|
||||
}
|
||||
|
||||
|
||||
a.arrow-down-orange {
|
||||
margin-left:5px;
|
||||
margin-top:4px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-top: 4px solid #ED3618;
|
||||
}
|
||||
|
||||
.whitespace {
|
||||
white-space:normal;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
@charset "UTF-8";
|
||||
@import "compass/utilities/text/replacement";
|
||||
@import "compass/typography/text/replacement";
|
||||
|
||||
.header {
|
||||
height: 55px;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
@import "client/common.css.scss";
|
||||
|
||||
#user-profile {
|
||||
.profile-about-right {
|
||||
|
||||
textarea {
|
||||
width:100%;
|
||||
height:150px;
|
||||
padding:0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.profile-head {
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,6 +122,9 @@
|
|||
float:left;
|
||||
font-size:18px;
|
||||
margin-top:12px;
|
||||
|
||||
.friend-name-label {
|
||||
}
|
||||
}
|
||||
|
||||
li .friend-status {
|
||||
|
|
|
|||
|
|
@ -1,16 +1,10 @@
|
|||
@import "bootstrap";
|
||||
@import "client/common";
|
||||
|
||||
/* mixins, variables, etc. */
|
||||
|
||||
$grayMediumLight: #eaeaea;
|
||||
|
||||
@mixin box_sizing {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* universal */
|
||||
|
||||
html {
|
||||
|
|
@ -115,7 +109,7 @@ footer {
|
|||
float: left;
|
||||
width: 100%;
|
||||
margin-top: 45px;
|
||||
@include box_sizing;
|
||||
@include border_box_sizing;
|
||||
}
|
||||
|
||||
/* sidebar */
|
||||
|
|
@ -184,7 +178,7 @@ input, textarea, select, .uneditable-input {
|
|||
padding: 10px;
|
||||
height: auto;
|
||||
margin-bottom: 15px;
|
||||
@include box_sizing;
|
||||
@include border_box_sizing;
|
||||
}
|
||||
|
||||
/** MSC: did this because firefox clips text if it's padding:4px on text input fields */
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ body.jam {
|
|||
|
||||
|
||||
.dropdown-wrapper div.dropdown-container {
|
||||
position: absolute;
|
||||
position: relative;
|
||||
height: 0;
|
||||
left: -1px;
|
||||
top: 100%;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,4 @@
|
|||
@mixin box_sizing {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@import "client/common.css.scss";
|
||||
|
||||
html {
|
||||
min-height:100%;
|
||||
|
|
@ -276,7 +271,7 @@ body.web {
|
|||
input[type=text], input[type=password] {
|
||||
margin-top:1px;
|
||||
width:100%;
|
||||
@include box_sizing;
|
||||
@include border_box_sizing;
|
||||
}
|
||||
|
||||
select {
|
||||
|
|
|
|||
|
|
@ -55,4 +55,8 @@
|
|||
position:absolute;
|
||||
top:3px;
|
||||
right:4px;
|
||||
}
|
||||
|
||||
#btnPlayPause {
|
||||
position: relative;
|
||||
}
|
||||
|
|
@ -16,4 +16,8 @@
|
|||
font-size:15px;
|
||||
color:#cccc00;
|
||||
margin-left:20px;
|
||||
}*/
|
||||
}*/
|
||||
|
||||
#btnPlayPause {
|
||||
position: relative;
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
@charset "UTF-8";
|
||||
|
||||
@import "client/common.css.scss";
|
||||
|
||||
body.web {
|
||||
.welcome {
|
||||
.landing-tag {
|
||||
|
|
@ -13,6 +14,202 @@ body.web {
|
|||
}
|
||||
}
|
||||
|
||||
.session-controls {
|
||||
margin-top: 15px;
|
||||
padding: 3px 5px 3px 10px;
|
||||
width: 93%;
|
||||
min-width: 200px;
|
||||
background-color: #242323;
|
||||
position: relative;
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
@include border_box_sizing;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.recording-controls {
|
||||
margin-top: 15px;
|
||||
padding: 3px 5px 3px 10px;
|
||||
width: 93%;
|
||||
min-width: 200px;
|
||||
background-color: #242323;
|
||||
position: relative;
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
@include border_box_sizing;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.feed-entry {
|
||||
position:relative;
|
||||
display:block;
|
||||
white-space:nowrap;
|
||||
min-width:700px;
|
||||
border-bottom:solid 1px #666;
|
||||
max-height:74px;
|
||||
overflow:hidden;
|
||||
margin-top:20px;
|
||||
|
||||
&:nth-child(1) {
|
||||
margin-top:0;
|
||||
}
|
||||
|
||||
/**
|
||||
&.animate-down {
|
||||
-webkit-transition: max-height height 2s;
|
||||
transition: max-height height 2s;
|
||||
-moz-transition: max-height height 2s;
|
||||
-o-transition: max-height height 2s;
|
||||
-ms-transition: max-height height 2s;
|
||||
}
|
||||
|
||||
&.animate-up {
|
||||
-webkit-transition: max-height height .4s;
|
||||
transition: max-height height .4s;
|
||||
-moz-transition: max-height height .4s;
|
||||
-o-transition: max-height height .4s;
|
||||
-ms-transition: max-height height .4s;
|
||||
}
|
||||
*/
|
||||
|
||||
.session-status {
|
||||
float:left;
|
||||
font-size:18px;
|
||||
}
|
||||
|
||||
.inprogress {
|
||||
.session-status {
|
||||
font-size: 15px;
|
||||
color: #cccc00;
|
||||
margin-left:20px;
|
||||
}
|
||||
}
|
||||
|
||||
.recording-current {
|
||||
top:8px;
|
||||
}
|
||||
.session-duration {
|
||||
top:8px;
|
||||
}
|
||||
|
||||
.recording-controls, .session-controls {
|
||||
margin-top:0px;
|
||||
margin-bottom:5px;
|
||||
padding:8px 5px 8px 10px;
|
||||
width:98%;
|
||||
line-height:19px;
|
||||
}
|
||||
|
||||
.session-controls {
|
||||
&.ended {
|
||||
background-color: #471f18;
|
||||
|
||||
.play-button {
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
|
||||
&.inprogress {
|
||||
background-color: #4C742E;
|
||||
}
|
||||
}
|
||||
|
||||
.details {
|
||||
color:#ED3618;
|
||||
}
|
||||
|
||||
.avatar-small {
|
||||
@include content_box_sizing;
|
||||
margin-top:0px;
|
||||
margin-left:0px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size:16px;
|
||||
color:#999;
|
||||
margin-bottom:3px;
|
||||
}
|
||||
|
||||
.artist {
|
||||
font-size:12px;
|
||||
font-weight:bold;
|
||||
color:#ccc;
|
||||
margin-bottom:10px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-weight:bold;
|
||||
font-size:14px;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size:12px;
|
||||
white-space:normal;
|
||||
line-height:14px;
|
||||
overflow:hidden;
|
||||
text-overflow:ellipsis;
|
||||
height:60px;
|
||||
}
|
||||
|
||||
.feed-details {
|
||||
vertical-align:middle;
|
||||
|
||||
img {
|
||||
vertical-align:middle;
|
||||
}
|
||||
}
|
||||
|
||||
.play-count {
|
||||
margin-right:10px;
|
||||
}
|
||||
|
||||
.comment-count {
|
||||
margin-right:10px;
|
||||
}
|
||||
|
||||
.like-count {
|
||||
margin-right:10px;
|
||||
}
|
||||
|
||||
.musicians {
|
||||
margin-top:10px;
|
||||
font-size:11px;
|
||||
}
|
||||
|
||||
.musicians td {
|
||||
border-right:none;
|
||||
border-top:none;
|
||||
padding:3px;
|
||||
vertical-align:middle;
|
||||
}
|
||||
|
||||
.musicians a {
|
||||
color:#fff;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
.avatar-tiny {
|
||||
float:left;
|
||||
padding:1px;
|
||||
width:24px;
|
||||
height:24px;
|
||||
background-color:#ed3618;
|
||||
-webkit-border-radius:12px;
|
||||
-moz-border-radius:12px;
|
||||
border-radius:12px;
|
||||
}
|
||||
|
||||
.avatar-tiny img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
-webkit-border-radius:12px;
|
||||
-moz-border-radius:12px;
|
||||
border-radius:12px;
|
||||
}
|
||||
}
|
||||
|
||||
.buzz {
|
||||
width: 300px;
|
||||
position:relative;
|
||||
|
|
@ -33,18 +230,15 @@ body.web {
|
|||
width: 750px;
|
||||
position:relative;
|
||||
top:-65px;
|
||||
* {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.home-session-list {
|
||||
width:100%;
|
||||
height:400px;
|
||||
border: solid 1px #ed3718;
|
||||
background-color:#353535;
|
||||
float:left;
|
||||
overflow:hidden;
|
||||
position:relative;
|
||||
|
||||
}
|
||||
.latest-head {
|
||||
|
|
@ -53,10 +247,14 @@ body.web {
|
|||
height: 53px;
|
||||
width:inherit;
|
||||
}
|
||||
|
||||
.latest-body {
|
||||
height: 100%;
|
||||
width:100%;
|
||||
padding-top:53px;
|
||||
top:65px;
|
||||
bottom:0;
|
||||
position:absolute;
|
||||
overflow-y:scroll;
|
||||
@include border_box_sizing;
|
||||
|
||||
.session-list-wrapper {
|
||||
padding: 0 20px;
|
||||
|
|
@ -145,7 +343,7 @@ Version: 1.1
|
|||
top :133px;
|
||||
width :35px;
|
||||
height :35px;
|
||||
background : url(web/next_button.png) no-repeat center;
|
||||
background : url(next_button.png) no-repeat center;
|
||||
cursor :pointer ;
|
||||
z-index :9999;
|
||||
}
|
||||
|
|
@ -157,7 +355,7 @@ Version: 1.1
|
|||
top :133px;
|
||||
width :35px;
|
||||
height: 35px;
|
||||
background : url(../images/web/prev_button.png);
|
||||
background : url(prev_button.png);
|
||||
cursor :pointer ;
|
||||
z-index :9999;
|
||||
}
|
||||
|
|
@ -209,7 +407,7 @@ Version: 1.1
|
|||
float :left ;
|
||||
width :16px;
|
||||
height :16px;
|
||||
background : url(web/Bullet-White.png) no-repeat center ;
|
||||
background : url(Bullet-White.png) no-repeat center ;
|
||||
margin :5px;
|
||||
float :left ;
|
||||
cursor :pointer ;
|
||||
|
|
@ -217,12 +415,12 @@ Version: 1.1
|
|||
|
||||
.carousel .buttonNav .bullet:hover
|
||||
{
|
||||
background : url(../images/web/Bullet-Black.png) no-repeat center ;
|
||||
background : url(Bullet-Black.png) no-repeat center ;
|
||||
}
|
||||
|
||||
.carousel .buttonNav .bulletActive
|
||||
{
|
||||
background : url(web/Bullet-Black.png) no-repeat center ;
|
||||
background : url(Bullet-Black.png) no-repeat center ;
|
||||
cursor :default ;
|
||||
}
|
||||
|
||||
|
|
@ -254,7 +452,7 @@ Version: 1.1
|
|||
|
||||
.carousel .shadow .shadowLeft
|
||||
{
|
||||
background : url(web/shadowLeft.png) no-repeat;
|
||||
background : url(shadowLeft.png) no-repeat;
|
||||
width :100px;
|
||||
height :82px;
|
||||
|
||||
|
|
@ -266,7 +464,7 @@ Version: 1.1
|
|||
.carousel .shadow .shadowMiddle
|
||||
{
|
||||
height :82px;
|
||||
background:url(web/shadowTile.png) repeat-x;
|
||||
background:url(shadowTile.png) repeat-x;
|
||||
|
||||
/* fix png problems in ie */
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=../images/web/shadowTile.png, sizingmethod=scale)"; /* IE8 */
|
||||
|
|
@ -278,7 +476,7 @@ Version: 1.1
|
|||
{
|
||||
width :100px;
|
||||
height :82px;
|
||||
background:url(web/shadowRight.png) no-repeat;
|
||||
background:url(shadowRight.png) no-repeat;
|
||||
|
||||
/* fix png problems in ie */
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=../images/web/shadowRight.png, sizingmethod=scale)"; /* IE8 */
|
||||
|
|
|
|||
|
|
@ -33,8 +33,7 @@ class ApiController < ApplicationController
|
|||
|
||||
def respond_with_model(model, options = {})
|
||||
if model.errors.any?
|
||||
response.status = :unprocessable_entity
|
||||
respond_with model, layout: nil
|
||||
respond_with model, status: :unprocessable_entity, layout: nil
|
||||
else
|
||||
status = options[:new] && options[:new] == true ? 201 : 200
|
||||
redirect_on_success = options[:location]
|
||||
|
|
|
|||
|
|
@ -24,9 +24,18 @@ class ApiMixesController < ApiController
|
|||
|
||||
def download
|
||||
@mix = Mix.find(params[:id])
|
||||
raise PermissionError, "You can only download a mix you didn't claim" unless @mix.can_download? current_user
|
||||
raise PermissionError, "You can only download a mix you have claimed" unless @mix.can_download? current_user
|
||||
|
||||
redirect_to @mix.sign_url
|
||||
@mix.current_user = current_user
|
||||
@mix.update_download_count
|
||||
|
||||
@mix.valid?
|
||||
if !@mix.errors.any?
|
||||
@mix.save!
|
||||
redirect_to @mix.sign_url
|
||||
else
|
||||
render :json => { :message => "download limit surpassed" }, :status => 404
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -38,7 +38,18 @@ class ApiRecordingsController < ApiController
|
|||
def download
|
||||
raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR unless @recorded_track.can_download?(current_user)
|
||||
|
||||
redirect_to @recorded_track.sign_url
|
||||
@recorded_track.current_user = current_user
|
||||
@recorded_track.update_download_count
|
||||
|
||||
@recorded_track.valid?
|
||||
if !@recorded_track.errors.any?
|
||||
@recorded_track.save!
|
||||
redirect_to @recorded_track.sign_url
|
||||
else
|
||||
render :json => { :message => "download limit surpassed" }, :status => 404
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
def start
|
||||
|
|
|
|||
|
|
@ -1,24 +1,86 @@
|
|||
class ApiScoringController < ApiController
|
||||
|
||||
respond_to :json
|
||||
# todo before_filter :api_signed_in_user
|
||||
before_filter :api_signed_in_user
|
||||
|
||||
def work # clientid; returns another clientid
|
||||
client_id = params[:clientid]
|
||||
if client_id.nil? then render :json => {message: 'client_id not specified'}, :status => 400; return end
|
||||
|
||||
c = Connection.where(client_id: client_id).first
|
||||
if c.nil? then render :json => {message: 'connection not found'}, :status => 404; return end
|
||||
if !c.user.id.eql?(current_user.id) then render :json => {message: 'user does not own client_id'}, :status => 403; return end
|
||||
|
||||
def work # clientid returns another clientid
|
||||
# todo clientid should come from the connection record of the signed in user
|
||||
# todo this method is a stub
|
||||
render :json => {:clientid => [params[:clientid]+'peer']}, :status => 200
|
||||
#puts "ApiScoringController#work(#{client_id}) => locidispid #{c.locidispid}"
|
||||
result_client_id = JamRuby::GetWork.get_work(c.locidispid)
|
||||
#result_client_id = client_id+'peer'
|
||||
|
||||
render :json => {:clientid => result_client_id}, :status => 200
|
||||
end
|
||||
|
||||
def worklist # clientid returns a list of clientids
|
||||
# todo clientid should come from the connection record of the signed in user
|
||||
def worklist # clientid; returns a list of clientid
|
||||
client_id = params[:clientid]
|
||||
if client_id.nil? then render :json => {message: 'client_id not specified'}, :status => 400; return end
|
||||
|
||||
c = Connection.where(client_id: client_id).first
|
||||
if c.nil? then render :json => {message: 'connection not found'}, :status => 404; return end
|
||||
if !c.user.id.eql?(current_user.id) then render :json => {message: 'user does not own client_id'}, :status => 403; return end
|
||||
|
||||
# todo this method is a stub
|
||||
render :json => {:clientids => [params[:clientid]+'1_peer', params[:clientid]+'2_peer']}, :status => 200
|
||||
result_client_ids = JamRuby::GetWork.get_work_list(c.locidispid)
|
||||
#result_client_ids = [client_id+'peer1', client_id+'peer2']
|
||||
|
||||
render :json => {:clientids => result_client_ids}, :status => 200
|
||||
end
|
||||
|
||||
def record # aclientid, aAddr, bclientid, bAddr, score returns nothing
|
||||
# todo aclientid, aAddr should come from the connection record of the signed in user
|
||||
# todo this method is a stub
|
||||
|
||||
aclient_id = params[:aclientid]
|
||||
aip_address = params[:aAddr]
|
||||
bclient_id = params[:bclientid]
|
||||
bip_address = params[:bAddr]
|
||||
score = params[:score]
|
||||
|
||||
if aclient_id.nil? then render :json => {message: 'aclient_id not specified'}, :status => 400; return end
|
||||
if aip_address.nil? then render :json => {message: 'aAddr not specified'}, :status => 400; return end
|
||||
if bclient_id.nil? then render :json => {message: 'bclient_id not specified'}, :status => 400; return end
|
||||
if bip_address.nil? then render :json => {message: 'bAddr not specified'}, :status => 400; return end
|
||||
if score.nil? then render :json => {message: 'score not specified'}, :status => 400; return end
|
||||
|
||||
aaddr = JamRuby::JamIsp.ip_to_num(aip_address)
|
||||
if aaddr.nil? then render :json => {message: 'aAddr not valid ip_address'}, :status => 400; return end
|
||||
|
||||
baddr = JamRuby::JamIsp.ip_to_num(bip_address)
|
||||
if baddr.nil? then render :json => {message: 'bAddr not valid ip_address'}, :status => 400; return end
|
||||
|
||||
if aaddr == baddr then render :json => {message: 'aAddr and bAddr are the same'}, :status => 403; return end
|
||||
|
||||
if !score.is_a? Numeric then render :json => {message: 'score not valid numeric'}, :status => 400; return end
|
||||
|
||||
aconn = Connection.where(client_id: aclient_id).first
|
||||
if aconn.nil? then render :json => {message: 'a\'s session not found'}, :status => 404; return end
|
||||
if aaddr != aconn.addr then render :json => {message: 'a\'s session addr does not match aAddr'}, :status => 403; return end
|
||||
if !current_user.id.eql?(aconn.user.id) then render :json => {message: 'a\' session not owned by user'}, :status => 403; return end
|
||||
|
||||
bconn = Connection.where(client_id: bclient_id).first
|
||||
if bconn.nil? then render :json => {message: 'b\'s session not found'}, :status => 404; return end
|
||||
if baddr != bconn.addr then render :json => {message: 'b\'s session addr does not match bAddr'}, :status => 403; return end
|
||||
|
||||
if score < 0 or score > 999 then render :json => {message: 'score < 0 or score > 999'}, :status => 403; return end
|
||||
|
||||
aloc = JamRuby::GeoIpBlocks.lookup(aaddr)
|
||||
aisp = JamRuby::JamIsp.lookup(aaddr)
|
||||
if aisp.nil? or aloc.nil? then render :json => {message: 'a\'s location or isp not found'}, :status => 404; return end
|
||||
alocidispid = aloc.locid*1000000+aisp.coid;
|
||||
|
||||
bloc = JamRuby::GeoIpBlocks.lookup(baddr)
|
||||
bisp = JamRuby::JamIsp.lookup(baddr)
|
||||
blocidispid = bloc.locid*1000000+bisp.coid
|
||||
|
||||
JamRuby::Score.createx(alocidispid, aclient_id, aaddr, blocidispid, bclient_id, baddr, score.ceil, nil)
|
||||
|
||||
render :json => {}, :status => 200
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
class ApiUsersController < ApiController
|
||||
|
||||
before_filter :api_signed_in_user, :except => [:create, :signup_confirm, :auth_session_create, :complete, :finalize_update_email, :isp_scoring]
|
||||
before_filter :api_signed_in_user, :except => [:create, :show, :signup_confirm, :auth_session_create, :complete, :finalize_update_email, :isp_scoring]
|
||||
before_filter :auth_user, :only => [:session_settings_show, :session_history_index, :session_user_history_index, :update, :delete,
|
||||
:liking_create, :liking_destroy, # likes
|
||||
:following_create, :following_show, :following_destroy, # followings
|
||||
|
|
@ -43,7 +43,7 @@ class ApiUsersController < ApiController
|
|||
@user.update_instruments(params[:instruments].nil? ? [] : params[:instruments]) if params.has_key?(:instruments)
|
||||
@user.show_whats_next = params[:show_whats_next] if params.has_key?(:show_whats_next)
|
||||
@user.subscribe_email = params[:subscribe_email] if params.has_key?(:subscribe_email)
|
||||
|
||||
@user.biography = params[:biography] if params.has_key?(:biography)
|
||||
@user.save
|
||||
|
||||
if @user.errors.any?
|
||||
|
|
@ -202,7 +202,7 @@ class ApiUsersController < ApiController
|
|||
end
|
||||
|
||||
def liking_destroy
|
||||
User.delete_liking(params[:id], params[:target_entity_id])
|
||||
User.delete_liking(params[:id], params[:likable_id])
|
||||
respond_with responder: ApiResponder, :status => 204
|
||||
end
|
||||
|
||||
|
|
@ -230,7 +230,7 @@ class ApiUsersController < ApiController
|
|||
end
|
||||
|
||||
def following_destroy
|
||||
User.delete_following(params[:id], params[:target_entity_id])
|
||||
User.delete_following(params[:id], params[:followable_id])
|
||||
respond_with responder: ApiResponder, :status => 204
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ class SpikesController < ApplicationController
|
|||
|
||||
def listen_in
|
||||
|
||||
if !current_user.admin
|
||||
raise PermissionError "must be administrator"
|
||||
end
|
||||
|
||||
#as_musician = false is the critical search criteria for sessions to list correctly
|
||||
@music_sessions = MusicSession.index(current_user, as_musician: false)
|
||||
|
||||
|
|
|
|||
|
|
@ -194,6 +194,7 @@ class UsersController < ApplicationController
|
|||
render :layout => "web"
|
||||
end
|
||||
|
||||
# DO NOT USE CURRENT_USER IN THIS ROUTINE. IT'S CACHED FOR THE WHOLE SITE
|
||||
def welcome
|
||||
|
||||
@slides = [
|
||||
|
|
@ -205,12 +206,14 @@ class UsersController < ApplicationController
|
|||
Slide.new("bands", "web/carousel_bands.jpg", "http://www.youtube.com/embed/eaYNM7p6Z5s")
|
||||
]
|
||||
|
||||
@promo_buzz = Promotional.where(:type => 'JamRuby::PromoBuzz', :aasm_state => :active)
|
||||
@promo_latest = Promotional.where(:type => 'JamRuby::PromoLatest', :aasm_state => :active);
|
||||
|
||||
if current_user
|
||||
@promo_buzz = Promotional.where(:type => 'JamRuby::PromoBuzz', :aasm_state => :active).order(:position)
|
||||
|
||||
if Rails.application.config.use_promos_on_homepage
|
||||
@promo_latest = Promotional.where(:type => 'JamRuby::PromoLatest', :aasm_state => :active).order(:position).limit(10)
|
||||
else
|
||||
@promo_latest, start = Feed.index(nil, limit: 10)
|
||||
end
|
||||
|
||||
@welcome_page = true
|
||||
render :layout => "web"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
module AvatarHelper
|
||||
|
||||
def render_avatarable(avatarable)
|
||||
image_tag resolve_avatarable(avatarable)
|
||||
end
|
||||
|
||||
def resolve_user_avatar_url(user)
|
||||
user.photo_url.nil? ? "shared/avatar_generic.png" : user.photo_url
|
||||
end
|
||||
|
||||
def resolve_band_avatar_url(band)
|
||||
band.photo_url.nil? ? "shared/avatar_generic_band.png" : band.photo_url
|
||||
end
|
||||
|
||||
def resolve_avatarable(avatarable)
|
||||
if avatarable.class == JamRuby::User || avatarable.class == JamRuby::MusicSessionUserHistory
|
||||
resolve_user_avatar_url(avatarable)
|
||||
elsif avatarable.class == JamRuby::Band
|
||||
resolve_band_avatar_url(avatarable)
|
||||
else
|
||||
raise "unable to resolve avatarable #{avatarable}"
|
||||
end
|
||||
end
|
||||
|
||||
def resolve_avatarables(*avatarables)
|
||||
avatarables.each do |avatarable|
|
||||
return resolve_avatarable(avatarable) if avatarable
|
||||
end
|
||||
|
||||
raise "at least one avatarable must be specified"
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
module FeedsHelper
|
||||
def session_artist_name(music_session_history)
|
||||
(music_session_history.band.nil? ? nil : music_session_history.band.name) || music_session_history.user.name
|
||||
end
|
||||
|
||||
def session_avatar(music_session_history)
|
||||
image_tag resolve_avatarables(music_session_history.band, music_session_history.user)
|
||||
end
|
||||
|
||||
def session_duration(music_session_history, options)
|
||||
if music_session_history.session_removed_at.nil?
|
||||
duration(Time.now - music_session_history.created_at, options)
|
||||
else
|
||||
duration(music_session_history.session_removed_at - music_session_history.created_at, options)
|
||||
end
|
||||
end
|
||||
|
||||
def session_description(music_session_history)
|
||||
music_session_history.description
|
||||
end
|
||||
|
||||
# grabs 1st genre
|
||||
def session_genre(music_session_history)
|
||||
genres_array = music_session_history.genres.nil? ? [] : music_session_history.genres.split(MusicSessionHistory::SEPARATOR)
|
||||
genre = genres_array.length > 0 ? Genre.find_by_id(genres_array[0]) : nil
|
||||
genre.nil? ? '' : genre.description
|
||||
end
|
||||
|
||||
def recording_artist_name(recording)
|
||||
(recording.band.nil? ? nil : recording.band.name) || recording.candidate_claimed_recording.user.name
|
||||
end
|
||||
|
||||
def recording_avatar(recording)
|
||||
image_tag resolve_avatarables(recording.band, recording.owner)
|
||||
end
|
||||
|
||||
def recording_duration(recording, options)
|
||||
duration(recording.duration, options)
|
||||
end
|
||||
|
||||
def recording_name(recording)
|
||||
recording.candidate_claimed_recording.name
|
||||
end
|
||||
|
||||
def recording_description(recording)
|
||||
recording.candidate_claimed_recording.description
|
||||
end
|
||||
|
||||
def recording_genre(recording)
|
||||
recording.candidate_claimed_recording.genre.description
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#http://brandonhilkert.com/blog/relative-timestamps-in-rails/
|
||||
module TimeHelper
|
||||
def timeago(time, options = {})
|
||||
options[:class] = "#{options[:class]} timeago"
|
||||
content_tag(
|
||||
:time,
|
||||
time.to_s,
|
||||
options.merge(datetime: time.getutc.iso8601)
|
||||
) if time
|
||||
end
|
||||
|
||||
def duration(duration, options = {})
|
||||
options[:class] = "#{options[:class]} duration"
|
||||
content_tag(
|
||||
:time,
|
||||
duration.to_s,
|
||||
options.merge(duration: duration.to_s)
|
||||
) if duration
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
object @mix
|
||||
|
|
@ -23,6 +23,10 @@ if @search.musicians_text_search?
|
|||
musician.friends?(current_user)
|
||||
end
|
||||
|
||||
node :pending_friend_request do |musician|
|
||||
musician.pending_friend_request?(current_user)
|
||||
end
|
||||
|
||||
child :musician_instruments => :instruments do
|
||||
attributes :instrument_id, :description, :proficiency_level, :priority
|
||||
end
|
||||
|
|
@ -30,7 +34,6 @@ if @search.musicians_text_search?
|
|||
end
|
||||
|
||||
if @search.musicians_filter_search?
|
||||
|
||||
node :city do |user|
|
||||
current_user.try(:location)
|
||||
end
|
||||
|
|
@ -50,6 +53,10 @@ if @search.musicians_filter_search?
|
|||
@search.is_follower?(musician)
|
||||
end
|
||||
|
||||
node :pending_friend_request do |musician|
|
||||
musician.pending_friend_request?(current_user)
|
||||
end
|
||||
|
||||
node :biography do |musician|
|
||||
musician.biography.nil? ? "" : musician.biography
|
||||
end
|
||||
|
|
@ -112,6 +119,10 @@ if @search.fans_text_search?
|
|||
node :is_friend do |fan|
|
||||
fan.friends?(current_user)
|
||||
end
|
||||
|
||||
node :pending_friend_request do |fan|
|
||||
fan.pending_friend_request?(current_user)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<!-- Account Summary Dialog -->
|
||||
<div layout="screen" layout-id="account/identity" class="screen secondary">
|
||||
<div layout="screen" layout-id="account/identity" class="screen secondary" id="account-identity">
|
||||
<!-- header -->
|
||||
<div class="content-head">
|
||||
<!-- icon -->
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<span id="alert-message"></span>
|
||||
<br clear="left" /><br />
|
||||
<div class="left">
|
||||
<a id="btn-alert-cancel" class="button-orange">CANCEL</a>
|
||||
<a id="btn-alert-cancel" class="button-grey">CANCEL</a>
|
||||
</div>
|
||||
<div class="right">
|
||||
<a id="btn-alert-ok" class="button-orange"></a>
|
||||
|
|
|
|||
|
|
@ -5,25 +5,49 @@
|
|||
<script type="text/javascript">
|
||||
var rest = JK.Rest();
|
||||
|
||||
function addLike(bandId) {
|
||||
rest.addLike({band_id: bandId})
|
||||
// function addLike(bandId) {
|
||||
// rest.addLike({band_id: bandId})
|
||||
// .done(function(response) {
|
||||
// $("#spnLikeCount", "#band-hover").html(parseInt($("#spnLikeCount", "#band-hover").text()) + 1);
|
||||
// var $btnLikeSelector = $("#btnLike", "#band-hover");
|
||||
// $btnLikeSelector.unbind("click");
|
||||
// $btnLikeSelector.html("LIKED");
|
||||
// });
|
||||
// }
|
||||
|
||||
function addBandFollowing(bandId) {
|
||||
rest.addFollowing({band_id: bandId})
|
||||
.done(function(response) {
|
||||
$("#spnLikeCount", "#band-hover").html(parseInt($("#spnLikeCount", "#band-hover").text()) + 1);
|
||||
var $btnLikeSelector = $("#btnLike", "#band-hover");
|
||||
$btnLikeSelector.unbind("click");
|
||||
$btnLikeSelector.html("LIKED");
|
||||
adjustBandFollowingCount(1);
|
||||
|
||||
var $btnFollowSelector = $("#btnFollow", "#band-hover");
|
||||
$btnFollowSelector.unbind('click');
|
||||
$btnFollowSelector.attr('onclick', '');
|
||||
$btnFollowSelector.click(function() {
|
||||
removeBandFollowing(bandId);
|
||||
});
|
||||
$btnFollowSelector.html("UNFOLLOW");
|
||||
});
|
||||
}
|
||||
|
||||
function addFollowing(bandId) {
|
||||
rest.addFollowing({band_id: bandId})
|
||||
function removeBandFollowing(bandId) {
|
||||
rest.removeFollowing(bandId)
|
||||
.done(function(response) {
|
||||
$("#spnFollowCount", "#band-hover").html(parseInt($("#spnFollowCount", "#band-hover").text()) + 1);
|
||||
var $btnFollowSelector = $("#btnFollow", "#band-hover");
|
||||
$btnFollowSelector.unbind("click");
|
||||
$btnFollowSelector.html("STOP FOLLOWING");
|
||||
adjustBandFollowingCount(-1);
|
||||
|
||||
var $btnFollowSelector = $("#btnFollow", "#band-hover");
|
||||
$btnFollowSelector.unbind('click');
|
||||
$btnFollowSelector.attr('onclick', '');
|
||||
$btnFollowSelector.click(function() {
|
||||
addBandFollowing(bandId);
|
||||
});
|
||||
$btnFollowSelector.html("FOLLOW");
|
||||
});
|
||||
}
|
||||
|
||||
function adjustBandFollowingCount(value) {
|
||||
$("#spnFollowCount", "#band-hover").text(parseInt($("#spnFollowCount", "#band-hover").text()) + value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="template-hover-band">
|
||||
|
|
@ -33,7 +57,6 @@
|
|||
<h3>{name}</h3>
|
||||
<small>{location}<br /><strong>{genres}</strong></small><br />
|
||||
<br clear="all" />
|
||||
<span id="spnLikeCount">{like_count}</span> <img src="/assets/content/icon_like.png" align="absmiddle" />
|
||||
<span id="spnFollowCount">{follower_count}</span> <img src="/assets/content/icon_followers.png" width="22" height="12" align="absmiddle" />
|
||||
{recording_count} <img src="/assets/content/icon_recordings.png" width="12" height="13" align="absmiddle" />
|
||||
{session_count} <img src="/assets/content/icon_session_tiny.png" width="12" height="12" align="absmiddle" />
|
||||
|
|
@ -47,8 +70,8 @@
|
|||
<br />
|
||||
<div align="center">
|
||||
<div class="left"><a href="{profile_url}" class="button-orange">PROFILE</a></div>
|
||||
<div class="left"><a class="button-orange">LIKE</a></div>
|
||||
<div class="left"><a class="button-orange">FOLLOW</a></div>
|
||||
<div class="left" style="display:none;"><a class="button-orange">LIKE</a></div>
|
||||
<div class="left"><a id="btnFollow" onclick="{followAction}('{bandId}');" class="button-orange">FOLLOW</a></div>
|
||||
</div>
|
||||
<br /><br />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,19 +5,58 @@
|
|||
<script type="text/javascript">
|
||||
var rest = JK.Rest();
|
||||
|
||||
function addFollowing(userId) {
|
||||
function addFanFollowing(userId) {
|
||||
rest.addFollowing({user_id: userId})
|
||||
.done(function(response) {
|
||||
$("#spnFollowCount", "#fan-hover").html(parseInt($("#spnFollowCount", "#fan-hover").text()) + 1);
|
||||
var $btnFollowSelector = $("#btnFollow", "#fan-hover");
|
||||
$btnFollowSelector.unbind("click");
|
||||
$btnFollowSelector.html("STOP FOLLOWING");
|
||||
adjustFanFollowingCount(1);
|
||||
|
||||
var $btnFollowSelector = $("#btnFollow", "#fan-hover");
|
||||
$btnFollowSelector.unbind('click');
|
||||
$btnFollowSelector.attr('onclick', '');
|
||||
$btnFollowSelector.click(function() {
|
||||
removeFanFollowing(userId);
|
||||
});
|
||||
$btnFollowSelector.html("UNFOLLOW");
|
||||
});
|
||||
}
|
||||
|
||||
function sendFriendRequest(userId) {
|
||||
rest.sendFriendRequest(JK.app, userId);
|
||||
function removeFanFollowing(userId) {
|
||||
rest.removeFollowing(userId)
|
||||
.done(function(response) {
|
||||
adjustFanFollowingCount(-1);
|
||||
|
||||
var $btnFollowSelector = $("#btnFollow", "#fan-hover");
|
||||
$btnFollowSelector.unbind('click');
|
||||
$btnFollowSelector.attr('onclick', '');
|
||||
$btnFollowSelector.click(function() {
|
||||
addFanFollowing(userId);
|
||||
});
|
||||
$btnFollowSelector.html("FOLLOW");
|
||||
});
|
||||
}
|
||||
|
||||
function adjustFanFollowingCount(value) {
|
||||
$("#spnFollowCount", "#fan-hover").text(parseInt($("#spnFollowCount", "#fan-hover").text()) + value);
|
||||
}
|
||||
|
||||
function sendFanFriendRequest(userId) {
|
||||
rest.sendFriendRequest(JK.app, userId);
|
||||
$("#btnFriend", "#fan-hover").hide();
|
||||
}
|
||||
|
||||
function removeFanFriend(userId) {
|
||||
rest.removeFriend({friend_id: userId})
|
||||
.done(function() {
|
||||
var $btnFriendSelector = $("#btnFriend", "#fan-hover");
|
||||
$btnFriendSelector.unbind("click");
|
||||
$btnFriendSelector.attr('onclick', '');
|
||||
$btnFriendSelector.html("CONNECT");
|
||||
$btnFriendSelector.click(function() {
|
||||
sendFanFriendRequest(userId);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="template-hover-fan">
|
||||
|
|
@ -38,8 +77,8 @@
|
|||
<br />
|
||||
<div align="center">
|
||||
<div class="left"><a href="{profile_url}" class="button-orange">PROFILE</a></div>
|
||||
<div class="left"><a id="btnFriend" onclick="sendFriendRequest('{userId}');" class="button-orange">FRIEND</a></div>
|
||||
<div class="left"><a id="btnFollow" onclick="addFollowing('{userId}');" class="button-orange">FOLLOW</a></div>
|
||||
<div class="left"><a id="btnFriend" onclick="{friendAction}('{userId}');" class="button-orange">CONNECT</a></div>
|
||||
<div class="left"><a id="btnFollow" onclick="{followAction}('{userId}');" class="button-orange">FOLLOW</a></div>
|
||||
</div>
|
||||
<br /><br />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,29 +4,174 @@
|
|||
|
||||
<script type="text/javascript">
|
||||
var rest = JK.Rest();
|
||||
var logger = JK.logger;
|
||||
|
||||
function addLike(userId) {
|
||||
rest.addLike({user_id: userId})
|
||||
.done(function(response) {
|
||||
$("#spnLikeCount", "#musician-hover").html(parseInt($("#spnLikeCount", "#musician-hover").text()) + 1);
|
||||
var $btnLikeSelector = $("#btnLike", "#musician-hover");
|
||||
$btnLikeSelector.unbind("click");
|
||||
$btnLikeSelector.html("LIKED");
|
||||
});
|
||||
// function addLike(userId) {
|
||||
// rest.addLike({user_id: userId})
|
||||
// .done(function(response) {
|
||||
// $("#spnLikeCount", "#musician-hover").text(parseInt($("#spnLikeCount", "#musician-hover").text()) + 1);
|
||||
// var $btnLikeSelector = $("#btnLike", "#musician-hover");
|
||||
// $btnLikeSelector.unbind("click");
|
||||
// $btnLikeSelector.html("LIKED");
|
||||
// });
|
||||
// }
|
||||
|
||||
/*********** TODO: THE NEXT 6 FUNCTIONS ARE COPIED FROM sessionList.js. REFACTOR TO COMMON PLACE. *************/
|
||||
|
||||
function joinClick(sessionId) {
|
||||
var hasInvitation = false;
|
||||
var session = null;
|
||||
// we need to do a real-time check of the session in case the settings have
|
||||
// changed while the user was sitting on the Find Session screen
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "/api/sessions/" + sessionId,
|
||||
async: false,
|
||||
success: function(response) {
|
||||
session = response;
|
||||
if ("invitations" in session) {
|
||||
var invitation;
|
||||
// user has invitations for this session
|
||||
for (var i=0; i < session.invitations.length; i++) {
|
||||
invitation = session.invitations[i];
|
||||
// session contains an invitation for this user
|
||||
if (invitation.receiver_id === JK.currentUserId) {
|
||||
hasInvitation = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (session) {
|
||||
// if user has an invitation, always open terms and allow joining regardless of settings
|
||||
if (hasInvitation) {
|
||||
logger.debug("Found invitation for user " + JK.currentUserId + ", session " + sessionId);
|
||||
openTerms(sessionId);
|
||||
}
|
||||
else {
|
||||
if (session.musician_access) {
|
||||
if (session.approval_required) {
|
||||
openAlert(sessionId);
|
||||
}
|
||||
else {
|
||||
openTerms(sessionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function(xhr, textStatus, errorMessage) {
|
||||
logger.debug("xhr.status = " + xhr.status);
|
||||
if (xhr.status === 404) {
|
||||
sessionNotJoinableAlert();
|
||||
}
|
||||
else {
|
||||
JK.app.notify(
|
||||
{ title: "Unable to Join Session",
|
||||
text: "There was an unexpected error while attempting to join the session."
|
||||
},
|
||||
{ no_cancel: true });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addFollowing(userId) {
|
||||
function openAlert(sessionId) {
|
||||
var alertDialog = new JK.AlertDialog(JK.app, "YES",
|
||||
"You must be approved to join this session. Would you like to send a request to join?",
|
||||
sessionId, onCreateJoinRequest);
|
||||
|
||||
alertDialog.initialize();
|
||||
JK.app.layout.showDialog('alert');
|
||||
}
|
||||
|
||||
function sessionNotJoinableAlert() {
|
||||
var alertDialog = new JK.AlertDialog(JK.app, "OK",
|
||||
"This session is over or is no longer public and cannot be joined. Please click Refresh to update the session list.",
|
||||
null,
|
||||
function(evt) {
|
||||
JK.app.layout.closeDialog('alert');
|
||||
}
|
||||
);
|
||||
|
||||
alertDialog.initialize();
|
||||
JK.app.layout.showDialog('alert');
|
||||
}
|
||||
|
||||
function onCreateJoinRequest(sessionId) {
|
||||
var joinRequest = {};
|
||||
joinRequest.music_session = sessionId;
|
||||
joinRequest.user = JK.currentUserId;
|
||||
rest.createJoinRequest(joinRequest)
|
||||
.done(function(response) {
|
||||
|
||||
}).error(JK.app.ajaxError);
|
||||
|
||||
JK.app.layout.closeDialog('alert');
|
||||
}
|
||||
|
||||
function openTerms(sessionId) {
|
||||
console.log("sessionId=%o", sessionId);
|
||||
var termsDialog = new JK.TermsDialog(JK.app, sessionId, onTermsAccepted);
|
||||
console.log("sessionId=%o", sessionId);
|
||||
termsDialog.initialize();
|
||||
JK.app.layout.showDialog('terms');
|
||||
}
|
||||
|
||||
function onTermsAccepted(sessionId) {
|
||||
window.location = '/client#/session/' + sessionId;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function addMusicianFollowing(userId) {
|
||||
rest.addFollowing({user_id: userId})
|
||||
.done(function(response) {
|
||||
$("#spnFollowCount", "#musician-hover").html(parseInt($("#spnFollowCount", "#musician-hover").text()) + 1);
|
||||
var $btnFollowSelector = $("#btnFollow", "#musician-hover");
|
||||
$btnFollowSelector.unbind("click");
|
||||
$btnFollowSelector.html("UNFOLLOW");
|
||||
adjustMusicianFollowingCount(1);
|
||||
|
||||
var $btnFollowSelector = $("#btnFollow", "#musician-hover");
|
||||
$btnFollowSelector.unbind('click');
|
||||
$btnFollowSelector.attr('onclick', '');
|
||||
$btnFollowSelector.click(function() {
|
||||
removeMusicianFollowing(userId);
|
||||
});
|
||||
$btnFollowSelector.html("UNFOLLOW");
|
||||
});
|
||||
}
|
||||
|
||||
function sendFriendRequest(userId) {
|
||||
function removeMusicianFollowing(userId) {
|
||||
rest.removeFollowing(userId)
|
||||
.done(function(response) {
|
||||
adjustMusicianFollowingCount(-1);
|
||||
|
||||
var $btnFollowSelector = $("#btnFollow", "#musician-hover");
|
||||
$btnFollowSelector.unbind('click');
|
||||
$btnFollowSelector.attr('onclick', '');
|
||||
$btnFollowSelector.click(function() {
|
||||
addMusicianFollowing(userId);
|
||||
});
|
||||
$btnFollowSelector.html("FOLLOW");
|
||||
});
|
||||
}
|
||||
|
||||
function adjustMusicianFollowingCount(value) {
|
||||
$("#spnFollowCount", "#musician-hover").html(parseInt($("#spnFollowCount", "#musician-hover").html()) + value);
|
||||
}
|
||||
|
||||
function sendMusicianFriendRequest(userId) {
|
||||
rest.sendFriendRequest(JK.app, userId);
|
||||
$("#btnFriend", "#musician-hover").hide();
|
||||
}
|
||||
|
||||
function removeMusicianFriend(userId) {
|
||||
rest.removeFriend({friend_id: userId})
|
||||
.done(function() {
|
||||
var $btnFriendSelector = $("#btnFriend", "#musician-hover");
|
||||
$btnFriendSelector.unbind("click");
|
||||
$btnFriendSelector.attr('onclick', '');
|
||||
$btnFriendSelector.html("CONNECT");
|
||||
$btnFriendSelector.click(function() {
|
||||
sendMusicianFriendRequest(userId);
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
@ -44,7 +189,11 @@
|
|||
{session_count} <img src="/assets/content/icon_session_tiny.png" width="12" height="12" align="absmiddle" />
|
||||
</div>
|
||||
<br clear="all" /><br />
|
||||
<div style="display:{session_display}" class="f12"><strong>IN SESSION — <a href="/client#/session/{session_id}">Click to Join</a></strong></div>
|
||||
<div style="display:{session_display}" class="f12">
|
||||
<strong>IN SESSION <span style="display:{join_display}">—</span>
|
||||
<a id="btnJoinSession" onclick="joinClick('{sessionId}');" style="display:{join_display}">Click to Join</a>
|
||||
</strong>
|
||||
</div>
|
||||
<br />
|
||||
<div class="f11">{biography}</div><br />
|
||||
<small><strong>FOLLOWING:</strong></small><br /><br />
|
||||
|
|
@ -54,9 +203,9 @@
|
|||
<br />
|
||||
<div align="center">
|
||||
<div class="left"><a href="{profile_url}" class="button-orange">PROFILE</a></div>
|
||||
<div class="left"><a id="btnLike" onclick="addLike('{userId}');" class="button-orange">LIKE</a></div>
|
||||
<div class="left"><a id="btnFriend" onclick="sendFriendRequest('{userId}');" class="button-orange">FRIEND</a></div>
|
||||
<div class="left"><a id="btnFollow" onclick="addFollowing('{userId}');" class="button-orange">FOLLOW</a></div>
|
||||
<div class="left" style="display:none;"><a id="btnLike" onclick="addLike('{userId}');" class="button-orange">LIKE</a></div>
|
||||
<div class="left"><a id="btnFriend" onclick="{friendAction}('{userId}');" class="button-orange">CONNECT</a></div>
|
||||
<div class="left"><a id="btnFollow" onclick="{followAction}('{userId}');" class="button-orange">FOLLOW</a></div>
|
||||
</div>
|
||||
<br /><br />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
<!-- Session Update Invite Musicians Dialog -->
|
||||
<div class="dialog invitemusicians-overlay" layout="dialog" layout-id="select-invites">
|
||||
<div class="dialog invitemusicians-overlay" layout="dialog" layout-id="select-invites" style="min-height:180px;">
|
||||
<div class="invitemusicians-inner" id="update-session-invite-musicians">
|
||||
</div>
|
||||
<br clear="all" />
|
||||
<div class="left">
|
||||
<a id="btn-cancel-invites" layout-action="close" class="button-grey">CANCEL</a>
|
||||
</div>
|
||||
<div class="right">
|
||||
<a id="btn-save-invites" layout-action="close" class="button-orange">INVITE</a>
|
||||
</div>
|
||||
<div class="right">
|
||||
<a id="btn-cancel-invites" layout-action="close" class="button-grey">CANCEL</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- invite musician friend selector template -->
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
<a id="btn-accept-leave-session" layout-action="close" class="button-orange">OK</a>
|
||||
</div>
|
||||
<div class="right">
|
||||
<a id="btn-cancel-leave-session" layout-action="close" class="button-orange">CANCEL</a>
|
||||
<a id="btn-cancel-leave-session" layout-action="close" class="button-grey">CANCEL</a>
|
||||
</div>
|
||||
<br clear="all" />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<!-- Profile -->
|
||||
<div layout="screen" layout-id="profile" layout-arg="id" class="screen secondary">
|
||||
<div layout="screen" layout-id="profile" layout-arg="id" class="screen secondary" id="user-profile">
|
||||
<div class="content-head">
|
||||
<div class="content-icon">
|
||||
<%= image_tag "content/icon_profile.png", :size => "19x19" %>
|
||||
|
|
@ -61,8 +61,27 @@
|
|||
<span id="profile-recording-stats"></span><br />
|
||||
</div>
|
||||
<div class="profile-about-right">
|
||||
<p id="profile-biography"></p><br />
|
||||
<div id="profile-instruments"></div>
|
||||
<div class="no-bio">
|
||||
<span>You have no bio to describe yourself as a musician. <a href="#" class="enter-bio">Enter one now!</a></span>
|
||||
</div>
|
||||
<div class="have-bio">
|
||||
<p id="profile-biography"></p><a id="profile-edit-biography" class="button-orange right" href="#">Edit Bio</a>
|
||||
</div>
|
||||
<div class="update-biography">
|
||||
<div class="field">
|
||||
<textarea name="biography" class="user-biography"></textarea>
|
||||
</div>
|
||||
<br clear="left" /><br />
|
||||
<div class="right">
|
||||
<a id="btn-update-user-biography" layout-action="close" class="button-orange">OK</a>
|
||||
</div>
|
||||
<div class="right">
|
||||
<a id="btn-cancel-user-biography" layout-action="close" class="button-grey">CANCEL</a>
|
||||
</div>
|
||||
<br clear="all" />
|
||||
</div>
|
||||
<br />
|
||||
<div id="profile-instruments"></div>
|
||||
</div>
|
||||
<br clear="all" />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<!-- Actual Session Screen -->
|
||||
<div layout="screen" layout-id="session" layout-arg="id" class="screen secondary">
|
||||
<div layout="screen" layout-id="session" layout-arg="id" class="screen secondary" id="session-screen">
|
||||
<div class="content-head">
|
||||
<div class="content-icon">
|
||||
<%= image_tag "shared/icon_session.png", {:height => 19, :width => 19} %>
|
||||
|
|
@ -151,7 +151,7 @@
|
|||
<img src="{avatar}"/>
|
||||
</div>
|
||||
<div class="track-instrument {preMasteredClass}">
|
||||
<img src="/assets/{instrumentIcon}" width="45" height="45"/>
|
||||
<img src="{instrumentIcon}" width="45" height="45"/>
|
||||
</div>
|
||||
<div class="track-gain" mixer-id="{mixerId}"></div>
|
||||
<!--
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@
|
|||
<li class="{cssClass}">
|
||||
<div class="avatar-small" user-id="{userId}" hoveraction="{hoverAction}"><img src="{avatar_url}" /></div>
|
||||
<div class="friend-name" user-id="{userId}" hoveraction="{hoverAction}">
|
||||
{userName}<br/>
|
||||
<span class="friend-name-label">{userName}</span><br/>
|
||||
<span class="friend-status">
|
||||
{status} {extra_info}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<a id="btn-accept-terms" layout-action="close" class="button-orange">ACCEPT</a>
|
||||
</div>
|
||||
<div class="right">
|
||||
<a id="btn-cancel-terms" layout-action="close" class="button-orange">CANCEL</a>
|
||||
<a id="btn-cancel-terms" layout-action="close" class="button-grey">CANCEL</a>
|
||||
</div>
|
||||
<br clear="all" />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -69,7 +69,6 @@
|
|||
</div>
|
||||
|
||||
<%= render "clients/invitationDialog" %>
|
||||
<%= render "clients/shareDialog" %>
|
||||
<%= render "users/signupDialog" %>
|
||||
<%= render "users/signinDialog" %>
|
||||
<%= render "users/videoDialog" %>
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue