Skip to content

UI improvements: Add Communities link to sidebar and fix dark mode colors#172

Open
rabble wants to merge 12 commits intomainfrom
tweaks_april_13th
Open

UI improvements: Add Communities link to sidebar and fix dark mode colors#172
rabble wants to merge 12 commits intomainfrom
tweaks_april_13th

Conversation

@rabble
Copy link
Copy Markdown
Collaborator

@rabble rabble commented Apr 13, 2025

Summary

  • Added Communities link to sidebar for easier navigation to groups page
  • Fixed text colors in dark mode to match design specifications (#B6A0E1)
  • Fixed card background and content coloring to ensure proper contrast in dark mode

Test plan

  1. Open the app and check the sidebar for the new Communities link
  2. Verify clicking the link navigates to the Communities screen
  3. Verify the text color in dark mode matches the design spec (#B6A0E1)
  4. Verify card backgrounds have proper contrast in dark mode
  5. Test both light and dark modes to ensure the colors work in both

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Introduced Asks & Offers: Create, view, and manage marketplace listings ("ask" or "offer") in public feeds or groups, with full lifecycle support (active, fulfilled, cancelled, expired).
    • Added screens for listing feeds, detail views, and creation/editing, with filtering, searching, and group scoping.
    • Listings support images, location, price, payment info, and expiration.
    • Integrated with Nostr protocol using real-time updates and replaceable event logic.
  • Improvements

    • Enhanced theming and color adaptation across the app for dark and light modes.
    • Improved error handling and logging, especially for image loading, cache management, and toast message suppression.
    • Updated navigation and sidebar with a direct link to Communities for easier group access.
    • UI refinements in community, event, onboarding, and group screens for better layout, theming, and text display.
    • Added robust group management, event caching, and subscription handling for improved performance and reliability.
    • Enhanced platform detection and web compatibility in image and file handling components.
  • Bug Fixes

    • Fixed localization key inconsistencies; all UI text now uses standardized camelCase keys.
    • Improved error suppression for non-critical image encoding errors to reduce unnecessary alerts.
    • Addressed iOS build issues with updated deployment targets, CocoaPods sources, and build configurations.
  • Documentation

    • Added detailed design specs, PRD, and draft NIP for the Asks & Offers feature.
    • Introduced a localization migration plan and updated localization configuration.
    • Added comprehensive build and troubleshooting guides for iOS/macOS.
    • Provided internal developer guidelines for code style, performance, and communication.
  • Chores

    • Updated iOS/macOS build settings and scripts for improved compatibility and Sentry integration.
    • Removed obsolete test mocks and expanded test coverage for group and listing features.
    • Refactored code to improve maintainability and remove unused methods.

rabble and others added 7 commits April 13, 2025 12:56
- Change hardcoded PlurColors.cardBackground to theme-aware PlurColors.cardBg(context)
- Change hardcoded PlurColors.separator to theme-aware PlurColors.separatorColor(context)
- Fix thread trace line color to use PlurColors.separatorColor(context)

These changes ensure post cards and thread indicators properly respect theme settings in both light and dark modes.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add centralized filtering logic in StyledBotToast
- Enhance ErrorLogger to filter encoding errors
- Update ErrorBoundary to avoid showing error UI for image errors
- Create comprehensive list of image error patterns to suppress
- Ensure errors are still logged to console for debugging

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Increase height of text container from 60px to 70px
- Adjust grid's childAspectRatio to 0.9 for taller cells
- Increase vertical spacing between rows to 40px
- Add more bottom padding to grid view (80px)
- Allow community titles to use up to 3 lines of text

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add informative message for empty communities feed explaining its purpose
- Replace default placeholder with custom explanatory message and action buttons
- Add safe navigation buttons to switch between grid and feed views
- Fix multiple rebuild issue in communities feed
- Add static methods to IndexProvider for safer view mode switching
- Improve widget caching to prevent duplicate screen displays

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Updated theme colors to match Plur design specs in theme_util.dart. Added proper theme-aware colors to post cards to ensure correct dark/light mode appearance.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Added Communities menu item to sidebar with appropriate icon
- Implemented highlighting when it's the current tab
- Used localization for multi-language support
- Updated CHANGELOG.md to document the change

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Updated primaryText color in PlurColors to use the correct #B6A0E1
- Ensured primaryForegroundColor in dark theme is correctly set
- Fixed card background colors in dark mode
- Applied consistent coloring across theme components

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 13, 2025

Walkthrough

This update introduces the "Asks & Offers" feature, enabling users to create, view, and manage marketplace listings (asks or offers) in both public and group-scoped contexts. It includes a comprehensive data model (ListingModel), Riverpod-based state management (ListingNotifier), and a suite of new Flutter screens for listing feeds, detail views, and creation/editing forms. The implementation follows Nostr protocol standards (kind:31111, NIP-33), supports real-time event updates, and provides filtering, searching, and status management for listings. Extensive documentation, design specifications, and a draft NIP are included, detailing event structure, lifecycle, and client/relay behavior.

Changes

File(s) Change Summary
lib/features/asks_offers/models/listing_model.dart New file: Defines ListingModel with enums for type/status, serialization/deserialization to/from Nostr events, status parsing, value equality, and copyWith support.
lib/features/asks_offers/providers/listing_provider.dart New file: Implements ListingNotifier Riverpod provider for managing listing state, loading from Nostr, real-time updates, creation, updating, filtering, and error handling.
lib/features/asks_offers/screens/create_edit_listing_screen.dart New file: Flutter screen for creating/editing asks/offers. Handles form fields, validation, image upload (placeholder), expiration, and save logic for both new and existing listings.
lib/features/asks_offers/screens/listing_detail_screen.dart New file: Flutter screen for displaying full details of a listing, with status badges, images, author/group info, and context-sensitive actions (edit, fulfill, message, etc.).
lib/features/asks_offers/screens/listings_screen.dart New file: Main listings feed screen with tabbed navigation (All/Asks/Offers), search, status filtering, pull-to-refresh, empty state handling, and FABs for new asks/offers.
lib/features/asks_offers/widgets/listing_card.dart New file: Widget for rendering a listing summary card, showing type, status, preview, images, author, group, location, and action buttons. Adapts style by theme and listing type.
doc/features/asks-offers.md
doc/features/asks_offers_nip.md
doc/features/asks_offers_prd.md
New documentation: Adds a design spec, draft NIP, and PRD for Asks & Offers, covering protocol, UI/UX, lifecycle, relay/client behavior, security, and roadmap.
lib/consts/router_path.dart Adds new route constants: groupMedia, listingCreateEdit, listingDetail for navigation to new screens.

Changes (Grouped)

File(s) Change Summary
doc/features/asks-offers.md, doc/features/asks_offers_nip.md, doc/features/asks_offers_prd.md Adds comprehensive documentation: design spec, draft NIP, and PRD for the Asks & Offers feature.
lib/features/asks_offers/models/listing_model.dart New data model for listings, with serialization, deserialization, and status/type enums.
lib/features/asks_offers/providers/listing_provider.dart Riverpod state notifier for managing, fetching, updating, and filtering listings via Nostr events.
lib/features/asks_offers/screens/create_edit_listing_screen.dart, listing_detail_screen.dart, listings_screen.dart New Flutter screens for listing creation/editing, detail view, and feed with filtering/searching.
lib/features/asks_offers/widgets/listing_card.dart Listing card widget for displaying summary and actions for each listing.
lib/consts/router_path.dart Adds new route constants for navigation to Asks & Offers screens.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ListingsScreen
    participant ListingProvider
    participant NostrClient
    participant ListingModel

    User->>ListingsScreen: Opens Listings Feed
    ListingsScreen->>ListingProvider: loadListings()
    ListingProvider->>NostrClient: Query kind:31111 events
    NostrClient-->>ListingProvider: List<Event>
    ListingProvider->>ListingModel: Parse events to ListingModel
    ListingProvider-->>ListingsScreen: Update state with listings

    User->>ListingsScreen: Tap "New Ask/Offer"
    ListingsScreen->>CreateEditListingScreen: Navigate to creation form

    User->>CreateEditListingScreen: Fill form and submit
    CreateEditListingScreen->>ListingProvider: createListing()
    ListingProvider->>ListingModel: toEvent()
    ListingProvider->>NostrClient: Publish event
    NostrClient-->>ListingProvider: Event ack
    ListingProvider-->>ListingsScreen: Update state with new listing

    User->>ListingsScreen: Tap ListingCard
    ListingsScreen->>ListingDetailScreen: Navigate to detail view
Loading

Possibly related PRs

  • Community Guidelines #140: Adds a "Community Guidelines" feature with UI, routing, and data handling for group guidelines. Related as both PRs enhance group/community navigation and features, though this PR focuses on marketplace listings while Community Guidelines #140 focuses on group guidelines.

Suggested reviewers

  • joshuatbrown
  • mplorentz
  • pelumy

Poem

In the meadow of code where new features bloom,
Rabbits hop forth with a listing or two.
Asks and offers, now easy to share,
In groups or in public, for those who care.
With cards and feeds and a detail screen bright,
Marketplace magic, both day and night.
🐇✨ Here’s to new trades, in code’s gentle light!

Tip

⚡💬 Agentic Chat (Pro Plan, General Availability)
  • We're introducing multi-step agentic chat in review comments and issue comments, within and outside of PR's. This feature enhances review and issue discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments and add commits to existing pull requests.

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (3)
lib/provider/group_feed_provider.dart (1)

549-566: 🛠️ Refactor suggestion

Subscription across multiple relays
Subscribing to multiple relays is effective for coverage. Ensure unsubscribes are robust to avoid orphaned listeners if relays are unresponsive.

lib/router/group/group_detail_note_list_widget.dart (1)

36-38: ⚠️ Potential issue

Dispose the first ScrollController or ensure consistent usage.
Both _controller (bound by bindLoadMoreScroll(_controller)) and scrollController are present. Only scrollController is disposed in dispose(), potentially leaving _controller undisposed. This can lead to memory leaks or unexpected scrolling behavior.

- final ScrollController _controller = ScrollController();
  ...
- ScrollController scrollController = ScrollController();
  ...
  @override
  void dispose() {
-   _unsubscribe();
-   disposeLater();
-   scrollController.dispose();
+   _controller.dispose(); // or if you want to unify, remove the second controller
    super.dispose();
  }

Also applies to: 319-321

lib/router/group/all_group_posts_widget.dart (1)

31-43: 🛠️ Refactor suggestion

Static caching and group count tracking might cause inconsistencies.
Using _lastGroupCount as a static field introduces potential mismatch if multiple instances of AllGroupPostsWidget exist. The same issue applies to _cachedContentWidget and _cachedEventIds.

Refactor these fields into an instance-wide or per-user approach so each widget can handle its own cache, preventing data contamination across different screens.

🧹 Nitpick comments (28)
lib/router/index/index_widget.dart (2)

94-95: Debug logging for Grid view selection.

The debug print statement helps track user interactions with the Grid tab selector during development.

Consider removing or conditionalizing debug print statements before releasing to production to avoid console pollution.


123-124: Debug logging for Feed view selection.

The debug print statement helps track user interactions with the Feed tab selector during development.

Consider removing or conditionalizing debug print statements before releasing to production to avoid console pollution.

lib/features/communities/communities_grid_widget.dart (1)

17-18: Added debug logging.

The debug print statement helps with tracking widget lifecycle during development. Consider using a structured logging approach or conditional logging for production builds.

-    debugPrint("🔍 SCREEN DISPLAYED: CommunitiesGridWidget (Communities grid)");
+    // Only print in debug mode for better performance in production
+    assert(() {
+      debugPrint("🔍 SCREEN DISPLAYED: CommunitiesGridWidget (Communities grid)");
+      return true;
+    }());
lib/component/content/content_image_widget.dart (1)

74-91: Enhanced error handling for image loading

The try-catch block with proper placeholder fallback improves robustness. Consider extracting the placeholder widget creation to a method to avoid code duplication.

-      // Use blurhash if available and enabled
-      if (settingsProvider.openBlurhashImage != OpenStatus.close &&
-          widget.fileMetadata != null &&
-          StringUtil.isNotBlank(widget.fileMetadata!.blurhash)) {
-        try {
-          placeholder = genBlurhashImageWidget(
-              widget.fileMetadata!, themeData.hintColor, widget.imageBoxFix);
-        } catch (e) {
-          // Silently fail if blurhash generation fails
-          placeholder = null;
-        }
-      }
+      // Use blurhash if available and enabled
+      placeholder = _createPlaceholder(themeData);

With a helper method like:

Widget? _createPlaceholder(ThemeData themeData) {
  if (settingsProvider.openBlurhashImage != OpenStatus.close &&
      widget.fileMetadata != null &&
      StringUtil.isNotBlank(widget.fileMetadata!.blurhash)) {
    try {
      return genBlurhashImageWidget(
          widget.fileMetadata!, themeData.hintColor, widget.imageBoxFix);
    } catch (e) {
      // Silently fail if blurhash generation fails
      return null;
    }
  }
  return null;
}
lib/component/styled_bot_toast.dart (1)

204-225: Well-structured error suppression mechanism.

The centralized implementation of message suppression is clean and maintainable. Consider adding a documentation comment explaining the suppression strategy and when to add new patterns.

/// Helper method to determine if a message should be suppressed
+/// 
+/// This method contains patterns for errors that should be hidden from users
+/// to avoid unnecessary notifications for non-actionable issues.
+/// 
+/// Add new patterns here when you identify error messages that:
+/// 1. Cannot be fixed by the user
+/// 2. Are expected in certain edge cases (like network issues)
+/// 3. Are too technical to be meaningful to end users
static bool _shouldSuppressMessage(String message) {
lib/component/event/event_main_widget.dart (1)

815-821: Use of GoogleFonts.nunito and dynamic text color
Adopting custom fonts and applying them via theme context improves brand consistency—nice addition.

Consider defining common text styles in the theme or a global style helper to keep typography consistent and avoid repetition.

lib/provider/group_feed_provider.dart (5)

26-29: New static event cache
Exposing _staticEventCache and its getter can be helpful for debugging. However, keep an eye on memory footprint when storing many events in memory.


33-35: Circular reference between ListProvider and GroupFeedProvider
Manually setting ListProvider.groupFeedProvider in the constructor introduces tight coupling. Evaluate if a more decoupled injection approach is feasible.


110-144: Early query throttling
Preventing rapid duplicate queries with _queryThrottleMs is good for performance. Consider logging at a debug level to avoid clutter in production.


207-255: Multi-relay query logic
Querying multiple relays to ensure data completeness is beneficial but can be quite chatty. Possibly introduce advanced rate-limiting or concurrency checks.


651-664: Unsubscribe logic
Unsubscribing from default and indexed relay subscriptions is good. Keep an eye on any potential leftover subscription IDs that might exceed your loop limit (1..20).

lib/component/blurhash_image_component/stub_platform.dart (2)

7-62: Consider centralizing repetitive isX() logic.

Each method in SafePlatform checks kIsWeb and then tries the relevant io.Platform.isX call in a try/catch. While this approach is valid, you could consider a private helper method to reduce repetitive patterns and ensure uniform error handling across platforms. For instance:

-static bool isIOS() {
-  if (kIsWeb) return false;
-  try {
-    return io.Platform.isIOS;
-  } catch (e) {
-    return false;
-  }
-}
+static bool _safeCheck(bool Function() check) {
+  if (kIsWeb) return false;
+  try {
+    return check();
+  } catch (e) {
+    return false;
+  }
+}
+
+static bool isIOS() => _safeCheck(() => io.Platform.isIOS);

65-65: Clarify "backward compatibility" usage.

The comment and class name Platform can be confusing for newcomers. Consider making the comment more explicit (e.g., "Retain for legacy uses on web-only builds") or deprecating this class if it’s no longer needed.

lib/main.dart (2)

318-340: Robust window manager initialization.

Nicely done restricting desktop-only logic. Consider logging final success/failure if any additional post-initialization actions are needed. Everything else looks fine.


431-433: Questionable fallback to web assumption.

If Platform is unavailable, defaulting to kIsWeb might mask unexpected conditions (like a brand-new platform). Consider logging or handling more dynamically if new platforms are added in the future.

lib/provider/list_provider.dart (5)

434-455: Multiple-relay approach for join requests.

Sending join events to both the specified and default relays is more robust. However, consider whether you should break early once the event is successfully sent, or if sending to all remains beneficial. That might reduce repeated requests.


470-470: Fallback membership handling.

Automatically assumptive membership on failed verification is slightly risky but acceptable given the noisy nature of relays. Just confirm that you have subsequent checks to remove or correct membership if eventually proven invalid.

Also applies to: 472-476, 480-482


630-670: Optimistic fallback for join failures.

Adding groups to the local set even if join events seemingly failed can be beneficial for user experience, but consider a scheduled re-check to ensure membership is truly established.


700-729: Aggregate group update.

Sending group list updates to all relays and refreshing the GroupFeedProvider is good synergy. Ensure _updateGroups() is not called too frequently in quick succession to avoid excessive network traffic.


900-950: Invite link logic is well-structured.

Generates a link with enough data to join. Logging errors for each failed relay is great. Consider returning partial success/failure for UI feedback if some relays fail.

test/group_feed_provider_test.dart (1)

307-314: Stub test for doQuery filters.
A placeholder test hints at future verification of query filters. Consider adding assertions or verifying filter contents once the implementation supports it.

lib/router/group/group_detail_note_list_widget.dart (2)

40-44: Consider potential collisions with static caching.
Storing the cached widget, event IDs, and group ID in static fields may cause unexpected clashes or stale data if multiple instances of GroupDetailNoteListWidget are initialized for different groups at the same time, or if the widget is reused in various places.

One approach is to move these fields into an instance-level cache or a dedicated cache manager keyed by group ID, ensuring thread safety and preventing data collisions.


421-501: Confirm correctness of event validation and forced refresh.
The _validateEvents method performs thorough checks, which is helpful. However, forcing a refresh if invalid events exceed the valid ones (>50%) might cause frequent refresh cycles if data temporarily skews.

Consider adding a short cooldown mechanism to avoid unnecessary repeated refreshes in transient states.

lib/component/blurhash_image_component/blurhash_image_component_web.dart (1)

40-55: Prevent layout issues by capping aspect ratios.
Capping aspect ratios between 0.3 and 3 is good, but it can alter the intended display drastically. If a user’s image is exceedingly wide/tall, they might see unexpected cropping or spacing.

Consider providing a fallback layout (e.g., letterboxing) or a more flexible range based on design specs if you want to preserve more of the original content shape.

Also applies to: 70-80

lib/router/group/all_group_posts_widget.dart (2)

58-114: Preloading content logic could fail if providers are not fully initialized.
The _preloadContent method depends on GroupFeedProvider and ListProvider. If one is unavailable, it logs an error but doesn’t retry or schedule another attempt.

Queue a second post-frame callback or a short retry mechanism to gracefully recover when providers become ready shortly after widget creation.


139-187: Cache restoration is helpful but might skip invalidation steps.
Force restoring events from the static cache is great for offline scenarios, but consider also removing stale or invalid entries that do not match the user’s current groups to keep the cache lean.

ios/Runner.xcodeproj/project.pbxproj (1)

143-143: Review new group and xcconfig file placements.

The Pods group references additional xcconfig files, and a new Frameworks group is introduced with Pods_Runner.framework. Confirm the grouping does not break any existing scripts or targets that expect these files under different folders.

Also applies to: 146-151, 156-163

lib/util/image/retry_http_file_service.dart (1)

2-2: Use of dart:developer for logging.

Using log() from dart:developer is fine for debugging. Be aware that in release mode, logs may get stripped out or be less visible. For production-level logging, consider a dedicated logging library if more control is needed.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 223f38b and 442ac47.

⛔ Files ignored due to path filters (2)
  • ios/Podfile.lock is excluded by !**/*.lock
  • macos/Podfile.lock is excluded by !**/*.lock
📒 Files selected for processing (57)
  • CHANGELOG.md (1 hunks)
  • ios/.xcode-version (1 hunks)
  • ios/Podfile (2 hunks)
  • ios/Runner.xcodeproj/project.pbxproj (11 hunks)
  • lib/component/blurhash_image_component/blurhash_image_component_io.dart (4 hunks)
  • lib/component/blurhash_image_component/blurhash_image_component_web.dart (1 hunks)
  • lib/component/blurhash_image_component/stub_platform.dart (1 hunks)
  • lib/component/content/content_image_widget.dart (3 hunks)
  • lib/component/content/content_widget.dart (1 hunks)
  • lib/component/event/event_list_widget.dart (3 hunks)
  • lib/component/event/event_main_widget.dart (3 hunks)
  • lib/component/event/event_reactions_widget.dart (6 hunks)
  • lib/component/event/event_top_widget.dart (2 hunks)
  • lib/component/image_preview_dialog.dart (1 hunks)
  • lib/component/image_widget.dart (2 hunks)
  • lib/component/input_field_widget.dart (3 hunks)
  • lib/component/styled_bot_toast.dart (5 hunks)
  • lib/consts/plur_colors.dart (1 hunks)
  • lib/consts/router_path.dart (1 hunks)
  • lib/features/communities/communities_grid_widget.dart (1 hunks)
  • lib/features/communities/communities_screen.dart (3 hunks)
  • lib/features/communities/community_title_widget.dart (1 hunks)
  • lib/features/communities/community_widget.dart (2 hunks)
  • lib/main.dart (12 hunks)
  • lib/provider/group_feed_provider.dart (8 hunks)
  • lib/provider/index_provider.dart (2 hunks)
  • lib/provider/list_provider.dart (9 hunks)
  • lib/provider/uploader.dart (1 hunks)
  • lib/router/dm/dm_detail_item_widget.dart (1 hunks)
  • lib/router/group/all_group_posts_widget.dart (6 hunks)
  • lib/router/group/communities_feed_widget.dart (1 hunks)
  • lib/router/group/group_detail_note_list_widget.dart (9 hunks)
  • lib/router/group/group_detail_provider.dart (9 hunks)
  • lib/router/group/group_info/group_info_menu_widget.dart (4 hunks)
  • lib/router/group/group_info/group_info_screen.dart (4 hunks)
  • lib/router/group/invite_people_widget.dart (1 hunks)
  • lib/router/group/no_communities_widget.dart (2 hunks)
  • lib/router/index/index_drawer_content.dart (1 hunks)
  • lib/router/index/index_widget.dart (2 hunks)
  • lib/router/onboarding/age_verification_step.dart (1 hunks)
  • lib/router/onboarding/name_input_step_widget.dart (1 hunks)
  • lib/router/onboarding/onboarding_screen.dart (5 hunks)
  • lib/router/onboarding/onboarding_step_widget.dart (6 hunks)
  • lib/router/thread_trace_router/thread_trace_widget.dart (2 hunks)
  • lib/util/community_join_util.dart (1 hunks)
  • lib/util/error_logger.dart (9 hunks)
  • lib/util/image/cache_manager_builder.dart (2 hunks)
  • lib/util/image/retry_http_file_service.dart (4 hunks)
  • lib/util/store_util.dart (3 hunks)
  • lib/util/theme_util.dart (1 hunks)
  • macos/Runner.xcodeproj/project.pbxproj (6 hunks)
  • macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme (4 hunks)
  • macos/Runner/AppDelegate.swift (1 hunks)
  • test/group_feed_provider_test.dart (2 hunks)
  • test/group_feed_provider_test.mocks.dart (0 hunks)
  • test/sign_up_test.mocks.dart (0 hunks)
  • test/tab_switching_performance_test.mocks.dart (2 hunks)
💤 Files with no reviewable changes (2)
  • test/group_feed_provider_test.mocks.dart
  • test/sign_up_test.mocks.dart
🔇 Additional comments (213)
ios/.xcode-version (1)

1-1: Xcode version updated from 16.2 to 16.3

This is a standard development environment update that ensures the iOS project is built with the latest tools.

CHANGELOG.md (1)

34-34: LGTM: New feature entry added to changelog

The added entry correctly documents the Communities link sidebar feature mentioned in the PR objectives.

lib/router/thread_trace_router/thread_trace_widget.dart (2)

4-4: Import added for PlurColors

This addition enables access to theme-aware color definitions for improved dark mode support.


118-118: Dark mode color fix implemented

Replacing the hardcoded theme color with PlurColors.separatorColor(context) improves theme consistency. This directly addresses the PR objective to fix dark mode colors.

lib/router/group/no_communities_widget.dart (2)

149-153: UI layout improvement: Row replaced with Wrap

Replacing Row with Wrap prevents potential overflow issues when the text gets too long. The added alignment and spacing properties ensure proper layout in all cases.


161-161: Text alignment improvement

Adding center alignment ensures consistent text display regardless of screen size.

macos/Runner.xcodeproj/project.pbxproj (4)

47-47: App name has been standardized to lowercase.

The application name has been changed from "Plur.app" to "plur.app", which is part of the application naming standardization. This change appears in multiple places throughout the file.


106-106: App name reference updated consistently.

The product reference has been updated to maintain consistency with the name change.


187-187: Product reference updated to match the new name.

Another reference to the application name has been updated for consistency.


436-436: Development team identifier has been updated.

The DEVELOPMENT_TEAM identifier has been changed to "GZCZBKH7MY" across all build configurations. This suggests a change in the team responsible for building and signing the application.

Make sure this development team identifier is correct and that all team members have access to the associated Apple Developer account.

Also applies to: 571-571, 600-600

lib/router/group/communities_feed_widget.dart (1)

10-12: Added debug logging to track screen display.

This logging statement helps with debugging by tracking when the CommunitiesFeedWidget is displayed. This supports the PR objective of adding a Communities link to the sidebar for navigation to the groups page.

lib/features/communities/community_title_widget.dart (1)

41-41: Improved display of community titles by increasing max lines.

Increasing the maximum lines from 2 to 3 allows longer community titles to be more fully displayed before being truncated with an ellipsis. This improves readability and user experience when communities have longer names.

lib/router/onboarding/age_verification_step.dart (1)

22-22: Changed emoji for age verification to family icon.

The emoji for the age verification step has been updated from a driver's license to a family emoji, which may better represent the purpose of age verification in a family-oriented context.

lib/consts/router_path.dart (1)

58-58: LGTM: New router path for group media.

This new route path constant properly follows the existing naming conventions and supports the feature to navigate to group media.

lib/features/communities/community_widget.dart (2)

49-50: Height adjustment improves text accommodation.

Increasing the height from the default to 70 pixels provides more space for the community title text, preventing potential truncation.


68-69: Consistent height adjustment in loading state.

Good consistency maintaining the same height value in both data and loading states.

macos/Runner/AppDelegate.swift (1)

10-12: Support for secure restorable state added for macOS.

This override correctly implements support for secure state restoration, which is a recommended practice for macOS applications.

lib/router/onboarding/name_input_step_widget.dart (1)

48-50: New comment adds clarity about emoji usage.

The added comment explains the design decision to use a simple emoji instead of a custom IconWidget, providing good context for future developers.

lib/router/group/invite_people_widget.dart (2)

147-149: Improved layout container structure.

Replacing ConstrainedBox with Container and setting width explicitly improves layout predictability. The Container provides a more straightforward way to handle dimensions.


156-156: Helpful comment for layout decision.

Good practice to explain why mainAxisSize: MainAxisSize.min is being used. This helps other developers understand the intention behind this property setting and prevents accidental changes that could reintroduce layout issues.

ios/Podfile (2)

7-8: Added source for CocoaPods.

Using GitHub as the source instead of CDN is a good solution for connection issues. This change improves build reliability.


39-42:

Details

❓ Verification inconclusive

Explicitly added Firebase dependencies.

Explicitly declaring Firebase dependencies ensures these components are properly installed regardless of transitive dependencies. This is a good practice for critical dependencies.

However, ensure that these Firebase dependencies align with the versions expected by Flutter Firebase plugins to avoid compatibility issues.


🏁 Script executed:

#!/bin/bash
# Check Flutter Firebase plugin versions in pubspec.yaml
grep -A 10 "firebase" pubspec.yaml || echo "No Firebase packages found in pubspec.yaml"

# Check if Firebase plugins are installed
flutter pub deps | grep -i firebase || echo "No Firebase packages installed"

Length of output: 516


Notice: Verify CocoaPods Version Alignment with Flutter Firebase Plugins

Explicitly adding Firebase dependencies in the iOS Podfile ensures that critical Firebase components are installed regardless of transitive changes, which is a good practice. The pubspec.yaml shows the following Firebase package versions that need to be aligned with the resolved iOS Firebase pods:

  • firebase_core: ^3.12.1
  • firebase_messaging: ^15.2.4

Please ensure that the CocoaPods-resolved versions for Firebase/Core and Firebase/Messaging match the expectations of these Flutter Firebase plugins to avoid compatibility issues. Note that our automated check using flutter pub deps did not run successfully due to environment limitations (it reported “flutter: command not found”), so it’s recommended to verify this alignment in your local environment.

lib/features/communities/communities_grid_widget.dart (2)

21-21: Improved padding for better visual spacing.

The increased bottom padding (80) helps with content visibility, especially on screens with bottom navigation or system bars. This aligns with the PR objective to improve UI.


25-26: Enhanced grid layout proportions.

The increased spacing (40.0) and adjusted aspect ratio (0.9) create a more visually pleasing grid with better content visibility. This change improves the overall user experience by giving community cards more breathing room.

lib/router/index/index_drawer_content.dart (1)

164-173:

Details

❌ Incorrect review comment

Navigation added: COMMUNITIES link in the sidebar

The new COMMUNITIES link has been added to the sidebar as specified in the PR objectives. This enhances navigation by allowing users to easily access the groups page.

One concern: the onTap handler sets the current tap index to 0, which is the same index used by the HOME option (line 133). This might lead to navigation confusion if both options point to the same screen.


🏁 Script executed:

#!/bin/bash
# Check if there's a correlation between the Communities navigation and index 0
# Also verify if there's documentation or another implementation related to this routing

# Look for any references to Communities in the router files
rg -A 2 "Communities" --glob "lib/router/**/*.dart"

# Check for index usage to see if index 0 is mapped to different screens in different contexts
rg "currentTap == 0" --glob "lib/**/*.dart"

Length of output: 10849


Verified: Communities Navigation is Correctly Configured

After verifying the codebase:

  • The drawer’s onTap handler in lib/router/index/index_drawer_content.dart sets currentTap to 0.
  • In lib/router/index/index_widget.dart, case 0 is clearly mapped to the CommunitiesScreen.
  • There is no conflicting “HOME” option using index 0—the intended design is that index 0 corresponds to Communities.

The earlier concern about potential navigation confusion is not applicable. Please disregard the comment regarding the HOME option conflict.

Likely an incorrect or invalid review comment.

lib/component/content/content_widget.dart (1)

1162-1165: Dark mode text color enhancement

The text styling has been updated to utilize the theme context, making it adaptable to light and dark mode. This change supports the PR objective of fixing dark mode text colors to align with design specifications.

lib/component/event/event_list_widget.dart (2)

72-73: Improved card background theming for dark mode

Replaced hardcoded card background color with theme-aware color from custom theme extension. This change supports the PR objective of adjusting card backgrounds to ensure adequate contrast in dark mode.


109-110: Enhanced separator color for dark mode

Updated separator color to use theme-dependent color from custom theme extension. This change contributes to the overall theme consistency in dark mode.

lib/router/dm/dm_detail_item_widget.dart (1)

315-316: Dark mode timestamp color improvement

Updated timestamp styling to use context-aware PlurColors.timestampStyle(context) instead of a static style. This ensures proper color adaptation in dark mode to align with the design specifications.

lib/provider/uploader.dart (2)

4-4: Good addition for cross-platform compatibility.

Adding the kIsWeb import from foundation.dart allows for proper platform detection, which will be useful for conditional logic based on whether the app is running on web or not.


9-10: Good use of conditional imports for web compatibility.

Using conditional imports with the if (dart.library.js) directive is the proper way to handle platform-specific dependencies in Flutter. This prevents issues with the path_provider package when running in a web context, which would otherwise cause runtime errors since this package has platform-specific implementations.

macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme (2)

18-18: Consistent app naming across configurations.

The change from "Plur.app" to "plur.app" ensures naming consistency throughout the project files, which is important for macOS build configurations.

Also applies to: 34-34, 58-58, 75-75


51-51: Added GPU validation for better debugging.

The enableGPUValidationMode = "1" setting enables GPU validation during app execution, which can help identify rendering issues during development and testing.

test/tab_switching_performance_test.mocks.dart (1)

43-43: Updated class naming in generated test code.

Changing from _FakeSettingData_2 to _FakeSettingData_1 keeps mock classes properly indexed after removal of other mock classes elsewhere in the file. Since this is auto-generated code, the change is likely automatic and maintains consistency with the rest of the test infrastructure.

lib/component/image_preview_dialog.dart (1)

230-270: Improved error handling with structured try-catch blocks.

The enhanced error handling in _doSaveImage method is a significant improvement. By implementing nested try-catch blocks with specific error logging, the code now gracefully handles failures during image processing and saving operations while providing useful diagnostic information.

Key improvements:

  • Outer try-catch captures any method-level errors
  • Inner try-catch specifically handles image processing errors
  • Proper null checking for image bytes
  • Detailed logging with context for different failure scenarios

This structured approach will make troubleshooting much easier while preventing crashes.

lib/component/input_field_widget.dart (6)

16-17: Good addition of dark mode support

Adding the isDarkMode parameter allows for explicit theme control when needed, which aligns with the PR's goal of fixing dark mode colors.


29-36: Well-implemented theme detection with fallback

Good approach to first check the explicit parameter and then fall back to context theme. The adaptive color definitions are clear and organized, improving maintainability.


48-53: Good text styling for dark mode

The hint text color now properly adapts to the theme, improving readability in dark mode as per the PR objectives.


54-56: Improved background color handling

The fillColor now adapts based on theme, providing better contrast in dark mode as specified in the PR objectives.


57-62: Proper border treatment for dark mode

The border color now adapts to the theme, providing better visual definition in dark mode.


73-78: Enhanced text readability in dark mode

The text color adaptation provides better contrast in dark mode, directly addressing the PR objective of fixing dark mode text colors.

lib/router/onboarding/onboarding_step_widget.dart (6)

52-63: Comprehensive theme adaptation for dark mode

Excellent implementation of theme-adaptive colors with clearly named variables. The defined colors for various UI elements provide appropriate contrast in dark mode, which directly addresses the PR objective of fixing dark mode colors.


76-85: Improved card background and shadow in dark mode

The card background and shadow now adapt to the theme, improving visual hierarchy and readability in dark mode as specified in the PR objectives.


92-106: Enhanced emoji container styling

The emoji container color now adapts to dark mode with appropriate alpha values, maintaining visual consistency across themes.


116-124: Better title contrast in dark mode

The title color now adapts to provide proper contrast in dark mode, addressing the PR objective of correcting text colors to align with design specifications.


134-142: Improved description text readability

The description text color and weight have been adjusted for better contrast in dark mode, and the slightly bolder font weight enhances readability.


146-146: Proper theme propagation to child components

Passing the isDarkMode parameter to the InputFieldWidget ensures consistent theming throughout the UI hierarchy.

lib/component/content/content_image_widget.dart (6)

56-72: Improved URL validation

Good defensive coding by validating image URLs and providing a clean placeholder for invalid URLs. This prevents potential crashes and improves the user experience.


117-131: Good error recovery with fallback

The catch block provides a clean fallback in case of widget building errors, enhancing app stability.


146-153: Improved handling of problematic URLs

Good filtering of empty, invalid, and known problematic URLs before processing, which prevents potential issues during image loading.


158-176: Comprehensive format handling

The enhanced detection and handling of problematic image formats improves compatibility, especially on web platforms.


194-196: Added URL validation

Good practice to verify that image URLs use proper http/https protocols before attempting to load them.


221-231: Improved dialog error handling

The try-catch block around the image preview dialog prevents crashes if the dialog fails to display properly.

lib/features/communities/communities_screen.dart (4)

166-170: Added debug logging for view mode changes

The logging helps with debugging theme and view mode transitions, supporting the PR's goal of UI improvements.


223-231: Enhanced widget caching diagnostics

The debug logs for widget creation and reuse improve visibility into the caching system's behavior, helping to identify potential performance issues.


236-247: Improved grid widget caching logging

Similar to the feed widget, the grid widget now has better logging for cache behavior tracking.


298-305: Enhanced view toggle with logging

The view mode toggle now includes debugging information, making it easier to track user interactions and verify the Communities link functionality described in the PR objectives.

lib/router/onboarding/onboarding_screen.dart (8)

48-62: Well implemented theme-adaptive color handling!

Good addition of dynamic color variables that adapt based on the theme mode. This approach ensures consistent appearance in both light and dark modes, aligning with the PR objective of fixing dark mode colors.


65-65: Good Scaffold background color update

The Scaffold now properly uses the theme-adaptive background color variable instead of a hardcoded color.


76-77: Appropriate gradient color adaptation

The gradient colors are now properly adapted based on the theme mode, improving the visual consistency.


94-94: Good logo color adaptation

The logo color now properly adapts based on theme mode, using the new theme-aware color variable.


145-146: Good indicator color adaptation

The page indicator colors now properly adapt to the theme.


153-153: UI spacing adjustment

Appropriate reduction of bottom padding for better spacing.


158-158: Good version text color adaptation

The version text color now properly adapts based on theme mode.


124-127: Good position adjustment to avoid UI overlap

Moving the indicator position up from 24 to 16 helps avoid overlap with buttons, improving the overall UI layout.

lib/router/group/group_info/group_info_menu_widget.dart (5)

12-16: Added new media option to menu items enum

Good addition of the new media option to the GroupInfoMenuItem enum. This aligns with adding enhanced UI options as mentioned in the PR objectives.


23-24: Good localization for new menu item

The title for the new media menu item is properly retrieved from the localization system.


36-37: Appropriate icon selection for media menu item

The Icons.photo_library_outlined icon appropriately represents the media functionality.


117-121: Feature temporarily disabled with clear TODO

Good practice to comment out the not-yet-complete media feature with a clear TODO note. This prevents exposing unfinished features while keeping the code ready for future implementation.


144-146: Added routing for new media menu item

Good implementation of the router path handling for the media menu item, ensuring the navigation works when the feature is enabled.

lib/provider/index_provider.dart (4)

17-24: Proper resource cleanup in dispose method

Good implementation of the dispose method to clear the static reference when disposed, preventing potential memory leaks.


27-27: Added static reference for global access

The static reference enables global access to the provider instance, which is necessary for the new static methods.


30-42: Good implementation of static methods for global view mode control

The static methods provide a convenient way to control the community view mode without requiring a context. These methods appropriately check if the instance exists before updating.


52-53: Updated constructor to store instance reference

The constructor now properly stores a reference to itself in the static variable, enabling the global access mechanism.

lib/util/error_logger.dart (10)

33-61: Improved error handling for image errors

Good enhancement to differentiate between image encoding errors and other errors. This change helps reduce noise in the logs and prevents unnecessary error notifications to users for common, non-critical issues.


67-93: Consistent handling of platform errors

The same pattern for handling image errors has been consistently applied to platform errors, which maintains code coherence.


107-133: Enhanced logError method with image error detection

Good improvement to the logError method to handle image encoding errors differently, using appropriate log levels and avoiding unnecessary user notifications.


138-143: Suppressed toast notifications for image errors

Good enhancement to skip showing toast notifications for image encoding errors, which prevents distracting users with non-critical issues.


172-177: Consistent toast suppression in context-based method

The same pattern for suppressing toast notifications for image errors has been applied to the context-based method, maintaining consistency.


209-226: Improved error handling in runWithCatch

Good enhancement to the runWithCatch method to provide different handling for image encoding errors, ensuring the appropriate level of user feedback.


267-291: Enhanced error boundary handling for image errors

Good improvement to the error boundary to handle image encoding errors differently, preventing unnecessary UI disruptions.


303-326: Optimized captureError method

The captureError method now appropriately skips updating state for image encoding errors, preventing unnecessary UI rebuilds.


359-376: Smart error UI suppression for image errors

Excellent enhancement to skip showing the error UI for image encoding errors and instead return the original child widget. This provides a much better user experience.


424-436: Consistent error handling in build method catch block

The catch block in the build method now consistently handles image encoding errors, providing a graceful fallback without disrupting the user experience.

lib/component/styled_bot_toast.dart (5)

24-28: Improved error suppression for toast messages.

The new error suppression logic prevents unnecessary noise from being shown to users when certain errors occur, particularly for image loading issues that don't require user attention.


60-64: Consistent error suppression strategy applied to error toasts.

Good implementation of the centralized error suppression logic across different toast types, ensuring consistent behavior for error messages.


89-93: Applied error suppression to success toasts for consistency.

Consistently applying the same suppression patterns across all toast types maintains predictable behavior throughout the application.


137-141: Improved error handling in the core toast display method.

Implementing suppression at the lower level ensures that all toast paths benefit from the enhanced error handling strategy.


231-236: Consistent application of error suppression to fallback toast.

Good job ensuring the suppression logic is applied even to the fallback toast mechanism.

lib/component/event/event_top_widget.dart (3)

113-120: Enhanced username styling with theme support.

Updated the username style to respect the current theme context, improving dark mode appearance by using the appropriate color.


127-133: Improved timestamp readability with theme-adaptive styling.

The timestamp now correctly adapts to the current theme, enhancing visibility in dark mode.


146-147: Added theme support for handle style.

The NIP-05 identifier now uses a theme-aware style, aligning with the PR objective to improve text visibility in dark mode.

lib/component/event/event_reactions_widget.dart (4)

9-9: Added essential theme-related imports.

The new imports enable theme-adaptive styling for the reactions widget.

Also applies to: 16-16


205-208: Enhanced card styling with theme-aware background.

Added proper background color and border radius to reaction cards, improving their appearance in dark mode by using the theme's card background color.


298-317: Improved reaction button styling with theme support.

The reaction buttons now intelligently adapt their colors based on theme and active state. The active state uses PlurColors.primaryPurple (#B6A0E1) which aligns with the PR objective to use this specific color for dark mode.


331-332: Applied consistent theme colors to button elements.

Button icons and text now use the same theme-aware color logic, ensuring visual consistency.

Also applies to: 340-341

lib/util/community_join_util.dart (5)

14-16: Added helpful debug logging.

New log statements improve traceability when troubleshooting community joining issues.


21-22: Enhanced relay parameter handling.

The code now properly extracts and validates relay parameters from join links, ensuring that users can successfully join communities even when custom relays are specified.

Also applies to: 34-45


30-33: Improved reliability with multiple relay attempts.

The implementation now tries multiple relays including fallbacks, significantly increasing the chances of successful community joins.

Also applies to: 47-51


60-72: Implemented robust multi-relay join strategy.

The function now attempts to join with each configured relay, with helpful logging to track the process.


54-58: Enhanced logging for debugging.

Comprehensive logging helps track the community join process, making it easier to identify and troubleshoot issues in production.

Also applies to: 74-78, 80-80

lib/router/group/group_info/group_info_screen.dart (7)

31-31: Class signature updated correctly after removing TabController

The removal of SingleTickerProviderStateMixin is appropriate since the class no longer uses a TabController for the tab-based interface.


97-107: UI layout improvement: Scrollable view replaces tabs

Replacing the tabbed interface with a SingleChildScrollView and Column layout improves usability by allowing users to see all content without switching tabs. The constraint setting ensures consistency across different screen sizes.


108-133: Good conditional display of the About section

The About section is well-implemented with proper conditionals to only display when content is available, appropriate spacing, and consistent styling with the theme.


135-185: Well-organized action buttons with conditional admin actions

The action buttons section is well-structured with:

  • Clear section heading
  • Consistent button styling
  • Proper spacing between buttons
  • Conditional rendering for admin-only actions
  • "Coming soon" indicators for incomplete features

192-227: Clean implementation of members list with "See All" capability

The members list section provides a good overview with the ability to see all members through the dedicated button, maintaining a clean and focused UI.


379-384: Improved member grid to prevent overflow issues

The adjustments to spacing, avatar size, and grid height calculation help prevent layout overflow issues that could occur with the previous implementation.


435-486: Responsive member item layout with LayoutBuilder

Using LayoutBuilder ensures member items adapt correctly to available space, preventing overflow issues. The truncated name display and smaller font size help maintain a clean appearance.

lib/util/store_util.dart (4)

3-10: Good platform compatibility with conditional imports

Adding the kIsWeb import and using conditional imports for path_provider is the correct approach for ensuring web compatibility.


20-33: Improved web platform handling for file paths

The implementation now properly handles web platforms by returning an empty string instead of attempting file system operations that would fail. The addition of try-catch blocks provides robust error handling.


63-88: Safe file handling for web platforms

The changes correctly check for web platform before attempting file operations and gracefully handle errors with appropriate fallbacks.


94-115: Consistent web platform checks for MD5 file operations

This implementation follows the same pattern of checking for web platform compatibility and providing error handling, maintaining consistency throughout the class.

lib/util/theme_util.dart (2)

58-75: Updated light theme colors to match Plur design system

The light theme colors have been updated to align with the Plur design system:

  • Changed accent color to purple (#7445FE)
  • Updated app background to white
  • Adjusted card backgrounds and text colors for consistency

The color changes are well-documented with clear comments referencing the design system.


77-94: Updated dark theme colors per PR requirements

The dark theme updates address the PR objectives by:

  • Setting primary text color to #B6A0E1 (as specified in the PR)
  • Adjusting card background and content colors for better contrast
  • Ensuring proper readability in dark mode

These changes align perfectly with the PR objectives to fix dark mode colors.

lib/component/blurhash_image_component/blurhash_image_component_io.dart (6)

4-6: Safe platform detection implementation

Using direct imports with the safe wrapper pattern and the new SafePlatform class improves cross-platform compatibility.


11-27: Improved platform-specific handling for blurhash

The implementation now properly checks for iOS/macOS platforms and null blurhash values, with appropriate fallbacks to prevent crashes on unsupported platforms.


63-66: Aspect ratio constraints prevent layout issues

Adding bounds for aspect ratio (between 0.3 and 3) prevents extreme values that could break layouts, particularly important for user-generated content with unpredictable dimensions.


72-78: Improved blurhash validation

The additional validation for blurhash format (checking for empty strings or too-short values) prevents potential rendering issues with invalid data.


92-94: Silent error handling with graceful fallback

Handling image loading errors by showing a placeholder instead of error messages provides a better user experience when images can't be loaded properly.


127-168: Redesigned placeholder with improved aspect ratio handling

The placeholder implementation has been improved with:

  • Better aspect ratio calculations with bounds checking
  • Simplified visual design
  • Proper error handling with fallback options
  • Consistent styling using theme colors

This creates a more consistent and reliable image loading experience.

lib/component/event/event_main_widget.dart (6)

6-6: New imports for styling and color constants
These new imports integrate well with the existing theme logic. Good job keeping design assets (fonts/colors) centralized.

Also applies to: 8-8, 16-16


794-804: Ensure themeData.customColors extension is safely provided
Accessing themeData.customColors.cardBgColor relies on a theme extension. Make sure the extension is always registered and available in all app routes to avoid runtime null issues.


810-810: Icon color usage
Replacing a hardcoded icon color with PlurColors.textColor(context) helps maintain dark mode consistency. This is a solid approach.


826-831: Sensitive content label
The label styling aligns with the newly introduced theme approach. Good job ensuring uniform typography for warnings.


847-847: Primary brand color usage
Explicitly setting PlurColors.primaryPurple enforces brand identity. Keep verifying that it meets contrast guidelines in dark mode.


852-857: Button text style
Using white text with semi-bold weight is a good choice for visual emphasis on a dark background.

lib/provider/group_feed_provider.dart (8)

68-88: onNewEvent logic for group notes
Skipping duplicates in notesBox and merging user’s own posts automatically is a clear approach. Make sure large merges don’t cause frame drops during UI rebuild.


145-203: Cache restoration with detailed logging
Excellent approach to verifying event validity in _staticEventCache before restoring. Watch out for potential repeated logs in production environments.


259-266: Fallback timeout for loading indicator
Helpful for ensuring the UI is not stuck in a loading state. Good defensive mechanism.


271-334: hasValidGroupTag method
Comprehensive checks for group membership and logs are thorough. This improves debugging but be mindful of potential overhead when handling large volumes of events.


336-403: onEvent batch processing
Batch event handling and summary logs are well-structured. Great job summarizing valid vs. rejected events.


423-520: refresh method with forced queries and re-subscription
Clearing caches, resetting _initTime, and forcing extra queries ensures fresh data. This is a robust refresh flow.


545-545: Early exit for empty group list
Properly checking for no groups is a good safeguard to prevent unnecessary subscriptions.


569-572: Relay addition loop
Appending unique relay hosts from groups is sound. Optionally consider advanced network error handling or fallback logic.

lib/util/image/cache_manager_builder.dart (4)

1-2: Added import 'dart:developer' and kIsWeb
Using kIsWeb for platform checks is appropriate, and log helps unify debugging output.


12-19: Skipping the cache manager on web
Preventing unintended disk cache usage in web builds is a solid approach to avoid errors or wasted resources.


20-49: Cache manager creation with fallback
Strong error handling and fallbacks ensure partial functionality if the primary setup fails. This is robust design.


50-69: clearCache method
Providing a web bypass and try-catch around emptyCache() is prudent. Ensures the app remains stable even if clearing fails.

lib/component/blurhash_image_component/stub_platform.dart (1)

1-3: Good use of descriptive comments.

These top-level comments clearly explain the file's purpose and how it's used across various platforms.

lib/main.dart (10)

8-8: Good usage of kIsWeb.

Importing kIsWeb here enables convenient platform checks without referencing dart:io. No issues found.


253-262: Safe fallback approach for cache manager.

Skipping cache manager initialization on web with a try-catch is a straightforward solution to platform-specific issues. No major concerns.


297-307: Clean web/desktop segregation.

Skipping MediaKit on web is appropriate given known web incompatibilities. The try-catch gracefully handles failures on desktop. Looks good.


423-426: Good defensive logic for skipping Sentry on web.

This ensures no unexpected errors from unsupported Sentry features on the web.


528-541: Well-structured theme gating.

Forcing light/dark/auto is clearly implemented. Ensure there's a test verifying correct fallback to system theme for any new devices or custom OS toggles.


685-712: Wrapping BotToast in a try-catch.

Catching BotToast errors is prudent. The fallback to an empty container for image encoding errors is also a nice user experience win.


802-806: Conditional theme logic is consistent.

Explicit ThemeMode usage for user preferences is correct. No issues discovered.


864-880: New route for group media.

The approach for optional arguments is consistent with other route logic. Confirm that GroupMediaScreen handles missing or incorrect arguments gracefully, if it’s not guaranteed.

Would you like me to generate a minimal additional test for the groupMedia route argument?


167-167:

Details

✅ Verification successful

Ensure correct usage of nullable cache manager.

Changing imageLocalCacheManager to nullable is sensible for web environments. However, confirm that any references to this variable handle null gracefully.


🏁 Script executed:

#!/bin/bash
# Find all usages of 'imageLocalCacheManager' and ensure they're null-checked
rg -A 5 "imageLocalCacheManager" lib/

Length of output: 3828


Nullable Cache Manager Usage Verified

The grep results confirm that all references to imageLocalCacheManager are properly guarded with null checks. Specifically:

  • In cache_manager_builder.dart, the variable is assigned null when appropriate and is subsequently null-checked before being used (with explicit checks and use of ! where safe).
  • In both the image widget files, null checks (if (imageLocalCacheManager != null)) ensure that a valid cache manager is present before being used.

No changes are required based on our verification.


41-42:

Details

❓ Verification inconclusive

Imports appear consistent.

New imports for group-specific media widgets look good. Verify you have tests covering routes to these new screens.

Would you like to run a quick test coverage check on these routes using a shell script that scans for any references to GroupMediaScreen or GroupMediaGridWidget in your test files?


🏁 Script executed:

#!/bin/bash
rg -A 5 "GroupMedia(Screen|GridWidget)" test/

Length of output: 47


Action: Verify Test Coverage for Group-Specific Media Routes

The imports in lib/main.dart (lines 41-42) are correct and reflect the new group-specific media widgets. However, our automated scan (using rg -A 5 "GroupMedia(Screen|GridWidget)" test/) did not return any references in the test files. This raises a concern that tests for these new routes may be missing or not named as expected. Please manually verify that there are adequate tests covering these routes or add the necessary tests if they are absent.

lib/provider/list_provider.dart (10)

18-18: New import recognized.

Importing group_feed_provider.dart for better coordination is clear. All good.


23-25: Static reference to GroupFeedProvider.

This sets up inter-provider communication. Ensure you handle potential null references to groupFeedProvider carefully (e.g. during tests or early initialization).


371-373: Logging join requests is beneficial.

These logs improve traceability and debugging for group join flows. No major concerns.

Also applies to: 375-375


377-380: Clear user feedback on re-joins.

Good job short-circuiting repeated membership attempts.


382-384: Ensuring group metadata fetch.

Proactively fetching group metadata if membership is known is helpful. Great for ensuring UI has fresh info.

Also applies to: 391-391


429-429: Elevated logging.

Helpful for debugging which relay is used during group join calls.

Also applies to: 432-432


459-459: Planned delay for membership processing.

Waiting a few seconds helps slow relays process membership. This is a practical workaround. No immediate issues.

Also applies to: 463-466


586-607: Strong post-join logic.

The code ensures the group is reflected in both the local data structures and UI. Keep an eye on new group concurrency situations if user attempts multiple joins in quick succession.


836-868: Setting group metadata.

Neatly sets defaults. If you want to allow custom images or descriptions at creation time, you may prompt the user or pass optional parameters. The approach is otherwise fine.


961-1003: Metadata re-check when adding group.

Adding both the specific host version and the default relay fallback is a smart tactic. Also, verifying for duplicates is wise. This ensures robust group coverage.

test/group_feed_provider_test.dart (11)

3-6: Imports look fine.
No issues with these added imports, and they appear consistent with the testing framework.


160-215: Good test coverage for event filtering logic.
This test thoroughly verifies that only matching events get added to the notes box. The logic and assertions look correct.


216-216: Empty line addition.
No concerns here.


217-245: Event processing test correctness.
Ensures that an event with a valid group tag is properly recognized and added. The expectations and usage of notesBox.contains are correct.


249-288: Valid vs. invalid group tag test.
This test covers both valid and invalid cases effectively. It’s a solid approach to verifying onNewEvent.


291-296: New test group creation.
Introducing a dedicated group for in-depth tag handling tests is a good organizational choice.


316-356: Group tag validation test logic.
Straightforward coverage for events with valid and invalid tags, ensuring hasValidGroupTag behaves as expected.


358-393: Edge cases and multiple tag mixes.
Successfully validates complex tag scenarios, including malformed tags. This provides valuable coverage.


395-441: Subscription event handling.
Checks valid/invalid tags under subscription flow. The logic is well-structured, ensuring only valid events make it into newNotesBox.


442-465: Handling extra data in valid tags.
This test confirms that tags with additional fields don’t break validation. Implementation appears solid and reliable.


467-494: Non-group events handling.
Correctly verifies that non-group notes are skipped even if they have a group tag. This maintains logical isolation for group-only flows.

lib/component/image_widget.dart (10)

5-5: Platform detection import.
Using stub_platform.dart aligns with the introduced SafePlatform checks.


29-29: Extension list expanded to include .jfif.
Good to see the coverage for another potential format that might cause issues on web.


35-57: Robust URL processing.
The try-catch safeguards against unexpected failures, returning the original URL on error. This helps maintain resiliency.


66-83: Initial checks for empty or unsupported formats on web.
Returning an error widget early prevents unnecessary load attempts. This is a clean and proactive approach.


85-92: Processed URL fallback logic.
Falling back to the original URL if processing fails ensures safe operation.


95-134: Web vs. cached image loading flow.
The branching logic for web vs. non-web is clear, with thorough fallback steps. Proper error and loading placeholders are provided.


138-167: Fallback to Image.network when cache manager is unavailable.
Gracefully handled, with consistent loading/error placeholders.


182-231: Web-specific method _buildWebImageWidget.
Dedicated handling for web scenarios is well-structured. The early checks for invalid or problematic URLs reduce error noise.


234-260: Detecting unsupported or malformed URLs.
Using _isUnsupportedFormatOnWeb with additional checks for known problematic hosts is a good defensive strategy.


264-281: Simple and consistent error widget.
The minimal appearance avoids confusing extra icons or text. Handling UI fallback robustly is commendable.

lib/router/group/group_detail_provider.dart (14)

2-3: Import changes.
No issues with the added imports for dart:developer and the new provider reference.


23-28: New fields for loading state and caching.
Introducing isLoading and _eventCache improves state tracking and event retrieval performance.


53-57: onNewEvent caches all events.
Storing events in _eventCache regardless of type is a helpful addition for quick lookups.


109-127: Handling null groupIdentifier in doQuery.
The logging and early exit logic prevent unnecessary queries, ensuring correctness in edge cases.


129-158: Querying multiple relays and fallback approach.
Covers both the group’s specific relay and default relay, maximizing event coverage. The try-catch ensures graceful error handling.


160-167: Loading indicator reset on query error.
Ensuring the UI is updated to reflect no longer loading is important for good user experience.


169-175: Scheduled timeout to remove loading state.
A practical safeguard if no events arrive in time.


182-209: onEvent logs and caching.
The event count check precisely determines when to deactivate loading, and _eventCache updates appear consistent.


225-236: _isEventForThisGroup checks the h-tag carefully.
Filtering on the correct tag index ensures only relevant events are processed. Logic is clear and robust.


253-254: Purging from _eventCache when deleting events.
Ensures the local cache remains in sync with removed events.


267-275: Updating group identifier with logging.
The transition to a new group triggers a data clear and sets isLoading. This flow is straightforward, preventing stale data.


283-296: Refresh flow clearing data and re-querying.
Reinitializing _initTime and redoing the query is a succinct approach to ensure fresh data.


335-339: Direct event handling caches the event.
Proper sorting and notification keep the UI consistent.


345-348: getEventById method.
Clean, straightforward retrieval from _eventCache. This provides a nice utility for other features.

lib/router/group/group_detail_note_list_widget.dart (1)

52-63: Validate cache reset logic for new groups.
When a new group is detected, the cache is cleared. Ensure that this logic covers all edge cases where multiple group pages might be viewed concurrently, or if the widget is reattached to the same group after removal.

Could you confirm whether only one instance of GroupDetailNoteListWidget is active at a time in practice? If multiple are active, we may need group-specific cache keys.

lib/component/blurhash_image_component/blurhash_image_component_web.dart (1)

5-10: Removed platform checks simplify logic.
Approach of returning a placeholder for all web builds is straightforward and reduces external dependency conflicts.

lib/router/group/all_group_posts_widget.dart (1)

421-501: Validation logic is thorough; consider partial acceptance.
Rejecting or counting events as invalid if they don’t match a user’s group is logical. However, forcibly refreshing whenever invalid events heavily outnumber valid ones can be too aggressive in large user bases.

If the feed can hold a variety of events (not strictly limited to user groups), consider an alternative design to store and show only relevant events without forcing repeated queries.

ios/Runner.xcodeproj/project.pbxproj (5)

10-10: Confirm framework integration.

A new build file reference is added for Pods_Runner.framework. Ensure that this framework is properly produced and embedded during the build process (e.g., via CocoaPods).

Would you like to verify that the file reference to Pods_Runner.framework appears in the actual output directory?


78-79: Check the framework references across build phases.

The Pods_Runner.framework file reference is also added to the Frameworks build phase. Ensure there are no duplicate references and that no conflicts exist with other frameworks.

Also applies to: 87-87


170-171: Ensure correct build phase ordering.

The addition of [CP] Check Pods Manifest.lock, [CP] Embed Pods Frameworks, and [CP] Copy Pods Resources references may alter the build phase sequence. Validate that each phase is triggered in the correct order, especially if the app depends on specific file copying or script execution timing.

Also applies to: 178-179, 260-294


463-465: Validate automatic code signing setup.

The switch to CODE_SIGN_STYLE = Automatic, with CODE_SIGN_IDENTITY = "Apple Development" and DEVELOPMENT_TEAM = GZCZBKH7MY, requires matching provisioning profiles. Confirm that your developer portal settings align with these identifiers to avoid code-signing errors.

Also applies to: 627-629, 707-709, 871-873


35-41:

Details

❓ Verification inconclusive

Validate added Xcconfig references.

Multiple new xcconfig references (Pods-Runner.debug.xcconfig, Pods-Runner.release.xcconfig, Pods-Runner.debug-runner-dev.xcconfig) are introduced. Verify that each file exists at the specified path and is correctly configured to avoid build failures.


🏁 Script executed:

#!/bin/bash
# Searches for the newly added xcconfig files in the repository
fd -E 'node_modules' -E 'build' -E 'DerivedData' -i -p 'Pods-Runner.debug.xcconfig' 'Pods-Runner.release.xcconfig' 'Pods-Runner.debug-runner-dev.xcconfig'

Length of output: 344


Action Required: Validate New xcconfig References

The project file now includes three new xcconfig references. Please confirm that the following files exist at the expected locations and are correctly configured to prevent build issues:

  • Pods-Runner.debug.xcconfig
    → Expected path: Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig
  • Pods-Runner.release.xcconfig
    → Expected path: Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig
  • Pods-Runner.debug-runner-dev.xcconfig
    → Expected path: Target Support Files/Pods-Runner/Pods-Runner.debug-runner-dev.xcconfig

The initial verification script encountered errors because it was improperly searching for the files as directories. Please run the corrected script below to search for these files within the repository:

#!/bin/bash
# Loop through the expected xcconfig file names and search the repository
for file in "Pods-Runner.debug.xcconfig" "Pods-Runner.release.xcconfig" "Pods-Runner.debug-runner-dev.xcconfig"; do
   echo "Searching for $file:"
   fd --type f "$file" .
done

Once you verify that these files exist at the expected paths (and review their configurations), please update this review comment accordingly.

lib/util/image/retry_http_file_service.dart (6)

23-27: Robust invalid-URL checks.

Great job handling empty, 'null', and 'undefined' URLs before proceeding. Consider including additional checks for non-HTTP(S) schemes if you want to block unusual protocols.

Also applies to: 30-30


33-40: Safe base64 decoding logic.

Wrapping the base64 decode in a try-catch block is a solid approach. This ensures the function gracefully handles malformed data instead of throwing uncaught exceptions.


43-51: Validate parsed Uri.

You ensure that Uri.parse(url) has a scheme and authority. This is helpful to prevent invalid URI usage. Logging the exception and returning an empty response is a clean fallback for ill-formed URLs.

Also applies to: 52-53


68-92: Redirect and retry logic.

Your approach to follow redirects and then optionally retry via the proxy on HTTP error (> 299) ensures robust error handling. Also, the catchAll block for unexpected errors is a good fail-safe.

Also applies to: 94-102, 103-104, 107-109


112-116: Proxy-based retry and empty responses.

Creating _createEmptyResponse for invalid scenarios and logging errors in the proxy path handled within retry() is a neat solution. This helps keep the code readable and prevents unhandled exceptions.

Also applies to: 120-157


169-174: Enhanced file response initialization.

  • Logging an empty Baes64FileResponse is helpful to debug missing or invalid data.
  • Returning HttpStatus.noContent for empty data clarifies the intended semantics of an “empty” image.
  • The validTill logic, with a short cache for empty images, is a thoughtful way to avoid storing invalid data for long.
  • Defaulting to "png" supports transparent images better than "jpeg".

Also applies to: 179-179, 195-201, 208-210

lib/consts/plur_colors.dart (5)

8-10: Primary text color alignment with design specs.

Updating primaryText to 0xFFB6A0E1 matches the design requirement. Good to see explicit usage of brand guidelines.


17-24: Light mode variant colors.

Introducing distinct light mode colors (lightPrimaryText, lightSecondaryText, etc.) is a neat way to handle multiple themes without losing the existing dark colors.


28-51: Theme-aware color getters.

The new getters (getTextColor, getSecondaryTextColor, etc.) improve clarity by centralizing the logic to switch between dark and light schemes. This helps maintain consistency and reduces repetition across the codebase.


65-85: Context-based color helpers.

Adding context-based getters (PlurColors.textColor(context), etc.) is a good practice. It ensures the code remains succinct at usage sites, letting you handle theming logic in one place.


88-127: Refactored text styles for theming.

Providing brightness-based text style getters (like getUsernameStyle) plus context-based variants improves reusability for both dark and light modes. Maintaining a consistent typographic hierarchy becomes much easier.

Also applies to: 129-163

Comment on lines +52 to +53
// Use the custom icon widget instead of a simple emoji
emoji: "👤", // Better emoji for user profile
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Conflicting comments about icon implementation.

There's a contradiction between the newly added comment at lines 48-49 (which says you're using a simple emoji) and the comment at line 52 (which says to use a custom icon widget). This can cause confusion for developers.

-      // Use the custom icon widget instead of a simple emoji
+      // Using a simple emoji as explained above
       emoji: "👤", // Better emoji for user profile
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Use the custom icon widget instead of a simple emoji
emoji: "👤", // Better emoji for user profile
// Using a simple emoji as explained above
emoji: "👤", // Better emoji for user profile

Comment on lines 735 to +834
String groupName) async {
print("createGroupAndGenerateInvite starting for group: $groupName");
log("Creating group: $groupName", name: "ListProvider");

// Show loading indicator
final cancelFunc = BotToast.showLoading();
const host = RelayProvider.defaultGroupsRelayAddress;
print("Using host: $host");

// Generate a random string for the group ID
final groupId = StringCodeGenerator.generateGroupId();
print("Generated group ID: $groupId");

// Create the event for creating a group.
// We only support private closed group for now.
final createGroupEvent = Event(
nostr!.publicKey,
EventKind.groupCreateGroup,
[
["h", groupId]
],
"",
);
print("Created group creation event: ${createGroupEvent.id}");

print("Sending event to relay...");
final resultEvent = await nostr!
.sendEvent(createGroupEvent, tempRelays: [host], targetRelays: [host]);
print("Result from group creation: ${resultEvent?.id ?? 'null'}");

String? inviteLink;
GroupIdentifier? newGroup;
// Event was successfully sent
if (resultEvent != null) {
newGroup = GroupIdentifier(host, groupId);
print("New group identifier created: $newGroup");

// Add the group to the list
print("Before adding to _groupIdentifiers. Current count: ${_groupIdentifiers.length}");
_groupIdentifiers.add(newGroup);
print("After adding to _groupIdentifiers. New count: ${_groupIdentifiers.length}");

try {
// Use multiple relays to maximize success chance
const List<String> relaysToTry = [
RelayProvider.defaultGroupsRelayAddress,
'wss://feeds.nostr.band', // Try another relay as backup
];

print("Creating group metadata for name: $groupName");
_editMetadata(newGroup, groupName);
// Generate a random string for the group ID
final groupId = StringCodeGenerator.generateGroupId();
log("Generated group ID: $groupId", name: "ListProvider");

print("Updating groups list event");
_updateGroups();

// Generate an invite code
final inviteCode = StringCodeGenerator.generateInviteCode();
print("Generated invite code: $inviteCode");
inviteLink = createInviteLink(newGroup, inviteCode);
print("Created invite link: $inviteLink");
// Create the event for creating a group
final createGroupEvent = Event(
nostr!.publicKey,
EventKind.groupCreateGroup,
[
["h", groupId]
],
"",
);

// Double check that the group was added
if (_groupIdentifiers.contains(newGroup)) {
print("Confirmed group is in the list");
} else {
print("ERROR: Group was not added to the list!");
// Try sending to multiple relays to ensure success
Event? resultEvent;
String usedRelay = relaysToTry[0]; // Default to first relay

for (final relay in relaysToTry) {
try {
log("Sending group creation event to relay: $relay", name: "ListProvider");
final result = await nostr!.sendEvent(
createGroupEvent,
tempRelays: [relay],
targetRelays: [relay]
);

if (result != null) {
resultEvent = result;
usedRelay = relay;
log("Group creation successful on relay: $relay", name: "ListProvider");
break; // Success, stop trying other relays
}
} catch (e) {
log("Error creating group on relay $relay: $e", name: "ListProvider");
// Continue to next relay
}
}

// Manually verify the group is in groupIdentifiers
print("Group identifiers list content verification:");
for (var group in _groupIdentifiers) {
print(" - $group");
String? inviteLink;
GroupIdentifier? newGroup;

// If event was successfully sent to any relay
if (resultEvent != null) {
// Create the group identifier
newGroup = GroupIdentifier(usedRelay, groupId);
log("Created group identifier: $usedRelay/$groupId", name: "ListProvider");

// Add the group properly using our method that handles metadata
_addGroupIdentifier(newGroup);
log("Added group to identifiers list", name: "ListProvider");

// Create and set metadata
await _createAndSetMetadata(newGroup, groupName);

// Update groups list event
_updateGroups();

// Generate an invite code and create invite link
final inviteCode = StringCodeGenerator.generateInviteCode();
inviteLink = createInviteLink(newGroup, inviteCode);
log("Created invite link: $inviteLink", name: "ListProvider");

// Force refresh group data
if (groupFeedProvider != null) {
log("Refreshing GroupFeedProvider to show new group", name: "ListProvider");
groupFeedProvider!.refresh();
}

// Notify listeners to update UI
notifyListeners();
} else {
log("Failed to create group - could not send event to any relay", name: "ListProvider");
BotToast.showText(text: "Failed to create community. Please check your connection and try again.");
}
} else {
print("ERROR: Group creation failed - resultEvent is null");

// Return results
return (inviteLink, newGroup);
} catch (e) {
log("Exception during group creation: $e", name: "ListProvider");
BotToast.showText(text: "Error creating community: $e");
return (null, null);
} finally {
// Always hide loading indicator
cancelFunc.call();
}

// Force a notify listeners to ensure UI updates
notifyListeners();
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Comprehensive multi-relay group creation.

This approach maximizes success. Consider some form of aggregated success/failure feedback if no relay acknowledges the creation event, instead of only logging.

rabble and others added 2 commits April 16, 2025 22:08
- Implemented message button in listing cards that opens DM with author
- Added message button in listing detail screen for direct author contact
- Used global dmProvider to create DM sessions with listing authors
- Connects listing pubkeys to the existing Nostr DM system

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (19)
lib/features/asks_offers/screens/listings_screen.dart (5)

110-114: Consider showing visual feedback for active filters
The filter icon doesn’t visually indicate when filters are applied. Providing a small badge or color change can help users recognize when the results are filtered, enhancing usability.


219-241: Potential collision for multiple FloatingActionButtons
On smaller screens, two extended FABs could overlap or push content off-screen. Consider using a speed-dial style FAB to host multiple actions more compactly.


246-256: Use named routes or router architecture for navigation
MaterialPageRoute is fine for small apps. However, using named routes or a central router utility scales better as the app grows, especially if future route changes or route guarding are needed.


293-305: Improve empty-state messaging
The empty-state text is informative but might be expanded to guide the user more, for example suggesting they check other filter criteria or clarifying how to create new listings beyond the default.


307-420: Allow additional filtering options in the bottom sheet
Currently, users can filter by status. Enabling more filter dimensions (e.g., date, price range, or location) could enhance discoverability of listings. If you expect future expansions, consider structuring filter logic in a more modular way.

lib/features/asks_offers/models/listing_model.dart (1)

73-95: Expose event kinds more explicitly
Inside toEvent(), the kind is set to 31111, which might be a custom type. If you anticipate multiple listing event types or further usage, store and reference these event kinds more systematically (e.g., constants or an enum) to prevent magic numbers.

lib/features/asks_offers/screens/create_edit_listing_screen.dart (3)

294-309: Ensure required fields have robust validation messages
The current validators simply request the user to enter text. For a better UX, provide context-specific error messages explaining why those fields are mandatory and how they will be used.


409-489: Allow user-defined image sources
Right now, _addImage() inserts a placeholder URL and images can only be removed. Consider integrating an image picker or file upload approach for real usage scenarios.

Do you want me to open a new issue with a proof-of-concept image picker implementation for this feature?


520-539: Consolidate button color logic
The color style for the submit button depends on the listing type and dark mode. To maintain clarity, consider a simple helper method or dedicated theme extension for consistent color handling across the app.

lib/features/asks_offers/widgets/listing_card.dart (4)

129-140: Provide a fallback or error builder for network images.
When loading images via Image.network, it's often beneficial to provide a placeholder or handle loading failures gracefully, especially in unreliable connectivity scenarios.

+child: Image.network(
+  listing.imageUrls.first,
+  height: 140,
+  width: double.infinity,
+  fit: BoxFit.cover,
+  errorBuilder: (context, error, stackTrace) {
+    return const Center(child: Icon(Icons.broken_image));
+  },
+  loadingBuilder: (context, child, progress) {
+    return progress == null 
+      ? child 
+      : const Center(child: CircularProgressIndicator());
+  },
+),

211-222: Ensure consistent theming for the action buttons container.
The container draws a border using separatorColor.withOpacity(0.3), while the text buttons inside might be referencing raw color constants (blue/green). To maintain brand consistency, consider referencing your custom color system for the button icons and labels as well.


303-343: Replace temporary ScaffoldMessenger calls with real or placeholders for user actions.
Currently, each "Feature coming soon" action triggers a SnackBar. If you're targeting an MVP, this might suffice. Otherwise, you can wire up real calls or a consistent placeholder service so that all "coming soon" features log to a single point, avoiding repetitive code across the codebase.


399-402: Consider localizing or extending date formatting.
Currently, _formatDate returns only a month/day string in English. If you anticipate i18n or need year-based formatting in the card, consider using Flutter’s internationalization tools or customizing the format.

lib/features/asks_offers/screens/listing_detail_screen.dart (3)

114-126: Add error handling for loaded images in PageView.
Similar to the card component, providing an errorBuilder and loadingBuilder can enhance the user experience in case of network or loading errors.


264-302: Proactively integrate planned comments functionality.
The placeholder indicates a pending comments feature. If the feature is on your roadmap, consider marking it clearly with a TODO or referencing a ticket ID so it’s easier to search and expedite.

Would you like me to open a new issue to track the development of comment threads?


492-527: Align status badge styling with ListingCard’s _buildStatusChip.
Both features display a status label with color-coded backgrounds, but each uses different shades and approaches. Merging them into a shared utility ensures consistent design across your app.

lib/features/asks_offers/providers/listing_provider.dart (3)

31-59: Assess approach for optional group ID filter.
The comment suggests needing a “more complex approach” for the Nostr filter. Consider creating a fallback or open an enhancement request if the SDK lacks native tag filters. This ensures group-based queries stay maintainable.

Want me to propose a workaround or open a tracking task for a refined filter approach?


192-197: Add defensive checks in handleEvent.
If events with unexpected kind or invalid data slip through, they could cause parsing issues. Consider verifying event structure, or at least logging events that don’t match the expected format, to aid debugging.


214-244: Enhance search flexibility.
The current filter allows searching titles and contents. You might also filter by location or group name if relevant in the future, ensuring advanced search coverage.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 442ac47 and 699907b.

📒 Files selected for processing (6)
  • lib/features/asks_offers/models/listing_model.dart (1 hunks)
  • lib/features/asks_offers/providers/listing_provider.dart (1 hunks)
  • lib/features/asks_offers/screens/create_edit_listing_screen.dart (1 hunks)
  • lib/features/asks_offers/screens/listing_detail_screen.dart (1 hunks)
  • lib/features/asks_offers/screens/listings_screen.dart (1 hunks)
  • lib/features/asks_offers/widgets/listing_card.dart (1 hunks)
🔇 Additional comments (7)
lib/features/asks_offers/screens/listings_screen.dart (1)

46-62: Ensure tab state changes are reflected consistently
The _handleTabSelection method updates _selectedType through setState, which is good for local UI. However, if more advanced logic is later added (e.g., analytics or advanced filtering), ensure the changes also propagate to any relevant providers.

lib/features/asks_offers/models/listing_model.dart (2)

67-69: Check for invalid or untrusted image URLs
The method gathers image URLs from tags without any validation. If the Nostr feed can contain untrusted data, consider adding URL validation or a fallback strategy for malformed links.


152-188: Ensure equality remains consistent with business logic
The == operator checks all fields, including title, content, status, etc. For collaborative editing scenarios or partial updates, confirm that it’s appropriate for every field to be part of equality, and that no fields require ignoring or special comparison logic.

lib/features/asks_offers/screens/create_edit_listing_screen.dart (1)

151-156: Maintain consistent contrast for text fields in dark mode
The inputStyle color is set to primaryForegroundColor, which is generally fine for dark themes. In some custom themes, ensure the foregroundColor is tested for adequate contrast, especially with user-provided backgrounds.

lib/features/asks_offers/widgets/listing_card.dart (1)

47-53: Consider hour-level expiration checks.
Right now, the logic uses a day-based difference to mark the listing as expired once daysRemaining is zero. This might inadvertently mark items that expire later in the same day as expired too soon. If you need more granular control, consider checking hours or a smaller interval before labeling it "Expired."

Would you like a quick script to scan for references to inDays to confirm that no other unintended day-based checks exist in the codebase?

lib/features/asks_offers/screens/listing_detail_screen.dart (1)

529-551: Check for negative or future times in _formatRelativeTime.
The function handles differences as “in the past,” but if a listing’s createdAt is somehow ahead of the local clock, your UI might show “just now.” Consider adding boundary checks for negative durations or user time zone discrepancies.

lib/features/asks_offers/providers/listing_provider.dart (1)

151-153: Confirm error state consistency.
You set AsyncValue.error only if the current state isn’t already an AsyncError. This might mask repeated errors within the same session. Evaluate if you’d prefer capturing every new error or if limiting errors is desired.

Comment on lines +163 to +172
Expanded(
child: listingsState.when(
data: (listings) {
final filteredListings = ref.read(listingProvider.notifier).filterListings(
type: _selectedType,
status: _selectedStatus,
groupId: widget.groupId,
searchQuery: _searchQuery,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid calling filter logic repeatedly in the build method
filterListings is invoked on each rebuild, which could be expensive if listing sets become large. Consider moving filtering logic to a separate provider selector or caching the filtered results to avoid unnecessary recomputation.

Comment on lines +25 to +37
late TabController _tabController;
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey = GlobalKey<RefreshIndicatorState>();

@override
void initState() {
super.initState();
// Initialize tab controller for Ask/Offer tabs
_tabController = TabController(length: 3, vsync: this);
_tabController.addListener(_handleTabSelection);

// Load listings when screen initializes
ref.read(listingProvider.notifier).loadListings(groupId: widget.groupId);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider lazy-loading or pagination for large datasets
Currently, the listings are loaded once in initState without pagination, which could lead to performance bottlenecks when there are many listings. Consider implementing pagination or incremental loading to reduce memory overhead and improve load times.

Comment on lines +64 to +66
Future<void> _refreshListings() async {
return ref.read(listingProvider.notifier).loadListings(groupId: widget.groupId);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for refresh failures
While _refreshListings calls loadListings, any potential error is silently ignored if something goes wrong. Consider wrapping this call in a try-catch or exposing an error state so the user can see a relevant notification on failure.

Comment on lines +22 to +24
final String title;
final String content;
final ListingStatus status;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Validate critical fields to avoid incomplete listings
The type, title, and content fields are required for a valid listing, but the model itself doesn’t enforce them beyond “required” constructor parameters. Ensure that callers avoid creating partial or invalid listings.

Comment on lines +66 to +91
try {
if (widget.listing == null) {
// Create new listing
// Get pubkey from global nostr instance
final pubkey = nostr?.publicKey;
if (pubkey == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Error: User not logged in')),
);
setState(() => _isLoading = false);
return;
}

await ref.read(listingProvider.notifier).createListing(
// pubkey: 'TODO: Get current user pubkey', // Removed placeholder
type: _type,
title: _titleController.text,
content: _contentController.text,
groupId: widget.groupId,
expiresAt: _expiresAt,
location: _locationController.text.isNotEmpty ? _locationController.text : null,
price: _priceController.text.isNotEmpty ? _priceController.text : null,
imageUrls: _imageUrls,
paymentInfo: _paymentInfoController.text.isNotEmpty ? _paymentInfoController.text : null,
);
} else {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Refine error handling during listing creation/update
In _saveListing(), the catch block only displays a snack bar with the error message. Consider capturing and logging the error or presenting more user-friendly messaging for different error cases (e.g., network issue vs. invalid data).

Comment on lines +313 to +389
if (listing.status == ListingStatus.active) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
decoration: BoxDecoration(
color: Theme.of(context).canvasColor,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
offset: const Offset(0, -1),
blurRadius: 4,
),
],
),
child: Row(
children: [
if (listing.type == ListingType.ask)
Expanded(
child: ElevatedButton.icon(
onPressed: () {
// TODO: Implement help action
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Feature coming soon: I can help')),
);
},
icon: const Icon(Icons.volunteer_activism),
label: const Text('I can help!'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
),
)
else
Expanded(
child: ElevatedButton.icon(
onPressed: () {
// TODO: Implement interest action
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Feature coming soon: I\'m interested')),
);
},
icon: const Icon(Icons.thumb_up_outlined),
label: const Text('I\'m interested'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
),
),
),
const SizedBox(width: 16),
// Owner actions
if (_isCurrentUserOwner()) ...[
OutlinedButton.icon(
onPressed: () {
_showStatusChangeDialog(context, ref);
},
icon: const Icon(Icons.check_circle_outline),
label: const Text('Mark as Fulfilled'),
),
]
// Non-owner actions
else ...[
OutlinedButton.icon(
onPressed: () {
// Use the global dmProvider to create a session
final detail = dmProvider.findOrNewADetail(listing.pubkey);
// Navigate to DM screen with the session detail
RouterUtil.router(context, RouterPath.dmDetail, detail);
},
icon: const Icon(Icons.message_outlined),
label: const Text('Message'),
),
],
],
),
);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Evaluate using a single widget or helper function for bottom bar actions.
This logic is very similar to your _buildActionButtons in ListingCard. Consider extracting a common actions builder to maintain consistency and reduce duplication. You can tailor small param differences (e.g., button color) with optional arguments.

Comment on lines +60 to +84
// Get recent listings
// Use SDK-provided methods for querying events
List<Event> initialEvents = [];
try {
// Adapting to the nostr_sdk available methods
initialEvents = await nostr!.queryEvents([filter.toJson()]);
} catch (e) {
// Log error but continue with empty list
}
for (final event in initialEvents) {
handleEvent(event);
}
// Update state after initial load
state = AsyncValue.data(_latestListings.values.toList()..sort((a, b) => b.createdAt.compareTo(a.createdAt)));

// Subscribe to future events using the SDK's event subscription mechanism
_subscriptionId = "listings_${DateTime.now().millisecondsSinceEpoch}";

// Based on codebase examples, we pass a callback function to handle events
nostr!.subscribe(
[filter.toJson()],
_handleSubscriptionEvent,
id: _subscriptionId,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Move subscription logic to an auto-dispose provider or lifecycle-bound approach.
In Flutter, tying subscription logic to a widget’s lifecycle can prevent stale or multiple subscriptions. If you only need real-time updates while a widget is active, evaluate an autoDispose approach or a tearDown callback to ensure you’re unsubscribing at the correct times.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (7)
l10n.yaml (1)

1-7: Add a newline at the end of the file.

The configuration is correct and follows best practices for Flutter localization. To resolve the static analysis warning and maintain consistency, add a newline at the end of the file.

-arb-dir: lib/l10n
-template-arb-file: intl_en.arb
-output-localization-file: l10n.dart
-output-dir: lib/generated
-output-class: S
-nullable-getter: false
-format: true
+arb-dir: lib/l10n
+template-arb-file: intl_en.arb
+output-localization-file: l10n.dart
+output-dir: lib/generated
+output-class: S
+nullable-getter: false
+format: true
+
🧰 Tools
🪛 YAMLlint (1.35.1)

[error] 7-7: no new line character at the end of file

(new-line-at-end-of-file)

LOCALIZATION_PLAN.md (1)

1-164: Well-structured localization refactoring plan with clear implementation phases.

This comprehensive document provides an excellent roadmap for addressing localization inconsistencies across the codebase. The plan identifies the core issue (using PascalCase/snake_case for localization keys when Flutter expects camelCase), outlines the current status, and provides a structured approach for implementation.

The phased approach with prioritized categories makes the effort manageable and systematic. Including already updated files, implementation guidelines, testing strategies, and completion criteria ensures everyone has clear direction.

Minor improvements:

```python
 
-141~
+141~
1. Consider keeping the original key in the ARB file but updating the code
 
-147~
+147~
- Estimated time: 3–4 days of focused work
🧰 Tools
🪛 LanguageTool

[uncategorized] ~141-~141: Possible missing preposition found.
Context: ...n the ARB file but updating the code 2. Create a mapping layer between old and new key...

(AI_HYDRA_LEO_MISSING_TO)


[typographical] ~147-~147: If specifying a range, consider using an en dash instead of a hyphen.
Context: ...) and rate of fixing: - Estimated time: 3-4 days of focused work - Breakdown: ~175 ...

(HYPHEN_TO_EN)

🪛 markdownlint-cli2 (0.17.2)

115-115: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

doc/features/asks_offers_nip.md (1)

111-111: Spelling nitpick: "auto-fill" should be "autofill".

The word "auto-fill" is typically spelled as "autofill" in technical documentation. Consider updating for consistency and clarity.

-	•	If user is in a group context, auto-fill h with that group ID. Otherwise, omit it for a public feed post.
+	•	If user is in a group context, autofill h with that group ID. Otherwise, omit it for a public feed post.
🧰 Tools
🪛 LanguageTool

[misspelling] ~111-~111: This word is normally spelled as one.
Context: ...tc.). • If user is in a group context, auto-fill h with that group ID. Otherwise, omit i...

(EN_COMPOUNDS_AUTO_FILL)

doc/features/asks_offers_prd.md (4)

41-45: Consider varying repetitive phrasing in user stories.

The phrase "I want to..." is used repeatedly in consecutive user stories. For improved readability and engagement, consider rephrasing some stories for variety.

Example alternatives:

  • "As a user, I need to post something I have or can do (an Offer)..."
  • "As a user, I should be able to see all Asks/Offers in one list..."
  • "As the author of a listing, I need to edit its details or mark it as fulfilled/cancelled/expired, so people know its current status."
🧰 Tools
🪛 LanguageTool

[style] ~41-~41: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...need.” 2. Post an Offer: “As a user, I want to post something I have or can do (an Off...

(REP_WANT_TO_VB)


[style] ~43-~43: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...over.” 3. View Listings: “As a user, I want to see all Asks/Offers in one list, filter...

(REP_WANT_TO_VB)


[style] ~45-~45: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...Listing: “As the author of a listing, I want to edit its details or mark it as fulfille...

(REP_WANT_TO_VB)


[uncategorized] ~45-~45: Use a comma before ‘so’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...r mark it as fulfilled/cancelled/expired so people know its current status.” 5. Gr...

(COMMA_COMPOUND_SENTENCE_2)


45-45: Add a comma before 'so' in compound sentence.

For clarity and grammatical correctness, add a comma before "so" as it connects two independent clauses.

- “As the author of a listing, I want to edit its details or mark it as fulfilled/cancelled/expired so people know its current status.”
+ “As the author of a listing, I want to edit its details or mark it as fulfilled/cancelled/expired, so people know its current status.”
🧰 Tools
🪛 LanguageTool

[style] ~45-~45: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...Listing: “As the author of a listing, I want to edit its details or mark it as fulfille...

(REP_WANT_TO_VB)


[uncategorized] ~45-~45: Use a comma before ‘so’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...r mark it as fulfilled/cancelled/expired so people know its current status.” 5. Gr...

(COMMA_COMPOUND_SENTENCE_2)


62-62: Remove double punctuation.

There are two consecutive periods after "etc." Remove the extra period.

-	•	status can be active, inactive, fulfilled, expired, or cancelled.
-	•	The client must allow the user to change from active → fulfilled/cancelled/etc..
+	•	status can be active, inactive, fulfilled, expired, or cancelled.
+	•	The client must allow the user to change from active → fulfilled/cancelled/etc.
🧰 Tools
🪛 LanguageTool

[typographical] ~62-~62: Two consecutive dots
Context: ...ge from active → fulfilled/cancelled/etc.. 4. Filtering & Display: • Must be abl...

(DOUBLE_PUNCTUATION)


173-173: Add missing determiner for clarity.

Add "the" before "newest event" for grammatical correctness.

-	•	Ensure newest event overrides older ones in local state.
+	•	Ensure the newest event overrides older ones in local state.
🧰 Tools
🪛 LanguageTool

[grammar] ~173-~173: A determiner may be missing.
Context: ...to fulfilled, cancelled, etc. • Ensure newest event overrides older ones in local sta...

(THE_SUPERLATIVE)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 699907b and fd82e27.

⛔ Files ignored due to path filters (59)
  • doc/screenshots/Simulator Screenshot - iPhone 16 Pro - 2025-04-13 at 20.40.26.png is excluded by !**/*.png
  • doc/screenshots/Simulator Screenshot - iPhone 16 Pro - 2025-04-13 at 20.40.46.png is excluded by !**/*.png
  • doc/screenshots/Simulator Screenshot - iPhone 16 Pro - 2025-04-13 at 20.41.02.png is excluded by !**/*.png
  • doc/screenshots/colors_right.png is excluded by !**/*.png
  • doc/screenshots/screenshot_1.png is excluded by !**/*.png
  • lib/generated/app_localizations.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_ar.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_bg.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_cs.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_da.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_de.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_el.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_en.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_es.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_et.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_fi.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_fr.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_hu.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_it.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_ja.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_ko.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_nl.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_pl.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_pt.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_ro.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_ru.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_sl.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_sv.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_th.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_vi.dart is excluded by !**/generated/**
  • lib/generated/app_localizations_zh.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_ar.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_bg.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_cs.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_da.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_de.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_el.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_en.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_es.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_et.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_fi.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_fr.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_hu.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_it.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_ja.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_ko.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_nl.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_pl.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_pt.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_ro.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_ru.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_sl.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_sv.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_th.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_vi.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_zh.dart is excluded by !**/generated/**
  • lib/generated/intl/messages_zh_TW.dart is excluded by !**/generated/**
  • lib/generated/l10n.dart is excluded by !**/generated/**
  • lib/generated/l10n.dart.bak is excluded by !**/generated/**
📒 Files selected for processing (49)
  • LOCALIZATION_PLAN.md (1 hunks)
  • doc/features/asks-offers.md (1 hunks)
  • doc/features/asks_offers_nip.md (1 hunks)
  • doc/features/asks_offers_prd.md (1 hunks)
  • ios/fix_sentry.sh (1 hunks)
  • l10n.yaml (1 hunks)
  • lib/component/badge_award_widget.dart (1 hunks)
  • lib/component/badge_detail_widget.dart (1 hunks)
  • lib/component/color_pick_dialog.dart (1 hunks)
  • lib/component/confirm_dialog.dart (1 hunks)
  • lib/component/content/content_lnbc_widget.dart (3 hunks)
  • lib/component/content/content_relay_widget.dart (1 hunks)
  • lib/component/content/content_widget.dart (2 hunks)
  • lib/component/datetime_picker_widget.dart (3 hunks)
  • lib/component/editor/custom_emoji_add_dialog.dart (5 hunks)
  • lib/component/editor/editor_mixin.dart (18 hunks)
  • lib/component/editor/gen_lnbc_widget.dart (6 hunks)
  • lib/component/editor/pic_embed_builder.dart (1 hunks)
  • lib/component/editor/poll_input_widget.dart (5 hunks)
  • lib/component/editor/search_mention_widget.dart (1 hunks)
  • lib/component/editor/text_input_and_search_dialog.dart (1 hunks)
  • lib/component/editor/text_input_dialog_inner_widget.dart (1 hunks)
  • lib/component/editor/zap_goal_input_widget.dart (2 hunks)
  • lib/component/editor/zap_split_input_widget.dart (3 hunks)
  • lib/component/emoji_picker_widget.dart (1 hunks)
  • lib/component/event/event_id_router_widget.dart (1 hunks)
  • lib/component/event/event_load_list_widget.dart (1 hunks)
  • lib/component/event/event_main_widget.dart (7 hunks)
  • lib/component/event/event_poll_widget.dart (4 hunks)
  • lib/component/event/event_quote_widget.dart (1 hunks)
  • lib/component/event/event_reactions_widget.dart (22 hunks)
  • lib/component/event/event_torrent_widget.dart (3 hunks)
  • lib/component/event/event_zap_goals_widget.dart (1 hunks)
  • lib/component/follow_set_follow_bottom_sheet.dart (2 hunks)
  • lib/component/group/admin_tag_widget.dart (1 hunks)
  • lib/component/image_preview_dialog.dart (2 hunks)
  • lib/component/json_view_dialog.dart (2 hunks)
  • lib/component/lightning_qrcode_dialog.dart (2 hunks)
  • lib/component/new_notes_updated_widget.dart (1 hunks)
  • lib/component/nip07_dialog.dart (6 hunks)
  • lib/component/paste_join_link_button.dart (1 hunks)
  • lib/component/qrcode_dialog.dart (1 hunks)
  • lib/component/sync_upload_dialog.dart (5 hunks)
  • lib/component/user/user_top_widget.dart (3 hunks)
  • lib/component/webview_widget.dart (7 hunks)
  • lib/component/zap/zap_bottom_sheet_widget.dart (5 hunks)
  • lib/component/zap/zaps_send_dialog.dart (1 hunks)
  • lib/consts/router_path.dart (1 hunks)
  • lib/features/community_guidelines/community_guidelines_screen.dart (4 hunks)
✅ Files skipped from review due to trivial changes (35)
  • lib/component/event/event_load_list_widget.dart
  • lib/component/new_notes_updated_widget.dart
  • lib/component/qrcode_dialog.dart
  • lib/component/event/event_quote_widget.dart
  • lib/component/zap/zaps_send_dialog.dart
  • lib/component/group/admin_tag_widget.dart
  • lib/component/emoji_picker_widget.dart
  • lib/component/event/event_id_router_widget.dart
  • lib/component/badge_award_widget.dart
  • lib/component/badge_detail_widget.dart
  • lib/component/content/content_relay_widget.dart
  • lib/component/editor/text_input_and_search_dialog.dart
  • lib/component/follow_set_follow_bottom_sheet.dart
  • lib/component/event/event_zap_goals_widget.dart
  • lib/component/paste_join_link_button.dart
  • lib/component/editor/text_input_dialog_inner_widget.dart
  • lib/component/event/event_torrent_widget.dart
  • lib/component/content/content_lnbc_widget.dart
  • lib/component/color_pick_dialog.dart
  • lib/features/community_guidelines/community_guidelines_screen.dart
  • lib/component/editor/custom_emoji_add_dialog.dart
  • lib/component/editor/search_mention_widget.dart
  • lib/component/editor/zap_goal_input_widget.dart
  • lib/component/editor/pic_embed_builder.dart
  • lib/component/editor/zap_split_input_widget.dart
  • lib/component/zap/zap_bottom_sheet_widget.dart
  • lib/component/json_view_dialog.dart
  • lib/component/nip07_dialog.dart
  • lib/component/datetime_picker_widget.dart
  • lib/component/webview_widget.dart
  • lib/component/editor/editor_mixin.dart
  • lib/component/lightning_qrcode_dialog.dart
  • lib/component/sync_upload_dialog.dart
  • lib/component/event/event_poll_widget.dart
  • lib/component/user/user_top_widget.dart
🚧 Files skipped from review as they are similar to previous changes (5)
  • lib/consts/router_path.dart
  • lib/component/image_preview_dialog.dart
  • lib/component/event/event_reactions_widget.dart
  • lib/component/event/event_main_widget.dart
  • lib/component/content/content_widget.dart
🧰 Additional context used
🪛 Gitleaks (8.21.2)
doc/features/asks_offers_nip.md

213-213: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🪛 LanguageTool
doc/features/asks_offers_nip.md

[misspelling] ~111-~111: This word is normally spelled as one.
Context: ...tc.). • If user is in a group context, auto-fill h with that group ID. Otherwise, omit i...

(EN_COMPOUNDS_AUTO_FILL)

doc/features/asks_offers_prd.md

[style] ~41-~41: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...need.” 2. Post an Offer: “As a user, I want to post something I have or can do (an Off...

(REP_WANT_TO_VB)


[style] ~43-~43: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...over.” 3. View Listings: “As a user, I want to see all Asks/Offers in one list, filter...

(REP_WANT_TO_VB)


[style] ~45-~45: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...Listing: “As the author of a listing, I want to edit its details or mark it as fulfille...

(REP_WANT_TO_VB)


[uncategorized] ~45-~45: Use a comma before ‘so’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...r mark it as fulfilled/cancelled/expired so people know its current status.” 5. Gr...

(COMMA_COMPOUND_SENTENCE_2)


[typographical] ~62-~62: Two consecutive dots
Context: ...ge from active → fulfilled/cancelled/etc.. 4. Filtering & Display: • Must be abl...

(DOUBLE_PUNCTUATION)


[grammar] ~173-~173: A determiner may be missing.
Context: ...to fulfilled, cancelled, etc. • Ensure newest event overrides older ones in local sta...

(THE_SUPERLATIVE)

LOCALIZATION_PLAN.md

[uncategorized] ~141-~141: Possible missing preposition found.
Context: ...n the ARB file but updating the code 2. Create a mapping layer between old and new key...

(AI_HYDRA_LEO_MISSING_TO)


[typographical] ~147-~147: If specifying a range, consider using an en dash instead of a hyphen.
Context: ...) and rate of fixing: - Estimated time: 3-4 days of focused work - Breakdown: ~175 ...

(HYPHEN_TO_EN)

🪛 markdownlint-cli2 (0.17.2)
LOCALIZATION_PLAN.md

115-115: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

🪛 YAMLlint (1.35.1)
l10n.yaml

[error] 7-7: no new line character at the end of file

(new-line-at-end-of-file)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: fastlane-deploy
🔇 Additional comments (6)
ios/fix_sentry.sh (1)

1-56: Shell script is clear, effective, and safe for its intended purpose.

The script is well-structured, clearly commented, and safely patches the problematic Sentry C++ file. It is idempotent, checks for file existence, and provides all necessary stub implementations to avoid build errors. No critical issues found.

Optional improvements:

  • Consider adding a check for the Sentry version to avoid overwriting the file if the issue is fixed in future releases.
  • Optionally add error handling for chmod and cat commands to provide more robust feedback in case of failures.

Overall, this is a pragmatic and maintainable workaround for the Sentry C++ exception handler issue.

lib/component/confirm_dialog.dart (1)

14-14: Localization key updates follow the new camelCase convention.

The changes in this file update all localization key references from PascalCase to camelCase format ('notice', 'cancel', 'confirm'), aligning with the localization standardization effort outlined in LOCALIZATION_PLAN.md.

Also applies to: 18-18, 24-24

lib/component/editor/gen_lnbc_widget.dart (1)

55-55: Consistently updated localization keys to camelCase format.

All localization references in this file have been properly updated from PascalCase/snake_case to camelCase format, following the localization plan. This includes user-facing text for error messages, hints, and button labels.

Also applies to: 63-63, 81-81, 97-97, 109-109, 133-133, 168-168

lib/component/editor/poll_input_widget.dart (1)

41-41: Successfully standardized localization keys to camelCase.

All localization keys throughout this file have been updated to follow the camelCase convention, including error messages shown via BotToast and UI text elements. These changes are consistent with the localization plan.

Also applies to: 48-48, 55-55, 102-102, 133-133, 150-150, 161-161

doc/features/asks_offers_nip.md (1)

213-213: No real API key exposure in example event.

The static analysis tool flagged a "Generic API Key" at line 213, but this is a placeholder public key in an example event, not a real credential. No action needed.

🧰 Tools
🪛 Gitleaks (8.21.2)

213-213: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

doc/features/asks-offers.md (1)

1-243: Excellent design and implementation documentation.

This document is clear, comprehensive, and well-structured. It effectively communicates the feature's purpose, user flows, technical details, and roadmap.

rabble and others added 2 commits April 16, 2025 22:58
- Updated Podfile to enforce minimum iOS 15.5 deployment target for all pods
- Added Xcode 16.3+ compatibility settings
- Fixed xcconfig files with proper CocoaPods includes
- Removed references to missing Hindi (hi) locale in intl files
- Added documentation for fixing iOS build in FIX_IOS_BUILD.md

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Added detailed iOS build steps
- Included troubleshooting tips for common iOS build issues
- Referenced the FIX_IOS_BUILD.md file for detailed instructions

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@joshuatbrown
Copy link
Copy Markdown
Contributor

@rabble looks like a very large PR; do you know what's going on here besides adding the communities link and fixing dark mode colors? For 80,000+ lines, something else must be happening.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
claude.yaml (4)

2-3: File header mismatch: .yaml vs Markdown content.
The literal block begins with a Markdown header (# CLAUDE.md), yet the file extension is .yaml. This may confuse readers or tooling. Consider renaming the file to CLAUDE.md (and removing the YAML wrapper) or updating the header to reflect its YAML nature.


2-30: Ensure YAML literal block indentation is consistent.
Blank lines inside the claude_markdown: | block have no indentation, which can prematurely terminate the block or cause parsing issues. All lines — including empty ones — should be indented to match the block’s content. For example:

 claude_markdown: |
-# CLAUDE.md
-
-This file provides guidance...
+  # CLAUDE.md
+  
+  This file provides guidance...

16-21: Clarify CocoaPods setup in iOS instructions.
The step cd ios && pod install assumes the user’s CocoaPods repo is up to date. It may fail if specs are outdated. Consider adding a preliminary note:

  • gem install cocoapods (if missing)
  • pod repo update

152-152: Add a newline at end of file.
YAML lint reports no newline at EOF. Please ensure a trailing newline to comply with POSIX standards and avoid parser warnings.

🧰 Tools
🪛 YAMLlint (1.35.1)

[error] 152-152: no new line character at the end of file

(new-line-at-end-of-file)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 722d17c and 699ff27.

📒 Files selected for processing (1)
  • claude.yaml (1 hunks)
🧰 Additional context used
🪛 YAMLlint (1.35.1)
claude.yaml

[error] 152-152: no new line character at the end of file

(new-line-at-end-of-file)

Comment thread claude.yaml
@@ -0,0 +1,152 @@
claude_markdown: |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Unrelated file in UI improvements PR: relocate or remove.
This PR is scoped to UI enhancements (Communities link, dark mode fixes); claude.yaml introduces AI-agent configuration unrelated to those objectives. Consider moving it to a dedicated docs/ or .github/ directory, or submit it in a separate PR to avoid scope creep.

I can help you split this file out or relocate it appropriately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants