Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ import com.flipcash.shared.chat.ui.ChatAnimations
import com.flipcash.shared.chat.ui.GroupInviteCardDefaults
import com.flipcash.shared.chat.ui.GroupInviteLinkCard
import com.flipcash.shared.chat.ui.QuickReactionStripPlacement
import com.flipcash.shared.chat.ui.transcriptCardWidth
import com.flipcash.shared.chat.ui.transcriptBubbleMaxWidth
import com.getcode.theme.CodeTheme
import com.getcode.ui.utils.rememberKeyboardController
import com.getcode.util.vibration.LocalVibrator
Expand Down Expand Up @@ -478,7 +478,7 @@ internal fun MessageList(
) {
// The width a link card gets in a DM transcript, so this reads as the
// same card sent there (iOS sizes both the same way).
val cardWidth = transcriptCardWidth(
val cardWidth = transcriptBubbleMaxWidth(
rowWidth = maxWidth,
rowInset = CodeTheme.dimens.inset,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import com.flipcash.shared.chat.reactions.ReactionStrip
import com.flipcash.shared.chat.ui.ContentBubble
import com.flipcash.shared.chat.ui.QuickReactionStripPopup
import com.flipcash.shared.chat.ui.ReactionPillRow
import com.flipcash.shared.chat.ui.bubbleMaxWidth
import com.flipcash.shared.chat.ui.bubblePositionOf
import com.flipcash.shared.chat.ui.rendersBare
import com.flipcash.shared.common.ui.ContactAvatar
Expand Down Expand Up @@ -399,9 +400,9 @@ internal fun MessageRow(
)
}
}
// Same width as the bubble above it (decision 2) — matched here
// against the same fraction MessageBubble sizes a text/reply/deleted
// bubble to, since the row doesn't expose its resolved width outward.
// Same width as the bubble above it (decision 2) — sized by the same
// rule MessageBubble caps every bubble with, since the row doesn't
// expose its resolved width outward.
// Kept on screen while selecting, as iOS keeps them under its
// backdrop, but inert like every other target on the row.
//
Expand Down Expand Up @@ -433,7 +434,13 @@ internal fun MessageRow(
},
modifier = Modifier
.align(if (item.isFromSelf) Alignment.TopEnd else Alignment.TopStart)
.width(maxWidth * BUBBLE_ROW_WIDTH_FRACTION),
.width(
bubbleMaxWidth(
content = item.content,
rowWidth = maxWidth,
rowInset = CodeTheme.dimens.inset,
),
),
alignEnd = item.isFromSelf,
animateInitialPills = !pillsAtFirstComposition,
)
Expand Down Expand Up @@ -554,10 +561,6 @@ private fun senderNameInset(showsGutter: Boolean): Dp =
CodeTheme.dimens.grid.x1
}

// Matches MessageBubble's own BUBBLE_MAX_WIDTH_FRACTION for a text/reply/deleted bubble — the row
// doesn't expose its resolved width outward, so the pill row underneath it re-derives the same
// fraction of the shared row width instead.
private const val BUBBLE_ROW_WIDTH_FRACTION = 0.78f
private val STRIP_BAR_GAP = 8.dp

private val AFFORDANCE_SIZE = 32.dp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ internal fun rememberSwipeToReply(
* where the bubble is aligned to the far side.
*
* [width] is that one avatar column's width, reused as the outgoing strip so both read the same. It
* cannot eat into an outgoing bubble: a bubble stops at 0.78 of the row, which leaves far more than
* cannot eat into an outgoing bubble: a bubble stops at 0.78 of the transcript, which leaves more than
* this to its leading side at every width the app runs at. The one content that fills the row is a
* system notice, and those carry no capabilities at all, so no drag on one was ever going to reply.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,19 +83,32 @@ enum class BubblePosition { Solo, First, Middle, Last }

private const val BUBBLE_MAX_WIDTH_FRACTION = 0.78f

private const val CASH_CARD_WIDTH_FRACTION = 0.64f

/**
* How wide a link card is drawn in a transcript whose rows are inset by [rowInset] on each side.
* The widest a bubble or link card grows in a transcript whose rows are inset by [rowInset] on each
* side.
*
* The same fraction as a text bubble, but of the transcript's full width rather than of the inset
* row, which is how iOS sizes its link cards and its group's head card
* (`ChatViewController.maxBubbleWidthFraction`). Taken of the inset row, the cards came out about
* 30dp narrower than iOS's on the same screen.
* A fraction of the transcript's full width rather than of the inset row, which is how iOS sizes
* its bubbles, link cards, and group head card (`ChatViewController.maxBubbleWidthFraction`). Taken
* of the inset row, they came out about 30dp narrower than iOS's on the same screen.
*/
fun transcriptCardWidth(rowWidth: Dp, rowInset: Dp): Dp =
fun transcriptBubbleMaxWidth(rowWidth: Dp, rowInset: Dp): Dp =
(rowWidth + rowInset * 2) * BUBBLE_MAX_WIDTH_FRACTION

/**
* The widest a bubble carrying [content] grows in a row [rowWidth] wide, inset by [rowInset] on each
* side. A system notice spans the row, and a cash card keeps its own narrower fraction of the inset
* row (iOS's `ChatViewController.cashCardWidthFraction` matches it); everything else takes
* [transcriptBubbleMaxWidth].
*/
fun bubbleMaxWidth(content: MessageContent, rowWidth: Dp, rowInset: Dp): Dp = when (content) {
is MessageContent.System -> rowWidth
is MessageContent.Cash -> rowWidth * CASH_CARD_WIDTH_FRACTION
else -> transcriptBubbleMaxWidth(rowWidth = rowWidth, rowInset = rowInset)
}

private val EDITED_MARKER_GAP = 6.dp
private const val CASH_BUBBLE_MAX_WIDTH_FRACTION = 0.64f

/**
* @param onLongClick what a long-press on this bubble reports, or `null` where the row behind it
Expand All @@ -118,18 +131,11 @@ fun ContentBubble(
val actionHandler = LocalChatActionHandler.current
val jumbo = remember(item) { item.rendersBareEmoji() }
BoxWithConstraints(modifier = Modifier.fillMaxWidth()) {
val isCardRow = item.linkCard != null && item.part == MessagePart.Card
val bubbleMaxWidth = if (isCardRow) {
transcriptCardWidth(rowWidth = maxWidth, rowInset = CodeTheme.dimens.inset)
} else when (item.content) {
is MessageContent.Text -> maxWidth * BUBBLE_MAX_WIDTH_FRACTION
is MessageContent.Cash -> maxWidth * CASH_BUBBLE_MAX_WIDTH_FRACTION
is MessageContent.Deleted -> maxWidth * BUBBLE_MAX_WIDTH_FRACTION
is MessageContent.Media -> maxWidth * CASH_BUBBLE_MAX_WIDTH_FRACTION
is MessageContent.Reply -> maxWidth * BUBBLE_MAX_WIDTH_FRACTION
is MessageContent.System -> maxWidth
is MessageContent.Encrypted -> maxWidth * BUBBLE_MAX_WIDTH_FRACTION
}
val bubbleMaxWidth = bubbleMaxWidth(
content = item.content,
rowWidth = maxWidth,
rowInset = CodeTheme.dimens.inset,
)

// A split message's citation goes on its first row and its marker on its last, so the
// pieces read as one message stacked rather than as three.
Expand Down
Loading