diff --git a/apps/flipcash/core/src/main/res/values/strings.xml b/apps/flipcash/core/src/main/res/values/strings.xml
index e2278171f..d9aa5d24c 100644
--- a/apps/flipcash/core/src/main/res/values/strings.xml
+++ b/apps/flipcash/core/src/main/res/values/strings.xml
@@ -772,7 +772,7 @@
You received
Delivered
- Read %1$s
+ Read
Yesterday
Today
diff --git a/apps/flipcash/features/direct-send/src/main/kotlin/com/flipcash/app/directsend/internal/screens/ContactListScreen.kt b/apps/flipcash/features/direct-send/src/main/kotlin/com/flipcash/app/directsend/internal/screens/ContactListScreen.kt
index fbc89e32b..4f21c980f 100644
--- a/apps/flipcash/features/direct-send/src/main/kotlin/com/flipcash/app/directsend/internal/screens/ContactListScreen.kt
+++ b/apps/flipcash/features/direct-send/src/main/kotlin/com/flipcash/app/directsend/internal/screens/ContactListScreen.kt
@@ -417,6 +417,7 @@ private fun ContactRowItem(
modifier = Modifier
.requiredSize(CodeTheme.dimens.staticGrid.x8)
.clip(CircleShape),
+ includeBorder = false,
)
}
diff --git a/apps/flipcash/features/messenger/src/main/kotlin/com/flipcash/app/messenger/internal/screens/MessengerScreen.kt b/apps/flipcash/features/messenger/src/main/kotlin/com/flipcash/app/messenger/internal/screens/MessengerScreen.kt
index b8b665a07..ea816462e 100644
--- a/apps/flipcash/features/messenger/src/main/kotlin/com/flipcash/app/messenger/internal/screens/MessengerScreen.kt
+++ b/apps/flipcash/features/messenger/src/main/kotlin/com/flipcash/app/messenger/internal/screens/MessengerScreen.kt
@@ -42,6 +42,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalDensity
@@ -115,15 +116,19 @@ private fun ChatTopBar(
chattingWith: DeviceContact?,
) {
var titleHeight by remember { mutableStateOf(0.dp) }
+ val bgColor = CodeTheme.colors.background
Box {
Box(
modifier = Modifier
.fillMaxWidth()
- .height(titleHeight)
- .drawWithGradient(
- color = CodeTheme.colors.background,
- startY = { size.height * 1.5f },
- endY = { size.height * 0.25f }
+ .height(titleHeight + 24.dp)
+ .background(
+ Brush.verticalGradient(
+ colorStops = arrayOf(
+ 0f to bgColor,
+ 1f to Color.Transparent,
+ )
+ )
)
)
AppBarWithTitle(
diff --git a/apps/flipcash/features/messenger/src/main/kotlin/com/flipcash/app/messenger/internal/screens/components/MessageBubble.kt b/apps/flipcash/features/messenger/src/main/kotlin/com/flipcash/app/messenger/internal/screens/components/MessageBubble.kt
index 89790f08c..efc024564 100644
--- a/apps/flipcash/features/messenger/src/main/kotlin/com/flipcash/app/messenger/internal/screens/components/MessageBubble.kt
+++ b/apps/flipcash/features/messenger/src/main/kotlin/com/flipcash/app/messenger/internal/screens/components/MessageBubble.kt
@@ -25,6 +25,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.Dp
@@ -44,6 +45,7 @@ import com.getcode.ui.core.addIf
internal enum class BubblePosition { Solo, First, Middle, Last }
private const val BUBBLE_MAX_WIDTH_FRACTION = 0.78f
+private const val CASH_BUBBLE_MAX_WIDTH_FRACTION = 0.64f
@Composable
internal fun ContentBubble(
@@ -52,7 +54,10 @@ internal fun ContentBubble(
modifier: Modifier = Modifier,
) {
BoxWithConstraints(modifier = modifier.fillMaxWidth()) {
- val bubbleMaxWidth = maxWidth * BUBBLE_MAX_WIDTH_FRACTION
+ val bubbleMaxWidth = when (item.content) {
+ is MessageContent.Text -> maxWidth * BUBBLE_MAX_WIDTH_FRACTION
+ is MessageContent.Cash -> maxWidth * CASH_BUBBLE_MAX_WIDTH_FRACTION
+ }
Row(
modifier = Modifier.fillMaxWidth(),
@@ -119,9 +124,10 @@ private fun CashBubble(
modifier = Modifier.align(Alignment.Start),
tokenName = tokenName,
tokenImage = tokenImageUrl,
- imageSize = 20.dp,
+ imageSize = CodeTheme.dimens.staticGrid.x4,
spacing = CodeTheme.dimens.grid.x1,
- textStyle = CodeTheme.typography.textSmall,
+ textStyle = CodeTheme.typography.caption,
+ textColor = CodeTheme.colors.textSecondary,
)
}
@@ -140,9 +146,9 @@ private fun CashBubble(
color = CodeTheme.colors.textSecondary,
)
PriceWithFlag(
- modifier = Modifier.padding(top = CodeTheme.dimens.grid.x1),
amount = amount.formatted(),
currencyCode = amount.currencyCode.name,
+ iconSize = CodeTheme.dimens.staticGrid.x5,
flag = exchange.getFlagByCurrency(amount.currencyCode.name),
text = { text ->
Text(
@@ -187,9 +193,9 @@ private fun Bubble(
@Composable
private fun bubbleShape(position: BubblePosition, isFromSelf: Boolean): Shape {
- // CodeTheme.shapes.medium = 12dp, extraSmall = 6dp
+ // CodeTheme.shapes.medium = 12dp, tiny = 4dp
val l = 12.dp
- val s = 6.dp
+ val s = 4.dp
// Corner radius morph — animate each corner with spring matching prototype
val cornerSpec = spring(dampingRatio = 0.68f, stiffness = 500f)
diff --git a/apps/flipcash/features/messenger/src/main/kotlin/com/flipcash/app/messenger/internal/screens/components/ReceiptLabel.kt b/apps/flipcash/features/messenger/src/main/kotlin/com/flipcash/app/messenger/internal/screens/components/ReceiptLabel.kt
index b2a73f4b1..fc8ee0fb7 100644
--- a/apps/flipcash/features/messenger/src/main/kotlin/com/flipcash/app/messenger/internal/screens/components/ReceiptLabel.kt
+++ b/apps/flipcash/features/messenger/src/main/kotlin/com/flipcash/app/messenger/internal/screens/components/ReceiptLabel.kt
@@ -11,7 +11,9 @@ import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.animation.togetherWith
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -25,13 +27,13 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewWrapper
-import kotlinx.coroutines.delay
import com.flipcash.app.theme.FlipcashThemeWrapper
import com.flipcash.features.messenger.R
import com.flipcash.services.models.chat.MessagePointer
import com.flipcash.services.models.chat.PointerType
import com.getcode.theme.CodeTheme
import com.getcode.util.formatLocalized
+import kotlinx.coroutines.delay
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime
import kotlin.time.Clock
@@ -60,7 +62,10 @@ internal fun ReceiptLabel(
AnimatedVisibility(
visible = visible,
- modifier = modifier.padding(top = CodeTheme.dimens.grid.x1),
+ modifier = modifier.padding(
+ top = CodeTheme.dimens.grid.x1,
+ end = CodeTheme.dimens.grid.x2,
+ ),
enter = scaleIn(deliveredSpec, initialScale = 0.95f) + fadeIn(deliveredSpec),
exit = fadeOut(snap()),
) {
@@ -70,25 +75,36 @@ internal fun ReceiptLabel(
targetState = status,
transitionSpec = {
(scaleIn(readSwapSpec, initialScale = 0.9f) + fadeIn(readSwapSpec)) togetherWith
- (scaleOut(readSwapSpec, targetScale = 0.9f) + fadeOut(readSwapSpec))
+ (scaleOut(readSwapSpec, targetScale = 0.9f) + fadeOut(readSwapSpec))
},
label = "receiptStatus",
) { animatedStatus ->
val text = when (animatedStatus) {
ReceiptStatus.SENT -> stringResource(R.string.label_chatReceipt_delivered)
- ReceiptStatus.READ -> {
- val readAtFormatted =
- readPointer?.timestamp?.let { formatReadTimestamp(it) } ?: ""
- stringResource(R.string.label_chatReceipt_read, readAtFormatted)
- }
-
+ ReceiptStatus.READ -> stringResource(R.string.label_chatReceipt_read)
else -> return@AnimatedContent
}
- Text(
- text = text,
- style = CodeTheme.typography.caption,
- color = CodeTheme.colors.textSecondary,
- )
+
+ val readAtFormatted =
+ readPointer?.timestamp?.let { formatReadTimestamp(it) } ?: ""
+
+ // split text into two lines to eventually support a Theme driven
+ // difference in font weights
+ Row(horizontalArrangement = Arrangement.spacedBy(CodeTheme.dimens.grid.x1)) {
+ Text(
+ text = text,
+ style = CodeTheme.typography.caption,
+ color = CodeTheme.colors.textSecondary,
+ )
+
+ if (animatedStatus == ReceiptStatus.READ && readAtFormatted.isNotEmpty()) {
+ Text(
+ text = readAtFormatted,
+ style = CodeTheme.typography.caption,
+ color = CodeTheme.colors.textSecondary,
+ )
+ }
+ }
}
}
}
diff --git a/apps/flipcash/shared/contacts/src/main/kotlin/com/flipcash/app/contacts/ui/ContactAvatar.kt b/apps/flipcash/shared/contacts/src/main/kotlin/com/flipcash/app/contacts/ui/ContactAvatar.kt
index 8fca8f7e9..43fb6e567 100644
--- a/apps/flipcash/shared/contacts/src/main/kotlin/com/flipcash/app/contacts/ui/ContactAvatar.kt
+++ b/apps/flipcash/shared/contacts/src/main/kotlin/com/flipcash/app/contacts/ui/ContactAvatar.kt
@@ -36,16 +36,25 @@ import coil3.request.ImageRequest
import coil3.request.crossfade
import com.flipcash.app.contacts.device.DeviceContact
import com.getcode.theme.CodeTheme
+import com.getcode.ui.core.addIf
@Composable
fun ContactAvatar(
contact: DeviceContact?,
modifier: Modifier = Modifier,
+ includeBorder: Boolean = true,
) {
if (contact == null || contact.isUnknown) {
Box(
modifier = modifier
- .background(Brush.linearGradient(CodeTheme.colors.contactAvatar.colors)),
+ .background(Brush.linearGradient(CodeTheme.colors.contactAvatar.colors))
+ .addIf(includeBorder) {
+ Modifier.border(
+ CodeTheme.dimens.border,
+ CodeTheme.colors.divider,
+ CircleShape,
+ )
+ },
contentAlignment = Alignment.BottomCenter,
) {
Image(
@@ -66,11 +75,13 @@ fun ContactAvatar(
} else {
ContactAvatar(
modifier = Modifier
- .border(
- CodeTheme.dimens.border,
- CodeTheme.colors.divider,
- CircleShape,
- ).then(modifier),
+ .addIf(includeBorder) {
+ Modifier.border(
+ CodeTheme.dimens.border,
+ CodeTheme.colors.divider,
+ CircleShape,
+ )
+ }.then(modifier),
photoUri = contact.photoUri,
displayName = contact.displayName,
)
diff --git a/ui/components/src/main/kotlin/com/getcode/ui/components/chat/ChatInput.kt b/ui/components/src/main/kotlin/com/getcode/ui/components/chat/ChatInput.kt
index 2ef5ad43a..ff690ea6e 100644
--- a/ui/components/src/main/kotlin/com/getcode/ui/components/chat/ChatInput.kt
+++ b/ui/components/src/main/kotlin/com/getcode/ui/components/chat/ChatInput.kt
@@ -36,6 +36,7 @@ import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -43,6 +44,7 @@ import com.getcode.theme.CodeTheme
import com.getcode.theme.DesignSystem
import com.getcode.theme.extraSmall
import com.getcode.theme.inputColors
+import com.getcode.ui.components.R
import com.getcode.ui.components.TextInput
@Composable
@@ -97,22 +99,23 @@ fun ChatInput(
transitionSpec = {
val spec = spring(dampingRatio = 0.66f, stiffness = 4000f)
(fadeIn(spec) + scaleIn(spec, initialScale = 0.6f)) togetherWith
- (fadeOut(spec) + scaleOut(spec, targetScale = 0.6f))
+ (fadeOut(spec) + scaleOut(spec, targetScale = 0.6f))
}
) { show ->
if (show) {
Icon(
modifier = Modifier
.padding(vertical = CodeTheme.dimens.grid.x1)
- .padding(end = CodeTheme.dimens.staticGrid.x2)
+ .padding(end = CodeTheme.dimens.staticGrid.x1)
.background(
Color.White,
shape = CodeTheme.shapes.extraSmall
- ).clip(CodeTheme.shapes.extraSmall)
+ )
+ .clip(CodeTheme.shapes.extraSmall)
.clickable { onSendMessage() }
.padding(CodeTheme.dimens.staticGrid.x1)
.size(CodeTheme.dimens.staticGrid.x5),
- imageVector = Icons.Rounded.ArrowUpward,
+ painter = painterResource(R.drawable.ic_arrow_up),
tint = Color.Black,
contentDescription = "Send message"
)
diff --git a/ui/components/src/main/res/drawable/ic_arrow_up.xml b/ui/components/src/main/res/drawable/ic_arrow_up.xml
new file mode 100644
index 000000000..a4625c641
--- /dev/null
+++ b/ui/components/src/main/res/drawable/ic_arrow_up.xml
@@ -0,0 +1,18 @@
+
+
+
+
diff --git a/ui/theme/src/main/kotlin/com/getcode/theme/Shape.kt b/ui/theme/src/main/kotlin/com/getcode/theme/Shape.kt
index 413778cc4..1dfd9cbc9 100644
--- a/ui/theme/src/main/kotlin/com/getcode/theme/Shape.kt
+++ b/ui/theme/src/main/kotlin/com/getcode/theme/Shape.kt
@@ -19,6 +19,8 @@ internal val shapes = Shapes(
large = RoundedCornerShape(15.dp)
)
+val Shapes.tiny: CornerBasedShape
+ @Composable get() = RoundedCornerShape(4.dp)
val Shapes.extraSmall: CornerBasedShape
@Composable get() = RoundedCornerShape(6.dp)
diff --git a/ui/theme/src/main/kotlin/com/getcode/theme/Type.kt b/ui/theme/src/main/kotlin/com/getcode/theme/Type.kt
index b490fda86..f420dbdc8 100644
--- a/ui/theme/src/main/kotlin/com/getcode/theme/Type.kt
+++ b/ui/theme/src/main/kotlin/com/getcode/theme/Type.kt
@@ -112,7 +112,6 @@ val codeTypography = CodeTypography(
lineHeight = 18.sp,
fontFamily = Avenir,
fontWeight = FontWeight.Medium,
- color = White50,
),
linkMedium = TextStyle(
fontSize = 16.sp,