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,