Timeline: clean up and fix some ui glitches

This commit is contained in:
ganfra 2022-12-02 18:14:49 +01:00
parent 3d25464bdc
commit 13bbcc6e26
3 changed files with 81 additions and 42 deletions

View file

@ -1,4 +1,8 @@
@file:OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
@file:OptIn(
ExperimentalMaterial3Api::class,
ExperimentalMaterialApi::class,
ExperimentalComposeUiApi::class
)
package io.element.android.x.features.messages
@ -21,8 +25,10 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.End
import androidx.compose.ui.Alignment.Companion.Start
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.LastBaseline
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
@ -48,8 +54,6 @@ import kotlinx.coroutines.launch
import timber.log.Timber
import java.lang.Math.random
private val COMPOSER_HEIGHT = 112.dp
@Composable
fun MessagesScreen(
roomId: String,
@ -64,10 +68,12 @@ fun MessagesScreen(
}
LogCompositions(tag = "MessagesScreen", msg = "Root")
val actionsSheetState = rememberModalBottomSheetState(
val itemActionsBottomSheetState = rememberModalBottomSheetState(
initialValue = ModalBottomSheetValue.Hidden,
)
val snackbarHostState = remember { SnackbarHostState() }
val coroutineScope = rememberCoroutineScope()
val focusManager = LocalFocusManager.current
val roomTitle by viewModel.collectAsState(MessagesViewState::roomName)
val roomAvatar by viewModel.collectAsState(MessagesViewState::roomAvatar)
val timelineItems by viewModel.collectAsState(MessagesViewState::timelineItems)
@ -78,7 +84,7 @@ fun MessagesScreen(
val composerFullScreen by composerViewModel.collectAsState(MessageComposerViewState::isFullScreen)
val composerCanSendMessage by composerViewModel.collectAsState(MessageComposerViewState::isSendButtonVisible)
val composerText by composerViewModel.collectAsState(MessageComposerViewState::text)
val snackbarHostState = remember { SnackbarHostState() }
MessagesScreenContent(
roomTitle = roomTitle,
roomAvatar = roomAvatar,
@ -99,33 +105,18 @@ fun MessagesScreen(
Timber.v("onClick on timeline item: ${it.id}")
},
onLongClick = {
focusManager.clearFocus(force = true)
viewModel.computeActionsSheetState(it)
coroutineScope.launch {
actionsSheetState.show()
itemActionsBottomSheetState.show()
}
},
snackbarHostState = snackbarHostState,
)
val itemActionsSheetState by viewModel.collectAsState(prop1 = MessagesViewState::itemActionsSheetState)
TimelineItemActionsScreen(
sheetState = actionsSheetState,
actionsSheetState = itemActionsSheetState(),
onActionClicked = {
viewModel.handleItemAction(it)
coroutineScope.launch {
val targetEvent = viewModel.getTargetEvent()
when (it) {
is MessagesItemAction.Edit -> {
// Entering Edit mode, update the text in the composer.
val newComposerText =
(targetEvent?.content as? MessagesTimelineItemTextBasedContent)?.body.orEmpty()
composerViewModel.updateText(newComposerText)
}
else -> Unit
}
actionsSheetState.hide()
}
}
viewModel = viewModel,
composerViewModel = composerViewModel,
modalBottomSheetState = itemActionsBottomSheetState,
)
snackBarContent?.let {
coroutineScope.launch {
@ -185,7 +176,12 @@ fun MessagesScreenContent(
composerText = composerText
)
},
snackbarHost = { SnackbarHost(snackbarHostState) },
snackbarHost = {
SnackbarHost(
snackbarHostState,
modifier = Modifier.navigationBarsPadding()
)
},
)
}

View file

@ -2,6 +2,7 @@ package io.element.android.x.features.messages
import com.airbnb.mvrx.MavericksViewModel
import com.airbnb.mvrx.MavericksViewModelFactory
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
import io.element.android.x.designsystem.components.avatar.AvatarData
import io.element.android.x.designsystem.components.avatar.AvatarSize
@ -92,12 +93,8 @@ class MessagesViewModel(
return currentState.itemActionsSheetState.invoke()?.targetItem
}
fun handleItemAction(action: MessagesItemAction) {
fun handleItemAction(action: MessagesItemAction, targetEvent: MessagesTimelineItemState.MessageEvent) {
viewModelScope.launch(Dispatchers.Default) {
val currentState = awaitState()
Timber.v("Handle $action for ${currentState.itemActionsSheetState}")
val targetEvent = getTargetEvent()
?: return@launch
when (action) {
MessagesItemAction.Copy -> notImplementedYet()
MessagesItemAction.Forward -> notImplementedYet()
@ -152,7 +149,11 @@ class MessagesViewModel(
}
}
fun computeActionsSheetState(messagesTimelineItemState: MessagesTimelineItemState.MessageEvent) {
fun computeActionsSheetState(messagesTimelineItemState: MessagesTimelineItemState.MessageEvent?) {
if (messagesTimelineItemState == null) {
setState { copy(itemActionsSheetState = Uninitialized) }
return
}
suspend {
val actions =
if (messagesTimelineItemState.content is MessagesTimelineItemRedactedContent) {

View file

@ -7,29 +7,71 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.unit.dp
import com.airbnb.mvrx.compose.collectAsState
import io.element.android.x.designsystem.components.VectorIcon
import io.element.android.x.features.messages.MessagesViewModel
import io.element.android.x.features.messages.model.MessagesItemAction
import io.element.android.x.features.messages.model.MessagesItemActionsSheetState
import io.element.android.x.features.messages.model.MessagesTimelineItemState
import io.element.android.x.features.messages.model.MessagesViewState
import io.element.android.x.features.messages.model.content.MessagesTimelineItemTextBasedContent
import io.element.android.x.features.messages.textcomposer.MessageComposerViewModel
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
@Composable
fun TimelineItemActionsScreen(
sheetState: ModalBottomSheetState,
actionsSheetState: MessagesItemActionsSheetState?,
onActionClicked: (MessagesItemAction) -> Unit,
viewModel: MessagesViewModel,
composerViewModel: MessageComposerViewModel,
modalBottomSheetState: ModalBottomSheetState,
modifier: Modifier = Modifier
) {
val coroutineScope = rememberCoroutineScope()
LaunchedEffect(modalBottomSheetState) {
snapshotFlow { modalBottomSheetState.currentValue }
.filter { it == ModalBottomSheetValue.Hidden }
.collect {
viewModel.computeActionsSheetState(null)
}
}
val itemActionsSheetState by viewModel.collectAsState(MessagesViewState::itemActionsSheetState)
fun onItemActionClicked(
itemAction: MessagesItemAction,
targetItem: MessagesTimelineItemState.MessageEvent
) {
viewModel.handleItemAction(itemAction, targetItem)
coroutineScope.launch {
val targetEvent = viewModel.getTargetEvent()
when (itemAction) {
is MessagesItemAction.Edit -> {
// Entering Edit mode, update the text in the composer.
val newComposerText =
(targetEvent?.content as? MessagesTimelineItemTextBasedContent)?.body.orEmpty()
composerViewModel.updateText(newComposerText)
}
else -> Unit
}
modalBottomSheetState.hide()
}
}
ModalBottomSheetLayout(
modifier = modifier,
sheetState = sheetState,
sheetState = modalBottomSheetState,
sheetContent = {
SheetContent(
actionsSheetState = actionsSheetState,
onActionClicked = onActionClicked,
modifier = Modifier.navigationBarsPadding()
actionsSheetState = itemActionsSheetState(),
onActionClicked = ::onItemActionClicked,
modifier = Modifier.navigationBarsPadding().imePadding()
)
}
) {}
@ -39,7 +81,7 @@ fun TimelineItemActionsScreen(
@Composable
private fun SheetContent(
actionsSheetState: MessagesItemActionsSheetState?,
onActionClicked: (MessagesItemAction) -> Unit,
onActionClicked: (MessagesItemAction, MessagesTimelineItemState.MessageEvent) -> Unit,
modifier: Modifier = Modifier
) {
if (actionsSheetState == null || actionsSheetState.actions.isEmpty()) {
@ -54,7 +96,7 @@ private fun SheetContent(
items(actionsSheetState.actions) {
ListItem(
modifier = Modifier.clickable {
onActionClicked(it)
onActionClicked(it, actionsSheetState.targetItem)
},
text = {
Text(