feat(messages): Implement reply functionality
This commit introduces the ability to reply to messages. - **API & Data Layer:** - Replaced `replyTo` parameter with `forward` in `sendMessage` calls across the data, domain, and repository layers to support the new reply mechanism. - **ViewModel:** - Added logic to handle the reply state, including storing the replied message's ID (`replyToCmId`). - When a message is sent, it now correctly constructs a `forward` JSON object if it is a reply. - The UI state (`MessagesHistoryScreenState`) is updated to show and hide the reply preview. - Added a `onReplyCloseClicked` handler to cancel a reply. - The ViewModel interface was removed, and the implementation class `MessagesHistoryViewModelImpl` is used directly. - **UI (Compose):** - A new `ReplyContainer` is displayed above the message input bar when a reply is active. - The input bar's corner radius animates to integrate with the reply container. - Added a `FocusRequester` to automatically focus the input field when the reply action is selected. - Added spacing in the message list to prevent the reply preview from overlapping messages. - The message options dialog now passes the `messageId` and `cmId` when an option is picked.
This commit is contained in:
@@ -32,7 +32,7 @@ interface MessagesRepository {
|
||||
peerId: Long,
|
||||
randomId: Long,
|
||||
message: String?,
|
||||
replyTo: Long?,
|
||||
forward: String?,
|
||||
attachments: List<VkAttachment>?,
|
||||
formatData: VkMessage.FormatData?
|
||||
): ApiResult<MessagesSendResponse, RestApiErrorDomain>
|
||||
|
||||
+2
-2
@@ -195,7 +195,7 @@ class MessagesRepositoryImpl(
|
||||
peerId: Long,
|
||||
randomId: Long,
|
||||
message: String?,
|
||||
replyTo: Long?,
|
||||
forward: String?,
|
||||
attachments: List<VkAttachment>?,
|
||||
formatData: VkMessage.FormatData?
|
||||
): ApiResult<MessagesSendResponse, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
@@ -203,7 +203,7 @@ class MessagesRepositoryImpl(
|
||||
peerId = peerId,
|
||||
randomId = randomId,
|
||||
message = message,
|
||||
replyTo = replyTo,
|
||||
forward = forward,
|
||||
attachments = attachments,
|
||||
formatData = formatData
|
||||
)
|
||||
|
||||
@@ -32,7 +32,7 @@ interface MessagesUseCase : BaseUseCase {
|
||||
peerId: Long,
|
||||
randomId: Long,
|
||||
message: String?,
|
||||
replyTo: Long?,
|
||||
forward: String?,
|
||||
attachments: List<VkAttachment>?,
|
||||
formatData: VkMessage.FormatData?
|
||||
): Flow<State<MessagesSendResponse>>
|
||||
|
||||
@@ -56,7 +56,7 @@ class MessagesUseCaseImpl(
|
||||
peerId: Long,
|
||||
randomId: Long,
|
||||
message: String?,
|
||||
replyTo: Long?,
|
||||
forward: String?,
|
||||
attachments: List<VkAttachment>?,
|
||||
formatData: VkMessage.FormatData?
|
||||
): Flow<State<MessagesSendResponse>> = flowNewState {
|
||||
@@ -64,7 +64,7 @@ class MessagesUseCaseImpl(
|
||||
peerId = peerId,
|
||||
randomId = randomId,
|
||||
message = message,
|
||||
replyTo = replyTo,
|
||||
forward = forward,
|
||||
attachments = attachments,
|
||||
formatData = formatData
|
||||
).mapToState()
|
||||
|
||||
@@ -34,7 +34,7 @@ data class MessagesSendRequest(
|
||||
val message: String?,
|
||||
val lat: Int? = null,
|
||||
val lon: Int? = null,
|
||||
val replyTo: Long? = null,
|
||||
val forward: String? = null,
|
||||
val stickerId: Long? = null,
|
||||
val disableMentions: Boolean? = null,
|
||||
val doNotParseLinks: Boolean? = null,
|
||||
@@ -51,7 +51,7 @@ data class MessagesSendRequest(
|
||||
message?.let { this["message"] = it }
|
||||
lat?.let { this["lat"] = it.toString() }
|
||||
lon?.let { this["lon"] = it.toString() }
|
||||
replyTo?.let { this["reply_to"] = it.toString() }
|
||||
forward?.let { this["forward"] = it }
|
||||
stickerId?.let { this["sticker_id"] = it.toString() }
|
||||
disableMentions?.let { this["disable_mentions"] = it.asInt().toString() }
|
||||
doNotParseLinks?.let { this["dont_parse_links"] = it.asInt().toString() }
|
||||
|
||||
+66
-56
@@ -65,62 +65,15 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.json.add
|
||||
import kotlinx.serialization.json.buildJsonArray
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import kotlin.math.abs
|
||||
import kotlin.random.Random
|
||||
|
||||
interface MessagesHistoryViewModel {
|
||||
|
||||
val screenState: StateFlow<MessagesHistoryScreenState>
|
||||
val navigation: StateFlow<MessageNavigation?>
|
||||
val messages: StateFlow<List<VkMessage>>
|
||||
val uiMessages: StateFlow<List<UiItem>>
|
||||
val dialog: StateFlow<MessageDialog?>
|
||||
val selectedMessages: StateFlow<List<VkMessage>>
|
||||
|
||||
val isNeedToScrollToIndex: StateFlow<Int?>
|
||||
|
||||
val baseError: StateFlow<BaseError?>
|
||||
val imagesToPreload: StateFlow<List<String>>
|
||||
|
||||
val currentOffset: StateFlow<Int>
|
||||
val canPaginate: StateFlow<Boolean>
|
||||
|
||||
fun onNavigationConsumed()
|
||||
|
||||
fun onTopBarClicked()
|
||||
|
||||
fun onDialogConfirmed(dialog: MessageDialog, bundle: Bundle)
|
||||
fun onDialogDismissed(dialog: MessageDialog)
|
||||
fun onDialogItemPicked(dialog: MessageDialog, bundle: Bundle)
|
||||
|
||||
fun onScrolledToIndex()
|
||||
|
||||
fun onCloseButtonClicked()
|
||||
fun onRefresh()
|
||||
fun onAttachmentButtonClicked()
|
||||
fun onMessageInputChanged(newText: TextFieldValue)
|
||||
fun onEmojiButtonLongClicked()
|
||||
fun onActionButtonClicked()
|
||||
|
||||
fun onPaginationConditionsMet()
|
||||
|
||||
fun onMessageClicked(messageId: Long)
|
||||
fun onMessageLongClicked(messageId: Long)
|
||||
|
||||
fun onPinnedMessageClicked(messageId: Long)
|
||||
fun onUnpinMessageClicked()
|
||||
|
||||
fun onDeleteSelectedMessagesClicked()
|
||||
|
||||
fun onBoldClicked()
|
||||
fun onItalicClicked()
|
||||
fun onUnderlineClicked()
|
||||
fun onLinkClicked()
|
||||
fun onRegularClicked()
|
||||
}
|
||||
|
||||
class MessagesHistoryViewModelImpl(
|
||||
private val applicationContext: Context,
|
||||
private val messagesUseCase: MessagesUseCase,
|
||||
@@ -137,6 +90,8 @@ class MessagesHistoryViewModelImpl(
|
||||
override val dialog = MutableStateFlow<MessageDialog?>(null)
|
||||
override val selectedMessages = MutableStateFlow<List<VkMessage>>(emptyList())
|
||||
|
||||
override val inputFieldFocusRequester = MutableStateFlow(false)
|
||||
|
||||
override val isNeedToScrollToIndex = MutableStateFlow<Int?>(null)
|
||||
|
||||
override val baseError = MutableStateFlow<BaseError?>(null)
|
||||
@@ -154,6 +109,8 @@ class MessagesHistoryViewModelImpl(
|
||||
private val sendingMessages: MutableList<VkMessage> = mutableListOf()
|
||||
private val failedMessages: MutableList<VkMessage> = mutableListOf()
|
||||
|
||||
private var replyToCmId: Long? = null
|
||||
|
||||
init {
|
||||
val arguments = MessagesHistory.from(savedStateHandle).arguments
|
||||
|
||||
@@ -268,6 +225,9 @@ class MessagesHistoryViewModelImpl(
|
||||
override fun onDialogItemPicked(dialog: MessageDialog, bundle: Bundle) {
|
||||
when (dialog) {
|
||||
is MessageDialog.MessageOptions -> {
|
||||
val messageId = bundle.getLong("messageId")
|
||||
val cmId = bundle.getLong("cmId")
|
||||
|
||||
when (val option = bundle.getParcelableCompat("option", MessageOption::class)) {
|
||||
null -> Unit
|
||||
|
||||
@@ -275,9 +235,30 @@ class MessagesHistoryViewModelImpl(
|
||||
// TODO: 28-Mar-25, Danil Nikolaev: retry sending
|
||||
}
|
||||
|
||||
MessageOption.Reply -> {}
|
||||
MessageOption.ForwardHere -> {}
|
||||
MessageOption.Forward -> {}
|
||||
MessageOption.Reply -> {
|
||||
inputFieldFocusRequester.setValue { true }
|
||||
replyToCmId = cmId
|
||||
screenState.setValue { old ->
|
||||
val msg = messages.value.find { it.id == messageId }
|
||||
|
||||
if (msg == null) {
|
||||
old
|
||||
} else {
|
||||
old.copy(
|
||||
replyTitle = msg.extractTitle(),
|
||||
replyText = msg.text
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MessageOption.ForwardHere -> {
|
||||
|
||||
}
|
||||
|
||||
MessageOption.Forward -> {
|
||||
|
||||
}
|
||||
|
||||
MessageOption.Pin -> {
|
||||
this.dialog.setValue {
|
||||
@@ -584,6 +565,17 @@ class MessagesHistoryViewModelImpl(
|
||||
updateStyles()
|
||||
}
|
||||
|
||||
override fun onReplyCloseClicked() {
|
||||
replyToCmId = null
|
||||
|
||||
screenState.setValue { old ->
|
||||
old.copy(
|
||||
replyTitle = null,
|
||||
replyText = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleNewMessage(event: LongPollParsedEvent.NewMessage) {
|
||||
val message = event.message
|
||||
|
||||
@@ -927,6 +919,7 @@ class MessagesHistoryViewModelImpl(
|
||||
forwards = null,
|
||||
attachments = null,
|
||||
replyMessage = when {
|
||||
replyToCmId != null -> messages.value.find { it.cmId == replyToCmId }
|
||||
else -> null
|
||||
},
|
||||
geoType = null,
|
||||
@@ -947,15 +940,32 @@ class MessagesHistoryViewModelImpl(
|
||||
screenState.setValue { old ->
|
||||
old.copy(
|
||||
message = TextFieldValue(),
|
||||
actionMode = ActionMode.RECORD_AUDIO
|
||||
actionMode = ActionMode.RECORD_AUDIO,
|
||||
replyTitle = null,
|
||||
replyText = null
|
||||
)
|
||||
}
|
||||
|
||||
val replyCmId = replyToCmId
|
||||
replyToCmId = null
|
||||
|
||||
val forward = when {
|
||||
replyCmId != null -> {
|
||||
buildJsonObject {
|
||||
put("peer_id", screenState.value.conversationId)
|
||||
put("conversation_message_ids", buildJsonArray { add(replyCmId) })
|
||||
put("is_reply", true)
|
||||
}.toString()
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
messagesUseCase.sendMessage(
|
||||
peerId = screenState.value.conversationId,
|
||||
randomId = newMessage.randomId,
|
||||
message = newMessage.text,
|
||||
replyTo = null,
|
||||
forward = forward,
|
||||
attachments = null,
|
||||
formatData = newMessage.formatData
|
||||
).listenValue(viewModelScope) { state ->
|
||||
+6
-2
@@ -24,7 +24,9 @@ data class MessagesHistoryScreenState(
|
||||
val conversation: VkConversation,
|
||||
val pinnedMessage: VkMessage?,
|
||||
val pinnedTitle: String?,
|
||||
val pinnedSummary: AnnotatedString?
|
||||
val pinnedSummary: AnnotatedString?,
|
||||
val replyTitle: String?,
|
||||
val replyText: String?
|
||||
) {
|
||||
|
||||
companion object {
|
||||
@@ -43,7 +45,9 @@ data class MessagesHistoryScreenState(
|
||||
conversation = VkConversation.EMPTY,
|
||||
pinnedMessage = null,
|
||||
pinnedTitle = null,
|
||||
pinnedSummary = null
|
||||
pinnedSummary = null,
|
||||
replyTitle = null,
|
||||
replyText = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -180,7 +180,13 @@ fun MessageOptionsDialog(
|
||||
onClick = {
|
||||
onDismissed()
|
||||
val pickedOption = options[index]
|
||||
onItemPicked(bundleOf("option" to pickedOption))
|
||||
onItemPicked(
|
||||
bundleOf(
|
||||
"option" to pickedOption,
|
||||
"messageId" to message.id,
|
||||
"cmId" to message.cmId
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
+44
-7
@@ -1,7 +1,9 @@
|
||||
package dev.meloda.fast.messageshistory.presentation
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.scaleIn
|
||||
@@ -32,10 +34,14 @@ import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.material3.surfaceColorAtElevation
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -54,9 +60,9 @@ import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
||||
import dev.chrisbanes.haze.materials.HazeMaterials
|
||||
import dev.meloda.fast.datastore.AppSettings
|
||||
import dev.meloda.fast.messageshistory.model.ActionMode
|
||||
import dev.meloda.fast.ui.R
|
||||
import dev.meloda.fast.ui.components.IconButton
|
||||
import dev.meloda.fast.ui.theme.LocalThemeConfig
|
||||
import dev.meloda.fast.ui.R
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class, ExperimentalHazeMaterialsApi::class)
|
||||
@Composable
|
||||
@@ -68,6 +74,9 @@ fun MessagesHistoryInputBar(
|
||||
showEmojiButton: Boolean,
|
||||
showAttachmentButton: Boolean,
|
||||
actionMode: ActionMode,
|
||||
replyTitle: String?,
|
||||
replyText: String?,
|
||||
inputFieldFocusRequester: Boolean,
|
||||
onMessageInputChanged: (TextFieldValue) -> Unit = {},
|
||||
onBoldRequested: () -> Unit = {},
|
||||
onItalicRequested: () -> Unit = {},
|
||||
@@ -77,16 +86,28 @@ fun MessagesHistoryInputBar(
|
||||
onSetMessageBarHeight: (Dp) -> Unit = {},
|
||||
onEmojiButtonLongClicked: () -> Unit = {},
|
||||
onAttachmentButtonClicked: () -> Unit = {},
|
||||
onActionButtonClicked: () -> Unit = {}
|
||||
onActionButtonClicked: () -> Unit = {},
|
||||
onReplyCloseClicked: () -> Unit = {}
|
||||
) {
|
||||
val view = LocalView.current
|
||||
val context = LocalContext.current
|
||||
val density = LocalDensity.current
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
|
||||
LaunchedEffect(inputFieldFocusRequester) {
|
||||
if (inputFieldFocusRequester) {
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
val theme = LocalThemeConfig.current
|
||||
|
||||
val inputBarTopCornerRadius by animateDpAsState(
|
||||
targetValue = if (replyTitle == null) 24.dp else 0.dp,
|
||||
label = "inputBarTopCornerRadius"
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
@@ -95,6 +116,14 @@ fun MessagesHistoryInputBar(
|
||||
.navigationBarsPadding()
|
||||
.imePadding()
|
||||
) {
|
||||
AnimatedVisibility(replyTitle != null) {
|
||||
ReplyContainer(
|
||||
title = replyTitle.orEmpty(),
|
||||
text = replyText.orEmpty(),
|
||||
onCloseClicked = onReplyCloseClicked,
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -102,10 +131,17 @@ fun MessagesHistoryInputBar(
|
||||
.imeNestedScroll(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(36.dp))
|
||||
.clip(
|
||||
RoundedCornerShape(
|
||||
topStart = inputBarTopCornerRadius,
|
||||
topEnd = inputBarTopCornerRadius,
|
||||
bottomStart = 24.dp,
|
||||
bottomEnd = 24.dp
|
||||
)
|
||||
)
|
||||
.then(
|
||||
if (theme.enableBlur) {
|
||||
Modifier
|
||||
@@ -166,6 +202,7 @@ fun MessagesHistoryInputBar(
|
||||
|
||||
TextField(
|
||||
modifier = Modifier
|
||||
.focusRequester(focusRequester)
|
||||
.weight(1f)
|
||||
.appendTextContextMenuComponents {
|
||||
separator()
|
||||
@@ -287,7 +324,7 @@ fun MessagesHistoryInputBar(
|
||||
Spacer(modifier = Modifier.width(6.dp))
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -4,7 +4,6 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import dev.meloda.fast.common.model.UiImage
|
||||
import dev.meloda.fast.datastore.AppSettings
|
||||
import dev.meloda.fast.messageshistory.MessagesHistoryViewModel
|
||||
import dev.meloda.fast.messageshistory.MessagesHistoryViewModelImpl
|
||||
@@ -30,6 +29,7 @@ fun MessagesHistoryRoute(
|
||||
val baseError by viewModel.baseError.collectAsStateWithLifecycle()
|
||||
val canPaginate by viewModel.canPaginate.collectAsStateWithLifecycle()
|
||||
val scrollIndex by viewModel.isNeedToScrollToIndex.collectAsStateWithLifecycle()
|
||||
val inputFieldFocusRequester by viewModel.inputFieldFocusRequester.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(navigationEvent) {
|
||||
val needToConsume = when (val navigation = navigationEvent) {
|
||||
@@ -56,6 +56,7 @@ fun MessagesHistoryRoute(
|
||||
showEmojiButton = AppSettings.General.showEmojiButton,
|
||||
showAttachmentButton = AppSettings.General.showAttachmentButton,
|
||||
enableHaptic = AppSettings.General.enableHaptic,
|
||||
inputFieldFocusRequester = inputFieldFocusRequester,
|
||||
onBack = onBack,
|
||||
onClose = viewModel::onCloseButtonClicked,
|
||||
onScrolledToIndex = viewModel::onScrolledToIndex,
|
||||
@@ -77,7 +78,8 @@ fun MessagesHistoryRoute(
|
||||
onItalicRequested = viewModel::onItalicClicked,
|
||||
onUnderlineRequested = viewModel::onUnderlineClicked,
|
||||
onLinkRequested = viewModel::onLinkClicked,
|
||||
onRegularRequested = viewModel::onRegularClicked
|
||||
onRegularRequested = viewModel::onRegularClicked,
|
||||
onReplyCloseClicked = viewModel::onReplyCloseClicked
|
||||
)
|
||||
|
||||
HandleDialogs(
|
||||
|
||||
+10
-3
@@ -44,13 +44,13 @@ import dev.meloda.fast.messageshistory.model.UiItem
|
||||
import dev.meloda.fast.messageshistory.util.indexOfMessageByCmId
|
||||
import dev.meloda.fast.model.BaseError
|
||||
import dev.meloda.fast.model.api.domain.VkMessage
|
||||
import dev.meloda.fast.ui.R
|
||||
import dev.meloda.fast.ui.components.Loader
|
||||
import dev.meloda.fast.ui.components.VkErrorView
|
||||
import dev.meloda.fast.ui.theme.LocalThemeConfig
|
||||
import dev.meloda.fast.ui.util.ImmutableList
|
||||
import dev.meloda.fast.ui.util.emptyImmutableList
|
||||
import kotlinx.coroutines.launch
|
||||
import dev.meloda.fast.ui.R
|
||||
|
||||
@OptIn(
|
||||
ExperimentalMaterial3Api::class,
|
||||
@@ -70,6 +70,7 @@ fun MessagesHistoryScreen(
|
||||
showEmojiButton: Boolean = false,
|
||||
showAttachmentButton: Boolean = false,
|
||||
enableHaptic: Boolean = false,
|
||||
inputFieldFocusRequester: Boolean,
|
||||
onBack: () -> Unit = {},
|
||||
onClose: () -> Unit = {},
|
||||
onScrolledToIndex: () -> Unit = {},
|
||||
@@ -91,7 +92,8 @@ fun MessagesHistoryScreen(
|
||||
onItalicRequested: () -> Unit = {},
|
||||
onLinkRequested: () -> Unit = {},
|
||||
onUnderlineRequested: () -> Unit = {},
|
||||
onRegularRequested: () -> Unit = {}
|
||||
onRegularRequested: () -> Unit = {},
|
||||
onReplyCloseClicked: () -> Unit = {},
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val view = LocalView.current
|
||||
@@ -215,6 +217,7 @@ fun MessagesHistoryScreen(
|
||||
uiMessages = uiMessages,
|
||||
isSelectedAtLeastOne = isSelectedAtLeastOne,
|
||||
isPaginating = screenState.isPaginating,
|
||||
isReplying = screenState.replyTitle != null,
|
||||
messageBarHeight = messageBarHeight,
|
||||
onRequestScrollToCmId = { cmId ->
|
||||
val index = uiMessages.values.indexOfMessageByCmId(cmId)
|
||||
@@ -252,10 +255,14 @@ fun MessagesHistoryScreen(
|
||||
showEmojiButton = showEmojiButton,
|
||||
showAttachmentButton = showAttachmentButton,
|
||||
actionMode = screenState.actionMode,
|
||||
replyTitle = screenState.replyTitle,
|
||||
replyText = screenState.replyText,
|
||||
inputFieldFocusRequester = inputFieldFocusRequester,
|
||||
onSetMessageBarHeight = { messageBarHeight = it },
|
||||
onEmojiButtonLongClicked = onEmojiButtonLongClicked,
|
||||
onAttachmentButtonClicked = onAttachmentButtonClicked,
|
||||
onActionButtonClicked = onActionButtonClicked
|
||||
onActionButtonClicked = onActionButtonClicked,
|
||||
onReplyCloseClicked = onReplyCloseClicked
|
||||
)
|
||||
|
||||
when {
|
||||
|
||||
+5
@@ -53,6 +53,7 @@ fun MessagesList(
|
||||
uiMessages: ImmutableList<UiItem>,
|
||||
isSelectedAtLeastOne: Boolean,
|
||||
isPaginating: Boolean,
|
||||
isReplying: Boolean,
|
||||
messageBarHeight: Dp,
|
||||
onRequestScrollToCmId: (cmId: Long) -> Unit = {},
|
||||
onMessageClicked: (Long) -> Unit = {},
|
||||
@@ -132,6 +133,10 @@ fun MessagesList(
|
||||
reverseLayout = true
|
||||
) {
|
||||
item {
|
||||
AnimatedVisibility(isReplying) {
|
||||
Spacer(modifier = Modifier.height(48.dp))
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(messageBarHeight.plus(18.dp)))
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
|
||||
Reference in New Issue
Block a user