Updated on 2026-08-14
This commit is contained in:
commit
732df9f174
9 changed files with 295 additions and 109 deletions
|
|
@ -12,7 +12,7 @@ import com.tangem.tap.features.details.ui.details.DetailsFragment
|
|||
import com.tangem.tap.features.details.ui.resetcard.ResetCardFragment
|
||||
import com.tangem.tap.features.details.ui.securitymode.SecurityModeFragment
|
||||
import com.tangem.tap.features.details.ui.walletconnect.QrScanFragment
|
||||
import com.tangem.tap.features.details.ui.walletconnect.WalletConnectSessionsFragment
|
||||
import com.tangem.tap.features.details.ui.walletconnect.WalletConnectFragment
|
||||
import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment
|
||||
import com.tangem.tap.features.home.HomeFragment
|
||||
import com.tangem.tap.features.onboarding.products.note.OnboardingNoteFragment
|
||||
|
|
@ -101,7 +101,7 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
|
|||
AppScreen.AddTokens -> AddTokensFragment()
|
||||
AppScreen.AddCustomToken -> AddCustomTokenFragment()
|
||||
AppScreen.WalletDetails -> WalletDetailsFragment()
|
||||
AppScreen.WalletConnectSessions -> WalletConnectSessionsFragment()
|
||||
AppScreen.WalletConnectSessions -> WalletConnectFragment()
|
||||
AppScreen.QrScan -> QrScanFragment()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.tap.features.details.redux.walletconnect
|
||||
|
||||
import android.app.Activity
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.tap.common.redux.NotificationAction
|
||||
|
|
@ -19,7 +18,7 @@ sealed class WalletConnectAction : Action {
|
|||
object RestoreSessions : WalletConnectAction()
|
||||
|
||||
data class StartWalletConnect(
|
||||
val activity: Activity,
|
||||
val copiedUri: String?,
|
||||
) : WalletConnectAction()
|
||||
|
||||
data class ShowClipboardOrScanQrDialog(val wcUri: String) : WalletConnectAction()
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import com.tangem.common.extensions.guard
|
|||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.getFromClipboard
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
|
|
@ -51,7 +50,7 @@ class WalletConnectMiddleware {
|
|||
}
|
||||
}
|
||||
is WalletConnectAction.StartWalletConnect -> {
|
||||
val uri = action.activity.getFromClipboard()?.toString()
|
||||
val uri = action.copiedUri
|
||||
if (uri != null && WalletConnectManager.isCorrectWcUri(uri)) {
|
||||
store.dispatchOnMain(WalletConnectAction.ShowClipboardOrScanQrDialog(uri))
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import com.tangem.wallet.R
|
|||
fun SettingsScreensScaffold(
|
||||
content: @Composable () -> Unit,
|
||||
background: @Composable (() -> Unit)? = null,
|
||||
fab: @Composable (() -> Unit)? = null,
|
||||
titleRes: Int,
|
||||
onBackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
|
|
@ -41,6 +42,7 @@ fun SettingsScreensScaffold(
|
|||
topBar = { EmptyTopBarWithNavigation(onBackClick = onBackClick) },
|
||||
modifier = modifier.systemBarsPadding(),
|
||||
backgroundColor = colorResource(id = R.color.background_primary),
|
||||
floatingActionButton = { fab?.invoke() },
|
||||
) {
|
||||
|
||||
Box(modifier = modifier.fillMaxSize()) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
package com.tangem.tap.features.details.ui.walletconnect
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.google.accompanist.appcompattheme.AppCompatTheme
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState
|
||||
import com.tangem.tap.store
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
class WalletConnectFragment : Fragment(), StoreSubscriber<WalletConnectState> {
|
||||
private val viewModel = WalletConnectViewModel(store)
|
||||
private var screenState: MutableState<WalletConnectScreenState> =
|
||||
mutableStateOf(viewModel.updateState(store.state.walletConnectState))
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val inflater = TransitionInflater.from(requireContext())
|
||||
enterTransition = inflater.inflateTransition(android.R.transition.fade)
|
||||
exitTransition = inflater.inflateTransition(android.R.transition.fade)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): View {
|
||||
return ComposeView(requireContext()).apply {
|
||||
setContent {
|
||||
isTransitionGroup = true
|
||||
AppCompatTheme {
|
||||
WalletConnectScreen(
|
||||
state = screenState.value,
|
||||
onBackPressed = { store.dispatch(NavigationAction.PopBackTo()) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
store.subscribe(this) { state ->
|
||||
state.skipRepeats { oldState, newState ->
|
||||
oldState.walletConnectState == newState.walletConnectState
|
||||
}.select { it.walletConnectState }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
store.unsubscribe(this)
|
||||
}
|
||||
|
||||
override fun newState(state: WalletConnectState) {
|
||||
if (activity == null || view == null) return
|
||||
screenState.value = viewModel.updateState(state)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +1,182 @@
|
|||
package com.tangem.tap.features.details.ui.walletconnect
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.FloatingActionButton
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.LinearProgressIndicator
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.tap.common.compose.TangemTypography
|
||||
import com.tangem.tap.common.extensions.getFromClipboard
|
||||
import com.tangem.tap.features.details.ui.common.SettingsScreensScaffold
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
fun WalletConnectScreen(
|
||||
|
||||
state: WalletConnectScreenState,
|
||||
onBackPressed: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
SettingsScreensScaffold(
|
||||
content = {
|
||||
if (state.sessions.isEmpty()) {
|
||||
EmptyScreen(state, modifier)
|
||||
} else {
|
||||
WalletConnectSessions(state, modifier)
|
||||
}
|
||||
},
|
||||
fab = {
|
||||
AddSessionFab(
|
||||
onAddSession = {
|
||||
state.onAddSession(context.getFromClipboard()?.toString())
|
||||
},
|
||||
)
|
||||
},
|
||||
titleRes = R.string.wallet_connect_title,
|
||||
onBackClick = onBackPressed,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AddSessionFab(
|
||||
onAddSession: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
FloatingActionButton(
|
||||
onClick = onAddSession,
|
||||
backgroundColor = colorResource(id = R.color.button_primary),
|
||||
contentColor = colorResource(id = R.color.icon_primary_2),
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
modifier = modifier,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_plus),
|
||||
contentDescription = "",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//data class WalletConnectScreenState(
|
||||
// sessions: WcSessionScreenState
|
||||
//)
|
||||
//
|
||||
//data class WcSessionScreenState(
|
||||
// val icon: Bitmap,
|
||||
//
|
||||
//)
|
||||
@Composable
|
||||
private fun EmptyScreen(state: WalletConnectScreenState, modifier: Modifier = Modifier) {
|
||||
if (state.isLoading) {
|
||||
LinearProgressIndicator(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
color = colorResource(id = R.color.icon_accent),
|
||||
)
|
||||
}
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.padding(bottom = 64.dp),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_walletconnect),
|
||||
contentDescription = "",
|
||||
colorFilter = ColorFilter.tint(colorResource(id = R.color.icon_inactive)),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier = modifier
|
||||
.width(width = 100.dp),
|
||||
)
|
||||
Spacer(modifier = modifier.size(24.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.wallet_connect_subtitle),
|
||||
style = TangemTypography.body2,
|
||||
color = colorResource(id = R.color.text_tertiary),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WalletConnectSessions(
|
||||
state: WalletConnectScreenState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (state.isLoading) {
|
||||
LinearProgressIndicator(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(2.dp),
|
||||
color = colorResource(id = R.color.icon_accent),
|
||||
)
|
||||
} else {
|
||||
Spacer(modifier = modifier.height(2.dp))
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
items(state.sessions) { session ->
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 20.dp, vertical = 16.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
Text(
|
||||
text = session.description,
|
||||
style = TangemTypography.subtitle1,
|
||||
color = colorResource(id = R.color.text_primary_1),
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = { state.onRemoveSession(session.sessionId) },
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_cross_rounded_24),
|
||||
contentDescription = "",
|
||||
tint = colorResource(id = R.color.icon_warning),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun WalletConnectScreenPreview() {
|
||||
WalletConnectScreen(
|
||||
state = WalletConnectScreenState(
|
||||
sessions = listOf(
|
||||
WcSessionForScreen(
|
||||
description = "session from some dApp",
|
||||
cardId = "12312312321",
|
||||
sessionId = "",
|
||||
),
|
||||
),
|
||||
isLoading = true,
|
||||
),
|
||||
{},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
package com.tangem.tap.features.details.ui.walletconnect
|
||||
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession
|
||||
|
||||
data class WalletConnectScreenState(
|
||||
val sessions: List<WcSessionForScreen>,
|
||||
val isLoading: Boolean = false,
|
||||
val onRemoveSession: (String) -> Unit = {},
|
||||
val onAddSession: (String?) -> Unit = {},
|
||||
)
|
||||
|
||||
data class WcSessionForScreen(
|
||||
val description: String,
|
||||
val cardId: String,
|
||||
val sessionId: String,
|
||||
) {
|
||||
companion object {
|
||||
fun fromSession(session: WalletConnectSession): WcSessionForScreen {
|
||||
return WcSessionForScreen(
|
||||
description = session.peerMeta.name,
|
||||
cardId = session.wallet.cardId,
|
||||
sessionId = session.session.toUri(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
package com.tangem.tap.features.details.ui.walletconnect
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.transition.TransitionInflater
|
||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.FragmentWalletConnectSessionsBinding
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
|
||||
class WalletConnectSessionsFragment : Fragment(R.layout.fragment_wallet_connect_sessions),
|
||||
StoreSubscriber<WalletConnectState> {
|
||||
|
||||
private lateinit var walletConnectSessionsAdapter: WalletConnectSessionsAdapter
|
||||
private val binding: FragmentWalletConnectSessionsBinding by viewBinding(
|
||||
FragmentWalletConnectSessionsBinding::bind
|
||||
)
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
activity?.onBackPressedDispatcher?.addCallback(
|
||||
this, object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
})
|
||||
val inflater = TransitionInflater.from(requireContext())
|
||||
enterTransition = inflater.inflateTransition(R.transition.slide_right)
|
||||
exitTransition = inflater.inflateTransition(R.transition.fade)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
store.subscribe(this) { state ->
|
||||
state.skipRepeats { oldState, newState ->
|
||||
oldState.walletConnectState == newState.walletConnectState
|
||||
}.select { it.walletConnectState }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
store.unsubscribe(this)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding.toolbar.setNavigationOnClickListener {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
setOnClickListeners()
|
||||
setupTransactionsRecyclerView()
|
||||
}
|
||||
|
||||
private fun setOnClickListeners() {
|
||||
binding.fabOpenSession.setOnClickListener {
|
||||
store.dispatch(WalletConnectAction.StartWalletConnect(activity = requireActivity()))
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupTransactionsRecyclerView() = with(binding) {
|
||||
walletConnectSessionsAdapter = WalletConnectSessionsAdapter()
|
||||
rvWalletConnectSessions.layoutManager = LinearLayoutManager(requireContext())
|
||||
rvWalletConnectSessions.adapter = walletConnectSessionsAdapter
|
||||
walletConnectSessionsAdapter.submitList(store.state.walletConnectState.sessions)
|
||||
}
|
||||
|
||||
override fun newState(state: WalletConnectState) {
|
||||
if (activity == null || view == null) return
|
||||
|
||||
binding.fabOpenSession.show(!state.loading)
|
||||
binding.pbWalletConnect.show(state.loading)
|
||||
|
||||
showUpdatedList(state.sessions)
|
||||
}
|
||||
|
||||
private fun showUpdatedList(sessions: List<WalletConnectSession>) = with(binding) {
|
||||
walletConnectSessionsAdapter.submitList(sessions)
|
||||
rvWalletConnectSessions.show(sessions.isNotEmpty())
|
||||
tvNoSessions.show(sessions.isEmpty())
|
||||
tvNoSessionsTitle.show(sessions.isEmpty())
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.tap.features.details.ui.walletconnect
|
||||
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState
|
||||
import org.rekotlin.Store
|
||||
|
||||
class WalletConnectViewModel(private val store: Store<AppState>) {
|
||||
fun updateState(state: WalletConnectState): WalletConnectScreenState {
|
||||
return WalletConnectScreenState(
|
||||
state.sessions.map { wcSession -> WcSessionForScreen.fromSession(wcSession) },
|
||||
isLoading = state.loading,
|
||||
onRemoveSession = { sessionUri -> onRemoveSession(sessionUri, state.sessions) },
|
||||
onAddSession = { copiedUri -> store.dispatch(WalletConnectAction.StartWalletConnect(copiedUri)) },
|
||||
)
|
||||
}
|
||||
|
||||
private fun onRemoveSession(sessionUri: String, sessions: List<WalletConnectSession>) {
|
||||
sessions
|
||||
.firstOrNull { it.session.toUri() == sessionUri }
|
||||
?.let { baseSession ->
|
||||
store.dispatch(WalletConnectAction.DisconnectSession(baseSession.session))
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue