Update the chat screen UI using RoomInfo. (#1640)
* Update the chat screen UI using `RoomInfo`. This is specially useful for getting live values for `hasRoomCall`. * Ensure the first `MatrixRoomInfo` is emitted ASAP * Try excluding `*Present$present$*` inner functions from kover as separate entities * Update strings --------- Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
parent
3ec62ad58a
commit
030e86f56b
36 changed files with 338 additions and 52 deletions
|
|
@ -200,6 +200,7 @@ koverMerged {
|
|||
"*Node$*",
|
||||
// Exclude `:libraries:matrix:impl` module, it contains only wrappers to access the Rust Matrix SDK api, so it is not really relevant to unit test it: there is no logic to test.
|
||||
"io.element.android.libraries.matrix.impl.*",
|
||||
"*Presenter\$present\$*"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -252,6 +253,7 @@ koverMerged {
|
|||
excludes += "io.element.android.appnav.loggedin.LoggedInPresenter$*"
|
||||
// Some options can't be tested at the moment
|
||||
excludes += "io.element.android.features.preferences.impl.developer.DeveloperSettingsPresenter$*"
|
||||
excludes += "*Presenter\$present\$*"
|
||||
}
|
||||
bound {
|
||||
minValue = 85
|
||||
|
|
|
|||
1
changelog.d/1158.feature
Normal file
1
changelog.d/1158.feature
Normal file
|
|
@ -0,0 +1 @@
|
|||
Element Call: change the 'join call' button in a chat room when there's an active call.
|
||||
|
|
@ -21,6 +21,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
|
|
@ -74,6 +75,7 @@ import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||
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
|
||||
|
|
@ -113,6 +115,7 @@ class MessagesPresenter @AssistedInject constructor(
|
|||
|
||||
@Composable
|
||||
override fun present(): MessagesState {
|
||||
val roomInfo by room.roomInfoFlow.collectAsState(null)
|
||||
val localCoroutineScope = rememberCoroutineScope()
|
||||
val composerState = composerPresenter.present()
|
||||
val voiceMessageComposerState = voiceMessageComposerPresenter.present()
|
||||
|
|
@ -125,14 +128,13 @@ class MessagesPresenter @AssistedInject constructor(
|
|||
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
||||
val userHasPermissionToSendMessage by room.canSendMessageAsState(type = MessageEventType.ROOM_MESSAGE, updateKey = syncUpdateFlow.value)
|
||||
val userHasPermissionToRedact by room.canRedactAsState(updateKey = syncUpdateFlow.value)
|
||||
var roomName: Async<String> by remember { mutableStateOf(Async.Uninitialized) }
|
||||
var roomAvatar: Async<AvatarData> by remember { mutableStateOf(Async.Uninitialized) }
|
||||
LaunchedEffect(syncUpdateFlow.value) {
|
||||
withContext(dispatchers.io) {
|
||||
roomName = Async.Success(room.displayName)
|
||||
roomAvatar = Async.Success(room.avatarData())
|
||||
}
|
||||
val roomName: Async<String> by remember {
|
||||
derivedStateOf { roomInfo?.name?.let { Async.Success(it) } ?: Async.Uninitialized }
|
||||
}
|
||||
val roomAvatar: Async<AvatarData> by remember {
|
||||
derivedStateOf { roomInfo?.avatarData()?.let { Async.Success(it) } ?: Async.Uninitialized }
|
||||
}
|
||||
|
||||
var hasDismissedInviteDialog by rememberSaveable {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
|
@ -207,14 +209,15 @@ class MessagesPresenter @AssistedInject constructor(
|
|||
enableVoiceMessages = enableVoiceMessages,
|
||||
enableInRoomCalls = enableInRoomCalls,
|
||||
appName = buildMeta.applicationName,
|
||||
isCallOngoing = roomInfo?.hasRoomCall ?: false,
|
||||
eventSink = { handleEvents(it) }
|
||||
)
|
||||
}
|
||||
|
||||
private fun MatrixRoom.avatarData(): AvatarData {
|
||||
private fun MatrixRoomInfo.avatarData(): AvatarData {
|
||||
return AvatarData(
|
||||
id = roomId.value,
|
||||
name = displayName,
|
||||
id = id,
|
||||
name = name,
|
||||
url = avatarUrl,
|
||||
size = AvatarSize.TimelineRoom
|
||||
)
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ data class MessagesState(
|
|||
val enableTextFormatting: Boolean,
|
||||
val enableVoiceMessages: Boolean,
|
||||
val enableInRoomCalls: Boolean,
|
||||
val isCallOngoing: Boolean,
|
||||
val appName: String,
|
||||
val eventSink: (MessagesEvents) -> Unit
|
||||
)
|
||||
|
|
|
|||
|
|
@ -63,6 +63,9 @@ open class MessagesStateProvider : PreviewParameterProvider<MessagesState> {
|
|||
attachmentsState = AttachmentsState.Sending.Uploading(0.33f)
|
||||
),
|
||||
),
|
||||
aMessagesState().copy(
|
||||
isCallOngoing = true,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -102,6 +105,7 @@ fun aMessagesState() = MessagesState(
|
|||
enableTextFormatting = true,
|
||||
enableVoiceMessages = true,
|
||||
enableInRoomCalls = true,
|
||||
isCallOngoing = false,
|
||||
appName = "Element",
|
||||
eventSink = {}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -21,17 +21,21 @@ import androidx.compose.foundation.clickable
|
|||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -94,6 +98,7 @@ import io.element.android.libraries.theme.ElementTheme
|
|||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import timber.log.Timber
|
||||
import androidx.compose.material3.Button as Material3Button
|
||||
|
||||
@Composable
|
||||
fun MessagesView(
|
||||
|
|
@ -174,6 +179,7 @@ fun MessagesView(
|
|||
inRoomCallsEnabled = state.enableInRoomCalls,
|
||||
onBackPressed = onBackPressed,
|
||||
onRoomDetailsClicked = onRoomDetailsClicked,
|
||||
isCallOngoing = state.isCallOngoing,
|
||||
onJoinCallClicked = onJoinCallClicked,
|
||||
)
|
||||
}
|
||||
|
|
@ -375,6 +381,7 @@ private fun MessagesViewTopBar(
|
|||
roomName: String?,
|
||||
roomAvatar: AvatarData?,
|
||||
inRoomCallsEnabled: Boolean,
|
||||
isCallOngoing: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
onRoomDetailsClicked: () -> Unit = {},
|
||||
onJoinCallClicked: () -> Unit = {},
|
||||
|
|
@ -402,15 +409,48 @@ private fun MessagesViewTopBar(
|
|||
},
|
||||
actions = {
|
||||
if (inRoomCallsEnabled) {
|
||||
IconButton(onClick = onJoinCallClicked) {
|
||||
Icon(CommonDrawables.ic_compound_video_call, contentDescription = null) // TODO add proper content description once we have the state
|
||||
if (isCallOngoing) {
|
||||
JoinCallMenuItem(onJoinCallClicked = onJoinCallClicked)
|
||||
} else {
|
||||
IconButton(onClick = onJoinCallClicked) {
|
||||
Icon(CommonDrawables.ic_compound_video_call, contentDescription = stringResource(CommonStrings.a11y_start_call))
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.width(8.dp))
|
||||
},
|
||||
windowInsets = WindowInsets(0.dp)
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun JoinCallMenuItem(
|
||||
modifier: Modifier = Modifier,
|
||||
onJoinCallClicked: () -> Unit,
|
||||
) {
|
||||
Material3Button(
|
||||
onClick = onJoinCallClicked,
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
contentColor = ElementTheme.colors.bgCanvasDefault,
|
||||
containerColor = ElementTheme.colors.iconAccentTertiary
|
||||
),
|
||||
contentPadding = PaddingValues(horizontal = 10.dp, vertical = 0.dp),
|
||||
modifier = modifier.heightIn(min = 36.dp),
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(20.dp),
|
||||
resourceId = CommonDrawables.ic_compound_video_call,
|
||||
contentDescription = null
|
||||
)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text(
|
||||
text = stringResource(CommonStrings.action_join),
|
||||
style = ElementTheme.typography.fontBodyMdMedium
|
||||
)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RoomAvatarAndNameRow(
|
||||
roomName: String,
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ import io.element.android.libraries.matrix.test.A_SESSION_ID
|
|||
import io.element.android.libraries.matrix.test.A_SESSION_ID_2
|
||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||
import io.element.android.libraries.matrix.test.room.aRoomMember
|
||||
import io.element.android.libraries.mediapickers.test.FakePickerProvider
|
||||
import io.element.android.libraries.mediaupload.api.MediaSender
|
||||
|
|
@ -106,7 +107,8 @@ class MessagesPresenterTest {
|
|||
val initialState = consumeItemsUntilTimeout().last()
|
||||
assertThat(initialState.roomId).isEqualTo(A_ROOM_ID)
|
||||
assertThat(initialState.roomName).isEqualTo(Async.Success(""))
|
||||
assertThat(initialState.roomAvatar).isEqualTo(Async.Success(AvatarData(id = A_ROOM_ID.value, name = "", size = AvatarSize.TimelineRoom)))
|
||||
assertThat(initialState.roomAvatar)
|
||||
.isEqualTo(Async.Success(AvatarData(id = A_ROOM_ID.value, name = "", url = AN_AVATAR_URL, size = AvatarSize.TimelineRoom)))
|
||||
assertThat(initialState.userHasPermissionToSendMessage).isTrue()
|
||||
assertThat(initialState.userHasPermissionToRedact).isFalse()
|
||||
assertThat(initialState.hasNetworkConnection).isTrue()
|
||||
|
|
@ -603,7 +605,9 @@ class MessagesPresenterTest {
|
|||
|
||||
private fun TestScope.createMessagesPresenter(
|
||||
coroutineDispatchers: CoroutineDispatchers = testCoroutineDispatchers(),
|
||||
matrixRoom: MatrixRoom = FakeMatrixRoom(),
|
||||
matrixRoom: MatrixRoom = FakeMatrixRoom().apply {
|
||||
givenRoomInfo(aRoomInfo(id = roomId.value, name = ""))
|
||||
},
|
||||
navigator: FakeMessagesNavigator = FakeMessagesNavigator(),
|
||||
clipboardHelper: FakeClipboardHelper = FakeClipboardHelper(),
|
||||
analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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.libraries.matrix.api.room
|
||||
|
||||
enum class CurrentUserMembership {
|
||||
INVITED, JOINED, LEFT
|
||||
}
|
||||
|
|
@ -32,6 +32,7 @@ import io.element.android.libraries.matrix.api.room.location.AssetType
|
|||
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.io.Closeable
|
||||
import java.io.File
|
||||
|
|
@ -51,6 +52,8 @@ interface MatrixRoom : Closeable {
|
|||
val activeMemberCount: Long
|
||||
val joinedMemberCount: Long
|
||||
|
||||
val roomInfoFlow: Flow<MatrixRoomInfo>
|
||||
|
||||
/**
|
||||
* A one-to-one is a room with exactly 2 members.
|
||||
* See [the Matrix spec](https://spec.matrix.org/latest/client-server-api/#default-underride-rules).
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.libraries.matrix.api.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||
|
||||
data class MatrixRoomInfo(
|
||||
val id: String,
|
||||
val name: String?,
|
||||
val topic: String?,
|
||||
val avatarUrl: String?,
|
||||
val isDirect: Boolean,
|
||||
val isPublic: Boolean,
|
||||
val isSpace: Boolean,
|
||||
val isTombstoned: Boolean,
|
||||
val canonicalAlias: String?,
|
||||
val alternativeAliases: List<String>,
|
||||
val currentUserMembership: CurrentUserMembership,
|
||||
val latestEvent: EventTimelineItem?,
|
||||
val inviter: RoomMember?,
|
||||
val activeMembersCount: Long,
|
||||
val invitedMembersCount: Long,
|
||||
val joinedMembersCount: Long,
|
||||
val highlightCount: Long,
|
||||
val notificationCount: Long,
|
||||
val userDefinedNotificationMode: RoomNotificationMode?,
|
||||
val hasRoomCall: Boolean,
|
||||
val activeRoomCallParticipants: List<String>
|
||||
)
|
||||
|
|
@ -47,6 +47,7 @@ import io.element.android.libraries.matrix.impl.notification.RustNotificationSer
|
|||
import io.element.android.libraries.matrix.impl.notificationsettings.RustNotificationSettingsService
|
||||
import io.element.android.libraries.matrix.impl.oidc.toRustAction
|
||||
import io.element.android.libraries.matrix.impl.pushers.RustPushersService
|
||||
import io.element.android.libraries.matrix.impl.room.MatrixRoomInfoMapper
|
||||
import io.element.android.libraries.matrix.impl.room.RoomContentForwarder
|
||||
import io.element.android.libraries.matrix.impl.room.RoomSyncSubscriber
|
||||
import io.element.android.libraries.matrix.impl.room.RustMatrixRoom
|
||||
|
|
@ -201,7 +202,8 @@ class RustMatrixClient constructor(
|
|||
systemClock = clock,
|
||||
roomContentForwarder = roomContentForwarder,
|
||||
sessionData = sessionStore.getSession(sessionId.value)!!,
|
||||
roomSyncSubscriber = roomSyncSubscriber
|
||||
roomSyncSubscriber = roomSyncSubscriber,
|
||||
matrixRoomInfoMapper = MatrixRoomInfoMapper(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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.libraries.matrix.impl.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelineItemMapper
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
import org.matrix.rustcomponents.sdk.Membership as RustMembership
|
||||
import org.matrix.rustcomponents.sdk.RoomInfo as RustRoomInfo
|
||||
import org.matrix.rustcomponents.sdk.RoomNotificationMode as RustRoomNotificationMode
|
||||
|
||||
class MatrixRoomInfoMapper(
|
||||
private val timelineItemMapper: EventTimelineItemMapper = EventTimelineItemMapper(),
|
||||
) {
|
||||
|
||||
fun map(rustRoomInfo: RustRoomInfo): MatrixRoomInfo = rustRoomInfo.use {
|
||||
return MatrixRoomInfo(
|
||||
id = it.id,
|
||||
name = it.name,
|
||||
topic = it.topic,
|
||||
avatarUrl = it.avatarUrl,
|
||||
isDirect = it.isDirect,
|
||||
isPublic = it.isPublic,
|
||||
isSpace = it.isSpace,
|
||||
isTombstoned = it.isTombstoned,
|
||||
canonicalAlias = it.canonicalAlias,
|
||||
alternativeAliases = it.alternativeAliases,
|
||||
currentUserMembership = it.membership.map(),
|
||||
latestEvent = it.latestEvent?.use (timelineItemMapper::map),
|
||||
inviter = it.inviter?.use(RoomMemberMapper::map),
|
||||
activeMembersCount = it.activeMembersCount.toLong(),
|
||||
invitedMembersCount = it.invitedMembersCount.toLong(),
|
||||
joinedMembersCount = it.joinedMembersCount.toLong(),
|
||||
highlightCount = it.highlightCount.toLong(),
|
||||
notificationCount = it.notificationCount.toLong(),
|
||||
userDefinedNotificationMode = it.userDefinedNotificationMode?.map(),
|
||||
hasRoomCall = it.hasRoomCall,
|
||||
activeRoomCallParticipants = it.activeRoomCallParticipants
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun RustMembership.map(): CurrentUserMembership = when(this) {
|
||||
RustMembership.INVITED -> CurrentUserMembership.INVITED
|
||||
RustMembership.JOINED -> CurrentUserMembership.JOINED
|
||||
RustMembership.LEFT -> CurrentUserMembership.LEFT
|
||||
}
|
||||
|
||||
fun RustRoomNotificationMode.map(): RoomNotificationMode = when(this) {
|
||||
RustRoomNotificationMode.ALL_MESSAGES -> RoomNotificationMode.ALL_MESSAGES
|
||||
RustRoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
|
||||
RustRoomNotificationMode.MUTE -> RoomNotificationMode.MUTE
|
||||
}
|
||||
|
|
@ -32,6 +32,7 @@ import io.element.android.libraries.matrix.api.media.MediaUploadHandler
|
|||
import io.element.android.libraries.matrix.api.media.VideoInfo
|
||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomNotificationSettingsState
|
||||
import io.element.android.libraries.matrix.api.room.MessageEventType
|
||||
|
|
@ -50,6 +51,7 @@ import io.element.android.libraries.matrix.impl.poll.toInner
|
|||
import io.element.android.libraries.matrix.impl.room.location.toInner
|
||||
import io.element.android.libraries.matrix.impl.timeline.RustMatrixTimeline
|
||||
import io.element.android.libraries.matrix.impl.util.destroyAll
|
||||
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
|
||||
import io.element.android.libraries.matrix.impl.widget.RustWidgetDriver
|
||||
import io.element.android.libraries.matrix.impl.widget.generateWidgetWebViewUrl
|
||||
import io.element.android.libraries.sessionstorage.api.SessionData
|
||||
|
|
@ -59,12 +61,16 @@ import kotlinx.coroutines.CoroutineScope
|
|||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.EventTimelineItem
|
||||
import org.matrix.rustcomponents.sdk.Room
|
||||
import org.matrix.rustcomponents.sdk.RoomInfo
|
||||
import org.matrix.rustcomponents.sdk.RoomInfoListener
|
||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||
import org.matrix.rustcomponents.sdk.RoomMember
|
||||
import org.matrix.rustcomponents.sdk.RoomMessageEventContentWithoutRelation
|
||||
|
|
@ -73,6 +79,7 @@ import org.matrix.rustcomponents.sdk.WidgetCapabilities
|
|||
import org.matrix.rustcomponents.sdk.WidgetCapabilitiesProvider
|
||||
import org.matrix.rustcomponents.sdk.messageEventContentFromHtml
|
||||
import org.matrix.rustcomponents.sdk.messageEventContentFromMarkdown
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
||||
|
|
@ -88,10 +95,23 @@ class RustMatrixRoom(
|
|||
private val roomContentForwarder: RoomContentForwarder,
|
||||
private val sessionData: SessionData,
|
||||
private val roomSyncSubscriber: RoomSyncSubscriber,
|
||||
private val matrixRoomInfoMapper: MatrixRoomInfoMapper,
|
||||
) : MatrixRoom {
|
||||
|
||||
override val roomId = RoomId(innerRoom.id())
|
||||
|
||||
override val roomInfoFlow: Flow<MatrixRoomInfo> = mxCallbackFlow {
|
||||
launch {
|
||||
val initial = innerRoom.roomInfo().use(matrixRoomInfoMapper::map)
|
||||
channel.trySend(initial)
|
||||
}
|
||||
innerRoom.subscribeToRoomInfoUpdates(object : RoomInfoListener {
|
||||
override fun call(roomInfo: RoomInfo) {
|
||||
channel.trySend(matrixRoomInfoMapper.map(roomInfo))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Create a dispatcher for all room methods...
|
||||
private val roomDispatcher = coroutineDispatchers.io.limitedParallelism(32)
|
||||
|
||||
|
|
|
|||
|
|
@ -68,7 +68,11 @@ class RustWidgetDriver(
|
|||
}
|
||||
|
||||
override suspend fun send(message: String) {
|
||||
driverAndHandle.handle.send(message)
|
||||
try {
|
||||
driverAndHandle.handle.send(message)
|
||||
} catch (e: IllegalStateException) {
|
||||
// The handle is closed, ignore
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
|
|
|
|||
|
|
@ -29,16 +29,23 @@ import io.element.android.libraries.matrix.api.media.MediaUploadHandler
|
|||
import io.element.android.libraries.matrix.api.media.VideoInfo
|
||||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomNotificationSettingsState
|
||||
import io.element.android.libraries.matrix.api.room.MessageEventType
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.api.room.StateEventType
|
||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
|
||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
|
||||
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
|
||||
|
|
@ -46,6 +53,8 @@ import io.element.android.libraries.matrix.test.timeline.FakeMatrixTimeline
|
|||
import io.element.android.libraries.matrix.test.widget.FakeWidgetDriver
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.io.File
|
||||
|
|
@ -143,6 +152,9 @@ class FakeMatrixRoom(
|
|||
|
||||
private var leaveRoomError: Throwable? = null
|
||||
|
||||
private val _roomInfoFlow: MutableSharedFlow<MatrixRoomInfo> = MutableStateFlow(aRoomInfo())
|
||||
override val roomInfoFlow: Flow<MatrixRoomInfo> = _roomInfoFlow
|
||||
|
||||
override val membersStateFlow: MutableStateFlow<MatrixRoomMembersState> = MutableStateFlow(MatrixRoomMembersState.Unknown)
|
||||
|
||||
override val roomNotificationSettingsStateFlow: MutableStateFlow<MatrixRoomNotificationSettingsState> =
|
||||
|
|
@ -497,6 +509,10 @@ class FakeMatrixRoom(
|
|||
fun givenGetWidgetDriverResult(result: Result<MatrixWidgetDriver>) {
|
||||
getWidgetDriverResult = result
|
||||
}
|
||||
|
||||
fun givenRoomInfo(roomInfo: MatrixRoomInfo) {
|
||||
_roomInfoFlow.tryEmit(roomInfo)
|
||||
}
|
||||
}
|
||||
|
||||
data class SendLocationInvocation(
|
||||
|
|
@ -523,3 +539,49 @@ data class EndPollInvocation(
|
|||
val pollStartId: EventId,
|
||||
val text: String,
|
||||
)
|
||||
|
||||
fun aRoomInfo(
|
||||
id: String = A_ROOM_ID.value,
|
||||
name: String? = A_ROOM_NAME,
|
||||
topic: String? = "A topic",
|
||||
avatarUrl: String? = AN_AVATAR_URL,
|
||||
isDirect: Boolean = false,
|
||||
isPublic: Boolean = true,
|
||||
isSpace: Boolean = false,
|
||||
isTombstoned: Boolean = false,
|
||||
canonicalAlias: String? = null,
|
||||
alternativeAliases: List<String> = emptyList(),
|
||||
currentUserMembership: CurrentUserMembership = CurrentUserMembership.JOINED,
|
||||
latestEvent: EventTimelineItem? = null,
|
||||
inviter: RoomMember? = null,
|
||||
activeMembersCount: Long = 1,
|
||||
invitedMembersCount: Long = 0,
|
||||
joinedMembersCount: Long = 1,
|
||||
highlightCount: Long = 0,
|
||||
notificationCount: Long = 0,
|
||||
userDefinedNotificationMode: RoomNotificationMode? = null,
|
||||
hasRoomCall: Boolean = false,
|
||||
activeRoomCallParticipants: List<String> = emptyList()
|
||||
) = MatrixRoomInfo(
|
||||
id = id,
|
||||
name = name,
|
||||
topic = topic,
|
||||
avatarUrl = avatarUrl,
|
||||
isDirect = isDirect,
|
||||
isPublic = isPublic,
|
||||
isSpace = isSpace,
|
||||
isTombstoned = isTombstoned,
|
||||
canonicalAlias = canonicalAlias,
|
||||
alternativeAliases = alternativeAliases,
|
||||
currentUserMembership = currentUserMembership,
|
||||
latestEvent = latestEvent,
|
||||
inviter = inviter,
|
||||
activeMembersCount = activeMembersCount,
|
||||
invitedMembersCount = invitedMembersCount,
|
||||
joinedMembersCount = joinedMembersCount,
|
||||
highlightCount = highlightCount,
|
||||
notificationCount = notificationCount,
|
||||
userDefinedNotificationMode = userDefinedNotificationMode,
|
||||
hasRoomCall = hasRoomCall,
|
||||
activeRoomCallParticipants = activeRoomCallParticipants
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
<string name="a11y_poll_end">"Ended poll"</string>
|
||||
<string name="a11y_send_files">"Send files"</string>
|
||||
<string name="a11y_show_password">"Show password"</string>
|
||||
<string name="a11y_start_call">"Start a call"</string>
|
||||
<string name="a11y_user_menu">"User menu"</string>
|
||||
<string name="a11y_voice_message_record">"Record voice message. Double tap and hold to record. Release to end recording."</string>
|
||||
<string name="action_accept">"Accept"</string>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5f219bd9b9363f237e15bb73655dd53b2ec143e18c8544c11efbfa90390e091c
|
||||
size 54312
|
||||
oid sha256:0eee7139a7675a08899accf49a97139b56333737e649e51ca1644ae6ae68d915
|
||||
size 54358
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6065f5330e1b3638719c6743db098bf6576fcffc6ccf8215f7f600a0b981144b
|
||||
size 55731
|
||||
oid sha256:39e0b212a2cc423f3e33254d4a965c3071e88e5dd21268c42b3b555b1800b236
|
||||
size 55774
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:33afbe67d0e4156d615e6a58d4356386d5e2c0922cf646c2ec385338d869d8f5
|
||||
size 56023
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f4bdafdfa50665f05ba5cd8749252e7e5d65bea8a8c6bfc1c46eb1acd1570b52
|
||||
size 56086
|
||||
oid sha256:42445e54ca2cfadbbd3f041f327825ea5595e27877376a760c37b746529d0980
|
||||
size 56135
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2bfcc4dcaa8980cfc9c724e64a93116ac1fb81a1bf4421532cb196d4e07db7c5
|
||||
size 56024
|
||||
oid sha256:b9f1300da0ac7ef29286fb37d4489b38fa07632f71aa7d1f1cc0e0786749c626
|
||||
size 56124
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c86a6da3d45b767a41f65e0cf4e8cacefb33e0409386a6233140891deb2258f6
|
||||
size 51965
|
||||
oid sha256:95f81914a56679419a7a6055040ae51b11616c87012310cbd221e3af41d55e8e
|
||||
size 52039
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5a1fc20759ff45eeef547621853b9684a663fe9dcdf78b316454dcae26d078f9
|
||||
size 52286
|
||||
oid sha256:ce97ccc29b01824ddd251dbe7756aa80749c0acc4fd8278cb1c3e4fc24c2337e
|
||||
size 52334
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7b65df67b1384aaa169f25d81e87072f2732c88676122e58e5d08850ada050ce
|
||||
size 60073
|
||||
oid sha256:6d387f9678367ec0b8d6617a3ba378d309979af0e534e9f683805e71cd6c2794
|
||||
size 60172
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:abe449182a4cddbf11b9004579d27043e41c74d45261c1435d18b516946f9eed
|
||||
size 42175
|
||||
oid sha256:0708776dd8884bd8975845c9fbb0b30fe6317d5692aaba36781fed5cff065c1b
|
||||
size 42279
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5deb0929efaab3160a7d0441bde9afe278064e8c0641cc62408bb25acbdced21
|
||||
size 41329
|
||||
oid sha256:766086d64f70b2a9f028149190d3f3c5b2ff34599907b38997657b6d093fa0f4
|
||||
size 41428
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7b9bc4654b6911d7f50b4b0242b650a529cb22c13a58caf5eae50366a2d27b37
|
||||
size 52532
|
||||
oid sha256:6d42806b454f15ed83e883bf5e2765d345163a57049b4b598c1b8d08458588dc
|
||||
size 52624
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0e051668beb9030ff184d6e75831a00202854f6448880f399a7ee2d5be187740
|
||||
size 53880
|
||||
oid sha256:27f4fd957e004579b8b20373e1649f8c067a604595a5dcaae60b0caec7c43954
|
||||
size 53972
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:010d76d537104b914a35cdb783c5cfcc1953a470d2d9bc23781c8837c9558f5f
|
||||
size 54122
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c6c6603452db218811f3f5a2baa934ca640755b969e81ca8cbf6b3dcf663aee9
|
||||
size 54551
|
||||
oid sha256:397f061ceeefb2ea67a5d9a342050cea136554e936854bd26b92c3acfde45d74
|
||||
size 54646
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e953bc91e9959f1635d12416f6afde79427aeac4edef44fb38f1512672e544bd
|
||||
size 51552
|
||||
oid sha256:a1a0280d6321b754e5e36d25eefc2d5c7192af16e52ae1304b4e7c57a7824e19
|
||||
size 51611
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0157713ae934c0771a5d3d4d064ce6dc9be11b1d8011f2360740d7f4a20f6f04
|
||||
size 50162
|
||||
oid sha256:99ded72c99dc1ba5b8c61cbacca6cc91f4b6325a16c12013150c6d63afd28182
|
||||
size 50273
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:24f1d30bb62ed620671e87062a338fd53f409bce6e08fb7b116249decf74e409
|
||||
size 50295
|
||||
oid sha256:d2fc04a6c21e851b4e10c88ad9485f43f2f1576f5aeb40d4611513352341536e
|
||||
size 50384
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9457cbf3ff35a5677f983eba7d5087ca82e4d99a2bb942f64f5f7ccdb71e01b5
|
||||
size 54882
|
||||
oid sha256:5f3a764349e13ebe7205ff4d0b04a02f64c6eee5d65bb3b9cc62ac1d721d2d39
|
||||
size 54944
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:549892647be61e9fd5fbbb80fa14734533e1b4628f95d409f2772dd990000f42
|
||||
size 38953
|
||||
oid sha256:459029e7871910e085378a40ff503d286b72fb7029c251d1e04a8a1f39cfd0ba
|
||||
size 39017
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c6bef454d8f57ba76b34b8588ba73635719feb6b1dde967b90f812d2410a7749
|
||||
size 38165
|
||||
oid sha256:a2d6cb8081da15f265d35e909c1d22bb6a48219162b20e1472230a698fdc2d2b
|
||||
size 38231
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue