diff --git a/app/controllers/bubbles_controller.rb b/app/controllers/bubbles_controller.rb index 6d9306e7a..1b9591008 100644 --- a/app/controllers/bubbles_controller.rb +++ b/app/controllers/bubbles_controller.rb @@ -34,7 +34,7 @@ class BubblesController < ApplicationController private def set_filter - @filter = Current.user.filters.build params: params.permit(*Filter::KNOWN_PARAMS) + @filter = Current.user.filters.build params.permit(*Filter::KNOWN_PARAMS) end def set_bubble diff --git a/app/controllers/buckets_controller.rb b/app/controllers/buckets_controller.rb index 8320b0735..320882381 100644 --- a/app/controllers/buckets_controller.rb +++ b/app/controllers/buckets_controller.rb @@ -12,7 +12,7 @@ class BucketsController < ApplicationController def create @bucket = Current.account.buckets.create! bucket_params - redirect_to bubbles_path(bucket_ids: @bucket) + redirect_to bubbles_path(bucket_ids: [ @bucket ]) end def edit @@ -24,7 +24,7 @@ class BucketsController < ApplicationController @bucket.update! bucket_params @bucket.accesses.revise granted: grantees, revoked: revokees - redirect_to bubbles_path(bucket_ids: @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 index 7683ce0c5..cc5ee531d 100644 --- a/app/controllers/filters_controller.rb +++ b/app/controllers/filters_controller.rb @@ -2,7 +2,7 @@ class FiltersController < ApplicationController before_action :set_filter, only: :destroy def create - @filter = Current.user.filters.create_or_find_by!(params: filter_params).tap(&:touch) + @filter = Current.user.filters.create_or_find_by_params!(filter_params).tap(&:touch) redirect_to bubbles_path(@filter.to_params) end diff --git a/app/helpers/filters_helper.rb b/app/helpers/filters_helper.rb index e4e02db13..677e43fc0 100644 --- a/app/helpers/filters_helper.rb +++ b/app/helpers/filters_helper.rb @@ -5,8 +5,8 @@ module FiltersHelper end def buckets_filter_text(filter) - if filter.buckets.any? - filter.buckets.pluck(:name).to_choice_sentence + if filter.buckets.present? + filter.buckets.map(&:name).to_choice_sentence else "all projects" end @@ -14,7 +14,7 @@ module FiltersHelper def assignments_filter_text(filter) if filter.assignees.present? - "assigned to #{filter.assignees.pluck(:name).to_choice_sentence}" + "assigned to #{filter.assignees.map(&:name).to_choice_sentence}" elsif filter.assignments.unassigned? "assigned to no one" else diff --git a/app/models/bucket.rb b/app/models/bucket.rb index f2476b761..3f986e0fb 100644 --- a/app/models/bucket.rb +++ b/app/models/bucket.rb @@ -1,4 +1,5 @@ class Bucket < ApplicationRecord + include Filterable include Accessible belongs_to :account diff --git a/app/models/concerns/filterable.rb b/app/models/concerns/filterable.rb new file mode 100644 index 000000000..81656b42c --- /dev/null +++ b/app/models/concerns/filterable.rb @@ -0,0 +1,17 @@ +module Filterable + extend ActiveSupport::Concern + + included do + has_and_belongs_to_many :filters + + after_update { filters.touch_all } + before_destroy :remove_from_filters + end + + private + def remove_from_filters + filters.each do |filter| + filter.resource_removed kind: self.class.name.downcase, id: id + end + end +end diff --git a/app/models/filter.rb b/app/models/filter.rb index b20a47d03..9c012911a 100644 --- a/app/models/filter.rb +++ b/app/models/filter.rb @@ -1,35 +1,65 @@ class Filter < ApplicationRecord - include Assignments, Buckets, Indexes, Summarized, Tags + include Summarized - KNOWN_PARAMS = %i[ indexed_by bucket_ids assignments tag_ids ] + KNOWN_PARAMS = [ :indexed_by, :assignments, bucket_ids: [], assignee_ids: [], tag_ids: [] ] + INDEXES = %w[ most_active most_discussed most_boosted newest oldest popped ] belongs_to :creator, class_name: "User", default: -> { Current.user } + has_one :account, through: :creator + has_and_belongs_to_many :tags + has_and_belongs_to_many :buckets + has_and_belongs_to_many :assignees, class_name: "User", join_table: "assignees_filters", association_foreign_key: "assignee_id" + + before_validation :denormalize_resource_ids + before_validation :remove_default_params + + store_accessor :params, :indexed_by + store_accessor :params, :assignments + class << self def default_params { "indexed_by" => "most_active" } end + + def create_or_find_by_params!(params) + filter = new params + filter.save! + filter + rescue ActiveRecord::RecordNotUnique + find_by! params: filter.params + 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 = result.in_bucket(buckets.ids) if buckets.present? + result = result.tagged_with(tags.ids) if tags.present? + result = result.assigned_to(assignees.ids) 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? + params.merge(tag_ids: tags.ids, assignee_ids: assignees.ids, bucket_ids: buckets.ids).then do |params| + ActionController::Parameters.new(params).permit(*KNOWN_PARAMS).tap do |params| + params[:filter_id] = id if persisted? + end end end + def indexed_by + (params["indexed_by"] || self.class.default_params["indexed_by"]).inquiry + end + + def assignments + params["assignments"].to_s.inquiry + end + def savable? !bucket_default? end @@ -42,12 +72,28 @@ class Filter < ApplicationRecord ActiveSupport::Cache.expand_cache_key buckets.cache_key_with_version, super end + def resource_removed(kind:, id:) + params["#{kind}_ids"] = Array(params["#{kind}_ids"]).without(id).presence + non_default_params.blank? ? destroy : touch + end + private - def bucket_default? - non_default_params.keys == %w[ bucket_ids ] && buckets.one? + def remove_default_params + self[:params] = non_default_params.compact_blank end def non_default_params params.reject { |k, v| self.class.default_params[k] == v } end + + # `denormalize_resource_ids` stores ids in the params column to enforce uniqueness + def denormalize_resource_ids + params[:bucket_ids] = buckets.ids.presence + params[:tag_ids] = tags.ids.presence + params[:assignee_ids] = assignees.ids.presence + end + + def bucket_default? + non_default_params.keys == %w[ bucket_ids ] && buckets.one? + end end diff --git a/app/models/filter/assignments.rb b/app/models/filter/assignments.rb deleted file mode 100644 index 43bcb90ad..000000000 --- a/app/models/filter/assignments.rb +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index e444c8d1a..000000000 --- a/app/models/filter/buckets.rb +++ /dev/null @@ -1,11 +0,0 @@ -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 deleted file mode 100644 index 746b8dec1..000000000 --- a/app/models/filter/indexes.rb +++ /dev/null @@ -1,7 +0,0 @@ -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/tags.rb b/app/models/filter/tags.rb deleted file mode 100644 index 4b457b461..000000000 --- a/app/models/filter/tags.rb +++ /dev/null @@ -1,11 +0,0 @@ -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/tag.rb b/app/models/tag.rb index fb8db226d..c4cd4209e 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -1,4 +1,6 @@ class Tag < ApplicationRecord + include Filterable + belongs_to :account has_many :taggings, dependent: :destroy diff --git a/app/views/bubbles/filters/_assignments.html.erb b/app/views/bubbles/filters/_assignments.html.erb index f3a4df950..fb893ebb4 100644 --- a/app/views/bubbles/filters/_assignments.html.erb +++ b/app/views/bubbles/filters/_assignments.html.erb @@ -5,16 +5,16 @@ - <% if filter.assignments.present? %> - <%= link_to bubbles_path(filter.to_params.without(:assignments)), class: "btn", style: "font-size: 0.4em;" do %> + <% if [ filter.assignees, filter.assignments ].any?(&:present?) %> + <%= link_to bubbles_path(filter.to_params.without(:assignments, :assignee_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/filters/_bookmark.html.erb b/app/views/bubbles/filters/_bookmark.html.erb index c38cf4e63..5b7b962dc 100644 --- a/app/views/bubbles/filters/_bookmark.html.erb +++ b/app/views/bubbles/filters/_bookmark.html.erb @@ -5,9 +5,27 @@ <% 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? %> + <%= hidden_field_tag :indexed_by, params[:indexed_by] if params[:indexed_by].present? %> + <%= hidden_field_tag :assignments, params[:assignments] if params[:assignments].present? %> + + <% if values = params[:bucket_ids].presence %> + <% values.each do |value| %> + <%= hidden_field_tag "bucket_ids[]", value, id: nil %> + <% end %> <% end %> + + <% if values = params[:tag_ids].presence %> + <% values.each do |value| %> + <%= hidden_field_tag "tag_ids[]", value, id: nil %> + <% end %> + <% end %> + + <% if values = params[:assignee_ids].presence %> + <% values.each do |value| %> + <%= hidden_field_tag "assignee_ids[]", value, id: nil %> + <% end %> + <% end %> + <%= button_tag type: :submit, class: "btn txt-small borderless" do %> <%= image_tag "bookmark-outline.svg", aria: { hidden: true }, size: 24 %> Save this filter diff --git a/app/views/bubbles/filters/_buckets.html.erb b/app/views/bubbles/filters/_buckets.html.erb index 33130a767..d5537aa07 100644 --- a/app/views/bubbles/filters/_buckets.html.erb +++ b/app/views/bubbles/filters/_buckets.html.erb @@ -8,7 +8,7 @@