Merge branch 'develop' into feature/fga/user_detail_direct_chat

This commit is contained in:
ganfra 2023-11-30 18:10:51 +01:00
commit 03a2e31d46
76 changed files with 329 additions and 148 deletions

View file

@ -16,6 +16,9 @@
package io.element.android.libraries.matrix.api.encryption
import androidx.compose.runtime.Immutable
@Immutable
sealed interface BackupUploadState {
data object Unknown : BackupUploadState

View file

@ -16,6 +16,9 @@
package io.element.android.libraries.matrix.api.encryption
import androidx.compose.runtime.Immutable
@Immutable
sealed interface SteadyStateException {
/**
* The backup can be deleted.

View file

@ -16,9 +16,10 @@
package io.element.android.libraries.matrix.api.media
import kotlinx.collections.immutable.ImmutableList
import kotlin.time.Duration
data class AudioDetails(
val duration: Duration,
val waveform: List<Float>,
val waveform: ImmutableList<Float>,
)

View file

@ -17,18 +17,21 @@
package io.element.android.libraries.matrix.api.permalink
import android.net.Uri
import androidx.compose.runtime.Immutable
import kotlinx.collections.immutable.ImmutableList
/**
* This sealed class represents all the permalink cases.
* You don't have to instantiate yourself but should use [PermalinkParser] instead.
*/
@Immutable
sealed interface PermalinkData {
data class RoomLink(
val roomIdOrAlias: String,
val isRoomAlias: Boolean,
val eventId: String?,
val viaParameters: List<String>
val viaParameters: ImmutableList<String>
) : PermalinkData
/*

View file

@ -19,6 +19,7 @@ package io.element.android.libraries.matrix.api.permalink
import android.net.Uri
import android.net.UrlQuerySanitizer
import io.element.android.libraries.matrix.api.core.MatrixPatterns
import kotlinx.collections.immutable.toImmutableList
import timber.log.Timber
import java.net.URLDecoder
@ -80,7 +81,7 @@ object PermalinkParser {
roomIdOrAlias = decodedIdentifier,
isRoomAlias = true,
eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) },
viaParameters = viaQueryParameters
viaParameters = viaQueryParameters.toImmutableList()
)
}
else -> PermalinkData.FallbackLink(uri, MatrixPatterns.isGroupId(identifier))
@ -119,7 +120,7 @@ object PermalinkParser {
roomIdOrAlias = identifier,
isRoomAlias = false,
eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) },
viaParameters = viaQueryParameters
viaParameters = viaQueryParameters.toImmutableList()
)
}
}

View file

@ -132,6 +132,8 @@ interface MatrixRoom : Closeable {
suspend fun canUserTriggerRoomNotification(userId: UserId): Result<Boolean>
suspend fun canUserJoinCall(userId: UserId): Result<Boolean>
suspend fun updateAvatar(mimeType: String, data: ByteArray): Result<Unit>
suspend fun removeAvatar(): Result<Unit>

View file

@ -16,8 +16,11 @@
package io.element.android.libraries.matrix.api.room
import androidx.compose.runtime.Immutable
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
import kotlinx.collections.immutable.ImmutableList
@Immutable
data class MatrixRoomInfo(
val id: String,
val name: String?,
@ -28,7 +31,7 @@ data class MatrixRoomInfo(
val isSpace: Boolean,
val isTombstoned: Boolean,
val canonicalAlias: String?,
val alternativeAliases: List<String>,
val alternativeAliases: ImmutableList<String>,
val currentUserMembership: CurrentUserMembership,
val latestEvent: EventTimelineItem?,
val inviter: RoomMember?,
@ -39,5 +42,5 @@ data class MatrixRoomInfo(
val notificationCount: Long,
val userDefinedNotificationMode: RoomNotificationMode?,
val hasRoomCall: Boolean,
val activeRoomCallParticipants: List<String>
val activeRoomCallParticipants: ImmutableList<String>
)

View file

@ -17,13 +17,14 @@
package io.element.android.libraries.matrix.api.room
import androidx.compose.runtime.Immutable
import kotlinx.collections.immutable.ImmutableList
@Immutable
sealed interface MatrixRoomMembersState {
data object Unknown : MatrixRoomMembersState
data class Pending(val prevRoomMembers: List<RoomMember>? = null) : MatrixRoomMembersState
data class Error(val failure: Throwable, val prevRoomMembers: List<RoomMember>? = null) : MatrixRoomMembersState
data class Ready(val roomMembers: List<RoomMember>) : MatrixRoomMembersState
data class Pending(val prevRoomMembers: ImmutableList<RoomMember>? = null) : MatrixRoomMembersState
data class Error(val failure: Throwable, val prevRoomMembers: ImmutableList<RoomMember>? = null) : MatrixRoomMembersState
data class Ready(val roomMembers: ImmutableList<RoomMember>) : MatrixRoomMembersState
}
fun MatrixRoomMembersState.roomMembers(): List<RoomMember>? {

View file

@ -16,6 +16,7 @@
package io.element.android.libraries.matrix.api.roomlist
import androidx.compose.runtime.Immutable
import kotlinx.coroutines.flow.StateFlow
/**
@ -25,6 +26,7 @@ import kotlinx.coroutines.flow.StateFlow
*/
interface RoomListService {
@Immutable
sealed interface State {
data object Idle : State
data object Running : State
@ -32,6 +34,7 @@ interface RoomListService {
data object Terminated : State
}
@Immutable
sealed interface SyncIndicator {
data object Show : SyncIndicator
data object Hide : SyncIndicator

View file

@ -16,11 +16,15 @@
package io.element.android.libraries.matrix.api.timeline.item.event
import androidx.compose.runtime.Immutable
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.media.ImageInfo
import io.element.android.libraries.matrix.api.poll.PollAnswer
import io.element.android.libraries.matrix.api.poll.PollKind
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.ImmutableMap
@Immutable
sealed interface EventContent
data class MessageContent(
@ -43,14 +47,16 @@ data class PollContent(
val question: String,
val kind: PollKind,
val maxSelections: ULong,
val answers: List<PollAnswer>,
val votes: Map<String, List<UserId>>,
val answers: ImmutableList<PollAnswer>,
val votes: ImmutableMap<String, ImmutableList<UserId>>,
val endTime: ULong?
) : EventContent
data class UnableToDecryptContent(
val data: Data
) : EventContent {
@Immutable
sealed interface Data {
data class OlmV1Curve25519AesSha2(
val senderKey: String

View file

@ -16,7 +16,11 @@
package io.element.android.libraries.matrix.api.timeline.item.event
import androidx.compose.runtime.Immutable
import kotlinx.collections.immutable.ImmutableList
@Immutable
data class EventReaction(
val key: String,
val senders: List<ReactionSender>
val senders: ImmutableList<ReactionSender>
)

View file

@ -20,6 +20,7 @@ import io.element.android.libraries.matrix.api.core.EventId
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.timeline.item.TimelineItemDebugInfo
import kotlinx.collections.immutable.ImmutableList
data class EventTimelineItem(
val eventId: EventId?,
@ -29,8 +30,8 @@ data class EventTimelineItem(
val isOwn: Boolean,
val isRemote: Boolean,
val localSendState: LocalEventSendState?,
val reactions: List<EventReaction>,
val receipts: List<Receipt>,
val reactions: ImmutableList<EventReaction>,
val receipts: ImmutableList<Receipt>,
val sender: UserId,
val senderProfile: ProfileTimelineDetails,
val timestamp: Long,

View file

@ -16,9 +16,11 @@
package io.element.android.libraries.matrix.api.timeline.item.event
import androidx.compose.runtime.Immutable
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.UserId
@Immutable
sealed interface InReplyTo {
/** The event details are not loaded yet. We can fetch them. */
data class NotLoaded(val eventId: EventId) : InReplyTo

View file

@ -16,8 +16,10 @@
package io.element.android.libraries.matrix.api.timeline.item.event
import androidx.compose.runtime.Immutable
import io.element.android.libraries.matrix.api.core.EventId
@Immutable
sealed interface LocalEventSendState {
data object NotSentYet : LocalEventSendState
data object Canceled : LocalEventSendState

View file

@ -16,6 +16,7 @@
package io.element.android.libraries.matrix.api.timeline.item.event
import androidx.compose.runtime.Immutable
import io.element.android.libraries.matrix.api.media.AudioDetails
import io.element.android.libraries.matrix.api.media.AudioInfo
import io.element.android.libraries.matrix.api.media.FileInfo
@ -23,6 +24,7 @@ import io.element.android.libraries.matrix.api.media.ImageInfo
import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.api.media.VideoInfo
@Immutable
sealed interface MessageType
data class EmoteMessageType(

View file

@ -16,6 +16,9 @@
package io.element.android.libraries.matrix.api.timeline.item.event
import androidx.compose.runtime.Immutable
@Immutable
sealed interface OtherState {
data object PolicyRuleRoom : OtherState
data object PolicyRuleServer : OtherState

View file

@ -16,6 +16,9 @@
package io.element.android.libraries.matrix.api.timeline.item.event
import androidx.compose.runtime.Immutable
@Immutable
sealed interface ProfileTimelineDetails {
data object Unavailable : ProfileTimelineDetails

View file

@ -16,7 +16,9 @@
package io.element.android.libraries.matrix.api.user
import kotlinx.collections.immutable.ImmutableList
data class MatrixSearchUserResults(
val results: List<MatrixUser>,
val results: ImmutableList<MatrixUser>,
val limited: Boolean,
)

View file

@ -16,6 +16,8 @@
package io.element.android.libraries.matrix.api.verification
import androidx.compose.runtime.Immutable
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@ -75,6 +77,7 @@ interface SessionVerificationService {
}
/** Verification status of the current session. */
@Immutable
sealed interface SessionVerifiedStatus {
/** Unknown status, we couldn't read the actual value from the SDK. */
data object Unknown : SessionVerifiedStatus
@ -87,6 +90,7 @@ sealed interface SessionVerifiedStatus {
}
/** States produced by the [SessionVerificationService]. */
@Immutable
sealed interface VerificationFlowState {
/** Initial state. */
data object Initial : VerificationFlowState
@ -98,7 +102,7 @@ sealed interface VerificationFlowState {
data object StartedSasVerification : VerificationFlowState
/** Verification data for the SAS verification (emojis) received. */
data class ReceivedVerificationData(val emoji: List<VerificationEmoji>) : VerificationFlowState
data class ReceivedVerificationData(val emoji: ImmutableList<VerificationEmoji>) : VerificationFlowState
/** Verification completed successfully. */
data object Finished : VerificationFlowState

View file

@ -17,6 +17,7 @@
package io.element.android.libraries.matrix.api.permalink
import com.google.common.truth.Truth.assertThat
import kotlinx.collections.immutable.persistentListOf
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
@ -66,7 +67,7 @@ class PermalinkParserTest {
roomIdOrAlias = "!aBCD1234:matrix.org",
isRoomAlias = false,
eventId = null,
viaParameters = emptyList(),
viaParameters = persistentListOf(),
)
)
}
@ -79,7 +80,7 @@ class PermalinkParserTest {
roomIdOrAlias = "!aBCD1234:matrix.org",
isRoomAlias = false,
eventId = "\$1234567890abcdef:matrix.org",
viaParameters = emptyList(),
viaParameters = persistentListOf(),
)
)
}
@ -92,7 +93,7 @@ class PermalinkParserTest {
roomIdOrAlias = "!aBCD1234:matrix.org",
isRoomAlias = false,
eventId = null,
viaParameters = emptyList(),
viaParameters = persistentListOf(),
)
)
}
@ -105,7 +106,7 @@ class PermalinkParserTest {
roomIdOrAlias = "!aBCD1234:matrix.org",
isRoomAlias = false,
eventId = "\$1234567890abcdef:matrix.org",
viaParameters = listOf("matrix.org", "matrix.com"),
viaParameters = persistentListOf("matrix.org", "matrix.com"),
)
)
}
@ -118,7 +119,7 @@ class PermalinkParserTest {
roomIdOrAlias = "#element-android:matrix.org",
isRoomAlias = true,
eventId = null,
viaParameters = emptyList(),
viaParameters = persistentListOf(),
)
)
}

View file

@ -47,6 +47,7 @@ dependencies {
implementation("net.java.dev.jna:jna:5.13.0@aar")
implementation(libs.androidx.datastore.preferences)
implementation(libs.serialization.json)
implementation(libs.kotlinx.collections.immutable)
testImplementation(libs.test.junit)
testImplementation(libs.test.truth)

View file

@ -77,6 +77,7 @@ import org.matrix.rustcomponents.sdk.BackupState
import org.matrix.rustcomponents.sdk.Client
import org.matrix.rustcomponents.sdk.ClientDelegate
import org.matrix.rustcomponents.sdk.NotificationProcessSetup
import org.matrix.rustcomponents.sdk.PowerLevels
import org.matrix.rustcomponents.sdk.Room
import org.matrix.rustcomponents.sdk.RoomListItem
import org.matrix.rustcomponents.sdk.TaskHandle
@ -274,6 +275,7 @@ class RustMatrixClient constructor(
},
invite = createRoomParams.invite?.map { it.value },
avatar = createRoomParams.avatar,
powerLevelContentOverride = defaultRoomCreationPowerLevels,
)
val roomId = RoomId(client.createRoom(rustParams))
@ -296,7 +298,7 @@ class RustMatrixClient constructor(
isDirect = true,
visibility = RoomVisibility.PRIVATE,
preset = RoomPreset.TRUSTED_PRIVATE_CHAT,
invite = listOf(userId)
invite = listOf(userId),
)
return createRoom(createRoomParams)
}
@ -481,3 +483,18 @@ class RustMatrixClient constructor(
}
}
private val defaultRoomCreationPowerLevels = PowerLevels(
usersDefault = null,
eventsDefault = null,
stateDefault = null,
ban = null,
kick = null,
redact = null,
invite = null,
notifications = null,
users = mapOf(),
events = mapOf(
"m.call.member" to 0,
"org.matrix.msc3401.call.member" to 0,
)
)

View file

@ -17,13 +17,14 @@
package io.element.android.libraries.matrix.impl.media
import io.element.android.libraries.matrix.api.media.AudioDetails
import kotlinx.collections.immutable.toImmutableList
import kotlin.time.toJavaDuration
import kotlin.time.toKotlinDuration
import org.matrix.rustcomponents.sdk.UnstableAudioDetailsContent as RustAudioDetails
fun RustAudioDetails.map(): AudioDetails = AudioDetails(
duration = duration.toKotlinDuration(),
waveform = waveform.fromMSC3246range(),
waveform = waveform.fromMSC3246range().toImmutableList(),
)
fun AudioDetails.map(): RustAudioDetails = RustAudioDetails(

View file

@ -20,6 +20,7 @@ import io.element.android.libraries.matrix.api.room.CurrentUserMembership
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelineItemMapper
import kotlinx.collections.immutable.toImmutableList
import org.matrix.rustcomponents.sdk.use
import org.matrix.rustcomponents.sdk.Membership as RustMembership
import org.matrix.rustcomponents.sdk.RoomInfo as RustRoomInfo
@ -40,7 +41,7 @@ class MatrixRoomInfoMapper(
isSpace = it.isSpace,
isTombstoned = it.isTombstoned,
canonicalAlias = it.canonicalAlias,
alternativeAliases = it.alternativeAliases,
alternativeAliases = it.alternativeAliases.toImmutableList(),
currentUserMembership = it.membership.map(),
latestEvent = it.latestEvent?.use (timelineItemMapper::map),
inviter = it.inviter?.use(RoomMemberMapper::map),
@ -51,7 +52,7 @@ class MatrixRoomInfoMapper(
notificationCount = it.notificationCount.toLong(),
userDefinedNotificationMode = it.userDefinedNotificationMode?.map(),
hasRoomCall = it.hasRoomCall,
activeRoomCallParticipants = it.activeRoomCallParticipants
activeRoomCallParticipants = it.activeRoomCallParticipants.toImmutableList()
)
}
}

View file

@ -57,6 +57,7 @@ import io.element.android.libraries.matrix.impl.widget.RustWidgetDriver
import io.element.android.libraries.matrix.impl.widget.generateWidgetWebViewUrl
import io.element.android.libraries.sessionstorage.api.SessionData
import io.element.android.services.toolbox.api.systemclock.SystemClock
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@ -198,7 +199,7 @@ class RustMatrixRoom(
override suspend fun updateMembers(): Result<Unit> = withContext(roomMembersDispatcher) {
val currentState = _membersStateFlow.value
val currentMembers = currentState.roomMembers()
val currentMembers = currentState.roomMembers()?.toImmutableList()
_membersStateFlow.value = MatrixRoomMembersState.Pending(prevRoomMembers = currentMembers)
var rustMembers: List<RoomMember>? = null
try {
@ -213,7 +214,7 @@ class RustMatrixRoom(
}
}
val mappedMembers = rustMembers.parallelMap(RoomMemberMapper::map)
_membersStateFlow.value = MatrixRoomMembersState.Ready(mappedMembers)
_membersStateFlow.value = MatrixRoomMembersState.Ready(mappedMembers.toImmutableList())
Result.success(Unit)
} catch (exception: CancellationException) {
_membersStateFlow.value = MatrixRoomMembersState.Error(prevRoomMembers = currentMembers, failure = exception)
@ -363,6 +364,12 @@ class RustMatrixRoom(
}
}
override suspend fun canUserJoinCall(userId: UserId): Result<Boolean> {
return runCatching {
innerRoom.canUserSendState(userId.value, StateEventType.ROOM_MEMBER_EVENT.map())
}
}
override suspend fun sendImage(file: File, thumbnailFile: File, imageInfo: ImageInfo, progressCallback: ProgressCallback?): Result<MediaUploadHandler> {
return sendAttachment(listOf(file, thumbnailFile)) {
innerTimeline.sendImage(file.path, thumbnailFile.path, imageInfo.map(), progressCallback?.toProgressWatcher())

View file

@ -27,6 +27,9 @@ import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimeli
import io.element.android.libraries.matrix.api.timeline.item.event.ReactionSender
import io.element.android.libraries.matrix.api.timeline.item.event.Receipt
import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import org.matrix.rustcomponents.sdk.Reaction
import org.matrix.rustcomponents.sdk.EventItemOrigin as RustEventItemOrigin
import org.matrix.rustcomponents.sdk.EventSendState as RustEventSendState
@ -81,7 +84,7 @@ fun RustEventSendState?.map(): LocalEventSendState? {
}
}
private fun List<Reaction>?.map(): List<EventReaction> {
private fun List<Reaction>?.map(): ImmutableList<EventReaction> {
return this?.map {
EventReaction(
key = it.key,
@ -90,18 +93,20 @@ private fun List<Reaction>?.map(): List<EventReaction> {
senderId = UserId(sender.senderId),
timestamp = sender.timestamp.toLong()
)
}
}.toImmutableList()
)
} ?: emptyList()
}?.toImmutableList() ?: persistentListOf()
}
private fun Map<String, RustReceipt>.map(): List<Receipt> {
private fun Map<String, RustReceipt>.map(): ImmutableList<Receipt> {
return map {
Receipt(
userId = UserId(it.key),
timestamp = it.value.timestamp?.toLong() ?: 0
)
}.sortedByDescending { it.timestamp }
Receipt(
userId = UserId(it.key),
timestamp = it.value.timestamp?.toLong() ?: 0
)
}
.sortedByDescending { it.timestamp }
.toImmutableList()
}
private fun RustEventTimelineItemDebugInfo.map(): TimelineItemDebugInfo {

View file

@ -32,6 +32,8 @@ import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecry
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent
import io.element.android.libraries.matrix.impl.media.map
import io.element.android.libraries.matrix.impl.poll.map
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toImmutableMap
import org.matrix.rustcomponents.sdk.TimelineItemContent
import org.matrix.rustcomponents.sdk.TimelineItemContentKind
import org.matrix.rustcomponents.sdk.use
@ -106,10 +108,10 @@ class TimelineEventContentMapper(private val eventMessageMapper: EventMessageMap
question = kind.question,
kind = kind.kind.map(),
maxSelections = kind.maxSelections,
answers = kind.answers.map { answer -> answer.map() },
answers = kind.answers.map { answer -> answer.map() }.toImmutableList(),
votes = kind.votes.mapValues { vote ->
vote.value.map { userId -> UserId(userId) }
},
vote.value.map { userId -> UserId(userId) }.toImmutableList()
}.toImmutableMap(),
endTime = kind.endTime,
)
}

View file

@ -17,13 +17,14 @@
package io.element.android.libraries.matrix.impl.usersearch
import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults
import kotlinx.collections.immutable.toImmutableList
import org.matrix.rustcomponents.sdk.SearchUsersResults
object UserSearchResultMapper {
fun map(result: SearchUsersResults): MatrixSearchUserResults {
return MatrixSearchUserResults(
results = result.results.map(UserProfileMapper::map),
results = result.results.map(UserProfileMapper::map).toImmutableList(),
limited = result.limited,
)
}

View file

@ -23,6 +23,7 @@ import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatu
import io.element.android.libraries.matrix.api.verification.VerificationEmoji
import io.element.android.libraries.matrix.api.verification.VerificationFlowState
import io.element.android.libraries.matrix.impl.sync.RustSyncService
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -106,8 +107,9 @@ class RustSessionVerificationService(
override fun didReceiveVerificationData(data: List<SessionVerificationEmoji>) {
val emojis = data.map { emoji ->
emoji.use { VerificationEmoji(it.symbol(), it.description()) }
}
emoji.use { VerificationEmoji(it.symbol(), it.description()) }
}
.toImmutableList()
_verificationFlowState.value = VerificationFlowState.ReceivedVerificationData(emojis)
}

View file

@ -28,4 +28,5 @@ dependencies {
api(libs.coroutines.core)
implementation(libs.coroutines.test)
implementation(projects.tests.testutils)
implementation(libs.kotlinx.collections.immutable)
}

View file

@ -53,6 +53,7 @@ import io.element.android.libraries.matrix.test.notificationsettings.FakeNotific
import io.element.android.libraries.matrix.test.timeline.FakeMatrixTimeline
import io.element.android.libraries.matrix.test.widget.FakeWidgetDriver
import io.element.android.tests.testutils.simulateLongTask
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
@ -110,6 +111,7 @@ class FakeMatrixRoom(
private var generateWidgetWebViewUrlResult = Result.success("https://call.element.io")
private var getWidgetDriverResult: Result<MatrixWidgetDriver> = Result.success(FakeWidgetDriver())
private var canUserTriggerRoomNotificationResult: Result<Boolean> = Result.success(true)
private var canUserJoinCallResult: Result<Boolean> = Result.success(true)
var sendMessageMentions = emptyList<Mention>()
val editMessageCalls = mutableListOf<Pair<String, String?>>()
@ -291,6 +293,10 @@ class FakeMatrixRoom(
return canUserTriggerRoomNotificationResult
}
override suspend fun canUserJoinCall(userId: UserId): Result<Boolean> {
return canUserJoinCallResult
}
override suspend fun sendImage(
file: File,
thumbnailFile: File,
@ -473,6 +479,10 @@ class FakeMatrixRoom(
canUserTriggerRoomNotificationResult = result
}
fun givenCanUserJoinCall(result: Result<Boolean>) {
canUserJoinCallResult = result
}
fun givenIgnoreResult(result: Result<Unit>) {
ignoreResult = result
}
@ -615,7 +625,7 @@ fun aRoomInfo(
isSpace = isSpace,
isTombstoned = isTombstoned,
canonicalAlias = canonicalAlias,
alternativeAliases = alternativeAliases,
alternativeAliases = alternativeAliases.toImmutableList(),
currentUserMembership = currentUserMembership,
latestEvent = latestEvent,
inviter = inviter,
@ -626,5 +636,5 @@ fun aRoomInfo(
notificationCount = notificationCount,
userDefinedNotificationMode = userDefinedNotificationMode,
hasRoomCall = hasRoomCall,
activeRoomCallParticipants = activeRoomCallParticipants
activeRoomCallParticipants = activeRoomCallParticipants.toImmutableList(),
)

View file

@ -44,6 +44,9 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_ROOM_NAME
import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.A_USER_NAME
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentMapOf
fun aRoomSummaryFilled(
roomId: RoomId = A_ROOM_ID,
@ -107,8 +110,8 @@ fun anEventTimelineItem(
isOwn: Boolean = false,
isRemote: Boolean = false,
localSendState: LocalEventSendState? = null,
reactions: List<EventReaction> = emptyList(),
receipts: List<Receipt> = emptyList(),
reactions: ImmutableList<EventReaction> = persistentListOf(),
receipts: ImmutableList<Receipt> = persistentListOf(),
sender: UserId = A_USER_ID,
senderProfile: ProfileTimelineDetails = aProfileTimelineDetails(),
timestamp: Long = 0L,
@ -181,12 +184,12 @@ fun aTimelineItemDebugInfo(
fun aPollContent(
question: String = "Do you like polls?",
answers: List<PollAnswer> = listOf(PollAnswer("1", "Yes"), PollAnswer("2", "No")),
answers: ImmutableList<PollAnswer> = persistentListOf(PollAnswer("1", "Yes"), PollAnswer("2", "No")),
) = PollContent(
question = question,
kind = PollKind.Disclosed,
maxSelections = 1u,
answers = answers,
votes = mapOf(),
votes = persistentMapOf(),
endTime = null
)

View file

@ -20,6 +20,8 @@ import io.element.android.libraries.matrix.api.verification.SessionVerificationS
import io.element.android.libraries.matrix.api.verification.VerificationFlowState
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
import io.element.android.libraries.matrix.api.verification.VerificationEmoji
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -29,7 +31,7 @@ class FakeSessionVerificationService : SessionVerificationService {
private val _sessionVerifiedStatus = MutableStateFlow<SessionVerifiedStatus>(SessionVerifiedStatus.Unknown)
private var _verificationFlowState = MutableStateFlow<VerificationFlowState>(VerificationFlowState.Initial)
private var _canVerifySessionFlow = MutableStateFlow(true)
private var emojiList = emptyList<VerificationEmoji>()
private var emojiList = persistentListOf<VerificationEmoji>()
var shouldFail = false
override val verificationFlowState: StateFlow<VerificationFlowState> =_verificationFlowState
@ -87,7 +89,7 @@ class FakeSessionVerificationService : SessionVerificationService {
}
fun givenEmojiList(emojis: List<VerificationEmoji>) {
this.emojiList = emojis
this.emojiList = emojis.toPersistentList()
}
override suspend fun reset() {

View file

@ -19,6 +19,7 @@ package io.element.android.libraries.textcomposer
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.libraries.designsystem.theme.bgSubtleTertiary
import io.element.android.compound.theme.ElementTheme
import io.element.android.wysiwyg.compose.RichTextEditorDefaults
@ -39,7 +40,8 @@ internal object ElementRichTextEditorStyle {
m3colors.primary
} else {
m3colors.secondary
}
},
lineHeight = 16.25.sp,
),
cursor = RichTextEditorDefaults.cursorStyle(
color = colors.iconAccentTertiary,

View file

@ -44,7 +44,6 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
@ -85,7 +84,6 @@ import io.element.android.libraries.textcomposer.model.VoiceMessagePlayerEvent
import io.element.android.libraries.textcomposer.model.VoiceMessageRecorderEvent
import io.element.android.libraries.textcomposer.model.VoiceMessageState
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.wysiwyg.compose.PillStyle
import io.element.android.wysiwyg.compose.RichTextEditor
import io.element.android.wysiwyg.compose.RichTextEditorState
import io.element.android.wysiwyg.display.TextDisplay
@ -444,8 +442,6 @@ private fun TextInput(
.fillMaxWidth(),
style = ElementRichTextEditorStyle.create(
hasFocus = state.hasFocus
).copy(
pill = PillStyle(Color.Red)
),
resolveMentionDisplay = resolveMentionDisplay,
resolveRoomMentionDisplay = resolveRoomMentionDisplay,

View file

@ -34,6 +34,7 @@ dependencies {
implementation(projects.libraries.matrixui)
implementation(projects.libraries.matrix.api)
api(projects.libraries.usersearch.api)
implementation(libs.kotlinx.collections.immutable)
testImplementation(libs.test.junit)
testImplementation(libs.coroutines.test)

View file

@ -25,6 +25,7 @@ import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.A_USER_ID_2
import io.element.android.libraries.matrix.test.A_USER_NAME
import io.element.android.libraries.matrix.test.FakeMatrixClient
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.test.runTest
import org.junit.Test
@ -37,7 +38,7 @@ internal class MatrixUserListDataSourceTest {
searchTerm = "test",
result = Result.success(
MatrixSearchUserResults(
results = listOf(
results = persistentListOf(
aMatrixUserProfile(),
aMatrixUserProfile(userId = A_USER_ID_2)
),