* VRFS-304 - user history (feed) tab

This commit is contained in:
Seth Call 2014-06-27 14:17:56 -05:00
parent 09364fc25b
commit 865b488f93
43 changed files with 1012 additions and 154 deletions

View File

@ -182,3 +182,4 @@ rsvp_slots_prof_level.sql
add_file_name_music_notation.sql
change_scheduled_start_music_session.sql
music_sessions_iso_639_3.sql
discardable_claimed_recordings.sql

View File

@ -0,0 +1 @@
ALTER TABLE claimed_recordings ADD COLUMN discarded BOOLEAN DEFAULT FALSE;

View File

@ -1,9 +1,18 @@
module JamRuby
# To use this, be sure to add this in before block: stub_const("APP_CONFIG", web_config)
class Nav
def self.home(options ={})
"#{APP_CONFIG.external_root_url}/client#/home#{dialog(options)}"
"#{base_url}/home#{dialog(options)}"
end
def self.profile(user)
"#{base_url}/profile/#{user.id}"
end
def self.feed
"#{base_url}/feed"
end
def self.accept_friend_request_dialog(friend_request_id)
@ -12,6 +21,10 @@ module JamRuby
private
def self.base_url
"#{APP_CONFIG.external_root_url}/client#"
end
def self.dialog(options)
dialog = ''
if options[:dialog]

View File

@ -8,9 +8,9 @@ module JamRuby
belongs_to :genre, :class_name => "JamRuby::Genre"
has_many :recorded_tracks, :through => :recording, :class_name => "JamRuby::RecordedTrack"
has_many :playing_sessions, :class_name => "JamRuby::ActiveMusicSession"
has_many :likes, :class_name => "JamRuby::RecordingLiker", :foreign_key => "claimed_recording_id"
has_many :likes, :class_name => "JamRuby::RecordingLiker", :foreign_key => "claimed_recording_id", :dependent => :destroy
has_many :plays, :class_name => "JamRuby::PlayablePlay", :foreign_key => "claimed_recording_id", :dependent => :destroy
has_one :share_token, :class_name => "JamRuby::ShareToken", :inverse_of => :shareable, :foreign_key => 'shareable_id'
has_one :share_token, :class_name => "JamRuby::ShareToken", :inverse_of => :shareable, :foreign_key => 'shareable_id', :dependent => :destroy
validates :name, no_profanity: true, length: {minimum: 3, maximum: 64}, presence: true
validates :description, no_profanity: true, length: {maximum: 8000}
@ -39,9 +39,9 @@ module JamRuby
raise PermissionError, "user doesn't own claimed_recording"
end
self.name = params[:name] unless params[:name].nil?
self.description = params[:description] unless params[:description].nil?
self.genre = Genre.find(params[:genre]) unless params[:genre].nil?
self.name = params[:name]
self.description = params[:description]
self.genre = Genre.find_by_id(params[:genre]) unless params[:genre].nil?
self.is_public = params[:is_public] unless params[:is_public].nil?
save
end
@ -50,13 +50,10 @@ module JamRuby
if user != self.user
raise PermissionError, "user doesn't own claimed_recording"
end
# If this is the only copy, destroy the entire recording. Otherwise, just destroy this claimed_recording
if recording.claimed_recordings.count == 1
recording.destroy
else
self.destroy
end
ClaimedRecording.where(:id => id).update_all(:discarded => true )
recording.discard(user)
end

View File

@ -43,6 +43,7 @@ module JamRuby
query = Feed.joins("LEFT OUTER JOIN recordings ON recordings.id = feeds.recording_id")
.joins("LEFT OUTER JOIN music_sessions ON music_sessions.id = feeds.music_session_id")
.limit(limit)
.where('recordings is NULL OR recordings.all_discarded = false') # remove any 'all_discarded recordings from the search results'
# handle sort
if sort == 'date'
@ -72,14 +73,14 @@ module JamRuby
end
if target_user
if target_user
if target_user != user.id
require_public_recordings = "claimed_recordings.is_public = TRUE AND"
require_public_sessions = "music_sessions.fan_access = TRUE AND"
end
query = query.joins("LEFT OUTER JOIN claimed_recordings ON recordings.id = claimed_recordings.recording_id AND #{require_public_recordings} (claimed_recordings.user_id = '#{target_user}' OR (recordings.band_id IN (SELECT band_id FROM bands_musicians where user_id='#{target_user}')))")
query = query.joins("LEFT OUTER JOIN claimed_recordings ON recordings.id = claimed_recordings.recording_id AND claimed_recordings.discarded = FALSE AND #{require_public_recordings} (claimed_recordings.user_id = '#{target_user}' OR (recordings.band_id IN (SELECT band_id FROM bands_musicians where user_id='#{target_user}')))")
query = query.joins("LEFT OUTER JOIN music_sessions_user_history ON music_sessions.id = music_sessions_user_history.music_session_id AND #{require_public_sessions} music_sessions_user_history.user_id = '#{target_user}'")
query = query.group("feeds.id, feeds.recording_id, feeds.music_session_id, feeds.created_at, feeds.updated_at, recordings.id, music_sessions.id")
if sort == 'plays'
@ -97,7 +98,7 @@ module JamRuby
require_public_sessions = "music_sessions.fan_access = TRUE AND"
end
query = query.joins("LEFT OUTER JOIN claimed_recordings ON recordings.id = claimed_recordings.recording_id AND #{require_public_recordings} recordings.band_id = '#{target_band}'")
query = query.joins("LEFT OUTER JOIN claimed_recordings ON recordings.id = claimed_recordings.recording_id AND claimed_recordings.discarded = FALSE AND #{require_public_recordings} recordings.band_id = '#{target_band}'")
query = query.where("music_sessions IS NULL OR #{require_public_sessions} music_sessions.band_id = '#{target_band}'")
query = query.group("feeds.id, feeds.recording_id, feeds.music_session_id, feeds.created_at, feeds.updated_at, recordings.id, music_sessions.id")
if sort == 'plays'
@ -108,7 +109,7 @@ module JamRuby
query = query.where('recordings.id is NULL OR claimed_recordings.id IS NOT NULL')
#query = query.where('music_sessions.id is NULL OR music_sessions_user_history.id IS NOT NULL')
else
query = query.joins('LEFT OUTER JOIN claimed_recordings ON recordings.id = claimed_recordings.recording_id AND claimed_recordings.is_public = TRUE')
query = query.joins('LEFT OUTER JOIN claimed_recordings ON recordings.id = claimed_recordings.recording_id AND claimed_recordings.discarded = FALSE AND claimed_recordings.is_public = TRUE')
query = query.joins("LEFT OUTER JOIN music_sessions_user_history ON music_sessions.id = music_sessions_user_history.music_session_id AND music_sessions.fan_access = TRUE")
query = query.group("feeds.id, feeds.recording_id, feeds.music_session_id, feeds.created_at, feeds.updated_at, recordings.id, music_sessions.id")
if sort == 'plays'

View File

@ -5,12 +5,12 @@ module JamRuby
attr_accessible :owner, :owner_id, :band, :band_id, :recorded_tracks_attributes, :mixes_attributes, :claimed_recordings_attributes, :name, :description, :genre, :is_public, :duration, as: :admin
has_many :claimed_recordings, :class_name => "JamRuby::ClaimedRecording", :inverse_of => :recording, :foreign_key => 'recording_id', :dependent => :destroy
has_many :users, :through => :recorded_tracks, :class_name => "JamRuby::User"
has_many :claimed_recordings, :class_name => "JamRuby::ClaimedRecording", :inverse_of => :recording, :foreign_key => 'recording_id', :dependent => :destroy
has_many :mixes, :class_name => "JamRuby::Mix", :inverse_of => :recording, :foreign_key => 'recording_id', :dependent => :destroy
has_many :recorded_tracks, :class_name => "JamRuby::RecordedTrack", :foreign_key => :recording_id, :dependent => :destroy
has_many :comments, :class_name => "JamRuby::RecordingComment", :foreign_key => "recording_id"
has_many :likes, :class_name => "JamRuby::RecordingLiker", :foreign_key => "recording_id"
has_many :comments, :class_name => "JamRuby::RecordingComment", :foreign_key => "recording_id", :dependent => :destroy
has_many :likes, :class_name => "JamRuby::RecordingLiker", :foreign_key => "recording_id", :dependent => :destroy
has_many :plays, :class_name => "JamRuby::PlayablePlay", :as => :playable, :dependent => :destroy
has_one :feed, :class_name => "JamRuby::Feed", :inverse_of => :recording, :foreign_key => 'recording_id', :dependent => :destroy
@ -128,7 +128,7 @@ module JamRuby
unless self.users.exists?(user)
raise PermissionError, "user was not in this session"
end
recorded_tracks.where(:user_id=> user.id)
recorded_tracks.where(:user_id => user.id)
end
def has_access?(user)
@ -211,7 +211,7 @@ module JamRuby
# check if all recorded_tracks for this recording are discarded
if recorded_tracks.where('discard = false or discard is NULL').length == 0
self.all_discarded = true
self.all_discarded = true # the feed won't pick this up; also background cleanup will find these and whack them later
self.save(:validate => false)
end
@ -238,7 +238,7 @@ module JamRuby
.order('recorded_tracks.id')
.where('recorded_tracks.fully_uploaded = TRUE')
.where('recorded_tracks.id > ?', since)
.where('claimed_recordings.user_id = ?', user).limit(limit).each do |recorded_track|
.where('claimed_recordings.user_id = ? AND claimed_recordings.discarded = FALSE', user).limit(limit).each do |recorded_track|
downloads.push(
{
:type => "recorded_track",
@ -258,7 +258,7 @@ module JamRuby
.order('mixes.id')
.where('mixes.completed_at IS NOT NULL')
.where('mixes.id > ?', since)
.where('claimed_recordings.user_id = ?', user)
.where('claimed_recordings.user_id = ? AND claimed_recordings.discarded = FALSE', user)
.limit(limit).each do |mix|
downloads.push(
{

View File

@ -188,8 +188,12 @@ describe Recording do
expect { @claimed_recordign.discard(@user2) }.to raise_error
@claimed_recording = @recording.claim(@user2, "name2", "description2", @genre, true)
@claimed_recording.discard(@user2)
@claimed_recording.reload
@claimed_recording.discarded.should == true
@recording.recorded_tracks_for_user(@user2)[0].discard.should == true
@recording.reload
@recording.claimed_recordings.length.should == 1
@recording.claimed_recordings.length.should == 2
@recording.all_discarded.should == false
end
it "should destroy the entire recording if there was only one claimed_recording which is discarded" do
@ -199,8 +203,10 @@ describe Recording do
@genre = FactoryGirl.create(:genre)
@claimed_recording = @recording.claim(@user, "name", "description", @genre, true)
@claimed_recording.discard(@user)
expect { Recording.find(@recording.id) }.to raise_error
expect { ClaimedRecording.find(@claimed_recording.id) }.to raise_error
@claimed_recording.reload
@claimed_recording.discarded.should == true
@claimed_recording.recording.all_discarded.should == true
@recording.recorded_tracks_for_user(@user)[0].discard.should == true
end
it "should use the since parameter when restricting uploads" do

View File

@ -9,6 +9,9 @@
var self = this;
var logger = context.JK.logger;
var $banner = null;
var $closeBtn = null;
var $yesBtn = null;
var $noBtn = null;
// you can also do
// * showAlert('title', 'text')
@ -26,6 +29,19 @@
return show(options);
}
function showYesNo(options) {
if (typeof options == 'string' || options instanceof String) {
if(arguments.length == 2) {
options = {title: options, html:arguments[1]}
}
else {
options = {html:options};
}
}
options.type = 'yes_no'
return show(options);
}
// responsible for updating the contents of the update dialog
// as well as registering for any event handlers
function show(options) {
@ -33,7 +49,12 @@
var html = options.html;
if(!options.title) {
options.title = 'alert'
if(options.type == 'alert') {
options.title = 'alert'
}
else if(options.type == 'yes_no') {
options.title = 'please confirm';
}
}
var $h1 = $banner.find('h1');
@ -50,11 +71,18 @@
throw "unable to show banner for empty message";
}
var $closeBtn = $banner.find('.close-btn');
if((options.type == "alert" && !options.buttons) || options.close) {
$closeBtn.show().click(function() {
var closeButtonText = 'CLOSE';
if(options.close !== null && typeof options.close == 'object') {
// extra styling options for close button
if(options.close.name) {
closeButtonText = options.close.name;
}
}
$closeBtn.show().text(closeButtonText).unbind('click').click(function() {
hide();
return false;
});
@ -63,6 +91,27 @@
$closeBtn.hide();
}
if(options.type == "yes_no") {
$yesBtn.show().unbind('click').click(function() {
if(options.yes) {
options.yes();
}
hide();
return false;
})
$noBtn.show().unbind('click').click(function() {
if(options.no) {
options.no();
}
hide();
return false;
})
}
else {
$yesBtn.hide();
$noBtn.hide();
}
if(options.buttons) {
var $buttons = $banner.find('.buttons')
context._.each(options.buttons, function(button) {
@ -86,6 +135,7 @@
return newContent;
}
function hide() {
$banner.hide();
$banner.find('.user-btn').remove();
@ -96,6 +146,10 @@
function initialize() {
$banner = $('#banner');
$closeBtn = $banner.find('.close-btn');
$yesBtn = $banner.find('.yes-btn');
$noBtn = $banner.find('.no-btn');
return self;
}
@ -104,6 +158,7 @@
initialize: initialize,
show: show,
showAlert: showAlert,
showYesNo: showYesNo,// shows Yes and Cancel button (confirmation dialog)
hide: hide
}

View File

@ -122,7 +122,7 @@
}
function showDialog() {
app.layout.showDialog('comment-dialog');
return app.layout.showDialog('comment-dialog');
}
function initialize() {

View File

@ -0,0 +1,170 @@
(function (context, $) {
"use strict";
context.JK = context.JK || {};
context.JK.EditRecordingDialog = function (app) {
var logger = context.JK.logger;
var rest = context.JK.Rest();
var claimedRecordingId = null;
var $dialog = null;
var $form = null;
var $name = null;
var $description = null;
var $genre = null;
var $isPublic = null;
var $cancelBtn = null;
var $saveBtn = null;
var $deleteBtn = null;
var updating = false;
var deleting = false;
function resetForm() {
// remove all display errors
$dialog.find('.error-text').remove()
$dialog.find('.error').removeClass("error")
}
function beforeShow(args) {
claimedRecordingId = args.d1;
if(!claimedRecordingId) throw "claimedRecordingId must be specified";
resetForm();
rest.getClaimedRecording(claimedRecordingId)
.done(function(data) {
var name = data.name;
var description = data.description;
var is_public = data.is_public;
var genre_id = data.genre_id;
context.JK.GenreSelectorHelper.setSelectedGenres($genre.parent(), [genre_id]);
$name.val(name);
$description.val(description);
if(is_public) {
$isPublic.attr('checked', 'checked').iCheck('check')
}
else {
$isPublic.removeAttr('checked').iCheck('uncheck')
}
})
.fail(app.ajaxError)
}
function afterHide() {
}
function attemptUpdate() {
if(updating) return;
updating = true;
var name = $name.val();
var description = $description.val();
var genre = $genre.val();
var is_public = $isPublic.is(':checked');
rest.updateClaimedRecording({id: claimedRecordingId, name: name, description: description, is_public: is_public, genre: genre })
.done(function(updated) {
resetForm();
$dialog.triggerHandler('recording_updated', {id: claimedRecordingId, name: name, description: description, is_public: is_public, genre: genre})
app.layout.closeDialog('edit-recording');
})
.fail(function(jqXHR) {
if(jqXHR.status = 422) {
// highlight fields in error
resetForm();
var errors = JSON.parse(jqXHR.responseText);
var $name_errors = context.JK.format_errors('name', errors);
if ($name_errors) $name.closest('div.field').addClass('error').end().after($name_errors);
var $description_errors = context.JK.format_errors('description', errors);
if ($description_errors) $description.closest('div.field').addClass('error').end().after($description_errors);
var $genre_errors = context.JK.format_errors('genre', errors);
if ($genre_errors) $genre.closest('div.field').addClass('error').end().after($genre_errors);
var $is_public_errors = context.JK.format_errors('is_public', errors);
if ($is_public_errors) $isPublic.closest('div.field').addClass('error').end().after($is_public_errors);
}
else {
app.ajaxError(arguments);
}
})
.always(function() {
updating = false;
})
}
function attemptDelete() {
if(deleting) return;
deleting = true;
context.JK.Banner.showYesNo({
title: "Confirm Deletion",
html: "Are you sure you want to delete this recording?",
yes: function() {
rest.deleteClaimedRecording(claimedRecordingId)
.done(function() {
$dialog.triggerHandler('recording_deleted', {id: claimedRecordingId});
app.layout.closeDialog('edit-recording');
})
.fail(app.ajaxError)
.always(function() {
deleting = false;
})
},
no : function() {
context.JK.Banner.hide();
deleting = false;
}
})
}
function cancel() {
app.layout.closeDialog('edit-recording');
}
function events() {
$saveBtn.click(attemptUpdate);
$deleteBtn.click(attemptDelete);
$cancelBtn.click(cancel)
$form.submit(false);
}
function initialize() {
var dialogBindings = {
'beforeShow': beforeShow,
'afterHide': afterHide
};
app.bindDialog('edit-recording', dialogBindings);
$dialog = $('#edit-recording-dialog');
$form = $dialog.find('form');
$cancelBtn = $dialog.find('.cancel-btn');
$saveBtn = $dialog.find('.save-btn');
$deleteBtn = $dialog.find('.delete-btn');
$name = $dialog.find('input[name="name"]');
$description = $dialog.find('textarea[name="description"]');
$genre = $dialog.find('select[name=genre]');
$isPublic = $dialog.find('input[name=is_public]');
events();
context.JK.GenreSelectorHelper.render($genre.parent());
context.JK.checkbox($isPublic);
};
this.initialize = initialize;
}
})(window, jQuery);

View File

@ -27,7 +27,7 @@
var $scroller = $screen.find('.content-body-scroller');
var $content = $screen.find('.feed-content');
var $noMoreFeeds = $('#end-of-feeds-list');
var $refresh = $screen.find('#btn-refresh-feed');
var $refresh = $screen.find('.btn-refresh-entries');
var $sortFeedBy = $screen.find('#feed_order_by');
var $includeDate = $screen.find('#feed_date');
var $includeType = $screen.find('#feed_show');

View File

@ -7,7 +7,9 @@
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;
@ -29,6 +31,10 @@
query.since = next;
}
if(userId) {
query.user = userId;
}
return query;
}
@ -61,6 +67,10 @@
$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'.
@ -75,6 +85,10 @@
}
}
function setUser(_userId) {
userId = _userId;
}
function refresh() {
clearResults();
@ -221,6 +235,49 @@
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;
@ -251,24 +308,10 @@
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();
$name.css('height', $name.data('original-height'));
$name.dotdotdot();
});
toggleClose($feedItem, $name, $description, $musicians)
}
else {
$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'});
toggleOpen($feedItem, $name, $description, $musicians)
}
toggledOpen = !toggledOpen;
@ -277,6 +320,64 @@
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) {
@ -308,10 +409,12 @@
}
$('.btn-comment', $feedItem).click(function() {
ui.launchCommentDialog({
var result = ui.launchCommentDialog({
session_id: feed.id,
entity_type: 'session'
});
}).one(EVENTS.DIALOG_CLOSED, function() {
updateStats($feedItem);
})
});
$('.btn-like', $feedItem).click(function() {
@ -336,7 +439,7 @@
}
var options = {
feed_item: feed,
candidate_claimed_recording: feed.claimed_recordings[0],
candidate_claimed_recording: obtainCandidate(feed),
mix_class: feed['has_mix?'] ? 'has-mix' : 'no-mix',
}
@ -349,6 +452,7 @@
$('.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');
@ -359,6 +463,9 @@
recording_id: feed.id,
claimed_recording_id: options.candidate_claimed_recording.id,
entity_type: 'recording'
})
.one(EVENTS.DIALOG_CLOSED, function() {
updateStats($feedItem);
});
});
@ -366,6 +473,47 @@
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);
@ -431,6 +579,7 @@
this.initialize = initialize;
this.refresh = refresh;
this.setUser = setUser;
return this;
}

View File

@ -7,21 +7,12 @@
"use strict";
context.JK = context.JK || {};
context.JK.GenreSelectorDeferred = null;
context.JK.GenreSelectorHelper = (function() {
var logger = context.JK.logger;
var _genres = []; // will be list of structs: [ {label:xxx, value:yyy}, {...}, ... ]
function loadGenres() {
var url = "/api/genres";
$.ajax({
type: "GET",
url: url,
async: false, // do this synchronously so the event handlers in events() can be wired up
success: genresLoaded
});
}
function reset(parentSelector, defaultGenre) {
defaultGenre = typeof(defaultGenre) == 'undefined' ? '' : defaultGenre;
$('select', parentSelector).val(defaultGenre);
@ -38,7 +29,7 @@
function render(parentSelector) {
$('select', parentSelector).empty();
$('select', parentSelector).append('<option value="">Any Genre</option>');
$('select', parentSelector).append('<option value="">Unspecified</option>');
var template = $('#template-genre-option').html();
$.each(_genres, function(index, value) {
// value will be a dictionary entry from _genres:
@ -67,6 +58,21 @@
return selectedGenres;
}
function getNameForId(genreId) {
var name = null;
context._.each(_genres, function(genre) {
if(genreId == genre.value) {
name = genre.label;
return false;
}
});
if(!name) {
logger.warn("no genre found for genreId: " + genreId);
}
return name;
}
function setSelectedGenres(parentSelector, genreList) {
if (!genreList) {
return;
@ -79,18 +85,31 @@
$('select', parentSelector).val(values[0]);
}
function initialize() {
loadGenres();
function initialize(app) {
// XXX; _instruments should be populated in a template, rather than round-trip to server
if(!context.JK.GenreSelectorDeferred) {
// this dance is to make sure there is only one server request instead of InstrumentSelector instances *
context.JK.GenreSelectorDeferred = rest.getGenres()
}
context.JK.GenreSelectorDeferred
.done(function(response) {genresLoaded(response)})
.fail(app.ajaxError)
return this;
}
var me = { // This will be our singleton.
initialize: initialize,
getSelectedGenres: getSelectedGenres,
setSelectedGenres: setSelectedGenres,
getNameForId : getNameForId,
getSelectedGenresValues: getSelectedGenresValues,
reset: reset,
render: render,
loadGenres: loadGenres
render: function() {
var _args = arguments;
context.JK.GenreSelectorDeferred.done(function(){render.apply(self, _args)})
}
};
return me;

View File

@ -948,6 +948,27 @@
});
}
function updateClaimedRecording(options) {
var claimedRecordingId = options["id"];
return $.ajax({
type: "PUT",
dataType: "json",
url: '/api/claimed_recordings/' + claimedRecordingId,
contentType: 'application/json',
processData: false,
data: JSON.stringify(options)
});
}
function deleteClaimedRecording(id) {
return $.ajax({
type: "DELETE",
dataType: "json",
contentType: 'application/json',
url: "/api/claimed_recordings/" + id
});
}
function claimRecording(options) {
var recordingId = options["id"];
@ -1213,6 +1234,8 @@
this.getRecording = getRecording;
this.getClaimedRecordings = getClaimedRecordings;
this.getClaimedRecording = getClaimedRecording;
this.updateClaimedRecording = updateClaimedRecording;
this.deleteClaimedRecording = deleteClaimedRecording;
this.claimRecording = claimRecording;
this.startPlayClaimedRecording = startPlayClaimedRecording;
this.stopPlayClaimedRecording = stopPlayClaimedRecording;

View File

@ -646,6 +646,7 @@
if (dialogEvent(dialog, 'beforeShow', options) === false) {
return null;
}
logger.debug("opening dialog: " + dialog)
var $overlay = $('.dialog-overlay')
if (opts.sizeOverlayToContent) {

View File

@ -13,6 +13,7 @@
var sentFriendRequest = false;
var profileScreen = null;
var textMessageDialog = null;
var feed = null;
var instrument_logo_map = context.JK.getInstrumentIconMap24();
@ -30,6 +31,7 @@
function beforeShow(data) {
userId = data.id;
feed.setUser(userId);
}
function afterShow(data) {
@ -37,6 +39,10 @@
resetForm();
}
function beforeHide(data) {
feed.setUser(null);
}
function resetForm() {
$('#profile-instruments').empty();
@ -571,7 +577,7 @@
}
function bindHistory() {
feed.refresh();
}
/****************** BANDS TAB *****************/
@ -752,15 +758,31 @@
function bindFavorites() {
}
function initializeFeed() {
var $scroller = profileScreen.find('.content-body-scroller');
var $content = profileScreen.find('.feed-content');
var $noMoreFeeds = $('#end-of-feeds-list');
var $refresh = profileScreen.find('.btn-refresh-entries');
var $sortFeedBy = profileScreen.find('#feed_order_by');
var $includeDate = profileScreen.find('#feed_date');
var $includeType = profileScreen.find('#feed_show');
feed = new context.JK.Feed(app);
feed.initialize(profileScreen, $scroller, $content, $noMoreFeeds, $refresh, $sortFeedBy, $includeDate, $includeType);
}
function initialize(textMessageDialogInstance) {
textMessageDialog = textMessageDialogInstance;
var screenBindings = {
'beforeShow': beforeShow,
'afterShow': afterShow
'afterShow': afterShow,
'beforeHide' : beforeHide
};
app.bindScreen('profile', screenBindings);
profileScreen = $('#user-profile');
events();
initializeFeed();
}
this.initialize = initialize;

View File

@ -98,10 +98,10 @@
id: recording.id
})
.done(function () {
console.error("recording discarded by user. recordingId=%o", recording.id);
logger.debug("recording discarded by user. recordingId=%o", recording.id);
})
.fail(function (jqXHR) {
console.error("recording discard by user failed. recordingId=%o. reason: %o", recording.id, jqXHR.responseText);
logger.error("recording discard by user failed. recordingId=%o. reason: %o", recording.id, jqXHR.responseText);
})
.always(function () {
app.layout.closeDialog('recordingFinished')

View File

@ -34,7 +34,7 @@
}
function showDialog() {
app.layout.showDialog('rsvp-cancel-dialog');
return app.layout.showDialog('rsvp-cancel-dialog');
}
function events() {

View File

@ -55,7 +55,7 @@
}
function showDialog() {
app.layout.showDialog(dialogId);
return app.layout.showDialog(dialogId);
}
function events() {

View File

@ -336,7 +336,7 @@
}
function showDialog() {
app.layout.showDialog('share-dialog');
return app.layout.showDialog('share-dialog');
}
// function initDialog() {

View File

@ -26,25 +26,25 @@
function launchCommentDialog(options) {
var commentDialog = new JK.CommentDialog(JK.app, options);
commentDialog.initialize();
commentDialog.showDialog();
return commentDialog.showDialog();
}
function launchShareDialog(entityId, entityType) {
var shareDialog = new JK.ShareDialog(JK.app, entityId, entityType);
shareDialog.initialize(JK.FacebookHelperInstance);
shareDialog.showDialog();
return shareDialog.showDialog();
}
function launchRsvpSubmitDialog(sessionId) {
var rsvpDialog = new JK.RsvpSubmitDialog(JK.app, sessionId);
rsvpDialog.initialize();
rsvpDialog.showDialog();
return rsvpDialog.showDialog();
}
function launchRsvpCancelDialog(sessionId, rsvpRequestId) {
var rsvpDialog = new JK.RsvpCancelDialog(JK.app, sessionId, rsvpRequestId);
rsvpDialog.initialize();
rsvpDialog.showDialog();
return rsvpDialog.showDialog();
}
this.addSessionLike = addSessionLike;

View File

@ -54,6 +54,7 @@
*= require ./textMessageDialog
*= require ./acceptFriendRequestDialog
*= require ./launchAppDialog
*= require ./editRecordingDialog
*= require ./iconInstrumentSelect
*= require ./terms
*= require ./createSession

View File

@ -0,0 +1,79 @@
@import "client/common";
#edit-recording-dialog {
min-height:330px;
input, textarea {
@include border_box_sizing;
}
.field {
margin-top:20px;
&:nth-of-type(1) {
margin-top:0;
}
}
.buttons {
float:right;
clear:both;
margin-top:20px;
}
label[for="name"] {
}
input[name="name"] {
margin-top:5px;
width:100%;
}
label[for="description"] {
}
textarea[name="description"] {
margin-top:5px;
width:100%;
}
label[for="genre"] {
display:inline;
float:left;
line-height:26px;
vertical-align:middle;
}
select[name="genre"] {
float:left;
margin-left:5px;
}
.genre-selector {
float:left;
.dropdown-wrapper {
margin-left:5px;
}
}
label[for="is_public"] {
display: inline;
float:right;
line-height: 26px;
padding-right: 5px;
vertical-align: middle;
}
select[name="is_public"] {
float:right;
}
div[purpose="is_public"] {
float:right;
.icheckbox_minimal {
float:right;
margin-top:4px;
}
}
}

View File

@ -2,4 +2,9 @@
.recording-current {
position:absolute; // solves a problem with duration wrapping--only in firefox
}
.btn-refresh-holder {
float:right;
margin-right:10px;
}
}

View File

@ -328,6 +328,7 @@ input[type="text"], input[type="password"]{
textarea {
font-size:15px;
padding:3px;
}

View File

@ -317,4 +317,60 @@
#btn-add-friend {
display:none;
}
#profile-history {
padding:0 10px 0 20px;
width:100%;
position:relative;
height:100%;
@include border_box_sizing;
#user-feed-controls {
width:100%;
@include border_box_sizing;
position:relative;
display:none;
}
.btn-refresh-holder {
left: 95%; // 5 * 19% to right-align 5 user blocks (in conjunction with the margin-left
margin-left: -65px;
position: absolute;
}
.filter-body {
bottom: 0;
right: 0;
top: 0;
left: 20px;
position: absolute;
width: 95%;
padding-top:0;
margin-top:10px;
height:auto;
}
.profile-wrapper {
padding: 10px 0;
}
.feed-entry .feed-details {
margin-right:5px;
}
.recording-current {
position:absolute; // solves a problem with duration wrapping--only in firefox
}
.content-body-scroller {
height: 100%;
width: 100%;
position: absolute;
}
.no-feed-msg {
text-align:center
}
}

View File

@ -272,6 +272,20 @@
}
}
a.edit-recording-dialog {
font-size:12px;
display:none;
}
.is_private {
display:none;
font-size:12px;
font-style:italic;
&.enabled {
display:inline;
}
}
.play-count {
margin-right:10px;
}

