ErrorComposable.kt (5967B)
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.compose 18 19 import androidx.compose.foundation.layout.Arrangement 20 import androidx.compose.foundation.layout.Column 21 import androidx.compose.foundation.layout.Row 22 import androidx.compose.foundation.layout.Spacer 23 import androidx.compose.foundation.layout.fillMaxWidth 24 import androidx.compose.foundation.layout.padding 25 import androidx.compose.foundation.layout.size 26 import androidx.compose.foundation.rememberScrollState 27 import androidx.compose.foundation.verticalScroll 28 import androidx.compose.material.icons.Icons 29 import androidx.compose.material.icons.filled.Close 30 import androidx.compose.material.icons.rounded.ErrorOutline 31 import androidx.compose.material3.Button 32 import androidx.compose.material3.ButtonDefaults 33 import androidx.compose.material3.Card 34 import androidx.compose.material3.CardDefaults 35 import androidx.compose.material3.Icon 36 import androidx.compose.material3.MaterialTheme 37 import androidx.compose.material3.Text 38 import androidx.compose.runtime.Composable 39 import androidx.compose.runtime.remember 40 import androidx.compose.ui.Alignment.Companion.CenterHorizontally 41 import androidx.compose.ui.Modifier 42 import androidx.compose.ui.res.stringResource 43 import androidx.compose.ui.text.font.FontFamily 44 import androidx.compose.ui.text.style.TextAlign 45 import androidx.compose.ui.tooling.preview.Preview 46 import androidx.compose.ui.unit.dp 47 import kotlinx.serialization.ExperimentalSerializationApi 48 import kotlinx.serialization.json.Json 49 import net.taler.wallet.BottomInsetsSpacer 50 import net.taler.wallet.R 51 import net.taler.wallet.backend.TalerErrorInfo 52 53 @OptIn(ExperimentalSerializationApi::class) 54 @Composable 55 fun ErrorComposable( 56 error: TalerErrorInfo, 57 modifier: Modifier = Modifier, 58 devMode: Boolean, 59 onClose: (() -> Unit)? = null, 60 ) { 61 val scrollState = rememberScrollState() 62 Column( 63 modifier = modifier 64 .fillMaxWidth() 65 .verticalScroll(scrollState) 66 .padding(16.dp), 67 horizontalAlignment = CenterHorizontally, 68 verticalArrangement = Arrangement.Top, 69 ) { 70 Spacer(Modifier.size(20.dp)) 71 72 Icon( 73 Icons.Rounded.ErrorOutline, 74 modifier = Modifier 75 .size(85.dp) 76 .padding(bottom = 5.dp), 77 contentDescription = null, 78 tint = MaterialTheme.colorScheme.error, 79 ) 80 81 Text( 82 modifier = Modifier.padding(bottom = 16.dp), 83 text = stringResource(R.string.error), 84 style = MaterialTheme.typography.displaySmall, 85 ) 86 87 val jsonError = remember(error) { 88 val json = Json { 89 prettyPrint = true 90 prettyPrintIndent = " " 91 } 92 json.encodeToString(error) 93 } 94 95 if (devMode) { 96 Card(Modifier 97 .fillMaxWidth()) { 98 Text( 99 modifier = Modifier.padding(16.dp), 100 text = jsonError, 101 style = MaterialTheme.typography.bodyMedium, 102 color = CardDefaults.cardColors().contentColor, 103 fontFamily = FontFamily.Monospace, 104 textAlign = TextAlign.Start, 105 ) 106 } 107 } else { 108 Text( 109 text = error.userFacingMsg, 110 fontFamily = FontFamily.Default, 111 textAlign = TextAlign.Center, 112 ) 113 } 114 115 Column(Modifier 116 .padding(bottom = 16.dp, top = 22.dp) 117 .fillMaxWidth(), 118 horizontalAlignment = CenterHorizontally, 119 ) { 120 Text(stringResource(R.string.error_export), 121 modifier = Modifier.padding(bottom = 12.dp), 122 style = MaterialTheme.typography.titleMedium) 123 Row(Modifier.fillMaxWidth(), 124 horizontalArrangement = Arrangement.SpaceEvenly, 125 ) { 126 CopyToClipboardButton( 127 label = "Error", 128 content = jsonError, 129 ) 130 131 ShareButton( 132 content = jsonError, 133 ) 134 } 135 } 136 137 if (onClose != null) Button( 138 modifier = Modifier.padding(bottom = 16.dp), 139 onClick = onClose, 140 colors = ButtonDefaults.buttonColors( 141 containerColor = MaterialTheme.colorScheme.error, 142 contentColor = MaterialTheme.colorScheme.onError, 143 ), 144 ) { 145 Icon( 146 Icons.Default.Close, 147 contentDescription = null, 148 modifier = Modifier.size(ButtonDefaults.IconSize), 149 ) 150 Spacer(Modifier.size(ButtonDefaults.IconSpacing)) 151 Text(text = stringResource(R.string.close)) 152 } 153 154 BottomInsetsSpacer() 155 } 156 } 157 158 @Preview 159 @Composable 160 fun ErrorComposablePreview(devMode: Boolean = false) { 161 TalerSurface { 162 ErrorComposable( 163 error = TalerErrorInfo.makeCustomError( 164 message = "Some random error", 165 ), 166 devMode = devMode, 167 onClose = {}, 168 ) 169 } 170 } 171 172 @Preview 173 @Composable 174 fun ErrorComposableDevPreview() { 175 ErrorComposablePreview( 176 devMode = true, 177 ) 178 }