Skip to content

Commit

Permalink
TIQR-475: Linking success screen
Browse files Browse the repository at this point in the history
  • Loading branch information
dzolnai committed Nov 19, 2024
1 parent ef654d6 commit 661c06e
Show file tree
Hide file tree
Showing 19 changed files with 405 additions and 90 deletions.
7 changes: 6 additions & 1 deletion app/src/main/kotlin/nl/eduid/di/api/EduIdApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import nl.eduid.di.model.EmailChangeRequest
import nl.eduid.di.model.EnrollResponse
import nl.eduid.di.model.IdpScoping
import nl.eduid.di.model.InstitutionNameResponse
import nl.eduid.di.model.LinkedAccount
import nl.eduid.di.model.LinkedAccountUpdateRequest
import nl.eduid.di.model.RequestEduIdAccount
import nl.eduid.di.model.RequestPhoneCode
Expand Down Expand Up @@ -78,6 +77,12 @@ interface EduIdApi {
@Query("schac_home") schac_home: String,
): Response<InstitutionNameResponse>

@PUT("/mobile/api/sp/prefer-linked-account")
suspend fun preferLinkedAccount(
@Body request: LinkedAccountUpdateRequest,
): Response<InstitutionNameResponse>


@GET("/mobile/api/sp/confirm-email")
suspend fun confirmEmail(
@Query("h") hash: String,
Expand Down
23 changes: 22 additions & 1 deletion app/src/main/kotlin/nl/eduid/di/assist/DataAssistant.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package nl.eduid.di.assist

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.firstOrNull
Expand Down Expand Up @@ -28,7 +29,7 @@ constructor(
private val cachedDetails = MutableStateFlow<SaveableResult<UserDetails>?>(null)
val observableDetails: Flow<SaveableResult<UserDetails>?> = cachedDetails.map { knownValue ->
if (knownValue == null) {
val fromNetwork = try { loadInCache() } catch (ex: Exception) { return@map handleError(ex) }
val fromNetwork = try { loadInCache() } catch (ex: Exception) { return@map handleError(ex) }
fromNetwork.fold(
onSuccess = {
SaveableResult.Success(it)
Expand All @@ -42,6 +43,16 @@ constructor(
}
}

suspend fun refreshDetails() = withContext(dispatcher) {
val fromNetwork = loadInCache()
cachedDetails.emit(
fromNetwork.fold(
onSuccess = { SaveableResult.Success(it) },
onFailure = { handleError(it) }
)
)
}

private suspend fun handleError(ex: Throwable): SaveableResult.LoadError {
return if (ex is UnauthorizedException) {
storageRepository.clearInvalidAuth()
Expand Down Expand Up @@ -144,6 +155,16 @@ constructor(
)
}

suspend fun preferLinkedAccount(request: LinkedAccountUpdateRequest) = withContext(dispatcher) {
if (infoRepository.preferLinkedAccount(request)) {
// Also refresh the details, if it was a success, so we have the latest data
refreshDetails()
true
} else {
false
}
}

suspend fun getExternalAccountLinkResult(idpScoping: IdpScoping, bankId: String?): String? = withContext(dispatcher) {
val result = infoRepository.getExternalAccountLinkResult(idpScoping, bankId)
result.fold(
Expand Down
5 changes: 3 additions & 2 deletions app/src/main/kotlin/nl/eduid/di/model/EduIdModels.kt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ data class UserDetails(
val givenName: String,
val chosenName: String,
val familyName: String,
val dateOfBirth: Long?,
val usePassword: Boolean,
val usePublicKey: Boolean,
val forgottenPassword: Boolean,
Expand Down Expand Up @@ -150,8 +151,8 @@ data class Registration(
@Parcelize
@JsonClass(generateAdapter = true)
data class InstitutionNameResponse(
val displayNameEn: String,
val displayNameNl: String,
val displayNameEn: String?,
val displayNameNl: String?,
) : Parcelable

@Parcelize
Expand Down
15 changes: 15 additions & 0 deletions app/src/main/kotlin/nl/eduid/di/model/ModelToState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ fun LinkedAccount.mapToInstitutionAccount(): PersonalInfo.InstitutionAccount? =
familyName = this.familyName,
createdStamp = this.createdAt,
expiryStamp = this.expiresAt,
updateRequest = LinkedAccountUpdateRequest(
eduPersonPrincipalName = this.eduPersonPrincipalName,
subjectId = this.subjectId,
external = false,
idpScoping = null
)
)
}

Expand All @@ -39,6 +45,12 @@ fun ExternalLinkedAccount.mapToInstitutionAccount(): PersonalInfo.InstitutionAcc
dateOfBirth = this.dateOfBirth?.let { LocalDate.ofInstant(Instant.ofEpochMilli(it), ZoneOffset.UTC) },
createdStamp = this.createdAt,
expiryStamp = this.expiresAt,
updateRequest = LinkedAccountUpdateRequest(
eduPersonPrincipalName = null,
subjectId = this.subjectId,
external = true,
idpScoping = this.idpScoping
)
)


Expand All @@ -57,6 +69,8 @@ fun UserDetails.mapToPersonalInfo(): PersonalInfo {
} ?: "${this.chosenName} ${this.familyName}"

val email: String = this.email
val dateOfBirth: LocalDate? = this.dateOfBirth?.let { LocalDate.ofInstant(Instant.ofEpochMilli(it), ZoneOffset.UTC) }

val linkedInternalAccounts = linkedAccounts.mapNotNull { account ->
account.mapToInstitutionAccount()
}.toImmutableList()
Expand All @@ -77,6 +91,7 @@ fun UserDetails.mapToPersonalInfo(): PersonalInfo {
givenName = givenNameConfirmer?.givenName,
givenNameConfirmedBy = givenNameConfirmer?.institutionIdentifier
),
dateOfBirth = dateOfBirth,
nameProvider = nameProvider,
email = email,
linkedInternalAccounts = linkedInternalAccounts,
Expand Down
16 changes: 12 additions & 4 deletions app/src/main/kotlin/nl/eduid/graphs/MainGraph.kt
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ fun MainGraph(
composable(Graph.FIRST_TIME_DIALOG) { entry ->
val viewModel = hiltViewModel<LinkAccountViewModel>(entry)
FirstTimeDialogRoute(viewModel = viewModel,
goToAccountLinked = { navController.goToWithPopCurrent(AccountLinked.route) },
goToAccountLinked = { navController.goToWithPopCurrent(AccountLinked.routeWithRegistrationFlowParam(true)) },
skipThis = {
navController.navigate(Graph.HOME_PAGE) {
//Clear existing home page that has no account
Expand All @@ -332,7 +332,8 @@ fun MainGraph(
})
}
composable(//region Account Linked
route = AccountLinked.route, deepLinks = listOf(
route = AccountLinked.routeWithArgs,
deepLinks = listOf(
navDeepLink {
uriPattern = AccountLinked.getUriPatternOK(baseUrl)
},
Expand All @@ -342,7 +343,8 @@ fun MainGraph(
navDeepLink {
uriPattern = AccountLinked.getUriPatternExpired(baseUrl)
},
)
),
arguments = AccountLinked.arguments
) { entry ->
val deepLinkIntent: Intent? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
entry.arguments?.getParcelable(
Expand All @@ -360,7 +362,13 @@ fun MainGraph(
AccountLinkedScreen(
viewModel = viewModel,
result = result,
continueToHome = { navController.goToWithPopCurrent(Graph.HOME_PAGE) },
continueToHome = {
navController.goToWithPopCurrent(Graph.HOME_PAGE)
},
continueToPersonalInfo = {
navController.goToWithPopCurrent(Graph.HOME_PAGE) //Clear the entire backstack
navController.navigate(Graph.PERSONAL_INFO)
}
)
}//endregion
//endregion
Expand Down
16 changes: 16 additions & 0 deletions app/src/main/kotlin/nl/eduid/graphs/Routes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavType
import androidx.navigation.navArgument
import nl.eduid.di.model.SelfAssertedName
import nl.eduid.graphs.RequestEduIdLinkSent.LOGIN_REASON
import nl.eduid.graphs.RequestEduIdLinkSent.emailArg
import nl.eduid.graphs.RequestEduIdLinkSent.reasonArg
import java.io.UnsupportedEncodingException

object Graph {
Expand Down Expand Up @@ -48,6 +51,19 @@ object AccountLinked {
fun getUriPatternOK(baseUrl: String) = "$baseUrl/client/mobile/account-linked"
fun getUriPatternFailed(baseUrl: String) = "$baseUrl/client/mobile/eppn-already-linked"
fun getUriPatternExpired(baseUrl: String) = "$baseUrl/client/mobile/expired"

const val isRegistrationFlowArg = "is_registration_flow"

const val routeWithArgs = "${route}?isRegistrationFlow={$isRegistrationFlowArg}"
val arguments = listOf(navArgument(isRegistrationFlowArg) {
type = NavType.BoolType
nullable = false
defaultValue = false
})

fun routeWithRegistrationFlowParam(isRegistrationFlow: Boolean) =
"$route/isRegistrationFlow=$isRegistrationFlow"

}

object VerifiedPersonalInfoRoute {
Expand Down
Loading

0 comments on commit 661c06e

Please sign in to comment.