Merge branch 'develop' into feature/valere/call/decline_timeline_rendering
This commit is contained in:
commit
a478d87fc3
995 changed files with 7864 additions and 3674 deletions
|
|
@ -70,6 +70,7 @@ import io.element.android.libraries.matrix.impl.room.RustRoomFactory
|
|||
import io.element.android.libraries.matrix.impl.room.TimelineEventFilterFactory
|
||||
import io.element.android.libraries.matrix.impl.room.history.map
|
||||
import io.element.android.libraries.matrix.impl.room.join.map
|
||||
import io.element.android.libraries.matrix.impl.room.location.map
|
||||
import io.element.android.libraries.matrix.impl.room.preview.RoomPreviewInfoMapper
|
||||
import io.element.android.libraries.matrix.impl.roomdirectory.RustRoomDirectoryService
|
||||
import io.element.android.libraries.matrix.impl.roomdirectory.map
|
||||
|
|
@ -113,6 +114,8 @@ import kotlinx.coroutines.withContext
|
|||
import kotlinx.coroutines.withTimeout
|
||||
import org.matrix.rustcomponents.sdk.AuthData
|
||||
import org.matrix.rustcomponents.sdk.AuthDataPasswordDetails
|
||||
import org.matrix.rustcomponents.sdk.BeaconInfoListener
|
||||
import org.matrix.rustcomponents.sdk.BeaconInfoUpdate
|
||||
import org.matrix.rustcomponents.sdk.Client
|
||||
import org.matrix.rustcomponents.sdk.ClientException
|
||||
import org.matrix.rustcomponents.sdk.IgnoredUsersListener
|
||||
|
|
@ -207,6 +210,15 @@ class RustMatrixClient(
|
|||
analyticsService = analyticsService,
|
||||
)
|
||||
|
||||
override val ownBeaconInfoUpdates = mxCallbackFlow {
|
||||
val listener = object : BeaconInfoListener {
|
||||
override fun onUpdate(update: BeaconInfoUpdate) {
|
||||
trySend(update.map())
|
||||
}
|
||||
}
|
||||
innerClient.subscribeToOwnBeaconInfoUpdates(listener)
|
||||
}
|
||||
|
||||
override val sessionVerificationService = RustSessionVerificationService(
|
||||
client = innerClient,
|
||||
isSyncServiceReady = syncService.syncState.map { it == SyncState.Running },
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ package io.element.android.libraries.matrix.impl.auth
|
|||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import dev.zacsweers.metro.SingleIn
|
||||
import io.element.android.features.enterprise.api.EnterpriseService
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.core.extensions.mapFailure
|
||||
import io.element.android.libraries.core.extensions.runCatchingExceptions
|
||||
|
|
@ -66,6 +67,7 @@ class RustMatrixAuthenticationService(
|
|||
private val rustMatrixClientFactory: RustMatrixClientFactory,
|
||||
private val passphraseGenerator: PassphraseGenerator,
|
||||
private val oAuthConfigurationProvider: OAuthConfigurationProvider,
|
||||
private val enterpriseService: EnterpriseService,
|
||||
) : MatrixAuthenticationService {
|
||||
// Any existing Element Classic session that we want to try to import secrets from during login.
|
||||
private var elementClassicSession: ElementClassicSession? = null
|
||||
|
|
@ -269,6 +271,12 @@ class RustMatrixAuthenticationService(
|
|||
additionalScopes = emptyList(),
|
||||
)
|
||||
val url = oAuthAuthorizationData.loginUrl()
|
||||
.let {
|
||||
enterpriseService.tweakMasUrl(
|
||||
url = it,
|
||||
homeserver = client.server() ?: client.homeserver(),
|
||||
)
|
||||
}
|
||||
pendingOAuthAuthorizationData = oAuthAuthorizationData
|
||||
OAuthDetails(url)
|
||||
}.mapFailure { failure ->
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ import io.element.android.libraries.matrix.impl.room.history.map
|
|||
import io.element.android.libraries.matrix.impl.room.join.map
|
||||
import io.element.android.libraries.matrix.impl.room.knock.RustKnockRequest
|
||||
import io.element.android.libraries.matrix.impl.room.location.liveLocationSharesFlow
|
||||
import io.element.android.libraries.matrix.impl.room.location.map
|
||||
import io.element.android.libraries.matrix.impl.room.location.timedByExpiry
|
||||
import io.element.android.libraries.matrix.impl.room.member.RoomMemberListFetcher
|
||||
import io.element.android.libraries.matrix.impl.room.threads.RustThreadsListService
|
||||
|
|
@ -72,6 +73,7 @@ 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.LiveLocationException
|
||||
import org.matrix.rustcomponents.sdk.RoomMessageEventMessageType
|
||||
import org.matrix.rustcomponents.sdk.RoomSendQueueUpdate
|
||||
import org.matrix.rustcomponents.sdk.SendQueueListener
|
||||
|
|
@ -525,12 +527,22 @@ class JoinedRustRoom(
|
|||
override suspend fun stopLiveLocationShare(): Result<Unit> = withContext(roomDispatcher) {
|
||||
runCatchingExceptions {
|
||||
innerRoom.stopLiveLocationShare()
|
||||
}.mapFailure { throwable ->
|
||||
when (throwable) {
|
||||
is LiveLocationException -> throwable.map()
|
||||
else -> throwable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun sendLiveLocation(geoUri: String): Result<Unit> = withContext(roomDispatcher) {
|
||||
runCatchingExceptions {
|
||||
innerRoom.sendLiveLocation(geoUri)
|
||||
}.mapFailure { throwable ->
|
||||
when (throwable) {
|
||||
is LiveLocationException -> throwable.map()
|
||||
else -> throwable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) 2026 Element Creations 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.location
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.location.BeaconInfoUpdate
|
||||
import org.matrix.rustcomponents.sdk.BeaconInfoUpdate as RustBeaconInfoUpdate
|
||||
|
||||
fun RustBeaconInfoUpdate.map(): BeaconInfoUpdate {
|
||||
return BeaconInfoUpdate(
|
||||
roomId = RoomId(roomId),
|
||||
beaconId = EventId(eventId),
|
||||
isLive = live
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2026 Element Creations 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.location
|
||||
|
||||
import io.element.android.libraries.matrix.api.room.location.LiveLocationException
|
||||
import org.matrix.rustcomponents.sdk.LiveLocationException as RustLiveLocationException
|
||||
|
||||
fun RustLiveLocationException.map(): LiveLocationException {
|
||||
return when (this) {
|
||||
is RustLiveLocationException.Network -> LiveLocationException.Network()
|
||||
is RustLiveLocationException.NotLive -> LiveLocationException.NotLive()
|
||||
else -> LiveLocationException.Other(this)
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
package io.element.android.libraries.matrix.impl.room.location
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.location.LastLocation
|
||||
import io.element.android.libraries.matrix.api.room.location.LiveLocationShare
|
||||
|
|
@ -41,9 +42,9 @@ fun RoomInterface.liveLocationSharesFlow(): Flow<List<LiveLocationShare>> {
|
|||
}
|
||||
}
|
||||
return callbackFlow {
|
||||
val liveLocationShares = liveLocationsObserver()
|
||||
val observer = liveLocationsObserver()
|
||||
val shares: MutableList<LiveLocationShare> = ArrayList()
|
||||
val taskHandle = liveLocationShares.subscribe(object : LiveLocationsListener {
|
||||
val taskHandle = observer.subscribe(object : LiveLocationsListener {
|
||||
override fun onUpdate(updates: List<LiveLocationShareUpdate>) {
|
||||
for (update in updates) {
|
||||
shares.applyUpdate(update)
|
||||
|
|
@ -53,13 +54,14 @@ fun RoomInterface.liveLocationSharesFlow(): Flow<List<LiveLocationShare>> {
|
|||
})
|
||||
awaitClose {
|
||||
taskHandle.cancelAndDestroy()
|
||||
liveLocationShares.destroy()
|
||||
observer.destroy()
|
||||
}
|
||||
}.buffer(Channel.UNLIMITED)
|
||||
}
|
||||
|
||||
private fun RustLiveLocationShare.into(): LiveLocationShare {
|
||||
return LiveLocationShare(
|
||||
beaconId = EventId(beaconId),
|
||||
userId = UserId(userId),
|
||||
lastLocation = lastLocation?.let {
|
||||
LastLocation(
|
||||
|
|
@ -69,6 +71,6 @@ private fun RustLiveLocationShare.into(): LiveLocationShare {
|
|||
)
|
||||
},
|
||||
startTimestamp = startTs.toLong(),
|
||||
endTimestamp = (startTs + timeout).toLong()
|
||||
endTimestamp = (startTs + timeout).toLong(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ object RoomPowerLevelsValuesMapper {
|
|||
roomAvatar = values.roomAvatar,
|
||||
roomTopic = values.roomTopic,
|
||||
spaceChild = values.spaceChild,
|
||||
beacon = values.beacon,
|
||||
beaconInfo = values.beaconInfo,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ class RustSessionVerificationService(
|
|||
this.listener = listener
|
||||
}
|
||||
|
||||
override suspend fun requestCurrentSessionVerification() = tryOrFail {
|
||||
override suspend fun requestDeviceVerification() = tryOrFail {
|
||||
ensureEncryptionIsInitialized()
|
||||
verificationController.requestDeviceVerification()
|
||||
currentVerificationRequest = VerificationRequest.Outgoing.CurrentSession
|
||||
|
|
@ -146,7 +146,7 @@ class RustSessionVerificationService(
|
|||
|
||||
override suspend fun declineVerification() = tryOrFail { verificationController.declineVerification() }
|
||||
|
||||
override suspend fun startVerification() = tryOrFail {
|
||||
override suspend fun startSasVerification() = tryOrFail {
|
||||
verificationController.startSasVerification()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
package io.element.android.libraries.matrix.impl.auth
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.enterprise.api.EnterpriseService
|
||||
import io.element.android.features.enterprise.test.FakeEnterpriseService
|
||||
import io.element.android.libraries.matrix.impl.ClientBuilderProvider
|
||||
import io.element.android.libraries.matrix.impl.FakeClientBuilderProvider
|
||||
import io.element.android.libraries.matrix.impl.createRustMatrixClientFactory
|
||||
|
|
@ -50,6 +52,7 @@ class RustMatrixAuthenticationServiceTest {
|
|||
private fun TestScope.createRustMatrixAuthenticationService(
|
||||
sessionStore: SessionStore = InMemorySessionStore(),
|
||||
clientBuilderProvider: ClientBuilderProvider = FakeClientBuilderProvider(),
|
||||
enterpriseService: EnterpriseService = FakeEnterpriseService(),
|
||||
): RustMatrixAuthenticationService {
|
||||
val baseDirectory = File("/base")
|
||||
val cacheDirectory = File("/cache")
|
||||
|
|
@ -68,6 +71,7 @@ class RustMatrixAuthenticationServiceTest {
|
|||
buildMeta = aBuildMeta(),
|
||||
oAuthRedirectUrlProvider = FakeOAuthRedirectUrlProvider(),
|
||||
),
|
||||
enterpriseService = enterpriseService,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ internal fun aRustNotificationRoomInfo(
|
|||
joinedMembersCount: ULong = 2u,
|
||||
isEncrypted: Boolean? = true,
|
||||
isDirect: Boolean = false,
|
||||
isDm: Boolean = false,
|
||||
joinRule: JoinRule? = null,
|
||||
isSpace: Boolean = false,
|
||||
serviceMembers: List<UserId> = emptyList(),
|
||||
|
|
@ -79,6 +80,7 @@ internal fun aRustNotificationRoomInfo(
|
|||
joinedMembersCount = joinedMembersCount,
|
||||
isEncrypted = isEncrypted,
|
||||
isDirect = isDirect,
|
||||
isDm = isDm,
|
||||
joinRule = joinRule,
|
||||
isSpace = isSpace,
|
||||
serviceMembers = serviceMembers.map { it.value },
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ internal fun aRustRoomInfo(
|
|||
isLowPriority: Boolean = false,
|
||||
activeRoomCallConsensusIntent: RtcCallIntentConsensus = RtcCallIntentConsensus.None,
|
||||
activeServiceMembersCount: Int = 0,
|
||||
isDm: Boolean = false,
|
||||
) = RoomInfo(
|
||||
id = id,
|
||||
displayName = displayName,
|
||||
|
|
@ -103,4 +104,5 @@ internal fun aRustRoomInfo(
|
|||
isLowPriority = isLowPriority,
|
||||
activeRoomCallConsensusIntent = activeRoomCallConsensusIntent,
|
||||
activeServiceMembersCount = activeServiceMembersCount.toULong(),
|
||||
isDm = isDm,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ internal fun aRustRoomMember(
|
|||
isIgnored: Boolean = false,
|
||||
role: RoomMemberRole = RoomMemberRole.USER,
|
||||
membershipChangeReason: String? = null,
|
||||
isServiceMember: Boolean = false,
|
||||
) = RoomMember(
|
||||
userId = userId.value,
|
||||
displayName = displayName,
|
||||
|
|
@ -34,4 +35,5 @@ internal fun aRustRoomMember(
|
|||
isIgnored = isIgnored,
|
||||
suggestedRoleForPowerLevel = role,
|
||||
membershipChangeReason = membershipChangeReason,
|
||||
isServiceMember = isServiceMember,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ internal fun aRustRoomPowerLevelsValues(
|
|||
roomAvatar: Long,
|
||||
roomTopic: Long,
|
||||
spaceChild: Long,
|
||||
beacon: Long,
|
||||
beaconInfo: Long,
|
||||
) = RoomPowerLevelsValues(
|
||||
ban = ban,
|
||||
invite = invite,
|
||||
|
|
@ -33,5 +35,7 @@ internal fun aRustRoomPowerLevelsValues(
|
|||
roomName = roomName,
|
||||
roomAvatar = roomAvatar,
|
||||
roomTopic = roomTopic,
|
||||
spaceChild = spaceChild
|
||||
spaceChild = spaceChild,
|
||||
beacon = beacon,
|
||||
beaconInfo = beaconInfo,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import org.matrix.rustcomponents.sdk.SpaceRoom
|
|||
internal fun aRustSpaceRoom(
|
||||
roomId: RoomId = A_ROOM_ID,
|
||||
isDirect: Boolean = false,
|
||||
isDm: Boolean = false,
|
||||
canonicalAlias: String? = null,
|
||||
rawName: String? = null,
|
||||
displayName: String = "",
|
||||
|
|
@ -35,6 +36,7 @@ internal fun aRustSpaceRoom(
|
|||
) = SpaceRoom(
|
||||
roomId = roomId.value,
|
||||
isDirect = isDirect,
|
||||
isDm = isDm,
|
||||
canonicalAlias = canonicalAlias,
|
||||
rawName = rawName,
|
||||
displayName = displayName,
|
||||
|
|
|
|||
|
|
@ -32,4 +32,6 @@ fun defaultFfiRoomPowerLevelValues() = RoomPowerLevelsValues(
|
|||
roomTopic = 50,
|
||||
spaceChild = 50,
|
||||
usersDefault = 0,
|
||||
beacon = 0,
|
||||
beaconInfo = 0,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import app.cash.turbine.test
|
|||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.location.LiveLocationShare
|
||||
import io.element.android.libraries.matrix.test.room.location.aLiveLocationShare
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
|
|
@ -24,9 +25,9 @@ class TimedLiveLocationSharesFlowTest {
|
|||
@Test
|
||||
fun `it keeps emitting shares for subsequent expiries without upstream changes`() = runTest {
|
||||
val shares = listOf(
|
||||
aLiveLocationShare(userId = "@alice:server", endTimestamp = 1_000),
|
||||
aLiveLocationShare(userId = "@bob:server", endTimestamp = 2_000),
|
||||
aLiveLocationShare(userId = "@carol:server", endTimestamp = 3_000),
|
||||
aLiveLocationShare(userId = UserId("@alice:server"), endTimestamp = 1_000),
|
||||
aLiveLocationShare(userId = UserId("@bob:server"), endTimestamp = 2_000),
|
||||
aLiveLocationShare(userId = UserId("@carol:server"), endTimestamp = 3_000),
|
||||
)
|
||||
|
||||
flowOf(shares)
|
||||
|
|
@ -56,8 +57,8 @@ class TimedLiveLocationSharesFlowTest {
|
|||
@Test
|
||||
fun `it does not double-emit when a share is already expired on receipt`() = runTest {
|
||||
val shares = listOf(
|
||||
aLiveLocationShare(userId = "@alice:server", endTimestamp = 500),
|
||||
aLiveLocationShare(userId = "@bob:server", endTimestamp = 2_000),
|
||||
aLiveLocationShare(userId = UserId("@alice:server"), endTimestamp = 500),
|
||||
aLiveLocationShare(userId = UserId("@bob:server"), endTimestamp = 2_000),
|
||||
)
|
||||
|
||||
flowOf(shares)
|
||||
|
|
@ -81,8 +82,8 @@ class TimedLiveLocationSharesFlowTest {
|
|||
val upstream = MutableSharedFlow<List<LiveLocationShare>>(extraBufferCapacity = 1)
|
||||
val initialShares = listOf(aLiveLocationShare(endTimestamp = 10_000))
|
||||
val updatedShares = listOf(
|
||||
aLiveLocationShare(userId = "@alice:server", endTimestamp = 10_000),
|
||||
aLiveLocationShare(userId = "@bob:server", endTimestamp = 6_000),
|
||||
aLiveLocationShare(userId = UserId("@alice:server"), endTimestamp = 10_000),
|
||||
aLiveLocationShare(userId = UserId("@bob:server"), endTimestamp = 6_000),
|
||||
)
|
||||
|
||||
upstream
|
||||
|
|
@ -133,15 +134,3 @@ class TimedLiveLocationSharesFlowTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun aLiveLocationShare(
|
||||
userId: String = "@user:server",
|
||||
endTimestamp: Long,
|
||||
): LiveLocationShare {
|
||||
return LiveLocationShare(
|
||||
userId = UserId(userId),
|
||||
lastLocation = null,
|
||||
startTimestamp = 0L,
|
||||
endTimestamp = endTimestamp,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ class RoomPowerLevelsValuesMapperTest {
|
|||
roomAvatar = 9,
|
||||
roomTopic = 10,
|
||||
spaceChild = 11,
|
||||
beacon = 12,
|
||||
beaconInfo = 13,
|
||||
)
|
||||
)
|
||||
).isEqualTo(
|
||||
|
|
@ -44,6 +46,8 @@ class RoomPowerLevelsValuesMapperTest {
|
|||
roomAvatar = 9,
|
||||
roomTopic = 10,
|
||||
spaceChild = 11,
|
||||
beacon = 12,
|
||||
beaconInfo = 13,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue