Updated on 2026-08-14
This commit is contained in:
parent
7e765758b4
commit
efe271e1a0
24 changed files with 751 additions and 75 deletions
|
|
@ -138,6 +138,8 @@
|
||||||
android:name="com.tangem.tap.features.sprinklr.ui.SprinklrActivity"
|
android:name="com.tangem.tap.features.sprinklr.ui.SprinklrActivity"
|
||||||
android:theme="@style/AppTheme" />
|
android:theme="@style/AppTheme" />
|
||||||
|
|
||||||
|
<activity android:name="com.tangem.feature.learn2earn.presentation.Learn2earnWebViewActivity"/>
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="${applicationId}.provider"
|
android:authorities="${applicationId}.provider"
|
||||||
|
|
|
||||||
|
|
@ -1,59 +1,67 @@
|
||||||
package com.tangem.tap
|
package com.tangem.tap
|
||||||
|
|
||||||
import android.app.*
|
import android.app.Application
|
||||||
import android.content.*
|
import android.content.Context
|
||||||
import android.content.pm.*
|
import android.content.pm.PackageManager
|
||||||
import coil.*
|
import coil.ImageLoader
|
||||||
import com.tangem.*
|
import coil.ImageLoaderFactory
|
||||||
import com.tangem.blockchain.common.*
|
import com.tangem.Log
|
||||||
import com.tangem.blockchain.network.*
|
import com.tangem.LogFormat
|
||||||
import com.tangem.core.analytics.*
|
import com.tangem.blockchain.common.BlockchainSdkConfig
|
||||||
import com.tangem.core.featuretoggle.manager.*
|
import com.tangem.blockchain.common.WalletManagerFactory
|
||||||
import com.tangem.data.source.preferences.*
|
import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder
|
||||||
import com.tangem.datasource.api.common.*
|
import com.tangem.core.analytics.Analytics
|
||||||
import com.tangem.datasource.asset.*
|
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
||||||
import com.tangem.datasource.config.*
|
import com.tangem.data.source.preferences.PreferencesDataSource
|
||||||
import com.tangem.datasource.config.models.*
|
import com.tangem.datasource.api.common.MoshiConverter
|
||||||
import com.tangem.datasource.connection.*
|
import com.tangem.datasource.asset.AssetReader
|
||||||
import com.tangem.domain.*
|
import com.tangem.datasource.config.ConfigManager
|
||||||
import com.tangem.domain.common.*
|
import com.tangem.datasource.config.FeaturesLocalLoader
|
||||||
import com.tangem.features.wallet.featuretoggles.*
|
import com.tangem.datasource.config.models.Config
|
||||||
import com.tangem.tap.common.*
|
import com.tangem.datasource.connection.NetworkConnectionManager
|
||||||
import com.tangem.tap.common.analytics.*
|
import com.tangem.domain.DomainLayer
|
||||||
import com.tangem.tap.common.analytics.api.*
|
import com.tangem.domain.common.LogConfig
|
||||||
import com.tangem.tap.common.analytics.handlers.amplitude.*
|
import com.tangem.feature.learn2earn.domain.api.Learn2earnInteractor
|
||||||
import com.tangem.tap.common.analytics.handlers.appsFlyer.*
|
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
|
||||||
import com.tangem.tap.common.analytics.handlers.firebase.*
|
import com.tangem.tap.common.analytics.AnalyticsFactory
|
||||||
import com.tangem.tap.common.analytics.topup.*
|
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
|
||||||
import com.tangem.tap.common.chat.*
|
import com.tangem.tap.common.analytics.handlers.amplitude.AmplitudeAnalyticsHandler
|
||||||
import com.tangem.tap.common.feedback.*
|
import com.tangem.tap.common.analytics.handlers.appsFlyer.AppsFlyerAnalyticsHandler
|
||||||
import com.tangem.tap.common.images.*
|
import com.tangem.tap.common.analytics.handlers.firebase.FirebaseAnalyticsHandler
|
||||||
import com.tangem.tap.common.log.*
|
import com.tangem.tap.common.analytics.topup.TopUpController
|
||||||
import com.tangem.tap.common.redux.*
|
import com.tangem.tap.common.chat.ChatManager
|
||||||
import com.tangem.tap.common.redux.global.*
|
import com.tangem.tap.common.feedback.AdditionalFeedbackInfo
|
||||||
import com.tangem.tap.common.shop.*
|
import com.tangem.tap.common.feedback.FeedbackManager
|
||||||
import com.tangem.tap.domain.configurable.warningMessage.*
|
import com.tangem.tap.common.images.createCoilImageLoader
|
||||||
import com.tangem.tap.domain.tokens.*
|
import com.tangem.tap.common.log.TangemLogCollector
|
||||||
import com.tangem.tap.domain.totalBalance.*
|
import com.tangem.tap.common.redux.AppState
|
||||||
import com.tangem.tap.domain.totalBalance.di.*
|
import com.tangem.tap.common.redux.appReducer
|
||||||
import com.tangem.tap.domain.walletCurrencies.*
|
import com.tangem.tap.common.redux.global.GlobalAction
|
||||||
import com.tangem.tap.domain.walletCurrencies.di.*
|
import com.tangem.tap.common.shop.TangemShopService
|
||||||
import com.tangem.tap.domain.walletStores.*
|
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
|
||||||
import com.tangem.tap.domain.walletStores.di.*
|
import com.tangem.tap.domain.tokens.UserTokensRepository
|
||||||
import com.tangem.tap.domain.walletStores.repository.*
|
import com.tangem.tap.domain.totalBalance.TotalFiatBalanceCalculator
|
||||||
import com.tangem.tap.domain.walletStores.repository.di.*
|
import com.tangem.tap.domain.totalBalance.di.provideDefaultImplementation
|
||||||
|
import com.tangem.tap.domain.walletCurrencies.WalletCurrenciesManager
|
||||||
|
import com.tangem.tap.domain.walletCurrencies.di.provideDefaultImplementation
|
||||||
|
import com.tangem.tap.domain.walletStores.WalletStoresManager
|
||||||
|
import com.tangem.tap.domain.walletStores.di.provideDefaultImplementation
|
||||||
|
import com.tangem.tap.domain.walletStores.repository.WalletAmountsRepository
|
||||||
|
import com.tangem.tap.domain.walletStores.repository.WalletManagersRepository
|
||||||
|
import com.tangem.tap.domain.walletStores.repository.WalletStoresRepository
|
||||||
|
import com.tangem.tap.domain.walletStores.repository.di.provideDefaultImplementation
|
||||||
import com.tangem.tap.domain.walletconnect.WalletConnectRepository
|
import com.tangem.tap.domain.walletconnect.WalletConnectRepository
|
||||||
import com.tangem.tap.domain.walletconnect2.domain.*
|
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectSessionsRepository
|
||||||
import com.tangem.tap.features.customtoken.api.featuretoggles.*
|
import com.tangem.tap.features.customtoken.api.featuretoggles.CustomTokenFeatureToggles
|
||||||
import com.tangem.tap.proxy.*
|
import com.tangem.tap.proxy.AppStateHolder
|
||||||
import com.tangem.tap.proxy.redux.*
|
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||||
import com.tangem.wallet.BuildConfig
|
import com.tangem.wallet.BuildConfig
|
||||||
import dagger.hilt.android.*
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.runBlocking
|
||||||
import okhttp3.logging.*
|
import okhttp3.logging.HttpLoggingInterceptor
|
||||||
import org.rekotlin.*
|
import org.rekotlin.Store
|
||||||
import timber.log.*
|
import timber.log.Timber
|
||||||
import javax.inject.*
|
import javax.inject.Inject
|
||||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectRepository as WalletConnect2Repository
|
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectRepository as WalletConnect2Repository
|
||||||
|
|
||||||
lateinit var store: Store<AppState>
|
lateinit var store: Store<AppState>
|
||||||
|
|
@ -136,6 +144,9 @@ class TapApplication : Application(), ImageLoaderFactory {
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var walletConnectSessionsRepository: WalletConnectSessionsRepository
|
lateinit var walletConnectSessionsRepository: WalletConnectSessionsRepository
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var learn2ernInteractor: Learn2earnInteractor
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
|
|
@ -189,8 +200,9 @@ class TapApplication : Application(), ImageLoaderFactory {
|
||||||
appStateHolder.userTokensRepository = userTokensRepository
|
appStateHolder.userTokensRepository = userTokensRepository
|
||||||
appStateHolder.walletStoresManager = walletStoresManager
|
appStateHolder.walletStoresManager = walletStoresManager
|
||||||
|
|
||||||
scope.launch {
|
runBlocking {
|
||||||
featureTogglesManager.init()
|
featureTogglesManager.init()
|
||||||
|
learn2ernInteractor.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
initTopUpController()
|
initTopUpController()
|
||||||
|
|
|
||||||
|
|
@ -12,26 +12,38 @@ import androidx.compose.ui.platform.ComposeView
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.core.view.WindowInsetsControllerCompat
|
import androidx.core.view.WindowInsetsControllerCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.viewModels
|
||||||
import com.tangem.core.analytics.Analytics
|
import com.tangem.core.analytics.Analytics
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
|
import com.tangem.feature.learn2earn.presentation.Learn2earnViewModel
|
||||||
import com.tangem.tap.common.analytics.events.IntroductionProcess
|
import com.tangem.tap.common.analytics.events.IntroductionProcess
|
||||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||||
import com.tangem.tap.features.home.compose.StoriesScreen
|
import com.tangem.tap.features.home.compose.StoriesScreen
|
||||||
import com.tangem.tap.features.home.redux.HomeAction
|
import com.tangem.tap.features.home.redux.HomeAction
|
||||||
import com.tangem.tap.features.home.redux.HomeState
|
import com.tangem.tap.features.home.redux.HomeState
|
||||||
|
import com.tangem.tap.features.home.redux.Stories
|
||||||
import com.tangem.tap.features.tokens.legacy.redux.TokensAction
|
import com.tangem.tap.features.tokens.legacy.redux.TokensAction
|
||||||
import com.tangem.tap.store
|
import com.tangem.tap.store
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import org.rekotlin.StoreSubscriber
|
import org.rekotlin.StoreSubscriber
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
class HomeFragment : Fragment(), StoreSubscriber<HomeState> {
|
class HomeFragment : Fragment(), StoreSubscriber<HomeState> {
|
||||||
|
|
||||||
private var homeState: MutableState<HomeState> = mutableStateOf(store.state.homeState)
|
private var homeState: MutableState<HomeState> = mutableStateOf(store.state.homeState)
|
||||||
|
|
||||||
|
private val learn2earnViewModel by viewModels<Learn2earnViewModel>()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
store.dispatch(HomeAction.OnCreate)
|
store.dispatch(HomeAction.OnCreate)
|
||||||
store.dispatch(HomeAction.Init)
|
store.dispatch(HomeAction.Init)
|
||||||
|
if (learn2earnViewModel.isNeedToShowViewOnStoriesScreen()) {
|
||||||
|
store.dispatch(HomeAction.InsertStory(position = 0, Stories.OneInchPromo))
|
||||||
|
// re init homeState after inserting learn2earn story
|
||||||
|
homeState = mutableStateOf(store.state.homeState)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
|
|
@ -79,6 +91,9 @@ class HomeFragment : Fragment(), StoreSubscriber<HomeState> {
|
||||||
private fun ScreenContent() {
|
private fun ScreenContent() {
|
||||||
StoriesScreen(
|
StoriesScreen(
|
||||||
homeState,
|
homeState,
|
||||||
|
onLearn2earnClick = {
|
||||||
|
learn2earnViewModel.onStoriesClick()
|
||||||
|
},
|
||||||
onScanButtonClick = {
|
onScanButtonClick = {
|
||||||
Analytics.send(IntroductionProcess.ButtonScanCard())
|
Analytics.send(IntroductionProcess.ButtonScanCard())
|
||||||
store.dispatch(HomeAction.ReadCard())
|
store.dispatch(HomeAction.ReadCard())
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.feature.learn2earn.presentation.ui.OneInchStoriesScreen
|
import com.tangem.feature.learn2earn.presentation.ui.StoriesScreen
|
||||||
import com.tangem.tap.features.home.compose.content.*
|
import com.tangem.tap.features.home.compose.content.*
|
||||||
import com.tangem.tap.features.home.compose.views.HomeButtons
|
import com.tangem.tap.features.home.compose.views.HomeButtons
|
||||||
import com.tangem.tap.features.home.compose.views.StoriesProgressBar
|
import com.tangem.tap.features.home.compose.views.StoriesProgressBar
|
||||||
|
|
@ -39,6 +39,7 @@ import kotlin.math.max
|
||||||
@Composable
|
@Composable
|
||||||
fun StoriesScreen(
|
fun StoriesScreen(
|
||||||
homeState: MutableState<HomeState>,
|
homeState: MutableState<HomeState>,
|
||||||
|
onLearn2earnClick: () -> Unit,
|
||||||
onScanButtonClick: () -> Unit,
|
onScanButtonClick: () -> Unit,
|
||||||
onShopButtonClick: () -> Unit,
|
onShopButtonClick: () -> Unit,
|
||||||
onSearchTokensClick: () -> Unit,
|
onSearchTokensClick: () -> Unit,
|
||||||
|
|
@ -152,7 +153,7 @@ fun StoriesScreen(
|
||||||
colorFilter = if (currentStory.isDarkBackground) null else ColorFilter.tint(Color.Black),
|
colorFilter = if (currentStory.isDarkBackground) null else ColorFilter.tint(Color.Black),
|
||||||
)
|
)
|
||||||
when (currentStory) {
|
when (currentStory) {
|
||||||
Stories.OneInchPromo -> OneInchStoriesScreen({})
|
Stories.OneInchPromo -> StoriesScreen(onLearn2earnClick)
|
||||||
Stories.TangemIntro -> FirstStoriesContent(isPaused, currentStory.duration) { hideContent.value = it }
|
Stories.TangemIntro -> FirstStoriesContent(isPaused, currentStory.duration) { hideContent.value = it }
|
||||||
Stories.RevolutionaryWallet -> StoriesRevolutionaryWallet(currentStory.duration)
|
Stories.RevolutionaryWallet -> StoriesRevolutionaryWallet(currentStory.duration)
|
||||||
Stories.UltraSecureBackup -> StoriesUltraSecureBackup(isPaused, currentStory.duration)
|
Stories.UltraSecureBackup -> StoriesUltraSecureBackup(isPaused, currentStory.duration)
|
||||||
|
|
@ -215,6 +216,7 @@ fun StoriesScreen(
|
||||||
@Composable
|
@Composable
|
||||||
private fun StoriesScreenPreview() {
|
private fun StoriesScreenPreview() {
|
||||||
StoriesScreen(
|
StoriesScreen(
|
||||||
|
onLearn2earnClick = {},
|
||||||
onScanButtonClick = {},
|
onScanButtonClick = {},
|
||||||
onShopButtonClick = {},
|
onShopButtonClick = {},
|
||||||
onSearchTokensClick = {},
|
onSearchTokensClick = {},
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ sealed class HomeAction : Action {
|
||||||
object OnCreate : HomeAction()
|
object OnCreate : HomeAction()
|
||||||
object Init : HomeAction()
|
object Init : HomeAction()
|
||||||
|
|
||||||
|
data class InsertStory(val position: Int, val story: Stories) : HomeAction()
|
||||||
|
|
||||||
data class ReadCard(
|
data class ReadCard(
|
||||||
val analyticsEvent: AnalyticsEvent? = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.Introduction),
|
val analyticsEvent: AnalyticsEvent? = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.Introduction),
|
||||||
) : HomeAction()
|
) : HomeAction()
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,13 @@ private fun internalReduce(action: Action, state: AppState): HomeState {
|
||||||
|
|
||||||
var state = state.homeState
|
var state = state.homeState
|
||||||
when (action) {
|
when (action) {
|
||||||
|
is HomeAction.InsertStory -> {
|
||||||
|
state = state.copy(
|
||||||
|
stories = state.stories.toMutableList().apply {
|
||||||
|
add(action.position, action.story)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
is HomeAction.ScanInProgress -> {
|
is HomeAction.ScanInProgress -> {
|
||||||
state = state.copy(scanInProgress = action.scanInProgress)
|
state = state.copy(scanInProgress = action.scanInProgress)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ data class HomeState(
|
||||||
val stories: List<Stories> = initDefaultStories(),
|
val stories: List<Stories> = initDefaultStories(),
|
||||||
) : StateType {
|
) : StateType {
|
||||||
|
|
||||||
val firstStory: Stories = stories[0]
|
val firstStory: Stories
|
||||||
|
get() = stories[0]
|
||||||
|
|
||||||
val btnScanStateInProgress: Boolean
|
val btnScanStateInProgress: Boolean
|
||||||
get() = btnScanState.progressState == ProgressState.Loading
|
get() = btnScanState.progressState == ProgressState.Loading
|
||||||
|
|
@ -20,7 +21,6 @@ data class HomeState(
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun initDefaultStories(): List<Stories> = listOf(
|
fun initDefaultStories(): List<Stories> = listOf(
|
||||||
Stories.OneInchPromo,
|
|
||||||
Stories.TangemIntro,
|
Stories.TangemIntro,
|
||||||
Stories.RevolutionaryWallet,
|
Stories.RevolutionaryWallet,
|
||||||
Stories.UltraSecureBackup,
|
Stories.UltraSecureBackup,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
package com.tangem.tap.features.welcome.redux
|
package com.tangem.tap.features.welcome.redux
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import com.tangem.common.*
|
|
||||||
import com.tangem.common.core.TangemSdkError
|
import com.tangem.common.core.TangemSdkError
|
||||||
|
import com.tangem.common.doOnFailure
|
||||||
|
import com.tangem.common.doOnResult
|
||||||
|
import com.tangem.common.doOnSuccess
|
||||||
|
import com.tangem.common.flatMap
|
||||||
import com.tangem.domain.models.scan.ScanResponse
|
import com.tangem.domain.models.scan.ScanResponse
|
||||||
import com.tangem.tap.*
|
import com.tangem.tap.*
|
||||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||||
|
|
@ -17,7 +19,6 @@ import com.tangem.tap.domain.model.builders.UserWalletBuilder
|
||||||
import com.tangem.tap.domain.scanCard.ScanCardProcessor
|
import com.tangem.tap.domain.scanCard.ScanCardProcessor
|
||||||
import com.tangem.tap.domain.userWalletList.unlockIfLockable
|
import com.tangem.tap.domain.userWalletList.unlockIfLockable
|
||||||
import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler
|
import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler
|
||||||
import com.tangem.tap.features.onboarding.products.wallet.saltPay.message.SaltPayActivationError
|
|
||||||
import com.tangem.tap.features.signin.redux.SignInAction
|
import com.tangem.tap.features.signin.redux.SignInAction
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.rekotlin.Middleware
|
import org.rekotlin.Middleware
|
||||||
|
|
@ -98,15 +99,6 @@ internal class WelcomeMiddleware {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleInitialIntent(intent: Intent?) {
|
|
||||||
val isBackgroundScanNotHandled = !intentHandler.handleBackgroundScan(intent, hasSavedUserWallets = true)
|
|
||||||
val hasNotIncompletedBackup = !backupService.hasIncompletedBackup
|
|
||||||
|
|
||||||
if (isBackgroundScanNotHandled && hasNotIncompletedBackup) {
|
|
||||||
store.dispatchOnMain(WelcomeAction.ProceedWithBiometrics)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend inline fun scanCardInternal(crossinline onCardScanned: suspend (ScanResponse) -> Unit) {
|
private suspend inline fun scanCardInternal(crossinline onCardScanned: suspend (ScanResponse) -> Unit) {
|
||||||
tangemSdkManager.setAccessCodeRequestPolicy(
|
tangemSdkManager.setAccessCodeRequestPolicy(
|
||||||
useBiometricsForAccessCode = preferencesStorage.shouldSaveAccessCodes,
|
useBiometricsForAccessCode = preferencesStorage.shouldSaveAccessCodes,
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,8 @@
|
||||||
android:id="@+id/cl_wallet"
|
android:id="@+id/cl_wallet"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:clipToPadding="false"
|
|
||||||
android:clipChildren="false"
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
android:paddingBottom="92dp">
|
android:paddingBottom="92dp">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
|
|
@ -113,6 +113,15 @@
|
||||||
android:paddingTop="8dp"
|
android:paddingTop="8dp"
|
||||||
app:layout_constraintTop_toBottomOf="@id/rv_warning_messages">
|
app:layout_constraintTop_toBottomOf="@id/rv_warning_messages">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/fl_learn_to_earn_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginBottom="8dp" />
|
||||||
|
|
||||||
<include
|
<include
|
||||||
android:id="@+id/l_wallet_rescan_warning"
|
android:id="@+id/l_wallet_rescan_warning"
|
||||||
layout="@layout/layout_wallet_rescan_warning"
|
layout="@layout/layout_wallet_rescan_warning"
|
||||||
|
|
@ -219,8 +228,8 @@
|
||||||
android:id="@+id/btn_add_token"
|
android:id="@+id/btn_add_token"
|
||||||
style="@style/TapPrimaryButton"
|
style="@style/TapPrimaryButton"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:text="@string/main_manage_tokens"
|
android:text="@string/main_manage_tokens"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
|
|
|
||||||
10
core/utils/src/main/java/com/tangem/utils/extensions/Any.kt
Normal file
10
core/utils/src/main/java/com/tangem/utils/extensions/Any.kt
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
package com.tangem.utils.extensions
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
|
/**
|
||||||
|
[REDACTED_AUTHOR]
|
||||||
|
*/
|
||||||
|
fun <T> T.toWeakReference(): WeakReference<T> {
|
||||||
|
return WeakReference(this)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
package com.tangem.feature.learn2earn.domain
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import com.tangem.datasource.api.promotion.models.PromotionInfoResponse
|
||||||
|
import com.tangem.feature.learn2earn.data.api.Learn2earnRepository
|
||||||
|
import com.tangem.feature.learn2earn.domain.api.Learn2earnInteractor
|
||||||
|
import com.tangem.feature.learn2earn.domain.models.CardType
|
||||||
|
import com.tangem.feature.learn2earn.domain.models.Promotion
|
||||||
|
import com.tangem.feature.learn2earn.domain.models.PromotionError
|
||||||
|
import com.tangem.feature.learn2earn.domain.models.PromotionError.Companion.toDomainError
|
||||||
|
import com.tangem.feature.learn2earn.domain.models.RedirectConsequences
|
||||||
|
import com.tangem.lib.auth.BasicAuthProvider
|
||||||
|
|
||||||
|
/**
|
||||||
|
[REDACTED_AUTHOR]
|
||||||
|
*/
|
||||||
|
class DefaultLearn2earnInteractor(
|
||||||
|
private val repository: Learn2earnRepository,
|
||||||
|
basicAuthProvider: BasicAuthProvider,
|
||||||
|
userCountryCodeProvider: () -> String,
|
||||||
|
) : Learn2earnInteractor {
|
||||||
|
|
||||||
|
private val webViewUriBuilder = WebViewUriBuilder(
|
||||||
|
basicAuthProvider = basicAuthProvider,
|
||||||
|
userCountryCodeProvider = userCountryCodeProvider,
|
||||||
|
promoCodeProvider = { repository.getPromoCode() },
|
||||||
|
promoNameProvider = { repository.getProgramName() },
|
||||||
|
)
|
||||||
|
|
||||||
|
private lateinit var promotion: Promotion
|
||||||
|
|
||||||
|
override suspend fun init() {
|
||||||
|
initPromotionInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isNeedToShowViewOnStoriesScreen(): Boolean {
|
||||||
|
return promotionIsActive()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getBasicAuthHeaders(): Map<String, String> {
|
||||||
|
return webViewUriBuilder.getBasicAuthHeaders()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun isNeedToShowViewOnWalletScreen(walletId: String): Boolean {
|
||||||
|
val response = repository.validate(walletId)
|
||||||
|
|
||||||
|
return if (response.valid == false && response.isError()) {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
promotionIsActive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun buildUriForStories(): Uri {
|
||||||
|
val type = if (repository.isHadActivatedCards()) {
|
||||||
|
CardType.NEW
|
||||||
|
} else {
|
||||||
|
CardType.EXISTED
|
||||||
|
}
|
||||||
|
|
||||||
|
return webViewUriBuilder.buildUriForStories(type)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun buildUriForMainPage(walletId: String, cardId: String, cardPubKey: String): Uri {
|
||||||
|
return webViewUriBuilder.buildUriForMainPage(walletId, cardId, cardPubKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handleWebViewRedirect(uri: Uri): RedirectConsequences {
|
||||||
|
if (webViewUriBuilder.isReadyForExistedCardAwardRedirect(uri)) {
|
||||||
|
return RedirectConsequences.FINISH_SESSION
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (webViewUriBuilder.isPromoCodeRedirect(uri)) {
|
||||||
|
webViewUriBuilder.extractPromoCode(uri)?.let { repository.savePromoCode(it) }
|
||||||
|
RedirectConsequences.NOTHING
|
||||||
|
} else {
|
||||||
|
RedirectConsequences.PROCEED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun promotionIsActive(): Boolean {
|
||||||
|
val isActive = when {
|
||||||
|
repository.isAlreadyReceivedAward() -> false
|
||||||
|
promotion.isError() -> false
|
||||||
|
else -> promotion.getInfo().status == PromotionInfoResponse.Status.ACTIVE
|
||||||
|
}
|
||||||
|
|
||||||
|
return isActive
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun initPromotionInfo() {
|
||||||
|
// return Promotion.dummyActive()
|
||||||
|
|
||||||
|
promotion = repository.getPromotionInfo()
|
||||||
|
.fold(
|
||||||
|
onSuccess = { response ->
|
||||||
|
val responseError = response.error
|
||||||
|
if (responseError == null) {
|
||||||
|
Promotion(
|
||||||
|
info = Promotion.PromotionInfo(
|
||||||
|
status = response.status!!,
|
||||||
|
awardForNewCard = response.awardForNewCard!!,
|
||||||
|
awardForOldCard = response.awardForOldCard!!,
|
||||||
|
awardPaymentToken = response.awardPaymentToken!!,
|
||||||
|
),
|
||||||
|
error = null,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Promotion(
|
||||||
|
info = null,
|
||||||
|
error = responseError.toDomainError(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onFailure = {
|
||||||
|
Promotion(
|
||||||
|
info = null,
|
||||||
|
error = PromotionError.NetworkUnreachable,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
package com.tangem.feature.learn2earn.domain
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import com.tangem.feature.learn2earn.domain.models.CardType
|
||||||
|
import com.tangem.feature.learn2earn.impl.BuildConfig
|
||||||
|
import com.tangem.lib.auth.BasicAuthProvider
|
||||||
|
|
||||||
|
/**
|
||||||
|
[REDACTED_AUTHOR]
|
||||||
|
*/
|
||||||
|
class WebViewUriBuilder(
|
||||||
|
private val basicAuthProvider: BasicAuthProvider,
|
||||||
|
private val userCountryCodeProvider: () -> String,
|
||||||
|
private val promoCodeProvider: () -> String?,
|
||||||
|
private val promoNameProvider: () -> String,
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun buildUriForStories(type: CardType): Uri {
|
||||||
|
val builder = makeWebViewUriBuilder()
|
||||||
|
.appendQueryParameter("type", type.typeName)
|
||||||
|
|
||||||
|
promoCodeProvider.invoke()?.let {
|
||||||
|
builder.appendQueryParameter("code", it)
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun buildUriForMainPage(walletId: String, cardId: String, cardPubKey: String): Uri {
|
||||||
|
val builder = makeWebViewUriBuilder()
|
||||||
|
.appendQueryParameter("type", CardType.EXISTED.typeName)
|
||||||
|
.appendQueryParameter("cardPublicKey", cardPubKey)
|
||||||
|
.appendQueryParameter("cardId", cardId)
|
||||||
|
.appendQueryParameter("walletId", walletId)
|
||||||
|
.appendQueryParameter("programName", promoNameProvider.invoke())
|
||||||
|
|
||||||
|
promoCodeProvider.invoke()?.let {
|
||||||
|
builder.appendQueryParameter("code", it)
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBasicAuthHeaders(): Map<String, String> {
|
||||||
|
return basicAuthProvider.getCredentials()?.let {
|
||||||
|
mapOf("Authorization" to "Basic $it")
|
||||||
|
} ?: mapOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isPromoCodeRedirect(uri: Uri): Boolean {
|
||||||
|
return uri.toString().contains(SUFFIX_CODE_CREATED)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isReadyForExistedCardAwardRedirect(uri: Uri): Boolean {
|
||||||
|
return uri.toString().endsWith(SUFFIX_READY_FOR_AWARD)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun extractPromoCode(uri: Uri): String? {
|
||||||
|
val url = uri.toString()
|
||||||
|
|
||||||
|
return if (isPromoCodeRedirect(uri)) {
|
||||||
|
val replaceString = makeWebViewUriBuilder().build().toString() + SUFFIX_CODE_CREATED
|
||||||
|
val code = url.replace(replaceString, "")
|
||||||
|
code
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeWebViewUriBuilder(): Uri.Builder {
|
||||||
|
val builder = Uri.Builder()
|
||||||
|
builder.scheme("https")
|
||||||
|
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
builder.authority(DEV_WEB_VIEW_BASE_URL)
|
||||||
|
builder.appendPath(userCountryCodeProvider.invoke())
|
||||||
|
builder.appendPath("promotion-test")
|
||||||
|
} else {
|
||||||
|
builder.authority(WEB_VIEW_BASE_URL)
|
||||||
|
builder.appendPath(userCountryCodeProvider.invoke())
|
||||||
|
builder.appendPath("promotion")
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val WEB_VIEW_BASE_URL = "tangem.com"
|
||||||
|
|
||||||
|
const val DEV_WEB_VIEW_BASE_URL = "devweb.tangem.com"
|
||||||
|
const val DEV_WEB_VIEW_BASIC_AUTH = "Basic dGFuZ2VtOnRhbmdlbWRldjc="
|
||||||
|
// TODO: 1inch: replace by invalid auth
|
||||||
|
// const val DEV_WEB_VIEW_BASIC_AUTH = "Basic paste valid value"
|
||||||
|
|
||||||
|
const val SUFFIX_CODE_CREATED = "/code-created?code="
|
||||||
|
const val SUFFIX_READY_FOR_AWARD = "/ready-for-exsited-card-award"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.tangem.feature.learn2earn.domain.api
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import com.tangem.feature.learn2earn.domain.models.RedirectConsequences
|
||||||
|
|
||||||
|
/**
|
||||||
|
[REDACTED_AUTHOR]
|
||||||
|
*/
|
||||||
|
interface Learn2earnInteractor {
|
||||||
|
|
||||||
|
suspend fun init()
|
||||||
|
fun isNeedToShowViewOnStoriesScreen(): Boolean
|
||||||
|
suspend fun isNeedToShowViewOnWalletScreen(walletId: String): Boolean
|
||||||
|
fun getBasicAuthHeaders(): Map<String, String>
|
||||||
|
fun buildUriForStories(): Uri
|
||||||
|
fun buildUriForMainPage(walletId: String, cardId: String, cardPubKey: String): Uri
|
||||||
|
fun handleWebViewRedirect(uri: Uri): RedirectConsequences
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
package com.tangem.feature.learn2earn.domain.di
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.tangem.feature.learn2earn.data.api.Learn2earnRepository
|
||||||
|
import com.tangem.feature.learn2earn.domain.DefaultLearn2earnInteractor
|
||||||
|
import com.tangem.feature.learn2earn.domain.api.Learn2earnInteractor
|
||||||
|
import com.tangem.feature.learn2earn.presentation.Learn2earnRouter
|
||||||
|
import com.tangem.lib.auth.BasicAuthProvider
|
||||||
|
import com.tangem.utils.extensions.toWeakReference
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
class Learn2earnDomainModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideRouter(@ApplicationContext context: Context): Learn2earnRouter {
|
||||||
|
return Learn2earnRouter(context.toWeakReference())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideInteractor(
|
||||||
|
repository: Learn2earnRepository,
|
||||||
|
basicAuthProvider: BasicAuthProvider,
|
||||||
|
): Learn2earnInteractor {
|
||||||
|
return DefaultLearn2earnInteractor(
|
||||||
|
repository = repository,
|
||||||
|
basicAuthProvider = basicAuthProvider,
|
||||||
|
userCountryCodeProvider = { "ru" },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.tangem.feature.learn2earn.domain.models
|
||||||
|
|
||||||
|
/**
|
||||||
|
[REDACTED_AUTHOR]
|
||||||
|
*/
|
||||||
|
enum class CardType(val typeName: String) {
|
||||||
|
NEW("new-card"),
|
||||||
|
EXISTED("existed-card"),
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
package com.tangem.feature.learn2earn.domain.models
|
||||||
|
|
||||||
|
import com.tangem.datasource.api.promotion.models.PromotionInfoResponse
|
||||||
|
|
||||||
|
/**
|
||||||
|
[REDACTED_AUTHOR]
|
||||||
|
*/
|
||||||
|
internal fun Promotion.Companion.dummyPending(): Promotion {
|
||||||
|
return Promotion(
|
||||||
|
info = Promotion.PromotionInfo.dummy().copy(
|
||||||
|
status = PromotionInfoResponse.Status.PENDING,
|
||||||
|
),
|
||||||
|
error = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun Promotion.Companion.dummyActive(): Promotion {
|
||||||
|
return Promotion(
|
||||||
|
info = Promotion.PromotionInfo.dummy().copy(
|
||||||
|
status = PromotionInfoResponse.Status.ACTIVE,
|
||||||
|
),
|
||||||
|
error = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun Promotion.Companion.dummyFinished(): Promotion {
|
||||||
|
return Promotion(
|
||||||
|
info = Promotion.PromotionInfo.dummy().copy(
|
||||||
|
status = PromotionInfoResponse.Status.FINISHED,
|
||||||
|
),
|
||||||
|
error = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun Promotion.Companion.dummyError(): Promotion {
|
||||||
|
return Promotion(
|
||||||
|
info = null,
|
||||||
|
error = PromotionError.Error(
|
||||||
|
code = 105,
|
||||||
|
description = "any",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun Promotion.Companion.dummyErrorUnreachable(): Promotion {
|
||||||
|
return Promotion(
|
||||||
|
info = null,
|
||||||
|
error = PromotionError.NetworkUnreachable,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Promotion.PromotionInfo.Companion.dummy(): Promotion.PromotionInfo {
|
||||||
|
return Promotion.PromotionInfo(
|
||||||
|
status = PromotionInfoResponse.Status.ACTIVE,
|
||||||
|
awardForNewCard = 10f,
|
||||||
|
awardForOldCard = 5.5f,
|
||||||
|
awardPaymentToken = Promotion.tokenInfo(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Promotion.Companion.tokenInfo(): PromotionInfoResponse.TokenInfo {
|
||||||
|
return PromotionInfoResponse.TokenInfo(
|
||||||
|
id = "1inch",
|
||||||
|
name = "1inch",
|
||||||
|
symbol = "1INCH",
|
||||||
|
active = true,
|
||||||
|
networks = listOf(
|
||||||
|
PromotionInfoResponse.TokenInfo.Network(
|
||||||
|
networkId = "polygon-pos",
|
||||||
|
exchangeable = false,
|
||||||
|
contractAddress = "0x9c2c5fd7b07e95ee044ddeba0e97a665f142394f",
|
||||||
|
decimalCount = 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.tangem.feature.learn2earn.domain.models
|
||||||
|
|
||||||
|
import com.tangem.datasource.api.promotion.models.PromotionInfoResponse
|
||||||
|
|
||||||
|
/**
|
||||||
|
[REDACTED_AUTHOR]
|
||||||
|
*/
|
||||||
|
data class Promotion(
|
||||||
|
val info: PromotionInfo?,
|
||||||
|
val error: PromotionError?,
|
||||||
|
) {
|
||||||
|
|
||||||
|
@Throws(NullPointerException::class)
|
||||||
|
fun getInfo(): PromotionInfo = info!!
|
||||||
|
|
||||||
|
fun isError(): Boolean = error != null
|
||||||
|
|
||||||
|
fun isUnreachable(): Boolean = error == PromotionError.NetworkUnreachable
|
||||||
|
|
||||||
|
data class PromotionInfo(
|
||||||
|
val status: PromotionInfoResponse.Status,
|
||||||
|
val awardForNewCard: Float,
|
||||||
|
val awardForOldCard: Float,
|
||||||
|
val awardPaymentToken: PromotionInfoResponse.TokenInfo,
|
||||||
|
) {
|
||||||
|
companion object
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.tangem.feature.learn2earn.domain.models
|
||||||
|
|
||||||
|
import com.tangem.datasource.api.promotion.models.AbstractPromotionResponse
|
||||||
|
|
||||||
|
/**
|
||||||
|
[REDACTED_AUTHOR]
|
||||||
|
*/
|
||||||
|
sealed class PromotionError(val code: Int, val description: String) {
|
||||||
|
object NetworkUnreachable : PromotionError(-1, "Network or service is unreachable")
|
||||||
|
class Error(code: Int, description: String) : PromotionError(code, description)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun AbstractPromotionResponse.Error.toDomainError(): PromotionError = Error(code, description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.tangem.feature.learn2earn.domain.models
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
|
||||||
|
/**
|
||||||
|
[REDACTED_AUTHOR]
|
||||||
|
*/
|
||||||
|
interface WebViewHelper {
|
||||||
|
fun handleWebViewRedirect(uri: Uri): RedirectConsequences
|
||||||
|
fun getWebViewHeaders(): Map<String, String>
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class RedirectConsequences {
|
||||||
|
NOTHING,
|
||||||
|
PROCEED,
|
||||||
|
FINISH_SESSION,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.tangem.feature.learn2earn.presentation
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
|
/**
|
||||||
|
[REDACTED_AUTHOR]
|
||||||
|
*/
|
||||||
|
class Learn2earnRouter(
|
||||||
|
private val wContext: WeakReference<Context>,
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun openWebView() {
|
||||||
|
val context = wContext.get()
|
||||||
|
if (context == null) {
|
||||||
|
Timber.e("Can't open the Learn2earnWebViewActivity")
|
||||||
|
} else {
|
||||||
|
context.startActivity(Intent(context, Learn2earnWebViewActivity::class.java))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
package com.tangem.feature.learn2earn.presentation
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.tangem.feature.learn2earn.domain.api.Learn2earnInteractor
|
||||||
|
import com.tangem.feature.learn2earn.domain.models.RedirectConsequences
|
||||||
|
import com.tangem.feature.learn2earn.domain.models.WebViewHelper
|
||||||
|
import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
[REDACTED_AUTHOR]
|
||||||
|
*/
|
||||||
|
@HiltViewModel
|
||||||
|
class Learn2earnViewModel @Inject constructor(
|
||||||
|
private val interactor: Learn2earnInteractor,
|
||||||
|
private val router: Learn2earnRouter,
|
||||||
|
private val dispatchers: AppCoroutineDispatcherProvider,
|
||||||
|
) : ViewModel(), WebViewHelper {
|
||||||
|
|
||||||
|
var webViewUri: Uri = Uri.parse("https://localhost")
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun isNeedToShowViewOnStoriesScreen(): Boolean = interactor.isNeedToShowViewOnStoriesScreen()
|
||||||
|
|
||||||
|
fun onStoriesClick() {
|
||||||
|
webViewUri = interactor.buildUriForStories()
|
||||||
|
router.openWebView()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isNeedToShowViewOnWalletScreen(walletId: String, callback: (Boolean) -> Unit) {
|
||||||
|
viewModelScope.launch(dispatchers.io) {
|
||||||
|
val isNeedToShow = interactor.isNeedToShowViewOnWalletScreen(walletId)
|
||||||
|
withContext(dispatchers.main) {
|
||||||
|
callback(isNeedToShow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onMainPageClick(walletId: String, cardId: String, cardPubKey: String) {
|
||||||
|
webViewUri = interactor.buildUriForMainPage(walletId, cardId, cardPubKey)
|
||||||
|
router.openWebView()
|
||||||
|
}
|
||||||
|
|
||||||
|
// region WebViewHelper
|
||||||
|
override fun handleWebViewRedirect(uri: Uri): RedirectConsequences {
|
||||||
|
return interactor.handleWebViewRedirect(uri)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getWebViewHeaders(): Map<String, String> {
|
||||||
|
return interactor.getBasicAuthHeaders()
|
||||||
|
}
|
||||||
|
// endregion WebViewHelper
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
package com.tangem.feature.learn2earn.presentation
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.webkit.WebResourceRequest
|
||||||
|
import android.webkit.WebView
|
||||||
|
import android.webkit.WebViewClient
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import androidx.appcompat.app.ActionBar
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.tangem.feature.learn2earn.domain.models.RedirectConsequences
|
||||||
|
import com.tangem.feature.learn2earn.domain.models.WebViewHelper
|
||||||
|
import com.tangem.feature.learn2earn.impl.R
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
|
/**
|
||||||
|
[REDACTED_AUTHOR]
|
||||||
|
*/
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class Learn2earnWebViewActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private val learn2earnViewModel by viewModels<Learn2earnViewModel>()
|
||||||
|
|
||||||
|
private lateinit var actionBar: ActionBar
|
||||||
|
private lateinit var webView: WebView
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_web_view)
|
||||||
|
|
||||||
|
setSupportActionBar(findViewById(R.id.toolbar))
|
||||||
|
actionBar = supportActionBar!!
|
||||||
|
actionBar.setDisplayHomeAsUpEnabled(true)
|
||||||
|
actionBar.setDisplayShowHomeEnabled(true)
|
||||||
|
actionBar.title = learn2earnViewModel.webViewUri.authority
|
||||||
|
|
||||||
|
webView = findViewById(R.id.web_view)
|
||||||
|
webView.settings.javaScriptEnabled = true
|
||||||
|
webView.settings.domStorageEnabled = true
|
||||||
|
webView.settings.loadsImagesAutomatically = true
|
||||||
|
|
||||||
|
webView.webViewClient = Learn2earnWebViewClient(
|
||||||
|
helper = learn2earnViewModel,
|
||||||
|
finishSessionHandler = { finish() },
|
||||||
|
)
|
||||||
|
webView.loadUrl(
|
||||||
|
learn2earnViewModel.webViewUri.toString(),
|
||||||
|
learn2earnViewModel.getWebViewHeaders(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
return when (item.itemId) {
|
||||||
|
android.R.id.home -> {
|
||||||
|
finish()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
webView.clearHistory()
|
||||||
|
webView.clearCache(true)
|
||||||
|
webView.destroy()
|
||||||
|
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Learn2earnWebViewClient(
|
||||||
|
private val helper: WebViewHelper,
|
||||||
|
private val finishSessionHandler: () -> Unit,
|
||||||
|
) : WebViewClient() {
|
||||||
|
|
||||||
|
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
|
||||||
|
when (helper.handleWebViewRedirect(request.url)) {
|
||||||
|
RedirectConsequences.NOTHING -> Unit
|
||||||
|
RedirectConsequences.PROCEED -> {
|
||||||
|
val headers = helper.getWebViewHeaders().toMutableMap()
|
||||||
|
request.requestHeaders?.let { headers.putAll(it) }
|
||||||
|
view.loadUrl(request.url.toString(), headers)
|
||||||
|
}
|
||||||
|
RedirectConsequences.FINISH_SESSION -> {
|
||||||
|
finishSessionHandler.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -24,9 +24,9 @@ import com.tangem.feature.learn2earn.presentation.ui.component.GradientCircle
|
||||||
/**
|
/**
|
||||||
[REDACTED_AUTHOR]
|
[REDACTED_AUTHOR]
|
||||||
*/
|
*/
|
||||||
// TODO: fixme: make function as internal after adding feature interface
|
// TODO: 1inch: make function as internal after adding feature interface
|
||||||
@Composable
|
@Composable
|
||||||
fun OneInchStoriesScreen(onLearnClick: () -> Unit, modifier: Modifier = Modifier) {
|
fun StoriesScreen(onLearnClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||||
Box(modifier = modifier.fillMaxSize()) {
|
Box(modifier = modifier.fillMaxSize()) {
|
||||||
ContentBackground(
|
ContentBackground(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -111,7 +111,7 @@ private fun OneInchStoriesContentPreview_Light() {
|
||||||
TangemTheme(
|
TangemTheme(
|
||||||
isDark = false,
|
isDark = false,
|
||||||
) {
|
) {
|
||||||
OneInchStoriesScreen(
|
StoriesScreen(
|
||||||
onLearnClick = {},
|
onLearnClick = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -123,7 +123,7 @@ private fun OneInchStoriesContentPreview_Dark() {
|
||||||
TangemTheme(
|
TangemTheme(
|
||||||
isDark = true,
|
isDark = true,
|
||||||
) {
|
) {
|
||||||
OneInchStoriesScreen(
|
StoriesScreen(
|
||||||
onLearnClick = {},
|
onLearnClick = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/coordinator_wallet"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/app_bar"
|
||||||
|
style="@style/Widget.MaterialComponents.Toolbar.Surface"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
app:navigationIcon="@drawable/ic_close_24" />
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<WebView
|
||||||
|
android:id="@+id/web_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue