feat(wallet): wire real sendRaw() — Phase 2 complete

- RustTimeline.sendRaw() now calls inner.sendRaw() via custom SDK .aar
- DefaultPaymentEventSender fully implemented: serializes payment data as JSON,
  sends co.sulkta.payment.request and co.sulkta.payment.status event types
- matrix-rust-sdk.aar built from sulkta/send-raw-v1 fork with UniFFI binding
- Removes UnsupportedOperationException stub — payments now actually send
This commit is contained in:
Kayos 2026-03-28 07:26:08 -07:00
parent 0113f65c7a
commit b867fa783e
2 changed files with 27 additions and 29 deletions

View file

@ -20,11 +20,8 @@ import kotlinx.serialization.json.Json
/**
* Default implementation of [PaymentEventSender].
*
* Since the Matrix SDK does not expose raw event sending, we send payment data
* as a structured message with a recognizable prefix that can be parsed by the UI.
*
* Message format: $CARDANO_PAY${json}
* This allows the timeline UI to render a payment card instead of raw text.
* Sends Cardano payment events as custom Matrix event types via Timeline.sendRaw().
* Events go through the send queue for reliability and encryption support.
*/
@ContributesBinding(SessionScope::class)
class DefaultPaymentEventSender @Inject constructor() : PaymentEventSender {
@ -50,16 +47,11 @@ class DefaultPaymentEventSender @Inject constructor() : PaymentEventSender {
)
val jsonContent = json.encodeToString(paymentData)
val message = "$PAYMENT_MESSAGE_PREFIX$jsonContent"
// Send as a regular message - the timeline renderer will recognize the prefix
return runCatching {
timeline.sendMessage(
body = message,
htmlBody = null,
intentionalMentions = emptyList(),
)
}
return timeline.sendRaw(
eventType = EVENT_TYPE_PAYMENT_REQUEST,
content = jsonContent,
)
}
override suspend fun sendStatusUpdate(
@ -75,21 +67,22 @@ class DefaultPaymentEventSender @Inject constructor() : PaymentEventSender {
)
val jsonContent = json.encodeToString(statusData)
val message = "$STATUS_MESSAGE_PREFIX$jsonContent"
return runCatching {
timeline.sendMessage(
body = message,
htmlBody = null,
intentionalMentions = emptyList(),
)
}
return timeline.sendRaw(
eventType = EVENT_TYPE_PAYMENT_STATUS,
content = jsonContent,
)
}
companion object {
/** Prefix for payment messages - UI parses this to render payment cards */
/** Matrix event type for Cardano payment requests */
const val EVENT_TYPE_PAYMENT_REQUEST = "co.sulkta.payment.request"
/** Matrix event type for payment status updates */
const val EVENT_TYPE_PAYMENT_STATUS = "co.sulkta.payment.status"
/** Legacy prefix for payment messages - kept for backward compatibility */
const val PAYMENT_MESSAGE_PREFIX = "\$CARDANO_PAY$"
/** Prefix for status update messages */
/** Legacy prefix for status update messages - kept for backward compatibility */
const val STATUS_MESSAGE_PREFIX = "\$CARDANO_STATUS$"
}
}

View file

@ -280,15 +280,20 @@ class RustTimeline(
}
/**
* Send a raw/custom event. Currently not supported by the Rust SDK bindings.
* The SDK Timeline does not expose sendRaw - custom events must use message markers for now.
* Send a raw/custom event to the room.
*
* @param eventType The event type (e.g., "co.sulkta.payment.request")
* @param content The JSON content of the event
* @return Result indicating success or failure
*/
override suspend fun sendRaw(
eventType: String,
content: String,
): Result<Unit> {
// The Rust SDK Timeline interface does not expose sendRaw yet.
return Result.failure(UnsupportedOperationException("sendRaw not yet supported by Matrix Rust SDK bindings"))
): Result<Unit> = withContext(dispatcher) {
runCatchingExceptions {
inner.sendRaw(eventType, content)
Unit
}
}
override suspend fun redactEvent(eventOrTransactionId: EventOrTransactionId, reason: String?): Result<Unit> = withContext(dispatcher) {