Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PWN-877 - add PNL feature toggle #2178

Merged
merged 2 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Expand Up @@ -17,6 +17,7 @@ import org.p2p.wallet.common.feature_toggles.toggles.remote.NetworkObservationDe
import org.p2p.wallet.common.feature_toggles.toggles.remote.NetworkObservationFeatureToggle
import org.p2p.wallet.common.feature_toggles.toggles.remote.NetworkObservationFrequencyFeatureToggle
import org.p2p.wallet.common.feature_toggles.toggles.remote.NetworkObservationPercentFeatureToggle
import org.p2p.wallet.common.feature_toggles.toggles.remote.PnlEnabledFeatureToggle
import org.p2p.wallet.common.feature_toggles.toggles.remote.ReferralProgramEnabledFeatureToggle
import org.p2p.wallet.common.feature_toggles.toggles.remote.RegisterUsernameEnabledFeatureToggle
import org.p2p.wallet.common.feature_toggles.toggles.remote.RegisterUsernameSkipEnabledFeatureToggle
Expand All @@ -38,7 +39,7 @@ object FeatureTogglesModule : InjectionModule {
singleOf(::FeatureTogglesValuesSource) bind RemoteConfigValuesProvider::class

factory {
setOf(
listOf(
get<SslPinningFeatureToggle>(),
get<SettingsNetworkListFeatureToggle>(),
get<NetworkObservationFeatureToggle>(),
Expand All @@ -54,7 +55,8 @@ object FeatureTogglesModule : InjectionModule {
get<SwapRoutesRefreshFeatureToggle>(),
get<ReferralProgramEnabledFeatureToggle>(),
get<ForceUpdateVersionCodeFeatureToggle>(),
).toList()
get<PnlEnabledFeatureToggle>(),
)
}

factoryOf(::SslPinningFeatureToggle)
Expand All @@ -75,6 +77,7 @@ object FeatureTogglesModule : InjectionModule {
factoryOf(::SwapRoutesValidationEnabledFeatureToggle)
factoryOf(::StrigaSignupEnabledFeatureToggle)
factoryOf(::ReferralProgramEnabledFeatureToggle)
factoryOf(::PnlEnabledFeatureToggle)

singleOf(::FeatureToggleProvider)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.p2p.wallet.common.feature_toggles.toggles.remote

import org.p2p.wallet.common.feature_toggles.remote_config.RemoteConfigValuesProvider

class PnlEnabledFeatureToggle(
valuesProvider: RemoteConfigValuesProvider
) : BooleanFeatureToggle(valuesProvider) {
override val featureKey: String = "pnl_enabled"
override val featureDescription: String = "Enables PNL calculation"
override val defaultValue: Boolean = false
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class TokenHistoryPresenter(
}

private fun observePnlData() {
pnlInteractor.state
pnlInteractor.pnlState
.onEach { updateTokenAmounts(this.token) }
.launchIn(this)
}
Expand Down Expand Up @@ -87,14 +87,14 @@ class TokenHistoryPresenter(
view?.renderTokenPnl(
pnlUiMapper.mapTokenBalancePnl(
tokenMint = token.mintAddressB58,
pnlDataState = pnlInteractor.state.value
pnlDataState = pnlInteractor.pnlState.value
)
)
}

override fun onTokenPnlClicked() {
if (pnlInteractor.state.value.isResult()) {
pnlInteractor.state.value.findForToken(token.mintAddressB58)?.let {
if (pnlInteractor.pnlState.value.isLoaded()) {
pnlInteractor.pnlState.value.findForToken(token.mintAddressB58)?.let {
view?.showPnlDetails(it.percent)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ class MyCryptoPresenter(
// pnl must restart it's 5 minutes timer after force refresh
// and we should restart it only if it was initially started
// other cases might indicate that we didn't start observer due to empty state
if (pnlInteractor.isStarted()) {
if (!pnlInteractor.canBeStarted()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it looks there are logical mistake: if it can't be started - restart
I suppose you wanted to check whether it can be started at all, considering feature toggle condition, in this case you need to add one more if:

if(pnlIntereactor.canBeStarted() && pnlInteractor.isStarted()) {
    pnlInteractor.restartAndRefresh()
}

checking for "isStarted" is necessary here, because we should restart only if it was initially started after the first tokens load (see handleTokenState is UserTokensState.Loaded block)

pnlInteractor.restartAndRefresh()
}
} catch (cancelled: CancellationException) {
Expand Down Expand Up @@ -119,7 +119,7 @@ class MyCryptoPresenter(
private fun observePnlData() {
pnlDataSubscription?.cancel()
pnlDataSubscription = launch {
pnlInteractor.state
pnlInteractor.pnlState
.onEach {
handleTokenState(tokenServiceCoordinator.observeLastState().value)
}
Expand All @@ -142,12 +142,12 @@ class MyCryptoPresenter(
is UserTokensState.Loaded -> {
view?.showEmptyState(isEmpty = false)

if (!pnlInteractor.isStarted()) {
if (pnlInteractor.canBeStarted()) {
pnlInteractor.start()
}

showTokensAndBalance(
pnlDataState = pnlInteractor.state.value,
pnlDataState = pnlInteractor.pnlState.value,
// separated screens logic: solTokens = filterCryptoTokens(newState.solTokens),
solTokens = newState.solTokens,
ethTokens = newState.ethTokens
Expand Down Expand Up @@ -186,8 +186,8 @@ class MyCryptoPresenter(
}

override fun onBalancePnlClicked() {
if (pnlInteractor.state.value.isResult()) {
view?.showPnlDetails(pnlInteractor.state.value.toResultOrNull()!!.total.percent)
if (pnlInteractor.pnlState.value.isLoaded()) {
view?.showPnlDetails(pnlInteractor.pnlState.value.toLoadedOrNull()!!.total.percent)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class MyCryptoMapper(
result += visibleTokens.map {
it.mapToCellModel(
isZerosHidden = isZerosHidden,
pnlTokenData = pnlDataState.toResultOrNull()?.findForToken(it.mintAddressB58)
pnlTokenData = pnlDataState.toLoadedOrNull()?.findForToken(it.mintAddressB58)
)
}

Expand All @@ -85,7 +85,7 @@ class MyCryptoMapper(
result += hiddenTokens.map {
it.mapToCellModel(
isZerosHidden = isZerosHidden,
pnlTokenData = pnlDataState.toResultOrNull()?.findForToken(it.mintAddressB58)
pnlTokenData = pnlDataState.toLoadedOrNull()?.findForToken(it.mintAddressB58)
)
}
}
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/org/p2p/wallet/pnl/PnlModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ object PnlModule : InjectionModule {
factory {
val api = get<Retrofit>(named(RETROFIT_QUALIFIER)).create(PnlServiceApi::class.java)
PnlRemoteRepository(
api = api
api = api,
dispatchers = get()
)
} bind PnlRepository::class

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.p2p.core.common.di.AppScope
import org.p2p.core.crypto.Base58String
import org.p2p.wallet.common.feature_toggles.toggles.remote.PnlEnabledFeatureToggle
import org.p2p.wallet.infrastructure.network.provider.TokenKeyProvider
import org.p2p.wallet.pnl.repository.PnlRepository
import org.p2p.wallet.tokenservice.TokenServiceCoordinator
Expand Down Expand Up @@ -43,6 +44,7 @@ class PnlDataObserver(
private val pnlRepository: PnlRepository,
private val tokenKeyProvider: TokenKeyProvider,
private val tokenServiceCoordinator: TokenServiceCoordinator,
private val pnlEnabledFeatureToggle: PnlEnabledFeatureToggle
) {

private companion object {
Expand All @@ -52,14 +54,14 @@ class PnlDataObserver(
private var updaterJob: Job? = null
private val tokenMints = mutableListOf<Base58String>()
private val tokenMintsMutex = Mutex()
private val _state = MutableStateFlow<PnlDataState>(PnlDataState.Idle)
val state = _state.asStateFlow()
private val _pnlState = MutableStateFlow<PnlDataState>(PnlDataState.Idle)
val pnlState = _pnlState.asStateFlow()

fun isStarted(): Boolean = updaterJob != null
fun canBeStarted(): Boolean = updaterJob != null

fun start() {
if (updaterJob != null) return
_state.value = PnlDataState.Loading
if (updaterJob != null || !pnlEnabledFeatureToggle.isFeatureEnabled) return
_pnlState.value = PnlDataState.Loading
launchJob()
}

Expand All @@ -85,14 +87,11 @@ class PnlDataObserver(

while (isActive) {
try {
_state.emit(
PnlDataState.Result(
pnlRepository.getPnlData(tokenKeyProvider.publicKeyBase58, tokenMints)
)
)
val pnlData = pnlRepository.getPnlData(tokenKeyProvider.publicKeyBase58, tokenMints)
_pnlState.emit(PnlDataState.Loaded(pnlData))
} catch (e: Throwable) {
Timber.e(e, "Unable to get pnl data")
_state.emit(
_pnlState.emit(
PnlDataState.Error(e)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import org.p2p.wallet.pnl.models.PnlTokenData
sealed class PnlDataState {
object Idle : PnlDataState()
object Loading : PnlDataState()
data class Result(val data: PnlData) : PnlDataState()
data class Loaded(val data: PnlData) : PnlDataState()
data class Error(val error: Throwable) : PnlDataState()

fun toResultOrNull(): PnlData? = (this as? Result)?.data
fun isResult(): Boolean = this is Result
fun toLoadedOrNull(): PnlData? = (this as? Loaded)?.data
fun isLoaded(): Boolean = this is Loaded

fun findForToken(tokenMint: Base58String): PnlTokenData? = toResultOrNull()?.findForToken(tokenMint)
fun findForToken(tokenMint: Base58String): PnlTokenData? = toLoadedOrNull()?.findForToken(tokenMint)
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package org.p2p.wallet.pnl.repository

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.p2p.core.crypto.Base58String
import org.p2p.core.dispatchers.CoroutineDispatchers
import org.p2p.core.network.gson.GsonProvider
import org.p2p.solanaj.model.types.RpcMapRequest
import org.p2p.wallet.pnl.api.PnlDataTimeSpan
Expand All @@ -12,25 +12,27 @@ import org.p2p.wallet.pnl.models.PnlData

class PnlRemoteRepository(
private val api: PnlServiceApi,
private val dispatchers: CoroutineDispatchers
) : PnlRepository {

override suspend fun getPnlData(
userWallet: Base58String,
tokenMints: List<Base58String>,
timeSpan: PnlDataTimeSpan,
): PnlData = withContext(Dispatchers.IO) {
): PnlData = withContext(dispatchers.io) {
val requestedTokenMints: List<String>? = tokenMints
.map { it.base58Value }
.ifEmpty { null }

val params = buildMap {
put("user_wallet", userWallet.base58Value)
this["user_wallet"] = userWallet.base58Value
// todo: backend decided to use null for a default duration
// currently it's 24 hours, something may change in the future
put("since", timeSpan.sinceEpochSeconds)
val requestedTokenMints: List<String>? = tokenMints
.map { it.base58Value }
.ifEmpty { null }
put("token_mints", requestedTokenMints)
this["since"] = timeSpan.sinceEpochSeconds
this["token_mints"] = requestedTokenMints
}

val rpcRequest = RpcMapRequest("get_pnl", params)
val rpcRequest = RpcMapRequest(method = "get_pnl", params = params)
val response = api.getAccountPnl(rpcRequest)
val gson = GsonProvider()
.withBuilder {
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/org/p2p/wallet/pnl/ui/PnlUiMapper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class PnlUiMapper {

fun mapBalancePnl(pnlDataState: PnlDataState): TextViewCellModel? {
return when (pnlDataState) {
is PnlDataState.Result -> {
is PnlDataState.Loaded -> {
TextViewCellModel.Raw(
TextContainer(R.string.home_pnl_format, pnlDataState.data.total.percent)
)
Expand All @@ -40,7 +40,7 @@ class PnlUiMapper {

fun mapTokenBalancePnl(tokenMint: Base58String, pnlDataState: PnlDataState): TextViewCellModel? {
return when (pnlDataState) {
is PnlDataState.Result -> {
is PnlDataState.Loaded -> {
pnlDataState.findForToken(tokenMint)?.let { pnlTokenData ->
TextViewCellModel.Raw(
TextContainer(R.string.home_pnl_format, pnlTokenData.percent)
Expand Down
Loading