diff --git a/app/controllers/bubbles_controller.rb b/app/controllers/bubbles_controller.rb index 741e61c91..6d9306e7a 100644 --- a/app/controllers/bubbles_controller.rb +++ b/app/controllers/bubbles_controller.rb @@ -1,12 +1,14 @@ class BubblesController < ApplicationController include BucketScoped + skip_before_action :set_bucket, only: :index + + before_action :set_filter, only: :index before_action :set_bubble, only: %i[ show edit update ] - before_action :clear_assignees, only: :index - before_action :set_view, :set_filter, only: :index def index @bubbles = @filter.bubbles + @bubbles = @bubbles.mentioning(params[:term]) if params[:term].present? end def new @@ -31,6 +33,10 @@ class BubblesController < ApplicationController end private + def set_filter + @filter = Current.user.filters.build params: params.permit(*Filter::KNOWN_PARAMS) + end + def set_bubble @bubble = @bucket.bubbles.find params[:id] end @@ -38,18 +44,4 @@ class BubblesController < ApplicationController def bubble_params params.expect(bubble: [ :title, :color, :due_on, :image, tag_ids: [] ]) end - - def clear_assignees - params[:assignee_ids] = nil if helpers.unassigned_filter_activated? - end - - def set_view - @view = @bucket.views.find_by(creator: Current.user, id: params[:view_id]) if params[:view_id] - @view ||= @bucket.views.find_by(creator: Current.user, filters: helpers.bubble_filter_params.to_h) - params[:view_id] = @view&.id - end - - def set_filter - @filter = @bucket.bubble_filter_from helpers.view_filter_params - end end diff --git a/app/controllers/buckets/views_controller.rb b/app/controllers/buckets/views_controller.rb deleted file mode 100644 index 51484d818..000000000 --- a/app/controllers/buckets/views_controller.rb +++ /dev/null @@ -1,29 +0,0 @@ -class Buckets::ViewsController < ApplicationController - include BucketScoped - - before_action :set_view, only: %i[ update destroy ] - - def create - @view = @bucket.views.create! filters: filter_params - redirect_to bucket_bubbles_path(@bucket, **filter_params.merge(view_id: @view.id)), notice: "✓" - end - - def update - @view.update! filters: filter_params - redirect_to bucket_bubbles_path(@bucket, **filter_params.merge(view_id: @view.id)), notice: "✓" - end - - def destroy - @view.destroy - redirect_to buckets_path - end - - private - def set_view - @view = @bucket.views.find_by creator: Current.user, id: params[:id] - end - - def filter_params - helpers.bubble_filter_params.to_h - end -end diff --git a/app/controllers/buckets_controller.rb b/app/controllers/buckets_controller.rb index c12843fb1..8320b0735 100644 --- a/app/controllers/buckets_controller.rb +++ b/app/controllers/buckets_controller.rb @@ -2,7 +2,8 @@ class BucketsController < ApplicationController before_action :set_bucket, except: %i[ index new create ] def index - @buckets = (Current.user.buckets.all + Current.user.bucket_views.all).sort_by(&:updated_at).reverse! + @filters = Current.user.filters.all + @buckets = Current.user.buckets.all end def new @@ -11,7 +12,7 @@ class BucketsController < ApplicationController def create @bucket = Current.account.buckets.create! bucket_params - redirect_to bucket_bubbles_path(@bucket) + redirect_to bubbles_path(bucket_ids: @bucket) end def edit @@ -23,7 +24,7 @@ class BucketsController < ApplicationController @bucket.update! bucket_params @bucket.accesses.revise granted: grantees, revoked: revokees - redirect_to bucket_bubbles_path(@bucket) + redirect_to bubbles_path(bucket_ids: @bucket) end def destroy diff --git a/app/controllers/filters_controller.rb b/app/controllers/filters_controller.rb new file mode 100644 index 000000000..7683ce0c5 --- /dev/null +++ b/app/controllers/filters_controller.rb @@ -0,0 +1,30 @@ +class FiltersController < ApplicationController + before_action :set_filter, only: :destroy + + def create + @filter = Current.user.filters.create_or_find_by!(params: filter_params).tap(&:touch) + redirect_to bubbles_path(@filter.to_params) + end + + def destroy + @filter.destroy! + redirect_after_destroy + end + + private + def set_filter + @filter = Current.user.filters.find params[:id] + end + + def filter_params + params.permit(*Filter::KNOWN_PARAMS).compact_blank + end + + def redirect_after_destroy + if request.referer == root_url + redirect_to root_path + else + redirect_to bubbles_path(@filter.to_params) + end + end +end diff --git a/app/helpers/filters_helper.rb b/app/helpers/filters_helper.rb index 15919f416..e4e02db13 100644 --- a/app/helpers/filters_helper.rb +++ b/app/helpers/filters_helper.rb @@ -1,61 +1,32 @@ module FiltersHelper - def main_filter_text - (Bucket::View::ORDERS[params[:order_by]] || Bucket::View::STATUSES[params[:status]] || Bubble.default_order_by).humanize + def bubble_filters_heading(filter, &) + tag.h1 class: "txt-large flex align-center gap-half", + style: token_list("margin-inline-end: calc(var(--btn-size) / -2);": filter.savable?), & end - def tag_filter_text(filter) - if filter.tags - filter.tags.map(&:hashtag).to_choice_sentence + def buckets_filter_text(filter) + if filter.buckets.any? + filter.buckets.pluck(:name).to_choice_sentence else - "any tag" + "all projects" end end - def assignee_filter_text(filter) - if filter.assignees + def assignments_filter_text(filter) + if filter.assignees.present? "assigned to #{filter.assignees.pluck(:name).to_choice_sentence}" + elsif filter.assignments.unassigned? + "assigned to no one" else "assigned to anyone" end end - # `#bubble_filter_params` is memoized to avoid spam in logs about unpermitted params - def bubble_filter_params - @bubble_filter_params ||= params.permit :order_by, :status, assignee_ids: [], tag_ids: [] - end - - # `#view_filter_params` is memoized to avoid spam in logs about unpermitted params - def view_filter_params - @view_filter_params ||= bubble_filter_params.merge params.permit(:term, :view_id) - end - - def unassigned_filter_activated? - params[:status] == "unassigned" - end - - def default_filters? - bubble_filter_params.values.all?(&:blank?) || bubble_filter_params.to_h == Bucket::View.default_filters - end - - def bubble_filter_form_tag(path, method:, id: nil) - form_tag path, method: method, id: id do - yield if block_given? - - if params[:order_by].present? - concat hidden_field_tag(:order_by, params[:order_by]) - end - - if params[:status].present? - concat hidden_field_tag(:status, params[:status]) - end - - Array(params[:assignee_ids]).each do |assignee_id| - concat hidden_field_tag("assignee_ids[]", assignee_id, id: nil) - end - - Array(params[:tag_ids]).each do |tag_id| - concat hidden_field_tag("tag_ids[]", tag_id, id: nil) - end + def tags_filter_text(filter) + if filter.tags.present? + filter.tags.map(&:hashtag).to_choice_sentence + else + "any tag" end end end diff --git a/app/javascript/controllers/back_navigation_controller.js b/app/javascript/controllers/back_navigation_controller.js new file mode 100644 index 000000000..8fad83419 --- /dev/null +++ b/app/javascript/controllers/back_navigation_controller.js @@ -0,0 +1,13 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + static values = { fallbackDestination: String } + + connect() { + if (history.state.turbo?.restorationIndex > 0) { + this.element.href = "javascript:history.back()" + } else { + this.element.href = this.fallbackDestinationValue + } + } +} diff --git a/app/javascript/controllers/query_merger_controller.js b/app/javascript/controllers/query_merger_controller.js new file mode 100644 index 000000000..2fd17d65d --- /dev/null +++ b/app/javascript/controllers/query_merger_controller.js @@ -0,0 +1,9 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + merge({ params: { key, value } }) { + const url = new URL(window.location.href) + url.searchParams.set(key, value) + Turbo.visit(url) + } +} diff --git a/app/models/bubble.rb b/app/models/bubble.rb index 76e225fb4..a52cb6bb1 100644 --- a/app/models/bubble.rb +++ b/app/models/bubble.rb @@ -1,7 +1,7 @@ class Bubble < ApplicationRecord include Assignable, Boostable, Colored, Eventable, Messages, Poppable, Searchable, Staged, Taggable - belongs_to :bucket + belongs_to :bucket, touch: true belongs_to :creator, class_name: "User", default: -> { Current.user } has_one_attached :image, dependent: :purge_later @@ -10,6 +10,7 @@ class Bubble < ApplicationRecord scope :reverse_chronologically, -> { order created_at: :desc, id: :desc } scope :chronologically, -> { order created_at: :asc, id: :asc } + scope :in_bucket, ->(bucket) { where bucket: bucket } # FIXME: Compute activity and comment count at write time so it's easier to query for. scope :left_joins_comments, -> do @@ -22,29 +23,14 @@ class Bubble < ApplicationRecord left_joins_comments.select("bubbles.*, COUNT(comments.id) AS comment_count").group(:id).order("comment_count DESC") end - # FIXME: `status` implies an enum. Consider a name change. - scope :with_status, ->(status) do - status = status.presence_in %w[ popped active unassigned ] - public_send(status) if status - end - - scope :ordered_by, ->(order) do - case order + scope :indexed_by, ->(index) do + case index when "most_active" then ordered_by_activity when "most_discussed" then ordered_by_comments when "most_boosted" then ordered_by_boosts when "newest" then reverse_chronologically when "oldest" then chronologically - end - end - - class << self - def default_order_by - "most_active" - end - - def default_status - "active" + when "popped" then popped end end diff --git a/app/models/bubble/searchable.rb b/app/models/bubble/searchable.rb index 5cd3128d4..8132ae136 100644 --- a/app/models/bubble/searchable.rb +++ b/app/models/bubble/searchable.rb @@ -7,11 +7,9 @@ module Bubble::Searchable searchable_by :title, using: :bubbles_search_index scope :mentioning, ->(query) do - if query = query.presence - bubbles = search(query).select(:id).to_sql - comments = Comment.search(query).select(:id).to_sql - left_joins(:messages).where("bubbles.id in (#{bubbles}) or messages.messageable_id in (#{comments})").distinct - end + bubbles = Bubble.search(query).select(:id).to_sql + comments = Comment.search(query).select(:id).to_sql + left_joins(:messages).where("bubbles.id in (#{bubbles}) or messages.messageable_id in (#{comments})").distinct end end end diff --git a/app/models/bucket.rb b/app/models/bucket.rb index 728cae12d..f2476b761 100644 --- a/app/models/bucket.rb +++ b/app/models/bucket.rb @@ -1,5 +1,5 @@ class Bucket < ApplicationRecord - include Accessible, Filterable, Views + include Accessible belongs_to :account belongs_to :creator, class_name: "User", default: -> { Current.user } diff --git a/app/models/bucket/bubble_filter.rb b/app/models/bucket/bubble_filter.rb deleted file mode 100644 index ca44a9852..000000000 --- a/app/models/bucket/bubble_filter.rb +++ /dev/null @@ -1,34 +0,0 @@ -class Bucket::BubbleFilter - def initialize(bucket, params = {}) - @bucket = bucket - @status = params["status"] - @order_by = params["order_by"] - @term = params["term"] - @tag_ids = params["tag_ids"] - @assignee_ids = params["assignee_ids"] - end - - def bubbles - @bubbles ||= begin - result = bucket.bubbles - result = result.ordered_by(order_by || Bubble.default_order_by) - result = result.with_status(status || Bubble.default_status) - result = result.tagged_with(tags) if tags - result = result.assigned_to(assignees) if assignees - result = result.mentioning(term) if term - result - end - end - - def tags - @tags ||= account.tags.where(id: tag_ids) if tag_ids - end - - def assignees - @assignees ||= account.users.where(id: assignee_ids) if assignee_ids - end - - private - attr_reader :bucket, :status, :order_by, :term, :tag_ids, :assignee_ids - delegate :account, to: :bucket, private: true -end diff --git a/app/models/bucket/filterable.rb b/app/models/bucket/filterable.rb deleted file mode 100644 index c5f665aac..000000000 --- a/app/models/bucket/filterable.rb +++ /dev/null @@ -1,5 +0,0 @@ -module Bucket::Filterable - def bubble_filter_from(params = {}) - Bucket::BubbleFilter.new self, params - end -end diff --git a/app/models/bucket/view.rb b/app/models/bucket/view.rb deleted file mode 100644 index 01f29d3a1..000000000 --- a/app/models/bucket/view.rb +++ /dev/null @@ -1,27 +0,0 @@ -class Bucket::View < ApplicationRecord - include Assignees, OrderBy, Status, Summarized, Tags - - belongs_to :creator, class_name: "User", default: -> { Current.user } - belongs_to :bucket - - has_one :account, through: :creator - - validate :must_not_be_the_default_view - - class << self - def default_filters - { "order_by" => "most_active" } - end - end - - def bubbles - @bubbles ||= bucket.bubble_filter_from(filters).bubbles - end - - private - def must_not_be_the_default_view - if filters.values.all?(&:blank?) || filters == self.class.default_filters - errors.add :base, "must be different than the default view" - end - end -end diff --git a/app/models/bucket/view/assignees.rb b/app/models/bucket/view/assignees.rb deleted file mode 100644 index 4cb5128f1..000000000 --- a/app/models/bucket/view/assignees.rb +++ /dev/null @@ -1,12 +0,0 @@ -module Bucket::View::Assignees - extend ActiveSupport::Concern - - included do - store_accessor :filters, :assignee_ids - end - - private - def assignees - @assignees ||= account.users.where id: assignee_ids - end -end diff --git a/app/models/bucket/view/order_by.rb b/app/models/bucket/view/order_by.rb deleted file mode 100644 index 49d87a29e..000000000 --- a/app/models/bucket/view/order_by.rb +++ /dev/null @@ -1,15 +0,0 @@ -module Bucket::View::OrderBy - extend ActiveSupport::Concern - - included do - store_accessor :filters, :order_by - end - - private - ORDERS = { - "most_active" => "most active", - "most_discussed" => "most discussed", - "most_boosted" => "most boosted", - "newest" => "newest", - "oldest" => "oldest" } -end diff --git a/app/models/bucket/view/status.rb b/app/models/bucket/view/status.rb deleted file mode 100644 index 64d95f04a..000000000 --- a/app/models/bucket/view/status.rb +++ /dev/null @@ -1,10 +0,0 @@ -module Bucket::View::Status - extend ActiveSupport::Concern - - included do - store_accessor :filters, :status - end - - private - STATUSES = %w[ unassigned popped ].index_by(&:itself) -end diff --git a/app/models/bucket/view/summarized.rb b/app/models/bucket/view/summarized.rb deleted file mode 100644 index 46cdf70e3..000000000 --- a/app/models/bucket/view/summarized.rb +++ /dev/null @@ -1,24 +0,0 @@ -module Bucket::View::Summarized - def summary - [ order_by_summary, status_summary, tag_summary, assignee_summary ].compact.to_sentence.upcase_first - end - - private - delegate :to_sentence, to: "ApplicationController.helpers", private: true - - def order_by_summary - Bucket::View::ORDERS.fetch order_by, nil - end - - def status_summary - Bucket::View::STATUSES.fetch status, nil - end - - def assignee_summary - "assigned to #{assignees.pluck(:name).to_choice_sentence}" if assignees.any? - end - - def tag_summary - "tagged #{tags.map(&:hashtag).to_choice_sentence}" if tags.any? - end -end diff --git a/app/models/bucket/view/tags.rb b/app/models/bucket/view/tags.rb deleted file mode 100644 index 0b92d65ff..000000000 --- a/app/models/bucket/view/tags.rb +++ /dev/null @@ -1,12 +0,0 @@ -module Bucket::View::Tags - extend ActiveSupport::Concern - - included do - store_accessor :filters, :tag_ids - end - - private - def tags - @tags ||= account.tags.where id: tag_ids - end -end diff --git a/app/models/bucket/views.rb b/app/models/bucket/views.rb deleted file mode 100644 index ab9cba2c8..000000000 --- a/app/models/bucket/views.rb +++ /dev/null @@ -1,7 +0,0 @@ -module Bucket::Views - extend ActiveSupport::Concern - - included do - has_many :views, dependent: :delete_all - end -end diff --git a/app/models/filter.rb b/app/models/filter.rb new file mode 100644 index 000000000..b20a47d03 --- /dev/null +++ b/app/models/filter.rb @@ -0,0 +1,53 @@ +class Filter < ApplicationRecord + include Assignments, Buckets, Indexes, Summarized, Tags + + KNOWN_PARAMS = %i[ indexed_by bucket_ids assignments tag_ids ] + + belongs_to :creator, class_name: "User", default: -> { Current.user } + has_one :account, through: :creator + + class << self + def default_params + { "indexed_by" => "most_active" } + end + end + + def bubbles + @bubbles ||= begin + result = creator.accessible_bubbles.indexed_by(indexed_by) + result = result.active unless indexed_by.popped? + result = result.in_bucket(buckets) if buckets.present? + result = result.tagged_with(tags) if tags.present? + result = result.unassigned if assignments.unassigned? + result = result.assigned_to(assignees) if assignees.present? + result + end + end + + def to_params + ActionController::Parameters.new(params).permit(*KNOWN_PARAMS).tap do |params| + params[:filter_id] = id if persisted? + end + end + + def savable? + !bucket_default? + end + + def cacheable? + buckets.exists? + end + + def cache_key + ActiveSupport::Cache.expand_cache_key buckets.cache_key_with_version, super + end + + private + def bucket_default? + non_default_params.keys == %w[ bucket_ids ] && buckets.one? + end + + def non_default_params + params.reject { |k, v| self.class.default_params[k] == v } + end +end diff --git a/app/models/filter/assignments.rb b/app/models/filter/assignments.rb new file mode 100644 index 000000000..43bcb90ad --- /dev/null +++ b/app/models/filter/assignments.rb @@ -0,0 +1,9 @@ +module Filter::Assignments + def assignments + params["assignments"].to_s.inquiry + end + + def assignees + @assignees ||= account.users.where id: assignments.split(",") + end +end diff --git a/app/models/filter/buckets.rb b/app/models/filter/buckets.rb new file mode 100644 index 000000000..e444c8d1a --- /dev/null +++ b/app/models/filter/buckets.rb @@ -0,0 +1,11 @@ +module Filter::Buckets + extend ActiveSupport::Concern + + included do + store_accessor :params, :bucket_ids + end + + def buckets + @buckets ||= account.buckets.where id: bucket_ids.to_s.split(",") + end +end diff --git a/app/models/filter/indexes.rb b/app/models/filter/indexes.rb new file mode 100644 index 000000000..746b8dec1 --- /dev/null +++ b/app/models/filter/indexes.rb @@ -0,0 +1,7 @@ +module Filter::Indexes + INDEXES = %w[ most_active most_discussed most_boosted newest oldest popped ] + + def indexed_by + (params["indexed_by"] || self.class.default_params["indexed_by"]).inquiry + end +end diff --git a/app/models/filter/summarized.rb b/app/models/filter/summarized.rb new file mode 100644 index 000000000..a1286da23 --- /dev/null +++ b/app/models/filter/summarized.rb @@ -0,0 +1,36 @@ +module Filter::Summarized + def summary + [ index_summary, tag_summary, assignee_summary ].compact.to_sentence + " #{bucket_summary}" + end + + def plain_summary + summary.remove(/<\/?mark>/) + end + + private + def index_summary + "#{indexed_by.humanize}" + end + + def tag_summary + if tags.exists? + "tagged #{tags.map(&:hashtag).to_choice_sentence}" + end + end + + def assignee_summary + if assignees.exists? + "assigned to #{assignees.pluck(:name).to_choice_sentence}" + elsif assignments.unassigned? + "assigned to no one" + end + end + + def bucket_summary + if buckets.exists? + "in #{buckets.pluck(:name).to_choice_sentence}" + else + "in all projects" + end + end +end diff --git a/app/models/filter/tags.rb b/app/models/filter/tags.rb new file mode 100644 index 000000000..4b457b461 --- /dev/null +++ b/app/models/filter/tags.rb @@ -0,0 +1,11 @@ +module Filter::Tags + extend ActiveSupport::Concern + + included do + store_accessor :params, :tag_ids + end + + def tags + @tags ||= account.tags.where id: tag_ids.to_s.split(",") + end +end diff --git a/app/models/pop.rb b/app/models/pop.rb index 059336c67..45ccb2411 100644 --- a/app/models/pop.rb +++ b/app/models/pop.rb @@ -1,4 +1,4 @@ class Pop < ApplicationRecord - belongs_to :bubble + belongs_to :bubble, touch: true belongs_to :user, optional: true end diff --git a/app/models/user.rb b/app/models/user.rb index d214b1120..d452c1756 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,6 +1,4 @@ class User < ApplicationRecord - include Viewer - belongs_to :account has_many :sessions, dependent: :destroy @@ -8,7 +6,10 @@ class User < ApplicationRecord has_many :accesses, dependent: :destroy has_many :buckets, through: :accesses - has_many :bubbles, through: :buckets + has_many :accessible_bubbles, through: :buckets, source: :bubbles + + has_many :filters, foreign_key: :creator_id, inverse_of: :creator, dependent: :destroy + has_many :pops, dependent: :nullify has_many :assignments, foreign_key: :assignee_id, dependent: :destroy diff --git a/app/models/user/viewer.rb b/app/models/user/viewer.rb deleted file mode 100644 index a3d59585e..000000000 --- a/app/models/user/viewer.rb +++ /dev/null @@ -1,7 +0,0 @@ -module User::Viewer - extend ActiveSupport::Concern - - included do - has_many :bucket_views, class_name: "Bucket::View", foreign_key: :creator_id, dependent: :destroy - end -end diff --git a/app/views/bubbles/_filters.html.erb b/app/views/bubbles/_filters.html.erb index ce16c39b9..0903cdda3 100644 --- a/app/views/bubbles/_filters.html.erb +++ b/app/views/bubbles/_filters.html.erb @@ -1,28 +1,16 @@
-

- <%= render "bubbles/filters/main", bucket: bucket %> + <%= bubble_filters_heading filter do %> + <%= render "bubbles/filters/index", filter: filter %> in - <%= render "bubbles/filters/bucket", bucket: bucket %> + <%= render "bubbles/filters/buckets", filter: filter %> - <% if false %> - <%= button_tag type: :submit, class: "btn txt-small borderless" do %> - <%= image_tag "bookmark.svg", aria: { hidden: true }, size: 24 %> - Save this filter - <% end %> - <% else %> - <%= button_tag type: :submit, class: "btn txt-small borderless" do %> - <%= image_tag "bookmark-outline.svg", aria: { hidden: true }, size: 24 %> - Save this filter - <% end %> + <%= render "bubbles/filters/bookmark", filter: filter if filter.savable? %> <% end %> -

- <%= filter.tags ? "Tagged" : "with" %> - <%= render "bubbles/filters/tags", bucket: bucket, filter: filter %> + <%= filter.tags.present? ? "Tagged" : "with" %> + <%= render "bubbles/filters/tags", filter: filter %> - <% unless unassigned_filter_activated? %> - and <%= render "bubbles/filters/assignees", bucket: bucket, filter: filter %> - <% end %> + and <%= render "bubbles/filters/assignments", filter: filter %>

