Updated on 2026-08-14

This commit is contained in:
Tangem 2025-07-15 18:32:16 +07:00
parent fb32e4d5da
commit 67503c6c1a
11 changed files with 82 additions and 15 deletions

View file

@ -3,10 +3,7 @@ package com.tangem.core.ui.components.bottomsheets.modal
import android.content.res.Configuration
import androidx.annotation.DrawableRes
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@ -20,6 +17,7 @@ import com.tangem.core.ui.components.buttons.small.TangemIconButton
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
@ -30,6 +28,7 @@ import com.tangem.core.ui.res.TangemThemePreview
fun TangemModalBottomSheetTitle(
modifier: Modifier = Modifier,
title: TextReference? = null,
subtitle: TextReference? = null,
@DrawableRes startIconRes: Int? = null,
onStartClick: (() -> Unit)? = null,
@DrawableRes endIconRes: Int? = null,
@ -49,13 +48,23 @@ fun TangemModalBottomSheetTitle(
.align(Alignment.CenterStart),
)
}
if (title != null) {
Text(
text = title.resolveReference(),
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.primary1,
modifier = Modifier.align(Alignment.Center),
)
Column(modifier = Modifier.align(Alignment.Center)) {
if (title != null) {
Text(
text = title.resolveReference(),
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.primary1,
modifier = Modifier.align(Alignment.CenterHorizontally),
)
}
if (subtitle != null) {
Text(
text = subtitle.resolveReference(),
style = TangemTheme.typography.caption1,
color = TangemTheme.colors.text.tertiary,
modifier = Modifier.align(Alignment.CenterHorizontally),
)
}
}
if (endIconRes != null && onEndClick != null) {
TangemIconButton(
@ -79,6 +88,7 @@ private fun Preview_TangemModalBottomSheetTitle(
TangemThemePreview {
TangemModalBottomSheetTitle(
title = params.title,
subtitle = params.subtitle,
startIconRes = params.startIconRes,
onStartClick = params.onStartClick,
endIconRes = params.endIconRes,
@ -90,6 +100,7 @@ private fun Preview_TangemModalBottomSheetTitle(
private data class TangemModalBottomSheetTitleData(
val title: TextReference?,
val subtitle: TextReference?,
val startIconRes: Int?,
val onStartClick: (() -> Unit)?,
val endIconRes: Int?,
@ -104,6 +115,7 @@ private class TangemModalBottomSheetTitleProvider : PreviewParameterProvider<Tan
onStartClick = {},
endIconRes = null,
onEndClick = null,
subtitle = null,
),
TangemModalBottomSheetTitleData(
title = resourceReference(R.string.wallet_title),
@ -111,6 +123,7 @@ private class TangemModalBottomSheetTitleProvider : PreviewParameterProvider<Tan
onStartClick = null,
endIconRes = R.drawable.ic_close_24,
onEndClick = {},
subtitle = null,
),
TangemModalBottomSheetTitleData(
title = resourceReference(R.string.wallet_title),
@ -118,6 +131,23 @@ private class TangemModalBottomSheetTitleProvider : PreviewParameterProvider<Tan
onStartClick = {},
endIconRes = R.drawable.ic_close_24,
onEndClick = {},
subtitle = null,
),
TangemModalBottomSheetTitleData(
title = resourceReference(R.string.wallet_title),
subtitle = stringReference("Today • 2:37 PM"),
startIconRes = R.drawable.ic_back_24,
onStartClick = {},
endIconRes = R.drawable.ic_close_24,
onEndClick = {},
),
TangemModalBottomSheetTitleData(
title = null,
subtitle = stringReference("Today • 2:37 PM"),
startIconRes = R.drawable.ic_back_24,
onStartClick = {},
endIconRes = R.drawable.ic_close_24,
onEndClick = {},
),
TangemModalBottomSheetTitleData(
title = null,
@ -125,6 +155,7 @@ private class TangemModalBottomSheetTitleProvider : PreviewParameterProvider<Tan
onStartClick = {},
endIconRes = R.drawable.ic_close_24,
onEndClick = {},
subtitle = null,
),
)
}

View file

@ -51,6 +51,7 @@ dependencies {
/* Other */
implementation(deps.kotlin.coroutines)
implementation(deps.arrow.core)
implementation(deps.jodatime)
implementation(projects.domain.blockaid)
implementation(projects.domain.blockaid.models)
implementation(projects.libs.crypto)

View file

@ -22,6 +22,7 @@ import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.*
import org.joda.time.DateTime
import timber.log.Timber
@Suppress("LongParameterList")
@ -94,6 +95,7 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
sdkModel = WcSdkSessionConverter.convert(settledSession.session),
securityStatus = proposalState.dAppSession.securityStatus,
networks = sessionForApprove.network.toSet(),
connectingTime = DateTime.now().millis,
)
sessionsManager.saveSession(newSession)
analytics.send(

View file

@ -23,6 +23,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.*
import org.joda.time.DateTime
import timber.log.Timber
import kotlin.coroutines.resume
@ -65,7 +66,14 @@ internal class DefaultWcSessionsManager(
}
override suspend fun saveSession(session: WcSession) {
store.saveSession(WcSessionDTO(session.sdkModel.topic, session.wallet.walletId, session.securityStatus))
store.saveSession(
WcSessionDTO(
topic = session.sdkModel.topic,
walletId = session.wallet.walletId,
securityStatus = session.securityStatus,
connectingTime = session.connectingTime ?: DateTime.now().millis,
),
)
}
override suspend fun removeSession(session: WcSession): Either<Throwable, Unit> {
@ -89,6 +97,7 @@ internal class DefaultWcSessionsManager(
sdkModel = WcSdkSessionConverter.convert(sdkSession),
securityStatus = storedSession.securityStatus,
networks = networks,
connectingTime = storedSession.connectingTime,
)
}
@ -137,6 +146,7 @@ internal class DefaultWcSessionsManager(
sdkModel = WcSdkSessionConverter.convert(sdkSession),
securityStatus = session.securityStatus,
networks = networks,
connectingTime = session.connectingTime,
)
}
return wcSessions

View file

@ -95,6 +95,7 @@ internal class DefaultWcPairUseCaseTest {
sdkModel = WcSdkSessionConverter.convert(this),
securityStatus = CheckDAppResult.SAFE,
networks = setOf(),
connectingTime = null,
)
private fun useCaseFactory() = DefaultWcPairUseCase(
@ -144,7 +145,7 @@ internal class DefaultWcPairUseCaseTest {
coEvery { sdkDelegate.pair(url) } returns sdkProposal.right()
coEvery { sdkDelegate.approve(sdkApprove) } returns sdkApproveSuccess.right()
coEvery { sessionsManager.saveSession(sessionForSave) } returns Unit
coEvery { sessionsManager.saveSession(any()) } returns Unit
coEvery { blockAidVerifier.verifyDApp(any()) } returns Either.catch { CheckDAppResult.SAFE }
val useCase = useCaseFactory()
@ -162,9 +163,12 @@ internal class DefaultWcPairUseCaseTest {
assertEquals(approveLoading, awaitItem())
coVerifyOrder {
sdkDelegate.approve(sdkApprove)
sessionsManager.saveSession(sessionForSave)
sessionsManager.saveSession(any())
}
assertEquals(result, awaitItem())
val actual: WcPairState = awaitItem()
assert(actual is WcPairState.Approving.Result)
actual as WcPairState.Approving.Result
assertEquals(result.session, actual.session)
awaitComplete()
}
}

View file

@ -51,6 +51,7 @@ internal class WcSignUseCaseDelegateTest {
wallet = MockUserWalletFactory.create(),
networks = setOf(),
securityStatus = CheckDAppResult.FAILED_TO_VERIFY,
connectingTime = 0L,
sdkModel = WcSdkSession(
topic = "",
appMetaData = WcAppMetaData(

View file

@ -10,4 +10,5 @@ data class WcSession(
val networks: Set<Network>,
val sdkModel: WcSdkSession,
val securityStatus: CheckDAppResult,
val connectingTime: Long?,
)

View file

@ -9,4 +9,5 @@ data class WcSessionDTO(
val topic: String,
val walletId: UserWalletId,
val securityStatus: CheckDAppResult = CheckDAppResult.FAILED_TO_VERIFY,
val connectingTime: Long? = null,
)

View file

@ -11,6 +11,7 @@ data class WcConnectedAppInfoUM(
val notification: WcAppInfoSecurityNotification? = null,
val appSubtitle: String,
val walletName: String,
val connectingTime: Long?,
val networks: ImmutableList<WcNetworkInfoItem.Required>,
val disconnectButtonConfig: WcPrimaryButtonConfig,
val onDismiss: () -> Unit,

View file

@ -69,6 +69,7 @@ internal class WcConnectedAppInfoModel @Inject constructor(
verifiedDAppState = extractVerifiedState(session),
appSubtitle = WcAppSubtitleConverter.convert(session.sdkModel.appMetaData),
walletName = session.wallet.name,
connectingTime = session.connectingTime,
networks = session.networks
.map {
WcNetworkInfoItem.Required(

View file

@ -24,9 +24,13 @@ import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTi
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.utils.DateTimeFormatters
import com.tangem.core.ui.utils.toDateFormatWithTodayYesterday
import com.tangem.core.ui.utils.toTimeFormat
import com.tangem.features.walletconnect.connections.entity.*
import com.tangem.features.walletconnect.impl.R
import kotlinx.collections.immutable.ImmutableList
@ -44,6 +48,15 @@ internal fun WcConnectedAppInfoBS(state: WcConnectedAppInfoUM) {
title = {
TangemModalBottomSheetTitle(
title = resourceReference(R.string.wc_wallet_connect),
subtitle = state.connectingTime?.let { timestamp ->
resourceReference(
R.string.send_date_format,
wrappedList(
timestamp.toDateFormatWithTodayYesterday(DateTimeFormatters.dateFormatter),
timestamp.toTimeFormat(),
),
)
},
endIconRes = R.drawable.ic_close_24,
onEndClick = state.onDismiss,
)
@ -185,6 +198,7 @@ private fun WcConnectedAppInfoBS_Preview() {
isVerified = true,
appSubtitle = "react-app.walletconnect.com",
walletName = "Tangem 2.0",
connectingTime = System.currentTimeMillis(),
networks = persistentListOf(
WcNetworkInfoItem.Required(
id = "1",