diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index 7ff867717..949360c32 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -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 diff --git a/web/app/assets/javascripts/jamkazam.js b/web/app/assets/javascripts/jamkazam.js index 0a1771068..2d184efb6 100644 --- a/web/app/assets/javascripts/jamkazam.js +++ b/web/app/assets/javascripts/jamkazam.js @@ -232,7 +232,7 @@ } } else { - logger.error("Unexpected ajax error: " + textStatus + ", msg:" + errorMessage); + app.notify({title: textStatus, text: errorMessage, detail: jqXHR.responseText}); } } @@ -293,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 { diff --git a/web/app/assets/javascripts/profile.js b/web/app/assets/javascripts/profile.js index 2b5a9d509..8924d9ae5 100644 --- a/web/app/assets/javascripts/profile.js +++ b/web/app/assets/javascripts/profile.js @@ -1,645 +1,709 @@ -(function(context,$) { - - "use strict"; - - context.JK = context.JK || {}; - context.JK.ProfileScreen = function(app) { - var logger = context.JK.logger; - var userId; - var user = null; - var userDefer = null; - var rest = context.JK.Rest(); - var decrementedFriendCountOnce = false; - var sentFriendRequest = false; - - var instrument_logo_map = context.JK.getInstrumentIconMap24(); - - var proficiencyDescriptionMap = { - "1": "BEGINNER", - "2": "INTERMEDIATE", - "3": "EXPERT" - }; - - var proficiencyCssMap = { - "1": "proficiency-beginner", - "2": "proficiency-intermediate", - "3": "proficiency-expert" - }; - - function beforeShow(data) { - userId = data.id; - } - - function afterShow(data) { - initUser(); - resetForm(); - } - - function resetForm() { - $('#profile-instruments').empty(); - - $('#profile-about').show(); - $('#profile-history').hide(); - $('#profile-bands').hide(); - $('#profile-social').hide(); - $('#profile-favorites').hide(); - - $('.profile-nav a.active').removeClass('active'); - $('.profile-nav a#profile-about-link').addClass('active'); - } - - function initUser() { - user = null; - decrementedFriendCountOnce = false; - sentFriendRequest = false; - userDefer = rest.getUserDetail({id: userId}) - .done(function(response) { - user = response; - configureUserType(); - renderActive(); - }) - .fail(function(jqXHR) { - if(jqXHR.status >= 500) { - context.JK.fetchUserNetworkOrServerFailure(); - } - else if(jqXHR.status == 404) { - context.JK.entityNotFound("User"); - } - else { - app.ajaxError(arguments); - } - }); - } - - function isMusician() { - return user.musician; - } - - function isCurrentUser() { - return userId === context.JK.currentUserId; - } - - function configureUserType() { - if (isMusician()) { - $('#profile-history-link').show(); - $('#profile-bands-link').show(); - $('#profile-instruments').show(); - $('#profile-session-stats').show(); - $('#profile-recording-stats').show(); - // $('#profile-following-stats').hide(); - // $('#profile-favorites-stats').hide(); - $('.profile-social-left').show(); - $('#profile-type-label').text('musician'); - $('#profile-location-label').text('Location'); - } - else { - $('#profile-history-link').hide(); - $('#profile-bands-link').hide(); - $('#profile-instruments').hide(); - $('#profile-session-stats').hide(); - $('#profile-recording-stats').hide(); - // $('#profile-following-stats').show(); - // $('#profile-favorites-stats').show(); - $('.profile-social-left').hide(); - $('#profile-type-label').text('fan'); - $('#profile-location-label').text('Presence'); - } - - if (isCurrentUser()) { - $('#btn-profile-edit').show(); - $('#btn-add-friend').hide(); - $('#btn-follow-user').hide(); - } - else { - configureFriendFollowersControls(); - - $('#btn-profile-edit').hide(); - $('#btn-add-friend').show(); - $('#btn-follow-user').show(); - } - } - - function configureFriendFollowersControls() { - // wire up Add Friend click - configureFriendButton(); - - // wire up Follow click - configureFollowingButton(); - } - - /****************** MAIN PORTION OF SCREEN *****************/ - // events for main screen - function events() { - // wire up panel clicks -- these need to check deferred because they can't be hidden when in an invalid state - $('#profile-about-link').click(function(){ renderTabDeferred(renderAbout)}); - $('#profile-history-link').click(function(){ renderTabDeferred(renderHistory)}); - $('#profile-bands-link').click(function(){ renderTabDeferred(renderBands)}); - $('#profile-social-link').click(function(){ renderTabDeferred(renderSocial)}); - $('#profile-favorites-link').click(function(){ renderTabDeferred(renderFavorites)}); - - // this doesn't need deferred because it's only shown when valid - $('#btn-add-friend').click(handleFriendChange); - $('#btn-follow-user').click(handleFollowingChange); - } - - function handleFriendChange(evt) { - if(isFriend()) { - removeFriend(evt); - } - else { - sendFriendRequest(evt); - } - } - - function handleFollowingChange(evt) { - if (isFollowing()) { - removeFollowing(false, userId); - } - else { - addFollowing(); - } - } - - function sendFriendRequest(evt) { - evt.stopPropagation(); - setFriend(true); // TODO: you aren't a friend yet. just a request to be one really there are 3 states here. - sentFriendRequest = true; - rest.sendFriendRequest(app, userId, friendRequestCallback); - } - - function removeFriend(evt) { - evt.stopPropagation(); - - rest.removeFriend({friend_id: userId}) - .done(function() { - updateFriendCount(-1); - setFriend(false); - configureFriendButton(); - }) - .fail(app.ajaxError); - } - - function isFriend() { - return user.is_friend; - } - - function setFriend(isFriend) { - user.is_friend = isFriend; - } - - function friendRequestCallback() { - configureFriendButton(); - } - - function configureFriendButton() { - if (isFriend()) { - $('#btn-add-friend').text('DISCONNECT'); - } - else { - $('#btn-add-friend').text('CONNECT'); - } - } - - function addFollowing() { - - rest.addFollowing({user_id: userId}) - .done(function() { - updateFollowingCount(1); - setFollowing(true); - configureFollowingButton(); - context.JK.GA.trackJKSocial(context.JK.GA.Categories.jkFollow, isMusician() ? context.JK.GA.JKSocialTargets.musician : context.JK.GA.JKSocialTargets.fan); - }) - .fail(app.ajaxError); - } - - function removeFollowing(isBand, id) { - rest.removeFollowing(id) - .done(function() { - if (!isBand) { - updateFollowingCount(-1); - setFollowing(false); - configureFollowingButton(); - } - else { - updateBandFollowingCount(id, -1); // refresh stats - configureBandFollowingButton(false, id); - } - }) - .fail(app.ajaxError); - } - - function isFollowing() { - return user.is_following; - } - - function setFollowing(isFollowing) { - user.is_following = isFollowing; - } - - function configureFollowingButton() { - - if (isFollowing()) { - $('#btn-follow-user').text('UNFOLLOW'); - } - else { - $('#btn-follow-user').text('FOLLOW'); - } - } - - function configureEditProfileButton() { - $('#btn-follow-user').click(addFollowing); - } - - // refreshes the currently active tab - function renderActive() { - if ($('#profile-about-link').hasClass('active')) { - renderAbout(); - } - else if ($('#profile-history-link').hasClass('active')) { - renderHistory(); - } - else if ($('#profile-bands-link').hasClass('active')) { - renderBands(); - } - else if ($('#profile-social-link').hasClass('active')) { - renderSocial(); - } - else if ($('#profile-favorites-link').hasClass('active')) { - renderFavorites(); - } - } - - function renderTabDeferred(tabRenderer) { - userDefer - .done(function() { - tabRenderer(); - }) - .fail(function() { - // try again - initUser(); - }) - } - - /****************** ABOUT TAB *****************/ - function renderAbout() { - $('#profile-instruments').empty(); - - $('#profile-about').show(); - $('#profile-history').hide(); - $('#profile-bands').hide(); - $('#profile-social').hide(); - $('#profile-favorites').hide(); - - $('.profile-nav a.active').removeClass('active'); - $('.profile-nav a#profile-about-link').addClass('active'); - - bindAbout(); - } - - function bindAbout() { - $('#profile-instruments').empty(); - - // name - $('#profile-username').html(user.name); - - // avatar - $('#profile-avatar').attr('src', context.JK.resolveAvatarUrl(user.photo_url)); - - // instruments - if (user.instruments) { - for (var i=0; i < user.instruments.length; i++) { - var instrument = user.instruments[i]; - var description = instrument.instrument_id; - var proficiency = instrument.proficiency_level; - var instrument_icon_url = context.JK.getInstrumentIcon45(description); - - // add instrument info to layout - var template = $('#template-profile-instruments').html(); - var instrumentHtml = context.JK.fillTemplate(template, { - instrument_logo_url: instrument_icon_url, - instrument_description: description, - proficiency_level: proficiencyDescriptionMap[proficiency], - proficiency_level_css: proficiencyCssMap[proficiency] - }); - - $('#profile-instruments').append(instrumentHtml); - } - } - - // location - $('#profile-location').html(user.location); - - // stats - var text = user.friend_count > 1 || user.friend_count === 0 ? " Friends" : " Friend"; - $('#profile-friend-stats').html('' + user.friend_count + '' + text); - - text = user.follower_count > 1 || user.follower_count === 0 ? " Followers" : " Follower"; - $('#profile-follower-stats').html('' + user.follower_count + '' + text); - - if (isMusician()) { - text = user.session_count > 1 || user.session_count === 0 ? " Sessions" : " Session"; - $('#profile-session-stats').html(user.session_count + text); - - text = user.recording_count > 1 || user.recording_count === 0 ? " Recordings" : " Recording"; - $('#profile-recording-stats').html(user.recording_count + text); - } else { - text = " Following"; - $('#profile-following-stats').html(user.following_count + text); - text = user.favorite_count > 1 || user.favorite_count === 0 ? " Favorites" : " Favorite"; - $('#profile-favorite-stats').html(user.favorite_count + text); - } - - $('#profile-biography').html(user.biography); - } - - /****************** SOCIAL TAB *****************/ - function renderSocial() { - $('#profile-social-friends').empty(); - $('#profile-social-followings').empty(); - $('#profile-social-followers').empty(); - - $('#profile-about').hide(); - $('#profile-history').hide(); - $('#profile-bands').hide(); - $('#profile-social').show(); - $('#profile-favorites').hide(); - - $('.profile-nav a.active').removeClass('active'); - $('.profile-nav a#profile-social-link').addClass('active'); - - bindSocial(); - } - - function bindSocial() { - if (isMusician()) { - // FRIENDS - rest.getFriends({id:userId}) - .done(function(response) { - $.each(response, function(index, val) { - var template = $('#template-profile-social').html(); - var friendHtml = context.JK.fillTemplate(template, { - userId: val.id, - hoverAction: val.musician ? "musician" : "fan", - avatar_url: context.JK.resolveAvatarUrl(val.photo_url), - userName: val.name, - location: val.location, - type: "Friends" - }); - - $('#profile-social-friends').append(friendHtml); - }); - context.JK.bindHoverEvents(); - }) - .fail(app.ajaxError) - } - - rest.getFollowings({id: userId}) - .done(function(response) { - $.each(response, function(index, val) { - var template = $('#template-profile-social').html(); - var followingHtml = context.JK.fillTemplate(template, { - userId: val.id, - hoverAction: val.musician ? "musician" : "fan", - avatar_url: context.JK.resolveAvatarUrl(val.photo_url), - userName: val.name, - location: val.location - }); - - $('#profile-social-followings').append(followingHtml); - }); - context.JK.bindHoverEvents(); - }) - .fail(app.ajaxError); - - rest.getFollowers({id: userId}) - .done(function(response) { - $.each(response, function(index, val) { - var template = $('#template-profile-social').html(); - var followerHtml = context.JK.fillTemplate(template, { - userId: val.id, - hoverAction: val.musician ? "musician" : "fan", - avatar_url: context.JK.resolveAvatarUrl(val.photo_url), - userName: val.name, - location: val.location - }); - - $('#profile-social-followers').append(followerHtml); - }); - context.JK.bindHoverEvents(); - }) - .fail(app.ajaxError); - } - - /****************** HISTORY TAB *****************/ - function renderHistory() { - $('#profile-about').hide(); - $('#profile-history').show(); - $('#profile-bands').hide(); - $('#profile-social').hide(); - $('#profile-favorites').hide(); - - $('.profile-nav a.active').removeClass('active'); - $('.profile-nav a#profile-history-link').addClass('active'); - - bindHistory(); - } - - function bindHistory() { - - } - - /****************** BANDS TAB *****************/ - function renderBands() { - $('#profile-bands').empty(); - - $('#profile-about').hide(); - $('#profile-history').hide(); - $('#profile-bands').show(); - $('#profile-social').hide(); - $('#profile-favorites').hide(); - - $('.profile-nav a.active').removeClass('active'); - $('.profile-nav a#profile-bands-link').addClass('active'); - - bindBands(); - } - - function bindBands() { - - rest.getBands({id:userId}) - .done(function(response) { - if ( (!response || response.length === 0) && isCurrentUser()) { - var noBandHtml = $('#template-no-bands').html(); - $("#profile-bands").html(noBandHtml); - } - else { - addMoreBandsLink(); - - $.each(response, function(index, val) { - - // build band member HTML - var musicianHtml = ''; - if (val.musicians) { - for (var i=0; i < val.musicians.length; i++) { - var musician = val.musicians[i]; - var instrumentLogoHtml = ''; - if (musician.instruments) { - for (var j=0; j < musician.instruments.length; j++) { - var instrument = musician.instruments[j]; - var inst = '/assets/content/icon_instrument_default24.png'; - if (instrument.instrument_id in instrument_logo_map) { - inst = instrument_logo_map[instrument.instrument_id]; - } - instrumentLogoHtml += ' '; - } - } - // this template is in _findSession.html.erb - var musicianTemplate = $('#template-musician-info').html(); - musicianHtml += context.JK.fillTemplate(musicianTemplate, { - userId: musician.id, - avatar_url: context.JK.resolveAvatarUrl(musician.photo_url), - profile_url: "/client#/profile/" + musician.id, - musician_name: musician.name, - instruments: instrumentLogoHtml - }); - } - } - var template = $('#template-profile-bands').html(); - var bandHtml = context.JK.fillTemplate(template, { - bandId: val.id, - biography: val.biography, - band_url: "/client#/bandProfile/" + val.id, - avatar_url: context.JK.resolveBandAvatarUrl(val.photo_url), - name: val.name, - location: val.location, - genres: formatGenres(val.genres), - follower_count: val.follower_count, - recording_count: val.recording_count, - session_count: val.session_count, - musicians: musicianHtml - }); - - $('#profile-bands').append(bandHtml); - - // wire up Band Follow button click handler - configureBandFollowingButton(val.is_following, val.id); - }); - - if(response.length >= 3) { - addMoreBandsLink(); - } - } - context.JK.bindHoverEvents(); - }) - .fail(app.ajaxError); - } - - function addMoreBandsLink() { - if (isCurrentUser()) { - var moreBandsHtml = $('#template-more-bands').html(); - $("#profile-bands").append(moreBandsHtml); - } - } - - function formatGenres(genres) { - var formattedGenres = ''; - if (genres) { - for (var i=0; i < genres.length; i++) { - var genre = genres[i]; - formattedGenres += genre.description; - if (i < genres.length -1) { - formattedGenres += ', '; - } - } - } - return formattedGenres; - } - - function updateFriendCount(value) { - if(!decrementedFriendCountOnce && !sentFriendRequest) { - decrementedFriendCountOnce = true; - var friendCount = $('#profile-friend-stats span.friend-count'); - friendCount.text(value + parseInt(friendCount.text())); - } - } - - function updateFollowingCount(value) { - var followingCount = $('#profile-follower-stats span.follower-count'); - followingCount.text(value + parseInt(followingCount.text())); - } - - function updateBandFollowingCount(bandId, value) { - var bandFollowing = $('div[band-id="' + bandId + '"].profile-bands span.follower-count'); - bandFollowing.text(value + parseInt(bandFollowing.text())); - } - - function addBandFollowing(evt) { - evt.stopPropagation(); - var bandId = $(this).parent().parent().parent().parent().attr('band-id'); - - var newFollowing = {}; - newFollowing.band_id = bandId; - - rest.addFollowing(newFollowing) - .done(function(response) { - logger.debug("following band " + bandId); - updateBandFollowingCount(bandId, 1); // increase counter - configureBandFollowingButton(true, bandId); - context.JK.GA.trackJKSocial(context.JK.GA.Categories.jkFollow, context.JK.GA.JKSocialTargets.band); - }) - .fail(app.ajaxError); - } - - function configureBandFollowingButton(following, bandId) { - var $btnFollowBand = $('div[band-id=' + bandId + ']', '#profile-bands').find('#btn-follow-band-2'); - $btnFollowBand.unbind("click"); - - if (following) { - $btnFollowBand.text('UN-FOLLOW'); - $btnFollowBand.click(function(evt) { - removeFollowing(true, bandId); - evt.stopPropagation(); - return false; - }); - } - else { - $btnFollowBand.text('FOLLOW'); - $btnFollowBand.click(addBandFollowing); - } - } - - /****************** FAVORITES TAB *****************/ - function renderFavorites() { - $('#profile-about').hide(); - $('#profile-history').hide(); - $('#profile-bands').hide(); - $('#profile-social').hide(); - $('#profile-favorites').show(); - - $('.profile-nav a.active').removeClass('active'); - $('.profile-nav a#profile-favorites-link').addClass('active'); - - bindFavorites(); - } - - - function bindFavorites() { - } - - function initialize() { - var screenBindings = { - 'beforeShow': beforeShow, - 'afterShow': afterShow - }; - app.bindScreen('profile', screenBindings); - - events(); - } - - this.initialize = initialize; - this.beforeShow = beforeShow; - this.afterShow = afterShow; - return this; +(function (context, $) { + + "use strict"; + + context.JK = context.JK || {}; + context.JK.ProfileScreen = function (app) { + var logger = context.JK.logger; + var userId; + var user = null; + var userDefer = null; + var rest = context.JK.Rest(); + var decrementedFriendCountOnce = false; + var sentFriendRequest = false; + + var instrument_logo_map = context.JK.getInstrumentIconMap24(); + + var proficiencyDescriptionMap = { + "1": "BEGINNER", + "2": "INTERMEDIATE", + "3": "EXPERT" }; - })(window,jQuery); \ No newline at end of file + var proficiencyCssMap = { + "1": "proficiency-beginner", + "2": "proficiency-intermediate", + "3": "proficiency-expert" + }; + + function beforeShow(data) { + userId = data.id; + } + + function afterShow(data) { + initUser(); + resetForm(); + } + + function resetForm() { + $('#profile-instruments').empty(); + + $('#profile-about').show(); + $('#profile-history').hide(); + $('#profile-bands').hide(); + $('#profile-social').hide(); + $('#profile-favorites').hide(); + + $('.profile-nav a.active').removeClass('active'); + $('.profile-nav a#profile-about-link').addClass('active'); + } + + function initUser() { + user = null; + decrementedFriendCountOnce = false; + sentFriendRequest = false; + userDefer = rest.getUserDetail({id: userId}) + .done(function (response) { + user = response; + configureUserType(); + renderActive(); + }) + .fail(function (jqXHR) { + if (jqXHR.status >= 500) { + context.JK.fetchUserNetworkOrServerFailure(); + } + else if (jqXHR.status == 404) { + context.JK.entityNotFound("User"); + } + else { + app.ajaxError(arguments); + } + }); + } + + function isMusician() { + return user.musician; + } + + function isCurrentUser() { + return userId === context.JK.currentUserId; + } + + function configureUserType() { + if (isMusician()) { + $('#profile-history-link').show(); + $('#profile-bands-link').show(); + $('#profile-instruments').show(); + $('#profile-session-stats').show(); + $('#profile-recording-stats').show(); + // $('#profile-following-stats').hide(); + // $('#profile-favorites-stats').hide(); + $('.profile-social-left').show(); + $('#profile-type-label').text('musician'); + $('#profile-location-label').text('Location'); + } + else { + $('#profile-history-link').hide(); + $('#profile-bands-link').hide(); + $('#profile-instruments').hide(); + $('#profile-session-stats').hide(); + $('#profile-recording-stats').hide(); + // $('#profile-following-stats').show(); + // $('#profile-favorites-stats').show(); + $('.profile-social-left').hide(); + $('#profile-type-label').text('fan'); + $('#profile-location-label').text('Presence'); + } + + if (isCurrentUser()) { + $('#btn-profile-edit').show(); + $('#btn-add-friend').hide(); + $('#btn-follow-user').hide(); + } + else { + configureFriendFollowersControls(); + + $('#btn-profile-edit').hide(); + $('#btn-add-friend').show(); + $('#btn-follow-user').show(); + } + } + + function configureFriendFollowersControls() { + // wire up Add Friend click + configureFriendButton(); + + // wire up Follow click + configureFollowingButton(); + } + + /****************** MAIN PORTION OF SCREEN *****************/ + // events for main screen + function events() { + // wire up panel clicks -- these need to check deferred because they can't be hidden when in an invalid state + $('#profile-about-link').click(function () { + renderTabDeferred(renderAbout) + }); + $('#profile-history-link').click(function () { + renderTabDeferred(renderHistory) + }); + $('#profile-bands-link').click(function () { + renderTabDeferred(renderBands) + }); + $('#profile-social-link').click(function () { + renderTabDeferred(renderSocial) + }); + $('#profile-favorites-link').click(function () { + renderTabDeferred(renderFavorites) + }); + + // this doesn't need deferred because it's only shown when valid + $('#btn-add-friend').click(handleFriendChange); + $('#btn-follow-user').click(handleFollowingChange); + } + + function handleFriendChange(evt) { + if (isFriend()) { + removeFriend(evt); + } + else { + sendFriendRequest(evt); + } + } + + function handleFollowingChange(evt) { + if (isFollowing()) { + removeFollowing(false, userId); + } + else { + addFollowing(); + } + } + + function sendFriendRequest(evt) { + evt.stopPropagation(); + setFriend(true); // TODO: you aren't a friend yet. just a request to be one really there are 3 states here. + sentFriendRequest = true; + rest.sendFriendRequest(app, userId, friendRequestCallback); + } + + function removeFriend(evt) { + evt.stopPropagation(); + + rest.removeFriend({friend_id: userId}) + .done(function () { + updateFriendCount(-1); + setFriend(false); + configureFriendButton(); + }) + .fail(app.ajaxError); + } + + function isFriend() { + return user.is_friend; + } + + function setFriend(isFriend) { + user.is_friend = isFriend; + } + + function friendRequestCallback() { + configureFriendButton(); + } + + function configureFriendButton() { + if (isFriend()) { + $('#btn-add-friend').text('DISCONNECT'); + } + else { + $('#btn-add-friend').text('CONNECT'); + } + } + + function addFollowing() { + + rest.addFollowing({user_id: userId}) + .done(function () { + updateFollowingCount(1); + setFollowing(true); + configureFollowingButton(); + context.JK.GA.trackJKSocial(context.JK.GA.Categories.jkFollow, isMusician() ? context.JK.GA.JKSocialTargets.musician : context.JK.GA.JKSocialTargets.fan); + }) + .fail(app.ajaxError); + } + + function removeFollowing(isBand, id) { + rest.removeFollowing(id) + .done(function () { + if (!isBand) { + updateFollowingCount(-1); + setFollowing(false); + configureFollowingButton(); + } + else { + updateBandFollowingCount(id, -1); // refresh stats + configureBandFollowingButton(false, id); + } + }) + .fail(app.ajaxError); + } + + function isFollowing() { + return user.is_following; + } + + function setFollowing(isFollowing) { + user.is_following = isFollowing; + } + + function configureFollowingButton() { + + if (isFollowing()) { + $('#btn-follow-user').text('UNFOLLOW'); + } + else { + $('#btn-follow-user').text('FOLLOW'); + } + } + + function configureEditProfileButton() { + $('#btn-follow-user').click(addFollowing); + } + + // refreshes the currently active tab + function renderActive() { + if ($('#profile-about-link').hasClass('active')) { + renderAbout(); + } + else if ($('#profile-history-link').hasClass('active')) { + renderHistory(); + } + else if ($('#profile-bands-link').hasClass('active')) { + renderBands(); + } + else if ($('#profile-social-link').hasClass('active')) { + renderSocial(); + } + else if ($('#profile-favorites-link').hasClass('active')) { + renderFavorites(); + } + } + + function renderTabDeferred(tabRenderer) { + userDefer + .done(function () { + tabRenderer(); + }) + .fail(function () { + // try again + initUser(); + }) + } + + /****************** ABOUT TAB *****************/ + function renderAbout() { + $('#profile-instruments').empty(); + + $('#profile-about').show(); + $('#profile-history').hide(); + $('#profile-bands').hide(); + $('#profile-social').hide(); + $('#profile-favorites').hide(); + + $('.profile-nav a.active').removeClass('active'); + $('.profile-nav a#profile-about-link').addClass('active'); + + bindAbout(); + } + + function bindAbout() { + $('#profile-instruments').empty(); + + // name + $('#profile-username').html(user.name); + + // avatar + $('#profile-avatar').attr('src', context.JK.resolveAvatarUrl(user.photo_url)); + + // instruments + if (user.instruments) { + for (var i = 0; i < user.instruments.length; i++) { + var instrument = user.instruments[i]; + var description = instrument.instrument_id; + var proficiency = instrument.proficiency_level; + var instrument_icon_url = context.JK.getInstrumentIcon45(description); + + // add instrument info to layout + var template = $('#template-profile-instruments').html(); + var instrumentHtml = context.JK.fillTemplate(template, { + instrument_logo_url: instrument_icon_url, + instrument_description: description, + proficiency_level: proficiencyDescriptionMap[proficiency], + proficiency_level_css: proficiencyCssMap[proficiency] + }); + + $('#profile-instruments').append(instrumentHtml); + } + } + + // location + $('#profile-location').html(user.location); + + // stats + var text = user.friend_count > 1 || user.friend_count === 0 ? " Friends" : " Friend"; + $('#profile-friend-stats').html('' + user.friend_count + '' + text); + + text = user.follower_count > 1 || user.follower_count === 0 ? " Followers" : " Follower"; + $('#profile-follower-stats').html('' + user.follower_count + '' + text); + + if (isMusician()) { + text = user.session_count > 1 || user.session_count === 0 ? " Sessions" : " Session"; + $('#profile-session-stats').html(user.session_count + text); + + text = user.recording_count > 1 || user.recording_count === 0 ? " Recordings" : " Recording"; + $('#profile-recording-stats').html(user.recording_count + text); + } else { + text = " Following"; + $('#profile-following-stats').html(user.following_count + text); + text = user.favorite_count > 1 || user.favorite_count === 0 ? " Favorites" : " Favorite"; + $('#profile-favorite-stats').html(user.favorite_count + text); + } + + if(isCurrentUser) { + if(user.biography) { + $('#profile-biography').text(user.biography); + } + else { + var createBio = $(context._.template($('#template-add-user-profile').html(), {}, { variable: 'data' })); + $('a.enter-bio', createBio).click(function() { + $('.update-biography').show(); + return false; + }); + + $('#btn-update-user-biography', createBio).click(function() { + var $bio = $('#profile-biography .user-biography'); + var bio = $bio.val(); + $bio.closest('div.field').removeClass('error'); + $('.error-text', $bio.closest('div.field')).remove(); + rest.updateUser({ + biography: bio + }) + .done(function(response) { + if(response.biography){ + $('#profile-biography').text(response.biography); + } else { + $('.update-biography').hide(); + } + }) + .fail(function(jqXHR) { + if(jqXHR.status == 422) { + var errors = JSON.parse(jqXHR.responseText) + var biography = context.JK.format_errors("biography", errors); + if(biography != null) { + $bio.closest('div.field').addClass('error').end().after(biography); + } + else { + app.notifyServerError(jqXHR, "Unable to update biography") + } + } + else { + app.notifyServerError(jqXHR, "Unable to update biography") + } + }) + return false; + }); + + $('#btn-cancel-user-biography', createBio).click(function() { + $('.update-biography').hide(); + return false; + }); + $('#profile-biography').html(createBio); + } + } + else { + $('#profile-biography').text(user.biography); + } + + } + + /****************** SOCIAL TAB *****************/ + function renderSocial() { + $('#profile-social-friends').empty(); + $('#profile-social-followings').empty(); + $('#profile-social-followers').empty(); + + $('#profile-about').hide(); + $('#profile-history').hide(); + $('#profile-bands').hide(); + $('#profile-social').show(); + $('#profile-favorites').hide(); + + $('.profile-nav a.active').removeClass('active'); + $('.profile-nav a#profile-social-link').addClass('active'); + + bindSocial(); + } + + function bindSocial() { + if (isMusician()) { + // FRIENDS + rest.getFriends({id: userId}) + .done(function (response) { + $.each(response, function (index, val) { + var template = $('#template-profile-social').html(); + var friendHtml = context.JK.fillTemplate(template, { + userId: val.id, + hoverAction: val.musician ? "musician" : "fan", + avatar_url: context.JK.resolveAvatarUrl(val.photo_url), + userName: val.name, + location: val.location, + type: "Friends" + }); + + $('#profile-social-friends').append(friendHtml); + }); + context.JK.bindHoverEvents(); + }) + .fail(app.ajaxError) + } + + rest.getFollowings({id: userId}) + .done(function (response) { + $.each(response, function (index, val) { + var template = $('#template-profile-social').html(); + var followingHtml = context.JK.fillTemplate(template, { + userId: val.id, + hoverAction: val.musician ? "musician" : "fan", + avatar_url: context.JK.resolveAvatarUrl(val.photo_url), + userName: val.name, + location: val.location + }); + + $('#profile-social-followings').append(followingHtml); + }); + context.JK.bindHoverEvents(); + }) + .fail(app.ajaxError); + + rest.getFollowers({id: userId}) + .done(function (response) { + $.each(response, function (index, val) { + var template = $('#template-profile-social').html(); + var followerHtml = context.JK.fillTemplate(template, { + userId: val.id, + hoverAction: val.musician ? "musician" : "fan", + avatar_url: context.JK.resolveAvatarUrl(val.photo_url), + userName: val.name, + location: val.location + }); + + $('#profile-social-followers').append(followerHtml); + }); + context.JK.bindHoverEvents(); + }) + .fail(app.ajaxError); + } + + /****************** HISTORY TAB *****************/ + function renderHistory() { + $('#profile-about').hide(); + $('#profile-history').show(); + $('#profile-bands').hide(); + $('#profile-social').hide(); + $('#profile-favorites').hide(); + + $('.profile-nav a.active').removeClass('active'); + $('.profile-nav a#profile-history-link').addClass('active'); + + bindHistory(); + } + + function bindHistory() { + + } + + /****************** BANDS TAB *****************/ + function renderBands() { + $('#profile-bands').empty(); + + $('#profile-about').hide(); + $('#profile-history').hide(); + $('#profile-bands').show(); + $('#profile-social').hide(); + $('#profile-favorites').hide(); + + $('.profile-nav a.active').removeClass('active'); + $('.profile-nav a#profile-bands-link').addClass('active'); + + bindBands(); + } + + function bindBands() { + + rest.getBands({id: userId}) + .done(function (response) { + if ((!response || response.length === 0) && isCurrentUser()) { + var noBandHtml = $('#template-no-bands').html(); + $("#profile-bands").html(noBandHtml); + } + else { + addMoreBandsLink(); + + $.each(response, function (index, val) { + + // build band member HTML + var musicianHtml = ''; + if (val.musicians) { + for (var i = 0; i < val.musicians.length; i++) { + var musician = val.musicians[i]; + var instrumentLogoHtml = ''; + if (musician.instruments) { + for (var j = 0; j < musician.instruments.length; j++) { + var instrument = musician.instruments[j]; + var inst = '/assets/content/icon_instrument_default24.png'; + if (instrument.instrument_id in instrument_logo_map) { + inst = instrument_logo_map[instrument.instrument_id]; + } + instrumentLogoHtml += ' '; + } + } + // this template is in _findSession.html.erb + var musicianTemplate = $('#template-musician-info').html(); + musicianHtml += context.JK.fillTemplate(musicianTemplate, { + userId: musician.id, + avatar_url: context.JK.resolveAvatarUrl(musician.photo_url), + profile_url: "/client#/profile/" + musician.id, + musician_name: musician.name, + instruments: instrumentLogoHtml + }); + } + } + var template = $('#template-profile-bands').html(); + var bandHtml = context.JK.fillTemplate(template, { + bandId: val.id, + biography: val.biography, + band_url: "/client#/bandProfile/" + val.id, + avatar_url: context.JK.resolveBandAvatarUrl(val.photo_url), + name: val.name, + location: val.location, + genres: formatGenres(val.genres), + follower_count: val.follower_count, + recording_count: val.recording_count, + session_count: val.session_count, + musicians: musicianHtml + }); + + $('#profile-bands').append(bandHtml); + + // wire up Band Follow button click handler + configureBandFollowingButton(val.is_following, val.id); + }); + + if (response.length >= 3) { + addMoreBandsLink(); + } + } + context.JK.bindHoverEvents(); + }) + .fail(app.ajaxError); + } + + function addMoreBandsLink() { + if (isCurrentUser()) { + var moreBandsHtml = $('#template-more-bands').html(); + $("#profile-bands").append(moreBandsHtml); + } + } + + function formatGenres(genres) { + var formattedGenres = ''; + if (genres) { + for (var i = 0; i < genres.length; i++) { + var genre = genres[i]; + formattedGenres += genre.description; + if (i < genres.length - 1) { + formattedGenres += ', '; + } + } + } + return formattedGenres; + } + + function updateFriendCount(value) { + if (!decrementedFriendCountOnce && !sentFriendRequest) { + decrementedFriendCountOnce = true; + var friendCount = $('#profile-friend-stats span.friend-count'); + friendCount.text(value + parseInt(friendCount.text())); + } + } + + function updateFollowingCount(value) { + var followingCount = $('#profile-follower-stats span.follower-count'); + followingCount.text(value + parseInt(followingCount.text())); + } + + function updateBandFollowingCount(bandId, value) { + var bandFollowing = $('div[band-id="' + bandId + '"].profile-bands span.follower-count'); + bandFollowing.text(value + parseInt(bandFollowing.text())); + } + + function addBandFollowing(evt) { + evt.stopPropagation(); + var bandId = $(this).parent().parent().parent().parent().attr('band-id'); + + var newFollowing = {}; + newFollowing.band_id = bandId; + + rest.addFollowing(newFollowing) + .done(function (response) { + logger.debug("following band " + bandId); + updateBandFollowingCount(bandId, 1); // increase counter + configureBandFollowingButton(true, bandId); + context.JK.GA.trackJKSocial(context.JK.GA.Categories.jkFollow, context.JK.GA.JKSocialTargets.band); + }) + .fail(app.ajaxError); + } + + function configureBandFollowingButton(following, bandId) { + var $btnFollowBand = $('div[band-id=' + bandId + ']', '#profile-bands').find('#btn-follow-band-2'); + $btnFollowBand.unbind("click"); + + if (following) { + $btnFollowBand.text('UN-FOLLOW'); + $btnFollowBand.click(function (evt) { + removeFollowing(true, bandId); + evt.stopPropagation(); + return false; + }); + } + else { + $btnFollowBand.text('FOLLOW'); + $btnFollowBand.click(addBandFollowing); + } + } + + /****************** FAVORITES TAB *****************/ + function renderFavorites() { + $('#profile-about').hide(); + $('#profile-history').hide(); + $('#profile-bands').hide(); + $('#profile-social').hide(); + $('#profile-favorites').show(); + + $('.profile-nav a.active').removeClass('active'); + $('.profile-nav a#profile-favorites-link').addClass('active'); + + bindFavorites(); + } + + + function bindFavorites() { + } + + function initialize() { + var screenBindings = { + 'beforeShow': beforeShow, + 'afterShow': afterShow + }; + app.bindScreen('profile', screenBindings); + + events(); + } + + this.initialize = initialize; + this.beforeShow = beforeShow; + this.afterShow = afterShow; + return this; + }; + +})(window, jQuery); \ No newline at end of file diff --git a/web/app/assets/javascripts/utils.js b/web/app/assets/javascripts/utils.js index 8af730244..e0cd22df3 100644 --- a/web/app/assets/javascripts/utils.js +++ b/web/app/assets/javascripts/utils.js @@ -239,11 +239,14 @@ }); } + // 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) { for(var val in vals) template=template.replace(new RegExp('{'+val+'}','g'), vals[val]); diff --git a/web/app/assets/stylesheets/client/profile.css.scss b/web/app/assets/stylesheets/client/profile.css.scss index 7ea0f6f47..8a93e1872 100644 --- a/web/app/assets/stylesheets/client/profile.css.scss +++ b/web/app/assets/stylesheets/client/profile.css.scss @@ -1,5 +1,23 @@ @import "client/common.css.scss"; +#user-profile { + #profile-biography { + a.enter-bio { + + } + + textarea { + width:100%; + height:150px; + padding:0; + } + + .update-biography { + display:none; + } + } +} + .profile-head { } diff --git a/web/app/controllers/api_users_controller.rb b/web/app/controllers/api_users_controller.rb index 697bc2c95..031fe0eeb 100644 --- a/web/app/controllers/api_users_controller.rb +++ b/web/app/controllers/api_users_controller.rb @@ -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? diff --git a/web/app/views/clients/_profile.html.erb b/web/app/views/clients/_profile.html.erb index ae2fba720..908b44667 100644 --- a/web/app/views/clients/_profile.html.erb +++ b/web/app/views/clients/_profile.html.erb @@ -1,5 +1,5 @@ -
+
<%= image_tag "content/icon_profile.png", :size => "19x19" %> @@ -192,3 +192,24 @@
{location}
+ + + + diff --git a/web/lib/tasks/sample_data.rake b/web/lib/tasks/sample_data.rake index 0b430cb47..8022ff53f 100644 --- a/web/lib/tasks/sample_data.rake +++ b/web/lib/tasks/sample_data.rake @@ -135,6 +135,11 @@ def make_bands state: state, country: country, ) + + Genre.order('RANDOM()').limit(rand(3)+1).each do |gg| + bb.genres << gg + end + begin bb.save! rescue