taler-android

Android apps for GNU Taler (wallet, PoS, cashier)
Log | Files | Refs | README | LICENSE

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 }