Hide add more reaction button if user do not have permission to send reaction #2093
Also: - move `userHasPermissionToSendMessage` to `TimelineRoomInfo` - remove `canReply` parameter which can be computed from other params.
This commit is contained in:
@@ -98,6 +98,7 @@ class TimelinePresenter @AssistedInject constructor(
|
||||
val paginationState by timeline.paginationState.collectAsState()
|
||||
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
||||
val userHasPermissionToSendMessage by room.canSendMessageAsState(type = MessageEventType.ROOM_MESSAGE, updateKey = syncUpdateFlow.value)
|
||||
val userHasPermissionToSendReaction by room.canSendMessageAsState(type = MessageEventType.REACTION_SENT, updateKey = syncUpdateFlow.value)
|
||||
|
||||
val prevMostRecentItemId = rememberSaveable { mutableStateOf<String?>(null) }
|
||||
val newItemState = remember { mutableStateOf(NewEventState.None) }
|
||||
@@ -175,12 +176,18 @@ class TimelinePresenter @AssistedInject constructor(
|
||||
.launchIn(this)
|
||||
}
|
||||
|
||||
val timelineRoomInfo by remember {
|
||||
derivedStateOf {
|
||||
TimelineRoomInfo(
|
||||
isDirect = room.isDirect,
|
||||
userHasPermissionToSendMessage = userHasPermissionToSendMessage,
|
||||
userHasPermissionToSendReaction = userHasPermissionToSendReaction,
|
||||
)
|
||||
}
|
||||
}
|
||||
return TimelineState(
|
||||
timelineRoomInfo = TimelineRoomInfo(
|
||||
isDirect = room.isDirect
|
||||
),
|
||||
timelineRoomInfo = timelineRoomInfo,
|
||||
highlightedEventId = highlightedEventId.value,
|
||||
userHasPermissionToSendMessage = userHasPermissionToSendMessage,
|
||||
paginationState = paginationState,
|
||||
timelineItems = timelineItems,
|
||||
showReadReceipts = readReceiptsEnabled,
|
||||
|
||||
@@ -30,7 +30,6 @@ data class TimelineState(
|
||||
val timelineRoomInfo: TimelineRoomInfo,
|
||||
val showReadReceipts: Boolean,
|
||||
val highlightedEventId: EventId?,
|
||||
val userHasPermissionToSendMessage: Boolean,
|
||||
val paginationState: MatrixTimeline.PaginationState,
|
||||
val newEventState: NewEventState,
|
||||
val sessionState: SessionState,
|
||||
@@ -40,4 +39,6 @@ data class TimelineState(
|
||||
@Immutable
|
||||
data class TimelineRoomInfo(
|
||||
val isDirect: Boolean,
|
||||
val userHasPermissionToSendMessage: Boolean,
|
||||
val userHasPermissionToSendReaction: Boolean,
|
||||
)
|
||||
|
||||
@@ -55,7 +55,6 @@ fun aTimelineState(timelineItems: ImmutableList<TimelineItem> = persistentListOf
|
||||
beginningOfRoomReached = false,
|
||||
),
|
||||
highlightedEventId = null,
|
||||
userHasPermissionToSendMessage = true,
|
||||
newEventState = NewEventState.None,
|
||||
sessionState = aSessionState(
|
||||
isSessionVerified = true,
|
||||
@@ -218,4 +217,6 @@ internal fun aTimelineRoomInfo(
|
||||
isDirect: Boolean = false,
|
||||
) = TimelineRoomInfo(
|
||||
isDirect = isDirect,
|
||||
userHasPermissionToSendMessage = true,
|
||||
userHasPermissionToSendReaction = true,
|
||||
)
|
||||
|
||||
@@ -123,7 +123,6 @@ fun TimelineView(
|
||||
isLastOutgoingMessage = (timelineItem as? TimelineItem.Event)?.isMine == true
|
||||
&& state.timelineItems.first().identifier() == timelineItem.identifier(),
|
||||
highlightedItem = state.highlightedEventId?.value,
|
||||
userHasPermissionToSendMessage = state.userHasPermissionToSendMessage,
|
||||
onClick = onMessageClicked,
|
||||
onLongClick = onMessageLongClicked,
|
||||
onUserDataClick = onUserDataClicked,
|
||||
|
||||
@@ -35,7 +35,6 @@ internal fun ATimelineItemEventRow(
|
||||
showReadReceipts = showReadReceipts,
|
||||
isLastOutgoingMessage = isLastOutgoingMessage,
|
||||
isHighlighted = isHighlighted,
|
||||
canReply = true,
|
||||
onClick = {},
|
||||
onLongClick = {},
|
||||
onUserDataClick = {},
|
||||
|
||||
@@ -81,6 +81,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.canBeRepliedTo
|
||||
import io.element.android.features.messages.impl.timeline.model.metadata
|
||||
import io.element.android.libraries.androidutils.system.openUrlInExternalApp
|
||||
import io.element.android.libraries.designsystem.colors.AvatarColorsProvider
|
||||
@@ -112,7 +113,6 @@ fun TimelineItemEventRow(
|
||||
showReadReceipts: Boolean,
|
||||
isLastOutgoingMessage: Boolean,
|
||||
isHighlighted: Boolean,
|
||||
canReply: Boolean,
|
||||
onClick: () -> Unit,
|
||||
onLongClick: () -> Unit,
|
||||
onUserDataClick: (UserId) -> Unit,
|
||||
@@ -151,6 +151,7 @@ fun TimelineItemEventRow(
|
||||
} else {
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
}
|
||||
val canReply = timelineRoomInfo.userHasPermissionToSendMessage && event.content.canBeRepliedTo()
|
||||
if (canReply) {
|
||||
val state: SwipeableActionsState = rememberSwipeableActionsState()
|
||||
val offset = state.offset.floatValue
|
||||
@@ -335,6 +336,7 @@ private fun TimelineItemEventRowContent(
|
||||
if (event.reactionsState.reactions.isNotEmpty()) {
|
||||
TimelineItemReactionsView(
|
||||
reactionsState = event.reactionsState,
|
||||
userCanSendReaction = timelineRoomInfo.userHasPermissionToSendReaction,
|
||||
isOutgoing = event.isMine,
|
||||
onReactionClicked = onReactionClicked,
|
||||
onReactionLongClicked = onReactionLongClicked,
|
||||
|
||||
@@ -131,7 +131,6 @@ private fun TimelineItemGroupedEventsRowContent(
|
||||
isLastOutgoingMessage = isLastOutgoingMessage,
|
||||
highlightedItem = highlightedItem,
|
||||
sessionState = sessionState,
|
||||
userHasPermissionToSendMessage = false,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
inReplyToClick = inReplyToClick,
|
||||
|
||||
@@ -26,8 +26,8 @@ import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.features.messages.impl.R
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.utils.CommonDrawables
|
||||
|
||||
/**
|
||||
@@ -46,7 +46,7 @@ import io.element.android.libraries.designsystem.utils.CommonDrawables
|
||||
@Composable
|
||||
fun TimelineItemReactionsLayout(
|
||||
expandButton: @Composable () -> Unit,
|
||||
addMoreButton: @Composable () -> Unit,
|
||||
addMoreButton: (@Composable () -> Unit)?,
|
||||
modifier: Modifier = Modifier,
|
||||
itemSpacing: Dp = 0.dp,
|
||||
rowSpacing: Dp = 0.dp,
|
||||
@@ -82,21 +82,21 @@ fun TimelineItemReactionsLayout(
|
||||
|
||||
// Used to render the collapsed state, this takes the rows inputted and adds the extra button to the last row,
|
||||
// removing only as many trailing reactions as needed to make space for it.
|
||||
fun replaceTrailingItemsWithButtons(rowsIn: List<List<Placeable>>, expandButton: Placeable, addMoreButton: Placeable): List<List<Placeable>> {
|
||||
fun replaceTrailingItemsWithButtons(rowsIn: List<List<Placeable>>, expandButton: Placeable, addMoreButton: Placeable?): List<List<Placeable>> {
|
||||
val rows = rowsIn.toMutableList()
|
||||
val lastRow = rows.last()
|
||||
val buttonsWidth = expandButton.width + itemSpacing.toPx().toInt() + addMoreButton.width
|
||||
val buttonsWidth = expandButton.width + itemSpacing.toPx().toInt() + (addMoreButton?.width ?: 0)
|
||||
var rowX = 0
|
||||
lastRow.forEachIndexed { i, placeable ->
|
||||
val horizontalSpacing = if (i == 0) 0 else itemSpacing.toPx().toInt()
|
||||
rowX += placeable.width + horizontalSpacing
|
||||
if (rowX > constraints.maxWidth - (buttonsWidth + horizontalSpacing)) {
|
||||
val lastRowWithButton = lastRow.take(i) + listOf(expandButton, addMoreButton)
|
||||
val lastRowWithButton = lastRow.take(i) + listOfNotNull(expandButton, addMoreButton)
|
||||
rows[rows.size - 1] = lastRowWithButton
|
||||
return rows
|
||||
}
|
||||
}
|
||||
val lastRowWithButton = lastRow + listOf(expandButton, addMoreButton)
|
||||
val lastRowWithButton = lastRow + listOfNotNull(expandButton, addMoreButton)
|
||||
rows[rows.size - 1] = lastRowWithButton
|
||||
return rows
|
||||
}
|
||||
@@ -155,16 +155,15 @@ fun TimelineItemReactionsLayout(
|
||||
val newConstrains = constraints.copy(minHeight = maxHeight)
|
||||
reactionsPlaceables = subcompose(2, reactions).map { it.measure(newConstrains) }
|
||||
expandPlaceable = subcompose(3, expandButton).first().measure(newConstrains)
|
||||
val addMorePlaceable = subcompose(4, addMoreButton).first().measure(newConstrains)
|
||||
|
||||
val addMorePlaceable = addMoreButton?.let { subcompose(4, addMoreButton).first().measure(newConstrains) }
|
||||
|
||||
// Calculate the layout of the rows with the reactions button and add more button
|
||||
val reactionsAndAddMore = calculateRows(reactionsPlaceables + listOf(addMorePlaceable))
|
||||
val reactionsAndAddMore = calculateRows(reactionsPlaceables + listOfNotNull(addMorePlaceable))
|
||||
// If we have extended beyond the defined number of rows we are showing the expand/collapse ui
|
||||
if (rowsBeforeCollapsible?.let { reactionsAndAddMore.size > it } == true) {
|
||||
if (expanded) {
|
||||
// Show all subviews with the add more button at the end
|
||||
var reactionsAndButtons = calculateRows(reactionsPlaceables + listOf(expandPlaceable, addMorePlaceable))
|
||||
var reactionsAndButtons = calculateRows(reactionsPlaceables + listOfNotNull(expandPlaceable, addMorePlaceable))
|
||||
reactionsAndButtons = ensureCollapseAndAddMoreButtonsAreOnTheSameRow(reactionsAndButtons)
|
||||
layoutRows(reactionsAndButtons)
|
||||
} else {
|
||||
|
||||
@@ -31,8 +31,8 @@ import io.element.android.features.messages.impl.R
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineItemReactions
|
||||
import io.element.android.features.messages.impl.timeline.model.AggregatedReaction
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItemReactions
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.utils.CommonDrawables
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@@ -40,6 +40,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
fun TimelineItemReactionsView(
|
||||
reactionsState: TimelineItemReactions,
|
||||
isOutgoing: Boolean,
|
||||
userCanSendReaction: Boolean,
|
||||
onReactionClicked: (emoji: String) -> Unit,
|
||||
onReactionLongClicked: (emoji: String) -> Unit,
|
||||
onMoreReactionsClicked: () -> Unit,
|
||||
@@ -49,6 +50,7 @@ fun TimelineItemReactionsView(
|
||||
TimelineItemReactionsView(
|
||||
modifier = modifier,
|
||||
reactions = reactionsState.reactions,
|
||||
userCanSendReaction = userCanSendReaction,
|
||||
expanded = expanded,
|
||||
isOutgoing = isOutgoing,
|
||||
onReactionClick = onReactionClicked,
|
||||
@@ -61,6 +63,7 @@ fun TimelineItemReactionsView(
|
||||
@Composable
|
||||
private fun TimelineItemReactionsView(
|
||||
reactions: ImmutableList<AggregatedReaction>,
|
||||
userCanSendReaction: Boolean,
|
||||
isOutgoing: Boolean,
|
||||
expanded: Boolean,
|
||||
onReactionClick: (emoji: String) -> Unit,
|
||||
@@ -93,19 +96,26 @@ private fun TimelineItemReactionsView(
|
||||
onLongClick = {}
|
||||
)
|
||||
},
|
||||
addMoreButton = {
|
||||
MessagesReactionButton(
|
||||
content = MessagesReactionsButtonContent.Icon(CommonDrawables.ic_add_reaction),
|
||||
onClick = onMoreReactionsClick,
|
||||
onLongClick = {}
|
||||
)
|
||||
},
|
||||
addMoreButton = if (userCanSendReaction) {
|
||||
{
|
||||
MessagesReactionButton(
|
||||
content = MessagesReactionsButtonContent.Icon(CommonDrawables.ic_add_reaction),
|
||||
onClick = onMoreReactionsClick,
|
||||
onLongClick = {}
|
||||
)
|
||||
}
|
||||
} else null,
|
||||
reactions = {
|
||||
reactions.forEach { reaction ->
|
||||
CompositionLocalProvider(LocalLayoutDirection provides currentLayout) {
|
||||
MessagesReactionButton(
|
||||
content = MessagesReactionsButtonContent.Reaction(reaction = reaction),
|
||||
onClick = { onReactionClick(reaction.key) },
|
||||
onClick = {
|
||||
// Always allow user to redact their own reactions
|
||||
if (reaction.isHighlighted || userCanSendReaction) {
|
||||
onReactionClick(reaction.key)
|
||||
}
|
||||
},
|
||||
onLongClick = { onReactionLongClick(reaction.key) }
|
||||
)
|
||||
}
|
||||
@@ -157,6 +167,7 @@ private fun ContentToPreview(
|
||||
reactionsState = TimelineItemReactions(
|
||||
reactions
|
||||
),
|
||||
userCanSendReaction = true,
|
||||
isOutgoing = isOutgoing,
|
||||
onReactionClicked = {},
|
||||
onReactionLongClicked = {},
|
||||
|
||||
@@ -18,11 +18,10 @@ package io.element.android.features.messages.impl.timeline.components
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import io.element.android.features.messages.impl.timeline.TimelineRoomInfo
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.TimelineRoomInfo
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.canBeRepliedTo
|
||||
import io.element.android.features.messages.impl.timeline.session.SessionState
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
@@ -34,7 +33,6 @@ internal fun TimelineItemRow(
|
||||
showReadReceipts: Boolean,
|
||||
isLastOutgoingMessage: Boolean,
|
||||
highlightedItem: String?,
|
||||
userHasPermissionToSendMessage: Boolean,
|
||||
sessionState: SessionState,
|
||||
onUserDataClick: (UserId) -> Unit,
|
||||
onClick: (TimelineItem.Event) -> Unit,
|
||||
@@ -77,7 +75,6 @@ internal fun TimelineItemRow(
|
||||
showReadReceipts = showReadReceipts,
|
||||
isLastOutgoingMessage = isLastOutgoingMessage,
|
||||
isHighlighted = highlightedItem == timelineItem.identifier(),
|
||||
canReply = userHasPermissionToSendMessage && timelineItem.content.canBeRepliedTo(),
|
||||
onClick = { onClick(timelineItem) },
|
||||
onLongClick = { onLongClick(timelineItem) },
|
||||
onUserDataClick = onUserDataClick,
|
||||
|
||||
Reference in New Issue
Block a user