SetDonauScreen.kt (7360B)
1 /* 2 * This file is part of GNU Taler 3 * (C) 2026 Taler Systems S.A. 4 * 5 * GNU Taler is free software; you can redistribute it and/or modify it under the 6 * terms of the GNU General Public License as published by the Free Software 7 * Foundation; either version 3, or (at your option) any later version. 8 * 9 * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY 10 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License along with 14 * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 17 package net.taler.wallet.donau 18 19 import androidx.compose.foundation.layout.Column 20 import androidx.compose.foundation.layout.fillMaxSize 21 import androidx.compose.foundation.layout.fillMaxWidth 22 import androidx.compose.foundation.layout.padding 23 import androidx.compose.foundation.rememberScrollState 24 import androidx.compose.foundation.text.KeyboardActions 25 import androidx.compose.foundation.text.KeyboardOptions 26 import androidx.compose.foundation.verticalScroll 27 import androidx.compose.material3.Button 28 import androidx.compose.material3.OutlinedTextField 29 import androidx.compose.material3.Text 30 import androidx.compose.runtime.Composable 31 import androidx.compose.runtime.LaunchedEffect 32 import androidx.compose.runtime.getValue 33 import androidx.compose.runtime.livedata.observeAsState 34 import androidx.compose.runtime.mutableStateOf 35 import androidx.compose.runtime.remember 36 import androidx.compose.runtime.setValue 37 import androidx.compose.ui.Alignment 38 import androidx.compose.ui.Modifier 39 import androidx.compose.ui.focus.FocusDirection 40 import androidx.compose.ui.platform.LocalFocusManager 41 import androidx.compose.ui.platform.LocalSoftwareKeyboardController 42 import androidx.compose.ui.res.stringResource 43 import androidx.compose.ui.text.TextStyle 44 import androidx.compose.ui.text.font.FontFamily.Companion.Monospace 45 import androidx.compose.ui.text.input.ImeAction 46 import androidx.compose.ui.tooling.preview.Preview 47 import androidx.compose.ui.unit.dp 48 import net.taler.wallet.BottomInsetsSpacer 49 import net.taler.wallet.R 50 import net.taler.wallet.backend.TalerErrorInfo 51 import net.taler.wallet.compose.ErrorComposable 52 import net.taler.wallet.compose.GlobalScaffold 53 import net.taler.wallet.compose.LoadingScreen 54 import net.taler.wallet.compose.TalerSurface 55 import net.taler.wallet.compose.collectAsStateLifecycleAware 56 import net.taler.wallet.donau.GetDonauStatus.Error 57 import net.taler.wallet.donau.GetDonauStatus.Loading 58 import net.taler.wallet.donau.GetDonauStatus.None 59 import net.taler.wallet.donau.GetDonauStatus.Success 60 import net.taler.wallet.main.MainViewModel 61 62 @Composable 63 fun SetDonauScreen( 64 model: MainViewModel, 65 donauBaseUrl: String?, 66 onShowMessage: (String) -> Unit, 67 onShowError: (TalerErrorInfo) -> Unit, 68 onNavigateBack: () -> Unit, 69 ) { 70 val donauManager = model.donauManager 71 val donauStatus by donauManager.donauStatus.collectAsStateLifecycleAware() 72 val devMode by model.devMode.observeAsState(false) 73 74 LaunchedEffect(Unit) { 75 donauManager.getDonau() 76 } 77 78 val readyMessage = stringResource(id = R.string.donau_ready) 79 80 TalerSurface { 81 GlobalScaffold( 82 model = model, 83 modifier = Modifier.fillMaxSize(), 84 onNavigateBack = onNavigateBack, 85 title = { Text(stringResource(R.string.donau_title)) }, 86 ) { paddingValues -> 87 when (val status = donauStatus) { 88 Loading, None -> LoadingScreen(Modifier.padding(paddingValues)) 89 is Success -> SetDonauComposable( 90 modifier = Modifier.padding(paddingValues), 91 donauInfo = status.donauInfo, 92 initialUrl = donauBaseUrl, 93 onSetDonauInfo = { info -> 94 donauManager.setDonau( 95 info, 96 { 97 onShowMessage(readyMessage) 98 onNavigateBack() 99 }, 100 { error -> onShowError(error) } 101 ) 102 }, 103 ) 104 105 is Error -> ErrorComposable( 106 error = status.error, 107 modifier = Modifier 108 .fillMaxSize() 109 .padding(paddingValues) 110 .verticalScroll(rememberScrollState()), 111 devMode = devMode, 112 ) 113 } 114 } 115 } 116 } 117 118 @Composable 119 fun SetDonauComposable( 120 donauInfo: DonauInfo?, 121 onSetDonauInfo: (info: DonauInfo) -> Unit, 122 modifier: Modifier = Modifier, 123 initialUrl: String? = null, 124 ) { 125 val focusManager = LocalFocusManager.current 126 val keyboardController = LocalSoftwareKeyboardController.current 127 var donauBaseUrl by remember { mutableStateOf(initialUrl ?: donauInfo?.donauBaseUrl ?: "") } 128 var taxPayerId by remember { mutableStateOf(donauInfo?.taxPayerId ?: "") } 129 130 Column(modifier.fillMaxSize()) { 131 OutlinedTextField( 132 modifier = Modifier.padding( 133 bottom = 16.dp, 134 start = 16.dp, 135 end = 16.dp, 136 ).fillMaxWidth(), 137 value = donauBaseUrl, 138 onValueChange = { 139 donauBaseUrl = it 140 }, 141 singleLine = true, 142 isError = donauBaseUrl.isBlank(), 143 label = { Text(stringResource(R.string.donau_url)) }, 144 keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), 145 keyboardActions = KeyboardActions(onNext = { focusManager.moveFocus(FocusDirection.Next) }), 146 ) 147 148 OutlinedTextField( 149 modifier = Modifier.padding( 150 bottom = 16.dp, 151 start = 16.dp, 152 end = 16.dp, 153 ).fillMaxWidth(), 154 value = taxPayerId, 155 onValueChange = { 156 taxPayerId = it 157 }, 158 singleLine = true, 159 textStyle = TextStyle(fontFamily = Monospace), 160 isError = taxPayerId.isBlank(), 161 label = { Text(stringResource(R.string.donau_id)) }, 162 keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), 163 keyboardActions = KeyboardActions(onNext = { focusManager.moveFocus(FocusDirection.Exit) }), 164 ) 165 166 Button( 167 modifier = Modifier 168 .padding(horizontal = 16.dp) 169 .align(Alignment.End), 170 onClick = { 171 focusManager.clearFocus() 172 keyboardController?.hide() 173 onSetDonauInfo(DonauInfo( 174 donauBaseUrl = donauBaseUrl, 175 taxPayerId = taxPayerId, 176 )) 177 }, 178 enabled = donauBaseUrl.isNotBlank() && 179 taxPayerId.isNotBlank() 180 ) { 181 Text(stringResource(R.string.save)) 182 } 183 184 BottomInsetsSpacer() 185 } 186 } 187 188 @Preview 189 @Composable 190 fun SetDonauComposablePreview() { 191 TalerSurface { 192 SetDonauComposable( 193 donauInfo = null, 194 initialUrl = "https://donau.test.taler.net/", 195 onSetDonauInfo = {}, 196 ) 197 } 198 }