Merge branch 'develop' into feature/fga/user_moderation_bottomsheet
This commit is contained in:
commit
03e1926e14
250 changed files with 3339 additions and 2742 deletions
|
|
@ -162,6 +162,11 @@ interface MatrixClient {
|
|||
|
||||
fun canDeactivateAccount(): Boolean
|
||||
suspend fun deactivateAccount(password: String, eraseData: Boolean): Result<Unit>
|
||||
|
||||
/**
|
||||
* Check if the user can report a room.
|
||||
*/
|
||||
suspend fun canReportRoom(): Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -43,7 +43,10 @@ interface MatrixAuthenticationService {
|
|||
/**
|
||||
* Get the Oidc url to display to the user.
|
||||
*/
|
||||
suspend fun getOidcUrl(prompt: OidcPrompt): Result<OidcDetails>
|
||||
suspend fun getOidcUrl(
|
||||
prompt: OidcPrompt,
|
||||
loginHint: String?,
|
||||
): Result<OidcDetails>
|
||||
|
||||
/**
|
||||
* Cancel Oidc login sequence.
|
||||
|
|
|
|||
|
|
@ -7,4 +7,6 @@
|
|||
|
||||
package io.element.android.libraries.matrix.api.auth.qrlogin
|
||||
|
||||
interface MatrixQrCodeLoginData
|
||||
interface MatrixQrCodeLoginData {
|
||||
fun serverName(): String?
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,12 @@ interface BaseRoom : Closeable {
|
|||
*/
|
||||
fun info(): RoomInfo = roomInfoFlow.value
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*/
|
||||
val isOneToOne: Boolean get() = info().activeMembersCount == 2L
|
||||
|
||||
/**
|
||||
* Try to load the room members and update the membersFlow.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -9,34 +9,21 @@ package io.element.android.libraries.matrix.api.room
|
|||
|
||||
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.ProgressCallback
|
||||
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.SendHandle
|
||||
import io.element.android.libraries.matrix.api.core.TransactionId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.encryption.identity.IdentityStateChange
|
||||
import io.element.android.libraries.matrix.api.media.AudioInfo
|
||||
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.poll.PollKind
|
||||
import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.room.knock.KnockRequest
|
||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||
import io.element.android.libraries.matrix.api.room.message.ReplyParameters
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevels
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.io.File
|
||||
|
||||
interface JoinedRoom : BaseRoom {
|
||||
val syncUpdateFlow: StateFlow<Long>
|
||||
|
|
@ -50,12 +37,6 @@ interface JoinedRoom : BaseRoom {
|
|||
*/
|
||||
val knockRequestsFlow: Flow<List<KnockRequest>>
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*/
|
||||
val isOneToOne: Boolean get() = info().activeMembersCount == 2L
|
||||
|
||||
/**
|
||||
* The live timeline of the room. Must be used to send Event to a room.
|
||||
*/
|
||||
|
|
@ -69,135 +50,14 @@ interface JoinedRoom : BaseRoom {
|
|||
createTimelineParams: CreateTimelineParams,
|
||||
): Result<Timeline>
|
||||
|
||||
suspend fun sendMessage(body: String, htmlBody: String?, intentionalMentions: List<IntentionalMention>): Result<Unit>
|
||||
|
||||
suspend fun editMessage(eventId: EventId, body: String, htmlBody: String?, intentionalMentions: List<IntentionalMention>): Result<Unit>
|
||||
|
||||
suspend fun sendImage(
|
||||
file: File,
|
||||
thumbnailFile: File?,
|
||||
imageInfo: ImageInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler>
|
||||
|
||||
suspend fun sendVideo(
|
||||
file: File,
|
||||
thumbnailFile: File?,
|
||||
videoInfo: VideoInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler>
|
||||
|
||||
suspend fun sendAudio(
|
||||
file: File,
|
||||
audioInfo: AudioInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler>
|
||||
|
||||
suspend fun sendFile(
|
||||
file: File,
|
||||
fileInfo: FileInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler>
|
||||
|
||||
suspend fun sendVoiceMessage(
|
||||
file: File,
|
||||
audioInfo: AudioInfo,
|
||||
waveform: List<Float>,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler>
|
||||
|
||||
/**
|
||||
* Share a location message in the room.
|
||||
*
|
||||
* @param body A human readable textual representation of the location.
|
||||
* @param geoUri A geo URI (RFC 5870) representing the location e.g. `geo:51.5008,0.1247;u=35`.
|
||||
* Respectively: latitude, longitude, and (optional) uncertainty.
|
||||
* @param description Optional description of the location to display to the user.
|
||||
* @param zoomLevel Optional zoom level to display the map at.
|
||||
* @param assetType Optional type of the location asset.
|
||||
* Set to SENDER if sharing own location. Set to PIN if sharing any location.
|
||||
*/
|
||||
suspend fun sendLocation(
|
||||
body: String,
|
||||
geoUri: String,
|
||||
description: String? = null,
|
||||
zoomLevel: Int? = null,
|
||||
assetType: AssetType? = null,
|
||||
): Result<Unit>
|
||||
|
||||
/**
|
||||
* Create a poll in the room.
|
||||
*
|
||||
* @param question The question to ask.
|
||||
* @param answers The list of answers.
|
||||
* @param maxSelections The maximum number of answers that can be selected.
|
||||
* @param pollKind The kind of poll to create.
|
||||
*/
|
||||
suspend fun createPoll(
|
||||
question: String,
|
||||
answers: List<String>,
|
||||
maxSelections: Int,
|
||||
pollKind: PollKind,
|
||||
): Result<Unit>
|
||||
|
||||
/**
|
||||
* Edit a poll in the room.
|
||||
*
|
||||
* @param pollStartId The event ID of the poll start event.
|
||||
* @param question The question to ask.
|
||||
* @param answers The list of answers.
|
||||
* @param maxSelections The maximum number of answers that can be selected.
|
||||
* @param pollKind The kind of poll to create.
|
||||
*/
|
||||
suspend fun editPoll(
|
||||
pollStartId: EventId,
|
||||
question: String,
|
||||
answers: List<String>,
|
||||
maxSelections: Int,
|
||||
pollKind: PollKind,
|
||||
): Result<Unit>
|
||||
|
||||
/**
|
||||
* Send a response to a poll.
|
||||
*
|
||||
* @param pollStartId The event ID of the poll start event.
|
||||
* @param answers The list of answer ids to send.
|
||||
*/
|
||||
suspend fun sendPollResponse(pollStartId: EventId, answers: List<String>): Result<Unit>
|
||||
|
||||
/**
|
||||
* Ends a poll in the room.
|
||||
*
|
||||
* @param pollStartId The event ID of the poll start event.
|
||||
* @param text Fallback text of the poll end event.
|
||||
*/
|
||||
suspend fun endPoll(pollStartId: EventId, text: String): Result<Unit>
|
||||
|
||||
/**
|
||||
* Send a typing notification.
|
||||
* @param isTyping True if the user is typing, false otherwise.
|
||||
*/
|
||||
suspend fun typingNotice(isTyping: Boolean): Result<Unit>
|
||||
|
||||
suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result<Unit>
|
||||
|
||||
suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit>
|
||||
|
||||
suspend fun cancelSend(transactionId: TransactionId): Result<Unit>
|
||||
|
||||
suspend fun inviteUserById(id: UserId): Result<Unit>
|
||||
|
||||
suspend fun updateAvatar(mimeType: String, data: ByteArray): Result<Unit>
|
||||
|
|
|
|||
|
|
@ -666,6 +666,12 @@ class RustMatrixClient(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun canReportRoom(): Boolean = withContext(sessionDispatcher) {
|
||||
runCatching {
|
||||
innerClient.isReportRoomApiSupported()
|
||||
}.getOrDefault(false)
|
||||
}
|
||||
|
||||
private suspend fun File.getCacheSize(
|
||||
includeCryptoDb: Boolean = false,
|
||||
): Long = withContext(sessionDispatcher) {
|
||||
|
|
|
|||
|
|
@ -187,14 +187,17 @@ class RustMatrixAuthenticationService @Inject constructor(
|
|||
|
||||
private var pendingOAuthAuthorizationData: OAuthAuthorizationData? = null
|
||||
|
||||
override suspend fun getOidcUrl(prompt: OidcPrompt): Result<OidcDetails> {
|
||||
override suspend fun getOidcUrl(
|
||||
prompt: OidcPrompt,
|
||||
loginHint: String?,
|
||||
): Result<OidcDetails> {
|
||||
return withContext(coroutineDispatchers.io) {
|
||||
runCatching {
|
||||
val client = currentClient ?: error("You need to call `setHomeserver()` first")
|
||||
val oAuthAuthorizationData = client.urlForOidc(
|
||||
oidcConfiguration = oidcConfigurationProvider.get(),
|
||||
prompt = prompt.toRustPrompt(),
|
||||
loginHint = null,
|
||||
loginHint = loginHint,
|
||||
)
|
||||
val url = oAuthAuthorizationData.loginUrl()
|
||||
pendingOAuthAuthorizationData = oAuthAuthorizationData
|
||||
|
|
|
|||
|
|
@ -12,4 +12,8 @@ import org.matrix.rustcomponents.sdk.QrCodeData as RustQrCodeData
|
|||
|
||||
class SdkQrCodeLoginData(
|
||||
internal val rustQrCodeData: RustQrCodeData,
|
||||
) : MatrixQrCodeLoginData
|
||||
) : MatrixQrCodeLoginData {
|
||||
override fun serverName(): String? {
|
||||
return rustQrCodeData.serverName()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,20 +13,11 @@ import io.element.android.libraries.core.extensions.mapFailure
|
|||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||
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.ProgressCallback
|
||||
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.SendHandle
|
||||
import io.element.android.libraries.matrix.api.core.TransactionId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.encryption.identity.IdentityStateChange
|
||||
import io.element.android.libraries.matrix.api.media.AudioInfo
|
||||
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.BaseRoom
|
||||
import io.element.android.libraries.matrix.api.room.CreateTimelineParams
|
||||
import io.element.android.libraries.matrix.api.room.IntentionalMention
|
||||
|
|
@ -35,14 +26,11 @@ import io.element.android.libraries.matrix.api.room.RoomNotificationSettingsStat
|
|||
import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.room.knock.KnockRequest
|
||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||
import io.element.android.libraries.matrix.api.room.message.ReplyParameters
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevels
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange
|
||||
import io.element.android.libraries.matrix.api.room.roomNotificationSettings
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
|
||||
import io.element.android.libraries.matrix.impl.core.RustSendHandle
|
||||
|
|
@ -59,11 +47,8 @@ import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
|
|||
import io.element.android.libraries.matrix.impl.widget.RustWidgetDriver
|
||||
import io.element.android.libraries.matrix.impl.widget.generateWidgetWebViewUrl
|
||||
import io.element.android.services.toolbox.api.systemclock.SystemClock
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.drop
|
||||
|
|
@ -71,12 +56,10 @@ import kotlinx.coroutines.flow.launchIn
|
|||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.DateDividerMode
|
||||
import org.matrix.rustcomponents.sdk.IdentityStatusChangeListener
|
||||
import org.matrix.rustcomponents.sdk.KnockRequestsListener
|
||||
import org.matrix.rustcomponents.sdk.RoomInfoListener
|
||||
import org.matrix.rustcomponents.sdk.RoomMessageEventMessageType
|
||||
import org.matrix.rustcomponents.sdk.TimelineConfiguration
|
||||
import org.matrix.rustcomponents.sdk.TimelineFilter
|
||||
|
|
@ -89,11 +72,9 @@ import org.matrix.rustcomponents.sdk.getElementCallRequiredPermissions
|
|||
import org.matrix.rustcomponents.sdk.use
|
||||
import timber.log.Timber
|
||||
import uniffi.matrix_sdk.RoomPowerLevelChanges
|
||||
import java.io.File
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
import org.matrix.rustcomponents.sdk.IdentityStatusChange as RustIdentityStateChange
|
||||
import org.matrix.rustcomponents.sdk.KnockRequest as InnerKnockRequest
|
||||
import org.matrix.rustcomponents.sdk.RoomInfo as InnerRoomInfo
|
||||
import org.matrix.rustcomponents.sdk.Timeline as InnerTimeline
|
||||
|
||||
class JoinedRustRoom(
|
||||
|
|
@ -101,7 +82,6 @@ class JoinedRustRoom(
|
|||
private val liveInnerTimeline: InnerTimeline,
|
||||
private val notificationSettingsService: NotificationSettingsService,
|
||||
private val coroutineDispatchers: CoroutineDispatchers,
|
||||
private val roomInfoMapper: RoomInfoMapper,
|
||||
private val systemClock: SystemClock,
|
||||
private val roomContentForwarder: RoomContentForwarder,
|
||||
private val featureFlagService: FeatureFlagService,
|
||||
|
|
@ -112,14 +92,6 @@ class JoinedRustRoom(
|
|||
|
||||
override val syncUpdateFlow = MutableStateFlow(0L)
|
||||
|
||||
override val roomInfoFlow: StateFlow<io.element.android.libraries.matrix.api.room.RoomInfo> = mxCallbackFlow {
|
||||
innerRoom.subscribeToRoomInfoUpdates(object : RoomInfoListener {
|
||||
override fun call(roomInfo: InnerRoomInfo) {
|
||||
channel.trySend(roomInfoMapper.map(roomInfo))
|
||||
}
|
||||
})
|
||||
}.stateIn(roomCoroutineScope, started = SharingStarted.Lazily, initialValue = baseRoom.info())
|
||||
|
||||
override val roomTypingMembersFlow: Flow<List<UserId>> = mxCallbackFlow {
|
||||
val initial = emptyList<UserId>()
|
||||
channel.trySend(initial)
|
||||
|
|
@ -264,10 +236,6 @@ class JoinedRustRoom(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun sendMessage(body: String, htmlBody: String?, intentionalMentions: List<IntentionalMention>): Result<Unit> {
|
||||
return liveTimeline.sendMessage(body, htmlBody, intentionalMentions)
|
||||
}
|
||||
|
||||
override suspend fun editMessage(
|
||||
eventId: EventId,
|
||||
body: String,
|
||||
|
|
@ -281,159 +249,12 @@ class JoinedRustRoom(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun sendImage(
|
||||
file: File,
|
||||
thumbnailFile: File?,
|
||||
imageInfo: ImageInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> {
|
||||
return liveTimeline.sendImage(
|
||||
file = file,
|
||||
thumbnailFile = thumbnailFile,
|
||||
imageInfo = imageInfo,
|
||||
caption = caption,
|
||||
formattedCaption = formattedCaption,
|
||||
progressCallback = progressCallback,
|
||||
replyParameters = replyParameters
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendVideo(
|
||||
file: File,
|
||||
thumbnailFile: File?,
|
||||
videoInfo: VideoInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> {
|
||||
return liveTimeline.sendVideo(
|
||||
file = file,
|
||||
thumbnailFile = thumbnailFile,
|
||||
videoInfo = videoInfo,
|
||||
caption = caption,
|
||||
formattedCaption = formattedCaption,
|
||||
progressCallback = progressCallback,
|
||||
replyParameters = replyParameters
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendAudio(
|
||||
file: File,
|
||||
audioInfo: AudioInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> {
|
||||
return liveTimeline.sendAudio(
|
||||
file = file,
|
||||
audioInfo = audioInfo,
|
||||
caption = caption,
|
||||
formattedCaption = formattedCaption,
|
||||
progressCallback = progressCallback,
|
||||
replyParameters = replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendFile(
|
||||
file: File,
|
||||
fileInfo: FileInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> {
|
||||
return liveTimeline.sendFile(
|
||||
file = file,
|
||||
fileInfo = fileInfo,
|
||||
caption = caption,
|
||||
formattedCaption = formattedCaption,
|
||||
progressCallback = progressCallback,
|
||||
replyParameters = replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendVoiceMessage(
|
||||
file: File,
|
||||
audioInfo: AudioInfo,
|
||||
waveform: List<Float>,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> {
|
||||
return liveTimeline.sendVoiceMessage(
|
||||
file = file,
|
||||
audioInfo = audioInfo,
|
||||
waveform = waveform,
|
||||
progressCallback = progressCallback,
|
||||
replyParameters = replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendLocation(
|
||||
body: String,
|
||||
geoUri: String,
|
||||
description: String?,
|
||||
zoomLevel: Int?,
|
||||
assetType: AssetType?,
|
||||
): Result<Unit> {
|
||||
return liveTimeline.sendLocation(body, geoUri, description, zoomLevel, assetType)
|
||||
}
|
||||
|
||||
override suspend fun createPoll(
|
||||
question: String,
|
||||
answers: List<String>,
|
||||
maxSelections: Int,
|
||||
pollKind: PollKind,
|
||||
): Result<Unit> {
|
||||
return liveTimeline.createPoll(question, answers, maxSelections, pollKind)
|
||||
}
|
||||
|
||||
override suspend fun editPoll(
|
||||
pollStartId: EventId,
|
||||
question: String,
|
||||
answers: List<String>,
|
||||
maxSelections: Int,
|
||||
pollKind: PollKind,
|
||||
): Result<Unit> {
|
||||
return liveTimeline.editPoll(pollStartId, question, answers, maxSelections, pollKind)
|
||||
}
|
||||
|
||||
override suspend fun sendPollResponse(
|
||||
pollStartId: EventId,
|
||||
answers: List<String>
|
||||
): Result<Unit> {
|
||||
return liveTimeline.sendPollResponse(pollStartId, answers)
|
||||
}
|
||||
|
||||
override suspend fun endPoll(
|
||||
pollStartId: EventId,
|
||||
text: String
|
||||
): Result<Unit> {
|
||||
return liveTimeline.endPoll(pollStartId, text)
|
||||
}
|
||||
|
||||
override suspend fun typingNotice(isTyping: Boolean) = withContext(roomDispatcher) {
|
||||
runCatching {
|
||||
innerRoom.typingNotice(isTyping)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result<Unit> {
|
||||
return liveTimeline.toggleReaction(emoji, eventOrTransactionId)
|
||||
}
|
||||
|
||||
override suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit> {
|
||||
return liveTimeline.forwardEvent(eventId, roomIds)
|
||||
}
|
||||
|
||||
override suspend fun cancelSend(transactionId: TransactionId): Result<Unit> {
|
||||
return liveTimeline.cancelSend(transactionId)
|
||||
}
|
||||
|
||||
override suspend fun inviteUserById(id: UserId): Result<Unit> = withContext(roomDispatcher) {
|
||||
runCatching {
|
||||
innerRoom.inviteUserById(id.value)
|
||||
|
|
@ -645,7 +466,6 @@ class JoinedRustRoom(
|
|||
override fun destroy() {
|
||||
baseRoom.destroy()
|
||||
liveInnerTimeline.destroy()
|
||||
roomCoroutineScope.cancel()
|
||||
}
|
||||
|
||||
private fun InnerTimeline.map(
|
||||
|
|
|
|||
|
|
@ -31,10 +31,14 @@ import io.element.android.libraries.matrix.impl.room.member.RoomMemberMapper
|
|||
import io.element.android.libraries.matrix.impl.room.powerlevels.RoomPowerLevelsMapper
|
||||
import io.element.android.libraries.matrix.impl.roomdirectory.map
|
||||
import io.element.android.libraries.matrix.impl.timeline.toRustReceiptType
|
||||
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.RoomInfoListener
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
import timber.log.Timber
|
||||
import uniffi.matrix_sdk_base.EncryptionState
|
||||
|
|
@ -48,6 +52,7 @@ class RustBaseRoom(
|
|||
private val roomSyncSubscriber: RoomSyncSubscriber,
|
||||
private val roomMembershipObserver: RoomMembershipObserver,
|
||||
sessionCoroutineScope: CoroutineScope,
|
||||
roomInfoMapper: RoomInfoMapper,
|
||||
initialRoomInfo: RoomInfo,
|
||||
) : BaseRoom {
|
||||
override val roomId = RoomId(innerRoom.id())
|
||||
|
|
@ -62,10 +67,16 @@ class RustBaseRoom(
|
|||
|
||||
override val membersStateFlow: StateFlow<RoomMembersState> = roomMemberListFetcher.membersFlow
|
||||
|
||||
override val roomInfoFlow: StateFlow<RoomInfo> = MutableStateFlow(initialRoomInfo)
|
||||
|
||||
override val roomCoroutineScope = sessionCoroutineScope.childScope(coroutineDispatchers.main, "RoomScope-$roomId")
|
||||
|
||||
override val roomInfoFlow: StateFlow<RoomInfo> = mxCallbackFlow {
|
||||
innerRoom.subscribeToRoomInfoUpdates(object : RoomInfoListener {
|
||||
override fun call(roomInfo: org.matrix.rustcomponents.sdk.RoomInfo) {
|
||||
channel.trySend(roomInfoMapper.map(roomInfo))
|
||||
}
|
||||
})
|
||||
}.stateIn(roomCoroutineScope, started = SharingStarted.Lazily, initialValue = initialRoomInfo)
|
||||
|
||||
override suspend fun subscribeToSync() = roomSyncSubscriber.subscribe(roomId)
|
||||
|
||||
override suspend fun updateMembers() {
|
||||
|
|
@ -98,6 +109,7 @@ class RustBaseRoom(
|
|||
|
||||
override fun destroy() {
|
||||
innerRoom.destroy()
|
||||
roomCoroutineScope.cancel()
|
||||
}
|
||||
|
||||
override suspend fun userDisplayName(userId: UserId): Result<String?> = withContext(roomDispatcher) {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ package io.element.android.libraries.matrix.impl.room
|
|||
|
||||
import io.element.android.appconfig.TimelineConfig
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.core.data.tryOrNull
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||
import io.element.android.libraries.matrix.api.core.DeviceId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
|
@ -21,6 +20,7 @@ import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
|||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import io.element.android.libraries.matrix.api.roomlist.awaitLoaded
|
||||
import io.element.android.libraries.matrix.impl.room.preview.RoomPreviewInfoMapper
|
||||
import io.element.android.libraries.matrix.impl.roomlist.fullRoomWithTimeline
|
||||
import io.element.android.libraries.matrix.impl.roomlist.roomOrNull
|
||||
import io.element.android.services.toolbox.api.systemclock.SystemClock
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
|
@ -30,10 +30,10 @@ import kotlinx.coroutines.sync.withLock
|
|||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.Client
|
||||
import org.matrix.rustcomponents.sdk.Membership
|
||||
import org.matrix.rustcomponents.sdk.Room
|
||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import org.matrix.rustcomponents.sdk.Room as SdkRoom
|
||||
import org.matrix.rustcomponents.sdk.RoomListService as InnerRoomListService
|
||||
|
||||
class RustRoomFactory(
|
||||
|
|
@ -56,11 +56,6 @@ class RustRoomFactory(
|
|||
private val mutex = Mutex()
|
||||
private val isDestroyed: AtomicBoolean = AtomicBoolean(false)
|
||||
|
||||
private data class RustRoomReferences(
|
||||
val roomListItem: RoomListItem,
|
||||
val room: SdkRoom,
|
||||
)
|
||||
|
||||
private val roomInfoMapper = RoomInfoMapper()
|
||||
|
||||
private val eventFilters = TimelineConfig.excludedEvents
|
||||
|
|
@ -84,20 +79,26 @@ class RustRoomFactory(
|
|||
Timber.d("Room factory is destroyed, returning null for $roomId")
|
||||
return@withContext null
|
||||
}
|
||||
val roomReferences = awaitRoomReferences(roomId) ?: return@withContext null
|
||||
getBaseRoom(roomReferences)
|
||||
val roomListItem = awaitRoomListItem(roomId) ?: return@withContext null
|
||||
getBaseRoom(roomListItem)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getBaseRoom(roomReferences: RustRoomReferences): RustBaseRoom? {
|
||||
val initialRoomInfo = roomReferences.room.roomInfo()
|
||||
private suspend fun getBaseRoom(roomListItem: RoomListItem): RustBaseRoom? {
|
||||
val sdkRoom = innerClient.getRoom(roomListItem.id()) ?: return null
|
||||
return getBaseRoom(sdkRoom)
|
||||
}
|
||||
|
||||
private suspend fun getBaseRoom(sdkRoom: Room): RustBaseRoom {
|
||||
val initialRoomInfo = sdkRoom.roomInfo()
|
||||
return RustBaseRoom(
|
||||
sessionId = sessionId,
|
||||
deviceId = deviceId,
|
||||
innerRoom = roomReferences.room,
|
||||
innerRoom = sdkRoom,
|
||||
coroutineDispatchers = dispatchers,
|
||||
roomSyncSubscriber = roomSyncSubscriber,
|
||||
roomMembershipObserver = roomMembershipObserver,
|
||||
roomInfoMapper = roomInfoMapper,
|
||||
initialRoomInfo = roomInfoMapper.map(initialRoomInfo),
|
||||
sessionCoroutineScope = sessionCoroutineScope,
|
||||
)
|
||||
|
|
@ -109,31 +110,26 @@ class RustRoomFactory(
|
|||
Timber.d("Room factory is destroyed, returning null for $roomId")
|
||||
return@withContext null
|
||||
}
|
||||
val roomReferences = awaitRoomReferences(roomId) ?: return@withContext null
|
||||
|
||||
if (roomReferences.room.membership() == Membership.JOINED) {
|
||||
val baseRoom = getBaseRoom(roomReferences) ?: return@withContext null
|
||||
val roomListItem = awaitRoomListItem(roomId) ?: return@withContext null
|
||||
|
||||
if (roomListItem.membership() == Membership.JOINED) {
|
||||
// Init the live timeline in the SDK from the RoomListItem
|
||||
if (!roomReferences.roomListItem.isTimelineInitialized()) {
|
||||
roomReferences.roomListItem.initTimeline(eventFilters, "LIVE")
|
||||
}
|
||||
val sdkRoom = roomListItem.fullRoomWithTimeline(eventFilters)
|
||||
|
||||
GetRoomResult.Joined(
|
||||
JoinedRustRoom(
|
||||
baseRoom = baseRoom,
|
||||
baseRoom = getBaseRoom(sdkRoom),
|
||||
notificationSettingsService = notificationSettingsService,
|
||||
roomContentForwarder = roomContentForwarder,
|
||||
liveInnerTimeline = roomReferences.room.timeline(),
|
||||
liveInnerTimeline = sdkRoom.timeline(),
|
||||
coroutineDispatchers = dispatchers,
|
||||
systemClock = systemClock,
|
||||
roomInfoMapper = roomInfoMapper,
|
||||
featureFlagService = featureFlagService,
|
||||
)
|
||||
)
|
||||
} else {
|
||||
val preview = try {
|
||||
roomReferences.roomListItem.previewRoom(via = emptyList())
|
||||
roomListItem.previewRoom(via = emptyList())
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Failed to get room preview for $roomId")
|
||||
return@withContext null
|
||||
|
|
@ -142,7 +138,7 @@ class RustRoomFactory(
|
|||
GetRoomResult.NotJoined(
|
||||
NotJoinedRustRoom(
|
||||
sessionId = sessionId,
|
||||
localRoom = getBaseRoom(roomReferences),
|
||||
localRoom = getBaseRoom(roomListItem),
|
||||
previewInfo = RoomPreviewInfoMapper.map(preview.info()),
|
||||
)
|
||||
)
|
||||
|
|
@ -150,36 +146,23 @@ class RustRoomFactory(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getRoomReferences(roomId: RoomId): RustRoomReferences? {
|
||||
val roomListItem = innerRoomListService.roomOrNull(roomId.value)
|
||||
/**
|
||||
* Get the Rust room list item for a room, retrying after the room list is loaded if necessary.
|
||||
*/
|
||||
private suspend fun awaitRoomListItem(roomId: RoomId): RoomListItem? {
|
||||
var roomListItem = innerRoomListService.roomOrNull(roomId.value)
|
||||
if (roomListItem == null) {
|
||||
// ... otherwise, lets wait for the SS to load all rooms and check again.
|
||||
roomListService.allRooms.awaitLoaded()
|
||||
roomListItem = innerRoomListService.roomOrNull(roomId.value)
|
||||
}
|
||||
|
||||
if (roomListItem == null) {
|
||||
Timber.d("Room not found for $roomId")
|
||||
return null
|
||||
}
|
||||
val room = tryOrNull {
|
||||
innerClient.getRoom(roomId.value)
|
||||
} ?: error("Failed to get room for room id: $roomId")
|
||||
|
||||
Timber.d("Got room for $roomId")
|
||||
return RustRoomReferences(
|
||||
roomListItem = roomListItem,
|
||||
room = room,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Rust room references for a room, retrying after the room list is loaded if necessary.
|
||||
*/
|
||||
private suspend fun awaitRoomReferences(roomId: RoomId): RustRoomReferences? {
|
||||
var roomReferences = getRoomReferences(roomId)
|
||||
|
||||
if (roomReferences == null) {
|
||||
// ... otherwise, lets wait for the SS to load all rooms and check again.
|
||||
roomListService.allRooms.awaitLoaded()
|
||||
roomReferences = getRoomReferences(roomId)
|
||||
}
|
||||
|
||||
return roomReferences
|
||||
return roomListItem
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ class DefaultCallWidgetSettingsProvider @Inject constructor(
|
|||
sentryEnvironment = if (buildMeta.buildType == BuildType.RELEASE) "RELEASE" else "DEBUG",
|
||||
parentUrl = null,
|
||||
hideHeader = true,
|
||||
controlledMediaDevices = false,
|
||||
)
|
||||
val rustWidgetSettings = newVirtualElementCallWidget(options)
|
||||
return MatrixWidgetSettings.fromRustWidgetSettings(rustWidgetSettings)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.auth.qrlogin
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeQrCodeData
|
||||
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
|
||||
import org.junit.Test
|
||||
|
||||
class SdkQrCodeLoginDataTest {
|
||||
@Test
|
||||
fun `getServer reads the value from the Rust side, null case`() {
|
||||
val sut = SdkQrCodeLoginData(
|
||||
rustQrCodeData = FakeQrCodeData(
|
||||
serverNameResult = { null },
|
||||
),
|
||||
)
|
||||
assertThat(sut.serverName()).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getServer reads the value from the Rust side`() {
|
||||
val sut = SdkQrCodeLoginData(
|
||||
rustQrCodeData = FakeQrCodeData(
|
||||
serverNameResult = { A_HOMESERVER_URL },
|
||||
),
|
||||
)
|
||||
assertThat(sut.serverName()).isEqualTo(A_HOMESERVER_URL)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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.fixtures.fakes
|
||||
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
import org.matrix.rustcomponents.sdk.QrCodeData
|
||||
|
||||
class FakeQrCodeData(
|
||||
private val serverNameResult: () -> String? = { lambdaError() },
|
||||
) : QrCodeData(NoPointer) {
|
||||
override fun serverName(): String? {
|
||||
return serverNameResult()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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.room
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustRoom
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustRoomListService
|
||||
import io.element.android.libraries.matrix.test.A_DEVICE_ID
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class RustBaseRoomTest {
|
||||
@Test
|
||||
fun `RustBaseRoom should cancel the room coroutine scope when it is destroyed`() = runTest {
|
||||
val rustBaseRoom = createRustBaseRoom(
|
||||
// Not using backgroundScope here, but the test scope
|
||||
sessionCoroutineScope = this
|
||||
)
|
||||
assertThat(rustBaseRoom.roomCoroutineScope.isActive).isTrue()
|
||||
rustBaseRoom.destroy()
|
||||
assertThat(rustBaseRoom.roomCoroutineScope.isActive).isFalse()
|
||||
}
|
||||
|
||||
private fun TestScope.createRustBaseRoom(
|
||||
sessionCoroutineScope: CoroutineScope,
|
||||
): RustBaseRoom {
|
||||
val dispatchers = testCoroutineDispatchers()
|
||||
return RustBaseRoom(
|
||||
sessionId = A_SESSION_ID,
|
||||
deviceId = A_DEVICE_ID,
|
||||
innerRoom = FakeRustRoom(),
|
||||
coroutineDispatchers = dispatchers,
|
||||
roomSyncSubscriber = RoomSyncSubscriber(
|
||||
roomListService = FakeRustRoomListService(),
|
||||
dispatchers = dispatchers,
|
||||
),
|
||||
roomMembershipObserver = RoomMembershipObserver(),
|
||||
sessionCoroutineScope = sessionCoroutineScope,
|
||||
roomInfoMapper = RoomInfoMapper(),
|
||||
initialRoomInfo = aRoomInfo(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -88,6 +88,7 @@ class FakeMatrixClient(
|
|||
private val availableSlidingSyncVersionsLambda: () -> Result<List<SlidingSyncVersion>> = { lambdaError() },
|
||||
private val ignoreUserResult: (UserId) -> Result<Unit> = { lambdaError() },
|
||||
private var unIgnoreUserResult: (UserId) -> Result<Unit> = { Result.success(Unit) },
|
||||
private val canReportRoomLambda: () -> Boolean = { false },
|
||||
override val ignoredUsersFlow: StateFlow<ImmutableList<UserId>> = MutableStateFlow(persistentListOf()),
|
||||
) : MatrixClient {
|
||||
var setDisplayNameCalled: Boolean = false
|
||||
|
|
@ -329,4 +330,8 @@ class FakeMatrixClient(
|
|||
override suspend fun availableSlidingSyncVersions(): Result<List<SlidingSyncVersion>> {
|
||||
return availableSlidingSyncVersionsLambda()
|
||||
}
|
||||
|
||||
override suspend fun canReportRoom(): Boolean {
|
||||
return canReportRoomLambda()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import io.element.android.libraries.matrix.api.core.TransactionId
|
|||
import io.element.android.libraries.matrix.api.core.UniqueId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationSettings
|
||||
|
||||
const val A_USER_NAME = "alice"
|
||||
const val A_USER_NAME_2 = "Bob"
|
||||
|
|
@ -73,7 +72,6 @@ const val A_HOMESERVER_URL_2 = "matrix-client.org"
|
|||
val A_HOMESERVER = MatrixHomeServerDetails(A_HOMESERVER_URL, supportsPasswordLogin = true, supportsOidcLogin = false)
|
||||
val A_HOMESERVER_OIDC = MatrixHomeServerDetails(A_HOMESERVER_URL, supportsPasswordLogin = false, supportsOidcLogin = true)
|
||||
val A_ROOM_NOTIFICATION_MODE = RoomNotificationMode.MUTE
|
||||
val A_ROOM_NOTIFICATION_SETTINGS = RoomNotificationSettings(mode = A_ROOM_NOTIFICATION_MODE, isDefault = false)
|
||||
|
||||
const val AN_AVATAR_URL = "mxc://data"
|
||||
|
||||
|
|
@ -88,3 +86,5 @@ val A_SERVER_LIST = listOf("server1", "server2")
|
|||
|
||||
const val A_TIMESTAMP = 567L
|
||||
const val A_FORMATTED_DATE = "April 6, 1980 at 6:35 PM"
|
||||
|
||||
const val A_LOGIN_HINT = "mxid:@alice:example.org"
|
||||
|
|
|
|||
|
|
@ -87,7 +87,10 @@ class FakeMatrixAuthenticationService(
|
|||
return importCreatedSessionLambda(externalSession)
|
||||
}
|
||||
|
||||
override suspend fun getOidcUrl(prompt: OidcPrompt): Result<OidcDetails> = simulateLongTask {
|
||||
override suspend fun getOidcUrl(
|
||||
prompt: OidcPrompt,
|
||||
loginHint: String?,
|
||||
): Result<OidcDetails> = simulateLongTask {
|
||||
oidcError?.let { Result.failure(it) } ?: Result.success(A_OIDC_DATA)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ package io.element.android.libraries.matrix.test.auth.qrlogin
|
|||
|
||||
import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData
|
||||
import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginDataFactory
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
|
||||
class FakeMatrixQrCodeLoginDataFactory(
|
||||
|
|
@ -20,4 +21,8 @@ class FakeMatrixQrCodeLoginDataFactory(
|
|||
}
|
||||
}
|
||||
|
||||
class FakeMatrixQrCodeLoginData : MatrixQrCodeLoginData
|
||||
class FakeMatrixQrCodeLoginData(
|
||||
private val serverNameResult: () -> String? = { lambdaError() },
|
||||
) : MatrixQrCodeLoginData {
|
||||
override fun serverName() = serverNameResult()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,17 +12,9 @@ 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.ProgressCallback
|
||||
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.SendHandle
|
||||
import io.element.android.libraries.matrix.api.core.TransactionId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.encryption.identity.IdentityStateChange
|
||||
import io.element.android.libraries.matrix.api.media.AudioInfo
|
||||
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.poll.PollKind
|
||||
import io.element.android.libraries.matrix.api.room.BaseRoom
|
||||
import io.element.android.libraries.matrix.api.room.CreateTimelineParams
|
||||
import io.element.android.libraries.matrix.api.room.IntentionalMention
|
||||
|
|
@ -33,16 +25,12 @@ import io.element.android.libraries.matrix.api.room.RoomNotificationSettingsStat
|
|||
import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.room.knock.KnockRequest
|
||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||
import io.element.android.libraries.matrix.api.room.message.ReplyParameters
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevels
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
|
||||
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
|
||||
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
|
||||
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
|
@ -53,7 +41,6 @@ import kotlinx.coroutines.flow.Flow
|
|||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import java.io.File
|
||||
|
||||
class FakeJoinedRoom(
|
||||
val baseRoom: FakeBaseRoom = FakeBaseRoom(),
|
||||
|
|
@ -63,35 +50,16 @@ class FakeJoinedRoom(
|
|||
override val roomTypingMembersFlow: Flow<List<UserId>> = MutableStateFlow(emptyList()),
|
||||
override val identityStateChangesFlow: Flow<List<IdentityStateChange>> = MutableStateFlow(emptyList()),
|
||||
override val roomNotificationSettingsStateFlow: StateFlow<RoomNotificationSettingsState> =
|
||||
MutableStateFlow(RoomNotificationSettingsState.Unknown),
|
||||
MutableStateFlow(RoomNotificationSettingsState.Unknown),
|
||||
override val knockRequestsFlow: Flow<List<KnockRequest>> = MutableStateFlow(emptyList()),
|
||||
private val roomNotificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(),
|
||||
private var createTimelineResult: (CreateTimelineParams) -> Result<Timeline> = { lambdaError() },
|
||||
private val sendMessageResult: (String, String?, List<IntentionalMention>) -> Result<Unit> = { _, _, _ -> lambdaError() },
|
||||
private val editMessageLambda: (EventId, String, String?, List<IntentionalMention>) -> Result<Unit> = { _, _, _, _ -> lambdaError() },
|
||||
private val sendImageResult: (File, File?, ImageInfo, String?, String?, ProgressCallback?, ReplyParameters?) -> Result<FakeMediaUploadHandler> =
|
||||
{ _, _, _, _, _, _, _ -> lambdaError() },
|
||||
private val sendVideoResult: (File, File?, VideoInfo, String?, String?, ProgressCallback?, ReplyParameters?) -> Result<FakeMediaUploadHandler> =
|
||||
{ _, _, _, _, _, _, _ -> lambdaError() },
|
||||
private val sendFileResult: (File, FileInfo, String?, String?, ProgressCallback?, ReplyParameters?) -> Result<FakeMediaUploadHandler> =
|
||||
{ _, _, _, _, _, _ -> lambdaError() },
|
||||
private val sendAudioResult: (File, AudioInfo, String?, String?, ProgressCallback?, ReplyParameters?) -> Result<FakeMediaUploadHandler> =
|
||||
{ _, _, _, _, _, _ -> lambdaError() },
|
||||
private val sendVoiceMessageResult: (File, AudioInfo, List<Float>, ProgressCallback?, ReplyParameters?) -> Result<FakeMediaUploadHandler> =
|
||||
{ _, _, _, _, _ -> lambdaError() },
|
||||
private val sendLocationResult: (String, String, String?, Int?, AssetType?) -> Result<Unit> = { _, _, _, _, _ -> lambdaError() },
|
||||
private val sendCallNotificationIfNeededResult: () -> Result<Unit> = { lambdaError() },
|
||||
private val progressCallbackValues: List<Pair<Long, Long>> = emptyList(),
|
||||
private val createPollResult: (String, List<String>, Int, PollKind) -> Result<Unit> = { _, _, _, _ -> lambdaError() },
|
||||
private val editPollResult: (EventId, String, List<String>, Int, PollKind) -> Result<Unit> = { _, _, _, _, _ -> lambdaError() },
|
||||
private val sendPollResponseResult: (EventId, List<String>) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
private val endPollResult: (EventId, String) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
private val generateWidgetWebViewUrlResult: (MatrixWidgetSettings, String, String?, String?) -> Result<String> = { _, _, _, _ -> lambdaError() },
|
||||
private val getWidgetDriverResult: (MatrixWidgetSettings) -> Result<MatrixWidgetDriver> = { lambdaError() },
|
||||
private val typingNoticeResult: (Boolean) -> Result<Unit> = { lambdaError() },
|
||||
private val toggleReactionResult: (String, EventOrTransactionId) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
private val forwardEventResult: (EventId, List<RoomId>) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
private val cancelSendResult: (TransactionId) -> Result<Unit> = { lambdaError() },
|
||||
private val inviteUserResult: (UserId) -> Result<Unit> = { lambdaError() },
|
||||
private val setNameResult: (String) -> Result<Unit> = { lambdaError() },
|
||||
private val setTopicResult: (String) -> Result<Unit> = { lambdaError() },
|
||||
|
|
@ -127,10 +95,6 @@ class FakeJoinedRoom(
|
|||
createTimelineResult(createTimelineParams)
|
||||
}
|
||||
|
||||
override suspend fun sendMessage(body: String, htmlBody: String?, intentionalMentions: List<IntentionalMention>): Result<Unit> = simulateLongTask {
|
||||
sendMessageResult(body, htmlBody, intentionalMentions)
|
||||
}
|
||||
|
||||
override suspend fun editMessage(
|
||||
eventId: EventId,
|
||||
body: String,
|
||||
|
|
@ -140,174 +104,10 @@ class FakeJoinedRoom(
|
|||
editMessageLambda(eventId, body, htmlBody, intentionalMentions)
|
||||
}
|
||||
|
||||
override suspend fun sendImage(
|
||||
file: File,
|
||||
thumbnailFile: File?,
|
||||
imageInfo: ImageInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
simulateSendMediaProgress(progressCallback)
|
||||
sendImageResult(
|
||||
file,
|
||||
thumbnailFile,
|
||||
imageInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendVideo(
|
||||
file: File,
|
||||
thumbnailFile: File?,
|
||||
videoInfo: VideoInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
simulateSendMediaProgress(progressCallback)
|
||||
sendVideoResult(
|
||||
file,
|
||||
thumbnailFile,
|
||||
videoInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendAudio(
|
||||
file: File,
|
||||
audioInfo: AudioInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
simulateSendMediaProgress(progressCallback)
|
||||
sendAudioResult(
|
||||
file,
|
||||
audioInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendFile(
|
||||
file: File,
|
||||
fileInfo: FileInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
simulateSendMediaProgress(progressCallback)
|
||||
sendFileResult(
|
||||
file,
|
||||
fileInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendVoiceMessage(
|
||||
file: File,
|
||||
audioInfo: AudioInfo,
|
||||
waveform: List<Float>,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
simulateSendMediaProgress(progressCallback)
|
||||
sendVoiceMessageResult(
|
||||
file,
|
||||
audioInfo,
|
||||
waveform,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendLocation(
|
||||
body: String,
|
||||
geoUri: String,
|
||||
description: String?,
|
||||
zoomLevel: Int?,
|
||||
assetType: AssetType?,
|
||||
): Result<Unit> = simulateLongTask {
|
||||
return sendLocationResult(
|
||||
body,
|
||||
geoUri,
|
||||
description,
|
||||
zoomLevel,
|
||||
assetType,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun createPoll(question: String, answers: List<String>, maxSelections: Int, pollKind: PollKind): Result<Unit> = simulateLongTask {
|
||||
return createPollResult(
|
||||
question,
|
||||
answers,
|
||||
maxSelections,
|
||||
pollKind,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun editPoll(
|
||||
pollStartId: EventId,
|
||||
question: String,
|
||||
answers: List<String>,
|
||||
maxSelections: Int,
|
||||
pollKind: PollKind
|
||||
): Result<Unit> = simulateLongTask {
|
||||
return editPollResult(
|
||||
pollStartId,
|
||||
question,
|
||||
answers,
|
||||
maxSelections,
|
||||
pollKind,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendPollResponse(pollStartId: EventId, answers: List<String>): Result<Unit> = simulateLongTask {
|
||||
return sendPollResponseResult(
|
||||
pollStartId,
|
||||
answers,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun endPoll(pollStartId: EventId, text: String): Result<Unit> = simulateLongTask {
|
||||
endPollResult(
|
||||
pollStartId,
|
||||
text,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun typingNotice(isTyping: Boolean): Result<Unit> = simulateLongTask {
|
||||
typingNoticeResult(isTyping)
|
||||
}
|
||||
|
||||
override suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result<Unit> = simulateLongTask {
|
||||
toggleReactionResult(emoji, eventOrTransactionId)
|
||||
}
|
||||
|
||||
override suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit> = simulateLongTask {
|
||||
forwardEventResult(eventId, roomIds)
|
||||
}
|
||||
|
||||
override suspend fun cancelSend(transactionId: TransactionId): Result<Unit> = simulateLongTask {
|
||||
cancelSendResult(transactionId)
|
||||
}
|
||||
|
||||
override suspend fun inviteUserById(id: UserId): Result<Unit> = simulateLongTask {
|
||||
inviteUserResult(id)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ package io.element.android.libraries.matrix.test.timeline
|
|||
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.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.TransactionId
|
||||
import io.element.android.libraries.matrix.api.media.AudioInfo
|
||||
import io.element.android.libraries.matrix.api.media.FileInfo
|
||||
import io.element.android.libraries.matrix.api.media.ImageInfo
|
||||
|
|
@ -26,6 +27,8 @@ import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransa
|
|||
import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo
|
||||
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
|
@ -47,23 +50,31 @@ class FakeTimeline(
|
|||
)
|
||||
),
|
||||
override val membershipChangeEventReceived: Flow<Unit> = MutableSharedFlow(),
|
||||
private val progressCallbackValues: List<Pair<Long, Long>> = emptyList(),
|
||||
private val cancelSendResult: (TransactionId) -> Result<Unit> = { lambdaError() },
|
||||
) : Timeline {
|
||||
var sendMessageLambda: (
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
) -> Result<Unit> = { _, _, _ ->
|
||||
Result.success(Unit)
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun cancelSend(transactionId: TransactionId): Result<Unit> = simulateLongTask {
|
||||
cancelSendResult(transactionId)
|
||||
}
|
||||
|
||||
override suspend fun sendMessage(
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
): Result<Unit> = sendMessageLambda(body, htmlBody, intentionalMentions)
|
||||
): Result<Unit> = simulateLongTask {
|
||||
sendMessageLambda(body, htmlBody, intentionalMentions)
|
||||
}
|
||||
|
||||
var redactEventLambda: (eventOrTransactionId: EventOrTransactionId, reason: String?) -> Result<Unit> = { _, _ ->
|
||||
Result.success(Unit)
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun redactEvent(
|
||||
|
|
@ -77,7 +88,7 @@ class FakeTimeline(
|
|||
htmlBody: String?,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
) -> Result<Unit> = { _, _, _, _ ->
|
||||
Result.success(Unit)
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun editMessage(
|
||||
|
|
@ -117,7 +128,7 @@ class FakeTimeline(
|
|||
intentionalMentions: List<IntentionalMention>,
|
||||
fromNotification: Boolean,
|
||||
) -> Result<Unit> = { _, _, _, _, _ ->
|
||||
Result.success(Unit)
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun replyMessage(
|
||||
|
|
@ -154,15 +165,18 @@ class FakeTimeline(
|
|||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> = sendImageLambda(
|
||||
file,
|
||||
thumbnailFile,
|
||||
imageInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
simulateSendMediaProgress(progressCallback)
|
||||
sendImageLambda(
|
||||
file,
|
||||
thumbnailFile,
|
||||
imageInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
var sendVideoLambda: (
|
||||
file: File,
|
||||
|
|
@ -184,15 +198,18 @@ class FakeTimeline(
|
|||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> = sendVideoLambda(
|
||||
file,
|
||||
thumbnailFile,
|
||||
videoInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
simulateSendMediaProgress(progressCallback)
|
||||
sendVideoLambda(
|
||||
file,
|
||||
thumbnailFile,
|
||||
videoInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
var sendAudioLambda: (
|
||||
file: File,
|
||||
|
|
@ -212,14 +229,17 @@ class FakeTimeline(
|
|||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> = sendAudioLambda(
|
||||
file,
|
||||
audioInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
simulateSendMediaProgress(progressCallback)
|
||||
sendAudioLambda(
|
||||
file,
|
||||
audioInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
var sendFileLambda: (
|
||||
file: File,
|
||||
|
|
@ -239,14 +259,17 @@ class FakeTimeline(
|
|||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> = sendFileLambda(
|
||||
file,
|
||||
fileInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
simulateSendMediaProgress(progressCallback)
|
||||
sendFileLambda(
|
||||
file,
|
||||
fileInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
var sendVoiceMessageLambda: (
|
||||
file: File,
|
||||
|
|
@ -264,13 +287,16 @@ class FakeTimeline(
|
|||
waveform: List<Float>,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> = sendVoiceMessageLambda(
|
||||
file,
|
||||
audioInfo,
|
||||
waveform,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
simulateSendMediaProgress(progressCallback)
|
||||
sendVoiceMessageLambda(
|
||||
file,
|
||||
audioInfo,
|
||||
waveform,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
var sendLocationLambda: (
|
||||
body: String,
|
||||
|
|
@ -279,7 +305,7 @@ class FakeTimeline(
|
|||
zoomLevel: Int?,
|
||||
assetType: AssetType?,
|
||||
) -> Result<Unit> = { _, _, _, _, _ ->
|
||||
Result.success(Unit)
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun sendLocation(
|
||||
|
|
@ -288,24 +314,30 @@ class FakeTimeline(
|
|||
description: String?,
|
||||
zoomLevel: Int?,
|
||||
assetType: AssetType?,
|
||||
): Result<Unit> = sendLocationLambda(
|
||||
body,
|
||||
geoUri,
|
||||
description,
|
||||
zoomLevel,
|
||||
assetType
|
||||
)
|
||||
): Result<Unit> = simulateLongTask {
|
||||
sendLocationLambda(
|
||||
body,
|
||||
geoUri,
|
||||
description,
|
||||
zoomLevel,
|
||||
assetType,
|
||||
)
|
||||
}
|
||||
|
||||
var toggleReactionLambda: (emoji: String, eventOrTransactionId: EventOrTransactionId) -> Result<Unit> = { _, _ -> Result.success(Unit) }
|
||||
var toggleReactionLambda: (emoji: String, eventOrTransactionId: EventOrTransactionId) -> Result<Unit> = { _, _ -> lambdaError() }
|
||||
|
||||
override suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result<Unit> = toggleReactionLambda(
|
||||
emoji,
|
||||
eventOrTransactionId
|
||||
)
|
||||
override suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result<Unit> = simulateLongTask {
|
||||
toggleReactionLambda(
|
||||
emoji,
|
||||
eventOrTransactionId,
|
||||
)
|
||||
}
|
||||
|
||||
var forwardEventLambda: (eventId: EventId, roomIds: List<RoomId>) -> Result<Unit> = { _, _ -> Result.success(Unit) }
|
||||
var forwardEventLambda: (eventId: EventId, roomIds: List<RoomId>) -> Result<Unit> = { _, _ -> lambdaError() }
|
||||
|
||||
override suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit> = forwardEventLambda(eventId, roomIds)
|
||||
override suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit> = simulateLongTask {
|
||||
forwardEventLambda(eventId, roomIds)
|
||||
}
|
||||
|
||||
var createPollLambda: (
|
||||
question: String,
|
||||
|
|
@ -313,20 +345,17 @@ class FakeTimeline(
|
|||
maxSelections: Int,
|
||||
pollKind: PollKind,
|
||||
) -> Result<Unit> = { _, _, _, _ ->
|
||||
Result.success(Unit)
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun createPoll(
|
||||
question: String,
|
||||
answers: List<String>,
|
||||
maxSelections: Int,
|
||||
pollKind: PollKind,
|
||||
): Result<Unit> = createPollLambda(
|
||||
question,
|
||||
answers,
|
||||
maxSelections,
|
||||
pollKind
|
||||
)
|
||||
override suspend fun createPoll(question: String, answers: List<String>, maxSelections: Int, pollKind: PollKind): Result<Unit> = simulateLongTask {
|
||||
createPollLambda(
|
||||
question,
|
||||
answers,
|
||||
maxSelections,
|
||||
pollKind,
|
||||
)
|
||||
}
|
||||
|
||||
var editPollLambda: (
|
||||
pollStartId: EventId,
|
||||
|
|
@ -335,7 +364,7 @@ class FakeTimeline(
|
|||
maxSelections: Int,
|
||||
pollKind: PollKind,
|
||||
) -> Result<Unit> = { _, _, _, _, _ ->
|
||||
Result.success(Unit)
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun editPoll(
|
||||
|
|
@ -343,44 +372,56 @@ class FakeTimeline(
|
|||
question: String,
|
||||
answers: List<String>,
|
||||
maxSelections: Int,
|
||||
pollKind: PollKind,
|
||||
): Result<Unit> = editPollLambda(
|
||||
pollStartId,
|
||||
question,
|
||||
answers,
|
||||
maxSelections,
|
||||
pollKind
|
||||
)
|
||||
pollKind: PollKind
|
||||
): Result<Unit> = simulateLongTask {
|
||||
editPollLambda(
|
||||
pollStartId,
|
||||
question,
|
||||
answers,
|
||||
maxSelections,
|
||||
pollKind,
|
||||
)
|
||||
}
|
||||
|
||||
var sendPollResponseLambda: (
|
||||
pollStartId: EventId,
|
||||
answers: List<String>,
|
||||
) -> Result<Unit> = { _, _ ->
|
||||
Result.success(Unit)
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun sendPollResponse(
|
||||
pollStartId: EventId,
|
||||
answers: List<String>,
|
||||
): Result<Unit> = sendPollResponseLambda(pollStartId, answers)
|
||||
): Result<Unit> = simulateLongTask {
|
||||
sendPollResponseLambda(
|
||||
pollStartId,
|
||||
answers,
|
||||
)
|
||||
}
|
||||
|
||||
var endPollLambda: (
|
||||
pollStartId: EventId,
|
||||
text: String,
|
||||
) -> Result<Unit> = { _, _ ->
|
||||
Result.success(Unit)
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun endPoll(
|
||||
pollStartId: EventId,
|
||||
text: String,
|
||||
): Result<Unit> = endPollLambda(pollStartId, text)
|
||||
): Result<Unit> = simulateLongTask {
|
||||
endPollLambda(
|
||||
pollStartId,
|
||||
text,
|
||||
)
|
||||
}
|
||||
|
||||
var sendReadReceiptLambda: (
|
||||
eventId: EventId,
|
||||
receiptType: ReceiptType,
|
||||
) -> Result<Unit> = { _, _ ->
|
||||
Result.success(Unit)
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun sendReadReceipt(
|
||||
|
|
@ -417,5 +458,12 @@ class FakeTimeline(
|
|||
closeCounter++
|
||||
}
|
||||
|
||||
private suspend fun simulateSendMediaProgress(progressCallback: ProgressCallback?) {
|
||||
progressCallbackValues.forEach { (current, total) ->
|
||||
progressCallback?.onProgress(current, total)
|
||||
delay(1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString() = "FakeTimeline: $name"
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue