diff --git a/web/app/assets/images/shared/spinner-32.gif b/web/app/assets/images/shared/spinner-32.gif new file mode 100644 index 000000000..bd39fb1a9 Binary files /dev/null and b/web/app/assets/images/shared/spinner-32.gif differ diff --git a/web/app/assets/javascripts/application.js b/web/app/assets/javascripts/application.js index e180fa24f..e96b3b6fe 100644 --- a/web/app/assets/javascripts/application.js +++ b/web/app/assets/javascripts/application.js @@ -24,6 +24,8 @@ //= require jquery.clipboard //= require jquery.timeago //= require jquery.easydropdown +//= require jquery.scrollTo +//= require jquery.infinitescroll //= require globals //= require AAB_message_factory //= require AAC_underscore diff --git a/web/app/assets/javascripts/findSession.js b/web/app/assets/javascripts/findSession.js index 24868b217..31421dfcd 100644 --- a/web/app/assets/javascripts/findSession.js +++ b/web/app/assets/javascripts/findSession.js @@ -1,377 +1,483 @@ -(function(context,$) { +(function (context, $) { - "use strict"; + "use strict"; - context.JK = context.JK || {}; - context.JK.FindSessionScreen = function(app) { - var CATEGORY = { - INVITATION : {index: 0, id: "table#sessions-invitations"}, - FRIEND : {index: 1, id: "table#sessions-friends"}, - OTHER : {index: 2, id: "table#sessions-other"} - }; - - var logger = context.JK.logger; - var rest = context.JK.Rest(); - var sessionLatency; - var sessions = {}; - var invitationSessionGroup = {}; - var friendSessionGroup = {}; - var otherSessionGroup = {}; - var sessionCounts = [0, 0, 0]; - var sessionList; - - // for unit tests - function getCategoryEnum() { - return CATEGORY; - } - - function removeSpinner() { - $('
') - - } - - function loadSessions(queryString) { - - addSpinner(); - - // squelch nulls and undefines - queryString = !!queryString ? queryString : ""; - - if(gon.use_cached_session_scores) { - rest.findScoredSessions(app.clientId, queryString) - .done(afterLoadScoredSessions) - .always(removeSpinner) - .fail(app.ajaxError) - } - else { - rest.findSessions(queryString) - .done(afterLoadSessions) - .fail(app.ajaxError) - .always(removeSpinner) - } - - } - - function search() { - logger.debug("Searching for sessions..."); - clearResults(); - var queryString = ''; - - // genre filter - var genres = context.JK.GenreSelectorHelper.getSelectedGenres('#find-session-genre'); - if (genres !== null && genres.length > 0) { - queryString += "genres=" + genres.join(','); - } - - // keyword filter - var keyword = $('#session-keyword-srch').val(); - if (keyword !== null && keyword.length > 0 && keyword !== 'Search by Keyword') { - if (queryString.length > 0) { - queryString += "&"; - } - - queryString += "keyword=" + $('#session-keyword-srch').val(); - } - - loadSessions(queryString); - } - - function refreshDisplay() { - var priorVisible; - - var INVITATION = 'div#sessions-invitations'; - var FRIEND = 'div#sessions-friends'; - var OTHER = 'div#sessions-other'; - - // INVITATION - //logger.debug("sessionCounts[CATEGORY.INVITATION.index]=" + sessionCounts[CATEGORY.INVITATION.index]); - if (sessionCounts[CATEGORY.INVITATION.index] === 0) { - priorVisible = false; - $(INVITATION).hide(); - } - else { - priorVisible = true; - $(INVITATION).show(); - } - - // FRIEND - if (!priorVisible) { - $(FRIEND).removeClass('mt35'); - } - - //logger.debug("sessionCounts[CATEGORY.FRIEND.index]=" + sessionCounts[CATEGORY.FRIEND.index]); - if (sessionCounts[CATEGORY.FRIEND.index] === 0) { - priorVisible = false; - $(FRIEND).hide(); - } - else { - priorVisible = true; - $(FRIEND).show(); - } - - // OTHER - if (!priorVisible) { - $(OTHER).removeClass('mt35'); - } - - //logger.debug("sessionCounts[CATEGORY.OTHER.index]=" + sessionCounts[CATEGORY.OTHER.index]); - if (sessionCounts[CATEGORY.OTHER.index] === 0) { - $(OTHER).hide(); - } - else { - $(OTHER).show(); - } - } - - function afterLoadScoredSessions(sessionList) { - - // display the 'no sessions' banner if appropriate - var $noSessionsFound = $('#sessions-none-found'); - if(sessionList.length == 0) { - $noSessionsFound.show(); - } - else { - $noSessionsFound.hide(); - } - - $.each(sessionList, function(i, session) { - sessions[session.id] = session; - session.latencyInfo - }); - - $.each(sessionList, function(i, session) { - renderSession(session.id); - }) - - context.JK.GA.trackFindSessions(sessionList.length); - } - - - function afterLoadSessions(sessionList) { - - // display the 'no sessions' banner if appropriate - var $noSessionsFound = $('#sessions-none-found'); - if(sessionList.length == 0) { - $noSessionsFound.show(); - } - else { - $noSessionsFound.hide(); - } - - startSessionLatencyChecks(sessionList); - - context.JK.GA.trackFindSessions(sessionList.length); - } - - function startSessionLatencyChecks(sessionList) { - logger.debug("Starting latency checks on " + sessionList.length + " sessions"); - - sessionLatency.subscribe(app.clientId, latencyResponse); - $.each(sessionList, function(index, session) { - sessions[session.id] = session; - sessionLatency.sessionPings(session); - }); - } - - function containsInvitation(session) { - var i, invitation = null; - - if (session !== undefined) { - if ("invitations" in session) { - // user has invitations for this session - for (i=0; i < session.invitations.length; i++) { - invitation = session.invitations[i]; - // session contains an invitation for this user - if (invitation.receiver_id == context.JK.currentUserId) { - return true; - } - } - } - } - - return false; - } - - function containsFriend(session) { - var i, participant = null; - - if (session !== undefined) { - if ("participants" in session) { - for (i=0; i < session.participants.length; i++) { - participant = session.participants[i]; - // this session participant is a friend - if (participant !== null && participant !== undefined && participant.user.is_friend) { - return true; - } - } - } - } - return false; - } - - function latencyResponse(sessionId) { - logger.debug("Received latency response for session " + sessionId); - renderSession(sessionId); - } - - /** - * Not used normally. Allows modular unit testing - * of the renderSession method without having to do - * as much heavy setup. - */ - function setSession(session) { - invitationSessionGroup[session.id] = session; - } - - /** - * Render a single session line into the table. - * It will be inserted at the appropriate place according to the - * sortScore in sessionLatency. - */ - function renderSession(sessionId) { - // store session in the appropriate bucket and increment category counts - var session = sessions[sessionId]; - if (containsInvitation(session)) { - invitationSessionGroup[sessionId] = session; - sessionCounts[CATEGORY.INVITATION.index]++; - } - else if (containsFriend(session)) { - friendSessionGroup[sessionId] = session; - sessionCounts[CATEGORY.FRIEND.index]++; - } - else { - otherSessionGroup[sessionId] = session; - sessionCounts[CATEGORY.OTHER.index]++; - } - - // hack to prevent duplicate rows from being rendered when filtering - var sessionAlreadyRendered = false; - var $tbGroup; - - logger.debug('Rendering session ID = ' + sessionId); - - if (invitationSessionGroup[sessionId] != null) { - $tbGroup = $(CATEGORY.INVITATION.id); - - if ($("table#sessions-invitations tr[id='" + sessionId + "']").length > 0) { - sessionAlreadyRendered = true; - } - } - else if (friendSessionGroup[sessionId] != null) {; - $tbGroup = $(CATEGORY.FRIEND.id); - - if ($("table#sessions-friends tr[id='" + sessionId + "']").length > 0) { - sessionAlreadyRendered = true; - } - } - else if (otherSessionGroup[sessionId] != null) { - $tbGroup = $(CATEGORY.OTHER.id); - - if ($("table#sessions-other tr[id='" + sessionId + "']").length > 0) { - sessionAlreadyRendered = true; - } - } - else { - logger.debug('ERROR: No session with ID = ' + sessionId + ' found.'); - return; - } - - if (!sessionAlreadyRendered) { - var row = sessionList.renderSession(session, sessionLatency, $tbGroup, $('#template-session-row').html(), $('#template-musician-info').html()); - } - - refreshDisplay(); - } - - function beforeShow(data) { - context.JK.GenreSelectorHelper.render('#find-session-genre'); - } - - function afterShow(data) { - clearResults(); - refreshDisplay(); - loadSessions(); - } - - function clearResults() { - $('table#sessions-invitations').children(':not(:first-child)').remove(); - $('table#sessions-friends').children(':not(:first-child)').remove(); - $('table#sessions-other').children(':not(:first-child)').remove(); - - sessionCounts = [0, 0, 0]; - - sessions = {}; - invitationSessionGroup = {}; - friendSessionGroup = {}; - otherSessionGroup = {}; - } - - function deleteSession(evt) { - var sessionId = $(evt.currentTarget).attr("action-id"); - if (sessionId) { - $.ajax({ - type: "DELETE", - url: "/api/sessions/" + sessionId, - error: app.ajaxError - }).done(loadSessions); - } - } - - function events() { - - $('#session-keyword-srch').focus(function() { - $(this).val(''); - }); - - $("#session-keyword-srch").keypress(function(evt) { - if (evt.which === 13) { - evt.preventDefault(); - search(); - } - }); - - $('#btn-refresh').on("click", search); - } - - /** - * Initialize, providing an instance of the SessionLatency class. - */ - function initialize(latency) { - - var screenBindings = { - 'beforeShow': beforeShow, - 'afterShow': afterShow - }; - app.bindScreen('findSession', screenBindings); - - if (latency) { - sessionLatency = latency; - } - else { - logger.warn("No sessionLatency provided."); - } - - sessionList = new context.JK.SessionList(app); - - events(); - } - - this.initialize = initialize; - this.renderSession = renderSession; - this.afterShow = afterShow; - - // Following exposed for easier testing. - this.setSession = setSession; - this.clearResults = clearResults; - this.getCategoryEnum = getCategoryEnum; - - return this; + context.JK = context.JK || {}; + context.JK.FindSessionScreen = function (app) { + var CATEGORY = { + INVITATION: {index: 0, id: "table#sessions-invitations"}, + FRIEND: {index: 1, id: "table#sessions-friends"}, + OTHER: {index: 2, id: "table#sessions-other"} }; - })(window,jQuery); \ No newline at end of file + var logger = context.JK.logger; + var rest = context.JK.Rest(); + var sessionLatency; + var sessions = {}; + var invitationSessionGroup = {}; + var friendSessionGroup = {}; + var otherSessionGroup = {}; + var sessionCounts = [0, 0, 0]; + var sessionList; + var currentQuery = defaultQuery(); + var currentPage = 0; + var LIMIT = 20; + var $next = null; + var $scroller = null; + var $noMoreSessions = null; + + + function defaultQuery() { + return {offset:currentPage * LIMIT, limit:LIMIT, page:currentPage}; + } + // for unit tests + function getCategoryEnum() { + return CATEGORY; + } + + function removeSpinner() { + $('') + } + + function loadSessionsOriginal() { + addSpinner(); + + rest.findSessions(currentQuery) + .done(afterLoadSessions) + .fail(app.ajaxError) + .always(removeSpinner) + } + + function loadSessionsNew() { + + addSpinner(); + + rest.findScoredSessions(app.clientId, currentQuery) + .done(function(response) { afterLoadScoredSessions(response); }) + .always(function(){ removeSpinner(); }) + .fail(app.ajaxError) + } + + function loadSessions() { + + + if (gon.use_cached_session_scores) { + loadSessionsNew(); + } + else { + loadSessionsOriginal(); + } + + } + + function buildQuery() { + currentQuery = defaultQuery(); + + // genre filter + var genres = context.JK.GenreSelectorHelper.getSelectedGenres('#find-session-genre'); + if (genres !== null && genres.length > 0) { + currentQuery.genres = genres.join(','); + } + + // keyword filter + var keyword = $('#session-keyword-srch').val(); + if (keyword !== null && keyword.length > 0 && keyword !== 'Search by Keyword') { + currentQuery.keyword = $('#session-keyword-srch').val(); + } + + return currentQuery; + } + + function search() { + logger.debug("Searching for sessions..."); + clearResults(); + buildQuery(); + refreshDisplay(); + loadSessions(); + } + + function refreshDisplay() { + var priorVisible; + + var INVITATION = 'div#sessions-invitations'; + var FRIEND = 'div#sessions-friends'; + var OTHER = 'div#sessions-other'; + + // INVITATION + //logger.debug("sessionCounts[CATEGORY.INVITATION.index]=" + sessionCounts[CATEGORY.INVITATION.index]); + if (sessionCounts[CATEGORY.INVITATION.index] === 0) { + priorVisible = false; + $(INVITATION).hide(); + } + else { + priorVisible = true; + $(INVITATION).show(); + } + + // FRIEND + if (!priorVisible) { + $(FRIEND).removeClass('mt35'); + } + + //logger.debug("sessionCounts[CATEGORY.FRIEND.index]=" + sessionCounts[CATEGORY.FRIEND.index]); + if (sessionCounts[CATEGORY.FRIEND.index] === 0) { + priorVisible = false; + $(FRIEND).hide(); + } + else { + priorVisible = true; + $(FRIEND).show(); + } + + // OTHER + if (!priorVisible) { + $(OTHER).removeClass('mt35'); + } + + //logger.debug("sessionCounts[CATEGORY.OTHER.index]=" + sessionCounts[CATEGORY.OTHER.index]); + if (sessionCounts[CATEGORY.OTHER.index] === 0) { + $(OTHER).hide(); + } + else { + $(OTHER).show(); + } + } + + function afterLoadScoredSessions(sessionList) { + + // display the 'no sessions' banner if appropriate + var $noSessionsFound = $('#sessions-none-found'); + if (currentPage == 0 && sessionList.length == 0) { + $noSessionsFound.show(); + } + else { + $noSessionsFound.hide(); + } + + // update pager so infinitescroll can do it's thing + //$next.attr('href', '/api/sessions/nindex/' + app.clientId + '?' + $.param(currentQuery)) + + /** + // XXX + if(sessionList.length > 0) { + for(var i = 0; i < 20; i++) { + var copied = $.extend(true, {}, sessionList[0]); + copied.id = 'session_' + i; + sessionList.push(copied); + } + } + */ + + $.each(sessionList, function (i, session) { + sessions[session.id] = session; + }); + + $.each(sessionList, function (i, session) { + renderSession(session.id); + }); + + if(sessionList.length < LIMIT) { + // if we less results than asked for, end searching + $scroller.infinitescroll('pause'); + + if(currentPage > 0) { + $noMoreSessions.show(); + } + + }else { + currentPage++; + buildQuery(); + registerInfiniteScroll(); + } + + context.JK.GA.trackFindSessions(sessionList.length); + } + + + function afterLoadSessions(sessionList) { + + // display the 'no sessions' banner if appropriate + var $noSessionsFound = $('#sessions-none-found'); + if (sessionList.length == 0) { + $noSessionsFound.show(); + } + else { + $noSessionsFound.hide(); + } + + startSessionLatencyChecks(sessionList); + + context.JK.GA.trackFindSessions(sessionList.length); + } + + function startSessionLatencyChecks(sessionList) { + logger.debug("Starting latency checks on " + sessionList.length + " sessions"); + + sessionLatency.subscribe(app.clientId, latencyResponse); + $.each(sessionList, function (index, session) { + sessions[session.id] = session; + sessionLatency.sessionPings(session); + }); + } + + function containsInvitation(session) { + var i, invitation = null; + + if (session !== undefined) { + if ("invitations" in session) { + // user has invitations for this session + for (i = 0; i < session.invitations.length; i++) { + invitation = session.invitations[i]; + // session contains an invitation for this user + if (invitation.receiver_id == context.JK.currentUserId) { + return true; + } + } + } + } + + return false; + } + + function containsFriend(session) { + var i, participant = null; + + if (session !== undefined) { + if ("participants" in session) { + for (i = 0; i < session.participants.length; i++) { + participant = session.participants[i]; + // this session participant is a friend + if (participant !== null && participant !== undefined && participant.user.is_friend) { + return true; + } + } + } + } + return false; + } + + function latencyResponse(sessionId) { + logger.debug("Received latency response for session " + sessionId); + renderSession(sessionId); + } + + /** + * Not used normally. Allows modular unit testing + * of the renderSession method without having to do + * as much heavy setup. + */ + function setSession(session) { + invitationSessionGroup[session.id] = session; + } + + /** + * Render a single session line into the table. + * It will be inserted at the appropriate place according to the + * sortScore in sessionLatency. + */ + function renderSession(sessionId) { + // store session in the appropriate bucket and increment category counts + var session = sessions[sessionId]; + if (containsInvitation(session)) { + invitationSessionGroup[sessionId] = session; + sessionCounts[CATEGORY.INVITATION.index]++; + } + else if (containsFriend(session)) { + friendSessionGroup[sessionId] = session; + sessionCounts[CATEGORY.FRIEND.index]++; + } + else { + otherSessionGroup[sessionId] = session; + sessionCounts[CATEGORY.OTHER.index]++; + } + + // hack to prevent duplicate rows from being rendered when filtering + var sessionAlreadyRendered = false; + var $tbGroup; + + logger.debug('Rendering session ID = ' + sessionId); + + if (invitationSessionGroup[sessionId] != null) { + $tbGroup = $(CATEGORY.INVITATION.id); + + if ($("table#sessions-invitations tr[id='" + sessionId + "']").length > 0) { + sessionAlreadyRendered = true; + } + } + else if (friendSessionGroup[sessionId] != null) { + ; + $tbGroup = $(CATEGORY.FRIEND.id); + + if ($("table#sessions-friends tr[id='" + sessionId + "']").length > 0) { + sessionAlreadyRendered = true; + } + } + else if (otherSessionGroup[sessionId] != null) { + $tbGroup = $(CATEGORY.OTHER.id); + + if ($("table#sessions-other tr[id='" + sessionId + "']").length > 0) { + sessionAlreadyRendered = true; + } + } + else { + logger.debug('ERROR: No session with ID = ' + sessionId + ' found.'); + return; + } + + if (!sessionAlreadyRendered) { + var row = sessionList.renderSession(session, sessionLatency, $tbGroup, $('#template-session-row').html(), $('#template-musician-info').html()); + } + + refreshDisplay(); + } + + function beforeShow(data) { + context.JK.GenreSelectorHelper.render('#find-session-genre'); + } + + function afterShow(data) { + clearResults(); + buildQuery(); + refreshDisplay(); + loadSessions(); + } + + function clearResults() { + currentPage = 0; + $noMoreSessions.hide(); + $scroller.infinitescroll('resume'); + $('table#sessions-invitations').children(':not(:first-child)').remove(); + $('table#sessions-friends').children(':not(:first-child)').remove(); + $('table#sessions-other').children(':not(:first-child)').remove(); + + sessionCounts = [0, 0, 0]; + + sessions = {}; + invitationSessionGroup = {}; + friendSessionGroup = {}; + otherSessionGroup = {}; + } + + function deleteSession(evt) { + var sessionId = $(evt.currentTarget).attr("action-id"); + if (sessionId) { + $.ajax({ + type: "DELETE", + url: "/api/sessions/" + sessionId, + error: app.ajaxError + }).done(loadSessions); + } + } + function tempDebugStuff() { + if (gon.allow_both_find_algos && context.JK.currentUserAdmin) { + // show extra Refresh button, and distinguish between them + $('#btn-refresh-other').show().click(function() { + clearResults(); + buildQuery(); + refreshDisplay(); + if(gon.use_cached_session_scores) { + loadSessionsOriginal(); + } + else { + loadSessionsNew(); + } + }); + $('#btn-refresh').find('.extra').text(gon.use_cached_session_scores ? ' (new way)' : ' (old way)') + $('#btn-refresh-other').find('.extra').text(gon.use_cached_session_scores ? ' (old way)' : ' (new way)') + } + } + + function registerInfiniteScroll() { + + if(gon.use_cached_session_scores) { + + $scroller.infinitescroll({ + + behavior: 'local', + navSelector: '#findSession .btn-next-wrapper', + nextSelector: '#findSession .btn-next', + binder: $scroller, + dataType: 'json', + appendCallback: false, + debug: true, + prefill: false, + bufferPx:100, + loading: { + msg: $('