From 64ab8cf5eb731e5805caaf164369094445f07df4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juliano=20C=C3=A9zar=20Chagas=20Tavares?= Date: Wed, 13 Nov 2024 12:28:07 -0300 Subject: [PATCH] [Android] [iOS] Loading screen while storing credential (#43) This adds a loading screen on Android and iOS apps while storing credentials (after clicking "Add to wallet"). --- .../credentials/AddToWalletView.kt | 29 +++++-- .../viewmodels/CredentialPacksViewModel.kt | 10 ++- .../mobilesdkexample/wallet/WalletHomeView.kt | 80 ++++++++++--------- 3 files changed, 76 insertions(+), 43 deletions(-) diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/credentials/AddToWalletView.kt b/example/src/main/java/com/spruceid/mobilesdkexample/credentials/AddToWalletView.kt index a47e695..652050a 100644 --- a/example/src/main/java/com/spruceid/mobilesdkexample/credentials/AddToWalletView.kt +++ b/example/src/main/java/com/spruceid/mobilesdkexample/credentials/AddToWalletView.kt @@ -25,6 +25,7 @@ import androidx.compose.ui.unit.sp import androidx.navigation.NavHostController import com.spruceid.mobile.sdk.CredentialPack import com.spruceid.mobilesdkexample.ErrorView +import com.spruceid.mobilesdkexample.LoadingView import com.spruceid.mobilesdkexample.navigation.Screen import com.spruceid.mobilesdkexample.ui.theme.CTAButtonGreen import com.spruceid.mobilesdkexample.ui.theme.Inter @@ -32,6 +33,8 @@ import com.spruceid.mobilesdkexample.ui.theme.SecondaryButtonRed import com.spruceid.mobilesdkexample.ui.theme.TextHeader import com.spruceid.mobilesdkexample.utils.credentialDisplaySelector import com.spruceid.mobilesdkexample.viewmodels.CredentialPacksViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async import kotlinx.coroutines.launch @Composable @@ -42,6 +45,7 @@ fun AddToWalletView( ) { var credentialItem by remember { mutableStateOf(null) } var err by remember { mutableStateOf(null) } + var storing by remember { mutableStateOf(false) } val scope = rememberCoroutineScope() @@ -57,13 +61,22 @@ fun AddToWalletView( fun saveCredential() { scope.launch { - val credentialPack = CredentialPack() - try { - credentialPack.tryAddRawCredential(rawCredential) - credentialPacksViewModel.saveCredentialPack(credentialPack) + storing = true + var error: String? = null + this.async(Dispatchers.Default) { + try { + val credentialPack = CredentialPack() + credentialPack.tryAddRawCredential(rawCredential) + credentialPacksViewModel.saveCredentialPack(credentialPack) + } catch (e: Exception) { + error = e.localizedMessage + } + }.await() + if (error == null) { back() - } catch (e: Exception) { - err = e.localizedMessage + } else { + err = error + storing = false } } } @@ -76,6 +89,10 @@ fun AddToWalletView( back() } ) + } else if (storing) { + LoadingView( + loadingText = "Storing credential..." + ) } else if (credentialItem != null) { Column( Modifier diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/viewmodels/CredentialPacksViewModel.kt b/example/src/main/java/com/spruceid/mobilesdkexample/viewmodels/CredentialPacksViewModel.kt index 02d26de..76bb8fc 100644 --- a/example/src/main/java/com/spruceid/mobilesdkexample/viewmodels/CredentialPacksViewModel.kt +++ b/example/src/main/java/com/spruceid/mobilesdkexample/viewmodels/CredentialPacksViewModel.kt @@ -9,6 +9,8 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope import com.spruceid.mobile.sdk.CredentialPack +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch @@ -17,10 +19,16 @@ class CredentialPacksViewModel(application: Application) : AndroidViewModel(appl private val storageManager = StorageManager(context = (application as Context)) private val _credentialPacks = MutableStateFlow(listOf()) val credentialPacks = _credentialPacks.asStateFlow() + private val _loading = MutableStateFlow(false) + val loading = _loading.asStateFlow() init { viewModelScope.launch { - _credentialPacks.value = CredentialPack.loadPacks(storageManager) + _loading.value = true + this.async(Dispatchers.Default) { + _credentialPacks.value = CredentialPack.loadPacks(storageManager) + }.await() + _loading.value = false } } diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/wallet/WalletHomeView.kt b/example/src/main/java/com/spruceid/mobilesdkexample/wallet/WalletHomeView.kt index 98261f6..6c9084f 100644 --- a/example/src/main/java/com/spruceid/mobilesdkexample/wallet/WalletHomeView.kt +++ b/example/src/main/java/com/spruceid/mobilesdkexample/wallet/WalletHomeView.kt @@ -30,6 +30,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController +import com.spruceid.mobilesdkexample.LoadingView import com.spruceid.mobilesdkexample.R import com.spruceid.mobilesdkexample.credentials.GenericCredentialItem import com.spruceid.mobilesdkexample.navigation.Screen @@ -111,47 +112,54 @@ fun WalletHomeHeader(navController: NavController) { @Composable fun WalletHomeBody(credentialPacksViewModel: CredentialPacksViewModel) { val credentialPacks by credentialPacksViewModel.credentialPacks.collectAsState() + val loadingCredentialPacks by credentialPacksViewModel.loading.collectAsState() - if (credentialPacks.isNotEmpty()) { - Box(modifier = Modifier.fillMaxSize()) { - Column( - Modifier - .fillMaxWidth() - .verticalScroll(rememberScrollState()) - .padding(top = 20.dp) - ) { - credentialPacks.forEach { credentialPack -> - GenericCredentialItem( - credentialPack = credentialPack, - onDelete = { - credentialPacksViewModel.deleteCredentialPack(credentialPack) - } + if (!loadingCredentialPacks) { + if (credentialPacks.isNotEmpty()) { + Box(modifier = Modifier.fillMaxSize()) { + Column( + Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + .padding(top = 20.dp) + ) { + credentialPacks.forEach { credentialPack -> + GenericCredentialItem( + credentialPack = credentialPack, + onDelete = { + credentialPacksViewModel.deleteCredentialPack(credentialPack) + } + ) + .credentialPreviewAndDetails() + } + // item { + // ShareableCredentialListItems(mdocBase64 = mdocBase64) + // } + } + } + } else { + Box(Modifier.fillMaxSize()) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Image( + painter = painterResource(id = R.drawable.add_first_credential), + contentDescription = stringResource(id = R.string.add_first_credential), + ) + } + Column( + Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Image( + painter = painterResource(id = R.drawable.empty_wallet), + contentDescription = stringResource(id = R.string.empty_wallet), ) - .credentialPreviewAndDetails() } - // item { - // ShareableCredentialListItems(mdocBase64 = mdocBase64) - // } } } } else { - Box(Modifier.fillMaxSize()) { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - Image( - painter = painterResource(id = R.drawable.add_first_credential), - contentDescription = stringResource(id = R.string.add_first_credential), - ) - } - Column( - Modifier.fillMaxSize(), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - Image( - painter = painterResource(id = R.drawable.empty_wallet), - contentDescription = stringResource(id = R.string.empty_wallet), - ) - } - } + LoadingView( + loadingText = "" + ) } }