Read receipt: Bottom sheet
This commit is contained in:
committed by
Benoit Marty
parent
3e0971f189
commit
d5d002ce5d
@@ -42,6 +42,7 @@ import io.element.android.features.messages.impl.timeline.TimelinePresenter
|
||||
import io.element.android.features.messages.impl.timeline.TimelineState
|
||||
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionPresenter
|
||||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryPresenter
|
||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetPresenter
|
||||
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuPresenter
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
|
||||
@@ -97,6 +98,7 @@ class MessagesPresenter @AssistedInject constructor(
|
||||
private val customReactionPresenter: CustomReactionPresenter,
|
||||
private val reactionSummaryPresenter: ReactionSummaryPresenter,
|
||||
private val retrySendMenuPresenter: RetrySendMenuPresenter,
|
||||
private val readReceiptBottomSheetPresenter: ReadReceiptBottomSheetPresenter,
|
||||
private val networkMonitor: NetworkMonitor,
|
||||
private val snackbarDispatcher: SnackbarDispatcher,
|
||||
private val messageSummaryFormatter: MessageSummaryFormatter,
|
||||
@@ -124,6 +126,7 @@ class MessagesPresenter @AssistedInject constructor(
|
||||
val customReactionState = customReactionPresenter.present()
|
||||
val reactionSummaryState = reactionSummaryPresenter.present()
|
||||
val retryState = retrySendMenuPresenter.present()
|
||||
val readReceiptBottomSheetState = readReceiptBottomSheetPresenter.present()
|
||||
|
||||
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
||||
val userHasPermissionToSendMessage by room.canSendMessageAsState(type = MessageEventType.ROOM_MESSAGE, updateKey = syncUpdateFlow.value)
|
||||
@@ -201,6 +204,7 @@ class MessagesPresenter @AssistedInject constructor(
|
||||
customReactionState = customReactionState,
|
||||
reactionSummaryState = reactionSummaryState,
|
||||
retrySendMenuState = retryState,
|
||||
readReceiptBottomSheetState = readReceiptBottomSheetState,
|
||||
hasNetworkConnection = networkConnectionStatus == NetworkStatus.Online,
|
||||
snackbarMessage = snackbarMessage,
|
||||
showReinvitePrompt = showReinvitePrompt,
|
||||
|
||||
@@ -22,6 +22,7 @@ import io.element.android.features.messages.impl.messagecomposer.MessageComposer
|
||||
import io.element.android.features.messages.impl.timeline.TimelineState
|
||||
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionState
|
||||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState
|
||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState
|
||||
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuState
|
||||
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState
|
||||
import io.element.android.libraries.architecture.Async
|
||||
@@ -43,6 +44,7 @@ data class MessagesState(
|
||||
val customReactionState: CustomReactionState,
|
||||
val reactionSummaryState: ReactionSummaryState,
|
||||
val retrySendMenuState: RetrySendMenuState,
|
||||
val readReceiptBottomSheetState: ReadReceiptBottomSheetState,
|
||||
val hasNetworkConnection: Boolean,
|
||||
val snackbarMessage: SnackbarMessage?,
|
||||
val inviteProgress: Async<Unit>,
|
||||
|
||||
@@ -24,6 +24,7 @@ import io.element.android.features.messages.impl.timeline.aTimelineItemList
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineState
|
||||
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionState
|
||||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState
|
||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState
|
||||
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuState
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
|
||||
import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessageComposerState
|
||||
@@ -96,6 +97,10 @@ fun aMessagesState() = MessagesState(
|
||||
selectedEvent = null,
|
||||
eventSink = {},
|
||||
),
|
||||
readReceiptBottomSheetState = ReadReceiptBottomSheetState(
|
||||
selectedEvent = null,
|
||||
eventSink = {},
|
||||
),
|
||||
actionListState = anActionListState(),
|
||||
customReactionState = CustomReactionState(
|
||||
target = CustomReactionState.Target.None,
|
||||
|
||||
@@ -70,6 +70,8 @@ import io.element.android.features.messages.impl.timeline.components.customreact
|
||||
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvents
|
||||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvents
|
||||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryView
|
||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvents
|
||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetView
|
||||
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuEvents
|
||||
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMessageMenu
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
@@ -212,9 +214,8 @@ fun MessagesView(
|
||||
onReactionClicked = ::onEmojiReactionClicked,
|
||||
onReactionLongClicked = ::onEmojiReactionLongClicked,
|
||||
onMoreReactionsClicked = ::onMoreReactionsClicked,
|
||||
onReadReceiptClick = { // targetEvent ->
|
||||
// TODO Open bottom sheet with read receipts
|
||||
// state.eventSink(MessagesEvents.HandleAction(TimelineItemAction.ShowReadReceipts, targetEvent))
|
||||
onReadReceiptClick = { event ->
|
||||
state.readReceiptBottomSheetState.eventSink(ReadReceiptBottomSheetEvents.EventSelected(event))
|
||||
},
|
||||
onSendLocationClicked = onSendLocationClicked,
|
||||
onCreatePollClicked = onCreatePollClicked,
|
||||
@@ -250,13 +251,9 @@ fun MessagesView(
|
||||
)
|
||||
|
||||
ReactionSummaryView(state = state.reactionSummaryState)
|
||||
RetrySendMessageMenu(
|
||||
state = state.retrySendMenuState
|
||||
)
|
||||
|
||||
ReinviteDialog(
|
||||
state = state
|
||||
)
|
||||
RetrySendMessageMenu(state = state.retrySendMenuState)
|
||||
ReadReceiptBottomSheetView(state = state.readReceiptBottomSheetState)
|
||||
ReinviteDialog(state = state)
|
||||
|
||||
// Since the textfield is now based on an Android view, this is no longer done automatically.
|
||||
// We need to hide the keyboard automatically when navigating out of this screen.
|
||||
@@ -412,7 +409,8 @@ private fun MessagesViewComposerBottomSheetContents(
|
||||
if (state.userHasPermissionToSendMessage) {
|
||||
Column(modifier = modifier.fillMaxWidth()) {
|
||||
MentionSuggestionsPickerView(
|
||||
modifier = Modifier.heightIn(max = 230.dp)
|
||||
modifier = Modifier
|
||||
.heightIn(max = 230.dp)
|
||||
// Consume all scrolling, preventing the bottom sheet from being dragged when interacting with the list of suggestions
|
||||
.nestedScroll(object : NestedScrollConnection {
|
||||
override fun onPostScroll(consumed: Offset, available: Offset, source: NestedScrollSource): Offset {
|
||||
|
||||
@@ -68,8 +68,8 @@ private fun aReadReceiptData(
|
||||
id = "$index",
|
||||
size = AvatarSize.TimelineReadReceipt
|
||||
),
|
||||
timestamp: Long = 1629780000000L,
|
||||
formattedDate: String = "12:34",
|
||||
) = ReadReceiptData(
|
||||
avatarData = avatarData,
|
||||
timestamp = timestamp,
|
||||
formattedDate = formattedDate,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet
|
||||
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
|
||||
sealed interface ReadReceiptBottomSheetEvents {
|
||||
data class EventSelected(val event: TimelineItem.Event) : ReadReceiptBottomSheetEvents
|
||||
data object Dismiss : ReadReceiptBottomSheetEvents
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import javax.inject.Inject
|
||||
|
||||
class ReadReceiptBottomSheetPresenter @Inject constructor(
|
||||
) : Presenter<ReadReceiptBottomSheetState> {
|
||||
|
||||
@Composable
|
||||
override fun present(): ReadReceiptBottomSheetState {
|
||||
var selectedEvent: TimelineItem.Event? by remember { mutableStateOf(null) }
|
||||
|
||||
fun handleEvent(event: ReadReceiptBottomSheetEvents) {
|
||||
@Suppress("LiftReturnOrAssignment")
|
||||
when (event) {
|
||||
is ReadReceiptBottomSheetEvents.EventSelected -> {
|
||||
selectedEvent = event.event
|
||||
}
|
||||
ReadReceiptBottomSheetEvents.Dismiss -> {
|
||||
selectedEvent = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ReadReceiptBottomSheetState(
|
||||
selectedEvent = selectedEvent,
|
||||
eventSink = { handleEvent(it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
|
||||
@Immutable
|
||||
data class ReadReceiptBottomSheetState(
|
||||
val selectedEvent: TimelineItem.Event?,
|
||||
val eventSink: (ReadReceiptBottomSheetEvents) -> Unit,
|
||||
)
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
||||
import io.element.android.features.messages.impl.timeline.components.receipt.ReadReceiptViewStateProvider
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItemReadReceipts
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
class ReadReceiptBottomSheetStateProvider : PreviewParameterProvider<ReadReceiptBottomSheetState> {
|
||||
// Reuse the provider ReadReceiptViewStateProvider
|
||||
private val readReceiptViewStateProvider = ReadReceiptViewStateProvider()
|
||||
override val values: Sequence<ReadReceiptBottomSheetState> = readReceiptViewStateProvider.values
|
||||
.filter { it.sendState is LocalEventSendState.Sent }
|
||||
.map { readReceiptViewState ->
|
||||
ReadReceiptBottomSheetState(
|
||||
selectedEvent = aTimelineItemEvent(
|
||||
readReceiptState = TimelineItemReadReceipts.ReadReceipts(
|
||||
receipts = readReceiptViewState.receipts.map { readReceiptData ->
|
||||
readReceiptData
|
||||
.copy(avatarData = readReceiptData.avatarData.copy(id = "@${readReceiptData.avatarData.id}:localhost"))
|
||||
}.toImmutableList()
|
||||
)
|
||||
),
|
||||
eventSink = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.features.messages.impl.timeline.model.receipts
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.ui.components.MatrixUserRow
|
||||
import io.element.android.libraries.theme.ElementTheme
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
internal fun ReadReceiptBottomSheetView(
|
||||
state: ReadReceiptBottomSheetState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val isVisible = state.selectedEvent != null
|
||||
|
||||
val sheetState = rememberModalBottomSheetState()
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
if (isVisible) {
|
||||
ModalBottomSheet(
|
||||
modifier = modifier,
|
||||
// modifier = modifier.navigationBarsPadding() - FIXME after https://issuetracker.google.com/issues/275849044
|
||||
// .imePadding()
|
||||
sheetState = sheetState,
|
||||
onDismissRequest = {
|
||||
coroutineScope.launch {
|
||||
sheetState.hide()
|
||||
state.eventSink(ReadReceiptBottomSheetEvents.Dismiss)
|
||||
}
|
||||
}
|
||||
) {
|
||||
ReadReceiptBottomSheetContents(
|
||||
state = state,
|
||||
)
|
||||
// FIXME remove after https://issuetracker.google.com/issues/275849044
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.ReadReceiptBottomSheetContents(
|
||||
state: ReadReceiptBottomSheetState,
|
||||
) {
|
||||
val receipts = state.selectedEvent?.readReceiptState?.receipts().orEmpty()
|
||||
receipts.forEach {
|
||||
MatrixUserRow(
|
||||
matrixUser = MatrixUser(
|
||||
UserId(it.avatarData.id),
|
||||
it.avatarData.name,
|
||||
it.avatarData.url,
|
||||
),
|
||||
avatarSize = AvatarSize.ReadReceiptList,
|
||||
trailingContent = {
|
||||
Text(
|
||||
text = it.formattedDate,
|
||||
style = ElementTheme.typography.fontBodySmRegular,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun ReadReceiptBottomSheetViewPreview(@PreviewParameter(ReadReceiptBottomSheetStateProvider::class) state: ReadReceiptBottomSheetState) = ElementPreview {
|
||||
// TODO restore RetrySendMessageMenuBottomSheet once the issue with bottom sheet not being previewable is fixed
|
||||
Column {
|
||||
ReadReceiptBottomSheetContents(
|
||||
state = state
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItemGrou
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItemReactions
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItemReadReceipts
|
||||
import io.element.android.libraries.core.bool.orTrue
|
||||
import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
@@ -39,6 +40,7 @@ import javax.inject.Inject
|
||||
class TimelineItemEventFactory @Inject constructor(
|
||||
private val contentFactory: TimelineItemContentFactory,
|
||||
private val matrixClient: MatrixClient,
|
||||
private val lastMessageTimestampFormatter: LastMessageTimestampFormatter,
|
||||
) {
|
||||
|
||||
suspend fun create(
|
||||
@@ -140,11 +142,11 @@ class TimelineItemEventFactory @Inject constructor(
|
||||
ReadReceiptData(
|
||||
avatarData = AvatarData(
|
||||
id = receipt.userId.value,
|
||||
name = roomMember?.displayName ?: receipt.userId.value,
|
||||
name = roomMember?.displayName,
|
||||
url = roomMember?.avatarUrl,
|
||||
size = AvatarSize.TimelineReadReceipt,
|
||||
),
|
||||
timestamp = receipt.timestamp
|
||||
formattedDate = lastMessageTimestampFormatter.format(receipt.timestamp)
|
||||
)
|
||||
}
|
||||
.toImmutableList()
|
||||
|
||||
@@ -31,7 +31,7 @@ sealed interface TimelineItemReadReceipts {
|
||||
|
||||
data class ReadReceiptData(
|
||||
val avatarData: AvatarData,
|
||||
val timestamp: Long
|
||||
val formattedDate: String,
|
||||
)
|
||||
|
||||
fun TimelineItemReadReceipts.receipts(): ImmutableList<ReadReceiptData> = when (this) {
|
||||
|
||||
@@ -35,6 +35,7 @@ import io.element.android.features.messages.impl.timeline.groups.TimelineItemGro
|
||||
import io.element.android.features.messages.impl.timeline.util.FileExtensionExtractorWithoutValidation
|
||||
import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter
|
||||
import io.element.android.libraries.dateformatter.test.FakeDaySeparatorFormatter
|
||||
import io.element.android.libraries.dateformatter.test.FakeLastMessageTimestampFormatter
|
||||
import io.element.android.libraries.eventformatter.api.TimelineEventFormatter
|
||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||
@@ -65,6 +66,7 @@ internal fun TestScope.aTimelineItemsFactory(): TimelineItemsFactory {
|
||||
failedToParseStateFactory = TimelineItemContentFailedToParseStateFactory(),
|
||||
),
|
||||
matrixClient = matrixClient,
|
||||
lastMessageTimestampFormatter = FakeLastMessageTimestampFormatter(),
|
||||
),
|
||||
virtualItemFactory = TimelineItemVirtualFactory(
|
||||
daySeparatorFactory = TimelineItemDaySeparatorFactory(
|
||||
|
||||
@@ -39,6 +39,8 @@ enum class AvatarSize(val dp: Dp) {
|
||||
TimelineSender(32.dp),
|
||||
TimelineReadReceipt(16.dp),
|
||||
|
||||
ReadReceiptList(32.dp),
|
||||
|
||||
MessageActionSender(32.dp),
|
||||
|
||||
RoomInviteItem(52.dp),
|
||||
|
||||
@@ -31,11 +31,13 @@ fun MatrixUserRow(
|
||||
matrixUser: MatrixUser,
|
||||
modifier: Modifier = Modifier,
|
||||
avatarSize: AvatarSize = AvatarSize.UserListItem,
|
||||
trailingContent: @Composable (() -> Unit)? = null,
|
||||
) = UserRow(
|
||||
avatarData = matrixUser.getAvatarData(avatarSize),
|
||||
name = matrixUser.getBestName(),
|
||||
subtext = if (matrixUser.displayName.isNullOrEmpty()) null else matrixUser.userId.value,
|
||||
modifier = modifier,
|
||||
trailingContent,
|
||||
)
|
||||
|
||||
@PreviewsDayNight
|
||||
|
||||
@@ -38,6 +38,7 @@ internal fun UserRow(
|
||||
name: String,
|
||||
subtext: String?,
|
||||
modifier: Modifier = Modifier,
|
||||
trailingContent: @Composable (() -> Unit)? = null,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
@@ -49,7 +50,8 @@ internal fun UserRow(
|
||||
Avatar(avatarData)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(start = 12.dp),
|
||||
.padding(start = 12.dp)
|
||||
.weight(1f),
|
||||
) {
|
||||
// Name
|
||||
Text(
|
||||
@@ -70,5 +72,6 @@ internal fun UserRow(
|
||||
)
|
||||
}
|
||||
}
|
||||
trailingContent?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user