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 8e01d253..eb1dfb0e 100644
--- a/app/src/main/kotlin/nl/eduid/screens/accountlinked/AccountLinkedScreen.kt
+++ b/app/src/main/kotlin/nl/eduid/screens/accountlinked/AccountLinkedScreen.kt
@@ -22,9 +22,9 @@ import nl.eduid.R
import nl.eduid.screens.personalinfo.PersonalInfo
import nl.eduid.screens.personalinfo.PersonalInfoViewModel
import nl.eduid.ui.AlertDialogWithSingleButton
+import nl.eduid.ui.ConnectionCard
import nl.eduid.ui.EduIdTopAppBar
import nl.eduid.ui.InfoField
-import nl.eduid.ui.InfoTab
import nl.eduid.ui.PrimaryButton
import nl.eduid.ui.theme.EduidAppAndroidTheme
import nl.eduid.ui.theme.TextGreen
@@ -109,15 +109,22 @@ private fun AccountLinkedContent(
label = stringResource(R.string.infotab_fullname)
)
Spacer(Modifier.height(16.dp))
+ if (personalInfo.institutionAccounts.isNotEmpty()) {
+ Text(
+ text = stringResource(R.string.infotab_role_institution),
+ style = MaterialTheme.typography.bodyLarge.copy(
+ textAlign = TextAlign.Start,
+ fontWeight = FontWeight.SemiBold,
+ ),
+ )
+ Spacer(Modifier.height(6.dp))
+ }
personalInfo.institutionAccounts.forEachIndexed { index, account ->
- InfoTab(
- header = if (index < 1) stringResource(R.string.infotab_role_institution) else "",
+ ConnectionCard(
title = account.role,
subtitle = stringResource(R.string.infotab_at, account.roleProvider),
institutionInfo = account,
- onClick = {},
- onDeleteButtonClicked = { removeConnection(index) },
- endIcon = R.drawable.chevron_down,
+ onRemoveConnection = { removeConnection(index) },
)
}
PrimaryButton(
diff --git a/app/src/main/kotlin/nl/eduid/screens/personalinfo/PersonalInfoData.kt b/app/src/main/kotlin/nl/eduid/screens/personalinfo/PersonalInfoData.kt
index c49ca035..37ecba46 100644
--- a/app/src/main/kotlin/nl/eduid/screens/personalinfo/PersonalInfoData.kt
+++ b/app/src/main/kotlin/nl/eduid/screens/personalinfo/PersonalInfoData.kt
@@ -19,7 +19,6 @@ data class PersonalInfo(
val roleProvider: String,
val institution: String,
val affiliationString: String,
- val status: InfoStatus = InfoStatus.Final,
val createdStamp: Long,
val expiryStamp: Long,
)
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 5f5ddbca..411f9748 100644
--- a/app/src/main/kotlin/nl/eduid/screens/personalinfo/PersonalInfoScreen.kt
+++ b/app/src/main/kotlin/nl/eduid/screens/personalinfo/PersonalInfoScreen.kt
@@ -37,9 +37,9 @@ import nl.eduid.ErrorData
import nl.eduid.R
import nl.eduid.screens.firsttimedialog.LinkAccountContract
import nl.eduid.ui.AlertDialogWithSingleButton
+import nl.eduid.ui.ConnectionCard
import nl.eduid.ui.EduIdTopAppBar
import nl.eduid.ui.InfoField
-import nl.eduid.ui.InfoTab
import nl.eduid.ui.getDateTimeString
import nl.eduid.ui.theme.ButtonGreen
import nl.eduid.ui.theme.ButtonTextGrey
@@ -140,19 +140,15 @@ fun PersonalInfoScreenContent(
}
Spacer(Modifier.height(12.dp))
InfoField(
- title = personalInfo.name,
- subtitle = if (personalInfo.nameProvider == null) {
+ title = personalInfo.name, subtitle = if (personalInfo.nameProvider == null) {
stringResource(R.string.infotab_providedby_you)
} else {
stringResource(R.string.infotab_providedby, personalInfo.nameProvider)
- },
- onClick = onNameClicked,
- endIcon = if (personalInfo.nameProvider == null) {
+ }, onClick = onNameClicked, endIcon = if (personalInfo.nameProvider == null) {
R.drawable.edit_icon
} else {
R.drawable.shield_tick_blue
- },
- label = stringResource(R.string.infotab_name)
+ }, label = stringResource(R.string.infotab_name)
)
Spacer(Modifier.height(16.dp))
InfoField(
@@ -163,15 +159,22 @@ fun PersonalInfoScreenContent(
label = stringResource(R.string.infotab_email),
)
Spacer(Modifier.height(16.dp))
+ if (personalInfo.institutionAccounts.isNotEmpty()) {
+ Text(
+ text = stringResource(R.string.infotab_role_institution),
+ style = MaterialTheme.typography.bodyLarge.copy(
+ textAlign = TextAlign.Start,
+ fontWeight = FontWeight.SemiBold,
+ ),
+ )
+ Spacer(Modifier.height(6.dp))
+ }
personalInfo.institutionAccounts.forEachIndexed { index, account ->
- InfoTab(
- header = if (index < 1) stringResource(R.string.infotab_role_institution) else "",
+ ConnectionCard(
title = account.role,
subtitle = stringResource(R.string.infotab_at, account.roleProvider),
institutionInfo = account,
- onClick = {},
- onDeleteButtonClicked = { removeConnection(index) },
- endIcon = R.drawable.chevron_down,
+ onRemoveConnection = { removeConnection(index) },
)
}
diff --git a/app/src/main/kotlin/nl/eduid/ui/ConnectionCard.kt b/app/src/main/kotlin/nl/eduid/ui/ConnectionCard.kt
new file mode 100644
index 00000000..9e75e7a7
--- /dev/null
+++ b/app/src/main/kotlin/nl/eduid/ui/ConnectionCard.kt
@@ -0,0 +1,217 @@
+package nl.eduid.ui
+
+import androidx.compose.animation.animateContentSize
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.sizeIn
+import androidx.compose.foundation.shape.CornerSize
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.Divider
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.constraintlayout.compose.ConstraintLayout
+import androidx.constraintlayout.compose.Dimension
+import nl.eduid.R
+import nl.eduid.screens.personalinfo.PersonalInfo
+import nl.eduid.ui.theme.BlueButton
+import nl.eduid.ui.theme.BlueText
+import nl.eduid.ui.theme.ButtonRed
+import nl.eduid.ui.theme.EduidAppAndroidTheme
+import nl.eduid.ui.theme.TextBlack
+import nl.eduid.ui.theme.TextGrayScale
+import java.util.Locale
+
+@Composable
+fun ConnectionCard(
+ title: String,
+ subtitle: String,
+ institutionInfo: PersonalInfo.InstitutionAccount? = null,
+ isExpanded: Boolean = false,
+ onRemoveConnection: (id: String) -> Unit = { },
+) {
+ val isOpen = remember { mutableStateOf(isExpanded) }
+ Spacer(Modifier.height(6.dp))
+ Box(modifier = Modifier
+ .clip(RoundedCornerShape(6.dp))
+ .border(
+ width = 3.dp, color = BlueButton
+ )
+ .sizeIn(minHeight = 72.dp)
+ .fillMaxWidth()
+ .clickable {
+ if (institutionInfo != null) {
+ isOpen.value = !isOpen.value
+ }
+ }
+ .animateContentSize()) {
+ ConstraintLayout(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(start = 18.dp, end = 18.dp, top = 12.dp, bottom = 12.dp)
+ ) {
+ val (titleArea, endImage, expandedArea) = createRefs()
+
+ Column(horizontalAlignment = Alignment.Start,
+ modifier = Modifier.constrainAs(titleArea) {
+ top.linkTo(parent.top)
+ start.linkTo(parent.start)
+ end.linkTo(endImage.start)
+ width = Dimension.fillToConstraints
+ }) {
+ Text(
+ text = title.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() },
+ style = MaterialTheme.typography.bodyLarge.copy(
+ textAlign = TextAlign.Start,
+ fontWeight = FontWeight.Bold,
+ lineHeight = 20.sp
+ ),
+ )
+ Spacer(Modifier.height(4.dp))
+ Text(
+ text = subtitle,
+ style = MaterialTheme.typography.bodySmall.copy(
+ textAlign = TextAlign.Start,
+ color = TextGrayScale,
+ ),
+ )
+ }
+ if (isOpen.value && institutionInfo != null) {
+ Column(horizontalAlignment = Alignment.Start,
+ modifier = Modifier.constrainAs(expandedArea) {
+ top.linkTo(titleArea.bottom, margin = 24.dp)
+ start.linkTo(parent.start)
+ end.linkTo(parent.end)
+ }) {
+ InstitutionInfoBlock(institutionInfo, onRemoveConnection)
+ }
+ }
+ Image(painter = painterResource(R.drawable.chevron_down),
+ contentDescription = "",
+ modifier = Modifier
+ .constrainAs(endImage) {
+ top.linkTo(titleArea.top)
+ bottom.linkTo(titleArea.bottom)
+ end.linkTo(parent.end)
+ }
+ .padding(start = 12.dp))
+ }
+ }
+ Spacer(Modifier.height(16.dp))
+}
+
+@Composable
+private fun InstitutionInfoBlock(
+ institutionInfo: PersonalInfo.InstitutionAccount,
+ onDeleteButtonClicked: (id: String) -> Unit,
+) = Column(
+ Modifier.fillMaxWidth()
+) {
+ InfoRow(
+ label = stringResource(
+ R.string.personalinfo_verified_by_on,
+ institutionInfo.institution,
+ institutionInfo.createdStamp.getDateString()
+ )
+ )
+ InfoRow(
+ label = stringResource(R.string.personalinfo_institution),
+ value = institutionInfo.institution
+ )
+ InfoRow(
+ label = stringResource(R.string.personalinfo_affiliations),
+ value = institutionInfo.affiliationString
+ )
+ InfoRow(
+ label = stringResource(R.string.personalinfo_expires),
+ value = institutionInfo.expiryStamp.getDateString()
+ )
+ Button(
+ shape = RoundedCornerShape(CornerSize(6.dp)),
+ onClick = { onDeleteButtonClicked(institutionInfo.id) },
+ border = BorderStroke(1.dp, Color.Red),
+ colors = ButtonDefaults.outlinedButtonColors(contentColor = ButtonRed),
+ modifier = Modifier
+ .sizeIn(minHeight = 48.dp)
+ .fillMaxWidth(),
+ ) {
+ Text(
+ text = stringResource(R.string.infotab_remove_connection),
+ style = MaterialTheme.typography.bodyLarge.copy(
+ color = ButtonRed, fontWeight = FontWeight.SemiBold
+ )
+ )
+ }
+}
+
+@Composable
+private fun InfoRow(label: String, value: String = "") {
+ Row(
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(
+ modifier = Modifier.weight(1f),
+ text = label,
+ style = MaterialTheme.typography.bodyMedium.copy(
+ textAlign = TextAlign.Start,
+ color = BlueText,
+ ),
+ )
+ if (value.isNotEmpty()) {
+ Text(
+ modifier = Modifier.weight(1f),
+ text = value,
+ style = MaterialTheme.typography.bodyMedium.copy(
+ textAlign = TextAlign.Start,
+ color = BlueText,
+ ),
+ )
+ }
+ }
+ Spacer(Modifier.height(12.dp))
+ Divider(color = TextBlack, thickness = 1.dp)
+ Spacer(Modifier.height(12.dp))
+}
+
+@Preview
+@Composable
+private fun PreviewConnectionCard() = EduidAppAndroidTheme {
+ ConnectionCard(
+ title = "Librarian",
+ subtitle = "Urangutan",
+ institutionInfo = PersonalInfo.InstitutionAccount(
+ id = "id",
+ role = "Librarian",
+ roleProvider = "Library",
+ institution = "Unseen University",
+ affiliationString = "Librarian",
+ createdStamp = 0L,
+ expiryStamp = 0L
+ ),
+ isExpanded = true
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index a7c21f26..683a1e72 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -236,6 +236,11 @@
Beheer jouw gegevens
Voeg een rol & instelling toe
Voeg dit toe via SURFConext
+ Geverifieerd door %1$s op %2$s.
+ Instelling
+ Aansluiting(en)
+ Link verloopt
+
Verwijder inloggegevens *
Verwijder sleutel
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 7de29b51..50720f73 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -236,6 +236,10 @@
Manage your account
Add role & institution
Proceed to add this via SURFConext
+ Verified by %1$s on %2$s.
+ Institution
+ Affiliation(s)
+ Link expires
Delete login details *
Delete key