diff --git a/web/app/assets/javascripts/createSession.js b/web/app/assets/javascripts/createSession.js index 6a6f1a9dc..ae3e5ff92 100644 --- a/web/app/assets/javascripts/createSession.js +++ b/web/app/assets/javascripts/createSession.js @@ -7,81 +7,19 @@ var logger = context.JK.logger; var rest = context.JK.Rest(); var realtimeMessaging = context.JK.JamServer; - var friendSelectorDialog = null; var invitationDialog = null; - var autoComplete = null; - var userNames = []; - var userIds = []; - var userPhotoUrls = []; + var inviteMusiciansUtil = null; var MAX_GENRES = 1; - var selectedFriendIds = {}; var sessionSettings = {}; function beforeShow(data) { - userNames = []; - userIds = []; - userPhotoUrls = []; + inviteMusiciansUtil.clearSelections(); context.JK.GenreSelectorHelper.render('#create-session-genre'); resetForm(); } function afterShow(data) { - friendSelectorDialog.setCallback(friendSelectorCallback); - - var friends = rest.getFriends({ id: context.JK.currentUserId }) - .done(function(friends) { - $.each(friends, function() { - userNames.push(this.name); - userIds.push(this.id); - userPhotoUrls.push(this.photo_url); - }); - - var autoCompleteOptions = { - lookup: { suggestions: userNames, data: userIds }, - onSelect: addInvitation - }; - - $('#friend-input').attr("placeholder", "Type a friend\'s name").prop('disabled', false); - - if (!autoComplete) { - autoComplete = $('#friend-input').autocomplete(autoCompleteOptions); - } - else { - autoComplete.setOptions(autoCompleteOptions); - } - - $(".autocomplete").width("150px"); - }) - .fail(function() { - $('#friend-input').attr("placeholder", "Unable to lookup friends"); - app.ajaxError(arguments); - }); - } - - function friendSelectorCallback(newSelections) { - var keys = Object.keys(newSelections); - for (var i=0; i < keys.length; i++) { - addInvitation(newSelections[keys[i]].userName, newSelections[keys[i]].userId); - } - } - - function addInvitation(value, data) { - if ($('#selected-friends div[user-id=' + data + ']').length === 0) { - var template = $('#template-added-invitation').html(); - var invitationHtml = context.JK.fillTemplate(template, {userId: data, userName: value}); - $('#selected-friends').append(invitationHtml); - $('#friend-input').select(); - selectedFriendIds[data] = true; - } - else { - $('#friend-input').select(); - context.alert('Invitation already exists for this musician.'); - } - } - - function removeInvitation(evt) { - delete selectedFriendIds[$(evt.currentTarget).parent().attr('user-id')]; - $(evt.currentTarget).closest('.invitation').remove(); + inviteMusiciansUtil.loadFriends(); } function resetForm() { @@ -226,7 +164,7 @@ data: jsonData, success: function(response) { var newSessionId = response.id; - var invitationCount = createInvitations(newSessionId, function() { + var invitationCount = inviteMusiciansUtil.createInvitations(newSessionId, function() { context.location = '#/session/' + newSessionId; }); // Re-loading the session settings will cause the form to reset with the right stuff in it. @@ -248,49 +186,12 @@ return false; } - function createInvitations(sessionId, onComplete) { - var callCount = 0; - var totalInvitations = 0; - $('#selected-friends .invitation').each(function(index, invitation) { - callCount++; - totalInvitations++; - var invite_id = $(invitation).attr('user-id'); - var invite = { - music_session: sessionId, - receiver: invite_id - }; - $.ajax({ - type: "POST", - url: "/api/invitations", - data: invite - }).done(function(response) { - callCount--; - }).fail(app.ajaxError); - }); - // TODO - this is the second time I've used this pattern. - // refactor to make a common utility for this. - function checker() { - if (callCount === 0) { - onComplete(); - } else { - context.setTimeout(checker, 10); - } - } - checker(); - return totalInvitations; - } - function events() { $('#create-session-form').on('submit', submitForm); $('#btn-create-session').on("click", submitForm); - $('#selected-friends').on("click", ".invitation a", removeInvitation); $('#musician-access').change(toggleMusicianAccess); $('#fan-access').change(toggleFanAccess); - $('#btn-choose-friends').click(function() { - friendSelectorDialog.showDialog(selectedFriendIds); - }); - $('div[layout-id="createSession"] .btn-email-invitation').click(function() { invitationDialog.showEmailDialog(); }); @@ -373,38 +274,10 @@ }); } - function searchFriends(query) { - if (query.length < 2) { - $('#friend-search-results').empty(); - return; - } - var url = "/api/search?query=" + query + "&userId=" + context.JK.currentUserId; - $.ajax({ - type: "GET", - url: url, - success: friendSearchComplete - }); - } - - function friendSearchComplete(response) { - // reset search results each time - $('#friend-search-results').empty(); - - // loop through each - $.each(response.friends, function() { - // only show friends who are musicians - if (this.musician === true) { - var template = $('#template-friend-search-results').html(); - var searchResultHtml = context.JK.fillTemplate(template, {userId: this.id, name: this.first_name + ' ' + this.last_name}); - $('#friend-search-results').append(searchResultHtml); - $('#friend-search-results').attr('style', 'display:block'); - } - }); - } - - function initialize(invitationDialogInstance, friendSelectorDialogInstance) { - friendSelectorDialog = friendSelectorDialogInstance; + function initialize(invitationDialogInstance, inviteMusiciansUtilInstance) { invitationDialog = invitationDialogInstance; + inviteMusiciansUtil = inviteMusiciansUtilInstance; + inviteMusiciansUtil.inviteSessionCreate('#create-session-invite-musicians'); events(); loadBands(); loadSessionSettings(); @@ -419,8 +292,6 @@ this.submitForm = submitForm; this.validateForm = validateForm; this.loadBands = loadBands; - this.searchFriends = searchFriends; - this.addInvitation = addInvitation; return this; }; diff --git a/web/app/assets/javascripts/inviteMusicians.js b/web/app/assets/javascripts/inviteMusicians.js new file mode 100644 index 000000000..95f5f58cb --- /dev/null +++ b/web/app/assets/javascripts/inviteMusicians.js @@ -0,0 +1,208 @@ +(function(context,$) { + + "use strict"; + + context.JK = context.JK || {}; + context.JK.InviteMusiciansUtil = function(app) { + var logger = context.JK.logger; + var userNames = []; + var userIds = []; + var userPhotoUrls = []; + var friendSelectorDialog = null; + var invitedFriends = []; + var existingInvites = []; + var autoComplete = null; + var rest = context.JK.Rest(); + var inviteAction = 'create'; // create/update + var updateSessionID = null; + + this.inviteSessionCreate = function(elemSelector) { + inviteAction = 'create'; + _appendFriendSelector($(elemSelector)); + }; + + this.inviteSessionUpdate = function(elemSelector, sessionId) { + this.clearSelections(); + updateSessionID = sessionId; + friendSelectorDialog.setCallback(friendSelectorCallback); + inviteAction = 'update'; + if (0 == $(elemSelector + ' .friendbox').length) { + _appendFriendSelector($(elemSelector)); + $('#btn-save-invites').click(function() { + createInvitations(updateSessionID); + }); + } + $.ajax({ + ajax: false, + url: "/api/invitations", + data: { session_id: sessionId, sender: context.JK.currentUserId } + }).done(function(response) { + response.map(function(item) { + var dd = item['receiver']; + existingInvites.push(dd.id); + addInvitation(dd.name, dd.id); + }); + }).fail(app.ajaxError); + } + + this.clearSelections = function() { + userNames = []; + userIds = []; + userPhotoUrls = []; + invitedFriends = []; + existingInvites = []; + updateSessionID = null; + $('.selected-friends').empty(); + }; + + this.loadFriends = function() { + friendSelectorDialog.setCallback(friendSelectorCallback); + + var friends = rest.getFriends({ id: context.JK.currentUserId }) + .done(function(friends) { + $.each(friends, function() { + userNames.push(this.name); + userIds.push(this.id); + userPhotoUrls.push(this.photo_url); + }); + + var autoCompleteOptions = { + lookup: { suggestions: userNames, data: userIds }, + onSelect: addInvitation + }; + + $('#friend-input').attr("placeholder", "Type a friend\'s name").prop('disabled', false); + + if (!autoComplete) { + autoComplete = $('#friend-input').autocomplete(autoCompleteOptions); + } + else { + autoComplete.setOptions(autoCompleteOptions); + } + + $(".autocomplete").width("150px"); + }) + .fail(function() { + $('#friend-input').attr("placeholder", "Unable to lookup friends"); + app.ajaxError(arguments); + }); + } + + function friendSelectorCallback(newSelections) { + var keys = Object.keys(newSelections); + for (var i=0; i < keys.length; i++) { + var dd = newSelections[keys[i]]; + addInvitation(dd.userName, dd.userId); + } + } + + function _inviteExists(userID) { + return 0 <= existingInvites.indexOf(userID); + } + + function addInvitation(value, data) { + if (0 > invitedFriends.indexOf(data)) { + var template = $('#template-added-invitation').html(); + var imgStyle = _inviteExists(data) ? 'display:none' : ''; + var invitationHtml = context.JK.fillTemplate(template, + {userId: data, + userName: value, + imageStyle: imgStyle}); + $('.selected-friends').append(invitationHtml); + $('#friend-input').select(); + invitedFriends.push(data); + + } else { + $('#friend-input').select(); + context.alert('Invitation already exists for this musician.'); + } + } + + function removeInvitation(evt) { + var idx = invitedFriends.indexOf($(evt.currentTarget).parent().attr('user-id')); + if (0 <= idx) invitedFriends.splice(idx, 1); + $(evt.currentTarget).closest('.invitation').remove(); + } + + function createInvitations(sessionId, onComplete) { + var callCount = 0; + var totalInvitations = invitedFriends.length - existingInvites.length; + invitedFriends.map(function(invite_id) { + if (!_inviteExists(invite_id)) { + callCount++; + var invite = { + music_session: sessionId, + receiver: invite_id + }; + $.ajax({ + type: "POST", + url: "/api/invitations", + data: invite + }).done(function(response) { + callCount--; + }).fail(app.ajaxError); + } + }); + // TODO - this is the second time I've used this pattern. + // refactor to make a common utility for this. + function checker() { + callCount === 0 ? onComplete() : context.setTimeout(checker, 10); + } + if (onComplete) checker(); + return totalInvitations; + } + this.createInvitations = createInvitations; + + function searchFriends(query) { + if (query.length < 2) { + $('#friend-search-results').empty(); + return; + } + var url = "/api/search?query=" + query + "&userId=" + context.JK.currentUserId; + $.ajax({ + type: "GET", + url: url, + success: friendSearchComplete + }); + } + + function friendSearchComplete(response) { + // reset search results each time + $('#friend-search-results').empty(); + + // loop through each + $.each(response.friends, function() { + // only show friends who are musicians + if (this.musician === true) { + var template = $('#template-friend-search-results').html(); + var searchResultHtml = context.JK.fillTemplate(template, {userId: this.id, name: this.first_name + ' ' + this.last_name}); + $('#friend-search-results').append(searchResultHtml); + $('#friend-search-results').attr('style', 'display:block'); + } + }); + } + + function _friendSelectorHTML() { + return context.JK.fillTemplate($('#template-session-invite-musicians').html(), + {choose_friends_id: 'btn-choose-friends-'+inviteAction, + selected_friends_id: 'selected-friends-'+inviteAction}); + } + + function _appendFriendSelector(elemSelector) { + elemSelector.append(_friendSelectorHTML()); + $('#selected-friends-'+inviteAction).on("click", ".invitation a", removeInvitation); + $('#btn-choose-friends-'+inviteAction).click(function(){ + var obj = {}; + invitedFriends.map(function(uid) { obj[uid] = true; }); + friendSelectorDialog.showDialog(obj); + }); + }; + + this.initialize = function(friendSelectorDialogInstance) { + friendSelectorDialog = friendSelectorDialogInstance; + }; + + return this; + }; + +})(window,jQuery); \ No newline at end of file diff --git a/web/app/assets/javascripts/session.js b/web/app/assets/javascripts/session.js index 4245dbee8..e017d1de6 100644 --- a/web/app/assets/javascripts/session.js +++ b/web/app/assets/javascripts/session.js @@ -14,6 +14,8 @@ var addNewGearDialog; var localRecordingsDialog = null; var recordingFinishedDialog = null; + var friendSelectorDialog = null; + var inviteMusiciansUtil = null; var screenActive = false; var currentMixerRangeMin = null; var currentMixerRangeMax = null; @@ -1302,12 +1304,17 @@ } } + function inviteMusicians() { + inviteMusiciansUtil.inviteSessionUpdate('#update-session-invite-musicians', sessionId); + } + function events() { $('#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); $('#open-a-recording').on('click', openRecording); + $('#session-invite-musicians').on('click', inviteMusicians); $('#track-settings').click(function() { configureTrackDialog.showVoiceChatPanel(true); configureTrackDialog.showMusicAudioPanel(true); @@ -1319,9 +1326,10 @@ .on('change-position', onChangePlayPosition); } - this.initialize = function(localRecordingsDialogInstance, recordingFinishedDialogInstance) { + this.initialize = function(localRecordingsDialogInstance, recordingFinishedDialogInstance, inviteMusiciansUtilInstance) { localRecordingsDialog = localRecordingsDialogInstance; recordingFinishedDialog = recordingFinishedDialogInstance; + inviteMusiciansUtil = inviteMusiciansUtilInstance; context.jamClient.SetVURefreshRate(150); playbackControls = new context.JK.PlaybackControls($('.session-recordings .recording-controls')); events(); diff --git a/web/app/assets/stylesheets/client/content.css.scss b/web/app/assets/stylesheets/client/content.css.scss index 286f32922..5282c4209 100644 --- a/web/app/assets/stylesheets/client/content.css.scss +++ b/web/app/assets/stylesheets/client/content.css.scss @@ -207,7 +207,6 @@ .friendbox { padding:5px; - width:100%; height:60px; } diff --git a/web/app/assets/stylesheets/client/createSession.css.scss b/web/app/assets/stylesheets/client/createSession.css.scss index c9fe9d001..3d189bae2 100644 --- a/web/app/assets/stylesheets/client/createSession.css.scss +++ b/web/app/assets/stylesheets/client/createSession.css.scss @@ -11,7 +11,7 @@ } } -#btn-choose-friends { +.btn-choose-friends { margin:0; } #create-session-genre select, #create-session-band select { diff --git a/web/app/assets/stylesheets/client/session.css.scss b/web/app/assets/stylesheets/client/session.css.scss index ad415a5da..fc455d55a 100644 --- a/web/app/assets/stylesheets/client/session.css.scss +++ b/web/app/assets/stylesheets/client/session.css.scss @@ -716,3 +716,6 @@ table.vu td { } +#update-session-invite-musicians { + margin: 10px; +} \ No newline at end of file diff --git a/web/app/controllers/api_invitations_controller.rb b/web/app/controllers/api_invitations_controller.rb index 266f97d6d..ac487c8a9 100644 --- a/web/app/controllers/api_invitations_controller.rb +++ b/web/app/controllers/api_invitations_controller.rb @@ -14,8 +14,11 @@ class ApiInvitationsController < ApiController if current_user.id != sender_id raise PermissionError, "You can only ask for your own sent invitations" end - - @invitations = Invitation.where(:sender_id => current_user.id) + if session_id = params[:session_id] + @invitations = Invitation.where(:sender_id => sender_id, :music_session_id => session_id) + else + @invitations = Invitation.where(:sender_id => current_user.id) + end elsif !receiver_id.nil? if current_user.id != receiver_id raise PermissionError, "You can only ask for your own received invitations" @@ -34,25 +37,33 @@ class ApiInvitationsController < ApiController sender = current_user join_request = JoinRequest.find(params[:join_request]) unless params[:join_request].nil? - @invitation = Invitation.new - @invitation.music_session = music_session - @invitation.sender = sender - @invitation.receiver = receiver - @invitation.join_request = join_request - - @invitation.save - - unless @invitation.errors.any? - User.save_session_settings(current_user, music_session) - - # send notification - Notification.send_session_invitation(receiver, current_user, music_session.id) + @invitation = Invitation.limit(1) + .where(:receiver_id => params[:receiver], + :sender_id => current_user.id, + :music_session_id => params[:music_session]) + .first + if @invitation respond_with @invitation, :responder => ApiResponder, :location => api_invitation_detail_url(@invitation) - else - # we have to do this because api_invitation_detail_url will fail with a bad @invitation - response.status = :unprocessable_entity - respond_with @invitation + @invitation = Invitation.new + @invitation.music_session = music_session + @invitation.sender = sender + @invitation.receiver = receiver + @invitation.join_request = join_request + @invitation.save + + unless @invitation.errors.any? + User.save_session_settings(current_user, music_session) + + # send notification + Notification.send_session_invitation(receiver, current_user, music_session.id) + respond_with @invitation, :responder => ApiResponder, :location => api_invitation_detail_url(@invitation) + + else + # we have to do this because api_invitation_detail_url will fail with a bad @invitation + response.status = :unprocessable_entity + respond_with @invitation + end end end diff --git a/web/app/views/clients/_createSession.html.erb b/web/app/views/clients/_createSession.html.erb index abacce414..7a0b3f565 100644 --- a/web/app/views/clients/_createSession.html.erb +++ b/web/app/views/clients/_createSession.html.erb @@ -84,22 +84,7 @@