fix(wallet): replace text-marker hack with proper raw event API (room.sendRaw + MsgLikeKind.Other)

- Add Timeline.sendRaw() to send custom Matrix events
- Add CustomEventContent type for receiving custom events
- Update TimelineEventContentMapper to handle MsgLikeKind.Other
- Update TimelineItemContentFactory to intercept payment events
- Rewrite DefaultPaymentEventSender to use sendRaw instead of text markers
- Update TimelineItemContentPaymentFactory to parse raw JSON
- Remove text-marker detection from TimelineItemContentMessageFactory
- Update tests to use raw event API
- Mark raw event SDK blocker as RESOLVED in BLOCKERS.md

Event type: co.sulkta.payment.request (reverse-domain format)
Status updates: co.sulkta.payment.status

Benefits:
- Proper Matrix protocol compliance
- No JSON embedded in text messages
- Events won't be indexed by search
- Clean separation from regular messages
This commit is contained in:
Kayos 2026-03-27 11:45:12 -07:00
parent adee67cf0d
commit f2b95d6b8a
10 changed files with 264 additions and 189 deletions

View file

@ -156,55 +156,51 @@
---
## Task 8: Raw Event Handling ✅ COMPLETE
## Task 8: Raw Event Handling ✅ COMPLETE (UPGRADED)
### ✅ RESOLVED: SDK Raw Event API
**Previous blocker:** Matrix Rust SDK did not expose raw event sending or raw JSON access.
**Resolution:** The SDK (version 26.03.24) now provides:
- `Timeline.sendRaw(eventType: String, content: String)` — Sends custom event types
- `MsgLikeKind.Other` with `eventType` field — Receives custom events
- `TimelineItemDebugInfo.originalJson` — Access to raw event JSON via debug info provider
**Implementation updated to use proper raw events instead of text markers.**
### Completed
- ✅ **PaymentEventSender.kt** — Interface for sending payment events
- ✅ **DefaultPaymentEventSender.kt** — Implementation
- Sends payment as formatted text message with JSON payload
- Format: `[cardano-payment:v1]{...json...}\n💰 Sent X ADA`
- HTML body includes data-payment attribute for future parsing
- Status updates use separate marker: `[cardano-payment-status:v1]`
- ✅ **TimelineItemContentPaymentFactory.kt** — Parser for payment messages
- `isPaymentEvent(body)` — Detects payment marker
- `isPaymentStatusUpdate(body)` — Detects status update marker
- `createFromBody(body, isSentByMe)` — Parses text message body
- `createFromRaw(json, isSentByMe)` — Parses raw JSON (for future SDK extension)
- ✅ **DefaultPaymentEventSender.kt** — Implementation using raw events
- Uses `timeline.sendRaw(eventType, content)` to send custom events
- Event type: `co.sulkta.payment.request` (reverse-domain format)
- Status updates: `co.sulkta.payment.status`
- No text marker hack — proper Matrix custom events
- ✅ **TimelineItemContentPaymentFactory.kt** — Parser for payment events
- `isPaymentEventType(eventType)` — Checks for payment event type
- `isStatusUpdateEventType(eventType)` — Checks for status update type
- `createFromRaw(json, isSentByMe)` — Parses raw JSON from custom events
- Supports both camelCase and snake_case field names
- Graceful error handling — returns null on malformed JSON
- ✅ **TimelineItemContentMessageFactory.kt** — Modified to intercept payments
- Added paymentFactory dependency
- Added isSentByMe parameter to create()
- TextMessageType checks for payment marker before creating text content
- ✅ **TimelineItemContentFactory.kt** — Passes isSentByMe to message factory
- ✅ **TimelineEventContentMapper.kt** — Maps `MsgLikeKind.Other` to `CustomEventContent`
- ✅ **TimelineItemContentFactory.kt** — Handles `CustomEventContent` for payments
- Gets raw JSON via `timelineItemDebugInfoProvider().originalJson`
- Delegates to paymentFactory for payment event types
- ✅ **CustomEventContent.kt** — New EventContent type for custom events
- ✅ **Timeline.sendRaw()** — Added to Timeline interface and RustTimeline implementation
- ✅ **FakePaymentEventSender.kt** — Test fake
- ✅ **TimelineItemContentPaymentFactoryTest.kt** — Unit tests
### SDK Limitations & Approach
The Matrix Rust SDK does NOT expose:
- Raw event sending (`room.sendRawEvent()`)
- Raw JSON access for UnknownContent
**Workaround implemented:**
Instead of custom event types, we encode payment data in standard text messages:
```
[cardano-payment:v1]{"amount_lovelace":10000000,"to_address":"...","from_address":"...","tx_hash":"...","status":"pending","network":"testnet"}
💰 Sent 10 ADA
```
This approach:
- Works with existing SDK (no fork needed)
- Falls back gracefully (non-wallet clients see "💰 Sent 10 ADA")
- Can be upgraded to proper custom events when SDK exposes raw event APIs
- ✅ **TimelineItemContentPaymentFactoryTest.kt** — Updated unit tests
### m.replace Status Updates
**Decision:** Due to SDK limitations (no direct access to m.replace relations), status updates are sent as new messages rather than event replacements.
**Decision:** Status updates are sent as separate events of type `co.sulkta.payment.status`.
**Future improvement:** When SDK exposes event relations, refactor to use m.replace for cleaner status update thread.
### Potential Issues
- ⚠️ Status updates create new timeline events (not ideal, but works)
- ⚠️ Payment messages may be indexed by search (contains JSON)
- ⚠️ Very long addresses in JSON may hit message length limits (unlikely in practice)
### Benefits of Raw Event Approach
- ✅ Proper Matrix protocol compliance (custom event types, not hacked text)
- ✅ Non-wallet clients see "Unknown event" instead of JSON-in-text
- ✅ Clean separation of payment events from regular messages
- ✅ Events won't be indexed by message search
- ✅ No message length limits concern
---