Skip to content

Commit df5b385

Browse files
authored
Merge pull request #501 from synonymdev/feat/transfer-banner
feat: Transfer banners
2 parents 4c86792 + 5036881 commit df5b385

File tree

9 files changed

+367
-77
lines changed

9 files changed

+367
-77
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package to.bitkit.models
2+
3+
import androidx.annotation.DrawableRes
4+
import androidx.annotation.StringRes
5+
import androidx.compose.ui.graphics.Color
6+
import to.bitkit.R
7+
import to.bitkit.ui.theme.Colors
8+
9+
enum class ActivityBannerType(
10+
@DrawableRes val icon: Int,
11+
@StringRes val title: Int,
12+
val color: Color,
13+
) {
14+
SPENDING(
15+
color = Colors.Purple,
16+
icon = R.drawable.ic_transfer,
17+
title = R.string.activity_banner__transfer_in_progress
18+
),
19+
SAVINGS(
20+
color = Colors.Brand,
21+
icon = R.drawable.ic_transfer,
22+
title = R.string.activity_banner__transfer_in_progress
23+
)
24+
}

app/src/main/java/to/bitkit/models/Suggestion.kt

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -67,40 +67,6 @@ enum class Suggestion(
6767
color = Colors.Green24,
6868
icon = R.drawable.fast_forward
6969
),
70-
71-
/**Replaces LIGHTNING when a LN channel is being force closed*/
72-
TRANSFER_PENDING(
73-
title = R.string.cards__lightningSettingUp__title,
74-
description = R.string.cards__transferPending__description,
75-
color = Colors.Purple24,
76-
icon = R.drawable.transfer,
77-
dismissible = false
78-
),
79-
80-
/**When the LN channel could not be cooped closed immediately*/
81-
TRANSFER_CLOSING_CHANNEL(
82-
title = R.string.cards__transferClosingChannel__title,
83-
description = R.string.cards__transferClosingChannel__description,
84-
color = Colors.Red24,
85-
icon = R.drawable.transfer,
86-
dismissible = false
87-
),
88-
89-
/**Replaces LIGHTNING when the transfer to spending balance is in progress*/
90-
LIGHTNING_SETTING_UP(
91-
title = R.string.cards__lightningSettingUp__title,
92-
description = R.string.cards__lightningSettingUp__description,
93-
color = Colors.Purple24,
94-
icon = R.drawable.transfer,
95-
dismissible = false
96-
),
97-
LIGHTNING_READY(
98-
title = R.string.cards__lightningReady__title,
99-
description = R.string.cards__lightningReady__description,
100-
color = Colors.Purple24,
101-
icon = R.drawable.transfer,
102-
dismissible = false,
103-
),
10470
NOTIFICATIONS(
10571
title = R.string.cards__notifications__title,
10672
description = R.string.cards__notifications__description,
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
package to.bitkit.ui.components
2+
3+
import androidx.annotation.DrawableRes
4+
import androidx.compose.animation.core.RepeatMode
5+
import androidx.compose.animation.core.animateFloat
6+
import androidx.compose.animation.core.infiniteRepeatable
7+
import androidx.compose.animation.core.rememberInfiniteTransition
8+
import androidx.compose.animation.core.tween
9+
import androidx.compose.foundation.background
10+
import androidx.compose.foundation.border
11+
import androidx.compose.foundation.layout.Arrangement
12+
import androidx.compose.foundation.layout.Box
13+
import androidx.compose.foundation.layout.Row
14+
import androidx.compose.foundation.layout.fillMaxSize
15+
import androidx.compose.foundation.layout.fillMaxWidth
16+
import androidx.compose.foundation.layout.padding
17+
import androidx.compose.foundation.layout.requiredHeight
18+
import androidx.compose.foundation.lazy.LazyColumn
19+
import androidx.compose.foundation.lazy.items
20+
import androidx.compose.material3.Icon
21+
import androidx.compose.material3.ShapeDefaults
22+
import androidx.compose.runtime.Composable
23+
import androidx.compose.runtime.getValue
24+
import androidx.compose.ui.Alignment
25+
import androidx.compose.ui.Modifier
26+
import androidx.compose.ui.draw.clip
27+
import androidx.compose.ui.geometry.Offset
28+
import androidx.compose.ui.graphics.Brush
29+
import androidx.compose.ui.graphics.Color
30+
import androidx.compose.ui.platform.LocalDensity
31+
import androidx.compose.ui.res.painterResource
32+
import androidx.compose.ui.res.stringResource
33+
import androidx.compose.ui.text.AnnotatedString
34+
import androidx.compose.ui.tooling.preview.Preview
35+
import androidx.compose.ui.unit.dp
36+
import to.bitkit.R
37+
import to.bitkit.models.ActivityBannerType
38+
import to.bitkit.ui.shared.util.clickableAlpha
39+
import to.bitkit.ui.shared.util.outerGlow
40+
import to.bitkit.ui.theme.Colors
41+
42+
private const val GLOW_ANIMATION_MILLIS = 1200
43+
44+
@Composable
45+
fun ActivityBanner(
46+
gradientColor: Color,
47+
title: String,
48+
@DrawableRes icon: Int,
49+
modifier: Modifier = Modifier,
50+
onClick: (() -> Unit)? = null,
51+
) {
52+
val infiniteTransition = rememberInfiniteTransition(label = "glow")
53+
54+
val innerShadowOpacity by infiniteTransition.animateFloat(
55+
initialValue = 0.32f,
56+
targetValue = 0.64f,
57+
animationSpec = infiniteRepeatable(
58+
animation = tween(durationMillis = GLOW_ANIMATION_MILLIS),
59+
repeatMode = RepeatMode.Reverse
60+
),
61+
label = "inner_shadow_opacity"
62+
)
63+
64+
val dropShadowOpacity by infiniteTransition.animateFloat(
65+
initialValue = 1.0f,
66+
targetValue = 0.0f,
67+
animationSpec = infiniteRepeatable(
68+
animation = tween(durationMillis = GLOW_ANIMATION_MILLIS),
69+
repeatMode = RepeatMode.Reverse
70+
),
71+
label = "drop_shadow_opacity"
72+
)
73+
74+
val radialGradientOpacity by infiniteTransition.animateFloat(
75+
initialValue = 0.6f,
76+
targetValue = 0.0f,
77+
animationSpec = infiniteRepeatable(
78+
animation = tween(durationMillis = GLOW_ANIMATION_MILLIS),
79+
repeatMode = RepeatMode.Reverse
80+
),
81+
label = "radial_gradient_opacity"
82+
)
83+
84+
val borderOpacity by infiniteTransition.animateFloat(
85+
initialValue = 0.32f,
86+
targetValue = 1.0f,
87+
animationSpec = infiniteRepeatable(
88+
animation = tween(durationMillis = GLOW_ANIMATION_MILLIS),
89+
repeatMode = RepeatMode.Reverse
90+
),
91+
label = "border_opacity"
92+
)
93+
94+
val density = LocalDensity.current.density
95+
96+
Box(
97+
modifier = modifier
98+
.requiredHeight(72.dp)
99+
.outerGlow(
100+
glowColor = gradientColor,
101+
glowOpacity = dropShadowOpacity,
102+
glowRadius = 12.dp,
103+
cornerRadius = 16.dp
104+
)
105+
.clickableAlpha(onClick = onClick)
106+
) {
107+
// Main card content with clipped backgrounds
108+
Box(
109+
modifier = Modifier
110+
.fillMaxWidth()
111+
.requiredHeight(72.dp)
112+
.clip(ShapeDefaults.Large)
113+
// Layer 1: Base color (black)
114+
.background(Color.Black)
115+
// Layer 2: Inner shadow approximation (radial gradient from edges)
116+
.background(
117+
brush = Brush.radialGradient(
118+
colors = listOf(
119+
Color.Transparent,
120+
gradientColor.copy(alpha = innerShadowOpacity)
121+
),
122+
radius = 400f
123+
)
124+
)
125+
// Layer 3: Linear gradient (top to bottom)
126+
.background(
127+
brush = Brush.verticalGradient(
128+
colors = listOf(
129+
gradientColor.copy(alpha = 0.32f),
130+
gradientColor.copy(alpha = 0f)
131+
)
132+
)
133+
)
134+
// Layer 4: Radial gradient (top-left corner)
135+
.background(
136+
brush = Brush.radialGradient(
137+
colors = listOf(
138+
gradientColor.copy(alpha = radialGradientOpacity),
139+
gradientColor.copy(alpha = 0f)
140+
),
141+
center = Offset(0f, 0f),
142+
radius = 160f * density
143+
)
144+
)
145+
// Border with animated opacity
146+
.border(
147+
width = 1.dp,
148+
color = gradientColor.copy(alpha = borderOpacity),
149+
shape = ShapeDefaults.Large
150+
)
151+
)
152+
153+
Row(
154+
modifier = Modifier
155+
.padding(horizontal = 16.dp, vertical = 12.dp)
156+
.align(Alignment.Center),
157+
horizontalArrangement = Arrangement.spacedBy(8.dp),
158+
verticalAlignment = Alignment.CenterVertically
159+
) {
160+
Icon(
161+
painter = painterResource(icon),
162+
contentDescription = null,
163+
tint = gradientColor
164+
)
165+
166+
Headline20(
167+
text = AnnotatedString(title),
168+
color = Colors.White,
169+
)
170+
}
171+
}
172+
}
173+
174+
@Preview(showSystemUi = true)
175+
@Composable
176+
private fun Preview() {
177+
LazyColumn(
178+
verticalArrangement = Arrangement.spacedBy(4.dp),
179+
modifier = Modifier.fillMaxSize(),
180+
) {
181+
items(items = ActivityBannerType.entries) { item ->
182+
ActivityBanner(
183+
gradientColor = item.color,
184+
title = stringResource(R.string.activity_banner__transfer_in_progress),
185+
icon = item.icon,
186+
onClick = {},
187+
modifier = Modifier.fillMaxWidth()
188+
)
189+
}
190+
}
191+
}

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

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ fun RowScope.WalletBalanceView(
4444
sats: Long,
4545
icon: Painter,
4646
modifier: Modifier = Modifier,
47-
showTransferIcon: Boolean = false,
4847
) {
4948
val isPreview = LocalInspectionMode.current
5049
if (isPreview) {
@@ -63,7 +62,6 @@ fun RowScope.WalletBalanceView(
6362
primaryDisplay = PrimaryDisplay.BITCOIN,
6463
displayUnit = BitcoinDisplayUnit.MODERN,
6564
hideBalance = false,
66-
showTransferIcon = showTransferIcon,
6765
)
6866
}
6967

@@ -82,7 +80,6 @@ fun RowScope.WalletBalanceView(
8280
primaryDisplay = primaryDisplay,
8381
displayUnit = displayUnit,
8482
hideBalance = hideBalance,
85-
showTransferIcon = showTransferIcon,
8683
)
8784
}
8885

@@ -95,7 +92,6 @@ private fun RowScope.Content(
9592
primaryDisplay: PrimaryDisplay,
9693
displayUnit: BitcoinDisplayUnit,
9794
hideBalance: Boolean,
98-
showTransferIcon: Boolean,
9995
) {
10096
Column(
10197
modifier = Modifier
@@ -134,14 +130,6 @@ private fun RowScope.Content(
134130
horizontalArrangement = Arrangement.spacedBy(4.dp),
135131
) {
136132
BodyMSB(text = if (isHidden) UiConstants.HIDE_BALANCE_SHORT else btcComponents.value)
137-
if (showTransferIcon) {
138-
Icon(
139-
painter = painterResource(R.drawable.ic_transfer),
140-
contentDescription = null,
141-
tint = Colors.White64,
142-
modifier = Modifier.size(16.dp)
143-
)
144-
}
145133
}
146134
}
147135
} else {
@@ -156,14 +144,6 @@ private fun RowScope.Content(
156144
) {
157145
BodyMSB(text = converted.symbol)
158146
BodyMSB(text = if (isHidden) UiConstants.HIDE_BALANCE_SHORT else converted.formatted)
159-
if (showTransferIcon) {
160-
Icon(
161-
painter = painterResource(R.drawable.ic_transfer),
162-
contentDescription = null,
163-
modifier = Modifier.size(16.dp),
164-
tint = Colors.White64
165-
)
166-
}
167147
}
168148
}
169149
}
@@ -217,7 +197,6 @@ private fun PreviewTransferToSpending() {
217197
title = stringResource(R.string.wallet__spending__title),
218198
sats = 250_000,
219199
icon = painterResource(R.drawable.ic_ln_circle),
220-
showTransferIcon = true,
221200
)
222201
}
223202
}
@@ -237,7 +216,6 @@ private fun PreviewTransferToSavings() {
237216
title = stringResource(R.string.wallet__savings__title),
238217
sats = 1_250_000,
239218
icon = painterResource(R.drawable.ic_btc_circle),
240-
showTransferIcon = true,
241219
)
242220
VerticalDivider()
243221
WalletBalanceView(
@@ -263,14 +241,12 @@ private fun PreviewTransfers() {
263241
title = stringResource(R.string.wallet__savings__title),
264242
sats = 1_150_000,
265243
icon = painterResource(R.drawable.ic_btc_circle),
266-
showTransferIcon = true,
267244
)
268245
VerticalDivider()
269246
WalletBalanceView(
270247
title = stringResource(R.string.wallet__spending__title),
271248
sats = 150_000,
272249
icon = painterResource(R.drawable.ic_ln_circle),
273-
showTransferIcon = true,
274250
)
275251
}
276252
}

0 commit comments

Comments
 (0)