VRFS-877 band photo work

This commit is contained in:
Brian Smith 2013-12-15 16:27:11 -05:00
parent b5efad954d
commit 5b5aef15c8
15 changed files with 679 additions and 10 deletions

View File

@ -77,4 +77,5 @@ whats_next.sql
add_user_bio.sql
users_geocoding.sql
recordings_public_launch.sql
notification_band_invite.sql
notification_band_invite.sql
band_photo_filepicker.sql

View File

@ -3,6 +3,8 @@ module JamRuby
attr_accessible :name, :website, :biography, :city, :state, :country
attr_accessor :updating_photo
self.primary_key = 'id'
validates :biography, no_profanity: true
@ -187,6 +189,40 @@ module JamRuby
return band
end
def update_avatar(original_fpfile, cropped_fpfile, crop_selection, aws_bucket)
self.updating_photo = true
cropped_s3_path = cropped_fpfile["key"]
return self.update_attributes(
:original_fpfile_photo => original_fpfile,
:cropped_fpfile_photo => cropped_fpfile,
:cropped_s3_path_photo => cropped_s3_path,
:crop_selection_photo => crop_selection,
:photo_url => S3Util.url(aws_bucket, cropped_s3_path, :secure => false)
)
end
def delete_avatar(aws_bucket)
User.transaction do
unless self.cropped_s3_path.nil?
S3Util.delete(aws_bucket, File.dirname(self.cropped_s3_path) + '/cropped.jpg')
S3Util.delete(aws_bucket, self.cropped_s3_path)
end
return self.update_attributes(
:original_fpfile_photo => nil,
:cropped_fpfile_photo => nil,
:cropped_s3_path_photo => nil,
:crop_selection_photo => nil,
:photo_url => nil
)
end
end
private
def self.validate_genres(genres, is_nil_ok)
if is_nil_ok && genres.nil?

View File

@ -18,6 +18,7 @@
var userDropdown;
function beforeShow(data) {
logger.debug("data.id=" + data.id);
userId = data.id;
}
@ -150,7 +151,7 @@
var avatar = $('img.preview_profile_avatar', avatarSpace);
var spinner = $('<div class="spinner spinner-large"></div>')
if(avatar.length == 0) {
if(avatar.length === 0) {
avatarSpace.prepend(spinner);
}
else {

View File

@ -229,7 +229,7 @@
userNames = [];
userIds = [];
userPhotoUrls = [];
bandId = $("#hdn-band-id").val();
bandId = "1158c8b6-4c92-47dc-82bf-1e390c4f9b2c";// $("#hdn-band-id").val();
resetForm();
}
@ -259,6 +259,9 @@
$("#band-setup-title").html("set up band");
$("#btn-band-setup-save").html("CREATE BAND");
$("#band-change-photo").unbind('click');
$("#band-change-photo").html('Set up band and then add photo.');
}
}
@ -271,7 +274,7 @@
loadGenres(band.genres);
loadCountries(band.country, function() {
loadRegions(band.region, function() {
loadRegions(band.state, function() {
loadCities(band.city);
});
});
@ -492,6 +495,13 @@
return false;
});
$('#band-change-photo').click(function(evt) {
evt.stopPropagation();
$("#hdn-band-id").val(bandId);
context.location = '#/band/setup/photo';
return false;
});
$('div[layout-id="band/setup"] .btn-email-invitation').click(function() {
invitationDialog.showEmailDialog();
});

View File

@ -0,0 +1,431 @@
(function(context,$) {
"use strict";
context.JK = context.JK || {};
context.JK.BandSetupPhotoScreen = function(app) {
var self = this;
var logger = context.JK.logger;
var rest = context.JK.Rest();
var bandId;
var band = {};
var tmpUploadPath = null;
var bandDetail = null;
var bandPhoto;
var selection = null;
var targetCropSize = 88;
var updatingBandPhoto = false;
function beforeShow(data) {
bandId = $("#hdn-band-id").val();
logger.debug("bandId=" + bandId);
if (!bandId) {
context.location = '#/home';
}
}
function afterShow(data) {
resetForm();
renderBandPhotoScreen()
}
function resetForm() {
// remove all display errors
$('#band-setup-photo-content-scroller form .error-text').remove()
$('#band-setup-photo-content-scroller form .error').removeClass("error")
}
function populateBandPhoto(bandDetail) {
self.bandDetail = bandDetail;
rest.getBandPhotoFilepickerPolicy({ id:bandId })
.done(function(filepicker_policy) {
var template= context.JK.fillTemplate($('#template-band-setup-photo').html(), {
"fp_apikey" : gon.fp_apikey,
"data-fp-store-path" : createStorePath(bandDetail) + createOriginalFilename(bandDetail),
"fp_policy" : filepicker_policy.policy,
"fp_signature" : filepicker_policy.signature
});
$('#band-setup-photo-content-scroller').html(template);
var currentFpfile = determineCurrentFpfile();
var currentCropSelection = determineCurrentSelection(bandDetail);
renderBandPhoto(currentFpfile, currentCropSelection ? JSON.parse(currentCropSelection) : null);
})
.error(app.ajaxError);
}
// events for main screen
function events() {
// wire up main panel clicks
$('#band-setup-photo-content-scroller').on('click', '#band-setup-photo-upload', function(evt) { evt.stopPropagation(); handleFilePick(); return false; } );
$('#band-setup-photo-content-scroller').on('click', '#band-setup-photo-delete', function(evt) { evt.stopPropagation(); handleDeletePhoto(); return false; } );
$('#band-setup-photo-content-scroller').on('click', '#band-setup-photo-cancel', function(evt) { evt.stopPropagation(); navToEditProfile(); return false; } );
$('#band-setup-photo-content-scroller').on('click', '#band-setup-photo-submit', function(evt) { evt.stopPropagation(); handleUpdatePhoto(); return false; } );
//$('#band-setup-photo-content-scroller').on('change', 'input[type=filepicker-dragdrop]', function(evt) { evt.stopPropagation(); afterImageUpload(evt.originalEvent.fpfile); return false; } );
}
function handleDeleteBandPhoto() {
if(self.updatingBandPhoto) {
// protect against concurrent update attempts
return;
}
self.updatingBandPhoto = true;
renderBandPhotoSpinner();
rest.deleteBandPhoto({ id: bandId })
.done(function() {
removeBandPhotoSpinner({ delete:true });
deleteBandPhotoSuccess(arguments);
selection = null;
})
.fail(function() {
app.ajaxError(arguments);
$.cookie('original_fpfile_band_photo', null);
self.updatingBandPhoto = false;
})
.always(function() {
})
}
function deleteBandPhotoSuccess(response) {
renderBandPhoto(null, null);
rest.getBand(bandId)
.done(function(bandDetail) {
self.bandDetail = bandDetail;
})
.error(app.ajaxError)
.always(function() {
self.updatingBandPhoto = false;
})
}
function handleFilePick() {
rest.getBandPhotoFilepickerPolicy({ id: bandId })
.done(function(filepickerPolicy) {
renderBandPhotoSpinner();
logger.debug("rendered spinner");
filepicker.setKey(gon.fp_apikey);
filepicker.pickAndStore({
mimetype: 'image/*',
maxSize: 10000*1024,
policy: filepickerPolicy.policy,
signature: filepickerPolicy.signature
},
{ path: createStorePath(self.bandDetail), access: 'public' },
function(fpfiles) {
removeBandPhotoSpinner();
afterImageUpload(fpfiles[0]);
}, function(fperror) {
removeBandPhotoSpinner();
if(fperror.code != 101) { // 101 just means the user closed the dialog
alert("unable to upload file: " + JSON.stringify(fperror))
}
})
})
.fail(app.ajaxError);
}
function renderBandPhotoScreen() {
rest.getBand(bandId)
.done(populateBandPhoto)
.error(app.ajaxError)
}
function navToEditProfile() {
resetForm();
$("#hdn-band-id").val(bandId);
context.location = '#/band/setup';
}
function renderBandPhotoSpinner() {
var bandPhotoSpace = $('#band-setup-photo-content-scroller .band-setup-photo .avatar-space');
// if there is already an image tag, we only obscure it.
var bandPhoto = $('img.preview_profile_avatar', bandPhotoSpace);
var spinner = $('<div class="spinner spinner-large"></div>')
if(bandPhoto.length === 0) {
bandPhotoSpace.prepend(spinner);
}
else {
// in this case, just style the spinner to obscure using opacity, and center it
var jcropHolder = $('.jcrop-holder', bandPhotoSpace);
spinner.width(jcropHolder.width());
spinner.height(jcropHolder.height());
spinner.addClass('op50');
var jcrop = bandPhoto.data('Jcrop');
if(jcrop) {
jcrop.disable();
}
bandPhotoSpace.append(spinner);
}
}
function removeBandPhotoSpinner(options) {
var bandPhotoSpace = $('#band-setup-photo-content-scroller .band-setup-photo .avatar-space');
if(options && options.delete) {
bandPhotoSpace.children().remove();
}
var spinner = $('.spinner-large', bandPhotoSpace);
spinner.remove();
var bandPhoto = $('img.preview_profile_avatar', bandPhotoSpace);
var jcrop = bandPhoto.data('Jcrop')
if(jcrop) {
jcrop.enable();
}
}
function renderBandPhoto(fpfile, storedSelection) {
// clear out
var bandPhotoSpace = $('#band-setup-photo-content-scroller .band-setup-photo .avatar-space');
if(!fpfile) {
renderNoBandPhoto(bandPhotoSpace);
}
else {
rest.getBandPhotoFilepickerPolicy({handle: fpfile.url, id: bandId})
.done(function(filepickerPolicy) {
bandPhotoSpace.children().remove();
renderBandPhotoSpinner();
var photo_url = fpfile.url + '?signature=' + filepickerPolicy.signature + '&policy=' + filepickerPolicy.policy;
bandPhoto = new Image();
$(bandPhoto)
.load(function(e) {
removeBandPhotoSpinner();
bandPhoto = $(this);
bandPhotoSpace.append(bandPhoto);
var width = bandPhoto.naturalWidth();
var height = bandPhoto.naturalHeight();
if(storedSelection) {
var left = storedSelection.x;
var right = storedSelection.x2;
var top = storedSelection.y;
var bottom = storedSelection.y2;
}
else {
if(width < height) {
var left = width * .25;
var right = width * .75;
var top = (height / 2) - (width / 4);
var bottom = (height / 2) + (width / 4);
}
else {
var top = height * .25;
var bottom = height * .75;
var left = (width / 2) - (height / 4);
var right = (width / 2) + (height / 4);
}
}
// jcrop only works well with px values (not percentages)
// so we get container, and work out a decent % ourselves
var container = $('#band-setup-photo-content-scroller');
bandPhoto.Jcrop({
aspectRatio: 1,
boxWidth: container.width() * .75,
boxHeight: container.height() * .75,
// minSelect: [targetCropSize, targetCropSize], unnecessary with scaling involved
setSelect: [ left, top, right, bottom ],
trueSize: [width, height],
onRelease: onSelectRelease,
onSelect: onSelect,
onChange: onChange
});
})
.error(function() {
// default to no avatar look of UI
renderNoBandPhoto(bandPhotoSpace);
})
.attr('src', photo_url)
.attr('alt', 'profile avatar')
.addClass('preview_profile_avatar');
})
.fail(app.ajaxError);
}
}
function afterImageUpload(fpfile) {
$.cookie('original_fpfile_band_photo', JSON.stringify(fpfile));
renderBandPhoto(fpfile, null);
}
function renderNoBandPhoto(bandPhotoSpace) {
// no photo found for band
removeBandPhotoSpinner();
var noAvatarSpace = $('<div></div>');
noAvatarSpace.addClass('no-avatar-space');
noAvatarSpace.text('Please upload a photo');
bandPhotoSpace.append(noAvatarSpace);
}
function handleUpdateBandPhoto(event) {
if(self.updatingBandPhoto) {
// protect against concurrent update attempts
return;
}
if(selection) {
var currentSelection = selection;
self.updatingBandPhoto = true;
renderBandPhotoSpinner();
console.log("Converting...");
// we convert two times; first we crop to the selected region,
// then we scale to 88x88 (targetCropSize X targetCropSize), which is the largest size we use throughout the site.
var fpfile = determineCurrentFpfile();
rest.getBandPhotoFilepickerPolicy({ handle: fpfile.url, convert: true, id: bandId })
.done(function(filepickerPolicy) {
filepicker.setKey(gon.fp_apikey);
filepicker.convert(fpfile, {
crop: [
Math.round(currentSelection.x),
Math.round(currentSelection.y),
Math.round(currentSelection.w),
Math.round(currentSelection.w)],
fit: 'crop',
format: 'jpg',
quality: 90,
policy: filepickerPolicy.policy,
signature: filepickerPolicy.signature
}, { path: createStorePath(self.bandDetail) + 'cropped-' + new Date().getTime() + '.jpg', access: 'public' },
function(cropped) {
logger.debug("converting cropped");
rest.getBandPhotoFilepickerPolicy({handle: cropped.url, convert: true, id: bandId})
.done(function(filepickerPolicy) {
filepicker.convert(cropped, {
height: targetCropSize,
width: targetCropSize,
fit: 'scale',
format: 'jpg',
quality: 75,
policy: filepickerPolicy.policy,
signature: filepickerPolicy.signature
}, { path: createStorePath(self.bandDetail), access: 'public' },
function(scaled) {
logger.debug("converted and scaled final image %o", scaled);
rest.updateBandPhoto({
original_fpfile: determineCurrentFpfile(),
cropped_fpfile: scaled,
crop_selection: currentSelection,
id: bandId
})
.done(updateBandPhotoSuccess)
.fail(app.ajaxError)
.always(function() { removeBandPhotoSpinner(); self.updatingBandPhoto = false;})
},
function(fperror) {
alert("unable to scale selection. error code: " + fperror.code);
removeBandPhotoSpinner();
self.updatingBandPhoto = false;
})
})
.fail(app.ajaxError);
},
function(fperror) {
alert("unable to crop selection. error code: " + fperror.code);
removeBandPhotoSpinner();
self.updatingBandPhoto = false;
}
);
})
.fail(app.ajaxError);
}
else {
app.notify(
{ title: "Upload a Band Photo First",
text: "To update your band photo, first you must upload an image using the UPLOAD button"
},
{ no_cancel: true });
}
}
function updateBandPhotoSuccess(response) {
$.cookie('original_fpfile_band_photo', null);
self.bandDetail = response;
app.notify(
{ title: "Band Photo Changed",
text: "You have updated your band photo successfully."
},
{ no_cancel: true });
}
function onSelectRelease(event) {
}
function onSelect(event) {
selection = event;
}
function onChange(event) {
}
function createStorePath(bandDetail) {
return gon.fp_upload_dir + '/' + bandDetail.id + '/'
}
function createOriginalFilename(bandDetail) {
// get the s3
var fpfile = bandDetail.original_fpfile_photo ? JSON.parse(bandDetail.original_fpfile_photo) : null;
return 'original_band_photo.jpg'
}
// retrieves a file that has not yet been used as an band photo (uploaded, but not cropped)
function getWorkingFpfile() {
return JSON.parse($.cookie('original_fpfile_band_photo'))
}
function determineCurrentFpfile() {
// precedence is as follows:
// * tempOriginal: if set, then the user is working on a new upload
// * storedOriginal: if set, then the user has previously uploaded and cropped a band photo
// * null: neither are set above
var tempOriginal = getWorkingFpfile();
var storedOriginal = self.bandDetail.original_fpfile_photo ? JSON.parse(self.bandDetail.original_fpfile_photo) : null;
return tempOriginal ? tempOriginal : storedOriginal;
}
function determineCurrentSelection(bandDetail) {
// if the cookie is set, don't use the storage selection, just default to null
return $.cookie('original_fpfile_band_photo') == null ? bandDetail.crop_selection_photo : null;
}
function initialize() {
var screenBindings = {
'beforeShow': beforeShow,
'afterShow': afterShow
};
app.bindScreen('band/setup/photo', screenBindings);
events();
}
this.initialize = initialize;
this.beforeShow = beforeShow;
this.afterShow = afterShow;
return this;
};
})(window,jQuery);

