Updated on 2026-08-14
This commit is contained in:
parent
efe271e1a0
commit
b87a0ae96b
18 changed files with 91 additions and 79 deletions
3
.idea/codeStyles/Project.xml
generated
3
.idea/codeStyles/Project.xml
generated
|
|
@ -13,6 +13,9 @@
|
|||
<package name="io.ktor" alias="false" withSubpackages="true" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="LINE_BREAK_AFTER_MULTILINE_WHEN_ENTRY" value="false" />
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="5" />
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="3" />
|
||||
<option name="ALLOW_TRAILING_COMMA" value="true" />
|
||||
<option name="BLANK_LINES_BEFORE_DECLARATION_WITH_COMMENT_OR_ANNOTATION_ON_SEPARATE_LINE" value="0" />
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
|
|
|
|||
|
|
@ -200,6 +200,8 @@ class TapApplication : Application(), ImageLoaderFactory {
|
|||
appStateHolder.userTokensRepository = userTokensRepository
|
||||
appStateHolder.walletStoresManager = walletStoresManager
|
||||
|
||||
// TODO: Try to performance and user experience.
|
||||
// [REDACTED_JIRA]
|
||||
runBlocking {
|
||||
featureTogglesManager.init()
|
||||
learn2ernInteractor.init()
|
||||
|
|
|
|||
|
|
@ -3,5 +3,10 @@ package com.tangem.tap.network.auth
|
|||
import com.tangem.lib.auth.BasicAuthProvider
|
||||
|
||||
class TangemBasicAuthProvider(private val credentials: String?) : BasicAuthProvider {
|
||||
override fun getCredentials(): String? = credentials
|
||||
|
||||
override fun getCredentials(): Map<String, String> = if (credentials == null) {
|
||||
mapOf()
|
||||
} else {
|
||||
mapOf("Authorization" to "Basic $credentials")
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.tap.network.auth.di
|
|||
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.lib.auth.AuthProvider
|
||||
import com.tangem.lib.auth.BasicAuthProvider
|
||||
import com.tangem.tap.network.auth.AuthProviderImpl
|
||||
import com.tangem.tap.network.auth.TangemBasicAuthProvider
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
|
|
@ -23,7 +24,7 @@ class AuthModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideTangemBasicAuthProvider(configManager: ConfigManager): com.tangem.lib.auth.BasicAuthProvider {
|
||||
fun provideTangemBasicAuthProvider(configManager: ConfigManager): BasicAuthProvider {
|
||||
return TangemBasicAuthProvider(configManager.config.tangemComAuthorization)
|
||||
}
|
||||
}
|
||||
|
|
@ -4,35 +4,26 @@ import com.tangem.datasource.api.promotion.models.*
|
|||
import retrofit2.http.*
|
||||
|
||||
/**
|
||||
*
|
||||
* Promotion API
|
||||
* @see <a href = "https://www.notion.so/tangem/Promotion-Program-API-0907159c3fdb4975aac761be632f44da">Documentation<a/>
|
||||
[REDACTED_AUTHOR]
|
||||
* Api details
|
||||
* https://www.notion.so/tangem/Promotion-Program-API-0907159c3fdb4975aac761be632f44da
|
||||
*/
|
||||
interface PromotionApi {
|
||||
|
||||
|
||||
@Headers("Cache-Control: max-age=$CACHE_CONTROL_SECONDS")
|
||||
@Headers("Cache-Control: max-age=3600")
|
||||
@GET("promotion")
|
||||
suspend fun getPromotionInfo(@Query("programName") name: String): PromotionInfoResponse
|
||||
|
||||
|
||||
@POST("promotion/code/validate")
|
||||
suspend fun codeValidate(@Body request: CodeValidateRequestBody): CodeValidateResponse
|
||||
suspend fun validateCode(@Body request: CodeValidateRequestBody): CodeValidateResponse
|
||||
|
||||
|
||||
@POST("promotion/code/award")
|
||||
suspend fun codeAward(@Body request: CodeAwardRequestBody): CodeAwardResponse
|
||||
suspend fun requestAwardByCode(@Body request: CodeAwardRequestBody): CodeAwardResponse
|
||||
|
||||
|
||||
@POST("promotion/validate")
|
||||
suspend fun validate(@Body request: ValidateRequestBody): ValidateResponse
|
||||
|
||||
|
||||
@GET("promotion/award")
|
||||
suspend fun award(@Body request: AwardRequestBody): AwardResponse
|
||||
|
||||
companion object {
|
||||
const val ONE_INCH_TIMEOUT_MS = 5000L
|
||||
const val CACHE_CONTROL_SECONDS = 3600
|
||||
}
|
||||
suspend fun requestAward(@Body request: AwardRequestBody): AwardResponse
|
||||
}
|
||||
|
|
@ -14,7 +14,5 @@ abstract class AbstractPromotionResponse {
|
|||
data class Error(
|
||||
@Json(name = "code") val code: Int,
|
||||
@Json(name = "description") val description: String,
|
||||
) {
|
||||
companion object
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -66,10 +66,10 @@ class NetworkModule {
|
|||
AuthenticationHeader(authProvider),
|
||||
)
|
||||
.allowLogging()
|
||||
.callTimeout(PromotionApi.ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
|
||||
.connectTimeout(PromotionApi.ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
|
||||
.readTimeout(PromotionApi.ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
|
||||
.writeTimeout(PromotionApi.ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
|
||||
.callTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
|
||||
.connectTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
|
||||
.readTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
|
||||
.writeTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
|
||||
.build()
|
||||
return createBasePromotionRetrofit(okClient, moshi)
|
||||
}
|
||||
|
|
@ -90,5 +90,6 @@ class NetworkModule {
|
|||
const val PROMOTION_BASE_URL = "https://tangem.com/v1/"
|
||||
|
||||
const val PAYMENTOLOGY_BASE_URL: String = "https://paymentologygate.oa.r.appspot.com/"
|
||||
const val API_ONE_INCH_TIMEOUT_MS = 5000L
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,8 @@ import com.tangem.datasource.api.promotion.models.*
|
|||
import com.tangem.feature.learn2earn.data.api.Learn2earnPreferenceStorage
|
||||
import com.tangem.feature.learn2earn.data.api.Learn2earnRepository
|
||||
import com.tangem.feature.learn2earn.data.api.UsedCardsPreferenceStorage
|
||||
import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
|
|
@ -16,6 +18,7 @@ internal class DefaultLearn2earnRepository(
|
|||
private val usedCardsPreferenceStorage: UsedCardsPreferenceStorage,
|
||||
private val moshi: Moshi,
|
||||
private val api: PromotionApi,
|
||||
private val dispatchers: AppCoroutineDispatcherProvider,
|
||||
) : Learn2earnRepository {
|
||||
|
||||
private val promotionInfoAdapter = moshi.adapter(PromotionInfoResponse::class.java)
|
||||
|
|
@ -40,50 +43,47 @@ internal class DefaultLearn2earnRepository(
|
|||
return preferencesStorage.alreadyReceivedAward
|
||||
}
|
||||
|
||||
override suspend fun requestAwardByCode(walletId: String, address: String): Result<Boolean> {
|
||||
return runCatching {
|
||||
api.codeAward(CodeAwardRequestBody(walletId, address, getPromoCode()))
|
||||
}.fold(
|
||||
onSuccess = { response ->
|
||||
val alreadyReceived = response.status ?: false
|
||||
preferencesStorage.alreadyReceivedAward = alreadyReceived
|
||||
Result.success(alreadyReceived)
|
||||
},
|
||||
onFailure = { exception -> Result.failure(exception) },
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getPromotionInfo(): Result<PromotionInfoResponse> {
|
||||
val info = restorePromotionInfo()
|
||||
return withContext(dispatchers.io) {
|
||||
val info = restorePromotionInfo()
|
||||
|
||||
return if (info?.status == PromotionInfoResponse.Status.FINISHED) {
|
||||
Result.success(info)
|
||||
} else {
|
||||
runCatching { api.getPromotionInfo(getProgramName()) }
|
||||
.fold(
|
||||
onSuccess = { infoResponse ->
|
||||
savePromotionInfo(infoResponse)
|
||||
Result.success(infoResponse)
|
||||
},
|
||||
onFailure = { exception -> Result.failure(exception) },
|
||||
)
|
||||
if (info?.status == PromotionInfoResponse.Status.FINISHED) {
|
||||
Result.success(info)
|
||||
} else {
|
||||
runCatching { api.getPromotionInfo(getProgramName()) }
|
||||
.fold(
|
||||
onSuccess = { infoResponse ->
|
||||
savePromotionInfo(infoResponse)
|
||||
Result.success(infoResponse)
|
||||
},
|
||||
onFailure = { exception -> Result.failure(exception) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun validate(walletId: String): ValidateResponse {
|
||||
return api.validate(ValidateRequestBody(walletId, getProgramName()))
|
||||
return withContext(dispatchers.io) {
|
||||
api.validate(ValidateRequestBody(walletId, getProgramName()))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun award(walletId: String): AwardResponse {
|
||||
return api.award(AwardRequestBody(walletId, getProgramName()))
|
||||
override suspend fun requestAward(walletId: String): AwardResponse {
|
||||
return withContext(dispatchers.io) {
|
||||
api.requestAward(AwardRequestBody(walletId, getProgramName()))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun codeValidate(walletId: String, code: String?): CodeValidateResponse {
|
||||
return api.codeValidate(CodeValidateRequestBody(walletId, code))
|
||||
override suspend fun validateCode(walletId: String, code: String?): CodeValidateResponse {
|
||||
return withContext(dispatchers.io) {
|
||||
api.validateCode(CodeValidateRequestBody(walletId, code))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun codeAward(walletId: String, address: String, code: String?): CodeAwardResponse {
|
||||
return api.codeAward(CodeAwardRequestBody(walletId, address, code))
|
||||
override suspend fun requestAwardByCode(walletId: String, address: String, code: String?): CodeAwardResponse {
|
||||
return withContext(dispatchers.io) {
|
||||
api.requestAwardByCode(CodeAwardRequestBody(walletId, address, code))
|
||||
}
|
||||
}
|
||||
|
||||
private fun restorePromotionInfo(): PromotionInfoResponse? {
|
||||
|
|
@ -101,7 +101,6 @@ internal class DefaultLearn2earnRepository(
|
|||
preferencesStorage.promotionInfo = promotionInfoAdapter.toJson(infoResponse)
|
||||
} catch (ex: Exception) {
|
||||
Timber.e(ex)
|
||||
preferencesStorage.promotionInfo = null
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ internal class DefaultPreferenceStorage(
|
|||
) : SimpleKrate(context = context, name = "Lear2earnPromotion"), Learn2earnPreferenceStorage {
|
||||
|
||||
override var promoCode: String? by stringPref().withDefault(null)
|
||||
|
||||
override var promotionInfo: String? by stringPref().withDefault(null)
|
||||
|
||||
override var alreadyReceivedAward: Boolean by booleanPref().withDefault(false)
|
||||
}
|
||||
|
|
@ -4,7 +4,10 @@ package com.tangem.feature.learn2earn.data.api
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface Learn2earnPreferenceStorage {
|
||||
|
||||
var promoCode: String?
|
||||
|
||||
var promotionInfo: String?
|
||||
|
||||
var alreadyReceivedAward: Boolean
|
||||
}
|
||||
|
|
@ -6,15 +6,24 @@ import com.tangem.datasource.api.promotion.models.*
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface Learn2earnRepository {
|
||||
|
||||
fun isHadActivatedCards(): Boolean
|
||||
|
||||
fun getPromoCode(): String?
|
||||
|
||||
fun savePromoCode(code: String)
|
||||
|
||||
fun getProgramName(): String
|
||||
|
||||
fun isAlreadyReceivedAward(): Boolean
|
||||
suspend fun requestAwardByCode(walletId: String, address: String): Result<Boolean>
|
||||
|
||||
suspend fun getPromotionInfo(): Result<PromotionInfoResponse>
|
||||
|
||||
suspend fun validate(walletId: String): ValidateResponse
|
||||
suspend fun award(walletId: String): AwardResponse
|
||||
suspend fun codeValidate(walletId: String, code: String?): CodeValidateResponse
|
||||
suspend fun codeAward(walletId: String, address: String, code: String?): CodeAwardResponse
|
||||
|
||||
suspend fun requestAward(walletId: String): AwardResponse
|
||||
|
||||
suspend fun validateCode(walletId: String, code: String?): CodeValidateResponse
|
||||
|
||||
suspend fun requestAwardByCode(walletId: String, address: String, code: String?): CodeAwardResponse
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ import com.tangem.feature.learn2earn.data.DefaultUsedCardsPreferenceStorage
|
|||
import com.tangem.feature.learn2earn.data.api.Learn2earnPreferenceStorage
|
||||
import com.tangem.feature.learn2earn.data.api.Learn2earnRepository
|
||||
import com.tangem.feature.learn2earn.data.api.UsedCardsPreferenceStorage
|
||||
import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -44,12 +45,14 @@ class Learn2earnDataModule {
|
|||
usedCardsPreferenceStorage: UsedCardsPreferenceStorage,
|
||||
@NetworkMoshi moshi: Moshi,
|
||||
@PromotionOneInch promotionApi: PromotionApi,
|
||||
dispatchers: AppCoroutineDispatcherProvider,
|
||||
): Learn2earnRepository {
|
||||
return DefaultLearn2earnRepository(
|
||||
preferencesStorage = preferenceStorage,
|
||||
usedCardsPreferenceStorage = usedCardsPreferenceStorage,
|
||||
moshi = moshi,
|
||||
api = promotionApi,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -89,8 +89,6 @@ class DefaultLearn2earnInteractor(
|
|||
}
|
||||
|
||||
private suspend fun initPromotionInfo() {
|
||||
// return Promotion.dummyActive()
|
||||
|
||||
promotion = repository.getPromotionInfo()
|
||||
.fold(
|
||||
onSuccess = { response ->
|
||||
|
|
|
|||
|
|
@ -42,9 +42,7 @@ class WebViewUriBuilder(
|
|||
}
|
||||
|
||||
fun getBasicAuthHeaders(): Map<String, String> {
|
||||
return basicAuthProvider.getCredentials()?.let {
|
||||
mapOf("Authorization" to "Basic $it")
|
||||
} ?: mapOf()
|
||||
return basicAuthProvider.getCredentials()
|
||||
}
|
||||
|
||||
fun isPromoCodeRedirect(uri: Uri): Boolean {
|
||||
|
|
|
|||
|
|
@ -9,10 +9,16 @@ import com.tangem.feature.learn2earn.domain.models.RedirectConsequences
|
|||
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
|
||||
}
|
||||
|
|
@ -6,7 +6,6 @@ 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
|
||||
|
|
@ -21,7 +20,7 @@ class Learn2earnDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideRouter(@ApplicationContext context: Context): Learn2earnRouter {
|
||||
return Learn2earnRouter(context.toWeakReference())
|
||||
return Learn2earnRouter(context)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
@ -33,6 +32,7 @@ class Learn2earnDomainModule {
|
|||
return DefaultLearn2earnInteractor(
|
||||
repository = repository,
|
||||
basicAuthProvider = basicAuthProvider,
|
||||
// TODO: 1inch: paste locale provider instead of hardcode
|
||||
userCountryCodeProvider = { "ru" },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,22 +2,15 @@ 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>,
|
||||
private val context: 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))
|
||||
}
|
||||
context.startActivity(Intent(context, Learn2earnWebViewActivity::class.java))
|
||||
}
|
||||
}
|
||||
|
|
@ -5,5 +5,5 @@ package com.tangem.lib.auth
|
|||
*/
|
||||
interface BasicAuthProvider {
|
||||
|
||||
fun getCredentials(): String?
|
||||
fun getCredentials(): Map<String, String>
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue