Updated on 2026-08-14

This commit is contained in:
Tangem 2022-11-21 20:39:13 +03:00
parent 67a245a19c
commit 834a7e40c5
13 changed files with 78 additions and 38 deletions

View file

@ -20,14 +20,14 @@ enum class SettingsElement(
WalletConnect(R.drawable.ic_walletconnect, R.string.wallet_connect_title),
Chat(R.drawable.ic_chat, R.string.details_ask_a_question),
SendFeedback(R.drawable.ic_comment, R.string.details_row_title_send_feedback),
ReferralProgram(R.drawable.ic_add_friends, R.string.details_referral_title),
CardSettings(R.drawable.ic_card_settings, R.string.card_settings_title),
AppCurrency(R.drawable.ic_currency, R.string.details_row_title_currency),
AppSettings(R.drawable.ic_settings, R.string.app_settings_title),
LinkMoreCards(R.drawable.ic_more_cards, R.string.details_row_title_create_backup),
TermsOfService(R.drawable.ic_text, R.string.disclaimer_title), // General Terms of Service of the App
TermsOfUse(R.drawable.ic_text, R.string.details_row_title_card_tou), // Terms of Use for S2C cards only
PrivacyPolicy(R.drawable.ic_lock, R.string.details_row_privacy_policy),
;
PrivacyPolicy(R.drawable.ic_lock, R.string.details_row_privacy_policy);
}
data class SocialNetworkLink(

View file

@ -95,6 +95,9 @@ class DetailsViewModel(private val store: Store<AppState>) {
SettingsElement.PrivacyPolicy -> {
// TODO: To be available later
}
SettingsElement.ReferralProgram -> {
store.dispatch(NavigationAction.NavigateTo(AppScreen.ReferralProgram))
}
}
}

View file

@ -6,7 +6,11 @@ import com.tangem.tap.proxy.AppStateHolder
class AuthProviderImpl(private val appStateHolder: AppStateHolder) : AuthProvider {
override fun getAuthToken(): String {
override fun getCardPublicKey(): String {
return appStateHolder.scanResponse?.card?.cardPublicKey?.toHexString() ?: ""
}
override fun getCardId(): String {
return appStateHolder.scanResponse?.card?.cardId ?: ""
}
}

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M15,4C13.939,4 12.922,4.421 12.172,5.172C11.421,5.922 11,6.939 11,8C11,9.061 11.421,10.078 12.172,10.828C12.922,11.579 13.939,12 15,12C16.061,12 17.078,11.579 17.828,10.828C18.579,10.078 19,9.061 19,8C19,6.939 18.579,5.922 17.828,5.172C17.078,4.421 16.061,4 15,4ZM15,5.9C16.16,5.9 17.1,6.84 17.1,8C17.1,9.16 16.16,10.1 15,10.1C14.443,10.1 13.909,9.879 13.515,9.485C13.121,9.091 12.9,8.557 12.9,8C12.9,7.443 13.121,6.909 13.515,6.515C13.909,6.121 14.443,5.9 15,5.9ZM4,7V10H1V12H4V15H6V12H9V10H6V7H4ZM15,13C12.33,13 7,14.33 7,17V20H23V17C23,14.33 17.67,13 15,13ZM15,14.9C17.97,14.9 21.1,16.36 21.1,17V18.1H8.9V17C8.9,16.36 12,14.9 15,14.9Z"
android:fillColor="#000000" />
</vector>

View file

@ -9,16 +9,17 @@ class AuthHeaderInterceptor(
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request().newBuilder().apply {
addHeader(
HEADER_CARD_KEY, authProvider.getAuthToken(),
)
}.build()
val request = chain.request()
.newBuilder()
.addHeader(HEADER_CARD_KEY, authProvider.getCardPublicKey())
.addHeader(HEADER_CARD_ID, authProvider.getCardId())
.build()
return chain.proceed(request)
}
companion object {
const val HEADER_CARD_KEY = "card_public_key"
const val HEADER_CARD_ID = "card_id"
}
}

View file

@ -5,17 +5,16 @@ import com.tangem.datasource.api.referral.models.StartReferralBody
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Query
import retrofit2.http.Path
/**
* Api for referral feature
*/
interface
ReferralApi {
interface ReferralApi {
/** Returns referral status by [walletId] */
@GET("referral/{walletId}")
suspend fun getReferralStatus(@Query("name") walletId: String): ReferralResponse
suspend fun getReferralStatus(@Path("walletId") walletId: String): ReferralResponse
/** Make user referral, requires [StartReferralBody] */
@POST("referral")

View file

@ -1,6 +1,7 @@
package com.tangem.datasource.api.referral.models
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import org.joda.time.DateTime
/**
@ -21,7 +22,7 @@ data class Conditions(
data class Discount(
@Json(name = "amount") val amount: Int,
@Json(name = "discountType") val discountType: DiscountType,
@Json(name = "type") val discountType: String,
)
data class Award(
@ -41,11 +42,7 @@ data class Token(
data class Referral(
@Json(name = "shareLink") val shareLink: String,
@Json(name = "address") val address: String,
@Json(name = "promocode") val promocode: String,
@Json(name = "promoCode") val promocode: String,
@Json(name = "walletsPurchased") val walletsPurchased: Int,
@Json(name = "termsAcceptedAt") val termsAcceptedAt: DateTime?,
)
enum class DiscountType {
PERCENTAGE, VALUE
}
@Json(name = "termsAcceptedAt") val termsAcceptedAt: String?,
)

View file

@ -1,5 +1,7 @@
package com.tangem.datasource.di
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import com.tangem.datasource.api.AuthHeaderInterceptor
import com.tangem.datasource.api.referral.ReferralApi
import com.tangem.lib.auth.AuthProvider
@ -22,7 +24,11 @@ class NetworkModule {
fun provideReferralApi(okHttpClient: OkHttpClient): ReferralApi {
return Retrofit.Builder()
.addConverterFactory(
MoshiConverterFactory.create(),
MoshiConverterFactory.create(
Moshi.Builder()
.addLast(KotlinJsonAdapterFactory())
.build(),
),
)
.baseUrl(PROD_REFERRAL_BASE_URL)
.client(okHttpClient)
@ -34,14 +40,14 @@ class NetworkModule {
@Singleton
fun provideOkHttpClient(authProvider: AuthProvider): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(
HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC),
)
.addInterceptor(AuthHeaderInterceptor(authProvider))
.addInterceptor(
HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY),
)
.build()
}
private companion object {
const val PROD_REFERRAL_BASE_URL = "https://api.tangem-tech.com/v1/"
const val PROD_REFERRAL_BASE_URL = "https://devapi.tangem-tech.com/v1/"//"https://api.tangem-tech.com/v1/"
}
}

View file

@ -0,0 +1,9 @@
package com.tangem.utils
inline fun <reified T : Enum<T>> safeValueOf(type: String, default: T): T {
return try {
java.lang.Enum.valueOf(T::class.java, type)
} catch (e: IllegalArgumentException) {
default
}
}

View file

@ -7,6 +7,8 @@ import com.tangem.feature.referral.domain.models.ReferralData
import com.tangem.feature.referral.domain.models.ReferralInfo
import com.tangem.feature.referral.domain.models.TokenData
import com.tangem.utils.converter.Converter
import com.tangem.utils.safeValueOf
import org.joda.time.format.ISODateTimeFormat
import javax.inject.Inject
class ReferralConverter @Inject constructor() : Converter<ReferralResponse, ReferralData> {
@ -19,7 +21,7 @@ class ReferralConverter @Inject constructor() : Converter<ReferralResponse, Refe
ReferralData.ParticipantData(
award = conditions.award,
discount = conditions.discount.amount,
discountType = DiscountType.valueOf(conditions.discount.discountType.name),
discountType = safeValueOf(conditions.discount.discountType.uppercase(), DiscountType.PERCENTAGE),
tosLink = conditions.tosLink,
tokens = tokenConverter.convertList(conditions.awards.map { it.token }),
referral = ReferralInfo(
@ -27,14 +29,16 @@ class ReferralConverter @Inject constructor() : Converter<ReferralResponse, Refe
address = referral.address,
promocode = referral.promocode,
walletsPurchased = referral.walletsPurchased,
termsAcceptedAt = referral.termsAcceptedAt,
termsAcceptedAt = referral.termsAcceptedAt?.let {
ISODateTimeFormat.dateTimeParser().parseDateTime(referral.termsAcceptedAt)
},
),
)
} else {
ReferralData.NonParticipantData(
award = conditions.award,
discount = conditions.discount.amount,
discountType = DiscountType.valueOf(conditions.discount.discountType.name),
discountType = safeValueOf(conditions.discount.discountType.uppercase(), DiscountType.PERCENTAGE),
tosLink = conditions.tosLink,
tokens = tokenConverter.convertList(conditions.awards.map { it.token }),
)

View file

@ -75,7 +75,7 @@ fun ReferralScreen(stateHolder: ReferralStateHolder) {
BottomSheetScaffold(
sheetContent = {
AgreementBottomSheetContent(
url = (stateHolder.referralInfoState as ReferralInfoContentState).url,
url = (stateHolder.referralInfoState as? ReferralInfoContentState)?.url ?: "",
)
},
scaffoldState = bottomSheetScaffoldState,

View file

@ -22,7 +22,7 @@ class ReferralViewModel @Inject constructor(
private val dispatchers: CoroutineDispatcherProvider,
) : ViewModel() {
var uiState by mutableStateOf(createInitiallyUiState())
var uiState: ReferralStateHolder by mutableStateOf(createInitiallyUiState())
private set
init {
@ -36,15 +36,24 @@ class ReferralViewModel @Inject constructor(
private fun createInitiallyUiState() = ReferralStateHolder(
headerState = ReferralStateHolder.HeaderState(onBackClicked = { }),
referralInfoState = ReferralInfoState.Loading,
errorToast = ReferralStateHolder.ErrorToast(visibility = false, changeVisibility = ::cancelErrorToast),
errorToast = ReferralStateHolder.ErrorToast(
visibility = false,
changeVisibility = {
uiState = uiState.copy(
referralInfoState = uiState.referralInfoState,
errorToast = uiState.errorToast.copy(visibility = false),
)
},
),
)
private fun loadReferralData() {
val lastUiState = uiState
uiState = uiState.copy(referralInfoState = ReferralInfoState.Loading)
viewModelScope.launch(dispatchers.main) {
runCatching(dispatchers.io) { referralInteractor.getReferralStatus() }
.onSuccess { uiState = uiState.copy(referralInfoState = it.convertToReferralInfoState()) }
.onFailure { uiState = uiState.copy(errorToast = uiState.errorToast.copy(visibility = true)) }
.onFailure { uiState = lastUiState.copy(errorToast = lastUiState.errorToast.copy(visibility = true)) }
}
}
@ -77,15 +86,12 @@ class ReferralViewModel @Inject constructor(
}
private fun onParticipateClicked() {
val lastUiState = uiState
uiState = uiState.copy(referralInfoState = ReferralInfoState.Loading)
viewModelScope.launch(dispatchers.main) {
runCatching(dispatchers.io) { referralInteractor.startReferral() }
.onSuccess { uiState = uiState.copy(referralInfoState = it.convertToReferralInfoState()) }
.onFailure { uiState = uiState.copy(errorToast = uiState.errorToast.copy(visibility = true)) }
.onFailure { uiState = lastUiState.copy(errorToast = lastUiState.errorToast.copy(visibility = true)) }
}
}
private fun cancelErrorToast() {
uiState = uiState.copy(errorToast = uiState.errorToast.copy(visibility = false))
}
}

View file

@ -8,5 +8,7 @@ interface AuthProvider {
/**
* Returns authToken for tangem tech api
*/
fun getAuthToken(): String
fun getCardPublicKey(): String
fun getCardId(): String
}