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