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
da9b751847
commit
1659572950
76 changed files with 647 additions and 431 deletions
|
|
@ -15,7 +15,7 @@ import io.element.android.x.di.AppComponent
|
||||||
import io.element.android.x.di.DaggerAppComponent
|
import io.element.android.x.di.DaggerAppComponent
|
||||||
import io.element.android.x.info.logApplicationInfo
|
import io.element.android.x.info.logApplicationInfo
|
||||||
import io.element.android.x.initializer.CrashInitializer
|
import io.element.android.x.initializer.CrashInitializer
|
||||||
import io.element.android.x.initializer.TracingInitializer
|
import io.element.android.x.initializer.PlatformInitializer
|
||||||
|
|
||||||
class ElementXApplication : Application(), DaggerComponentOwner {
|
class ElementXApplication : Application(), DaggerComponentOwner {
|
||||||
override val daggerComponent: AppComponent = DaggerAppComponent.factory().create(this)
|
override val daggerComponent: AppComponent = DaggerAppComponent.factory().create(this)
|
||||||
|
|
@ -24,7 +24,7 @@ class ElementXApplication : Application(), DaggerComponentOwner {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
AppInitializer.getInstance(this).apply {
|
AppInitializer.getInstance(this).apply {
|
||||||
initializeComponent(CrashInitializer::class.java)
|
initializeComponent(CrashInitializer::class.java)
|
||||||
initializeComponent(TracingInitializer::class.java)
|
initializeComponent(PlatformInitializer::class.java)
|
||||||
initializeComponent(CacheCleanerInitializer::class.java)
|
initializeComponent(CacheCleanerInitializer::class.java)
|
||||||
}
|
}
|
||||||
logApplicationInfo(this)
|
logApplicationInfo(this)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import io.element.android.libraries.core.meta.BuildMeta
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||||
import io.element.android.libraries.di.AppScope
|
import io.element.android.libraries.di.AppScope
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||||
|
import io.element.android.libraries.matrix.api.platform.InitPlatformService
|
||||||
import io.element.android.libraries.matrix.api.tracing.TracingService
|
import io.element.android.libraries.matrix.api.tracing.TracingService
|
||||||
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
||||||
import io.element.android.services.analytics.api.AnalyticsService
|
import io.element.android.services.analytics.api.AnalyticsService
|
||||||
|
|
@ -27,6 +28,8 @@ interface AppBindings {
|
||||||
|
|
||||||
fun tracingService(): TracingService
|
fun tracingService(): TracingService
|
||||||
|
|
||||||
|
fun platformService(): InitPlatformService
|
||||||
|
|
||||||
fun bugReporter(): BugReporter
|
fun bugReporter(): BugReporter
|
||||||
|
|
||||||
fun lockScreenService(): LockScreenService
|
fun lockScreenService(): LockScreenService
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,11 @@ import timber.log.Timber
|
||||||
|
|
||||||
private const val ELEMENT_X_TARGET = "elementx"
|
private const val ELEMENT_X_TARGET = "elementx"
|
||||||
|
|
||||||
class TracingInitializer : Initializer<Unit> {
|
class PlatformInitializer : Initializer<Unit> {
|
||||||
override fun create(context: Context) {
|
override fun create(context: Context) {
|
||||||
val appBindings = context.bindings<AppBindings>()
|
val appBindings = context.bindings<AppBindings>()
|
||||||
val tracingService = appBindings.tracingService()
|
val tracingService = appBindings.tracingService()
|
||||||
|
val platformService = appBindings.platformService()
|
||||||
val bugReporter = appBindings.bugReporter()
|
val bugReporter = appBindings.bugReporter()
|
||||||
Timber.plant(tracingService.createTimberTree(ELEMENT_X_TARGET))
|
Timber.plant(tracingService.createTimberTree(ELEMENT_X_TARGET))
|
||||||
val preferencesStore = appBindings.preferencesStore()
|
val preferencesStore = appBindings.preferencesStore()
|
||||||
|
|
@ -38,7 +39,7 @@ class TracingInitializer : Initializer<Unit> {
|
||||||
extraTargets = listOf(ELEMENT_X_TARGET),
|
extraTargets = listOf(ELEMENT_X_TARGET),
|
||||||
)
|
)
|
||||||
bugReporter.setCurrentTracingLogLevel(logLevel.name)
|
bugReporter.setCurrentTracingLogLevel(logLevel.name)
|
||||||
tracingService.setupTracing(tracingConfiguration)
|
platformService.init(tracingConfiguration)
|
||||||
// Also set env variable for rust back trace
|
// Also set env variable for rust back trace
|
||||||
Os.setenv("RUST_BACKTRACE", "1", true)
|
Os.setenv("RUST_BACKTRACE", "1", true)
|
||||||
}
|
}
|
||||||
|
|
@ -38,7 +38,8 @@ class DefaultCallWidgetProvider @Inject constructor(
|
||||||
val baseUrl = appPreferencesStore.getCustomElementCallBaseUrlFlow().firstOrNull()
|
val baseUrl = appPreferencesStore.getCustomElementCallBaseUrlFlow().firstOrNull()
|
||||||
?: elementCallBaseUrlProvider.provides(matrixClient)
|
?: elementCallBaseUrlProvider.provides(matrixClient)
|
||||||
?: ElementCallConfig.DEFAULT_BASE_URL
|
?: ElementCallConfig.DEFAULT_BASE_URL
|
||||||
val widgetSettings = callWidgetSettingsProvider.provide(baseUrl, encrypted = room.isEncrypted)
|
val isEncrypted = room.info().isEncrypted ?: room.getUpdatedIsEncrypted().getOrThrow()
|
||||||
|
val widgetSettings = callWidgetSettingsProvider.provide(baseUrl, encrypted = isEncrypted)
|
||||||
val callUrl = room.generateWidgetWebViewUrl(
|
val callUrl = room.generateWidgetWebViewUrl(
|
||||||
widgetSettings = widgetSettings,
|
widgetSettings = widgetSettings,
|
||||||
clientId = clientId,
|
clientId = clientId,
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.room.isDm
|
import io.element.android.libraries.matrix.api.room.isDm
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
@ -74,10 +75,11 @@ private suspend fun showLeaveRoomAlert(
|
||||||
confirmation: MutableState<LeaveRoomState.Confirmation>,
|
confirmation: MutableState<LeaveRoomState.Confirmation>,
|
||||||
) {
|
) {
|
||||||
matrixClient.getRoom(roomId)?.use { room ->
|
matrixClient.getRoom(roomId)?.use { room ->
|
||||||
|
val roomInfo = room.roomInfoFlow.first()
|
||||||
confirmation.value = when {
|
confirmation.value = when {
|
||||||
room.isDm -> Dm(roomId)
|
roomInfo.isDm -> Dm(roomId)
|
||||||
!room.isPublic -> PrivateRoom(roomId)
|
!roomInfo.isPublic -> PrivateRoom(roomId)
|
||||||
room.joinedMemberCount == 1L -> LastUserInRoom(roomId)
|
roomInfo.joinedMembersCount == 1L -> LastUserInRoom(roomId)
|
||||||
else -> Generic(roomId)
|
else -> Generic(roomId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
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.WarmUpRule
|
||||||
import io.element.android.tests.testutils.lambda.assert
|
import io.element.android.tests.testutils.lambda.assert
|
||||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
|
|
@ -52,7 +53,9 @@ class LeaveRoomPresenterTest {
|
||||||
client = FakeMatrixClient().apply {
|
client = FakeMatrixClient().apply {
|
||||||
givenGetRoomResult(
|
givenGetRoomResult(
|
||||||
roomId = A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
result = FakeMatrixRoom()
|
result = FakeMatrixRoom().apply {
|
||||||
|
givenRoomInfo(aRoomInfo(isDirect = false, isPublic = true, joinedMembersCount = 10))
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -72,7 +75,9 @@ class LeaveRoomPresenterTest {
|
||||||
client = FakeMatrixClient().apply {
|
client = FakeMatrixClient().apply {
|
||||||
givenGetRoomResult(
|
givenGetRoomResult(
|
||||||
roomId = A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
result = FakeMatrixRoom(isPublic = false),
|
result = FakeMatrixRoom().apply {
|
||||||
|
givenRoomInfo(aRoomInfo(isPublic = false))
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -92,7 +97,9 @@ class LeaveRoomPresenterTest {
|
||||||
client = FakeMatrixClient().apply {
|
client = FakeMatrixClient().apply {
|
||||||
givenGetRoomResult(
|
givenGetRoomResult(
|
||||||
roomId = A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
result = FakeMatrixRoom(joinedMemberCount = 1),
|
result = FakeMatrixRoom().apply {
|
||||||
|
givenRoomInfo(aRoomInfo(joinedMembersCount = 1))
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -112,7 +119,9 @@ class LeaveRoomPresenterTest {
|
||||||
client = FakeMatrixClient().apply {
|
client = FakeMatrixClient().apply {
|
||||||
givenGetRoomResult(
|
givenGetRoomResult(
|
||||||
roomId = A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
result = FakeMatrixRoom(activeMemberCount = 2, isDirect = true),
|
result = FakeMatrixRoom().apply {
|
||||||
|
givenRoomInfo(aRoomInfo(isDirect = true, activeMembersCount = 2))
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ class ConfirmAccountProviderPresenter @AssistedInject constructor(
|
||||||
val matrixHomeServerDetails = authenticationService.getHomeserverDetails().value!!
|
val matrixHomeServerDetails = authenticationService.getHomeserverDetails().value!!
|
||||||
if (matrixHomeServerDetails.supportsOidcLogin) {
|
if (matrixHomeServerDetails.supportsOidcLogin) {
|
||||||
// Retrieve the details right now
|
// Retrieve the details right now
|
||||||
val oidcPrompt = if (params.isAccountCreation) OidcPrompt.Create else OidcPrompt.Consent
|
val oidcPrompt = if (params.isAccountCreation) OidcPrompt.Create else OidcPrompt.Login
|
||||||
LoginFlow.OidcFlow(authenticationService.getOidcUrl(oidcPrompt).getOrThrow())
|
LoginFlow.OidcFlow(authenticationService.getOidcUrl(oidcPrompt).getOrThrow())
|
||||||
} else if (params.isAccountCreation) {
|
} else if (params.isAccountCreation) {
|
||||||
val url = webClientUrlForAuthenticationRetriever.retrieve(homeserverUrl)
|
val url = webClientUrlForAuthenticationRetriever.retrieve(homeserverUrl)
|
||||||
|
|
|
||||||
|
|
@ -59,11 +59,14 @@ import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugIn
|
||||||
import io.element.android.libraries.mediaplayer.api.MediaPlayer
|
import io.element.android.libraries.mediaplayer.api.MediaPlayer
|
||||||
import io.element.android.services.analytics.api.AnalyticsService
|
import io.element.android.services.analytics.api.AnalyticsService
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@ContributesNode(RoomScope::class)
|
@ContributesNode(RoomScope::class)
|
||||||
class MessagesNode @AssistedInject constructor(
|
class MessagesNode @AssistedInject constructor(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
private val coroutineScope: CoroutineScope,
|
||||||
private val room: MatrixRoom,
|
private val room: MatrixRoom,
|
||||||
private val analyticsService: AnalyticsService,
|
private val analyticsService: AnalyticsService,
|
||||||
messageComposerPresenterFactory: MessageComposerPresenter.Factory,
|
messageComposerPresenterFactory: MessageComposerPresenter.Factory,
|
||||||
|
|
@ -108,7 +111,7 @@ class MessagesNode @AssistedInject constructor(
|
||||||
super.onBuilt()
|
super.onBuilt()
|
||||||
lifecycle.subscribe(
|
lifecycle.subscribe(
|
||||||
onCreate = {
|
onCreate = {
|
||||||
analyticsService.capture(room.toAnalyticsViewRoom())
|
coroutineScope.launch { analyticsService.capture(room.toAnalyticsViewRoom()) }
|
||||||
},
|
},
|
||||||
onDestroy = {
|
onDestroy = {
|
||||||
mediaPlayer.close()
|
mediaPlayer.close()
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ class MessagesPresenter @AssistedInject constructor(
|
||||||
override fun present(): MessagesState {
|
override fun present(): MessagesState {
|
||||||
htmlConverterProvider.Update(currentUserId = room.sessionId)
|
htmlConverterProvider.Update(currentUserId = room.sessionId)
|
||||||
|
|
||||||
val roomInfo by room.roomInfoFlow.collectAsState(null)
|
val roomInfo by room.roomInfoFlow.collectAsState()
|
||||||
val localCoroutineScope = rememberCoroutineScope()
|
val localCoroutineScope = rememberCoroutineScope()
|
||||||
val composerState = composerPresenter.present()
|
val composerState = composerPresenter.present()
|
||||||
val voiceMessageComposerState = voiceMessageComposerPresenter.present()
|
val voiceMessageComposerState = voiceMessageComposerPresenter.present()
|
||||||
|
|
@ -147,13 +147,13 @@ class MessagesPresenter @AssistedInject constructor(
|
||||||
val userEventPermissions by userEventPermissions(syncUpdateFlow.value)
|
val userEventPermissions by userEventPermissions(syncUpdateFlow.value)
|
||||||
|
|
||||||
val roomName: AsyncData<String> by remember {
|
val roomName: AsyncData<String> by remember {
|
||||||
derivedStateOf { roomInfo?.name?.let { AsyncData.Success(it) } ?: AsyncData.Uninitialized }
|
derivedStateOf { roomInfo.name?.let { AsyncData.Success(it) } ?: AsyncData.Uninitialized }
|
||||||
}
|
}
|
||||||
val roomAvatar: AsyncData<AvatarData> by remember {
|
val roomAvatar: AsyncData<AvatarData> by remember {
|
||||||
derivedStateOf { roomInfo?.avatarData()?.let { AsyncData.Success(it) } ?: AsyncData.Uninitialized }
|
derivedStateOf { AsyncData.Success(roomInfo.avatarData()) }
|
||||||
}
|
}
|
||||||
val heroes by remember {
|
val heroes by remember {
|
||||||
derivedStateOf { roomInfo?.heroes().orEmpty().toPersistentList() }
|
derivedStateOf { roomInfo.heroes().toPersistentList() }
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasDismissedInviteDialog by rememberSaveable {
|
var hasDismissedInviteDialog by rememberSaveable {
|
||||||
|
|
@ -164,14 +164,20 @@ class MessagesPresenter @AssistedInject constructor(
|
||||||
// as those will be handled by the timeline.
|
// as those will be handled by the timeline.
|
||||||
withContext(dispatchers.io) {
|
withContext(dispatchers.io) {
|
||||||
room.setUnreadFlag(isUnread = false)
|
room.setUnreadFlag(isUnread = false)
|
||||||
|
|
||||||
|
// If for some reason the encryption state is unknown, fetch it
|
||||||
|
if (roomInfo.isEncrypted == null) {
|
||||||
|
room.getUpdatedIsEncrypted()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val inviteProgress = remember { mutableStateOf<AsyncData<Unit>>(AsyncData.Uninitialized) }
|
val inviteProgress = remember { mutableStateOf<AsyncData<Unit>>(AsyncData.Uninitialized) }
|
||||||
var showReinvitePrompt by remember { mutableStateOf(false) }
|
var showReinvitePrompt by remember { mutableStateOf(false) }
|
||||||
LaunchedEffect(hasDismissedInviteDialog, composerState.textEditorState.hasFocus(), syncUpdateFlow.value) {
|
val composerHasFocus by remember { derivedStateOf { composerState.textEditorState.hasFocus() } }
|
||||||
|
LaunchedEffect(hasDismissedInviteDialog, composerHasFocus, roomInfo) {
|
||||||
withContext(dispatchers.io) {
|
withContext(dispatchers.io) {
|
||||||
showReinvitePrompt = !hasDismissedInviteDialog && composerState.textEditorState.hasFocus() && room.isDm && room.activeMemberCount == 1L
|
showReinvitePrompt = !hasDismissedInviteDialog && composerHasFocus && roomInfo.isDm && roomInfo.activeMembersCount == 1L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val isOnline by syncService.isOnline().collectAsState()
|
val isOnline by syncService.isOnline().collectAsState()
|
||||||
|
|
@ -189,9 +195,8 @@ class MessagesPresenter @AssistedInject constructor(
|
||||||
val dmRoomMember by room.getDirectRoomMember(membersState)
|
val dmRoomMember by room.getDirectRoomMember(membersState)
|
||||||
val roomMemberIdentityStateChanges = identityChangeState.roomMemberIdentityStateChanges
|
val roomMemberIdentityStateChanges = identityChangeState.roomMemberIdentityStateChanges
|
||||||
|
|
||||||
// TODO use `RoomInfo.isEncrypted` as a key here once it's available
|
LifecycleResumeEffect(dmRoomMember, roomInfo.isEncrypted) {
|
||||||
LifecycleResumeEffect(dmRoomMember, roomMemberIdentityStateChanges) {
|
if (roomInfo.isEncrypted == true) {
|
||||||
if (room.isEncrypted) {
|
|
||||||
val dmRoomMemberId = dmRoomMember?.userId
|
val dmRoomMemberId = dmRoomMember?.userId
|
||||||
localCoroutineScope.launch {
|
localCoroutineScope.launch {
|
||||||
dmRoomMemberId?.let { userId ->
|
dmRoomMemberId?.let { userId ->
|
||||||
|
|
@ -275,7 +280,7 @@ class MessagesPresenter @AssistedInject constructor(
|
||||||
return AvatarData(
|
return AvatarData(
|
||||||
id = id.value,
|
id = id.value,
|
||||||
name = name,
|
name = name,
|
||||||
url = avatarUrl ?: room.avatarUrl,
|
url = avatarUrl ?: room.info().avatarUrl,
|
||||||
size = AvatarSize.TimelineRoom
|
size = AvatarSize.TimelineRoom
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -202,7 +202,7 @@ class MessageComposerPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
suspend fun canSendRoomMention(): Boolean {
|
suspend fun canSendRoomMention(): Boolean {
|
||||||
val userCanSendAtRoom = room.canUserTriggerRoomNotification(currentUserId).getOrDefault(false)
|
val userCanSendAtRoom = room.canUserTriggerRoomNotification(currentUserId).getOrDefault(false)
|
||||||
return !room.isDm && userCanSendAtRoom
|
return !room.isDm() && userCanSendAtRoom
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will trigger a search immediately when `@` is typed
|
// This will trigger a search immediately when `@` is typed
|
||||||
|
|
|
||||||
|
|
@ -38,11 +38,11 @@ import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
import io.element.android.libraries.matrix.api.room.isDm
|
|
||||||
import io.element.android.libraries.matrix.api.room.powerlevels.canPinUnpin
|
import io.element.android.libraries.matrix.api.room.powerlevels.canPinUnpin
|
||||||
import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOther
|
import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOther
|
||||||
import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOwn
|
import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOwn
|
||||||
import io.element.android.libraries.matrix.api.room.roomMembers
|
import io.element.android.libraries.matrix.api.room.roomMembers
|
||||||
|
import io.element.android.libraries.matrix.ui.room.isDmAsState
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
import io.element.android.services.analytics.api.AnalyticsService
|
import io.element.android.services.analytics.api.AnalyticsService
|
||||||
import io.element.android.services.analyticsproviders.api.trackers.captureInteraction
|
import io.element.android.services.analyticsproviders.api.trackers.captureInteraction
|
||||||
|
|
@ -85,10 +85,12 @@ class PinnedMessagesListPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): PinnedMessagesListState {
|
override fun present(): PinnedMessagesListState {
|
||||||
val timelineRoomInfo = remember {
|
val isDm by room.isDmAsState()
|
||||||
|
|
||||||
|
val timelineRoomInfo = remember(isDm) {
|
||||||
TimelineRoomInfo(
|
TimelineRoomInfo(
|
||||||
isDm = room.isDm,
|
isDm = isDm,
|
||||||
name = room.displayName,
|
name = room.info().name,
|
||||||
// We don't need to compute those values
|
// We don't need to compute those values
|
||||||
userHasPermissionToSendMessage = false,
|
userHasPermissionToSendMessage = false,
|
||||||
userHasPermissionToSendReaction = false,
|
userHasPermissionToSendReaction = false,
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import io.element.android.features.poll.api.actions.EndPollAction
|
||||||
import io.element.android.features.poll.api.actions.SendPollResponseAction
|
import io.element.android.features.poll.api.actions.SendPollResponseAction
|
||||||
import io.element.android.features.roomcall.api.RoomCallState
|
import io.element.android.features.roomcall.api.RoomCallState
|
||||||
import io.element.android.libraries.architecture.Presenter
|
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.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.core.UniqueId
|
import io.element.android.libraries.matrix.api.core.UniqueId
|
||||||
|
|
@ -95,7 +96,7 @@ class TimelinePresenter @AssistedInject constructor(
|
||||||
|
|
||||||
val lastReadReceiptId = rememberSaveable { mutableStateOf<EventId?>(null) }
|
val lastReadReceiptId = rememberSaveable { mutableStateOf<EventId?>(null) }
|
||||||
|
|
||||||
val roomInfo by room.roomInfoFlow.collectAsState(initial = null)
|
val roomInfo by room.roomInfoFlow.collectAsState()
|
||||||
|
|
||||||
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
||||||
|
|
||||||
|
|
@ -231,15 +232,15 @@ class TimelinePresenter @AssistedInject constructor(
|
||||||
|
|
||||||
val typingNotificationState = typingNotificationPresenter.present()
|
val typingNotificationState = typingNotificationPresenter.present()
|
||||||
val roomCallState = roomCallStatePresenter.present()
|
val roomCallState = roomCallStatePresenter.present()
|
||||||
val timelineRoomInfo by remember(typingNotificationState, roomCallState) {
|
val timelineRoomInfo by remember(typingNotificationState, roomCallState, roomInfo) {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
TimelineRoomInfo(
|
TimelineRoomInfo(
|
||||||
name = room.displayName,
|
name = roomInfo.name,
|
||||||
isDm = room.isDm,
|
isDm = roomInfo.isDm.orFalse(),
|
||||||
userHasPermissionToSendMessage = userHasPermissionToSendMessage,
|
userHasPermissionToSendMessage = userHasPermissionToSendMessage,
|
||||||
userHasPermissionToSendReaction = userHasPermissionToSendReaction,
|
userHasPermissionToSendReaction = userHasPermissionToSendReaction,
|
||||||
roomCallState = roomCallState,
|
roomCallState = roomCallState,
|
||||||
pinnedEventIds = roomInfo?.pinnedEventIds.orEmpty(),
|
pinnedEventIds = roomInfo.pinnedEventIds.orEmpty(),
|
||||||
typingNotificationState = typingNotificationState,
|
typingNotificationState = typingNotificationState,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -536,15 +536,15 @@ class MessagesPresenterTest {
|
||||||
fun `present - shows prompt to reinvite users in DM`() = runTest {
|
fun `present - shows prompt to reinvite users in DM`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = FakeMatrixRoom(
|
||||||
sessionId = A_SESSION_ID,
|
sessionId = A_SESSION_ID,
|
||||||
isDirect = true,
|
|
||||||
activeMemberCount = 1L,
|
|
||||||
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
canRedactOwnResult = { Result.success(true) },
|
canRedactOwnResult = { Result.success(true) },
|
||||||
canRedactOtherResult = { Result.success(true) },
|
canRedactOtherResult = { Result.success(true) },
|
||||||
canUserJoinCallResult = { Result.success(true) },
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
typingNoticeResult = { Result.success(Unit) },
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
canUserPinUnpinResult = { Result.success(true) },
|
canUserPinUnpinResult = { Result.success(true) },
|
||||||
)
|
).apply {
|
||||||
|
givenRoomInfo(aRoomInfo(isDirect = true, joinedMembersCount = 1, activeMembersCount = 1))
|
||||||
|
}
|
||||||
val presenter = createMessagesPresenter(matrixRoom = room)
|
val presenter = createMessagesPresenter(matrixRoom = room)
|
||||||
presenter.testWithLifecycleOwner {
|
presenter.testWithLifecycleOwner {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
@ -552,7 +552,8 @@ class MessagesPresenterTest {
|
||||||
assertThat(initialState.showReinvitePrompt).isFalse()
|
assertThat(initialState.showReinvitePrompt).isFalse()
|
||||||
// When the input field is focused we show the alert
|
// When the input field is focused we show the alert
|
||||||
(initialState.composerState.textEditorState as TextEditorState.Markdown).state.hasFocus = true
|
(initialState.composerState.textEditorState as TextEditorState.Markdown).state.hasFocus = true
|
||||||
skipItems(1)
|
// Skip intermediate states
|
||||||
|
skipItems(2)
|
||||||
val focusedState = awaitItem()
|
val focusedState = awaitItem()
|
||||||
assertThat(focusedState.showReinvitePrompt).isTrue()
|
assertThat(focusedState.showReinvitePrompt).isTrue()
|
||||||
// If it's dismissed then we stop showing the alert
|
// If it's dismissed then we stop showing the alert
|
||||||
|
|
@ -567,20 +568,22 @@ class MessagesPresenterTest {
|
||||||
fun `present - doesn't show reinvite prompt in non-direct room`() = runTest {
|
fun `present - doesn't show reinvite prompt in non-direct room`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = FakeMatrixRoom(
|
||||||
sessionId = A_SESSION_ID,
|
sessionId = A_SESSION_ID,
|
||||||
isDirect = false,
|
|
||||||
activeMemberCount = 1L,
|
|
||||||
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
canRedactOwnResult = { Result.success(true) },
|
canRedactOwnResult = { Result.success(true) },
|
||||||
canRedactOtherResult = { Result.success(true) },
|
canRedactOtherResult = { Result.success(true) },
|
||||||
canUserJoinCallResult = { Result.success(true) },
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
typingNoticeResult = { Result.success(Unit) },
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
canUserPinUnpinResult = { Result.success(true) },
|
canUserPinUnpinResult = { Result.success(true) },
|
||||||
)
|
).apply {
|
||||||
|
givenRoomInfo(aRoomInfo(isDirect = false, joinedMembersCount = 1, activeMembersCount = 1))
|
||||||
|
}
|
||||||
val presenter = createMessagesPresenter(matrixRoom = room)
|
val presenter = createMessagesPresenter(matrixRoom = room)
|
||||||
presenter.testWithLifecycleOwner {
|
presenter.testWithLifecycleOwner {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.showReinvitePrompt).isFalse()
|
assertThat(initialState.showReinvitePrompt).isFalse()
|
||||||
(initialState.composerState.textEditorState as TextEditorState.Markdown).state.hasFocus = true
|
(initialState.composerState.textEditorState as TextEditorState.Markdown).state.hasFocus = true
|
||||||
|
// Skip intermediate events
|
||||||
|
skipItems(1)
|
||||||
val focusedState = awaitItem()
|
val focusedState = awaitItem()
|
||||||
assertThat(focusedState.showReinvitePrompt).isFalse()
|
assertThat(focusedState.showReinvitePrompt).isFalse()
|
||||||
}
|
}
|
||||||
|
|
@ -590,20 +593,22 @@ class MessagesPresenterTest {
|
||||||
fun `present - doesn't show reinvite prompt if other party is present`() = runTest {
|
fun `present - doesn't show reinvite prompt if other party is present`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = FakeMatrixRoom(
|
||||||
sessionId = A_SESSION_ID,
|
sessionId = A_SESSION_ID,
|
||||||
isDirect = true,
|
|
||||||
activeMemberCount = 2L,
|
|
||||||
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
canRedactOwnResult = { Result.success(true) },
|
canRedactOwnResult = { Result.success(true) },
|
||||||
canRedactOtherResult = { Result.success(true) },
|
canRedactOtherResult = { Result.success(true) },
|
||||||
canUserJoinCallResult = { Result.success(true) },
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
typingNoticeResult = { Result.success(Unit) },
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
canUserPinUnpinResult = { Result.success(true) },
|
canUserPinUnpinResult = { Result.success(true) },
|
||||||
)
|
).apply {
|
||||||
|
givenRoomInfo(aRoomInfo(isDirect = true, joinedMembersCount = 2, activeMembersCount = 2))
|
||||||
|
}
|
||||||
val presenter = createMessagesPresenter(matrixRoom = room)
|
val presenter = createMessagesPresenter(matrixRoom = room)
|
||||||
presenter.testWithLifecycleOwner {
|
presenter.testWithLifecycleOwner {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.showReinvitePrompt).isFalse()
|
assertThat(initialState.showReinvitePrompt).isFalse()
|
||||||
(initialState.composerState.textEditorState as TextEditorState.Markdown).state.hasFocus = true
|
(initialState.composerState.textEditorState as TextEditorState.Markdown).state.hasFocus = true
|
||||||
|
// Skip intermediate events
|
||||||
|
skipItems(1)
|
||||||
val focusedState = awaitItem()
|
val focusedState = awaitItem()
|
||||||
assertThat(focusedState.showReinvitePrompt).isFalse()
|
assertThat(focusedState.showReinvitePrompt).isFalse()
|
||||||
}
|
}
|
||||||
|
|
@ -1090,28 +1095,25 @@ class MessagesPresenterTest {
|
||||||
fun `present - when room is encrypted and a DM, the DM user's identity state is fetched onResume`() = runTest {
|
fun `present - when room is encrypted and a DM, the DM user's identity state is fetched onResume`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = FakeMatrixRoom(
|
||||||
sessionId = A_SESSION_ID,
|
sessionId = A_SESSION_ID,
|
||||||
isEncrypted = true,
|
|
||||||
isDirect = true,
|
|
||||||
activeMemberCount = 2L,
|
|
||||||
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
canRedactOwnResult = { Result.success(true) },
|
canRedactOwnResult = { Result.success(true) },
|
||||||
canRedactOtherResult = { Result.success(true) },
|
canRedactOtherResult = { Result.success(true) },
|
||||||
canUserJoinCallResult = { Result.success(true) },
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
typingNoticeResult = { Result.success(Unit) },
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
canUserPinUnpinResult = { Result.success(true) },
|
canUserPinUnpinResult = { Result.success(true) },
|
||||||
|
initialRoomInfo = aRoomInfo(isDirect = true, isEncrypted = true)
|
||||||
).apply {
|
).apply {
|
||||||
givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(aRoomMember(userId = A_SESSION_ID), aRoomMember(userId = A_USER_ID_2))))
|
givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(aRoomMember(userId = A_SESSION_ID), aRoomMember(userId = A_USER_ID_2))))
|
||||||
givenRoomInfo(aRoomInfo(id = roomId, name = "", isDirect = true))
|
|
||||||
}
|
}
|
||||||
val encryptionService = FakeEncryptionService(getUserIdentityResult = { Result.success(IdentityState.Verified) })
|
val encryptionService = FakeEncryptionService(getUserIdentityResult = { Result.success(IdentityState.Verified) })
|
||||||
|
|
||||||
val presenter = createMessagesPresenter(matrixRoom = room, encryptionService = encryptionService)
|
val presenter = createMessagesPresenter(matrixRoom = room, encryptionService = encryptionService)
|
||||||
val lifecycleOwner = FakeLifecycleOwner()
|
val lifecycleOwner = FakeLifecycleOwner()
|
||||||
presenter.testWithLifecycleOwner(lifecycleOwner) {
|
presenter.testWithLifecycleOwner(lifecycleOwner) {
|
||||||
skipItems(1)
|
|
||||||
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.dmUserVerificationState).isNull()
|
assertThat(initialState.dmUserVerificationState).isNull()
|
||||||
|
|
||||||
|
skipItems(1)
|
||||||
ensureAllEventsConsumed()
|
ensureAllEventsConsumed()
|
||||||
|
|
||||||
lifecycleOwner.givenState(Lifecycle.State.RESUMED)
|
lifecycleOwner.givenState(Lifecycle.State.RESUMED)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import io.element.android.libraries.matrix.test.A_USER_ID
|
||||||
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||||
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
|
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.FakeMatrixRoom
|
||||||
|
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||||
import io.element.android.libraries.matrix.test.room.aRoomMember
|
import io.element.android.libraries.matrix.test.room.aRoomMember
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
|
|
@ -44,7 +45,9 @@ class IdentityChangeStatePresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - when the room emits identity change, the presenter emits new state`() = runTest {
|
fun `present - when the room emits identity change, the presenter emits new state`() = runTest {
|
||||||
val room = FakeMatrixRoom(isEncrypted = true)
|
val room = FakeMatrixRoom().apply {
|
||||||
|
givenRoomInfo(aRoomInfo(isEncrypted = true))
|
||||||
|
}
|
||||||
val presenter = createIdentityChangeStatePresenter(room)
|
val presenter = createIdentityChangeStatePresenter(room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
@ -67,10 +70,7 @@ class IdentityChangeStatePresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - when the clear room emits identity change, the presenter does not emit new state`() = runTest {
|
fun `present - when the clear room emits identity change, the presenter does not emit new state`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = FakeMatrixRoom(enableEncryptionResult = { Result.success(Unit) })
|
||||||
isEncrypted = false,
|
|
||||||
enableEncryptionResult = { Result.success(Unit) }
|
|
||||||
)
|
|
||||||
val presenter = createIdentityChangeStatePresenter(room)
|
val presenter = createIdentityChangeStatePresenter(room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
@ -85,8 +85,9 @@ class IdentityChangeStatePresenterTest {
|
||||||
)
|
)
|
||||||
// No item emitted.
|
// No item emitted.
|
||||||
expectNoEvents()
|
expectNoEvents()
|
||||||
// Room become encrypted.
|
// Room becomes encrypted.
|
||||||
room.enableEncryption()
|
room.givenRoomInfo(aRoomInfo(isEncrypted = true))
|
||||||
|
|
||||||
val finalItem = awaitItem()
|
val finalItem = awaitItem()
|
||||||
assertThat(finalItem.roomMemberIdentityStateChanges).hasSize(1)
|
assertThat(finalItem.roomMemberIdentityStateChanges).hasSize(1)
|
||||||
val value = finalItem.roomMemberIdentityStateChanges.first()
|
val value = finalItem.roomMemberIdentityStateChanges.first()
|
||||||
|
|
@ -99,7 +100,7 @@ class IdentityChangeStatePresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - when the room emits identity change, the presenter emits new state with member details`() =
|
fun `present - when the room emits identity change, the presenter emits new state with member details`() =
|
||||||
runTest {
|
runTest {
|
||||||
val room = FakeMatrixRoom(isEncrypted = true).apply {
|
val room = FakeMatrixRoom().apply {
|
||||||
givenRoomMembersState(
|
givenRoomMembersState(
|
||||||
MatrixRoomMembersState.Ready(
|
MatrixRoomMembersState.Ready(
|
||||||
listOf(
|
listOf(
|
||||||
|
|
@ -110,6 +111,7 @@ class IdentityChangeStatePresenterTest {
|
||||||
).toImmutableList()
|
).toImmutableList()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
givenRoomInfo(aRoomInfo(isEncrypted = true))
|
||||||
}
|
}
|
||||||
val presenter = createIdentityChangeStatePresenter(room)
|
val presenter = createIdentityChangeStatePresenter(room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||||
import io.element.android.libraries.matrix.test.permalink.FakePermalinkBuilder
|
import io.element.android.libraries.matrix.test.permalink.FakePermalinkBuilder
|
||||||
import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser
|
import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
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.matrix.test.room.aRoomMember
|
||||||
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
||||||
import io.element.android.libraries.matrix.ui.messages.RoomMemberProfilesCache
|
import io.element.android.libraries.matrix.ui.messages.RoomMemberProfilesCache
|
||||||
|
|
@ -998,7 +999,6 @@ class MessageComposerPresenterTest {
|
||||||
val david = aRoomMember(userId = A_USER_ID_4, displayName = "Dave", membership = RoomMembershipState.JOIN)
|
val david = aRoomMember(userId = A_USER_ID_4, displayName = "Dave", membership = RoomMembershipState.JOIN)
|
||||||
var canUserTriggerRoomNotificationResult = true
|
var canUserTriggerRoomNotificationResult = true
|
||||||
val room = FakeMatrixRoom(
|
val room = FakeMatrixRoom(
|
||||||
isDirect = false,
|
|
||||||
canUserTriggerRoomNotificationResult = { Result.success(canUserTriggerRoomNotificationResult) },
|
canUserTriggerRoomNotificationResult = { Result.success(canUserTriggerRoomNotificationResult) },
|
||||||
typingNoticeResult = { Result.success(Unit) }
|
typingNoticeResult = { Result.success(Unit) }
|
||||||
).apply {
|
).apply {
|
||||||
|
|
@ -1007,6 +1007,7 @@ class MessageComposerPresenterTest {
|
||||||
persistentListOf(currentUser, invitedUser, bob, david),
|
persistentListOf(currentUser, invitedUser, bob, david),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
givenRoomInfo(aRoomInfo(isDirect = false))
|
||||||
}
|
}
|
||||||
val flagsService = FakeFeatureFlagService(
|
val flagsService = FakeFeatureFlagService(
|
||||||
mapOf(
|
mapOf(
|
||||||
|
|
@ -1060,9 +1061,6 @@ class MessageComposerPresenterTest {
|
||||||
val bob = aRoomMember(userId = A_USER_ID_2, membership = RoomMembershipState.JOIN)
|
val bob = aRoomMember(userId = A_USER_ID_2, membership = RoomMembershipState.JOIN)
|
||||||
val david = aRoomMember(userId = A_USER_ID_4, displayName = "Dave", membership = RoomMembershipState.JOIN)
|
val david = aRoomMember(userId = A_USER_ID_4, displayName = "Dave", membership = RoomMembershipState.JOIN)
|
||||||
val room = FakeMatrixRoom(
|
val room = FakeMatrixRoom(
|
||||||
isDirect = true,
|
|
||||||
activeMemberCount = 2,
|
|
||||||
isEncrypted = true,
|
|
||||||
canUserTriggerRoomNotificationResult = { Result.success(true) },
|
canUserTriggerRoomNotificationResult = { Result.success(true) },
|
||||||
typingNoticeResult = { Result.success(Unit) }
|
typingNoticeResult = { Result.success(Unit) }
|
||||||
).apply {
|
).apply {
|
||||||
|
|
@ -1071,6 +1069,12 @@ class MessageComposerPresenterTest {
|
||||||
persistentListOf(currentUser, invitedUser, bob, david),
|
persistentListOf(currentUser, invitedUser, bob, david),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
givenRoomInfo(
|
||||||
|
aRoomInfo(
|
||||||
|
isDirect = true,
|
||||||
|
activeMembersCount = 2,
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
val flagsService = FakeFeatureFlagService(
|
val flagsService = FakeFeatureFlagService(
|
||||||
mapOf(
|
mapOf(
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,12 @@ class RoomCallStatePresenter @Inject constructor(
|
||||||
) : Presenter<RoomCallState> {
|
) : Presenter<RoomCallState> {
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): RoomCallState {
|
override fun present(): RoomCallState {
|
||||||
val roomInfo by room.roomInfoFlow.collectAsState(null)
|
val roomInfo by room.roomInfoFlow.collectAsState()
|
||||||
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
||||||
val canJoinCall by room.canCall(updateKey = syncUpdateFlow.value)
|
val canJoinCall by room.canCall(updateKey = syncUpdateFlow.value)
|
||||||
val isUserInTheCall by remember {
|
val isUserInTheCall by remember {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
room.sessionId in roomInfo?.activeRoomCallParticipants.orEmpty()
|
room.sessionId in roomInfo.activeRoomCallParticipants
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val currentCall by currentCallService.currentCall.collectAsState()
|
val currentCall by currentCallService.currentCall.collectAsState()
|
||||||
|
|
@ -41,7 +41,7 @@ class RoomCallStatePresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val callState = when {
|
val callState = when {
|
||||||
roomInfo?.hasRoomCall == true -> RoomCallState.OnGoing(
|
roomInfo.hasRoomCall -> RoomCallState.OnGoing(
|
||||||
canJoinCall = canJoinCall,
|
canJoinCall = canJoinCall,
|
||||||
isUserInTheCall = isUserInTheCall,
|
isUserInTheCall = isUserInTheCall,
|
||||||
isUserLocallyInTheCall = isUserLocallyInTheCall,
|
isUserLocallyInTheCall = isUserLocallyInTheCall,
|
||||||
|
|
|
||||||
|
|
@ -58,12 +58,10 @@ class RoomCallStatePresenterTest {
|
||||||
fun `present - call is disabled if user cannot join it even if there is an ongoing call`() = runTest {
|
fun `present - call is disabled if user cannot join it even if there is an ongoing call`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = FakeMatrixRoom(
|
||||||
canUserJoinCallResult = { Result.success(false) },
|
canUserJoinCallResult = { Result.success(false) },
|
||||||
).apply {
|
initialRoomInfo = aRoomInfo(hasRoomCall = true),
|
||||||
givenRoomInfo(aRoomInfo(hasRoomCall = true))
|
)
|
||||||
}
|
|
||||||
val presenter = createRoomCallStatePresenter(matrixRoom = room)
|
val presenter = createRoomCallStatePresenter(matrixRoom = room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
skipItems(1)
|
|
||||||
assertThat(awaitItem()).isEqualTo(
|
assertThat(awaitItem()).isEqualTo(
|
||||||
RoomCallState.OnGoing(
|
RoomCallState.OnGoing(
|
||||||
canJoinCall = false,
|
canJoinCall = false,
|
||||||
|
|
|
||||||
|
|
@ -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.members.details.RoomMemberDetailsPresenter
|
||||||
import io.element.android.features.roomdetails.impl.securityandprivacy.permissions.securityAndPrivacyPermissionsAsState
|
import io.element.android.features.roomdetails.impl.securityandprivacy.permissions.securityAndPrivacyPermissionsAsState
|
||||||
import io.element.android.libraries.architecture.Presenter
|
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.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
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.MatrixRoomMembersState
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
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.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.join.JoinRule
|
||||||
import io.element.android.libraries.matrix.api.room.powerlevels.canInvite
|
import io.element.android.libraries.matrix.api.room.powerlevels.canInvite
|
||||||
import io.element.android.libraries.matrix.api.room.powerlevels.canSendState
|
import io.element.android.libraries.matrix.api.room.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.canHandleKnockRequestsAsState
|
||||||
import io.element.android.libraries.matrix.ui.room.getCurrentRoomMember
|
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.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.isOwnUserAdmin
|
||||||
import io.element.android.libraries.matrix.ui.room.roomMemberIdentityStateChange
|
import io.element.android.libraries.matrix.ui.room.roomMemberIdentityStateChange
|
||||||
import io.element.android.services.analytics.api.AnalyticsService
|
import io.element.android.services.analytics.api.AnalyticsService
|
||||||
|
|
@ -72,22 +71,22 @@ class RoomDetailsPresenter @Inject constructor(
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val leaveRoomState = leaveRoomPresenter.present()
|
val leaveRoomState = leaveRoomPresenter.present()
|
||||||
val canShowNotificationSettings = remember { mutableStateOf(false) }
|
val canShowNotificationSettings = remember { mutableStateOf(false) }
|
||||||
val roomInfo by room.roomInfoFlow.collectAsState(initial = null)
|
val roomInfo by room.roomInfoFlow.collectAsState()
|
||||||
val isUserAdmin = room.isOwnUserAdmin()
|
val isUserAdmin = room.isOwnUserAdmin()
|
||||||
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
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 roomName by remember { derivedStateOf { roomInfo.name?.trim().orEmpty() } }
|
||||||
val roomTopic by remember { derivedStateOf { roomInfo?.topic ?: room.topic } }
|
val roomTopic by remember { derivedStateOf { roomInfo.topic } }
|
||||||
val isFavorite by remember { derivedStateOf { roomInfo?.isFavorite.orFalse() } }
|
val isFavorite by remember { derivedStateOf { roomInfo.isFavorite } }
|
||||||
val joinRule by remember { derivedStateOf { roomInfo?.joinRule } }
|
val joinRule by remember { derivedStateOf { roomInfo.joinRule } }
|
||||||
|
|
||||||
val canShowPinnedMessages = isPinnedMessagesFeatureEnabled()
|
val canShowPinnedMessages = isPinnedMessagesFeatureEnabled()
|
||||||
var canShowMediaGallery by remember { mutableStateOf(false) }
|
var canShowMediaGallery by remember { mutableStateOf(false) }
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
canShowMediaGallery = featureFlagService.isFeatureEnabled(FeatureFlags.MediaGallery)
|
canShowMediaGallery = featureFlagService.isFeatureEnabled(FeatureFlags.MediaGallery)
|
||||||
}
|
}
|
||||||
val pinnedMessagesCount by remember { derivedStateOf { roomInfo?.pinnedEventIds?.size } }
|
val pinnedMessagesCount by remember { derivedStateOf { roomInfo.pinnedEventIds.size } }
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
canShowNotificationSettings.value = featureFlagService.isFeatureEnabled(FeatureFlags.NotificationSettings)
|
canShowNotificationSettings.value = featureFlagService.isFeatureEnabled(FeatureFlags.NotificationSettings)
|
||||||
|
|
@ -100,6 +99,9 @@ class RoomDetailsPresenter @Inject constructor(
|
||||||
val membersState by room.membersStateFlow.collectAsState()
|
val membersState by room.membersStateFlow.collectAsState()
|
||||||
val canInvite by getCanInvite(membersState)
|
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 canEditName by getCanSendState(membersState, StateEventType.ROOM_NAME)
|
||||||
val canEditAvatar by getCanSendState(membersState, StateEventType.ROOM_AVATAR)
|
val canEditAvatar by getCanSendState(membersState, StateEventType.ROOM_AVATAR)
|
||||||
val canEditTopic by getCanSendState(membersState, StateEventType.ROOM_TOPIC)
|
val canEditTopic by getCanSendState(membersState, StateEventType.ROOM_TOPIC)
|
||||||
|
|
@ -108,6 +110,7 @@ class RoomDetailsPresenter @Inject constructor(
|
||||||
val roomMemberDetailsPresenter = roomMemberDetailsPresenter(dmMember)
|
val roomMemberDetailsPresenter = roomMemberDetailsPresenter(dmMember)
|
||||||
val roomType = getRoomType(dmMember, currentMember)
|
val roomType = getRoomType(dmMember, currentMember)
|
||||||
val roomCallState = roomCallStatePresenter.present()
|
val roomCallState = roomCallStatePresenter.present()
|
||||||
|
val joinedMemberCount by remember { derivedStateOf { roomInfo.joinedMembersCount } }
|
||||||
|
|
||||||
val topicState = remember(canEditTopic, roomTopic, roomType) {
|
val topicState = remember(canEditTopic, roomTopic, roomType) {
|
||||||
val topic = roomTopic
|
val topic = roomTopic
|
||||||
|
|
@ -140,7 +143,7 @@ class RoomDetailsPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
RoomDetailsEvent.UnmuteNotification -> {
|
RoomDetailsEvent.UnmuteNotification -> {
|
||||||
scope.launch(dispatchers.io) {
|
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)
|
is RoomDetailsEvent.SetFavorite -> scope.setFavorite(event.isFavorite)
|
||||||
|
|
@ -165,11 +168,11 @@ class RoomDetailsPresenter @Inject constructor(
|
||||||
return RoomDetailsState(
|
return RoomDetailsState(
|
||||||
roomId = room.roomId,
|
roomId = room.roomId,
|
||||||
roomName = roomName,
|
roomName = roomName,
|
||||||
roomAlias = room.canonicalAlias,
|
roomAlias = canonicalAlias,
|
||||||
roomAvatarUrl = roomAvatar,
|
roomAvatarUrl = roomAvatar,
|
||||||
roomTopic = topicState,
|
roomTopic = topicState,
|
||||||
memberCount = room.joinedMemberCount,
|
memberCount = joinedMemberCount,
|
||||||
isEncrypted = room.isEncrypted,
|
isEncrypted = isEncrypted,
|
||||||
canInvite = canInvite,
|
canInvite = canInvite,
|
||||||
canEdit = (canEditAvatar || canEditName || canEditTopic) && roomType == RoomDetailsType.Room,
|
canEdit = (canEditAvatar || canEditName || canEditTopic) && roomType == RoomDetailsType.Room,
|
||||||
canShowNotificationSettings = canShowNotificationSettings.value,
|
canShowNotificationSettings = canShowNotificationSettings.value,
|
||||||
|
|
@ -179,9 +182,9 @@ class RoomDetailsPresenter @Inject constructor(
|
||||||
leaveRoomState = leaveRoomState,
|
leaveRoomState = leaveRoomState,
|
||||||
roomNotificationSettings = roomNotificationSettingsState.roomNotificationSettings(),
|
roomNotificationSettings = roomNotificationSettingsState.roomNotificationSettings(),
|
||||||
isFavorite = isFavorite,
|
isFavorite = isFavorite,
|
||||||
displayRolesAndPermissionsSettings = !room.isDm && isUserAdmin,
|
displayRolesAndPermissionsSettings = !isDm && isUserAdmin,
|
||||||
isPublic = joinRule == JoinRule.Public,
|
isPublic = joinRule == JoinRule.Public,
|
||||||
heroes = roomInfo?.heroes.orEmpty().toPersistentList(),
|
heroes = roomInfo.heroes.toPersistentList(),
|
||||||
canShowPinnedMessages = canShowPinnedMessages,
|
canShowPinnedMessages = canShowPinnedMessages,
|
||||||
canShowMediaGallery = canShowMediaGallery,
|
canShowMediaGallery = canShowMediaGallery,
|
||||||
pinnedMessagesCount = pinnedMessagesCount,
|
pinnedMessagesCount = pinnedMessagesCount,
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,7 @@ class RoomMemberListPresenter @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun RoomMember.withIdentityState(identityStates: ImmutableMap<UserId, IdentityState>): RoomMemberWithIdentityState {
|
private suspend fun RoomMember.withIdentityState(identityStates: ImmutableMap<UserId, IdentityState>): RoomMemberWithIdentityState {
|
||||||
return if (!room.isEncrypted) {
|
return if (room.info().isEncrypted != true) {
|
||||||
RoomMemberWithIdentityState(this, null)
|
RoomMemberWithIdentityState(this, null)
|
||||||
} else {
|
} else {
|
||||||
val identityState = identityStates[userId] ?: encryptionService.getUserIdentity(userId).getOrNull()
|
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.api.room.MatrixRoom
|
||||||
import io.element.android.libraries.matrix.ui.room.getRoomMemberAsState
|
import io.element.android.libraries.matrix.ui.room.getRoomMemberAsState
|
||||||
import io.element.android.libraries.matrix.ui.room.roomMemberIdentityStateChange
|
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.filterNotNull
|
||||||
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
@ -47,6 +50,7 @@ class RoomMemberDetailsPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
private val userProfilePresenter = userProfilePresenterFactory.create(roomMemberId)
|
private val userProfilePresenter = userProfilePresenterFactory.create(roomMemberId)
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): UserProfileState {
|
override fun present(): UserProfileState {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
@ -75,21 +79,22 @@ class RoomMemberDetailsPresenter @AssistedInject constructor(
|
||||||
val userProfileState = userProfilePresenter.present()
|
val userProfileState = userProfilePresenter.present()
|
||||||
|
|
||||||
val identityStateChanges by produceState<IdentityStateChange?>(initialValue = null) {
|
val identityStateChanges by produceState<IdentityStateChange?>(initialValue = null) {
|
||||||
if (room.isEncrypted) {
|
room.roomInfoFlow.filter { it.isEncrypted == true }
|
||||||
// Fetch the initial identity state manually
|
.flatMapLatest {
|
||||||
val identityState = encryptionService.getUserIdentity(roomMemberId).getOrNull()
|
// Fetch the initial identity state manually
|
||||||
value = identityState?.let { IdentityStateChange(roomMemberId, it) }
|
val identityState = encryptionService.getUserIdentity(roomMemberId).getOrNull()
|
||||||
|
value = identityState?.let { IdentityStateChange(roomMemberId, it) }
|
||||||
|
|
||||||
// Subscribe to the identity changes
|
// Subscribe to the identity changes
|
||||||
room.roomMemberIdentityStateChange()
|
room.roomMemberIdentityStateChange()
|
||||||
.map { it.find { it.identityRoomMember.userId == roomMemberId } }
|
.map { it.find { it.identityRoomMember.userId == roomMemberId } }
|
||||||
.map { roomMemberIdentityStateChange ->
|
.map { roomMemberIdentityStateChange ->
|
||||||
// If we didn't receive any info, manually fetch it
|
// If we didn't receive any info, manually fetch it
|
||||||
roomMemberIdentityStateChange?.identityState ?: encryptionService.getUserIdentity(roomMemberId).getOrNull()
|
roomMemberIdentityStateChange?.identityState ?: encryptionService.getUserIdentity(roomMemberId).getOrNull()
|
||||||
}
|
}
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
.collect { value = IdentityStateChange(roomMemberId, it) }
|
}
|
||||||
}
|
.collect { value = IdentityStateChange(roomMemberId, it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
val verificationState = remember(identityStateChanges) {
|
val verificationState = remember(identityStateChanges) {
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ class RoomMembersModerationPresenter @Inject constructor(
|
||||||
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
||||||
val canBan by room.canBanAsState(syncUpdateFlow.value)
|
val canBan by room.canBanAsState(syncUpdateFlow.value)
|
||||||
val canKick by room.canKickAsState(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 currentUserMemberPowerLevel by room.userPowerLevelAsState(syncUpdateFlow.value)
|
||||||
|
|
||||||
val canDisplayModerationActions by remember {
|
val canDisplayModerationActions by remember {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.produceState
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
|
@ -78,11 +79,23 @@ class RoomNotificationSettingsPresenter @AssistedInject constructor(
|
||||||
mutableStateOf(null)
|
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) {
|
LaunchedEffect(Unit) {
|
||||||
getDefaultRoomNotificationMode(defaultRoomNotificationMode)
|
getDefaultRoomNotificationMode(defaultRoomNotificationMode)
|
||||||
fetchNotificationSettings(pendingRoomNotificationMode, roomNotificationSettings)
|
fetchNotificationSettings(pendingRoomNotificationMode, roomNotificationSettings)
|
||||||
observeNotificationSettings(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) {
|
fun handleEvents(event: RoomNotificationSettingsEvents) {
|
||||||
|
|
@ -113,7 +126,7 @@ class RoomNotificationSettingsPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
return RoomNotificationSettingsState(
|
return RoomNotificationSettingsState(
|
||||||
showUserDefinedSettingStyle = showUserDefinedSettingStyle,
|
showUserDefinedSettingStyle = showUserDefinedSettingStyle,
|
||||||
roomName = room.displayName,
|
roomName = displayName.orEmpty(),
|
||||||
roomNotificationSettings = roomNotificationSettings.value,
|
roomNotificationSettings = roomNotificationSettings.value,
|
||||||
pendingRoomNotificationMode = pendingRoomNotificationMode.value,
|
pendingRoomNotificationMode = pendingRoomNotificationMode.value,
|
||||||
pendingSetDefault = pendingSetDefault.value,
|
pendingSetDefault = pendingSetDefault.value,
|
||||||
|
|
@ -143,16 +156,18 @@ class RoomNotificationSettingsPresenter @AssistedInject constructor(
|
||||||
roomNotificationSettings: MutableState<AsyncData<RoomNotificationSettings>>
|
roomNotificationSettings: MutableState<AsyncData<RoomNotificationSettings>>
|
||||||
) = launch {
|
) = launch {
|
||||||
suspend {
|
suspend {
|
||||||
|
val isEncrypted = room.info().isEncrypted ?: room.getUpdatedIsEncrypted().getOrThrow()
|
||||||
pendingModeState.value = null
|
pendingModeState.value = null
|
||||||
notificationSettingsService.getRoomNotificationSettings(room.roomId, room.isEncrypted, room.isOneToOne).getOrThrow()
|
notificationSettingsService.getRoomNotificationSettings(room.roomId, isEncrypted, room.isOneToOne).getOrThrow()
|
||||||
}.runCatchingUpdatingState(roomNotificationSettings)
|
}.runCatchingUpdatingState(roomNotificationSettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.getDefaultRoomNotificationMode(
|
private fun CoroutineScope.getDefaultRoomNotificationMode(
|
||||||
defaultRoomNotificationMode: MutableState<RoomNotificationMode?>
|
defaultRoomNotificationMode: MutableState<RoomNotificationMode?>
|
||||||
) = launch {
|
) = launch {
|
||||||
|
val isEncrypted = room.info().isEncrypted ?: room.getUpdatedIsEncrypted().getOrThrow()
|
||||||
defaultRoomNotificationMode.value = notificationSettingsService.getDefaultRoomNotificationMode(
|
defaultRoomNotificationMode.value = notificationSettingsService.getDefaultRoomNotificationMode(
|
||||||
room.isEncrypted,
|
isEncrypted,
|
||||||
room.isOneToOne
|
room.isOneToOne
|
||||||
).getOrThrow()
|
).getOrThrow()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ class RolesAndPermissionsPresenter @Inject constructor(
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): RolesAndPermissionsState {
|
override fun present(): RolesAndPermissionsState {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
val roomInfo by room.roomInfoFlow.collectAsState(initial = null)
|
val roomInfo by room.roomInfoFlow.collectAsState()
|
||||||
val roomMembers by room.membersStateFlow.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
|
// Get the list of active room members (joined or invited), in order to filter members present in the power
|
||||||
// level state Event.
|
// level state Event.
|
||||||
|
|
@ -109,8 +109,8 @@ class RolesAndPermissionsPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MatrixRoomInfo?.userCountWithRole(userIds: List<UserId>, role: RoomMember.Role): Int {
|
private fun MatrixRoomInfo.userCountWithRole(userIds: List<UserId>, role: RoomMember.Role): Int {
|
||||||
return this?.userPowerLevels.orEmpty().count { (userId, level) ->
|
return this.userPowerLevels.count { (userId, level) ->
|
||||||
RoomMember.Role.forPowerLevel(level) == role && userId in userIds
|
RoomMember.Role.forPowerLevel(level) == role && userId in userIds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -106,10 +106,10 @@ class ChangeRolesPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
val hasPendingChanges = usersWithRole.value != selectedUsers.value
|
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 {
|
fun canChangeMemberRole(userId: UserId): Boolean {
|
||||||
// An admin can't remove or demote another admin
|
// 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
|
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 saveAction = remember { mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized) }
|
||||||
val homeserverName = remember { matrixClient.userIdServerName() }
|
val homeserverName = remember { matrixClient.userIdServerName() }
|
||||||
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
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) }
|
val savedIsVisibleInRoomDirectory = remember { mutableStateOf<AsyncData<Boolean>>(AsyncData.Uninitialized) }
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
|
|
@ -66,12 +66,13 @@ class SecurityAndPrivacyPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
val savedSettings by remember {
|
val savedSettings by remember {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
|
val historyVisibility = roomInfo.historyVisibility.map()
|
||||||
SecurityAndPrivacySettings(
|
SecurityAndPrivacySettings(
|
||||||
roomAccess = roomInfo.value?.joinRule.map(),
|
roomAccess = roomInfo.joinRule.map(),
|
||||||
isEncrypted = room.isEncrypted,
|
isEncrypted = roomInfo.isEncrypted == true,
|
||||||
isVisibleInRoomDirectory = savedIsVisibleInRoomDirectory.value,
|
isVisibleInRoomDirectory = savedIsVisibleInRoomDirectory.value,
|
||||||
historyVisibility = roomInfo.value?.historyVisibility.map(),
|
historyVisibility = historyVisibility,
|
||||||
address = roomInfo.value?.firstDisplayableAlias(homeserverName)?.value,
|
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.Composable
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
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.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
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.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.room.alias.RoomAliasHelper
|
||||||
import io.element.android.libraries.matrix.api.roomAliasFromName
|
import io.element.android.libraries.matrix.api.roomAliasFromName
|
||||||
import io.element.android.libraries.matrix.ui.room.address.RoomAddressValidity
|
import io.element.android.libraries.matrix.ui.room.address.RoomAddressValidity
|
||||||
|
|
@ -45,15 +48,16 @@ class EditRoomAddressPresenter @AssistedInject constructor(
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): EditRoomAddressState {
|
override fun present(): EditRoomAddressState {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val roomInfo by room.roomInfoFlow.collectAsState()
|
||||||
val homeserverName = remember { client.userIdServerName() }
|
val homeserverName = remember { client.userIdServerName() }
|
||||||
val roomAddressValidity = remember {
|
val roomAddressValidity = remember {
|
||||||
mutableStateOf<RoomAddressValidity>(RoomAddressValidity.Unknown)
|
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) }
|
val saveAction = remember { mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized) }
|
||||||
var newRoomAddress by remember {
|
var newRoomAddress by remember {
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
savedRoomAddress ?: roomAliasHelper.roomAliasNameFromRoomDisplayName(room.displayName)
|
savedRoomAddress ?: roomAliasHelper.roomAliasNameFromRoomDisplayName(roomInfo.name.orEmpty())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,8 +101,9 @@ class EditRoomAddressPresenter @AssistedInject constructor(
|
||||||
newRoomAddress: String,
|
newRoomAddress: String,
|
||||||
) = launch {
|
) = launch {
|
||||||
suspend {
|
suspend {
|
||||||
val savedCanonicalAlias = room.canonicalAlias
|
val roomInfo = room.info()
|
||||||
val savedAliasFromHomeserver = room.firstAliasMatching(serverName)
|
val savedCanonicalAlias = roomInfo.canonicalAlias
|
||||||
|
val savedAliasFromHomeserver = roomInfo.firstAliasMatching(serverName)
|
||||||
val newRoomAlias = client.roomAliasFromName(newRoomAddress) ?: throw IllegalArgumentException("Invalid room address")
|
val newRoomAlias = client.roomAliasFromName(newRoomAddress) ?: throw IllegalArgumentException("Invalid room address")
|
||||||
|
|
||||||
// First publish the new alias in the room directory
|
// First publish the new alias in the room directory
|
||||||
|
|
@ -112,7 +117,7 @@ class EditRoomAddressPresenter @AssistedInject constructor(
|
||||||
when {
|
when {
|
||||||
// Allow to update the canonical alias only if the saved canonical alias matches the homeserver or if there is no canonical alias
|
// 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) -> {
|
savedCanonicalAlias == null || savedCanonicalAlias.matchesServer(serverName) -> {
|
||||||
val newAlternativeAliases = room.alternativeAliases.filter { it != savedAliasFromHomeserver }
|
val newAlternativeAliases = roomInfo.alternativeAliases.filter { it != savedAliasFromHomeserver }
|
||||||
room.updateCanonicalAlias(newRoomAlias, newAlternativeAliases).getOrThrow()
|
room.updateCanonicalAlias(newRoomAlias, newAlternativeAliases).getOrThrow()
|
||||||
}
|
}
|
||||||
// Otherwise, only update the alternative aliases and keep the current canonical alias
|
// 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
|
// New alias is added first, so we make sure we pick it first
|
||||||
add(newRoomAlias)
|
add(newRoomAlias)
|
||||||
// Add all other aliases, except the one we just removed from the room directory
|
// 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()
|
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.
|
* 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
|
// Check if the canonical alias matches the homeserver
|
||||||
if (canonicalAlias?.matchesServer(serverName) == true) {
|
if (canonicalAlias?.matchesServer(serverName) == true) {
|
||||||
return canonicalAlias
|
return canonicalAlias
|
||||||
|
|
|
||||||
|
|
@ -7,34 +7,43 @@
|
||||||
|
|
||||||
package io.element.android.features.roomdetails.impl
|
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.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.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
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.StateEventType
|
||||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
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.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_ID
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
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_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.notificationsettings.FakeNotificationSettingsService
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
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.aRoomInfo
|
||||||
import io.element.android.tests.testutils.lambda.lambdaError
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
|
|
||||||
fun aMatrixRoom(
|
fun aMatrixRoom(
|
||||||
|
sessionId: SessionId = A_SESSION_ID,
|
||||||
roomId: RoomId = A_ROOM_ID,
|
roomId: RoomId = A_ROOM_ID,
|
||||||
displayName: String = A_ROOM_NAME,
|
displayName: String = A_ROOM_NAME,
|
||||||
rawName: String? = displayName,
|
rawName: String? = displayName,
|
||||||
topic: String? = A_ROOM_TOPIC,
|
topic: String? = A_ROOM_TOPIC,
|
||||||
avatarUrl: String? = AN_AVATAR_URL,
|
avatarUrl: String? = AN_AVATAR_URL,
|
||||||
|
canonicalAlias: RoomAlias? = A_ROOM_ALIAS,
|
||||||
isEncrypted: Boolean = true,
|
isEncrypted: Boolean = true,
|
||||||
isPublic: Boolean = true,
|
isPublic: Boolean = true,
|
||||||
isDirect: Boolean = false,
|
isDirect: Boolean = false,
|
||||||
joinRule: JoinRule? = null,
|
joinRule: JoinRule? = null,
|
||||||
|
activeMemberCount: Long = 1,
|
||||||
|
joinedMemberCount: Long = 1,
|
||||||
|
invitedMemberCount: Long = 0,
|
||||||
notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(),
|
notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(),
|
||||||
emitRoomInfo: Boolean = false,
|
|
||||||
canInviteResult: (UserId) -> Result<Boolean> = { lambdaError() },
|
canInviteResult: (UserId) -> Result<Boolean> = { lambdaError() },
|
||||||
canBanResult: (UserId) -> Result<Boolean> = { lambdaError() },
|
canBanResult: (UserId) -> Result<Boolean> = { lambdaError() },
|
||||||
|
canKickResult: (UserId) -> Result<Boolean> = { lambdaError() },
|
||||||
canSendStateResult: (UserId, StateEventType) -> Result<Boolean> = { _, _ -> lambdaError() },
|
canSendStateResult: (UserId, StateEventType) -> Result<Boolean> = { _, _ -> lambdaError() },
|
||||||
userDisplayNameResult: (UserId) -> Result<String?> = { lambdaError() },
|
userDisplayNameResult: (UserId) -> Result<String?> = { lambdaError() },
|
||||||
userAvatarUrlResult: () -> Result<String?> = { lambdaError() },
|
userAvatarUrlResult: () -> Result<String?> = { lambdaError() },
|
||||||
|
|
@ -44,17 +53,20 @@ fun aMatrixRoom(
|
||||||
removeAvatarResult: () -> Result<Unit> = { lambdaError() },
|
removeAvatarResult: () -> Result<Unit> = { lambdaError() },
|
||||||
canUserJoinCallResult: (UserId) -> Result<Boolean> = { lambdaError() },
|
canUserJoinCallResult: (UserId) -> Result<Boolean> = { lambdaError() },
|
||||||
getUpdatedMemberResult: (UserId) -> Result<RoomMember> = { 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(
|
) = FakeMatrixRoom(
|
||||||
|
sessionId = sessionId,
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
displayName = displayName,
|
|
||||||
topic = topic,
|
|
||||||
avatarUrl = avatarUrl,
|
|
||||||
isEncrypted = isEncrypted,
|
|
||||||
isPublic = isPublic,
|
|
||||||
isDirect = isDirect,
|
|
||||||
notificationSettingsService = notificationSettingsService,
|
notificationSettingsService = notificationSettingsService,
|
||||||
canInviteResult = canInviteResult,
|
canInviteResult = canInviteResult,
|
||||||
canBanResult = canBanResult,
|
canBanResult = canBanResult,
|
||||||
|
canKickResult = canKickResult,
|
||||||
canSendStateResult = canSendStateResult,
|
canSendStateResult = canSendStateResult,
|
||||||
userDisplayNameResult = userDisplayNameResult,
|
userDisplayNameResult = userDisplayNameResult,
|
||||||
userAvatarUrlResult = userAvatarUrlResult,
|
userAvatarUrlResult = userAvatarUrlResult,
|
||||||
|
|
@ -64,17 +76,25 @@ fun aMatrixRoom(
|
||||||
removeAvatarResult = removeAvatarResult,
|
removeAvatarResult = removeAvatarResult,
|
||||||
canUserJoinCallResult = canUserJoinCallResult,
|
canUserJoinCallResult = canUserJoinCallResult,
|
||||||
getUpdatedMemberResult = getUpdatedMemberResult,
|
getUpdatedMemberResult = getUpdatedMemberResult,
|
||||||
).apply {
|
userRoleResult = userRoleResult,
|
||||||
if (emitRoomInfo) {
|
kickUserResult = kickUserResult,
|
||||||
givenRoomInfo(
|
banUserResult = banUserResult,
|
||||||
aRoomInfo(
|
unBanUserResult = unBanUserResult,
|
||||||
name = displayName,
|
updateCanonicalAliasResult = updateCanonicalAliasResult,
|
||||||
rawName = rawName,
|
publishRoomAliasInRoomDirectoryResult = publishRoomAliasInRoomDirectoryResult,
|
||||||
topic = topic,
|
removeRoomAliasFromRoomDirectoryResult = removeRoomAliasFromRoomDirectoryResult,
|
||||||
avatarUrl = avatarUrl,
|
initialRoomInfo = aRoomInfo(
|
||||||
isDirect = isDirect,
|
name = displayName,
|
||||||
joinRule = joinRule,
|
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
|
@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(
|
val room = aMatrixRoom(
|
||||||
canInviteResult = { Result.success(true) },
|
canInviteResult = { Result.success(true) },
|
||||||
canUserJoinCallResult = { Result.success(true) },
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
|
@ -118,22 +118,22 @@ class RoomDetailsPresenterTest {
|
||||||
)
|
)
|
||||||
val presenter = createRoomDetailsPresenter(room)
|
val presenter = createRoomDetailsPresenter(room)
|
||||||
presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) {
|
presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) {
|
||||||
skipItems(1)
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.roomId).isEqualTo(room.roomId)
|
assertThat(initialState.roomId).isEqualTo(room.roomId)
|
||||||
assertThat(initialState.roomName).isEqualTo(room.displayName)
|
assertThat(initialState.roomName).isEqualTo(room.info().name)
|
||||||
assertThat(initialState.roomAvatarUrl).isEqualTo(room.avatarUrl)
|
assertThat(initialState.roomAvatarUrl).isEqualTo(room.info().avatarUrl)
|
||||||
assertThat(initialState.roomTopic).isEqualTo(RoomTopicState.ExistingTopic(room.topic!!))
|
assertThat(initialState.roomTopic).isEqualTo(RoomTopicState.ExistingTopic(room.info().topic!!))
|
||||||
assertThat(initialState.memberCount).isEqualTo(room.joinedMemberCount)
|
assertThat(initialState.memberCount).isEqualTo(room.info().joinedMembersCount)
|
||||||
assertThat(initialState.isEncrypted).isEqualTo(room.isEncrypted)
|
|
||||||
assertThat(initialState.canShowPinnedMessages).isTrue()
|
assertThat(initialState.canShowPinnedMessages).isTrue()
|
||||||
assertThat(initialState.pinnedMessagesCount).isNull()
|
assertThat(initialState.pinnedMessagesCount).isEqualTo(0)
|
||||||
assertThat(initialState.canShowSecurityAndPrivacy).isFalse()
|
assertThat(initialState.canShowSecurityAndPrivacy).isFalse()
|
||||||
|
|
||||||
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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(
|
val roomInfo = aRoomInfo(
|
||||||
name = A_ROOM_NAME,
|
name = A_ROOM_NAME,
|
||||||
topic = A_ROOM_TOPIC,
|
topic = A_ROOM_TOPIC,
|
||||||
|
|
@ -170,7 +170,7 @@ class RoomDetailsPresenterTest {
|
||||||
val presenter = createRoomDetailsPresenter(room)
|
val presenter = createRoomDetailsPresenter(room)
|
||||||
presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) {
|
presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.roomName).isEqualTo(room.displayName)
|
assertThat(initialState.roomName).isEqualTo(room.info().name)
|
||||||
|
|
||||||
cancelAndIgnoreRemainingEvents()
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
|
|
@ -181,8 +181,6 @@ class RoomDetailsPresenterTest {
|
||||||
val myRoomMember = aRoomMember(A_SESSION_ID)
|
val myRoomMember = aRoomMember(A_SESSION_ID)
|
||||||
val otherRoomMember = aRoomMember(A_USER_ID_2)
|
val otherRoomMember = aRoomMember(A_USER_ID_2)
|
||||||
val room = aMatrixRoom(
|
val room = aMatrixRoom(
|
||||||
isEncrypted = true,
|
|
||||||
isDirect = true,
|
|
||||||
canInviteResult = { Result.success(true) },
|
canInviteResult = { Result.success(true) },
|
||||||
canUserJoinCallResult = { Result.success(true) },
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
canSendStateResult = { _, _ -> Result.success(true) },
|
canSendStateResult = { _, _ -> Result.success(true) },
|
||||||
|
|
@ -196,6 +194,13 @@ class RoomDetailsPresenterTest {
|
||||||
).apply {
|
).apply {
|
||||||
val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
|
val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
|
||||||
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
|
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
|
||||||
|
|
||||||
|
givenRoomInfo(
|
||||||
|
aRoomInfo(
|
||||||
|
isEncrypted = true,
|
||||||
|
isDirect = true,
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
val presenter = createRoomDetailsPresenter(room)
|
val presenter = createRoomDetailsPresenter(room)
|
||||||
presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) {
|
presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) {
|
||||||
|
|
@ -287,8 +292,6 @@ class RoomDetailsPresenterTest {
|
||||||
val myRoomMember = aRoomMember(A_SESSION_ID)
|
val myRoomMember = aRoomMember(A_SESSION_ID)
|
||||||
val otherRoomMember = aRoomMember(A_USER_ID_2)
|
val otherRoomMember = aRoomMember(A_USER_ID_2)
|
||||||
val room = aMatrixRoom(
|
val room = aMatrixRoom(
|
||||||
isEncrypted = true,
|
|
||||||
isDirect = true,
|
|
||||||
canSendStateResult = { _, stateEventType ->
|
canSendStateResult = { _, stateEventType ->
|
||||||
when (stateEventType) {
|
when (stateEventType) {
|
||||||
StateEventType.ROOM_TOPIC,
|
StateEventType.ROOM_TOPIC,
|
||||||
|
|
@ -309,6 +312,13 @@ class RoomDetailsPresenterTest {
|
||||||
).apply {
|
).apply {
|
||||||
val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
|
val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
|
||||||
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
|
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
|
||||||
|
|
||||||
|
givenRoomInfo(
|
||||||
|
aRoomInfo(
|
||||||
|
isEncrypted = true,
|
||||||
|
isDirect = true,
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
val presenter = createRoomDetailsPresenter(room)
|
val presenter = createRoomDetailsPresenter(room)
|
||||||
presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) {
|
presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) {
|
||||||
|
|
@ -318,7 +328,7 @@ class RoomDetailsPresenterTest {
|
||||||
val settledState = awaitItem()
|
val settledState = awaitItem()
|
||||||
assertThat(settledState.canEdit).isFalse()
|
assertThat(settledState.canEdit).isFalse()
|
||||||
// If there is a topic, it's visible
|
// 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()
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
|
|
@ -329,7 +339,6 @@ class RoomDetailsPresenterTest {
|
||||||
val myRoomMember = aRoomMember(A_SESSION_ID)
|
val myRoomMember = aRoomMember(A_SESSION_ID)
|
||||||
val otherRoomMember = aRoomMember(A_USER_ID_2)
|
val otherRoomMember = aRoomMember(A_USER_ID_2)
|
||||||
val room = aMatrixRoom(
|
val room = aMatrixRoom(
|
||||||
isEncrypted = true,
|
|
||||||
isDirect = true,
|
isDirect = true,
|
||||||
topic = null,
|
topic = null,
|
||||||
canSendStateResult = { _, stateEventType ->
|
canSendStateResult = { _, stateEventType ->
|
||||||
|
|
@ -352,6 +361,14 @@ class RoomDetailsPresenterTest {
|
||||||
).apply {
|
).apply {
|
||||||
val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
|
val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
|
||||||
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
|
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
|
||||||
|
|
||||||
|
givenRoomInfo(
|
||||||
|
aRoomInfo(
|
||||||
|
isDirect = true,
|
||||||
|
activeMembersCount = 2,
|
||||||
|
topic = null,
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val presenter = createRoomDetailsPresenter(room)
|
val presenter = createRoomDetailsPresenter(room)
|
||||||
|
|
@ -630,7 +647,6 @@ class RoomDetailsPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - show knock requests`() = runTest {
|
fun `present - show knock requests`() = runTest {
|
||||||
val room = aMatrixRoom(
|
val room = aMatrixRoom(
|
||||||
emitRoomInfo = true,
|
|
||||||
canInviteResult = { Result.success(true) },
|
canInviteResult = { Result.success(true) },
|
||||||
canUserJoinCallResult = { Result.success(true) },
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
canSendStateResult = { _, _ -> Result.success(true) },
|
canSendStateResult = { _, _ -> Result.success(true) },
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,6 @@ class RoomDetailsEditPresenterTest {
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
displayName = A_ROOM_NAME,
|
displayName = A_ROOM_NAME,
|
||||||
rawName = A_ROOM_RAW_NAME,
|
rawName = A_ROOM_RAW_NAME,
|
||||||
emitRoomInfo = true,
|
|
||||||
canSendStateResult = { _, _ -> Result.success(true) }
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
)
|
)
|
||||||
val deleteCallback = lambdaRecorder<Uri?, Unit> {}
|
val deleteCallback = lambdaRecorder<Uri?, Unit> {}
|
||||||
|
|
@ -106,7 +105,7 @@ class RoomDetailsEditPresenterTest {
|
||||||
assertThat(initialState.roomId).isEqualTo(room.roomId)
|
assertThat(initialState.roomId).isEqualTo(room.roomId)
|
||||||
assertThat(initialState.roomRawName).isEqualTo(A_ROOM_RAW_NAME)
|
assertThat(initialState.roomRawName).isEqualTo(A_ROOM_RAW_NAME)
|
||||||
assertThat(initialState.roomAvatarUrl).isEqualTo(roomAvatarUri)
|
assertThat(initialState.roomAvatarUrl).isEqualTo(roomAvatarUri)
|
||||||
assertThat(initialState.roomTopic).isEqualTo(room.topic.orEmpty())
|
assertThat(initialState.roomTopic).isEqualTo(room.info().topic.orEmpty())
|
||||||
assertThat(initialState.avatarActions).containsExactly(
|
assertThat(initialState.avatarActions).containsExactly(
|
||||||
AvatarAction.ChoosePhoto,
|
AvatarAction.ChoosePhoto,
|
||||||
AvatarAction.TakePhoto,
|
AvatarAction.TakePhoto,
|
||||||
|
|
@ -220,7 +219,6 @@ class RoomDetailsEditPresenterTest {
|
||||||
topic = "My topic",
|
topic = "My topic",
|
||||||
displayName = "Name",
|
displayName = "Name",
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
emitRoomInfo = true,
|
|
||||||
canSendStateResult = { _, _ -> Result.success(true) }
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
)
|
)
|
||||||
val deleteCallback = lambdaRecorder<Uri?, Unit> {}
|
val deleteCallback = lambdaRecorder<Uri?, Unit> {}
|
||||||
|
|
@ -266,7 +264,6 @@ class RoomDetailsEditPresenterTest {
|
||||||
topic = "My topic",
|
topic = "My topic",
|
||||||
displayName = "Name",
|
displayName = "Name",
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
emitRoomInfo = true,
|
|
||||||
canSendStateResult = { _, _ -> Result.success(true) }
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
)
|
)
|
||||||
fakePickerProvider.givenResult(anotherAvatarUri)
|
fakePickerProvider.givenResult(anotherAvatarUri)
|
||||||
|
|
@ -291,7 +288,6 @@ class RoomDetailsEditPresenterTest {
|
||||||
topic = "My topic",
|
topic = "My topic",
|
||||||
displayName = "Name",
|
displayName = "Name",
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
emitRoomInfo = true,
|
|
||||||
canSendStateResult = { _, _ -> Result.success(true) }
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
)
|
)
|
||||||
fakePickerProvider.givenResult(anotherAvatarUri)
|
fakePickerProvider.givenResult(anotherAvatarUri)
|
||||||
|
|
@ -319,8 +315,7 @@ class RoomDetailsEditPresenterTest {
|
||||||
stateWithNewAvatar.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.TakePhoto))
|
stateWithNewAvatar.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.TakePhoto))
|
||||||
val stateWithNewAvatar2 = awaitItem()
|
val stateWithNewAvatar2 = awaitItem()
|
||||||
assertThat(stateWithNewAvatar2.roomAvatarUrl).isEqualTo(roomAvatarUri)
|
assertThat(stateWithNewAvatar2.roomAvatarUrl).isEqualTo(roomAvatarUri)
|
||||||
deleteCallback.assertions().isCalledExactly(4).withSequence(
|
deleteCallback.assertions().isCalledExactly(3).withSequence(
|
||||||
listOf(value(null)),
|
|
||||||
listOf(value(null)),
|
listOf(value(null)),
|
||||||
listOf(value(roomAvatarUri)),
|
listOf(value(roomAvatarUri)),
|
||||||
listOf(value(anotherAvatarUri)),
|
listOf(value(anotherAvatarUri)),
|
||||||
|
|
@ -334,7 +329,6 @@ class RoomDetailsEditPresenterTest {
|
||||||
topic = "My topic",
|
topic = "My topic",
|
||||||
displayName = "Name",
|
displayName = "Name",
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
emitRoomInfo = true,
|
|
||||||
canSendStateResult = { _, _ -> Result.success(true) }
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
)
|
)
|
||||||
fakePickerProvider.givenResult(roomAvatarUri)
|
fakePickerProvider.givenResult(roomAvatarUri)
|
||||||
|
|
@ -385,7 +379,6 @@ class RoomDetailsEditPresenterTest {
|
||||||
topic = null,
|
topic = null,
|
||||||
displayName = "fallback",
|
displayName = "fallback",
|
||||||
avatarUrl = null,
|
avatarUrl = null,
|
||||||
emitRoomInfo = true,
|
|
||||||
canSendStateResult = { _, _ -> Result.success(true) }
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
)
|
)
|
||||||
fakePickerProvider.givenResult(roomAvatarUri)
|
fakePickerProvider.givenResult(roomAvatarUri)
|
||||||
|
|
@ -439,7 +432,6 @@ class RoomDetailsEditPresenterTest {
|
||||||
topic = "My topic",
|
topic = "My topic",
|
||||||
displayName = "Name",
|
displayName = "Name",
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
emitRoomInfo = true,
|
|
||||||
setNameResult = setNameResult,
|
setNameResult = setNameResult,
|
||||||
setTopicResult = setTopicResult,
|
setTopicResult = setTopicResult,
|
||||||
removeAvatarResult = removeAvatarResult,
|
removeAvatarResult = removeAvatarResult,
|
||||||
|
|
@ -553,7 +545,7 @@ class RoomDetailsEditPresenterTest {
|
||||||
updateAvatarResult.assertions().isCalledOnce().with(value(MimeTypes.Jpeg), value(fakeFileContents))
|
updateAvatarResult.assertions().isCalledOnce().with(value(MimeTypes.Jpeg), value(fakeFileContents))
|
||||||
deleteCallback.assertions().isCalledExactly(2).withSequence(
|
deleteCallback.assertions().isCalledExactly(2).withSequence(
|
||||||
listOf(value(null)),
|
listOf(value(null)),
|
||||||
listOf(value(null)),
|
listOf(value(roomAvatarUri)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -588,11 +580,10 @@ class RoomDetailsEditPresenterTest {
|
||||||
topic = "My topic",
|
topic = "My topic",
|
||||||
displayName = "Name",
|
displayName = "Name",
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
emitRoomInfo = true,
|
|
||||||
setNameResult = { Result.failure(Throwable("!")) },
|
setNameResult = { Result.failure(Throwable("!")) },
|
||||||
canSendStateResult = { _, _ -> Result.success(true) }
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
)
|
)
|
||||||
saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomName("New name"))
|
saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomName("New name"), deleteCallbackNumberOfInvocation = 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -601,11 +592,10 @@ class RoomDetailsEditPresenterTest {
|
||||||
topic = "My topic",
|
topic = "My topic",
|
||||||
displayName = "Name",
|
displayName = "Name",
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
emitRoomInfo = true,
|
|
||||||
setTopicResult = { Result.failure(Throwable("!")) },
|
setTopicResult = { Result.failure(Throwable("!")) },
|
||||||
canSendStateResult = { _, _ -> Result.success(true) }
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
)
|
)
|
||||||
saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomTopic("New topic"))
|
saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomTopic("New topic"), deleteCallbackNumberOfInvocation = 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -614,11 +604,10 @@ class RoomDetailsEditPresenterTest {
|
||||||
topic = "My topic",
|
topic = "My topic",
|
||||||
displayName = "Name",
|
displayName = "Name",
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
emitRoomInfo = true,
|
|
||||||
removeAvatarResult = { Result.failure(Throwable("!")) },
|
removeAvatarResult = { Result.failure(Throwable("!")) },
|
||||||
canSendStateResult = { _, _ -> Result.success(true) }
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
)
|
)
|
||||||
saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove), deleteCallbackNumberOfInvocation = 3)
|
saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove), deleteCallbackNumberOfInvocation = 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -628,11 +617,10 @@ class RoomDetailsEditPresenterTest {
|
||||||
topic = "My topic",
|
topic = "My topic",
|
||||||
displayName = "Name",
|
displayName = "Name",
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
emitRoomInfo = true,
|
|
||||||
updateAvatarResult = { _, _ -> Result.failure(Throwable("!")) },
|
updateAvatarResult = { _, _ -> Result.failure(Throwable("!")) },
|
||||||
canSendStateResult = { _, _ -> Result.success(true) }
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
)
|
)
|
||||||
saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto), deleteCallbackNumberOfInvocation = 3)
|
saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto), deleteCallbackNumberOfInvocation = 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -704,6 +692,6 @@ class RoomDetailsEditPresenterTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun <T> ReceiveTurbine<T>.awaitFirstItem(): T {
|
private suspend fun <T> ReceiveTurbine<T>.awaitFirstItem(): T {
|
||||||
skipItems(2)
|
skipItems(1)
|
||||||
return awaitItem()
|
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.A_USER_ID
|
||||||
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
|
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.FakeMatrixRoom
|
||||||
|
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
import io.element.android.tests.testutils.consumeItemsUntilPredicate
|
import io.element.android.tests.testutils.consumeItemsUntilPredicate
|
||||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
|
|
@ -167,10 +168,10 @@ class RoomMemberDetailsPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - when user's identity is verified, the value in the state is VERIFIED`() = runTest {
|
fun `present - when user's identity is verified, the value in the state is VERIFIED`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = FakeMatrixRoom(
|
||||||
isEncrypted = true,
|
|
||||||
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
||||||
userDisplayNameResult = { Result.success("A custom name") },
|
userDisplayNameResult = { Result.success("A custom name") },
|
||||||
userAvatarUrlResult = { Result.success("A custom avatar") },
|
userAvatarUrlResult = { Result.success("A custom avatar") },
|
||||||
|
initialRoomInfo = aRoomInfo(isEncrypted = true),
|
||||||
)
|
)
|
||||||
val encryptionService = FakeEncryptionService(
|
val encryptionService = FakeEncryptionService(
|
||||||
getUserIdentityResult = { Result.success(IdentityState.Verified) },
|
getUserIdentityResult = { Result.success(IdentityState.Verified) },
|
||||||
|
|
@ -186,10 +187,10 @@ class RoomMemberDetailsPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - when user's identity is unknown, the value in the state is UNKNOWN`() = runTest {
|
fun `present - when user's identity is unknown, the value in the state is UNKNOWN`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = FakeMatrixRoom(
|
||||||
isEncrypted = true,
|
|
||||||
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
||||||
userDisplayNameResult = { Result.success("A custom name") },
|
userDisplayNameResult = { Result.success("A custom name") },
|
||||||
userAvatarUrlResult = { Result.success("A custom avatar") },
|
userAvatarUrlResult = { Result.success("A custom avatar") },
|
||||||
|
initialRoomInfo = aRoomInfo(isEncrypted = true),
|
||||||
)
|
)
|
||||||
val encryptionService = FakeEncryptionService(
|
val encryptionService = FakeEncryptionService(
|
||||||
getUserIdentityResult = { Result.success(null) },
|
getUserIdentityResult = { Result.success(null) },
|
||||||
|
|
@ -205,10 +206,10 @@ class RoomMemberDetailsPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - when user's identity is pinned, the value in the state is UNVERIFIED`() = runTest {
|
fun `present - when user's identity is pinned, the value in the state is UNVERIFIED`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = FakeMatrixRoom(
|
||||||
isEncrypted = true,
|
|
||||||
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
||||||
userDisplayNameResult = { Result.success("A custom name") },
|
userDisplayNameResult = { Result.success("A custom name") },
|
||||||
userAvatarUrlResult = { Result.success("A custom avatar") },
|
userAvatarUrlResult = { Result.success("A custom avatar") },
|
||||||
|
initialRoomInfo = aRoomInfo(isEncrypted = true),
|
||||||
)
|
)
|
||||||
val encryptionService = FakeEncryptionService(
|
val encryptionService = FakeEncryptionService(
|
||||||
getUserIdentityResult = { Result.success(IdentityState.Pinned) },
|
getUserIdentityResult = { Result.success(IdentityState.Pinned) },
|
||||||
|
|
@ -224,10 +225,10 @@ class RoomMemberDetailsPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - when user's identity is pin violation, the value in the state is UNVERIFIED`() = runTest {
|
fun `present - when user's identity is pin violation, the value in the state is UNVERIFIED`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = FakeMatrixRoom(
|
||||||
isEncrypted = true,
|
|
||||||
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
||||||
userDisplayNameResult = { Result.success("A custom name") },
|
userDisplayNameResult = { Result.success("A custom name") },
|
||||||
userAvatarUrlResult = { Result.success("A custom avatar") },
|
userAvatarUrlResult = { Result.success("A custom avatar") },
|
||||||
|
initialRoomInfo = aRoomInfo(isEncrypted = true),
|
||||||
)
|
)
|
||||||
val encryptionService = FakeEncryptionService(
|
val encryptionService = FakeEncryptionService(
|
||||||
getUserIdentityResult = { Result.success(IdentityState.PinViolation) },
|
getUserIdentityResult = { Result.success(IdentityState.PinViolation) },
|
||||||
|
|
@ -243,10 +244,10 @@ class RoomMemberDetailsPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - when user's identity has a verification violation, the value in the state is VERIFICATION_VIOLATION`() = runTest {
|
fun `present - when user's identity has a verification violation, the value in the state is VERIFICATION_VIOLATION`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = FakeMatrixRoom(
|
||||||
isEncrypted = true,
|
|
||||||
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
||||||
userDisplayNameResult = { Result.success("A custom name") },
|
userDisplayNameResult = { Result.success("A custom name") },
|
||||||
userAvatarUrlResult = { Result.success("A custom avatar") },
|
userAvatarUrlResult = { Result.success("A custom avatar") },
|
||||||
|
initialRoomInfo = aRoomInfo(isEncrypted = true),
|
||||||
)
|
)
|
||||||
val encryptionService = FakeEncryptionService(
|
val encryptionService = FakeEncryptionService(
|
||||||
getUserIdentityResult = { Result.success(IdentityState.VerificationViolation) },
|
getUserIdentityResult = { Result.success(IdentityState.VerificationViolation) },
|
||||||
|
|
@ -262,10 +263,10 @@ class RoomMemberDetailsPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - user identity updates in real time if the room is encrypted`() = runTest {
|
fun `present - user identity updates in real time if the room is encrypted`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = FakeMatrixRoom(
|
||||||
isEncrypted = true,
|
|
||||||
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
||||||
userDisplayNameResult = { Result.success("A custom name") },
|
userDisplayNameResult = { Result.success("A custom name") },
|
||||||
userAvatarUrlResult = { Result.success("A custom avatar") },
|
userAvatarUrlResult = { Result.success("A custom avatar") },
|
||||||
|
initialRoomInfo = aRoomInfo(isEncrypted = true),
|
||||||
)
|
)
|
||||||
val encryptionService = FakeEncryptionService(
|
val encryptionService = FakeEncryptionService(
|
||||||
getUserIdentityResult = { Result.success(null) },
|
getUserIdentityResult = { Result.success(null) },
|
||||||
|
|
@ -291,10 +292,10 @@ class RoomMemberDetailsPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - user identity can't update in real time if the room is not encrypted`() = runTest {
|
fun `present - user identity can't update in real time if the room is not encrypted`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = FakeMatrixRoom(
|
||||||
isEncrypted = false,
|
|
||||||
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
||||||
userDisplayNameResult = { Result.success("A custom name") },
|
userDisplayNameResult = { Result.success("A custom name") },
|
||||||
userAvatarUrlResult = { Result.success("A custom avatar") },
|
userAvatarUrlResult = { Result.success("A custom avatar") },
|
||||||
|
initialRoomInfo = aRoomInfo(isEncrypted = false),
|
||||||
)
|
)
|
||||||
val encryptionService = FakeEncryptionService(
|
val encryptionService = FakeEncryptionService(
|
||||||
getUserIdentityResult = { Result.success(null) },
|
getUserIdentityResult = { Result.success(null) },
|
||||||
|
|
@ -315,10 +316,10 @@ class RoomMemberDetailsPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - handles WithdrawVerification action`() = runTest {
|
fun `present - handles WithdrawVerification action`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = FakeMatrixRoom(
|
||||||
isEncrypted = true,
|
|
||||||
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) },
|
||||||
userDisplayNameResult = { Result.success("A custom name") },
|
userDisplayNameResult = { Result.success("A custom name") },
|
||||||
userAvatarUrlResult = { Result.success("A custom avatar") },
|
userAvatarUrlResult = { Result.success("A custom avatar") },
|
||||||
|
initialRoomInfo = aRoomInfo(isEncrypted = true),
|
||||||
)
|
)
|
||||||
val withdrawVerificationResult = lambdaRecorder<UserId, Result<Unit>> { Result.success(Unit) }
|
val withdrawVerificationResult = lambdaRecorder<UserId, Result<Unit>> { Result.success(Unit) }
|
||||||
val encryptionService = FakeEncryptionService(
|
val encryptionService = FakeEncryptionService(
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import app.cash.molecule.moleculeFlow
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import im.vector.app.features.analytics.plan.RoomModeration
|
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.aRoomMember
|
||||||
import io.element.android.features.roomdetails.impl.members.aVictor
|
import io.element.android.features.roomdetails.impl.members.aVictor
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
|
|
@ -34,8 +35,7 @@ import org.junit.Test
|
||||||
class RoomMembersModerationPresenterTest {
|
class RoomMembersModerationPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `canDisplayModerationActions - when room is DM is false`() = runTest {
|
fun `canDisplayModerationActions - when room is DM is false`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = aMatrixRoom(
|
||||||
isDirect = true,
|
|
||||||
isPublic = true,
|
isPublic = true,
|
||||||
activeMemberCount = 2,
|
activeMemberCount = 2,
|
||||||
canKickResult = { Result.success(true) },
|
canKickResult = { Result.success(true) },
|
||||||
|
|
@ -52,8 +52,7 @@ class RoomMembersModerationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `canDisplayModerationActions - when user can kick other users, FF is enabled and room is not a DM returns true`() = runTest {
|
fun `canDisplayModerationActions - when user can kick other users, FF is enabled and room is not a DM returns true`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = aMatrixRoom(
|
||||||
isDirect = false,
|
|
||||||
activeMemberCount = 10,
|
activeMemberCount = 10,
|
||||||
canKickResult = { Result.success(true) },
|
canKickResult = { Result.success(true) },
|
||||||
canBanResult = { Result.success(true) },
|
canBanResult = { Result.success(true) },
|
||||||
|
|
@ -68,8 +67,7 @@ class RoomMembersModerationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `canDisplayModerationActions - when user can ban other users, FF is enabled and room is not a DM returns true`() = runTest {
|
fun `canDisplayModerationActions - when user can ban other users, FF is enabled and room is not a DM returns true`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = aMatrixRoom(
|
||||||
isDirect = false,
|
|
||||||
activeMemberCount = 10,
|
activeMemberCount = 10,
|
||||||
canKickResult = { Result.success(true) },
|
canKickResult = { Result.success(true) },
|
||||||
canBanResult = { Result.success(true) },
|
canBanResult = { Result.success(true) },
|
||||||
|
|
@ -84,7 +82,7 @@ class RoomMembersModerationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - SelectRoomMember when the current user has permissions displays member actions`() = runTest {
|
fun `present - SelectRoomMember when the current user has permissions displays member actions`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = aMatrixRoom(
|
||||||
canKickResult = { Result.success(true) },
|
canKickResult = { Result.success(true) },
|
||||||
canBanResult = { Result.success(true) },
|
canBanResult = { Result.success(true) },
|
||||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||||
|
|
@ -110,7 +108,7 @@ class RoomMembersModerationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - SelectRoomMember displays only view profile if selected member has same power level as the current user`() = runTest {
|
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,
|
sessionId = A_USER_ID,
|
||||||
canKickResult = { Result.success(true) },
|
canKickResult = { Result.success(true) },
|
||||||
canBanResult = { Result.success(true) },
|
canBanResult = { Result.success(true) },
|
||||||
|
|
@ -136,7 +134,7 @@ class RoomMembersModerationPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - SelectRoomMember displays an unban confirmation dialog when the member is banned`() = runTest {
|
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 selectedMember = aRoomMember(A_USER_ID_2, membership = RoomMembershipState.BAN)
|
||||||
val room = FakeMatrixRoom(
|
val room = aMatrixRoom(
|
||||||
canKickResult = { Result.success(true) },
|
canKickResult = { Result.success(true) },
|
||||||
canBanResult = { Result.success(true) },
|
canBanResult = { Result.success(true) },
|
||||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||||
|
|
@ -157,7 +155,7 @@ class RoomMembersModerationPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - Kick removes the user`() = runTest {
|
fun `present - Kick removes the user`() = runTest {
|
||||||
val analyticsService = FakeAnalyticsService()
|
val analyticsService = FakeAnalyticsService()
|
||||||
val room = FakeMatrixRoom(
|
val room = aMatrixRoom(
|
||||||
canKickResult = { Result.success(true) },
|
canKickResult = { Result.success(true) },
|
||||||
canBanResult = { Result.success(true) },
|
canBanResult = { Result.success(true) },
|
||||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||||
|
|
@ -186,7 +184,7 @@ class RoomMembersModerationPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - BanUser requires confirmation and then bans the user`() = runTest {
|
fun `present - BanUser requires confirmation and then bans the user`() = runTest {
|
||||||
val analyticsService = FakeAnalyticsService()
|
val analyticsService = FakeAnalyticsService()
|
||||||
val room = FakeMatrixRoom(
|
val room = aMatrixRoom(
|
||||||
canKickResult = { Result.success(true) },
|
canKickResult = { Result.success(true) },
|
||||||
canBanResult = { Result.success(true) },
|
canBanResult = { Result.success(true) },
|
||||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||||
|
|
@ -222,7 +220,7 @@ class RoomMembersModerationPresenterTest {
|
||||||
fun `present - UnbanUser requires confirmation and then unbans the user`() = runTest {
|
fun `present - UnbanUser requires confirmation and then unbans the user`() = runTest {
|
||||||
val analyticsService = FakeAnalyticsService()
|
val analyticsService = FakeAnalyticsService()
|
||||||
val selectedMember = aRoomMember(A_USER_ID_2, membership = RoomMembershipState.BAN)
|
val selectedMember = aRoomMember(A_USER_ID_2, membership = RoomMembershipState.BAN)
|
||||||
val room = FakeMatrixRoom(
|
val room = aMatrixRoom(
|
||||||
canKickResult = { Result.success(true) },
|
canKickResult = { Result.success(true) },
|
||||||
canBanResult = { Result.success(true) },
|
canBanResult = { Result.success(true) },
|
||||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||||
|
|
@ -254,7 +252,7 @@ class RoomMembersModerationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - Reset removes the selected user and actions`() = runTest {
|
fun `present - Reset removes the selected user and actions`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = aMatrixRoom(
|
||||||
canKickResult = { Result.success(true) },
|
canKickResult = { Result.success(true) },
|
||||||
canBanResult = { Result.success(true) },
|
canBanResult = { Result.success(true) },
|
||||||
userRoleResult = { Result.success(RoomMember.Role.USER) },
|
userRoleResult = { Result.success(RoomMember.Role.USER) },
|
||||||
|
|
@ -276,7 +274,7 @@ class RoomMembersModerationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - Reset resets any async actions`() = runTest {
|
fun `present - Reset resets any async actions`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = aMatrixRoom(
|
||||||
canKickResult = { Result.success(true) },
|
canKickResult = { Result.success(true) },
|
||||||
canBanResult = { Result.success(true) },
|
canBanResult = { Result.success(true) },
|
||||||
kickUserResult = { _, _ -> Result.failure(Throwable("Eek")) },
|
kickUserResult = { _, _ -> Result.failure(Throwable("Eek")) },
|
||||||
|
|
@ -324,7 +322,7 @@ class RoomMembersModerationPresenterTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TestScope.createRoomMembersModerationPresenter(
|
private fun TestScope.createRoomMembersModerationPresenter(
|
||||||
matrixRoom: FakeMatrixRoom = FakeMatrixRoom(),
|
matrixRoom: FakeMatrixRoom = aMatrixRoom(),
|
||||||
dispatchers: CoroutineDispatchers = testCoroutineDispatchers(),
|
dispatchers: CoroutineDispatchers = testCoroutineDispatchers(),
|
||||||
analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
|
analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
|
||||||
): RoomMembersModerationPresenter {
|
): RoomMembersModerationPresenter {
|
||||||
|
|
|
||||||
|
|
@ -56,17 +56,15 @@ class SecurityAndPrivacyPresenterTest {
|
||||||
fun `present - room info change updates saved and edited settings`() = runTest {
|
fun `present - room info change updates saved and edited settings`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = FakeMatrixRoom(
|
||||||
canSendStateResult = { _, _ -> Result.success(true) },
|
canSendStateResult = { _, _ -> Result.success(true) },
|
||||||
|
initialRoomInfo = aRoomInfo(
|
||||||
|
joinRule = JoinRule.Public,
|
||||||
|
historyVisibility = RoomHistoryVisibility.WorldReadable,
|
||||||
|
canonicalAlias = A_ROOM_ALIAS,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
val presenter = createSecurityAndPrivacyPresenter(room = room)
|
val presenter = createSecurityAndPrivacyPresenter(room = room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
skipItems(2)
|
skipItems(1)
|
||||||
room.givenRoomInfo(
|
|
||||||
aRoomInfo(
|
|
||||||
joinRule = JoinRule.Public,
|
|
||||||
historyVisibility = RoomHistoryVisibility.WorldReadable,
|
|
||||||
canonicalAlias = A_ROOM_ALIAS,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
with(awaitItem()) {
|
with(awaitItem()) {
|
||||||
assertThat(editedSettings).isEqualTo(savedSettings)
|
assertThat(editedSettings).isEqualTo(savedSettings)
|
||||||
assertThat(editedSettings.roomAccess).isEqualTo(SecurityAndPrivacyRoomAccess.Anyone)
|
assertThat(editedSettings.roomAccess).isEqualTo(SecurityAndPrivacyRoomAccess.Anyone)
|
||||||
|
|
@ -151,6 +149,7 @@ class SecurityAndPrivacyPresenterTest {
|
||||||
assertThat(canBeSaved).isTrue()
|
assertThat(canBeSaved).isTrue()
|
||||||
eventSink(SecurityAndPrivacyEvents.ToggleEncryptionState)
|
eventSink(SecurityAndPrivacyEvents.ToggleEncryptionState)
|
||||||
}
|
}
|
||||||
|
skipItems(1)
|
||||||
with(awaitItem()) {
|
with(awaitItem()) {
|
||||||
assertThat(editedSettings.isEncrypted).isFalse()
|
assertThat(editedSettings.isEncrypted).isFalse()
|
||||||
assertThat(canBeSaved).isFalse()
|
assertThat(canBeSaved).isFalse()
|
||||||
|
|
@ -162,7 +161,8 @@ class SecurityAndPrivacyPresenterTest {
|
||||||
fun `present - room visibility loading and change`() = runTest {
|
fun `present - room visibility loading and change`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = FakeMatrixRoom(
|
||||||
canSendStateResult = { _, _ -> Result.success(true) },
|
canSendStateResult = { _, _ -> Result.success(true) },
|
||||||
roomVisibilityResult = { Result.success(RoomVisibility.Private) }
|
roomVisibilityResult = { Result.success(RoomVisibility.Private) },
|
||||||
|
initialRoomInfo = aRoomInfo(historyVisibility = RoomHistoryVisibility.Shared)
|
||||||
)
|
)
|
||||||
val presenter = createSecurityAndPrivacyPresenter(room = room)
|
val presenter = createSecurityAndPrivacyPresenter(room = room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
|
|
@ -212,7 +212,8 @@ class SecurityAndPrivacyPresenterTest {
|
||||||
updateJoinRuleResult = updateJoinRuleLambda,
|
updateJoinRuleResult = updateJoinRuleLambda,
|
||||||
updateRoomVisibilityResult = updateRoomVisibilityLambda,
|
updateRoomVisibilityResult = updateRoomVisibilityLambda,
|
||||||
updateRoomHistoryVisibilityResult = updateRoomHistoryVisibilityLambda,
|
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)
|
val presenter = createSecurityAndPrivacyPresenter(room = room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
|
|
@ -245,6 +246,7 @@ class SecurityAndPrivacyPresenterTest {
|
||||||
aRoomInfo(
|
aRoomInfo(
|
||||||
joinRule = JoinRule.Public,
|
joinRule = JoinRule.Public,
|
||||||
historyVisibility = RoomHistoryVisibility.WorldReadable,
|
historyVisibility = RoomHistoryVisibility.WorldReadable,
|
||||||
|
isEncrypted = true,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
// Saved settings are updated 3 times to match the edited settings
|
// Saved settings are updated 3 times to match the edited settings
|
||||||
|
|
@ -275,7 +277,8 @@ class SecurityAndPrivacyPresenterTest {
|
||||||
updateJoinRuleResult = updateJoinRuleLambda,
|
updateJoinRuleResult = updateJoinRuleLambda,
|
||||||
updateRoomVisibilityResult = updateRoomVisibilityLambda,
|
updateRoomVisibilityResult = updateRoomVisibilityLambda,
|
||||||
updateRoomHistoryVisibilityResult = updateRoomHistoryVisibilityLambda,
|
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)
|
val presenter = createSecurityAndPrivacyPresenter(room = room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
|
|
@ -311,7 +314,7 @@ class SecurityAndPrivacyPresenterTest {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
// Saved settings are updated 2 times to match the edited settings
|
// Saved settings are updated 2 times to match the edited settings
|
||||||
skipItems(2)
|
skipItems(3)
|
||||||
with(awaitItem()) {
|
with(awaitItem()) {
|
||||||
assertThat(saveAction).isInstanceOf(AsyncAction.Failure::class.java)
|
assertThat(saveAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||||
assertThat(savedSettings.isVisibleInRoomDirectory).isNotEqualTo(editedSettings.isVisibleInRoomDirectory)
|
assertThat(savedSettings.isVisibleInRoomDirectory).isNotEqualTo(editedSettings.isVisibleInRoomDirectory)
|
||||||
|
|
@ -328,7 +331,8 @@ class SecurityAndPrivacyPresenterTest {
|
||||||
serverName: String = "matrix.org",
|
serverName: String = "matrix.org",
|
||||||
room: MatrixRoom = FakeMatrixRoom(
|
room: MatrixRoom = FakeMatrixRoom(
|
||||||
canSendStateResult = { _, _ -> Result.success(true) },
|
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(),
|
navigator: SecurityAndPrivacyNavigator = FakeSecurityAndPrivacyNavigator(),
|
||||||
): SecurityAndPrivacyPresenter {
|
): SecurityAndPrivacyPresenter {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
package io.element.android.features.roomdetails.impl.securityandprivacy.editroomaddress
|
package io.element.android.features.roomdetails.impl.securityandprivacy.editroomaddress
|
||||||
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
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.FakeSecurityAndPrivacyNavigator
|
||||||
import io.element.android.features.roomdetails.impl.securityandprivacy.SecurityAndPrivacyNavigator
|
import io.element.android.features.roomdetails.impl.securityandprivacy.SecurityAndPrivacyNavigator
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
|
|
@ -31,7 +32,9 @@ import java.util.Optional
|
||||||
class EditRoomAddressPresenterTest {
|
class EditRoomAddressPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state no address`() = runTest {
|
fun `present - initial state no address`() = runTest {
|
||||||
val presenter = createEditRoomAddressPresenter()
|
val presenter = createEditRoomAddressPresenter(
|
||||||
|
room = aMatrixRoom(displayName = "")
|
||||||
|
)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
with(awaitItem()) {
|
with(awaitItem()) {
|
||||||
assertThat(homeserverName).isEqualTo("matrix.org")
|
assertThat(homeserverName).isEqualTo("matrix.org")
|
||||||
|
|
@ -45,7 +48,7 @@ class EditRoomAddressPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state address matching own homeserver`() = runTest {
|
fun `present - initial state address matching own homeserver`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = aMatrixRoom(
|
||||||
canonicalAlias = RoomAlias("#canonical:matrix.org"),
|
canonicalAlias = RoomAlias("#canonical:matrix.org"),
|
||||||
)
|
)
|
||||||
val presenter = createEditRoomAddressPresenter(room = room)
|
val presenter = createEditRoomAddressPresenter(room = room)
|
||||||
|
|
@ -62,7 +65,8 @@ class EditRoomAddressPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state address not matching own homeserver`() = runTest {
|
fun `present - initial state address not matching own homeserver`() = runTest {
|
||||||
val room = FakeMatrixRoom(
|
val room = aMatrixRoom(
|
||||||
|
displayName = "",
|
||||||
canonicalAlias = RoomAlias("#canonical:notmatrix.org"),
|
canonicalAlias = RoomAlias("#canonical:notmatrix.org"),
|
||||||
)
|
)
|
||||||
val presenter = createEditRoomAddressPresenter(room = room)
|
val presenter = createEditRoomAddressPresenter(room = room)
|
||||||
|
|
@ -190,7 +194,7 @@ class EditRoomAddressPresenterTest {
|
||||||
|
|
||||||
val navigator = FakeSecurityAndPrivacyNavigator(closeEditRoomAddressLambda = closeEditAddressLambda)
|
val navigator = FakeSecurityAndPrivacyNavigator(closeEditRoomAddressLambda = closeEditAddressLambda)
|
||||||
val canonicalAlias = RoomAlias("#canonical:matrix.org")
|
val canonicalAlias = RoomAlias("#canonical:matrix.org")
|
||||||
val room = FakeMatrixRoom(
|
val room = aMatrixRoom(
|
||||||
canonicalAlias = canonicalAlias,
|
canonicalAlias = canonicalAlias,
|
||||||
updateCanonicalAliasResult = updateCanonicalAliasResult,
|
updateCanonicalAliasResult = updateCanonicalAliasResult,
|
||||||
publishRoomAliasInRoomDirectoryResult = publishAliasInRoomDirectoryResult,
|
publishRoomAliasInRoomDirectoryResult = publishAliasInRoomDirectoryResult,
|
||||||
|
|
@ -240,7 +244,7 @@ class EditRoomAddressPresenterTest {
|
||||||
|
|
||||||
val navigator = FakeSecurityAndPrivacyNavigator(closeEditRoomAddressLambda = closeEditAddressLambda)
|
val navigator = FakeSecurityAndPrivacyNavigator(closeEditRoomAddressLambda = closeEditAddressLambda)
|
||||||
val canonicalAlias = RoomAlias("#canonical:notmatrix.org")
|
val canonicalAlias = RoomAlias("#canonical:notmatrix.org")
|
||||||
val room = FakeMatrixRoom(
|
val room = aMatrixRoom(
|
||||||
canonicalAlias = canonicalAlias,
|
canonicalAlias = canonicalAlias,
|
||||||
updateCanonicalAliasResult = updateCanonicalAliasResult,
|
updateCanonicalAliasResult = updateCanonicalAliasResult,
|
||||||
publishRoomAliasInRoomDirectoryResult = publishAliasInRoomDirectoryResult,
|
publishRoomAliasInRoomDirectoryResult = publishAliasInRoomDirectoryResult,
|
||||||
|
|
@ -314,6 +318,7 @@ class EditRoomAddressPresenterTest {
|
||||||
with(awaitItem()) {
|
with(awaitItem()) {
|
||||||
eventSink(EditRoomAddressEvents.Save)
|
eventSink(EditRoomAddressEvents.Save)
|
||||||
}
|
}
|
||||||
|
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||||
with(awaitItem()) {
|
with(awaitItem()) {
|
||||||
assertThat(saveAction).isInstanceOf(AsyncAction.Failure::class.java)
|
assertThat(saveAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||||
eventSink(EditRoomAddressEvents.DismissError)
|
eventSink(EditRoomAddressEvents.DismissError)
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,12 @@ import io.element.android.tests.testutils.lambda.value
|
||||||
import io.element.android.tests.testutils.test
|
import io.element.android.tests.testutils.test
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.test.TestScope
|
import kotlinx.coroutines.test.TestScope
|
||||||
|
import kotlinx.coroutines.test.advanceTimeBy
|
||||||
import kotlinx.coroutines.test.advanceUntilIdle
|
import kotlinx.coroutines.test.advanceUntilIdle
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
@ExperimentalCoroutinesApi
|
@ExperimentalCoroutinesApi
|
||||||
class IncomingVerificationPresenterTest {
|
class IncomingVerificationPresenterTest {
|
||||||
|
|
@ -61,6 +63,9 @@ class IncomingVerificationPresenterTest {
|
||||||
isWaiting = false,
|
isWaiting = false,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
advanceTimeBy(1.seconds)
|
||||||
|
|
||||||
resetLambda.assertions().isCalledOnce().with(value(false))
|
resetLambda.assertions().isCalledOnce().with(value(false))
|
||||||
acknowledgeVerificationRequestLambda.assertions().isCalledOnce().with(value(anIncomingSessionVerificationRequest))
|
acknowledgeVerificationRequestLambda.assertions().isCalledOnce().with(value(anIncomingSessionVerificationRequest))
|
||||||
acceptVerificationRequestLambda.assertions().isNeverCalled()
|
acceptVerificationRequestLambda.assertions().isNeverCalled()
|
||||||
|
|
@ -69,7 +74,9 @@ class IncomingVerificationPresenterTest {
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val initialWaitingState = awaitItem()
|
val initialWaitingState = awaitItem()
|
||||||
assertThat((initialWaitingState.step as IncomingVerificationState.Step.Initial).isWaiting).isTrue()
|
assertThat((initialWaitingState.step as IncomingVerificationState.Step.Initial).isWaiting).isTrue()
|
||||||
advanceUntilIdle()
|
|
||||||
|
advanceTimeBy(1.seconds)
|
||||||
|
|
||||||
acceptVerificationRequestLambda.assertions().isCalledOnce()
|
acceptVerificationRequestLambda.assertions().isCalledOnce()
|
||||||
// Remote sent the data
|
// Remote sent the data
|
||||||
fakeSessionVerificationService.emitVerificationFlowState(VerificationFlowState.DidAcceptVerificationRequest)
|
fakeSessionVerificationService.emitVerificationFlowState(VerificationFlowState.DidAcceptVerificationRequest)
|
||||||
|
|
@ -218,6 +225,9 @@ class IncomingVerificationPresenterTest {
|
||||||
isWaiting = false,
|
isWaiting = false,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
advanceTimeBy(1.seconds)
|
||||||
|
|
||||||
resetLambda.assertions().isCalledOnce().with(value(false))
|
resetLambda.assertions().isCalledOnce().with(value(false))
|
||||||
acknowledgeVerificationRequestLambda.assertions().isCalledOnce().with(value(anIncomingSessionVerificationRequest))
|
acknowledgeVerificationRequestLambda.assertions().isCalledOnce().with(value(anIncomingSessionVerificationRequest))
|
||||||
acceptVerificationRequestLambda.assertions().isNeverCalled()
|
acceptVerificationRequestLambda.assertions().isNeverCalled()
|
||||||
|
|
@ -226,7 +236,9 @@ class IncomingVerificationPresenterTest {
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val initialWaitingState = awaitItem()
|
val initialWaitingState = awaitItem()
|
||||||
assertThat((initialWaitingState.step as IncomingVerificationState.Step.Initial).isWaiting).isTrue()
|
assertThat((initialWaitingState.step as IncomingVerificationState.Step.Initial).isWaiting).isTrue()
|
||||||
advanceUntilIdle()
|
|
||||||
|
advanceTimeBy(1.seconds)
|
||||||
|
|
||||||
acceptVerificationRequestLambda.assertions().isCalledOnce()
|
acceptVerificationRequestLambda.assertions().isCalledOnce()
|
||||||
// Remote sent the data
|
// Remote sent the data
|
||||||
fakeSessionVerificationService.emitVerificationFlowState(VerificationFlowState.DidAcceptVerificationRequest)
|
fakeSessionVerificationService.emitVerificationFlowState(VerificationFlowState.DidAcceptVerificationRequest)
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,7 @@ jsoup = "org.jsoup:jsoup:1.19.1"
|
||||||
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
|
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
|
||||||
molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0"
|
molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0"
|
||||||
timber = "com.jakewharton.timber:timber:5.0.1"
|
timber = "com.jakewharton.timber:timber:5.0.1"
|
||||||
matrix_sdk = "org.matrix.rustcomponents:sdk-android:25.3.6"
|
matrix_sdk = "org.matrix.rustcomponents:sdk-android:25.3.13"
|
||||||
matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" }
|
matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" }
|
||||||
matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" }
|
matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" }
|
||||||
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
|
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.core.coroutine
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Deferred
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
import kotlin.coroutines.EmptyCoroutineContext
|
||||||
|
|
||||||
|
fun <T> suspendLazy(coroutineContext: CoroutineContext = EmptyCoroutineContext, block: suspend () -> T): Lazy<Deferred<T>> {
|
||||||
|
return lazy(LazyThreadSafetyMode.NONE) {
|
||||||
|
val deferred = CompletableDeferred<T>()
|
||||||
|
CoroutineScope(coroutineContext).launch {
|
||||||
|
deferred.complete(block())
|
||||||
|
}
|
||||||
|
deferred
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,8 +18,8 @@ fun MatrixRoom.toAnalyticsViewRoom(
|
||||||
val activeSpace = selectedSpace?.toActiveSpace() ?: ViewRoom.ActiveSpace.Home
|
val activeSpace = selectedSpace?.toActiveSpace() ?: ViewRoom.ActiveSpace.Home
|
||||||
|
|
||||||
return ViewRoom(
|
return ViewRoom(
|
||||||
isDM = isDirect,
|
isDM = info().isDirect,
|
||||||
isSpace = isSpace,
|
isSpace = info().isSpace,
|
||||||
trigger = trigger,
|
trigger = trigger,
|
||||||
activeSpace = activeSpace,
|
activeSpace = activeSpace,
|
||||||
viaKeyboard = viaKeyboard
|
viaKeyboard = viaKeyboard
|
||||||
|
|
@ -27,5 +27,5 @@ fun MatrixRoom.toAnalyticsViewRoom(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MatrixRoom.toActiveSpace(): ViewRoom.ActiveSpace {
|
private fun MatrixRoom.toActiveSpace(): ViewRoom.ActiveSpace {
|
||||||
return if (isPublic) ViewRoom.ActiveSpace.Public else ViewRoom.ActiveSpace.Private
|
return if (info().isPublic) ViewRoom.ActiveSpace.Public else ViewRoom.ActiveSpace.Private
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,34 +8,12 @@
|
||||||
package io.element.android.libraries.matrix.api.auth
|
package io.element.android.libraries.matrix.api.auth
|
||||||
|
|
||||||
sealed interface OidcPrompt {
|
sealed interface OidcPrompt {
|
||||||
/**
|
|
||||||
* The Authorization Server must not display any authentication or consent
|
|
||||||
* user interface pages.
|
|
||||||
*/
|
|
||||||
data object None : OidcPrompt
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Authorization Server should prompt the End-User for
|
* The Authorization Server should prompt the End-User for
|
||||||
* reauthentication.
|
* reauthentication.
|
||||||
*/
|
*/
|
||||||
data object Login : OidcPrompt
|
data object Login : OidcPrompt
|
||||||
|
|
||||||
/**
|
|
||||||
* The Authorization Server should prompt the End-User for consent before
|
|
||||||
* returning information to the Client.
|
|
||||||
*/
|
|
||||||
data object Consent : OidcPrompt
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Authorization Server should prompt the End-User to select a user
|
|
||||||
* account.
|
|
||||||
*
|
|
||||||
* This enables an End-User who has multiple accounts at the Authorization
|
|
||||||
* Server to select amongst the multiple accounts that they might have
|
|
||||||
* current sessions for.
|
|
||||||
*/
|
|
||||||
data object SelectAccount : OidcPrompt
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Authorization Server should prompt the End-User to create a user
|
* The Authorization Server should prompt the End-User to create a user
|
||||||
* account.
|
* account.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.matrix.api.platform
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.tracing.TracingConfiguration
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This service is responsible for initializing the platform-related settings of the SDK.
|
||||||
|
*/
|
||||||
|
interface InitPlatformService {
|
||||||
|
/**
|
||||||
|
* Initialize the platform-related settings of the SDK.
|
||||||
|
* @param tracingConfiguration the tracing configuration to use for logging.
|
||||||
|
*/
|
||||||
|
fun init(tracingConfiguration: TracingConfiguration)
|
||||||
|
}
|
||||||
|
|
@ -45,21 +45,10 @@ import java.io.File
|
||||||
interface MatrixRoom : Closeable {
|
interface MatrixRoom : Closeable {
|
||||||
val sessionId: SessionId
|
val sessionId: SessionId
|
||||||
val roomId: RoomId
|
val roomId: RoomId
|
||||||
val displayName: String
|
|
||||||
val canonicalAlias: RoomAlias?
|
|
||||||
val alternativeAliases: List<RoomAlias>
|
|
||||||
val topic: String?
|
|
||||||
val avatarUrl: String?
|
|
||||||
val isEncrypted: Boolean
|
|
||||||
val isSpace: Boolean
|
|
||||||
val isDirect: Boolean
|
|
||||||
val isPublic: Boolean
|
|
||||||
val activeMemberCount: Long
|
|
||||||
val joinedMemberCount: Long
|
|
||||||
|
|
||||||
val roomCoroutineScope: CoroutineScope
|
val roomCoroutineScope: CoroutineScope
|
||||||
|
|
||||||
val roomInfoFlow: Flow<MatrixRoomInfo>
|
val roomInfoFlow: StateFlow<MatrixRoomInfo>
|
||||||
val roomTypingMembersFlow: Flow<List<UserId>>
|
val roomTypingMembersFlow: Flow<List<UserId>>
|
||||||
val identityStateChangesFlow: Flow<List<IdentityStateChange>>
|
val identityStateChangesFlow: Flow<List<IdentityStateChange>>
|
||||||
|
|
||||||
|
|
@ -72,7 +61,7 @@ interface MatrixRoom : Closeable {
|
||||||
* A one-to-one is a room with exactly 2 members.
|
* 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).
|
* See [the Matrix spec](https://spec.matrix.org/latest/client-server-api/#default-underride-rules).
|
||||||
*/
|
*/
|
||||||
val isOneToOne: Boolean get() = activeMemberCount == 2L
|
val isOneToOne: Boolean get() = info().activeMembersCount == 2L
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current loaded members as a StateFlow.
|
* The current loaded members as a StateFlow.
|
||||||
|
|
@ -83,6 +72,11 @@ interface MatrixRoom : Closeable {
|
||||||
|
|
||||||
val roomNotificationSettingsStateFlow: StateFlow<MatrixRoomNotificationSettingsState>
|
val roomNotificationSettingsStateFlow: StateFlow<MatrixRoomNotificationSettingsState>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the latest room info we have received from the SDK stream.
|
||||||
|
*/
|
||||||
|
fun info(): MatrixRoomInfo = roomInfoFlow.value
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to load the room members and update the membersFlow.
|
* Try to load the room members and update the membersFlow.
|
||||||
*/
|
*/
|
||||||
|
|
@ -453,4 +447,6 @@ interface MatrixRoom : Closeable {
|
||||||
* Update the join rule for this room.
|
* Update the join rule for this room.
|
||||||
*/
|
*/
|
||||||
suspend fun updateJoinRule(joinRule: JoinRule): Result<Unit>
|
suspend fun updateJoinRule(joinRule: JoinRule): Result<Unit>
|
||||||
|
|
||||||
|
suspend fun getUpdatedIsEncrypted(): Result<Boolean>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,9 @@ data class MatrixRoomInfo(
|
||||||
val rawName: String?,
|
val rawName: String?,
|
||||||
val topic: String?,
|
val topic: String?,
|
||||||
val avatarUrl: String?,
|
val avatarUrl: String?,
|
||||||
|
val isPublic: Boolean,
|
||||||
val isDirect: Boolean,
|
val isDirect: Boolean,
|
||||||
|
val isEncrypted: Boolean?,
|
||||||
val joinRule: JoinRule?,
|
val joinRule: JoinRule?,
|
||||||
val isSpace: Boolean,
|
val isSpace: Boolean,
|
||||||
val isTombstoned: Boolean,
|
val isTombstoned: Boolean,
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.api.room
|
package io.element.android.libraries.matrix.api.room
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the room with the provided info is a DM.
|
* Returns whether the room with the provided info is a DM.
|
||||||
* A DM is a room with at most 2 active members (one of them may have left).
|
* A DM is a room with at most 2 active members (one of them may have left).
|
||||||
|
|
@ -19,9 +21,9 @@ fun isDm(isDirect: Boolean, activeMembersCount: Int): Boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the [MatrixRoom] is a DM.
|
* Returns whether the [MatrixRoom] is a DM, with an updated state from the latest [MatrixRoomInfo].
|
||||||
*/
|
*/
|
||||||
val MatrixRoom.isDm get() = isDm(isDirect, activeMemberCount.toInt())
|
suspend fun MatrixRoom.isDm() = roomInfoFlow.first().isDm
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the [MatrixRoomInfo] is from a DM.
|
* Returns whether the [MatrixRoomInfo] is from a DM.
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ fun MatrixRoom.matches(roomIdOrAlias: RoomIdOrAlias): Boolean {
|
||||||
roomIdOrAlias.roomId == roomId
|
roomIdOrAlias.roomId == roomId
|
||||||
}
|
}
|
||||||
is RoomIdOrAlias.Alias -> {
|
is RoomIdOrAlias.Alias -> {
|
||||||
roomIdOrAlias.roomAlias == canonicalAlias || roomIdOrAlias.roomAlias in alternativeAliases
|
roomIdOrAlias.roomAlias == info().canonicalAlias || roomIdOrAlias.roomAlias in info().alternativeAliases
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ suspend fun MatrixClient.getRecentDirectRooms(
|
||||||
getRecentlyVisitedRooms().getOrNull()?.let { roomIds ->
|
getRecentlyVisitedRooms().getOrNull()?.let { roomIds ->
|
||||||
roomIds
|
roomIds
|
||||||
.mapNotNull { roomId -> getRoom(roomId) }
|
.mapNotNull { roomId -> getRoom(roomId) }
|
||||||
.filter { it.isDm && it.isJoined() }
|
.filter { it.isDm() && it.isJoined() }
|
||||||
.map { room ->
|
.map { room ->
|
||||||
val otherUser = room.getMembers().getOrNull()
|
val otherUser = room.getMembers().getOrNull()
|
||||||
?.firstOrNull { it.userId != sessionId }
|
?.firstOrNull { it.userId != sessionId }
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,6 @@ package io.element.android.libraries.matrix.api.timeline.item.event
|
||||||
enum class TimelineItemEventOrigin {
|
enum class TimelineItemEventOrigin {
|
||||||
LOCAL,
|
LOCAL,
|
||||||
SYNC,
|
SYNC,
|
||||||
PAGINATION
|
PAGINATION,
|
||||||
|
CACHE,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,5 @@ package io.element.android.libraries.matrix.api.tracing
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
interface TracingService {
|
interface TracingService {
|
||||||
fun setupTracing(tracingConfiguration: TracingConfiguration)
|
|
||||||
fun createTimberTree(target: String): Timber.Tree
|
fun createTimberTree(target: String): Timber.Tree
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -151,8 +151,7 @@ class RustMatrixClient(
|
||||||
private val notificationProcessSetup = NotificationProcessSetup.SingleProcess(innerSyncService)
|
private val notificationProcessSetup = NotificationProcessSetup.SingleProcess(innerSyncService)
|
||||||
private val innerNotificationClient = runBlocking { innerClient.notificationClient(notificationProcessSetup) }
|
private val innerNotificationClient = runBlocking { innerClient.notificationClient(notificationProcessSetup) }
|
||||||
private val notificationService = RustNotificationService(innerNotificationClient, dispatchers, clock)
|
private val notificationService = RustNotificationService(innerNotificationClient, dispatchers, clock)
|
||||||
private val notificationSettingsService = RustNotificationSettingsService(innerClient, dispatchers)
|
private val notificationSettingsService = RustNotificationSettingsService(innerClient, sessionCoroutineScope, dispatchers)
|
||||||
.apply { start() }
|
|
||||||
private val encryptionService = RustEncryptionService(
|
private val encryptionService = RustEncryptionService(
|
||||||
client = innerClient,
|
client = innerClient,
|
||||||
syncService = rustSyncService,
|
syncService = rustSyncService,
|
||||||
|
|
@ -216,7 +215,7 @@ class RustMatrixClient(
|
||||||
userId = sessionId,
|
userId = sessionId,
|
||||||
// TODO cache for displayName?
|
// TODO cache for displayName?
|
||||||
displayName = null,
|
displayName = null,
|
||||||
avatarUrl = innerClient.cachedAvatarUrl(),
|
avatarUrl = null,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -237,6 +236,9 @@ class RustMatrixClient(
|
||||||
sessionDelegate.bindClient(this)
|
sessionDelegate.bindClient(this)
|
||||||
|
|
||||||
sessionCoroutineScope.launch {
|
sessionCoroutineScope.launch {
|
||||||
|
// Start notification settings
|
||||||
|
notificationSettingsService.start()
|
||||||
|
|
||||||
// Force a refresh of the profile
|
// Force a refresh of the profile
|
||||||
getUserProfile()
|
getUserProfile()
|
||||||
}
|
}
|
||||||
|
|
@ -479,10 +481,10 @@ class RustMatrixClient(
|
||||||
appCoroutineScope.launch {
|
appCoroutineScope.launch {
|
||||||
roomFactory.destroy()
|
roomFactory.destroy()
|
||||||
rustSyncService.destroy()
|
rustSyncService.destroy()
|
||||||
|
notificationSettingsService.destroy()
|
||||||
}
|
}
|
||||||
sessionCoroutineScope.cancel()
|
sessionCoroutineScope.cancel()
|
||||||
clientDelegateTaskHandle?.cancelAndDestroy()
|
clientDelegateTaskHandle?.cancelAndDestroy()
|
||||||
notificationSettingsService.destroy()
|
|
||||||
verificationService.destroy()
|
verificationService.destroy()
|
||||||
|
|
||||||
sessionDelegate.clearCurrentClient()
|
sessionDelegate.clearCurrentClient()
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ package io.element.android.libraries.matrix.impl.analytics
|
||||||
|
|
||||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
|
import io.element.android.libraries.matrix.api.room.isDm
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
|
||||||
private fun Long.toAnalyticsRoomSize(): JoinedRoom.RoomSize {
|
private fun Long.toAnalyticsRoomSize(): JoinedRoom.RoomSize {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
|
|
@ -22,11 +24,12 @@ private fun Long.toAnalyticsRoomSize(): JoinedRoom.RoomSize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun MatrixRoom.toAnalyticsJoinedRoom(trigger: JoinedRoom.Trigger?): JoinedRoom {
|
suspend fun MatrixRoom.toAnalyticsJoinedRoom(trigger: JoinedRoom.Trigger?): JoinedRoom {
|
||||||
|
val roomInfo = roomInfoFlow.first()
|
||||||
return JoinedRoom(
|
return JoinedRoom(
|
||||||
isDM = isDirect,
|
isDM = roomInfo.isDm,
|
||||||
isSpace = isSpace,
|
isSpace = roomInfo.isSpace,
|
||||||
roomSize = joinedMemberCount.toAnalyticsRoomSize(),
|
roomSize = roomInfo.joinedMembersCount.toAnalyticsRoomSize(),
|
||||||
trigger = trigger
|
trigger = trigger
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,7 @@ import org.matrix.rustcomponents.sdk.OidcPrompt as RustOidcPrompt
|
||||||
|
|
||||||
internal fun OidcPrompt.toRustPrompt(): RustOidcPrompt {
|
internal fun OidcPrompt.toRustPrompt(): RustOidcPrompt {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
OidcPrompt.None -> RustOidcPrompt.None
|
OidcPrompt.Login -> RustOidcPrompt.Unknown("consent")
|
||||||
OidcPrompt.Login -> RustOidcPrompt.Login
|
|
||||||
OidcPrompt.Consent -> RustOidcPrompt.Consent
|
|
||||||
OidcPrompt.SelectAccount -> RustOidcPrompt.SelectAccount
|
|
||||||
OidcPrompt.Create -> RustOidcPrompt.Create
|
OidcPrompt.Create -> RustOidcPrompt.Create
|
||||||
is OidcPrompt.Unknown -> RustOidcPrompt.Unknown(value)
|
is OidcPrompt.Unknown -> RustOidcPrompt.Unknown(value)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,12 @@
|
||||||
package io.element.android.libraries.matrix.impl.notificationsettings
|
package io.element.android.libraries.matrix.impl.notificationsettings
|
||||||
|
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
|
import io.element.android.libraries.core.coroutine.suspendLazy
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
import io.element.android.libraries.matrix.api.room.RoomNotificationSettings
|
import io.element.android.libraries.matrix.api.room.RoomNotificationSettings
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.channels.BufferOverflow
|
import kotlinx.coroutines.channels.BufferOverflow
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
|
|
@ -24,9 +26,10 @@ import timber.log.Timber
|
||||||
|
|
||||||
class RustNotificationSettingsService(
|
class RustNotificationSettingsService(
|
||||||
client: Client,
|
client: Client,
|
||||||
|
sessionCoroutineScope: CoroutineScope,
|
||||||
private val dispatchers: CoroutineDispatchers,
|
private val dispatchers: CoroutineDispatchers,
|
||||||
) : NotificationSettingsService {
|
) : NotificationSettingsService {
|
||||||
private val notificationSettings = client.getNotificationSettings()
|
private val notificationSettings by suspendLazy(sessionCoroutineScope.coroutineContext + dispatchers.io) { client.getNotificationSettings() }
|
||||||
private val _notificationSettingsChangeFlow = MutableSharedFlow<Unit>(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
private val _notificationSettingsChangeFlow = MutableSharedFlow<Unit>(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
||||||
override val notificationSettingsChangeFlow: SharedFlow<Unit> = _notificationSettingsChangeFlow.asSharedFlow()
|
override val notificationSettingsChangeFlow: SharedFlow<Unit> = _notificationSettingsChangeFlow.asSharedFlow()
|
||||||
|
|
||||||
|
|
@ -36,22 +39,22 @@ class RustNotificationSettingsService(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun start() {
|
suspend fun start() {
|
||||||
notificationSettings.setDelegate(notificationSettingsDelegate)
|
notificationSettings.await().setDelegate(notificationSettingsDelegate)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun destroy() {
|
suspend fun destroy() {
|
||||||
notificationSettings.setDelegate(null)
|
notificationSettings.await().setDelegate(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result<RoomNotificationSettings> =
|
override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result<RoomNotificationSettings> =
|
||||||
runCatching {
|
runCatching {
|
||||||
notificationSettings.getRoomNotificationSettings(roomId.value, isEncrypted, isOneToOne).let(RoomNotificationSettingsMapper::map)
|
notificationSettings.await().getRoomNotificationSettings(roomId.value, isEncrypted, isOneToOne).let(RoomNotificationSettingsMapper::map)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getDefaultRoomNotificationMode(isEncrypted: Boolean, isOneToOne: Boolean): Result<RoomNotificationMode> =
|
override suspend fun getDefaultRoomNotificationMode(isEncrypted: Boolean, isOneToOne: Boolean): Result<RoomNotificationMode> =
|
||||||
runCatching {
|
runCatching {
|
||||||
notificationSettings.getDefaultRoomNotificationMode(isEncrypted, isOneToOne).let(RoomNotificationSettingsMapper::mapMode)
|
notificationSettings.await().getDefaultRoomNotificationMode(isEncrypted, isOneToOne).let(RoomNotificationSettingsMapper::mapMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun setDefaultRoomNotificationMode(
|
override suspend fun setDefaultRoomNotificationMode(
|
||||||
|
|
@ -61,7 +64,7 @@ class RustNotificationSettingsService(
|
||||||
): Result<Unit> = withContext(dispatchers.io) {
|
): Result<Unit> = withContext(dispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
try {
|
try {
|
||||||
notificationSettings.setDefaultRoomNotificationMode(isEncrypted, isOneToOne, mode.let(RoomNotificationSettingsMapper::mapMode))
|
notificationSettings.await().setDefaultRoomNotificationMode(isEncrypted, isOneToOne, mode.let(RoomNotificationSettingsMapper::mapMode))
|
||||||
} catch (exception: NotificationSettingsException.RuleNotFound) {
|
} catch (exception: NotificationSettingsException.RuleNotFound) {
|
||||||
// `setDefaultRoomNotificationMode` updates multiple rules including unstable rules (e.g. the polls push rules defined in the MSC3930)
|
// `setDefaultRoomNotificationMode` updates multiple rules including unstable rules (e.g. the polls push rules defined in the MSC3930)
|
||||||
// since production home servers may not have these rules yet, we drop the RuleNotFound error
|
// since production home servers may not have these rules yet, we drop the RuleNotFound error
|
||||||
|
|
@ -72,13 +75,13 @@ class RustNotificationSettingsService(
|
||||||
|
|
||||||
override suspend fun setRoomNotificationMode(roomId: RoomId, mode: RoomNotificationMode): Result<Unit> = withContext(dispatchers.io) {
|
override suspend fun setRoomNotificationMode(roomId: RoomId, mode: RoomNotificationMode): Result<Unit> = withContext(dispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
notificationSettings.setRoomNotificationMode(roomId.value, mode.let(RoomNotificationSettingsMapper::mapMode))
|
notificationSettings.await().setRoomNotificationMode(roomId.value, mode.let(RoomNotificationSettingsMapper::mapMode))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun restoreDefaultRoomNotificationMode(roomId: RoomId): Result<Unit> = withContext(dispatchers.io) {
|
override suspend fun restoreDefaultRoomNotificationMode(roomId: RoomId): Result<Unit> = withContext(dispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
notificationSettings.restoreDefaultRoomNotificationMode(roomId.value)
|
notificationSettings.await().restoreDefaultRoomNotificationMode(roomId.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,53 +89,53 @@ class RustNotificationSettingsService(
|
||||||
|
|
||||||
override suspend fun unmuteRoom(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean) = withContext(dispatchers.io) {
|
override suspend fun unmuteRoom(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean) = withContext(dispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
notificationSettings.unmuteRoom(roomId.value, isEncrypted, isOneToOne)
|
notificationSettings.await().unmuteRoom(roomId.value, isEncrypted, isOneToOne)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun isRoomMentionEnabled(): Result<Boolean> = withContext(dispatchers.io) {
|
override suspend fun isRoomMentionEnabled(): Result<Boolean> = withContext(dispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
notificationSettings.isRoomMentionEnabled()
|
notificationSettings.await().isRoomMentionEnabled()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun setRoomMentionEnabled(enabled: Boolean): Result<Unit> = withContext(dispatchers.io) {
|
override suspend fun setRoomMentionEnabled(enabled: Boolean): Result<Unit> = withContext(dispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
notificationSettings.setRoomMentionEnabled(enabled)
|
notificationSettings.await().setRoomMentionEnabled(enabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun isCallEnabled(): Result<Boolean> = withContext(dispatchers.io) {
|
override suspend fun isCallEnabled(): Result<Boolean> = withContext(dispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
notificationSettings.isCallEnabled()
|
notificationSettings.await().isCallEnabled()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun setCallEnabled(enabled: Boolean): Result<Unit> = withContext(dispatchers.io) {
|
override suspend fun setCallEnabled(enabled: Boolean): Result<Unit> = withContext(dispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
notificationSettings.setCallEnabled(enabled)
|
notificationSettings.await().setCallEnabled(enabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun isInviteForMeEnabled(): Result<Boolean> = withContext(dispatchers.io) {
|
override suspend fun isInviteForMeEnabled(): Result<Boolean> = withContext(dispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
notificationSettings.isInviteForMeEnabled()
|
notificationSettings.await().isInviteForMeEnabled()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun setInviteForMeEnabled(enabled: Boolean): Result<Unit> = withContext(dispatchers.io) {
|
override suspend fun setInviteForMeEnabled(enabled: Boolean): Result<Unit> = withContext(dispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
notificationSettings.setInviteForMeEnabled(enabled)
|
notificationSettings.await().setInviteForMeEnabled(enabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getRoomsWithUserDefinedRules(): Result<List<String>> =
|
override suspend fun getRoomsWithUserDefinedRules(): Result<List<String>> =
|
||||||
runCatching {
|
runCatching {
|
||||||
notificationSettings.getRoomsWithUserDefinedRules(enabled = true)
|
notificationSettings.await().getRoomsWithUserDefinedRules(enabled = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun canHomeServerPushEncryptedEventsToDevice(): Result<Boolean> =
|
override suspend fun canHomeServerPushEncryptedEventsToDevice(): Result<Boolean> =
|
||||||
runCatching {
|
runCatching {
|
||||||
notificationSettings.canPushEncryptedEventToDevice()
|
notificationSettings.await().canPushEncryptedEventToDevice()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.matrix.impl.platform
|
||||||
|
|
||||||
|
import com.squareup.anvil.annotations.ContributesBinding
|
||||||
|
import io.element.android.libraries.di.AppScope
|
||||||
|
import io.element.android.libraries.matrix.api.platform.InitPlatformService
|
||||||
|
import io.element.android.libraries.matrix.api.tracing.TracingConfiguration
|
||||||
|
import io.element.android.libraries.matrix.impl.tracing.map
|
||||||
|
import org.matrix.rustcomponents.sdk.initPlatform
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ContributesBinding(AppScope::class)
|
||||||
|
class RustInitPlatformService @Inject constructor() : InitPlatformService {
|
||||||
|
override fun init(tracingConfiguration: TracingConfiguration) {
|
||||||
|
initPlatform(
|
||||||
|
config = tracingConfiguration.map(),
|
||||||
|
useLightweightTokioRuntime = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,6 +23,7 @@ import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.collections.immutable.toPersistentMap
|
import kotlinx.collections.immutable.toPersistentMap
|
||||||
import org.matrix.rustcomponents.sdk.Membership
|
import org.matrix.rustcomponents.sdk.Membership
|
||||||
import org.matrix.rustcomponents.sdk.RoomHero
|
import org.matrix.rustcomponents.sdk.RoomHero
|
||||||
|
import uniffi.matrix_sdk_base.EncryptionState
|
||||||
import org.matrix.rustcomponents.sdk.Membership as RustMembership
|
import org.matrix.rustcomponents.sdk.Membership as RustMembership
|
||||||
import org.matrix.rustcomponents.sdk.RoomInfo as RustRoomInfo
|
import org.matrix.rustcomponents.sdk.RoomInfo as RustRoomInfo
|
||||||
import org.matrix.rustcomponents.sdk.RoomNotificationMode as RustRoomNotificationMode
|
import org.matrix.rustcomponents.sdk.RoomNotificationMode as RustRoomNotificationMode
|
||||||
|
|
@ -36,7 +37,13 @@ class MatrixRoomInfoMapper {
|
||||||
rawName = it.rawName,
|
rawName = it.rawName,
|
||||||
topic = it.topic,
|
topic = it.topic,
|
||||||
avatarUrl = it.avatarUrl,
|
avatarUrl = it.avatarUrl,
|
||||||
|
isPublic = it.isPublic,
|
||||||
isDirect = it.isDirect,
|
isDirect = it.isDirect,
|
||||||
|
isEncrypted = when (it.encryptionState) {
|
||||||
|
EncryptionState.ENCRYPTED -> true
|
||||||
|
EncryptionState.NOT_ENCRYPTED -> false
|
||||||
|
EncryptionState.UNKNOWN -> null
|
||||||
|
},
|
||||||
joinRule = it.joinRule?.map(),
|
joinRule = it.joinRule?.map(),
|
||||||
isSpace = it.isSpace,
|
isSpace = it.isSpace,
|
||||||
isTombstoned = it.isTombstoned,
|
isTombstoned = it.isTombstoned,
|
||||||
|
|
@ -63,6 +70,44 @@ class MatrixRoomInfoMapper {
|
||||||
historyVisibility = it.historyVisibility.map(),
|
historyVisibility = it.historyVisibility.map(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fun map(rustRoom: Room): MatrixRoomInfo = with(rustRoom) {
|
||||||
|
// return MatrixRoomInfo(
|
||||||
|
// id = RoomId(id()),
|
||||||
|
// name = rawName(),
|
||||||
|
// rawName = displayName(),
|
||||||
|
// topic = topic(),
|
||||||
|
// avatarUrl = avatarUrl(),
|
||||||
|
// isPublic = isPublic(),
|
||||||
|
// isDirect = null,
|
||||||
|
// isEncrypted = encryptionState() == EncryptionState.ENCRYPTED,
|
||||||
|
// joinRule = null,
|
||||||
|
// isSpace = isSpace(),
|
||||||
|
// isTombstoned = isTombstoned(),
|
||||||
|
// isFavorite = null,
|
||||||
|
// canonicalAlias = canonicalAlias()?.let(::RoomAlias),
|
||||||
|
// alternativeAliases = alternativeAliases().map(::RoomAlias).toImmutableList(),
|
||||||
|
// currentUserMembership = membership().map(),
|
||||||
|
// inviter = null,
|
||||||
|
// activeMembersCount = activeMembersCount().toLong(),
|
||||||
|
// invitedMembersCount = invitedMembersCount().toLong(),
|
||||||
|
// joinedMembersCount = joinedMembersCount().toLong(),
|
||||||
|
// userPowerLevels = persistentMapOf(),
|
||||||
|
// highlightCount = 0,
|
||||||
|
// notificationCount = 0,
|
||||||
|
// userDefinedNotificationMode = null,
|
||||||
|
// hasRoomCall = hasActiveRoomCall(),
|
||||||
|
// activeRoomCallParticipants = activeRoomCallParticipants().map(::UserId).toImmutableList(),
|
||||||
|
// isMarkedUnread = false,
|
||||||
|
// numUnreadMessages = 0,
|
||||||
|
// numUnreadNotifications = 0,
|
||||||
|
// numUnreadMentions = 0,
|
||||||
|
// heroes = heroes().map(RoomHero::map).toImmutableList(),
|
||||||
|
// pinnedEventIds = persistentListOf(),
|
||||||
|
// creator = null,
|
||||||
|
// historyVisibility = null,
|
||||||
|
// )
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun RustMembership.map(): CurrentUserMembership = when (this) {
|
fun RustMembership.map(): CurrentUserMembership = when (this) {
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.cancel
|
import kotlinx.coroutines.cancel
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
|
|
@ -82,13 +83,13 @@ import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.onStart
|
import kotlinx.coroutines.flow.onStart
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.matrix.rustcomponents.sdk.DateDividerMode
|
import org.matrix.rustcomponents.sdk.DateDividerMode
|
||||||
import org.matrix.rustcomponents.sdk.IdentityStatusChangeListener
|
import org.matrix.rustcomponents.sdk.IdentityStatusChangeListener
|
||||||
import org.matrix.rustcomponents.sdk.KnockRequestsListener
|
import org.matrix.rustcomponents.sdk.KnockRequestsListener
|
||||||
import org.matrix.rustcomponents.sdk.RoomInfo
|
import org.matrix.rustcomponents.sdk.RoomInfo
|
||||||
import org.matrix.rustcomponents.sdk.RoomInfoListener
|
import org.matrix.rustcomponents.sdk.RoomInfoListener
|
||||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
|
||||||
import org.matrix.rustcomponents.sdk.RoomMessageEventMessageType
|
import org.matrix.rustcomponents.sdk.RoomMessageEventMessageType
|
||||||
import org.matrix.rustcomponents.sdk.TimelineConfiguration
|
import org.matrix.rustcomponents.sdk.TimelineConfiguration
|
||||||
import org.matrix.rustcomponents.sdk.TimelineFilter
|
import org.matrix.rustcomponents.sdk.TimelineFilter
|
||||||
|
|
@ -101,6 +102,7 @@ import org.matrix.rustcomponents.sdk.getElementCallRequiredPermissions
|
||||||
import org.matrix.rustcomponents.sdk.use
|
import org.matrix.rustcomponents.sdk.use
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import uniffi.matrix_sdk.RoomPowerLevelChanges
|
import uniffi.matrix_sdk.RoomPowerLevelChanges
|
||||||
|
import uniffi.matrix_sdk_base.EncryptionState
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.coroutines.cancellation.CancellationException
|
import kotlin.coroutines.cancellation.CancellationException
|
||||||
import org.matrix.rustcomponents.sdk.IdentityStatusChange as RustIdentityStateChange
|
import org.matrix.rustcomponents.sdk.IdentityStatusChange as RustIdentityStateChange
|
||||||
|
|
@ -112,7 +114,6 @@ import org.matrix.rustcomponents.sdk.Timeline as InnerTimeline
|
||||||
class RustMatrixRoom(
|
class RustMatrixRoom(
|
||||||
override val sessionId: SessionId,
|
override val sessionId: SessionId,
|
||||||
private val deviceId: DeviceId,
|
private val deviceId: DeviceId,
|
||||||
private val roomListItem: RoomListItem,
|
|
||||||
private val innerRoom: InnerRoom,
|
private val innerRoom: InnerRoom,
|
||||||
innerTimeline: InnerTimeline,
|
innerTimeline: InnerTimeline,
|
||||||
private val notificationSettingsService: NotificationSettingsService,
|
private val notificationSettingsService: NotificationSettingsService,
|
||||||
|
|
@ -124,22 +125,17 @@ class RustMatrixRoom(
|
||||||
private val matrixRoomInfoMapper: MatrixRoomInfoMapper,
|
private val matrixRoomInfoMapper: MatrixRoomInfoMapper,
|
||||||
private val featureFlagService: FeatureFlagService,
|
private val featureFlagService: FeatureFlagService,
|
||||||
private val roomMembershipObserver: RoomMembershipObserver,
|
private val roomMembershipObserver: RoomMembershipObserver,
|
||||||
|
initialRoomInfo: MatrixRoomInfo,
|
||||||
) : MatrixRoom {
|
) : MatrixRoom {
|
||||||
override val roomId = RoomId(innerRoom.id())
|
override val roomId = RoomId(innerRoom.id())
|
||||||
|
|
||||||
override val roomInfoFlow: Flow<MatrixRoomInfo> = mxCallbackFlow {
|
override val roomInfoFlow: StateFlow<MatrixRoomInfo> = mxCallbackFlow {
|
||||||
runCatching { innerRoom.roomInfo() }
|
|
||||||
.getOrNull()
|
|
||||||
?.let(matrixRoomInfoMapper::map)
|
|
||||||
?.let { initial ->
|
|
||||||
channel.trySend(initial)
|
|
||||||
}
|
|
||||||
innerRoom.subscribeToRoomInfoUpdates(object : RoomInfoListener {
|
innerRoom.subscribeToRoomInfoUpdates(object : RoomInfoListener {
|
||||||
override fun call(roomInfo: RoomInfo) {
|
override fun call(roomInfo: RoomInfo) {
|
||||||
channel.trySend(matrixRoomInfoMapper.map(roomInfo))
|
channel.trySend(matrixRoomInfoMapper.map(roomInfo))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}.stateIn(sessionCoroutineScope, started = SharingStarted.Lazily, initialValue = initialRoomInfo)
|
||||||
|
|
||||||
override val roomTypingMembersFlow: Flow<List<UserId>> = mxCallbackFlow {
|
override val roomTypingMembersFlow: Flow<List<UserId>> = mxCallbackFlow {
|
||||||
val initial = emptyList<UserId>()
|
val initial = emptyList<UserId>()
|
||||||
|
|
@ -307,39 +303,6 @@ class RustMatrixRoom(
|
||||||
liveTimeline.close()
|
liveTimeline.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
override val displayName: String
|
|
||||||
get() = runCatching { innerRoom.displayName() ?: "" }.getOrDefault("")
|
|
||||||
|
|
||||||
override val topic: String?
|
|
||||||
get() = runCatching { innerRoom.topic() }.getOrDefault(null)
|
|
||||||
|
|
||||||
override val avatarUrl: String?
|
|
||||||
get() = runCatching { roomListItem.avatarUrl() ?: innerRoom.avatarUrl() }.getOrDefault(null)
|
|
||||||
|
|
||||||
override val isEncrypted: Boolean
|
|
||||||
get() = runCatching { innerRoom.isEncrypted() }.getOrDefault(false)
|
|
||||||
|
|
||||||
override val canonicalAlias: RoomAlias?
|
|
||||||
get() = runCatching { innerRoom.canonicalAlias()?.let(::RoomAlias) }.getOrDefault(null)
|
|
||||||
|
|
||||||
override val alternativeAliases: List<RoomAlias>
|
|
||||||
get() = runCatching { innerRoom.alternativeAliases().map { RoomAlias(it) } }.getOrDefault(emptyList())
|
|
||||||
|
|
||||||
override val isPublic: Boolean
|
|
||||||
get() = runCatching { innerRoom.isPublic() }.getOrDefault(false)
|
|
||||||
|
|
||||||
override val isSpace: Boolean
|
|
||||||
get() = runCatching { innerRoom.isSpace() }.getOrDefault(false)
|
|
||||||
|
|
||||||
override val isDirect: Boolean
|
|
||||||
get() = runCatching { innerRoom.isDirect() }.getOrDefault(false)
|
|
||||||
|
|
||||||
override val joinedMemberCount: Long
|
|
||||||
get() = runCatching { innerRoom.joinedMembersCount().toLong() }.getOrDefault(0)
|
|
||||||
|
|
||||||
override val activeMemberCount: Long
|
|
||||||
get() = runCatching { innerRoom.activeMembersCount().toLong() }.getOrDefault(0)
|
|
||||||
|
|
||||||
override suspend fun updateMembers() {
|
override suspend fun updateMembers() {
|
||||||
val useCache = membersStateFlow.value is MatrixRoomMembersState.Unknown
|
val useCache = membersStateFlow.value is MatrixRoomMembersState.Unknown
|
||||||
val source = if (useCache) {
|
val source = if (useCache) {
|
||||||
|
|
@ -377,6 +340,7 @@ class RustMatrixRoom(
|
||||||
val currentRoomNotificationSettings = currentState.roomNotificationSettings()
|
val currentRoomNotificationSettings = currentState.roomNotificationSettings()
|
||||||
_roomNotificationSettingsStateFlow.value = MatrixRoomNotificationSettingsState.Pending(prevRoomNotificationSettings = currentRoomNotificationSettings)
|
_roomNotificationSettingsStateFlow.value = MatrixRoomNotificationSettingsState.Pending(prevRoomNotificationSettings = currentRoomNotificationSettings)
|
||||||
runCatching {
|
runCatching {
|
||||||
|
val isEncrypted = roomInfoFlow.value.isEncrypted ?: getUpdatedIsEncrypted().getOrThrow()
|
||||||
notificationSettingsService.getRoomNotificationSettings(roomId, isEncrypted, isOneToOne).getOrThrow()
|
notificationSettingsService.getRoomNotificationSettings(roomId, isEncrypted, isOneToOne).getOrThrow()
|
||||||
}.map {
|
}.map {
|
||||||
_roomNotificationSettingsStateFlow.value = MatrixRoomNotificationSettingsState.Ready(it)
|
_roomNotificationSettingsStateFlow.value = MatrixRoomNotificationSettingsState.Ready(it)
|
||||||
|
|
@ -866,6 +830,12 @@ class RustMatrixRoom(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getUpdatedIsEncrypted(): Result<Boolean> = withContext(roomDispatcher) {
|
||||||
|
runCatching {
|
||||||
|
innerRoom.latestEncryptionState() == EncryptionState.ENCRYPTED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun createTimeline(
|
private fun createTimeline(
|
||||||
timeline: InnerTimeline,
|
timeline: InnerTimeline,
|
||||||
mode: Timeline.Mode,
|
mode: Timeline.Mode,
|
||||||
|
|
|
||||||
|
|
@ -104,10 +104,10 @@ class RustRoomFactory(
|
||||||
return@withContext null
|
return@withContext null
|
||||||
}
|
}
|
||||||
val liveTimeline = roomReferences.fullRoom.timeline()
|
val liveTimeline = roomReferences.fullRoom.timeline()
|
||||||
|
val initialRoomInfo = roomReferences.fullRoom.roomInfo()
|
||||||
RustMatrixRoom(
|
RustMatrixRoom(
|
||||||
sessionId = sessionId,
|
sessionId = sessionId,
|
||||||
deviceId = deviceId,
|
deviceId = deviceId,
|
||||||
roomListItem = roomReferences.roomListItem,
|
|
||||||
innerRoom = roomReferences.fullRoom,
|
innerRoom = roomReferences.fullRoom,
|
||||||
innerTimeline = liveTimeline,
|
innerTimeline = liveTimeline,
|
||||||
sessionCoroutineScope = sessionCoroutineScope,
|
sessionCoroutineScope = sessionCoroutineScope,
|
||||||
|
|
@ -119,6 +119,7 @@ class RustRoomFactory(
|
||||||
matrixRoomInfoMapper = matrixRoomInfoMapper,
|
matrixRoomInfoMapper = matrixRoomInfoMapper,
|
||||||
featureFlagService = featureFlagService,
|
featureFlagService = featureFlagService,
|
||||||
roomMembershipObserver = roomMembershipObserver,
|
roomMembershipObserver = roomMembershipObserver,
|
||||||
|
initialRoomInfo = matrixRoomInfoMapper.map(initialRoomInfo),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.getAndUpdate
|
import kotlinx.coroutines.flow.getAndUpdate
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
|
@ -214,19 +215,19 @@ class RustTimeline(
|
||||||
_timelineItems,
|
_timelineItems,
|
||||||
backPaginationStatus,
|
backPaginationStatus,
|
||||||
forwardPaginationStatus,
|
forwardPaginationStatus,
|
||||||
matrixRoom.roomInfoFlow.map { it.creator },
|
matrixRoom.roomInfoFlow.map { it.creator to it.isDm }.distinctUntilChanged(),
|
||||||
isTimelineInitialized,
|
isTimelineInitialized,
|
||||||
) { timelineItems,
|
) { timelineItems,
|
||||||
backwardPaginationStatus,
|
backwardPaginationStatus,
|
||||||
forwardPaginationStatus,
|
forwardPaginationStatus,
|
||||||
roomCreator,
|
(roomCreator, isDm),
|
||||||
isTimelineInitialized ->
|
isTimelineInitialized ->
|
||||||
withContext(dispatcher) {
|
withContext(dispatcher) {
|
||||||
timelineItems
|
timelineItems
|
||||||
.let { items ->
|
.let { items ->
|
||||||
roomBeginningPostProcessor.process(
|
roomBeginningPostProcessor.process(
|
||||||
items = items,
|
items = items,
|
||||||
isDm = matrixRoom.isDm,
|
isDm = isDm,
|
||||||
roomCreator = roomCreator,
|
roomCreator = roomCreator,
|
||||||
hasMoreToLoadBackwards = backwardPaginationStatus.hasMoreToLoad,
|
hasMoreToLoadBackwards = backwardPaginationStatus.hasMoreToLoad,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,7 @@ private fun RustEventItemOrigin.map(): TimelineItemEventOrigin {
|
||||||
RustEventItemOrigin.LOCAL -> TimelineItemEventOrigin.LOCAL
|
RustEventItemOrigin.LOCAL -> TimelineItemEventOrigin.LOCAL
|
||||||
RustEventItemOrigin.SYNC -> TimelineItemEventOrigin.SYNC
|
RustEventItemOrigin.SYNC -> TimelineItemEventOrigin.SYNC
|
||||||
RustEventItemOrigin.PAGINATION -> TimelineItemEventOrigin.PAGINATION
|
RustEventItemOrigin.PAGINATION -> TimelineItemEventOrigin.PAGINATION
|
||||||
|
RustEventItemOrigin.CACHE -> TimelineItemEventOrigin.CACHE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,17 +20,6 @@ import javax.inject.Inject
|
||||||
|
|
||||||
@ContributesBinding(AppScope::class)
|
@ContributesBinding(AppScope::class)
|
||||||
class RustTracingService @Inject constructor(private val buildMeta: BuildMeta) : TracingService {
|
class RustTracingService @Inject constructor(private val buildMeta: BuildMeta) : TracingService {
|
||||||
override fun setupTracing(tracingConfiguration: TracingConfiguration) {
|
|
||||||
val rustTracingConfiguration = org.matrix.rustcomponents.sdk.TracingConfiguration(
|
|
||||||
writeToStdoutOrSystem = tracingConfiguration.writesToLogcat,
|
|
||||||
logLevel = tracingConfiguration.logLevel.toRustLogLevel(),
|
|
||||||
extraTargets = tracingConfiguration.extraTargets,
|
|
||||||
writeToFiles = tracingConfiguration.writesToFilesConfiguration.toTracingFileConfiguration(),
|
|
||||||
)
|
|
||||||
org.matrix.rustcomponents.sdk.setupTracing(rustTracingConfiguration)
|
|
||||||
Timber.d("setupTracing: $rustTracingConfiguration")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createTimberTree(target: String): Timber.Tree {
|
override fun createTimberTree(target: String): Timber.Tree {
|
||||||
return RustTracingTree(target = target, retrieveFromStackTrace = buildMeta.isDebuggable)
|
return RustTracingTree(target = target, retrieveFromStackTrace = buildMeta.isDebuggable)
|
||||||
}
|
}
|
||||||
|
|
@ -57,3 +46,10 @@ private fun WriteToFilesConfiguration.toTracingFileConfiguration(): TracingFileC
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun TracingConfiguration.map(): org.matrix.rustcomponents.sdk.TracingConfiguration = org.matrix.rustcomponents.sdk.TracingConfiguration(
|
||||||
|
writeToStdoutOrSystem = writesToLogcat,
|
||||||
|
logLevel = logLevel.toRustLogLevel(),
|
||||||
|
extraTargets = extraTargets,
|
||||||
|
writeToFiles = writesToFilesConfiguration.toTracingFileConfiguration(),
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ fun TestScope.createRustMatrixClientFactory(
|
||||||
) = RustMatrixClientFactory(
|
) = RustMatrixClientFactory(
|
||||||
baseDirectory = baseDirectory,
|
baseDirectory = baseDirectory,
|
||||||
cacheDirectory = cacheDirectory,
|
cacheDirectory = cacheDirectory,
|
||||||
appCoroutineScope = this,
|
appCoroutineScope = backgroundScope,
|
||||||
coroutineDispatchers = testCoroutineDispatchers(),
|
coroutineDispatchers = testCoroutineDispatchers(),
|
||||||
sessionStore = sessionStore,
|
sessionStore = sessionStore,
|
||||||
userAgentProvider = SimpleUserAgentProvider(),
|
userAgentProvider = SimpleUserAgentProvider(),
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ class RustMatrixClientTest {
|
||||||
innerClient = FakeRustClient(),
|
innerClient = FakeRustClient(),
|
||||||
baseDirectory = File(""),
|
baseDirectory = File(""),
|
||||||
sessionStore = sessionStore,
|
sessionStore = sessionStore,
|
||||||
appCoroutineScope = this,
|
appCoroutineScope = backgroundScope,
|
||||||
sessionDelegate = aRustClientSessionDelegate(
|
sessionDelegate = aRustClientSessionDelegate(
|
||||||
sessionStore = sessionStore,
|
sessionStore = sessionStore,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,14 @@ package io.element.android.libraries.matrix.impl.analytics
|
||||||
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||||
|
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
class JoinedRoomExtKtTest {
|
class JoinedRoomExtKtTest {
|
||||||
@Test
|
@Test
|
||||||
fun `test room size mapping`() {
|
fun `test room size mapping`() = runTest {
|
||||||
mapOf(
|
mapOf(
|
||||||
listOf(0L, 1L) to JoinedRoom.RoomSize.One,
|
listOf(0L, 1L) to JoinedRoom.RoomSize.One,
|
||||||
listOf(2L, 2L) to JoinedRoom.RoomSize.Two,
|
listOf(2L, 2L) to JoinedRoom.RoomSize.Two,
|
||||||
|
|
@ -39,7 +40,7 @@ class JoinedRoomExtKtTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `test isDirect parameter mapping`() {
|
fun `test isDirect parameter mapping`() = runTest {
|
||||||
assertThat(aMatrixRoom(isDirect = true).toAnalyticsJoinedRoom(null))
|
assertThat(aMatrixRoom(isDirect = true).toAnalyticsJoinedRoom(null))
|
||||||
.isEqualTo(
|
.isEqualTo(
|
||||||
JoinedRoom(
|
JoinedRoom(
|
||||||
|
|
@ -52,7 +53,7 @@ class JoinedRoomExtKtTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `test isSpace parameter mapping`() {
|
fun `test isSpace parameter mapping`() = runTest {
|
||||||
assertThat(aMatrixRoom(isSpace = true).toAnalyticsJoinedRoom(null))
|
assertThat(aMatrixRoom(isSpace = true).toAnalyticsJoinedRoom(null))
|
||||||
.isEqualTo(
|
.isEqualTo(
|
||||||
JoinedRoom(
|
JoinedRoom(
|
||||||
|
|
@ -65,8 +66,8 @@ class JoinedRoomExtKtTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `test trigger parameter mapping`() {
|
fun `test trigger parameter mapping`() = runTest {
|
||||||
assertThat(aMatrixRoom().toAnalyticsJoinedRoom(JoinedRoom.Trigger.Invite))
|
assertThat(aMatrixRoom(isDirect = false, isSpace = false, joinedMemberCount = 1).toAnalyticsJoinedRoom(JoinedRoom.Trigger.Invite))
|
||||||
.isEqualTo(
|
.isEqualTo(
|
||||||
JoinedRoom(
|
JoinedRoom(
|
||||||
isDM = false,
|
isDM = false,
|
||||||
|
|
@ -81,11 +82,9 @@ class JoinedRoomExtKtTest {
|
||||||
isDirect: Boolean = false,
|
isDirect: Boolean = false,
|
||||||
isSpace: Boolean = false,
|
isSpace: Boolean = false,
|
||||||
joinedMemberCount: Long = 0
|
joinedMemberCount: Long = 0
|
||||||
): MatrixRoom {
|
): FakeMatrixRoom {
|
||||||
return FakeMatrixRoom(
|
return FakeMatrixRoom().apply {
|
||||||
isDirect = isDirect,
|
givenRoomInfo(aRoomInfo(isDirect = isDirect, isSpace = isSpace, joinedMembersCount = joinedMemberCount))
|
||||||
isSpace = isSpace,
|
}
|
||||||
joinedMemberCount = joinedMemberCount
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import org.matrix.rustcomponents.sdk.RoomHistoryVisibility
|
||||||
import org.matrix.rustcomponents.sdk.RoomInfo
|
import org.matrix.rustcomponents.sdk.RoomInfo
|
||||||
import org.matrix.rustcomponents.sdk.RoomMember
|
import org.matrix.rustcomponents.sdk.RoomMember
|
||||||
import org.matrix.rustcomponents.sdk.RoomNotificationMode
|
import org.matrix.rustcomponents.sdk.RoomNotificationMode
|
||||||
|
import uniffi.matrix_sdk_base.EncryptionState
|
||||||
|
|
||||||
fun aRustRoomInfo(
|
fun aRustRoomInfo(
|
||||||
id: String = A_ROOM_ID.value,
|
id: String = A_ROOM_ID.value,
|
||||||
|
|
@ -24,6 +25,7 @@ fun aRustRoomInfo(
|
||||||
rawName: String? = A_ROOM_NAME,
|
rawName: String? = A_ROOM_NAME,
|
||||||
topic: String? = null,
|
topic: String? = null,
|
||||||
avatarUrl: String? = null,
|
avatarUrl: String? = null,
|
||||||
|
encryptionState: EncryptionState = EncryptionState.UNKNOWN,
|
||||||
isDirect: Boolean = false,
|
isDirect: Boolean = false,
|
||||||
isPublic: Boolean = false,
|
isPublic: Boolean = false,
|
||||||
isSpace: Boolean = false,
|
isSpace: Boolean = false,
|
||||||
|
|
@ -57,6 +59,7 @@ fun aRustRoomInfo(
|
||||||
rawName = rawName,
|
rawName = rawName,
|
||||||
topic = topic,
|
topic = topic,
|
||||||
avatarUrl = avatarUrl,
|
avatarUrl = avatarUrl,
|
||||||
|
encryptionState = encryptionState,
|
||||||
isDirect = isDirect,
|
isDirect = isDirect,
|
||||||
isPublic = isPublic,
|
isPublic = isPublic,
|
||||||
isSpace = isSpace,
|
isSpace = isSpace,
|
||||||
|
|
|
||||||
|
|
@ -35,11 +35,11 @@ class FakeRustClient(
|
||||||
override fun userId(): String = userId
|
override fun userId(): String = userId
|
||||||
override fun deviceId(): String = deviceId
|
override fun deviceId(): String = deviceId
|
||||||
override suspend fun notificationClient(processSetup: NotificationProcessSetup) = notificationClient
|
override suspend fun notificationClient(processSetup: NotificationProcessSetup) = notificationClient
|
||||||
override fun getNotificationSettings(): NotificationSettings = notificationSettings
|
override suspend fun getNotificationSettings(): NotificationSettings = notificationSettings
|
||||||
override fun encryption(): Encryption = encryption
|
override fun encryption(): Encryption = encryption
|
||||||
override fun session(): Session = session
|
override fun session(): Session = session
|
||||||
override fun setDelegate(delegate: ClientDelegate?): TaskHandle = FakeRustTaskHandle()
|
override fun setDelegate(delegate: ClientDelegate?): TaskHandle = FakeRustTaskHandle()
|
||||||
override fun cachedAvatarUrl(): String? = null
|
override suspend fun cachedAvatarUrl(): String? = null
|
||||||
override suspend fun restoreSession(session: Session) = Unit
|
override suspend fun restoreSession(session: Session) = Unit
|
||||||
override fun syncService(): SyncServiceBuilder = FakeRustSyncServiceBuilder()
|
override fun syncService(): SyncServiceBuilder = FakeRustSyncServiceBuilder()
|
||||||
override fun roomDirectorySearch(): RoomDirectorySearch = FakeRustRoomDirectorySearch()
|
override fun roomDirectorySearch(): RoomDirectorySearch = FakeRustRoomDirectorySearch()
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ class RustNotificationSettingsServiceTest {
|
||||||
client = FakeRustClient(
|
client = FakeRustClient(
|
||||||
notificationSettings = notificationSettings,
|
notificationSettings = notificationSettings,
|
||||||
),
|
),
|
||||||
|
sessionCoroutineScope = this,
|
||||||
dispatchers = testCoroutineDispatchers(),
|
dispatchers = testCoroutineDispatchers(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import kotlinx.collections.immutable.toImmutableMap
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.matrix.rustcomponents.sdk.Membership
|
import org.matrix.rustcomponents.sdk.Membership
|
||||||
|
import uniffi.matrix_sdk_base.EncryptionState
|
||||||
import org.matrix.rustcomponents.sdk.JoinRule as RustJoinRule
|
import org.matrix.rustcomponents.sdk.JoinRule as RustJoinRule
|
||||||
import org.matrix.rustcomponents.sdk.RoomHistoryVisibility as RustRoomHistoryVisibility
|
import org.matrix.rustcomponents.sdk.RoomHistoryVisibility as RustRoomHistoryVisibility
|
||||||
import org.matrix.rustcomponents.sdk.RoomNotificationMode as RustRoomNotificationMode
|
import org.matrix.rustcomponents.sdk.RoomNotificationMode as RustRoomNotificationMode
|
||||||
|
|
@ -48,6 +49,7 @@ class MatrixRoomInfoMapperTest {
|
||||||
rawName = "rawName",
|
rawName = "rawName",
|
||||||
topic = "topic",
|
topic = "topic",
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
|
encryptionState = EncryptionState.ENCRYPTED,
|
||||||
isDirect = true,
|
isDirect = true,
|
||||||
isPublic = false,
|
isPublic = false,
|
||||||
isSpace = false,
|
isSpace = false,
|
||||||
|
|
@ -84,7 +86,9 @@ class MatrixRoomInfoMapperTest {
|
||||||
rawName = "rawName",
|
rawName = "rawName",
|
||||||
topic = "topic",
|
topic = "topic",
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
|
isPublic = false,
|
||||||
isDirect = true,
|
isDirect = true,
|
||||||
|
isEncrypted = true,
|
||||||
isSpace = false,
|
isSpace = false,
|
||||||
isTombstoned = false,
|
isTombstoned = false,
|
||||||
isFavorite = false,
|
isFavorite = false,
|
||||||
|
|
@ -130,6 +134,7 @@ class MatrixRoomInfoMapperTest {
|
||||||
rawName = null,
|
rawName = null,
|
||||||
topic = null,
|
topic = null,
|
||||||
avatarUrl = null,
|
avatarUrl = null,
|
||||||
|
encryptionState = EncryptionState.UNKNOWN,
|
||||||
isDirect = false,
|
isDirect = false,
|
||||||
isPublic = true,
|
isPublic = true,
|
||||||
joinRule = null,
|
joinRule = null,
|
||||||
|
|
@ -165,6 +170,8 @@ class MatrixRoomInfoMapperTest {
|
||||||
rawName = null,
|
rawName = null,
|
||||||
topic = null,
|
topic = null,
|
||||||
avatarUrl = null,
|
avatarUrl = null,
|
||||||
|
isEncrypted = null,
|
||||||
|
isPublic = true,
|
||||||
isDirect = false,
|
isDirect = false,
|
||||||
joinRule = null,
|
joinRule = null,
|
||||||
isSpace = false,
|
isSpace = false,
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||||
import io.element.android.libraries.matrix.test.A_SERVER_LIST
|
import io.element.android.libraries.matrix.test.A_SERVER_LIST
|
||||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
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.aRoomSummary
|
import io.element.android.libraries.matrix.test.room.aRoomSummary
|
||||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
|
|
@ -32,7 +33,9 @@ class DefaultJoinRoomTest {
|
||||||
val roomSummary = aRoomSummary()
|
val roomSummary = aRoomSummary()
|
||||||
val joinRoomLambda = lambdaRecorder { _: RoomId -> Result.success(roomSummary) }
|
val joinRoomLambda = lambdaRecorder { _: RoomId -> Result.success(roomSummary) }
|
||||||
val joinRoomByIdOrAliasLambda = lambdaRecorder { _: RoomIdOrAlias, _: List<String> -> Result.success(roomSummary) }
|
val joinRoomByIdOrAliasLambda = lambdaRecorder { _: RoomIdOrAlias, _: List<String> -> Result.success(roomSummary) }
|
||||||
val roomResult = FakeMatrixRoom()
|
val roomResult = FakeMatrixRoom().apply {
|
||||||
|
givenRoomInfo(aRoomInfo())
|
||||||
|
}
|
||||||
val aTrigger = JoinedRoom.Trigger.MobilePermalink
|
val aTrigger = JoinedRoom.Trigger.MobilePermalink
|
||||||
val client: MatrixClient = FakeMatrixClient().also {
|
val client: MatrixClient = FakeMatrixClient().also {
|
||||||
it.joinRoomLambda = joinRoomLambda
|
it.joinRoomLambda = joinRoomLambda
|
||||||
|
|
@ -67,7 +70,9 @@ class DefaultJoinRoomTest {
|
||||||
val roomSummary = aRoomSummary()
|
val roomSummary = aRoomSummary()
|
||||||
val joinRoomLambda = lambdaRecorder { _: RoomId -> Result.success(roomSummary) }
|
val joinRoomLambda = lambdaRecorder { _: RoomId -> Result.success(roomSummary) }
|
||||||
val joinRoomByIdOrAliasLambda = lambdaRecorder { _: RoomIdOrAlias, _: List<String> -> Result.success(roomSummary) }
|
val joinRoomByIdOrAliasLambda = lambdaRecorder { _: RoomIdOrAlias, _: List<String> -> Result.success(roomSummary) }
|
||||||
val roomResult = FakeMatrixRoom()
|
val roomResult = FakeMatrixRoom().apply {
|
||||||
|
givenRoomInfo(aRoomInfo())
|
||||||
|
}
|
||||||
val aTrigger = JoinedRoom.Trigger.MobilePermalink
|
val aTrigger = JoinedRoom.Trigger.MobilePermalink
|
||||||
val client: MatrixClient = FakeMatrixClient().also {
|
val client: MatrixClient = FakeMatrixClient().also {
|
||||||
it.joinRoomLambda = joinRoomLambda
|
it.joinRoomLambda = joinRoomLambda
|
||||||
|
|
@ -103,7 +108,9 @@ class DefaultJoinRoomTest {
|
||||||
val roomSummary = aRoomSummary()
|
val roomSummary = aRoomSummary()
|
||||||
val joinRoomLambda = lambdaRecorder { _: RoomId -> Result.success(roomSummary) }
|
val joinRoomLambda = lambdaRecorder { _: RoomId -> Result.success(roomSummary) }
|
||||||
val joinRoomByIdOrAliasLambda = lambdaRecorder { _: RoomIdOrAlias, _: List<String> -> Result.success(roomSummary) }
|
val joinRoomByIdOrAliasLambda = lambdaRecorder { _: RoomIdOrAlias, _: List<String> -> Result.success(roomSummary) }
|
||||||
val roomResult = FakeMatrixRoom()
|
val roomResult = FakeMatrixRoom().apply {
|
||||||
|
givenRoomInfo(aRoomInfo())
|
||||||
|
}
|
||||||
val aTrigger = JoinedRoom.Trigger.MobilePermalink
|
val aTrigger = JoinedRoom.Trigger.MobilePermalink
|
||||||
val client: MatrixClient = FakeMatrixClient().also {
|
val client: MatrixClient = FakeMatrixClient().also {
|
||||||
it.joinRoomLambda = joinRoomLambda
|
it.joinRoomLambda = joinRoomLambda
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.test.room
|
package io.element.android.libraries.matrix.test.room
|
||||||
|
|
||||||
|
import io.element.android.libraries.core.bool.orFalse
|
||||||
import io.element.android.libraries.matrix.api.core.DeviceId
|
import io.element.android.libraries.matrix.api.core.DeviceId
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
||||||
|
|
@ -66,19 +67,9 @@ import java.io.File
|
||||||
class FakeMatrixRoom(
|
class FakeMatrixRoom(
|
||||||
override val sessionId: SessionId = A_SESSION_ID,
|
override val sessionId: SessionId = A_SESSION_ID,
|
||||||
override val roomId: RoomId = A_ROOM_ID,
|
override val roomId: RoomId = A_ROOM_ID,
|
||||||
override val displayName: String = "",
|
|
||||||
override val topic: String? = null,
|
|
||||||
override val avatarUrl: String? = null,
|
|
||||||
override var isEncrypted: Boolean = false,
|
|
||||||
override val canonicalAlias: RoomAlias? = null,
|
|
||||||
override val alternativeAliases: List<RoomAlias> = emptyList(),
|
|
||||||
override val isPublic: Boolean = true,
|
|
||||||
override val isSpace: Boolean = false,
|
|
||||||
override val isDirect: Boolean = false,
|
|
||||||
override val joinedMemberCount: Long = 123L,
|
|
||||||
override val activeMemberCount: Long = 234L,
|
|
||||||
val notificationSettingsService: NotificationSettingsService = FakeNotificationSettingsService(),
|
val notificationSettingsService: NotificationSettingsService = FakeNotificationSettingsService(),
|
||||||
override val liveTimeline: Timeline = FakeTimeline(),
|
override val liveTimeline: Timeline = FakeTimeline(),
|
||||||
|
initialRoomInfo: MatrixRoomInfo = aRoomInfo(),
|
||||||
override val roomCoroutineScope: CoroutineScope = TestScope(),
|
override val roomCoroutineScope: CoroutineScope = TestScope(),
|
||||||
private var roomPermalinkResult: () -> Result<String> = { lambdaError() },
|
private var roomPermalinkResult: () -> Result<String> = { lambdaError() },
|
||||||
private var eventPermalinkResult: (EventId) -> Result<String> = { lambdaError() },
|
private var eventPermalinkResult: (EventId) -> Result<String> = { lambdaError() },
|
||||||
|
|
@ -156,8 +147,8 @@ class FakeMatrixRoom(
|
||||||
private val enableEncryptionResult: () -> Result<Unit> = { lambdaError() },
|
private val enableEncryptionResult: () -> Result<Unit> = { lambdaError() },
|
||||||
private val updateJoinRuleResult: (JoinRule) -> Result<Unit> = { lambdaError() },
|
private val updateJoinRuleResult: (JoinRule) -> Result<Unit> = { lambdaError() },
|
||||||
) : MatrixRoom {
|
) : MatrixRoom {
|
||||||
private val _roomInfoFlow: MutableSharedFlow<MatrixRoomInfo> = MutableSharedFlow(replay = 1)
|
private val _roomInfoFlow: MutableStateFlow<MatrixRoomInfo> = MutableStateFlow(initialRoomInfo)
|
||||||
override val roomInfoFlow: Flow<MatrixRoomInfo> = _roomInfoFlow
|
override val roomInfoFlow: StateFlow<MatrixRoomInfo> = _roomInfoFlow
|
||||||
|
|
||||||
fun givenRoomInfo(roomInfo: MatrixRoomInfo) {
|
fun givenRoomInfo(roomInfo: MatrixRoomInfo) {
|
||||||
_roomInfoFlow.tryEmit(roomInfo)
|
_roomInfoFlow.tryEmit(roomInfo)
|
||||||
|
|
@ -200,14 +191,14 @@ class FakeMatrixRoom(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun updateRoomNotificationSettings(): Result<Unit> = simulateLongTask {
|
override suspend fun updateRoomNotificationSettings(): Result<Unit> = simulateLongTask {
|
||||||
val notificationSettings = notificationSettingsService.getRoomNotificationSettings(roomId, isEncrypted, isOneToOne).getOrThrow()
|
val notificationSettings = notificationSettingsService.getRoomNotificationSettings(roomId, info().isEncrypted.orFalse(), isOneToOne).getOrThrow()
|
||||||
roomNotificationSettingsStateFlow.value = MatrixRoomNotificationSettingsState.Ready(notificationSettings)
|
roomNotificationSettingsStateFlow.value = MatrixRoomNotificationSettingsState.Ready(notificationSettings)
|
||||||
return Result.success(Unit)
|
return Result.success(Unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun enableEncryption(): Result<Unit> = simulateLongTask {
|
override suspend fun enableEncryption(): Result<Unit> = simulateLongTask {
|
||||||
enableEncryptionResult().onSuccess {
|
enableEncryptionResult().onSuccess {
|
||||||
isEncrypted = true
|
givenRoomInfo(info().copy(isEncrypted = true))
|
||||||
emitSyncUpdate()
|
emitSyncUpdate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -616,6 +607,10 @@ class FakeMatrixRoom(
|
||||||
updateJoinRuleResult(joinRule)
|
updateJoinRuleResult(joinRule)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getUpdatedIsEncrypted(): Result<Boolean> = simulateLongTask {
|
||||||
|
Result.success(info().isEncrypted.orFalse())
|
||||||
|
}
|
||||||
|
|
||||||
fun givenRoomMembersState(state: MatrixRoomMembersState) {
|
fun givenRoomMembersState(state: MatrixRoomMembersState) {
|
||||||
membersStateFlow.value = state
|
membersStateFlow.value = state
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,9 @@ fun aRoomInfo(
|
||||||
rawName: String? = A_ROOM_RAW_NAME,
|
rawName: String? = A_ROOM_RAW_NAME,
|
||||||
topic: String? = A_ROOM_TOPIC,
|
topic: String? = A_ROOM_TOPIC,
|
||||||
avatarUrl: String? = AN_AVATAR_URL,
|
avatarUrl: String? = AN_AVATAR_URL,
|
||||||
|
isPublic: Boolean = true,
|
||||||
isDirect: Boolean = false,
|
isDirect: Boolean = false,
|
||||||
|
isEncrypted: Boolean = false,
|
||||||
joinRule: JoinRule? = JoinRule.Public,
|
joinRule: JoinRule? = JoinRule.Public,
|
||||||
isSpace: Boolean = false,
|
isSpace: Boolean = false,
|
||||||
isTombstoned: Boolean = false,
|
isTombstoned: Boolean = false,
|
||||||
|
|
@ -65,7 +67,9 @@ fun aRoomInfo(
|
||||||
rawName = rawName,
|
rawName = rawName,
|
||||||
topic = topic,
|
topic = topic,
|
||||||
avatarUrl = avatarUrl,
|
avatarUrl = avatarUrl,
|
||||||
|
isPublic = isPublic,
|
||||||
isDirect = isDirect,
|
isDirect = isDirect,
|
||||||
|
isEncrypted = isEncrypted,
|
||||||
joinRule = joinRule,
|
joinRule = joinRule,
|
||||||
isSpace = isSpace,
|
isSpace = isSpace,
|
||||||
isTombstoned = isTombstoned,
|
isTombstoned = isTombstoned,
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,9 @@ fun aRoomSummary(
|
||||||
rawName: String? = A_ROOM_RAW_NAME,
|
rawName: String? = A_ROOM_RAW_NAME,
|
||||||
topic: String? = A_ROOM_TOPIC,
|
topic: String? = A_ROOM_TOPIC,
|
||||||
avatarUrl: String? = null,
|
avatarUrl: String? = null,
|
||||||
|
isPublic: Boolean = true,
|
||||||
isDirect: Boolean = false,
|
isDirect: Boolean = false,
|
||||||
|
isEncrypted: Boolean = false,
|
||||||
joinRule: JoinRule? = JoinRule.Public,
|
joinRule: JoinRule? = JoinRule.Public,
|
||||||
isSpace: Boolean = false,
|
isSpace: Boolean = false,
|
||||||
isTombstoned: Boolean = false,
|
isTombstoned: Boolean = false,
|
||||||
|
|
@ -80,7 +82,9 @@ fun aRoomSummary(
|
||||||
rawName = rawName,
|
rawName = rawName,
|
||||||
topic = topic,
|
topic = topic,
|
||||||
avatarUrl = avatarUrl,
|
avatarUrl = avatarUrl,
|
||||||
|
isPublic = isPublic,
|
||||||
isDirect = isDirect,
|
isDirect = isDirect,
|
||||||
|
isEncrypted = isEncrypted,
|
||||||
joinRule = joinRule,
|
joinRule = joinRule,
|
||||||
isSpace = isSpace,
|
isSpace = isSpace,
|
||||||
isTombstoned = isTombstoned,
|
isTombstoned = isTombstoned,
|
||||||
|
|
|
||||||
|
|
@ -40,11 +40,12 @@ fun getRoomMemberAsState(roomMembersState: MatrixRoomMembersState, userId: UserI
|
||||||
@Composable
|
@Composable
|
||||||
fun MatrixRoom.getDirectRoomMember(roomMembersState: MatrixRoomMembersState): State<RoomMember?> {
|
fun MatrixRoom.getDirectRoomMember(roomMembersState: MatrixRoomMembersState): State<RoomMember?> {
|
||||||
val roomMembers = roomMembersState.roomMembers()
|
val roomMembers = roomMembersState.roomMembers()
|
||||||
return remember(roomMembersState) {
|
val roomInfo by roomInfoFlow.collectAsState()
|
||||||
|
return remember(roomMembersState, roomInfo.isDirect) {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
roomMembers
|
roomMembers
|
||||||
?.filter { it.membership.isActive() }
|
?.filter { it.membership.isActive() }
|
||||||
?.takeIf { it.size == 2 && isDirect }
|
?.takeIf { it.size == 2 && roomInfo.isDirect == true }
|
||||||
?.find { it.userId != sessionId }
|
?.find { it.userId != sessionId }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,9 +67,9 @@ fun MatrixRoom.canPinUnpin(updateKey: Long): State<Boolean> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MatrixRoom.isDmAsState(updateKey: Long): State<Boolean> {
|
fun MatrixRoom.isDmAsState(): State<Boolean> {
|
||||||
return produceState(initialValue = false, key1 = updateKey) {
|
return produceState(initialValue = false) {
|
||||||
value = isDm
|
roomInfoFlow.collect { value = it.isDm }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,25 +105,25 @@ fun MatrixRoom.userPowerLevelAsState(updateKey: Long): State<Long> {
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MatrixRoom.isOwnUserAdmin(): Boolean {
|
fun MatrixRoom.isOwnUserAdmin(): Boolean {
|
||||||
val roomInfo by roomInfoFlow.collectAsState(initial = null)
|
val roomInfo by roomInfoFlow.collectAsState()
|
||||||
val powerLevel = roomInfo?.userPowerLevels?.get(sessionId) ?: 0L
|
val powerLevel = roomInfo.userPowerLevels[sessionId] ?: 0L
|
||||||
return RoomMember.Role.forPowerLevel(powerLevel) == RoomMember.Role.ADMIN
|
return RoomMember.Role.forPowerLevel(powerLevel) == RoomMember.Role.ADMIN
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MatrixRoom.rawName(): String? {
|
fun MatrixRoom.rawName(): String? {
|
||||||
val roomInfo by roomInfoFlow.collectAsState(initial = null)
|
val roomInfo by roomInfoFlow.collectAsState()
|
||||||
return roomInfo?.rawName
|
return roomInfo.rawName
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MatrixRoom.topic(): String? {
|
fun MatrixRoom.topic(): String? {
|
||||||
val roomInfo by roomInfoFlow.collectAsState(initial = null)
|
val roomInfo by roomInfoFlow.collectAsState()
|
||||||
return roomInfo?.topic
|
return roomInfo.topic
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MatrixRoom.avatarUrl(): String? {
|
fun MatrixRoom.avatarUrl(): String? {
|
||||||
val roomInfo by roomInfoFlow.collectAsState(initial = null)
|
val roomInfo by roomInfoFlow.collectAsState()
|
||||||
return roomInfo?.avatarUrl
|
return roomInfo.avatarUrl
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,10 @@ import kotlinx.coroutines.flow.onEach
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
fun MatrixRoom.roomMemberIdentityStateChange(): Flow<ImmutableList<RoomMemberIdentityStateChange>> {
|
fun MatrixRoom.roomMemberIdentityStateChange(): Flow<ImmutableList<RoomMemberIdentityStateChange>> {
|
||||||
return syncUpdateFlow
|
return roomInfoFlow
|
||||||
.filter {
|
.filter {
|
||||||
// Room cannot become unencrypted, so we can just apply a filter here.
|
// Room cannot become unencrypted, so we can just apply a filter here.
|
||||||
isEncrypted
|
it.isEncrypted == true
|
||||||
}
|
}
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.flatMapLatest {
|
.flatMapLatest {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import io.element.android.libraries.matrix.test.A_USER_ID
|
||||||
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||||
import io.element.android.libraries.matrix.test.A_USER_ID_3
|
import io.element.android.libraries.matrix.test.A_USER_ID_3
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
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.matrix.test.room.aRoomMember
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
|
|
@ -31,8 +32,10 @@ class MatrixRoomMembersTest {
|
||||||
fun `getDirectRoomMember emits other member for encrypted DM with 2 joined members`() = runTest {
|
fun `getDirectRoomMember emits other member for encrypted DM with 2 joined members`() = runTest {
|
||||||
val matrixRoom = FakeMatrixRoom(
|
val matrixRoom = FakeMatrixRoom(
|
||||||
sessionId = A_USER_ID,
|
sessionId = A_USER_ID,
|
||||||
isEncrypted = true,
|
initialRoomInfo = aRoomInfo(
|
||||||
isDirect = true,
|
isDirect = true,
|
||||||
|
joinedMembersCount = 2,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
matrixRoom.getDirectRoomMember(
|
matrixRoom.getDirectRoomMember(
|
||||||
|
|
@ -47,8 +50,7 @@ class MatrixRoomMembersTest {
|
||||||
fun `getDirectRoomMember emit null if the room is not a dm`() = runTest {
|
fun `getDirectRoomMember emit null if the room is not a dm`() = runTest {
|
||||||
val matrixRoom = FakeMatrixRoom(
|
val matrixRoom = FakeMatrixRoom(
|
||||||
sessionId = A_USER_ID,
|
sessionId = A_USER_ID,
|
||||||
isEncrypted = true,
|
initialRoomInfo = aRoomInfo(isDirect = false)
|
||||||
isDirect = false,
|
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
matrixRoom.getDirectRoomMember(
|
matrixRoom.getDirectRoomMember(
|
||||||
|
|
@ -63,8 +65,10 @@ class MatrixRoomMembersTest {
|
||||||
fun `getDirectRoomMember emits other member even if the room is not encrypted`() = runTest {
|
fun `getDirectRoomMember emits other member even if the room is not encrypted`() = runTest {
|
||||||
val matrixRoom = FakeMatrixRoom(
|
val matrixRoom = FakeMatrixRoom(
|
||||||
sessionId = A_USER_ID,
|
sessionId = A_USER_ID,
|
||||||
isEncrypted = false,
|
initialRoomInfo = aRoomInfo(
|
||||||
isDirect = true,
|
isDirect = true,
|
||||||
|
activeMembersCount = 2,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
matrixRoom.getDirectRoomMember(
|
matrixRoom.getDirectRoomMember(
|
||||||
|
|
@ -79,8 +83,7 @@ class MatrixRoomMembersTest {
|
||||||
fun `getDirectRoomMember emit null if the room has only 1 member`() = runTest {
|
fun `getDirectRoomMember emit null if the room has only 1 member`() = runTest {
|
||||||
val matrixRoom = FakeMatrixRoom(
|
val matrixRoom = FakeMatrixRoom(
|
||||||
sessionId = A_USER_ID,
|
sessionId = A_USER_ID,
|
||||||
isEncrypted = true,
|
initialRoomInfo = aRoomInfo(isDirect = true)
|
||||||
isDirect = true,
|
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
matrixRoom.getDirectRoomMember(
|
matrixRoom.getDirectRoomMember(
|
||||||
|
|
@ -95,9 +98,9 @@ class MatrixRoomMembersTest {
|
||||||
fun `getDirectRoomMember emit null if the room has only 3 members`() = runTest {
|
fun `getDirectRoomMember emit null if the room has only 3 members`() = runTest {
|
||||||
val matrixRoom = FakeMatrixRoom(
|
val matrixRoom = FakeMatrixRoom(
|
||||||
sessionId = A_USER_ID,
|
sessionId = A_USER_ID,
|
||||||
isEncrypted = true,
|
).apply {
|
||||||
isDirect = true,
|
givenRoomInfo(aRoomInfo(isDirect = true))
|
||||||
)
|
}
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
matrixRoom.getDirectRoomMember(
|
matrixRoom.getDirectRoomMember(
|
||||||
MatrixRoomMembersState.Ready(persistentListOf(roomMember1, roomMember2, roomMember3))
|
MatrixRoomMembersState.Ready(persistentListOf(roomMember1, roomMember2, roomMember3))
|
||||||
|
|
@ -111,8 +114,7 @@ class MatrixRoomMembersTest {
|
||||||
fun `getDirectRoomMember emit null if the other member is not active`() = runTest {
|
fun `getDirectRoomMember emit null if the other member is not active`() = runTest {
|
||||||
val matrixRoom = FakeMatrixRoom(
|
val matrixRoom = FakeMatrixRoom(
|
||||||
sessionId = A_USER_ID,
|
sessionId = A_USER_ID,
|
||||||
isEncrypted = true,
|
initialRoomInfo = aRoomInfo(isDirect = true),
|
||||||
isDirect = true,
|
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
matrixRoom.getDirectRoomMember(
|
matrixRoom.getDirectRoomMember(
|
||||||
|
|
@ -132,8 +134,10 @@ class MatrixRoomMembersTest {
|
||||||
fun `getDirectRoomMember emit the other member if there are 2 active members`() = runTest {
|
fun `getDirectRoomMember emit the other member if there are 2 active members`() = runTest {
|
||||||
val matrixRoom = FakeMatrixRoom(
|
val matrixRoom = FakeMatrixRoom(
|
||||||
sessionId = A_USER_ID,
|
sessionId = A_USER_ID,
|
||||||
isEncrypted = true,
|
initialRoomInfo = aRoomInfo(
|
||||||
isDirect = true,
|
isDirect = true,
|
||||||
|
activeMembersCount = 2,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
matrixRoom.getDirectRoomMember(
|
matrixRoom.getDirectRoomMember(
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ class MediaGalleryPresenter @AssistedInject constructor(
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
var mode by remember { mutableStateOf(MediaGalleryMode.Images) }
|
var mode by remember { mutableStateOf(MediaGalleryMode.Images) }
|
||||||
|
|
||||||
val roomInfo by room.roomInfoFlow.collectAsState(null)
|
val roomInfo by room.roomInfoFlow.collectAsState()
|
||||||
|
|
||||||
var mediaBottomSheetState by remember { mutableStateOf<MediaBottomSheetState>(MediaBottomSheetState.Hidden) }
|
var mediaBottomSheetState by remember { mutableStateOf<MediaBottomSheetState>(MediaBottomSheetState.Hidden) }
|
||||||
|
|
||||||
|
|
@ -139,7 +139,7 @@ class MediaGalleryPresenter @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
return MediaGalleryState(
|
return MediaGalleryState(
|
||||||
roomName = roomInfo?.name ?: room.displayName,
|
roomName = roomInfo.name.orEmpty(),
|
||||||
mode = mode,
|
mode = mode,
|
||||||
groupedMediaItems = groupedMediaItems,
|
groupedMediaItems = groupedMediaItems,
|
||||||
mediaBottomSheetState = mediaBottomSheetState,
|
mediaBottomSheetState = mediaBottomSheetState,
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import io.element.android.libraries.matrix.test.A_USER_ID
|
||||||
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||||
import io.element.android.libraries.matrix.test.media.FakeMatrixMediaLoader
|
import io.element.android.libraries.matrix.test.media.FakeMatrixMediaLoader
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
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.timeline.FakeTimeline
|
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
||||||
import io.element.android.libraries.mediaviewer.impl.datasource.FakeMediaGalleryDataSource
|
import io.element.android.libraries.mediaviewer.impl.datasource.FakeMediaGalleryDataSource
|
||||||
import io.element.android.libraries.mediaviewer.impl.datasource.MediaGalleryDataSource
|
import io.element.android.libraries.mediaviewer.impl.datasource.MediaGalleryDataSource
|
||||||
|
|
@ -51,7 +52,7 @@ class MediaGalleryPresenterTest {
|
||||||
startLambda = startLambda,
|
startLambda = startLambda,
|
||||||
),
|
),
|
||||||
room = FakeMatrixRoom(
|
room = FakeMatrixRoom(
|
||||||
displayName = A_ROOM_NAME,
|
initialRoomInfo = aRoomInfo(name = A_ROOM_NAME),
|
||||||
createTimelineResult = { Result.success(FakeTimeline()) },
|
createTimelineResult = { Result.success(FakeTimeline()) },
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -70,7 +71,7 @@ class MediaGalleryPresenterTest {
|
||||||
fun `present - change mode`() = runTest {
|
fun `present - change mode`() = runTest {
|
||||||
val presenter = createMediaGalleryPresenter(
|
val presenter = createMediaGalleryPresenter(
|
||||||
room = FakeMatrixRoom(
|
room = FakeMatrixRoom(
|
||||||
displayName = A_ROOM_NAME,
|
initialRoomInfo = aRoomInfo(name = A_ROOM_NAME),
|
||||||
createTimelineResult = { Result.success(FakeTimeline()) },
|
createTimelineResult = { Result.success(FakeTimeline()) },
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -100,7 +101,7 @@ class MediaGalleryPresenterTest {
|
||||||
val presenter = createMediaGalleryPresenter(
|
val presenter = createMediaGalleryPresenter(
|
||||||
room = FakeMatrixRoom(
|
room = FakeMatrixRoom(
|
||||||
sessionId = A_USER_ID,
|
sessionId = A_USER_ID,
|
||||||
displayName = A_ROOM_NAME,
|
initialRoomInfo = aRoomInfo(name = A_ROOM_NAME),
|
||||||
createTimelineResult = { Result.success(FakeTimeline()) },
|
createTimelineResult = { Result.success(FakeTimeline()) },
|
||||||
canRedactOwnResult = { Result.success(canDeleteOwn) }
|
canRedactOwnResult = { Result.success(canDeleteOwn) }
|
||||||
)
|
)
|
||||||
|
|
@ -143,7 +144,7 @@ class MediaGalleryPresenterTest {
|
||||||
val presenter = createMediaGalleryPresenter(
|
val presenter = createMediaGalleryPresenter(
|
||||||
room = FakeMatrixRoom(
|
room = FakeMatrixRoom(
|
||||||
sessionId = A_USER_ID,
|
sessionId = A_USER_ID,
|
||||||
displayName = A_ROOM_NAME,
|
initialRoomInfo = aRoomInfo(name = A_ROOM_NAME),
|
||||||
createTimelineResult = { Result.success(FakeTimeline()) },
|
createTimelineResult = { Result.success(FakeTimeline()) },
|
||||||
canRedactOtherResult = { Result.success(canDeleteOther) }
|
canRedactOtherResult = { Result.success(canDeleteOther) }
|
||||||
)
|
)
|
||||||
|
|
@ -176,7 +177,7 @@ class MediaGalleryPresenterTest {
|
||||||
fun `present - delete bottom sheet`() = runTest {
|
fun `present - delete bottom sheet`() = runTest {
|
||||||
val presenter = createMediaGalleryPresenter(
|
val presenter = createMediaGalleryPresenter(
|
||||||
room = FakeMatrixRoom(
|
room = FakeMatrixRoom(
|
||||||
displayName = A_ROOM_NAME,
|
initialRoomInfo = aRoomInfo(name = A_ROOM_NAME),
|
||||||
createTimelineResult = { Result.success(FakeTimeline()) },
|
createTimelineResult = { Result.success(FakeTimeline()) },
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -152,8 +152,8 @@ class NotificationBroadcastReceiverHandler @Inject constructor(
|
||||||
imageUriString = null,
|
imageUriString = null,
|
||||||
imageMimeType = null,
|
imageMimeType = null,
|
||||||
threadId = threadId,
|
threadId = threadId,
|
||||||
roomName = room.displayName,
|
roomName = room.info().name,
|
||||||
roomIsDm = room.isDm,
|
roomIsDm = room.isDm(),
|
||||||
outGoingMessage = true,
|
outGoingMessage = true,
|
||||||
)
|
)
|
||||||
onNotifiableEventReceived.onNotifiableEventReceived(notifiableMessageEvent)
|
onNotifiableEventReceived.onNotifiableEventReceived(notifiableMessageEvent)
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||||
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
|
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
|
||||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
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.FakeMatrixRoom
|
||||||
|
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||||
import io.element.android.libraries.matrix.test.room.aRoomMember
|
import io.element.android.libraries.matrix.test.room.aRoomMember
|
||||||
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
||||||
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
|
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
|
||||||
|
|
@ -337,7 +338,14 @@ class NotificationBroadcastReceiverHandlerTest {
|
||||||
val matrixRoom = FakeMatrixRoom(
|
val matrixRoom = FakeMatrixRoom(
|
||||||
liveTimeline = liveTimeline,
|
liveTimeline = liveTimeline,
|
||||||
getUpdatedMemberResult = { Result.success(aRoomMember()) },
|
getUpdatedMemberResult = { Result.success(aRoomMember()) },
|
||||||
)
|
).apply {
|
||||||
|
givenRoomInfo(
|
||||||
|
aRoomInfo(
|
||||||
|
isDirect = true,
|
||||||
|
activeMembersCount = 2,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
val onNotifiableEventReceivedResult = lambdaRecorder<NotifiableEvent, Unit> { _ -> }
|
val onNotifiableEventReceivedResult = lambdaRecorder<NotifiableEvent, Unit> { _ -> }
|
||||||
val onNotifiableEventReceived = FakeOnNotifiableEventReceived(onNotifiableEventReceivedResult = onNotifiableEventReceivedResult)
|
val onNotifiableEventReceived = FakeOnNotifiableEventReceived(onNotifiableEventReceivedResult = onNotifiableEventReceivedResult)
|
||||||
val sut = createNotificationBroadcastReceiverHandler(
|
val sut = createNotificationBroadcastReceiverHandler(
|
||||||
|
|
@ -396,7 +404,14 @@ class NotificationBroadcastReceiverHandlerTest {
|
||||||
val matrixRoom = FakeMatrixRoom(
|
val matrixRoom = FakeMatrixRoom(
|
||||||
liveTimeline = liveTimeline,
|
liveTimeline = liveTimeline,
|
||||||
getUpdatedMemberResult = { Result.success(aRoomMember()) },
|
getUpdatedMemberResult = { Result.success(aRoomMember()) },
|
||||||
)
|
).apply {
|
||||||
|
givenRoomInfo(
|
||||||
|
aRoomInfo(
|
||||||
|
isDirect = true,
|
||||||
|
activeMembersCount = 2,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
val onNotifiableEventReceivedResult = lambdaRecorder<NotifiableEvent, Unit> { _ -> }
|
val onNotifiableEventReceivedResult = lambdaRecorder<NotifiableEvent, Unit> { _ -> }
|
||||||
val onNotifiableEventReceived = FakeOnNotifiableEventReceived(onNotifiableEventReceivedResult = onNotifiableEventReceivedResult)
|
val onNotifiableEventReceived = FakeOnNotifiableEventReceived(onNotifiableEventReceivedResult = onNotifiableEventReceivedResult)
|
||||||
val sut = createNotificationBroadcastReceiverHandler(
|
val sut = createNotificationBroadcastReceiverHandler(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue