diff --git a/Gemfile b/Gemfile index 533a87e9b..30f8a6483 100644 --- a/Gemfile +++ b/Gemfile @@ -55,9 +55,6 @@ gem "prometheus-client-mmap", "~> 1.1" gem "autotuner" gem "benchmark" # indirect dependency, being removed from Ruby 3.5 stdlib so here to quash warnings -# AI -gem "sqlite-vec", "0.1.7.alpha.2" - group :development, :test do gem "debug" gem "bundler-audit", require: false diff --git a/Gemfile.lock b/Gemfile.lock index 3c76704a6..59c1891fd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -36,7 +36,7 @@ GIT GIT remote: https://github.com/rails/rails.git - revision: def13087d55a6dbbfa80d114c847555927ce2ed7 + revision: d3d17c297a70d3e255a34693a1fb0074712e9930 branch: main specs: actioncable (8.2.0.alpha) @@ -267,7 +267,7 @@ GEM activesupport (>= 6.0.0) railties (>= 6.0.0) io-console (0.8.1) - irb (1.15.2) + irb (1.15.3) pp (>= 0.6.0) rdoc (>= 4.0.0) reline (>= 0.4.2) @@ -345,7 +345,7 @@ GEM net-smtp (0.5.1) net-protocol net-ssh (7.3.0) - nio4r (2.7.4) + nio4r (2.7.5) nokogiri (1.18.10) mini_portile2 (~> 2.8.2) racc (~> 1.4) @@ -407,7 +407,7 @@ GEM nio4r (~> 2.0) raabro (1.4.0) racc (1.8.1) - rack (3.2.3) + rack (3.2.4) rack-session (2.1.1) base64 (>= 0.1.0) rack (>= 3.0.0) @@ -427,7 +427,7 @@ GEM rake-compiler-dock (1.9.1) rb_sys (0.9.117) rake-compiler-dock (= 1.9.1) - rdoc (6.15.0) + rdoc (6.15.1) erb psych (>= 4.0.0) tsort @@ -508,10 +508,6 @@ GEM fugit (~> 1.11) railties (>= 7.1) thor (>= 1.3.1) - sqlite-vec (0.1.7.alpha.2-arm64-darwin) - sqlite-vec (0.1.7.alpha.2-arm64-linux) - sqlite-vec (0.1.7.alpha.2-x86_64-darwin) - sqlite-vec (0.1.7.alpha.2-x86_64-linux) sqlite3 (2.7.4) mini_portile2 (~> 2.8.0) sqlite3 (2.7.4-arm64-darwin) @@ -543,7 +539,7 @@ GEM unicode-display_width (3.2.0) unicode-emoji (~> 4.1) unicode-emoji (4.1.0) - uri (1.0.4) + uri (1.1.0) useragent (0.16.11) vcr (6.3.1) base64 @@ -646,7 +642,6 @@ DEPENDENCIES solid_cable (>= 3.0) solid_cache (~> 1.0) solid_queue (~> 1.2) - sqlite-vec (= 0.1.7.alpha.2) sqlite3 (>= 2.0) stimulus-rails thruster diff --git a/app/assets/stylesheets/events.css b/app/assets/stylesheets/events.css index 42d129125..2ff88e5eb 100644 --- a/app/assets/stylesheets/events.css +++ b/app/assets/stylesheets/events.css @@ -198,6 +198,12 @@ z-index: var(--z-events-column-header); } + .events__column-footer { + grid-row-start: 26; + margin: var(--events-gap); + padding: var(--block-space-half) var(--inline-space); + } + .events__time-block { align-content: end; display: grid; diff --git a/app/controllers/account/entropies_controller.rb b/app/controllers/account/entropies_controller.rb new file mode 100644 index 000000000..f45f3ef5f --- /dev/null +++ b/app/controllers/account/entropies_controller.rb @@ -0,0 +1,11 @@ +class Account::EntropiesController < ApplicationController + def update + Account.sole.entropy.update!(entropy_params) + redirect_to account_settings_path, notice: "Account updated" + end + + private + def entropy_params + params.expect(entropy: [ :auto_postpone_period ]) + end +end diff --git a/app/controllers/account/entropy_configurations_controller.rb b/app/controllers/account/entropy_configurations_controller.rb deleted file mode 100644 index 53b1f54e8..000000000 --- a/app/controllers/account/entropy_configurations_controller.rb +++ /dev/null @@ -1,12 +0,0 @@ -class Account::EntropyConfigurationsController < ApplicationController - def update - Entropy::Configuration.default.update!(entropy_configuration_params) - - redirect_to account_settings_path, notice: "Account updated" - end - - private - def entropy_configuration_params - params.expect(entropy_configuration: [ :auto_postpone_period ]) - end -end diff --git a/app/controllers/account/join_codes_controller.rb b/app/controllers/account/join_codes_controller.rb index b67246b80..026c692f9 100644 --- a/app/controllers/account/join_codes_controller.rb +++ b/app/controllers/account/join_codes_controller.rb @@ -8,11 +8,8 @@ class Account::JoinCodesController < ApplicationController end def update - if @join_code.update(join_code_params) - redirect_to account_join_code_path - else - render :edit, status: :unprocessable_entity - end + @join_code.update!(join_code_params) + redirect_to account_join_code_path end def destroy diff --git a/app/controllers/account/settings_controller.rb b/app/controllers/account/settings_controller.rb index 9acebbae4..42a6a60f7 100644 --- a/app/controllers/account/settings_controller.rb +++ b/app/controllers/account/settings_controller.rb @@ -1,19 +1,21 @@ class Account::SettingsController < ApplicationController + before_action :ensure_admin, only: :update + before_action :set_account + def show - @account = Account.sole @users = User.active.alphabetically end def update - if Current.user.can_administer? - Account.sole.update!(account_params) - redirect_to account_settings_path - else - head :forbidden - end + @account.update!(account_params) + redirect_to account_settings_path end private + def set_account + @account = Account.sole + end + def account_params params.expect account: %i[ name ] end diff --git a/app/controllers/cards/assignments_controller.rb b/app/controllers/cards/assignments_controller.rb index 7b9464a61..d32696f11 100644 --- a/app/controllers/cards/assignments_controller.rb +++ b/app/controllers/cards/assignments_controller.rb @@ -2,11 +2,11 @@ class Cards::AssignmentsController < ApplicationController include CardScoped def new + @users = @collection.users.active.alphabetically + fresh_when @users end def create @card.toggle_assignment @collection.users.active.find(params[:assignee_id]) - - render turbo_stream: turbo_stream.replace([ @card, :assignees ], partial: "cards/display/perma/assignees", locals: { card: @card.reload }) end end diff --git a/app/controllers/cards/collections_controller.rb b/app/controllers/cards/collections_controller.rb index 7d3ff8b9b..791fd945e 100644 --- a/app/controllers/cards/collections_controller.rb +++ b/app/controllers/cards/collections_controller.rb @@ -2,15 +2,20 @@ class Cards::CollectionsController < ApplicationController include CollectionScoped skip_before_action :set_collection, only: %i[ edit ] + before_action :set_card def edit - @card = Current.user.accessible_cards.find(params[:card_id]) @collections = Current.user.collections.ordered_by_recently_accessed + fresh_when @collections end def update - @card = Current.user.accessible_cards.find(params[:card_id]) @card.move_to(@collection) redirect_to @card end + + private + def set_card + @card = Current.user.accessible_cards.find(params[:card_id]) + end end diff --git a/app/controllers/cards/pins_controller.rb b/app/controllers/cards/pins_controller.rb index 6ac118543..f8da07da3 100644 --- a/app/controllers/cards/pins_controller.rb +++ b/app/controllers/cards/pins_controller.rb @@ -2,6 +2,7 @@ class Cards::PinsController < ApplicationController include CardScoped def show + fresh_when etag: @card.pin_for(Current.user) || "none" end def create diff --git a/app/controllers/cards/recovers_controller.rb b/app/controllers/cards/recovers_controller.rb deleted file mode 100644 index b4614bc2a..000000000 --- a/app/controllers/cards/recovers_controller.rb +++ /dev/null @@ -1,7 +0,0 @@ -class Cards::RecoversController < ApplicationController - include CardScoped - - def create - redirect_to @card.recover_abandoned_creation - end -end diff --git a/app/controllers/cards/steps_controller.rb b/app/controllers/cards/steps_controller.rb index 4f3434632..305890508 100644 --- a/app/controllers/cards/steps_controller.rb +++ b/app/controllers/cards/steps_controller.rb @@ -5,7 +5,6 @@ class Cards::StepsController < ApplicationController def create @step = @card.steps.create!(step_params) - render_card_replacement end def show @@ -16,12 +15,10 @@ class Cards::StepsController < ApplicationController def update @step.update!(step_params) - render_card_replacement end def destroy @step.destroy! - render_card_replacement end private diff --git a/app/controllers/cards/taggings_controller.rb b/app/controllers/cards/taggings_controller.rb index a8fe7ecf9..50f58e34b 100644 --- a/app/controllers/cards/taggings_controller.rb +++ b/app/controllers/cards/taggings_controller.rb @@ -3,6 +3,7 @@ class Cards::TaggingsController < ApplicationController def new @tags = Tag.all.alphabetically + fresh_when @tags end def create diff --git a/app/controllers/cards/triages_controller.rb b/app/controllers/cards/triages_controller.rb index 12fe1a5fb..10fb3df34 100644 --- a/app/controllers/cards/triages_controller.rb +++ b/app/controllers/cards/triages_controller.rb @@ -5,11 +5,11 @@ class Cards::TriagesController < ApplicationController column = @card.collection.columns.find(params[:column_id]) @card.triage_into(column) - render_card_replacement + redirect_to @card end def destroy @card.send_back_to_triage - render_card_replacement + redirect_to @card end end diff --git a/app/controllers/cards/watches_controller.rb b/app/controllers/cards/watches_controller.rb index 7308c2279..4d83567d0 100644 --- a/app/controllers/cards/watches_controller.rb +++ b/app/controllers/cards/watches_controller.rb @@ -1,6 +1,10 @@ class Cards::WatchesController < ApplicationController include CardScoped + def show + fresh_when etag: @card.watch_for(Current.user) || "none" + end + def create @card.watch_by Current.user end diff --git a/app/controllers/cards_controller.rb b/app/controllers/cards_controller.rb index db1a0d7e0..7fcd7123e 100644 --- a/app/controllers/cards_controller.rb +++ b/app/controllers/cards_controller.rb @@ -9,7 +9,7 @@ class CardsController < ApplicationController end def create - card = @collection.cards.create! + card = @collection.cards.find_or_create_by!(creator: Current.user, status: "drafted") redirect_to card end @@ -30,7 +30,7 @@ class CardsController < ApplicationController def destroy @card.destroy! - redirect_to @card.collection, notice: ("Card deleted" unless @card.creating?) + redirect_to @card.collection, notice: "Card deleted" end private diff --git a/app/controllers/collections/columns_controller.rb b/app/controllers/collections/columns_controller.rb index b8f4d4de1..a6c9103c5 100644 --- a/app/controllers/collections/columns_controller.rb +++ b/app/controllers/collections/columns_controller.rb @@ -1,7 +1,7 @@ class Collections::ColumnsController < ApplicationController - include ActionView::RecordIdentifier, CollectionScoped + include CollectionScoped - before_action :set_column, only: [ :show, :update, :destroy ] + before_action :set_column, only: %i[ show update destroy ] def show set_page_and_extract_portion_from @column.cards.active.latest.with_golden_first @@ -26,6 +26,6 @@ class Collections::ColumnsController < ApplicationController end def column_params - params.require(:column).permit(:name, :color) + params.expect(column: [ :name, :color ]) end end diff --git a/app/controllers/collections/entropies_controller.rb b/app/controllers/collections/entropies_controller.rb new file mode 100644 index 000000000..4affaf5af --- /dev/null +++ b/app/controllers/collections/entropies_controller.rb @@ -0,0 +1,12 @@ +class Collections::EntropiesController < ApplicationController + include CollectionScoped + + def update + @collection.entropy.update!(entropy_params) + end + + private + def entropy_params + params.expect(collection: [ :auto_postpone_period ]) + end +end diff --git a/app/controllers/collections/entropy_configurations_controller.rb b/app/controllers/collections/entropy_configurations_controller.rb deleted file mode 100644 index 76957627d..000000000 --- a/app/controllers/collections/entropy_configurations_controller.rb +++ /dev/null @@ -1,17 +0,0 @@ -class Collections::EntropyConfigurationsController < ApplicationController - include CollectionScoped - - def update - @collection.entropy_configuration.update!(entropy_configuration_params) - - render turbo_stream: [ - turbo_stream.replace([ @collection, :entropy_configuration ], partial: "collections/edit/auto_close", locals: { collection: @collection }), - turbo_stream_flash(notice: "Saved") - ] - end - - private - def entropy_configuration_params - params.expect(collection: [ :auto_postpone_period ]) - end -end diff --git a/app/controllers/collections/publications_controller.rb b/app/controllers/collections/publications_controller.rb index 2043cf60a..78400dd3c 100644 --- a/app/controllers/collections/publications_controller.rb +++ b/app/controllers/collections/publications_controller.rb @@ -3,18 +3,10 @@ class Collections::PublicationsController < ApplicationController def create @collection.publish - render turbo_stream: [ - turbo_stream.replace([ @collection, :publication ], partial: "collections/edit/publication", locals: { collection: @collection }), - turbo_stream_flash(notice: "Saved") - ] end def destroy @collection.unpublish @collection.reload - render turbo_stream: [ - turbo_stream.replace([ @collection, :publication ], partial: "collections/edit/publication", locals: { collection: @collection }), - turbo_stream_flash(notice: "Saved") - ] end end diff --git a/app/controllers/collections_controller.rb b/app/controllers/collections_controller.rb index 1fd336ce9..b7c722eca 100644 --- a/app/controllers/collections_controller.rb +++ b/app/controllers/collections_controller.rb @@ -1,11 +1,7 @@ class CollectionsController < ApplicationController - before_action :set_collection, except: %i[ new create ] - include FilterScoped - def new - @collection = Collection.new - end + before_action :set_collection, except: %i[ new create ] def show if @filter.used?(ignore_collections: true) @@ -15,6 +11,10 @@ class CollectionsController < ApplicationController end end + def new + @collection = Collection.new + end + def create @collection = Collection.create! collection_params.with_defaults(all_access: true) redirect_to collection_path(@collection) @@ -22,12 +22,14 @@ class CollectionsController < ApplicationController def edit selected_user_ids = @collection.users.pluck :id - @selected_users, @unselected_users = User.active.alphabetically.partition { |user| selected_user_ids.include? user.id } + @selected_users, @unselected_users = \ + User.active.alphabetically.partition { |user| selected_user_ids.include? user.id } end def update @collection.update! collection_params @collection.accesses.revise granted: grantees, revoked: revokees if grantees_changed? + if @collection.accessible_to?(Current.user) redirect_to edit_collection_path(@collection), notice: "Saved" else diff --git a/app/controllers/columns/cards/drops/closures_controller.rb b/app/controllers/columns/cards/drops/closures_controller.rb index ba0dc67df..a58e86775 100644 --- a/app/controllers/columns/cards/drops/closures_controller.rb +++ b/app/controllers/columns/cards/drops/closures_controller.rb @@ -3,7 +3,5 @@ class Columns::Cards::Drops::ClosuresController < ApplicationController def create @card.close - - render turbo_stream: turbo_stream.replace("closed-cards", partial: "collections/show/closed", method: :morph, locals: { collection: @card.collection }) end end diff --git a/app/controllers/columns/cards/drops/columns_controller.rb b/app/controllers/columns/cards/drops/columns_controller.rb index cb2c3b4a8..3de47992e 100644 --- a/app/controllers/columns/cards/drops/columns_controller.rb +++ b/app/controllers/columns/cards/drops/columns_controller.rb @@ -1,10 +1,8 @@ class Columns::Cards::Drops::ColumnsController < ApplicationController - include ActionView::RecordIdentifier, CardScoped + include CardScoped def create - column = @card.collection.columns.find(params[:column_id]) - @card.triage_into(column) - - render turbo_stream: turbo_stream.replace(dom_id(column), partial: "collections/show/column", method: :morph, locals: { column: column }) + @column = @card.collection.columns.find(params[:column_id]) + @card.triage_into(@column) end end diff --git a/app/controllers/columns/cards/drops/not_nows_controller.rb b/app/controllers/columns/cards/drops/not_nows_controller.rb index a8fe74298..c1b6a5fac 100644 --- a/app/controllers/columns/cards/drops/not_nows_controller.rb +++ b/app/controllers/columns/cards/drops/not_nows_controller.rb @@ -3,7 +3,5 @@ class Columns::Cards::Drops::NotNowsController < ApplicationController def create @card.postpone - - render turbo_stream: turbo_stream.replace("not-now", partial: "collections/show/not_now", method: :morph, locals: { collection: @card.collection }) end end diff --git a/app/controllers/columns/cards/drops/streams_controller.rb b/app/controllers/columns/cards/drops/streams_controller.rb index 09418133e..d3ce8d1fa 100644 --- a/app/controllers/columns/cards/drops/streams_controller.rb +++ b/app/controllers/columns/cards/drops/streams_controller.rb @@ -4,7 +4,5 @@ class Columns::Cards::Drops::StreamsController < ApplicationController def create @card.send_back_to_triage set_page_and_extract_portion_from @collection.cards.awaiting_triage.latest.with_golden_first - - render turbo_stream: turbo_stream.replace("the-stream", partial: "collections/show/stream", method: :morph, locals: { collection: @card.collection, page: @page }) end end diff --git a/app/controllers/concerns/authentication.rb b/app/controllers/concerns/authentication.rb index 485904051..6acbd92b6 100644 --- a/app/controllers/concerns/authentication.rb +++ b/app/controllers/concerns/authentication.rb @@ -37,7 +37,7 @@ module Authentication end def require_tenant - unless ApplicationRecord.current_tenant.present? + if ApplicationRecord.current_tenant.blank? redirect_to session_menu_url(script_name: nil) end end @@ -52,6 +52,7 @@ module Authentication end end + # FIXME: Remove before launch def clear_old_scoped_session_cookies if request.script_name.present? && cookies.signed[:session_token].present? && !find_session_by_cookie cookies.signed[:session_token] = { value: "invalid-session-token", path: request.script_name, expires: 1.hour.ago } diff --git a/app/controllers/concerns/authorization.rb b/app/controllers/concerns/authorization.rb index df48f62f5..0e5e9240d 100644 --- a/app/controllers/concerns/authorization.rb +++ b/app/controllers/concerns/authorization.rb @@ -29,7 +29,7 @@ module Authorization if Current.membership.blank? redirect_to session_menu_url(script_name: nil) elsif Current.user.nil? && Current.membership.join_code.present? - redirect_to new_user_path + redirect_to new_users_join_path elsif !Current.user&.active? redirect_to unlink_membership_url(script_name: nil, membership_id: Current.membership.signed_id(purpose: :unlinking)) end diff --git a/app/controllers/concerns/cached_publicly.rb b/app/controllers/concerns/cached_publicly.rb deleted file mode 100644 index 62ab23d05..000000000 --- a/app/controllers/concerns/cached_publicly.rb +++ /dev/null @@ -1,12 +0,0 @@ -module CachedPublicly - extend ActiveSupport::Concern - - included do - before_action :set_cache_headers - end - - private - def set_cache_headers - expires_in 30.seconds, public: true - end -end diff --git a/app/controllers/concerns/public_card_scoped.rb b/app/controllers/concerns/public_card_scoped.rb deleted file mode 100644 index 7416609ef..000000000 --- a/app/controllers/concerns/public_card_scoped.rb +++ /dev/null @@ -1,13 +0,0 @@ -module PublicCardScoped - extend ActiveSupport::Concern - - included do - before_action :set_collection_and_card - end - - private - def set_collection_and_card - @collection = Collection.find_by_published_key(params[:collection_id]) - @card = @collection.cards.find(params[:id]) - end -end diff --git a/app/controllers/concerns/public_collection_scoped.rb b/app/controllers/concerns/public_collection_scoped.rb deleted file mode 100644 index 11ecacfca..000000000 --- a/app/controllers/concerns/public_collection_scoped.rb +++ /dev/null @@ -1,12 +0,0 @@ -module PublicCollectionScoped - extend ActiveSupport::Concern - - included do - before_action :set_collection - end - - private - def set_collection - @collection = Collection.find_by_published_key(params[:collection_id] || params[:id]) - end -end diff --git a/app/controllers/concerns/search/query_terms_scoped.rb b/app/controllers/concerns/search/query_terms_scoped.rb deleted file mode 100644 index 11b88cdf2..000000000 --- a/app/controllers/concerns/search/query_terms_scoped.rb +++ /dev/null @@ -1,12 +0,0 @@ -module Search::QueryTermsScoped - extend ActiveSupport::Concern - - included do - before_action :set_query_terms - end - - private - def set_query_terms - @query_terms = params[:q] - end -end diff --git a/app/controllers/concerns/turbo_flash.rb b/app/controllers/concerns/turbo_flash.rb index 7e313a5b9..93dad24ed 100644 --- a/app/controllers/concerns/turbo_flash.rb +++ b/app/controllers/concerns/turbo_flash.rb @@ -1,4 +1,10 @@ module TurboFlash + extend ActiveSupport::Concern + + included do + helper_method :turbo_stream_flash + end + private def turbo_stream_flash(**flash_options) turbo_stream.replace(:flash, partial: "layouts/shared/flash", locals: { flash: flash_options }) diff --git a/app/controllers/my/pins_controller.rb b/app/controllers/my/pins_controller.rb index f361b6af7..27e14535b 100644 --- a/app/controllers/my/pins_controller.rb +++ b/app/controllers/my/pins_controller.rb @@ -1,5 +1,6 @@ class My::PinsController < ApplicationController def index @pins = Current.user.pins.includes(:card).ordered.limit(20) + fresh_when @pins end end diff --git a/app/controllers/public/base_controller.rb b/app/controllers/public/base_controller.rb new file mode 100644 index 000000000..71209224f --- /dev/null +++ b/app/controllers/public/base_controller.rb @@ -0,0 +1,21 @@ +class Public::BaseController < ApplicationController + allow_unauthenticated_access + allow_unauthorized_access + + before_action :set_collection, :set_card, :set_public_cache_expiration + + layout "public" + + private + def set_collection + @collection = Collection.find_by_published_key(params[:collection_id] || params[:id]) + end + + def set_card + @card = @collection.cards.find(params[:id]) if params[:collection_id] && params[:id] + end + + def set_public_cache_expiration + expires_in 30.seconds, public: true + end +end diff --git a/app/controllers/public/cards_controller.rb b/app/controllers/public/cards_controller.rb index 50d9b1ed0..353bbf4cf 100644 --- a/app/controllers/public/cards_controller.rb +++ b/app/controllers/public/cards_controller.rb @@ -1,11 +1,4 @@ -class Public::CardsController < ApplicationController - include CachedPublicly, PublicCardScoped - - allow_unauthenticated_access only: :show - allow_unauthorized_access only: :show - - layout "public" - +class Public::CardsController < Public::BaseController def show end end diff --git a/app/controllers/public/collections/columns/closeds_controller.rb b/app/controllers/public/collections/columns/closeds_controller.rb index c049785cb..f782fbc8f 100644 --- a/app/controllers/public/collections/columns/closeds_controller.rb +++ b/app/controllers/public/collections/columns/closeds_controller.rb @@ -1,11 +1,4 @@ -class Public::Collections::Columns::ClosedsController < ApplicationController - include CachedPublicly, PublicCollectionScoped - - allow_unauthenticated_access only: :show - allow_unauthorized_access only: :show - - layout "public" - +class Public::Collections::Columns::ClosedsController < Public::BaseController def show set_page_and_extract_portion_from @collection.cards.closed.recently_closed_first end diff --git a/app/controllers/public/collections/columns/not_nows_controller.rb b/app/controllers/public/collections/columns/not_nows_controller.rb index d3cd2ed02..6ee68e3e7 100644 --- a/app/controllers/public/collections/columns/not_nows_controller.rb +++ b/app/controllers/public/collections/columns/not_nows_controller.rb @@ -1,11 +1,4 @@ -class Public::Collections::Columns::NotNowsController < ApplicationController - include CachedPublicly, PublicCollectionScoped - - allow_unauthenticated_access only: :show - allow_unauthorized_access only: :show - - layout "public" - +class Public::Collections::Columns::NotNowsController < Public::BaseController def show set_page_and_extract_portion_from @collection.cards.postponed.reverse_chronologically.with_golden_first end diff --git a/app/controllers/public/collections/columns/streams_controller.rb b/app/controllers/public/collections/columns/streams_controller.rb index a8a2165e5..602b3d91d 100644 --- a/app/controllers/public/collections/columns/streams_controller.rb +++ b/app/controllers/public/collections/columns/streams_controller.rb @@ -1,11 +1,4 @@ -class Public::Collections::Columns::StreamsController < ApplicationController - include CachedPublicly, PublicCollectionScoped - - allow_unauthenticated_access only: :show - allow_unauthorized_access only: :show - - layout "public" - +class Public::Collections::Columns::StreamsController < Public::BaseController def show set_page_and_extract_portion_from @collection.cards.awaiting_triage.latest.with_golden_first end diff --git a/app/controllers/public/collections/columns_controller.rb b/app/controllers/public/collections/columns_controller.rb index dcc2b46a3..44262c545 100644 --- a/app/controllers/public/collections/columns_controller.rb +++ b/app/controllers/public/collections/columns_controller.rb @@ -1,18 +1,15 @@ -class Public::Collections::ColumnsController < ApplicationController - include ActionView::RecordIdentifier, CachedPublicly, PublicCollectionScoped - - allow_unauthenticated_access only: :show - allow_unauthorized_access only: :show - - layout "public" - - before_action :set_column, only: :show +class Public::Collections::ColumnsController < Public::BaseController + before_action :set_column def show set_page_and_extract_portion_from @column.cards.active.latest.with_golden_first end private + # Unlike the other public controllers, this is using params[:id] to fetch the column instead of the card + def set_card + end + def set_column @column = @collection.columns.find(params[:id]) end diff --git a/app/controllers/public/collections_controller.rb b/app/controllers/public/collections_controller.rb index 18ae24022..d7e4977af 100644 --- a/app/controllers/public/collections_controller.rb +++ b/app/controllers/public/collections_controller.rb @@ -1,11 +1,4 @@ -class Public::CollectionsController < ApplicationController - include CachedPublicly, PublicCollectionScoped - - allow_unauthenticated_access only: :show - allow_unauthorized_access only: :show - - layout "public" - +class Public::CollectionsController < Public::BaseController def show set_page_and_extract_portion_from @collection.cards.awaiting_triage.latest.with_golden_first end diff --git a/app/controllers/searches/queries_controller.rb b/app/controllers/searches/queries_controller.rb index f6b474baa..5dae4b1c6 100644 --- a/app/controllers/searches/queries_controller.rb +++ b/app/controllers/searches/queries_controller.rb @@ -1,8 +1,6 @@ class Searches::QueriesController < ApplicationController - include Search::QueryTermsScoped - def create - Current.user.remember_search(@query_terms) + Current.user.remember_search(params[:q]) head :ok end end diff --git a/app/controllers/searches_controller.rb b/app/controllers/searches_controller.rb index ad9f7a690..b36d0b0d7 100644 --- a/app/controllers/searches_controller.rb +++ b/app/controllers/searches_controller.rb @@ -1,8 +1,8 @@ class SearchesController < ApplicationController - include Search::QueryTermsScoped, Turbo::DriveHelper + include Turbo::DriveHelper def show - if card = Current.user.accessible_cards.find_by_id(@query_terms) + if card = Current.user.accessible_cards.find_by_id(params[:q]) @card = card else perform_search @@ -11,7 +11,7 @@ class SearchesController < ApplicationController private def perform_search - set_page_and_extract_portion_from Current.user.search(@query_terms) + set_page_and_extract_portion_from Current.user.search(params[:q]) @recent_search_queries = Current.user.search_queries.order(updated_at: :desc).limit(10) end end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 25cb98408..10a3c9773 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -10,7 +10,6 @@ class SessionsController < ApplicationController def create Identity.find_by_email_address(email_address)&.send_magic_link - redirect_to session_magic_link_path end diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb deleted file mode 100644 index 3a240af09..000000000 --- a/app/controllers/uploads_controller.rb +++ /dev/null @@ -1,29 +0,0 @@ -class UploadsController < ApplicationController - include ActiveStorage::SetCurrent - - before_action :set_file, only: :create - before_action :set_attachment, only: :show - - def create - # FIXME: Try to get upload attachments on root - @upload = Account.sole.uploads_attachments.create! blob: create_blob! - end - - def show - expires_in 5.minutes, public: true - redirect_to @attachment.url, allow_other_host: true - end - - private - def set_file - @file = params[:file] - end - - def set_attachment - @attachment = ActiveStorage::Attachment.find_by! slug: "#{params[:slug]}.#{params[:format]}" - end - - def create_blob! - ActiveStorage::Blob.create_and_upload! io: @file, filename: @file.original_filename, content_type: @file.content_type - end -end diff --git a/app/controllers/users/avatars_controller.rb b/app/controllers/users/avatars_controller.rb index 180a7c506..8be049cc3 100644 --- a/app/controllers/users/avatars_controller.rb +++ b/app/controllers/users/avatars_controller.rb @@ -4,31 +4,28 @@ class Users::AvatarsController < ApplicationController allow_unauthenticated_access only: :show before_action :set_user + before_action :ensure_permission_to_administer_user, only: :destroy def show - if stale? @user, cache_control: { max_age: cache_max_age, stale_while_revalidate: 1.week } + if stale? @user, cache_control: { max_age: (30.minutes unless Current.user == @user), stale_while_revalidate: 1.week }.compact render_avatar_or_initials end end def destroy @user.avatar.destroy - redirect_to user_path(@user) + redirect_to @user end private - def cache_max_age - if Current.user == @user - 0 - else - 30.minutes - end - end - def set_user @user = User.find(params[:user_id]) end + def ensure_permission_to_administer_user + head :forbidden unless Current.user.admin? || Current.user == @user + end + def render_avatar_or_initials if @user.avatar.attached? send_blob_stream @user.avatar diff --git a/app/controllers/users/events_controller.rb b/app/controllers/users/events_controller.rb new file mode 100644 index 000000000..2f1e3afb0 --- /dev/null +++ b/app/controllers/users/events_controller.rb @@ -0,0 +1,25 @@ +class Users::EventsController < ApplicationController + include FilterScoped + + before_action :set_user, :set_filter, :set_user_filtering + + def show + @filter = Current.user.filters.new(creator_ids: [ @user.id ]) + @day_timeline = Current.user.timeline_for(day_param, filter: @filter) + + fresh_when @day_timeline + end + + private + def set_user + @user = User.active.find(params[:user_id]) + end + + def day_param + if params[:day].present? + Time.zone.parse(params[:day]) + else + Time.current + end + end +end diff --git a/app/controllers/users/joins_controller.rb b/app/controllers/users/joins_controller.rb new file mode 100644 index 000000000..493887452 --- /dev/null +++ b/app/controllers/users/joins_controller.rb @@ -0,0 +1,31 @@ +class Users::JoinsController < ApplicationController + require_access_without_a_user + + before_action :set_join_code, :ensure_join_code_is_valid + + def new + end + + def create + @join_code.redeem do + User.create!(user_params.merge(membership: Current.membership)) + end + + redirect_to root_path + end + + private + def set_join_code + @join_code = Account::JoinCode.active.find_by(code: Current.membership.join_code) + end + + def ensure_join_code_is_valid + unless @join_code&.active? + redirect_to unlink_membership_url(script_name: nil, membership_id: Current.membership.signed_id(purpose: :unlinking)) + end + end + + def user_params + params.expect(user: [ :name, :avatar ]) + end +end diff --git a/app/controllers/users/roles_controller.rb b/app/controllers/users/roles_controller.rb index 8cffa232b..f9eb7d232 100644 --- a/app/controllers/users/roles_controller.rb +++ b/app/controllers/users/roles_controller.rb @@ -3,7 +3,7 @@ class Users::RolesController < ApplicationController before_action :ensure_permission_to_administer_user def update - @user.update(role_params) + @user.update!(role_params) redirect_to users_path end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 811261c87..2ba535fef 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,32 +1,11 @@ class UsersController < ApplicationController - include FilterScoped - - require_access_without_a_user only: %i[ new create ] - - before_action :set_join_code, only: %i[ new create] - before_action :ensure_join_code_is_valid, only: %i[ new create ] before_action :set_user, only: %i[ show edit update destroy ] before_action :ensure_permission_to_change_user, only: %i[ update destroy ] - before_action :set_filter, only: %i[ edit show ] - before_action :set_user_filtering, only: %i[ edit show] - - def new - end - - def create - @join_code.redeem do - User.create!(user_params.merge(membership: Current.membership)) - end - - redirect_to root_path - end def edit end def show - @filter = Current.user.filters.new(creator_ids: [ @user.id ]) - @day_timeline = Current.user.timeline_for(day_param, filter: @filter) end def update @@ -40,16 +19,6 @@ class UsersController < ApplicationController end private - def set_join_code - @join_code = Account::JoinCode.active.find_by(code: Current.membership.join_code) - end - - def ensure_join_code_is_valid - unless @join_code&.active? - redirect_to unlink_membership_url(script_name: nil, membership_id: Current.membership.signed_id(purpose: :unlinking)) - end - end - def set_user @user = User.active.find(params[:id]) end @@ -58,14 +27,6 @@ class UsersController < ApplicationController head :forbidden unless Current.user.can_change?(@user) end - def day_param - if params[:day].present? - Time.zone.parse(params[:day]) - else - Time.current - end - end - def user_params params.expect(user: [ :name, :avatar ]) end diff --git a/app/controllers/webhooks/activations_controller.rb b/app/controllers/webhooks/activations_controller.rb index e99cd35b4..87b05cf91 100644 --- a/app/controllers/webhooks/activations_controller.rb +++ b/app/controllers/webhooks/activations_controller.rb @@ -1,14 +1,10 @@ class Webhooks::ActivationsController < ApplicationController before_action :ensure_admin - before_action :set_webhook def create - @webhook.activate unless @webhook.active? - redirect_to @webhook, status: :see_other - end + webhook = Webhook.find(params[:webhook_id]) + webhook.activate - private - def set_webhook - @webhook = Webhook.find(params[:webhook_id]) - end + redirect_to webhook + end end diff --git a/app/controllers/webhooks_controller.rb b/app/controllers/webhooks_controller.rb index 41f9edffb..b9ad06187 100644 --- a/app/controllers/webhooks_controller.rb +++ b/app/controllers/webhooks_controller.rb @@ -1,6 +1,7 @@ class WebhooksController < ApplicationController + include CollectionScoped + before_action :ensure_admin - before_action :set_collection before_action :set_webhook, except: %i[ index new create ] def index @@ -15,8 +16,8 @@ class WebhooksController < ApplicationController end def create - @webhook = @collection.webhooks.create!(webhook_params) - redirect_to @webhook + webhook = @collection.webhooks.create!(webhook_params) + redirect_to webhook end def edit @@ -29,14 +30,10 @@ class WebhooksController < ApplicationController def destroy @webhook.destroy! - redirect_to collection_webhooks_path, status: :see_other + redirect_to collection_webhooks_path end private - def set_collection - @collection = Collection.find(params[:collection_id]) - end - def set_webhook @webhook = @collection.webhooks.find(params[:id]) end diff --git a/app/helpers/ai_helper.rb b/app/helpers/ai_helper.rb deleted file mode 100644 index 171dcda01..000000000 --- a/app/helpers/ai_helper.rb +++ /dev/null @@ -1,20 +0,0 @@ -require "zlib" - -module AiHelper - LLM_MODELS = %w[ - gpt-5-chat-latest - gpt-5-mini - gpt-5-nano - gpt-5 - chatgpt-4o-latest - gpt-4.1 - gpt-3.5-turbo - gpt-4.1-mini - gpt-4.1-nano - gpt-4o-mini - ] - - def llm_model_options - LLM_MODELS.map { |model| [ model, model ] } - end -end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 8447f024d..f6afd0ba4 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,6 +1,6 @@ module ApplicationHelper def page_title_tag - tag.title @page_title || "BOXCAR" + tag.title @page_title || "Fizzy" end def icon_tag(name, **options) diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 6ee25f658..c7aa72715 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -3,24 +3,30 @@ module EventsHelper case event_type when "added" events = day_timeline.events.where(action: [ "card_published", "card_reopened" ]) + full_events_count = events.count { - title: event_column_title("Added", events.count, day_timeline.day), + title: event_column_title("Added", full_events_count, day_timeline.day), index: 1, - events: events + events: events.limit(100).load, + full_events_count: full_events_count } when "closed" events = day_timeline.events.where(action: "card_closed") + full_events_count = events.count { - title: event_column_title("Closed", events.count, day_timeline.day), + title: event_column_title("Closed", full_events_count, day_timeline.day), index: 3, - events: events + events: events.limit(100).load, + full_events_count: full_events_count } else events = day_timeline.events.where.not(action: [ "card_published", "card_closed", "card_reopened" ]) + full_events_count = events.count { - title: event_column_title("Updated", events.count, day_timeline.day), + title: event_column_title("Updated", full_events_count, day_timeline.day), index: 2, - events: events + events: events.limit(100).load, + full_events_count: full_events_count } end end diff --git a/app/helpers/my/menu_helper.rb b/app/helpers/my/menu_helper.rb new file mode 100644 index 000000000..a972a3408 --- /dev/null +++ b/app/helpers/my/menu_helper.rb @@ -0,0 +1,19 @@ +module My::MenuHelper + def jump_field_tag + text_field_tag :search, nil, + type: "search", + role: "combobox", + placeholder: "Type to jump to a collection, person, place, or tag…", + class: "input input--transparent txt-small", + autofocus: true, + autocorrect: "off", + autocomplete: "off", + aria: { activedescendant: "" }, + data: { + "1p-ignore": "true", + filter_target: "input", + nav_section_expander_target: "input", + navigable_list_target: "input", + action: "input->filter#filter" } + end +end diff --git a/app/javascript/controllers/bar_controller.js b/app/javascript/controllers/bar_controller.js index 0f995ae65..6b10a27b4 100644 --- a/app/javascript/controllers/bar_controller.js +++ b/app/javascript/controllers/bar_controller.js @@ -2,7 +2,7 @@ import { Controller } from "@hotwired/stimulus" import { post } from "@rails/request.js" export default class extends Controller { - static targets = [ "turboFrame", "searchInput", "buttonsContainer" ] + static targets = [ "turboFrame", "search", "searchInput", "form", "buttonsContainer" ] static outlets = [ "dialog" ] static values = { searchUrl: String, @@ -17,18 +17,13 @@ export default class extends Controller { this.dialogOutlet.close() this.#clearTurboFrame() - this.searchInputTarget.querySelector("input").value = "" this.#showItem(this.buttonsContainerTarget) - this.#hideItem(this.searchInputTarget) + this.#hideItem(this.searchTarget) } showModalAndSubmit(event) { - event.preventDefault() - - const form = event.target.closest("form") - this.showModal() - form.requestSubmit() + this.formTarget.requestSubmit() } showModal() { @@ -36,10 +31,18 @@ export default class extends Controller { } search(event) { - this.#openInTurboFrame(this.searchUrlValue) - - this.#showItem(this.searchInputTarget) + this.#showItem(this.searchTarget) this.#hideItem(this.buttonsContainerTarget) + + if (this.searchInputTarget.value.trim()) { + this.showModalAndSubmit() + } else { + this.#loadTurboFrame() + } + } + + #loadTurboFrame() { + this.turboFrameTarget.src = this.searchUrlValue } #clearTurboFrame() { @@ -47,10 +50,6 @@ export default class extends Controller { this.turboFrameTarget.innerHtml = "" } - #openInTurboFrame(url) { - this.turboFrameTarget.src = url - } - #showItem(element) { element.removeAttribute("hidden") diff --git a/app/jobs/card/touch_all_job.rb b/app/jobs/card/touch_all_job.rb deleted file mode 100644 index 7b96fa5b7..000000000 --- a/app/jobs/card/touch_all_job.rb +++ /dev/null @@ -1,7 +0,0 @@ -class Card::TouchAllJob < ApplicationJob - queue_as :backend - - def perform(container) - container.cards.in_batches(&:touch_all) - end -end diff --git a/app/jobs/remove_abandoned_creations_job.rb b/app/jobs/remove_abandoned_creations_job.rb deleted file mode 100644 index 6ca63169d..000000000 --- a/app/jobs/remove_abandoned_creations_job.rb +++ /dev/null @@ -1,7 +0,0 @@ -class RemoveAbandonedCreationsJob < ApplicationJob - def perform - ApplicationRecord.with_each_tenant do |tenant| - Card.remove_abandoned_creations - end - end -end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index d6db07d61..4e7878b62 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -1,5 +1,5 @@ class ApplicationMailer < ActionMailer::Base - default from: "The BOXCAR team " + default from: "The Fizzy team " layout "mailer" append_view_path Rails.root.join("app/views/mailers") diff --git a/app/mailers/magic_link_mailer.rb b/app/mailers/magic_link_mailer.rb index 52b9be031..391e64f6d 100644 --- a/app/mailers/magic_link_mailer.rb +++ b/app/mailers/magic_link_mailer.rb @@ -3,7 +3,7 @@ class MagicLinkMailer < ApplicationMailer @magic_link = magic_link @identity = @magic_link.identity - mail to: @identity.email_address, subject: "Sign in to BOXCAR" + mail to: @identity.email_address, subject: "Sign in to Fizzy" end private diff --git a/app/mailers/notification/bundle_mailer.rb b/app/mailers/notification/bundle_mailer.rb index 32f883efd..c821a1274 100644 --- a/app/mailers/notification/bundle_mailer.rb +++ b/app/mailers/notification/bundle_mailer.rb @@ -11,6 +11,6 @@ class Notification::BundleMailer < ApplicationMailer mail \ to: bundle.user.identity.email_address, - subject: "Latest Activity in BOXCAR" + subject: "Latest Activity in Fizzy" end end diff --git a/app/models/account.rb b/app/models/account.rb index e32f0bcdb..0c730a937 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -16,7 +16,7 @@ class Account < ApplicationRecord end end - # To use the account as a generic card container. See +Entropy::Configuration+. + # To use the account as a generic card container. See +Entropy+. def cards Card.all end diff --git a/app/models/account/entropic.rb b/app/models/account/entropic.rb index b82b1051b..e54af1432 100644 --- a/app/models/account/entropic.rb +++ b/app/models/account/entropic.rb @@ -1,17 +1,10 @@ module Account::Entropic extend ActiveSupport::Concern + DEFAULT_ENTROPY_PERIOD = 30.days + included do - has_one :default_entropy_configuration, class_name: "Entropy::Configuration", as: :container, dependent: :destroy - - before_save :set_default_entropy_configuration + has_one :entropy, as: :container, dependent: :destroy + after_create -> { create_entropy!(auto_postpone_period: DEFAULT_ENTROPY_PERIOD) } end - - private - DEFAULT_ENTROPY_PERIOD = 30.days - - def set_default_entropy_configuration - self.default_entropy_configuration ||= build_default_entropy_configuration \ - auto_postpone_period: DEFAULT_ENTROPY_PERIOD - end end diff --git a/app/models/account/join_code.rb b/app/models/account/join_code.rb index ddf4cf822..69d681197 100644 --- a/app/models/account/join_code.rb +++ b/app/models/account/join_code.rb @@ -3,11 +3,7 @@ class Account::JoinCode < ApplicationRecord scope :active, -> { where("usage_count < usage_limit") } - before_validation :generate_code, on: :create, if: -> { code.blank? } - - validates :code, presence: true, uniqueness: true - validates :usage_limit, numericality: { only_integer: true, greater_than_or_equal_to: 0 } - validates :usage_count, numericality: { only_integer: true, greater_than_or_equal_to: 0 } + before_create :generate_code, if: -> { code.blank? } def redeem transaction do diff --git a/app/models/card.rb b/app/models/card.rb index 3b455a424..7ac40ca09 100644 --- a/app/models/card.rb +++ b/app/models/card.rb @@ -1,7 +1,7 @@ class Card < ApplicationRecord - include Assignable, Attachments, Closeable, Colored, Entropic, Eventable, - Golden, Mentions, Multistep, Pinnable, Postponable, Promptable, Readable, - Searchable, Stallable, Statuses, Taggable, Triageable, Watchable + include Assignable, Attachments, Broadcastable, Closeable, Colored, Entropic, + Eventable, Golden, Mentions, Multistep, Pinnable, Postponable, Promptable, + Readable, Searchable, Stallable, Statuses, Taggable, Triageable, Watchable belongs_to :collection, touch: true belongs_to :creator, class_name: "User", default: -> { Current.user } @@ -52,6 +52,10 @@ class Card < ApplicationRecord end end + def filled? + title.present? || description.present? + end + private def set_default_title self.title = "Untitled" if title.blank? diff --git a/app/models/card/activity_spike/detector.rb b/app/models/card/activity_spike/detector.rb index 6721b946f..63f246ca0 100644 --- a/app/models/card/activity_spike/detector.rb +++ b/app/models/card/activity_spike/detector.rb @@ -20,10 +20,12 @@ class Card::ActivitySpike::Detector end def register_activity_spike - if card.activity_spike - card.activity_spike.touch - else - card.create_activity_spike! + Card.suppressing_turbo_broadcasts do + if card.activity_spike + card.activity_spike.touch + else + card.create_activity_spike! + end end end diff --git a/app/models/card/broadcastable.rb b/app/models/card/broadcastable.rb new file mode 100644 index 000000000..aa24b776c --- /dev/null +++ b/app/models/card/broadcastable.rb @@ -0,0 +1,7 @@ +module Card::Broadcastable + extend ActiveSupport::Concern + + included do + broadcasts_refreshes + end +end diff --git a/app/models/card/entropic.rb b/app/models/card/entropic.rb index 46acd8da8..68c0c6aa1 100644 --- a/app/models/card/entropic.rb +++ b/app/models/card/entropic.rb @@ -4,16 +4,16 @@ module Card::Entropic included do scope :due_to_be_postponed, -> do active - .left_outer_joins(collection: :entropy_configuration) - .where("last_active_at <= DATETIME('now', '-' || COALESCE(entropy_configurations.auto_postpone_period, ?) || ' seconds')", - Entropy::Configuration.default.auto_postpone_period) + .left_outer_joins(collection: :entropy) + .where("last_active_at <= DATETIME('now', '-' || COALESCE(entropies.auto_postpone_period, ?) || ' seconds')", + Account.sole.entropy.auto_postpone_period) end scope :postponing_soon, -> do active - .left_outer_joins(collection: :entropy_configuration) - .where("last_active_at > DATETIME('now', '-' || COALESCE(entropy_configurations.auto_postpone_period, ?) || ' seconds')", Entropy::Configuration.default.auto_postpone_period) - .where("last_active_at <= DATETIME('now', '-' || CAST(COALESCE(entropy_configurations.auto_postpone_period, ?) * 0.75 AS INTEGER) || ' seconds')", Entropy::Configuration.default.auto_postpone_period) + .left_outer_joins(collection: :entropy) + .where("last_active_at > DATETIME('now', '-' || COALESCE(entropies.auto_postpone_period, ?) || ' seconds')", Account.sole.entropy.auto_postpone_period) + .where("last_active_at <= DATETIME('now', '-' || CAST(COALESCE(entropies.auto_postpone_period, ?) * 0.75 AS INTEGER) || ' seconds')", Account.sole.entropy.auto_postpone_period) end delegate :auto_postpone_period, to: :collection diff --git a/app/models/card/eventable.rb b/app/models/card/eventable.rb index 520fd95d4..c52fb7a29 100644 --- a/app/models/card/eventable.rb +++ b/app/models/card/eventable.rb @@ -19,6 +19,7 @@ module Card::Eventable def touch_last_active_at # Not using touch so that we can detect attribute change on callbacks update!(last_active_at: Time.current) + broadcast_activity end private @@ -35,4 +36,8 @@ module Card::Eventable def create_system_comment_for(event) SystemCommenter.new(self, event).comment end + + def broadcast_activity + broadcast_render_later_to self, :activity, partial: "card/display/refresh_activity", locals: { card: self } + end end diff --git a/app/models/card/pinnable.rb b/app/models/card/pinnable.rb index 0cdaf4000..ff3407ea0 100644 --- a/app/models/card/pinnable.rb +++ b/app/models/card/pinnable.rb @@ -9,6 +9,10 @@ module Card::Pinnable pins.exists?(user: user) end + def pin_for(user) + pins.find_by(user: user) + end + def pin_by(user) pins.find_or_create_by!(user: user) end diff --git a/app/models/card/statuses.rb b/app/models/card/statuses.rb index 73d3f122a..f26896b21 100644 --- a/app/models/card/statuses.rb +++ b/app/models/card/statuses.rb @@ -2,38 +2,17 @@ module Card::Statuses extend ActiveSupport::Concern included do - enum :status, %w[ creating drafted published ].index_by(&:itself) + enum :status, %w[ drafted published ].index_by(&:itself) after_create -> { track_event :published }, if: :published? scope :published_or_drafted_by, ->(user) { where(status: :published).or(where(status: :drafted, creator: user)) } end - class_methods do - def remove_abandoned_creations - Card.creating.where(updated_at: ..1.day.ago).destroy_all - end - end - - def can_recover_abandoned_creation? - abandoned_creations.where(updated_at: 1.day.ago..).any? - end - - def recover_abandoned_creation - abandoned_creations.last.tap do |card| - Card.creating.where(creator: creator).excluding(card).destroy_all - end - end - def publish transaction do published! track_event :published end end - - private - def abandoned_creations - Card.creating.where(creator: creator).where("created_at != updated_at").excluding(self) - end end diff --git a/app/models/card/watchable.rb b/app/models/card/watchable.rb index 2d23868bf..17235529e 100644 --- a/app/models/card/watchable.rb +++ b/app/models/card/watchable.rb @@ -9,7 +9,11 @@ module Card::Watchable end def watched_by?(user) - watchers.include?(user) + watch_for(user)&.watching? + end + + def watch_for(user) + watches.find_by(user: user) end def watch_by(user) diff --git a/app/models/collection.rb b/app/models/collection.rb index 7b756409d..ae09d9471 100644 --- a/app/models/collection.rb +++ b/app/models/collection.rb @@ -8,7 +8,6 @@ class Collection < ApplicationRecord has_many :tags, -> { distinct }, through: :cards has_many :events has_many :webhooks, dependent: :destroy - has_one :entropy_configuration, class_name: "Entropy::Configuration", as: :container, dependent: :destroy scope :alphabetically, -> { order("lower(name)") } scope :ordered_by_recently_accessed, -> { merge(Access.ordered_by_recently_accessed) } diff --git a/app/models/collection/cards.rb b/app/models/collection/cards.rb index 17ed592aa..105f4912b 100644 --- a/app/models/collection/cards.rb +++ b/app/models/collection/cards.rb @@ -4,11 +4,6 @@ module Collection::Cards included do has_many :cards, dependent: :destroy - after_update_commit :touch_all_cards_later, if: :saved_change_to_name? + after_update_commit -> { cards.touch_all }, if: :saved_change_to_name? end - - private - def touch_all_cards_later - Card::TouchAllJob.perform_later(self) - end end diff --git a/app/models/collection/entropic.rb b/app/models/collection/entropic.rb index a3879955b..a84e7eb62 100644 --- a/app/models/collection/entropic.rb +++ b/app/models/collection/entropic.rb @@ -2,15 +2,16 @@ module Collection::Entropic extend ActiveSupport::Concern included do - delegate :auto_postpone_period, to: :entropy_configuration + delegate :auto_postpone_period, to: :entropy + has_one :entropy, as: :container, dependent: :destroy end - def entropy_configuration - super || Entropy::Configuration.default + def entropy + super || Account.sole.entropy end def auto_postpone_period=(new_value) - entropy_configuration ||= association(:entropy_configuration).reader || self.build_entropy_configuration - entropy_configuration.update auto_postpone_period: new_value + entropy ||= association(:entropy).reader || self.build_entropy + entropy.update auto_postpone_period: new_value end end diff --git a/app/models/column.rb b/app/models/column.rb index 0285ab185..87772367f 100644 --- a/app/models/column.rb +++ b/app/models/column.rb @@ -4,27 +4,7 @@ class Column < ApplicationRecord belongs_to :collection, touch: true has_many :cards, dependent: :nullify - validates :name, presence: true - validates :color, presence: true - - before_validation :set_default_color - after_save_commit :touch_all_cards_later, if: :should_invalidate_cards? - after_destroy_commit :touch_all_collection_cards - - private - def set_default_color - self.color ||= Card::DEFAULT_COLOR - end - - def touch_all_cards_later - Card::TouchAllJob.perform_later(self) - end - - def should_invalidate_cards? - saved_change_to_name? || saved_change_to_color? - end - - def touch_all_collection_cards - Card::TouchAllJob.perform_later(collection) - end + before_validation -> { self.color ||= Card::DEFAULT_COLOR } + after_save_commit -> { cards.touch_all }, if: -> { saved_change_to_name? || saved_change_to_color? } + after_destroy_commit -> { collection.cards.touch_all } end diff --git a/app/models/entropy.rb b/app/models/entropy.rb index 42e730a09..6be6675ee 100644 --- a/app/models/entropy.rb +++ b/app/models/entropy.rb @@ -1,5 +1,5 @@ -module Entropy - def self.table_name_prefix - "entropy_" - end +class Entropy < ApplicationRecord + belongs_to :container, polymorphic: true + + after_commit -> { container.cards.touch_all } end diff --git a/app/models/entropy/configuration.rb b/app/models/entropy/configuration.rb deleted file mode 100644 index 1884cb112..000000000 --- a/app/models/entropy/configuration.rb +++ /dev/null @@ -1,16 +0,0 @@ -class Entropy::Configuration < ApplicationRecord - belongs_to :container, polymorphic: true - - after_commit :touch_all_cards_later - - class << self - def default - Account.sole.default_entropy_configuration - end - end - - private - def touch_all_cards_later - Card::TouchAllJob.perform_later(container) - end -end diff --git a/app/models/step.rb b/app/models/step.rb index c4e35f33e..c2063dfaa 100644 --- a/app/models/step.rb +++ b/app/models/step.rb @@ -3,6 +3,8 @@ class Step < ApplicationRecord scope :completed, -> { where(completed: true) } + validates :content, presence: true + def completed? completed end diff --git a/app/models/user/filtering.rb b/app/models/user/filtering.rb index c9f049ab3..fdc7f2bcb 100644 --- a/app/models/user/filtering.rb +++ b/app/models/user/filtering.rb @@ -1,6 +1,4 @@ class User::Filtering - include Rails.application.routes.url_helpers - attr_reader :user, :filter, :expanded delegate :as_params, :single_collection, to: :filter diff --git a/app/models/webhook.rb b/app/models/webhook.rb index 9a103d9bc..1af19cd47 100644 --- a/app/models/webhook.rb +++ b/app/models/webhook.rb @@ -40,11 +40,11 @@ class Webhook < ApplicationRecord validate :validate_url def activate - update_columns active: true + update! active: true unless active? end def deactivate - update_columns active: false + update! active: false end def renderer diff --git a/app/views/account/join_codes/edit.html.erb b/app/views/account/join_codes/edit.html.erb index c96a4ac82..de53bf8c8 100644 --- a/app/views/account/join_codes/edit.html.erb +++ b/app/views/account/join_codes/edit.html.erb @@ -1,7 +1,6 @@ <% @page_title = "Change usage limit" %> <% content_for :header do %> - <%= render "my/menus/menu" %>
<%= link_to account_join_code_path, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %> Invite link @@ -14,13 +13,11 @@

<%= @page_title %>

How many times can this link be used to join the account?

+ <%= form_with model: @join_code, url: account_join_code_path, method: :patch, data: { controller: "form" }, html: { class: "flex flex-column gap" } do |form| %> <%= form.number_field :usage_limit, - required: true, - autofocus: true, - min: @join_code.usage_count, - class: "input center txt-large fit-content font-weight-black txt-align-center", - style: "max-inline-size: 8ch", + required: true, autofocus: true, min: @join_code.usage_count, + class: "input center txt-large fit-content font-weight-black txt-align-center", style: "max-inline-size: 8ch", data: { action: "keydown.esc@document->form#cancel focus->form#select" } %>

diff --git a/app/views/account/join_codes/show.html.erb b/app/views/account/join_codes/show.html.erb index f076518ff..f3142b091 100644 --- a/app/views/account/join_codes/show.html.erb +++ b/app/views/account/join_codes/show.html.erb @@ -1,7 +1,6 @@ <% @page_title = "Add people" %> <% content_for :header do %> - <%= render "my/menus/menu" %>

<%= link_to account_settings_path, class: "btn borderless txt-medium", data: { controller: "hotkey", action: "keydown.esc@document->hotkey#click" } do %> Account Settings @@ -18,8 +17,8 @@ <% url = join_url(code: @join_code.code, tenant: @join_code.tenant, script_name: nil) %>
- <%= button_to account_join_code_path, method: :delete, class: "btn btn--circle txt-small", - data: { turbo_confirm: "Are you sure you want to generate a new link? The previous code will stop working." } do %> + <%= button_to account_join_code_path, method: :delete, class: "btn btn--circle txt-small", data: { + turbo_confirm: "Are you sure you want to generate a new link? The previous code will stop working." } do %> <%= icon_tag "refresh" %> Generate a new code <% end %> @@ -27,9 +26,8 @@
<%= tag.button class: "btn btn--link", data: { - controller: "copy-to-clipboard", action: "copy-to-clipboard#copy", - copy_to_clipboard_success_class: "btn--success", copy_to_clipboard_content_value: url } do %> - + controller: "copy-to-clipboard", action: "copy-to-clipboard#copy", + copy_to_clipboard_success_class: "btn--success", copy_to_clipboard_content_value: url } do %> <%= icon_tag "copy-paste" %> Copy invite link <% end %> @@ -60,6 +58,7 @@

"> This code has been used <%= @join_code.usage_count %>/<%= @join_code.usage_limit %> times (<%= @join_code.active? ? @join_code.usage_limit - @join_code.usage_count : "none" %> remaining) + <%= link_to edit_account_join_code_path, class: @join_code.active? ? "txt-link" : "txt-negative txt-underline" do %> Change limit <% end %> diff --git a/app/views/account/settings/_entropy.html.erb b/app/views/account/settings/_entropy.html.erb new file mode 100644 index 000000000..f6056ab37 --- /dev/null +++ b/app/views/account/settings/_entropy.html.erb @@ -0,0 +1,8 @@ +

+
+

Auto close

+

Fizzy doesn’t let stale cards stick around forever. Cards automatically close as “Not Now” without activity for specific period of time. This is the default, global setting — you can override it on each board.

+
+ + <%= render "entropy/auto_close", model: account.entropy, url: account_entropy_path %> +
diff --git a/app/views/account/settings/_entropy_configuration.html.erb b/app/views/account/settings/_entropy_configuration.html.erb deleted file mode 100644 index 9ecc64db6..000000000 --- a/app/views/account/settings/_entropy_configuration.html.erb +++ /dev/null @@ -1,8 +0,0 @@ -
-
-

Auto close

-

BOXCAR doesn’t let stale cards stick around forever. Cards automatically close as “Not Now” without activity for specific period of time. This is the default, global setting — you can override it on each board.

-
- - <%= render "entropy/auto_close", model: account.default_entropy_configuration, url: account_entropy_configuration_path %> -
diff --git a/app/views/account/settings/show.html.erb b/app/views/account/settings/show.html.erb index 53533bba4..7e2dbbfbf 100644 --- a/app/views/account/settings/show.html.erb +++ b/app/views/account/settings/show.html.erb @@ -10,5 +10,5 @@ <%= render "account/settings/users", users: @users %>
- <%= render "account/settings/entropy_configuration", account: @account %> + <%= render "account/settings/entropy", account: @account %> diff --git a/app/views/bar/_bar.html.erb b/app/views/bar/_bar.html.erb index 4858ce2a8..095e7c0b2 100644 --- a/app/views/bar/_bar.html.erb +++ b/app/views/bar/_bar.html.erb @@ -7,7 +7,7 @@ <% end %>
- diff --git a/app/views/cards/container/_status.html.erb b/app/views/cards/container/_status.html.erb index cc093ab06..492130b9e 100644 --- a/app/views/cards/container/_status.html.erb +++ b/app/views/cards/container/_status.html.erb @@ -1,13 +1,4 @@ -<% if card.creating? && card.can_recover_abandoned_creation? %> -
- You have an unsaved card. Would you like to continue where you left off? - <%= button_to card_recover_path(card), class: "btn txt-small", data: { turbo_action: "replace" } do %> - Restore - <% end %> -
-<% end %> - -<% if card.drafted? %> +<% if card.drafted? && card.filled? %>
This is a draft, it’s only visible to you. <%= button_to card_publish_path(@card), class: "btn txt-small", style: "--btn-background: #{card.color}" do %> diff --git a/app/views/cards/container/footer/_draft.html.erb b/app/views/cards/container/footer/_draft.html.erb index e22d4619b..a2317ce08 100644 --- a/app/views/cards/container/footer/_draft.html.erb +++ b/app/views/cards/container/footer/_draft.html.erb @@ -1,8 +1,7 @@
- <%= button_to "Create card", card_publish_path(card), name: "creation_type", value: "add", class: "btn" %> + <%= button_to "Create card", card_publish_path(card), name: "creation_type", value: "add", class: "btn", + form: { data: { controller: "form", action: "keydown.ctrl+enter@document->form#submit:prevent keydown.meta+enter@document->form#submit:prevent" } } %> <%= button_to "Create and add another", card_publish_path(card), method: :post, class: "btn btn--reversed", name: "creation_type", value: "add_another", title: "Create and add another (ctrl+shift+enter)", form: { data: { turbo: false } }, data: { controller: "hotkey", action: "keydown.ctrl+shift+enter@document->hotkey#click" } %> - - <%= button_to "Save as a draft", card_path(card), name: "card[status]", value: "drafted", method: :put, class: "btn btn--reversed" %>
diff --git a/app/views/cards/display/common/_assignees.html.erb b/app/views/cards/display/common/_assignees.html.erb index 19aebd6a9..c07ec9e6d 100644 --- a/app/views/cards/display/common/_assignees.html.erb +++ b/app/views/cards/display/common/_assignees.html.erb @@ -9,7 +9,7 @@ Assign - + <%= yield %>
diff --git a/app/views/cards/display/common/_meta.html.erb b/app/views/cards/display/common/_meta.html.erb index 77d306198..dd2d8efbd 100644 --- a/app/views/cards/display/common/_meta.html.erb +++ b/app/views/cards/display/common/_meta.html.erb @@ -15,13 +15,9 @@ - <% if card.creating? %> -   - <% else %> - <%= icon_tag "refresh--meta" %> - Updated - <%= local_datetime_tag(card.last_active_at, style: :daysago) %> - <% end %> + <%= icon_tag "refresh--meta" %> + Updated + <%= local_datetime_tag(card.last_active_at, style: :daysago) %> diff --git a/app/views/cards/display/perma/_assignees.html.erb b/app/views/cards/display/perma/_assignees.html.erb index 1c530263e..a115f39b2 100644 --- a/app/views/cards/display/perma/_assignees.html.erb +++ b/app/views/cards/display/perma/_assignees.html.erb @@ -1,3 +1,3 @@ <%= render "cards/display/common/assignees", card: card do %> - <%= turbo_frame_tag card, :assignment, src: new_card_assignment_path(card) %> + <%= turbo_frame_tag card, :assignment, src: new_card_assignment_path(card), refresh: "morph" %> <% end %> diff --git a/app/views/cards/display/perma/_collection.html.erb b/app/views/cards/display/perma/_collection.html.erb index a9e0cbbfa..aecf85628 100644 --- a/app/views/cards/display/perma/_collection.html.erb +++ b/app/views/cards/display/perma/_collection.html.erb @@ -1,14 +1,12 @@ <%= render "cards/display/common/collection", card: card do %> - <% if Current.user.collections.count > 1 %> -
> - +
> + - - <%= turbo_frame_tag "collection_picker", src: edit_card_collection_path(card), target: "_top" %> - -
- <% end %> + + <%= turbo_frame_tag "collection_picker", src: edit_card_collection_path(card), target: "_top", refresh: "morph" %> + +
<% end %> diff --git a/app/views/cards/display/perma/_steps.html.erb b/app/views/cards/display/perma/_steps.html.erb index 5ce71f721..6cca251d2 100644 --- a/app/views/cards/display/perma/_steps.html.erb +++ b/app/views/cards/display/perma/_steps.html.erb @@ -1,9 +1,9 @@
    <%= render partial: "cards/steps/step", collection: card.steps, as: :step %> -
  1. +
  2. - <%= form_with model: [card, Step.new], url: card_steps_path(card), data: { controller: "form", action: "submit->form#preventEmptySubmit" } do |form| %> + <%= form_with model: [card, Step.new], url: card_steps_path(card), data: { controller: "form", action: "submit->form#preventEmptySubmit turbo:submit-end->form#reset" } do |form| %> <%= form.text_field :content, class: "input step__content hide-focus-ring", placeholder: "Add a step…", autocomplete: "off", data: { form_target: "input" }, aria: { label: "Add a step" } %> <% end %>
  3. diff --git a/app/views/cards/display/perma/_tags.html.erb b/app/views/cards/display/perma/_tags.html.erb index 6cd87d932..e72041211 100644 --- a/app/views/cards/display/perma/_tags.html.erb +++ b/app/views/cards/display/perma/_tags.html.erb @@ -5,12 +5,12 @@ Add a tag - - <%= turbo_frame_tag card, :tagging, src: new_card_tagging_path(card) %> + + <%= turbo_frame_tag card, :tagging, src: new_card_tagging_path(card), refresh: "morph" %>
- <% if card.tags.any? || card.creating? %> + <% if card.tags.any? %>
<% card.tags.each_with_index do |tag, index| %> <%= link_to cards_path(collection_ids: [ card.collection ], tag_ids: [ tag.id ]), diff --git a/app/views/cards/display/preview/_bubble.html.erb b/app/views/cards/display/preview/_bubble.html.erb index af2d71d04..c39772090 100644 --- a/app/views/cards/display/preview/_bubble.html.erb +++ b/app/views/cards/display/preview/_bubble.html.erb @@ -1,4 +1,7 @@ -<%= tag.div hidden: true, class: "bubble", +<%= tag.div \ + id: dom_id(card, "bubble"), + hidden: true, + class: "bubble", data: { controller: "bubble", action: "turbo:morph-element->bubble#update", diff --git a/app/views/cards/show.html.erb b/app/views/cards/show.html.erb index 5c5549fa4..c33a2c40b 100644 --- a/app/views/cards/show.html.erb +++ b/app/views/cards/show.html.erb @@ -12,10 +12,11 @@ <% end %> <%= turbo_stream_from @card %> +<%= turbo_stream_from @card, :activity %>
<%= render "cards/container", card: @card %> - <%= render "cards/messages", card: @card unless @card.creating? %> + <%= render "cards/messages", card: @card unless @card.drafted? %> <%= render "layouts/lightbox" do %> <%= button_to_remove_card_image(@card) if @card.image.attached? %> diff --git a/app/views/cards/steps/create.turbo_stream.erb b/app/views/cards/steps/create.turbo_stream.erb new file mode 100644 index 000000000..399593809 --- /dev/null +++ b/app/views/cards/steps/create.turbo_stream.erb @@ -0,0 +1,3 @@ +<%= turbo_stream.before dom_id(@card, :new_step) do %> + <%= render "cards/steps/step", step: @step %> +<% end %> diff --git a/app/views/cards/steps/destroy.turbo_stream.erb b/app/views/cards/steps/destroy.turbo_stream.erb new file mode 100644 index 000000000..a5c5deefb --- /dev/null +++ b/app/views/cards/steps/destroy.turbo_stream.erb @@ -0,0 +1 @@ +<%= turbo_stream.remove @step %> diff --git a/app/views/cards/steps/update.turbo_stream.erb b/app/views/cards/steps/update.turbo_stream.erb new file mode 100644 index 000000000..24fd7f934 --- /dev/null +++ b/app/views/cards/steps/update.turbo_stream.erb @@ -0,0 +1,3 @@ +<%= turbo_stream.replace @step do %> + <%= render "cards/steps/step", step: @step %> +<% end %> diff --git a/app/views/cards/triage/_columns.html.erb b/app/views/cards/triage/_columns.html.erb index 2ceaab98b..591a8e256 100644 --- a/app/views/cards/triage/_columns.html.erb +++ b/app/views/cards/triage/_columns.html.erb @@ -1 +1 @@ -<%= turbo_frame_tag dom_id(card, :columns), src: edit_card_column_path(card), target: "_top" %> +<%= turbo_frame_tag dom_id(card, :columns), src: edit_card_column_path(card), target: "_top", refresh: "morph" %> diff --git a/app/views/collections/edit/_auto_close.html.erb b/app/views/collections/edit/_auto_close.html.erb index fba5f8771..e7e135d8d 100644 --- a/app/views/collections/edit/_auto_close.html.erb +++ b/app/views/collections/edit/_auto_close.html.erb @@ -1,7 +1,7 @@ -<%= turbo_frame_tag @collection, :entropy_configuration do %> +<%= turbo_frame_tag @collection, :entropy do %>

Auto close

-

BOXCAR doesn’t let stale cards stick around forever. Cards automatically close as “Not now” if no one updates, comments, or moves a card for…

- <%= render "entropy/auto_close", model: collection, url: collection_entropy_configuration_path(collection) %> +

Fizzy doesn’t let stale cards stick around forever. Cards automatically close as “Not now” if no one updates, comments, or moves a card for…

+ <%= render "entropy/auto_close", model: collection, url: collection_entropy_path(collection) %>
<% end %> diff --git a/app/views/collections/edit/_publication.html.erb b/app/views/collections/edit/_publication.html.erb index 6f90bb0b0..0673bd043 100644 --- a/app/views/collections/edit/_publication.html.erb +++ b/app/views/collections/edit/_publication.html.erb @@ -1,7 +1,7 @@ <%= turbo_frame_tag @collection, :publication do %>

Public link

-
Turn on the Public link to share this board with anyone in the world. They won’t need to log in and they won’t be able to see anything else in BOXCAR.
+
Turn on the Public link to share this board with anyone in the world. They won’t need to log in and they won’t be able to see anything else in Fizzy.
<% if collection.published? %> diff --git a/app/views/collections/entropies/update.turbo_stream.erb b/app/views/collections/entropies/update.turbo_stream.erb new file mode 100644 index 000000000..e8164aba4 --- /dev/null +++ b/app/views/collections/entropies/update.turbo_stream.erb @@ -0,0 +1,2 @@ +<%= turbo_stream.replace([ @collection, :entropy ], partial: "collections/edit/auto_close", locals: { collection: @collection }) %> +<%= turbo_stream_flash(notice: "Saved") %> diff --git a/app/views/collections/publications/create.turbo_stream.erb b/app/views/collections/publications/create.turbo_stream.erb new file mode 100644 index 000000000..62379328c --- /dev/null +++ b/app/views/collections/publications/create.turbo_stream.erb @@ -0,0 +1,2 @@ +<%= turbo_stream.replace([ @collection, :publication ], partial: "collections/edit/publication", locals: { collection: @collection }) %> +<%= turbo_stream_flash(notice: "Saved") %> diff --git a/app/views/collections/publications/destroy.turbo_stream.erb b/app/views/collections/publications/destroy.turbo_stream.erb new file mode 100644 index 000000000..62379328c --- /dev/null +++ b/app/views/collections/publications/destroy.turbo_stream.erb @@ -0,0 +1,2 @@ +<%= turbo_stream.replace([ @collection, :publication ], partial: "collections/edit/publication", locals: { collection: @collection }) %> +<%= turbo_stream_flash(notice: "Saved") %> diff --git a/app/views/columns/cards/drops/closures/create.turbo_stream.erb b/app/views/columns/cards/drops/closures/create.turbo_stream.erb new file mode 100644 index 000000000..8c4630d20 --- /dev/null +++ b/app/views/columns/cards/drops/closures/create.turbo_stream.erb @@ -0,0 +1 @@ +<%= turbo_stream.replace("closed-cards", partial: "collections/show/closed", method: :morph, locals: { collection: @card.collection }) %> diff --git a/app/views/columns/cards/drops/columns/create.turbo_stream.erb b/app/views/columns/cards/drops/columns/create.turbo_stream.erb new file mode 100644 index 000000000..5ab9915a0 --- /dev/null +++ b/app/views/columns/cards/drops/columns/create.turbo_stream.erb @@ -0,0 +1 @@ +<%= turbo_stream.replace(dom_id(@column), partial: "collections/show/column", method: :morph, locals: { column: @column }) %> diff --git a/app/views/columns/cards/drops/not_nows/create.turbo_stream.erb b/app/views/columns/cards/drops/not_nows/create.turbo_stream.erb new file mode 100644 index 000000000..faf65ba56 --- /dev/null +++ b/app/views/columns/cards/drops/not_nows/create.turbo_stream.erb @@ -0,0 +1 @@ +<%= turbo_stream.replace("not-now", partial: "collections/show/not_now", method: :morph, locals: { collection: @card.collection }) %> diff --git a/app/views/columns/cards/drops/streams/create.turbo_stream.erb b/app/views/columns/cards/drops/streams/create.turbo_stream.erb new file mode 100644 index 000000000..1e957f827 --- /dev/null +++ b/app/views/columns/cards/drops/streams/create.turbo_stream.erb @@ -0,0 +1 @@ +<%= turbo_stream.replace("the-stream", partial: "collections/show/stream", method: :morph, locals: { collection: @card.collection, page: @page }) %> diff --git a/app/views/events/day_timeline/_column.html.erb b/app/views/events/day_timeline/_column.html.erb index a1186da6b..aefe9258e 100644 --- a/app/views/events/day_timeline/_column.html.erb +++ b/app/views/events/day_timeline/_column.html.erb @@ -8,4 +8,10 @@ <%= local_datetime_tag events.first.created_at, class: "event__timestamp txt-small translucent" %> <% end %> <% end %> + + <% if column[:events].count < column[:full_events_count] %> + + <% end %>
diff --git a/app/views/layouts/public.html.erb b/app/views/layouts/public.html.erb index 6df1eabeb..c217f44bb 100644 --- a/app/views/layouts/public.html.erb +++ b/app/views/layouts/public.html.erb @@ -8,7 +8,7 @@ diff --git a/app/views/mailers/identity_mailer/email_change_confirmation.html.erb b/app/views/mailers/identity_mailer/email_change_confirmation.html.erb index 0c46017ff..438dc18e9 100644 --- a/app/views/mailers/identity_mailer/email_change_confirmation.html.erb +++ b/app/views/mailers/identity_mailer/email_change_confirmation.html.erb @@ -1,5 +1,5 @@

Confirm your email address change

-

Hit the button below to use this email address in BOXCAR.

+

Hit the button below to use this email address in Fizzy.

<%= link_to "Yes use use this email address", email_address_confirmation_url(membership_id: @membership.id, email_address_token: @token), class: "btn" %> diff --git a/app/views/mailers/identity_mailer/email_change_confirmation.text.erb b/app/views/mailers/identity_mailer/email_change_confirmation.text.erb index 7d669a4d4..265c9cbf2 100644 --- a/app/views/mailers/identity_mailer/email_change_confirmation.text.erb +++ b/app/views/mailers/identity_mailer/email_change_confirmation.text.erb @@ -1,7 +1,7 @@ Confirm your email address change <%= "=" * 80 %> -Hit the link below to use this email address in BOXCAR: +Hit the link below to use this email address in Fizzy: <%= email_address_confirmation_url(membership_id: @membership.id, email_address_token: @token) %> diff --git a/app/views/mailers/magic_link_mailer/sign_in_instructions.html.erb b/app/views/mailers/magic_link_mailer/sign_in_instructions.html.erb index 7a193eb08..b966ecfa6 100644 --- a/app/views/mailers/magic_link_mailer/sign_in_instructions.html.erb +++ b/app/views/mailers/magic_link_mailer/sign_in_instructions.html.erb @@ -1,7 +1,7 @@ -

Sign in to BOXCAR

-

Hit the button below to sign in to BOXCAR on this device

+

Sign in to Fizzy

+

Hit the button below to sign in to Fizzy on this device

-<%= link_to "Sign in to BOXCAR", session_magic_link_url(code: @magic_link.code), class: "btn" %> +<%= link_to "Sign in to Fizzy", session_magic_link_url(code: @magic_link.code), class: "btn" %>

If you’re signing in on another device, enter this special code:
<%= @magic_link.code %> diff --git a/app/views/mailers/magic_link_mailer/sign_in_instructions.text.erb b/app/views/mailers/magic_link_mailer/sign_in_instructions.text.erb index 037cda365..f44e1c908 100644 --- a/app/views/mailers/magic_link_mailer/sign_in_instructions.text.erb +++ b/app/views/mailers/magic_link_mailer/sign_in_instructions.text.erb @@ -1,4 +1,4 @@ -Sign in to BOXCAR +Sign in to Fizzy <%= "=" * 80 %> Open this link in your browser to login on this device: diff --git a/app/views/mailers/notification/bundle_mailer/notification.html.erb b/app/views/mailers/notification/bundle_mailer/notification.html.erb index 5983a2bc1..e7478b6b7 100644 --- a/app/views/mailers/notification/bundle_mailer/notification.html.erb +++ b/app/views/mailers/notification/bundle_mailer/notification.html.erb @@ -3,7 +3,7 @@

Everything since <%= @bundle.starts_at.strftime("%-l%P on %A, %B %-d") %>

You have <%= link_to pluralize(@notifications.count, "new notification"), notifications_url %>.

<%= render partial: "notification/bundle_mailer/notification", collection: @notifications, as: :notification %> - diff --git a/app/views/mailers/notification/bundle_mailer/notification.text.erb b/app/views/mailers/notification/bundle_mailer/notification.text.erb index ce383e249..0149767e6 100644 --- a/app/views/mailers/notification/bundle_mailer/notification.text.erb +++ b/app/views/mailers/notification/bundle_mailer/notification.text.erb @@ -4,7 +4,7 @@ You have <%= pluralize @notifications.count, "new notification" %>. <%= render partial: "notification/bundle_mailer/notification", collection: @notifications, as: :notification %> -------------------------------------------------------------------------------- -BOXCAR emails you about new notifications every few hours. +Fizzy emails you about new notifications every few hours. Change how often you get these: <%= notifications_settings_url %> diff --git a/app/views/my/menus/_button.html.erb b/app/views/my/menus/_button.html.erb index 589c69bf2..52305cf60 100644 --- a/app/views/my/menus/_button.html.erb +++ b/app/views/my/menus/_button.html.erb @@ -1,7 +1,8 @@ -<%= tag.button class:"boxcar-menu-trigger input input--select center flex-inline align-center txt-normal", +<%= tag.button class:"boxcar-menu-trigger input input--select center flex-inline gap-half align-center txt-normal", data: { action: "click->dialog#open:stop keydown.j@document->hotkey#click keydown.meta+j@document->hotkey#click keydown.ctrl+j@document->hotkey#click", controller: "hotkey" } do %> <%= image_tag "logo.png" %> - BOXCAR + Fizzy + J <% end %> diff --git a/app/views/my/menus/_footer.html.erb b/app/views/my/menus/_footer.html.erb new file mode 100644 index 000000000..22cfc379d --- /dev/null +++ b/app/views/my/menus/_footer.html.erb @@ -0,0 +1,3 @@ +
+ Fizzy is designed, built, and backed by <%= icon_tag "37signals", class: "v-align-middle" %> 37signals. +
diff --git a/app/views/my/menus/_hotkeys.html.erb b/app/views/my/menus/_hotkeys.html.erb index b722cf3fa..1f6494c0d 100644 --- a/app/views/my/menus/_hotkeys.html.erb +++ b/app/views/my/menus/_hotkeys.html.erb @@ -1,4 +1,4 @@ -
+
<%= filter_hotkey_link "Home", root_path, 1, "home" %> <%= filter_hotkey_link "New board", new_collection_path, 2, "collection-add" %> <%= filter_hotkey_link "Assigned to me", cards_path(assignee_ids: [Current.user.id]), 3, "clipboard" %> diff --git a/app/views/my/menus/_menu.html.erb b/app/views/my/menus/_menu.html.erb index a22061f9a..26b0c291c 100644 --- a/app/views/my/menus/_menu.html.erb +++ b/app/views/my/menus/_menu.html.erb @@ -1,13 +1,12 @@ diff --git a/app/views/my/menus/_shortcut_notice.html.erb b/app/views/my/menus/_shortcut_notice.html.erb deleted file mode 100644 index 70f5f5fb7..000000000 --- a/app/views/my/menus/_shortcut_notice.html.erb +++ /dev/null @@ -1,5 +0,0 @@ -<% if platform.desktop? %> -
- Press J anytime to open this, esc to close, to move, enter to navigate. -
-<% end %> diff --git a/app/views/my/menus/_users.html.erb b/app/views/my/menus/_users.html.erb index 8fea7f8a3..b8ca2cb59 100644 --- a/app/views/my/menus/_users.html.erb +++ b/app/views/my/menus/_users.html.erb @@ -5,5 +5,6 @@ Add people <% end %> + <%= render partial: "my/menus/users/user", collection: user_filtering.users, as: :user %> <% end %> diff --git a/app/views/my/menus/show.html.erb b/app/views/my/menus/show.html.erb index 077139170..d5eaa1dd5 100644 --- a/app/views/my/menus/show.html.erb +++ b/app/views/my/menus/show.html.erb @@ -1,20 +1,7 @@ <%= turbo_frame_tag "my_menu", target: "_top" do %>
- <%= text_field_tag :search, nil, - type: "search", - role: "combobox", - placeholder: "Type to jump to a collection, person, place, or tag…", - class: "input input--transparent txt-small", - autofocus: true, - autocorrect: "off", - autocomplete: "off", - aria: { activedescendant: "" }, - data: { - "1p-ignore": "true", - filter_target: "input", - nav_section_expander_target: "input", - navigable_list_target: "input", - action: "input->filter#filter" } %> + <%= jump_field_tag %> +
<%= render "hotkeys" %> - -
- BOXCAR is designed, built, and backed by <%= icon_tag "37signals", class: "v-align-middle" %> 37signals. -
- <%= render "saved_filter", filters: @user_filtering.filters %> <%= render "collections", user_filtering: @user_filtering %> <%= render "tags", user_filtering: @user_filtering %> <%= render "users", user_filtering: @user_filtering %> <%= render "places", user_filtering: @user_filtering %> <%= render "accounts", user_filtering: @user_filtering %> - - <%= render "shortcut_notice" %> + <%= render "footer" %> <% end %> diff --git a/app/views/notifications/settings/_install.html.erb b/app/views/notifications/settings/_install.html.erb index be618ae0d..f8d664043 100644 --- a/app/views/notifications/settings/_install.html.erb +++ b/app/views/notifications/settings/_install.html.erb @@ -1,10 +1,10 @@ <% unless (platform.chrome? && !platform.ios?) || (platform.firefox? && !platform.android?) %>
-

<%= platform.safari? && platform.desktop? ? "…or install" : "Install " -%> BOXCAR as a web app.

+

<%= platform.safari? && platform.desktop? ? "…or install" : "Install " -%> Fizzy as a web app.

<% case when platform.edge? %>
    -
  1. Click <%= icon_tag "install-edge", alt: "the app available - install BOXCAR button" %>in the address bar.
  2. +
  3. Click <%= icon_tag "install-edge", alt: "the app available - install Fizzy button" %>in the address bar.
  4. Click Install.
<% when platform.chrome? && platform.android? %> @@ -23,13 +23,13 @@
  • Click Add to Dock….
  • <% when (platform.safari? || platform.chrome?) && platform.ios? %> -

    To receive push notifications in <%= platform.browser.capitalize %> for <%= platform.operating_system %>, you must first install BOXCAR as a web app.

    +

    To receive push notifications in <%= platform.browser.capitalize %> for <%= platform.operating_system %>, you must first install Fizzy as a web app.

    1. Tap <%= icon_tag "share", alt: "the share button" %>
    2. Tap Add to Home Screen.
    <% else %> -

    Some platforms require you to install BOXCAR as a web app to receive push notifications.

    +

    Some platforms require you to install Fizzy as a web app to receive push notifications.

    <% end %>
    <% end %> diff --git a/app/views/notifications/settings/_system.html.erb b/app/views/notifications/settings/_system.html.erb index 23741ee66..afe0efd45 100644 --- a/app/views/notifications/settings/_system.html.erb +++ b/app/views/notifications/settings/_system.html.erb @@ -12,19 +12,19 @@
    1. Click Start, then Settings.
    2. Go to System > Notification.
    3. -
    4. Click <%= icon_tag "switch", alt: "the toggle button" %> ON for BOXCAR.
    5. +
    6. Click <%= icon_tag "switch", alt: "the toggle button" %> ON for Fizzy.
    <% when (platform.firefox? || platform.chrome?) && platform.desktop? %>
      <% if platform.windows? %>
    1. Click Start, then Settings.
    2. Go to System > Notification.
    3. -
    4. Click <%= icon_tag "switch", alt: "the toggle button" %> ON for BOXCAR.
    5. +
    6. Click <%= icon_tag "switch", alt: "the toggle button" %> ON for Fizzy.
    7. <% else %>
    8. Click in the top left.
    9. Click System Settings….
    10. Click Notifications.
    11. -
    12. Click BOXCAR.
    13. +
    14. Click Fizzy.
    15. Click <%= icon_tag "switch", alt: "the allow notifications switch" %> to Allow notifications.
    16. <% end %>
    @@ -33,13 +33,13 @@
  • Click in the top left.
  • Click System Settings….
  • Click Notifications.
  • -
  • Click BOXCAR.
  • +
  • Click Fizzy.
  • Click <%= icon_tag "switch", alt: "the allow notifications switch" %> to Allow notifications.
  • <% when (platform.safari? || platform.chrome?) && platform.ios? %>
    1. Open the <%= icon_tag "gear", aria: { hidden: "true" } %> Settings app.
    2. -
    3. Scroll to and tap BOXCAR.
    4. +
    5. Scroll to and tap Fizzy.
    6. Tap Notifications.
    7. Tap <%= icon_tag "switch", alt: "the allow notifications switch button" %> to Allow Notifications.
    @@ -48,7 +48,7 @@
  • Open the <%= icon_tag "gear", aria: { hidden: "true" } %> Settings app.
  • Tap Notifications.
  • Tap App notifications.
  • -
  • Scroll to BOXCAR.
  • +
  • Scroll to Fizzy.
  • Tap <%= icon_tag "switch", alt: "the switch" %> to Allow Notifications.
  • <% else %> diff --git a/app/views/notifications/unsubscribes/show.html.erb b/app/views/notifications/unsubscribes/show.html.erb index b41fa6431..3e100b14d 100644 --- a/app/views/notifications/unsubscribes/show.html.erb +++ b/app/views/notifications/unsubscribes/show.html.erb @@ -1,9 +1,9 @@

    You’re unsubscribed

    -

    Thanks, <%= @user.first_name %>! BOXCAR won’t send you any more email notifications. If you change your mind you can turn them back on in <%= link_to "Notifications Settings", notifications_settings_path %>.

    +

    Thanks, <%= @user.first_name %>! Fizzy won’t send you any more email notifications. If you change your mind you can turn them back on in <%= link_to "Notifications Settings", notifications_settings_path %>.

    <%= link_to root_path, class: "btn btn--link margin-block-start" do %> <%= icon_tag "arrow-left" %> - Back to BOXCAR + Back to Fizzy <% end %>
    diff --git a/app/views/pwa/manifest.json.erb b/app/views/pwa/manifest.json.erb index c697f8eba..ff66cb7b3 100644 --- a/app/views/pwa/manifest.json.erb +++ b/app/views/pwa/manifest.json.erb @@ -1,5 +1,5 @@ { - "name": "<%= [ Account.sole.name, "BOXCAR", Rails.env.production? ? nil : Rails.env ].compact.join(" - ") %>", + "name": "<%= [ Account.sole.name, "Fizzy", Rails.env.production? ? nil : Rails.env ].compact.join(" - ") %>", "icons": [ { "src": "/app-icon-192.png", diff --git a/app/views/searches/_form.html.erb b/app/views/searches/_form.html.erb index 5c956c38f..f9a1b778a 100644 --- a/app/views/searches/_form.html.erb +++ b/app/views/searches/_form.html.erb @@ -1,5 +1,5 @@ -<%= form_with url: search_path, method: :get, class: "search__form flex align-center justify-center gap-half", data: { turbo_frame: "bar_content" } do |form| %> - <%= form.label :q, "Search BOXCAR", class: "font-weight-black txt-nowrap" %> +<%= form_with url: search_path, method: :get, class: "search__form flex align-center justify-center gap-half", data: { bar_target: "form", turbo_frame: "bar_content" } do |form| %> + <%= form.label :q, "Search Fizzy", class: "font-weight-black txt-nowrap" %> <%= text_field_tag :q, query_terms, class: "search__input input", type: "search", @@ -7,7 +7,8 @@ autocomplete: "off", autofocus: true, data: { - action: "keydown.enter->bar#showModalAndSubmit keydown.esc->bar#reset" } %> + bar_target: "searchInput", + action: "keydown.enter->bar#showModalAndSubmit:prevent keydown.esc->bar#reset" } %>