commits
Non-Arabica users (no feed registration and no Arabica records) now
get a 404 page instead of a full profile. Applies to both the full
page and the HTMX partial endpoint.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: fix like button only being clickable once per page render
Add dynamic OpenGraph and Twitter Card metadata support for brew pages
to enable rich social sharing previews.
Changes:
- Extend LayoutData with OG fields (OGTitle, OGDescription, OGImage, OGType, OGUrl)
- Add helper methods with fallbacks to site defaults
- Update layout.templ to render dynamic meta tags
- Add populateBrewOGMetadata() helper in handlers
- Pass PublicURL through handler config for absolute URLs
- Add comprehensive tests for OG metadata generation
When sharing brew links, previews now show:
- Bean name and origin as title
- Rating, tasting notes, and roaster in description
- Proper article type for social platforms
Likes are indexed for like counts but should not appear as standalone
feed items. Previously, they were filtered inside recordToFeedItem(),
which caused unnecessary warning logs. Now they're skipped earlier in
the processing loop, avoiding the warning entirely.
Fixes: 01KGGK6V4WMC
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
* feat: like records -- added ability to "like" feed items
* fix: sum water from pours when not set explicitly
When viewing a brew, if the water amount field is 0 (not set explicitly),
the display now calculates the total by summing water amounts from all pours.
This provides a more accurate display when users log individual pours but
don't set a total water amount.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
feat: add like button to brew view page
Added a like button on the brew view page, positioned in the same row
as the back button but on the right side. The button:
- Shows the current like count
- Allows authenticated users to toggle likes via HTMX
- Uses the existing LikeButton component
Added GetBrewRecordByRKey method to AtprotoStore to fetch brew records
with their AT Protocol metadata (URI and CID) needed for the like button.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: move like and share buttons
* refactor: move RecordType to lexicons package
- Create new internal/lexicons package for AT Protocol lexicon types
- Add RecordType type with constants for bean, brew, brewer, grinder, like, roaster
- Add String() and DisplayName() helper methods
- Update feed/service.go, firehose/index.go, and feed.templ to use lexicons.RecordType
- Remove magic strings throughout the codebase for type safety
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* feat: prevent like button requests when unauthenticated
- Modified LikeButton to conditionally add HTMX attributes based on IsAuthenticated
- Added IsAuthenticated prop to LikeButtonProps and SocialButtonsProps
- Updated FeedCard and FeedPartial to pass authentication state through component hierarchy
- Added TODO comment to implement login prompt instead of doing nothing when unauthenticated users click like
- Edit buttons already check isOwnProfile, so they only appear for authenticated owners
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Move static assets from web/static/ to top-level static/ directory
for a cleaner project structure. Update all references in:
- flake.nix
- default.nix
- justfile
- .gitignore
- .gitattributes
- internal/routing/routing.go
- deploy/Dockerfile
- CLAUDE.md
* feat: templ stuff
* feat: templ phase 2 - shared components
* feat: templ phase 3, page migration
* feat: modal alpine -> templ conversions
* feat: smooth page transitions
* feat: remove old template code
* fix: transition and modal click fixes
* feat: add cellular for work management
refactor: Refactor forms to use form components
Form components in forms.templ are defined but rarely used (only in brew_form.templ).
Modals and other forms use inline HTML instead.
Components available:
- TextInput
- NumberInput
- TextArea
- Select
- FormField
Acceptance criteria:
- All forms use form components
- Remove inline form HTML
- Consistent form styling
- Better maintainability
Cell: 01KGA7TZPQPY2JC3HFPVPKHNBQ
refactor: Consolidate three modal systems into one
Acceptance criteria:
- All modals use consistent implementation
- Better accessibility
- Reduced maintenance burden
Cell: 01KGA7S43ENCHDZQ9DXPGEA6W1
fix: Fix modal record creation not updating list
When creating a new entity (bean, grinder, brewer) from a modal on the brew form
page, the dropdown wasn't being updated with the new item until page refresh.
Root cause: The refreshEntityDropdown function tried to access Alpine's internal
_x_dataStack, which was unreliable. When falling back to ArabicaCache, it
refreshed the cache but never actually repopulated the dropdown elements.
Fix: In the fallback path, create a temporary dropdown manager, apply the fresh
data, and call populateDropdowns() to update the DOM. Also added a small delay
before auto-selecting to ensure the dropdown is fully populated.
Acceptance criteria:
- New records appear in list immediately after creation
- Auto-select newly created record
- No page refresh needed
perf: Reduce table flash on page load
Wrap table loading skeletons in a single animate-pulse container
instead of applying the animation to individual rows. This prevents
a flash where the table structure renders before the animation kicks in.
Changes:
- brew_list.templ: Wrap skeleton in outer animate-pulse div
- shared.templ: Fix LoadingSkeletonTable component similarly
This matches the pattern used in profile and manage pages which
load smoothly without visible flash.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix: fix broken back button on view brew page
* refactor: consolidate manage and profile tables into shared components
* style: use design system classes for terms page links
- Replace inline mail link styles with link-bold class
- Replace inline button styles with btn-primary class
- Ensures consistency with site design system
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
refactor: Replace inline card classes with Card component
card.templ defines Card component but many places use inline card classes instead.
Acceptance criteria:
- All card-style elements use Card component
- Remove inline card class usage
- Consistent card styling
- Component-based approach
Cell: 01KGA7TZRHNG2AVHBT91NJY4KD
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add AT Protocol explanation page
Add /atproto page explaining what the AT Protocol is and how Arabica uses it.
Update footer link to point to internal page instead of external atproto.com.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
On brew view page, if WaterAmount is 0 but pours exist,
calculate and display the sum of all pour water amounts.
- Fix non-authed feed to refresh when firehose becomes ready instead of
serving stale polling data until TTL expires
- Fix bean dropdown not updating after adding a bean via the brew form
by forcing a fresh fetch even when a background refresh is in progress
Co-Authored-By: Patrick Dewey <p@pdewey.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add firehose-based feed that consumes AT Protocol events via Jetstream,
replacing polling for dramatically improved performance:
- Add internal/firehose package with:
- config.go: Jetstream configuration and Arabica collection list
- index.go: BoltDB-backed feed index with multi-key indexes
- consumer.go: WebSocket consumer with reconnection and backoff
- adapter.go: Bridge between firehose and feed service interfaces
- Modify feed service to optionally use firehose index when available
- Add -firehose CLI flag to enable the feature
- Add graceful shutdown handling for firehose consumer
Benefits:
- Real-time feed updates (<1s latency vs 5min cache)
- Zero API calls per feed request (vs ~10N for N users)
- Automatic user discovery from firehose events
- Fallback to polling if firehose unavailable
Co-Authored-By: Claude <noreply@anthropic.com>
feat: full atproto integration
- It will be added in data export menu in settings in the future
Add dynamic OpenGraph and Twitter Card metadata support for brew pages
to enable rich social sharing previews.
Changes:
- Extend LayoutData with OG fields (OGTitle, OGDescription, OGImage, OGType, OGUrl)
- Add helper methods with fallbacks to site defaults
- Update layout.templ to render dynamic meta tags
- Add populateBrewOGMetadata() helper in handlers
- Pass PublicURL through handler config for absolute URLs
- Add comprehensive tests for OG metadata generation
When sharing brew links, previews now show:
- Bean name and origin as title
- Rating, tasting notes, and roaster in description
- Proper article type for social platforms
Likes are indexed for like counts but should not appear as standalone
feed items. Previously, they were filtered inside recordToFeedItem(),
which caused unnecessary warning logs. Now they're skipped earlier in
the processing loop, avoiding the warning entirely.
Fixes: 01KGGK6V4WMC
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
* feat: like records -- added ability to "like" feed items
* fix: sum water from pours when not set explicitly
When viewing a brew, if the water amount field is 0 (not set explicitly),
the display now calculates the total by summing water amounts from all pours.
This provides a more accurate display when users log individual pours but
don't set a total water amount.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
feat: add like button to brew view page
Added a like button on the brew view page, positioned in the same row
as the back button but on the right side. The button:
- Shows the current like count
- Allows authenticated users to toggle likes via HTMX
- Uses the existing LikeButton component
Added GetBrewRecordByRKey method to AtprotoStore to fetch brew records
with their AT Protocol metadata (URI and CID) needed for the like button.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: move like and share buttons
* refactor: move RecordType to lexicons package
- Create new internal/lexicons package for AT Protocol lexicon types
- Add RecordType type with constants for bean, brew, brewer, grinder, like, roaster
- Add String() and DisplayName() helper methods
- Update feed/service.go, firehose/index.go, and feed.templ to use lexicons.RecordType
- Remove magic strings throughout the codebase for type safety
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* feat: prevent like button requests when unauthenticated
- Modified LikeButton to conditionally add HTMX attributes based on IsAuthenticated
- Added IsAuthenticated prop to LikeButtonProps and SocialButtonsProps
- Updated FeedCard and FeedPartial to pass authentication state through component hierarchy
- Added TODO comment to implement login prompt instead of doing nothing when unauthenticated users click like
- Edit buttons already check isOwnProfile, so they only appear for authenticated owners
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat: templ stuff
* feat: templ phase 2 - shared components
* feat: templ phase 3, page migration
* feat: modal alpine -> templ conversions
* feat: smooth page transitions
* feat: remove old template code
* fix: transition and modal click fixes
* feat: add cellular for work management
refactor: Refactor forms to use form components
Form components in forms.templ are defined but rarely used (only in brew_form.templ).
Modals and other forms use inline HTML instead.
Components available:
- TextInput
- NumberInput
- TextArea
- Select
- FormField
Acceptance criteria:
- All forms use form components
- Remove inline form HTML
- Consistent form styling
- Better maintainability
Cell: 01KGA7TZPQPY2JC3HFPVPKHNBQ
refactor: Consolidate three modal systems into one
Acceptance criteria:
- All modals use consistent implementation
- Better accessibility
- Reduced maintenance burden
Cell: 01KGA7S43ENCHDZQ9DXPGEA6W1
fix: Fix modal record creation not updating list
When creating a new entity (bean, grinder, brewer) from a modal on the brew form
page, the dropdown wasn't being updated with the new item until page refresh.
Root cause: The refreshEntityDropdown function tried to access Alpine's internal
_x_dataStack, which was unreliable. When falling back to ArabicaCache, it
refreshed the cache but never actually repopulated the dropdown elements.
Fix: In the fallback path, create a temporary dropdown manager, apply the fresh
data, and call populateDropdowns() to update the DOM. Also added a small delay
before auto-selecting to ensure the dropdown is fully populated.
Acceptance criteria:
- New records appear in list immediately after creation
- Auto-select newly created record
- No page refresh needed
perf: Reduce table flash on page load
Wrap table loading skeletons in a single animate-pulse container
instead of applying the animation to individual rows. This prevents
a flash where the table structure renders before the animation kicks in.
Changes:
- brew_list.templ: Wrap skeleton in outer animate-pulse div
- shared.templ: Fix LoadingSkeletonTable component similarly
This matches the pattern used in profile and manage pages which
load smoothly without visible flash.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix: fix broken back button on view brew page
* refactor: consolidate manage and profile tables into shared components
* style: use design system classes for terms page links
- Replace inline mail link styles with link-bold class
- Replace inline button styles with btn-primary class
- Ensures consistency with site design system
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
refactor: Replace inline card classes with Card component
card.templ defines Card component but many places use inline card classes instead.
Acceptance criteria:
- All card-style elements use Card component
- Remove inline card class usage
- Consistent card styling
- Component-based approach
Cell: 01KGA7TZRHNG2AVHBT91NJY4KD
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add AT Protocol explanation page
Add /atproto page explaining what the AT Protocol is and how Arabica uses it.
Update footer link to point to internal page instead of external atproto.com.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
- Fix non-authed feed to refresh when firehose becomes ready instead of
serving stale polling data until TTL expires
- Fix bean dropdown not updating after adding a bean via the brew form
by forcing a fresh fetch even when a background refresh is in progress
Co-Authored-By: Patrick Dewey <p@pdewey.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add firehose-based feed that consumes AT Protocol events via Jetstream,
replacing polling for dramatically improved performance:
- Add internal/firehose package with:
- config.go: Jetstream configuration and Arabica collection list
- index.go: BoltDB-backed feed index with multi-key indexes
- consumer.go: WebSocket consumer with reconnection and backoff
- adapter.go: Bridge between firehose and feed service interfaces
- Modify feed service to optionally use firehose index when available
- Add -firehose CLI flag to enable the feature
- Add graceful shutdown handling for firehose consumer
Benefits:
- Real-time feed updates (<1s latency vs 5min cache)
- Zero API calls per feed request (vs ~10N for N users)
- Automatic user discovery from firehose events
- Fallback to polling if firehose unavailable
Co-Authored-By: Claude <noreply@anthropic.com>