Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package one.mixin.android.api.response

import com.google.gson.annotations.SerializedName
import org.threeten.bp.Instant

data class WalletHomeBanner(
@SerializedName(value = "banner_id", alternate = ["id"])
Expand Down Expand Up @@ -60,6 +61,13 @@ data class WalletHomeBanner(
val isActive: Boolean
get() = status.isBlank() || status.equals(BANNER_STATUS_ACTIVE, ignoreCase = true)

fun isExpired(now: Instant = Instant.now()): Boolean {
if (endAt.isBlank()) return false
return runCatching {
!now.isBefore(Instant.parse(endAt))
}.getOrDefault(false)
}

companion object {
const val BANNER_STATUS_ACTIVE = "active"
const val BANNER_STATUS_INACTIVE = "inactive"
Expand All @@ -73,14 +81,27 @@ data class WalletHomeBannerAction(
val action: String = "",
)

fun List<WalletHomeBanner>.filterWalletHomeBannersByChains(chains: Collection<String>): List<WalletHomeBanner> {
val walletChains = chains.filterTo(mutableSetOf(), String::isNotBlank)
if (walletChains.isEmpty()) return this

return filter { banner ->
banner.chains.none(String::isNotBlank) || banner.chains.any(walletChains::contains)
}
}

fun Set<String>.syncedWalletHomeClosedBannerIds(remoteBanners: List<WalletHomeBanner>): Set<String> {
return this
}

fun List<WalletHomeBanner>.visibleWalletHomeBanners(closedBannerIds: Set<String>): List<WalletHomeBanner> =
fun List<WalletHomeBanner>.visibleWalletHomeBanners(
closedBannerIds: Set<String>,
now: Instant = Instant.now(),
): List<WalletHomeBanner> =
filter { banner ->
banner.key.isNotBlank() &&
banner.isActive &&
!banner.isExpired(now) &&
banner.hasVisualContent &&
(!banner.actionUrl.isNullOrBlank() || banner.visibleActions.isNotEmpty()) &&
!closedBannerIds.contains(banner.key)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,11 @@ import one.mixin.android.api.MixinResponse
import one.mixin.android.api.response.WalletHomeBanner
import one.mixin.android.api.response.referral.ReferralResponse
import retrofit2.http.GET
import retrofit2.http.Query

interface ReferralService {
@GET("referral")
suspend fun referral(): MixinResponse<ReferralResponse>

@GET("app-banners")
suspend fun walletHomeBanners(
@Query("chain") chains: List<String>? = null,
): MixinResponse<List<WalletHomeBanner>>
suspend fun walletHomeBanners(): MixinResponse<List<WalletHomeBanner>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ class CashRepository
private val cashService: CashService,
private val userRepository: UserRepository,
) {
suspend fun cachedAccount(): CashAccount? =
PropertyHelper.findCashAccount()

suspend fun account(): MixinResponse<CashAccount> {
userRepository.getBotPublicKey(MIXIN_CASH_USER_ID, false)
val response = cashService.account()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,15 @@ class ReferralRepository
)
}

suspend fun fetchWalletHomeBanners(chains: List<String> = emptyList()): List<WalletHomeBanner>? {
suspend fun fetchWalletHomeBanners(): List<WalletHomeBanner>? {
runCatching {
userRepository.getBotPublicKey(REFERRAL_BOT_USER_ID, false)
}.onFailure {
Timber.w(it, "Failed to warm up referral bot session before fetching wallet home banners")
}

return requestReferralMixinAPI(
invokeNetwork = { referralService.walletHomeBanners(chains.takeIf { it.isNotEmpty() }) },
invokeNetwork = { referralService.walletHomeBanners() },
successBlock = { response -> response.data.orEmpty() },
failureBlock = { response ->
Timber.d(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class Web3ViewModel @Inject constructor(

suspend fun findOrSyncApp(appId: String) = userRepository.findOrSyncApp(appId)

suspend fun walletHomeBanners(chains: List<String>) = referralRepository.fetchWalletHomeBanners(chains)
suspend fun walletHomeBanners() = referralRepository.fetchWalletHomeBanners()

suspend fun findMarketItemByAssetId(assetId: String) = tokenRepository.findMarketItemByAssetId(assetId)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import one.mixin.android.R
import one.mixin.android.RxBus
import one.mixin.android.api.response.WalletHomeBanner
import one.mixin.android.api.response.WalletHomeBannerAction
import one.mixin.android.api.response.filterWalletHomeBannersByChains
import one.mixin.android.api.response.syncedWalletHomeClosedBannerIds
import one.mixin.android.api.response.visibleWalletHomeBanners
import one.mixin.android.databinding.FragmentPrivacyWalletBinding
Expand Down Expand Up @@ -83,15 +84,18 @@ import one.mixin.android.ui.wallet.home.WalletHomeType
import one.mixin.android.ui.wallet.home.calculateWalletHomeBtcTotal
import one.mixin.android.ui.wallet.home.calculateWalletHomeTokenFiat
import one.mixin.android.ui.wallet.home.formatWalletHomeBtcTotal
import one.mixin.android.ui.wallet.home.getWalletHomeBannerCache
import one.mixin.android.ui.wallet.home.getWalletHomeCache
import one.mixin.android.ui.wallet.home.putWalletHomeCache
import one.mixin.android.ui.wallet.home.putWalletHomeBannerCache
import one.mixin.android.ui.wallet.home.WalletHomeImportKeyAction
import one.mixin.android.ui.wallet.home.WalletHomeImportKeyKind
import one.mixin.android.ui.wallet.home.walletHomePendingTransactionCount
import one.mixin.android.ui.wallet.home.walletHomePendingTransactionIndicator
import one.mixin.android.ui.wallet.home.walletHomeWatchIndicator
import one.mixin.android.ui.wallet.home.walletHomeCacheKey
import one.mixin.android.ui.wallet.home.walletHomeImportKeyAction
import one.mixin.android.ui.wallet.home.withDynamicBanners
import one.mixin.android.ui.wallet.adapter.WalletWeb3TokenAdapter
import one.mixin.android.util.analytics.AnalyticsTracker
import one.mixin.android.util.analytics.AnalyticsTracker.TradeSource
Expand Down Expand Up @@ -143,6 +147,7 @@ class WalletHomeClassicFragment : BaseFragment(R.layout.fragment_privacy_wallet)
private var pendingTransactionCount: Int = 0
private var watchAddresses: List<String> = emptyList()
private var dynamicBanners: List<WalletHomeBanner> = emptyList()
private var currentWalletHomeBannerChains: Set<String> = emptySet()
private var isDynamicBannerLoaded = false
private var closedDynamicBannerIds: Set<String> = emptySet()
private var walletHomeBannerRefreshJob: Job? = null
Expand All @@ -169,6 +174,7 @@ class WalletHomeClassicFragment : BaseFragment(R.layout.fragment_privacy_wallet)
field = value
walletHomeDataState = WalletHomeDataState.EMPTY
dynamicBanners = emptyList()
currentWalletHomeBannerChains = emptySet()
isDynamicBannerLoaded = false
_walletId.value = value
loadWalletHomeCache()
Expand Down Expand Up @@ -499,7 +505,7 @@ class WalletHomeClassicFragment : BaseFragment(R.layout.fragment_privacy_wallet)
fiatRate = fiatRate,
)
val showAddWalletBanner = !defaultSharedPreferences.getBoolean(PREF_WALLET_HOME_ADD_WALLET_BANNER_CLOSED, false)
val visibleDynamicBanners = dynamicBanners.visibleWalletHomeBanners(closedDynamicBannerIds)
val visibleDynamicBanners = currentVisibleDynamicBanners()
val showBanner = shouldShowWalletHomeBannerCard(
showAddWalletBanner = showAddWalletBanner,
isDynamicBannerLoaded = isDynamicBannerLoaded,
Expand Down Expand Up @@ -617,32 +623,77 @@ class WalletHomeClassicFragment : BaseFragment(R.layout.fragment_privacy_wallet)
if (!isAdded || walletId.isEmpty()) return
val requestWalletId = walletId
walletHomeBannerRefreshJob = lifecycleScope.launch {
val requestWalletChains = try {
walletHomeBannerChains(requestWalletId)
} catch (e: CancellationException) {
throw e
} catch (t: Throwable) {
Timber.w(t, "Read wallet chains for wallet home banners failed")
return@launch
}
if (!shouldApplyClassicWalletHomeBannerResponse(requestWalletId, walletId)) return@launch
currentWalletHomeBannerChains = requestWalletChains
val cacheKey = classicWalletHomeCacheKey(requestWalletId)
applyCachedWalletHomeBanners(requestWalletId, cacheKey)
val remoteBanners = try {
web3ViewModel.walletHomeBanners(walletHomeBannerChains(requestWalletId))
web3ViewModel.walletHomeBanners()
} catch (e: CancellationException) {
throw e
} catch (t: Throwable) {
Timber.w(t, "Fetch wallet home banners failed")
null
} ?: return@launch
defaultSharedPreferences.putWalletHomeBannerCache(cacheKey, remoteBanners)
if (!shouldApplyClassicWalletHomeBannerResponse(requestWalletId, walletId)) return@launch
runCatching {
syncClosedDynamicBannerIds(remoteBanners)
}.onFailure {
Timber.w(it, "Sync wallet home banner closed ids failed")
}
if (!shouldApplyClassicWalletHomeBannerResponse(requestWalletId, walletId)) return@launch
dynamicBanners = remoteBanners
isDynamicBannerLoaded = true
renderHome()
updateDynamicBanners(remoteBanners)
}
}

private suspend fun walletHomeBannerChains(walletId: String): List<String> =
private suspend fun applyCachedWalletHomeBanners(
requestWalletId: String,
cacheKey: String,
) {
val cachedBanners = defaultSharedPreferences.getWalletHomeBannerCache(cacheKey) ?: return
if (!shouldApplyClassicWalletHomeBannerResponse(requestWalletId, walletId)) return
updateDynamicBanners(cachedBanners)
runCatching {
syncClosedDynamicBannerIds(cachedBanners)
}.onFailure {
Timber.w(it, "Sync cached wallet home banner closed ids failed")
}
if (!shouldApplyClassicWalletHomeBannerResponse(requestWalletId, walletId)) return
updateDynamicBanners(cachedBanners)
}

private fun updateDynamicBanners(banners: List<WalletHomeBanner>) {
dynamicBanners = banners
isDynamicBannerLoaded = true
_homeState.value = _homeState.value.withDynamicBanners(
dynamicBanners = currentVisibleDynamicBanners(),
showAddWalletBanner = !defaultSharedPreferences.getBoolean(
PREF_WALLET_HOME_ADD_WALLET_BANNER_CLOSED,
false,
),
)
renderHome()
}

private fun currentVisibleDynamicBanners(): List<WalletHomeBanner> =
dynamicBanners
.filterWalletHomeBannersByChains(currentWalletHomeBannerChains)
.visibleWalletHomeBanners(closedDynamicBannerIds)

private suspend fun walletHomeBannerChains(walletId: String): Set<String> =
web3ViewModel.getAddresses(walletId)
.map { it.chainId }
.filter(String::isNotBlank)
.distinct()
.toSet()

private suspend fun syncClosedDynamicBannerIds(remoteBanners: List<WalletHomeBanner>) {
val closedBannerIds = findWalletHomeDynamicBannerClosedIds()
Expand Down Expand Up @@ -676,7 +727,7 @@ class WalletHomeClassicFragment : BaseFragment(R.layout.fragment_privacy_wallet)
}
}

private fun classicWalletHomeCacheKey(): String =
private fun classicWalletHomeCacheKey(walletId: String = this.walletId): String =
walletHomeCacheKey(WalletHomeType.CLASSIC, walletId)

private fun openClassicBuy() {
Expand Down
Loading