Skip to content

Commit 9bd0445

Browse files
committed
refactor: simplify glow effect
1 parent d857fa0 commit 9bd0445

File tree

2 files changed

+80
-188
lines changed

2 files changed

+80
-188
lines changed

app/src/main/java/to/bitkit/ui/components/SuggestionCard.kt

Lines changed: 60 additions & 187 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
package to.bitkit.ui.components
22

33
import androidx.annotation.DrawableRes
4-
import androidx.compose.animation.core.LinearEasing
54
import androidx.compose.animation.core.RepeatMode
65
import androidx.compose.animation.core.animateFloat
76
import androidx.compose.animation.core.infiniteRepeatable
87
import androidx.compose.animation.core.rememberInfiniteTransition
98
import androidx.compose.animation.core.tween
109
import androidx.compose.foundation.Image
11-
import androidx.compose.foundation.background
12-
import androidx.compose.foundation.border
1310
import androidx.compose.foundation.layout.Arrangement
1411
import androidx.compose.foundation.layout.Box
1512
import androidx.compose.foundation.layout.Column
@@ -21,21 +18,15 @@ import androidx.compose.foundation.layout.size
2118
import androidx.compose.foundation.lazy.grid.GridCells
2219
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
2320
import androidx.compose.foundation.lazy.grid.items
24-
import androidx.compose.foundation.shape.RoundedCornerShape
2521
import androidx.compose.material3.Icon
2622
import androidx.compose.material3.IconButton
23+
import androidx.compose.material3.ShapeDefaults
2724
import androidx.compose.runtime.Composable
2825
import androidx.compose.runtime.LaunchedEffect
2926
import androidx.compose.runtime.getValue
3027
import androidx.compose.ui.Modifier
31-
import androidx.compose.ui.draw.alpha
3228
import androidx.compose.ui.draw.clip
33-
import androidx.compose.ui.draw.drawBehind
34-
import androidx.compose.ui.graphics.Brush
3529
import androidx.compose.ui.graphics.Color
36-
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
37-
import androidx.compose.ui.graphics.nativeCanvas
38-
import androidx.compose.ui.graphics.toArgb
3930
import androidx.compose.ui.layout.ContentScale
4031
import androidx.compose.ui.platform.testTag
4132
import androidx.compose.ui.res.painterResource
@@ -47,7 +38,8 @@ import kotlinx.coroutines.delay
4738
import to.bitkit.R
4839
import to.bitkit.models.Suggestion
4940
import to.bitkit.ui.shared.util.clickableAlpha
50-
import to.bitkit.ui.shared.util.gradientBackground
41+
import to.bitkit.ui.shared.util.gradientLinearBackground
42+
import to.bitkit.ui.shared.util.gradientRadialBackground
5143
import to.bitkit.ui.theme.Colors
5244
import kotlin.time.Duration
5345
import kotlin.time.Duration.Companion.seconds
@@ -65,206 +57,87 @@ fun SuggestionCard(
6557
captionColor: Color = Colors.White64,
6658
onClick: () -> Unit,
6759
) {
68-
val dismissable = onClose != null
69-
7060
LaunchedEffect(Unit) {
7161
duration?.let {
7262
delay(it)
7363
onClose?.invoke()
7464
}
7565
}
7666

77-
Box(modifier = modifier) {
78-
if (!dismissable) {
79-
GlowEffect(
80-
size = size,
81-
color = gradientColor,
82-
modifier = Modifier.size(size.dp)
83-
)
84-
}
85-
86-
Box(
87-
modifier = Modifier
88-
.size(size.dp)
89-
.clip(RoundedCornerShape(16.dp))
90-
.then(
91-
if (dismissable) {
92-
Modifier.gradientBackground(gradientColor)
93-
} else {
94-
Modifier
95-
.border(
96-
width = 1.dp,
97-
color = getBorderColorForGradient(gradientColor),
98-
shape = RoundedCornerShape(16.dp)
99-
)
100-
.gradientBackground(gradientColor)
101-
}
102-
)
103-
.clickableAlpha { onClick() }
104-
) {
105-
// Shade effect for dismissable cards (similar to the Shade component in RN)
106-
if (dismissable) {
107-
Box(
108-
modifier = Modifier
109-
.fillMaxSize()
110-
.background(
111-
brush = Brush.verticalGradient(
112-
colors = listOf(
113-
Color.Transparent,
114-
Color.Black.copy(alpha = 0.6f)
115-
),
116-
startY = size * 0.4f,
117-
endY = size.toFloat()
118-
)
119-
)
120-
)
121-
}
122-
123-
Column(
124-
modifier = Modifier
125-
.fillMaxWidth()
126-
.padding(horizontal = 16.dp, vertical = 12.dp),
127-
verticalArrangement = Arrangement.spacedBy(8.dp)
128-
) {
129-
Row(
130-
modifier = Modifier
131-
.fillMaxWidth()
132-
.weight(1f)
133-
) {
134-
Image(
135-
painter = painterResource(icon),
136-
contentDescription = null,
137-
contentScale = ContentScale.FillHeight,
138-
modifier = Modifier.weight(1f)
139-
)
140-
141-
if (duration == null && onClose != null) {
142-
IconButton(
143-
onClick = onClose,
144-
modifier = Modifier
145-
.size(16.dp)
146-
.testTag("SuggestionDismiss")
147-
) {
148-
Icon(
149-
painter = painterResource(R.drawable.ic_x),
150-
contentDescription = null,
151-
tint = Colors.White,
152-
)
153-
}
154-
}
155-
}
156-
157-
Headline20(
158-
text = AnnotatedString(title),
159-
color = Colors.White,
160-
)
161-
162-
CaptionB(
163-
text = description,
164-
color = captionColor,
165-
)
166-
}
167-
}
168-
}
169-
}
67+
val isDismissible = onClose != null
17068

171-
@Composable
172-
private fun GlowEffect(
173-
size: Int,
174-
color: Color,
175-
modifier: Modifier = Modifier,
176-
) {
177-
val infiniteTransition = rememberInfiniteTransition(label = "glowTransition")
69+
// Glow animation for non-dismissible cards
70+
val infiniteTransition = rememberInfiniteTransition(label = "glow")
17871
val glowAlpha by infiniteTransition.animateFloat(
179-
initialValue = 0f,
72+
initialValue = 0.24f,
18073
targetValue = 1f,
18174
animationSpec = infiniteRepeatable(
182-
animation = tween(1100, easing = LinearEasing),
75+
animation = tween(1000),
18376
repeatMode = RepeatMode.Reverse
18477
),
185-
label = "glowAlpha"
78+
label = "glow_alpha"
18679
)
18780

188-
val (shadowColor, _, radialGradientColor) = getGlowColors(color)
189-
190-
Box(modifier = modifier) {
191-
// Outer glow with animated opacity
192-
Box(
81+
Box(
82+
modifier = modifier
83+
.size(size.dp)
84+
.clip(ShapeDefaults.Large)
85+
.then(
86+
if (isDismissible) {
87+
Modifier.gradientLinearBackground(gradientColor)
88+
} else {
89+
Modifier.gradientRadialBackground(gradientColor, glowAlpha)
90+
}
91+
)
92+
.clickableAlpha { onClick() }
93+
) {
94+
Column(
19395
modifier = Modifier
194-
.fillMaxSize()
195-
.alpha(glowAlpha)
196-
.drawBehind {
197-
drawIntoCanvas { canvas ->
198-
val paint = android.graphics.Paint().apply {
199-
this.color = shadowColor.toArgb()
200-
setShadowLayer(15f, 0f, 0f, shadowColor.toArgb())
201-
isAntiAlias = true
202-
}
203-
204-
val rect = android.graphics.RectF(
205-
5f,
206-
5f,
207-
size.toFloat() - 5f,
208-
size.toFloat() - 5f
209-
)
96+
.fillMaxWidth()
97+
.padding(horizontal = 16.dp, vertical = 12.dp),
98+
verticalArrangement = Arrangement.spacedBy(8.dp)
99+
) {
100+
Row(
101+
modifier = Modifier
102+
.fillMaxWidth()
103+
.weight(1f)
104+
) {
105+
Image(
106+
painter = painterResource(icon),
107+
contentDescription = null,
108+
contentScale = ContentScale.FillHeight,
109+
modifier = Modifier.weight(1f)
110+
)
210111

211-
canvas.nativeCanvas.drawRoundRect(
212-
rect,
213-
16f,
214-
16f,
215-
paint
112+
if (duration == null && onClose != null) {
113+
IconButton(
114+
onClick = onClose,
115+
modifier = Modifier
116+
.size(16.dp)
117+
.testTag("SuggestionDismiss")
118+
) {
119+
Icon(
120+
painter = painterResource(R.drawable.ic_x),
121+
contentDescription = null,
122+
tint = Colors.White,
216123
)
217124
}
218125
}
219-
)
220-
221-
// Static radial gradient overlay
222-
Box(
223-
modifier = Modifier
224-
.fillMaxSize()
225-
.alpha(0.4f)
226-
.background(
227-
brush = Brush.radialGradient(
228-
colors = listOf(radialGradientColor, color),
229-
center = androidx.compose.ui.geometry.Offset(
230-
size / 2f,
231-
size / 2f
232-
),
233-
radius = size / 2f
234-
),
235-
shape = RoundedCornerShape(16.dp)
236-
)
237-
)
238-
}
239-
}
126+
}
240127

241-
private fun getGlowColors(color: Color): Triple<Color, Color, Color> {
242-
return when (color) {
243-
Colors.Brand24 -> Triple(
244-
Color(200, 48, 0), // shadowColor
245-
Color(255, 68, 0), // borderColor
246-
Color(100, 24, 0) // radialGradientColor
247-
)
128+
Headline20(
129+
text = AnnotatedString(title),
130+
color = Colors.White,
131+
)
248132

249-
else -> Triple(
250-
Color(130, 65, 175), // shadowColor (default purple)
251-
Color(185, 92, 232), // borderColor
252-
Color(65, 32, 80) // radialGradientColor
253-
)
133+
CaptionB(
134+
text = description,
135+
color = captionColor,
136+
)
137+
}
254138
}
255139
}
256140

257-
private fun getBorderColorForGradient(color: Color): Color {
258-
return when (color) {
259-
Colors.Brand24 -> Color(255, 68, 0)
260-
Colors.Purple24 -> Color(185, 92, 232)
261-
Colors.Blue24 -> Color(92, 185, 232)
262-
Colors.Green24 -> Color(92, 232, 185)
263-
Colors.Yellow24 -> Color(232, 185, 92)
264-
Colors.Red24 -> Color(232, 92, 92)
265-
else -> Color(185, 92, 232)
266-
}
267-
}
268141

269142
@Preview(showSystemUi = true)
270143
@Composable
@@ -281,7 +154,7 @@ private fun Preview() {
281154
description = stringResource(item.description),
282155
icon = item.icon,
283156
onClose = {},
284-
onClick = {},
157+
onClick = {}, // All cards are clickable
285158
duration = 5.seconds.takeIf { item == Suggestion.LIGHTNING_READY }
286159
)
287160
}

app/src/main/java/to/bitkit/ui/shared/util/Modifiers.kt

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import androidx.compose.ui.graphics.Brush
1515
import androidx.compose.ui.graphics.Color
1616
import androidx.compose.ui.graphics.graphicsLayer
1717
import androidx.compose.ui.input.pointer.pointerInput
18+
import androidx.compose.ui.unit.dp
1819
import to.bitkit.ui.theme.Colors
1920

2021
/**
@@ -65,14 +66,32 @@ fun Modifier.clickableAlpha(
6566
)
6667
}
6768

68-
fun Modifier.gradientBackground(startColor: Color = Colors.Gray6, endColor: Color = Colors.Black): Modifier {
69+
fun Modifier.gradientLinearBackground(startColor: Color = Colors.Gray6, endColor: Color = Colors.Black): Modifier {
6970
return this.background(
7071
brush = Brush.verticalGradient(
7172
colors = listOf(startColor, endColor)
7273
)
7374
)
7475
}
7576

77+
fun Modifier.gradientRadialBackground(
78+
centerColor: Color,
79+
glowAlpha: Float = 1f,
80+
): Modifier {
81+
return this
82+
.background(
83+
brush = Brush.radialGradient(
84+
colors = listOf(
85+
centerColor.copy(alpha = glowAlpha * 0.10f),
86+
centerColor.copy(alpha = glowAlpha * 0.50f),
87+
centerColor.copy(alpha = glowAlpha * 0.80f),
88+
centerColor.copy(alpha = glowAlpha),
89+
),
90+
)
91+
)
92+
}
93+
94+
7695
fun Modifier.blockPointerInputPassthrough(): Modifier {
7796
return this.pointerInput(Unit) {
7897
awaitPointerEventScope {

0 commit comments

Comments
 (0)