Updated on 2026-08-14

This commit is contained in:
Tangem 2022-02-22 23:49:06 +03:00
parent 3c3db2cc09
commit 2c62f977c1
46 changed files with 660 additions and 73 deletions

View file

@ -6,18 +6,21 @@ apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'
android {
compileSdkVersion 30
compileSdkVersion 31
defaultConfig {
applicationId "com.tangem.wallet"
versionCode project.hasProperty('versionCode') ? project.property('versionCode') as int : 1
versionName project.hasProperty('versionName') ? project.property('versionName') : "1.0.0.-SNAPSHOT"
minSdkVersion 21
targetSdkVersion 30
targetSdkVersion 31
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
androidExtensions {
experimental = true
}
buildFeatures {
compose true
}
buildTypes {
release {
debuggable false
@ -47,12 +50,14 @@ android {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
coreLibraryDesugaringEnabled false
}
composeOptions {
kotlinCompilerExtensionVersion '1.1.0'
}
packagingOptions {
exclude 'lib/x86_64/darwin/libscrypt.dylib'
exclude 'lib/x86_64/freebsd/libscrypt.so'
@ -67,13 +72,13 @@ repositories {
dependencies {
implementation fileTree(include: ['*.aar'], dir: 'libs')
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'androidx.fragment:fragment-ktx:1.3.6'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
implementation 'com.google.android.material:material:1.4.0'
implementation "androidx.core:core-ktx:1.6.0"
implementation 'com.google.android.play:core:1.10.2'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.fragment:fragment-ktx:1.4.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'com.google.android.material:material:1.5.0'
implementation "androidx.core:core-ktx:1.7.0"
implementation 'com.google.android.play:core:1.10.3'
implementation 'com.google.android.play:core-ktx:1.8.1'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
@ -138,6 +143,13 @@ dependencies {
// Google Pay
implementation 'com.google.android.gms:play-services-wallet:19.1.0'
//Compose
implementation 'androidx.activity:activity-compose:1.4.0'
implementation 'androidx.compose.material:material:1.1.0'
implementation 'androidx.compose.animation:animation:1.1.0'
implementation 'androidx.compose.ui:ui-tooling:1.1.0'
implementation "com.google.accompanist:accompanist-appcompat-theme:0.23.0"
testImplementation 'junit:junit:4.13.2'
testImplementation "com.google.truth:truth:1.1.3"
androidTestImplementation 'androidx.test.ext:junit:1.1.3'

View file

@ -44,6 +44,7 @@
android:value="true" />
<activity
android:exported="true"
android:name="com.tangem.tap.MainActivity"
android:launchMode="singleTask"
android:screenOrientation="portrait"

@ -1 +1 @@
Subproject commit 42803296690ad6914ec2c59c5fdc3f0fb30f8afc
Subproject commit 180252680e80b88f8fec9add783207d41d30e4e3

View file

@ -2,35 +2,25 @@ package com.tangem.tap.features.home
import android.os.Bundle
import android.view.View
import androidx.transition.TransitionInflater
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.redux.navigation.FragmentShareTransition
import com.tangem.tap.common.redux.navigation.ShareElement
import com.tangem.tap.common.toggleWidget.IndeterminateProgressButtonWidget
import com.tangem.tap.common.toggleWidget.ViewStateWidget
import com.tangem.tap.common.transitions.FrontCardEnterTransition
import com.tangem.tap.common.transitions.FrontCardExitTransition
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.Fragment
import com.google.accompanist.appcompattheme.AppCompatTheme
import com.tangem.tap.features.home.compose.StoriesScreen
import com.tangem.tap.features.home.redux.HomeAction
import com.tangem.tap.features.home.redux.HomeState
import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_onboarding_main.*
import kotlinx.android.synthetic.main.layout_onboarding_container_bottom.*
import kotlinx.android.synthetic.main.layout_onboarding_home_top.*
import kotlinx.android.synthetic.main.view_bg_home.*
import org.rekotlin.StoreSubscriber
class HomeFragment : BaseOnboardingFragment<HomeState>() {
class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber<HomeState> {
private lateinit var btnScanCard: ViewStateWidget
override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_home_top
var homeState: MutableState<HomeState> = mutableStateOf(store.state.homeState)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val inflater = TransitionInflater.from(requireContext())
exitTransition = inflater.inflateTransition(R.transition.fade)
store.dispatch(HomeAction.Init)
}
@ -39,50 +29,36 @@ class HomeFragment : BaseOnboardingFragment<HomeState>() {
store.dispatch(BackupAction.CheckForUnfinishedBackup)
toolbar.hide()
val shareTransition = FragmentShareTransition(
listOf(
ShareElement(imv_front_card, ShareElement.imvFrontCard),
ShareElement(imv_back_card, ShareElement.imvBackCard),
ShareElement(bg_circle_large),
ShareElement(bg_circle_medium),
ShareElement(bg_circle_min),
),
FrontCardEnterTransition(),
FrontCardExitTransition()
)
store.dispatch(HomeAction.SetFragmentShareTransition(shareTransition))
tv_header.setText(R.string.home_welcome_header)
tv_body.setText(R.string.home_welcome_body)
btn_main_action.setText(R.string.home_button_scan)
btn_main_action.setOnClickListener { store.dispatch(HomeAction.ReadCard) }
btn_alternative_action.setText(R.string.home_button_get_new_card)
btn_alternative_action.setOnClickListener { store.dispatch(HomeAction.GoToShop) }
btnScanCard = IndeterminateProgressButtonWidget(btn_main_action, progress)
getView()?.findViewById<ComposeView>(R.id.cv_stories)?.setContent {
AppCompatTheme {
StoriesScreen(
homeState,
onScanButtonClick = { store.dispatch(HomeAction.ReadCard) },
onShopButtonClick = { store.dispatch(HomeAction.GoToShop) }
)
}
}
}
override fun subscribeToStore() {
override fun onStart() {
super.onStart()
store.subscribe(this) { state ->
state.skipRepeats { oldState, newState ->
oldState.homeState == newState.homeState
}.select { it.homeState }
}
storeSubscribersList.add(this)
}
override fun onStop() {
super.onStop()
store.unsubscribe(this)
}
override fun newState(state: HomeState) {
if (activity == null) return
btnScanCard.changeState(state.btnScanState.progressState)
btnScanCard.mainView.isEnabled = state.btnScanState.enabled
homeState.value = state
}
override fun onDestroyView() {
store.dispatch(HomeAction.SetFragmentShareTransition(null))
super.onDestroyView()
}
}

View file

@ -0,0 +1,146 @@
package com.tangem.tap.features.home.compose
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
import androidx.compose.animation.scaleIn
import androidx.compose.animation.slideInVertically
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.tangem.wallet.R
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun FirstStoriesContent(
paused: Boolean, duration: Int = 8_000,
hideContent: (Boolean) -> Unit
) {
val screenState = remember { mutableStateOf(StartingScreenState.INIT) }
val progress = remember { Animatable(0f) }
LaunchedEffect(paused) {
if (paused) {
progress.stop()
} else {
progress.animateTo(
targetValue = 2f,
animationSpec = tween(
durationMillis = duration,
easing = LinearEasing
)
)
}
}
when (progress.value) {
in 0f..0.2f -> screenState.value = StartingScreenState.INIT
in 0.2f..0.3f -> screenState.value = StartingScreenState.BUY
in 0.3f..0.4f -> screenState.value = StartingScreenState.STORE
in 0.4f..0.5f -> screenState.value = StartingScreenState.SEND
in 0.5f..0.6f -> screenState.value = StartingScreenState.PAY
in 0.6f..0.7f -> screenState.value = StartingScreenState.EXCHANGE
in 0.7f..0.8f -> screenState.value = StartingScreenState.BORROW
in 0.8f..1f -> screenState.value = StartingScreenState.LEND
in 1f..1.2f -> screenState.value = StartingScreenState.SHOW_CARD
in 1.2f..2f -> screenState.value = StartingScreenState.MEET_TANGEM
}
if (screenState.value == StartingScreenState.INIT) hideContent(true)
if (screenState.value == StartingScreenState.BUY) hideContent(false)
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier =
Modifier
.fillMaxSize(),
) {
val text = when (screenState.value) {
StartingScreenState.INIT -> null
StartingScreenState.BUY -> R.string.story_meet_buy
StartingScreenState.STORE -> R.string.story_meet_store
StartingScreenState.SEND -> R.string.story_meet_send
StartingScreenState.PAY -> R.string.story_meet_pay
StartingScreenState.EXCHANGE -> R.string.story_meet_exchange
StartingScreenState.BORROW -> R.string.story_meet_borrow
StartingScreenState.LEND -> R.string.story_meet_lend
StartingScreenState.SHOW_CARD -> R.string.story_meet_title
StartingScreenState.MEET_TANGEM -> R.string.story_meet_title
}
if (screenState.value != StartingScreenState.MEET_TANGEM) {
Text(
text = text?.let { stringResource(text) } ?: "",
fontSize = 60.sp,
fontWeight = FontWeight.SemiBold,
modifier = Modifier
.padding(start = 40.dp, end = 40.dp)
.alpha(if (screenState.value == StartingScreenState.SHOW_CARD) 0f else 1f),
color = Color.White,
textAlign = TextAlign.Center,
)
}
AnimatedVisibility(
visible = screenState.value == StartingScreenState.MEET_TANGEM,
enter = slideInVertically() { it / 2 }
) {
Text(
text = text?.let { stringResource(text) } ?: "",
fontSize = 60.sp,
fontWeight = FontWeight.SemiBold,
modifier = Modifier
.padding(start = 40.dp, end = 40.dp),
color = Color.White,
textAlign = TextAlign.Center
)
}
AnimatedVisibility(
visible = screenState.value == StartingScreenState.SHOW_CARD ||
screenState.value == StartingScreenState.MEET_TANGEM,
enter = scaleIn(initialScale = 3f)
) {
Image(
painter = painterResource(
id = R.drawable.meet_tangem
),
contentDescription = null,
contentScale = ContentScale.FillWidth,
modifier = Modifier.fillMaxWidth()
)
}
}
}
enum class StartingScreenState {
INIT, BUY, STORE, SEND, PAY, EXCHANGE, BORROW, LEND, SHOW_CARD, MEET_TANGEM
}
@Preview
@Composable
fun Stories1Preview() {
FirstStoriesContent(false, 7_000) {}
}

View file

@ -0,0 +1,66 @@
package com.tangem.tap.features.home.compose
import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.tangem.wallet.R
import androidx.compose.foundation.layout.fillMaxWidth as fillMaxWidth1
@Composable
fun HomeButtons(
isDarkBackground: Boolean, modifier: Modifier = Modifier,
onScanButtonClick: () -> Unit,
onShopButtonClick: () -> Unit
) {
val darkColorBackground = Color(0xFF26292E)
val darkColorButton = Color(0xFF080C10)
Row(
horizontalArrangement = Arrangement.SpaceEvenly,
modifier = modifier.fillMaxWidth1()
) {
Spacer(modifier = Modifier.size(16.dp))
Button(
modifier = Modifier
.weight(1f)
.height(42.dp),
onClick = onScanButtonClick,
colors = ButtonDefaults.textButtonColors(
backgroundColor = if (isDarkBackground) darkColorBackground else Color.White,
contentColor = if (isDarkBackground) Color.White else darkColorButton
)
) {
Text(
text = stringResource(id = R.string.home_button_scan),
fontWeight = FontWeight.Medium,
fontSize = 16.sp
)
}
Spacer(modifier = Modifier.size(8.dp))
Button(
modifier = Modifier
.weight(1f)
.height(42.dp),
onClick = onShopButtonClick,
colors = ButtonDefaults.textButtonColors(
backgroundColor = if (isDarkBackground) Color.White else darkColorBackground,
contentColor = if (isDarkBackground) darkColorButton else Color.White
)
) {
Text(
text = stringResource(id = R.string.home_button_order),
fontWeight = FontWeight.Medium,
fontSize = 16.sp
)
}
Spacer(modifier = Modifier.size(16.dp))
}
}

View file

@ -0,0 +1,145 @@
package com.tangem.tap.features.home.compose
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.tap.features.home.redux.HomeState
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.wallet.R
import kotlin.math.max
@Composable
fun StoriesScreen(
homeState: MutableState<HomeState> = mutableStateOf(HomeState()),
onScanButtonClick: () -> Unit,
onShopButtonClick: () -> Unit
) {
val steps = 6
val stepDuration = 8_000
val currentStep = remember { mutableStateOf(1) }
val isDarkBackground = currentStep.value !in 3..5
val goToPreviousScreen = {
currentStep.value = max(1, currentStep.value - 1)
}
val goToNextScreen = {
currentStep.value = if (currentStep.value < steps) currentStep.value + 1 else 1
}
val isPressed = remember { mutableStateOf(false) }
val needsToBePaused =
remember(homeState) { mutableStateOf(homeState.value.btnScanState.progressState == ProgressState.Loading) }
val pause = isPressed.value || needsToBePaused.value
val hideContent = remember { mutableStateOf(true) }
Box(
Modifier
.fillMaxSize()
.background(Color(0xFF090E13))
.pointerInput(Unit) {
val maxWidth = this.size.width
detectTapGestures(
onPress = {
val pressStartTime = System.currentTimeMillis()
isPressed.value = true
this.tryAwaitRelease()
val pressEndTime = System.currentTimeMillis()
val totalPressTime = pressEndTime - pressStartTime
if (totalPressTime < 200) {
val isTapOnRightTwoTiers = (it.x > (maxWidth / 2))
if (isTapOnRightTwoTiers) {
goToNextScreen()
} else {
goToPreviousScreen()
}
}
isPressed.value = false
},
)
}
) {
if (!isDarkBackground) {
Image(
painter = painterResource(id = R.drawable.ic_overlay),
contentDescription = null,
modifier = Modifier
.fillMaxSize(),
contentScale = ContentScale.FillBounds
)
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier =
Modifier
.fillMaxSize()
) {
Spacer(modifier = Modifier.size(24.dp))
StoriesProgressBar(
steps = steps,
currentStep = currentStep.value,
// paused = isPressed.value,
stepDuration = stepDuration,
paused = pause,
onStepFinished = goToNextScreen,
)
Image(
painter = painterResource(id = R.drawable.ic_tangem_logo),
contentDescription = null,
contentScale = ContentScale.FillHeight,
modifier = Modifier
.padding(start = 16.dp, top = 10.dp)
.height(17.dp)
.alpha(if (hideContent.value) 0f else 1f)
.align(Alignment.Start),
colorFilter = if (isDarkBackground) null else ColorFilter.tint(Color.Black)
)
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxWidth()
) {
when (currentStep.value) {
1 -> FirstStoriesContent(pause, stepDuration) { hideContent.value = it }
2 -> StoriesRevolutionaryWallet()
3 -> StoriesUltraSecureBackup()
4 -> StoriesThousandsOfCurrencies()
5 -> StoriesWeb3()
6 -> StoriesWalletForEveryone()
}
}
HomeButtons(
isDarkBackground = isDarkBackground,
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(37.dp),
onScanButtonClick = onScanButtonClick,
onShopButtonClick = onShopButtonClick
)
}
}
@Preview
@Composable
fun InstagramScreenPreview() {
StoriesScreen(onScanButtonClick = {}, onShopButtonClick = {})
}

View file

@ -0,0 +1,120 @@
package com.tangem.tap.features.home.compose
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
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.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.tangem.wallet.R
@Composable
fun StoriesGeneralContent(
titleText: String,
subtitleText: String,
imageSource: Int?,
isDarkBackground: Boolean,
imageComposable: (() -> Unit)? = null,
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier =
Modifier
.fillMaxWidth()
.fillMaxHeight(),
) {
Text(
text = titleText,
fontSize = 32.sp,
fontWeight = FontWeight.SemiBold,
modifier = Modifier
.padding(start = 40.dp, end = 40.dp),
color = if (isDarkBackground) Color.White else Color(0xFF090E13),
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.size(16.dp))
Text(
text = subtitleText,
fontSize = 20.sp,
fontWeight = FontWeight.Normal,
modifier = Modifier
.padding(start = 40.dp, end = 40.dp),
color = Color(0xFFA6AAAD),
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.size(25.dp))
if (imageSource != null) {
Image(
painter = painterResource(id = imageSource),
contentDescription = null,
contentScale = ContentScale.FillWidth,
modifier = Modifier.fillMaxWidth()
)
}
imageComposable?.invoke()
}
}
@Composable
fun StoriesRevolutionaryWallet() {
StoriesGeneralContent(
titleText = stringResource(id = R.string.story_awe_title),
subtitleText = stringResource(id = R.string.story_awe_description),
imageSource = R.drawable.revolutionary_wallet,
isDarkBackground = true
)
}
@Composable
fun StoriesUltraSecureBackup() {
StoriesGeneralContent(
titleText = stringResource(id = R.string.story_backup_title),
subtitleText = stringResource(id = R.string.story_backup_description),
imageSource = R.drawable.floating_cards,
isDarkBackground = false
)
}
@Composable
fun StoriesThousandsOfCurrencies() {
StoriesGeneralContent(
titleText = stringResource(id = R.string.story_currencies_title),
subtitleText = stringResource(id = R.string.story_currencies_description),
imageSource = R.drawable.thousands_of_currencies,
isDarkBackground = false
)
}
@Composable
fun StoriesWeb3() {
StoriesGeneralContent(
titleText = stringResource(id = R.string.story_web3_title),
subtitleText = stringResource(id = R.string.story_web3_description),
imageSource = R.drawable.web_3,
isDarkBackground = false
)
}
@Composable
fun StoriesWalletForEveryone() {
StoriesGeneralContent(
titleText = stringResource(id = R.string.story_finish_title),
subtitleText = stringResource(id = R.string.story_finish_description),
imageSource = R.drawable.wallet_for_everyone,
isDarkBackground = true
)
}

View file

@ -0,0 +1,80 @@
package com.tangem.tap.features.home.compose
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@Composable
fun StoriesProgressBar(
steps: Int,
currentStep: Int,
paused: Boolean = false,
stepDuration: Int = 8_000,
onStepFinished: () -> Unit = {},
) {
val progress = remember(currentStep) { Animatable(0f) }
LaunchedEffect(paused, currentStep) {
if (paused) {
progress.stop()
} else {
progress.animateTo(
targetValue = 1f,
animationSpec = tween(
durationMillis = (stepDuration * (1f - progress.value)).toInt(),
easing = LinearEasing
)
)
progress.snapTo(0f)
onStepFinished()
}
}
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
// .height()
.padding(start = 9.dp, end = 9.dp, top = 16.dp),
) {
for (index in 1..steps) {
Row(
modifier = Modifier
.height(2.dp)
.weight(1f)
.background(Color.White.copy(alpha = 0.4f))
) {
Box(
modifier = Modifier
.background(Color.White)
.fillMaxHeight().let {
when (index) {
currentStep -> it.fillMaxWidth(progress.value)
in 0..currentStep -> it.fillMaxWidth(1f)
else -> it
}
},
) {}
}
if (index != steps) {
Spacer(modifier = Modifier.width(4.dp))
}
}
}
}
@Preview
@Composable
fun StoriesProgressBarPreview() {
StoriesProgressBar(steps = 3, currentStep = 2, paused = false) { }
}

View file

@ -1,7 +1,6 @@
package com.tangem.tap.features.home.redux
import com.tangem.tap.common.entities.IndeterminateProgressButton
import com.tangem.tap.common.redux.navigation.FragmentShareTransition
import org.rekotlin.Action
sealed class HomeAction : Action {
@ -10,10 +9,9 @@ sealed class HomeAction : Action {
object GoToShop : HomeAction()
// internal
data class ShouldScanCardOnResume(val shouldScanCard: Boolean):HomeAction()
data class ShouldScanCardOnResume(val shouldScanCard: Boolean) : HomeAction()
object Init : HomeAction()
data class SetFragmentShareTransition(val shareTransition: FragmentShareTransition?) : HomeAction()
data class SetTermsOfUseState(val isDisclaimerAccepted: Boolean) : HomeAction()
data class ChangeScanCardButtonState(val state: IndeterminateProgressButton) : HomeAction()

View file

@ -73,14 +73,14 @@ private fun handleReadCard() {
if (OnboardingHelper.isOnboardingCase(scanResponse)) {
val navigateTo = OnboardingHelper.whereToNavigate(scanResponse)
store.dispatch(GlobalAction.Onboarding.Start(scanResponse))
navigateTo(navigateTo, store.state.homeState.shareTransition)
navigateTo(navigateTo)
} else {
scope.launch {
store.onCardScanned(scanResponse)
withMainContext {
if (scanResponse.twinsIsTwinned() && !preferencesStorage.wasTwinsOnboardingShown()) {
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.WelcomeOnly))
navigateTo(AppScreen.OnboardingTwins, store.state.homeState.shareTransition)
navigateTo(AppScreen.OnboardingTwins)
} else {
navigateTo(AppScreen.Wallet, null)
}
@ -98,7 +98,7 @@ private fun changeButtonState(state: ButtonState) {
store.dispatch(HomeAction.ChangeScanCardButtonState(btnState))
}
private fun navigateTo(screen: AppScreen, transition: FragmentShareTransition?) {
private fun navigateTo(screen: AppScreen, transition: FragmentShareTransition? = null) {
post(DELAY_SDK_DIALOG_CLOSE) {
changeButtonState(ButtonState.ENABLED)
store.dispatch(NavigationAction.NavigateTo(screen, transition))

View file

@ -15,9 +15,6 @@ private fun internalReduce(action: Action, state: AppState): HomeState {
var state = state.homeState
when (action) {
is HomeAction.SetFragmentShareTransition -> {
state = state.copy(shareTransition = action.shareTransition)
}
is HomeAction.ShouldScanCardOnResume -> {
state = state.copy(shouldScanCardOnResume = action.shouldScanCard)
}

View file

@ -1,12 +1,10 @@
package com.tangem.tap.features.home.redux
import com.tangem.tap.common.entities.IndeterminateProgressButton
import com.tangem.tap.common.redux.navigation.FragmentShareTransition
import com.tangem.tap.features.send.redux.states.ButtonState
import org.rekotlin.StateType
data class HomeState(
val shouldScanCardOnResume: Boolean = false,
val shareTransition: FragmentShareTransition? = null,
val btnScanState: IndeterminateProgressButton = IndeterminateProgressButton(ButtonState.ENABLED),
) : StateType

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 495 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 455 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 997 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 920 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 693 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 550 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 983 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 KiB

View file

@ -0,0 +1,14 @@
<vector android:autoMirrored="true" android:height="720dp"
android:viewportHeight="720" android:viewportWidth="360"
android:width="360dp" xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#F8F9F9" android:pathData="M0,0h360v720h-360z"/>
<path android:fillAlpha="0.2" android:pathData="M0,0h360v720h-360z">
<aapt:attr name="android:fillColor">
<gradient android:endX="179.591" android:endY="82.4634"
android:startX="180" android:startY="0" android:type="linear">
<item android:color="#FF090E13" android:offset="0"/>
<item android:color="#00000000" android:offset="1"/>
</gradient>
</aapt:attr>
</path>
</vector>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.compose.ui.platform.ComposeView
android:id="@+id/cv_stories"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -21,6 +21,7 @@
<string name="home_ready_title">Get ready to tap your \ncard to the back of the \nphone.</string>
<string name="home_button_tapin">Tap in</string>
<string name="home_button_scan">Scan card</string>
<string name="home_button_order">Order card</string>
<string name="home_button_yes">Yes! Scan it.</string>
<string name="wallet_button_scan">Scan</string>

View file

@ -329,4 +329,26 @@ Amount to pay: %s</string>
<string name="shop_title" translatable="false">Order card</string>
<string name="shop_free_delivery" translatable="false">Delivery (Free shipping)</string>
<string name="story_meet_title" translatable="false">Meet\nTangem</string>
<string name="story_meet_buy" translatable="false">Buy</string>
<string name="story_meet_store" translatable="false">Store</string>
<string name="story_meet_send" translatable="false">Send</string>
<string name="story_meet_pay" translatable="false">Pay</string>
<string name="story_meet_exchange" translatable="false">Exchange</string>
<string name="story_meet_lend" translatable="false">Lend</string>
<string name="story_meet_borrow" translatable="false">Borrow</string>
<string name="story_meet_stake" translatable="false">Stake</string>
<string name="story_awe_title" translatable="false">Revolutionary Hardware Wallet</string>
<string name="story_awe_description" translatable="false">Store your crypto assets secure while keeping private keys contained in your card</string>
<string name="story_backup_title" translatable="false">Ultra Secure Backup</string>
<string name="story_backup_description" translatable="false">Up to 3 physical cards to one wallet</string>
<string name="story_currencies_title" translatable="false">Thousands of Currencies</string>
<string name="story_currencies_description" translatable="false">A hardware wallet for your Bitcoin, Ethereum and many more currencies simultaneously all in one card</string>
<string name="story_web3_title" translatable="false">Web3 Compatible</string>
<string name="story_web3_description" translatable="false">Exchange, buy NFTs, make loans and deposits in more than 100 different decentralized services</string>
<string name="story_finish_title" translatable="false">The Wallet for Everyone</string>
<string name="story_finish_description" translatable="false">Use it on the go, anywhere, anytime. No wires or batteries. Just tap the card to your phone when you need your crypto.</string>
</resources>