View File

@ -252,6 +252,54 @@
});
}
function updateBandPhoto(options) {
var id = getId(options);
var original_fpfile = options['original_fpfile'];
var cropped_fpfile = options['cropped_fpfile'];
var crop_selection = options['crop_selection'];
var url = "/api/bands/" + id + "/photo";
return $.ajax({
type: "POST",
dataType: "json",
url: url,
contentType: 'application/json',
processData:false,
data: JSON.stringify({
original_fpfile : original_fpfile,
cropped_fpfile : cropped_fpfile,
crop_selection : crop_selection
})
});
}
function deleteBandPhoto(options) {
var id = getId(options);
var url = "/api/bands/" + id + "/photo";
return $.ajax({
type: "DELETE",
dataType: "json",
url: url,
contentType: 'application/json',
processData:false
});
}
function getBandPhotoFilepickerPolicy(options) {
var id = getId(options);
var handle = options && options["handle"];
var convert = options && options["convert"]
var url = "/api/bands/" + id + "/filepicker_policy";
return $.ajax(url, {
data : { handle : handle, convert: convert },
dataType : 'json'
});
}
function getFriends(options) {
var friends = [];
var id = getId(options);
@ -499,6 +547,9 @@
this.putTrackSyncChange = putTrackSyncChange;
this.createBand = createBand;
this.updateBand = updateBand;
this.updateBandPhoto = updateBandPhoto;
this.deleteBandPhoto = deleteBandPhoto;
this.getBandPhotoFilepickerPolicy = getBandPhotoFilepickerPolicy;
this.getBand = getBand;
this.createBandInvitation = createBandInvitation;
this.updateBandInvitation = updateBandInvitation;

View File

@ -17,6 +17,43 @@
font-size:14px;
}
.band-setup-photo {
.avatar-space {
color: $color2;
margin-bottom: 20px;
position:relative;
min-height:300px;
img.preview_profile_avatar {
}
}
.spinner-large {
width:300px;
height:300px;
line-height: 300px;
position:absolute;
top:0;
left:0;
z-index: 2000; // to win over jcrop
}
.no-avatar-space {
border:1px dotted $color2;
color: $color2;
width:300px;
height:300px;
line-height: 300px;
text-align: center;
vertical-align: middle;
background-color:$ColorTextBoxBackground;
}
}
.band-profile-header {
padding:20px;
height:120px;

View File

@ -3,7 +3,8 @@ class ApiBandsController < ApiController
before_filter :api_signed_in_user, :except => [:index, :show, :follower_index]
before_filter :auth_band_member, :only => [:update,
:recording_create, :recording_update, :recording_destroy,
:invitation_index, :invitation_show, :invitation_create, :invitation_destroy]
:invitation_index, :invitation_show, :invitation_create, :invitation_destroy,
:update_photo, :delete_photo, :generate_filepicker_policy]
respond_to :json
@ -187,6 +188,56 @@ class ApiBandsController < ApiController
end
end
def update_photo
original_fpfile = params[:original_fpfile]
cropped_fpfile = params[:cropped_fpfile]
crop_selection = params[:crop_selection]
# public bucket to allow images to be available to public
@band.update_photo(original_fpfile, cropped_fpfile, crop_selection, Rails.application.config.aws_bucket_public)
if @band.errors.any?
respond_with @band, status: :unprocessable_entity
else
respond_with @band, responder: ApiResponder, status: 200
end
end
def delete_photo
@band.delete_photo(Rails.application.config.aws_bucket_public)
if @band.errors.any?
respond_with @band, status: :unprocessable_entity
else
respond_with @band, responder: ApiResponder, status: 204
end
end
def generate_filepicker_policy
# generates a soon-expiring filepicker policy so that a band can only upload to their own folder in their bucket
handle = params[:handle]
call = 'pick,convert,store'
policy = { :expiry => (DateTime.now + 5.minutes).to_i(),
:call => call
#:path => 'avatars/' + @band.id + '/.*jpg'
}
# if the caller specifies a handle, add it to the hash
unless handle.nil?
start = handle.rindex('/') + 1
policy[:handle] = handle[start..-1]
end
policy = Base64.urlsafe_encode64( policy.to_json )
digest = OpenSSL::Digest::Digest.new('sha256')
signature = OpenSSL::HMAC.hexdigest(digest, Rails.application.config.fp_secret, policy)
render :json => { :signature => signature, :policy => policy }, :status => :ok
end
#############################################################################
protected
# ensures user is a member of the band

View File

@ -1,6 +1,7 @@
object @band
attributes :id, :name, :city, :state, :country, :location, :website, :biography, :photo_url, :logo_url, :liker_count, :follower_count, :recording_count, :session_count
attributes :id, :name, :city, :state, :country, :location, :website, :biography, :photo_url, :logo_url, :liker_count, :follower_count, :recording_count, :session_count,
:original_fpfile_photo, :cropped_fpfile_photo, :crop_selection_photo
unless @band.users.nil? || @band.users.size == 0
child :users => :musicians do

View File

@ -1,4 +1,4 @@
<!-- Account Summary Dialog -->
<!-- Account Profile Screen -->
<div layout="screen" layout-id="account/profile" class="screen secondary">
<!-- header -->
<div class="content-head">

View File

@ -1,4 +1,4 @@
<!-- Account Summary Dialog -->
<!-- Profile Avatar Screen -->
<div layout="screen" layout-id="account/profile/avatar" class="screen secondary">
<!-- header -->
<div class="content-head">
@ -35,7 +35,7 @@
</div>
<!-- end content wrapper -->
<!-- taken from filepickr.io -->
<!-- taken from filepicker.io -->
<script type="text/javascript">
(function(a){if(window.filepicker){return}var b=a.createElement("script");b.type="text/javascript";b.async=!0;b.src=("https:"===a.location.protocol?"https:":"http:")+"//api.filepicker.io/v1/filepicker.js?signature={fp_signature}&policy={fp_policy}";var c=a.getElementsByTagName("script")[0];c.parentNode.insertBefore(b,c);var d={};d._queue=[];var e="pick,pickMultiple,pickAndStore,read,write,writeUrl,export,convert,store,storeUrl,remove,stat,setKey,constructWidget,makeDropPane".split(",");var f=function(a,b){return function(){b.push([a,arguments])}};for(var g=0;g<e.length;g++){d[e[g]]=f(e[g],d._queue)}window.filepicker=d})(document);
</script>

View File

@ -25,7 +25,7 @@
<%= image_tag "shared/avatar_generic_band.png", {:id => "band-avatar", :align=>"absmiddle", :height => 88, :width => 88 } %>
</a>
<br/><br/>
<a href="#" class="small ml20">Upload Band Photo</a><br clear="all"><br/>
<a id="band-change-photo" href="#" class="small ml20">Upload Band Photo</a><br clear="all"><br/>
</td>
</tr>
<tr>

View File

@ -0,0 +1,41 @@
<!-- Band Photo Setup -->
<div layout="screen" layout-id="band/setup/photo" class="screen secondary">
<!-- header -->
<div class="content-head">
<!-- icon -->
<div class="content-icon">
<%= image_tag "content/icon_bands.png", {:width => 19, :height => 19} %>
</div>
<!-- section head text -->
<h1>band setup</h1>
<%= render "screen_navigation" %>
</div>
<!-- end header -->
<!-- profile scrolling area -->
<div id="band-setup-photo-content-scroller" class="content-scroller">
</div>
<!-- end content scrolling area -->
</div>
<script type="text/template" id="template-band-setup-photo">
<!-- content wrapper -->
<div class="content-wrapper band-setup-photo">
<br />
<div class="avatar-space"></div>
<form id="band-setup-photo-form">
&nbsp;&nbsp; <a href="#" id="band-setup-photo-upload" class="button-orange">UPLOAD</a>
</form>
<br clear="all" />
<div class="right"><a id="band-setup-photo-cancel" href="#" class="button-grey">CANCEL</a>&nbsp;&nbsp;<a id="band-setup-photo-delete" href="#" class="button-orange">DELETE PHOTO</a>&nbsp;&nbsp;<a id="band-setup-photo-submit" href="#" class="button-orange">UPDATE PHOTO</a></div>
</div>
<!-- end content wrapper -->
<!-- taken from filepicker.io -->
<script type="text/javascript">
(function(a){if(window.filepicker){return}var b=a.createElement("script");b.type="text/javascript";b.async=!0;b.src=("https:"===a.location.protocol?"https:":"http:")+"//api.filepicker.io/v1/filepicker.js?signature={fp_signature}&policy={fp_policy}";var c=a.getElementsByTagName("script")[0];c.parentNode.insertBefore(b,c);var d={};d._queue=[];var e="pick,pickMultiple,pickAndStore,read,write,writeUrl,export,convert,store,storeUrl,remove,stat,setKey,constructWidget,makeDropPane".split(",");var f=function(a,b){return function(){b.push([a,arguments])}};for(var g=0;g<e.length;g++){d[e[g]]=f(e[g],d._queue)}window.filepicker=d})(document);
</script>

