Fix Polkadot staking, add USDT bridge, update dashboard card

- Restore staking module to Nova upstream (fix Polkadot ACTIVE/shimmer)
- Add USDT(DOT) <-> USDT(HEZ) bridge option to Buy/Sell screen
- Dashboard card: add telegram_welcome image, info text, Non-Citizen role
- Add non-citizen info text to all 12 locale files
- Simplify ComputationalCache (remove stale scope recreation)
This commit is contained in:
2026-02-18 02:43:53 +03:00
parent 9c7bb7c6e9
commit 14519d7818
39 changed files with 458 additions and 315 deletions
@@ -22,7 +22,7 @@ class PezkuwiDashboardRepository(
val trustScore = queryTrustScore(chainId, accountId)
return PezkuwiDashboardData(
roles = roles,
roles = roles.ifEmpty { listOf("Non-Citizen") },
trustScore = trustScore
)
}
@@ -75,7 +75,8 @@ class RealBuySellSelectorMixin(
private suspend fun openAllAssetsSelector() = BuySellSelectorMixin.SelectorPayload(
buyItem(enabled = true) { router.openBuyFlow() },
sellItem(enabled = buySellRestrictionCheckMixin.isAllowed()) { router.openSellFlow() },
bridgeItem(enabled = true) { router.openBridgeFlow() }
bridgeItem(enabled = true) { router.openBridgeFlow() },
bridgeUsdtItem(enabled = true) { router.openBridgeFlow() }
)
private suspend fun openSpecifiedAssetSelector(selectorType: SelectorType.Asset): BuySellSelectorMixin.SelectorPayload? {
@@ -125,6 +126,16 @@ class RealBuySellSelectorMixin(
)
}
private fun bridgeUsdtItem(enabled: Boolean, action: () -> Unit): ListSelectorMixin.Item {
return ListSelectorMixin.Item(
R.drawable.ic_bridge,
if (enabled) R.color.icon_primary else R.color.icon_inactive,
R.string.wallet_asset_bridge_usdt,
if (enabled) R.color.text_primary else R.color.button_text_inactive,
action
)
}
private fun sellErrorAction(): () -> Unit = {
coroutineScope.launch {
buySellRestrictionCheckMixin.checkRestrictionAndDo {
@@ -18,13 +18,22 @@ class BridgeFragment : BaseFragment<BridgeViewModel, FragmentBridgeBinding>() {
override fun initViews() {
binder.bridgeToolbar.setHomeButtonListener { viewModel.backClicked() }
// Direction toggle
binder.bridgeDirectionDotToHez.setOnClickListener {
viewModel.setDirection(BridgeDirection.DOT_TO_HEZ)
// Pair selector
binder.bridgePairDotHez.setOnClickListener {
viewModel.setPair(BridgePair.DOT_HEZ)
}
binder.bridgeDirectionHezToDot.setOnClickListener {
viewModel.setDirection(BridgeDirection.HEZ_TO_DOT)
binder.bridgePairUsdt.setOnClickListener {
viewModel.setPair(BridgePair.USDT)
}
// Direction toggle
binder.bridgeDirectionLeft.setOnClickListener {
viewModel.setDirectionLeft()
}
binder.bridgeDirectionRight.setOnClickListener {
viewModel.setDirectionRight()
}
// Amount input
@@ -54,6 +63,10 @@ class BridgeFragment : BaseFragment<BridgeViewModel, FragmentBridgeBinding>() {
}
override fun subscribe(viewModel: BridgeViewModel) {
viewModel.pair.observe { pair ->
updatePairUI(pair)
}
viewModel.direction.observe { direction ->
updateDirectionUI(direction)
}
@@ -74,11 +87,11 @@ class BridgeFragment : BaseFragment<BridgeViewModel, FragmentBridgeBinding>() {
binder.bridgeSwapButton.setState(state)
}
viewModel.showHezToDotWarning.observe { show ->
viewModel.showWarning.observe { show ->
binder.bridgeHezToDotWarning.visibility = if (show) View.VISIBLE else View.GONE
}
viewModel.hezToDotBlocked.observe { blocked ->
viewModel.warningBlocked.observe { blocked ->
if (blocked) {
binder.bridgeHezToDotWarning.setBackgroundColor(resources.getColor(R.color.error_block_background, null))
} else {
@@ -86,40 +99,82 @@ class BridgeFragment : BaseFragment<BridgeViewModel, FragmentBridgeBinding>() {
}
}
viewModel.blockReason.observe { reason ->
if (reason.isNotEmpty()) {
binder.bridgeHezToDotWarning.text = reason
} else {
binder.bridgeHezToDotWarning.text = getString(R.string.bridge_hez_to_dot_warning)
viewModel.warningText.observe { text ->
if (text.isNotEmpty()) {
binder.bridgeHezToDotWarning.text = text
}
}
}
private fun updatePairUI(pair: BridgePair) {
when (pair) {
BridgePair.DOT_HEZ -> {
binder.bridgePairDotHez.setBackgroundResource(R.drawable.bg_button_primary)
binder.bridgePairDotHez.setTextColor(resources.getColor(R.color.text_primary, null))
binder.bridgePairUsdt.background = null
binder.bridgePairUsdt.setTextColor(resources.getColor(R.color.text_secondary, null))
}
BridgePair.USDT -> {
binder.bridgePairUsdt.setBackgroundResource(R.drawable.bg_button_primary)
binder.bridgePairUsdt.setTextColor(resources.getColor(R.color.text_primary, null))
binder.bridgePairDotHez.background = null
binder.bridgePairDotHez.setTextColor(resources.getColor(R.color.text_secondary, null))
}
}
}
private fun updateDirectionUI(direction: BridgeDirection) {
val isLeft = direction == BridgeDirection.DOT_TO_HEZ || direction == BridgeDirection.USDT_TO_WUSDT
if (isLeft) {
binder.bridgeDirectionLeft.setBackgroundResource(R.drawable.bg_button_primary)
binder.bridgeDirectionLeft.setTextColor(resources.getColor(R.color.text_primary, null))
binder.bridgeDirectionRight.background = null
binder.bridgeDirectionRight.setTextColor(resources.getColor(R.color.text_secondary, null))
} else {
binder.bridgeDirectionRight.setBackgroundResource(R.drawable.bg_button_primary)
binder.bridgeDirectionRight.setTextColor(resources.getColor(R.color.text_primary, null))
binder.bridgeDirectionLeft.background = null
binder.bridgeDirectionLeft.setTextColor(resources.getColor(R.color.text_secondary, null))
}
when (direction) {
BridgeDirection.DOT_TO_HEZ -> {
binder.bridgeDirectionDotToHez.setBackgroundResource(R.drawable.bg_button_primary)
binder.bridgeDirectionDotToHez.setTextColor(resources.getColor(R.color.text_primary, null))
binder.bridgeDirectionHezToDot.background = null
binder.bridgeDirectionHezToDot.setTextColor(resources.getColor(R.color.text_secondary, null))
binder.bridgeDirectionLeft.text = "DOT → HEZ"
binder.bridgeDirectionRight.text = "HEZ → DOT"
binder.bridgeFromToken.text = "DOT"
binder.bridgeToToken.text = "HEZ"
}
BridgeDirection.HEZ_TO_DOT -> {
binder.bridgeDirectionHezToDot.setBackgroundResource(R.drawable.bg_button_primary)
binder.bridgeDirectionHezToDot.setTextColor(resources.getColor(R.color.text_primary, null))
binder.bridgeDirectionDotToHez.background = null
binder.bridgeDirectionDotToHez.setTextColor(resources.getColor(R.color.text_secondary, null))
binder.bridgeDirectionLeft.text = "DOT → HEZ"
binder.bridgeDirectionRight.text = "HEZ → DOT"
binder.bridgeFromToken.text = "HEZ"
binder.bridgeToToken.text = "DOT"
}
BridgeDirection.USDT_TO_WUSDT -> {
binder.bridgeDirectionLeft.text = "USDT(Pol) → USDT(Pez)"
binder.bridgeDirectionRight.text = "USDT(Pez) → USDT(Pol)"
binder.bridgeFromToken.text = "USDT"
binder.bridgeToToken.text = "USDT"
}
BridgeDirection.WUSDT_TO_USDT -> {
binder.bridgeDirectionLeft.text = "USDT(Pol) → USDT(Pez)"
binder.bridgeDirectionRight.text = "USDT(Pez) → USDT(Pol)"
binder.bridgeFromToken.text = "USDT"
binder.bridgeToToken.text = "USDT"
}
}
}
}
enum class BridgePair {
DOT_HEZ,
USDT
}
enum class BridgeDirection {
DOT_TO_HEZ,
HEZ_TO_DOT
HEZ_TO_DOT,
USDT_TO_WUSDT,
WUSDT_TO_USDT
}
@@ -28,34 +28,30 @@ class BridgeViewModel(
) : BaseViewModel() {
companion object {
// Bridge wallet account ID (derived from seed, same on all chains)
// Address: 5C5CW7xDmiXtCgfUCbKFF4ViJuCJJQpDZqWQ1mSTjehGzE3p (generic format)
private const val BRIDGE_ADDRESS_GENERIC = "5C5CW7xDmiXtCgfUCbKFF4ViJuCJJQpDZqWQ1mSTjehGzE3p"
// Chain IDs
val POLKADOT_ASSET_HUB_ID = ChainGeneses.POLKADOT_ASSET_HUB
val PEZKUWI_ASSET_HUB_ID = ChainGeneses.PEZKUWI_ASSET_HUB
// Utility asset ID (native token)
const val UTILITY_ASSET_ID = 0
// Fallback rate: 1 DOT = 3 HEZ (only if CoinGecko unavailable)
// USDT asset IDs in chain config
const val POLKADOT_USDT_ASSET_ID = 1 // assetId in chains.json for Polkadot AH
const val PEZKUWI_USDT_ASSET_ID = 1000 // assetId in chains.json for Pezkuwi AH
const val FALLBACK_RATE = 3.0
// Fee: 0.1%
const val FEE_PERCENT = 0.001
// Minimums
const val MIN_DOT = 0.1
const val MIN_HEZ = 0.3
const val MIN_USDT = 1.0
// CoinGecko API
const val COINGECKO_API = "https://api.coingecko.com/api/v3/simple/price?ids=polkadot,hezkurd&vs_currencies=usd"
// Bridge Status API
const val BRIDGE_STATUS_API = "http://217.77.6.126:3030/status"
}
private val _pair = MutableLiveData(BridgePair.DOT_HEZ)
val pair: LiveData<BridgePair> = _pair
private val _direction = MutableLiveData(BridgeDirection.DOT_TO_HEZ)
val direction: LiveData<BridgeDirection> = _direction
@@ -71,122 +67,64 @@ class BridgeViewModel(
private val _buttonState = MutableLiveData<ButtonState>()
val buttonState: LiveData<ButtonState> = _buttonState
private val _showHezToDotWarning = MutableLiveData(false)
val showHezToDotWarning: LiveData<Boolean> = _showHezToDotWarning
private val _showWarning = MutableLiveData(false)
val showWarning: LiveData<Boolean> = _showWarning
private val _hezToDotBlocked = MutableLiveData(false)
val hezToDotBlocked: LiveData<Boolean> = _hezToDotBlocked
private val _warningBlocked = MutableLiveData(false)
val warningBlocked: LiveData<Boolean> = _warningBlocked
private val _blockReason = MutableLiveData<String>()
val blockReason: LiveData<String> = _blockReason
private val _rateSource = MutableLiveData<String>()
val rateSource: LiveData<String> = _rateSource
private val _warningText = MutableLiveData<String>()
val warningText: LiveData<String> = _warningText
private var currentAmount: Double = 0.0
private var dotToHezRate: Double = FALLBACK_RATE
private var isUsingFallback: Boolean = true
private var isHezToDotActive: Boolean = false
private var isWusdtToUsdtActive: Boolean = false
init {
fetchExchangeRate()
fetchBridgeStatus()
}
private fun fetchExchangeRate() {
launch {
try {
val (rate, source) = withContext(Dispatchers.IO) {
fetchRateFromCoinGecko()
}
dotToHezRate = rate
isUsingFallback = source == "fallback"
_rateSource.postValue(source)
updateUI()
calculateOutput()
} catch (e: Exception) {
// Use fallback
dotToHezRate = FALLBACK_RATE
isUsingFallback = true
_rateSource.postValue("fallback")
updateUI()
fun setPair(newPair: BridgePair) {
if (_pair.value != newPair) {
_pair.value = newPair
// Reset direction to left (forward) when switching pair
_direction.value = when (newPair) {
BridgePair.DOT_HEZ -> BridgeDirection.DOT_TO_HEZ
BridgePair.USDT -> BridgeDirection.USDT_TO_WUSDT
}
}
}
private fun fetchRateFromCoinGecko(): Pair<Double, String> {
return try {
val response = URL(COINGECKO_API).readText()
val json = JSONObject(response)
val dotPrice = json.optJSONObject("polkadot")?.optDouble("usd", 0.0) ?: 0.0
val hezPrice = json.optJSONObject("hezkurd")?.optDouble("usd", 0.0) ?: 0.0
when {
dotPrice > 0 && hezPrice > 0 -> {
// Both prices available - calculate real rate
val rate = dotPrice / hezPrice
Pair(rate, "coingecko")
}
dotPrice > 0 -> {
// Only DOT price - use fallback for HEZ (1 DOT = 3 HEZ means HEZ = DOT/3)
Pair(FALLBACK_RATE, "coingecko+fallback")
}
else -> {
// No prices - use pure fallback
Pair(FALLBACK_RATE, "fallback")
}
}
} catch (e: Exception) {
Pair(FALLBACK_RATE, "fallback")
}
}
private fun fetchBridgeStatus() {
launch {
try {
val active = withContext(Dispatchers.IO) {
fetchHezToDotStatus()
}
isHezToDotActive = active
updateHezToDotState()
} catch (e: Exception) {
// If API unavailable, assume not active for safety
isHezToDotActive = false
updateHezToDotState()
}
}
}
private fun fetchHezToDotStatus(): Boolean {
return try {
val response = URL(BRIDGE_STATUS_API).readText()
val json = JSONObject(response)
json.optBoolean("hezToDotActive", false)
} catch (e: Exception) {
false
}
}
private fun updateHezToDotState() {
val dir = _direction.value ?: return
if (dir == BridgeDirection.HEZ_TO_DOT && !isHezToDotActive) {
_hezToDotBlocked.postValue(true)
_blockReason.postValue(resourceManager.getString(R.string.bridge_hez_to_dot_blocked))
} else {
_hezToDotBlocked.postValue(false)
_blockReason.postValue("")
}
updateButtonState()
}
fun setDirection(newDirection: BridgeDirection) {
if (_direction.value != newDirection) {
_direction.value = newDirection
_showHezToDotWarning.value = newDirection == BridgeDirection.HEZ_TO_DOT
updateHezToDotState()
updateUI()
calculateOutput()
updateWarningState()
}
}
fun setDirectionLeft() {
val newDir = when (_pair.value) {
BridgePair.DOT_HEZ -> BridgeDirection.DOT_TO_HEZ
BridgePair.USDT -> BridgeDirection.USDT_TO_WUSDT
null -> BridgeDirection.DOT_TO_HEZ
}
if (_direction.value != newDir) {
_direction.value = newDir
updateUI()
calculateOutput()
updateWarningState()
}
}
fun setDirectionRight() {
val newDir = when (_pair.value) {
BridgePair.DOT_HEZ -> BridgeDirection.HEZ_TO_DOT
BridgePair.USDT -> BridgeDirection.WUSDT_TO_USDT
null -> BridgeDirection.HEZ_TO_DOT
}
if (_direction.value != newDir) {
_direction.value = newDir
updateUI()
calculateOutput()
updateWarningState()
}
}
@@ -201,26 +139,27 @@ class BridgeViewModel(
if (currentAmount <= 0) return
launch {
// Determine which chain and asset to send from based on direction
val chainId = when (dir) {
BridgeDirection.DOT_TO_HEZ -> POLKADOT_ASSET_HUB_ID // Send DOT from Polkadot Asset Hub
BridgeDirection.HEZ_TO_DOT -> PEZKUWI_ASSET_HUB_ID // Send HEZ from Pezkuwi Asset Hub
BridgeDirection.DOT_TO_HEZ -> POLKADOT_ASSET_HUB_ID
BridgeDirection.HEZ_TO_DOT -> PEZKUWI_ASSET_HUB_ID
BridgeDirection.USDT_TO_WUSDT -> POLKADOT_ASSET_HUB_ID
BridgeDirection.WUSDT_TO_USDT -> PEZKUWI_ASSET_HUB_ID
}
// Get the chain to convert address to correct format
val chain = chainRegistry.getChain(chainId)
val assetId = when (dir) {
BridgeDirection.DOT_TO_HEZ -> UTILITY_ASSET_ID
BridgeDirection.HEZ_TO_DOT -> UTILITY_ASSET_ID
BridgeDirection.USDT_TO_WUSDT -> POLKADOT_USDT_ASSET_ID
BridgeDirection.WUSDT_TO_USDT -> PEZKUWI_USDT_ASSET_ID
}
// Convert generic address to chain-specific format
val chain = chainRegistry.getChain(chainId)
val accountId = BRIDGE_ADDRESS_GENERIC.toAccountId()
val bridgeAddress = chain.addressOf(accountId)
// Create asset payload (utility asset = native token)
val assetPayload = AssetPayload(chainId, UTILITY_ASSET_ID)
// Create send payload specifying the origin asset
val assetPayload = AssetPayload(chainId, assetId)
val sendPayload = SendPayload.SpecifiedOrigin(assetPayload)
// Open send screen with pre-filled bridge address AND amount
router.openSend(sendPayload, bridgeAddress, currentAmount)
}
}
@@ -229,15 +168,108 @@ class BridgeViewModel(
router.back()
}
private fun fetchExchangeRate() {
launch {
try {
val (rate, _) = withContext(Dispatchers.IO) {
fetchRateFromCoinGecko()
}
dotToHezRate = rate
updateUI()
calculateOutput()
} catch (e: Exception) {
dotToHezRate = FALLBACK_RATE
updateUI()
}
}
}
private fun fetchRateFromCoinGecko(): Pair<Double, String> {
return try {
val response = URL(COINGECKO_API).readText()
val json = JSONObject(response)
val dotPrice = json.optJSONObject("polkadot")?.optDouble("usd", 0.0) ?: 0.0
val hezPrice = json.optJSONObject("hezkurd")?.optDouble("usd", 0.0) ?: 0.0
when {
dotPrice > 0 && hezPrice > 0 -> Pair(dotPrice / hezPrice, "coingecko")
else -> Pair(FALLBACK_RATE, "fallback")
}
} catch (e: Exception) {
Pair(FALLBACK_RATE, "fallback")
}
}
private fun fetchBridgeStatus() {
launch {
try {
val (hezToDot, wusdtToUsdt) = withContext(Dispatchers.IO) {
fetchStatusFromApi()
}
isHezToDotActive = hezToDot
isWusdtToUsdtActive = wusdtToUsdt
updateWarningState()
} catch (e: Exception) {
isHezToDotActive = false
isWusdtToUsdtActive = false
updateWarningState()
}
}
}
private fun fetchStatusFromApi(): Pair<Boolean, Boolean> {
return try {
val response = URL(BRIDGE_STATUS_API).readText()
val json = JSONObject(response)
val hezToDot = json.optBoolean("hezToDotActive", false)
val wusdtToUsdt = json.optBoolean("wusdtToUsdtActive", false)
Pair(hezToDot, wusdtToUsdt)
} catch (e: Exception) {
Pair(false, false)
}
}
private fun updateWarningState() {
val dir = _direction.value ?: return
when (dir) {
BridgeDirection.HEZ_TO_DOT -> {
_showWarning.postValue(true)
if (!isHezToDotActive) {
_warningBlocked.postValue(true)
_warningText.postValue(resourceManager.getString(R.string.bridge_hez_to_dot_blocked))
} else {
_warningBlocked.postValue(false)
_warningText.postValue(resourceManager.getString(R.string.bridge_hez_to_dot_warning))
}
}
BridgeDirection.WUSDT_TO_USDT -> {
_showWarning.postValue(true)
if (!isWusdtToUsdtActive) {
_warningBlocked.postValue(true)
_warningText.postValue(resourceManager.getString(R.string.bridge_wusdt_to_usdt_blocked))
} else {
_warningBlocked.postValue(false)
_warningText.postValue("")
_showWarning.postValue(false)
}
}
else -> {
_showWarning.postValue(false)
}
}
updateButtonState()
}
private fun calculateOutput() {
val dir = _direction.value ?: return
val grossOutput = when (dir) {
BridgeDirection.DOT_TO_HEZ -> currentAmount * dotToHezRate
BridgeDirection.HEZ_TO_DOT -> currentAmount / dotToHezRate
BridgeDirection.USDT_TO_WUSDT -> currentAmount // 1:1
BridgeDirection.WUSDT_TO_USDT -> currentAmount // 1:1
}
// Apply fee
val netOutput = grossOutput * (1 - FEE_PERCENT)
_outputAmount.value = if (netOutput > 0) {
@@ -250,18 +282,21 @@ class BridgeViewModel(
private fun updateUI() {
val dir = _direction.value ?: return
val rateFormatted = BigDecimal(dotToHezRate).setScale(4, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString()
val reverseRateFormatted = BigDecimal(1.0 / dotToHezRate).setScale(6, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString()
when (dir) {
BridgeDirection.DOT_TO_HEZ -> {
val rateFormatted = BigDecimal(dotToHezRate).setScale(4, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString()
_exchangeRateText.value = "1 DOT = $rateFormatted HEZ"
_minimumText.value = "$MIN_DOT DOT"
}
BridgeDirection.HEZ_TO_DOT -> {
_exchangeRateText.value = "1 HEZ = $reverseRateFormatted DOT"
val reverseRate = BigDecimal(1.0 / dotToHezRate).setScale(6, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString()
_exchangeRateText.value = "1 HEZ = $reverseRate DOT"
_minimumText.value = "$MIN_HEZ HEZ"
}
BridgeDirection.USDT_TO_WUSDT, BridgeDirection.WUSDT_TO_USDT -> {
_exchangeRateText.value = "1:1 (fee 0.1%)"
_minimumText.value = "$MIN_USDT USDT"
}
}
updateButtonState()
@@ -272,12 +307,14 @@ class BridgeViewModel(
val minimum = when (dir) {
BridgeDirection.DOT_TO_HEZ -> MIN_DOT
BridgeDirection.HEZ_TO_DOT -> MIN_HEZ
BridgeDirection.USDT_TO_WUSDT, BridgeDirection.WUSDT_TO_USDT -> MIN_USDT
}
_buttonState.value = when {
currentAmount <= 0 -> ButtonState.DISABLED
currentAmount < minimum -> ButtonState.DISABLED
dir == BridgeDirection.HEZ_TO_DOT && !isHezToDotActive -> ButtonState.DISABLED
dir == BridgeDirection.WUSDT_TO_USDT && !isWusdtToUsdtActive -> ButtonState.DISABLED
else -> ButtonState.NORMAL
}
}
@@ -25,6 +25,41 @@
android:orientation="vertical"
android:padding="16dp">
<!-- Pair Selector -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:background="@drawable/bg_primary_list_item"
android:orientation="horizontal"
android:padding="4dp">
<TextView
android:id="@+id/bridgePairDotHez"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_button_primary"
android:gravity="center"
android:padding="10dp"
android:text="DOT / HEZ"
android:textColor="@color/text_primary"
android:textSize="13sp"
android:textStyle="bold" />
<TextView
android:id="@+id/bridgePairUsdt"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:padding="10dp"
android:text="USDT"
android:textColor="@color/text_secondary"
android:textSize="13sp" />
</LinearLayout>
<!-- Direction Toggle -->
<LinearLayout
android:layout_width="match_parent"
@@ -35,7 +70,7 @@
android:padding="4dp">
<TextView
android:id="@+id/bridgeDirectionDotToHez"
android:id="@+id/bridgeDirectionLeft"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
@@ -48,7 +83,7 @@
android:textStyle="bold" />
<TextView
android:id="@+id/bridgeDirectionHezToDot"
android:id="@+id/bridgeDirectionRight"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
@@ -15,53 +15,92 @@
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/pezkuwiDashboardTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/pezkuwi_dashboard_title"
android:textColor="@android:color/white"
android:textSize="18sp"
android:textStyle="bold" />
<com.google.android.flexbox.FlexboxLayout
android:id="@+id/pezkuwiDashboardRoles"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:flexWrap="wrap"
app:alignItems="center"
app:justifyContent="flex_start" />
<!-- Top row: Title + Logo -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:orientation="vertical">
android:orientation="horizontal"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/pezkuwi_dashboard_trust_score"
android:textColor="#B0BEC5"
android:textSize="12sp" />
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/pezkuwiDashboardTrustValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:textColor="@android:color/white"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/pezkuwiDashboardTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/pezkuwi_dashboard_title"
android:textColor="@android:color/white"
android:textSize="18sp"
android:textStyle="bold" />
<com.google.android.flexbox.FlexboxLayout
android:id="@+id/pezkuwiDashboardRoles"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
app:flexWrap="wrap"
app:alignItems="center"
app:justifyContent="flex_start" />
<!-- Trust Score -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/pezkuwi_dashboard_trust_score"
android:textColor="#B0BEC5"
android:textSize="12sp" />
<TextView
android:id="@+id/pezkuwiDashboardTrustValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textColor="#FFD54F"
android:textSize="16sp"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
<ImageView
android:id="@+id/pezkuwiDashboardLogo"
android:layout_width="120dp"
android:layout_height="48dp"
android:layout_marginStart="8dp"
android:src="@drawable/telegram_welcome"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
android:contentDescription="@string/pezkuwi_dashboard_title" />
</LinearLayout>
<!-- Info text -->
<TextView
android:id="@+id/pezkuwiDashboardInfo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/pezkuwi_dashboard_info"
android:textColor="#CFD8DC"
android:textSize="11sp"
android:lineSpacingExtra="2dp" />
<!-- Action button -->
<com.google.android.material.button.MaterialButton
android:id="@+id/pezkuwiDashboardBasvuruButton"
style="@style/Widget.Nova.MaterialButton.Primary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_marginTop="10dp"
android:text="@string/pezkuwi_dashboard_basvuru"
android:textAllCaps="false" />
</LinearLayout>