Skip to content

Commit

Permalink
feat: RQES redesign
Browse files Browse the repository at this point in the history
Modifications in SelectionOptionUi model to act as a generic UI option model that can carry an event, using it to define the items for document, qtsp and certificate selection. Equivalent modifications in Success screen, also using now the ContentHeader. Imported WrapImage, WrapText, AppIconAndText, RelyingParty components. LocalizableKey updated with new wordings. Drawables and text resources for content description added.

Signed-off-by: Christos Kaitatzis <[email protected]>
  • Loading branch information
ckaitatzis committed Feb 4, 2025
1 parent 24a6945 commit d4de0ab
Show file tree
Hide file tree
Showing 19 changed files with 911 additions and 246 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import eu.europa.ec.eudi.rqesui.infrastructure.config.data.DocumentData
import eu.europa.ec.eudi.rqesui.infrastructure.config.data.QtspData
import eu.europa.ec.eudi.rqesui.infrastructure.config.data.toCertificatesData
import eu.europa.ec.eudi.rqesui.infrastructure.provider.ResourceProvider
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.net.URL
Expand Down Expand Up @@ -78,6 +79,7 @@ internal interface RqesController {
internal class RqesControllerImpl(
private val eudiRQESUi: EudiRQESUi,
private val resourceProvider: ResourceProvider,
private val dispatcher: CoroutineDispatcher = Dispatchers.IO
) : RqesController {

private val genericErrorMsg
Expand Down Expand Up @@ -164,7 +166,7 @@ internal class RqesControllerImpl(
}

override suspend fun getServiceAuthorizationUrl(rqesService: RQESService): EudiRqesGetServiceAuthorizationUrlPartialState {
return withContext(Dispatchers.IO) {
return withContext(dispatcher) {
runCatching {
val authorizationUrl = rqesService.getServiceAuthorizationUrl()
.getOrThrow()
Expand All @@ -181,7 +183,7 @@ internal class RqesControllerImpl(
}

override suspend fun authorizeService(): EudiRqesAuthorizeServicePartialState {
return withContext(Dispatchers.IO) {
return withContext(dispatcher) {
runCatching {
safeLet(
eudiRQESUi.getRqesService(),
Expand Down Expand Up @@ -216,7 +218,7 @@ internal class RqesControllerImpl(
}

override suspend fun getAvailableCertificates(authorizedService: Authorized): EudiRqesGetCertificatesPartialState {
return withContext(Dispatchers.IO) {
return withContext(dispatcher) {
runCatching {
val certificates = authorizedService.listCredentials()
.getOrThrow()
Expand Down Expand Up @@ -251,7 +253,7 @@ internal class RqesControllerImpl(
authorizedService: Authorized,
certificateData: CertificateData
): EudiRqesGetCredentialAuthorizationUrlPartialState {
return withContext(Dispatchers.IO) {
return withContext(dispatcher) {
runCatching {
eudiRQESUi.getSessionData().file?.let { safeSelectedFile ->

Expand Down Expand Up @@ -295,7 +297,7 @@ internal class RqesControllerImpl(
}

override suspend fun authorizeCredential(): EudiRqesAuthorizeCredentialPartialState {
return withContext(Dispatchers.IO) {
return withContext(dispatcher) {
runCatching {
safeLet(
getAuthorizedService(),
Expand Down Expand Up @@ -323,7 +325,7 @@ internal class RqesControllerImpl(
}

override suspend fun signDocuments(authorizedCredential: RQESService.CredentialAuthorized): EudiRqesSignDocumentsPartialState {
return withContext(Dispatchers.IO) {
return withContext(dispatcher) {
runCatching {
val signedDocuments = authorizedCredential.signDocuments().getOrThrow()
EudiRqesSignDocumentsPartialState.Success(signedDocuments = signedDocuments)
Expand All @@ -341,7 +343,7 @@ internal class RqesControllerImpl(
originalDocumentName: String,
signedDocuments: SignedDocuments,
): EudiRqesSaveSignedDocumentsPartialState {
return withContext(Dispatchers.IO) {
return withContext(dispatcher) {
runCatching {
val uris = mutableListOf<Uri>()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ enum class LocalizableKey {
SigningCertificates,
Success,
SuccessfullySignedDocument,
SuccessDescription,
SignedBy,
View,
Close,
Expand Down Expand Up @@ -90,6 +91,7 @@ enum class LocalizableKey {
SelectCertificateSubtitle -> "The signing certificate is used to verify your identity and is linked to your electronic signature."
Success -> "Success!"
SuccessfullySignedDocument -> "You successfully signed your document"
SuccessDescription -> "You have successfully signed your document."
SignedBy -> "Signed by: $ARGUMENTS_SEPARATOR"
View -> "VIEW"
Close -> "Close"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2023 European Commission
*
* Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European
* Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work
* except in compliance with the Licence.
*
* You may obtain a copy of the Licence at:
* https://joinup.ec.europa.eu/software/page/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the Licence for the specific language
* governing permissions and limitations under the Licence.
*/

package eu.europa.ec.eudi.rqesui.presentation.entities

import eu.europa.ec.eudi.rqesui.presentation.architecture.ViewEvent

internal data class ButtonActionUi<T : ViewEvent>(
val buttonText: String,
val event: T
)
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,18 @@
package eu.europa.ec.eudi.rqesui.presentation.entities

import androidx.compose.ui.graphics.Color
import eu.europa.ec.eudi.rqesui.infrastructure.config.data.DocumentData
import eu.europa.ec.eudi.rqesui.infrastructure.config.data.QtspData
import eu.europa.ec.eudi.rqesui.presentation.architecture.ViewEvent
import eu.europa.ec.eudi.rqesui.presentation.ui.component.IconData

internal data class SelectionItemUi(
val documentData: DocumentData?,
val qtspData: QtspData?,

internal data class SelectionOptionUi<T : ViewEvent>(
val overlineText: String? = null,
val mainText: String? = null,
val subtitle: String? = null,
val action: String? = null,
val actionText: String? = null,
val leadingIcon: IconData? = null,
val leadingIconTint: Color? = null,
val trailingIcon: IconData? = null,
val trailingIconTint: Color? = null,
val enabled: Boolean = true
val enabled: Boolean = true,
val event: T?,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright (c) 2023 European Commission
*
* Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European
* Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work
* except in compliance with the Licence.
*
* You may obtain a copy of the Licence at:
* https://joinup.ec.europa.eu/software/page/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the Licence for the specific language
* governing permissions and limitations under the Licence.
*/

package eu.europa.ec.eudi.rqesui.presentation.ui.component

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import eu.europa.ec.eudi.rqesui.presentation.ui.component.preview.PreviewTheme
import eu.europa.ec.eudi.rqesui.presentation.ui.component.preview.ThemeModePreviews
import eu.europa.ec.eudi.rqesui.presentation.ui.component.utils.SPACING_SMALL
import eu.europa.ec.eudi.rqesui.presentation.ui.component.wrap.WrapImage


internal data class AppIconAndTextData(
val appIcon: IconData = AppIcons.LogoPlain,
val appText: IconData = AppIcons.LogoText,
)

@Composable
internal fun AppIconAndText(
modifier: Modifier = Modifier,
appIconAndTextData: AppIconAndTextData
) {
Row(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(
space = SPACING_SMALL.dp,
alignment = Alignment.CenterHorizontally
),
verticalAlignment = Alignment.Top
) {
WrapImage(iconData = appIconAndTextData.appIcon)
WrapImage(iconData = appIconAndTextData.appText)
}
}

@ThemeModePreviews
@Composable
private fun AppIconAndTextPreview() {
PreviewTheme {
AppIconAndText(
appIconAndTextData = AppIconAndTextData(
appIcon = AppIcons.LogoPlain,
appText = AppIcons.LogoText,
)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ internal object AppIcons {
)

val Verified: IconData = IconData(
resourceId = R.drawable.ic_verified,
resourceId = R.drawable.ic_rqes_verified,
contentDescriptionId = R.string.content_description_verified_icon,
imageVector = null
)
Expand Down Expand Up @@ -110,4 +110,16 @@ internal object AppIcons {
contentDescriptionId = R.string.content_description_selection_step_icon,
imageVector = null
)

val LogoPlain: IconData = IconData(
resourceId = R.drawable.ic_logo_plain,
contentDescriptionId = R.string.content_description_logo_plain_icon,
imageVector = null
)

val LogoText: IconData = IconData(
resourceId = R.drawable.ic_logo_text,
contentDescriptionId = R.string.content_description_logo_text_icon,
imageVector = null
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright (c) 2023 European Commission
*
* Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European
* Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work
* except in compliance with the Licence.
*
* You may obtain a copy of the Licence at:
* https://joinup.ec.europa.eu/software/page/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the Licence for the specific language
* governing permissions and limitations under the Licence.
*/

package eu.europa.ec.eudi.rqesui.presentation.ui.component

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import eu.europa.ec.eudi.rqesui.infrastructure.theme.values.success
import eu.europa.ec.eudi.rqesui.presentation.ui.component.preview.PreviewTheme
import eu.europa.ec.eudi.rqesui.presentation.ui.component.preview.TextLengthPreviewProvider
import eu.europa.ec.eudi.rqesui.presentation.ui.component.preview.ThemeModePreviews
import eu.europa.ec.eudi.rqesui.presentation.ui.component.wrap.TextConfig
import eu.europa.ec.eudi.rqesui.presentation.ui.component.wrap.WrapIcon
import eu.europa.ec.eudi.rqesui.presentation.ui.component.wrap.WrapText

/**
* Data class representing information about a Relying Party.
*
* @property isVerified A boolean indicating whether the Relying Party is verified.
* @property name The name of the Relying Party.
*/
data class RelyingPartyData(
val isVerified: Boolean,
val name: String,
)

@Composable
fun RelyingParty(
modifier: Modifier = Modifier,
relyingPartyData: RelyingPartyData,
) {
val commonTextAlign = TextAlign.Center

Column(
modifier = modifier,
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
with(relyingPartyData) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
if (isVerified) {
WrapIcon(
modifier = Modifier.size(20.dp),
iconData = AppIcons.Verified,
customTint = MaterialTheme.colorScheme.success,
)
}
WrapText(
modifier = Modifier.wrapContentWidth(),
text = name,
textConfig = TextConfig(
style = MaterialTheme.typography.titleMedium,
textAlign = commonTextAlign,
)
)
}

}
}
}

@ThemeModePreviews
@Composable
private fun RelyingPartyPreview(
@PreviewParameter(TextLengthPreviewProvider::class) text: String
) {
PreviewTheme {
RelyingParty(
relyingPartyData = RelyingPartyData(
isVerified = true,
name = "Relying Party Name: $text",
)
)
}
}
Loading

0 comments on commit d4de0ab

Please sign in to comment.