diff --git a/app/views/bubbles/_tags.html.erb b/app/views/bubbles/_tags.html.erb index 833a5f5e4..afd4a69e1 100644 --- a/app/views/bubbles/_tags.html.erb +++ b/app/views/bubbles/_tags.html.erb @@ -1,5 +1,5 @@ <% bubble.tags.each do |tag| %> - <%= link_to "##{tag.title}", bucket_bubbles_path(bubble.bucket, tag_id: tag), class: "bubble__bubble bubble__meta bubble__tag" %> + <%= link_to tag.hashtag, bubbles_path(bucket_id: bubble.bucket, tag_ids: tag), class: "bubble__bubble bubble__meta bubble__tag" %> <% end %> <% if bubble.tags.size < 3 %> diff --git a/app/views/bubbles/filters/_assignees.html.erb b/app/views/bubbles/filters/_assignments.html.erb similarity index 52% rename from app/views/bubbles/filters/_assignees.html.erb rename to app/views/bubbles/filters/_assignments.html.erb index f5bad92ea..f3a4df950 100644 --- a/app/views/bubbles/filters/_assignees.html.erb +++ b/app/views/bubbles/filters/_assignments.html.erb @@ -1,18 +1,20 @@
- + - <% bucket.users.active.order(:name).each do |user| %> -
  • <%= link_to user.name, bucket_bubbles_path(bucket, view_filter_params.merge(assignee_ids: [ user.id ])) %>
  • +
  • <%= link_to "No one", bubbles_path(filter.to_params.merge(assignments: :unassigned)), class: "filter__button" %>
  • + + <% Current.account.users.active.order(:name).each do |user| %> +
  • <%= link_to user.name, bubbles_path(filter.to_params.merge(assignments: user.id)) %>
  • <% end %>
    - <% if filter.assignees %> - <%= link_to bucket_bubbles_path(bucket, view_filter_params.without(:assignee_ids)), class: "btn", style: "font-size: 0.4em;" do %> + <% if filter.assignments.present? %> + <%= link_to bubbles_path(filter.to_params.without(:assignments)), class: "btn", style: "font-size: 0.4em;" do %> <%= image_tag "remove.svg", aria: { hidden: true }, size: 24 %> Clear <% end %> diff --git a/app/views/bubbles/filters/_bookmark.html.erb b/app/views/bubbles/filters/_bookmark.html.erb new file mode 100644 index 000000000..c38cf4e63 --- /dev/null +++ b/app/views/bubbles/filters/_bookmark.html.erb @@ -0,0 +1,16 @@ +<% if params[:filter_id] %> + <%= button_to filter_path(params[:filter_id]), method: :delete, class: "btn txt-small borderless" do %> + <%= image_tag "bookmark.svg", aria: { hidden: true }, size: 24 %> + Delete this filter + <% end %> +<% else %> + <%= form_with url: filters_path, method: :post do %> + <% Filter::KNOWN_PARAMS.each do |param| %> + <%= hidden_field_tag param, params[param] if params[param].present? %> + <% end %> + <%= button_tag type: :submit, class: "btn txt-small borderless" do %> + <%= image_tag "bookmark-outline.svg", aria: { hidden: true }, size: 24 %> + Save this filter + <% end %> + <% end %> +<% end %> diff --git a/app/views/bubbles/filters/_bucket_view_form.html.erb b/app/views/bubbles/filters/_bucket_view_form.html.erb deleted file mode 100644 index 3d2afceff..000000000 --- a/app/views/bubbles/filters/_bucket_view_form.html.erb +++ /dev/null @@ -1,19 +0,0 @@ -<% view ||= Bucket::View.new %> - -<%= bubble_filter_form_tag bucket_views_path(bucket), method: :post, id: dom_id(view, :new_form) %> - -<% if view.persisted? %> - <%= bubble_filter_form_tag bucket_view_path(bucket, view), method: :put, id: dom_id(view, :edit_form) %> -<% end %> - -<%= button_tag type: :submit, form: dom_id(view, :new_form), class: "btn", style: "font-size: 0.4em;" do %> - <%= image_tag "add.svg", aria: { hidden: true }, size: 24 %> - New -<% end %> - -<% if view.persisted? %> - <%= button_tag type: :submit, form: dom_id(view, :edit_form), class: "btn", style: "font-size: 0.4em;" do %> - <%= image_tag "pencil.svg", aria: { hidden: true }, size: 24 %> - Update - <% end %> -<% end %> diff --git a/app/views/bubbles/filters/_buckets.html.erb b/app/views/bubbles/filters/_buckets.html.erb new file mode 100644 index 000000000..33130a767 --- /dev/null +++ b/app/views/bubbles/filters/_buckets.html.erb @@ -0,0 +1,15 @@ +
    + + + + +
  • <%= link_to "all projects", bubbles_path(filter.to_params.merge(bucket_ids: nil)), class: "filter__button" %>
  • + + <% Current.user.buckets.each do |bucket| %> +
  • <%= link_to bucket.name, bubbles_path(filter.to_params.merge(bucket_ids: bucket.id)), class: "filter__button" %>
  • + <% end %> +
    +
    +
    diff --git a/app/views/bubbles/filters/_bucket.html.erb b/app/views/bubbles/filters/_index.html.erb similarity index 63% rename from app/views/bubbles/filters/_bucket.html.erb rename to app/views/bubbles/filters/_index.html.erb index 376516df9..ff83a8640 100644 --- a/app/views/bubbles/filters/_bucket.html.erb +++ b/app/views/bubbles/filters/_index.html.erb @@ -1,12 +1,12 @@
    - + - <% Current.user.buckets.order(:name).each do |bucket| %> -
  • <%= link_to bucket.name, bucket_bubbles_path(bucket, view_filter_params), class: "filter__button" %>
  • + <% Filter::INDEXES.each do |index| %> +
  • <%= link_to index.humanize, bubbles_path(filter.to_params.merge(indexed_by: index)), class: "filter__button" %>
  • <% end %>
    diff --git a/app/views/bubbles/filters/_main.html.erb b/app/views/bubbles/filters/_main.html.erb deleted file mode 100644 index ebb0adbd6..000000000 --- a/app/views/bubbles/filters/_main.html.erb +++ /dev/null @@ -1,17 +0,0 @@ -
    - - - - - <% Bucket::View::ORDERS.each do |key, value| %> -
  • <%= link_to value.humanize, bucket_bubbles_path(bucket, view_filter_params.merge(order_by: key, status: nil)), class: "filter__button" %>
  • - <% end %> - - <% Bucket::View::STATUSES.each do |key, value| %> -
  • <%= link_to value.humanize, bucket_bubbles_path(bucket, view_filter_params.merge(order_by: nil, status: key)), class: "filter__button" %>
  • - <% end %> -
    -
    -
    diff --git a/app/views/bubbles/filters/_tags.html.erb b/app/views/bubbles/filters/_tags.html.erb index 7fa28e9a6..e520a761c 100644 --- a/app/views/bubbles/filters/_tags.html.erb +++ b/app/views/bubbles/filters/_tags.html.erb @@ -1,18 +1,18 @@
    - + - <% bucket.tags.order(:title).each do |tag| %> -
  • <%= link_to tag.title, bucket_bubbles_path(bucket, view_filter_params.merge(tag_ids: [ tag.id ])) %>
  • + <% Current.account.tags.order(:title).each do |tag| %> +
  • <%= link_to tag.title, bubbles_path(filter.to_params.merge(tag_ids: tag.id)) %>
  • <% end %>
    - <% if filter.tags %> - <%= link_to bucket_bubbles_path(bucket, view_filter_params.without(:tag_ids)), class: "btn", style: "font-size: 0.4em;" do %> + <% if filter.tags.present? %> + <%= link_to bubbles_path(filter.to_params.without(:tag_ids)), class: "btn", style: "font-size: 0.4em;" do %> <%= image_tag "remove.svg", aria: { hidden: true }, size: 24 %> Clear <% end %> diff --git a/app/views/bubbles/index.html.erb b/app/views/bubbles/index.html.erb index c6ae7517e..406a1960d 100644 --- a/app/views/bubbles/index.html.erb +++ b/app/views/bubbles/index.html.erb @@ -1,24 +1,30 @@ -<% @page_title = @bucket.name %> +<% @page_title = @filter.plain_summary %> <% content_for :header do %> <% end %> -
    +
    <% if @bubbles.any? %> <%= render partial: "bubbles/bubble", collection: @bubbles.limit(10), cached: true %> <% else %> @@ -28,12 +34,12 @@
    - <%= bubble_filter_form_tag bucket_bubbles_path(@bucket), method: :get do %> + <%= form_tag bubbles_path(@filter.to_params), method: :get do %> <%= search_field_tag :term, params[:term], class: "input center flex-inline", placeholder: "Type to filter…" %> <% end %>
      - <%= render partial: "bubbles/list/bubble", collection: @bubbles %> + <%= render partial: "bubbles/list/bubble", collection: @bubbles, cached: true %>
    diff --git a/app/views/bubbles/list/_bubble.html.erb b/app/views/bubbles/list/_bubble.html.erb index 2862f5777..1d822e861 100644 --- a/app/views/bubbles/list/_bubble.html.erb +++ b/app/views/bubbles/list/_bubble.html.erb @@ -20,8 +20,10 @@ <% bubble.tags.each do |tag| %> - <%# FIXME: This is not going to work, this partial must be cacheable, so it can't mutate a changeable view filter live. Need a JS solution. %> - <%= link_to "##{tag.title}", bucket_bubbles_path(bubble.bucket, view_filter_params.merge(tag_ids: tag.id)), style: "color: #{bubble.color}" %> + <%= button_tag tag.hashtag, type: :button, + class: "btn btn--plain", style: "color: #{bubble.color}", data: { + controller: "query-merger", action: "query-merger#merge", + query_merger_key_param: "tag_ids", query_merger_value_param: tag.id } %> <% end %> <%= tag.time bubble.created_at, class: "txt-nowrap flex-item-justify-end" do %> diff --git a/app/views/bubbles/show.html.erb b/app/views/bubbles/show.html.erb index 92c2b14cd..9d941ee9e 100644 --- a/app/views/bubbles/show.html.erb +++ b/app/views/bubbles/show.html.erb @@ -2,9 +2,9 @@ <% content_for :header do %>