From 942f87f1100e14469d6c18f244dacfd6a794c88d Mon Sep 17 00:00:00 2001 From: Iulia Stana Date: Fri, 14 Apr 2023 18:03:04 +0200 Subject: [PATCH] Edured-83: implement add role/institution from the personal information screen. --- .../accountlinked/AccountLinkedScreen.kt | 4 +- .../personalinfo/PersonalInfoRepository.kt | 17 +++++ .../personalinfo/PersonalInfoScreen.kt | 73 +++++++++++++++++-- .../personalinfo/PersonalInfoViewModel.kt | 43 +++++++++++ .../nl/eduid/screens/personalinfo/UiState.kt | 6 +- .../{plus_icon_gray.xml => ic_plus.xml} | 0 app/src/main/res/values/strings.xml | 2 + 7 files changed, 136 insertions(+), 9 deletions(-) rename app/src/main/res/drawable/{plus_icon_gray.xml => ic_plus.xml} (100%) diff --git a/app/src/main/kotlin/nl/eduid/screens/accountlinked/AccountLinkedScreen.kt b/app/src/main/kotlin/nl/eduid/screens/accountlinked/AccountLinkedScreen.kt index 6f17a79c..e69fd41e 100644 --- a/app/src/main/kotlin/nl/eduid/screens/accountlinked/AccountLinkedScreen.kt +++ b/app/src/main/kotlin/nl/eduid/screens/accountlinked/AccountLinkedScreen.kt @@ -58,7 +58,9 @@ private fun AccountLinkedContent( withBackIcon = false ) { Column( - modifier = Modifier.verticalScroll(rememberScrollState()) + modifier = Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) ) { if (errorData != null) { AlertDialogWithSingleButton( diff --git a/app/src/main/kotlin/nl/eduid/screens/personalinfo/PersonalInfoRepository.kt b/app/src/main/kotlin/nl/eduid/screens/personalinfo/PersonalInfoRepository.kt index b22fe87c..468277fd 100644 --- a/app/src/main/kotlin/nl/eduid/screens/personalinfo/PersonalInfoRepository.kt +++ b/app/src/main/kotlin/nl/eduid/screens/personalinfo/PersonalInfoRepository.kt @@ -108,4 +108,21 @@ class PersonalInfoRepository(private val eduIdApi: EduIdApi) { Timber.e(e, "Failed to retrieve institution name") null } + + suspend fun getStartLinkAccount(): String? = try { + val response = eduIdApi.getStartLinkAccount() + if (response.isSuccessful) { + response.body()?.url + } else { + Timber.w( + "Failed to retrieve start link account URL: [${response.code()}/${response.message()}]${ + response.errorBody()?.string() + }" + ) + null + } + } catch (e: Exception) { + Timber.e(e, "Failed to retrieve start link account URL") + null + } } \ No newline at end of file diff --git a/app/src/main/kotlin/nl/eduid/screens/personalinfo/PersonalInfoScreen.kt b/app/src/main/kotlin/nl/eduid/screens/personalinfo/PersonalInfoScreen.kt index 06fbe1f9..45f4f615 100644 --- a/app/src/main/kotlin/nl/eduid/screens/personalinfo/PersonalInfoScreen.kt +++ b/app/src/main/kotlin/nl/eduid/screens/personalinfo/PersonalInfoScreen.kt @@ -1,5 +1,6 @@ package nl.eduid.screens.personalinfo +import androidx.activity.compose.rememberLauncherForActivityResult import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -17,12 +18,17 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment.Companion.CenterStart import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -30,6 +36,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import nl.eduid.ErrorData import nl.eduid.R +import nl.eduid.screens.firsttimedialog.LinkAccountContract import nl.eduid.ui.AlertDialogWithSingleButton import nl.eduid.ui.EduIdTopAppBar import nl.eduid.ui.InfoTab @@ -43,11 +50,30 @@ fun PersonalInfoScreen( viewModel: PersonalInfoViewModel, onEmailClicked: () -> Unit, onManageAccountClicked: (dateString: String) -> Unit, + goToAccountLinked: () -> Unit = {}, goBack: () -> Unit, ) = EduIdTopAppBar( onBackClicked = goBack, ) { val uiState by viewModel.uiState.observeAsState(UiState()) + var isGettingLinkUrl by rememberSaveable { mutableStateOf(false) } + var isLinkingStarted by rememberSaveable { mutableStateOf(false) } + val launcher = + rememberLauncherForActivityResult(contract = LinkAccountContract(), onResult = { _ -> + if (isLinkingStarted) { + isLinkingStarted = false + goToAccountLinked() + } + }) + + if (isGettingLinkUrl && uiState.haveValidLinkIntent()) { + LaunchedEffect(key1 = viewModel) { + isGettingLinkUrl = false + launcher.launch(uiState.linkUrl) + isLinkingStarted = true + } + } + PersonalInfoScreenContent( personalInfo = uiState.personalInfo, isLoading = uiState.isLoading, @@ -56,6 +82,10 @@ fun PersonalInfoScreen( onEmailClicked = onEmailClicked, removeConnection = { index -> viewModel.removeConnection(index) }, onManageAccountClicked = onManageAccountClicked, + addLinkToAccount = { + isGettingLinkUrl = true + viewModel.requestLinkUrl() + }, ) } @@ -68,10 +98,10 @@ fun PersonalInfoScreenContent( onEmailClicked: () -> Unit = {}, removeConnection: (Int) -> Unit = {}, onManageAccountClicked: (dateString: String) -> Unit = {}, + addLinkToAccount: () -> Unit = {}, ) = Column( verticalArrangement = Arrangement.Bottom, - modifier = Modifier - .verticalScroll(rememberScrollState()) + modifier = Modifier.verticalScroll(rememberScrollState()) ) { if (errorData != null) { AlertDialogWithSingleButton( @@ -86,9 +116,7 @@ fun PersonalInfoScreenContent( Text( style = MaterialTheme.typography.titleLarge.copy( textAlign = TextAlign.Start, color = ButtonGreen - ), - text = stringResource(R.string.personal_info_title), - modifier = Modifier.fillMaxWidth() + ), text = stringResource(R.string.personal_info_title), modifier = Modifier.fillMaxWidth() ) Spacer(Modifier.height(12.dp)) Text( @@ -151,6 +179,38 @@ fun PersonalInfoScreenContent( ) } + Spacer(Modifier.height(12.dp)) + OutlinedButton( + onClick = addLinkToAccount, + shape = RoundedCornerShape(CornerSize(6.dp)), + modifier = Modifier + .fillMaxWidth() + .sizeIn(minHeight = 72.dp) + ) { + Column(modifier = Modifier.weight(1f)) { + Text( + text = stringResource(R.string.personalinfo_add_role_institution), + style = MaterialTheme.typography.bodyLarge.copy( + textAlign = TextAlign.Start, + color = ButtonTextGrey, + fontWeight = FontWeight.Bold, + ) + ) + Text( + text = stringResource(R.string.personalinfo_add_via), + style = MaterialTheme.typography.bodyMedium.copy( + textAlign = TextAlign.Start, + color = ButtonTextGrey, + fontWeight = FontWeight.Light, + fontStyle = FontStyle.Italic + ) + ) + } + Image( + painter = painterResource(R.drawable.ic_plus), + contentDescription = "", + ) + } Spacer(Modifier.height(42.dp)) OutlinedButton( onClick = { onManageAccountClicked(personalInfo.dateCreated.getDateTimeString("EEEE, dd MMMM yyyy 'at' HH:MM")) }, @@ -163,8 +223,7 @@ fun PersonalInfoScreenContent( painter = painterResource(R.drawable.cog_icon), alignment = CenterStart, contentDescription = "", - modifier = Modifier - .padding(end = 48.dp) + modifier = Modifier.padding(start = 24.dp, end = 24.dp) ) Text( text = stringResource(R.string.personalinfo_manage_your_account), diff --git a/app/src/main/kotlin/nl/eduid/screens/personalinfo/PersonalInfoViewModel.kt b/app/src/main/kotlin/nl/eduid/screens/personalinfo/PersonalInfoViewModel.kt index 203e767d..0d9d7c47 100644 --- a/app/src/main/kotlin/nl/eduid/screens/personalinfo/PersonalInfoViewModel.kt +++ b/app/src/main/kotlin/nl/eduid/screens/personalinfo/PersonalInfoViewModel.kt @@ -1,5 +1,7 @@ package nl.eduid.screens.personalinfo +import android.content.Intent +import android.net.Uri import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -7,6 +9,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import nl.eduid.ErrorData import nl.eduid.di.model.UserDetails +import timber.log.Timber import javax.inject.Inject @HiltViewModel @@ -82,6 +85,46 @@ class PersonalInfoViewModel @Inject constructor(private val repository: Personal ) } + fun requestLinkUrl() = viewModelScope.launch { + val currentUiState = uiState.value ?: UiState() + uiState.postValue(currentUiState.copy(isLoading = true, linkUrl = null)) + try { + val response = repository.getStartLinkAccount() + if (response != null) { + uiState.postValue( + currentUiState.copy( + linkUrl = createLaunchIntent(response), isLoading = false + ) + ) + } else { + uiState.postValue( + currentUiState.copy( + isLoading = false, errorData = ErrorData( + "Failed to get link URL", + "Could not retrieve URL to link your current account" + ) + ) + ) + } + } catch (e: Exception) { + Timber.e(e, "Failed to get link account for current user") + uiState.postValue( + currentUiState.copy( + isLoading = false, errorData = ErrorData( + "Failed to get link URL", + "Could not retrieve URL to link your current account" + ) + ) + ) + } + } + + private fun createLaunchIntent(url: String): Intent { + val intent = Intent(Intent.ACTION_VIEW) + intent.data = Uri.parse(url) + return intent + } + private fun convertToUiData(userDetails: UserDetails): PersonalInfo { val dateCreated = userDetails.created val linkedAccounts = userDetails.linkedAccounts diff --git a/app/src/main/kotlin/nl/eduid/screens/personalinfo/UiState.kt b/app/src/main/kotlin/nl/eduid/screens/personalinfo/UiState.kt index 712fc130..ec4965c5 100644 --- a/app/src/main/kotlin/nl/eduid/screens/personalinfo/UiState.kt +++ b/app/src/main/kotlin/nl/eduid/screens/personalinfo/UiState.kt @@ -1,9 +1,13 @@ package nl.eduid.screens.personalinfo +import android.content.Intent import nl.eduid.ErrorData data class UiState( val personalInfo: PersonalInfo = PersonalInfo(), + val linkUrl: Intent? = null, val isLoading: Boolean = false, val errorData: ErrorData? = null, -) \ No newline at end of file +) { + fun haveValidLinkIntent() = !isLoading && errorData == null && linkUrl != null +} \ No newline at end of file diff --git a/app/src/main/res/drawable/plus_icon_gray.xml b/app/src/main/res/drawable/ic_plus.xml similarity index 100% rename from app/src/main/res/drawable/plus_icon_gray.xml rename to app/src/main/res/drawable/ic_plus.xml diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d9a2592c..d10bc2ff 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -203,6 +203,8 @@ At %s Role & institution Manage your account + Add role & institution + Proceed to add this via SURFConext Delete login details * Delete service