Skip to content

Phase 2 user-admin chat: unify dispute chat message model with NostrEvent and gift wrap storage#501

Open
Catrya wants to merge 2 commits intomainfrom
phase-2-admin-user-chat
Open

Phase 2 user-admin chat: unify dispute chat message model with NostrEvent and gift wrap storage#501
Catrya wants to merge 2 commits intomainfrom
phase-2-admin-user-chat

Conversation

@Catrya
Copy link
Member

@Catrya Catrya commented Feb 25, 2026

  • Replace DisputeChat model with DisputeChatMessage wrapper around NostrEvent
  • Store gift wrap events (encrypted) on disk, consistent with P2P chat pattern
  • Reconstruct and unwrap gift wraps on historical message load
  • Add getAdminSharedKey() for future multimedia support (Phase 3)
  • Add isFromUser() method to notifier for sender determination
  • Update dispute_messages_list, dispute_content, and read status service
  • Update plan doc: Phase 1 and Phase 2 marked as DONE

Summary by CodeRabbit

  • New Features

    • Added message state tracking to display pending and error status for sent messages.
    • Implemented persistent storage for encrypted messages in Dispute Chat.
  • Documentation

    • Updated Phase 1/Phase 2 transition plan for Dispute Chat feature, clarifying encryption and messaging architecture.

…rap storage

  - Replace DisputeChat model with DisputeChatMessage wrapper around NostrEvent
  - Store gift wrap events (encrypted) on disk, consistent with P2P chat pattern
  - Reconstruct and unwrap gift wraps on historical message load
  - Add getAdminSharedKey() for future multimedia support (Phase 3)
  - Add isFromUser() method to notifier for sender determination
  - Update dispute_messages_list, dispute_content, and read status service
  - Update plan doc: Phase 1 and Phase 2 marked as DONE
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 25, 2026

Warning

Rate limit exceeded

@Catrya has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 24 minutes and 47 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 02a9e41 and a9813da.

📒 Files selected for processing (1)
  • lib/features/disputes/notifiers/dispute_chat_notifier.dart

Walkthrough

This change refactors the dispute chat system by introducing a UI-friendly DisputeChatMessage wrapper containing NostrEvent with pending/error state, replacing raw DisputeChat usage. The implementation switches to single-layer p2pWrap encryption, adds persistent storage for encrypted gift-wrap events, and updates related widgets and services accordingly.

Changes

Cohort / File(s) Summary
Documentation Updates
docs/DISPUTE_CHAT_MULTIMEDIA_PLAN.md
Updated phase labels, phase transitions, and timestamps to reflect Phase 1 targets and Phase 2 scope changes; changed wrap strategy to single-layer p2pWrap and updated message model documentation.
Core Notifier Refactor
lib/features/disputes/notifiers/dispute_chat_notifier.dart
Introduced DisputeChatMessage wrapper with pending/error state; replaced DisputeChat with DisputeChatMessage in state; added gift-wrap persistence to disk during message receipt; enhanced historical message loading with unwrapping and deduplication; refactored message sending to track pending state and persist wrapped events; added helper methods for admin key derivation and message origin determination.
UI Widget Updates
lib/features/disputes/widgets/dispute_content.dart, lib/features/disputes/widgets/dispute_messages_list.dart
Updated to consume DisputeChatMessage model instead of DisputeChat; replaced message.message with message.content; integrated notifier's isFromUser() method for message origin determination; updated helper method signatures to accept DisputeChatMessage lists.
Service Layer Updates
lib/services/dispute_read_status_service.dart
Changed hasUnreadMessages signature to accept DisputeChatMessage list and required isFromUser predicate parameter; updated internal logic to use predicate-driven message filtering instead of direct property access.

Sequence Diagram

sequenceDiagram
    participant User as User/Network
    participant Notifier as DisputeChatNotifier
    participant Storage as Disk Storage
    participant Service as DisputeReadStatusService
    participant UI as UI Widgets

    User->>Notifier: New gift-wrap event received (_onChatEvent)
    Notifier->>Notifier: Unwrap & extract inner event
    Notifier->>Storage: Persist wrapped event
    Storage-->>Notifier: Stored
    Notifier->>Notifier: Create DisputeChatMessage(event, isPending=false)
    Notifier->>Notifier: Update state.messages
    Notifier-->>UI: Notify state change

    User->>Notifier: Send message (sendMessage)
    Notifier->>Notifier: Create DisputeChatMessage(isPending=true)
    Notifier->>Notifier: Update state with pending message
    Notifier-->>UI: Show pending indicator
    Notifier->>Notifier: Wrap via p2pWrap
    Notifier->>Notifier: Publish wrapped event
    Notifier->>Storage: Persist wrapped event
    Storage-->>Notifier: Stored
    Notifier->>Notifier: Update message (isPending=false)
    Notifier-->>UI: Message confirmed

    UI->>Notifier: Load historical messages (_loadHistoricalMessages)
    Notifier->>Storage: Fetch stored wrapped events
    Storage-->>Notifier: Wrapped events
    Notifier->>Notifier: Unwrap all events
    Notifier->>Notifier: Create DisputeChatMessage list
    Notifier->>Notifier: Update state.messages
    Notifier-->>UI: Render message list

    UI->>Service: Check unread status (hasUnreadMessages)
    Service->>Service: Filter messages using isFromUser predicate
    Service-->>UI: Unread status result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • AndreaDiazCorreia
  • BraCR10
  • grunch
  • mostronator

Poem

🐰 Wrapped with grace in single layers,
Messages stored without a care,
DisputeChatMessage leads the way,
Pending states show what we say,
The warren's disputes now shine so bright! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title clearly and specifically describes the main changes: unifying the dispute chat message model with NostrEvent and adding gift wrap storage, which are the primary objectives reflected in the code changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch phase-2-admin-user-chat

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/features/disputes/notifiers/dispute_chat_notifier.dart`:
- Line 28: The timestamp getter currently falls back to DateTime.now() which
yields non-deterministic ordering; change the fallback in the timestamp getter
(DateTime get timestamp) to a deterministic value such as
DateTime.fromMillisecondsSinceEpoch(0) or another stable field on event (e.g.,
event.updatedAt or an event ID-derived timestamp) instead of DateTime.now(),
leaving the primary use of event.createdAt intact.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e8b542 and 02a9e41.

📒 Files selected for processing (5)
  • docs/DISPUTE_CHAT_MULTIMEDIA_PLAN.md
  • lib/features/disputes/notifiers/dispute_chat_notifier.dart
  • lib/features/disputes/widgets/dispute_content.dart
  • lib/features/disputes/widgets/dispute_messages_list.dart
  • lib/services/dispute_read_status_service.dart

…ring bug

  - Per-message send errors now stay at message level only                                                              - Removed state.error propagation from _updateMessageState
  - Prevents full-chat error overlay on individual message failures
Copy link
Contributor

@mostronatorcoder mostronatorcoder bot left a comment

Choose a reason for hiding this comment

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

Clean refactor that properly unifies the dispute chat model with the P2P chat pattern. Key improvements:

  1. Encrypted storage: Storing gift wraps (encrypted) on disk instead of decrypted content is the correct approach — consistent with P2P chat and better for privacy.
  2. DisputeChatMessage wrapper: Thin, focused wrapper around NostrEvent with UI-only state (pending/error) — good separation of concerns.
  3. isFromUser() via pubkey comparison: Better than storing is_from_user flag — single source of truth.
  4. _updateMessageState() extraction: Eliminates significant duplication in error handling paths.
  5. getAdminSharedKey(): Good forward-thinking for Phase 3 multimedia.

CI passes, no conflicts, no unresolved comments. LGTM ✅

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant