commits
Add ViewerState class to represent the viewer's relationship with posts:
- vote: direction ("up", "down", or null)
- voteUri: AT-URI of the vote record
- saved: bookmark status
- tags: user-applied tags
This enables the feed to include viewer-specific state from the backend,
allowing proper initialization of vote UI state on feed refresh.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Major OAuth architecture rework - delegate complexity to backend.
Key changes:
- Add CovesSession model for simplified sealed tokens
- Add CovesAuthService for backend-delegated OAuth
- Update OAuth config for private-use URI scheme (RFC 8252)
- Add automatic token refresh on 401 responses
- Remove atproto_oauth_flutter package (~14K lines)
The backend now handles all OAuth complexity (DPoP, PKCE, token exchange)
and returns opaque sealed tokens that the client simply stores and sends.
- Remove unused imports in test files
- Add assertion on unused variable in singleton test
- Clean up redaction test file (remove unused mocks)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Auto-format all Dart files per CODE_QUALITY_GUIDE.md standards.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update feed_screen_test.dart for the new OAuth patterns.
iOS changes:
- Add Runner.entitlements for associated domains
- Enable Universal Links (applinks:coves.social)
- Reference entitlements in Xcode project
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove the client-side OAuth implementation now that auth is delegated
to the Coves backend. This eliminates ~14,000 lines of complex OAuth
code that handled:
Removed oauth_service.dart:
- Complex OAuthSession management
- Client-side token refresh
- DPoP key generation and proof signing
- PKCE code verifier/challenge generation
Removed atproto_oauth_flutter package:
- DPoP implementation (fetch_dpop.dart)
- Identity resolution (did/handle resolvers)
- OAuth server discovery and metadata
- Token exchange and refresh logic
- Cryptographic key management
- Session state persistence
The backend now handles all of this complexity, returning opaque
sealed tokens that the client simply stores and sends.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Wire up VoteService with the new session getter and auth callbacks
for the backend-delegated OAuth architecture.
Key changes:
- VoteService now uses sessionGetter instead of direct OAuthSession
- Add tokenRefresher callback for automatic 401 recovery
- Add signOutHandler callback for failed refresh cleanup
- Remove OAuthService initialization (deleted)
The new flow ensures votes go through the Coves backend which
has the DPoP keys needed to write to user PDSs.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update FeedProvider and CommentsProvider to work with the simplified
session model and backend-proxied auth flow.
Key changes:
- Use VoteService callback pattern instead of OAuthSession
- Remove direct PDS URL handling
- Simplify test mocks to match new API
Provider updates:
- FeedProvider: Use token getter instead of session getter
- CommentsProvider: Same simplification
Test updates:
- Update mocks to use CovesSession instead of OAuthSession
- Remove PDS URL getter mocks
- Simplify vote service setup in tests
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update CovesApiService and VoteService with automatic 401 handling
and token refresh. With sealed tokens, the backend must proxy all
authenticated requests to user PDSs.
CovesApiService changes:
- Add tokenGetter, tokenRefresher, signOutHandler callbacks
- Dio interceptor for fresh token on each request
- Automatic retry on 401 with token refresh
- Prevent infinite loops with retried flag
- Sign out user if refresh fails
VoteService changes:
- Switch from direct PDS writes to backend-proxied votes
- Backend unseals token and uses stored DPoP keys
- Same 401 retry pattern as CovesApiService
- Remove OAuthSession dependency (was for DPoP)
New tests:
- Token refresh on 401 scenarios
- Retry prevention for refresh endpoint
- Sign out on failed refresh
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Simplify AuthProvider by delegating OAuth operations to the new
CovesAuthService. The provider now focuses on state management while
the service handles authentication logic.
Key changes:
- Use CovesSession instead of OAuthSession
- Simplified token access (sealed tokens are opaque)
- Dependency injection support for testing
- Token refresh delegated to CovesAuthService
Removed:
- Complex session getter with DPoP key management
- Direct PDS URL handling (backend proxies requests)
- Manual OAuth state machine management
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Switch from development callback URL to proper atproto-compliant
private-use URI scheme (RFC 8252):
- Scheme: social.coves:/callback (single slash per spec)
- Works on both Android and iOS without Universal Links complexity
Platform changes:
- Android: Update CallbackActivity intent filter scheme
- iOS: Update CFBundleURLSchemes in Info.plist
- Remove taskAffinity from MainActivity (not needed)
Dependencies:
- Add flutter_web_auth_2 for browser-based OAuth
- Remove atproto_oauth_flutter path dependency (to be deleted)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
New authentication service that delegates OAuth complexity to the Coves
backend. Instead of managing DPoP keys, PKCE, and token exchange client-side,
the backend handles everything and returns sealed tokens.
Key features:
- Browser-based OAuth via flutter_web_auth_2
- Secure token storage per environment (prevents cross-env token reuse)
- Mutex pattern for concurrent token refresh handling
- Handle/DID validation with Bluesky profile URL extraction
- Singleton pattern with test instance creation
The backend's /oauth/mobile/login endpoint handles:
- Handle → DID resolution
- PDS discovery
- PKCE/DPoP key generation
- Token exchange and sealing (AES-256-GCM)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Simplified session model that works with the Coves backend's sealed token
architecture. The backend handles all OAuth complexity (DPoP, PKCE, token
refresh) and gives us an opaque AES-256-GCM encrypted token.
Key features:
- Parse session from OAuth callback URI (RFC 8252 private-use scheme)
- JSON serialization for secure storage persistence
- Immutable with copyWithToken for refresh operations
- Proper redaction of sensitive data in toString()
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add Android productFlavors (dev: social.coves.dev, prod: social.coves)
- Create iOS flavor xcconfig files for future scheme setup
- Update EnvironmentConfig to support flavor-based environment detection
- Add VSCode launch configurations for easy flavor switching
- Update app icon to lil_dude mascot with proper adaptive icon padding
Dev flavor points to local server, prod flavor points to coves.social.
Both apps can be installed side-by-side on the same device.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Store timeout timer reference and cancel it in finally block to prevent
stale timer from firing after operation completes
- Check if stream controller is closed before adding cancellation event
to prevent "Cannot add event after closing" error
Add author information to reply screen for consistency with detail view:
- Enable showAuthorFooter in PostCard configuration
- Shows author handle and timestamp above post title
- Provides context about who created the original post
- Maintains same enhanced typography as detail view
This ensures users see clear attribution when composing replies,
matching the enhanced information hierarchy of the detail view.
Changes:
- Set showAuthorFooter: true in PostCard configuration
- Matches detail view settings: 20px title, 16px text, 280px embeds
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Enhance post detail screen with better organization and interaction:
Comment Navigation:
- Wire up dual comment button behavior:
- Input field opens reply composer
- Comment count button scrolls to comments section
- Add _scrollToComments() method with smooth animation
- Uses GlobalKey on CommentsHeader for precise scrolling
Visual Improvements:
- Add 1px border divider before comments section
- Replace blank spacing with clear visual separator
- Matches feed/comment divider styling for consistency
- 16px vertical margins for breathing room
Content Organization:
- Enable author footer in detail view (showAuthorFooter: true)
- Shows author info and timestamp above post title
- Enhanced typography: 20px title, 16px text, 1.6 line height
- Larger embeds (280px) for better content visibility
Changes:
- Add _commentsHeaderKey GlobalKey for scroll targeting
- Add _scrollToComments() method with Scrollable.ensureVisible
- Use onCommentInputTap and onCommentCountTap callbacks
- Add Container divider with AppColors.border
- Enable showAuthorFooter parameter
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Improve post card typography and information hierarchy:
Feed View:
- Increase text opacity from 70% to 85% for better readability
- Maintains visual hierarchy while improving scannability
Detail View:
- Add author info above title (avatar + handle + timestamp)
- Show when post was created (e.g., "2h ago")
- Layout: [Avatar] @username • 2h ago
- Provides clear context about post author and recency
Typography Enhancements:
- Add configurable title font size and weight parameters
- Add configurable text font size, line height, and embed height
- Enable full customization for different view contexts
Changes:
- Add showAuthorFooter parameter to PostCard
- Add _buildAuthorFooter() method with avatar, handle, timestamp
- Add _buildAuthorFallbackAvatar() for missing avatars
- Move author info to appear before title in detail views
- Update text opacity: alpha: 0.7 → 0.85 in feed view
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Split comment button functionality into two separate actions:
- Left button (comment input field): Opens reply composer
- Right button (comment count): Scrolls to comments section
This provides contextual behavior - users can either write a new
comment or quickly navigate to view existing comments.
Changes:
- Add onCommentInputTap and onCommentCountTap callbacks
- Maintain backward compatibility with deprecated onCommentTap
- Update documentation to clarify each button's purpose
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
PostCard enhancements:
- Add showBorder prop to control border independently from showHeader
- Fix duplicate border in post detail by passing showBorder: false
Post detail screen integration:
- Connect comment button to new reply screen
- Add authentication check before opening composer
- Implement comment submission handler (TODO: atProto integration)
- Navigate to reply screen with full post context
Fixes duplicate border issue where post detail showed extra divider
between post content and comments when showHeader: false was used.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements full-screen comment/reply composition UI with:
- ReplyScreen: Full-screen interface with Thunder-style keyboard handling
- CommentComposer: Reusable inline comment widget
- Post/comment context preview while composing
- Mention and image upload buttons (coming soon)
- Proper keyboard handling and auto-scroll
- Loading states and error handling
Features:
- Natural scrolling without fixed split ratios
- Manual keyboard margin for smooth transitions
- Text selection and copy/paste enabled
- Haptic feedback on submission
- Auto-dismiss banners for coming soon features
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Resolved all 96 flutter analyze issues by applying automated fixes and
manual corrections to meet Flutter/Dart best practices.
Changes:
- Auto-formatted 15 files with dart format
- Applied 82 automated fixes with dart fix --apply
- Fixed BuildContext async gap in post_detail_screen
- Resolved 18 line length violations (80 char limit)
- Escaped HTML angle brackets in doc comments
- Removed unused imports
- Added const constructors where applicable
- Fixed test issues: unawaited futures, cascade invocations, bool params
Result: flutter analyze now reports 0 issues found.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Auto-generated files updated after adding share_plus dependency
and running flutter pub get.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Major redesign of the post detail view with better visual hierarchy,
community branding, and interactive elements. Introduces a new action
bar component for post interactions.
New PostActionBar widget:
- Dedicated action bar for upvote, downvote, share, and comment actions
- Consistent with feed card actions but optimized for detail view
- Haptic feedback on all interactions
- Proper vote state management
Post detail improvements:
- Custom app bar showing community avatar and styled handle
- Better community branding with fallback avatars
- Removed redundant post card display in detail view
- Cleaner layout with post content displayed directly
- Fixed bottom navigation bar spacing issues
- Integrated share functionality with native share sheet
Visual enhancements:
- Styled community handles with color-coded parts (!community@instance)
- Circular community avatars with fallback to first letter
- Improved spacing and padding throughout
- Better error and loading states
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Improve the comments sort UI with intuitive icons and better visual
feedback. Each sort option now has a matching icon for easier recognition.
Changes:
- Added icons for each sort type (🔥 hot, ✨ top, 🆕 new)
- Show checkmark next to currently selected sort option
- Improved popup menu styling with rounded corners
- Better spacing and alignment in sort dropdown
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add optional parameters to control visibility of header and actions in
PostCard widget. This allows the same component to be reused in different
contexts (feed vs detail view) with appropriate customization.
Changes:
- Added showActions parameter (default: true) to toggle action buttons
- Added showHeader parameter (default: true) to toggle community/author info
- Adjusted margins and padding based on header visibility
- Maintained backward compatibility with default values
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace placeholder share button with actual share functionality using
the share_plus package. Users can now share posts with the post title
and URI through their device's native share sheet.
Changes:
- Added haptic feedback on share button tap for better UX
- Share includes post title (or fallback text) and post URI
- Uses native share sheet for platform-appropriate sharing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace hardcoded seedColor with AppColors.primary to ensure Material
widgets and custom components use the same accent color. This creates
a single source of truth for the app's primary color.
Benefits:
- Material widgets (AppBar, Dialogs, etc.) now match custom components
- Changing AppColors.primary updates the entire color scheme
- Eliminates visual inconsistencies between theme and custom colors
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace the old shark-themed logo with new branding featuring:
- lil_dude.svg: Character mascot (120x120)
- coves_bubble.svg: Brand text/wordmark (180x60)
The new logo layout stacks the character above the brand text with
appropriate spacing. Removed old shark logo files and references.
Also updated landing screen to:
- Remove "Coves" text heading (now part of logo)
- Use new SVG assets with proper sizing
- Update "Create account" button to show "coming soon" snackbar
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace all hardcoded orange color values with AppColors.primary constant
for better maintainability and easier theme updates.
Changes:
- PrimaryButton: Use AppColors.primary for background, shadow, and ripple
- LoginScreen: Use AppColors.primary for input focus border and help link
- Placeholder screens: Use AppColors.primary for icon colors
- MainShellScreen: Use AppColors.primary for selected nav item color
This makes it easier to update the accent color across the entire app
by changing a single constant.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update app branding with new name and Bluesky-style icon system. Replace Material icons with custom SVG icons for cleaner navigation UI. Generate adaptive launcher icons from Coves shark logo with proper safe zones.
Key changes:
- Rename app from "Coves Flutter" to "Coves" on Android and iOS
- Add BlueSkyIcon widget with custom SVG icon support
- Implement Bluesky-style navigation icons (home, search, plus, bell, person)
- Generate adaptive launcher icons with flutter_launcher_icons
- Configure Android adaptive icons with #0B0F14 background
- Bump version to 1.0.0+2
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add communityName color to AppColors for threading indicators
- Update dart:async import to use official unawaited function
- Regenerate feed provider test mocks for latest dependencies
- Remove unused imports from test files
Minor code quality improvements following Dart best practices.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Main.dart changes:
- Add CommentsProvider to app-level provider tree
- Use ChangeNotifierProxyProvider2 for proper dependency injection
- Integrate with AuthProvider and VoteProvider
- Add NotFoundError screen for missing post routes
- Ensure proper provider lifecycle management
PostCard changes:
- Add disableNavigation parameter to prevent recursive navigation
- Fix issue where tapping post on detail screen creates infinite stack
- Maintain backward compatibility (defaults to clickable)
Architecture improvements:
- Follows existing FeedProvider pattern for consistency
- Proper provider disposal handled by framework
- Hot reload support via provider reuse pattern
- No manual provider creation in widgets
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Comment model tests (12 test cases):
- JSON parsing with valid and invalid data
- Null handling and edge cases
- Nested reply structures
CommentsProvider tests (25 test cases):
- loadComments success and error scenarios
- Pagination logic with cursor handling
- Sort option changes and pending refresh mechanism
- Auth state change handling
- Time update mechanism with ValueNotifier
- Error recovery and rollback
CovesApiService tests:
- getComments endpoint success cases
- Error handling (404, 500, network errors)
- Timeout scenarios
- Response parsing with viewer state
All tests use proper mocking with mockito and achieve >80%
code coverage for new comment features.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
LoadingErrorStates widgets:
- FullScreenLoading: Full-screen loading spinner for initial loads
- FullScreenError: Full-screen error with title, message, and retry
- InlineLoading: Inline loading indicator for pagination
- InlineError: Inline error with retry for pagination
- NotFoundError: User-friendly not-found screen with back navigation
ErrorMessages utility:
- Centralized error message transformation
- Converts technical errors to user-friendly messages
- Handles network errors, timeouts, HTTP status codes
- Provides consistent error UX across the app
All widgets follow Material Design patterns and maintain
consistent styling with AppColors theme.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Full post detail view with nested comment display
- Pull-to-refresh support for comments
- Sort dropdown integration (Hot/Top/New)
- Infinite scroll pagination with scroll listener
- Loading and error states with retry functionality
- Performance optimized with ValueListenableBuilder
- Prevents unnecessary rebuilds via _PostHeader and _CommentItem wrappers
Features:
- Post card displayed without navigation (disableNavigation flag)
- Real-time comment sorting with error recovery
- Proper safe area handling (top and bottom)
- Error snackbar with retry action on sort failures
- NotFoundError screen for missing posts
- Integrates with CommentsProvider and VoteProvider
Performance optimizations:
- Timer updates only rebuild time-dependent widgets
- Post header doesn't rebuild when comments change
- Individual comment items rebuild independently
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
CommentCard:
- Display individual comments with author info and content
- Colorful threading indicators (6-color palette) showing nesting depth
- Optimistic voting via VoteProvider integration
- Right-aligned vote button with proper accessibility labels
- Custom painter for vertical threading lines (2px stroke, 6px spacing)
- Dynamic left padding based on nesting depth
CommentThread:
- Recursive rendering of nested comment replies
- Proper depth tracking and max depth limiting
- "Load more replies" button support
CommentsHeader:
- Comment count display with pluralization
- Sort dropdown (Hot/Top/New) with visual feedback
- Empty state handling
Visual features:
- Threading lines extend through full comment height
- Border dividers respect threading indicator spacing
- Subtle author name and timestamp styling (50% opacity)
- Consistent spacing (6px per level + 14px base padding)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add comprehensive state management for comments with pagination
- Implement sort options (hot/top/new) with pending refresh mechanism
- Support optimistic vote integration via VoteProvider
- Add time update mechanism with ValueNotifier for efficient rebuilds
- Handle auth state changes and automatic vote state loading
- Implement error recovery with rollback on sort change failures
Features:
- Cursor-based pagination for infinite scroll
- Automatic refresh scheduling when sort changes during loading
- Clean separation of concerns with proper provider lifecycle
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add CommentView, ThreadViewComment, and CommentsResponse models
- Implement getComments API endpoint in CovesApiService
- Support optional Dio injection for testing
- Handle nested comment threads with proper JSON parsing
- Add support for comment stats, viewer state, and parent tracking
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update CLAUDE.md with Flutter PR review guidelines
- Regenerate macOS plugin registrant
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Dependencies added/updated:
- video_player: ^2.8.7 (pinned for stability)
- http_mock_adapter: ^0.6.1 (dev, for testing)
- Removed chewie: ^1.7.0 (unused)
Configuration fixes:
- Moved http to dev_dependencies (was misplaced)
- Fixed YAML syntax error
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace unsafe .cast<Map<String, dynamic>>() with .whereType() to
prevent runtime errors when parsing images array.
The .cast() method throws at runtime if list contains non-Map items.
The .whereType() method safely filters out invalid items.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add unit and widget tests for video functionality:
StreamableService tests (19 test cases):
- Shortcode extraction from various URL formats
- Standard URLs: streamable.com/abc123
- /e/ URLs: streamable.com/e/abc123
- URLs without scheme
- URLs with query parameters
- Video URL fetching with mocked API responses
- Caching behavior validation
- Error handling (404, missing fields, network errors)
- Protocol-relative URL handling
PostCard widget tests:
- Play button display for Streamable videos
- Loading indicator during API calls
- No play button for non-video embeds
- Proper StreamableService injection
All tests passing with proper mocking using http_mock_adapter.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Integrate video playback with proper architecture:
- PostCard: Display play button overlay on Streamable videos
- Loading state with spinner during URL fetch
- Context-safe async handling (captured before await)
- Navigates to fullscreen player on tap
- Error handling with user-friendly snackbar
- Dependency Injection: StreamableService via Provider
- Added to app-level providers in main.dart
- Injected into _EmbedCard via constructor
- No direct service instantiation in widgets
- Architecture: Clean separation of concerns
- Widget layer handles UI only
- Service layer handles API calls
- Proper state management (StatefulWidget)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add core services and widgets for Streamable video playback:
- StreamableService: API client for fetching video URLs
- Extracts shortcodes from URLs (handles /e/ format)
- 5-minute URL caching to reduce API calls
- Singleton Dio instance with 10s timeouts
- Handles protocol-relative URLs
- FullscreenVideoPlayer: Immersive video playback
- Swipe-to-dismiss gesture (vertical drag)
- Tap to play/pause
- Fade out background during swipe
- Lifecycle-aware (pauses on app background)
- MinimalVideoControls: Clean scrubber interface
- Progress bar with seek support
- Current time / total duration display
- Minimal, non-intrusive design
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Integrates backend community handles into PostCard with bidirectional
conversion utilities. Community names shown in light blue/cyan with
grey instance domains (!gaming@coves.social format), matching the
user-friendly display from Lemmy.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update dependencies to support URL launching and apply Dart
formatting to all modified files.
Dependencies:
- Add url_launcher: ^6.3.1 (for external browser launching)
- Add url_launcher_platform_interface: ^2.3.2 (dev, for testing)
Code Formatting:
- Apply dart format to all 95 files in codebase
- Ensures consistent style following Dart guidelines
- All files pass flutter analyze with 0 errors, 0 warnings
Quality Checks:
✅ dart format . (95 files formatted)
✅ flutter analyze (0 errors, 0 warnings, 43 info)
✅ flutter test (143 passing, 9 skipped, 0 failing)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add 26 new tests covering URL launcher, external link bar, and
post card functionality. Also add mock providers for widget testing.
New Tests:
- test/utils/url_launcher_test.dart (15 tests)
* Security validation (blocks javascript:, file:, data: schemes)
* Invalid URL handling
* Error snackbar display
* Successful launches with external application mode
- test/widgets/external_link_bar_test.dart (11 tests)
* Domain extraction from URLs
* Favicon display
* URL launching on tap
* Accessibility labels
* Edge cases (empty domain, subdomains, ports, invalid URIs)
- test/widgets/post_card_test.dart (9 tests - currently skipped)
* Basic component rendering
* Community avatars
* External link display
* Action buttons
* Note: Temporarily skipped due to provider mock refactoring needed
- test/test_helpers/mock_providers.dart
* MockAuthProvider with full interface
* MockVoteProvider with voting logic
* MockUrlLauncherPlatform for URL testing
Test Results: 143 passing, 9 skipped, 0 failing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update PostCard to display external links and use extracted
PostCardActions widget.
Changes:
- Display external links using new ExternalLinkBar widget
- Use extracted PostCardActions for action buttons
- Show real community avatars with fallback to placeholder
- Reduce widget size from 504 to 249 lines
Features:
- External links show domain name with favicon
- Click links to open in external browser
- Proper error handling for missing avatars
- Maintains all existing functionality
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add new ExternalLinkBar widget to display external links with
domain name and favicon in a styled, clickable box.
Features:
- Displays domain name (extracted from URL if not provided)
- Shows favicon via Google's favicon service
- Launches URL in external browser on tap
- Comprehensive accessibility support with descriptive labels
- Handles edge cases: invalid URIs, missing domains, subdomains, ports
Design:
- Styled box with secondary background color
- Favicon on left, domain in center, open icon on right
- Consistent with app's design system
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Extract action buttons (menu, share, comment, vote) into a separate
PostCardActions widget to reduce PostCard size and improve maintainability.
Changes:
- Create standalone PostCardActions widget
- Reduce PostCard from 504 to 249 lines
- Add comprehensive accessibility labels for all actions
- Maintain all existing functionality (voting, navigation, etc.)
Benefits:
- Better code organization and separation of concerns
- Meets 300-line widget limit requirement
- Easier to test action button behavior independently
- Improved screen reader support with dynamic Semantics labels
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add UrlLauncher utility class with security validation to prevent
malicious URL schemes (javascript:, file:, data:). Only allows
http and https schemes for external links.
Features:
- URL scheme validation with whitelist approach
- User-friendly error messages via SnackBar
- External browser launch mode for better security
- Case-insensitive scheme checking
Security: Prevents XSS and local file access attacks
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Complete DPoP key persistence implementation with PR review fixes.
This merge addresses all critical bugs and improvements from PR review:
- P0 requestUri bug in retry logic
- Critical token refresh key preservation bug
- Critical onStoreError key generation bug
- Comprehensive error handling for key restoration
- Security improvements (reduced logging verbosity)
- Updated documentation
All changes verified with flutter analyze and successful debug build.
Fixes: DPoP key persistence across OAuth lifecycle
Closes: PR review comments for DPoP implementation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Mark key serialization as IMPLEMENTED:
- FlutterKey.fromJwk() factory for deserialization
- FlutterKey.toJson() / privateJwk for serialization
- Keys now persist across app restarts
- Maintains token binding consistency
Remove obsolete TODO that suggested key serialization was missing.
The implementation has been complete since the initial FlutterKey
implementation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add try-catch blocks with graceful fallbacks for all FlutterKey.fromJwk() calls:
- oauth_client.dart:683 (callback/token exchange)
- Throws descriptive exception on key corruption
- User prompted to re-authenticate
- oauth_client.dart:851 (session restore)
- Deletes corrupted session with delStored()
- Forces clean re-authentication flow
- oauth_client.dart:923 (session revoke)
- Skips server-side revocation if key corrupted
- Still deletes local session in finally block
- Logs warning in debug mode
- session_getter.dart:265 (token refresh)
- Throws TokenRefreshError for corrupted keys
- Triggers session deletion via existing error handling
Also reduces DPoP key logging verbosity:
- Removes detailed key structure logging that exposed implementation
- Simplified to basic confirmation messages
- Improves security posture
Handles edge case where JWK data becomes corrupted in secure storage,
preventing cryptic errors and providing clear recovery path.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
CRITICAL FIXES:
- [P0] Use requestUri instead of request path in DPoP retry (fetch_dpop.dart)
- Was using relative path, now uses absolute URI
- Prevents endpoint resolution failures during retry
- [CRITICAL] Fix token refresh to preserve DPoP key (session_getter.dart:293)
- Was using undefined newDpopKey.bareJwk variable
- Now correctly preserves storedSession.dpopKey
- [CRITICAL] Fix onStoreError to use stored key for revocation (session_getter.dart:178)
- Was generating new key instead of using stored key
- Now properly restores key with FlutterKey.fromJwk()
- Remove duplicate dpopKey declaration (session_getter.dart:234)
- Add automatic nonce retry in onResponse handler (fetch_dpop.dart)
- Handles 401 responses when validateStatus: true
- Implements same retry logic as onError handler
These fixes ensure DPoP keys persist correctly across the entire
OAuth lifecycle, preventing "DPoP proof does not match JKT" errors.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Complete voting system improvements including lexicon migration,
optimistic UI updates, initial vote state loading, and performance
optimizations from PR review.
All 119 tests passing ✅
Add ViewerState class to represent the viewer's relationship with posts:
- vote: direction ("up", "down", or null)
- voteUri: AT-URI of the vote record
- saved: bookmark status
- tags: user-applied tags
This enables the feed to include viewer-specific state from the backend,
allowing proper initialization of vote UI state on feed refresh.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Major OAuth architecture rework - delegate complexity to backend.
Key changes:
- Add CovesSession model for simplified sealed tokens
- Add CovesAuthService for backend-delegated OAuth
- Update OAuth config for private-use URI scheme (RFC 8252)
- Add automatic token refresh on 401 responses
- Remove atproto_oauth_flutter package (~14K lines)
The backend now handles all OAuth complexity (DPoP, PKCE, token exchange)
and returns opaque sealed tokens that the client simply stores and sends.
Update feed_screen_test.dart for the new OAuth patterns.
iOS changes:
- Add Runner.entitlements for associated domains
- Enable Universal Links (applinks:coves.social)
- Reference entitlements in Xcode project
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove the client-side OAuth implementation now that auth is delegated
to the Coves backend. This eliminates ~14,000 lines of complex OAuth
code that handled:
Removed oauth_service.dart:
- Complex OAuthSession management
- Client-side token refresh
- DPoP key generation and proof signing
- PKCE code verifier/challenge generation
Removed atproto_oauth_flutter package:
- DPoP implementation (fetch_dpop.dart)
- Identity resolution (did/handle resolvers)
- OAuth server discovery and metadata
- Token exchange and refresh logic
- Cryptographic key management
- Session state persistence
The backend now handles all of this complexity, returning opaque
sealed tokens that the client simply stores and sends.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Wire up VoteService with the new session getter and auth callbacks
for the backend-delegated OAuth architecture.
Key changes:
- VoteService now uses sessionGetter instead of direct OAuthSession
- Add tokenRefresher callback for automatic 401 recovery
- Add signOutHandler callback for failed refresh cleanup
- Remove OAuthService initialization (deleted)
The new flow ensures votes go through the Coves backend which
has the DPoP keys needed to write to user PDSs.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update FeedProvider and CommentsProvider to work with the simplified
session model and backend-proxied auth flow.
Key changes:
- Use VoteService callback pattern instead of OAuthSession
- Remove direct PDS URL handling
- Simplify test mocks to match new API
Provider updates:
- FeedProvider: Use token getter instead of session getter
- CommentsProvider: Same simplification
Test updates:
- Update mocks to use CovesSession instead of OAuthSession
- Remove PDS URL getter mocks
- Simplify vote service setup in tests
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update CovesApiService and VoteService with automatic 401 handling
and token refresh. With sealed tokens, the backend must proxy all
authenticated requests to user PDSs.
CovesApiService changes:
- Add tokenGetter, tokenRefresher, signOutHandler callbacks
- Dio interceptor for fresh token on each request
- Automatic retry on 401 with token refresh
- Prevent infinite loops with retried flag
- Sign out user if refresh fails
VoteService changes:
- Switch from direct PDS writes to backend-proxied votes
- Backend unseals token and uses stored DPoP keys
- Same 401 retry pattern as CovesApiService
- Remove OAuthSession dependency (was for DPoP)
New tests:
- Token refresh on 401 scenarios
- Retry prevention for refresh endpoint
- Sign out on failed refresh
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Simplify AuthProvider by delegating OAuth operations to the new
CovesAuthService. The provider now focuses on state management while
the service handles authentication logic.
Key changes:
- Use CovesSession instead of OAuthSession
- Simplified token access (sealed tokens are opaque)
- Dependency injection support for testing
- Token refresh delegated to CovesAuthService
Removed:
- Complex session getter with DPoP key management
- Direct PDS URL handling (backend proxies requests)
- Manual OAuth state machine management
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Switch from development callback URL to proper atproto-compliant
private-use URI scheme (RFC 8252):
- Scheme: social.coves:/callback (single slash per spec)
- Works on both Android and iOS without Universal Links complexity
Platform changes:
- Android: Update CallbackActivity intent filter scheme
- iOS: Update CFBundleURLSchemes in Info.plist
- Remove taskAffinity from MainActivity (not needed)
Dependencies:
- Add flutter_web_auth_2 for browser-based OAuth
- Remove atproto_oauth_flutter path dependency (to be deleted)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
New authentication service that delegates OAuth complexity to the Coves
backend. Instead of managing DPoP keys, PKCE, and token exchange client-side,
the backend handles everything and returns sealed tokens.
Key features:
- Browser-based OAuth via flutter_web_auth_2
- Secure token storage per environment (prevents cross-env token reuse)
- Mutex pattern for concurrent token refresh handling
- Handle/DID validation with Bluesky profile URL extraction
- Singleton pattern with test instance creation
The backend's /oauth/mobile/login endpoint handles:
- Handle → DID resolution
- PDS discovery
- PKCE/DPoP key generation
- Token exchange and sealing (AES-256-GCM)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Simplified session model that works with the Coves backend's sealed token
architecture. The backend handles all OAuth complexity (DPoP, PKCE, token
refresh) and gives us an opaque AES-256-GCM encrypted token.
Key features:
- Parse session from OAuth callback URI (RFC 8252 private-use scheme)
- JSON serialization for secure storage persistence
- Immutable with copyWithToken for refresh operations
- Proper redaction of sensitive data in toString()
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add Android productFlavors (dev: social.coves.dev, prod: social.coves)
- Create iOS flavor xcconfig files for future scheme setup
- Update EnvironmentConfig to support flavor-based environment detection
- Add VSCode launch configurations for easy flavor switching
- Update app icon to lil_dude mascot with proper adaptive icon padding
Dev flavor points to local server, prod flavor points to coves.social.
Both apps can be installed side-by-side on the same device.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add author information to reply screen for consistency with detail view:
- Enable showAuthorFooter in PostCard configuration
- Shows author handle and timestamp above post title
- Provides context about who created the original post
- Maintains same enhanced typography as detail view
This ensures users see clear attribution when composing replies,
matching the enhanced information hierarchy of the detail view.
Changes:
- Set showAuthorFooter: true in PostCard configuration
- Matches detail view settings: 20px title, 16px text, 280px embeds
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Enhance post detail screen with better organization and interaction:
Comment Navigation:
- Wire up dual comment button behavior:
- Input field opens reply composer
- Comment count button scrolls to comments section
- Add _scrollToComments() method with smooth animation
- Uses GlobalKey on CommentsHeader for precise scrolling
Visual Improvements:
- Add 1px border divider before comments section
- Replace blank spacing with clear visual separator
- Matches feed/comment divider styling for consistency
- 16px vertical margins for breathing room
Content Organization:
- Enable author footer in detail view (showAuthorFooter: true)
- Shows author info and timestamp above post title
- Enhanced typography: 20px title, 16px text, 1.6 line height
- Larger embeds (280px) for better content visibility
Changes:
- Add _commentsHeaderKey GlobalKey for scroll targeting
- Add _scrollToComments() method with Scrollable.ensureVisible
- Use onCommentInputTap and onCommentCountTap callbacks
- Add Container divider with AppColors.border
- Enable showAuthorFooter parameter
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Improve post card typography and information hierarchy:
Feed View:
- Increase text opacity from 70% to 85% for better readability
- Maintains visual hierarchy while improving scannability
Detail View:
- Add author info above title (avatar + handle + timestamp)
- Show when post was created (e.g., "2h ago")
- Layout: [Avatar] @username • 2h ago
- Provides clear context about post author and recency
Typography Enhancements:
- Add configurable title font size and weight parameters
- Add configurable text font size, line height, and embed height
- Enable full customization for different view contexts
Changes:
- Add showAuthorFooter parameter to PostCard
- Add _buildAuthorFooter() method with avatar, handle, timestamp
- Add _buildAuthorFallbackAvatar() for missing avatars
- Move author info to appear before title in detail views
- Update text opacity: alpha: 0.7 → 0.85 in feed view
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Split comment button functionality into two separate actions:
- Left button (comment input field): Opens reply composer
- Right button (comment count): Scrolls to comments section
This provides contextual behavior - users can either write a new
comment or quickly navigate to view existing comments.
Changes:
- Add onCommentInputTap and onCommentCountTap callbacks
- Maintain backward compatibility with deprecated onCommentTap
- Update documentation to clarify each button's purpose
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
PostCard enhancements:
- Add showBorder prop to control border independently from showHeader
- Fix duplicate border in post detail by passing showBorder: false
Post detail screen integration:
- Connect comment button to new reply screen
- Add authentication check before opening composer
- Implement comment submission handler (TODO: atProto integration)
- Navigate to reply screen with full post context
Fixes duplicate border issue where post detail showed extra divider
between post content and comments when showHeader: false was used.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements full-screen comment/reply composition UI with:
- ReplyScreen: Full-screen interface with Thunder-style keyboard handling
- CommentComposer: Reusable inline comment widget
- Post/comment context preview while composing
- Mention and image upload buttons (coming soon)
- Proper keyboard handling and auto-scroll
- Loading states and error handling
Features:
- Natural scrolling without fixed split ratios
- Manual keyboard margin for smooth transitions
- Text selection and copy/paste enabled
- Haptic feedback on submission
- Auto-dismiss banners for coming soon features
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Resolved all 96 flutter analyze issues by applying automated fixes and
manual corrections to meet Flutter/Dart best practices.
Changes:
- Auto-formatted 15 files with dart format
- Applied 82 automated fixes with dart fix --apply
- Fixed BuildContext async gap in post_detail_screen
- Resolved 18 line length violations (80 char limit)
- Escaped HTML angle brackets in doc comments
- Removed unused imports
- Added const constructors where applicable
- Fixed test issues: unawaited futures, cascade invocations, bool params
Result: flutter analyze now reports 0 issues found.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Major redesign of the post detail view with better visual hierarchy,
community branding, and interactive elements. Introduces a new action
bar component for post interactions.
New PostActionBar widget:
- Dedicated action bar for upvote, downvote, share, and comment actions
- Consistent with feed card actions but optimized for detail view
- Haptic feedback on all interactions
- Proper vote state management
Post detail improvements:
- Custom app bar showing community avatar and styled handle
- Better community branding with fallback avatars
- Removed redundant post card display in detail view
- Cleaner layout with post content displayed directly
- Fixed bottom navigation bar spacing issues
- Integrated share functionality with native share sheet
Visual enhancements:
- Styled community handles with color-coded parts (!community@instance)
- Circular community avatars with fallback to first letter
- Improved spacing and padding throughout
- Better error and loading states
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Improve the comments sort UI with intuitive icons and better visual
feedback. Each sort option now has a matching icon for easier recognition.
Changes:
- Added icons for each sort type (🔥 hot, ✨ top, 🆕 new)
- Show checkmark next to currently selected sort option
- Improved popup menu styling with rounded corners
- Better spacing and alignment in sort dropdown
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add optional parameters to control visibility of header and actions in
PostCard widget. This allows the same component to be reused in different
contexts (feed vs detail view) with appropriate customization.
Changes:
- Added showActions parameter (default: true) to toggle action buttons
- Added showHeader parameter (default: true) to toggle community/author info
- Adjusted margins and padding based on header visibility
- Maintained backward compatibility with default values
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace placeholder share button with actual share functionality using
the share_plus package. Users can now share posts with the post title
and URI through their device's native share sheet.
Changes:
- Added haptic feedback on share button tap for better UX
- Share includes post title (or fallback text) and post URI
- Uses native share sheet for platform-appropriate sharing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace hardcoded seedColor with AppColors.primary to ensure Material
widgets and custom components use the same accent color. This creates
a single source of truth for the app's primary color.
Benefits:
- Material widgets (AppBar, Dialogs, etc.) now match custom components
- Changing AppColors.primary updates the entire color scheme
- Eliminates visual inconsistencies between theme and custom colors
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace the old shark-themed logo with new branding featuring:
- lil_dude.svg: Character mascot (120x120)
- coves_bubble.svg: Brand text/wordmark (180x60)
The new logo layout stacks the character above the brand text with
appropriate spacing. Removed old shark logo files and references.
Also updated landing screen to:
- Remove "Coves" text heading (now part of logo)
- Use new SVG assets with proper sizing
- Update "Create account" button to show "coming soon" snackbar
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace all hardcoded orange color values with AppColors.primary constant
for better maintainability and easier theme updates.
Changes:
- PrimaryButton: Use AppColors.primary for background, shadow, and ripple
- LoginScreen: Use AppColors.primary for input focus border and help link
- Placeholder screens: Use AppColors.primary for icon colors
- MainShellScreen: Use AppColors.primary for selected nav item color
This makes it easier to update the accent color across the entire app
by changing a single constant.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update app branding with new name and Bluesky-style icon system. Replace Material icons with custom SVG icons for cleaner navigation UI. Generate adaptive launcher icons from Coves shark logo with proper safe zones.
Key changes:
- Rename app from "Coves Flutter" to "Coves" on Android and iOS
- Add BlueSkyIcon widget with custom SVG icon support
- Implement Bluesky-style navigation icons (home, search, plus, bell, person)
- Generate adaptive launcher icons with flutter_launcher_icons
- Configure Android adaptive icons with #0B0F14 background
- Bump version to 1.0.0+2
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add communityName color to AppColors for threading indicators
- Update dart:async import to use official unawaited function
- Regenerate feed provider test mocks for latest dependencies
- Remove unused imports from test files
Minor code quality improvements following Dart best practices.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Main.dart changes:
- Add CommentsProvider to app-level provider tree
- Use ChangeNotifierProxyProvider2 for proper dependency injection
- Integrate with AuthProvider and VoteProvider
- Add NotFoundError screen for missing post routes
- Ensure proper provider lifecycle management
PostCard changes:
- Add disableNavigation parameter to prevent recursive navigation
- Fix issue where tapping post on detail screen creates infinite stack
- Maintain backward compatibility (defaults to clickable)
Architecture improvements:
- Follows existing FeedProvider pattern for consistency
- Proper provider disposal handled by framework
- Hot reload support via provider reuse pattern
- No manual provider creation in widgets
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Comment model tests (12 test cases):
- JSON parsing with valid and invalid data
- Null handling and edge cases
- Nested reply structures
CommentsProvider tests (25 test cases):
- loadComments success and error scenarios
- Pagination logic with cursor handling
- Sort option changes and pending refresh mechanism
- Auth state change handling
- Time update mechanism with ValueNotifier
- Error recovery and rollback
CovesApiService tests:
- getComments endpoint success cases
- Error handling (404, 500, network errors)
- Timeout scenarios
- Response parsing with viewer state
All tests use proper mocking with mockito and achieve >80%
code coverage for new comment features.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
LoadingErrorStates widgets:
- FullScreenLoading: Full-screen loading spinner for initial loads
- FullScreenError: Full-screen error with title, message, and retry
- InlineLoading: Inline loading indicator for pagination
- InlineError: Inline error with retry for pagination
- NotFoundError: User-friendly not-found screen with back navigation
ErrorMessages utility:
- Centralized error message transformation
- Converts technical errors to user-friendly messages
- Handles network errors, timeouts, HTTP status codes
- Provides consistent error UX across the app
All widgets follow Material Design patterns and maintain
consistent styling with AppColors theme.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Full post detail view with nested comment display
- Pull-to-refresh support for comments
- Sort dropdown integration (Hot/Top/New)
- Infinite scroll pagination with scroll listener
- Loading and error states with retry functionality
- Performance optimized with ValueListenableBuilder
- Prevents unnecessary rebuilds via _PostHeader and _CommentItem wrappers
Features:
- Post card displayed without navigation (disableNavigation flag)
- Real-time comment sorting with error recovery
- Proper safe area handling (top and bottom)
- Error snackbar with retry action on sort failures
- NotFoundError screen for missing posts
- Integrates with CommentsProvider and VoteProvider
Performance optimizations:
- Timer updates only rebuild time-dependent widgets
- Post header doesn't rebuild when comments change
- Individual comment items rebuild independently
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
CommentCard:
- Display individual comments with author info and content
- Colorful threading indicators (6-color palette) showing nesting depth
- Optimistic voting via VoteProvider integration
- Right-aligned vote button with proper accessibility labels
- Custom painter for vertical threading lines (2px stroke, 6px spacing)
- Dynamic left padding based on nesting depth
CommentThread:
- Recursive rendering of nested comment replies
- Proper depth tracking and max depth limiting
- "Load more replies" button support
CommentsHeader:
- Comment count display with pluralization
- Sort dropdown (Hot/Top/New) with visual feedback
- Empty state handling
Visual features:
- Threading lines extend through full comment height
- Border dividers respect threading indicator spacing
- Subtle author name and timestamp styling (50% opacity)
- Consistent spacing (6px per level + 14px base padding)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add comprehensive state management for comments with pagination
- Implement sort options (hot/top/new) with pending refresh mechanism
- Support optimistic vote integration via VoteProvider
- Add time update mechanism with ValueNotifier for efficient rebuilds
- Handle auth state changes and automatic vote state loading
- Implement error recovery with rollback on sort change failures
Features:
- Cursor-based pagination for infinite scroll
- Automatic refresh scheduling when sort changes during loading
- Clean separation of concerns with proper provider lifecycle
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add CommentView, ThreadViewComment, and CommentsResponse models
- Implement getComments API endpoint in CovesApiService
- Support optional Dio injection for testing
- Handle nested comment threads with proper JSON parsing
- Add support for comment stats, viewer state, and parent tracking
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Dependencies added/updated:
- video_player: ^2.8.7 (pinned for stability)
- http_mock_adapter: ^0.6.1 (dev, for testing)
- Removed chewie: ^1.7.0 (unused)
Configuration fixes:
- Moved http to dev_dependencies (was misplaced)
- Fixed YAML syntax error
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace unsafe .cast<Map<String, dynamic>>() with .whereType() to
prevent runtime errors when parsing images array.
The .cast() method throws at runtime if list contains non-Map items.
The .whereType() method safely filters out invalid items.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add unit and widget tests for video functionality:
StreamableService tests (19 test cases):
- Shortcode extraction from various URL formats
- Standard URLs: streamable.com/abc123
- /e/ URLs: streamable.com/e/abc123
- URLs without scheme
- URLs with query parameters
- Video URL fetching with mocked API responses
- Caching behavior validation
- Error handling (404, missing fields, network errors)
- Protocol-relative URL handling
PostCard widget tests:
- Play button display for Streamable videos
- Loading indicator during API calls
- No play button for non-video embeds
- Proper StreamableService injection
All tests passing with proper mocking using http_mock_adapter.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Integrate video playback with proper architecture:
- PostCard: Display play button overlay on Streamable videos
- Loading state with spinner during URL fetch
- Context-safe async handling (captured before await)
- Navigates to fullscreen player on tap
- Error handling with user-friendly snackbar
- Dependency Injection: StreamableService via Provider
- Added to app-level providers in main.dart
- Injected into _EmbedCard via constructor
- No direct service instantiation in widgets
- Architecture: Clean separation of concerns
- Widget layer handles UI only
- Service layer handles API calls
- Proper state management (StatefulWidget)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add core services and widgets for Streamable video playback:
- StreamableService: API client for fetching video URLs
- Extracts shortcodes from URLs (handles /e/ format)
- 5-minute URL caching to reduce API calls
- Singleton Dio instance with 10s timeouts
- Handles protocol-relative URLs
- FullscreenVideoPlayer: Immersive video playback
- Swipe-to-dismiss gesture (vertical drag)
- Tap to play/pause
- Fade out background during swipe
- Lifecycle-aware (pauses on app background)
- MinimalVideoControls: Clean scrubber interface
- Progress bar with seek support
- Current time / total duration display
- Minimal, non-intrusive design
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Integrates backend community handles into PostCard with bidirectional
conversion utilities. Community names shown in light blue/cyan with
grey instance domains (!gaming@coves.social format), matching the
user-friendly display from Lemmy.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update dependencies to support URL launching and apply Dart
formatting to all modified files.
Dependencies:
- Add url_launcher: ^6.3.1 (for external browser launching)
- Add url_launcher_platform_interface: ^2.3.2 (dev, for testing)
Code Formatting:
- Apply dart format to all 95 files in codebase
- Ensures consistent style following Dart guidelines
- All files pass flutter analyze with 0 errors, 0 warnings
Quality Checks:
✅ dart format . (95 files formatted)
✅ flutter analyze (0 errors, 0 warnings, 43 info)
✅ flutter test (143 passing, 9 skipped, 0 failing)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add 26 new tests covering URL launcher, external link bar, and
post card functionality. Also add mock providers for widget testing.
New Tests:
- test/utils/url_launcher_test.dart (15 tests)
* Security validation (blocks javascript:, file:, data: schemes)
* Invalid URL handling
* Error snackbar display
* Successful launches with external application mode
- test/widgets/external_link_bar_test.dart (11 tests)
* Domain extraction from URLs
* Favicon display
* URL launching on tap
* Accessibility labels
* Edge cases (empty domain, subdomains, ports, invalid URIs)
- test/widgets/post_card_test.dart (9 tests - currently skipped)
* Basic component rendering
* Community avatars
* External link display
* Action buttons
* Note: Temporarily skipped due to provider mock refactoring needed
- test/test_helpers/mock_providers.dart
* MockAuthProvider with full interface
* MockVoteProvider with voting logic
* MockUrlLauncherPlatform for URL testing
Test Results: 143 passing, 9 skipped, 0 failing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update PostCard to display external links and use extracted
PostCardActions widget.
Changes:
- Display external links using new ExternalLinkBar widget
- Use extracted PostCardActions for action buttons
- Show real community avatars with fallback to placeholder
- Reduce widget size from 504 to 249 lines
Features:
- External links show domain name with favicon
- Click links to open in external browser
- Proper error handling for missing avatars
- Maintains all existing functionality
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add new ExternalLinkBar widget to display external links with
domain name and favicon in a styled, clickable box.
Features:
- Displays domain name (extracted from URL if not provided)
- Shows favicon via Google's favicon service
- Launches URL in external browser on tap
- Comprehensive accessibility support with descriptive labels
- Handles edge cases: invalid URIs, missing domains, subdomains, ports
Design:
- Styled box with secondary background color
- Favicon on left, domain in center, open icon on right
- Consistent with app's design system
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Extract action buttons (menu, share, comment, vote) into a separate
PostCardActions widget to reduce PostCard size and improve maintainability.
Changes:
- Create standalone PostCardActions widget
- Reduce PostCard from 504 to 249 lines
- Add comprehensive accessibility labels for all actions
- Maintain all existing functionality (voting, navigation, etc.)
Benefits:
- Better code organization and separation of concerns
- Meets 300-line widget limit requirement
- Easier to test action button behavior independently
- Improved screen reader support with dynamic Semantics labels
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add UrlLauncher utility class with security validation to prevent
malicious URL schemes (javascript:, file:, data:). Only allows
http and https schemes for external links.
Features:
- URL scheme validation with whitelist approach
- User-friendly error messages via SnackBar
- External browser launch mode for better security
- Case-insensitive scheme checking
Security: Prevents XSS and local file access attacks
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Complete DPoP key persistence implementation with PR review fixes.
This merge addresses all critical bugs and improvements from PR review:
- P0 requestUri bug in retry logic
- Critical token refresh key preservation bug
- Critical onStoreError key generation bug
- Comprehensive error handling for key restoration
- Security improvements (reduced logging verbosity)
- Updated documentation
All changes verified with flutter analyze and successful debug build.
Fixes: DPoP key persistence across OAuth lifecycle
Closes: PR review comments for DPoP implementation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Mark key serialization as IMPLEMENTED:
- FlutterKey.fromJwk() factory for deserialization
- FlutterKey.toJson() / privateJwk for serialization
- Keys now persist across app restarts
- Maintains token binding consistency
Remove obsolete TODO that suggested key serialization was missing.
The implementation has been complete since the initial FlutterKey
implementation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add try-catch blocks with graceful fallbacks for all FlutterKey.fromJwk() calls:
- oauth_client.dart:683 (callback/token exchange)
- Throws descriptive exception on key corruption
- User prompted to re-authenticate
- oauth_client.dart:851 (session restore)
- Deletes corrupted session with delStored()
- Forces clean re-authentication flow
- oauth_client.dart:923 (session revoke)
- Skips server-side revocation if key corrupted
- Still deletes local session in finally block
- Logs warning in debug mode
- session_getter.dart:265 (token refresh)
- Throws TokenRefreshError for corrupted keys
- Triggers session deletion via existing error handling
Also reduces DPoP key logging verbosity:
- Removes detailed key structure logging that exposed implementation
- Simplified to basic confirmation messages
- Improves security posture
Handles edge case where JWK data becomes corrupted in secure storage,
preventing cryptic errors and providing clear recovery path.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
CRITICAL FIXES:
- [P0] Use requestUri instead of request path in DPoP retry (fetch_dpop.dart)
- Was using relative path, now uses absolute URI
- Prevents endpoint resolution failures during retry
- [CRITICAL] Fix token refresh to preserve DPoP key (session_getter.dart:293)
- Was using undefined newDpopKey.bareJwk variable
- Now correctly preserves storedSession.dpopKey
- [CRITICAL] Fix onStoreError to use stored key for revocation (session_getter.dart:178)
- Was generating new key instead of using stored key
- Now properly restores key with FlutterKey.fromJwk()
- Remove duplicate dpopKey declaration (session_getter.dart:234)
- Add automatic nonce retry in onResponse handler (fetch_dpop.dart)
- Handles 401 responses when validateStatus: true
- Implements same retry logic as onError handler
These fixes ensure DPoP keys persist correctly across the entire
OAuth lifecycle, preventing "DPoP proof does not match JKT" errors.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>