Skip to content

Commit

Permalink
Validator details progress: panels and icons completed.
Browse files Browse the repository at this point in the history
  • Loading branch information
kukabi committed Feb 12, 2024
1 parent 764a698 commit c59d6ba
Show file tree
Hide file tree
Showing 41 changed files with 740 additions and 26 deletions.
2 changes: 1 addition & 1 deletion subvt/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ dependencies {
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3:1.2.0-rc01")
implementation("androidx.compose.material3:material3:1.2.0")
implementation("androidx.navigation:navigation-compose:2.7.6")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0")
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.7.0")
Expand Down
2 changes: 2 additions & 0 deletions subvt/src/main/java/io/helikon/subvt/app/SubVTApp.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.helikon.subvt.app

import android.app.Application
import com.google.android.filament.utils.Utils
import dagger.hilt.android.HiltAndroidApp
import io.helikon.subvt.BuildConfig
import timber.log.Timber
Expand All @@ -9,6 +10,7 @@ import timber.log.Timber
class SubVTApp : Application() {
override fun onCreate() {
super.onCreate()
Utils.init()
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package io.helikon.subvt.data.extension

import android.content.Context
import io.helikon.subvt.R
import io.helikon.subvt.data.model.substrate.RewardDestination
import io.helikon.subvt.data.model.substrate.RewardDestinationType
import io.helikon.subvt.util.truncateAddress

fun RewardDestination.display(
context: Context,
prefix: Short,
): String {
return when (this.destinationType) {
RewardDestinationType.ACCOUNT ->
if (this.destination != null) {
truncateAddress(this.destination!!.getAddress(prefix))
} else {
"-"
}

RewardDestinationType.CONTROLLER -> context.resources.getString(R.string.reward_destination_controller)
RewardDestinationType.NONE -> context.resources.getString(R.string.reward_destination_none)
RewardDestinationType.STAKED -> context.resources.getString(R.string.reward_destination_staked)
RewardDestinationType.STASH -> context.resources.getString(R.string.reward_destination_stash)
}
}
2 changes: 1 addition & 1 deletion subvt/src/main/java/io/helikon/subvt/ui/screen/main/Tab.kt
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ fun TabLayout(
modifier =
Modifier
.fillMaxWidth()
.height(104.dp)
.height(dimensionResource(id = R.dimen.common_bottom_gradient_height))
.zIndex(12f)
.align(Alignment.BottomCenter)
.background(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package io.helikon.subvt.ui.screen.validator.details

import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.LocalOverscrollConfiguration
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
Expand All @@ -24,14 +22,13 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
Expand All @@ -45,7 +42,9 @@ import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import io.helikon.subvt.R
import io.helikon.subvt.data.extension.display
import io.helikon.subvt.data.extension.inactiveNominationTotal
import io.helikon.subvt.data.extension.inactiveNominations
import io.helikon.subvt.data.extension.nominationTotal
import io.helikon.subvt.data.model.Network
import io.helikon.subvt.data.model.app.ValidatorDetails
Expand All @@ -55,12 +54,19 @@ import io.helikon.subvt.ui.component.AnimatedBackground
import io.helikon.subvt.ui.modifier.noRippleClickable
import io.helikon.subvt.ui.modifier.scrollHeader
import io.helikon.subvt.ui.screen.network.status.view.NetworkSelectorButton
import io.helikon.subvt.ui.screen.validator.details.view.AccountAgeView
import io.helikon.subvt.ui.screen.validator.details.view.BalanceView
import io.helikon.subvt.ui.screen.validator.details.view.HorizontalDataView
import io.helikon.subvt.ui.screen.validator.details.view.IconsView
import io.helikon.subvt.ui.screen.validator.details.view.IdenticonView
import io.helikon.subvt.ui.screen.validator.details.view.IdentityView
import io.helikon.subvt.ui.screen.validator.details.view.NominatorListView
import io.helikon.subvt.ui.screen.validator.details.view.OneKVDetailsView
import io.helikon.subvt.ui.screen.validator.details.view.VerticalDataView
import io.helikon.subvt.ui.style.Color
import io.helikon.subvt.ui.style.Font
import io.helikon.subvt.ui.util.ThemePreviews
import io.helikon.subvt.util.formatDecimal

data class ValidatorDetailsScreenState(
val serviceStatus: RPCSubscriptionServiceStatus,
Expand Down Expand Up @@ -107,7 +113,6 @@ fun ValidatorDetailsScreen(
)
}

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ValidatorDetailsScreenContent(
modifier: Modifier = Modifier,
Expand Down Expand Up @@ -313,6 +318,7 @@ fun ValidatorDetailsScreenContent(
.height(dimensionResource(id = R.dimen.validator_details_identicon_height)),
)
IdentityView(validator = state.validator)
// Spacer(modifier = Modifier.height(dimensionResource(id = R.dimen.common_panel_padding) / 2))
BalanceView(
titleResourceId = R.string.validator_details_nomination_total,
network = state.network,
Expand All @@ -323,28 +329,142 @@ fun ValidatorDetailsScreenContent(
network = state.network,
balance = state.validator?.selfStake?.activeAmount,
)
BalanceView(
titleResourceId = R.string.validator_details_active_stake,
network = state.network,
balance = state.validator?.validatorStake?.totalStake,
)
BalanceView(
state.validator?.let { validator ->
validator.validatorStake?.let { validatorStake ->
NominatorListView(
titleResourceId = R.string.validator_details_active_stake,
network = state.network,
count = validatorStake.nominators.size,
total = validatorStake.totalStake,
nominations =
validatorStake.nominators.map {
Triple(it.account.address, false, it.stake)
}.sortedByDescending {
it.third
},
)
}
}
NominatorListView(
titleResourceId = R.string.validator_details_inactive_nominations,
network = state.network,
balance = state.validator?.inactiveNominationTotal(),
count = state.validator?.inactiveNominations()?.size,
total = state.validator?.inactiveNominationTotal(),
nominations =
state.validator?.inactiveNominations()?.map {
Triple(it.stashAccount.address, false, it.stake.activeAmount)
}?.sortedByDescending {
it.third
},
)
state.validator?.account?.discoveredAt?.let {
AccountAgeView(discoveredAt = it)
}
HorizontalDataView(
titleResourceId = R.string.validator_details_offline_faults,
text = state.validator?.offlineOffenceCount?.toString() ?: "-",
displayExclamation = (state.validator?.offlineOffenceCount ?: 0) > 0,
)
VerticalDataView(
modifier = Modifier.fillMaxWidth(),
titleResourceId = R.string.validator_details_reward_destination,
text =
state.validator?.rewardDestination?.display(
context = LocalContext.current,
prefix = state.network?.ss58Prefix?.toShort() ?: 0,
) ?: "-",
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.common_panel_padding)),
) {
VerticalDataView(
modifier = Modifier.weight(1.0f),
titleResourceId = R.string.validator_details_commission,
text =
String.format(
stringResource(id = R.string.percentage),
formatDecimal(
number =
(
state.validator?.preferences?.commissionPerBillion
?: 0
).toBigInteger(),
tokenDecimalCount = 7,
formatDecimalCount = 2,
),
),
)
VerticalDataView(
modifier = Modifier.weight(1.0f),
titleResourceId = R.string.validator_details_apr,
text =
String.format(
stringResource(id = R.string.percentage),
formatDecimal(
number = (state.validator?.returnRatePerBillion ?: 0).toBigInteger(),
tokenDecimalCount = 7,
formatDecimalCount = 2,
),
),
)
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.common_panel_padding)),
) {
VerticalDataView(
modifier = Modifier.weight(1.0f),
titleResourceId = R.string.validator_details_era_blocks,
text = state.validator?.blocksAuthored?.toString() ?: "-",
)
VerticalDataView(
modifier = Modifier.weight(1.0f),
titleResourceId = R.string.validator_details_era_points,
text = state.validator?.rewardPoints?.toString() ?: "-",
)
}
state.validator?.let { validator ->
if (validator.onekvCandidateRecordId != null) {
OneKVDetailsView(
modifier = Modifier.fillMaxWidth(),
validator = validator,
)
}
}
Spacer(
modifier =
Modifier
.navigationBarsPadding()
.padding(
0.dp,
0.dp,
0.dp,
dimensionResource(id = R.dimen.common_scrollable_content_margin_bottom),
),
)
Spacer(modifier = Modifier.navigationBarsPadding())
}

CompositionLocalProvider(
LocalOverscrollConfiguration provides null,
) {
state.validator?.let {
IconsView(
Modifier
.padding(
0.dp,
0.dp,
0.dp,
dimensionResource(id = R.dimen.common_padding),
)
.navigationBarsPadding()
.fillMaxWidth()
.zIndex(8.0f)
.align(Alignment.BottomCenter),
validator = it,
)
}
Box(
modifier =
Modifier
.fillMaxWidth()
.height(104.dp)
.height(dimensionResource(id = R.dimen.common_bottom_gradient_height))
.zIndex(7.0f)
.align(Alignment.BottomCenter)
.background(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package io.helikon.subvt.ui.screen.validator.details.view

import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.sp
import io.helikon.subvt.R
import io.helikon.subvt.ui.style.Color
import io.helikon.subvt.ui.style.Font
import java.time.Instant
import java.time.LocalDate
import java.time.Period
import java.time.ZoneId

@Composable
fun AccountAgeView(
modifier: Modifier = Modifier,
isDark: Boolean = isSystemInDarkTheme(),
discoveredAt: Long,
) {
val period =
Period.between(
Instant.ofEpochMilli(discoveredAt).atZone(ZoneId.systemDefault()).toLocalDate(),
LocalDate.now(),
)
val periodText =
if (period.years > 0) {
String.format(
stringResource(R.string.validator_details_period_years),
period.years,
)
} else {
""
} +
if (period.months > 0) {
" " +
String.format(
stringResource(R.string.validator_details_period_months),
period.months,
)
} else {
""
} +
if (period.days > 0) {
val days = period.days % 7
val weeks = period.days / 7
var weeksText = ""
if (weeks > 0) {
weeksText =
" " +
String.format(
stringResource(R.string.validator_details_period_weeks),
weeks,
)
}
if (days > 0) {
"$weeksText " +
String.format(
stringResource(R.string.validator_details_period_days),
days,
)
} else {
weeksText
}
} else {
""
}
Column(
modifier =
modifier
.background(
color = Color.panelBg(isDark),
shape = RoundedCornerShape(dimensionResource(id = R.dimen.common_panel_border_radius)),
)
.padding(dimensionResource(id = R.dimen.common_padding))
.fillMaxWidth(),
) {
Text(
text = stringResource(id = R.string.validator_details_account_age),
style = Font.light(12.sp),
color = Color.text(isDark),
)
Spacer(
modifier = Modifier.height(dimensionResource(id = R.dimen.common_data_panel_content_margin_top)),
)
Text(
text = periodText.trim(),
style = Font.semiBold(20.sp),
color = Color.text(isDark),
)
}
}
Loading

0 comments on commit c59d6ba

Please sign in to comment.