TimelineItemEventContent is a sealed interface in messages:impl, so external
modules cannot add implementers to its hierarchy.
Solution: Create TimelineItemPaymentContentWrapper in messages:impl that
implements the sealed interface and wraps the wallet API's payment content.
Changes:
- Remove inheritance from TimelineItemPaymentContent (wallet:api)
- Add TimelineItemPaymentContentWrapper (messages:impl)
- Update TimelineItemContentFactory to wrap payment content
- Update TimelineItemEventContentView to use wrapper
- 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
Task 7: Timeline Payment Card
- TimelineItemPaymentView integration with TimelineItemEventContentView
- Payment card rendering for both sender and recipient perspectives
- Unit tests for TimelineItemPaymentContent
Task 8: Raw Event Handling
- Modified TimelineItemContentMessageFactory to intercept payment events
- Added isSentByMe parameter propagation through content factories
- FakePaymentEventSender for testing
- Unit tests for TimelineItemContentPaymentFactory
SDK Limitation Workaround:
Since matrix-rust-sdk doesn't expose raw event sending or UnknownContent
raw JSON, payment events are encoded as text messages with a marker:
[cardano-payment:v1]{...json...}
This falls back gracefully for non-wallet clients while enabling
rich payment card rendering for wallet-enabled clients.
Implements the payment flow UI for the Element X ADA wallet:
## Screens
- PaymentEntryScreen: Amount/recipient input with pre-filling from /pay command
- PaymentConfirmationScreen: Transaction details with fee estimation (FLAG_SECURE)
- PaymentProgressScreen: Submission status with polling for confirmation
## Features
- Biometric authentication before payment confirmation
- Matrix user detection with 'hasn't linked wallet' inline message
- CardanoScan explorer link for transaction viewing
- Testnet warning banner
- Insufficient funds detection
## Wire-up
- MessageComposerPresenter intercepts /pay commands
- SlashCommandParser integration for command detection
- Navigation to PaymentFlowNode on valid /pay command
- Snackbar error on parse errors
## Technical
- Circuit presenter pattern with Molecule/Turbine tests
- @PreviewsDayNight for all Composables
- Metro DI integration
- Fake implementations for testing
Includes PaymentEntryPresenterTest, PaymentConfirmationPresenterTest,
PaymentProgressPresenterTest with comprehensive coverage.
* Fix long messages not being clickable
As @bmarty found out, `clip = true` causes the click event to be ignored in some cases. Since we have the shape we want to draw and we're using a custom `onDraw` modifier anyway to cut-out part of the path, we can just draw everything using the modifier and avoid using `clip = true`.
This seems to fix the issue.
* Fix clipping of images or other items that cover the bubble
* Fix borders being displayed for contents
* Extract the layer drawing logic into `drawInLayer` to simplify the inlined code. Remove redundant code, those changes are now in the `drawInLayer` block
* Workaround for lint issue: it seems like detekt can't properly detect usages in content receivers
* Update screenshots
---------
Co-authored-by: ElementBot <android@element.io>
- Added `MediaSource.safeUrl` property replacing `withCleanUrl` method.
- Made `url` private so it can't be used externally.
- Reverted code in `CoilMediaFetcher`
- Also add tests
if something else grabs focus mid-recording (phone call, etc), stop
the recording and keep the partial result in preview state instead
of silently recording garbage
Signed-off-by: vmfunc <celeste@linux.com>
separates recording from playback focus - willPausedWhenDucked is false
for recording so notification sounds don't interrupt mid-recording
Signed-off-by: vmfunc <celeste@linux.com>
* Ensure aspect ratio of images in the timeline is restricted
Otherwise, this could cause a crash in Compose since the width and height values could become way too large.