commits
Add a mandatory community guidelines acceptance screen that users must
complete after accepting the EULA and before accessing the app. This
follows the same fail-closed pattern as the EULA gate.
Changes:
- Add community guidelines document (assets/legal/community-guidelines.md)
- Add CommunityGuidelinesProvider with versioned acceptance tracking
- Add CommunityGuidelinesScreen with scroll-to-bottom, checkbox, and
accept flow matching the EULA screen pattern
- Update router redirect chain: EULA → Community Guidelines → App
- Add "Community Guidelines" view-only link to profile settings menu
- Add Sentry error reporting to EULA provider
- Improve EULA screen: teal accent color, better link error handling,
empty content retry state
- Bump version to 1.0.5+6
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Increment app version and build number for the next release.
Changes:
- Bump version from 1.0.4+4 to 1.0.5+5
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update EULA Section 5 to explicitly state zero-tolerance policy for
objectionable content and abusive users, including account termination
consequences. Add references to in-app blocking and reporting features
that empower users to manage their experience.
Changes:
- Add zero-tolerance policy statement for objectionable content
- Document account termination for abusive behavior
- Reference user blocking feature and its immediate effect
- Reference in-app content reporting to the Coves Team
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a mandatory EULA acceptance flow that blocks app access until the
user scrolls through and agrees to the End User License Agreement.
The EULA is rendered from a bundled markdown file and supports
versioning so users can be re-prompted when terms change.
Changes:
- Add EulaProvider with versioned acceptance tracking via shared_preferences
- Add EulaScreen with scroll-to-bottom requirement, checkbox, and accept button
- Integrate EULA gate into GoRouter redirect logic in main.dart
- Add view-only EULA access from profile settings sheet
- Change project license from GPL v3 to AGPL v3
- Add flutter_markdown dependency for EULA rendering
- Fix getProfile XRPC method casing in API service and model comment
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add ability to block/unblock users and communities from post and comment
overflow menus with optimistic UI updates and rollback on failure.
Changes:
- Add BlockProvider with optimistic toggle, pending state, and auto-clear on sign-out
- Add blockUser/unblockUser/blockCommunity/unblockCommunity API methods
- Add shared block_action_helpers with auth check, confirmation dialog, and snackbar feedback
- Add block menu items to post card (user + community) and comment card (user)
- Add block error message to ErrorMessage
- Register BlockProvider in main.dart
- Add haptic feedback on comment card tap
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduce tablet-optimized layouts using a NavigationRail on larger screens
and constrained content widths for improved readability.
Changes:
- Add ResponsiveUtils utility class with tablet detection (600dp breakpoint)
and maxContentWidth constant (640px for optimal line length)
- Replace bottom navigation with NavigationRail on tablets in MainShellScreen
- Constrain feed content to maxContentWidth on tablets across all screens:
- FeedPage, CommunityFeedScreen, ProfileScreen posts/comments
- PostDetailScreen post card and comment threads
- CommunitiesScreen placeholder content
- Use wrapForTablet helper for consistent responsive wrapping
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace string-based error pattern matching with type-based matching against
the ApiException hierarchy. This provides more reliable error handling and
cleaner call sites throughout the app.
Changes:
- Rewrite ErrorMessages to use ApiException types (Auth, Network, Server, etc.)
- Add ErrorMessage class with context-specific methods (vote, comment, subscription, etc.)
- Add isAuthError() helper for sign-in prompt flows
- Simplify exception handling in community_feed_screen, post_detail_screen
- Update comment_card and post_card_actions to use new error utilities
- Configure iOS release signing (bundle ID: social.coves)
- Add ExportOptions.plist for App Store Connect uploads
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Set up proper code signing for Android and iOS release builds
in preparation for app store submission.
Changes:
- Add Android release signing config loading from key.properties
- Replace debug signing with release signing config for Android builds
- Add development team ID to iOS project for all build configurations
- Bump version from 1.0.0+2 to 1.0.4+4
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace legacy PopupMenuButton with the newer MenuAnchor widget in
comment and post overflow menus. This modernizes the menu implementation
to use Material 3 APIs.
Changes:
- Replace PopupMenuButton with MenuAnchor in comment_card.dart
- Replace PopupMenuButton with MenuAnchor in post_card_actions.dart
- Use MenuItemButton with leadingIcon/trailingIcon properties
- Add MenuController-based open/close logic
- Apply consistent MenuStyle for background and shape
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements user-facing content moderation by allowing authenticated users
to report posts and comments. Reports are categorized by reason (spam,
harassment, doxing, illegal content, child safety, other) with an optional
explanation field.
Changes:
- Add ReportDialog widget with type-safe ReportReason enum
- Add submitReport() API method with input validation
- Add "Report comment" option to comment overflow menu
- Add "Report post" option to post overflow menu
- Show sign-in prompt when unauthenticated users try to report
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extract duplicated share button logic from multiple screens into a single
reusable ShareButton widget. This improves consistency and makes it easier
to implement real share functionality in the future.
Changes:
- Add ShareButton widget with IconButton and InkWell style variants
- Update ShareIcon to use ArrowShareRight from Bluesky's design system
- Replace inline share implementations in CommunityFeedScreen, PostDetailScreen,
ProfileScreen, and PostCardActions with the new ShareButton component
- Remove direct share_plus imports from individual screens (share logic
will be centralized when fully implemented)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement full community feed navigation allowing users to tap on community
avatars/names in post cards to view community details and browse posts.
Changes:
- Add CommunityFeedScreen with collapsing SliverAppBar and frosted glass effect
- Add Feed/About tabs with feed sorting (Hot, New, Top)
- Add getCommunity() and getCommunityFeed() API methods
- Add TappableCommunity widget for consistent navigation from post cards
- Add CommunityHeader widget matching profile header design pattern
- Add DisplayUtils for shared avatar colors and number formatting
- Add share functionality for communities
- Improve error handling with specific exception types (Network, NotFound, Server)
- Add subscribe/join button with loading states and animations
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add comprehensive delete support for posts and comments with proper
error handling, authorization checks, and UI feedback.
Features:
- Add deleteComment method to CommentService and CommentsProvider
- Add deletePost method to CovesApiService
- Add delete menu option to CommentCard and PostCardActions (author-only)
- Use API's replyCount for accurate collapsed/continue thread counts
- Improve deleted comment display with deletion reason
Error Handling:
- Add 404 and network timeout handling to delete operations
- Fix _isDeleting flag timing to prevent race conditions
- Add specific NetworkException and NotFoundException handlers in UI
- Add haptics error handling for unsupported devices
Other:
- Add const to CommentStats constructor
- Improve deletePost docstring with exception types
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Initialize vote states from viewer data when fetching comments on user
profile pages. This ensures server scores remain authoritative and
prevents double-counting votes that were made elsewhere.
Changes:
- Add VoteProvider dependency to UserProfileProvider via ProxyProvider2
- Initialize vote states on refresh (all comments) and pagination (new only)
- Add _initializeCommentVoteState helper for flat CommentView objects
- Add debug warning when VoteProvider is unexpectedly null
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Aligns models with backend structure where content lives inside record objects.
This refactor enables proper soft-delete support while maintaining backward
compatibility through convenience getters.
Changes:
- Add PostRecord and CommentRecord classes for nested content/facets
- Add isDeleted and deletionReason fields to PostView and CommentView
- Add backward-compatible getters (content, title, text, facets)
- Update CommentCard to show [deleted] placeholder and hide vote buttons
- Add date parsing error handling with specific messages
- Add debug logging for facet parsing failures
- Type embed field properly as Map<String, dynamic>?
- Update all tests for new record structure
- Add new comment_card_test.dart with deleted comment tests
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement rich text facet support for displaying and creating posts and
comments with clickable links. Links in post and comment bodies are now
tappable and open in an external browser.
Changes:
- Add facet models (ByteSlice, RichTextFacet, LinkFacetFeature) with
proper UTF-8 byte indexing for cross-platform compatibility
- Add FacetDetector utility for detecting URLs in text and generating
facets with correct byte indices (handles emoji and multi-byte chars)
- Add RichTextRenderer widget that converts facets to tappable TextSpans
- Update Post and Comment models to parse facets from record['facets']
per backend API update
- Update PostCard, DetailedPostView, and CommentCard to render rich text
- Update CreatePostScreen and ReplyScreen to detect links when composing
- Update CovesApiService and CommentService to send facets to backend
- Add comprehensive test coverage for facet models, detector, and renderer
- Fix error handling in CommentCard (show snackbar on vote failure)
- Fix sign-in navigation in CommentCard
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add debounce to keyboard dismissal detection in create post screen to
prevent false unfocus events during keyboard animations. Also adjust
the profile header gradient overlay for iOS devices which need more
aggressive opacity due to larger SafeArea from Dynamic Island/notch.
Changes:
- Add 100ms debounce before unfocusing fields on keyboard close
- Add mounted check for async safety in keyboard handler
- Detect iOS platform in profile header
- Use stronger gradient (0.6 alpha) on iOS for text readability
- Adjust gradient stops for iOS SafeArea compensation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update iOS dependencies and improve security configuration:
Changes:
- Add image_cropper and image_picker_ios pods for profile image editing
- Add sentry_flutter for error monitoring and crash reporting
- Add package_info_plus for app version info
- Ignore PEM files in Android gitignore to prevent accidental commits of certificates
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update community avatar styling in post cards from rounded squares
to circles for visual consistency with other avatar elements in the app.
Changes:
- Increase avatar border radius from 4 to 12 for circular CachedNetworkImage
- Use BoxShape.circle for fallback letter avatars
- Add const to fallback avatar decoration
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add complete profile editing functionality allowing users to update their
avatar, banner, display name, and bio with native platform image croppers.
Changes:
- Add EditProfileScreen with tappable avatar/banner image pickers
- Add image_cropper package for native iOS (TOCropViewController) and
Android (uCrop) image cropping with configurable styles
- Add ImageCropUtils utility with CropConfig for avatar (1:1 circle) and
banner (3:1 rectangle) presets
- Add updateProfile() API method with base64 image encoding and validation
- Add UserProfileProvider.updateProfile() with auto-refresh after save
- Add edit button to profile screen app bar for own profile
- Migrate community admin panel to CachedNetworkImage + native cropping
- Configure iOS camera/photo library permissions in Info.plist
- Configure Android UCrop activity with dark theme in AndroidManifest.xml
- Set iOS minimum deployment target to 13.0 for image_cropper compatibility
- Fix DID text overflow in ProfileHeader with Expanded widget
- Remove unused edit button/callbacks from ProfileHeader widget
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace Image.network with CachedNetworkImage for better caching,
error handling, and consistent loading states in the admin panel.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extract image picking logic from communities_admin_panel into reusable
components for consistent image handling across the app:
- PickedImage model for typed image data (file, bytes, mimeType)
- ImagePickerUtils with validation, constraints, and MIME detection
- ImageSourcePicker widget for gallery/camera selection bottom sheet
Also improves error handling with ImageValidationException and replaces
debugPrint calls with dart:developer logging.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extract admin functionality into dedicated CommunitiesAdminPanel with a
menu-based navigation system. Add ability for admins to update community
avatars via the new social.coves.community.update XRPC endpoint.
Changes:
- Add CommunitiesAdminPanel with multi-page navigation (menu, create, profile pic)
- Add updateCommunity API method with base64 image encoding
- Add image_picker dependency for gallery/camera image selection
- Simplify CommunitiesScreen to StatelessWidget, delegating admin to panel
- Validate image size (max 1 MB) and MIME type (jpeg/png/webp)
- Upgrade Gradle to 8.11.1 and Android plugin to 8.9.1
- Add current vs new avatar preview before upload
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Introduce branded Bluesky authentication option with official butterfly
logo and Coves-consistent styling. The outline button matches the design
system while maintaining Bluesky brand identity.
Changes:
- Add BlueskySignInButton widget with press animations and disabled state
- Add official Bluesky butterfly SVG asset
- Add linkBlueLight color constant for interactive states
- Integrate sign-in button on landing screen (routes to /login)
- Add Sentry error tracking for SVG asset loading failures
- Add Claude Code merge-to-main command
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace static tab indicators with a smoothly animated sliding underline
that follows swipe gestures. Key changes:
- Add _FeedTabsWithSlidingUnderline widget for animated tab switching
- Interpolate underline position based on PageController page value
- Smooth opacity transitions for tab labels during swipe
- Sync provider state only when page settles to avoid jank
- Use consistent font weight during animation to prevent layout shifts
- Properly dispose page controller listener
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Match the license used by the Coves backend.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add ability to subscribe/unsubscribe to communities directly from posts.
Includes optimistic UI updates, subscription state management, and
proper auth handling.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove thumb field from ExternalEmbedInput and create post UI
- Add URI validation tests (empty, invalid schemes, malformed URLs)
- Add == , hashCode, and toString overrides to ExternalEmbedInput
- Update test mock data to use nested structure with $type
- Fix stale documentation mentioning thumbnail
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add error snackbar feedback when vote toggle fails silently
- Add context.mounted checks after async gaps to prevent crashes
- Wrap HapticFeedback calls in try-catch for unsupported platforms
- Add debug logging to dispose() exception catches
- Add error handling for Clipboard.setData() with user feedback
- Add explicit FocusNode listener removal before dispose
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extract post detail rendering into dedicated DetailedPostView widget,
replacing PostCard with many config flags. Cleaner API with
purpose-built layout for detail view.
Changes:
- Add DetailedPostView with Reddit-style author row and clean typography
- Update post_detail_screen to use new widget
- Adjust font sizes (title: 15px, body: 12.5px)
- Add 16px padding and 8px rounded corners to single images
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add new coves_logo_text.svg and remove old coves_bubble.svg
- Wrap authProvider.initialize() in try-catch with Sentry logging
- Use unawaited() for SVG errorBuilder Sentry calls to avoid lifecycle issues
- Change login navigation from go() to push()/pop() for smooth transitions
- Disable resizeToAvoidBottomInset on login to prevent keyboard pushing content
- Remove animated glow from PrimaryButton for cleaner appearance
- Update app colors and dependencies
- Remove desktop platform files (linux, macos, windows) - targeting mobile only
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace orange app icon with new teal version across iOS and Android.
Remove old orange icon assets from assets/logo/.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Addresses intermittent connection timeout errors on mobile networks by
adding automatic retry with exponential backoff for transient failures.
Key changes:
- Add RetryInterceptor with configurable max retries and backoff delay
- Prevent POST receiveTimeout retries to avoid duplicate comments/votes
- Reduce connect timeout from 30s to 10s with 2 retries (better UX)
- Add constructor validation (assertions) for interceptor params
- Refactor coves_auth_service to use nullable Dio pattern
- Add comprehensive documentation for restoreSession error handling
Safety: POST + receiveTimeout is NOT retried because the server may
have already processed the request. Other timeout types (connection,
send) are safe to retry as the request never reached the server.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Reverts the link icon placeholder for embeds without thumbnails.
Posts with failed embed processing will simply not show an embed
area instead of displaying a placeholder icon.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use ListView.custom with findChildIndexCallback to maintain stable scroll
position when new posts are loaded. Key changes:
- Switch from ListView.builder to ListView.custom with SliverChildBuilderDelegate
- Add findChildIndexCallback to track posts by URI and footer by key
- Always show footer slot when posts exist to keep itemCount stable
- Use consistent 80px height for idle footer to match loading spinner
- Extract footer building to _buildFooter() method with KeyedSubtree wrapper
These changes prevent the 6k+ pixel scroll jumps that occurred when
Flutter couldn't track item positions across list rebuilds during pagination.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Per-feed pagination throttle: change from shared DateTime to per-feed-type
Map to prevent cross-feed throttle interference
- Embed placeholder UX: show link icon placeholder when embed has no thumbnail
instead of hiding the embed area entirely
- Loading placeholder UX: add image icon to loading placeholders instead of
blank containers for better user feedback
- Disable CachedNetworkImage fade animations across all widgets to prevent
scroll jitter from height changes during image load transitions
- Fix cacheExtent comment: clarify "5000px in each direction (10000px total)"
- Clean up feed_page.dart: remove debug code, use default platform physics
- Replace animated spinners with static placeholders in profile_header.dart
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Prevents users from being trapped in the app when their atProto profile
is misconfigured or not found. Adds optional secondary action support
to FullScreenError widget.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Integrate the new social.coves.actor.getComments backend endpoint
into the profile page's Comments tab:
- Add getActorComments() method to CovesApiService
- Add CommentsState model with immutable list (List.unmodifiable)
- Add ActorCommentsResponse model with proper documentation
- Add loadComments/loadMoreComments/retryComments to UserProfileProvider
- Wire up lazy loading in profile screen (loads on first tab switch)
- Display comments using existing CommentCard widget
- Handle pagination, loading states, and errors
- Properly handle 404 as "User not found" (not empty state)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add banner image support with gradient fallback
- Add avatar drop shadow for depth
- Display handle and DID (with QR icon) instead of display name
- Add "Joined" date with calendar icon
- Add tabbed content bar with icons (Posts, Comments, Likes)
- Implement frosted glass effect on collapsed header
- Show Memberships stat instead of Communities/Reputation
- Add UserProfile model and UserProfileProvider
- Add TappableAuthor widget for navigating to profiles
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tapping the feed tab while already on the feed screen now scrolls the
current feed to the top, matching common social media app behavior.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add hasMore prop to FeedPage widget to track pagination state
- Show "You're all caught up!" message with checkmark when feed ends
- Add _shouldShowFooter getter for cleaner footer logic
- Prevents confusing bounce behavior when reaching end of feed
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Adjust post detail text sizing to match feed (title 16px, text 14px)
- Remove bold styling from title and author handle
- Reduce author avatar size from 24px to 20px
- Keep external embed images at same height as feed view (180px)
- Add sources section for megathread posts with clickable source links
- Add EmbedSource model with URI validation and security checks
- Add SourceLinkBar widget matching ExternalLinkBar styling
- Improve ValueKey comment clarity in feed_page.dart
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add external link embed support (link cards with thumbnail, title, description)
- Add BlueskyExternalEmbed model with domain extraction
- Improve quoted posts: show handle, timestamp, media placeholder
- Handle unavailable quoted posts (blocked, deleted, detached)
- Add official Bluesky SVG icons (reply, repost, like, logo)
- Match Bluesky's dim theme colors exactly
- Remove text truncation for posts (Bluesky has 300 char limit)
- Add formatCount and formatFullDateTime utilities
- Add comprehensive tests for Bluesky post models (67 tests)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add UI components to render Bluesky crosspost embeds in feeds with
Bluesky-styled post cards.
New files:
- lib/models/bluesky_post.dart: Data models for BlueskyPostEmbed,
BlueskyPostResult with URL helpers and robust JSON validation
- lib/constants/bluesky_colors.dart: Bluesky brand color palette
- lib/widgets/bluesky_action_bar.dart: Disabled action bar showing
engagement counts (view-only)
- lib/widgets/bluesky_post_card.dart: Main card widget with avatar,
author info, text, media placeholder, quote posts, and action bar
Changes:
- lib/models/post.dart: Add BlueskyPostEmbed parsing for
social.coves.embed.post type, fix nullable text field handling
- lib/widgets/post_card.dart: Conditionally render BlueskyPostCard
when embed is present
- lib/services/coves_api_service.dart: Add catch blocks for parsing
errors to prevent silent failures
Features:
- 42px circular avatar (tappable → opens Bluesky profile)
- Author name, handle, and relative timestamp
- Post text with max 6 lines
- Media placeholder with "View on Bluesky" link
- Nested quote post support (1 level)
- Disabled action bar with reply/repost/like counts
- "View on Bluesky" footer link
- Graceful unavailable post handling
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add CreateCommunityResponse model with defensive JSON parsing
- Add createCommunity() API method to CovesApiService
- Transform CommunitiesScreen to admin-aware with creation form
- Admin check via handle (coves.social, alex.local.coves.dev)
- Backend enforces actual permissions via DID allowlist
- DNS-valid name validation with regex
- Proper listener cleanup to prevent memory leaks
- Cached API service instance per screen lifecycle
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update formatHandleForDisplay to handle both formats:
- New: c-gaming.coves.social → !gaming@coves.social
- Legacy: gaming.community.coves.social → !gaming@coves.social
Add null fallback in PostCard._buildCommunityHandle to prevent
crashes when handle format is unrecognized.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
User-specific Claude Code permissions should not be committed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add atproto provider logos to login and landing screens
- Add errorBuilder to all SVG assets for graceful degradation
- Improve sign-in error messages with user-friendly text
- Replace hardcoded colors with AppColors constants
- Extract help dialog to dedicated method for cleaner code
- Add keyboard dismiss on tap outside input field
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Multi-feed state management with security hardening:
- FeedState model with sentinel copyWith pattern for nullable field clearing
- MultiFeedProvider with per-feed state and cross-session security guards
- FeedPage widget with pull-to-refresh on empty state
- FeedScreen auth sync and lazy feed loading
- Comprehensive tests for new architecture
Fixes:
- copyWith can now clear nullable fields (cursor, error, lastRefreshTime)
- Cross-session data leaks prevented via DID comparison
- Empty feed states are now refreshable
- PageController syncs with auth state on sign-out
- For You tab loads on first access after sign-in
Addresses code quality checks from CODE_QUALITY_GUIDE.md:
- Use cascade notation where appropriate (cascade_invocations)
- Put control body on separate line (always_put_control_body_on_new_line)
- Break long string to stay under 80 chars (lines_longer_than_80_chars)
- Apply dart format to all changed files
All files now pass:
- dart format --output=none --set-exit-if-changed
- flutter analyze (0 issues)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Updates provider registration to use MultiFeedProvider instead of
FeedProvider. The new provider is initialized with:
- CovesApiService for API calls
- AuthProvider for auth state and session identity
- VoteProvider for vote state initialization from feed responses
Also includes pubspec.lock updates from dependency resolution.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Updates widget tests for the new MultiFeedProvider architecture:
- FakeMultiFeedProvider replaces FakeFeedProvider
- Supports per-feed state management (FeedType parameter)
- Uses sentinel-compatible copyWith for state mutations
- Tests cover both authenticated and unauthenticated flows
- Tests for PageView swipe navigation when authenticated
- Tests for single-feed display when not authenticated
Removes orphaned test/providers/feed_provider_test.dart that
referenced the deleted FeedProvider.
Updates test/widget_test.dart counter test with provider setup.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Refactors FeedScreen to work with MultiFeedProvider and adds several
UX and reliability improvements:
PageView for feed switching:
- Authenticated users can swipe between Discover and For You
- Unauthenticated users see only Discover (no PageView)
- Per-feed ScrollControllers with position restoration
Auth state synchronization:
- Listens to AuthProvider changes
- Jumps PageController to page 0 on sign-out to match provider state
- Prevents tab/page mismatch after re-authentication
Lazy feed loading (_ensureFeedLoaded):
- Triggers initial load when switching to an unloaded feed
- Handles case where user signs in after app start and taps For You
- Called from both tab tap and swipe navigation
This fixes issues where:
- For You tab showed empty state after signing in mid-session
- PageController stayed on page 1 after sign-out while provider
switched to Discover, causing misalignment on re-auth
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extracts feed rendering logic into a reusable FeedPage widget that
handles all feed states:
- Loading: Centered CircularProgressIndicator
- Error: User-friendly message with Retry button
- Empty: Contextual message based on auth state
- Posts: ListView.builder with pagination support
Key improvements:
- Empty state now wrapped in RefreshIndicator with CustomScrollView
and SliverFillRemaining, allowing pull-to-refresh when feed is empty
- Error messages are user-friendly (maps technical errors to readable text)
- Loading more indicator at list bottom during pagination
- Proper scroll controller integration for infinite scroll
This fixes an issue where unauthenticated users with an empty Discover
feed had no way to retry without restarting the app.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replaces FeedProvider with MultiFeedProvider that manages separate
state for Discover and For You feeds. Key improvements:
Architecture:
- Per-feed state management using Map<FeedType, FeedState>
- Centralized _fetchFeed helper eliminates code duplication
- Auth-aware feed switching (For You requires authentication)
- Minute-based time updates for relative timestamps
Security (cross-session data leak prevention):
- Captures session DID before fetch to detect auth changes
- Discards For You responses if session changed during fetch
- Guards both success and error paths to prevent stale data
- Removes feed state entirely on session change (not copyWith)
This prevents scenarios where:
- User signs out during fetch → old data reappears
- User A signs out, User B signs in → User A's feed shown to B
- Fetch errors after sign-out → stale posts restored
Removes the old single-feed FeedProvider.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Introduces FeedState as an immutable state container for per-feed data
(Discover and For You feeds). Key features:
- Holds posts, cursor, loading states, error, scroll position, and
last refresh time
- Uses sentinel pattern for copyWith to distinguish "not provided"
from "explicitly set to null" for nullable fields (cursor, error,
lastRefreshTime)
- Enables proper clearing of fields on refresh/error recovery
The sentinel pattern fixes a bug where nullable fields couldn't be
cleared back to null through copyWith - critical for pagination
cursor handling when reaching the end of a feed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move provider initialization from postFrameCallback to didChangeDependencies
for synchronous access before first build. Create ScrollController with
initialScrollOffset set to cached position, eliminating the visible flash
from loading → content at top → jump to cached position.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
View.of(context) was being called in didChangeMetrics() during widget
deactivation, causing "Looking up a deactivated widget's ancestor"
errors. The context becomes invalid before mounted becomes false.
Fixed by caching the FlutterView reference in didChangeDependencies()
for both _ReplyScreenState and _ReplyToolbarState, then using the
cached reference in didChangeMetrics().
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add a mandatory community guidelines acceptance screen that users must
complete after accepting the EULA and before accessing the app. This
follows the same fail-closed pattern as the EULA gate.
Changes:
- Add community guidelines document (assets/legal/community-guidelines.md)
- Add CommunityGuidelinesProvider with versioned acceptance tracking
- Add CommunityGuidelinesScreen with scroll-to-bottom, checkbox, and
accept flow matching the EULA screen pattern
- Update router redirect chain: EULA → Community Guidelines → App
- Add "Community Guidelines" view-only link to profile settings menu
- Add Sentry error reporting to EULA provider
- Improve EULA screen: teal accent color, better link error handling,
empty content retry state
- Bump version to 1.0.5+6
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update EULA Section 5 to explicitly state zero-tolerance policy for
objectionable content and abusive users, including account termination
consequences. Add references to in-app blocking and reporting features
that empower users to manage their experience.
Changes:
- Add zero-tolerance policy statement for objectionable content
- Document account termination for abusive behavior
- Reference user blocking feature and its immediate effect
- Reference in-app content reporting to the Coves Team
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a mandatory EULA acceptance flow that blocks app access until the
user scrolls through and agrees to the End User License Agreement.
The EULA is rendered from a bundled markdown file and supports
versioning so users can be re-prompted when terms change.
Changes:
- Add EulaProvider with versioned acceptance tracking via shared_preferences
- Add EulaScreen with scroll-to-bottom requirement, checkbox, and accept button
- Integrate EULA gate into GoRouter redirect logic in main.dart
- Add view-only EULA access from profile settings sheet
- Change project license from GPL v3 to AGPL v3
- Add flutter_markdown dependency for EULA rendering
- Fix getProfile XRPC method casing in API service and model comment
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add ability to block/unblock users and communities from post and comment
overflow menus with optimistic UI updates and rollback on failure.
Changes:
- Add BlockProvider with optimistic toggle, pending state, and auto-clear on sign-out
- Add blockUser/unblockUser/blockCommunity/unblockCommunity API methods
- Add shared block_action_helpers with auth check, confirmation dialog, and snackbar feedback
- Add block menu items to post card (user + community) and comment card (user)
- Add block error message to ErrorMessage
- Register BlockProvider in main.dart
- Add haptic feedback on comment card tap
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduce tablet-optimized layouts using a NavigationRail on larger screens
and constrained content widths for improved readability.
Changes:
- Add ResponsiveUtils utility class with tablet detection (600dp breakpoint)
and maxContentWidth constant (640px for optimal line length)
- Replace bottom navigation with NavigationRail on tablets in MainShellScreen
- Constrain feed content to maxContentWidth on tablets across all screens:
- FeedPage, CommunityFeedScreen, ProfileScreen posts/comments
- PostDetailScreen post card and comment threads
- CommunitiesScreen placeholder content
- Use wrapForTablet helper for consistent responsive wrapping
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace string-based error pattern matching with type-based matching against
the ApiException hierarchy. This provides more reliable error handling and
cleaner call sites throughout the app.
Changes:
- Rewrite ErrorMessages to use ApiException types (Auth, Network, Server, etc.)
- Add ErrorMessage class with context-specific methods (vote, comment, subscription, etc.)
- Add isAuthError() helper for sign-in prompt flows
- Simplify exception handling in community_feed_screen, post_detail_screen
- Update comment_card and post_card_actions to use new error utilities
- Configure iOS release signing (bundle ID: social.coves)
- Add ExportOptions.plist for App Store Connect uploads
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Set up proper code signing for Android and iOS release builds
in preparation for app store submission.
Changes:
- Add Android release signing config loading from key.properties
- Replace debug signing with release signing config for Android builds
- Add development team ID to iOS project for all build configurations
- Bump version from 1.0.0+2 to 1.0.4+4
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace legacy PopupMenuButton with the newer MenuAnchor widget in
comment and post overflow menus. This modernizes the menu implementation
to use Material 3 APIs.
Changes:
- Replace PopupMenuButton with MenuAnchor in comment_card.dart
- Replace PopupMenuButton with MenuAnchor in post_card_actions.dart
- Use MenuItemButton with leadingIcon/trailingIcon properties
- Add MenuController-based open/close logic
- Apply consistent MenuStyle for background and shape
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements user-facing content moderation by allowing authenticated users
to report posts and comments. Reports are categorized by reason (spam,
harassment, doxing, illegal content, child safety, other) with an optional
explanation field.
Changes:
- Add ReportDialog widget with type-safe ReportReason enum
- Add submitReport() API method with input validation
- Add "Report comment" option to comment overflow menu
- Add "Report post" option to post overflow menu
- Show sign-in prompt when unauthenticated users try to report
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extract duplicated share button logic from multiple screens into a single
reusable ShareButton widget. This improves consistency and makes it easier
to implement real share functionality in the future.
Changes:
- Add ShareButton widget with IconButton and InkWell style variants
- Update ShareIcon to use ArrowShareRight from Bluesky's design system
- Replace inline share implementations in CommunityFeedScreen, PostDetailScreen,
ProfileScreen, and PostCardActions with the new ShareButton component
- Remove direct share_plus imports from individual screens (share logic
will be centralized when fully implemented)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement full community feed navigation allowing users to tap on community
avatars/names in post cards to view community details and browse posts.
Changes:
- Add CommunityFeedScreen with collapsing SliverAppBar and frosted glass effect
- Add Feed/About tabs with feed sorting (Hot, New, Top)
- Add getCommunity() and getCommunityFeed() API methods
- Add TappableCommunity widget for consistent navigation from post cards
- Add CommunityHeader widget matching profile header design pattern
- Add DisplayUtils for shared avatar colors and number formatting
- Add share functionality for communities
- Improve error handling with specific exception types (Network, NotFound, Server)
- Add subscribe/join button with loading states and animations
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add comprehensive delete support for posts and comments with proper
error handling, authorization checks, and UI feedback.
Features:
- Add deleteComment method to CommentService and CommentsProvider
- Add deletePost method to CovesApiService
- Add delete menu option to CommentCard and PostCardActions (author-only)
- Use API's replyCount for accurate collapsed/continue thread counts
- Improve deleted comment display with deletion reason
Error Handling:
- Add 404 and network timeout handling to delete operations
- Fix _isDeleting flag timing to prevent race conditions
- Add specific NetworkException and NotFoundException handlers in UI
- Add haptics error handling for unsupported devices
Other:
- Add const to CommentStats constructor
- Improve deletePost docstring with exception types
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Initialize vote states from viewer data when fetching comments on user
profile pages. This ensures server scores remain authoritative and
prevents double-counting votes that were made elsewhere.
Changes:
- Add VoteProvider dependency to UserProfileProvider via ProxyProvider2
- Initialize vote states on refresh (all comments) and pagination (new only)
- Add _initializeCommentVoteState helper for flat CommentView objects
- Add debug warning when VoteProvider is unexpectedly null
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Aligns models with backend structure where content lives inside record objects.
This refactor enables proper soft-delete support while maintaining backward
compatibility through convenience getters.
Changes:
- Add PostRecord and CommentRecord classes for nested content/facets
- Add isDeleted and deletionReason fields to PostView and CommentView
- Add backward-compatible getters (content, title, text, facets)
- Update CommentCard to show [deleted] placeholder and hide vote buttons
- Add date parsing error handling with specific messages
- Add debug logging for facet parsing failures
- Type embed field properly as Map<String, dynamic>?
- Update all tests for new record structure
- Add new comment_card_test.dart with deleted comment tests
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement rich text facet support for displaying and creating posts and
comments with clickable links. Links in post and comment bodies are now
tappable and open in an external browser.
Changes:
- Add facet models (ByteSlice, RichTextFacet, LinkFacetFeature) with
proper UTF-8 byte indexing for cross-platform compatibility
- Add FacetDetector utility for detecting URLs in text and generating
facets with correct byte indices (handles emoji and multi-byte chars)
- Add RichTextRenderer widget that converts facets to tappable TextSpans
- Update Post and Comment models to parse facets from record['facets']
per backend API update
- Update PostCard, DetailedPostView, and CommentCard to render rich text
- Update CreatePostScreen and ReplyScreen to detect links when composing
- Update CovesApiService and CommentService to send facets to backend
- Add comprehensive test coverage for facet models, detector, and renderer
- Fix error handling in CommentCard (show snackbar on vote failure)
- Fix sign-in navigation in CommentCard
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add debounce to keyboard dismissal detection in create post screen to
prevent false unfocus events during keyboard animations. Also adjust
the profile header gradient overlay for iOS devices which need more
aggressive opacity due to larger SafeArea from Dynamic Island/notch.
Changes:
- Add 100ms debounce before unfocusing fields on keyboard close
- Add mounted check for async safety in keyboard handler
- Detect iOS platform in profile header
- Use stronger gradient (0.6 alpha) on iOS for text readability
- Adjust gradient stops for iOS SafeArea compensation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update iOS dependencies and improve security configuration:
Changes:
- Add image_cropper and image_picker_ios pods for profile image editing
- Add sentry_flutter for error monitoring and crash reporting
- Add package_info_plus for app version info
- Ignore PEM files in Android gitignore to prevent accidental commits of certificates
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update community avatar styling in post cards from rounded squares
to circles for visual consistency with other avatar elements in the app.
Changes:
- Increase avatar border radius from 4 to 12 for circular CachedNetworkImage
- Use BoxShape.circle for fallback letter avatars
- Add const to fallback avatar decoration
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add complete profile editing functionality allowing users to update their
avatar, banner, display name, and bio with native platform image croppers.
Changes:
- Add EditProfileScreen with tappable avatar/banner image pickers
- Add image_cropper package for native iOS (TOCropViewController) and
Android (uCrop) image cropping with configurable styles
- Add ImageCropUtils utility with CropConfig for avatar (1:1 circle) and
banner (3:1 rectangle) presets
- Add updateProfile() API method with base64 image encoding and validation
- Add UserProfileProvider.updateProfile() with auto-refresh after save
- Add edit button to profile screen app bar for own profile
- Migrate community admin panel to CachedNetworkImage + native cropping
- Configure iOS camera/photo library permissions in Info.plist
- Configure Android UCrop activity with dark theme in AndroidManifest.xml
- Set iOS minimum deployment target to 13.0 for image_cropper compatibility
- Fix DID text overflow in ProfileHeader with Expanded widget
- Remove unused edit button/callbacks from ProfileHeader widget
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extract image picking logic from communities_admin_panel into reusable
components for consistent image handling across the app:
- PickedImage model for typed image data (file, bytes, mimeType)
- ImagePickerUtils with validation, constraints, and MIME detection
- ImageSourcePicker widget for gallery/camera selection bottom sheet
Also improves error handling with ImageValidationException and replaces
debugPrint calls with dart:developer logging.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extract admin functionality into dedicated CommunitiesAdminPanel with a
menu-based navigation system. Add ability for admins to update community
avatars via the new social.coves.community.update XRPC endpoint.
Changes:
- Add CommunitiesAdminPanel with multi-page navigation (menu, create, profile pic)
- Add updateCommunity API method with base64 image encoding
- Add image_picker dependency for gallery/camera image selection
- Simplify CommunitiesScreen to StatelessWidget, delegating admin to panel
- Validate image size (max 1 MB) and MIME type (jpeg/png/webp)
- Upgrade Gradle to 8.11.1 and Android plugin to 8.9.1
- Add current vs new avatar preview before upload
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Introduce branded Bluesky authentication option with official butterfly
logo and Coves-consistent styling. The outline button matches the design
system while maintaining Bluesky brand identity.
Changes:
- Add BlueskySignInButton widget with press animations and disabled state
- Add official Bluesky butterfly SVG asset
- Add linkBlueLight color constant for interactive states
- Integrate sign-in button on landing screen (routes to /login)
- Add Sentry error tracking for SVG asset loading failures
- Add Claude Code merge-to-main command
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace static tab indicators with a smoothly animated sliding underline
that follows swipe gestures. Key changes:
- Add _FeedTabsWithSlidingUnderline widget for animated tab switching
- Interpolate underline position based on PageController page value
- Smooth opacity transitions for tab labels during swipe
- Sync provider state only when page settles to avoid jank
- Use consistent font weight during animation to prevent layout shifts
- Properly dispose page controller listener
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove thumb field from ExternalEmbedInput and create post UI
- Add URI validation tests (empty, invalid schemes, malformed URLs)
- Add == , hashCode, and toString overrides to ExternalEmbedInput
- Update test mock data to use nested structure with $type
- Fix stale documentation mentioning thumbnail
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add error snackbar feedback when vote toggle fails silently
- Add context.mounted checks after async gaps to prevent crashes
- Wrap HapticFeedback calls in try-catch for unsupported platforms
- Add debug logging to dispose() exception catches
- Add error handling for Clipboard.setData() with user feedback
- Add explicit FocusNode listener removal before dispose
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extract post detail rendering into dedicated DetailedPostView widget,
replacing PostCard with many config flags. Cleaner API with
purpose-built layout for detail view.
Changes:
- Add DetailedPostView with Reddit-style author row and clean typography
- Update post_detail_screen to use new widget
- Adjust font sizes (title: 15px, body: 12.5px)
- Add 16px padding and 8px rounded corners to single images
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add new coves_logo_text.svg and remove old coves_bubble.svg
- Wrap authProvider.initialize() in try-catch with Sentry logging
- Use unawaited() for SVG errorBuilder Sentry calls to avoid lifecycle issues
- Change login navigation from go() to push()/pop() for smooth transitions
- Disable resizeToAvoidBottomInset on login to prevent keyboard pushing content
- Remove animated glow from PrimaryButton for cleaner appearance
- Update app colors and dependencies
- Remove desktop platform files (linux, macos, windows) - targeting mobile only
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Addresses intermittent connection timeout errors on mobile networks by
adding automatic retry with exponential backoff for transient failures.
Key changes:
- Add RetryInterceptor with configurable max retries and backoff delay
- Prevent POST receiveTimeout retries to avoid duplicate comments/votes
- Reduce connect timeout from 30s to 10s with 2 retries (better UX)
- Add constructor validation (assertions) for interceptor params
- Refactor coves_auth_service to use nullable Dio pattern
- Add comprehensive documentation for restoreSession error handling
Safety: POST + receiveTimeout is NOT retried because the server may
have already processed the request. Other timeout types (connection,
send) are safe to retry as the request never reached the server.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use ListView.custom with findChildIndexCallback to maintain stable scroll
position when new posts are loaded. Key changes:
- Switch from ListView.builder to ListView.custom with SliverChildBuilderDelegate
- Add findChildIndexCallback to track posts by URI and footer by key
- Always show footer slot when posts exist to keep itemCount stable
- Use consistent 80px height for idle footer to match loading spinner
- Extract footer building to _buildFooter() method with KeyedSubtree wrapper
These changes prevent the 6k+ pixel scroll jumps that occurred when
Flutter couldn't track item positions across list rebuilds during pagination.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Per-feed pagination throttle: change from shared DateTime to per-feed-type
Map to prevent cross-feed throttle interference
- Embed placeholder UX: show link icon placeholder when embed has no thumbnail
instead of hiding the embed area entirely
- Loading placeholder UX: add image icon to loading placeholders instead of
blank containers for better user feedback
- Disable CachedNetworkImage fade animations across all widgets to prevent
scroll jitter from height changes during image load transitions
- Fix cacheExtent comment: clarify "5000px in each direction (10000px total)"
- Clean up feed_page.dart: remove debug code, use default platform physics
- Replace animated spinners with static placeholders in profile_header.dart
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Integrate the new social.coves.actor.getComments backend endpoint
into the profile page's Comments tab:
- Add getActorComments() method to CovesApiService
- Add CommentsState model with immutable list (List.unmodifiable)
- Add ActorCommentsResponse model with proper documentation
- Add loadComments/loadMoreComments/retryComments to UserProfileProvider
- Wire up lazy loading in profile screen (loads on first tab switch)
- Display comments using existing CommentCard widget
- Handle pagination, loading states, and errors
- Properly handle 404 as "User not found" (not empty state)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add banner image support with gradient fallback
- Add avatar drop shadow for depth
- Display handle and DID (with QR icon) instead of display name
- Add "Joined" date with calendar icon
- Add tabbed content bar with icons (Posts, Comments, Likes)
- Implement frosted glass effect on collapsed header
- Show Memberships stat instead of Communities/Reputation
- Add UserProfile model and UserProfileProvider
- Add TappableAuthor widget for navigating to profiles
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add hasMore prop to FeedPage widget to track pagination state
- Show "You're all caught up!" message with checkmark when feed ends
- Add _shouldShowFooter getter for cleaner footer logic
- Prevents confusing bounce behavior when reaching end of feed
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Adjust post detail text sizing to match feed (title 16px, text 14px)
- Remove bold styling from title and author handle
- Reduce author avatar size from 24px to 20px
- Keep external embed images at same height as feed view (180px)
- Add sources section for megathread posts with clickable source links
- Add EmbedSource model with URI validation and security checks
- Add SourceLinkBar widget matching ExternalLinkBar styling
- Improve ValueKey comment clarity in feed_page.dart
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add external link embed support (link cards with thumbnail, title, description)
- Add BlueskyExternalEmbed model with domain extraction
- Improve quoted posts: show handle, timestamp, media placeholder
- Handle unavailable quoted posts (blocked, deleted, detached)
- Add official Bluesky SVG icons (reply, repost, like, logo)
- Match Bluesky's dim theme colors exactly
- Remove text truncation for posts (Bluesky has 300 char limit)
- Add formatCount and formatFullDateTime utilities
- Add comprehensive tests for Bluesky post models (67 tests)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add UI components to render Bluesky crosspost embeds in feeds with
Bluesky-styled post cards.
New files:
- lib/models/bluesky_post.dart: Data models for BlueskyPostEmbed,
BlueskyPostResult with URL helpers and robust JSON validation
- lib/constants/bluesky_colors.dart: Bluesky brand color palette
- lib/widgets/bluesky_action_bar.dart: Disabled action bar showing
engagement counts (view-only)
- lib/widgets/bluesky_post_card.dart: Main card widget with avatar,
author info, text, media placeholder, quote posts, and action bar
Changes:
- lib/models/post.dart: Add BlueskyPostEmbed parsing for
social.coves.embed.post type, fix nullable text field handling
- lib/widgets/post_card.dart: Conditionally render BlueskyPostCard
when embed is present
- lib/services/coves_api_service.dart: Add catch blocks for parsing
errors to prevent silent failures
Features:
- 42px circular avatar (tappable → opens Bluesky profile)
- Author name, handle, and relative timestamp
- Post text with max 6 lines
- Media placeholder with "View on Bluesky" link
- Nested quote post support (1 level)
- Disabled action bar with reply/repost/like counts
- "View on Bluesky" footer link
- Graceful unavailable post handling
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add CreateCommunityResponse model with defensive JSON parsing
- Add createCommunity() API method to CovesApiService
- Transform CommunitiesScreen to admin-aware with creation form
- Admin check via handle (coves.social, alex.local.coves.dev)
- Backend enforces actual permissions via DID allowlist
- DNS-valid name validation with regex
- Proper listener cleanup to prevent memory leaks
- Cached API service instance per screen lifecycle
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update formatHandleForDisplay to handle both formats:
- New: c-gaming.coves.social → !gaming@coves.social
- Legacy: gaming.community.coves.social → !gaming@coves.social
Add null fallback in PostCard._buildCommunityHandle to prevent
crashes when handle format is unrecognized.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add atproto provider logos to login and landing screens
- Add errorBuilder to all SVG assets for graceful degradation
- Improve sign-in error messages with user-friendly text
- Replace hardcoded colors with AppColors constants
- Extract help dialog to dedicated method for cleaner code
- Add keyboard dismiss on tap outside input field
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Multi-feed state management with security hardening:
- FeedState model with sentinel copyWith pattern for nullable field clearing
- MultiFeedProvider with per-feed state and cross-session security guards
- FeedPage widget with pull-to-refresh on empty state
- FeedScreen auth sync and lazy feed loading
- Comprehensive tests for new architecture
Fixes:
- copyWith can now clear nullable fields (cursor, error, lastRefreshTime)
- Cross-session data leaks prevented via DID comparison
- Empty feed states are now refreshable
- PageController syncs with auth state on sign-out
- For You tab loads on first access after sign-in
Addresses code quality checks from CODE_QUALITY_GUIDE.md:
- Use cascade notation where appropriate (cascade_invocations)
- Put control body on separate line (always_put_control_body_on_new_line)
- Break long string to stay under 80 chars (lines_longer_than_80_chars)
- Apply dart format to all changed files
All files now pass:
- dart format --output=none --set-exit-if-changed
- flutter analyze (0 issues)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Updates provider registration to use MultiFeedProvider instead of
FeedProvider. The new provider is initialized with:
- CovesApiService for API calls
- AuthProvider for auth state and session identity
- VoteProvider for vote state initialization from feed responses
Also includes pubspec.lock updates from dependency resolution.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Updates widget tests for the new MultiFeedProvider architecture:
- FakeMultiFeedProvider replaces FakeFeedProvider
- Supports per-feed state management (FeedType parameter)
- Uses sentinel-compatible copyWith for state mutations
- Tests cover both authenticated and unauthenticated flows
- Tests for PageView swipe navigation when authenticated
- Tests for single-feed display when not authenticated
Removes orphaned test/providers/feed_provider_test.dart that
referenced the deleted FeedProvider.
Updates test/widget_test.dart counter test with provider setup.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Refactors FeedScreen to work with MultiFeedProvider and adds several
UX and reliability improvements:
PageView for feed switching:
- Authenticated users can swipe between Discover and For You
- Unauthenticated users see only Discover (no PageView)
- Per-feed ScrollControllers with position restoration
Auth state synchronization:
- Listens to AuthProvider changes
- Jumps PageController to page 0 on sign-out to match provider state
- Prevents tab/page mismatch after re-authentication
Lazy feed loading (_ensureFeedLoaded):
- Triggers initial load when switching to an unloaded feed
- Handles case where user signs in after app start and taps For You
- Called from both tab tap and swipe navigation
This fixes issues where:
- For You tab showed empty state after signing in mid-session
- PageController stayed on page 1 after sign-out while provider
switched to Discover, causing misalignment on re-auth
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extracts feed rendering logic into a reusable FeedPage widget that
handles all feed states:
- Loading: Centered CircularProgressIndicator
- Error: User-friendly message with Retry button
- Empty: Contextual message based on auth state
- Posts: ListView.builder with pagination support
Key improvements:
- Empty state now wrapped in RefreshIndicator with CustomScrollView
and SliverFillRemaining, allowing pull-to-refresh when feed is empty
- Error messages are user-friendly (maps technical errors to readable text)
- Loading more indicator at list bottom during pagination
- Proper scroll controller integration for infinite scroll
This fixes an issue where unauthenticated users with an empty Discover
feed had no way to retry without restarting the app.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replaces FeedProvider with MultiFeedProvider that manages separate
state for Discover and For You feeds. Key improvements:
Architecture:
- Per-feed state management using Map<FeedType, FeedState>
- Centralized _fetchFeed helper eliminates code duplication
- Auth-aware feed switching (For You requires authentication)
- Minute-based time updates for relative timestamps
Security (cross-session data leak prevention):
- Captures session DID before fetch to detect auth changes
- Discards For You responses if session changed during fetch
- Guards both success and error paths to prevent stale data
- Removes feed state entirely on session change (not copyWith)
This prevents scenarios where:
- User signs out during fetch → old data reappears
- User A signs out, User B signs in → User A's feed shown to B
- Fetch errors after sign-out → stale posts restored
Removes the old single-feed FeedProvider.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Introduces FeedState as an immutable state container for per-feed data
(Discover and For You feeds). Key features:
- Holds posts, cursor, loading states, error, scroll position, and
last refresh time
- Uses sentinel pattern for copyWith to distinguish "not provided"
from "explicitly set to null" for nullable fields (cursor, error,
lastRefreshTime)
- Enables proper clearing of fields on refresh/error recovery
The sentinel pattern fixes a bug where nullable fields couldn't be
cleared back to null through copyWith - critical for pagination
cursor handling when reaching the end of a feed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move provider initialization from postFrameCallback to didChangeDependencies
for synchronous access before first build. Create ScrollController with
initialScrollOffset set to cached position, eliminating the visible flash
from loading → content at top → jump to cached position.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
View.of(context) was being called in didChangeMetrics() during widget
deactivation, causing "Looking up a deactivated widget's ancestor"
errors. The context becomes invalid before mounted becomes false.
Fixed by caching the FlutterView reference in didChangeDependencies()
for both _ReplyScreenState and _ReplyToolbarState, then using the
cached reference in didChangeMetrics().
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>