Skip to content

Commit d4e833e

Browse files
authored
chore(tip/chat): add "via Tip Card" to chat profile card for tip chats (#1140)
Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent dace75b commit d4e833e

3 files changed

Lines changed: 196 additions & 18 deletions

File tree

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="20dp"
3+
android:height="20dp"
4+
android:viewportWidth="20"
5+
android:viewportHeight="20">
6+
<path
7+
android:pathData="M6.26,17.549C6.413,17.346 6.688,17.281 6.921,17.38C8.047,17.857 9.287,18.075 10.552,17.985C10.804,17.967 11.042,18.116 11.122,18.356C11.225,18.669 11.021,19.001 10.693,19.027C9.212,19.145 7.759,18.888 6.447,18.32C6.146,18.19 6.062,17.812 6.26,17.549Z"
8+
android:strokeAlpha="0.5"
9+
android:fillColor="#ffffff"
10+
android:fillAlpha="0.5"/>
11+
<path
12+
android:pathData="M18.196,11.688C18.532,11.683 18.788,11.987 18.702,12.312C18.127,14.497 16.748,16.42 14.815,17.65C14.536,17.827 14.175,17.684 14.072,17.371C13.993,17.131 14.095,16.87 14.307,16.732C15.971,15.655 17.161,13.993 17.674,12.106C17.739,11.867 17.949,11.692 18.196,11.688Z"
13+
android:strokeAlpha="0.5"
14+
android:fillColor="#ffffff"
15+
android:fillAlpha="0.5"/>
16+
<path
17+
android:pathData="M1.944,12.917C2.225,12.761 2.577,12.896 2.704,13.191C2.984,13.842 3.345,14.441 3.769,14.979C3.925,15.176 3.937,15.455 3.785,15.656C3.587,15.919 3.198,15.939 2.993,15.683C2.48,15.043 2.049,14.326 1.719,13.543C1.621,13.309 1.722,13.041 1.944,12.917Z"
18+
android:strokeAlpha="0.5"
19+
android:fillColor="#ffffff"
20+
android:fillAlpha="0.5"/>
21+
<path
22+
android:pathData="M17.397,5.917C17.653,5.749 17.997,5.834 18.128,6.11C18.359,6.599 18.549,7.115 18.693,7.657C18.918,8.501 19.015,9.349 18.998,10.182C18.993,10.451 18.77,10.661 18.5,10.665C18.194,10.67 17.952,10.41 17.956,10.103C17.965,9.385 17.879,8.655 17.685,7.928C17.563,7.469 17.402,7.03 17.209,6.614C17.094,6.366 17.168,6.067 17.397,5.917Z"
23+
android:strokeAlpha="0.5"
24+
android:fillColor="#ffffff"
25+
android:fillAlpha="0.5"/>
26+
<path
27+
android:pathData="M2.706,5.783C2.917,5.941 2.976,6.232 2.86,6.47C2.264,7.689 1.973,9.065 2.057,10.473C2.07,10.687 1.964,10.894 1.776,10.998C1.451,11.18 1.046,10.981 1.021,10.609C0.909,8.969 1.247,7.365 1.951,5.951C2.092,5.668 2.454,5.594 2.706,5.783Z"
28+
android:strokeAlpha="0.5"
29+
android:fillColor="#ffffff"
30+
android:fillAlpha="0.5"/>
31+
<path
32+
android:pathData="M16.17,4.006C16.379,3.868 16.658,3.899 16.82,4.09C17.015,4.319 16.955,4.666 16.703,4.832L16.651,4.866C16.449,4.999 16.18,4.951 16.022,4.769C15.831,4.548 15.868,4.204 16.112,4.043L16.17,4.006Z"
33+
android:strokeAlpha="0.5"
34+
android:fillColor="#ffffff"
35+
android:fillAlpha="0.5"/>
36+
<path
37+
android:pathData="M8.878,1.644C8.774,1.331 8.978,0.999 9.307,0.973C11.337,0.812 13.313,1.355 14.947,2.436C15.252,2.638 15.233,3.08 14.927,3.281C14.742,3.403 14.503,3.396 14.318,3.276C12.9,2.356 11.198,1.89 9.448,2.015C9.195,2.033 8.957,1.884 8.878,1.644Z"
38+
android:strokeAlpha="0.5"
39+
android:fillColor="#ffffff"
40+
android:fillAlpha="0.5"/>
41+
<path
42+
android:pathData="M5.927,2.629C6.007,2.869 5.904,3.13 5.692,3.268C4.969,3.736 4.335,4.314 3.808,4.974C3.622,5.205 3.287,5.261 3.05,5.084C2.825,4.914 2.773,4.595 2.948,4.373C3.568,3.586 4.321,2.899 5.184,2.349C5.463,2.172 5.824,2.316 5.927,2.629Z"
43+
android:strokeAlpha="0.5"
44+
android:fillColor="#ffffff"
45+
android:fillAlpha="0.5"/>
46+
<path
47+
android:pathData="M6.681,14.826C6.859,14.707 7.09,14.708 7.276,14.814C8.491,15.511 9.968,15.746 11.425,15.353C11.82,15.247 12.193,15.099 12.542,14.917C12.847,14.757 13.24,14.896 13.335,15.227C13.396,15.443 13.308,15.676 13.111,15.784C12.67,16.023 12.196,16.216 11.691,16.352C9.925,16.828 8.135,16.528 6.677,15.66C6.362,15.473 6.375,15.03 6.681,14.826Z"
48+
android:strokeAlpha="0.5"
49+
android:fillColor="#ffffff"
50+
android:fillAlpha="0.5"/>
51+
<path
52+
android:pathData="M13.625,5.095C13.803,4.903 14.099,4.876 14.295,5.048C15.242,5.877 15.962,6.988 16.311,8.299C16.662,9.616 16.588,10.946 16.176,12.144C16.007,12.633 15.37,12.566 15.229,12.069C15.195,11.95 15.2,11.824 15.238,11.706C15.555,10.723 15.604,9.639 15.319,8.567C15.031,7.489 14.447,6.571 13.678,5.878C13.452,5.674 13.416,5.319 13.625,5.095Z"
53+
android:strokeAlpha="0.5"
54+
android:fillColor="#ffffff"
55+
android:fillAlpha="0.5"/>
56+
<path
57+
android:pathData="M6.168,5.321C6.353,5.54 6.322,5.868 6.118,6.072C4.767,7.426 4.153,9.448 4.681,11.435C4.729,11.616 4.786,11.792 4.851,11.963C4.942,12.205 4.867,12.484 4.652,12.628C4.391,12.802 4.036,12.708 3.922,12.417C3.832,12.186 3.754,11.947 3.689,11.702C3.053,9.314 3.81,6.881 5.459,5.274C5.661,5.077 5.985,5.106 6.168,5.321Z"
58+
android:strokeAlpha="0.5"
59+
android:fillColor="#ffffff"
60+
android:fillAlpha="0.5"/>
61+
<path
62+
android:pathData="M8.309,3.65C8.829,3.51 9.352,3.438 9.867,3.427C10.32,3.418 10.513,3.946 10.204,4.279C10.096,4.395 9.945,4.459 9.788,4.465C9.386,4.48 8.98,4.54 8.575,4.649C8.244,4.739 7.929,4.856 7.631,4.999C7.402,5.109 7.124,5.063 6.96,4.87C6.753,4.625 6.812,4.251 7.1,4.108C7.48,3.919 7.884,3.765 8.309,3.65Z"
63+
android:strokeAlpha="0.5"
64+
android:fillColor="#ffffff"
65+
android:fillAlpha="0.5"/>
66+
<path
67+
android:pathData="M13.329,9.99C13.329,11.806 11.855,13.279 10.038,13.279C8.22,13.279 6.747,11.806 6.747,9.99C6.747,8.174 8.22,6.701 10.038,6.701C11.855,6.701 13.329,8.174 13.329,9.99Z"
68+
android:strokeAlpha="0.5"
69+
android:fillColor="#ffffff"
70+
android:fillAlpha="0.5"/>
71+
</vector>

‎apps/flipcash/core/src/main/res/values/strings.xml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -896,5 +896,6 @@
896896
<string name="subtitle_tipHintMinimum">Minimum tip %1$s</string>
897897
<string name="error_title_tipMinimum">Tips Start at %1$s</string>
898898
<string name="error_description_tipMinimum">Enter a larger amount to send this tip</string>
899+
<string name="label_viaTipCard">via Tip Card</string>
899900

900901
</resources>

‎apps/flipcash/features/messenger/src/main/kotlin/com/flipcash/app/messenger/internal/screens/components/ContactInfoContainer.kt‎

Lines changed: 124 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.Spacer
1212
import androidx.compose.foundation.layout.fillMaxWidth
1313
import androidx.compose.foundation.layout.padding
1414
import androidx.compose.foundation.layout.size
15+
import androidx.compose.foundation.layout.width
1516
import androidx.compose.foundation.shape.CircleShape
1617
import androidx.compose.foundation.text.TextAutoSize
1718
import androidx.compose.material3.Icon
@@ -27,10 +28,15 @@ import androidx.compose.ui.draw.clip
2728
import androidx.compose.ui.res.painterResource
2829
import androidx.compose.ui.res.stringResource
2930
import androidx.compose.ui.text.style.TextOverflow
31+
import androidx.compose.ui.tooling.preview.Preview
32+
import androidx.compose.ui.unit.dp
33+
import androidx.compose.ui.tooling.preview.PreviewWrapper
3034
import com.flipcash.app.core.android.IntentUtils
3135
import com.flipcash.app.core.contacts.DeviceContact
3236
import com.flipcash.app.messenger.internal.ChatParticipant
37+
import com.flipcash.app.theme.FlipcashThemeWrapper
3338
import com.flipcash.features.messenger.R
39+
import com.flipcash.services.models.UserProfile
3440
import com.getcode.theme.CodeTheme
3541

3642
@Composable
@@ -84,35 +90,55 @@ internal fun ContactInfoContainer(
8490
contract = ActivityResultContracts.StartActivityForResult()
8591
) { onRefreshContact() }
8692

87-
ContactPill(
93+
Indicator(
94+
participant = participant,
8895
modifier = Modifier.padding(top = CodeTheme.dimens.inset),
89-
contact = contact
9096
) {
91-
contact ?: return@ContactPill
92-
val intent = IntentUtils.openContact(contact).apply {
93-
// Remove NEW_TASK so the result callback fires when the user returns,
94-
// not immediately.
95-
flags = flags and Intent.FLAG_ACTIVITY_NEW_TASK.inv()
97+
when (participant) {
98+
is ChatParticipant.Contact -> {
99+
val intent = IntentUtils.openContact(participant.contact).apply {
100+
// Remove NEW_TASK so the result callback fires when the user returns,
101+
// not immediately.
102+
flags = flags and Intent.FLAG_ACTIVITY_NEW_TASK.inv()
103+
}
104+
launcher.launch(intent)
105+
}
106+
is ChatParticipant.TipUser -> Unit
107+
null -> Unit
96108
}
97-
launcher.launch(intent)
98109
}
99110
}
100111
}
101112

113+
@Composable
114+
private fun Indicator(
115+
participant: ChatParticipant?,
116+
modifier: Modifier = Modifier,
117+
onClick: () -> Unit,
118+
) {
119+
when (participant) {
120+
is ChatParticipant.Contact -> ContactPill(
121+
participant.contact,
122+
modifier = modifier,
123+
onClick = onClick
124+
)
125+
126+
is ChatParticipant.TipUser -> TipPill(modifier = modifier)
127+
null -> Spacer(modifier = modifier.fillMaxWidth())
128+
}
129+
}
130+
102131
@Composable
103132
private fun ContactPill(
104-
contact: DeviceContact?,
133+
contact: DeviceContact,
105134
modifier: Modifier = Modifier,
106135
onClick: () -> Unit,
107136
) {
108137
AnimatedContent(contact) { c ->
109-
if (c == null) {
110-
Spacer(modifier = modifier.fillMaxWidth())
111-
return@AnimatedContent
112-
}
113-
114138
val backgroundColor by animateColorAsState(
115-
if (!c.isUnknown) CodeTheme.colors.surfaceVariant else CodeTheme.colors.warning.copy(alpha = 0.10f)
139+
if (!c.isUnknown) CodeTheme.colors.surfaceVariant else CodeTheme.colors.warning.copy(
140+
alpha = 0.10f
141+
)
116142
)
117143

118144
val contentColor by animateColorAsState(
@@ -124,7 +150,10 @@ private fun ContactPill(
124150
.background(color = backgroundColor, shape = CircleShape)
125151
.clip(CircleShape)
126152
.clickable { onClick() }
127-
.padding(horizontal = CodeTheme.dimens.grid.x2, vertical = CodeTheme.dimens.grid.x1),
153+
.padding(
154+
horizontal = CodeTheme.dimens.grid.x2,
155+
vertical = CodeTheme.dimens.grid.x1
156+
),
128157
verticalAlignment = Alignment.CenterVertically,
129158
horizontalArrangement = Arrangement.spacedBy(CodeTheme.dimens.grid.x1)
130159
) {
@@ -142,10 +171,87 @@ private fun ContactPill(
142171
)
143172

144173
Text(
145-
text = if (!c.isUnknown) stringResource(R.string.label_fromYourContacts) else stringResource(R.string.label_addContact),
174+
text = if (!c.isUnknown) stringResource(R.string.label_fromYourContacts) else stringResource(
175+
R.string.label_addContact
176+
),
146177
color = contentColor,
147178
style = CodeTheme.typography.textSmall,
148179
)
149180
}
150181
}
151-
}
182+
}
183+
184+
@Composable
185+
private fun TipPill(
186+
modifier: Modifier = Modifier,
187+
) {
188+
Row(
189+
modifier = modifier
190+
.background(color = CodeTheme.colors.surfaceVariant, shape = CircleShape)
191+
.clip(CircleShape)
192+
.padding(horizontal = CodeTheme.dimens.grid.x2, vertical = CodeTheme.dimens.grid.x1),
193+
verticalAlignment = Alignment.CenterVertically,
194+
horizontalArrangement = Arrangement.spacedBy(CodeTheme.dimens.grid.x1)
195+
) {
196+
Icon(
197+
modifier = Modifier.size(CodeTheme.dimens.staticGrid.x4),
198+
painter = painterResource(R.drawable.ic_tipcode),
199+
contentDescription = null,
200+
tint = CodeTheme.colors.textSecondary,
201+
)
202+
203+
Text(
204+
text = stringResource(R.string.label_viaTipCard),
205+
color = CodeTheme.colors.textSecondary,
206+
style = CodeTheme.typography.textSmall,
207+
)
208+
}
209+
}
210+
211+
// region Previews
212+
213+
@Preview
214+
@PreviewWrapper(FlipcashThemeWrapper::class)
215+
@Composable
216+
private fun Preview_AllStates() {
217+
// A saved device contact: name + number + the "From your contacts" pill.
218+
val knownContact = ChatParticipant.Contact(
219+
DeviceContact(
220+
e164 = "+15551234567",
221+
androidContactId = 1L,
222+
displayName = "Ada Lovelace",
223+
photoUri = null,
224+
displayNumber = "(555) 123-4567",
225+
)
226+
)
227+
228+
// An unknown number: falls back to the number as the name and shows the warning
229+
// "Add contact" pill.
230+
val unknownContact = ChatParticipant.Contact(
231+
DeviceContact.unknownContact(
232+
e164 = "+15559876543",
233+
displayNumber = "(555) 987-6543",
234+
)
235+
)
236+
237+
// A tip DM's counterparty: identity from a server profile — no phone number, no pill.
238+
val tipUser = ChatParticipant.TipUser(
239+
userId = listOf(1.toByte()),
240+
profile = UserProfile.Empty.copy(displayName = "Grace Hopper"),
241+
)
242+
243+
// Fixed width so every state renders at the same size regardless of name/number length.
244+
val cardWidth = Modifier.width(300.dp)
245+
Column(
246+
modifier = Modifier.padding(CodeTheme.dimens.grid.x4),
247+
verticalArrangement = Arrangement.spacedBy(CodeTheme.dimens.grid.x4),
248+
) {
249+
ContactInfoContainer(participant = knownContact, modifier = cardWidth)
250+
ContactInfoContainer(participant = unknownContact, modifier = cardWidth)
251+
ContactInfoContainer(participant = tipUser, modifier = cardWidth)
252+
// Null participant: loading / unknown fallback avatar, name empty, no pill.
253+
ContactInfoContainer(participant = null, modifier = cardWidth)
254+
}
255+
}
256+
257+
// endregion

0 commit comments

Comments
 (0)