diff --git a/app/controllers/bubbles_controller.rb b/app/controllers/bubbles_controller.rb index 59fb765b5..3987ac5a5 100644 --- a/app/controllers/bubbles_controller.rb +++ b/app/controllers/bubbles_controller.rb @@ -2,24 +2,15 @@ class BubblesController < ApplicationController include BucketScoped before_action :set_bubble, only: %i[ show edit update ] + before_action :set_assignee_filters, :set_tag_filters, only: :index def index @bubbles = @bucket.bubbles - @bubbles = params.include?(:popped) ? @bubbles.popped : @bubbles.not_popped - - if params[:term].present? - @bubbles = @bubbles.mentioning(params[:term]) - end - - if params[:tag_id] - @tag = Current.account.tags.find(params[:tag_id]) - @bubbles = @bubbles.tagged_with(@tag) - end - - if params[:assignee_id] - @assignee = @bucket.users.find(params[:assignee_id]) - @bubbles = @bubbles.assigned_to(@assignee) - end + @bubbles = @bubbles.ordered_by(params[:order_by] || Bubble.default_order_by) + @bubbles = @bubbles.with_status(params[:status] || Bubble.default_status) + @bubbles = @bubbles.tagged_with(@tag_filters) if @tag_filters + @bubbles = @bubbles.assigned_to(@assignee_filters) if @assignee_filters + @bubbles = @bubbles.mentioning(params[:term]) if params[:term] end def new @@ -38,13 +29,27 @@ class BubblesController < ApplicationController end def update - @bubble.update!(bubble_params) + @bubble.update! bubble_params redirect_to bucket_bubble_url(@bucket, @bubble) end private def set_bubble - @bubble = @bucket.bubbles.find(params[:id]) + @bubble = @bucket.bubbles.find params[:id] + end + + def set_assignee_filters + params[:assignee_ids] = nil if status_filter_param.unassigned? + @assignee_filters = Current.account.users.where(id: params[:assignee_ids]) if params[:assignee_ids] + end + + def status_filter_param + params.fetch(:status, "")&.inquiry + end + helper_method :status_filter_param + + def set_tag_filters + @tag_filters = Current.account.tags.where(id: params[:tag_ids]) if params[:tag_ids] end def bubble_params diff --git a/app/controllers/buckets/views_controller.rb b/app/controllers/buckets/views_controller.rb new file mode 100644 index 000000000..b225cffae --- /dev/null +++ b/app/controllers/buckets/views_controller.rb @@ -0,0 +1,21 @@ +class Buckets::ViewsController < ApplicationController + include BucketScoped + + def create + @bucket.views.create! filters: view_params.merge(assignee_ids:, tag_ids:).compact_blank + redirect_back_or_to bucket_bubbles_path(@bucket), notice: "Filters saved" + end + + private + def view_params + params.require(:view).permit(:order_by, :status, :assignee_ids, :tag_ids) + end + + def assignee_ids + view_params[:assignee_ids]&.split(",") + end + + def tag_ids + view_params[:tag_ids]&.split(",") + end +end diff --git a/app/controllers/buckets_controller.rb b/app/controllers/buckets_controller.rb index 275496794..3c70164f6 100644 --- a/app/controllers/buckets_controller.rb +++ b/app/controllers/buckets_controller.rb @@ -2,7 +2,7 @@ class BucketsController < ApplicationController before_action :set_bucket, except: %i[ index new create ] def index - @buckets = Current.user.buckets.all + @buckets = (Current.user.buckets.all + Current.user.bucket_views.all).sort_by(&:updated_at) end def new @@ -10,7 +10,7 @@ class BucketsController < ApplicationController end def create - @bucket = Current.account.buckets.create!(bucket_params) + @bucket = Current.account.buckets.create! bucket_params redirect_to bucket_bubbles_url(@bucket) end diff --git a/app/helpers/bubbles_helper.rb b/app/helpers/bubbles_helper.rb index 8bdf50648..9cb9bd30d 100644 --- a/app/helpers/bubbles_helper.rb +++ b/app/helpers/bubbles_helper.rb @@ -20,8 +20,4 @@ module BubblesHelper "--bubble-size: var(--bubble-size-#{rank});" end - - def bubble_filter_params - @bubble_filter_params ||= params.permit(:popped, :term, :tag_id, :assignee_id) - end end diff --git a/app/helpers/filters_helper.rb b/app/helpers/filters_helper.rb new file mode 100644 index 000000000..143b19390 --- /dev/null +++ b/app/helpers/filters_helper.rb @@ -0,0 +1,26 @@ +module FiltersHelper + def main_filter_text + (Bucket::View::ORDERS[params[:order_by]] || Bucket::View::STATUSES[params[:status]] || Bubble.default_order_by.humanize).upcase_first + end + + def tag_filter_text + if @tag_filters + @tag_filters.map(&:hashtag).to_choice_sentence + else + "any tag" + end + end + + def assignee_filter_text + if @assignee_filters + "assigned to #{@assignee_filters.map(&:name).to_choice_sentence}" + 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, :term, :assignee_ids, :tag_ids, assignee_ids: [], tag_ids: [] + end +end diff --git a/app/models/bubble.rb b/app/models/bubble.rb index debbf14cd..1e7b4ebef 100644 --- a/app/models/bubble.rb +++ b/app/models/bubble.rb @@ -1,4 +1,5 @@ class Bubble < ApplicationRecord + include ::Searchable include Assignable, Boostable, Colored, Commentable, Eventable, Poppable, Searchable, Taggable, Threaded belongs_to :bucket @@ -10,16 +11,28 @@ class Bubble < ApplicationRecord before_save :set_default_title - scope :reverse_chronologically, -> { order(created_at: :desc, id: :desc) } + scope :reverse_chronologically, -> { order created_at: :desc, id: :desc } + scope :chronologically, -> { order created_at: :asc, id: :asc } scope :ordered_by_activity, -> { left_joins(:comments).group(:id).order(Arel.sql("COUNT(comments.id) + boost_count DESC")) } + scope :with_status, ->(status) { public_send status.presence_in(%w[ popped not_popped unassigned ]) } + scope :ordered_by, ->(order) do + case order + 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 - scope :mentioning, ->(query) do - bubbles = search(query).select(:id).to_sql - comments = Comment.search(query).select(:bubble_id).to_sql + class << self + def default_order_by + "most_active" + end - left_joins(:comments) - .where("bubbles.id in (#{bubbles}) or comments.bubble_id in (#{comments})") - .distinct + def default_status + "not_popped" + end end private diff --git a/app/models/bubble/assignable.rb b/app/models/bubble/assignable.rb index 828034dab..db0b405eb 100644 --- a/app/models/bubble/assignable.rb +++ b/app/models/bubble/assignable.rb @@ -7,8 +7,8 @@ module Bubble::Assignable has_many :assignees, through: :assignments has_many :assigners, through: :assignments - scope :assigned_to, ->(user) { joins(:assignments).where(assignments: { assignee: user }) } - scope :assigned_by, ->(user) { joins(:assignments).where(assignments: { assigner: user }) } + scope :unassigned, -> { where.missing :assignments } + scope :assigned_to, ->(users) { joins(:assignments).where(assignments: { assignee: users }) } end def assign(users, assigner: Current.user) diff --git a/app/models/bubble/boostable.rb b/app/models/bubble/boostable.rb index 98ce28485..0853603bc 100644 --- a/app/models/bubble/boostable.rb +++ b/app/models/bubble/boostable.rb @@ -1,6 +1,10 @@ module Bubble::Boostable extend ActiveSupport::Concern + included do + scope :ordered_by_boosts, -> { order boost_count: :desc } + end + def boost! transaction do increment! :boost_count diff --git a/app/models/bubble/commentable.rb b/app/models/bubble/commentable.rb index 911fd3956..7d4dda8c9 100644 --- a/app/models/bubble/commentable.rb +++ b/app/models/bubble/commentable.rb @@ -3,6 +3,8 @@ module Bubble::Commentable included do has_many :comments, dependent: :destroy + + scope :ordered_by_comments, -> { left_joins(:comments).group(:id).order("COUNT(comments.id) DESC") } end def comment!(body) diff --git a/app/models/bubble/poppable.rb b/app/models/bubble/poppable.rb index 30e6cde65..9ac34ec3d 100644 --- a/app/models/bubble/poppable.rb +++ b/app/models/bubble/poppable.rb @@ -4,8 +4,8 @@ module Bubble::Poppable included do has_one :pop, dependent: :destroy - scope :popped, -> { joins(:pop) } - scope :not_popped, -> { where.missing(:pop) } + scope :popped, -> { joins(:pop) } + scope :not_popped, -> { where.missing(:pop) } end def popped? diff --git a/app/models/bubble/searchable.rb b/app/models/bubble/searchable.rb new file mode 100644 index 000000000..dd951899c --- /dev/null +++ b/app/models/bubble/searchable.rb @@ -0,0 +1,14 @@ +module Bubble::Searchable + extend ActiveSupport::Concern + + included do + scope :mentioning, ->(query) do + bubbles = search(query).select(:id).to_sql + comments = Comment.search(query).select(:bubble_id).to_sql + + left_joins(:comments) + .where("bubbles.id in (#{bubbles}) or comments.bubble_id in (#{comments})") + .distinct + end + end +end diff --git a/app/models/bubble/taggable.rb b/app/models/bubble/taggable.rb index 33c53d160..bad321ad0 100644 --- a/app/models/bubble/taggable.rb +++ b/app/models/bubble/taggable.rb @@ -5,6 +5,6 @@ module Bubble::Taggable has_many :taggings, dependent: :destroy has_many :tags, through: :taggings - scope :tagged_with, ->(tag) { joins(:taggings).where(taggings: { tag: tag }) } + scope :tagged_with, ->(tags) { joins(:taggings).where(taggings: { tag: tags }) } end end diff --git a/app/models/bucket.rb b/app/models/bucket.rb index f2476b761..651b8e600 100644 --- a/app/models/bucket.rb +++ b/app/models/bucket.rb @@ -1,5 +1,5 @@ class Bucket < ApplicationRecord - include Accessible + include Accessible, Views belongs_to :account belongs_to :creator, class_name: "User", default: -> { Current.user } diff --git a/app/models/bucket/view.rb b/app/models/bucket/view.rb new file mode 100644 index 000000000..6eed3e940 --- /dev/null +++ b/app/models/bucket/view.rb @@ -0,0 +1,37 @@ +class Bucket::View < ApplicationRecord + FILTERS = [ :assignee_ids, :order_by, :status, :tag_ids ] + + include Summarized, Assignees, Tags + + belongs_to :creator, class_name: "User", default: -> { Current.user } + belongs_to :bucket + + has_one :account, through: :creator + + store_accessor :filters, *FILTERS + + validate :must_have_filters, :must_not_be_the_default_view + + def to_bucket_params + filters.compact_blank + end + + private + ORDERS = { + "most_active" => "most active", + "most_discussed" => "most discussed", + "most_boosted" => "most boosted", + "newest" => "newest", + "oldest" => "oldest" } + STATUSES = { + "unassigned" => "unassigned", + "popped" => "popped" } + + def must_have_filters + errors.add(:base, "must have filters") if filters.values.all?(&:blank?) + end + + def must_not_be_the_default_view + errors.add(:base, "must be different than the default view") if filters.compact_blank == { "order_by" => "most_active" } + end +end diff --git a/app/models/bucket/view/assignees.rb b/app/models/bucket/view/assignees.rb new file mode 100644 index 000000000..fd0aaa5c1 --- /dev/null +++ b/app/models/bucket/view/assignees.rb @@ -0,0 +1,10 @@ +module Bucket::View::Assignees + private + def assignee_names + assignees.map &:name + end + + def assignees + @assignees ||= account.users.where id: assignee_ids + end +end diff --git a/app/models/bucket/view/summarized.rb b/app/models/bucket/view/summarized.rb new file mode 100644 index 000000000..a9c8cf7ed --- /dev/null +++ b/app/models/bucket/view/summarized.rb @@ -0,0 +1,24 @@ +module Bucket::View::Summarized + def summary + [ order_by_summary, status_summary, assignee_summary, tag_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 #{assignee_names.to_choice_sentence}" if assignee_names.any? + end + + def tag_summary + "tagged #{tag_names.to_choice_sentence}" if tag_names.any? + end +end diff --git a/app/models/bucket/view/tags.rb b/app/models/bucket/view/tags.rb new file mode 100644 index 000000000..f033c2dc7 --- /dev/null +++ b/app/models/bucket/view/tags.rb @@ -0,0 +1,10 @@ +module Bucket::View::Tags + private + def tag_names + tags.map &:hashtag + end + + 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 new file mode 100644 index 000000000..ab9cba2c8 --- /dev/null +++ b/app/models/bucket/views.rb @@ -0,0 +1,7 @@ +module Bucket::Views + extend ActiveSupport::Concern + + included do + has_many :views, dependent: :delete_all + end +end diff --git a/app/models/event.rb b/app/models/event.rb index 8994acc05..76e60ff30 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -6,5 +6,7 @@ class Event < ApplicationRecord belongs_to :creator, class_name: "User" belongs_to :bubble + has_one :account, through: :creator + scope :threadable, -> { where action: THREADABLE_ACTIONS } end diff --git a/app/models/event/assignments.rb b/app/models/event/assignments.rb index ade131d31..1daedbf9f 100644 --- a/app/models/event/assignments.rb +++ b/app/models/event/assignments.rb @@ -11,6 +11,6 @@ module Event::Assignments private def assignees - @assignees ||= creator.account.users.find assignee_ids + @assignees ||= account.users.where id: assignee_ids end end diff --git a/app/models/tag.rb b/app/models/tag.rb index 1e028466f..fb8db226d 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -3,4 +3,8 @@ class Tag < ApplicationRecord has_many :taggings, dependent: :destroy has_many :bubbles, through: :taggings + + def hashtag + "#" + title + end end diff --git a/app/models/user.rb b/app/models/user.rb index 2d1c5ce6f..d214b1120 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,4 +1,6 @@ class User < ApplicationRecord + include Viewer + belongs_to :account has_many :sessions, dependent: :destroy diff --git a/app/models/user/viewer.rb b/app/models/user/viewer.rb new file mode 100644 index 000000000..a3d59585e --- /dev/null +++ b/app/models/user/viewer.rb @@ -0,0 +1,7 @@ +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 a98fe7771..60e4b31e2 100644 --- a/app/views/bubbles/_filters.html.erb +++ b/app/views/bubbles/_filters.html.erb @@ -1,21 +1,18 @@

-
  • <%= link_to "Most active", bucket_bubbles_path(bucket, bubble_filter_params.without(:popped)), class: "filter__button" %>
  • -
  • Most discussed
  • -
  • Most boosted
  • -
  • Newest
  • -
  • Oldest
  • -
  • <%= link_to "Popped", bucket_bubbles_path(bucket, bubble_filter_params.merge(popped: true)), class: "filter__button" %>
  • + <% Bucket::View::ORDERS.each do |key, value| %> +
  • <%= link_to value.upcase_first, bucket_bubbles_path(bucket, bubble_filter_params.merge(order_by: key, status: nil)), class: "filter__button" %>
  • + <% end %> + + <% Bucket::View::STATUSES.each do |key, value| %> +
  • <%= link_to value.upcase_first, bucket_bubbles_path(bucket, bubble_filter_params.merge(order_by: nil, status: key)), class: "filter__button" %>
  • + <% end %>
    @@ -30,7 +27,7 @@ <% Current.user.buckets.order(:name).each do |bucket| %> -
  • <%= link_to bucket.name, bucket_bubbles_path(bucket), class: "filter__button" %>
  • +
  • <%= link_to bucket.name, bucket_bubbles_path(bucket, bubble_filter_params), class: "filter__button" %>
  • <% end %>
    @@ -43,49 +40,61 @@

    - <%= tag ? "Tagged" : "with" %> + <%= @tag_filters ? "Tagged" : "with" %>
    <% bucket.tags.order(:title).each do |tag| %> -
  • <%= link_to tag.title, bucket_bubbles_path(bucket, bubble_filter_params.merge(tag_id: tag.id)) %>
  • +
  • <%= link_to tag.title, bucket_bubbles_path(bucket, bubble_filter_params.merge(tag_ids: [ tag.id ])) %>
  • <% end %>
    - <% if tag %> - <%= link_to bucket_bubbles_path(bucket, bubble_filter_params.without(:tag_id)), class: "btn", style: "font-size: 0.4em;" do %> + <% if @tag_filters %> + <%= link_to bucket_bubbles_path(bucket, bubble_filter_params.without(:tag_ids)), class: "btn", style: "font-size: 0.4em;" do %> <%= image_tag "remove.svg", aria: { hidden: true }, size: 24 %> Clear <% end %> <% end %>
    - and + <% unless status_filter_param.unassigned? %> + and -
    - +
    + - - - <% bucket.users.active.order(:name).each do |user| %> -
  • <%= link_to user.name, bucket_bubbles_path(bucket, bubble_filter_params.merge(assignee_id: user.id)) %>
  • + + + <% bucket.users.active.order(:name).each do |user| %> +
  • <%= link_to user.name, bucket_bubbles_path(bucket, bubble_filter_params.merge(assignee_ids: [ user.id ])) %>
  • + <% end %> +
    +
    + + <% if @assignee_filters %> + <%= link_to bucket_bubbles_path(bucket, bubble_filter_params.without(:assignee_ids)), class: "btn", style: "font-size: 0.4em;" do %> + <%= image_tag "remove.svg", aria: { hidden: true }, size: 24 %> + Clear <% end %> -
    -
    - - <% if assignee %> - <%= link_to bucket_bubbles_path(bucket, bubble_filter_params.without(:assignee_id)), class: "btn", style: "font-size: 0.4em;" do %> - <%= image_tag "remove.svg", aria: { hidden: true }, size: 24 %> - Clear <% end %> - <% end %> -
    +
    + <% end %> + + <%= button_to bucket_views_path(bucket), class: "btn", style: "font-size: 0.4em;" do %> + <%= hidden_field_tag :order_by, params[:order_by], name: "view[order_by]" %> + <%= hidden_field_tag :status, params[:status], name: "view[status]" %> + <%= hidden_field_tag :assignee_ids, Array(params[:assignee_ids]).join(","), name: "view[assignee_ids]" %> + <%= hidden_field_tag :tag_ids, Array(params[:tag_ids]).join(","), name: "view[tag_ids]" %> + + <%= image_tag "add.svg", aria: { hidden: true }, size: 24 %> + Save filters + <% end %>

    diff --git a/app/views/bubbles/index.html.erb b/app/views/bubbles/index.html.erb index 5b184c2fc..16ec3d15b 100644 --- a/app/views/bubbles/index.html.erb +++ b/app/views/bubbles/index.html.erb @@ -19,8 +19,8 @@ <% end %>
    - <% if @bubbles.ordered_by_activity.any? %> - <%= render partial: "bubbles/bubble", collection: @bubbles.ordered_by_activity.limit(10) %> + <% if @bubbles.any? %> + <%= render partial: "bubbles/bubble", collection: @bubbles.limit(10) %> <% else %>

    Nothing here.

    <% end %> @@ -37,6 +37,6 @@
    diff --git a/app/views/bubbles/list/_bubble.html.erb b/app/views/bubbles/list/_bubble.html.erb index 404dddd43..eb0f6e8fc 100644 --- a/app/views/bubbles/list/_bubble.html.erb +++ b/app/views/bubbles/list/_bubble.html.erb @@ -24,7 +24,7 @@ <% bubble.tags.each do |tag| %> - <%= link_to "##{tag.title}", bucket_bubbles_path(bubble.bucket, tag_id: tag.id), style: "color: #{bubble.color}" %> + <%= link_to "##{tag.title}", bucket_bubbles_path(bubble.bucket, tag_ids: [ tag.id ]), style: "color: #{bubble.color}" %> <% end %> <%= tag.time bubble.created_at, class: "txt-nowrap flex-item-justify-end" do %> diff --git a/app/views/buckets/_bucket.html.erb b/app/views/buckets/_bucket.html.erb new file mode 100644 index 000000000..5d0b444e4 --- /dev/null +++ b/app/views/buckets/_bucket.html.erb @@ -0,0 +1,18 @@ +
  • + <%= link_to bucket_bubbles_path(bucket), class: "windshield__container flex justify-center align-center position-relative" do %> +
    + <% bucket.bubbles.not_popped.ordered_by_activity.limit(10).each do |bubble| %> +
    + + + + +
    + <% end %> +
    + <% end %> + +
    + <%= link_to bucket.name, bucket_bubbles_path(bucket), class: "txt-x-large txt-ink" %> +
    +
  • diff --git a/app/views/buckets/_view.html.erb b/app/views/buckets/_view.html.erb new file mode 100644 index 000000000..51a38cc3c --- /dev/null +++ b/app/views/buckets/_view.html.erb @@ -0,0 +1,21 @@ +
  • + <%= link_to bucket_bubbles_path(view.bucket, **view.to_bucket_params), class: "windshield__container flex justify-center align-center position-relative" do %> +
    + <% view.bucket.bubbles.not_popped.ordered_by_activity.limit(10).each do |bubble| %> +
    + + + + +
    + <% end %> +
    + <% end %> + +
    + <%= link_to bucket_bubbles_path(view.bucket, **view.to_bucket_params), class: "txt-ink" do %> + <%= view.bucket.name %> +

    <%= view.summary %>

    + <% end %> +
    +
  • diff --git a/app/views/buckets/index.html.erb b/app/views/buckets/index.html.erb index 8436c5658..a20a18a2c 100644 --- a/app/views/buckets/index.html.erb +++ b/app/views/buckets/index.html.erb @@ -16,28 +16,10 @@ <% @buckets.each do |bucket| %> -
  • - <%= link_to bucket_bubbles_path(bucket), class: "windshield__container flex justify-center align-center position-relative" do %> -
    - - <% bucket.bubbles.not_popped.ordered_by_activity.limit(10).each do |bubble| %> -
    - - - - -
    - <% end %> -
    - <% end %> -
    - <%= link_to bucket.name, bucket_bubbles_path(bucket), class: "txt-x-large txt-ink" %> - - <%= link_to edit_bucket_path(bucket), class: "btn txt-small", hidden: true do %> - <%= image_tag "pencil.svg", aria: { hidden: true }, size: 24 %> - Edit bucket - <% end %> -
    -
  • + <% if bucket.is_a? Bucket %> + <%= render partial: "buckets/bucket", locals: { bucket: bucket } %> + <% elsif bucket.is_a? Bucket::View %> + <%= render partial: "buckets/view", locals: { view: bucket } %> + <% end %> <% end %>
    diff --git a/app/views/comments/_new.html.erb b/app/views/comments/_new.html.erb index 8854762a8..a1055f3d5 100644 --- a/app/views/comments/_new.html.erb +++ b/app/views/comments/_new.html.erb @@ -6,7 +6,7 @@ <%= Current.user.name %>
    - <%= form_with model: Comment.new, url: bucket_bubble_comments_path(bubble.bucket, bubble), class: "flex flex-column gap full-width" do |form| %> + <%= form_with model: Comment.new, url: bucket_bubble_comments_path(bubble.bucket, bubble), class: "flex flex-column gap full-width", data: { controller: "form", action: "keydown.meta+enter->form#submit" } do |form| %> <%= form.text_area :body, class: "input", required: true, rows: 4, placeholder: (bubble.comments.empty? && bubble.creator == Current.user) ? "Add some notes…" : "Type your comment…" %> diff --git a/config/initializers/extensions.rb b/config/initializers/extensions.rb new file mode 100644 index 000000000..6fa93d530 --- /dev/null +++ b/config/initializers/extensions.rb @@ -0,0 +1,3 @@ +%w[ rails_ext ].each do |extensions_dir| + Dir["#{Rails.root}/lib/#{extensions_dir}/*"].each { |path| require "#{extensions_dir}/#{File.basename(path)}" } +end diff --git a/config/routes.rb b/config/routes.rb index 391c1c3e0..7465bc6db 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,31 +2,30 @@ Rails.application.routes.draw do root "buckets#index" resource :first_run - resource :session resource :account do - scope module: "accounts" do + scope module: :accounts do resource :join_code - resources :users end end - get "join/:join_code", to: "users#new", as: :join - post "join/:join_code", to: "users#create" - resources :users do - scope module: "users" do + scope module: :users do resource :avatar end end resources :buckets do - resource :access, controller: "buckets/accesses" + resources :tags, only: :index + + scope module: :buckets do + resources :views + end resources :bubbles do - scope module: "bubbles" do + scope module: :bubbles do resource :image resource :pop end @@ -36,9 +35,9 @@ Rails.application.routes.draw do resources :comments resources :tags, shallow: true end - - resources :tags, only: :index end + get "join/:join_code", to: "users#new", as: :join + post "join/:join_code", to: "users#create" get "up", to: "rails/health#show", as: :rails_health_check end diff --git a/db/migrate/20241015224007_create_views.rb b/db/migrate/20241015224007_create_views.rb new file mode 100644 index 000000000..2acea70cd --- /dev/null +++ b/db/migrate/20241015224007_create_views.rb @@ -0,0 +1,11 @@ +class CreateViews < ActiveRecord::Migration[8.0] + def change + create_table :bucket_views do |t| + t.references :creator, null: false + t.references :bucket, null: false + t.json :filters, default: {}, null: false + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 0218e49b4..8ff1c6132 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2024_10_09_211414) do +ActiveRecord::Schema[8.0].define(version: 2024_10_15_224007) do create_table "accesses", force: :cascade do |t| t.integer "bucket_id", null: false t.integer "user_id", null: false @@ -80,6 +80,16 @@ ActiveRecord::Schema[8.0].define(version: 2024_10_09_211414) do t.index ["bucket_id"], name: "index_bubbles_on_bucket_id" end + create_table "bucket_views", force: :cascade do |t| + t.integer "creator_id", null: false + t.integer "bucket_id", null: false + t.json "filters", default: {}, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["bucket_id"], name: "index_bucket_views_on_bucket_id" + t.index ["creator_id"], name: "index_bucket_views_on_creator_id" + end + create_table "buckets", force: :cascade do |t| t.integer "account_id", null: false t.integer "creator_id", null: false diff --git a/lib/rails_ext/active_support_array_conversions.rb b/lib/rails_ext/active_support_array_conversions.rb new file mode 100644 index 000000000..1c41bee60 --- /dev/null +++ b/lib/rails_ext/active_support_array_conversions.rb @@ -0,0 +1,5 @@ +class Array + def to_choice_sentence + to_sentence two_words_connector: " or ", last_word_connector: ", or " + end +end