UX cleanup: room details (#2816)
* UX cleanup: room details screen Add new CTA buttons for Invite and Call actions * Update screenshots * Fix maestro --------- Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
committed by
GitHub
parent
0f60299de0
commit
b524645b89
@@ -16,7 +16,7 @@ appId: ${MAESTRO_APP_ID}
|
||||
- tapOn: "Create"
|
||||
- takeScreenshot: build/maestro/320-createAndDeleteRoom
|
||||
- tapOn: "aRoomName"
|
||||
- tapOn: "Invite people"
|
||||
- tapOn: "Invite"
|
||||
# assert there's 1 member and 1 invitee
|
||||
- tapOn: "Search for someone"
|
||||
- inputText: ${MAESTRO_INVITEE2_MXID}
|
||||
|
||||
1
changelog.d/2814.misc
Normal file
1
changelog.d/2814.misc
Normal file
@@ -0,0 +1 @@
|
||||
UX cleanup: room details screen, add new CTA buttons for Invite and Call actions.
|
||||
@@ -86,6 +86,7 @@ import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
||||
import io.element.android.libraries.matrix.api.room.MessageEventType
|
||||
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
|
||||
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
|
||||
import io.element.android.libraries.matrix.ui.room.canCall
|
||||
import io.element.android.libraries.matrix.ui.room.canRedactOtherAsState
|
||||
import io.element.android.libraries.matrix.ui.room.canRedactOwnAsState
|
||||
import io.element.android.libraries.matrix.ui.room.canSendMessageAsState
|
||||
@@ -158,9 +159,7 @@ class MessagesPresenter @AssistedInject constructor(
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
var canJoinCall by rememberSaveable {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
val canJoinCall by room.canCall(updateKey = syncUpdateFlow.value)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
// Remove the unread flag on entering but don't send read receipts
|
||||
@@ -170,12 +169,6 @@ class MessagesPresenter @AssistedInject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(syncUpdateFlow.value) {
|
||||
withContext(dispatchers.io) {
|
||||
canJoinCall = room.canUserJoinCall(room.sessionId).getOrDefault(false)
|
||||
}
|
||||
}
|
||||
|
||||
val inviteProgress = remember { mutableStateOf<AsyncData<Unit>>(AsyncData.Uninitialized) }
|
||||
var showReinvitePrompt by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(hasDismissedInviteDialog, composerState.hasFocus, syncUpdateFlow.value) {
|
||||
|
||||
@@ -288,7 +288,7 @@ class MessagesPresenterTest {
|
||||
initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Reply, aMessageEvent()))
|
||||
val finalState = awaitItem()
|
||||
assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Reply::class.java)
|
||||
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
|
||||
assertThat(finalState.actionListState.target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,10 +298,9 @@ class MessagesPresenterTest {
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(2)
|
||||
val initialState = awaitItem()
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Reply, aMessageEvent(eventId = null)))
|
||||
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
|
||||
assertThat(initialState.actionListState.target).isEqualTo(ActionListState.Target.None)
|
||||
// Otherwise we would have some extra items here
|
||||
ensureAllEventsConsumed()
|
||||
}
|
||||
@@ -335,7 +334,7 @@ class MessagesPresenterTest {
|
||||
assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Reply::class.java)
|
||||
val replyMode = finalState.composerState.mode as MessageComposerMode.Reply
|
||||
assertThat(replyMode.attachmentThumbnailInfo).isNotNull()
|
||||
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
|
||||
assertThat(finalState.actionListState.target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,7 +367,7 @@ class MessagesPresenterTest {
|
||||
assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Reply::class.java)
|
||||
val replyMode = finalState.composerState.mode as MessageComposerMode.Reply
|
||||
assertThat(replyMode.attachmentThumbnailInfo).isNotNull()
|
||||
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
|
||||
assertThat(finalState.actionListState.target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,7 +393,7 @@ class MessagesPresenterTest {
|
||||
assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Reply::class.java)
|
||||
val replyMode = finalState.composerState.mode as MessageComposerMode.Reply
|
||||
assertThat(replyMode.attachmentThumbnailInfo).isNotNull()
|
||||
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
|
||||
assertThat(finalState.actionListState.target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,7 +407,7 @@ class MessagesPresenterTest {
|
||||
initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Edit, aMessageEvent()))
|
||||
val finalState = awaitItem()
|
||||
assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Edit::class.java)
|
||||
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
|
||||
assertThat(finalState.actionListState.target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -732,7 +731,7 @@ class MessagesPresenterTest {
|
||||
assertThat(replyMode.attachmentThumbnailInfo).isNotNull()
|
||||
assertThat(replyMode.attachmentThumbnailInfo?.textContent)
|
||||
.isEqualTo("What type of food should we have at the party?")
|
||||
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
|
||||
assertThat(finalState.actionListState.target).isEqualTo(ActionListState.Target.None)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,8 +56,9 @@ dependencies {
|
||||
api(projects.libraries.usersearch.api)
|
||||
api(projects.services.apperror.api)
|
||||
implementation(libs.coil.compose)
|
||||
implementation(projects.features.leaveroom.api)
|
||||
implementation(projects.features.call)
|
||||
implementation(projects.features.createroom.api)
|
||||
implementation(projects.features.leaveroom.api)
|
||||
implementation(projects.features.userprofile.shared)
|
||||
implementation(projects.services.analytics.api)
|
||||
implementation(projects.features.poll.api)
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package io.element.android.features.roomdetails.impl
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -28,6 +29,8 @@ import com.bumble.appyx.navmodel.backstack.operation.push
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.anvilannotations.ContributesNode
|
||||
import io.element.android.features.call.CallType
|
||||
import io.element.android.features.call.ui.ElementCallActivity
|
||||
import io.element.android.features.poll.api.history.PollHistoryEntryPoint
|
||||
import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint
|
||||
import io.element.android.features.roomdetails.impl.edit.RoomDetailsEditNode
|
||||
@@ -42,10 +45,12 @@ import io.element.android.libraries.architecture.BackstackView
|
||||
import io.element.android.libraries.architecture.BaseFlowNode
|
||||
import io.element.android.libraries.architecture.createNode
|
||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||
import io.element.android.libraries.di.ApplicationContext
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.mediaviewer.api.local.MediaInfo
|
||||
import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode
|
||||
import kotlinx.parcelize.Parcelize
|
||||
@@ -54,7 +59,9 @@ import kotlinx.parcelize.Parcelize
|
||||
class RoomDetailsFlowNode @AssistedInject constructor(
|
||||
@Assisted buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
@ApplicationContext private val context: Context,
|
||||
private val pollHistoryEntryPoint: PollHistoryEntryPoint,
|
||||
private val room: MatrixRoom,
|
||||
) : BaseFlowNode<RoomDetailsFlowNode.NavTarget>(
|
||||
backstack = BackStack(
|
||||
initialElement = plugins.filterIsInstance<RoomDetailsEntryPoint.Params>().first().initialElement.toNavTarget(),
|
||||
@@ -129,6 +136,14 @@ class RoomDetailsFlowNode @AssistedInject constructor(
|
||||
override fun openAdminSettings() {
|
||||
backstack.push(NavTarget.AdminSettings)
|
||||
}
|
||||
|
||||
override fun onJoinCall() {
|
||||
val inputs = CallType.RoomCall(
|
||||
sessionId = room.sessionId,
|
||||
roomId = room.roomId,
|
||||
)
|
||||
ElementCallActivity.start(context, inputs)
|
||||
}
|
||||
}
|
||||
createNode<RoomDetailsNode>(buildContext, listOf(roomDetailsCallback))
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ class RoomDetailsNode @AssistedInject constructor(
|
||||
fun openAvatarPreview(name: String, url: String)
|
||||
fun openPollHistory()
|
||||
fun openAdminSettings()
|
||||
fun onJoinCall()
|
||||
}
|
||||
|
||||
private val callbacks = plugins<Callback>()
|
||||
@@ -86,6 +87,10 @@ class RoomDetailsNode @AssistedInject constructor(
|
||||
callbacks.forEach { it.openPollHistory() }
|
||||
}
|
||||
|
||||
private fun onJoinCall() {
|
||||
callbacks.forEach { it.onJoinCall() }
|
||||
}
|
||||
|
||||
private fun CoroutineScope.onShareRoom(context: Context) = launch {
|
||||
room.getPermalink()
|
||||
.onSuccess { permalink ->
|
||||
@@ -162,6 +167,7 @@ class RoomDetailsNode @AssistedInject constructor(
|
||||
openAvatarPreview = ::openAvatarPreview,
|
||||
openPollHistory = ::openPollHistory,
|
||||
openAdminSettings = this::openAdminSettings,
|
||||
onJoinCallClicked = ::onJoinCall,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ import io.element.android.libraries.matrix.api.room.StateEventType
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.canInvite
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.canSendState
|
||||
import io.element.android.libraries.matrix.api.room.roomNotificationSettings
|
||||
import io.element.android.libraries.matrix.ui.room.canCall
|
||||
import io.element.android.libraries.matrix.ui.room.getDirectRoomMember
|
||||
import io.element.android.libraries.matrix.ui.room.isOwnUserAdmin
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
@@ -86,11 +87,14 @@ class RoomDetailsPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
val syncUpdateTimestamp by room.syncUpdateFlow.collectAsState()
|
||||
|
||||
val membersState by room.membersStateFlow.collectAsState()
|
||||
val canInvite by getCanInvite(membersState)
|
||||
val canEditName by getCanSendState(membersState, StateEventType.ROOM_NAME)
|
||||
val canEditAvatar by getCanSendState(membersState, StateEventType.ROOM_AVATAR)
|
||||
val canEditTopic by getCanSendState(membersState, StateEventType.ROOM_TOPIC)
|
||||
val canJoinCall by room.canCall(updateKey = syncUpdateTimestamp)
|
||||
val dmMember by room.getDirectRoomMember(membersState)
|
||||
val roomMemberDetailsPresenter = roomMemberDetailsPresenter(dmMember)
|
||||
val roomType by getRoomType(dmMember)
|
||||
@@ -138,6 +142,7 @@ class RoomDetailsPresenter @Inject constructor(
|
||||
canInvite = canInvite,
|
||||
canEdit = (canEditAvatar || canEditName || canEditTopic) && roomType == RoomDetailsType.Room,
|
||||
canShowNotificationSettings = canShowNotificationSettings.value,
|
||||
canCall = canJoinCall,
|
||||
roomType = roomType,
|
||||
roomMemberDetailsState = roomMemberDetailsState,
|
||||
leaveRoomState = leaveRoomState,
|
||||
|
||||
@@ -36,6 +36,7 @@ data class RoomDetailsState(
|
||||
val canEdit: Boolean,
|
||||
val canInvite: Boolean,
|
||||
val canShowNotificationSettings: Boolean,
|
||||
val canCall: Boolean,
|
||||
val leaveRoomState: LeaveRoomState,
|
||||
val roomNotificationSettings: RoomNotificationSettings?,
|
||||
val isFavorite: Boolean,
|
||||
|
||||
@@ -46,6 +46,7 @@ open class RoomDetailsStateProvider : PreviewParameterProvider<RoomDetailsState>
|
||||
// Also test the roomNotificationSettings ALL_MESSAGES in the same screenshot. Icon 'Mute' should be displayed
|
||||
roomNotificationSettings = aRoomNotificationSettings(mode = RoomNotificationMode.ALL_MESSAGES, isDefault = true)
|
||||
),
|
||||
aRoomDetailsState(canCall = false, canInvite = false),
|
||||
// Add other state here
|
||||
)
|
||||
}
|
||||
@@ -89,6 +90,7 @@ fun aRoomDetailsState(
|
||||
canInvite: Boolean = false,
|
||||
canEdit: Boolean = false,
|
||||
canShowNotificationSettings: Boolean = true,
|
||||
canCall: Boolean = true,
|
||||
roomType: RoomDetailsType = RoomDetailsType.Room,
|
||||
roomMemberDetailsState: UserProfileState? = null,
|
||||
leaveRoomState: LeaveRoomState = aLeaveRoomState(),
|
||||
@@ -107,6 +109,7 @@ fun aRoomDetailsState(
|
||||
canInvite = canInvite,
|
||||
canEdit = canEdit,
|
||||
canShowNotificationSettings = canShowNotificationSettings,
|
||||
canCall = canCall,
|
||||
roomType = roomType,
|
||||
roomMemberDetailsState = roomMemberDetailsState,
|
||||
leaveRoomState = leaveRoomState,
|
||||
|
||||
@@ -27,7 +27,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
@@ -100,6 +99,7 @@ fun RoomDetailsView(
|
||||
openAvatarPreview: (name: String, url: String) -> Unit,
|
||||
openPollHistory: () -> Unit,
|
||||
openAdminSettings: () -> Unit,
|
||||
onJoinCallClicked: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
fun onShareMember() {
|
||||
@@ -137,7 +137,9 @@ fun RoomDetailsView(
|
||||
)
|
||||
MainActionsSection(
|
||||
state = state,
|
||||
onShareRoom = onShareRoom
|
||||
onShareRoom = onShareRoom,
|
||||
onInvitePeople = invitePeople,
|
||||
onCall = onJoinCallClicked,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -188,20 +190,12 @@ fun RoomDetailsView(
|
||||
}
|
||||
|
||||
val displayMemberListItem = state.roomType is RoomDetailsType.Room
|
||||
val displayInviteMembersItem = state.canInvite
|
||||
if (displayMemberListItem || displayInviteMembersItem) {
|
||||
if (displayMemberListItem) {
|
||||
PreferenceCategory {
|
||||
if (displayMemberListItem) {
|
||||
MembersItem(
|
||||
memberCount = state.memberCount,
|
||||
openRoomMemberList = openRoomMemberList,
|
||||
)
|
||||
}
|
||||
if (displayInviteMembersItem) {
|
||||
InviteItem(
|
||||
invitePeople = invitePeople
|
||||
)
|
||||
}
|
||||
MembersItem(
|
||||
memberCount = state.memberCount,
|
||||
openRoomMemberList = openRoomMemberList,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,10 +261,12 @@ private fun RoomDetailsTopBar(
|
||||
private fun MainActionsSection(
|
||||
state: RoomDetailsState,
|
||||
onShareRoom: () -> Unit,
|
||||
onInvitePeople: () -> Unit,
|
||||
onCall: () -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
) {
|
||||
val roomNotificationSettings = state.roomNotificationSettings
|
||||
if (state.canShowNotificationSettings && roomNotificationSettings != null) {
|
||||
@@ -292,9 +288,22 @@ private fun MainActionsSection(
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.width(20.dp))
|
||||
if (state.canCall) {
|
||||
MainActionButton(
|
||||
title = stringResource(CommonStrings.action_call),
|
||||
imageVector = CompoundIcons.VideoCall(),
|
||||
onClick = onCall,
|
||||
)
|
||||
}
|
||||
if (state.roomType is RoomDetailsType.Room && state.canInvite) {
|
||||
MainActionButton(
|
||||
title = stringResource(CommonStrings.action_invite),
|
||||
imageVector = CompoundIcons.UserAdd(),
|
||||
onClick = onInvitePeople,
|
||||
)
|
||||
}
|
||||
MainActionButton(
|
||||
title = stringResource(R.string.screen_room_details_share_room_title),
|
||||
title = stringResource(CommonStrings.action_share),
|
||||
imageVector = CompoundIcons.ShareAndroid(),
|
||||
onClick = onShareRoom
|
||||
)
|
||||
@@ -410,17 +419,6 @@ private fun MembersItem(
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InviteItem(
|
||||
invitePeople: () -> Unit,
|
||||
) {
|
||||
ListItem(
|
||||
headlineContent = { Text(stringResource(R.string.screen_room_details_invite_people_title)) },
|
||||
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.UserAdd())),
|
||||
onClick = invitePeople,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PollsSection(
|
||||
openPollHistory: () -> Unit,
|
||||
@@ -491,5 +489,6 @@ private fun ContentToPreview(state: RoomDetailsState) {
|
||||
openAvatarPreview = { _, _ -> },
|
||||
openPollHistory = {},
|
||||
openAdminSettings = {},
|
||||
onJoinCallClicked = {},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ class RoomDetailsViewTest {
|
||||
rule.setRoomDetailView(
|
||||
onShareRoom = callback,
|
||||
)
|
||||
rule.clickOn(R.string.screen_room_details_share_room_title)
|
||||
rule.clickOn(CommonStrings.action_share)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,9 +112,8 @@ class RoomDetailsViewTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `click on invite people invokes expected callback`() {
|
||||
fun `click on invite invokes expected callback`() {
|
||||
ensureCalledOnce { callback ->
|
||||
rule.setRoomDetailView(
|
||||
state = aRoomDetailsState(
|
||||
@@ -123,7 +122,21 @@ class RoomDetailsViewTest {
|
||||
),
|
||||
invitePeople = callback,
|
||||
)
|
||||
rule.clickOn(R.string.screen_room_details_invite_people_title)
|
||||
rule.clickOn(CommonStrings.action_invite)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `click on call invokes expected callback`() {
|
||||
ensureCalledOnce { callback ->
|
||||
rule.setRoomDetailView(
|
||||
state = aRoomDetailsState(
|
||||
eventSink = EventsRecorder(expectEvents = false),
|
||||
canInvite = true,
|
||||
),
|
||||
onJoinCallClicked = callback,
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_call)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,6 +271,7 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setRoomD
|
||||
openAvatarPreview: (name: String, url: String) -> Unit = EnsureNeverCalledWithTwoParams(),
|
||||
openPollHistory: () -> Unit = EnsureNeverCalled(),
|
||||
openAdminSettings: () -> Unit = EnsureNeverCalled(),
|
||||
onJoinCallClicked: () -> Unit = EnsureNeverCalled(),
|
||||
) {
|
||||
setContent {
|
||||
RoomDetailsView(
|
||||
@@ -272,6 +286,7 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setRoomD
|
||||
openAvatarPreview = openAvatarPreview,
|
||||
openPollHistory = openPollHistory,
|
||||
openAdminSettings = openAdminSettings,
|
||||
onJoinCallClicked = onJoinCallClicked,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@@ -53,12 +54,14 @@ fun MainActionButton(
|
||||
val ripple = rememberRipple(bounded = false)
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
Column(
|
||||
modifier.clickable(
|
||||
enabled = enabled,
|
||||
interactionSource = interactionSource,
|
||||
onClick = onClick,
|
||||
indication = ripple
|
||||
),
|
||||
modifier
|
||||
.clickable(
|
||||
enabled = enabled,
|
||||
interactionSource = interactionSource,
|
||||
onClick = onClick,
|
||||
indication = ripple
|
||||
)
|
||||
.widthIn(min = 76.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
val tintColor = if (enabled) LocalContentColor.current else MaterialTheme.colorScheme.secondary
|
||||
|
||||
@@ -49,6 +49,13 @@ fun MatrixRoom.canRedactOtherAsState(updateKey: Long): State<Boolean> {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MatrixRoom.canCall(updateKey: Long): State<Boolean> {
|
||||
return produceState(initialValue = false, key1 = updateKey) {
|
||||
value = canUserJoinCall(sessionId).getOrElse { false }
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MatrixRoom.isOwnUserAdmin(): Boolean {
|
||||
val roomInfo by roomInfoFlow.collectAsState(initial = null)
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
<string name="action_accept">"Accept"</string>
|
||||
<string name="action_add_to_timeline">"Add to timeline"</string>
|
||||
<string name="action_back">"Back"</string>
|
||||
<string name="action_call">"Call"</string>
|
||||
<string name="action_cancel">"Cancel"</string>
|
||||
<string name="action_choose_photo">"Choose photo"</string>
|
||||
<string name="action_clear">"Clear"</string>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user