From df89bc0bfad2b21c756a94548a05030a769ae5a9 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 28 Jul 2025 17:23:23 +0300 Subject: [PATCH 01/17] POCopyButton --- .../sdk/ui/core/component/POCopyButton.kt | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCopyButton.kt diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCopyButton.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCopyButton.kt new file mode 100644 index 000000000..a1d2856f3 --- /dev/null +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCopyButton.kt @@ -0,0 +1,63 @@ +package com.processout.sdk.ui.core.component + +import androidx.compose.animation.animateContentSize +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalClipboardManager +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.unit.Dp +import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi +import com.processout.sdk.ui.core.shared.image.PODrawableImage +import com.processout.sdk.ui.core.theme.ProcessOutTheme.dimensions +import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +/** @suppress */ +@ProcessOutInternalApi +@Composable +fun POCopyButton( + textToCopy: String, + copyText: String, + copiedText: String, + modifier: Modifier = Modifier, + style: POButton.Style = POCopyButton.default, + copyIcon: PODrawableImage? = null, + copiedIcon: PODrawableImage? = null, + iconSize: Dp = dimensions.iconSizeSmall +) { + val clipboardManager = LocalClipboardManager.current + var isCopied by remember { mutableStateOf(false) } + val coroutineScope = rememberCoroutineScope() + var timerJob by remember { mutableStateOf(null) } + POButton( + text = if (isCopied) copiedText else copyText, + onClick = { + clipboardManager.setText(AnnotatedString(textToCopy)) + isCopied = true + timerJob?.cancel() + timerJob = coroutineScope.launch { + delay(timeMillis = 3000) + isCopied = false + } + }, + modifier = modifier.animateContentSize(), + style = style, + icon = if (isCopied) copiedIcon else copyIcon, + iconSize = iconSize + ) +} + +/** @suppress */ +@ProcessOutInternalApi +object POCopyButton { + + val default: POButton.Style + @Composable get() = POButton.secondary2.let { + it.copy( + normal = it.normal.copy(paddingHorizontal = spacing.space12), + disabled = it.disabled.copy(paddingHorizontal = spacing.space12) + ) + } +} From 2c2aad74572fa7f590c4880b3f0b0a64f2d857f7 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 28 Jul 2025 19:54:22 +0300 Subject: [PATCH 02/17] Icons --- .../sdk/ui/core/component/POCopyButton.kt | 16 ++++++++++++++-- ui-core/src/main/res/drawable/po_icon_check.xml | 13 +++++++++++++ ui-core/src/main/res/drawable/po_icon_copy.xml | 17 +++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 ui-core/src/main/res/drawable/po_icon_check.xml create mode 100644 ui-core/src/main/res/drawable/po_icon_copy.xml diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCopyButton.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCopyButton.kt index a1d2856f3..1835f4ae3 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCopyButton.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCopyButton.kt @@ -6,8 +6,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.unit.Dp +import com.processout.sdk.ui.core.R import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi import com.processout.sdk.ui.core.shared.image.PODrawableImage +import com.processout.sdk.ui.core.shared.image.POImageRenderingMode import com.processout.sdk.ui.core.theme.ProcessOutTheme.dimensions import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing import kotlinx.coroutines.Job @@ -23,8 +25,8 @@ fun POCopyButton( copiedText: String, modifier: Modifier = Modifier, style: POButton.Style = POCopyButton.default, - copyIcon: PODrawableImage? = null, - copiedIcon: PODrawableImage? = null, + copyIcon: PODrawableImage? = POCopyButton.CopyIcon, + copiedIcon: PODrawableImage? = POCopyButton.CopiedIcon, iconSize: Dp = dimensions.iconSizeSmall ) { val clipboardManager = LocalClipboardManager.current @@ -60,4 +62,14 @@ object POCopyButton { disabled = it.disabled.copy(paddingHorizontal = spacing.space12) ) } + + internal val CopyIcon = PODrawableImage( + resId = R.drawable.po_icon_copy, + renderingMode = POImageRenderingMode.TEMPLATE + ) + + internal val CopiedIcon = PODrawableImage( + resId = R.drawable.po_icon_check, + renderingMode = POImageRenderingMode.TEMPLATE + ) } diff --git a/ui-core/src/main/res/drawable/po_icon_check.xml b/ui-core/src/main/res/drawable/po_icon_check.xml new file mode 100644 index 000000000..5dfaacb03 --- /dev/null +++ b/ui-core/src/main/res/drawable/po_icon_check.xml @@ -0,0 +1,13 @@ + + + diff --git a/ui-core/src/main/res/drawable/po_icon_copy.xml b/ui-core/src/main/res/drawable/po_icon_copy.xml new file mode 100644 index 000000000..26f419010 --- /dev/null +++ b/ui-core/src/main/res/drawable/po_icon_copy.xml @@ -0,0 +1,17 @@ + + + + + + From 731feaefc1e3cdfc5a19465a36061808ff02c936 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 28 Jul 2025 20:41:07 +0300 Subject: [PATCH 03/17] Animation --- .../sdk/ui/core/component/POCopyButton.kt | 55 +++++++++++++------ 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCopyButton.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCopyButton.kt index 1835f4ae3..e87407c3c 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCopyButton.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCopyButton.kt @@ -1,6 +1,9 @@ +@file:Suppress("MayBeConstant") + package com.processout.sdk.ui.core.component -import androidx.compose.animation.animateContentSize +import androidx.compose.animation.* +import androidx.compose.animation.core.tween import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalClipboardManager @@ -8,6 +11,8 @@ import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.unit.Dp import com.processout.sdk.ui.core.R import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi +import com.processout.sdk.ui.core.component.POCopyButton.FadeAnimationDurationMillis +import com.processout.sdk.ui.core.component.POCopyButton.SizeAnimationDurationMillis import com.processout.sdk.ui.core.shared.image.PODrawableImage import com.processout.sdk.ui.core.shared.image.POImageRenderingMode import com.processout.sdk.ui.core.theme.ProcessOutTheme.dimensions @@ -31,24 +36,37 @@ fun POCopyButton( ) { val clipboardManager = LocalClipboardManager.current var isCopied by remember { mutableStateOf(false) } - val coroutineScope = rememberCoroutineScope() var timerJob by remember { mutableStateOf(null) } - POButton( - text = if (isCopied) copiedText else copyText, - onClick = { - clipboardManager.setText(AnnotatedString(textToCopy)) - isCopied = true - timerJob?.cancel() - timerJob = coroutineScope.launch { - delay(timeMillis = 3000) - isCopied = false + val coroutineScope = rememberCoroutineScope() + AnimatedContent( + targetState = isCopied, + transitionSpec = { + fadeIn( + tween(durationMillis = FadeAnimationDurationMillis) + ) togetherWith fadeOut( + tween(durationMillis = FadeAnimationDurationMillis) + ) using SizeTransform { _, _ -> + tween(durationMillis = SizeAnimationDurationMillis) } - }, - modifier = modifier.animateContentSize(), - style = style, - icon = if (isCopied) copiedIcon else copyIcon, - iconSize = iconSize - ) + } + ) { isCopiedAnimated -> + POButton( + text = if (isCopiedAnimated) copiedText else copyText, + onClick = { + clipboardManager.setText(AnnotatedString(textToCopy)) + isCopied = true + timerJob?.cancel() + timerJob = coroutineScope.launch { + delay(timeMillis = 3000) + isCopied = false + } + }, + modifier = modifier, + style = style, + icon = if (isCopiedAnimated) copiedIcon else copyIcon, + iconSize = iconSize + ) + } } /** @suppress */ @@ -72,4 +90,7 @@ object POCopyButton { resId = R.drawable.po_icon_check, renderingMode = POImageRenderingMode.TEMPLATE ) + + internal val FadeAnimationDurationMillis = 300 + internal val SizeAnimationDurationMillis = 400 } From 18fde739ebb2c36496cf87ca2e60cb2640b041bf Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 28 Jul 2025 20:54:51 +0300 Subject: [PATCH 04/17] Localizations --- sdk/src/main/res/values-ar/strings.xml | 2 ++ sdk/src/main/res/values-fr/strings.xml | 2 ++ sdk/src/main/res/values-pl/strings.xml | 2 ++ sdk/src/main/res/values-pt/strings.xml | 2 ++ sdk/src/main/res/values/strings.xml | 2 ++ 5 files changed, 10 insertions(+) diff --git a/sdk/src/main/res/values-ar/strings.xml b/sdk/src/main/res/values-ar/strings.xml index 91311d4f8..ac6a36b68 100644 --- a/sdk/src/main/res/values-ar/strings.xml +++ b/sdk/src/main/res/values-ar/strings.xml @@ -27,6 +27,8 @@ إلغاء لقد دفعت تم + نسخ + تم النسخ! تم إرسال التفاصيل في انتظار التأكيد يرجى الانتظار حتى %s دقيقة diff --git a/sdk/src/main/res/values-fr/strings.xml b/sdk/src/main/res/values-fr/strings.xml index 64d8684cb..ec497cebe 100644 --- a/sdk/src/main/res/values-fr/strings.xml +++ b/sdk/src/main/res/values-fr/strings.xml @@ -27,6 +27,8 @@ Annuler J\'ai payé Terminé + Copier + Copié ! Détails envoyés En attente de confirmation Veuillez patienter jusqu\'à %s minutes diff --git a/sdk/src/main/res/values-pl/strings.xml b/sdk/src/main/res/values-pl/strings.xml index 8a3c6382b..04f1b44b1 100644 --- a/sdk/src/main/res/values-pl/strings.xml +++ b/sdk/src/main/res/values-pl/strings.xml @@ -27,6 +27,8 @@ Anuluj Płatność wykonana Gotowe + Skopiuj + Skopiowano! Szczegóły przesłane Oczekiwanie na potwierdzenie Poczekaj maksymalnie %s minut diff --git a/sdk/src/main/res/values-pt/strings.xml b/sdk/src/main/res/values-pt/strings.xml index d6aaa4f1f..918b28d72 100644 --- a/sdk/src/main/res/values-pt/strings.xml +++ b/sdk/src/main/res/values-pt/strings.xml @@ -27,6 +27,8 @@ Cancelar Já paguei Concluído + Copiar + Copiado! Detalhes enviados Aguardando confirmação Aguarde até %s minutos diff --git a/sdk/src/main/res/values/strings.xml b/sdk/src/main/res/values/strings.xml index 9b50a19f8..2b8821b97 100644 --- a/sdk/src/main/res/values/strings.xml +++ b/sdk/src/main/res/values/strings.xml @@ -27,6 +27,8 @@ Cancel I\'ve paid Done + Copy + Copied! Details submitted Waiting for confirmation Please wait for up to %s minutes From 672fdf6e5895be66cc82a02436459c5e9172ecdc Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 29 Jul 2025 14:44:53 +0300 Subject: [PATCH 05/17] POLabeledContent --- .../sdk/ui/core/component/POLabeledContent.kt | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POLabeledContent.kt diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POLabeledContent.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POLabeledContent.kt new file mode 100644 index 000000000..798320cf9 --- /dev/null +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POLabeledContent.kt @@ -0,0 +1,52 @@ +package com.processout.sdk.ui.core.component + +import androidx.compose.foundation.layout.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi +import com.processout.sdk.ui.core.theme.ProcessOutTheme.colors +import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing +import com.processout.sdk.ui.core.theme.ProcessOutTheme.typography + +/** @suppress */ +@ProcessOutInternalApi +@Composable +fun POLabeledContent( + label: String, + modifier: Modifier = Modifier, + labelStyle: POText.Style = POText.Style( + color = colors.text.placeholder, + textStyle = typography.s12(FontWeight.Medium) + ), + trailingContent: @Composable () -> Unit = {}, + trailingContentAlignment: Alignment = Alignment.TopStart, + content: @Composable () -> Unit +) { + Row( + modifier = modifier.height(IntrinsicSize.Min), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Column( + modifier = Modifier + .fillMaxHeight() + .padding(end = spacing.space8) + .weight(1f, fill = false), + verticalArrangement = Arrangement.spacedBy(spacing.space6) + ) { + POText( + text = label, + color = labelStyle.color, + style = labelStyle.textStyle + ) + content() + } + Box( + modifier = Modifier.fillMaxHeight(), + contentAlignment = trailingContentAlignment + ) { + trailingContent() + } + } +} From 386419a65f7abbbfff711b3552a99a39aab18ed8 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 29 Jul 2025 18:16:39 +0300 Subject: [PATCH 06/17] border1: Color --- .../src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt index 182f24586..4f0a371aa 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt @@ -94,6 +94,7 @@ data class POColors( @Immutable data class Border( + val border1: Color, val border2: Color, val border4: Color ) @@ -164,6 +165,7 @@ val POLightColorPalette = POColors( toastError = Color(0xFFFDE3DE) ), border = Border( + border1 = Color(0x0F121314), border2 = Color(0x14121314), border4 = Color(0x29212222) ) @@ -234,6 +236,7 @@ val PODarkColorPalette = POColors( toastError = Color(0xFF511511) ), border = Border( + border1 = Color(0x14F6F8FB), border2 = Color(0x1FF6F8FB), border4 = Color(0x33F6F8FB) ) From 3231d9bf235c3fd58a36497a43e1b82be5136bfb Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Wed, 30 Jul 2025 12:58:30 +0300 Subject: [PATCH 07/17] POGroupedContent --- .../sdk/ui/core/component/POGroupedContent.kt | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POGroupedContent.kt diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POGroupedContent.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POGroupedContent.kt new file mode 100644 index 000000000..4139c4c8f --- /dev/null +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POGroupedContent.kt @@ -0,0 +1,95 @@ +package com.processout.sdk.ui.core.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.HorizontalDivider +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi +import com.processout.sdk.ui.core.state.POImmutableList +import com.processout.sdk.ui.core.theme.ProcessOutTheme.colors +import com.processout.sdk.ui.core.theme.ProcessOutTheme.shapes +import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing +import com.processout.sdk.ui.core.theme.ProcessOutTheme.typography + +/** @suppress */ +@ProcessOutInternalApi +@Composable +fun POGroupedContent( + items: POImmutableList<@Composable () -> Unit>, + modifier: Modifier = Modifier, + style: POGroupedContent.Style = POGroupedContent.default, + title: String? = null +) { + Column(modifier = modifier) { + if (!title.isNullOrBlank()) { + POText( + text = title, + modifier = Modifier.padding(bottom = spacing.space12), + color = style.title.color, + style = style.title.textStyle + ) + } + Column( + modifier = Modifier + .fillMaxWidth() + .border( + width = style.border.width, + color = style.border.color, + shape = style.shape + ) + .clip(shape = style.shape) + .background(style.backgroundColor) + .padding(style.border.width) + ) { + items.elements.forEachIndexed { index, item -> + Box(modifier = Modifier.padding(spacing.space16)) { + item() + } + if (index != items.elements.lastIndex) { + HorizontalDivider( + modifier = Modifier.padding(horizontal = spacing.space4), + thickness = 1.dp, + color = style.dividerColor + ) + } + } + } + } +} + +/** @suppress */ +@ProcessOutInternalApi +object POGroupedContent { + + @Immutable + data class Style( + val title: POText.Style, + val shape: Shape, + val border: POBorderStroke, + val dividerColor: Color, + val backgroundColor: Color + ) + + val default: Style + @Composable get() = Style( + title = POText.Style( + color = colors.text.primary, + textStyle = typography.s16(FontWeight.Medium) + ), + shape = shapes.roundedCorners6, + border = POBorderStroke(width = 1.5.dp, color = colors.input.borderDefault2), + dividerColor = colors.border.border1, + backgroundColor = colors.surface.default + ) +} From 531d963779a5bef2cd6be1952fd05d1f6164f968 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Wed, 30 Jul 2025 15:39:13 +0300 Subject: [PATCH 08/17] POLabeledContentStyle & POGroupedContentStyle --- .../sdk/ui/core/style/POGroupedContentStyle.kt | 15 +++++++++++++++ .../sdk/ui/core/style/POLabeledContentStyle.kt | 10 ++++++++++ 2 files changed, 25 insertions(+) create mode 100644 ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POGroupedContentStyle.kt create mode 100644 ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POLabeledContentStyle.kt diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POGroupedContentStyle.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POGroupedContentStyle.kt new file mode 100644 index 000000000..37e05ab5c --- /dev/null +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POGroupedContentStyle.kt @@ -0,0 +1,15 @@ +package com.processout.sdk.ui.core.style + +import android.os.Parcelable +import androidx.annotation.ColorRes +import kotlinx.parcelize.Parcelize + +@Parcelize +data class POGroupedContentStyle( + val title: POTextStyle, + val border: POBorderStyle, + @ColorRes + val dividerColorResId: Int, + @ColorRes + val backgroundColorResId: Int +) : Parcelable diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POLabeledContentStyle.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POLabeledContentStyle.kt new file mode 100644 index 000000000..fce03201a --- /dev/null +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POLabeledContentStyle.kt @@ -0,0 +1,10 @@ +package com.processout.sdk.ui.core.style + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class POLabeledContentStyle( + val label: POTextStyle, + val text: POTextStyle +) : Parcelable From e1020edae1483d72155ada28807b4d34d12b282e Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Wed, 30 Jul 2025 16:30:22 +0300 Subject: [PATCH 09/17] Custom POGroupedContent.Style --- .../sdk/ui/core/component/POGroupedContent.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POGroupedContent.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POGroupedContent.kt index 4139c4c8f..0fb21f86d 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POGroupedContent.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POGroupedContent.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.HorizontalDivider import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable @@ -13,10 +14,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.res.colorResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi import com.processout.sdk.ui.core.state.POImmutableList +import com.processout.sdk.ui.core.style.POGroupedContentStyle import com.processout.sdk.ui.core.theme.ProcessOutTheme.colors import com.processout.sdk.ui.core.theme.ProcessOutTheme.shapes import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing @@ -92,4 +95,16 @@ object POGroupedContent { dividerColor = colors.border.border1, backgroundColor = colors.surface.default ) + + @Composable + fun custom(style: POGroupedContentStyle) = Style( + title = POText.custom(style = style.title), + shape = RoundedCornerShape(size = style.border.radiusDp.dp), + border = POBorderStroke( + width = style.border.widthDp.dp, + color = colorResource(id = style.border.colorResId) + ), + dividerColor = colorResource(id = style.dividerColorResId), + backgroundColor = colorResource(id = style.backgroundColorResId) + ) } From 66d276bcb8ea3aa8ea4fe2b60335abffa33248af Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Wed, 30 Jul 2025 16:42:13 +0300 Subject: [PATCH 10/17] Updated PONativeAlternativePaymentConfiguration.Style --- .../sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt index 627e2b891..162c48c11 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt @@ -388,7 +388,9 @@ data class PONativeAlternativePaymentConfiguration( * Custom style. * * @param[title] Title style. - * @param[bodyText] Body text style, such as customer instruction. + * @param[bodyText] Body text style, such as customer instructions. + * @param[labeledContent] Labeled content style, such as customer instructions. + * @param[groupedContent] Grouped content style, such as customer instructions. * @param[field] Field style. * @param[codeField] Code field style. * @param[radioField] Radio field style. @@ -409,6 +411,8 @@ data class PONativeAlternativePaymentConfiguration( data class Style( val title: POTextStyle? = null, val bodyText: POTextStyle? = null, + val labeledContent: POLabeledContentStyle? = null, + val groupedContent: POGroupedContentStyle? = null, val field: POFieldStyle? = null, val codeField: POFieldStyle? = null, val radioField: PORadioFieldStyle? = null, From f59a71c6f47f552ce8e47434400eeb080e541bed Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Wed, 30 Jul 2025 17:35:47 +0300 Subject: [PATCH 11/17] Apply CopyableText --- .../ui/napm/NativeAlternativePaymentScreen.kt | 103 +++++++++++++++++- 1 file changed, 97 insertions(+), 6 deletions(-) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt index 39dcf8db4..b250e181f 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt @@ -25,6 +25,7 @@ import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.rememberNestedScrollInteropConnection import androidx.compose.ui.res.colorResource @@ -53,14 +54,17 @@ import com.processout.sdk.ui.core.state.POActionState import com.processout.sdk.ui.core.state.POImmutableList import com.processout.sdk.ui.core.state.POPhoneNumberFieldState import com.processout.sdk.ui.core.style.POAxis +import com.processout.sdk.ui.core.style.POLabeledContentStyle import com.processout.sdk.ui.core.theme.ProcessOutTheme import com.processout.sdk.ui.core.theme.ProcessOutTheme.colors +import com.processout.sdk.ui.core.theme.ProcessOutTheme.dimensions import com.processout.sdk.ui.core.theme.ProcessOutTheme.shapes import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing import com.processout.sdk.ui.core.theme.ProcessOutTheme.typography import com.processout.sdk.ui.napm.NativeAlternativePaymentEvent.* import com.processout.sdk.ui.napm.NativeAlternativePaymentScreen.AnimationDurationMillis import com.processout.sdk.ui.napm.NativeAlternativePaymentScreen.ContentTransitionSpec +import com.processout.sdk.ui.napm.NativeAlternativePaymentScreen.LabeledContentStyle import com.processout.sdk.ui.napm.NativeAlternativePaymentScreen.LogoHeight import com.processout.sdk.ui.napm.NativeAlternativePaymentScreen.SuccessStyle import com.processout.sdk.ui.napm.NativeAlternativePaymentViewModelState.* @@ -339,12 +343,10 @@ private fun Elements( descriptionStyle = style.errorMessageBox, modifier = Modifier.fillMaxWidth() ) - is InstructionMessage -> AndroidTextView( - text = element.value, - style = style.bodyText, - modifier = Modifier.fillMaxWidth(), - selectable = true, - linksClickable = true + is InstructionMessage -> InstructionMessage( + message = element, + bodyTextStyle = style.bodyText, + labeledContentStyle = style.labeledContent ) is Image -> Image( image = element, @@ -614,6 +616,61 @@ private fun PhoneNumberField( } } +@Composable +private fun InstructionMessage( + message: InstructionMessage, + bodyTextStyle: AndroidTextView.Style, + labeledContentStyle: LabeledContentStyle +) { + if (message.label == null) { + AndroidTextView( + text = message.value, + style = bodyTextStyle, + modifier = Modifier.fillMaxWidth(), + selectable = true, + linksClickable = true + ) + } else { + CopyableText( + label = message.label, + text = message.value, + style = labeledContentStyle, + modifier = Modifier.fillMaxWidth() + ) + } +} + +@Composable +private fun CopyableText( + label: String, + text: String, + style: LabeledContentStyle, + modifier: Modifier = Modifier +) { + val context = LocalContext.current + POLabeledContent( + label = label, + labelStyle = style.label, + modifier = modifier, + trailingContent = { + POCopyButton( + textToCopy = text, + copyText = context.getString(com.processout.sdk.R.string.po_native_apm_copy_button_text), + copiedText = context.getString(com.processout.sdk.R.string.po_native_apm_copied_button_text), + modifier = Modifier.requiredHeightIn(min = dimensions.buttonIconSizeSmall), + style = style.copyButton + ) + }, + trailingContentAlignment = Alignment.Center + ) { + POText( + text = text, + color = style.text.color, + style = style.text.textStyle + ) + } +} + @Composable private fun Image( image: Image, @@ -781,6 +838,8 @@ internal object NativeAlternativePaymentScreen { data class Style( val title: POText.Style, val bodyText: AndroidTextView.Style, + val labeledContent: LabeledContentStyle, + val groupedContent: POGroupedContent.Style, val field: POField.Style, val codeField: POField.Style, val radioField: PORadioField.Style, @@ -797,6 +856,13 @@ internal object NativeAlternativePaymentScreen { val dragHandleColor: Color ) + @Immutable + data class LabeledContentStyle( + val label: POText.Style, + val text: POText.Style, + val copyButton: POButton.Style + ) + @Immutable data class SuccessStyle( val title: POText.Style, @@ -822,6 +888,10 @@ internal object NativeAlternativePaymentScreen { controlsTintColor = controlsTintColor ?: colors.text.primary ) } ?: AndroidTextView.default, + labeledContent = custom?.labeledContent?.custom() ?: defaultLabeledContent, + groupedContent = custom?.groupedContent?.let { + POGroupedContent.custom(style = it) + } ?: POGroupedContent.default, field = custom?.field?.let { POField.custom(style = it) } ?: POField.default2, @@ -865,6 +935,27 @@ internal object NativeAlternativePaymentScreen { ) } + private val defaultLabeledContent: LabeledContentStyle + @Composable get() = LabeledContentStyle( + label = POText.Style( + color = colors.text.placeholder, + textStyle = typography.s12(FontWeight.Medium) + ), + text = POText.Style( + color = colors.text.primary, + textStyle = typography.s15(FontWeight.Medium) + ), + copyButton = POCopyButton.default + ) + + @Composable + private fun POLabeledContentStyle.custom() = + LabeledContentStyle( + label = POText.custom(style = label), + text = POText.custom(style = text), + copyButton = defaultLabeledContent.copyButton + ) + private val defaultSuccess: SuccessStyle @Composable get() = SuccessStyle( title = POText.Style( From 4acdc654cb78f447c829d70520cec2a4eb98be61 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Wed, 30 Jul 2025 18:03:49 +0300 Subject: [PATCH 12/17] Update copy button default style --- .../sdk/ui/core/component/POCopyButton.kt | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCopyButton.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCopyButton.kt index e87407c3c..f74b06342 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCopyButton.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCopyButton.kt @@ -8,6 +8,7 @@ import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.Dp import com.processout.sdk.ui.core.R import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi @@ -17,6 +18,7 @@ import com.processout.sdk.ui.core.shared.image.PODrawableImage import com.processout.sdk.ui.core.shared.image.POImageRenderingMode import com.processout.sdk.ui.core.theme.ProcessOutTheme.dimensions import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing +import com.processout.sdk.ui.core.theme.ProcessOutTheme.typography import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -76,8 +78,18 @@ object POCopyButton { val default: POButton.Style @Composable get() = POButton.secondary2.let { it.copy( - normal = it.normal.copy(paddingHorizontal = spacing.space12), - disabled = it.disabled.copy(paddingHorizontal = spacing.space12) + normal = it.normal.copy( + text = it.normal.text.copy( + textStyle = typography.s13(FontWeight.Medium) + ), + paddingHorizontal = spacing.space10 + ), + disabled = it.disabled.copy( + text = it.disabled.text.copy( + textStyle = typography.s13(FontWeight.Medium) + ), + paddingHorizontal = spacing.space10 + ) ) } From a80b7d10d2ad58062adb0d673ff0da281307b08b Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Wed, 30 Jul 2025 18:06:09 +0300 Subject: [PATCH 13/17] Copy: delay(timeMillis = 2500) --- .../kotlin/com/processout/sdk/ui/core/component/POCopyButton.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCopyButton.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCopyButton.kt index f74b06342..42169ef54 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCopyButton.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCopyButton.kt @@ -59,7 +59,7 @@ fun POCopyButton( isCopied = true timerJob?.cancel() timerJob = coroutineScope.launch { - delay(timeMillis = 3000) + delay(timeMillis = 2500) isCopied = false } }, From 53139809a06fb80ae4bd32348bc24a83c4208c72 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Wed, 30 Jul 2025 18:39:19 +0300 Subject: [PATCH 14/17] CopyableInstructionMessage --- .../ui/napm/NativeAlternativePaymentScreen.kt | 54 ++++++------------- .../napm/NativeAlternativePaymentViewModel.kt | 17 ++++-- .../NativeAlternativePaymentViewModelState.kt | 10 +++- 3 files changed, 39 insertions(+), 42 deletions(-) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt index b250e181f..a8797fccd 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt @@ -25,7 +25,6 @@ import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.onGloballyPositioned -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.rememberNestedScrollInteropConnection import androidx.compose.ui.res.colorResource @@ -343,10 +342,17 @@ private fun Elements( descriptionStyle = style.errorMessageBox, modifier = Modifier.fillMaxWidth() ) - is InstructionMessage -> InstructionMessage( + is InstructionMessage -> AndroidTextView( + text = element.value, + style = style.bodyText, + modifier = Modifier.fillMaxWidth(), + selectable = true, + linksClickable = true + ) + is CopyableInstructionMessage -> CopyableInstructionMessage( message = element, - bodyTextStyle = style.bodyText, - labeledContentStyle = style.labeledContent + style = style.labeledContent, + modifier = Modifier.fillMaxWidth() ) is Image -> Image( image = element, @@ -617,46 +623,20 @@ private fun PhoneNumberField( } @Composable -private fun InstructionMessage( - message: InstructionMessage, - bodyTextStyle: AndroidTextView.Style, - labeledContentStyle: LabeledContentStyle -) { - if (message.label == null) { - AndroidTextView( - text = message.value, - style = bodyTextStyle, - modifier = Modifier.fillMaxWidth(), - selectable = true, - linksClickable = true - ) - } else { - CopyableText( - label = message.label, - text = message.value, - style = labeledContentStyle, - modifier = Modifier.fillMaxWidth() - ) - } -} - -@Composable -private fun CopyableText( - label: String, - text: String, +private fun CopyableInstructionMessage( + message: CopyableInstructionMessage, style: LabeledContentStyle, modifier: Modifier = Modifier ) { - val context = LocalContext.current POLabeledContent( - label = label, + label = message.label, labelStyle = style.label, modifier = modifier, trailingContent = { POCopyButton( - textToCopy = text, - copyText = context.getString(com.processout.sdk.R.string.po_native_apm_copy_button_text), - copiedText = context.getString(com.processout.sdk.R.string.po_native_apm_copied_button_text), + textToCopy = message.value, + copyText = message.copyText, + copiedText = message.copiedText, modifier = Modifier.requiredHeightIn(min = dimensions.buttonIconSizeSmall), style = style.copyButton ) @@ -664,7 +644,7 @@ private fun CopyableText( trailingContentAlignment = Alignment.Center ) { POText( - text = text, + text = message.value, color = style.text.color, style = style.text.textStyle ) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt index ff86aae60..e6f2f3404 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt @@ -474,14 +474,23 @@ internal class NativeAlternativePaymentViewModel private constructor( private fun Instruction.map(): NativeAlternativePaymentViewModelState.Element = when (this) { - is Instruction.Message -> InstructionMessage( - label = label, - value = value - ) + is Instruction.Message -> map() is Instruction.Image -> Image(value = value) is Instruction.Barcode -> map() } + private fun Instruction.Message.map() = + if (label == null) { + InstructionMessage(value = value) + } else { + CopyableInstructionMessage( + label = label, + value = value, + copyText = app.getString(R.string.po_native_apm_copy_button_text), + copiedText = app.getString(R.string.po_native_apm_copied_button_text) + ) + } + private fun Instruction.Barcode.map() = Barcode( image = bitmap, diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModelState.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModelState.kt index c9e62e54b..f66d7632a 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModelState.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModelState.kt @@ -80,7 +80,15 @@ internal sealed interface NativeAlternativePaymentViewModelState { data class PhoneNumberField(val state: POPhoneNumberFieldState) : Element @Immutable - data class InstructionMessage(val label: String?, val value: String) : Element + data class InstructionMessage(val value: String) : Element + + @Immutable + data class CopyableInstructionMessage( + val label: String, + val value: String, + val copyText: String, + val copiedText: String + ) : Element @Immutable data class Image(val value: POImageResource) : Element From 586e6cbac05793416e7fbd4217a1f99d85e0d3aa Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 31 Jul 2025 12:40:48 +0300 Subject: [PATCH 15/17] InstructionGroup VM element --- .../ui/napm/NativeAlternativePaymentScreen.kt | 12 ++++++---- .../napm/NativeAlternativePaymentViewModel.kt | 22 ++++++++++++++----- .../NativeAlternativePaymentViewModelState.kt | 17 ++++++++++---- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt index a8797fccd..dd7088c77 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt @@ -342,14 +342,14 @@ private fun Elements( descriptionStyle = style.errorMessageBox, modifier = Modifier.fillMaxWidth() ) - is InstructionMessage -> AndroidTextView( + is Message -> AndroidTextView( text = element.value, style = style.bodyText, modifier = Modifier.fillMaxWidth(), selectable = true, linksClickable = true ) - is CopyableInstructionMessage -> CopyableInstructionMessage( + is CopyableMessage -> CopyableMessage( message = element, style = style.labeledContent, modifier = Modifier.fillMaxWidth() @@ -363,6 +363,10 @@ private fun Elements( onEvent = onEvent, style = style ) + is InstructionGroup -> { + // TODO(v2) + } + else -> {} } } } @@ -623,8 +627,8 @@ private fun PhoneNumberField( } @Composable -private fun CopyableInstructionMessage( - message: CopyableInstructionMessage, +private fun CopyableMessage( + message: CopyableMessage, style: LabeledContentStyle, modifier: Modifier = Modifier ) { diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt index e6f2f3404..8b3de00ae 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt @@ -21,9 +21,10 @@ import com.processout.sdk.ui.core.state.POActionState.Confirmation import com.processout.sdk.ui.core.transformation.POPhoneNumberVisualTransformation import com.processout.sdk.ui.core.transformation.POPhoneNumberVisualTransformation.PhoneNumberFormat import com.processout.sdk.ui.napm.NativeAlternativePaymentInteractorState.* -import com.processout.sdk.ui.napm.NativeAlternativePaymentViewModelState.Content +import com.processout.sdk.ui.napm.NativeAlternativePaymentInteractorState.Element +import com.processout.sdk.ui.napm.NativeAlternativePaymentInteractorState.Loading +import com.processout.sdk.ui.napm.NativeAlternativePaymentViewModelState.* import com.processout.sdk.ui.napm.NativeAlternativePaymentViewModelState.Element.* -import com.processout.sdk.ui.napm.NativeAlternativePaymentViewModelState.Stage import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.CancelButton import com.processout.sdk.ui.shared.extension.currentAppLocale import com.processout.sdk.ui.shared.extension.map @@ -206,7 +207,7 @@ internal class NativeAlternativePaymentViewModel private constructor( when (element) { is Element.Form -> element.form.parameterDefinitions.map(fields) is Element.Instruction -> listOf(element.instruction.map()) - is Element.InstructionGroup -> emptyList() // TODO(v2) + is Element.InstructionGroup -> listOf(element.map()) } } return POImmutableList(elements) @@ -481,9 +482,9 @@ internal class NativeAlternativePaymentViewModel private constructor( private fun Instruction.Message.map() = if (label == null) { - InstructionMessage(value = value) + Message(value = value) } else { - CopyableInstructionMessage( + CopyableMessage( label = label, value = value, copyText = app.getString(R.string.po_native_apm_copy_button_text), @@ -518,6 +519,17 @@ internal class NativeAlternativePaymentViewModel private constructor( } else null ) + private fun Element.InstructionGroup.map() = + InstructionGroup( + label = label, + instructions = POImmutableList( + instructions.mapNotNull { + val mapped = it.map() + if (mapped is InstructionElement) mapped else null + } + ) + ) + private fun Invoice.price(): String? = try { val formatter = NumberFormat.getCurrencyInstance() diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModelState.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModelState.kt index f66d7632a..01ed268a9 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModelState.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModelState.kt @@ -80,24 +80,33 @@ internal sealed interface NativeAlternativePaymentViewModelState { data class PhoneNumberField(val state: POPhoneNumberFieldState) : Element @Immutable - data class InstructionMessage(val value: String) : Element + data class Message(val value: String) : InstructionElement @Immutable - data class CopyableInstructionMessage( + data class CopyableMessage( val label: String, val value: String, val copyText: String, val copiedText: String - ) : Element + ) : InstructionElement @Immutable - data class Image(val value: POImageResource) : Element + data class Image(val value: POImageResource) : InstructionElement @Immutable data class Barcode( val image: Bitmap, val saveBarcodeAction: POActionState, val confirmationDialog: ConfirmationDialogState? + ) : InstructionElement + + @Immutable + data class InstructionGroup( + val label: String?, + val instructions: POImmutableList ) : Element } + + @Immutable + interface InstructionElement : Element } From ed3cc22c40113f000ee34069e734d83991ce0792 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 31 Jul 2025 13:30:16 +0300 Subject: [PATCH 16/17] Apply POGroupedContent --- .../ui/napm/NativeAlternativePaymentScreen.kt | 69 ++++++++++++++++++- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt index dd7088c77..cdd302165 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt @@ -363,9 +363,13 @@ private fun Elements( onEvent = onEvent, style = style ) - is InstructionGroup -> { - // TODO(v2) - } + is InstructionGroup -> InstructionGroup( + group = element, + onEvent = onEvent, + style = style, + isLightTheme = isLightTheme, + modifier = Modifier.fillMaxWidth() + ) else -> {} } } @@ -731,6 +735,65 @@ private fun Barcode( } } +@Composable +private fun InstructionGroup( + group: InstructionGroup, + onEvent: (NativeAlternativePaymentEvent) -> Unit, + style: NativeAlternativePaymentScreen.Style, + isLightTheme: Boolean, + modifier: Modifier = Modifier +) { + val instructions = group.instructions.elements + val items: List<@Composable () -> Unit> = instructions.mapNotNull { instruction -> + when (instruction) { + is Message -> { + { + AndroidTextView( + text = instruction.value, + style = style.bodyText, + modifier = Modifier.fillMaxWidth(), + selectable = true, + linksClickable = true + ) + } + } + is CopyableMessage -> { + { + CopyableMessage( + message = instruction, + style = style.labeledContent, + modifier = Modifier.fillMaxWidth() + ) + } + } + is Image -> { + { + Image( + image = instruction, + isLightTheme = isLightTheme + ) + } + } + is Barcode -> { + { + Barcode( + barcode = instruction, + onEvent = onEvent, + style = style + ) + } + } + else -> null + } + } + POGroupedContent( + title = group.label, + items = POImmutableList(items), + modifier = modifier, + style = style.groupedContent + ) +} + @Composable private fun SuccessContent( state: Stage.Completed, From bb622db94a91969e49e8611965065149a5e66a49 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 31 Jul 2025 19:37:48 +0300 Subject: [PATCH 17/17] Fix header --- .../ui/napm/NativeAlternativePaymentScreen.kt | 49 ++++++++++++------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt index cdd302165..4de228143 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt @@ -31,6 +31,7 @@ import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.lifecycle.Lifecycle import coil.compose.AsyncImage @@ -167,7 +168,11 @@ private fun Header( modifier: Modifier = Modifier, withDragHandle: Boolean = true ) { - Box(modifier = modifier.fillMaxWidth()) { + Box( + modifier = modifier + .fillMaxWidth() + .height(IntrinsicSize.Min) + ) { if (withDragHandle) { PODragHandle( modifier = Modifier @@ -176,8 +181,16 @@ private fun Header( color = dragHandleColor ) } + var showLogo by remember { mutableStateOf(true) } + val logoUrl = logo?.let { + if (isLightTheme) { + it.lightUrl.raster + } else { + it.darkUrl?.raster ?: it.lightUrl.raster + } + } AnimatedVisibility( - visible = logo != null || !title.isNullOrBlank(), + visible = showLogo && !logoUrl.isNullOrBlank() || !title.isNullOrBlank(), enter = fadeIn(animationSpec = tween(durationMillis = AnimationDurationMillis)), exit = fadeOut(animationSpec = tween(durationMillis = AnimationDurationMillis)), ) { @@ -199,27 +212,29 @@ private fun Header( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { - val logoUrl = logo?.let { - if (isLightTheme) { - it.lightUrl.raster - } else { - it.darkUrl?.raster ?: it.lightUrl.raster + Box { + if (showLogo && !logoUrl.isNullOrBlank()) { + AsyncImage( + model = logoUrl, + contentDescription = null, + modifier = Modifier + .requiredHeight(LogoHeight) + .padding(end = spacing.space16), + contentScale = ContentScale.FillHeight, + onError = { + showLogo = false + } + ) } } - AsyncImage( - model = logoUrl, - contentDescription = null, - modifier = Modifier - .weight(0.8f, fill = false) - .requiredHeight(LogoHeight), - contentScale = ContentScale.FillHeight - ) - if (title != null) { + if (!title.isNullOrBlank()) { POText( text = title, modifier = Modifier.weight(1f, fill = false), color = titleStyle.color, - style = titleStyle.textStyle + style = titleStyle.textStyle, + overflow = TextOverflow.Ellipsis, + maxLines = 2 ) } }