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:
parent
d86b7d24db
commit
46b22d7db7
34 changed files with 149 additions and 94 deletions
|
|
@ -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,21 +190,13 @@ fun RoomDetailsView(
|
|||
}
|
||||
|
||||
val displayMemberListItem = state.roomType is RoomDetailsType.Room
|
||||
val displayInviteMembersItem = state.canInvite
|
||||
if (displayMemberListItem || displayInviteMembersItem) {
|
||||
PreferenceCategory {
|
||||
if (displayMemberListItem) {
|
||||
PreferenceCategory {
|
||||
MembersItem(
|
||||
memberCount = state.memberCount,
|
||||
openRoomMemberList = openRoomMemberList,
|
||||
)
|
||||
}
|
||||
if (displayInviteMembersItem) {
|
||||
InviteItem(
|
||||
invitePeople = invitePeople
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PollsSection(
|
||||
|
|
@ -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(R.string.screen_room_details_share_room_title),
|
||||
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(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(
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fe5642aa6804a99d9b3505bb63fe0c328015592f35e095415c910d9a5abfa052
|
||||
size 47238
|
||||
oid sha256:ba1133eb6569d4821b12fe56c3347a472bd95ad5b269546fed57f249e365d432
|
||||
size 47379
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7465cc5ca6b9f49ab4ac1a529fc2ee21547afb85a231d74491df6b2a2781bd18
|
||||
size 33075
|
||||
oid sha256:d0f46ff6cfe976d3d96ecbf323fcf2ed2ed57c326c3bed3c450c30e851442567
|
||||
size 33239
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c3a1bc43adac954bc9ab1b9b9d69814e376d2f2a41006b8126acdeb19bb93df7
|
||||
size 44471
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:93d4eb6dce460237fe6650e965b901c28b020057907867ac9f73665032a85f88
|
||||
size 35427
|
||||
oid sha256:84dcef17832aad71a9bf0505346731b27f882795d9bf75b194b647dfad40eca2
|
||||
size 35595
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c8a655fb4e64fa300c6169d332fa894b6bb215a9acd09c0fbe876c7430f3ae1d
|
||||
size 34786
|
||||
oid sha256:72b731453adaad784d3c037c06defd7b24939435ab54ee47a693f6aabcc0dce7
|
||||
size 34928
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4329c322042bc8489177886e37f6d3a876cbfee8a8a73e3aae6414a17e92ad67
|
||||
size 42330
|
||||
oid sha256:c675a4d9422ba2d73e860420e25f10a21c899b33e780a4f61a41f71921f3078a
|
||||
size 42471
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8359c8e69df44350b30b5aaf9ad99ace52fe378ba6db2cfd86a4deb35aa3abd1
|
||||
size 46347
|
||||
oid sha256:78a182e17a2ef55a4d7dbcb4ad952d5f06f12044da4dc3578a5cd3cb7b7d34a1
|
||||
size 45723
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8659b897a22fd88915a8df3de3f0ba8d6775057f51b1c5cd1db83b0e58497576
|
||||
size 44394
|
||||
oid sha256:ccf59aaaba1ebbf35518840ba069f2ea587c5f5590ed1f0be5904922d10321e0
|
||||
size 44535
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7a195980dff4ad4343eec99438a35ab655abd124346f5e4d66011c3c1736938e
|
||||
size 44589
|
||||
oid sha256:972414b2900a7b2fddc7c45640f0718bbed3ff799067a0dde4f9e3f2fb70266a
|
||||
size 44574
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:29abf008345cd7c7150a64b17c6bd1c0af06c8d960c5e91a2cf0e4b1c0365954
|
||||
size 48791
|
||||
oid sha256:f89ab41056584b086cf6fbeabff68cee7ee7dde4d52fcef9849dcdc296c69a1e
|
||||
size 48983
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e6228ac8af567a9d4fc402d2b14ff65315f6cfca2d02407664b0db00df5fa68e
|
||||
size 34363
|
||||
oid sha256:d3dead46255af9a750e9afe09f2e5201a4f12adea8f2e1a3d0806062775e3ea0
|
||||
size 34587
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:52ded1bfd8ff7a95041172bdca31be2a869f5e2b81f1b181f0ece14ffdf4c7aa
|
||||
size 45708
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3987ef26780da018bedbe05b1af43c3a9c3f31963aff1eb17833a284118d29ec
|
||||
size 36951
|
||||
oid sha256:82107faba76f5c6cc992ec47d6e4d765761743094fcae65c5ed948fce3cf1f05
|
||||
size 37183
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c74c580b51aaa480a1ca15f2f33e3e6acb9e2a5581d5501ff67b6c4b56d92099
|
||||
size 35582
|
||||
oid sha256:73a016fc3d40517042f8d231ab682d71e477843195fbe4e080cd62fc12e8b954
|
||||
size 35778
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d81c82c5d5f07bb57911488e18e2cf0867009233ad678557a8ead13e6827ba2b
|
||||
size 43640
|
||||
oid sha256:b9250d7927307d397b969e0803ed9965a7ae301347446fef264004d690d064f6
|
||||
size 43826
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:bdfe3fb1c1594335fda27a62f2c1b4740815778248ba76923a2184937db02144
|
||||
size 47743
|
||||
oid sha256:fd42eba5e0daa676298cfb2813d595083e8abd1c5abf8dfb416910fbbc2ea02c
|
||||
size 47128
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d23cfed590306aec6eb92664c616e38f18e54274bb381e2e044e22916e8b1129
|
||||
size 45723
|
||||
oid sha256:58cdd2ba7d10769a29ad59fa2cff7c972767d01352d656a06bfcb922522980b3
|
||||
size 45918
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:33e9d17227fda64a35cd885d029b70bab0088d5748cc5440c32d1cfa0a3bbf52
|
||||
size 45908
|
||||
oid sha256:43d176f6e953006f72b09ba374f95c28388f10ce600c9d495761c4c11ccc109b
|
||||
size 45874
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3a4cd869d7be5ebd07dc72947fe36f6d601fa55494bb4f7c75b828d1569d983a
|
||||
size 15105
|
||||
oid sha256:9c5d638b74379f27b9774309848b4cb068514faa856f5882f8f0f8614ecaad12
|
||||
size 15712
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue