Merge branch 'develop' of https://github.com/vector-im/element-x-android into dla/feature/connect_sdk_to_global_notifications_ui

This commit is contained in:
David Langley 2023-09-12 16:30:36 +01:00
commit c3fbac4678
686 changed files with 7212 additions and 2257 deletions

View file

@ -85,11 +85,11 @@ interface MatrixRoom : Closeable {
suspend fun userAvatarUrl(userId: UserId): Result<String?>
suspend fun sendMessage(message: String): Result<Unit>
suspend fun sendMessage(body: String, htmlBody: String): Result<Unit>
suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, message: String): Result<Unit>
suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String): Result<Unit>
suspend fun replyMessage(eventId: EventId, message: String): Result<Unit>
suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String): Result<Unit>
suspend fun redactEvent(eventId: EventId, reason: String? = null): Result<Unit>

View file

@ -32,6 +32,11 @@ interface RoomListService {
data object Terminated : State()
}
sealed class SyncIndicator {
data object Show : SyncIndicator()
data object Hide : SyncIndicator()
}
/**
* returns a [RoomList] object of all rooms we want to display.
* This will exclude some rooms like the invites, or spaces.
@ -49,6 +54,11 @@ interface RoomListService {
*/
fun updateAllRoomsVisibleRange(range: IntRange)
/**
* The sync indicator as a flow.
*/
val syncIndicator: StateFlow<SyncIndicator>
/**
* The state of the service as a flow.
*/

View file

@ -19,24 +19,27 @@ package io.element.android.libraries.matrix.api.tracing
data class TracingFilterConfiguration(
val overrides: Map<Target, LogLevel> = emptyMap(),
) {
private val defaultLogLevel = LogLevel.INFO
// Order should matters
private val targetsToLogLevel: MutableMap<Target, LogLevel> = mutableMapOf(
Target.COMMON to LogLevel.Info,
Target.HYPER to LogLevel.Warn,
Target.MATRIX_SDK_CRYPTO to LogLevel.Debug,
Target.MATRIX_SDK_HTTP_CLIENT to LogLevel.Debug,
Target.MATRIX_SDK_SLIDING_SYNC to LogLevel.Trace,
Target.MATRIX_SDK_BASE_SLIDING_SYNC to LogLevel.Trace,
Target.MATRIX_SDK_UI_TIMELINE to LogLevel.Info,
private val targetsToLogLevel: Map<Target, LogLevel> = mapOf(
Target.HYPER to LogLevel.WARN,
Target.MATRIX_SDK_CRYPTO to LogLevel.DEBUG,
Target.MATRIX_SDK_HTTP_CLIENT to LogLevel.DEBUG,
Target.MATRIX_SDK_SLIDING_SYNC to LogLevel.TRACE,
Target.MATRIX_SDK_BASE_SLIDING_SYNC to LogLevel.TRACE,
)
fun getLogLevel(target: Target): LogLevel {
return overrides[target] ?: targetsToLogLevel[target] ?: defaultLogLevel
}
val filter: String
get() {
overrides.forEach { (target, logLevel) ->
targetsToLogLevel[target] = logLevel
val fullMap = Target.values().associateWith {
overrides[it] ?: targetsToLogLevel[it] ?: defaultLogLevel
}
return targetsToLogLevel.map {
return fullMap.map {
if (it.key.filter.isEmpty()) {
it.value.filter
} else {
@ -53,31 +56,32 @@ enum class Target(open val filter: String) {
MATRIX_SDK_FFI("matrix_sdk_ffi"),
MATRIX_SDK_UNIFFI_API("matrix_sdk_ffi::uniffi_api"),
MATRIX_SDK_CRYPTO("matrix_sdk_crypto"),
MATRIX_SDK("matrix_sdk"),
MATRIX_SDK_HTTP_CLIENT("matrix_sdk::http_client"),
MATRIX_SDK_CLIENT("matrix_sdk::client"),
MATRIX_SDK_OIDC("matrix_sdk::oidc"),
MATRIX_SDK_SLIDING_SYNC("matrix_sdk::sliding_sync"),
MATRIX_SDK_BASE_SLIDING_SYNC("matrix_sdk_base::sliding_sync"),
MATRIX_SDK_UI_TIMELINE("matrix_sdk_ui::timeline"),
}
sealed class LogLevel(val filter: String) {
data object Warn : LogLevel("warn")
data object Trace : LogLevel("trace")
data object Info : LogLevel("info")
data object Debug : LogLevel("debug")
data object Error : LogLevel("error")
enum class LogLevel(open val filter: String) {
ERROR("error"),
WARN("warn"),
INFO("info"),
DEBUG("debug"),
TRACE("trace"),
}
object TracingFilterConfigurations {
val release = TracingFilterConfiguration(
overrides = mapOf(
Target.COMMON to LogLevel.Info,
Target.ELEMENT to LogLevel.Debug
Target.ELEMENT to LogLevel.DEBUG
),
)
val debug = TracingFilterConfiguration(
overrides = mapOf(
Target.COMMON to LogLevel.Info,
Target.ELEMENT to LogLevel.Trace
Target.ELEMENT to LogLevel.TRACE
)
)

View file

@ -35,6 +35,7 @@ dependencies {
implementation(projects.libraries.androidutils)
implementation(projects.libraries.network)
implementation(projects.services.toolbox.api)
implementation(projects.libraries.featureflag.api)
api(projects.libraries.matrix.api)
implementation(libs.dagger)
implementation(projects.libraries.core)

View file

@ -67,6 +67,7 @@ import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeout
import org.matrix.rustcomponents.sdk.Client
import org.matrix.rustcomponents.sdk.ClientDelegate
import org.matrix.rustcomponents.sdk.NotificationProcessSetup
import org.matrix.rustcomponents.sdk.Room
import org.matrix.rustcomponents.sdk.RoomListItem
import org.matrix.rustcomponents.sdk.use
@ -100,9 +101,13 @@ class RustMatrixClient constructor(
client = client,
dispatchers = dispatchers,
)
private val notificationClient = client.notificationClient().use { builder ->
builder.filterByPushRules().finish()
}
private val notificationProcessSetup = NotificationProcessSetup.SingleProcess(syncService)
private val notificationClient = client.notificationClient(notificationProcessSetup)
.use { builder ->
builder
.filterByPushRules()
.finish()
}
private val notificationSettings = client.getNotificationSettings()
private val notificationService = RustNotificationService(sessionId, notificationClient, dispatchers, clock)
@ -288,6 +293,7 @@ class RustMatrixClient constructor(
syncService.destroy()
innerRoomListService.destroy()
notificationClient.destroy()
notificationProcessSetup.destroy()
client.destroy()
}
@ -325,6 +331,7 @@ class RustMatrixClient constructor(
client.accountUrl()
}
}
override suspend fun loadUserDisplayName(): Result<String> = withContext(sessionDispatcher) {
runCatching {
client.displayName()

View file

@ -53,7 +53,8 @@ class RustMatrixClientFactory @Inject constructor(
client.restoreSession(sessionData.toSession())
val syncService = client.syncService().finish()
val syncService = client.syncService()
.finish()
RustMatrixClient(
client = client,

View file

@ -66,7 +66,7 @@ import org.matrix.rustcomponents.sdk.RoomMember
import org.matrix.rustcomponents.sdk.RoomSubscription
import org.matrix.rustcomponents.sdk.SendAttachmentJoinHandle
import org.matrix.rustcomponents.sdk.genTransactionId
import org.matrix.rustcomponents.sdk.messageEventContentFromMarkdown
import org.matrix.rustcomponents.sdk.messageEventContentFromHtml
import timber.log.Timber
import java.io.File
@ -227,31 +227,32 @@ class RustMatrixRoom(
}
}
override suspend fun sendMessage(message: String): Result<Unit> = withContext(roomDispatcher) {
override suspend fun sendMessage(body: String, htmlBody: String): Result<Unit> = withContext(roomDispatcher) {
val transactionId = genTransactionId()
messageEventContentFromMarkdown(message).use { content ->
messageEventContentFromHtml(body, htmlBody).use { content ->
runCatching {
innerRoom.send(content, transactionId)
}
}
}
override suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, message: String): Result<Unit> = withContext(roomDispatcher) {
if (originalEventId != null) {
runCatching {
innerRoom.edit(messageEventContentFromMarkdown(message), originalEventId.value, transactionId?.value)
}
} else {
runCatching {
transactionId?.let { cancelSend(it) }
innerRoom.send(messageEventContentFromMarkdown(message), genTransactionId())
override suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String): Result<Unit> =
withContext(roomDispatcher) {
if (originalEventId != null) {
runCatching {
innerRoom.edit(messageEventContentFromHtml(body, htmlBody), originalEventId.value, transactionId?.value)
}
} else {
runCatching {
transactionId?.let { cancelSend(it) }
innerRoom.send(messageEventContentFromHtml(body, htmlBody), genTransactionId())
}
}
}
}
override suspend fun replyMessage(eventId: EventId, message: String): Result<Unit> = withContext(roomDispatcher) {
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String): Result<Unit> = withContext(roomDispatcher) {
runCatching {
innerRoom.sendReply(messageEventContentFromMarkdown(message), eventId.value, genTransactionId())
innerRoom.sendReply(messageEventContentFromHtml(body, htmlBody), eventId.value, genTransactionId())
}
}

View file

@ -33,6 +33,8 @@ import org.matrix.rustcomponents.sdk.RoomListLoadingStateListener
import org.matrix.rustcomponents.sdk.RoomListService
import org.matrix.rustcomponents.sdk.RoomListServiceState
import org.matrix.rustcomponents.sdk.RoomListServiceStateListener
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicator
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicatorListener
import timber.log.Timber
fun RoomList.loadingStateFlow(): Flow<RoomListLoadingState> =
@ -83,6 +85,18 @@ fun RoomListService.stateFlow(): Flow<RoomListServiceState> =
}
}.buffer(Channel.UNLIMITED)
fun RoomListService.syncIndicator(): Flow<RoomListServiceSyncIndicator> =
mxCallbackFlow {
val listener = object : RoomListServiceSyncIndicatorListener {
override fun onUpdate(syncIndicator: RoomListServiceSyncIndicator) {
trySendBlocking(syncIndicator)
}
}
tryOrNull {
syncIndicator(listener)
}
}.buffer(Channel.UNLIMITED)
fun RoomListService.roomOrNull(roomId: String): RoomListItem? {
return try {
room(roomId)

View file

@ -20,25 +20,26 @@ import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
import io.element.android.libraries.matrix.impl.room.RoomMemberMapper
import io.element.android.libraries.matrix.impl.room.message.RoomMessageFactory
import org.matrix.rustcomponents.sdk.Room
import org.matrix.rustcomponents.sdk.RoomListItem
import org.matrix.rustcomponents.sdk.RoomInfo
import org.matrix.rustcomponents.sdk.use
class RoomSummaryDetailsFactory(private val roomMessageFactory: RoomMessageFactory = RoomMessageFactory()) {
suspend fun create(roomListItem: RoomListItem, room: Room?): RoomSummaryDetails {
val latestRoomMessage = roomListItem.latestEvent()?.use {
fun create(roomInfo: RoomInfo): RoomSummaryDetails {
val latestRoomMessage = roomInfo.latestEvent?.use {
roomMessageFactory.create(it)
}
return RoomSummaryDetails(
roomId = RoomId(roomListItem.id()),
name = roomListItem.name() ?: roomListItem.id(),
canonicalAlias = roomListItem.canonicalAlias(),
isDirect = roomListItem.isDirect(),
avatarURLString = roomListItem.avatarUrl(),
unreadNotificationCount = roomListItem.unreadNotifications().use { it.notificationCount().toInt() },
roomId = RoomId(roomInfo.id),
name = roomInfo.name ?: roomInfo.id,
canonicalAlias = roomInfo.canonicalAlias,
isDirect = roomInfo.isDirect,
avatarURLString = roomInfo.avatarUrl,
unreadNotificationCount = roomInfo.notificationCount.toInt(),
lastMessage = latestRoomMessage,
lastMessageTimestamp = latestRoomMessage?.originServerTs,
inviter = room?.inviter()?.let(RoomMemberMapper::map),
inviter = roomInfo.inviter?.let(RoomMemberMapper::map),
)
}
}

View file

@ -22,11 +22,10 @@ import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.matrix.rustcomponents.sdk.Room
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
import org.matrix.rustcomponents.sdk.RoomListEntry
import org.matrix.rustcomponents.sdk.RoomListItem
import org.matrix.rustcomponents.sdk.RoomListService
import org.matrix.rustcomponents.sdk.use
import timber.log.Timber
import java.util.UUID
@ -34,7 +33,6 @@ class RoomSummaryListProcessor(
private val roomSummaries: MutableStateFlow<List<RoomSummary>>,
private val roomListService: RoomListService,
private val roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory(),
private val shouldFetchFullRoom: Boolean = false,
) {
private val roomSummariesByIdentifier = HashMap<String, RoomSummary>()
@ -120,9 +118,9 @@ class RoomSummaryListProcessor(
private suspend fun buildAndCacheRoomSummaryForIdentifier(identifier: String): RoomSummary {
val builtRoomSummary = roomListService.roomOrNull(identifier)?.use { roomListItem ->
roomListItem.fullRoomOrNull().use { fullRoom ->
roomListItem.roomInfo().use { roomInfo ->
RoomSummary.Filled(
details = roomSummaryDetailsFactory.create(roomListItem, fullRoom)
details = roomSummaryDetailsFactory.create(roomInfo)
)
}
} ?: buildEmptyRoomSummary()
@ -130,14 +128,6 @@ class RoomSummaryListProcessor(
return builtRoomSummary
}
private fun RoomListItem.fullRoomOrNull(): Room? {
return if (shouldFetchFullRoom) {
fullRoom()
} else {
null
}
}
private suspend fun updateRoomSummaries(block: suspend MutableList<RoomSummary>.() -> Unit) =
mutex.withLock {
val mutableRoomSummaries = roomSummaries.value.toMutableList()

View file

@ -38,6 +38,7 @@ import org.matrix.rustcomponents.sdk.RoomListInput
import org.matrix.rustcomponents.sdk.RoomListLoadingState
import org.matrix.rustcomponents.sdk.RoomListRange
import org.matrix.rustcomponents.sdk.RoomListServiceState
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicator
import timber.log.Timber
import org.matrix.rustcomponents.sdk.RoomListService as InnerRustRoomListService
@ -52,9 +53,9 @@ class RustRoomListService(
private val inviteRooms = MutableStateFlow<List<RoomSummary>>(emptyList())
private val allRoomsLoadingState: MutableStateFlow<RoomList.LoadingState> = MutableStateFlow(RoomList.LoadingState.NotLoaded)
private val allRoomsListProcessor = RoomSummaryListProcessor(allRooms, innerRoomListService, roomSummaryDetailsFactory, shouldFetchFullRoom = false)
private val allRoomsListProcessor = RoomSummaryListProcessor(allRooms, innerRoomListService, roomSummaryDetailsFactory)
private val invitesLoadingState: MutableStateFlow<RoomList.LoadingState> = MutableStateFlow(RoomList.LoadingState.NotLoaded)
private val inviteRoomsListProcessor = RoomSummaryListProcessor(inviteRooms, innerRoomListService, roomSummaryDetailsFactory, shouldFetchFullRoom = true)
private val inviteRoomsListProcessor = RoomSummaryListProcessor(inviteRooms, innerRoomListService, roomSummaryDetailsFactory)
init {
sessionCoroutineScope.launch(dispatcher) {
@ -106,6 +107,15 @@ class RustRoomListService(
}
}
override val syncIndicator: StateFlow<RoomListService.SyncIndicator> =
innerRoomListService.syncIndicator()
.map { it.toSyncIndicator() }
.onEach { syncIndicator ->
Timber.d("SyncIndicator = $syncIndicator")
}
.distinctUntilChanged()
.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, RoomListService.SyncIndicator.Hide)
override val state: StateFlow<RoomListService.State> =
innerRoomListService.stateFlow()
.map { it.toRoomListState() }
@ -126,6 +136,7 @@ private fun RoomListLoadingState.toLoadingState(): RoomList.LoadingState {
private fun RoomListServiceState.toRoomListState(): RoomListService.State {
return when (this) {
RoomListServiceState.INITIAL,
RoomListServiceState.RECOVERING,
RoomListServiceState.SETTING_UP -> RoomListService.State.Idle
RoomListServiceState.RUNNING -> RoomListService.State.Running
RoomListServiceState.ERROR -> RoomListService.State.Error
@ -133,6 +144,13 @@ private fun RoomListServiceState.toRoomListState(): RoomListService.State {
}
}
private fun RoomListServiceSyncIndicator.toSyncIndicator(): RoomListService.SyncIndicator {
return when (this) {
RoomListServiceSyncIndicator.SHOW -> RoomListService.SyncIndicator.Show
RoomListServiceSyncIndicator.HIDE -> RoomListService.SyncIndicator.Hide
}
}
private fun org.matrix.rustcomponents.sdk.RoomList.observeEntriesWithProcessor(processor: RoomSummaryListProcessor): Flow<List<RoomListEntriesUpdate>> {
return entriesFlow { roomListEntries ->
processor.postEntries(roomListEntries)

View file

@ -34,85 +34,92 @@ import io.element.android.libraries.matrix.impl.media.map
import io.element.android.libraries.matrix.impl.poll.map
import org.matrix.rustcomponents.sdk.TimelineItemContent
import org.matrix.rustcomponents.sdk.TimelineItemContentKind
import org.matrix.rustcomponents.sdk.use
import org.matrix.rustcomponents.sdk.EncryptedMessage as RustEncryptedMessage
import org.matrix.rustcomponents.sdk.MembershipChange as RustMembershipChange
import org.matrix.rustcomponents.sdk.OtherState as RustOtherState
class TimelineEventContentMapper(private val eventMessageMapper: EventMessageMapper = EventMessageMapper()) {
fun map(content: TimelineItemContent): EventContent = content.use {
when (val kind = it.kind()) {
is TimelineItemContentKind.FailedToParseMessageLike -> {
FailedToParseMessageLikeContent(
eventType = kind.eventType,
error = kind.error
)
fun map(content: TimelineItemContent): EventContent {
return content.use {
content.kind().use { kind ->
map(content, kind)
}
is TimelineItemContentKind.FailedToParseState -> {
FailedToParseStateContent(
eventType = kind.eventType,
stateKey = kind.stateKey,
error = kind.error
)
}
TimelineItemContentKind.Message -> {
val message = it.asMessage()
if (message == null) {
UnknownContent
} else {
eventMessageMapper.map(message)
}
}
is TimelineItemContentKind.ProfileChange -> {
ProfileChangeContent(
displayName = kind.displayName,
prevDisplayName = kind.prevDisplayName,
avatarUrl = kind.avatarUrl,
prevAvatarUrl = kind.prevAvatarUrl
)
}
TimelineItemContentKind.RedactedMessage -> {
RedactedContent
}
is TimelineItemContentKind.RoomMembership -> {
RoomMembershipContent(
UserId(kind.userId),
kind.change?.map()
)
}
is TimelineItemContentKind.State -> {
StateContent(
stateKey = kind.stateKey,
content = kind.content.map()
)
}
is TimelineItemContentKind.Sticker -> {
StickerContent(
body = kind.body,
info = kind.info.map(),
url = kind.url,
)
}
is TimelineItemContentKind.Poll -> {
PollContent(
question = kind.question,
kind = kind.kind.map(),
maxSelections = kind.maxSelections,
answers = kind.answers.map { answer -> answer.map() },
votes = kind.votes.mapValues { vote ->
vote.value.map { userId -> UserId(userId) }
},
endTime = kind.endTime,
)
}
is TimelineItemContentKind.UnableToDecrypt -> {
UnableToDecryptContent(
data = kind.msg.map()
)
}
else -> UnknownContent
}
}
private fun map(content: TimelineItemContent, kind: TimelineItemContentKind) = when (kind) {
is TimelineItemContentKind.FailedToParseMessageLike -> {
FailedToParseMessageLikeContent(
eventType = kind.eventType,
error = kind.error
)
}
is TimelineItemContentKind.FailedToParseState -> {
FailedToParseStateContent(
eventType = kind.eventType,
stateKey = kind.stateKey,
error = kind.error
)
}
TimelineItemContentKind.Message -> {
val message = content.asMessage()
if (message == null) {
UnknownContent
} else {
eventMessageMapper.map(message)
}
}
is TimelineItemContentKind.ProfileChange -> {
ProfileChangeContent(
displayName = kind.displayName,
prevDisplayName = kind.prevDisplayName,
avatarUrl = kind.avatarUrl,
prevAvatarUrl = kind.prevAvatarUrl
)
}
TimelineItemContentKind.RedactedMessage -> {
RedactedContent
}
is TimelineItemContentKind.RoomMembership -> {
RoomMembershipContent(
UserId(kind.userId),
kind.change?.map()
)
}
is TimelineItemContentKind.State -> {
StateContent(
stateKey = kind.stateKey,
content = kind.content.map()
)
}
is TimelineItemContentKind.Sticker -> {
StickerContent(
body = kind.body,
info = kind.info.map(),
url = kind.url,
)
}
is TimelineItemContentKind.Poll -> {
PollContent(
question = kind.question,
kind = kind.kind.map(),
maxSelections = kind.maxSelections,
answers = kind.answers.map { answer -> answer.map() },
votes = kind.votes.mapValues { vote ->
vote.value.map { userId -> UserId(userId) }
},
endTime = kind.endTime,
)
}
is TimelineItemContentKind.UnableToDecrypt -> {
UnableToDecryptContent(
data = kind.msg.map()
)
}
else -> UnknownContent
}
}
private fun RustMembershipChange.map(): MembershipChange {

View file

@ -21,47 +21,45 @@ import io.element.android.libraries.matrix.api.notificationsettings.Notification
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
import io.element.android.libraries.matrix.api.room.RoomNotificationSettings
import io.element.android.libraries.matrix.test.A_ROOM_NOTIFICATION_MODE
import io.element.android.libraries.matrix.test.A_ROOM_NOTIFICATION_SETTINGS
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
class FakeNotificationSettingsService : NotificationSettingsService {
class FakeNotificationSettingsService(
initialMode: RoomNotificationMode = A_ROOM_NOTIFICATION_MODE,
initialDefaultMode: RoomNotificationMode = A_ROOM_NOTIFICATION_MODE
) : NotificationSettingsService {
private var _roomNotificationSettingsStateFlow = MutableStateFlow(Unit)
private val muteRoomResult: Result<Unit> = Result.success(Unit)
private val unmuteRoomResult: Result<Unit> = Result.success(Unit)
private val setRoomNotificationMode: Result<Unit> = Result.success(Unit)
private val restoreDefaultRoomNotificationMode: Result<Unit> = Result.success(Unit)
private val getRoomNotificationSettingsResult: Result<RoomNotificationSettings> = Result.success(A_ROOM_NOTIFICATION_SETTINGS)
private val getDefaultRoomNotificationMode: Result<RoomNotificationMode> = Result.success(A_ROOM_NOTIFICATION_MODE)
private var defaultRoomNotificationMode: RoomNotificationMode = initialDefaultMode
private var roomNotificationMode: RoomNotificationMode = initialMode
override val notificationSettingsChangeFlow: SharedFlow<Unit>
get() = _roomNotificationSettingsStateFlow
override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result<RoomNotificationSettings> {
return getRoomNotificationSettingsResult
override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, membersCount: Long): Result<RoomNotificationSettings> {
return Result.success(RoomNotificationSettings(mode = roomNotificationMode, isDefault = roomNotificationMode == defaultRoomNotificationMode))
}
override suspend fun getDefaultRoomNotificationMode(isEncrypted: Boolean, isOneToOne: Boolean): Result<RoomNotificationMode> {
return getDefaultRoomNotificationMode
}
override suspend fun setDefaultRoomNotificationMode(isEncrypted: Boolean, mode: RoomNotificationMode, isOneToOne: Boolean): Result<Unit> {
TODO("Not yet implemented")
override suspend fun getDefaultRoomNotificationMode(isEncrypted: Boolean, membersCount: Long): Result<RoomNotificationMode> {
return Result.success(defaultRoomNotificationMode)
}
override suspend fun setRoomNotificationMode(roomId: RoomId, mode: RoomNotificationMode): Result<Unit> {
return setRoomNotificationMode
roomNotificationMode = mode
_roomNotificationSettingsStateFlow.emit(Unit)
return Result.success(Unit)
}
override suspend fun restoreDefaultRoomNotificationMode(roomId: RoomId): Result<Unit> {
return restoreDefaultRoomNotificationMode
roomNotificationMode = defaultRoomNotificationMode
_roomNotificationSettingsStateFlow.emit(Unit)
return Result.success(Unit)
}
override suspend fun muteRoom(roomId: RoomId): Result<Unit> {
return muteRoomResult
return setRoomNotificationMode(roomId, RoomNotificationMode.MUTE)
}
override suspend fun unmuteRoom(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result<Unit> {
return unmuteRoomResult
override suspend fun unmuteRoom(roomId: RoomId, isEncrypted: Boolean, membersCount: Long): Result<Unit> {
return restoreDefaultRoomNotificationMode(roomId)
}
override suspend fun isRoomMentionEnabled(): Result<Boolean> {
@ -79,5 +77,4 @@ class FakeNotificationSettingsService : NotificationSettingsService {
override suspend fun setCallEnabled(enabled: Boolean): Result<Unit> {
return Result.success(Unit)
}
}

View file

@ -27,6 +27,7 @@ import io.element.android.libraries.matrix.api.media.FileInfo
import io.element.android.libraries.matrix.api.media.ImageInfo
import io.element.android.libraries.matrix.api.media.MediaUploadHandler
import io.element.android.libraries.matrix.api.media.VideoInfo
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
import io.element.android.libraries.matrix.api.poll.PollKind
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
@ -38,6 +39,7 @@ import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
import io.element.android.libraries.matrix.test.timeline.FakeMatrixTimeline
import io.element.android.tests.testutils.simulateLongTask
import kotlinx.coroutines.delay
@ -59,6 +61,7 @@ class FakeMatrixRoom(
override val isDirect: Boolean = false,
override val joinedMemberCount: Long = 123L,
override val activeMemberCount: Long = 234L,
val notificationSettingsService: NotificationSettingsService = FakeNotificationSettingsService(),
private val matrixTimeline: MatrixTimeline = FakeMatrixTimeline(),
canRedact: Boolean = false,
) : MatrixRoom {
@ -90,7 +93,7 @@ class FakeMatrixRoom(
private var sendPollResponseResult = Result.success(Unit)
private var endPollResult = Result.success(Unit)
private var progressCallbackValues = emptyList<Pair<Long, Long>>()
val editMessageCalls = mutableListOf<String>()
val editMessageCalls = mutableListOf<Pair<String, String>>()
var sendMediaCount = 0
private set
@ -146,7 +149,9 @@ class FakeMatrixRoom(
}
override suspend fun updateRoomNotificationSettings(): Result<Unit> = simulateLongTask {
updateRoomNotificationSettingsResult
val notificationSettings = notificationSettingsService.getRoomNotificationSettings(roomId, isEncrypted, activeMemberCount).getOrThrow()
roomNotificationSettingsStateFlow.value = MatrixRoomNotificationSettingsState.Ready(notificationSettings)
return Result.success(Unit)
}
override val syncUpdateFlow: StateFlow<Long> = MutableStateFlow(0L)
@ -167,7 +172,7 @@ class FakeMatrixRoom(
userAvatarUrlResult
}
override suspend fun sendMessage(message: String): Result<Unit> = simulateLongTask {
override suspend fun sendMessage(body: String, htmlBody: String) = simulateLongTask {
Result.success(Unit)
}
@ -196,16 +201,16 @@ class FakeMatrixRoom(
return cancelSendResult
}
override suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, message: String): Result<Unit> {
editMessageCalls += message
override suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String): Result<Unit> {
editMessageCalls += body to htmlBody
return Result.success(Unit)
}
var replyMessageParameter: String? = null
var replyMessageParameter: Pair<String, String>? = null
private set
override suspend fun replyMessage(eventId: EventId, message: String): Result<Unit> {
replyMessageParameter = message
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String): Result<Unit> {
replyMessageParameter = body to htmlBody
return Result.success(Unit)
}

View file

@ -29,6 +29,7 @@ class FakeRoomListService : RoomListService {
private val allRoomsLoadingStateFlow = MutableStateFlow<RoomList.LoadingState>(RoomList.LoadingState.NotLoaded)
private val inviteRoomsLoadingStateFlow = MutableStateFlow<RoomList.LoadingState>(RoomList.LoadingState.NotLoaded)
private val roomListStateFlow = MutableStateFlow<RoomListService.State>(RoomListService.State.Idle)
private val syncIndicatorStateFlow = MutableStateFlow<RoomListService.SyncIndicator>(RoomListService.SyncIndicator.Hide)
suspend fun postAllRooms(roomSummaries: List<RoomSummary>) {
allRoomSummariesFlow.emit(roomSummaries)
@ -72,4 +73,6 @@ class FakeRoomListService : RoomListService {
}
override val state: StateFlow<RoomListService.State> = roomListStateFlow
override val syncIndicator: StateFlow<RoomListService.SyncIndicator> = syncIndicatorStateFlow
}