View File

@ -19,30 +19,20 @@ class ApiClaimedRecordingsController < ApiController
end
def update
if @claimed_recording.user_id != current_user.id
raise PermissionError, 'only owner of claimed_recording can update it'
end
begin
@claimed_recording.update_fields(current_user, params)
respond_with responder: ApiResponder, :status => 204
rescue
render :json => { :message => "claimed_recording could not be updated" }, :status => 403
end
@claimed_recording.update_fields(current_user, params)
respond_with @claimed_recording
end
def delete
if @claimed_recording.user_id != current_user.id
raise PermissionError, 'only owner of claimed_recording can update it'
end
#begin
#@claimed_recording.discard(current_user)
#render :json => {}, :status => 204
# respond_with responder: ApiResponder, :status => 204
#rescue
#render :json => { :message => "claimed_recording could not be deleted" }, :status => 403
#end
@claimed_recording.discard(current_user)
render :json => {}, :status => 200
end
def download

View File

@ -205,6 +205,7 @@ class ApiRecordingsController < ApiController
end
end
private
def parse_filename
@recorded_track = RecordedTrack.find_by_recording_id_and_client_track_id!(params[:id], params[:track_id])

View File

@ -140,7 +140,7 @@ glue :recording do
child(:claimed_recordings => :claimed_recordings) {
attributes :id, :name, :description, :is_public, :genre_id, :has_mix?
attributes :id, :name, :description, :is_public, :genre_id, :has_mix?, :user_id
child(:user => :creator) {
attributes :id, :first_name, :last_name, :photo_url

View File

@ -17,7 +17,7 @@ if !current_user
else
attributes :id, :music_session_id, :name, :description, :musician_access, :approval_required, :fan_access, :fan_chat,
:band_id, :user_id, :genre_id, :created_at, :like_count, :comment_count, :scheduled_start, :scheduled_duration,
:band_id, :user_id, :genre_id, :created_at, :like_count, :comment_count, :play_count, :scheduled_start, :scheduled_duration,
:language, :recurring_mode, :language_description, :scheduled_start_time, :access_description, :timezone, :timezone_description,
:musician_access_description, :fan_access_description, :session_removed_at, :legal_policy, :open_rsvps

View File

@ -28,7 +28,7 @@ child(:comments => :comments) {
child(:claimed_recordings => :claimed_recordings) {
attributes :id, :name, :description, :is_public, :genre_id
attributes :id, :name, :description, :is_public, :genre_id, :has_mix?, :user_id
node :share_url do |claimed_recording|
unless claimed_recording.share_token.nil?

View File

@ -1,43 +0,0 @@
<!-- generic banner for use by an code -->
<div class="overlay" id="banner_overlay"></div>
<div id="banner" class="dialog-overlay-sm" data-type="">
<!-- dialog header -->
<div class="content-head">
<%= image_tag("content/icon_alert.png", :height => '24', :width => '24', :class => "content-icon") %><h1></h1>
</div>
<div class="dialog-inner">
</div>
<!-- end right column -->
<br clear="all" class="end-content">
<div class="right buttons">
<a class="button-orange close-btn">CLOSE</a>
</div>
</div>
<script type="text/template" id="template-app-in-read-only-volume">
<div class="template-app-in-read-only-volume">
<p> The JamKazam application is running in a read-only volume. This stops the automatic update feature
from working, and may cause other issues because it is not a supported configuration.</p>
<p> So let's fix it. Don't worry--it's easy to do. Please read on.</p>
<p> First, here's almost certainly what happened to cause this problem: after JamKazam.dmg was downloaded, it was then double-clicked and a window opened showing the contents of the dmg.
The JamKazam application icon was double-clicked inside that opened window. Unfortunately, that isn't OK.</p>
<p>Instead, do this to move JamKazam to a good location, and run it from there:</p>
<ol>
<li class="download-dmg">Download the latest mac installer from the <a href="/downloads">Downloads</a> page.<br/><em>(the download will have a filename ending in .dmg)</em></li>
<li>Double-click the downloaded dmg file to open it.</li>
<li>In the resulting screen, drag the JamKazam icon to the Applications folder. It will show a progress bar as it copies.</li>
<li>Double-click the Applications folder to go into it.</li>
<li>If you are still running the JamKazam application, you will need to stop it before executing the last step.</li>
<li>Find the JamKazam application in the Applications folder, and double-click the icon to launch it!</li>
</ol>
<!---<p>The following animation shows the the steps, after once you've downloaded the JamKazam '.dmg'.</p>-->
</div>
</script>

View File

@ -0,0 +1,33 @@
#banner_overlay.overlay
#banner.dialog-overlay-sm{ 'data-type' => '' }
.content-head
= image_tag("content/icon_alert.png", :height => '24', :width => '24', :class => "content-icon")
%h1
.dialog-inner
%br.end-content{ clear: 'all'}
.right.buttons
%a.button-orange.close-btn CLOSE
%a.button-orange.yes-btn YES
%a.button-grey.no-btn CANCEL
%script{type: 'text/template', id: 'template-app-in-read-only-volume'}
.template-app-in-read-only-volume
%p The JamKazam application is running in a read-only volume. This stops the automatic update feature from working, and may cause other issues because it is not a supported configuration.
%p So let's fix it. Don't worry--it's easy to do--please read on.
%p First, here's almost certainly what happened to cause this problem: after JamKazam.dmg was downloaded, it was then double-clicked and a window opened showing the contents of the dmg. The JamKazam application icon was double-clicked inside that opened window. Unfortunately, that isn't OK.
%p Instead, do this to move JamKazam to a good location, and run it from there:
%ol
%li.download-dmg
Download the latest mac installer from the
%a{href:"/downloads"}Downloads
page.
%br
%em (the download will have a filename ending in .dmg)
%li Double-click the downloaded dmg file to open it.
%li In the resulting screen, drag the JamKazam icon to the Applications folder. It will show a progress bar as it copies.
%li Double-click the Applications folder to go into it.
%li If you are still running the JamKazam application, you will need to stop it before executing the last step.
%li Find the JamKazam application in the Applications folder, and double-click the icon to launch it!

View File

@ -0,0 +1,24 @@
.dialog.configure-tracks{ layout: 'dialog', 'layout-id' => 'edit-recording', id: 'edit-recording-dialog'}
.content-head
= image_tag "content/icon_add.png", {:width => 19, :height => 19, :class => 'content-icon' }
%h1 Edit Recording
.dialog-inner
%form
.field
%label{for: 'name'} Recording name:
%input{type: 'text', name: 'name'}
.field
%label{for: 'description'} Description:
%textarea{name: 'description', rows: '4'}
.field.genre-selector
%label{for: 'genre'} Genre:
%select{name:'genre'}
.field{purpose: 'is_public'}
%input{type: 'checkbox', name: 'is_public'}
%label{for: 'is_public'} Public Recording
.buttons
%a.button-grey.cancel-btn CANCEL
%a.button-orange.delete-btn DELETE
%a.button-orange.save-btn UPDATE
%br{clear: 'all'}

View File

@ -88,9 +88,19 @@
</div>
</div>
<div id="profile-history" class="profile-wrapper">
<%= form_tag('', {:id => 'user-feed-form', :class => 'inner-content'}) do %>
<%= render(:partial => "web_filter", :locals => {:search_type => Search::PARAM_FEED, :id => 'user-feed-controls'}) %>
<div class="filter-body">
<div class="content-body-scroller">
<br clear="all" />
<div class="profile-wrapper">
<div class="feed-content"></div>
<a href="/api/feeds?page=1" class="btn-next-pager">Next</a>
<div id="end-of-user-feeds-list" class="end-of-list">No more feed entries</div>
</div>
</div>
</div>
<% end %>
</div>
<div id="profile-bands" class="profile-wrapper profile-body-content">
<br clear="all" />

View File

@ -72,8 +72,8 @@
<% end -%>
<% if :feed == filter_label %>
<div class="right mr10">
<a class="button-grey btn-refresh-entries" href="/client#/feed" id="btn-refresh-feed">REFRESH</a>
<div class="btn-refresh-holder">
<a class="button-grey btn-refresh-entries" href="/client#/feed">REFRESH</a>
</div>
<% end %>
<% end -%>

View File

@ -50,6 +50,7 @@
<%= render "account_audio_profile" %>
<%= render "account_sessions" %>
<%= render "configure_tracks_dialog" %>
<%= render "edit_recording_dialog" %>
<%= render "invitationDialog" %>
<%= render "inviteMusicians" %>
<%= render "hoverBand" %>
@ -129,6 +130,9 @@
if (this.didInitAfterConnect) return;
this.didInitAfterConnect = true
// This is a helper class with a singleton. No need to instantiate.
JK.GenreSelectorHelper.initialize(JK.app);
var recordingManager = new JK.RecordingManager();
var facebookHelper = new JK.FacebookHelper(JK.app);
@ -156,6 +160,8 @@
var launchAppDialog = new JK.LaunchAppDialog(JK.app);
launchAppDialog.initialize();
var editRecordingDialog = new JK.EditRecordingDialog(JK.app);
editRecordingDialog.initialize();
var userDropdown = new JK.UserDropdown(JK.app);
JK.UserDropdown = userDropdown;
@ -202,9 +208,6 @@
var searchResultScreen = new JK.SearchResultScreen(JK.app);
searchResultScreen.initialize();
// This is a helper class with a singleton. No need to instantiate.
JK.GenreSelectorHelper.initialize();
var inviteMusiciansUtil1 = new JK.InviteMusiciansUtil(JK.app);
inviteMusiciansUtil1.initialize(friendSelectorDialog);
// var createSessionScreen = new JK.CreateSessionScreen(JK.app);

View File

@ -1,5 +1,5 @@
%script{type: 'text/template', id: 'template-feed-recording'}
.feed-entry.recording-entry{:'data-claimed-recording-id' => '{{data.candidate_claimed_recording.id}}' }
.feed-entry.recording-entry{:'data-claimed-recording-id' => '{{data.candidate_claimed_recording.id}}', :'data-recording-id' => '{{data.feed_item.id}}' }
/ avatar
.avatar-small.ib
%a{:hoveraction => "{{data.feed_item.helpers.artist_hoveraction}}", :profileaction => "{{data.feed_item.helpers.artist_hoveraction}}", :"{{data.feed_item.helpers.artist_datakey}}" => "{{data.feed_item.helpers.artist_id}}"}
@ -8,6 +8,7 @@
.left.ml20.w15
.title{hoveraction: 'recording', :'recording-id' => '{{data.candidate_claimed_recording.id}}' }
%a{:href => "/recordings/{{data.candidate_claimed_recording.id}}", :rel => "external"} RECORDING
%a.edit-recording-dialog{href: "#"} (edit)
.artist
%a.artist{:hoveraction => '{{data.feed_item.helpers.artist_hoveraction}}', :profileaction => "{{data.feed_item.helpers.artist_hoveraction}}", :'{{data.feed_item.helpers.artist_datakey}}' => '{{data.feed_item.helpers.artist_id}}'}
= '{{data.feed_item.helpers.artist_name}}'
@ -16,7 +17,8 @@
/ name and description
.left.ml20.w30
.name.dotdotdot
= '{{data.feed_item.helpers.name}}'
%span.name-text {{data.feed_item.helpers.name}}
%span.is_private (private)
.description.dotdotdot
= '{{data.feed_item.helpers.description}}'
/ timeline and controls
@ -54,7 +56,7 @@
0:00
/ end recording play controls
/ genre and social
.left.small= '{{data.feed_item.helpers.genre}}'
.left.small.genre= '{{data.feed_item.helpers.genre}}'
.right.small.feed-details
%a{title: 'Share', class: 'btn-share'}
= image_tag 'content/icon_share.png', :height => "12", :width => "7"

View File

@ -56,7 +56,7 @@ describe "Feed", :js => true, :type => :feature, :capybara_feature => true do
find('div.comment-text', text: comment)
find('#dialog-close-button', '[layout-id="comment-dialog"]').trigger(:click)
find('#btn-refresh-feed').trigger(:click)
find('[layout-id="feed"] .btn-refresh-entries').trigger(:click)
find('span.comments').should have_content('1')
# Likes
@ -142,7 +142,7 @@ describe "Feed", :js => true, :type => :feature, :capybara_feature => true do
find('div.comment-text', text: comment)
find('#dialog-close-button', '[layout-id="comment-dialog"]').trigger(:click)
find('#btn-refresh-feed').trigger(:click)
find('[layout-id="feed"] .btn-refresh-entries').trigger(:click)
find('span.comments').should have_content('1')
# Likes
@ -173,12 +173,6 @@ describe "Feed", :js => true, :type => :feature, :capybara_feature => true do
find("#user-profile h2[id=profile-username]", text: user.name)
end
# it "should render play widget" do
# it " and allow recording playback" do
# end
# end
end
end

View File

@ -0,0 +1,196 @@
require 'spec_helper'
describe "Profile History", :js => true, :type => :feature, :capybara_feature => true do
subject { page }
let(:user) { FactoryGirl.create(:user) }
before do
MusicSession.delete_all
Recording.delete_all
sign_in_poltergeist user
stub_const("APP_CONFIG", web_config)
end
it "loads empty history" do
nav_profile_history(user)
find('.no-feed-msg', text: 'This user has no history.')
end
# the same feedHelper instance is used to show any user's feed. so we need to make sure bouncing back and forth
# between feeds is safe
it "between users" do
create_session(creator: user)
formal_leave_by(user)
user2 = FactoryGirl.create(:user)
nav_profile_history(user)
find('.feed-entry.music-session-history-entry')
visit Nav.feed # to get around bug where you can't cause a screen to update if it's the same screen, different params
nav_profile_history(user2)
should_not have_selector('.feed-entry.music-session-history-entry')
find('.no-feed-msg', text: 'This user has no history.')
end
describe "sessions" do
before(:each) do
create_session(creator: user)
formal_leave_by(user)
end
it "should render stats" do
nav_profile_history(user)
# initial stats
find('span.plays').should have_content('0')
find('span.comments').should have_content('0')
find('span.likes').should have_content('0')
# Comments
find('a.btn-comment').trigger(:click)
comment = 'this sounds great'
fill_in "txtComment", with: comment
find('#btn-add-comment').trigger(:click)
find('div.comment-text', text: comment)
find('#dialog-close-button', '[layout-id="comment-dialog"]').trigger(:click)
find('span.comments').should have_content('1')
# Likes
find('a.btn-like').trigger(:click)
find('span.likes').should have_content('1')
end
it "should render details" do
nav_profile_history(user)
find('.feed-details a.details').trigger(:click)
# confirm user avatar exists
find("a.avatar-tiny[user-id=\"#{user.id}\"][hoveraction=\"musician\"] img")
# confirm user name exists
find("a.musician-name[user-id=\"#{user.id}\"][hoveraction=\"musician\"]", text: user.name)
# confirm instrument icons exist
find("img[instrument-id=\"electric guitar\"]")
# confirm hover bubbles show
find("a.avatar-tiny[user-id=\"#{user.id}\"][hoveraction=\"musician\"]").hover_intent
# confirm navigate to user profile page
find(".avatar-tiny[user-id=\"#{user.id}\"][hoveraction=\"musician\"]").trigger(:click)
find("#user-profile h2[id=profile-username]", text: user.name)
end
end
describe "recordings" do
before(:each) do
start_recording_with(user)
stop_recording
claim_recording("my recording", "my recording description")
formal_leave_by(user)
MusicSession.delete_all
end
# it "should render avatar" do
# it " and link to profile" do
# end
# it " and render artist hover bubble" do
# end
# end
# it "should render description" do
# it " and link to recording landing" do
# end
# it " and render recording hover bubble" do
# end
# end
# it "should render artist name" do
# it " and link to profile" do
# end
# it " and render artist hover bubble"
# end
it "should render stats" do
nav_profile_history(user)
# initial stats
find('span.plays').should have_content('0')
find('span.comments').should have_content('0')
find('span.likes').should have_content('0')
# ensure Share icon exists
find('a.btn-share')
# Comments
find('a.btn-comment').trigger(:click)
comment = 'this sounds great'
fill_in "txtComment", with: comment
find('#btn-add-comment').trigger(:click)
find('div.comment-text', text: comment)
find('#dialog-close-button', '[layout-id="comment-dialog"]').trigger(:click)
find('span.comments').should have_content('1')
# Likes
find('a.btn-like').trigger(:click)
find('span.likes').should have_content('1')
end
it "should render details" do
nav_profile_history(user)
find('.feed-details a.details').trigger(:click)
# confirm user avatar exists
find("a.avatar-tiny[user-id=\"#{user.id}\"][hoveraction=\"musician\"] img")
# confirm user name exists
find("a.musician-name[user-id=\"#{user.id}\"][hoveraction=\"musician\"]", text: user.name)
# confirm instrument icons exist
find("img[instrument-id=\"electric guitar\"]")
# confirm hover bubbles show
find("a.avatar-tiny[user-id=\"#{user.id}\"][hoveraction=\"musician\"]").hover_intent
# confirm navigate to user profile page
find(".avatar-tiny[user-id=\"#{user.id}\"][hoveraction=\"musician\"]").trigger(:click)
find("#user-profile h2[id=profile-username]", text: user.name)
end
it "should allow edit and delete" do
nav_profile_history(user)
# edit it's name
find('.edit-recording-dialog').trigger(:click)
find('h1', text: 'Edit Recording')
fill_in 'name', with: 'some crazy name'
find('.save-btn').trigger(:click)
should_not have_selector('h1', text: 'Edit Recording')
find('.name-text', text:'some crazy name')
# now delete it
find('.edit-recording-dialog').trigger(:click)
find('h1', text: 'Edit Recording')
find('.delete-btn').trigger(:click)
# confirm...
find('.yes-btn').trigger(:click)
should_not have_selector('h1', text: 'Edit Recording')
should_not have_selector('.feed-entry.recording-entry')
ClaimedRecording.first.discarded.should == true
end
end
end

View File

@ -17,6 +17,35 @@ def web_config
def external_root_url
"#{external_protocol}#{external_hostname}#{(external_port == 80 || external_port == 443) ? '' : ':' + external_port.to_s}"
end
def aws_bucket
JAMKAZAM_TESTING_BUCKET
end
def aws_access_key_id
'AKIAJESQY24TOT542UHQ'
end
def aws_secret_access_key
'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'
end
def aws_region
'us-east-1'
end
def aws_bucket_public
'jamkazam-testing-public'
end
def aws_cache
'315576000'
end
def max_audio_downloads
10
end
end
klass.new
end

View File

@ -581,3 +581,8 @@ def garbage length
length.times { output << special_characters.sample }
output.slice(0, length)
end
def nav_profile_history(user)
visit Nav.profile(user)
find('#profile-history-link').trigger(:click)
end