diff --git a/app/src/main/java/to/bitkit/ui/components/BottomSheet.kt b/app/src/main/java/to/bitkit/ui/components/BottomSheet.kt index 8d74e1a93..1ea07354b 100644 --- a/app/src/main/java/to/bitkit/ui/components/BottomSheet.kt +++ b/app/src/main/java/to/bitkit/ui/components/BottomSheet.kt @@ -53,7 +53,7 @@ fun BottomSheet( contentAlignment = Alignment.Center, modifier = Modifier .fillMaxWidth() - .background(color = Colors.Gray6) + .background(color = Colors.White08) ) { SheetDragHandle() } diff --git a/app/src/main/java/to/bitkit/ui/components/Button.kt b/app/src/main/java/to/bitkit/ui/components/Button.kt index 55d3ddcfc..f18aecdc4 100644 --- a/app/src/main/java/to/bitkit/ui/components/Button.kt +++ b/app/src/main/java/to/bitkit/ui/components/Button.kt @@ -16,18 +16,21 @@ import androidx.compose.material.icons.filled.Home import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import to.bitkit.ui.shared.util.primaryButtonStyle import to.bitkit.ui.theme.AppButtonDefaults import to.bitkit.ui.theme.AppThemeSurface import to.bitkit.ui.theme.Colors @@ -57,41 +60,68 @@ fun PrimaryButton( size: ButtonSize = ButtonSize.Large, enabled: Boolean = true, fullWidth: Boolean = true, - color: Color = Colors.White16, + color: Color? = null, ) { val contentPadding = PaddingValues(horizontal = size.horizontalPadding.takeIf { text != null } ?: 0.dp) + val buttonShape = MaterialTheme.shapes.large + Button( onClick = onClick, enabled = enabled && !isLoading, - colors = AppButtonDefaults.primaryColors.copy(containerColor = color), - contentPadding = contentPadding, + colors = AppButtonDefaults.primaryColors.copy( + containerColor = Color.Transparent, + disabledContainerColor = Color.Transparent + ), + contentPadding = PaddingValues(0.dp), + shape = buttonShape, modifier = Modifier .then(if (fullWidth) Modifier.fillMaxWidth() else Modifier) .requiredHeight(size.height) .then(modifier) ) { - if (isLoading) { - CircularProgressIndicator( - color = Colors.White32, - strokeWidth = 2.dp, - modifier = Modifier.size(size.height / 2) - ) - } else { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(8.dp), - ) { - if (icon != null) { - Box(modifier = if (enabled) Modifier else Modifier.alpha(0.5f)) { - icon() + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .then(if (fullWidth) Modifier.fillMaxWidth() else Modifier) + .requiredHeight(size.height) + .primaryButtonStyle( + isEnabled = enabled && !isLoading, + shape = buttonShape, + primaryColor = color + ) + .padding(contentPadding) + ) { + if (isLoading) { + CircularProgressIndicator( + color = Colors.White32, + strokeWidth = 2.dp, + modifier = Modifier.size(size.height / 2) + ) + } else { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + if (icon != null) { + Box( + modifier = if (enabled) { + Modifier + } else { + Modifier.graphicsLayer { + colorFilter = ColorFilter.tint(Colors.White32) + } + } + ) { + icon() + } + } + text?.let { + Text( + text = text, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) } - } - text?.let { - Text( - text = text, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) } } } @@ -110,7 +140,7 @@ fun SecondaryButton( fullWidth: Boolean = true, ) { val contentPadding = PaddingValues(horizontal = size.horizontalPadding.takeIf { text != null } ?: 0.dp) - val border = BorderStroke(2.dp, if (enabled) Colors.White16 else Color.Transparent) + val border = BorderStroke(2.dp, if (enabled) Colors.Gray4 else Color.Transparent) OutlinedButton( onClick = onClick, enabled = enabled && !isLoading, @@ -134,7 +164,15 @@ fun SecondaryButton( horizontalArrangement = Arrangement.spacedBy(8.dp), ) { if (icon != null) { - Box(modifier = if (enabled) Modifier else Modifier.alpha(0.5f)) { + Box( + modifier = if (enabled) { + Modifier + } else { + Modifier.graphicsLayer { + colorFilter = ColorFilter.tint(Colors.White32) + } + } + ) { icon() } } @@ -179,17 +217,30 @@ fun TertiaryButton( modifier = Modifier.size(size.height / 2) ) } else { - if (icon != null) { - Box(modifier = if (enabled) Modifier else Modifier.alpha(0.5f)) { - icon() + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + if (icon != null) { + Box( + modifier = if (enabled) { + Modifier + } else { + Modifier.graphicsLayer { + colorFilter = ColorFilter.tint(Colors.White32) + } + } + ) { + icon() + } + } + text?.let { + Text( + text = text, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) } - } - text?.let { - Text( - text = text, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) } } } @@ -237,6 +288,7 @@ private fun PrimaryButtonPreview() { ) PrimaryButton( text = "Primary Small", + fullWidth = false, size = ButtonSize.Small, onClick = {}, ) @@ -339,6 +391,7 @@ private fun SecondaryButtonPreview() { SecondaryButton( text = "Secondary Small", size = ButtonSize.Small, + fullWidth = false, onClick = {}, ) SecondaryButton( @@ -421,11 +474,20 @@ private fun TertiaryButtonPreview() { TertiaryButton( text = "Tertiary Disabled", enabled = false, + icon = { + Icon( + imageVector = Icons.Filled.Favorite, + contentDescription = "", + tint = Colors.Brand, + modifier = Modifier.size(16.dp) + ) + }, onClick = {} ) TertiaryButton( text = "Tertiary Small", size = ButtonSize.Small, + fullWidth = false, onClick = {} ) TertiaryButton( diff --git a/app/src/main/java/to/bitkit/ui/components/SheetHost.kt b/app/src/main/java/to/bitkit/ui/components/SheetHost.kt index b9ffba7be..f7700fc5e 100644 --- a/app/src/main/java/to/bitkit/ui/components/SheetHost.kt +++ b/app/src/main/java/to/bitkit/ui/components/SheetHost.kt @@ -21,6 +21,7 @@ import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch import to.bitkit.ui.sheets.BackupRoute @@ -31,6 +32,8 @@ import to.bitkit.ui.theme.Colors enum class SheetSize { LARGE, MEDIUM, SMALL, CALENDAR; } +private val sheetContainerColor = Color(0xFF141414) // Equivalent to White08 on a Black background + @Stable sealed interface Sheet { data class Send(val route: SendRoute = SendRoute.Recipient) : Sheet @@ -92,7 +95,7 @@ fun SheetHost( sheetShape = AppShapes.sheet, sheetContent = sheets, sheetDragHandle = { SheetDragHandle() }, - sheetContainerColor = Colors.Gray6, + sheetContainerColor = sheetContainerColor, sheetContentColor = MaterialTheme.colorScheme.onSurface, ) { content() diff --git a/app/src/main/java/to/bitkit/ui/settings/backups/ShowMnemonicScreen.kt b/app/src/main/java/to/bitkit/ui/settings/backups/ShowMnemonicScreen.kt index 4da79b388..c5650ef5f 100644 --- a/app/src/main/java/to/bitkit/ui/settings/backups/ShowMnemonicScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/backups/ShowMnemonicScreen.kt @@ -168,7 +168,6 @@ private fun ShowMnemonicContent( text = stringResource(R.string.security__mnemonic_reveal), fullWidth = false, onClick = onRevealClick, - color = Colors.Black50, modifier = Modifier .alpha(buttonAlpha) .testTag("TapToReveal") diff --git a/app/src/main/java/to/bitkit/ui/shared/util/Modifiers.kt b/app/src/main/java/to/bitkit/ui/shared/util/Modifiers.kt index befd166c8..f4780e839 100644 --- a/app/src/main/java/to/bitkit/ui/shared/util/Modifiers.kt +++ b/app/src/main/java/to/bitkit/ui/shared/util/Modifiers.kt @@ -17,10 +17,17 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.composed +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.drawWithContent +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.unit.dp import to.bitkit.ui.theme.Colors /** @@ -71,12 +78,17 @@ fun Modifier.clickableAlpha( ) } -fun Modifier.gradientBackground(startColor: Color = Colors.Gray6, endColor: Color = Colors.Black): Modifier { - return this.background( - brush = Brush.verticalGradient( - colors = listOf(startColor, endColor) +fun Modifier.gradientBackground( + startColor: Color = Colors.White08, + endColor: Color = Color.White.copy(alpha = 0.012f), +): Modifier { + return this + .background(Color.Black) + .background( + brush = Brush.verticalGradient( + colors = listOf(startColor, endColor) + ) ) - ) } fun Modifier.blockPointerInputPassthrough(): Modifier { @@ -97,3 +109,63 @@ fun Modifier.screen( .fillMaxSize() .then(if (noBackground) Modifier else Modifier.background(MaterialTheme.colorScheme.background)) .then(if (insets == null) Modifier else Modifier.windowInsetsPadding(insets)) + +fun Modifier.primaryButtonStyle( + isEnabled: Boolean, + shape: Shape, + primaryColor: Color? = null, +): Modifier { + return this + // Step 1: Add shadow (only when enabled) + .then( + if (isEnabled) { + Modifier.shadow( + elevation = 16.dp, + shape = shape, + clip = false // Don't clip content, just add shadow + ) + } else { + Modifier + } + ) + // Step 2: Clip to shape first + .clip(shape) + // Step 3: Apply gradient background with border overlay + .then( + if (isEnabled) { + Modifier.drawWithContent { + // Draw the main gradient background filling entire button + val mainBrush = Brush.verticalGradient( + colors = listOf(primaryColor ?: Colors.Gray5, Colors.Gray6), + startY = 0f, + endY = size.height + ) + drawRect( + brush = mainBrush, + topLeft = Offset.Zero, + size = size + ) + + // Draw top border highlight (2dp gradient fade) + val borderHeight = 2.dp.toPx() + drawRect( + brush = Brush.verticalGradient( + colors = listOf( + Colors.White16, + Color.Transparent + ), + startY = 0f, + endY = borderHeight + ), + topLeft = Offset(0f, 0f), + size = Size(size.width, borderHeight) + ) + + // Draw the actual button content on top + drawContent() + } + } else { + Modifier.background(Colors.White06) + } + ) +} diff --git a/app/src/main/java/to/bitkit/ui/theme/Colors.kt b/app/src/main/java/to/bitkit/ui/theme/Colors.kt index 087296203..cd5c38b47 100644 --- a/app/src/main/java/to/bitkit/ui/theme/Colors.kt +++ b/app/src/main/java/to/bitkit/ui/theme/Colors.kt @@ -16,12 +16,13 @@ object Colors { val White = Color(0xFFFFFFFF) // Gray Base - val Gray6 = Color(0xFF151515) - val Gray5 = Color(0xFF1C1C1D) - val Gray4 = Color(0xFF3A343C) + val Gray7 = Color(0xFF101010) + val Gray6 = Color(0xFF1C1C1C) + val Gray5 = Color(0xFF2A2A2A) + val Gray4 = Color(0xFF3A3A3A) val Gray3 = Color(0xFF48484A) val Gray2 = Color(0xFF636366) - val Gray1 = Color(0xFF8E8E93) + val Gray1 = Color(0xFF8E8E8E) // Alpha val Black50 = Color.Black.copy(alpha = 0.5f)