refactor: BrightnessController and QrDisplayCard to ui/common

This commit is contained in:
2026-06-10 19:05:53 +02:00
parent e71a0ffdb9
commit 8405f33c47
4 changed files with 37 additions and 63 deletions
@@ -15,10 +15,11 @@ fun rememberBrightnessController(): BrightnessController {
val window = (LocalContext.current as? Activity)?.window val window = (LocalContext.current as? Activity)?.window
var isOn by remember { mutableStateOf(false) } var isOn by remember { mutableStateOf(false) }
LaunchedEffect(isOn) { // Sync side-effect: no coroutine needed
SideEffect {
window?.attributes = window?.attributes?.apply { window?.attributes = window?.attributes?.apply {
screenBrightness = if (isOn) 1f screenBrightness = if (isOn) WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_FULL
else WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE else WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
} }
} }
@@ -30,9 +31,9 @@ fun rememberBrightnessController(): BrightnessController {
} }
} }
return remember(isOn) { // No remember needed — BrightnessController is a tiny data class,
BrightnessController(isOn = isOn, toggle = { isOn = !isOn }) // and this composable already recomposes only when isOn changes.
} return BrightnessController(isOn = isOn, toggle = { isOn = !isOn })
} }
data class BrightnessController( data class BrightnessController(
@@ -1,4 +1,4 @@
package com.bitcointxoko.gudariwallet.ui.receive package com.bitcointxoko.gudariwallet.ui.common
import android.content.ClipData import android.content.ClipData
import android.graphics.Bitmap import android.graphics.Bitmap
@@ -1,10 +1,7 @@
package com.bitcointxoko.gudariwallet.ui.nwc package com.bitcointxoko.gudariwallet.ui.nwc
import android.app.Activity
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.Intent import android.content.Intent
import android.view.WindowManager
import android.widget.Toast
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
@@ -13,18 +10,12 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.net.toUri import androidx.core.net.toUri
import com.bitcointxoko.gudariwallet.ui.common.rememberBrightnessController import com.bitcointxoko.gudariwallet.ui.common.rememberBrightnessController
import com.bitcointxoko.gudariwallet.ui.receive.QrDisplayCard import com.bitcointxoko.gudariwallet.ui.common.QrDisplayCard
@Composable @Composable
internal fun NwcPairingUrlDialog( internal fun NwcPairingUrlDialog(
@@ -1,7 +1,5 @@
package com.bitcointxoko.gudariwallet.ui.receive package com.bitcointxoko.gudariwallet.ui.receive
import android.app.Activity
import android.view.WindowManager
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@@ -58,14 +56,13 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bitcointxoko.gudariwallet.LocalAppStrings import com.bitcointxoko.gudariwallet.LocalAppStrings
import com.bitcointxoko.gudariwallet.ui.ReceiveState import com.bitcointxoko.gudariwallet.ui.ReceiveState
import com.bitcointxoko.gudariwallet.ui.SendState
import com.bitcointxoko.gudariwallet.ui.WalletViewModel import com.bitcointxoko.gudariwallet.ui.WalletViewModel
import com.bitcointxoko.gudariwallet.ui.common.QrDisplayCard
import com.bitcointxoko.gudariwallet.ui.common.rememberBrightnessController
import com.bitcointxoko.gudariwallet.ui.components.UnitWheelPicker import com.bitcointxoko.gudariwallet.ui.components.UnitWheelPicker
import com.bitcointxoko.gudariwallet.ui.nfc.NfcViewModel import com.bitcointxoko.gudariwallet.ui.nfc.NfcViewModel
import com.bitcointxoko.gudariwallet.util.buildUpTaps import com.bitcointxoko.gudariwallet.util.buildUpTaps
import com.bitcointxoko.gudariwallet.util.doubleTap
import com.bitcointxoko.gudariwallet.util.fiatLabel import com.bitcointxoko.gudariwallet.util.fiatLabel
import com.bitcointxoko.gudariwallet.util.heavyClick
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@Composable @Composable
@@ -74,32 +71,17 @@ fun ReceiveScreen(
nfcVm : NfcViewModel, nfcVm : NfcViewModel,
onNavigateToPaymentDetail: (checkingId: String) -> Unit onNavigateToPaymentDetail: (checkingId: String) -> Unit
) { ) {
val strings = LocalAppStrings.current val strings = LocalAppStrings.current
val context = LocalContext.current
val brightness = rememberBrightnessController()
var showAddress by remember { mutableStateOf(false) }
val receiveState by vm.receiveState.collectAsStateWithLifecycle() val receiveState by vm.receiveState.collectAsStateWithLifecycle()
val lightningAddress by vm.lightningAddress.collectAsStateWithLifecycle() val lightningAddress by vm.lightningAddress.collectAsStateWithLifecycle()
val fiatRate by vm.fiatSatsPerUnit.collectAsStateWithLifecycle() val fiatRate by vm.fiatSatsPerUnit.collectAsStateWithLifecycle()
val fiatCurrency by vm.selectedCurrency.collectAsStateWithLifecycle() val fiatCurrency by vm.selectedCurrency.collectAsStateWithLifecycle()
val context = LocalContext.current
val window = (context as Activity).window
val originalBrightness = remember { window.attributes.screenBrightness }
var brightnessOn by remember { mutableStateOf(false) }
var showAddress by remember { mutableStateOf(false) }
val onToggleBrightness = {
val lp = window.attributes
lp.screenBrightness = if (!brightnessOn)
WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_FULL
else originalBrightness
window.attributes = lp
brightnessOn = !brightnessOn
}
DisposableEffect(Unit) { DisposableEffect(Unit) {
onDispose { onDispose {
val lp = window.attributes
lp.screenBrightness = originalBrightness
window.attributes = lp
nfcVm.stopEmulating() nfcVm.stopEmulating()
vm.resetReceiveState() vm.resetReceiveState()
} }
@@ -141,8 +123,8 @@ fun ReceiveScreen(
lightningAddress = lightningAddress, lightningAddress = lightningAddress,
showAddress = showAddress, showAddress = showAddress,
onToggleAddress = { showAddress = !showAddress }, onToggleAddress = { showAddress = !showAddress },
brightnessOn = brightnessOn, brightnessOn = brightness.isOn,
onToggleBrightness = onToggleBrightness, onToggleBrightness = brightness.toggle,
isLoading = receiveState is ReceiveState.AwaitingInvoice, isLoading = receiveState is ReceiveState.AwaitingInvoice,
onCreateInvoice = { amountSats, memo -> onCreateInvoice = { amountSats, memo ->
vm.createInvoice( vm.createInvoice(
@@ -216,8 +198,8 @@ fun ReceiveScreen(
state = receiveState as ReceiveState.InvoiceReady, state = receiveState as ReceiveState.InvoiceReady,
fiatRate = fiatRate, fiatRate = fiatRate,
fiatCurrency = fiatCurrency, fiatCurrency = fiatCurrency,
brightnessOn = brightnessOn, brightnessOn = brightness.isOn,
onToggleBrightness = onToggleBrightness, onToggleBrightness = brightness.toggle,
nfcVm = nfcVm, nfcVm = nfcVm,
onReset = { vm.resetReceiveState() } onReset = { vm.resetReceiveState() }
) )
@@ -438,12 +420,12 @@ private fun ReceiveIdleContent(
Spacer(Modifier.height(12.dp)) Spacer(Modifier.height(12.dp))
QrDisplayCard( QrDisplayCard(
content = lightningAddress, content = lightningAddress,
contentDescription = strings.lightningAddressQrCode, // ← "Lightning address QR code" contentDescription = strings.lightningAddressQrCode, // ← "Lightning address QR code"
clipLabel = strings.lightningAddressLabel, // ← "Lightning Address" clipLabel = strings.lightningAddressLabel, // ← "Lightning Address"
shareTitle = strings.shareLightningAddress, // ← "Share Lightning Address" shareTitle = strings.shareLightningAddress, // ← "Share Lightning Address"
textToCopy = lightningAddress, textToCopy = lightningAddress,
brightnessOn = brightnessOn, brightnessOn = brightnessOn,
onToggleBrightness = onToggleBrightness onToggleBrightness = onToggleBrightness
) )
} }
@@ -512,7 +494,7 @@ private fun AmountStepContent(
val number = amountText.trim().toDoubleOrNull() ?: return@remember null val number = amountText.trim().toDoubleOrNull() ?: return@remember null
when (activeUnit) { when (activeUnit) {
AmountUnit.SATS -> fiatLabel(number.toLong(), fiatRate, fiatCurrency) AmountUnit.SATS -> fiatLabel(number.toLong(), fiatRate, fiatCurrency)
AmountUnit.FIAT -> "${(number * fiatRate!!).toLong()} sats" AmountUnit.FIAT -> "${(number * fiatRate).toLong()} sats"
} }
} }
@@ -560,7 +542,7 @@ private fun AmountStepContent(
) )
UnitWheelPicker( UnitWheelPicker(
units = if (showFiatToggle) listOf(strings.sats, fiatCurrency!!) else listOf(strings.sats), units = if (showFiatToggle) listOf(strings.sats, fiatCurrency) else listOf(strings.sats),
selectedIndex = if (activeUnit == AmountUnit.SATS) 0 else 1, selectedIndex = if (activeUnit == AmountUnit.SATS) 0 else 1,
onIndexSelected = { newIndex -> onIndexSelected = { newIndex ->
val newUnit = if (newIndex == 0) AmountUnit.SATS else AmountUnit.FIAT val newUnit = if (newIndex == 0) AmountUnit.SATS else AmountUnit.FIAT
@@ -682,23 +664,23 @@ private fun ReceiveInvoiceContent(
} }
QrDisplayCard( QrDisplayCard(
content = state.bolt11.uppercase(), content = state.bolt11.uppercase(),
contentDescription = strings.lightningInvoiceQrCode, // ← "Lightning invoice QR code" contentDescription = strings.lightningInvoiceQrCode, // ← "Lightning invoice QR code"
clipLabel = strings.invoiceLabel, // ← "Invoice" clipLabel = strings.invoiceLabel, // ← "Invoice"
shareTitle = strings.shareInvoice, // ← "Share Invoice" shareTitle = strings.shareInvoice, // ← "Share Invoice"
textToCopy = state.bolt11, textToCopy = state.bolt11,
brightnessOn = brightnessOn, brightnessOn = brightnessOn,
onToggleBrightness = onToggleBrightness, onToggleBrightness = onToggleBrightness,
isNfcEmulating = isEmulating, isNfcEmulating = isEmulating,
onToggleNfc = { onToggleNfc = {
if (isEmulating) nfcVm.stopEmulating() if (isEmulating) nfcVm.stopEmulating()
else nfcVm.startEmulating("lightning:${state.bolt11}") else nfcVm.startEmulating("lightning:${state.bolt11}")
}, },
nfcEnabled = !isExpired, nfcEnabled = !isExpired,
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.fillMaxWidth(), .fillMaxWidth(),
qrModifier = Modifier qrModifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.weight(1f) .weight(1f)
) )