Skip to content

[codex] Make hearting optimistic and animated in every context#642

Draft
zachlatta wants to merge 1 commit into
mainfrom
codex/heart-animation-messaging
Draft

[codex] Make hearting optimistic and animated in every context#642
zachlatta wants to merge 1 commit into
mainfrom
codex/heart-animation-messaging

Conversation

@zachlatta

@zachlatta zachlatta commented Jun 23, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds a Stimulus like controller so devlog hearts update optimistically, animate immediately, and persist with JSON requests instead of navigating/reloading inside feed/profile/project/devlog contexts.
  • Keeps the no-JS/Turbo Stream fallback intact while rendering both outline and filled hearts for instant state changes.
  • Namespaces the filled-heart SVG gradient per likeable record so multiple liked hearts on one page render correctly.
  • Adds controller coverage for JSON create/destroy responses and the existing Turbo Stream response.

Scope guard

This branch is rebuilt from current origin/main and intentionally excludes the unrelated improved-agent-experience commits. Checked absent from codex/heart-animation-messaging: bca38f86, 0799caf6, 18f39f9b.

Changed files are limited to the like implementation/tests; the flow GIFs below are hosted on cdn.hackclub.com.

AGENTS.md / repo conventions

  • No DB migrations, schema changes, generators, admin surfaces, PaperTrail audit flows, encrypted fields, or Lockbox/blind-index changes are involved.
  • Authorization remains through the existing LikePolicy; the JSON response path is added inside the already-authorized create/destroy actions.
  • Tests use Minitest under test/controllers; no RSpec was added.
  • Frontend code stays in app/javascript/controllers and is registered through the Stimulus manifest used by the esbuild pipeline.
  • SCSS stays on the existing BEM-style .like-button block, uses classes instead of inline styles, and references Stardance CSS variables / brand tokens rather than new hardcoded theme colors.
  • The existing like partial and Turbo Stream fallback are reused instead of replacing the whole like system; no Flavortown styling or dead code is carried forward.
  • GIF evidence is hosted on Hack Club CDN; no CDN key or generated binary artifact is stored in the repo.

Validation

  • git diff --check origin/main...HEAD
  • RAILS_ENV=test DATABASE_URL=postgresql://postgres:pass@localhost:5432/stardance_test mise exec ruby@ruby-3.4.3 -- bin/rails test test/controllers/likes_controller_test.rb
  • mise exec ruby@ruby-3.4.3 -- bundle exec rubocop app/controllers/likes_controller.rb app/helpers/likes_helper.rb test/controllers/likes_controller_test.rb
  • mise exec ruby@ruby-3.4.3 -- bundle exec erb_lint app/views/likes/_button.html.erb
  • ./node_modules/.bin/prettier --check app/javascript/controllers/like_controller.js app/javascript/controllers/index.js app/assets/stylesheets/components/_like_button.scss
  • npm exec -- esbuild app/javascript/application.js --bundle --sourcemap --format=esm --outdir=/tmp/stardance-heart-build --public-path=/assets
  • Playwright/Chrome runtime verification against local Rails test server: home feed, project page, profile feed, and devlog detail each POSTed /devlogs/:id/like, stayed on the same URL, set aria-pressed="true", applied the liked class, and showed count 1.
  • Each CDN GIF URL below was verified as 200 image/gif.

Note: Rails boot emitted the existing local MJML 5 binary warning during test/server commands.

Flow GIFs

Home feed

Home feed heart flow

Project page

Project page heart flow

Profile feed

Profile feed heart flow

Devlog detail

Devlog detail heart flow

The like button uses one shared partial everywhere but relied on
button_to + Turbo Stream, which only updates in place when Turbo Drive
is active. Inside target="_top" Turbo Frames (profile feed, paginated
home feed) and turbo:false regions, clicking the heart fell through to
a full page reload with no animation.

Add a `like` Stimulus controller that intercepts the submit, updates the
UI instantly (so the heart animation always plays) and persists via a
background fetch, so liking never navigates regardless of the Turbo
context. A JSON branch on LikesController returns the authoritative
{ liked, count } for the controller to reconcile against; button_to +
Turbo Stream stay as the no-JS fallback.

Also fix the filled heart rendering empty when several liked hearts
share a page: the gradient id in like-fill.svg is now namespaced per
record (LikesHelper#like_fill_icon), so each heart references its own
gradient instead of the first (display:none) match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@zachlatta zachlatta force-pushed the codex/heart-animation-messaging branch from 4b31c42 to e36d8fa Compare June 23, 2026 01:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant