SetDonauFragment.kt (8051B)
1 /* 2 * This file is part of GNU Taler 3 * (C) 2025 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 android.os.Bundle 20 import android.view.LayoutInflater 21 import android.view.View 22 import android.view.ViewGroup 23 import android.widget.Toast 24 import androidx.compose.foundation.layout.Column 25 import androidx.compose.foundation.layout.fillMaxSize 26 import androidx.compose.foundation.layout.fillMaxWidth 27 import androidx.compose.foundation.layout.padding 28 import androidx.compose.foundation.rememberScrollState 29 import androidx.compose.foundation.text.KeyboardActions 30 import androidx.compose.foundation.text.KeyboardOptions 31 import androidx.compose.foundation.verticalScroll 32 import androidx.compose.material3.Button 33 import androidx.compose.material3.OutlinedTextField 34 import androidx.compose.material3.Text 35 import androidx.compose.runtime.Composable 36 import androidx.compose.runtime.LaunchedEffect 37 import androidx.compose.runtime.collectAsState 38 import androidx.compose.runtime.getValue 39 import androidx.compose.runtime.livedata.observeAsState 40 import androidx.compose.runtime.mutableStateOf 41 import androidx.compose.runtime.remember 42 import androidx.compose.runtime.setValue 43 import androidx.compose.ui.Alignment 44 import androidx.compose.ui.Modifier 45 import androidx.compose.ui.focus.FocusDirection 46 import androidx.compose.ui.platform.ComposeView 47 import androidx.compose.ui.platform.LocalFocusManager 48 import androidx.compose.ui.platform.LocalSoftwareKeyboardController 49 import androidx.compose.ui.res.stringResource 50 import androidx.compose.ui.text.TextStyle 51 import androidx.compose.ui.text.font.FontFamily.Companion.Monospace 52 import androidx.compose.ui.text.input.ImeAction 53 import androidx.compose.ui.tooling.preview.Preview 54 import androidx.compose.ui.unit.dp 55 import androidx.fragment.app.Fragment 56 import androidx.fragment.app.activityViewModels 57 import androidx.navigation.fragment.findNavController 58 import net.taler.common.showError 59 import net.taler.wallet.BottomInsetsSpacer 60 import net.taler.wallet.main.MainViewModel 61 import net.taler.wallet.R 62 import net.taler.wallet.compose.ErrorComposable 63 import net.taler.wallet.compose.LoadingScreen 64 import net.taler.wallet.compose.TalerSurface 65 import net.taler.wallet.donau.GetDonauStatus.Error 66 import net.taler.wallet.donau.GetDonauStatus.Loading 67 import net.taler.wallet.donau.GetDonauStatus.None 68 import net.taler.wallet.donau.GetDonauStatus.Success 69 import net.taler.wallet.showError 70 71 class SetDonauFragment: Fragment() { 72 val model: MainViewModel by activityViewModels() 73 val donauManager by lazy { model.donauManager } 74 75 override fun onCreateView( 76 inflater: LayoutInflater, 77 container: ViewGroup?, 78 savedInstanceState: Bundle? 79 ): View = ComposeView(requireContext()).apply { 80 val saveShouldExit = arguments?.getBoolean("saveShouldExit") == true 81 val donauBaseUrl = arguments?.getString("donauBaseUrl") 82 83 setContent { 84 TalerSurface { 85 val donauStatus by donauManager.donauStatus.collectAsState() 86 val devMode by model.devMode.observeAsState() 87 when(val status = donauStatus) { 88 Loading, None -> LoadingScreen() 89 is Success -> SetDonauComposable( 90 initialUrl = donauBaseUrl, 91 donauInfo = status.donauInfo, 92 onSetDonauInfo = { info -> 93 donauManager.setDonau(info, 94 { 95 Toast.makeText( 96 requireContext(), 97 getString(R.string.donau_ready), 98 Toast.LENGTH_LONG).show() 99 if (saveShouldExit) { 100 findNavController().popBackStack() 101 } 102 }, 103 { error -> 104 if(model.devMode.value == true) { 105 showError(error) 106 } else { 107 showError(error.userFacingMsg) 108 } 109 } 110 ) 111 }, 112 ) 113 is Error -> ErrorComposable( 114 error = status.error, 115 modifier = Modifier 116 .fillMaxSize() 117 .verticalScroll(rememberScrollState()), 118 devMode = devMode == true, 119 ) 120 } 121 122 LaunchedEffect(Unit) { 123 donauManager.getDonau() 124 } 125 } 126 } 127 } 128 } 129 130 @Composable 131 fun SetDonauComposable( 132 initialUrl: String? = null, 133 donauInfo: DonauInfo?, 134 onSetDonauInfo: (info: DonauInfo) -> Unit, 135 ) { 136 val focusManager = LocalFocusManager.current 137 val keyboardController = LocalSoftwareKeyboardController.current 138 var donauBaseUrl by remember { mutableStateOf(initialUrl ?: donauInfo?.donauBaseUrl ?: "") } 139 var taxPayerId by remember { mutableStateOf(donauInfo?.taxPayerId ?: "") } 140 141 Column(Modifier.fillMaxSize()) { 142 OutlinedTextField( 143 modifier = Modifier.padding( 144 bottom = 16.dp, 145 start = 16.dp, 146 end = 16.dp, 147 ).fillMaxWidth(), 148 value = donauBaseUrl, 149 onValueChange = { 150 donauBaseUrl = it 151 }, 152 singleLine = true, 153 isError = donauBaseUrl.isBlank(), 154 label = { Text(stringResource(R.string.donau_url)) }, 155 keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), 156 keyboardActions = KeyboardActions(onNext = { focusManager.moveFocus(FocusDirection.Next) }), 157 ) 158 159 OutlinedTextField( 160 modifier = Modifier.padding( 161 bottom = 16.dp, 162 start = 16.dp, 163 end = 16.dp, 164 ).fillMaxWidth(), 165 value = taxPayerId, 166 onValueChange = { 167 taxPayerId = it 168 }, 169 singleLine = true, 170 textStyle = TextStyle(fontFamily = Monospace), 171 isError = taxPayerId.isBlank(), 172 label = { Text(stringResource(R.string.donau_id)) }, 173 keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), 174 keyboardActions = KeyboardActions(onNext = { focusManager.moveFocus(FocusDirection.Exit) }), 175 ) 176 177 Button( 178 modifier = Modifier 179 .padding(horizontal = 16.dp) 180 .align(Alignment.End), 181 onClick = { 182 focusManager.clearFocus() 183 keyboardController?.hide() 184 onSetDonauInfo(DonauInfo( 185 donauBaseUrl = donauBaseUrl, 186 taxPayerId = taxPayerId, 187 )) 188 }, 189 enabled = donauBaseUrl.isNotBlank() && 190 taxPayerId.isNotBlank() 191 ) { 192 Text(stringResource(R.string.save)) 193 } 194 195 BottomInsetsSpacer() 196 } 197 } 198 199 @Preview 200 @Composable 201 fun SetDonauComposablePreview() { 202 TalerSurface { 203 SetDonauComposable("https://donau.test.taler.net/", null) {} 204 } 205 }