Updated on 2026-08-14
This commit is contained in:
parent
db08399066
commit
ae809ece30
28 changed files with 939 additions and 10 deletions
|
|
@ -80,4 +80,29 @@ fun String.capitalize(): String = replaceFirstChar { if (it.isLowerCase()) it.ti
|
|||
|
||||
fun String.orMaskWithStars(maskWithStars: Boolean): String {
|
||||
return if (maskWithStars) THREE_STARS else this
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a masked representation of the string for safe display.
|
||||
*
|
||||
* Rules:
|
||||
* - empty string: returned unchanged
|
||||
* - length <= 2: all characters are replaced with '*'
|
||||
* - length <= 4: keep the first and last characters, mask the middle
|
||||
* - length > 4: keep the first two and last two characters, mask the middle with "**"
|
||||
*
|
||||
* @return masked string according to the rules above
|
||||
*/
|
||||
@Suppress("MagicNumber")
|
||||
fun String.mask(): String {
|
||||
return when {
|
||||
this.isEmpty() -> this
|
||||
this.length <= 2 -> "*".repeat(this.length) // mask all if too short
|
||||
this.length <= 4 -> this.first() + "*".repeat(this.length - 2) + this.last()
|
||||
else -> {
|
||||
val prefix = this.take(2)
|
||||
val suffix = this.takeLast(2)
|
||||
"$prefix**$suffix"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -147,6 +147,8 @@ data class DialogMessage(
|
|||
}
|
||||
}
|
||||
|
||||
data class GlobalLoadingMessage(val isShow: Boolean) : EventMessage
|
||||
|
||||
/**
|
||||
* Shows a bottom sheet.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -2,13 +2,23 @@ package com.tangem.core.ui.message
|
|||
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.SnackbarDuration
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.SnackbarResult
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import com.tangem.core.ui.components.BasicDialog
|
||||
import com.tangem.core.ui.components.DialogButtonUM
|
||||
import com.tangem.core.ui.components.SpacerHMax
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetUM
|
||||
|
|
@ -17,6 +27,7 @@ import com.tangem.core.ui.event.EventEffect
|
|||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.LocalEventMessageHandler
|
||||
import com.tangem.core.ui.res.LocalSnackbarHostState
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
fun EventMessageEffect(
|
||||
|
|
@ -33,6 +44,7 @@ fun EventMessageEffect(
|
|||
var dialogMessage: DialogMessage? by remember { mutableStateOf(value = null) }
|
||||
var bottomSheetMessage: BottomSheetMessage? by remember { mutableStateOf(value = null) }
|
||||
var bottomSheetMessageV2: BottomSheetMessageV2? by remember { mutableStateOf(value = null) }
|
||||
var loadingMessage: GlobalLoadingMessage? by remember { mutableStateOf(value = null) }
|
||||
|
||||
EventEffect(event = messageEvent) { message ->
|
||||
when (message) {
|
||||
|
|
@ -51,6 +63,13 @@ fun EventMessageEffect(
|
|||
is ToastMessage -> {
|
||||
onShowToast(message, context)
|
||||
}
|
||||
is GlobalLoadingMessage -> {
|
||||
loadingMessage = if (message.isShow) {
|
||||
message
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -80,6 +99,34 @@ fun EventMessageEffect(
|
|||
onDismissRequest = { bottomSheetMessageV2 = null },
|
||||
)
|
||||
}
|
||||
|
||||
loadingMessage?.let {
|
||||
LoadingDialog()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LoadingDialog() {
|
||||
Dialog(
|
||||
onDismissRequest = {},
|
||||
properties = DialogProperties(
|
||||
dismissOnBackPress = false,
|
||||
dismissOnClickOutside = false,
|
||||
),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
SpacerHMax()
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||
color = TangemTheme.colors.icon.primary1,
|
||||
)
|
||||
SpacerHMax()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
package com.tangem.core.ui.extensions
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Test
|
||||
|
||||
class StringMaskTest {
|
||||
|
||||
@Test
|
||||
fun GIVEN_empty_string_WHEN_mask_THEN_returns_empty_string() {
|
||||
// GIVEN
|
||||
val input = ""
|
||||
|
||||
// WHEN
|
||||
val actual = input.mask()
|
||||
|
||||
// THEN
|
||||
assertThat(actual).isEqualTo("")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_one_char_WHEN_mask_THEN_returns_asterisk() {
|
||||
// GIVEN
|
||||
val input = "a"
|
||||
|
||||
// WHEN
|
||||
val actual = input.mask()
|
||||
|
||||
// THEN
|
||||
assertThat(actual).isEqualTo("*")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_two_chars_WHEN_mask_THEN_returns_two_asterisks() {
|
||||
// GIVEN
|
||||
val input = "ab"
|
||||
|
||||
// WHEN
|
||||
val actual = input.mask()
|
||||
|
||||
// THEN
|
||||
assertThat(actual).isEqualTo("**")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_three_chars_WHEN_mask_THEN_masks_middle_only() {
|
||||
// GIVEN
|
||||
val input = "abc"
|
||||
|
||||
// WHEN
|
||||
val actual = input.mask()
|
||||
|
||||
// THEN
|
||||
assertThat(actual).isEqualTo("a*c")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_four_chars_WHEN_mask_THEN_masks_middle_two() {
|
||||
// GIVEN
|
||||
val input = "abcd"
|
||||
|
||||
// WHEN
|
||||
val actual = input.mask()
|
||||
|
||||
// THEN
|
||||
assertThat(actual).isEqualTo("a**d")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_five_chars_WHEN_mask_THEN_keeps_edges_and_two_stars_in_middle() {
|
||||
// GIVEN
|
||||
val input = "abcde"
|
||||
|
||||
// WHEN
|
||||
val actual = input.mask()
|
||||
|
||||
// THEN
|
||||
assertThat(actual).isEqualTo("ab**de")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_seven_chars_WHEN_mask_THEN_keeps_two_on_each_side_and_two_stars() {
|
||||
// GIVEN
|
||||
val input = "abcdefg"
|
||||
|
||||
// WHEN
|
||||
val actual = input.mask()
|
||||
|
||||
// THEN
|
||||
assertThat(actual).isEqualTo("ab**fg")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue