* feed shows items. no scrolling, no support for query modification yet

This commit is contained in:
Seth Call 2014-03-11 06:09:44 +00:00
parent 4cf4aad497
commit c9438fcf99
18 changed files with 535 additions and 62 deletions

View File

@ -123,19 +123,35 @@ module JamRuby
if query.length == 0
[query, nil]
elsif query.length < limit
[query, nil]
else
if sort == 'date'
[query, query.last.id]
if params[:hash]
if query.length == 0
{ query:query, next: nil}
elsif query.length < limit
{ query:query, next: nil}
else
[query, start + limit]
if sort == 'date'
{ query:query, next: query.last.id}
else
{ query:query, next: start + limit}
end
end
else
if query.length == 0
[query, nil]
elsif query.length < limit
[query, nil]
else
if sort == 'date'
[query, query.last.id]
else
[query, start + limit]
end
end
end
end
end
end

View File

@ -19,6 +19,7 @@ else
gem 'jam_websockets', "0.1.#{ENV["BUILD_NUMBER"]}"
ENV['NOKOGIRI_USE_SYSTEM_LIBRARIES'] ||= "true"
end
gem 'oj'
gem 'builder'
gem 'rails', '~>3.2.11'
gem 'jquery-rails', '2.0.2'

File diff suppressed because one or more lines are too long

View File

@ -26,6 +26,7 @@
//= require jquery.easydropdown
//= require jquery.scrollTo
//= require jquery.infinitescroll
//= require jquery.dotdotdot
//= require globals
//= require AAB_message_factory
//= require AAC_underscore

View File

@ -6,13 +6,284 @@
var logger = context.JK.logger;
var rest = context.JK.Rest();
var currentQuery = defaultQuery();
var currentPage = 0;
var LIMIT = 20;
var $screen = null;
var $next = null;
var $scroller = null;
var $content = null;
function defaultQuery() {
return { offset:currentPage * LIMIT, limit:LIMIT, page:currentPage };
}
function buildQuery() {
currentQuery = defaultQuery();
return currentQuery;
}
function beforeShow(data) {
}
function afterShow(data) {
refresh();
}
function refresh() {
currentQuery = buildQuery();
rest.getFeeds(currentQuery)
.done(function(response) {
renderFeeds(response);
})
.fail(function(jqXHR) {
app.notifyServerError(jqXHR, 'Feed Unavailable')
})
}
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 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) {
$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();
});
}
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'});
}
toggledOpen = !toggledOpen;
return false;
}
function renderFeeds(feeds) {
$.each(feeds.entries, function(i, feed) {
if(feed.type == 'music_session_history') {
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);
// 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'));
}
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: feed.claimed_recordings[0],
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);
// 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'));
}
else {
logger.warn("skipping feed type: " + feed.type);
}
})
}
function renderFeed(feed) {
$content.append(feed);
}
@ -23,6 +294,9 @@
};
app.bindScreen('feed', screenBindings);
$screen = $('[layout-id="feed"]');
$scroller = $screen.find('.content-body-scroller');
$content = $screen.find('.profile-wrapper');
}
this.initialize = initialize;

View File

@ -618,6 +618,18 @@
});
}
function getFeeds(options) {
if(!options) { options = {}; }
return $.ajax({
type: 'GET',
dataType: "json",
url: "/api/feeds?" + $.param(options),
processData:false
})
}
function sendFriendRequest(app, userId, callback) {
var url = "/api/users/" + context.JK.currentUserId + "/friend_requests";
$.ajax({
@ -929,6 +941,7 @@
this.getClientDownloads = getClientDownloads;
this.createEmailInvitations = createEmailInvitations;
this.postFeedback = postFeedback;
this.getFeeds = getFeeds;
this.serverHealthCheck = serverHealthCheck;
this.sendFriendRequest = sendFriendRequest;
this.acceptFriendRequest = acceptFriendRequest;

View File

@ -347,6 +347,7 @@
$parent.triggerHandler('statechange.listenRecording',
{
element: $parent,
state: playState,
displayText: displayText,
isEnd: isEnd,

View File

@ -389,6 +389,7 @@
$parent.triggerHandler('statechange.listenBroadcast',
{
element: $parent,
state: playState,
displayText: displayText,
isEnd: isEnd,

View File

@ -49,6 +49,9 @@
*= require ./banner
*= require ./clientUpdate
*= require ./musician
*= require web/audioWidgets
*= require web/recordings
#= require web/sessions
*= require jquery.Jcrop
*= require icheck/minimal/minimal
*/

View File

@ -517,7 +517,6 @@ table.vu td {
}
.recording-controls {
display:none;
.play-button {
outline:none;

View File

@ -3,14 +3,22 @@ class ApiFeedsController < ApiController
respond_to :json
def index
@feeds, @next = Feed.index(current_user,
data = Feed.index(current_user,
start: params[:since],
limit: params[:limit],
sort: params[:sort],
time_range: params[:time_range],
type: params[:type],
user: params[:user],
band: params[:band])
band: params[:band],
hash: true)
@feeds = data[:query]
@next = data[:next]
puts "#{@feeds.inspect} FEEEEDS"
puts "#{@next.inspect} NEEEEXT"
render "api_feeds/index", :layout => nil
end

View File

@ -7,14 +7,18 @@ module FeedsHelper
image_tag resolve_avatarables(music_session_history.band, music_session_history.user)
end
def session_duration(music_session_history, options={})
def session_duration_value(music_session_history)
if music_session_history.session_removed_at.nil?
duration(Time.now - music_session_history.created_at, options)
Time.now - music_session_history.created_at
else
duration(music_session_history.session_removed_at - music_session_history.created_at, options)
music_session_history.session_removed_at - music_session_history.created_at
end
end
def session_duration(music_session_history, options={})
duration(session_duration_value(music_session_history), options)
end
def session_text(music_session_history)
if music_session_history.is_over?
'SESSION ENDED'

View File

@ -7,8 +7,22 @@ glue :music_session_history do
'music_session_history'
end
attributes :id, :description, :genres, :created_at, :session_removed_at, :comment_count, :like_count, :play_count, :fan_access
attributes :id, :description, :genres, :created_at, :session_removed_at, :comment_count, :like_count, :play_count, :fan_access, :is_over?, :has_mount?
node do |history|
{
helpers: {
avatar: asset_path(resolve_avatarables(history.band, history.user)),
artist_name: session_artist_name(history),
utc_created_at: history.created_at.getutc.iso8601,
description: session_description(history),
status: session_text(history),
duration: session_duration_value(history),
duration_secs: history.created_at.to_i,
genre: session_genre(history)
}
}
end
child(:user => :creator) {
attributes :id, :first_name, :last_name, :photo_url
@ -17,17 +31,25 @@ glue :music_session_history do
child(:unique_user_histories => :participants) {
attributes :first_name, :last_name, :photo_url
node :id do |history|
history.user_id
node :id do |user|
user.user_id
end
node do |user|
{
helpers: {
avatar: asset_path(resolve_avatarables(user))
}
}
end
# total_duration comes back from the database as a string
node :duration do |history|
history.total_duration.nil? ? 0 : history.total_duration.to_i
node :duration do |user|
user.total_duration.nil? ? 0 : user.total_duration.to_i
end
node :instruments do |history|
history.total_instruments.nil? ? [] : history.total_instruments.split('|').uniq
node :instruments do |user|
user.total_instruments.nil? ? [] : user.total_instruments.split('|').uniq
end
}
@ -52,7 +74,20 @@ glue :recording do
'recording'
end
attributes :id, :band, :created_at, :duration, :comment_count, :like_count, :play_count
attributes :id, :band, :created_at, :duration, :comment_count, :like_count, :play_count, :has_mix?
node do |recording|
{
helpers: {
avatar: asset_path(resolve_avatarables(recording.band, recording.owner)),
artist_name: recording_artist_name(recording),
utc_created_at: recording.created_at.getutc.iso8601,
name: recording_name(recording),
description: recording_description(recording),
genre: recording_genre(recording)
}
}
end
child(:owner => :owner) {
attributes :id, :name, :location, :photo_url
@ -70,6 +105,23 @@ glue :recording do
}
}
child(:grouped_tracks => :grouped_tracks) {
:instrument_ids
child(:musician => :musician) {
attributes :id, :first_name, :last_name, :city, :state, :country, :location, :photo_url
node do |user|
{
helpers: {
avatar: asset_path(resolve_avatarables(user))
}
}
end
}
}
child(:comments => :comments) {
attributes :comment, :created_at
@ -80,7 +132,7 @@ glue :recording do
child(:claimed_recordings => :claimed_recordings) {
attributes :id, :name, :description, :is_public, :genre_id
attributes :id, :name, :description, :is_public, :genre_id, :has_mix?
child(:user => :creator) {
attributes :id, :first_name, :last_name, :photo_url

View File

@ -9,5 +9,4 @@
= render(:partial => "web_filter", :locals => {:search_type => Search::PARAM_FEED})
.filter-body
.content-body-scroller
%p This feature not yet implemented
.content-wrapper
.profile-wrapper

View File

@ -25,6 +25,8 @@
<%= render "bandProfile" %>
<%= render "band_setup" %>
<%= render "band_setup_photo" %>
<%= render "users/feed_music_session_ajax" %>
<%= render "users/feed_recording_ajax" %>
<%= render "feed" %>
<%= render "bands" %>
<%= render "musicians" %>
@ -268,6 +270,8 @@
var clientUpdate = new JK.ClientUpdate(JK.app)
clientUpdate.initialize().check()
JK.TickDuration('.feed-entry.music-session-history-entry .inprogress .tick-duration');
JK.JamServer.connect(); // singleton here defined in JamServer.js
// this ensures that there is always a CurrentSessionModel, even if it's for a non-active session
JK.CurrentSessionModel = new JK.SessionModel(JK.app, JK.JamServer, window.jamClient);

View File

@ -1,48 +1,49 @@
%script {type: 'text/javascript'}
.feed-entry.music-session-history-entry{'data-music-session' => feed_item.id}
%script{type: 'text/template', id: 'template-feed-music-session'}
.feed-entry.music-session-history-entry{'data-music-session' => '{{data.feed_item.id}}' }
/ avatar
.avatar-small.ib
= session_avatar(feed_item)
%img{ src: '{{data.feed_item.helpers.avatar}}' }
/ type and artist
.left.ml20.w15
.title{hoveraction: 'session', :'session-id' => feed_item.id } SESSION
.title{hoveraction: 'session', :'session-id' => '{{data.feed_item.id}}' } SESSION
.artist
= session_artist_name(feed_item)
= timeago(feed_item.created_at, class: 'small created_at')
= '{{data.feed_item.helpers.artist_name}}'
%time.small.created_at.timeago{datetime: '{{data.feed_item.helpers.utc_created_at}}'}= '{{data.feed_item.created_at}}'
/ name and description
.left.ml20.w30
.description.dotdotdot
= session_description(feed_item)
= '{{data.feed_item.helpers.description}}'
/ timeline and controls
.right.w40
/ recording play controls
.session-controls{ class: "#{(feed_item.is_over? ? 'ended' : 'inprogress')} #{feed_item.has_mount? ? 'has-mount' : 'no-mount'}", 'data-music-session' => feed_item.id, 'fan-access' => feed_item.fan_access.to_s}
.session-controls{class:'{{data.status_class}} {{data.mount_class}}', :'data-music-session' => '{{data.feed_item.id}}', 'fan-access' => '{{data.feed_item.fan_access}}'}
/ session status
%a.left.play-button{href:'#'}
= image_tag 'content/icon_playbutton.png', width:20, height:20, class:'play-icon'
- if feed_item.has_mount?
%audio{preload: 'none'}
%source{src: feed_item.music_session.mount.url, type: feed_item.music_session.mount.resolve_string(:mime_type)}
= "{% if(data.feed_item['has_mount?']) { %}"
%audio{preload: 'none'}
%source{src: '{{data.feed_item.music_session.mount.url}}', type: '{{data.feed_item.music_session.mime_type}}'}
= '{% } %}'
%span.session-status
= session_text(feed_item)
= '{{data.feed_item.helpers.status}}'
/ current playback time
= session_duration(feed_item, class: 'session-duration tick-duration recording-current', 'data-created-at' => feed_item.created_at.to_i)
%time{class: 'session-duration tick-duration recording-current duration', 'data-created-at' => '{{data.feed_item.helpers.duration_secs}}', 'duration' => '{{data.feed_item.helpers.duration}}'}
= '{{data.feed_item.helpers.duration}}'
/ end recording play controls
/ genre and social
.left.small
= session_genre(feed_item)
.left.small= '{{data.feed_item.helpers.genre}}'
.right.small.feed-details
%span.play-count
%span.plays
= feed_item.play_count
= '{{data.feed_item.play_count}}'
= image_tag 'content/icon_arrow.png', :height => "12", :width => "7"
%span.comment-count
%span.comments
= feed_item.comment_count
= '{{data.feed_item.comment_count}}'
= image_tag 'content/icon_comment.png', :height => "12", :width => "13"
%span.like-count
%span.likes
= feed_item.like_count
= '{{data.feed_item.comment_count}}'
= image_tag 'content/icon_like.png', :height => "12", :width => "12"
%a.details{:href => "#"} Details
%a.details-arrow.arrow-down-orange{:href => "#"}
@ -51,22 +52,24 @@
/ sub-table of musicians
%table.musicians{:cellpadding => "0", :cellspacing => "5"}
%tbody
- feed_item.unique_user_histories.each do |user|
%tr
%td{:width => "24"}
%a.avatar-tiny{:href => "#"}
= render_avatarable(user)
%td
%a{:href => "#"}
= "#{user.first_name} #{user.last_name}"
%td
.nowrap
- if user.total_instruments
- user.total_instruments.split('|').uniq.each do |instrument_id|
%img.instrument-icon{'instrument-id' =>instrument_id, height:24, width:24}
- else
%img.instrument-icon{'instrument-id' =>'default', height:24, width:24}
= '{% _.each(data.feed_item.participants, function(user) { %}'
%tr
%td{:width => "24"}
%a.avatar-tiny{:href => "#"}
%img{src: '{{user.helpers.avatar}}'}
%td
%a{:href => "#"}
= '{{user.first_name}} {{user.last_name}}'
%td
.nowrap
= '{% if(user.total_instruments) { %}'
= '{% _.each(_.uniq(user.total_instruments), function(instrument_id) { %}'
%img.instrument-icon{'instrument-id' =>'{{instrument_id}}', height:24, width:24}
= '{% }) %}'
= '{% } else { %}'
%img.instrument-icon{'instrument-id' =>'default', height:24, width:24}
= '{% } %}'
= '{% }) %}'
%br{:clear => "all"}/
%br/

View File

@ -0,0 +1,95 @@
%script{type: 'text/template', id: 'template-feed-recording'}
.feed-entry.recording-entry{:id => '{{data.feed_item.id}}', :'data-claimed-recording-id' => '{{data.candidate_claimed_recording.id}}' }
/ avatar
.avatar-small.ib
%img{ src: '{{data.feed_item.helpers.avatar}}' }
/ type and artist
.left.ml20.w15
.title{hoveraction: 'recording', :'recording-id' => '{{data.candidate_claimed_recording.id}}' } RECORDING
.artist
= '{{data.feed_item.helpers.artist_name}}'
%time.small.created_at.timeago{datetime: '{{data.feed_item.helpers.utc_created_at}}'}
= '{{data.feed_item.created_at}}'
/ name and description
.left.ml20.w30
.name.dotdotdot
= '{{data.feed_item.helpers.name}}'
.description.dotdotdot
= '{{data.feed_item.helpers.description}}'
/ timeline and controls
.right.w40
/ recording play controls
.recording-controls{ class: '{{data.mix_class}}'}
/ play button
%a.left.play-button{:href => "#"}
= image_tag 'content/icon_playbutton.png', width:20, height:20, class:'play-icon'
= "{% if(data.feed_item['has_mix?']) { %}"
%audio{preload: 'none'}
%source{src: '{{data.candidate_claimed_recording.mix.mp3_url}}', type:'audio/mpeg'}
%source{src: '{{data.candidate_claimed_recording.mix.ogg_url}}', type:'audio/ogg'}
= "{% } %}"
.recording-status
%span.status-text STILL MIXING
.recording-duration
%time{class: 'tick-duration duration', duration: '{{data.feed_item.duration}}'}
= '{{data.feed_item.duration}}'
/ playback position
.recording-position
/ start time
.recording-time 0:00
/ playback background &amp; slider
.recording-playback
.recording-slider
= image_tag 'content/slider_playcontrols.png', width:5, height:16
/ end time
.recording-time.recording-duration
%time{class: 'tick-duration duration', duration: '{{data.feed_item.duration}}'}
= '{{data.feed_item.duration}}'
/ end playback position
/ current playback time
.recording-current
0:00
/ end recording play controls
/ genre and social
.left.small= '{{data.feed_item.helpers.genre}}'
.right.small.feed-details
%span.play-count
%span.plays
= '{{data.feed_item.play_count}}'
= image_tag 'content/icon_arrow.png', :height => "12", :width => "7"
%span.comment-count
%span.comments
= '{{data.feed_item.comment_count}}'
= image_tag 'content/icon_comment.png', :height => "12", :width => "13"
%span.like-count
%span.likes
= '{{data.feed_item.like_count}}'
= image_tag 'content/icon_like.png', :height => "12", :width => "12"
%a.details{:href => "#"} Details
%a.details-arrow.arrow-down-orange{:href => "#"}
%br/
.musician-detail.hidden
/ sub-table of musicians
%table.musicians{:cellpadding => "0", :cellspacing => "5"}
%tbody
= '{% _.each(data.feed_item.grouped_tracks, function(track) { %}'
%tr
%td{:width => "24"}
%a.avatar-tiny{:href => "#"}
%img{src: '{{track.musician.helpers.avatar}}'}
%td
%a{:href => "#"}
= '{{track.musician.first_name}} {{track.musician.last_name}}'
%td
.nowrap
= '{% if(track.instrument_ids) { %}'
= '{% _.each(_.uniq(track.instrument_ids), function(instrument_id) { %}'
%img.instrument-icon{'instrument-id' =>'{{instrument_id}}', height:24, width:24}
= '{% }) %}'
= '{% } else { %}'
%img.instrument-icon{'instrument-id' =>'default', height:24, width:24}
= '{% } %}'
= '{% }) %}'
%br{:clear => "all"}/
%br/

View File

@ -60,7 +60,7 @@ SampleApp::Application.routes.draw do
match '/gmail_contacts', to: 'gmail#gmail_contacts'
match '/events/:slug', to: 'events#show', :via => :get
match '/events/:slug', to: 'events#show', :via => :get, :as => 'event'
# temporarily allow for debugging--only allows admini n
match '/listen_in', to: 'spikes#listen_in'