Commit Graph

9085 Commits

Author SHA1 Message Date
Denis Švara a75d56d243 Merge branch 'main' into pins-api
* main: (127 commits)
  Align board name start so it doesn't get too big
  Clean up card/events header layout
  Sync email to Stripe when user changes email address (#2432)
  Fix IDOR in webhook activation endpoint (#2431)
  Add card reactions to API docs and reactions_url to card JSON (#2427)
  Remove unnecessary claude plan
  Allow boosts on cards (#2411)
  Revert "Fix notification click URL by using correct data property"
  Add migration to remove draft cards from search index
  Guard search indexing with searchable? check
  Forbid comments on draft cards
  prefactor: update search to use published cards
  Fix notification click URL by using correct data property
  Wait for service worker to be active before subscribing
  Fix stuck state when permission granted but no subscription
  Extract Card::Commentable
  Include arm64 build in Docker workflow
  Remove unnecessary `await` in push handler
  Correctly initialise WebPush connection (#2417)
  Update models, views, and fixtures for polymorphic reactions
  ...
2026-01-27 15:37:47 +01:00
Denis Švara 8e24988c6f Add a new Pins section to docs/API.md covering pin/unpin and the pinned cards list response. 2026-01-27 15:37:07 +01:00
Andy Smith 02527349f9 Merge pull request #2435 from basecamp/card-header-alignment
Card header alignment
2026-01-26 13:23:08 -06:00
Andy Smith 8cfcc6fca5 Align board name start so it doesn't get too big 2026-01-26 12:57:26 -06:00
Andy Smith 5beb413c41 Clean up card/events header layout 2026-01-26 12:53:01 -06:00
Jeremy Daer f2ff5af4f5 Sync email to Stripe when user changes email address (#2432)
* Sync owner email to Stripe when changed

When an account owner changes their email address, update the
corresponding Stripe customer record via a background job.

Also handles ownership transfers: when a user becomes the account
owner, their email is synced to Stripe.

Responsibility chain:
- User::NotifiesAccountOfEmailChange triggers on owner identity change
  or when a user becomes owner
- Account::Billing#owner_email_changed enqueues sync job
- Account::SyncStripeCustomerEmailJob performs the update with
  polynomial backoff retries
- Account::Subscription#sync_customer_email_to_stripe calls Stripe API

* Address PR feedback: error handling and test coverage

- Handle Stripe::InvalidRequestError in sync_customer_email_to_stripe
  (mirrors cancel method behavior for deleted customers)
- Add test for deactivated owner (owner with nil identity)
- Add test for deleted Stripe customer scenario
2026-01-25 14:20:39 -08:00
Jeremy Daer 4efa4da809 Fix IDOR in webhook activation endpoint (#2431)
The webhook activation controller was using account-scoped lookup
instead of board-scoped lookup, allowing users to reactivate webhooks
on boards they don't have access to.

This was an oversight when board-scoping was added to the main webhooks
controller - the activations controller was missed in that update.

The fix adds the BoardScoped concern to properly restrict webhook
activation to boards the user has explicit access to.
2026-01-24 17:06:12 -08:00
Mike Dalessio 16745e89d1 Add card reactions to API docs and reactions_url to card JSON (#2427)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 16:55:50 -05:00
Mike Dalessio c822639260 Remove unnecessary claude plan 2026-01-23 13:27:04 -05:00
Mike Dalessio cb61b36715 Allow boosts on cards (#2411)
* Add reactions association to Card model

Adds `has_many :reactions` to Card, matching the implementation in Comment.
This allows cards to be reacted to directly with emoji reactions.

The association:
- Orders reactions chronologically
- Uses polymorphic `:reactable` interface
- Deletes reactions when card is destroyed

Includes test coverage for the new association.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add reactions UI to card detail view

- Create Cards::ReactionsController with turbo stream responses
- Add card reactions views (reactions list, new form, menu partial)
- Position reaction button flush-right when no reactions exist
- Show reactions on bottom-left when present, with button inline
- Handle narrow viewports by flowing button below meta section
- Add controller tests for card reactions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add boost count to card preview

Display reaction count alongside comment count on card tiles in board
columns. Introduces a .card__counts wrapper to group both counts with
proper positioning on all viewport sizes.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Consolidate reaction views for cards and comments

Extract shared views to app/views/reactions/ using polymorphic routing.
Both card and comment reactions now render the same partials, with a
helper to compute the correct path prefix for nested routes.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix emoji picker width in card reactions

Override .card .popup { inline-size: 260px } for the reaction popup
so the emoji grid displays without horizontal scrollbar.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Hide boost count where comment count is hidden

Add .card__boosts alongside .card__comments in:
- Tray view (display: none)
- Cards with background images (opacity toggle on hover)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix reactions button overlap with zoom button in card footer

When a card has a background image, the footer contains both a reactions
button and a zoom button. Previously they would overlap because the
reactions button was absolutely positioned to the bottom-right.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Tighten up previews

* Move card reactions from meta partial to container

Fixes two issues:

1. Reactions were duplicated each time a card was assigned because the
   turbo stream replaced the meta div with the full perma/meta partial,
   which included reactions outside the replaced element.

2. Draft cards incorrectly showed the boost button because the meta
   partial was shared between published and draft containers.

Moving reactions to _container.html.erb (published cards only) fixes
both issues and keeps the meta partial focused on meta content.

Fixes https://app.fizzy.do/5986089/cards/3837
Fixes https://app.fizzy.do/5986089/cards/3835

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* WIP adjust perma page

* Position and style the footer

* Render the stamp in the card body instead of the footer

* Fix undefined local variable in public cards view

Changed `card` to `@card` when rendering the stamp partial.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Preload card reactions to avoid N+1 queries

Include reactions and their reacters in the preloaded scope, matching
what we already do for comments.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add model tests for card and comment reaction cleanup

Test that:
- Creating a card reaction touches the card's last_active_at
- Reactions are deleted when their parent comment is destroyed
- Reactions are deleted when their parent card is destroyed

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Minor improvements to reactions code

- Use size instead of count in boosts partial to avoid extra SQL query
  on already-loaded collection
- Add else clause to reaction_path_prefix_for to raise ArgumentError
  for unknown reactable types instead of silently returning nil

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Bump specificity of reaction popup in the card perma

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Andy Smith <andy@37signals.com>
2026-01-23 13:25:55 -05:00
Rosa Gutierrez ffb2a0b82a Revert "Fix notification click URL by using correct data property"
This reverts commit 4f2308d380.

This was from another branch, shoudln't have been included here.
2026-01-23 19:25:23 +01:00
Mike Dalessio dfa4875a13 Merge pull request #2418 from basecamp/filter-drafts-in-search
Exclude draft cards from search index
2026-01-23 12:17:25 -05:00
Mike Dalessio a9c6bc2a28 Add migration to remove draft cards from search index
One-time script to clean up any search records that were indexed
before the searchable? guard was added.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 12:13:10 -05:00
Mike Dalessio 0d138df952 Guard search indexing with searchable? check
Only index cards and comments if their card is `published?`, using a
new method `searchable?` that is implemented on both Card and Comment.
This prevents draft cards and their comments from being indexed.

When drafts are published, the existing `update_in_search_index`
callback creates the record via `upsert!`, or else ensures unpublished
records are removed from the index.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 12:13:08 -05:00
Mike Dalessio f6211dbacc Merge pull request #2423 from basecamp/forbid-comments-on-draft-cards
Forbid comments on draft cards
2026-01-23 11:40:46 -05:00
Mike Dalessio 9aa15e9fb2 Forbid comments on draft cards
- Add Card#commentable? method that returns true only for published cards.
- CardsController#create ensures that the card is commentable.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 11:28:26 -05:00
Mike Dalessio bb2d3a59b5 prefactor: update search to use published cards
We're about to make a change to assert that draft cards are not added
to the search index, and so let's make the intention behind these
tests clear first.
2026-01-23 11:07:43 -05:00
Rosa Gutierrez 4f2308d380 Fix notification click URL by using correct data property
The web push payload sends the URL in data.url but the service
worker was looking for data.path, resulting in undefined URLs.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 16:38:45 +01:00
Rosa Gutierrez 26d5701217 Wait for service worker to be active before subscribing
The push subscription requires an active service worker. When
registering a new service worker, we now wait for it to become
active using navigator.serviceWorker.ready before attempting
to subscribe to push notifications.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 16:38:45 +01:00
Rosa Gutierrez c650cba179 Fix stuck state when permission granted but no subscription
When you had already granted notification permission but hadn't
completed the subscription flow (no service worker or no push
subscription), the UI showed neither the subscribe button nor the
enabled state, leaving you stuck with no way to subscribe, and wrong
instructions to fix it.

Instead, let's just show the button to allow you to subscribe.
2026-01-23 16:38:45 +01:00
Mike Dalessio 1aea6850f0 Extract Card::Commentable 2026-01-23 10:30:12 -05:00
Kevin McConnell 40946930d0 Merge pull request #2422 from basecamp/arm-build
Add arm64 architecture to Docker image
2026-01-23 14:38:13 +00:00
Kevin McConnell e27456b8f8 Include arm64 build in Docker workflow 2026-01-23 14:33:31 +00:00
Kevin McConnell 97be8b593f Merge pull request #2420 from basecamp/move-async-push-operation
Remove unnecessary `await` in push handler
2026-01-23 13:16:47 +00:00
Kevin McConnell d7c9c4f3b0 Remove unnecessary await in push handler
We were `await`ing a synchronous method here, which will have the effect
of deferring its execution, and thus also the `event.waitUntil` call. We
want the latter to happen before control returns from the event.
Removing the unnecessary `await` solves this.

This seems unlikely to be a problem in practice, but all the same, it's
not right :)
2026-01-23 12:12:22 +00:00
Kevin McConnell 23fe40fa81 Correctly initialise WebPush connection (#2417)
The `ipaddr` arg here is being interpreted as a positional arg (since
the keyword arg doesn't exist), which results in an invalid connection
object. This was causing push notifications to silently fail.

We should initialise the property on the object instead.
2026-01-22 12:03:02 -08:00
Mike Dalessio 8ba6a97014 Merge pull request #2416 from basecamp/introduce-reactable
Make `Reaction` polymorphic with `reactable` association
2026-01-22 12:46:25 -05:00
Mike Dalessio ffab4cad6f Update models, views, and fixtures for polymorphic reactions
- Reaction: belongs_to :reactable (polymorphic) instead of :comment
- Comment: has_many :reactions with as: :reactable
- Views: use reaction.reactable instead of reaction.comment
- Fixtures: use polymorphic syntax for reactable association

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 12:29:54 -05:00
Mike Dalessio 1570c16f67 Make Reaction polymorphic
Add migration to replace comment_id with polymorphic reactable
association (reactable_type, reactable_id), enabling reactions on both
comments and cards.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 12:29:54 -05:00
Denis Švara 5384116f11 Rename the helper to user_pins and extract a pins_limit method. 2026-01-22 16:44:30 +01:00
Andy Smith 49f45b6645 Merge pull request #2410 from basecamp/handle-single-board-filters
Return to Board page when clearing filters
2026-01-21 15:32:43 -06:00
Andy Smith f9f638a9a7 Return to Board page when clearing filters 2026-01-21 15:05:47 -06:00
Andy Smith 5b94248169 Merge pull request #2409 from basecamp/improve-filter-settings-ux
Improve filter settings ux
2026-01-21 14:39:48 -06:00
Andy Smith 6c8d333bc9 Bump card perma z-index when dialog is open 2026-01-21 14:35:30 -06:00
Andy Smith eccef3b19c Only submit on blur if the input has a value 2026-01-21 14:33:19 -06:00
Andy Smith 9b1dd0bcd9 Merge pull request #2401 from nqst/modal-scroll-block
Prevent page scrolling when modal dialog is open
2026-01-21 14:02:20 -06:00
Jeremy Daer 783faf3c95 Make the CLAUDE.md stub less obtrusive
(Until Claude supports AGENTS.md natively, we need to keep the stub.)
2026-01-21 11:03:06 -08:00
Andy Smith 72123dc251 Merge pull request #2404 from nqst/dialog-viewport-spacing
Prevent modal dialogs from touching viewport edges
2026-01-21 11:23:01 -06:00
Kevin McConnell 74aca0867f Merge pull request #2406 from basecamp/search-content-limit
Only index up to 32KB of search content
2026-01-21 10:05:24 +00:00
Kevin McConnell 468fe5f16b Only index up to 32KB of search content
To prevent overflowing the columns in the search index tables (and also
just to avoid creating massive indexes), let's limit the searchable
content to the first 32KB.
2026-01-21 09:52:23 +00:00
Alexander Zaytsev 73cf1dede4 Use inline spacing variable 2026-01-20 20:51:01 +01:00
Denis Švara 90a7b15db3 Move pins scope into a variable. Keep web limit to 20 and set JSON to 100. 2026-01-20 12:32:45 +01:00
Alexander Zaytsev bfea02ebd0 Actually make it affect larger screens too
And remove a magic number
2026-01-20 10:54:39 +01:00
Alexander Zaytsev aba88cdc3f Use CSS variable for panel size in delete dialogs 2026-01-20 10:40:29 +01:00
Alexander Zaytsev aea90f4b50 Add viewport padding to dialogs on mobile 2026-01-20 10:40:04 +01:00
Alexander Zaytsev 772b378668 Merge branch 'main' into modal-scroll-block 2026-01-19 22:19:51 +01:00
Jay Ohms 3fa850da3d Merge pull request #2307 from basecamp/mobile/bridge-components
Mobile: Bridge components
2026-01-19 15:59:16 -05:00
Jay Ohms cd6478d05b Merge branch 'main' into mobile/bridge-components
* main:
  Fix notification broadcast test for turbo-rails 2.0.21
  Update `turbo-rails` to get latest Turbo version
2026-01-19 15:23:17 -05:00
Alexander Zaytsev fa4e77f35e Prevent page scrolling when modal dialog is open 2026-01-19 20:46:49 +01:00
Rosa Gutierrez 80e0a03a40 Merge pull request #2399 from basecamp/update-turbo
Update `turbo-rails` to get latest Turbo version
2026-01-19 20:41:43 +01:00