View File

@ -22,6 +22,7 @@
<%= render "profile" %>
<%= render "bandProfile" %>
<%= render "band_setup" %>
<%= render "band_setup_photo" %>
<%= render "feed" %>
<%= render "bands" %>
<%= render "musicians" %>
@ -141,6 +142,9 @@
var bandSetupScreen = new JK.BandSetupScreen(JK.app);
bandSetupScreen.initialize(invitationDialog, friendSelectorDialog);
var bandSetupPhotoScreen = new JK.BandSetupPhotoScreen(JK.app);
bandSetupPhotoScreen.initialize();
var findSessionScreen = new JK.FindSessionScreen(JK.app);
var sessionLatency = null;
if ("jamClient" in window) {

View File

@ -204,6 +204,11 @@ SampleApp::Application.routes.draw do
match '/bands' => 'api_bands#create', :via => :post
match '/bands/:id' => 'api_bands#update', :via => :post
# photo
match '/bands/:id/photo' => 'api_bands#update_photo', :via => :post
match '/bands/:id/photo' => 'api_bands#delete_photo', :via => :delete
match '/bands/:id/filepicker_policy' => 'api_bands#generate_filepicker_policy', :via => :get
# band members
match '/bands/:id/musicians' => 'api_bands#musician_index', :via => :get
match '/bands/:id/musicians' => 'api_bands#musician_create', :via => :post