1
1
package io.helikon.subvt.ui.screen.validator.details
2
2
3
- import androidx.compose.foundation.ExperimentalFoundationApi
4
3
import androidx.compose.foundation.Image
5
- import androidx.compose.foundation.LocalOverscrollConfiguration
6
4
import androidx.compose.foundation.background
7
5
import androidx.compose.foundation.isSystemInDarkTheme
8
6
import androidx.compose.foundation.layout.Arrangement
@@ -24,14 +22,13 @@ import androidx.compose.foundation.verticalScroll
24
22
import androidx.compose.material3.Surface
25
23
import androidx.compose.material3.Text
26
24
import androidx.compose.runtime.Composable
27
- import androidx.compose.runtime.CompositionLocalProvider
28
25
import androidx.compose.runtime.DisposableEffect
29
26
import androidx.compose.runtime.getValue
30
27
import androidx.compose.ui.Alignment
31
28
import androidx.compose.ui.Modifier
32
- import androidx.compose.ui.draw.alpha
33
29
import androidx.compose.ui.draw.clipToBounds
34
30
import androidx.compose.ui.graphics.Brush
31
+ import androidx.compose.ui.platform.LocalContext
35
32
import androidx.compose.ui.platform.LocalLifecycleOwner
36
33
import androidx.compose.ui.res.dimensionResource
37
34
import androidx.compose.ui.res.painterResource
@@ -45,7 +42,9 @@ import androidx.lifecycle.LifecycleEventObserver
45
42
import androidx.lifecycle.LifecycleOwner
46
43
import androidx.lifecycle.compose.collectAsStateWithLifecycle
47
44
import io.helikon.subvt.R
45
+ import io.helikon.subvt.data.extension.display
48
46
import io.helikon.subvt.data.extension.inactiveNominationTotal
47
+ import io.helikon.subvt.data.extension.inactiveNominations
49
48
import io.helikon.subvt.data.extension.nominationTotal
50
49
import io.helikon.subvt.data.model.Network
51
50
import io.helikon.subvt.data.model.app.ValidatorDetails
@@ -55,12 +54,19 @@ import io.helikon.subvt.ui.component.AnimatedBackground
55
54
import io.helikon.subvt.ui.modifier.noRippleClickable
56
55
import io.helikon.subvt.ui.modifier.scrollHeader
57
56
import io.helikon.subvt.ui.screen.network.status.view.NetworkSelectorButton
57
+ import io.helikon.subvt.ui.screen.validator.details.view.AccountAgeView
58
58
import io.helikon.subvt.ui.screen.validator.details.view.BalanceView
59
+ import io.helikon.subvt.ui.screen.validator.details.view.HorizontalDataView
60
+ import io.helikon.subvt.ui.screen.validator.details.view.IconsView
59
61
import io.helikon.subvt.ui.screen.validator.details.view.IdenticonView
60
62
import io.helikon.subvt.ui.screen.validator.details.view.IdentityView
63
+ import io.helikon.subvt.ui.screen.validator.details.view.NominatorListView
64
+ import io.helikon.subvt.ui.screen.validator.details.view.OneKVDetailsView
65
+ import io.helikon.subvt.ui.screen.validator.details.view.VerticalDataView
61
66
import io.helikon.subvt.ui.style.Color
62
67
import io.helikon.subvt.ui.style.Font
63
68
import io.helikon.subvt.ui.util.ThemePreviews
69
+ import io.helikon.subvt.util.formatDecimal
64
70
65
71
data class ValidatorDetailsScreenState (
66
72
val serviceStatus : RPCSubscriptionServiceStatus ,
@@ -107,7 +113,6 @@ fun ValidatorDetailsScreen(
107
113
)
108
114
}
109
115
110
- @OptIn(ExperimentalFoundationApi ::class )
111
116
@Composable
112
117
fun ValidatorDetailsScreenContent (
113
118
modifier : Modifier = Modifier ,
@@ -313,6 +318,7 @@ fun ValidatorDetailsScreenContent(
313
318
.height(dimensionResource(id = R .dimen.validator_details_identicon_height)),
314
319
)
315
320
IdentityView (validator = state.validator)
321
+ // Spacer(modifier = Modifier.height(dimensionResource(id = R.dimen.common_panel_padding) / 2))
316
322
BalanceView (
317
323
titleResourceId = R .string.validator_details_nomination_total,
318
324
network = state.network,
@@ -323,28 +329,142 @@ fun ValidatorDetailsScreenContent(
323
329
network = state.network,
324
330
balance = state.validator?.selfStake?.activeAmount,
325
331
)
326
- BalanceView (
327
- titleResourceId = R .string.validator_details_active_stake,
328
- network = state.network,
329
- balance = state.validator?.validatorStake?.totalStake,
330
- )
331
- BalanceView (
332
+ state.validator?.let { validator ->
333
+ validator.validatorStake?.let { validatorStake ->
334
+ NominatorListView (
335
+ titleResourceId = R .string.validator_details_active_stake,
336
+ network = state.network,
337
+ count = validatorStake.nominators.size,
338
+ total = validatorStake.totalStake,
339
+ nominations =
340
+ validatorStake.nominators.map {
341
+ Triple (it.account.address, false , it.stake)
342
+ }.sortedByDescending {
343
+ it.third
344
+ },
345
+ )
346
+ }
347
+ }
348
+ NominatorListView (
332
349
titleResourceId = R .string.validator_details_inactive_nominations,
333
350
network = state.network,
334
- balance = state.validator?.inactiveNominationTotal(),
351
+ count = state.validator?.inactiveNominations()?.size,
352
+ total = state.validator?.inactiveNominationTotal(),
353
+ nominations =
354
+ state.validator?.inactiveNominations()?.map {
355
+ Triple (it.stashAccount.address, false , it.stake.activeAmount)
356
+ }?.sortedByDescending {
357
+ it.third
358
+ },
359
+ )
360
+ state.validator?.account?.discoveredAt?.let {
361
+ AccountAgeView (discoveredAt = it)
362
+ }
363
+ HorizontalDataView (
364
+ titleResourceId = R .string.validator_details_offline_faults,
365
+ text = state.validator?.offlineOffenceCount?.toString() ? : " -" ,
366
+ displayExclamation = (state.validator?.offlineOffenceCount ? : 0 ) > 0 ,
367
+ )
368
+ VerticalDataView (
369
+ modifier = Modifier .fillMaxWidth(),
370
+ titleResourceId = R .string.validator_details_reward_destination,
371
+ text =
372
+ state.validator?.rewardDestination?.display(
373
+ context = LocalContext .current,
374
+ prefix = state.network?.ss58Prefix?.toShort() ? : 0 ,
375
+ ) ? : " -" ,
376
+ )
377
+ Row (
378
+ modifier = Modifier .fillMaxWidth(),
379
+ horizontalArrangement = Arrangement .spacedBy(dimensionResource(id = R .dimen.common_panel_padding)),
380
+ ) {
381
+ VerticalDataView (
382
+ modifier = Modifier .weight(1.0f ),
383
+ titleResourceId = R .string.validator_details_commission,
384
+ text =
385
+ String .format(
386
+ stringResource(id = R .string.percentage),
387
+ formatDecimal(
388
+ number =
389
+ (
390
+ state.validator?.preferences?.commissionPerBillion
391
+ ? : 0
392
+ ).toBigInteger(),
393
+ tokenDecimalCount = 7 ,
394
+ formatDecimalCount = 2 ,
395
+ ),
396
+ ),
397
+ )
398
+ VerticalDataView (
399
+ modifier = Modifier .weight(1.0f ),
400
+ titleResourceId = R .string.validator_details_apr,
401
+ text =
402
+ String .format(
403
+ stringResource(id = R .string.percentage),
404
+ formatDecimal(
405
+ number = (state.validator?.returnRatePerBillion ? : 0 ).toBigInteger(),
406
+ tokenDecimalCount = 7 ,
407
+ formatDecimalCount = 2 ,
408
+ ),
409
+ ),
410
+ )
411
+ }
412
+ Row (
413
+ modifier = Modifier .fillMaxWidth(),
414
+ horizontalArrangement = Arrangement .spacedBy(dimensionResource(id = R .dimen.common_panel_padding)),
415
+ ) {
416
+ VerticalDataView (
417
+ modifier = Modifier .weight(1.0f ),
418
+ titleResourceId = R .string.validator_details_era_blocks,
419
+ text = state.validator?.blocksAuthored?.toString() ? : " -" ,
420
+ )
421
+ VerticalDataView (
422
+ modifier = Modifier .weight(1.0f ),
423
+ titleResourceId = R .string.validator_details_era_points,
424
+ text = state.validator?.rewardPoints?.toString() ? : " -" ,
425
+ )
426
+ }
427
+ state.validator?.let { validator ->
428
+ if (validator.onekvCandidateRecordId != null ) {
429
+ OneKVDetailsView (
430
+ modifier = Modifier .fillMaxWidth(),
431
+ validator = validator,
432
+ )
433
+ }
434
+ }
435
+ Spacer (
436
+ modifier =
437
+ Modifier
438
+ .navigationBarsPadding()
439
+ .padding(
440
+ 0 .dp,
441
+ 0 .dp,
442
+ 0 .dp,
443
+ dimensionResource(id = R .dimen.common_scrollable_content_margin_bottom),
444
+ ),
335
445
)
336
- Spacer (modifier = Modifier .navigationBarsPadding())
337
446
}
338
-
339
- CompositionLocalProvider (
340
- LocalOverscrollConfiguration provides null ,
341
- ) {
447
+ state.validator?.let {
448
+ IconsView (
449
+ Modifier
450
+ .padding(
451
+ 0 .dp,
452
+ 0 .dp,
453
+ 0 .dp,
454
+ dimensionResource(id = R .dimen.common_padding),
455
+ )
456
+ .navigationBarsPadding()
457
+ .fillMaxWidth()
458
+ .zIndex(8.0f )
459
+ .align(Alignment .BottomCenter ),
460
+ validator = it,
461
+ )
342
462
}
343
463
Box (
344
464
modifier =
345
465
Modifier
346
466
.fillMaxWidth()
347
- .height(104 .dp )
467
+ .height(dimensionResource(id = R .dimen.common_bottom_gradient_height) )
348
468
.zIndex(7.0f )
349
469
.align(Alignment .BottomCenter )
350
470
.background(
0 commit comments