Phase 3: Wallet panel UI and full /pay flow wiring
- Add WalletPanelView with 4 tabs (Overview, Assets, History, Settings) - Overview tab shows balance, QR code for receiving, and Send ADA button - Assets tab shows native tokens held at address - History tab shows recent transactions with explorer links - Settings tab shows address, network, and backup/delete options - Add NativeAsset and TxSummary models to wallet API - Add getAddressAssets() and getAddressTransactions() to CardanoClient - Implement new methods in KoiosCardanoClient and FakeCardanoClient - Add wallet button to MessagesViewTopBar (DM rooms only) - Add isDmRoom to MessagesState for conditional UI - Wire navigateToWallet() callback through to MessagesFlowNode - Add NavTarget.WalletPanel and WalletPanelNode integration - Add string resources for wallet panel UI Known limitations: - Uses Chart icon as placeholder for wallet (Compound lacks wallet icon) - Wallet setup flow not implemented (TODO) - Transaction amounts in history need additional API calls to calculate
This commit is contained in:
parent
b867fa783e
commit
e33c87c164
24 changed files with 1685 additions and 1 deletions
|
|
@ -53,6 +53,7 @@ import io.element.android.features.messages.impl.timeline.model.event.duration
|
|||
import io.element.android.features.poll.api.create.CreatePollEntryPoint
|
||||
import io.element.android.features.poll.api.create.CreatePollMode
|
||||
import io.element.android.features.wallet.api.WalletEntryPoint
|
||||
import io.element.android.features.wallet.impl.panel.WalletPanelNode
|
||||
import io.element.android.libraries.architecture.BackstackWithOverlayBox
|
||||
import io.element.android.libraries.architecture.BaseFlowNode
|
||||
import io.element.android.libraries.architecture.callback
|
||||
|
|
@ -182,6 +183,9 @@ class MessagesFlowNode(
|
|||
@Parcelize
|
||||
data class Thread(val threadRootId: ThreadId, val focusedEventId: EventId?) : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data object WalletPanel : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data class PaymentFlow(
|
||||
val roomId: RoomId,
|
||||
|
|
@ -304,6 +308,10 @@ class MessagesFlowNode(
|
|||
backstack.push(NavTarget.Thread(threadRootId, focusedEventId))
|
||||
}
|
||||
|
||||
override fun navigateToWallet() {
|
||||
backstack.push(NavTarget.WalletPanel)
|
||||
}
|
||||
|
||||
override fun navigateToPaymentFlow(
|
||||
roomId: RoomId,
|
||||
recipientUserId: UserId?,
|
||||
|
|
@ -533,6 +541,23 @@ class MessagesFlowNode(
|
|||
}
|
||||
createNode<ThreadedMessagesNode>(buildContext, listOf(inputs, callback))
|
||||
}
|
||||
is NavTarget.WalletPanel -> {
|
||||
val walletPanelCallback = object : WalletPanelNode.Callback {
|
||||
override fun onClose() {
|
||||
backstack.pop()
|
||||
}
|
||||
|
||||
override fun onSendAda() {
|
||||
backstack.pop()
|
||||
backstack.push(NavTarget.PaymentFlow(room.roomId, null, null, null))
|
||||
}
|
||||
|
||||
override fun onSetupWallet() {
|
||||
// TODO: Navigate to wallet setup flow
|
||||
}
|
||||
}
|
||||
createNode<WalletPanelNode>(buildContext, listOf(walletPanelCallback))
|
||||
}
|
||||
is NavTarget.PaymentFlow -> {
|
||||
val walletCallback = object : WalletEntryPoint.Callback {
|
||||
override fun onPaymentSent(txHash: String) {
|
||||
|
|
|
|||
|
|
@ -130,6 +130,7 @@ class MessagesNode(
|
|||
fun navigateToRoomDetails()
|
||||
fun navigateToPinnedMessagesList()
|
||||
fun navigateToKnockRequestsList()
|
||||
fun navigateToWallet()
|
||||
fun navigateToPaymentFlow(roomId: RoomId, recipientUserId: UserId?, recipientAddress: String?, amountLovelace: Long?)
|
||||
}
|
||||
|
||||
|
|
@ -293,6 +294,7 @@ class MessagesNode(
|
|||
callback.navigateToRoomCall(room.roomId, isAudioCall)
|
||||
},
|
||||
onViewAllPinnedMessagesClick = callback::navigateToPinnedMessagesList,
|
||||
onWalletClick = callback::navigateToWallet,
|
||||
modifier = modifier,
|
||||
knockRequestsBannerView = {
|
||||
knockRequestsBannerRenderer.View(
|
||||
|
|
|
|||
|
|
@ -295,6 +295,7 @@ class MessagesPresenter(
|
|||
dmUserVerificationState = dmUserVerificationState,
|
||||
roomMemberModerationState = roomMemberModerationState,
|
||||
topBarSharedHistoryIcon = topBarSharedHistoryIcon,
|
||||
isDmRoom = roomInfo.isDm,
|
||||
successorRoom = roomInfo.successorRoom,
|
||||
eventSink = ::handleEvent,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ data class MessagesState(
|
|||
val roomMemberModerationState: RoomMemberModerationState,
|
||||
/** Type of "shared history" icon to show in the top bar. */
|
||||
val topBarSharedHistoryIcon: SharedHistoryIcon,
|
||||
val isDmRoom: Boolean,
|
||||
val successorRoom: SuccessorRoom?,
|
||||
val eventSink: (MessagesEvent) -> Unit
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ fun aMessagesState(
|
|||
dmUserVerificationState: IdentityState? = null,
|
||||
roomMemberModerationState: RoomMemberModerationState = aRoomMemberModerationState(),
|
||||
topBarSharedHistoryIcon: SharedHistoryIcon = SharedHistoryIcon.NONE,
|
||||
isDmRoom: Boolean = false,
|
||||
successorRoom: SuccessorRoom? = null,
|
||||
eventSink: (MessagesEvent) -> Unit = {},
|
||||
) = MessagesState(
|
||||
|
|
@ -149,6 +150,7 @@ fun aMessagesState(
|
|||
dmUserVerificationState = dmUserVerificationState,
|
||||
roomMemberModerationState = roomMemberModerationState,
|
||||
topBarSharedHistoryIcon = topBarSharedHistoryIcon,
|
||||
isDmRoom = isDmRoom,
|
||||
successorRoom = successorRoom,
|
||||
eventSink = eventSink,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -131,6 +131,7 @@ fun MessagesView(
|
|||
onSendLocationClick: () -> Unit,
|
||||
onCreatePollClick: () -> Unit,
|
||||
onJoinCallClick: (isAudioCall: Boolean) -> Unit,
|
||||
onWalletClick: () -> Unit,
|
||||
onViewAllPinnedMessagesClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
forceJumpToBottomVisibility: Boolean = false,
|
||||
|
|
@ -226,9 +227,11 @@ fun MessagesView(
|
|||
roomCallState = state.roomCallState,
|
||||
dmUserIdentityState = state.dmUserVerificationState,
|
||||
sharedHistoryIcon = state.topBarSharedHistoryIcon,
|
||||
isDmRoom = state.isDmRoom,
|
||||
onBackClick = { hidingKeyboard { onBackClick() } },
|
||||
onRoomDetailsClick = { hidingKeyboard { onRoomDetailsClick() } },
|
||||
onJoinCallClick = onJoinCallClick,
|
||||
onWalletClick = onWalletClick,
|
||||
)
|
||||
}
|
||||
},
|
||||
|
|
@ -268,6 +271,7 @@ fun MessagesView(
|
|||
},
|
||||
forceJumpToBottomVisibility = forceJumpToBottomVisibility,
|
||||
onJoinCallClick = onJoinCallClick,
|
||||
onWalletClick = onWalletClick,
|
||||
onViewAllPinnedMessagesClick = onViewAllPinnedMessagesClick,
|
||||
knockRequestsBannerView = knockRequestsBannerView,
|
||||
)
|
||||
|
|
@ -424,6 +428,7 @@ private fun MessagesViewContent(
|
|||
onSendLocationClick: () -> Unit,
|
||||
onCreatePollClick: () -> Unit,
|
||||
onJoinCallClick: (isAudioCall: Boolean) -> Unit,
|
||||
onWalletClick: () -> Unit,
|
||||
onViewAllPinnedMessagesClick: () -> Unit,
|
||||
forceJumpToBottomVisibility: Boolean,
|
||||
onSwipeToReply: (TimelineItem.Event) -> Unit,
|
||||
|
|
@ -592,6 +597,7 @@ internal fun MessagesViewPreview(@PreviewParameter(MessagesStateProvider::class)
|
|||
onSendLocationClick = {},
|
||||
onCreatePollClick = {},
|
||||
onJoinCallClick = {},
|
||||
onWalletClick = {},
|
||||
onViewAllPinnedMessagesClick = { },
|
||||
forceJumpToBottomVisibility = true,
|
||||
knockRequestsBannerView = {},
|
||||
|
|
@ -646,6 +652,7 @@ internal fun MessagesViewA11yPreview() = ElementPreview {
|
|||
onSendLocationClick = {},
|
||||
onCreatePollClick = {},
|
||||
onJoinCallClick = {},
|
||||
onWalletClick = {},
|
||||
onViewAllPinnedMessagesClick = { },
|
||||
forceJumpToBottomVisibility = true,
|
||||
knockRequestsBannerView = {},
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ internal fun MessagesViewWithIdentityChangePreview(
|
|||
onSendLocationClick = {},
|
||||
onCreatePollClick = {},
|
||||
onJoinCallClick = {},
|
||||
onWalletClick = {},
|
||||
onViewAllPinnedMessagesClick = {},
|
||||
knockRequestsBannerView = {}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -298,6 +298,7 @@ class ThreadedMessagesNode(
|
|||
onJoinCallClick = { isAudioCall ->
|
||||
callback.navigateToRoomCall(room.roomId, isAudioCall)
|
||||
},
|
||||
onWalletClick = {},
|
||||
onViewAllPinnedMessagesClick = {},
|
||||
modifier = modifier,
|
||||
knockRequestsBannerView = {},
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview
|
|||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.IconButton
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||
import io.element.android.libraries.matrix.api.encryption.identity.IdentityState
|
||||
|
|
@ -65,8 +66,10 @@ internal fun MessagesViewTopBar(
|
|||
roomCallState: RoomCallState,
|
||||
dmUserIdentityState: IdentityState?,
|
||||
sharedHistoryIcon: SharedHistoryIcon,
|
||||
isDmRoom: Boolean,
|
||||
onRoomDetailsClick: () -> Unit,
|
||||
onJoinCallClick: (isAudioCall: Boolean) -> Unit,
|
||||
onWalletClick: () -> Unit,
|
||||
onBackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
|
@ -127,6 +130,15 @@ internal fun MessagesViewTopBar(
|
|||
}
|
||||
},
|
||||
actions = {
|
||||
// Wallet button - only show in DM rooms
|
||||
if (isDmRoom) {
|
||||
IconButton(onClick = onWalletClick) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.Chart(),
|
||||
contentDescription = "Cardano Wallet",
|
||||
)
|
||||
}
|
||||
}
|
||||
CallMenuItem(
|
||||
roomCallState = roomCallState,
|
||||
onJoinCallClick = onJoinCallClick,
|
||||
|
|
@ -186,6 +198,7 @@ internal fun MessagesViewTopBarPreview() = ElementPreview {
|
|||
roomCallState: RoomCallState = RoomCallState.Unavailable,
|
||||
dmUserIdentityState: IdentityState? = null,
|
||||
sharedHistoryIcon: SharedHistoryIcon = SharedHistoryIcon.NONE,
|
||||
isDmRoom: Boolean = false,
|
||||
) = MessagesViewTopBar(
|
||||
roomName = roomName,
|
||||
roomAvatar = roomAvatar,
|
||||
|
|
@ -194,8 +207,10 @@ internal fun MessagesViewTopBarPreview() = ElementPreview {
|
|||
roomCallState = roomCallState,
|
||||
dmUserIdentityState = dmUserIdentityState,
|
||||
sharedHistoryIcon = sharedHistoryIcon,
|
||||
isDmRoom = isDmRoom,
|
||||
onRoomDetailsClick = {},
|
||||
onJoinCallClick = {},
|
||||
onWalletClick = {},
|
||||
onBackClick = {},
|
||||
)
|
||||
Column {
|
||||
|
|
@ -218,7 +233,8 @@ internal fun MessagesViewTopBarPreview() = ElementPreview {
|
|||
url = "https://some-avatar.jpg"
|
||||
),
|
||||
roomCallState = aStandByCallState(canStartCall = false),
|
||||
dmUserIdentityState = IdentityState.Verified
|
||||
dmUserIdentityState = IdentityState.Verified,
|
||||
isDmRoom = true,
|
||||
)
|
||||
HorizontalDivider()
|
||||
AMessagesViewTopBar(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue