diff --git a/app/controllers/ambiguity_controller.rb b/app/controllers/ambiguity_controller.rb
index d7159135..f56a5fc9 100644
--- a/app/controllers/ambiguity_controller.rb
+++ b/app/controllers/ambiguity_controller.rb
@@ -18,7 +18,13 @@ def resolve
redirect_to :root
end
- private
+ protected
+
+ def apply_shows_tag_filter
+ @all_shows = @shows
+ return if params[:tag_slug].blank? || params[:tag_slug] == 'all'
+ @shows = @shows.tagged_with(params[:tag_slug])
+ end
def validate_sorting_for_tracks
params[:sort] = 'shows.date desc' unless
diff --git a/app/controllers/concerns/ambiguity/day_of_year.rb b/app/controllers/concerns/ambiguity/day_of_year.rb
index 52e66b84..07846d87 100644
--- a/app/controllers/concerns/ambiguity/day_of_year.rb
+++ b/app/controllers/concerns/ambiguity/day_of_year.rb
@@ -6,6 +6,7 @@ def slug_as_day_of_year
validate_sorting_for_shows
fetch_shows_on_day_of_year
+ apply_shows_tag_filter
hydrate_day_of_year
true
@@ -31,13 +32,12 @@ def fetch_shows_on_day_of_year
end
def day_of_year_sections
- @shows.group_by(&:tour_name)
- .each_with_object({}) do |(tour, shows), sections|
- sections[tour] = {
- shows:,
- likes: user_likes_for_shows(shows)
- }
- end
+ {
+ 'Today in History' => {
+ shows: @shows,
+ likes: user_likes_for_shows(@shows)
+ }
+ }
end
def month
diff --git a/app/controllers/concerns/ambiguity/song_title.rb b/app/controllers/concerns/ambiguity/song_title.rb
index da3c8f54..226dddd8 100644
--- a/app/controllers/concerns/ambiguity/song_title.rb
+++ b/app/controllers/concerns/ambiguity/song_title.rb
@@ -16,13 +16,21 @@ def hydrate_song_page
@next_song = next_song
@view = 'songs/show'
@ambiguity_controller = 'songs'
- @tracks = song.tracks
- .includes(:songs, show: :venue, track_tags: :tag)
- .order(@order_by)
- .paginate(page: params[:page], per_page: params[:per_page].presence || 20)
+ @tracks = fetch_song_tracks
@tracks_likes = user_likes_for_tracks(@tracks)
end
+ def fetch_song_tracks
+ tracks = song.tracks.includes(:songs, show: :venue, track_tags: :tag).order(@order_by)
+ tracks = tagged_tracks(tracks)
+ tracks.paginate(page: params[:page], per_page: params[:per_page].presence || 20)
+ end
+
+ def tagged_tracks(tracks)
+ return tracks if params[:tag_slug].blank? || params[:tag_slug] == 'all'
+ tracks.tagged_with(params[:tag_slug])
+ end
+
def previous_song
Song.where('title < ?', @song.title)
.order(title: :desc)
diff --git a/app/controllers/concerns/ambiguity/tour_name.rb b/app/controllers/concerns/ambiguity/tour_name.rb
index f1bbcc98..746ac145 100644
--- a/app/controllers/concerns/ambiguity/tour_name.rb
+++ b/app/controllers/concerns/ambiguity/tour_name.rb
@@ -19,6 +19,7 @@ def tour
def hydrate_tour_page
@ogp_title = "Listen to shows from #{tour.name}"
@shows = tour.shows.order(@order_by)
+ apply_shows_tag_filter
@shows_likes = user_likes_for_shows(@shows)
@sections = tour_sections
@title = tour.name
diff --git a/app/controllers/concerns/ambiguity/venue_name.rb b/app/controllers/concerns/ambiguity/venue_name.rb
index 6fcedb6f..1f7ba714 100644
--- a/app/controllers/concerns/ambiguity/venue_name.rb
+++ b/app/controllers/concerns/ambiguity/venue_name.rb
@@ -16,7 +16,7 @@ def venue
def hydrate_venue_page
@ogp_title = "Listen to shows from #{venue.name}"
- @shows = venue.shows.includes(show_tags: :tag).order(@order_by)
+ @shows = fetch_shows
@shows_likes = user_likes_for_shows(@shows)
@previous_venue = prev_venue
@next_venue = next_venue
@@ -25,6 +25,12 @@ def hydrate_venue_page
@ambiguity_controller = 'venues'
end
+ def fetch_shows
+ shows = venue.shows.includes(show_tags: :tag).order(@order_by)
+ return shows if params[:tag_slug].blank? || params[:tag_slug] == 'all'
+ shows.tagged_with(params[:tag_slug])
+ end
+
def prev_venue
Venue.where('name < ?', venue.name).order(name: :desc).first ||
Venue.order(name: :desc).first
diff --git a/app/controllers/concerns/ambiguity/year.rb b/app/controllers/concerns/ambiguity/year.rb
index eee278ef..b35a3ae6 100644
--- a/app/controllers/concerns/ambiguity/year.rb
+++ b/app/controllers/concerns/ambiguity/year.rb
@@ -26,6 +26,7 @@ def shows_during_year
def hydrate_year_page # rubocop:disable Metrics/MethodLength
@ogp_title = "Listen to shows from #{current_slug}"
@shows = shows_during_year
+ apply_shows_tag_filter
@sections =
@shows.group_by(&:tour_name)
.each_with_object({}) do |(tour, shows), sections|
diff --git a/app/controllers/concerns/ambiguity/year_range.rb b/app/controllers/concerns/ambiguity/year_range.rb
index e4efa932..826b4925 100644
--- a/app/controllers/concerns/ambiguity/year_range.rb
+++ b/app/controllers/concerns/ambiguity/year_range.rb
@@ -15,6 +15,7 @@ def slug_as_year_range
def hydrate_year_range_page # rubocop:disable Metrics/MethodLength
@ogp_title = "Listen to shows from #{year1} to #{year2}"
@shows = shows_during_year_range
+ apply_shows_tag_filter
@sections =
@shows.group_by(&:tour_name)
.each_with_object({}) do |(tour, shows), sections|
diff --git a/app/helpers/filter_helper.rb b/app/helpers/filter_helper.rb
new file mode 100644
index 00000000..d0c3ee23
--- /dev/null
+++ b/app/helpers/filter_helper.rb
@@ -0,0 +1,120 @@
+module FilterHelper
+ def filter_title(param_name, items)
+ item = items.find { |_k, v| params[param_name] == v }&.first&.html_safe
+ item.presence || items.first.first.html_safe
+ end
+
+ def filter(param_name, items) # rubocop:disable Metrics/AbcSize
+ skippables = %w[controller action name t slug] + [param_name.to_s]
+ items.map do |k, v|
+ link = params[param_name] == v ? "#{k}" : k
+ param_str = "?#{param_name}=#{CGI.escape(v)}"
+ params.each do |key, val|
+ next if key.in?(skippables)
+ param_str += "{key}=#{val}" if val.present?
+ end
+ tag.li(link_to(link.html_safe, param_str))
+ end.join.html_safe
+ end
+
+ def sort_songs_and_venues_links(item_hash)
+ item_hash.map do |k, v|
+ link = params[:sort] == v ? "#{k}" : k
+ tag.li(link_to(link.html_safe, "?char=#{params[:char]}&sort=#{CGI.escape(v)}"))
+ end.join.html_safe
+ end
+
+ def sort_tags_title(item_hash)
+ item_hash.each_with_index do |(key, val), idx|
+ if (idx.zero? && params[:sort].blank?) ||
+ params[:sort] == val
+ return "#{key}".html_safe
+ end
+ end
+ end
+
+ def sort_tags_links(item_hash)
+ str = ''
+ item_hash.each do |k, v|
+ link = params[:sort] == v ? "#{k}" : k
+ str += tag.li(link_to(link.html_safe, "?sort=#{CGI.escape(v)}"))
+ end
+ str.html_safe
+ end
+
+ def sort_songs_title(items)
+ items.each_with_index do |(key, val), i|
+ return key.html_safe if (i.zero? && params[:sort].empty?) ||
+ params[:sort] == val
+ end
+ end
+
+ def show_sort_items
+ {
+ 'Reverse Date' => 'date desc',
+ 'Date' => 'date asc',
+ 'Likes' => 'likes',
+ 'Duration' => 'duration'
+ }
+ end
+
+ def my_track_sort_items
+ { 'Title' => 'title' }.merge(track_sort_items)
+ end
+
+ def track_sort_items
+ {
+ 'Reverse Date' => 'shows.date desc',
+ 'Date' => 'shows.date asc',
+ 'Likes' => 'likes',
+ 'Duration' => 'duration'
+ }
+ end
+
+ def songs_and_venues_sort_items
+ {
+ 'Title' => 'title',
+ 'Track Count' => 'performances'
+ }
+ end
+
+ def tag_sort_items
+ {
+ 'Name' => 'name',
+ 'Track Count' => 'tracks_count',
+ 'Show Count' => 'shows_count'
+ }
+ end
+
+ def venues_sort_items
+ {
+ 'Name' => 'name',
+ 'Show Count' => 'performances'
+ }
+ end
+
+ def stored_playlist_sort_items
+ {
+ 'Name' => 'name',
+ 'Duration' => 'duration'
+ }
+ end
+
+ def song_track_tag_items(song)
+ tag_data = song.tracks.joins(:tags).order('tags.name').pluck('tags.name', 'tags.slug').uniq
+ generic_track_items(tag_data)
+ end
+
+ def shows_tag_items(shows)
+ tag_data = shows.joins(:tags).order('tags.name').pluck('tags.name', 'tags.slug').uniq
+ generic_track_items(tag_data)
+ end
+
+ def generic_track_items(tag_data)
+ items = { 'All Tags' => 'all' }
+ tag_data.each do |name, slug|
+ items[name] = slug
+ end
+ items
+ end
+end
diff --git a/app/helpers/sort_helper.rb b/app/helpers/sort_helper.rb
deleted file mode 100644
index 82e82f18..00000000
--- a/app/helpers/sort_helper.rb
+++ /dev/null
@@ -1,103 +0,0 @@
-module SortHelper
- def sort_songs_and_venues_links(item_hash)
- item_hash.map do |k, v|
- link = params[:sort] == v ? "#{k}" : k
- tag.li(link_to(link.html_safe, "?char=#{params[:char]}&sort=#{CGI.escape(v)}"))
- end.join.html_safe
- end
-
- def sort_tags_title(item_hash)
- item_hash.each_with_index do |(key, val), idx|
- if (idx.zero? && params[:sort].blank?) ||
- params[:sort] == val
- return "#{key}".html_safe
- end
- end
- end
-
- def sort_tags_links(item_hash)
- str = ''
- item_hash.each do |k, v|
- link = params[:sort] == v ? "#{k}" : k
- str += tag.li(link_to(link.html_safe, "?sort=#{CGI.escape(v)}"))
- end
- str.html_safe
- end
-
- def sort_filter_link_title(items)
- items.each do |k, v|
- return k.html_safe if params[:sort] == v || params[:sort].blank?
- end
- items.first.first.html_safe
- end
-
- def sort_filter(items)
- items.map do |k, v|
- link = params[:sort] == v ? "#{k}" : k
- param_str = "?sort=#{CGI.escape(v)}"
- params.each do |key, val|
- next if key.in?(%w[controller action name t sort])
- param_str += "{key}=#{val}" if val.present?
- end
- tag.li(link_to(link.html_safe, param_str))
- end.join.html_safe
- end
-
- def sort_songs_title(items)
- items.each_with_index do |(key, val), i|
- return key.html_safe if (i.zero? && params[:sort].empty?) ||
- params[:sort] == val
- end
- end
-
- def show_sort_items
- {
- ' Reverse Date' => 'date desc',
- ' Date' => 'date asc',
- ' Likes' => 'likes',
- ' Duration' => 'duration'
- }
- end
-
- def my_track_sort_items
- { ' Title' => 'title' }.merge(track_sort_items)
- end
-
- def track_sort_items
- {
- ' Reverse Date' => 'shows.date desc',
- ' Date' => 'shows.date asc',
- ' Likes' => 'likes',
- ' Duration' => 'duration'
- }
- end
-
- def songs_and_venues_sort_items
- {
- ' Title' => 'title',
- ' Track Count' => 'performances'
- }
- end
-
- def tag_sort_items
- {
- ' Name' => 'name',
- ' Track Count' => 'tracks_count',
- ' Show Count' => 'shows_count'
- }
- end
-
- def venues_sort_items
- {
- ' Name' => 'name',
- ' Show Count' => 'performances'
- }
- end
-
- def stored_playlist_sort_items
- {
- ' Name' => 'name',
- ' Duration' => 'duration'
- }
- end
-end
diff --git a/app/javascript/packs/application.css.scss b/app/javascript/packs/application.css.scss
index 3cae33b7..5cc53edc 100644
--- a/app/javascript/packs/application.css.scss
+++ b/app/javascript/packs/application.css.scss
@@ -302,6 +302,7 @@ html {
.current {
color: $nav_highlight !important;
font-weight: bold !important;
+ margin-right: 3px;
}
a:hover, a:focus {
text-decoration: underline !important;
@@ -1086,7 +1087,6 @@ html {
width: 100%;
border-bottom: 1px solid $hr_gray;
margin-top: 10px;
- margin-bottom: 5px;
}
.next_item_link {
float: right;
@@ -1145,6 +1145,19 @@ html {
font-size: 18px;
}
}
+ .filter_label {
+ float: left;
+ width: 30px;
+ position: relative;
+ top: 7px;
+ font-size: 1.2em;
+ }
+ .filter_dropdown {
+ float: left;
+ }
+ .hr {
+ margin-bottom: 15px;
+ }
.app_callout {
* {
float: left;
diff --git a/app/views/shared/_sort_filter.html.slim b/app/views/shared/_sort_filter.html.slim
index e2b906bd..cb3a05de 100644
--- a/app/views/shared/_sort_filter.html.slim
+++ b/app/views/shared/_sort_filter.html.slim
@@ -1,7 +1,8 @@
-h3
- |> Sort by
+.filter_label Sort
+.filter_dropdown
.btn-group
button.btn.btn-default.dropdown-toggle type='button' data-toggle='dropdown' aria-haspopup='true' aria-expanded='false'
- => sort_filter_link_title(item_hash)
+ => filter_title(:sort, item_hash)
span.caret
- ul.dropdown-menu = sort_filter(item_hash)
+ ul.dropdown-menu = filter(:sort, item_hash)
+= clear_both
diff --git a/app/views/shared/_sort_songs_and_venues_filter.html.slim b/app/views/shared/_sort_songs_and_venues_filter.html.slim
index 48686d98..1008a218 100644
--- a/app/views/shared/_sort_songs_and_venues_filter.html.slim
+++ b/app/views/shared/_sort_songs_and_venues_filter.html.slim
@@ -1,7 +1,8 @@
-h3
- |> Sort by
+.filter_label Sort
+.filter_dropdown
.btn-group
button.btn.btn-default.dropdown-toggle type='button' data-toggle='dropdown' aria-haspopup='true' aria-expanded='false'
=> sort_songs_title(item_hash)
span.caret
ul.dropdown-menu = sort_songs_and_venues_links(item_hash)
+= clear_both
diff --git a/app/views/shared/_tag_filter.html.slim b/app/views/shared/_tag_filter.html.slim
new file mode 100644
index 00000000..ed44c3ca
--- /dev/null
+++ b/app/views/shared/_tag_filter.html.slim
@@ -0,0 +1,8 @@
+.filter_label Tag
+.filter_dropdown
+ .btn-group
+ button.btn.btn-default.dropdown-toggle type='button' data-toggle='dropdown' aria-haspopup='true' aria-expanded='false'
+ => filter_title(:tag_slug, item_hash)
+ span.caret
+ ul.dropdown-menu = filter(:tag_slug, item_hash)
+= clear_both
diff --git a/app/views/shows/index.html.slim b/app/views/shows/index.html.slim
index ec390e2d..1198bdc8 100644
--- a/app/views/shows/index.html.slim
+++ b/app/views/shows/index.html.slim
@@ -10,6 +10,8 @@
.hr
= render partial: 'shared/sort_filter', locals: { item_hash: show_sort_items }
+ = render partial: 'shared/tag_filter', locals: { item_hash: shows_tag_items(@all_shows) }
+
#content_box
- if @shows.empty?
diff --git a/app/views/songs/show.html.slim b/app/views/songs/show.html.slim
index 5cc0cfb3..6b4258a9 100644
--- a/app/views/songs/show.html.slim
+++ b/app/views/songs/show.html.slim
@@ -15,10 +15,14 @@
- if @song.instrumental?
h3 (Instrumental)
- h3 Total tracks: #{@song.tracks.size}
+ h3 Total tracks: #{@tracks.size}
+
+ .hr
+
+ = render partial: 'shared/sort_filter', locals: { item_hash: track_sort_items }
+ = render partial: 'shared/tag_filter', locals: { item_hash: song_track_tag_items(@song) }
.hr
- br
- if @song.lyrics.present?
= link_to "#{tag.i(class: 'glyphicon glyphicon-book')} Lyrics".html_safe, '#', class: 'song_lyrics btn btn-default', data: { title: @song.title, lyrics: lyrics_for(@song) }
@@ -35,10 +39,6 @@
.hr
- = render partial: 'shared/sort_filter', locals: { item_hash: track_sort_items }
-
- .hr
-
= link_to('<< Previous Song'.html_safe, "/#{@previous_song.slug}")
= clear_both
= link_to('Next Song >>'.html_safe, "/#{@next_song.slug}", class: 'next_item_link')
diff --git a/app/views/venues/show.html.slim b/app/views/venues/show.html.slim
index 659cca2e..8a79d6d9 100644
--- a/app/views/venues/show.html.slim
+++ b/app/views/venues/show.html.slim
@@ -13,6 +13,8 @@
.hr
= render partial: 'shared/sort_filter', locals: { item_hash: show_sort_items }
+ = render partial: 'shared/tag_filter', locals: { item_hash: shows_tag_items(@venue.shows) }
+
.hr
= link_to('<< Previous Venue'.html_safe, "/#{@previous_venue.slug}")
@@ -35,6 +37,7 @@
= likable(show, @shows_likes[idx], 'small')
h3 = duration_readable(show.duration, 'letters')
= render partial: 'shared/context_menu_for_show', locals: { show: show, viewing_this_show: false }
+ = display_tag_instances(show.show_tags)
= clear_both
= clear_both
diff --git a/spec/features/songs_spec.rb b/spec/features/songs_spec.rb
index 8b1c3e35..154e6b49 100644
--- a/spec/features/songs_spec.rb
+++ b/spec/features/songs_spec.rb
@@ -55,7 +55,7 @@
# Default sort by Title
within('#title_box') do
- expect_content('Sort by', 'Title')
+ expect_content('Sort', 'Title')
end
expect_content_in_order([song1, song2, song3].map(&:title))
@@ -63,7 +63,7 @@
within('#title_box') do
first('.btn-group').click
click_link('Track Count')
- expect_content('Sort by', 'Track Count')
+ expect_content('Sort', 'Track Count')
end
expect_content_in_order([song2, song1, song3].map(&:title))
end
diff --git a/spec/features/venues_spec.rb b/spec/features/venues_spec.rb
index 0c65894c..195eab29 100644
--- a/spec/features/venues_spec.rb
+++ b/spec/features/venues_spec.rb
@@ -58,7 +58,7 @@
# Default sort by Name
within('#title_box') do
- expect_content('Sort by', 'Name')
+ expect_content('Sort', 'Name')
end
expect_content_in_order([venue1, venue2, venue3].map(&:name))
@@ -66,7 +66,7 @@
within('#title_box') do
first('.btn-group').click
click_link('Show Count')
- expect_content('Sort by', 'Show Count')
+ expect_content('Sort', 'Show Count')
end
expect_content_in_order([venue3, venue1, venue2].map(&:name))
end
diff --git a/spec/support/feature_helpers.rb b/spec/support/feature_helpers.rb
index 6bda540a..11097b4f 100644
--- a/spec/support/feature_helpers.rb
+++ b/spec/support/feature_helpers.rb
@@ -28,7 +28,7 @@ def expect_track_sorting_controls(tracks) # rubocop:disable Metrics/AbcSize
# Default sort by Reverse date
within('#title_box') do
- expect_content('Sort by', 'Reverse Date')
+ expect_content('Sort', 'Reverse Date')
end
expect_content_in_order(titles_by_date.reverse)
@@ -36,7 +36,7 @@ def expect_track_sorting_controls(tracks) # rubocop:disable Metrics/AbcSize
within('#title_box') do
first('.dropdown-toggle').click
click_link('Date')
- expect_content('Sort by', 'Date')
+ expect_content('Sort', 'Date')
end
expect_content_in_order(titles_by_date)
@@ -44,7 +44,7 @@ def expect_track_sorting_controls(tracks) # rubocop:disable Metrics/AbcSize
within('#title_box') do
first('.dropdown-toggle').click
click_link('Likes')
- expect_content('Sort by', 'Likes')
+ expect_content('Sort', 'Likes')
end
expect_content_in_order(titles_by_likes)
@@ -52,7 +52,7 @@ def expect_track_sorting_controls(tracks) # rubocop:disable Metrics/AbcSize
within('#title_box') do
first('.dropdown-toggle').click
click_link('Duration')
- expect_content('Sort by', 'Duration')
+ expect_content('Sort', 'Duration')
end
expect_content_in_order(titles_by_duration)
end
@@ -64,7 +64,7 @@ def expect_show_sorting_controls(shows) # rubocop:disable Metrics/AbcSize
# Default sort by Reverse date
within('#title_box') do
- expect_content('Sort by', 'Reverse Date')
+ expect_content('Sort', 'Reverse Date')
end
expect_content_in_order(dates_by_date.reverse)
@@ -72,7 +72,7 @@ def expect_show_sorting_controls(shows) # rubocop:disable Metrics/AbcSize
within('#title_box') do
first('.dropdown-toggle').click
click_link('Date')
- expect_content('Sort by', 'Date')
+ expect_content('Sort', 'Date')
end
expect_content_in_order(dates_by_date)
@@ -80,7 +80,7 @@ def expect_show_sorting_controls(shows) # rubocop:disable Metrics/AbcSize
within('#title_box') do
first('.dropdown-toggle').click
click_link('Likes')
- expect_content('Sort by', 'Likes')
+ expect_content('Sort', 'Likes')
end
expect_content_in_order(dates_by_likes)
@@ -88,7 +88,7 @@ def expect_show_sorting_controls(shows) # rubocop:disable Metrics/AbcSize
within('#title_box') do
first('.dropdown-toggle').click
click_link('Duration')
- expect_content('Sort by', 'Duration')
+ expect_content('Sort', 'Duration')
end
expect_content_in_order(dates_by_duration)
end