587 lines
20 KiB
JavaScript
587 lines
20 KiB
JavaScript
(function (context, $) {
|
|
|
|
"use strict";
|
|
|
|
context.JK = context.JK || {};
|
|
context.JK.Feed = function (app) {
|
|
|
|
var logger = context.JK.logger;
|
|
var rest = new context.JK.Rest();
|
|
var EVENTS = context.JK.EVENTS;
|
|
var ui = new context.JK.UIHelper(JK.app);
|
|
var userId = null;
|
|
var currentQuery = null;
|
|
var currentPage = 0;
|
|
var LIMIT = 20;
|
|
var $next = null;
|
|
var $screen = null;
|
|
var $scroller = null;
|
|
var $content = null;
|
|
var $noMoreFeeds = null;
|
|
var $refresh = null;
|
|
var $sortFeedBy = null;
|
|
var $includeDate = null;
|
|
var $includeType = null;
|
|
var next = null;
|
|
|
|
function defaultQuery() {
|
|
var query = { limit:LIMIT, page:currentPage};
|
|
|
|
if(next) {
|
|
query.since = next;
|
|
}
|
|
|
|
if(userId) {
|
|
query.user = userId;
|
|
}
|
|
|
|
return query;
|
|
}
|
|
|
|
function buildQuery() {
|
|
currentQuery = defaultQuery();
|
|
|
|
// specify search criteria based on form
|
|
currentQuery.sort = $sortFeedBy.val();
|
|
currentQuery.time_range = $includeDate.val();
|
|
currentQuery.type = $includeType.val();
|
|
|
|
return currentQuery;
|
|
}
|
|
|
|
|
|
function clearResults() {
|
|
currentPage = 0;
|
|
$content.empty(); // TODO: do we need to delete audio elements?
|
|
$noMoreFeeds.hide();
|
|
next = null;
|
|
}
|
|
|
|
function handleFeedResponse(response) {
|
|
next = response.next;
|
|
|
|
renderFeeds(response);
|
|
|
|
if(response.next == null) {
|
|
// if we less results than asked for, end searching
|
|
$scroller.infinitescroll('pause');
|
|
logger.debug("end of feeds")
|
|
|
|
if(currentPage == 0 && response.entries.length == 0) {
|
|
$content.append("<div class='no-feed-msg'>This user has no history.</div>") ;
|
|
}
|
|
|
|
if(currentPage > 0) {
|
|
$noMoreFeeds.show();
|
|
// there are bugs with infinitescroll not removing the 'loading'.
|
|
// it's most noticeable at the end of the list, so whack all such entries
|
|
$('.infinite-scroll-loader').remove();
|
|
}
|
|
}
|
|
else {
|
|
currentPage++;
|
|
buildQuery();
|
|
registerInfiniteScroll();
|
|
}
|
|
}
|
|
|
|
function setUser(_userId) {
|
|
userId = _userId;
|
|
}
|
|
|
|
function refresh() {
|
|
|
|
clearResults();
|
|
|
|
currentQuery = buildQuery();
|
|
rest.getFeeds(currentQuery)
|
|
.done(function(response) {
|
|
handleFeedResponse(response);
|
|
})
|
|
.fail(function(jqXHR) {
|
|
app.notifyServerError(jqXHR, 'Feed Unavailable')
|
|
})
|
|
}
|
|
|
|
function registerInfiniteScroll() {
|
|
|
|
$scroller.infinitescroll({
|
|
behavior: 'local',
|
|
navSelector: '#feedScreen .btn-next-pager',
|
|
nextSelector: '#feedScreen .btn-next-pager',
|
|
binder: $scroller,
|
|
dataType: 'json',
|
|
appendCallback: false,
|
|
prefill: false,
|
|
bufferPx:100,
|
|
loading: {
|
|
msg: $('<div class="infinite-scroll-loader">Loading ...</div>'),
|
|
img: '/assets/shared/spinner.gif'
|
|
},
|
|
path: function(page) {
|
|
return '/api/feeds?' + $.param(buildQuery());
|
|
}
|
|
},function(json, opts) {
|
|
handleFeedResponse(json);
|
|
});
|
|
$scroller.infinitescroll('resume');
|
|
}
|
|
|
|
|
|
function toggleSessionDetails() {
|
|
var $detailsLink = $(this);
|
|
var $feedItem = $detailsLink.closest('.feed-entry');
|
|
var $musicians = $feedItem.find('.musician-detail');
|
|
var $description = $feedItem.find('.description');
|
|
var toggledOpen = $detailsLink.data('toggledOpen');
|
|
|
|
if(toggledOpen) {
|
|
$feedItem.css('height', $feedItem.height() + 'px')
|
|
$feedItem.animate({'height': $feedItem.data('original-max-height')}).promise().done(function() {
|
|
$feedItem.css('height', 'auto').css('max-height', $feedItem.data('original-max-height'));
|
|
|
|
$musicians.hide();
|
|
$description.css('height', $description.data('original-height'));
|
|
$description.dotdotdot();
|
|
});
|
|
}
|
|
else {
|
|
$description.trigger('destroy.dot');
|
|
$description.data('original-height', $description.css('height')).css('height', 'auto');
|
|
$musicians.show();
|
|
$feedItem.animate({'max-height': '1000px'});
|
|
}
|
|
|
|
toggledOpen = !toggledOpen;
|
|
$detailsLink.data('toggledOpen', toggledOpen);
|
|
return false;
|
|
}
|
|
|
|
function startSessionPlay($feedItem) {
|
|
var img = $('.play-icon', $feedItem);
|
|
var $controls = $feedItem.find('.session-controls');
|
|
img.attr('src', '/assets/content/icon_pausebutton.png');
|
|
$controls.trigger('play.listenBroadcast');
|
|
$feedItem.data('playing', true);
|
|
}
|
|
|
|
function stopSessionPlay($feedItem) {
|
|
var img = $('.play-icon', $feedItem);
|
|
var $controls = $feedItem.find('.session-controls');
|
|
img.attr('src', '/assets/content/icon_playbutton.png');
|
|
$controls.trigger('pause.listenBroadcast');
|
|
$feedItem.data('playing', false);
|
|
}
|
|
|
|
function toggleSessionPlay() {
|
|
var $playLink = $(this);
|
|
var $feedItem = $playLink.closest('.feed-entry');
|
|
|
|
var $status = $feedItem.find('.session-status')
|
|
var playing = $feedItem.data('playing');
|
|
|
|
if(playing) {
|
|
$status.text('SESSION IN PROGRESS');
|
|
stopSessionPlay($feedItem);
|
|
}
|
|
else {
|
|
startSessionPlay($feedItem);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function stateChangeSession(e, data) {
|
|
var $controls = data.element;
|
|
var $feedItem = $controls.closest('.feed-entry');
|
|
var $status = $feedItem.find('.session-status');
|
|
|
|
if(data.displayText) $status.text(data.displayText);
|
|
|
|
if(data.isEnd) stopSessionPlay();
|
|
|
|
if(data.isSessionOver) {
|
|
$controls.removeClass('inprogress').addClass('ended')
|
|
}
|
|
}
|
|
|
|
function startRecordingPlay($feedItem) {
|
|
var img = $('.play-icon', $feedItem);
|
|
var $controls = $feedItem.find('.recording-controls');
|
|
img.attr('src', '/assets/content/icon_pausebutton.png');
|
|
$controls.trigger('play.listenRecording');
|
|
$feedItem.data('playing', true);
|
|
}
|
|
|
|
function stopRecordingPlay($feedItem) {
|
|
var img = $('.play-icon', $feedItem);
|
|
var $controls = $feedItem.find('.recording-controls');
|
|
img.attr('src', '/assets/content/icon_playbutton.png');
|
|
$controls.trigger('pause.listenRecording');
|
|
$feedItem.data('playing', false);
|
|
}
|
|
|
|
function toggleRecordingPlay() {
|
|
|
|
var $playLink = $(this);
|
|
var $feedItem = $playLink.closest('.feed-entry');
|
|
var playing = $feedItem.data('playing');
|
|
|
|
if(playing) {
|
|
stopRecordingPlay($feedItem);
|
|
}
|
|
else {
|
|
startRecordingPlay($feedItem);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function isOwner() {
|
|
return userId == context.JK.currentUserId;
|
|
}
|
|
|
|
function obtainCandidate(recording) {
|
|
if(isOwner()) {
|
|
var candidate = null;
|
|
context._.each(recording.claimed_recordings, function(claimedRecording) {
|
|
if(claimedRecording.user_id == context.JK.currentUserId) {
|
|
candidate = claimedRecording;
|
|
return false;
|
|
}
|
|
})
|
|
|
|
if(!candidate) throw "unable to find candidate claimed recording, yet we can see this recording. server error..."
|
|
return candidate;
|
|
}
|
|
else {
|
|
return recording.claimed_recordings[0]
|
|
}
|
|
}
|
|
|
|
function toggleOpen($feedItem, $name, $description, $musicians) {
|
|
$description.trigger('destroy.dot');
|
|
$description.data('original-height', $description.css('height')).css('height', 'auto');
|
|
$name.trigger('destroy.dot');
|
|
$name.data('original-height', $name.css('height')).css('height', 'auto');
|
|
$musicians.show();
|
|
$feedItem.animate({'max-height': '1000px'});
|
|
}
|
|
|
|
function toggleClose($feedItem, $name, $description, $musicians, immediate) {
|
|
$feedItem.css('height', $feedItem.height() + 'px')
|
|
$feedItem.animate({'height': $feedItem.data('original-max-height')}, immediate ? 0 : 400).promise().done(function() {
|
|
$feedItem.css('height', 'auto').css('max-height', $feedItem.data('original-max-height'));
|
|
|
|
$musicians.hide();
|
|
$description.css('height', $description.data('original-height'));
|
|
$description.dotdotdot();
|
|
$name.css('height', $name.data('original-height'));
|
|
$name.dotdotdot();
|
|
});
|
|
}
|
|
|
|
function stateChangeRecording(e, data) {
|
|
var $controls = data.element;
|
|
var $feedItem = $controls.closest('.feed-entry');
|
|
|
|
var $sliderBar = $('.recording-position', $feedItem);
|
|
var $statusBar = $('.recording-status', $feedItem);
|
|
var $currentTime = $('.recording-current', $feedItem);
|
|
var $status = $('.status-text', $feedItem);
|
|
var $playButton = $('.play-button', $feedItem);
|
|
|
|
if(data.isEnd) stopRecordingPlay($feedItem);
|
|
if(data.isError) {
|
|
$sliderBar.hide();
|
|
$playButton.hide();
|
|
$currentTime.hide();
|
|
$statusBar.show();
|
|
$status.text(data.displayText);
|
|
}
|
|
}
|
|
|
|
function toggleRecordingDetails() {
|
|
var $detailsLink = $(this);
|
|
var $feedItem = $detailsLink.closest('.feed-entry');
|
|
var $musicians = $feedItem.find('.musician-detail');
|
|
var $description = $feedItem.find('.description');
|
|
var $name = $feedItem.find('.name');
|
|
var toggledOpen = $detailsLink.data('toggledOpen');
|
|
|
|
if(toggledOpen) {
|
|
toggleClose($feedItem, $name, $description, $musicians)
|
|
}
|
|
else {
|
|
toggleOpen($feedItem, $name, $description, $musicians)
|
|
}
|
|
|
|
toggledOpen = !toggledOpen;
|
|
$detailsLink.data('toggledOpen', toggledOpen);
|
|
|
|
return false;
|
|
}
|
|
|
|
function updateRecordingName($feedEntry, name) {
|
|
$feedEntry.find('.name-text').text(name);
|
|
}
|
|
|
|
function updateRecordingDescription($feedEntry, description) {
|
|
$feedEntry.find('.description').text(description);
|
|
}
|
|
|
|
function updateIsPublic($feedEntry, isPublic) {
|
|
var $isPrivate = $feedEntry.find('.is_private')
|
|
if(isPublic) {
|
|
$isPrivate.removeClass('enabled')
|
|
}
|
|
else {
|
|
$isPrivate.addClass('enabled')
|
|
}
|
|
}
|
|
|
|
function updateComments($feedEntry, comments) {
|
|
$feedEntry.find('.comments').html(comments)
|
|
}
|
|
|
|
function updatePlays($feedEntry, plays) {
|
|
$feedEntry.find('.plays').html(plays);
|
|
}
|
|
|
|
function updateLikes($feedEntry, likes) {
|
|
$feedEntry.find('.likes').html(likes);
|
|
}
|
|
|
|
function updateStats($feedEntry) {
|
|
if($feedEntry.is('.recording-entry')) {
|
|
var id = $feedEntry.attr('data-claimed-recording-id');
|
|
rest.getClaimedRecording(id)
|
|
.done(function(claimedRecording) {
|
|
updateComments($feedEntry, claimedRecording.recording.comment_count);
|
|
updateLikes($feedEntry, claimedRecording.recording.like_count);
|
|
updatePlays($feedEntry, claimedRecording.recording.play_count);
|
|
|
|
})
|
|
.fail(app.ajaxError)
|
|
}
|
|
else {
|
|
var id = $feedEntry.attr('data-music-session');
|
|
rest.getSessionHistory(id)
|
|
.done(function(music_session) {
|
|
updateComments($feedEntry, music_session.comment_count);
|
|
updateLikes($feedEntry, music_session.like_count);
|
|
updatePlays($feedEntry, music_session.play_count);
|
|
})
|
|
.fail(app.ajaxError)
|
|
}
|
|
}
|
|
|
|
function updateGenre($feedEntry, genre) {
|
|
$feedEntry.find('.genre').text(context.JK.GenreSelectorHelper.getNameForId(genre));
|
|
}
|
|
|
|
function renderFeeds(feeds) {
|
|
|
|
$.each(feeds.entries, function(i, feed) {
|
|
if(feed.type == 'music_session') {
|
|
var options = {
|
|
feed_item: feed,
|
|
status_class: feed['is_over?'] ? 'ended' : 'inprogress',
|
|
mount_class: feed['has_mount?'] ? 'has-mount' : 'no-mount'
|
|
}
|
|
var $feedItem = $(context._.template($('#template-feed-music-session').html(), options, {variable: 'data'}));
|
|
var $controls = $feedItem.find('.session-controls');
|
|
|
|
// do everything we can before we attach the item to the page
|
|
$('.timeago', $feedItem).timeago();
|
|
context.JK.prettyPrintElements($('.duration', $feedItem).show());
|
|
context.JK.setInstrumentAssetPath($('.instrument-icon', $feedItem));
|
|
$('.details', $feedItem).click(toggleSessionDetails);
|
|
$('.details-arrow', $feedItem).click(toggleSessionDetails);
|
|
$('.play-button', $feedItem).click(toggleSessionPlay);
|
|
|
|
if (!feed.session_removed_at)
|
|
{
|
|
$('.btn-share', $feedItem).click(function() {
|
|
ui.launchShareDialog(feed.id, 'session');
|
|
});
|
|
}
|
|
else {
|
|
$('.btn-share', $feedItem).hide();
|
|
}
|
|
|
|
$('.btn-comment', $feedItem).click(function() {
|
|
var result = ui.launchCommentDialog({
|
|
session_id: feed.id,
|
|
entity_type: 'session'
|
|
}).one(EVENTS.DIALOG_CLOSED, function() {
|
|
updateStats($feedItem);
|
|
})
|
|
});
|
|
|
|
$('.btn-like', $feedItem).click(function() {
|
|
ui.addSessionLike(feed.id, JK.currentUserId, $('.likes', $feedItem), $('.btn-like', $feedItem))
|
|
});
|
|
|
|
// put the feed item on the page
|
|
renderFeed($feedItem);
|
|
|
|
// these routines need the item to have height to work (must be after renderFeed)
|
|
$controls.listenBroadcast();
|
|
$controls.bind('statechange.listenBroadcast', stateChangeSession);
|
|
$('.dotdotdot', $feedItem).dotdotdot();
|
|
$feedItem.data('original-max-height', $feedItem.css('height'));
|
|
context.JK.bindHoverEvents($feedItem);
|
|
context.JK.bindProfileClickEvents($feedItem);
|
|
}
|
|
else if(feed.type == 'recording') {
|
|
if(feed.claimed_recordings.length == 0) {
|
|
logger.error("a recording in the feed should always have one claimed_recording")
|
|
return;
|
|
}
|
|
var options = {
|
|
feed_item: feed,
|
|
candidate_claimed_recording: obtainCandidate(feed),
|
|
mix_class: feed['has_mix?'] ? 'has-mix' : 'no-mix',
|
|
}
|
|
|
|
var $feedItem = $(context._.template($('#template-feed-recording').html(), options, {variable: 'data'}));
|
|
var $controls = $feedItem.find('.recording-controls');
|
|
|
|
$('.timeago', $feedItem).timeago();
|
|
context.JK.prettyPrintElements($('.duration', $feedItem));
|
|
context.JK.setInstrumentAssetPath($('.instrument-icon', $feedItem));
|
|
$('.details', $feedItem).click(toggleRecordingDetails);
|
|
$('.details-arrow', $feedItem).click(toggleRecordingDetails);
|
|
$('.play-button', $feedItem).click(toggleRecordingPlay);
|
|
updateIsPublic($feedItem, options.candidate_claimed_recording.is_public);
|
|
|
|
$('.btn-share', $feedItem).click(function() {
|
|
ui.launchShareDialog(options.candidate_claimed_recording.id, 'recording');
|
|
});
|
|
|
|
$('.btn-comment', $feedItem).click(function() {
|
|
ui.launchCommentDialog({
|
|
recording_id: feed.id,
|
|
claimed_recording_id: options.candidate_claimed_recording.id,
|
|
entity_type: 'recording'
|
|
})
|
|
.one(EVENTS.DIALOG_CLOSED, function() {
|
|
updateStats($feedItem);
|
|
});
|
|
});
|
|
|
|
$('.btn-like', $feedItem).click(function() {
|
|
ui.addRecordingLike(feed.id, options.candidate_claimed_recording.id, JK.currentUserId, $('.likes', $feedItem), $('.btn-like', $feedItem));
|
|
});
|
|
|
|
if(isOwner()) {
|
|
$('.edit-recording-dialog', $feedItem).data('claimed_recording_id', options.candidate_claimed_recording.id).click(function() {
|
|
app.layout.showDialog('edit-recording', {d1: $(this).data('claimed_recording_id')})
|
|
.one(EVENTS.DIALOG_CLOSED, function() {
|
|
$(this).unbind('recording_updated').unbind('recording_deleted');
|
|
})
|
|
.one('recording_updated', function(e, data) {
|
|
// find recording by claimed recording id
|
|
var $feedEntry = $screen.find('.feed-entry.recording-entry[data-claimed-recording-id="'+ data.id +'"]');
|
|
var $musicians = $feedEntry.find('.musician-detail');
|
|
var $description = $feedEntry.find('.description');
|
|
var $name = $feedEntry.find('.name');
|
|
var $detailsLink = $feedEntry.find('.details');
|
|
var toggledOpen = $detailsLink.data('toggledOpen');
|
|
|
|
if(toggledOpen) {
|
|
toggleClose($feedEntry, $name, $description, $musicians, true);
|
|
}
|
|
|
|
$description.trigger('destroy.dot');
|
|
$name.trigger('destroy.dot');
|
|
|
|
updateRecordingName($feedEntry, data.name);
|
|
updateRecordingDescription($feedEntry, data.description);
|
|
updateIsPublic($feedEntry, data.is_public);
|
|
updateGenre($feedEntry, data.genre);
|
|
|
|
$name.dotdotdot();
|
|
$description.dotdotdot();
|
|
$feedItem.data('original-max-height', $feedEntry.css('height'));
|
|
|
|
$detailsLink.data('toggledOpen', false);
|
|
})
|
|
.one('recording_deleted', function(e, data) {
|
|
var $feedEntry = $screen.find('.feed-entry.recording-entry[data-claimed-recording-id="'+ data.id +'"]');
|
|
$feedEntry.remove();
|
|
})
|
|
return false;
|
|
}).show();
|
|
}
|
|
|
|
// put the feed item on the page
|
|
renderFeed($feedItem);
|
|
|
|
// these routines need the item to have height to work (must be after renderFeed)
|
|
$controls.listenRecording({recordingId: feed.id, claimedRecordingId: options.candidate_claimed_recording.id, sliderSelector:'.recording-slider', sliderBarSelector: '.recording-playback', currentTimeSelector:'.recording-current'});
|
|
$controls.bind('statechange.listenRecording', stateChangeRecording);
|
|
$('.dotdotdot', $feedItem).dotdotdot();
|
|
$feedItem.data('original-max-height', $feedItem.css('height'));
|
|
context.JK.bindHoverEvents($feedItem);
|
|
context.JK.bindProfileClickEvents($feedItem);
|
|
}
|
|
else {
|
|
logger.warn("skipping feed type: " + feed.type);
|
|
}
|
|
|
|
context.JK.bindProfileClickEvents();
|
|
});
|
|
}
|
|
|
|
function renderFeed(feed) {
|
|
$content.append(feed);
|
|
}
|
|
|
|
function search() {
|
|
logger.debug("Searching for feeds...");
|
|
refresh();
|
|
return false;
|
|
}
|
|
|
|
function events() {
|
|
$refresh.on("click", search);
|
|
$sortFeedBy.on('change', search);
|
|
$includeDate.on('change', search);
|
|
$includeType.on('change', search);
|
|
}
|
|
|
|
function initialize(_$parent, _$scroller, _$content, _$noMorefeeds, _$refresh, _$sortFeedBy, _$includeDate, _$includeType, defaults) {
|
|
$screen = _$parent;
|
|
$scroller = _$scroller;
|
|
$content = _$content;
|
|
$noMoreFeeds = _$noMorefeeds;
|
|
$refresh = _$refresh;
|
|
$sortFeedBy = _$sortFeedBy;
|
|
$includeDate = _$includeDate;
|
|
$includeType = _$includeType;
|
|
|
|
if($screen.length == 0) throw "$screen must be specified";
|
|
if($scroller.length == 0) throw "$scroller must be specified";
|
|
if($content.length == 0) throw "$content must be specified";
|
|
if($noMoreFeeds.length == 0) throw "$noMoreFeeds must be specified";
|
|
if($refresh.length == 0) throw "$refresh must be specified";
|
|
if($sortFeedBy.length == 0) throw "$sortFeedBy must be specified";
|
|
if($includeDate.length == 0) throw "$includeDate must be specified";
|
|
if($includeType.length ==0) throw "$includeType must be specified";
|
|
|
|
defaults = $.extend({}, {sort: 'date', time_range: 'month', type: 'all'}, defaults)
|
|
// set default search criteria
|
|
$sortFeedBy.val(defaults.date)
|
|
$includeDate.val(defaults.time_range)
|
|
$includeType.val(defaults.type)
|
|
|
|
events();
|
|
}
|
|
|
|
this.initialize = initialize;
|
|
this.refresh = refresh;
|
|
this.setUser = setUser;
|
|
|
|
return this;
|
|
}
|
|
})(window, jQuery) |