Update SDK version to 25.03.13 and fix breaking changes (#4406)
Breaking changes addressed: * Make `MatrixClient.getNotificationSettings()` async, cache its result. * Use `RoomInfo` for accessing the updated room's info. * Refactor `MatrixRoom` so it always receives an initial `MatrixRoomInfo` value: this value will be used to make `MatrixRoom.roomInfoFlow` a `StateFlow` so we can assume the initial updated Room data will be present. * Fetch encryption state when loading a room if it's unknown
This commit is contained in:
parent
0c07a8165f
commit
fccd881b1f
76 changed files with 647 additions and 431 deletions
|
|
@ -25,7 +25,6 @@ import io.element.android.features.roomcall.api.RoomCallState
|
|||
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter
|
||||
import io.element.android.features.roomdetails.impl.securityandprivacy.permissions.securityAndPrivacyPermissionsAsState
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.core.bool.orFalse
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||
|
|
@ -36,7 +35,6 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom
|
|||
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.StateEventType
|
||||
import io.element.android.libraries.matrix.api.room.isDm
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.canInvite
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.canSendState
|
||||
|
|
@ -44,6 +42,7 @@ import io.element.android.libraries.matrix.api.room.roomNotificationSettings
|
|||
import io.element.android.libraries.matrix.ui.room.canHandleKnockRequestsAsState
|
||||
import io.element.android.libraries.matrix.ui.room.getCurrentRoomMember
|
||||
import io.element.android.libraries.matrix.ui.room.getDirectRoomMember
|
||||
import io.element.android.libraries.matrix.ui.room.isDmAsState
|
||||
import io.element.android.libraries.matrix.ui.room.isOwnUserAdmin
|
||||
import io.element.android.libraries.matrix.ui.room.roomMemberIdentityStateChange
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
|
|
@ -72,22 +71,22 @@ class RoomDetailsPresenter @Inject constructor(
|
|||
val scope = rememberCoroutineScope()
|
||||
val leaveRoomState = leaveRoomPresenter.present()
|
||||
val canShowNotificationSettings = remember { mutableStateOf(false) }
|
||||
val roomInfo by room.roomInfoFlow.collectAsState(initial = null)
|
||||
val roomInfo by room.roomInfoFlow.collectAsState()
|
||||
val isUserAdmin = room.isOwnUserAdmin()
|
||||
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
||||
val roomAvatar by remember { derivedStateOf { roomInfo?.avatarUrl ?: room.avatarUrl } }
|
||||
val roomAvatar by remember { derivedStateOf { roomInfo.avatarUrl } }
|
||||
|
||||
val roomName by remember { derivedStateOf { (roomInfo?.name ?: room.displayName).trim() } }
|
||||
val roomTopic by remember { derivedStateOf { roomInfo?.topic ?: room.topic } }
|
||||
val isFavorite by remember { derivedStateOf { roomInfo?.isFavorite.orFalse() } }
|
||||
val joinRule by remember { derivedStateOf { roomInfo?.joinRule } }
|
||||
val roomName by remember { derivedStateOf { roomInfo.name?.trim().orEmpty() } }
|
||||
val roomTopic by remember { derivedStateOf { roomInfo.topic } }
|
||||
val isFavorite by remember { derivedStateOf { roomInfo.isFavorite } }
|
||||
val joinRule by remember { derivedStateOf { roomInfo.joinRule } }
|
||||
|
||||
val canShowPinnedMessages = isPinnedMessagesFeatureEnabled()
|
||||
var canShowMediaGallery by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(Unit) {
|
||||
canShowMediaGallery = featureFlagService.isFeatureEnabled(FeatureFlags.MediaGallery)
|
||||
}
|
||||
val pinnedMessagesCount by remember { derivedStateOf { roomInfo?.pinnedEventIds?.size } }
|
||||
val pinnedMessagesCount by remember { derivedStateOf { roomInfo.pinnedEventIds.size } }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
canShowNotificationSettings.value = featureFlagService.isFeatureEnabled(FeatureFlags.NotificationSettings)
|
||||
|
|
@ -100,6 +99,9 @@ class RoomDetailsPresenter @Inject constructor(
|
|||
val membersState by room.membersStateFlow.collectAsState()
|
||||
val canInvite by getCanInvite(membersState)
|
||||
|
||||
val canonicalAlias by remember { derivedStateOf { roomInfo.canonicalAlias } }
|
||||
val isEncrypted by remember { derivedStateOf { roomInfo.isEncrypted == true } }
|
||||
val isDm by room.isDmAsState()
|
||||
val canEditName by getCanSendState(membersState, StateEventType.ROOM_NAME)
|
||||
val canEditAvatar by getCanSendState(membersState, StateEventType.ROOM_AVATAR)
|
||||
val canEditTopic by getCanSendState(membersState, StateEventType.ROOM_TOPIC)
|
||||
|
|
@ -108,6 +110,7 @@ class RoomDetailsPresenter @Inject constructor(
|
|||
val roomMemberDetailsPresenter = roomMemberDetailsPresenter(dmMember)
|
||||
val roomType = getRoomType(dmMember, currentMember)
|
||||
val roomCallState = roomCallStatePresenter.present()
|
||||
val joinedMemberCount by remember { derivedStateOf { roomInfo.joinedMembersCount } }
|
||||
|
||||
val topicState = remember(canEditTopic, roomTopic, roomType) {
|
||||
val topic = roomTopic
|
||||
|
|
@ -140,7 +143,7 @@ class RoomDetailsPresenter @Inject constructor(
|
|||
}
|
||||
RoomDetailsEvent.UnmuteNotification -> {
|
||||
scope.launch(dispatchers.io) {
|
||||
client.notificationSettingsService().unmuteRoom(room.roomId, room.isEncrypted, room.isOneToOne)
|
||||
client.notificationSettingsService().unmuteRoom(room.roomId, isEncrypted, room.isOneToOne)
|
||||
}
|
||||
}
|
||||
is RoomDetailsEvent.SetFavorite -> scope.setFavorite(event.isFavorite)
|
||||
|
|
@ -165,11 +168,11 @@ class RoomDetailsPresenter @Inject constructor(
|
|||
return RoomDetailsState(
|
||||
roomId = room.roomId,
|
||||
roomName = roomName,
|
||||
roomAlias = room.canonicalAlias,
|
||||
roomAlias = canonicalAlias,
|
||||
roomAvatarUrl = roomAvatar,
|
||||
roomTopic = topicState,
|
||||
memberCount = room.joinedMemberCount,
|
||||
isEncrypted = room.isEncrypted,
|
||||
memberCount = joinedMemberCount,
|
||||
isEncrypted = isEncrypted,
|
||||
canInvite = canInvite,
|
||||
canEdit = (canEditAvatar || canEditName || canEditTopic) && roomType == RoomDetailsType.Room,
|
||||
canShowNotificationSettings = canShowNotificationSettings.value,
|
||||
|
|
@ -179,9 +182,9 @@ class RoomDetailsPresenter @Inject constructor(
|
|||
leaveRoomState = leaveRoomState,
|
||||
roomNotificationSettings = roomNotificationSettingsState.roomNotificationSettings(),
|
||||
isFavorite = isFavorite,
|
||||
displayRolesAndPermissionsSettings = !room.isDm && isUserAdmin,
|
||||
displayRolesAndPermissionsSettings = !isDm && isUserAdmin,
|
||||
isPublic = joinRule == JoinRule.Public,
|
||||
heroes = roomInfo?.heroes.orEmpty().toPersistentList(),
|
||||
heroes = roomInfo.heroes.toPersistentList(),
|
||||
canShowPinnedMessages = canShowPinnedMessages,
|
||||
canShowMediaGallery = canShowMediaGallery,
|
||||
pinnedMessagesCount = pinnedMessagesCount,
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ class RoomMemberListPresenter @AssistedInject constructor(
|
|||
}
|
||||
|
||||
private suspend fun RoomMember.withIdentityState(identityStates: ImmutableMap<UserId, IdentityState>): RoomMemberWithIdentityState {
|
||||
return if (!room.isEncrypted) {
|
||||
return if (room.info().isEncrypted != true) {
|
||||
RoomMemberWithIdentityState(this, null)
|
||||
} else {
|
||||
val identityState = identityStates[userId] ?: encryptionService.getUserIdentity(userId).getOrNull()
|
||||
|
|
|
|||
|
|
@ -27,7 +27,10 @@ import io.element.android.libraries.matrix.api.encryption.identity.IdentityState
|
|||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.ui.room.getRoomMemberAsState
|
||||
import io.element.android.libraries.matrix.ui.room.roomMemberIdentityStateChange
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
|
@ -47,6 +50,7 @@ class RoomMemberDetailsPresenter @AssistedInject constructor(
|
|||
|
||||
private val userProfilePresenter = userProfilePresenterFactory.create(roomMemberId)
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Composable
|
||||
override fun present(): UserProfileState {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
|
@ -75,21 +79,22 @@ class RoomMemberDetailsPresenter @AssistedInject constructor(
|
|||
val userProfileState = userProfilePresenter.present()
|
||||
|
||||
val identityStateChanges by produceState<IdentityStateChange?>(initialValue = null) {
|
||||
if (room.isEncrypted) {
|
||||
// Fetch the initial identity state manually
|
||||
val identityState = encryptionService.getUserIdentity(roomMemberId).getOrNull()
|
||||
value = identityState?.let { IdentityStateChange(roomMemberId, it) }
|
||||
room.roomInfoFlow.filter { it.isEncrypted == true }
|
||||
.flatMapLatest {
|
||||
// Fetch the initial identity state manually
|
||||
val identityState = encryptionService.getUserIdentity(roomMemberId).getOrNull()
|
||||
value = identityState?.let { IdentityStateChange(roomMemberId, it) }
|
||||
|
||||
// Subscribe to the identity changes
|
||||
room.roomMemberIdentityStateChange()
|
||||
.map { it.find { it.identityRoomMember.userId == roomMemberId } }
|
||||
.map { roomMemberIdentityStateChange ->
|
||||
// If we didn't receive any info, manually fetch it
|
||||
roomMemberIdentityStateChange?.identityState ?: encryptionService.getUserIdentity(roomMemberId).getOrNull()
|
||||
}
|
||||
.filterNotNull()
|
||||
.collect { value = IdentityStateChange(roomMemberId, it) }
|
||||
}
|
||||
// Subscribe to the identity changes
|
||||
room.roomMemberIdentityStateChange()
|
||||
.map { it.find { it.identityRoomMember.userId == roomMemberId } }
|
||||
.map { roomMemberIdentityStateChange ->
|
||||
// If we didn't receive any info, manually fetch it
|
||||
roomMemberIdentityStateChange?.identityState ?: encryptionService.getUserIdentity(roomMemberId).getOrNull()
|
||||
}
|
||||
.filterNotNull()
|
||||
}
|
||||
.collect { value = IdentityStateChange(roomMemberId, it) }
|
||||
}
|
||||
|
||||
val verificationState = remember(identityStateChanges) {
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ class RoomMembersModerationPresenter @Inject constructor(
|
|||
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
||||
val canBan by room.canBanAsState(syncUpdateFlow.value)
|
||||
val canKick by room.canKickAsState(syncUpdateFlow.value)
|
||||
val isDm by room.isDmAsState(syncUpdateFlow.value)
|
||||
val isDm by room.isDmAsState()
|
||||
val currentUserMemberPowerLevel by room.userPowerLevelAsState(syncUpdateFlow.value)
|
||||
|
||||
val canDisplayModerationActions by remember {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import androidx.compose.runtime.LaunchedEffect
|
|||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
|
|
@ -78,11 +79,23 @@ class RoomNotificationSettingsPresenter @AssistedInject constructor(
|
|||
mutableStateOf(null)
|
||||
}
|
||||
|
||||
val displayName by produceState(room.info().name) {
|
||||
room.roomInfoFlow.collect { value = it.name }
|
||||
}
|
||||
|
||||
val isRoomEncrypted by produceState(room.info().isEncrypted) {
|
||||
room.roomInfoFlow.collect { value = it.isEncrypted }
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
getDefaultRoomNotificationMode(defaultRoomNotificationMode)
|
||||
fetchNotificationSettings(pendingRoomNotificationMode, roomNotificationSettings)
|
||||
observeNotificationSettings(pendingRoomNotificationMode, roomNotificationSettings)
|
||||
shouldDisplayMentionsOnlyDisclaimer = room.isEncrypted && !notificationSettingsService.canHomeServerPushEncryptedEventsToDevice().getOrDefault(true)
|
||||
}
|
||||
|
||||
LaunchedEffect(isRoomEncrypted) {
|
||||
shouldDisplayMentionsOnlyDisclaimer = isRoomEncrypted == true &&
|
||||
!notificationSettingsService.canHomeServerPushEncryptedEventsToDevice().getOrDefault(true)
|
||||
}
|
||||
|
||||
fun handleEvents(event: RoomNotificationSettingsEvents) {
|
||||
|
|
@ -113,7 +126,7 @@ class RoomNotificationSettingsPresenter @AssistedInject constructor(
|
|||
|
||||
return RoomNotificationSettingsState(
|
||||
showUserDefinedSettingStyle = showUserDefinedSettingStyle,
|
||||
roomName = room.displayName,
|
||||
roomName = displayName.orEmpty(),
|
||||
roomNotificationSettings = roomNotificationSettings.value,
|
||||
pendingRoomNotificationMode = pendingRoomNotificationMode.value,
|
||||
pendingSetDefault = pendingSetDefault.value,
|
||||
|
|
@ -143,16 +156,18 @@ class RoomNotificationSettingsPresenter @AssistedInject constructor(
|
|||
roomNotificationSettings: MutableState<AsyncData<RoomNotificationSettings>>
|
||||
) = launch {
|
||||
suspend {
|
||||
val isEncrypted = room.info().isEncrypted ?: room.getUpdatedIsEncrypted().getOrThrow()
|
||||
pendingModeState.value = null
|
||||
notificationSettingsService.getRoomNotificationSettings(room.roomId, room.isEncrypted, room.isOneToOne).getOrThrow()
|
||||
notificationSettingsService.getRoomNotificationSettings(room.roomId, isEncrypted, room.isOneToOne).getOrThrow()
|
||||
}.runCatchingUpdatingState(roomNotificationSettings)
|
||||
}
|
||||
|
||||
private fun CoroutineScope.getDefaultRoomNotificationMode(
|
||||
defaultRoomNotificationMode: MutableState<RoomNotificationMode?>
|
||||
) = launch {
|
||||
val isEncrypted = room.info().isEncrypted ?: room.getUpdatedIsEncrypted().getOrThrow()
|
||||
defaultRoomNotificationMode.value = notificationSettingsService.getDefaultRoomNotificationMode(
|
||||
room.isEncrypted,
|
||||
isEncrypted,
|
||||
room.isOneToOne
|
||||
).getOrThrow()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ class RolesAndPermissionsPresenter @Inject constructor(
|
|||
@Composable
|
||||
override fun present(): RolesAndPermissionsState {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val roomInfo by room.roomInfoFlow.collectAsState(initial = null)
|
||||
val roomInfo by room.roomInfoFlow.collectAsState()
|
||||
val roomMembers by room.membersStateFlow.collectAsState()
|
||||
// Get the list of active room members (joined or invited), in order to filter members present in the power
|
||||
// level state Event.
|
||||
|
|
@ -109,8 +109,8 @@ class RolesAndPermissionsPresenter @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun MatrixRoomInfo?.userCountWithRole(userIds: List<UserId>, role: RoomMember.Role): Int {
|
||||
return this?.userPowerLevels.orEmpty().count { (userId, level) ->
|
||||
private fun MatrixRoomInfo.userCountWithRole(userIds: List<UserId>, role: RoomMember.Role): Int {
|
||||
return this.userPowerLevels.count { (userId, level) ->
|
||||
RoomMember.Role.forPowerLevel(level) == role && userId in userIds
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,10 +106,10 @@ class ChangeRolesPresenter @AssistedInject constructor(
|
|||
|
||||
val hasPendingChanges = usersWithRole.value != selectedUsers.value
|
||||
|
||||
val roomInfo by room.roomInfoFlow.collectAsState(initial = null)
|
||||
val roomInfo by room.roomInfoFlow.collectAsState()
|
||||
fun canChangeMemberRole(userId: UserId): Boolean {
|
||||
// An admin can't remove or demote another admin
|
||||
val powerLevel = roomInfo?.userPowerLevels?.get(userId) ?: 0L
|
||||
val powerLevel = roomInfo.userPowerLevels[userId] ?: 0L
|
||||
return RoomMember.Role.forPowerLevel(powerLevel) != RoomMember.Role.ADMIN
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ class SecurityAndPrivacyPresenter @AssistedInject constructor(
|
|||
val saveAction = remember { mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized) }
|
||||
val homeserverName = remember { matrixClient.userIdServerName() }
|
||||
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
||||
val roomInfo = room.roomInfoFlow.collectAsState(null)
|
||||
val roomInfo by room.roomInfoFlow.collectAsState()
|
||||
|
||||
val savedIsVisibleInRoomDirectory = remember { mutableStateOf<AsyncData<Boolean>>(AsyncData.Uninitialized) }
|
||||
LaunchedEffect(Unit) {
|
||||
|
|
@ -66,12 +66,13 @@ class SecurityAndPrivacyPresenter @AssistedInject constructor(
|
|||
|
||||
val savedSettings by remember {
|
||||
derivedStateOf {
|
||||
val historyVisibility = roomInfo.historyVisibility.map()
|
||||
SecurityAndPrivacySettings(
|
||||
roomAccess = roomInfo.value?.joinRule.map(),
|
||||
isEncrypted = room.isEncrypted,
|
||||
roomAccess = roomInfo.joinRule.map(),
|
||||
isEncrypted = roomInfo.isEncrypted == true,
|
||||
isVisibleInRoomDirectory = savedIsVisibleInRoomDirectory.value,
|
||||
historyVisibility = roomInfo.value?.historyVisibility.map(),
|
||||
address = roomInfo.value?.firstDisplayableAlias(homeserverName)?.value,
|
||||
historyVisibility = historyVisibility,
|
||||
address = roomInfo.firstDisplayableAlias(homeserverName)?.value,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ package io.element.android.features.roomdetails.impl.securityandprivacy.editroom
|
|||
|
||||
import androidx.compose.runtime.Composable
|
||||
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
|
||||
|
|
@ -24,6 +26,7 @@ import io.element.android.libraries.architecture.runCatchingUpdatingState
|
|||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
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.alias.RoomAliasHelper
|
||||
import io.element.android.libraries.matrix.api.roomAliasFromName
|
||||
import io.element.android.libraries.matrix.ui.room.address.RoomAddressValidity
|
||||
|
|
@ -45,15 +48,16 @@ class EditRoomAddressPresenter @AssistedInject constructor(
|
|||
@Composable
|
||||
override fun present(): EditRoomAddressState {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val roomInfo by room.roomInfoFlow.collectAsState()
|
||||
val homeserverName = remember { client.userIdServerName() }
|
||||
val roomAddressValidity = remember {
|
||||
mutableStateOf<RoomAddressValidity>(RoomAddressValidity.Unknown)
|
||||
}
|
||||
val savedRoomAddress = remember { room.firstAliasMatching(homeserverName)?.addressName() }
|
||||
val savedRoomAddress by remember { derivedStateOf { roomInfo.firstAliasMatching(homeserverName)?.addressName() } }
|
||||
val saveAction = remember { mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized) }
|
||||
var newRoomAddress by remember {
|
||||
mutableStateOf(
|
||||
savedRoomAddress ?: roomAliasHelper.roomAliasNameFromRoomDisplayName(room.displayName)
|
||||
savedRoomAddress ?: roomAliasHelper.roomAliasNameFromRoomDisplayName(roomInfo.name.orEmpty())
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -97,8 +101,9 @@ class EditRoomAddressPresenter @AssistedInject constructor(
|
|||
newRoomAddress: String,
|
||||
) = launch {
|
||||
suspend {
|
||||
val savedCanonicalAlias = room.canonicalAlias
|
||||
val savedAliasFromHomeserver = room.firstAliasMatching(serverName)
|
||||
val roomInfo = room.info()
|
||||
val savedCanonicalAlias = roomInfo.canonicalAlias
|
||||
val savedAliasFromHomeserver = roomInfo.firstAliasMatching(serverName)
|
||||
val newRoomAlias = client.roomAliasFromName(newRoomAddress) ?: throw IllegalArgumentException("Invalid room address")
|
||||
|
||||
// First publish the new alias in the room directory
|
||||
|
|
@ -112,7 +117,7 @@ class EditRoomAddressPresenter @AssistedInject constructor(
|
|||
when {
|
||||
// Allow to update the canonical alias only if the saved canonical alias matches the homeserver or if there is no canonical alias
|
||||
savedCanonicalAlias == null || savedCanonicalAlias.matchesServer(serverName) -> {
|
||||
val newAlternativeAliases = room.alternativeAliases.filter { it != savedAliasFromHomeserver }
|
||||
val newAlternativeAliases = roomInfo.alternativeAliases.filter { it != savedAliasFromHomeserver }
|
||||
room.updateCanonicalAlias(newRoomAlias, newAlternativeAliases).getOrThrow()
|
||||
}
|
||||
// Otherwise, only update the alternative aliases and keep the current canonical alias
|
||||
|
|
@ -121,7 +126,7 @@ class EditRoomAddressPresenter @AssistedInject constructor(
|
|||
// New alias is added first, so we make sure we pick it first
|
||||
add(newRoomAlias)
|
||||
// Add all other aliases, except the one we just removed from the room directory
|
||||
addAll(room.alternativeAliases.filter { it != savedAliasFromHomeserver })
|
||||
addAll(roomInfo.alternativeAliases.filter { it != savedAliasFromHomeserver })
|
||||
}
|
||||
room.updateCanonicalAlias(savedCanonicalAlias, newAlternativeAliases).getOrThrow()
|
||||
}
|
||||
|
|
@ -134,7 +139,7 @@ class EditRoomAddressPresenter @AssistedInject constructor(
|
|||
/**
|
||||
* Returns the first alias that matches the given server name, or null if none match.
|
||||
*/
|
||||
private fun MatrixRoom.firstAliasMatching(serverName: String): RoomAlias? {
|
||||
private fun MatrixRoomInfo.firstAliasMatching(serverName: String): RoomAlias? {
|
||||
// Check if the canonical alias matches the homeserver
|
||||
if (canonicalAlias?.matchesServer(serverName) == true) {
|
||||
return canonicalAlias
|
||||
|
|
|
|||
|
|
@ -7,34 +7,43 @@
|
|||
|
||||
package io.element.android.features.roomdetails.impl
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.StateEventType
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ALIAS
|
||||
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_ROOM_TOPIC
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
||||
fun aMatrixRoom(
|
||||
sessionId: SessionId = A_SESSION_ID,
|
||||
roomId: RoomId = A_ROOM_ID,
|
||||
displayName: String = A_ROOM_NAME,
|
||||
rawName: String? = displayName,
|
||||
topic: String? = A_ROOM_TOPIC,
|
||||
avatarUrl: String? = AN_AVATAR_URL,
|
||||
canonicalAlias: RoomAlias? = A_ROOM_ALIAS,
|
||||
isEncrypted: Boolean = true,
|
||||
isPublic: Boolean = true,
|
||||
isDirect: Boolean = false,
|
||||
joinRule: JoinRule? = null,
|
||||
activeMemberCount: Long = 1,
|
||||
joinedMemberCount: Long = 1,
|
||||
invitedMemberCount: Long = 0,
|
||||
notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(),
|
||||
emitRoomInfo: Boolean = false,
|
||||
canInviteResult: (UserId) -> Result<Boolean> = { lambdaError() },
|
||||
canBanResult: (UserId) -> Result<Boolean> = { lambdaError() },
|
||||
canKickResult: (UserId) -> Result<Boolean> = { lambdaError() },
|
||||
canSendStateResult: (UserId, StateEventType) -> Result<Boolean> = { _, _ -> lambdaError() },
|
||||
userDisplayNameResult: (UserId) -> Result<String?> = { lambdaError() },
|
||||
userAvatarUrlResult: () -> Result<String?> = { lambdaError() },
|
||||
|
|
@ -44,17 +53,20 @@ fun aMatrixRoom(
|
|||
removeAvatarResult: () -> Result<Unit> = { lambdaError() },
|
||||
canUserJoinCallResult: (UserId) -> Result<Boolean> = { lambdaError() },
|
||||
getUpdatedMemberResult: (UserId) -> Result<RoomMember> = { lambdaError() },
|
||||
userRoleResult: () -> Result<RoomMember.Role> = { lambdaError() },
|
||||
kickUserResult: (UserId, String?) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
banUserResult: (UserId, String?) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
unBanUserResult: (UserId, String?) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
updateCanonicalAliasResult: (RoomAlias?, List<RoomAlias>) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
publishRoomAliasInRoomDirectoryResult: (RoomAlias) -> Result<Boolean> = { lambdaError() },
|
||||
removeRoomAliasFromRoomDirectoryResult: (RoomAlias) -> Result<Boolean> = { lambdaError() },
|
||||
) = FakeMatrixRoom(
|
||||
sessionId = sessionId,
|
||||
roomId = roomId,
|
||||
displayName = displayName,
|
||||
topic = topic,
|
||||
avatarUrl = avatarUrl,
|
||||
isEncrypted = isEncrypted,
|
||||
isPublic = isPublic,
|
||||
isDirect = isDirect,
|
||||
notificationSettingsService = notificationSettingsService,
|
||||
canInviteResult = canInviteResult,
|
||||
canBanResult = canBanResult,
|
||||
canKickResult = canKickResult,
|
||||
canSendStateResult = canSendStateResult,
|
||||
userDisplayNameResult = userDisplayNameResult,
|
||||
userAvatarUrlResult = userAvatarUrlResult,
|
||||
|
|
@ -64,17 +76,25 @@ fun aMatrixRoom(
|
|||
removeAvatarResult = removeAvatarResult,
|
||||
canUserJoinCallResult = canUserJoinCallResult,
|
||||
getUpdatedMemberResult = getUpdatedMemberResult,
|
||||
).apply {
|
||||
if (emitRoomInfo) {
|
||||
givenRoomInfo(
|
||||
aRoomInfo(
|
||||
name = displayName,
|
||||
rawName = rawName,
|
||||
topic = topic,
|
||||
avatarUrl = avatarUrl,
|
||||
isDirect = isDirect,
|
||||
joinRule = joinRule,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
userRoleResult = userRoleResult,
|
||||
kickUserResult = kickUserResult,
|
||||
banUserResult = banUserResult,
|
||||
unBanUserResult = unBanUserResult,
|
||||
updateCanonicalAliasResult = updateCanonicalAliasResult,
|
||||
publishRoomAliasInRoomDirectoryResult = publishRoomAliasInRoomDirectoryResult,
|
||||
removeRoomAliasFromRoomDirectoryResult = removeRoomAliasFromRoomDirectoryResult,
|
||||
initialRoomInfo = aRoomInfo(
|
||||
name = displayName,
|
||||
rawName = rawName,
|
||||
topic = topic,
|
||||
avatarUrl = avatarUrl,
|
||||
canonicalAlias = canonicalAlias,
|
||||
isDirect = isDirect,
|
||||
isPublic = isPublic,
|
||||
isEncrypted = isEncrypted,
|
||||
joinRule = joinRule,
|
||||
joinedMembersCount = joinedMemberCount,
|
||||
activeMembersCount = activeMemberCount,
|
||||
invitedMembersCount = invitedMemberCount,
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ class RoomDetailsPresenterTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `present - initial state is created from room if roomInfo is null`() = runTest {
|
||||
fun `present - initial state is created from initial room info`() = runTest {
|
||||
val room = aMatrixRoom(
|
||||
canInviteResult = { Result.success(true) },
|
||||
canUserJoinCallResult = { Result.success(true) },
|
||||
|
|
@ -118,22 +118,22 @@ class RoomDetailsPresenterTest {
|
|||
)
|
||||
val presenter = createRoomDetailsPresenter(room)
|
||||
presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) {
|
||||
skipItems(1)
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.roomId).isEqualTo(room.roomId)
|
||||
assertThat(initialState.roomName).isEqualTo(room.displayName)
|
||||
assertThat(initialState.roomAvatarUrl).isEqualTo(room.avatarUrl)
|
||||
assertThat(initialState.roomTopic).isEqualTo(RoomTopicState.ExistingTopic(room.topic!!))
|
||||
assertThat(initialState.memberCount).isEqualTo(room.joinedMemberCount)
|
||||
assertThat(initialState.isEncrypted).isEqualTo(room.isEncrypted)
|
||||
assertThat(initialState.roomName).isEqualTo(room.info().name)
|
||||
assertThat(initialState.roomAvatarUrl).isEqualTo(room.info().avatarUrl)
|
||||
assertThat(initialState.roomTopic).isEqualTo(RoomTopicState.ExistingTopic(room.info().topic!!))
|
||||
assertThat(initialState.memberCount).isEqualTo(room.info().joinedMembersCount)
|
||||
assertThat(initialState.canShowPinnedMessages).isTrue()
|
||||
assertThat(initialState.pinnedMessagesCount).isNull()
|
||||
assertThat(initialState.pinnedMessagesCount).isEqualTo(0)
|
||||
assertThat(initialState.canShowSecurityAndPrivacy).isFalse()
|
||||
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - initial state is updated with roomInfo if it exists`() = runTest {
|
||||
fun `present - initial state is updated with a new roomInfo`() = runTest {
|
||||
val roomInfo = aRoomInfo(
|
||||
name = A_ROOM_NAME,
|
||||
topic = A_ROOM_TOPIC,
|
||||
|
|
@ -170,7 +170,7 @@ class RoomDetailsPresenterTest {
|
|||
val presenter = createRoomDetailsPresenter(room)
|
||||
presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.roomName).isEqualTo(room.displayName)
|
||||
assertThat(initialState.roomName).isEqualTo(room.info().name)
|
||||
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
|
|
@ -181,8 +181,6 @@ class RoomDetailsPresenterTest {
|
|||
val myRoomMember = aRoomMember(A_SESSION_ID)
|
||||
val otherRoomMember = aRoomMember(A_USER_ID_2)
|
||||
val room = aMatrixRoom(
|
||||
isEncrypted = true,
|
||||
isDirect = true,
|
||||
canInviteResult = { Result.success(true) },
|
||||
canUserJoinCallResult = { Result.success(true) },
|
||||
canSendStateResult = { _, _ -> Result.success(true) },
|
||||
|
|
@ -196,6 +194,13 @@ class RoomDetailsPresenterTest {
|
|||
).apply {
|
||||
val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
|
||||
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
|
||||
|
||||
givenRoomInfo(
|
||||
aRoomInfo(
|
||||
isEncrypted = true,
|
||||
isDirect = true,
|
||||
)
|
||||
)
|
||||
}
|
||||
val presenter = createRoomDetailsPresenter(room)
|
||||
presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) {
|
||||
|
|
@ -287,8 +292,6 @@ class RoomDetailsPresenterTest {
|
|||
val myRoomMember = aRoomMember(A_SESSION_ID)
|
||||
val otherRoomMember = aRoomMember(A_USER_ID_2)
|
||||
val room = aMatrixRoom(
|
||||
isEncrypted = true,
|
||||
isDirect = true,
|
||||
canSendStateResult = { _, stateEventType ->
|
||||
when (stateEventType) {
|
||||
StateEventType.ROOM_TOPIC,
|
||||
|
|
@ -309,6 +312,13 @@ class RoomDetailsPresenterTest {
|
|||
).apply {
|
||||
val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
|
||||
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
|
||||
|
||||
givenRoomInfo(
|
||||
aRoomInfo(
|
||||
isEncrypted = true,
|
||||
isDirect = true,
|
||||
)
|
||||
)
|
||||
}
|
||||
val presenter = createRoomDetailsPresenter(room)
|
||||
presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) {
|
||||
|
|
@ -318,7 +328,7 @@ class RoomDetailsPresenterTest {
|
|||
val settledState = awaitItem()
|
||||
assertThat(settledState.canEdit).isFalse()
|
||||
// If there is a topic, it's visible
|
||||
assertThat(settledState.roomTopic).isEqualTo(RoomTopicState.ExistingTopic(room.topic!!))
|
||||
assertThat(settledState.roomTopic).isEqualTo(RoomTopicState.ExistingTopic(room.info().topic!!))
|
||||
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
|
|
@ -329,7 +339,6 @@ class RoomDetailsPresenterTest {
|
|||
val myRoomMember = aRoomMember(A_SESSION_ID)
|
||||
val otherRoomMember = aRoomMember(A_USER_ID_2)
|
||||
val room = aMatrixRoom(
|
||||
isEncrypted = true,
|
||||
isDirect = true,
|
||||
topic = null,
|
||||
canSendStateResult = { _, stateEventType ->
|
||||
|
|
@ -352,6 +361,14 @@ class RoomDetailsPresenterTest {
|
|||
).apply {
|
||||
val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
|
||||
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
|
||||
|
||||
givenRoomInfo(
|
||||
aRoomInfo(
|
||||
isDirect = true,
|
||||
activeMembersCount = 2,
|
||||
topic = null,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val presenter = createRoomDetailsPresenter(room)
|
||||
|
|
@ -630,7 +647,6 @@ class RoomDetailsPresenterTest {
|
|||
@Test
|
||||
fun `present - show knock requests`() = runTest {
|
||||
val room = aMatrixRoom(
|
||||
emitRoomInfo = true,
|
||||
canInviteResult = { Result.success(true) },
|
||||
canUserJoinCallResult = { Result.success(true) },
|
||||
canSendStateResult = { _, _ -> Result.success(true) },
|
||||
|
|
|
|||
|
|
@ -93,7 +93,6 @@ class RoomDetailsEditPresenterTest {
|
|||
avatarUrl = AN_AVATAR_URL,
|
||||
displayName = A_ROOM_NAME,
|
||||
rawName = A_ROOM_RAW_NAME,
|
||||
emitRoomInfo = true,
|
||||
canSendStateResult = { _, _ -> Result.success(true) }
|
||||
)
|
||||
val deleteCallback = lambdaRecorder<Uri?, Unit> {}
|
||||
|
|
@ -106,7 +105,7 @@ class RoomDetailsEditPresenterTest {
|
|||
assertThat(initialState.roomId).isEqualTo(room.roomId)
|
||||
assertThat(initialState.roomRawName).isEqualTo(A_ROOM_RAW_NAME)
|
||||
assertThat(initialState.roomAvatarUrl).isEqualTo(roomAvatarUri)
|
||||
assertThat(initialState.roomTopic).isEqualTo(room.topic.orEmpty())
|
||||
assertThat(initialState.roomTopic).isEqualTo(room.info().topic.orEmpty())
|
||||
assertThat(initialState.avatarActions).containsExactly(
|
||||
AvatarAction.ChoosePhoto,
|
||||
AvatarAction.TakePhoto,
|
||||
|
|
@ -220,7 +219,6 @@ class RoomDetailsEditPresenterTest {
|
|||
topic = "My topic",
|
||||
displayName = "Name",
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
emitRoomInfo = true,
|
||||
canSendStateResult = { _, _ -> Result.success(true) }
|
||||
)
|
||||
val deleteCallback = lambdaRecorder<Uri?, Unit> {}
|
||||
|
|
@ -266,7 +264,6 @@ class RoomDetailsEditPresenterTest {
|
|||
topic = "My topic",
|
||||
displayName = "Name",
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
emitRoomInfo = true,
|
||||
canSendStateResult = { _, _ -> Result.success(true) }
|
||||
)
|
||||
fakePickerProvider.givenResult(anotherAvatarUri)
|
||||
|
|
@ -291,7 +288,6 @@ class RoomDetailsEditPresenterTest {
|
|||
topic = "My topic",
|
||||
displayName = "Name",
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
emitRoomInfo = true,
|
||||
canSendStateResult = { _, _ -> Result.success(true) }
|
||||
)
|
||||
fakePickerProvider.givenResult(anotherAvatarUri)
|
||||
|
|
@ -319,8 +315,7 @@ class RoomDetailsEditPresenterTest {
|
|||
stateWithNewAvatar.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.TakePhoto))
|
||||
val stateWithNewAvatar2 = awaitItem()
|
||||
assertThat(stateWithNewAvatar2.roomAvatarUrl).isEqualTo(roomAvatarUri)
|
||||
deleteCallback.assertions().isCalledExactly(4).withSequence(
|
||||
listOf(value(null)),
|
||||
deleteCallback.assertions().isCalledExactly(3).withSequence(
|
||||
listOf(value(null)),
|
||||
listOf(value(roomAvatarUri)),
|
||||
listOf(value(anotherAvatarUri)),
|
||||
|
|
@ -334,7 +329,6 @@ class RoomDetailsEditPresenterTest {
|
|||
topic = "My topic",
|
||||
displayName = "Name",
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
emitRoomInfo = true,
|
||||
canSendStateResult = { _, _ -> Result.success(true) }
|
||||
)
|
||||
fakePickerProvider.givenResult(roomAvatarUri)
|
||||
|
|
@ -385,7 +379,6 @@ class RoomDetailsEditPresenterTest {
|
|||
topic = null,
|
||||
displayName = "fallback",
|
||||
avatarUrl = null,
|
||||
emitRoomInfo = true,
|
||||
canSendStateResult = { _, _ -> Result.success(true) }
|
||||
)
|
||||
fakePickerProvider.givenResult(roomAvatarUri)
|
||||
|
|
@ -439,7 +432,6 @@ class RoomDetailsEditPresenterTest {
|
|||
topic = "My topic",
|
||||
displayName = "Name",
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
emitRoomInfo = true,
|
||||
setNameResult = setNameResult,
|
||||
setTopicResult = setTopicResult,
|
||||
removeAvatarResult = removeAvatarResult,
|
||||
|
|
@ -553,7 +545,7 @@ class RoomDetailsEditPresenterTest {
|
|||
updateAvatarResult.assertions().isCalledOnce().with(value(MimeTypes.Jpeg), value(fakeFileContents))
|
||||
deleteCallback.assertions().isCalledExactly(2).withSequence(
|
||||
listOf(value(null)),
|
||||
listOf(value(null)),
|
||||
listOf(value(roomAvatarUri)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -588,11 +580,10 @@ class RoomDetailsEditPresenterTest {
|
|||
topic = "My topic",
|
||||
displayName = "Name",
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
emitRoomInfo = true,
|
||||
setNameResult = { Result.failure(Throwable("!")) },
|
||||
canSendStateResult = { _, _ -> Result.success(true) }
|
||||
)
|
||||
saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomName("New name"))
|
||||
saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomName("New name"), deleteCallbackNumberOfInvocation = 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -601,11 +592,10 @@ class RoomDetailsEditPresenterTest {
|
|||
topic = "My topic",
|
||||
displayName = "Name",
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
emitRoomInfo = true,
|
||||
setTopicResult = { Result.failure(Throwable("!")) },
|
||||
canSendStateResult = { _, _ -> Result.success(true) }
|
||||
)
|
||||
saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomTopic("New topic"))
|
||||
saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomTopic("New topic"), deleteCallbackNumberOfInvocation = 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -614,11 +604,10 @@ class RoomDetailsEditPresenterTest {
|
|||
topic = "My topic",
|
||||
displayName = "Name",
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
emitRoomInfo = true,
|
||||
removeAvatarResult = { Result.failure(Throwable("!")) },
|
||||
canSendStateResult = { _, _ -> Result.success(true) }
|
||||
)
|
||||
saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove), deleteCallbackNumberOfInvocation = 3)
|
||||
saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove), deleteCallbackNumberOfInvocation = 2)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -628,11 +617,10 @@ class RoomDetailsEditPresenterTest {
|
|||
topic = "My topic",
|
||||
displayName = "Name",
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
emitRoomInfo = true,
|
||||
updateAvatarResult = { _, _ -> Result.failure(Throwable("!")) },
|
||||
canSendStateResult = { _, _ -> Result.success(true) }
|
||||
)
|
||||
saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto), deleteCallbackNumberOfInvocation = 3)
|
||||
saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto), deleteCallbackNumberOfInvocation = 2)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -704,6 +692,6 @@ class RoomDetailsEditPresenterTest {
|
|||
}
|
||||
|
||||
private suspend fun <T> ReceiveTurbine<T>.awaitFirstItem(): T {
|
||||
skipItems(2)
|
||||
skipItems(1)
|
||||
return awaitItem()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
|||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.consumeItemsUntilPredicate
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
|
|
@ -167,10 +168,10 @@ class RoomMemberDetailsPresenterTest {
|
|||
@Test
|
||||
fun `present - when user's identity is verified, the value in the state is VERIFIED`() = runTest {
|
||||
val room = FakeMatrixRoom(
|
||||
isEncrypted = true,
|
||||
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
||||
userDisplayNameResult = { Result.success("A custom name") },
|
||||
userAvatarUrlResult = { Result.success("A custom avatar") },
|
||||
initialRoomInfo = aRoomInfo(isEncrypted = true),
|
||||
)
|
||||
val encryptionService = FakeEncryptionService(
|
||||
getUserIdentityResult = { Result.success(IdentityState.Verified) },
|
||||
|
|
@ -186,10 +187,10 @@ class RoomMemberDetailsPresenterTest {
|
|||
@Test
|
||||
fun `present - when user's identity is unknown, the value in the state is UNKNOWN`() = runTest {
|
||||
val room = FakeMatrixRoom(
|
||||
isEncrypted = true,
|
||||
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
||||
userDisplayNameResult = { Result.success("A custom name") },
|
||||
userAvatarUrlResult = { Result.success("A custom avatar") },
|
||||
initialRoomInfo = aRoomInfo(isEncrypted = true),
|
||||
)
|
||||
val encryptionService = FakeEncryptionService(
|
||||
getUserIdentityResult = { Result.success(null) },
|
||||
|
|
@ -205,10 +206,10 @@ class RoomMemberDetailsPresenterTest {
|
|||
@Test
|
||||
fun `present - when user's identity is pinned, the value in the state is UNVERIFIED`() = runTest {
|
||||
val room = FakeMatrixRoom(
|
||||
isEncrypted = true,
|
||||
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
||||
userDisplayNameResult = { Result.success("A custom name") },
|
||||
userAvatarUrlResult = { Result.success("A custom avatar") },
|
||||
initialRoomInfo = aRoomInfo(isEncrypted = true),
|
||||
)
|
||||
val encryptionService = FakeEncryptionService(
|
||||
getUserIdentityResult = { Result.success(IdentityState.Pinned) },
|
||||
|
|
@ -224,10 +225,10 @@ class RoomMemberDetailsPresenterTest {
|
|||
@Test
|
||||
fun `present - when user's identity is pin violation, the value in the state is UNVERIFIED`() = runTest {
|
||||
val room = FakeMatrixRoom(
|
||||
isEncrypted = true,
|
||||
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
||||
userDisplayNameResult = { Result.success("A custom name") },
|
||||
userAvatarUrlResult = { Result.success("A custom avatar") },
|
||||
initialRoomInfo = aRoomInfo(isEncrypted = true),
|
||||
)
|
||||
val encryptionService = FakeEncryptionService(
|
||||
getUserIdentityResult = { Result.success(IdentityState.PinViolation) },
|
||||
|
|
@ -243,10 +244,10 @@ class RoomMemberDetailsPresenterTest {
|
|||
@Test
|
||||
fun `present - when user's identity has a verification violation, the value in the state is VERIFICATION_VIOLATION`() = runTest {
|
||||
val room = FakeMatrixRoom(
|
||||
isEncrypted = true,
|
||||
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
||||
userDisplayNameResult = { Result.success("A custom name") },
|
||||
userAvatarUrlResult = { Result.success("A custom avatar") },
|
||||
initialRoomInfo = aRoomInfo(isEncrypted = true),
|
||||
)
|
||||
val encryptionService = FakeEncryptionService(
|
||||
getUserIdentityResult = { Result.success(IdentityState.VerificationViolation) },
|
||||
|
|
@ -262,10 +263,10 @@ class RoomMemberDetailsPresenterTest {
|
|||
@Test
|
||||
fun `present - user identity updates in real time if the room is encrypted`() = runTest {
|
||||
val room = FakeMatrixRoom(
|
||||
isEncrypted = true,
|
||||
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
||||
userDisplayNameResult = { Result.success("A custom name") },
|
||||
userAvatarUrlResult = { Result.success("A custom avatar") },
|
||||
initialRoomInfo = aRoomInfo(isEncrypted = true),
|
||||
)
|
||||
val encryptionService = FakeEncryptionService(
|
||||
getUserIdentityResult = { Result.success(null) },
|
||||
|
|
@ -291,10 +292,10 @@ class RoomMemberDetailsPresenterTest {
|
|||
@Test
|
||||
fun `present - user identity can't update in real time if the room is not encrypted`() = runTest {
|
||||
val room = FakeMatrixRoom(
|
||||
isEncrypted = false,
|
||||
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
||||
userDisplayNameResult = { Result.success("A custom name") },
|
||||
userAvatarUrlResult = { Result.success("A custom avatar") },
|
||||
initialRoomInfo = aRoomInfo(isEncrypted = false),
|
||||
)
|
||||
val encryptionService = FakeEncryptionService(
|
||||
getUserIdentityResult = { Result.success(null) },
|
||||
|
|
@ -315,10 +316,10 @@ class RoomMemberDetailsPresenterTest {
|
|||
@Test
|
||||
fun `present - handles WithdrawVerification action`() = runTest {
|
||||
val room = FakeMatrixRoom(
|
||||
isEncrypted = true,
|
||||
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
||||
userDisplayNameResult = { Result.success("A custom name") },
|
||||
userAvatarUrlResult = { Result.success("A custom avatar") },
|
||||
initialRoomInfo = aRoomInfo(isEncrypted = true),
|
||||
)
|
||||
val withdrawVerificationResult = lambdaRecorder<UserId, Result<Unit>> { Result.success(Unit) }
|
||||
val encryptionService = FakeEncryptionService(
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import app.cash.molecule.moleculeFlow
|
|||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import im.vector.app.features.analytics.plan.RoomModeration
|
||||
import io.element.android.features.roomdetails.impl.aMatrixRoom
|
||||
import io.element.android.features.roomdetails.impl.members.aRoomMember
|
||||
import io.element.android.features.roomdetails.impl.members.aVictor
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
|
|
@ -34,8 +35,7 @@ import org.junit.Test
|
|||
class RoomMembersModerationPresenterTest {
|
||||
@Test
|
||||
fun `canDisplayModerationActions - when room is DM is false`() = runTest {
|
||||
val room = FakeMatrixRoom(
|
||||
isDirect = true,
|
||||
val room = aMatrixRoom(
|
||||
isPublic = true,
|
||||
activeMemberCount = 2,
|
||||
canKickResult = { Result.success(true) },
|
||||
|
|
@ -52,8 +52,7 @@ class RoomMembersModerationPresenterTest {
|
|||
|
||||
@Test
|
||||
fun `canDisplayModerationActions - when user can kick other users, FF is enabled and room is not a DM returns true`() = runTest {
|
||||
val room = FakeMatrixRoom(
|
||||
isDirect = false,
|
||||
val room = aMatrixRoom(
|
||||
activeMemberCount = 10,
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
|
|
@ -68,8 +67,7 @@ class RoomMembersModerationPresenterTest {
|
|||
|
||||
@Test
|
||||
fun `canDisplayModerationActions - when user can ban other users, FF is enabled and room is not a DM returns true`() = runTest {
|
||||
val room = FakeMatrixRoom(
|
||||
isDirect = false,
|
||||
val room = aMatrixRoom(
|
||||
activeMemberCount = 10,
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
|
|
@ -84,7 +82,7 @@ class RoomMembersModerationPresenterTest {
|
|||
|
||||
@Test
|
||||
fun `present - SelectRoomMember when the current user has permissions displays member actions`() = runTest {
|
||||
val room = FakeMatrixRoom(
|
||||
val room = aMatrixRoom(
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||
|
|
@ -110,7 +108,7 @@ class RoomMembersModerationPresenterTest {
|
|||
|
||||
@Test
|
||||
fun `present - SelectRoomMember displays only view profile if selected member has same power level as the current user`() = runTest {
|
||||
val room = FakeMatrixRoom(
|
||||
val room = aMatrixRoom(
|
||||
sessionId = A_USER_ID,
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
|
|
@ -136,7 +134,7 @@ class RoomMembersModerationPresenterTest {
|
|||
@Test
|
||||
fun `present - SelectRoomMember displays an unban confirmation dialog when the member is banned`() = runTest {
|
||||
val selectedMember = aRoomMember(A_USER_ID_2, membership = RoomMembershipState.BAN)
|
||||
val room = FakeMatrixRoom(
|
||||
val room = aMatrixRoom(
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||
|
|
@ -157,7 +155,7 @@ class RoomMembersModerationPresenterTest {
|
|||
@Test
|
||||
fun `present - Kick removes the user`() = runTest {
|
||||
val analyticsService = FakeAnalyticsService()
|
||||
val room = FakeMatrixRoom(
|
||||
val room = aMatrixRoom(
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||
|
|
@ -186,7 +184,7 @@ class RoomMembersModerationPresenterTest {
|
|||
@Test
|
||||
fun `present - BanUser requires confirmation and then bans the user`() = runTest {
|
||||
val analyticsService = FakeAnalyticsService()
|
||||
val room = FakeMatrixRoom(
|
||||
val room = aMatrixRoom(
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||
|
|
@ -222,7 +220,7 @@ class RoomMembersModerationPresenterTest {
|
|||
fun `present - UnbanUser requires confirmation and then unbans the user`() = runTest {
|
||||
val analyticsService = FakeAnalyticsService()
|
||||
val selectedMember = aRoomMember(A_USER_ID_2, membership = RoomMembershipState.BAN)
|
||||
val room = FakeMatrixRoom(
|
||||
val room = aMatrixRoom(
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||
|
|
@ -254,7 +252,7 @@ class RoomMembersModerationPresenterTest {
|
|||
|
||||
@Test
|
||||
fun `present - Reset removes the selected user and actions`() = runTest {
|
||||
val room = FakeMatrixRoom(
|
||||
val room = aMatrixRoom(
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.USER) },
|
||||
|
|
@ -276,7 +274,7 @@ class RoomMembersModerationPresenterTest {
|
|||
|
||||
@Test
|
||||
fun `present - Reset resets any async actions`() = runTest {
|
||||
val room = FakeMatrixRoom(
|
||||
val room = aMatrixRoom(
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
kickUserResult = { _, _ -> Result.failure(Throwable("Eek")) },
|
||||
|
|
@ -324,7 +322,7 @@ class RoomMembersModerationPresenterTest {
|
|||
}
|
||||
|
||||
private fun TestScope.createRoomMembersModerationPresenter(
|
||||
matrixRoom: FakeMatrixRoom = FakeMatrixRoom(),
|
||||
matrixRoom: FakeMatrixRoom = aMatrixRoom(),
|
||||
dispatchers: CoroutineDispatchers = testCoroutineDispatchers(),
|
||||
analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
|
||||
): RoomMembersModerationPresenter {
|
||||
|
|
|
|||
|
|
@ -56,17 +56,15 @@ class SecurityAndPrivacyPresenterTest {
|
|||
fun `present - room info change updates saved and edited settings`() = runTest {
|
||||
val room = FakeMatrixRoom(
|
||||
canSendStateResult = { _, _ -> Result.success(true) },
|
||||
initialRoomInfo = aRoomInfo(
|
||||
joinRule = JoinRule.Public,
|
||||
historyVisibility = RoomHistoryVisibility.WorldReadable,
|
||||
canonicalAlias = A_ROOM_ALIAS,
|
||||
)
|
||||
)
|
||||
val presenter = createSecurityAndPrivacyPresenter(room = room)
|
||||
presenter.test {
|
||||
skipItems(2)
|
||||
room.givenRoomInfo(
|
||||
aRoomInfo(
|
||||
joinRule = JoinRule.Public,
|
||||
historyVisibility = RoomHistoryVisibility.WorldReadable,
|
||||
canonicalAlias = A_ROOM_ALIAS,
|
||||
)
|
||||
)
|
||||
skipItems(1)
|
||||
with(awaitItem()) {
|
||||
assertThat(editedSettings).isEqualTo(savedSettings)
|
||||
assertThat(editedSettings.roomAccess).isEqualTo(SecurityAndPrivacyRoomAccess.Anyone)
|
||||
|
|
@ -151,6 +149,7 @@ class SecurityAndPrivacyPresenterTest {
|
|||
assertThat(canBeSaved).isTrue()
|
||||
eventSink(SecurityAndPrivacyEvents.ToggleEncryptionState)
|
||||
}
|
||||
skipItems(1)
|
||||
with(awaitItem()) {
|
||||
assertThat(editedSettings.isEncrypted).isFalse()
|
||||
assertThat(canBeSaved).isFalse()
|
||||
|
|
@ -162,7 +161,8 @@ class SecurityAndPrivacyPresenterTest {
|
|||
fun `present - room visibility loading and change`() = runTest {
|
||||
val room = FakeMatrixRoom(
|
||||
canSendStateResult = { _, _ -> Result.success(true) },
|
||||
roomVisibilityResult = { Result.success(RoomVisibility.Private) }
|
||||
roomVisibilityResult = { Result.success(RoomVisibility.Private) },
|
||||
initialRoomInfo = aRoomInfo(historyVisibility = RoomHistoryVisibility.Shared)
|
||||
)
|
||||
val presenter = createSecurityAndPrivacyPresenter(room = room)
|
||||
presenter.test {
|
||||
|
|
@ -212,7 +212,8 @@ class SecurityAndPrivacyPresenterTest {
|
|||
updateJoinRuleResult = updateJoinRuleLambda,
|
||||
updateRoomVisibilityResult = updateRoomVisibilityLambda,
|
||||
updateRoomHistoryVisibilityResult = updateRoomHistoryVisibilityLambda,
|
||||
roomVisibilityResult = { Result.success(RoomVisibility.Private) }
|
||||
roomVisibilityResult = { Result.success(RoomVisibility.Private) },
|
||||
initialRoomInfo = aRoomInfo(joinRule = JoinRule.Invite, historyVisibility = RoomHistoryVisibility.Shared)
|
||||
)
|
||||
val presenter = createSecurityAndPrivacyPresenter(room = room)
|
||||
presenter.test {
|
||||
|
|
@ -245,6 +246,7 @@ class SecurityAndPrivacyPresenterTest {
|
|||
aRoomInfo(
|
||||
joinRule = JoinRule.Public,
|
||||
historyVisibility = RoomHistoryVisibility.WorldReadable,
|
||||
isEncrypted = true,
|
||||
)
|
||||
)
|
||||
// Saved settings are updated 3 times to match the edited settings
|
||||
|
|
@ -275,7 +277,8 @@ class SecurityAndPrivacyPresenterTest {
|
|||
updateJoinRuleResult = updateJoinRuleLambda,
|
||||
updateRoomVisibilityResult = updateRoomVisibilityLambda,
|
||||
updateRoomHistoryVisibilityResult = updateRoomHistoryVisibilityLambda,
|
||||
roomVisibilityResult = { Result.success(RoomVisibility.Private) }
|
||||
roomVisibilityResult = { Result.success(RoomVisibility.Private) },
|
||||
initialRoomInfo = aRoomInfo(historyVisibility = RoomHistoryVisibility.Shared, joinRule = JoinRule.Private)
|
||||
)
|
||||
val presenter = createSecurityAndPrivacyPresenter(room = room)
|
||||
presenter.test {
|
||||
|
|
@ -311,7 +314,7 @@ class SecurityAndPrivacyPresenterTest {
|
|||
)
|
||||
)
|
||||
// Saved settings are updated 2 times to match the edited settings
|
||||
skipItems(2)
|
||||
skipItems(3)
|
||||
with(awaitItem()) {
|
||||
assertThat(saveAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
assertThat(savedSettings.isVisibleInRoomDirectory).isNotEqualTo(editedSettings.isVisibleInRoomDirectory)
|
||||
|
|
@ -328,7 +331,8 @@ class SecurityAndPrivacyPresenterTest {
|
|||
serverName: String = "matrix.org",
|
||||
room: MatrixRoom = FakeMatrixRoom(
|
||||
canSendStateResult = { _, _ -> Result.success(true) },
|
||||
roomVisibilityResult = { Result.success(RoomVisibility.Private) }
|
||||
roomVisibilityResult = { Result.success(RoomVisibility.Private) },
|
||||
initialRoomInfo = aRoomInfo(historyVisibility = RoomHistoryVisibility.Shared, joinRule = JoinRule.Private)
|
||||
),
|
||||
navigator: SecurityAndPrivacyNavigator = FakeSecurityAndPrivacyNavigator(),
|
||||
): SecurityAndPrivacyPresenter {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
package io.element.android.features.roomdetails.impl.securityandprivacy.editroomaddress
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.roomdetails.impl.aMatrixRoom
|
||||
import io.element.android.features.roomdetails.impl.securityandprivacy.FakeSecurityAndPrivacyNavigator
|
||||
import io.element.android.features.roomdetails.impl.securityandprivacy.SecurityAndPrivacyNavigator
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
|
|
@ -31,7 +32,9 @@ import java.util.Optional
|
|||
class EditRoomAddressPresenterTest {
|
||||
@Test
|
||||
fun `present - initial state no address`() = runTest {
|
||||
val presenter = createEditRoomAddressPresenter()
|
||||
val presenter = createEditRoomAddressPresenter(
|
||||
room = aMatrixRoom(displayName = "")
|
||||
)
|
||||
presenter.test {
|
||||
with(awaitItem()) {
|
||||
assertThat(homeserverName).isEqualTo("matrix.org")
|
||||
|
|
@ -45,7 +48,7 @@ class EditRoomAddressPresenterTest {
|
|||
|
||||
@Test
|
||||
fun `present - initial state address matching own homeserver`() = runTest {
|
||||
val room = FakeMatrixRoom(
|
||||
val room = aMatrixRoom(
|
||||
canonicalAlias = RoomAlias("#canonical:matrix.org"),
|
||||
)
|
||||
val presenter = createEditRoomAddressPresenter(room = room)
|
||||
|
|
@ -62,7 +65,8 @@ class EditRoomAddressPresenterTest {
|
|||
|
||||
@Test
|
||||
fun `present - initial state address not matching own homeserver`() = runTest {
|
||||
val room = FakeMatrixRoom(
|
||||
val room = aMatrixRoom(
|
||||
displayName = "",
|
||||
canonicalAlias = RoomAlias("#canonical:notmatrix.org"),
|
||||
)
|
||||
val presenter = createEditRoomAddressPresenter(room = room)
|
||||
|
|
@ -190,7 +194,7 @@ class EditRoomAddressPresenterTest {
|
|||
|
||||
val navigator = FakeSecurityAndPrivacyNavigator(closeEditRoomAddressLambda = closeEditAddressLambda)
|
||||
val canonicalAlias = RoomAlias("#canonical:matrix.org")
|
||||
val room = FakeMatrixRoom(
|
||||
val room = aMatrixRoom(
|
||||
canonicalAlias = canonicalAlias,
|
||||
updateCanonicalAliasResult = updateCanonicalAliasResult,
|
||||
publishRoomAliasInRoomDirectoryResult = publishAliasInRoomDirectoryResult,
|
||||
|
|
@ -240,7 +244,7 @@ class EditRoomAddressPresenterTest {
|
|||
|
||||
val navigator = FakeSecurityAndPrivacyNavigator(closeEditRoomAddressLambda = closeEditAddressLambda)
|
||||
val canonicalAlias = RoomAlias("#canonical:notmatrix.org")
|
||||
val room = FakeMatrixRoom(
|
||||
val room = aMatrixRoom(
|
||||
canonicalAlias = canonicalAlias,
|
||||
updateCanonicalAliasResult = updateCanonicalAliasResult,
|
||||
publishRoomAliasInRoomDirectoryResult = publishAliasInRoomDirectoryResult,
|
||||
|
|
@ -314,6 +318,7 @@ class EditRoomAddressPresenterTest {
|
|||
with(awaitItem()) {
|
||||
eventSink(EditRoomAddressEvents.Save)
|
||||
}
|
||||
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
with(awaitItem()) {
|
||||
assertThat(saveAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
eventSink(EditRoomAddressEvents.DismissError)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue