Merge branch 'develop' into hughns/qr-grant-error-mapping
This commit is contained in:
commit
7d27ff3c59
29 changed files with 265 additions and 44 deletions
1
.github/workflows/recordScreenshots.yml
vendored
1
.github/workflows/recordScreenshots.yml
vendored
|
|
@ -17,6 +17,7 @@ jobs:
|
|||
permissions:
|
||||
# Need write permissions on PRs to remove the label "Record-Screenshots"
|
||||
pull-requests: write
|
||||
contents: write
|
||||
name: Record screenshots on branch ${{ github.event.pull_request.head.ref || github.ref_name }}
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'workflow_dispatch' || github.event.label.name == 'Record-Screenshots'
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import dev.zacsweers.metro.AssistedInject
|
|||
import io.element.android.annotations.ContributesNode
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.features.linknewdevice.api.LinkNewDeviceEntryPoint
|
||||
import io.element.android.features.linknewdevice.impl.screens.confirmation.CodeConfirmationNode
|
||||
import io.element.android.features.linknewdevice.impl.screens.desktop.DesktopNoticeNode
|
||||
import io.element.android.features.linknewdevice.impl.screens.error.ErrorNode
|
||||
import io.element.android.features.linknewdevice.impl.screens.error.ErrorScreenType
|
||||
|
|
@ -107,6 +108,11 @@ class LinkNewDeviceFlowNode(
|
|||
val data: String,
|
||||
) : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data class CodeConfirmation(
|
||||
val code: String,
|
||||
) : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data object MobileEnterNumber : NavTarget
|
||||
|
||||
|
|
@ -166,7 +172,9 @@ class LinkNewDeviceFlowNode(
|
|||
is LinkDesktopStep.Error -> {
|
||||
navigateToError(linkDesktopStep.errorType)
|
||||
}
|
||||
is LinkDesktopStep.EstablishingSecureChannel -> Unit
|
||||
is LinkDesktopStep.EstablishingSecureChannel -> {
|
||||
backstack.push(NavTarget.CodeConfirmation(linkDesktopStep.checkCodeString))
|
||||
}
|
||||
is LinkDesktopStep.InvalidQrCode -> {
|
||||
// This error will be handled by the ScanQrCodeNode
|
||||
}
|
||||
|
|
@ -247,6 +255,18 @@ class LinkNewDeviceFlowNode(
|
|||
}
|
||||
createNode<EnterNumberNode>(buildContext, listOf(callback))
|
||||
}
|
||||
is NavTarget.CodeConfirmation -> {
|
||||
val callback = object : CodeConfirmationNode.Callback {
|
||||
override fun onCancel() {
|
||||
// Push error
|
||||
backstack.push(NavTarget.Error(ErrorScreenType.Cancelled))
|
||||
}
|
||||
}
|
||||
val inputs = CodeConfirmationNode.Inputs(
|
||||
code = navTarget.code,
|
||||
)
|
||||
createNode<CodeConfirmationNode>(buildContext, listOf(inputs, callback))
|
||||
}
|
||||
is NavTarget.MobileShowQrCode -> {
|
||||
val callback = object : ShowQrCodeNode.Callback {
|
||||
override fun navigateBack() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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.features.linknewdevice.impl.screens.confirmation
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.plugin.Plugin
|
||||
import dev.zacsweers.metro.Assisted
|
||||
import dev.zacsweers.metro.AssistedInject
|
||||
import io.element.android.annotations.ContributesNode
|
||||
import io.element.android.libraries.architecture.NodeInputs
|
||||
import io.element.android.libraries.architecture.callback
|
||||
import io.element.android.libraries.architecture.inputs
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
|
||||
@ContributesNode(SessionScope::class)
|
||||
@AssistedInject
|
||||
class CodeConfirmationNode(
|
||||
@Assisted buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
) : Node(buildContext = buildContext, plugins = plugins) {
|
||||
interface Callback : Plugin {
|
||||
fun onCancel()
|
||||
}
|
||||
|
||||
data class Inputs(
|
||||
val code: String,
|
||||
) : NodeInputs
|
||||
|
||||
private val callback: Callback = callback()
|
||||
private val input = inputs<Inputs>()
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
CodeConfirmationView(
|
||||
code = input.code,
|
||||
onCancel = callback::onCancel,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* 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.features.linknewdevice.impl.screens.confirmation
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.features.linknewdevice.impl.R
|
||||
import io.element.android.libraries.designsystem.atomic.pages.FlowStepPage
|
||||
import io.element.android.libraries.designsystem.components.BigIcon
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
|
||||
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
fun CodeConfirmationView(
|
||||
code: String,
|
||||
onCancel: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
BackHandler(onBack = onCancel)
|
||||
FlowStepPage(
|
||||
modifier = modifier,
|
||||
iconStyle = BigIcon.Style.Default(CompoundIcons.Computer()),
|
||||
title = stringResource(R.string.screen_qr_code_login_device_code_title),
|
||||
subTitle = stringResource(R.string.screen_qr_code_login_device_code_subtitle),
|
||||
content = { Content(code = code) },
|
||||
buttons = { Buttons(onCancel = onCancel) }
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Content(code: String) {
|
||||
Column(
|
||||
modifier = Modifier.padding(top = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Digits(code = code)
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
WaitingForOtherDevice()
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
private fun Digits(code: String) {
|
||||
FlowRow(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
code.forEach {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 6.dp, vertical = 4.dp)
|
||||
.clip(RoundedCornerShape(4.dp))
|
||||
.background(ElementTheme.colors.bgActionSecondaryPressed)
|
||||
.padding(horizontal = 16.dp, vertical = 17.dp),
|
||||
text = it.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WaitingForOtherDevice() {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.padding(2.dp),
|
||||
strokeWidth = 2.dp,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.screen_qr_code_login_verify_code_loading),
|
||||
style = ElementTheme.typography.fontBodySmRegular,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Buttons(
|
||||
onCancel: () -> Unit,
|
||||
) {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
OutlinedButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(CommonStrings.action_cancel),
|
||||
onClick = onCancel,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun CodeConfirmationViewPreview() {
|
||||
ElementPreview {
|
||||
CodeConfirmationView(
|
||||
code = "67",
|
||||
onCancel = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -34,6 +34,8 @@
|
|||
<string name="screen_qr_code_login_connection_note_secure_state_list_item_2">"If you encounter the same problem, try a different wifi network or use your mobile data instead of wifi"</string>
|
||||
<string name="screen_qr_code_login_connection_note_secure_state_list_item_3">"If that doesn’t work, sign in manually"</string>
|
||||
<string name="screen_qr_code_login_connection_note_secure_state_title">"Connection not secure"</string>
|
||||
<string name="screen_qr_code_login_device_code_subtitle">"You’ll be asked to enter the two digits shown on this device."</string>
|
||||
<string name="screen_qr_code_login_device_code_title">"Enter the number below on your other device"</string>
|
||||
<string name="screen_qr_code_login_error_cancelled_subtitle">"The sign in was cancelled on the other device."</string>
|
||||
<string name="screen_qr_code_login_error_cancelled_title">"Sign in request cancelled"</string>
|
||||
<string name="screen_qr_code_login_error_declined_subtitle">"The sign in was declined on the other device."</string>
|
||||
|
|
@ -54,4 +56,5 @@ Try signing in manually, or scan the QR code with another device."</string>
|
|||
<string name="screen_qr_code_login_no_camera_permission_state_description">"You need to give permission for %1$s to use your device’s camera in order to continue."</string>
|
||||
<string name="screen_qr_code_login_no_camera_permission_state_title">"Allow camera access to scan the QR code"</string>
|
||||
<string name="screen_qr_code_login_unknown_error_description">"An unexpected error occurred. Please try again."</string>
|
||||
<string name="screen_qr_code_login_verify_code_loading">"Waiting for your other device"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ test_detekt_test = { module = "io.gitlab.arturbosch.detekt:detekt-test", version
|
|||
# https://github.com/matrix-org/matrix-rust-components-kotlin/commits/main/sdk/sdk-android/src/main/kotlin/org/matrix/rustcomponents/sdk/matrix_sdk_ffi.kt
|
||||
# All new features should not be implemented in the pull request that upgrades the version, developers should
|
||||
# only fix API breaks and may add some TODOs.
|
||||
matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.04.21"
|
||||
matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.04.27"
|
||||
|
||||
# Others
|
||||
coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" }
|
||||
|
|
|
|||
|
|
@ -196,9 +196,9 @@ interface JoinedRoom : BaseRoom {
|
|||
/**
|
||||
* Start sharing live location in this room.
|
||||
* @param durationMillis How long to share location (in milliseconds).
|
||||
* @return Result indicating success or failure.
|
||||
* @return Result containing the [EventId] of the beacon state event on success or an error on failure.
|
||||
*/
|
||||
suspend fun startLiveLocationShare(durationMillis: Long): Result<Unit>
|
||||
suspend fun startLiveLocationShare(durationMillis: Long): Result<EventId>
|
||||
|
||||
/**
|
||||
* Stop sharing live location in this room.
|
||||
|
|
|
|||
|
|
@ -214,5 +214,5 @@ fun SessionData.toSession() = Session(
|
|||
deviceId = deviceId,
|
||||
homeserverUrl = homeserverUrl,
|
||||
slidingSyncVersion = SlidingSyncVersion.NATIVE,
|
||||
oidcData = oidcData,
|
||||
oauthData = oidcData,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ package io.element.android.libraries.matrix.impl.auth
|
|||
|
||||
import io.element.android.libraries.matrix.api.auth.AuthenticationException
|
||||
import org.matrix.rustcomponents.sdk.ClientBuildException
|
||||
import org.matrix.rustcomponents.sdk.OidcException
|
||||
import org.matrix.rustcomponents.sdk.OAuthException
|
||||
|
||||
fun Throwable.mapAuthenticationException(): AuthenticationException {
|
||||
return when (this) {
|
||||
|
|
@ -29,12 +29,12 @@ fun Throwable.mapAuthenticationException(): AuthenticationException {
|
|||
is ClientBuildException.WellKnownLookupFailed -> AuthenticationException.Generic(message)
|
||||
is ClientBuildException.EventCache -> AuthenticationException.Generic(message)
|
||||
}
|
||||
is OidcException -> when (this) {
|
||||
is OidcException.Generic -> AuthenticationException.Oidc(message)
|
||||
is OidcException.CallbackUrlInvalid -> AuthenticationException.Oidc(message)
|
||||
is OidcException.Cancelled -> AuthenticationException.Oidc(message)
|
||||
is OidcException.MetadataInvalid -> AuthenticationException.Oidc(message)
|
||||
is OidcException.NotSupported -> AuthenticationException.Oidc(message)
|
||||
is OAuthException -> when (this) {
|
||||
is OAuthException.Generic -> AuthenticationException.Oidc(message)
|
||||
is OAuthException.CallbackUrlInvalid -> AuthenticationException.Oidc(message)
|
||||
is OAuthException.Cancelled -> AuthenticationException.Oidc(message)
|
||||
is OAuthException.MetadataInvalid -> AuthenticationException.Oidc(message)
|
||||
is OAuthException.NotSupported -> AuthenticationException.Oidc(message)
|
||||
}
|
||||
else -> AuthenticationException.Generic(message)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,6 @@ fun HomeserverLoginDetails.map(): MatrixHomeServerDetails = use {
|
|||
MatrixHomeServerDetails(
|
||||
url = url(),
|
||||
supportsPasswordLogin = supportsPasswordLogin(),
|
||||
supportsOidcLogin = supportsOidcLogin(),
|
||||
supportsOidcLogin = supportsOauthLogin(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,14 +12,14 @@ import dev.zacsweers.metro.Inject
|
|||
import io.element.android.libraries.core.meta.BuildMeta
|
||||
import io.element.android.libraries.matrix.api.auth.OidcConfig
|
||||
import io.element.android.libraries.matrix.api.auth.OidcRedirectUrlProvider
|
||||
import org.matrix.rustcomponents.sdk.OidcConfiguration
|
||||
import org.matrix.rustcomponents.sdk.OAuthConfiguration
|
||||
|
||||
@Inject
|
||||
class OidcConfigurationProvider(
|
||||
private val buildMeta: BuildMeta,
|
||||
private val oidcRedirectUrlProvider: OidcRedirectUrlProvider,
|
||||
) {
|
||||
fun get(): OidcConfiguration = OidcConfiguration(
|
||||
fun get(): OAuthConfiguration = OAuthConfiguration(
|
||||
clientName = buildMeta.applicationName,
|
||||
redirectUri = oidcRedirectUrlProvider.provide(),
|
||||
clientUri = OidcConfig.CLIENT_URI,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
package io.element.android.libraries.matrix.impl.auth
|
||||
|
||||
import io.element.android.libraries.matrix.api.auth.OidcPrompt
|
||||
import org.matrix.rustcomponents.sdk.OidcPrompt as RustOidcPrompt
|
||||
import org.matrix.rustcomponents.sdk.OAuthPrompt as RustOidcPrompt
|
||||
|
||||
internal fun OidcPrompt.toRustPrompt(): RustOidcPrompt {
|
||||
return when (this) {
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ class RustHomeServerLoginCompatibilityChecker(
|
|||
it.homeserverLoginDetails()
|
||||
}
|
||||
.use {
|
||||
Timber.d("Homeserver $url | OIDC: ${it.supportsOidcLogin()} | Password: ${it.supportsPasswordLogin()} | SSO: ${it.supportsSsoLogin()}")
|
||||
it.supportsOidcLogin() || it.supportsPasswordLogin()
|
||||
Timber.d("Homeserver $url | OIDC: ${it.supportsOauthLogin()} | Password: ${it.supportsPasswordLogin()} | SSO: ${it.supportsSsoLogin()}")
|
||||
it.supportsOauthLogin() || it.supportsPasswordLogin()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -260,8 +260,8 @@ class RustMatrixAuthenticationService(
|
|||
return withContext(coroutineDispatchers.io) {
|
||||
runCatchingExceptions {
|
||||
val client = currentClient ?: error("You need to call `setHomeserver()` first")
|
||||
val oAuthAuthorizationData = client.urlForOidc(
|
||||
oidcConfiguration = oidcConfigurationProvider.get(),
|
||||
val oAuthAuthorizationData = client.urlForOauth(
|
||||
oauthConfiguration = oidcConfigurationProvider.get(),
|
||||
prompt = prompt.toRustPrompt(),
|
||||
loginHint = loginHint,
|
||||
// If we want to restore a previous session for which we have encryption keys, we can pass the deviceId here. At the moment, we don't
|
||||
|
|
@ -282,7 +282,7 @@ class RustMatrixAuthenticationService(
|
|||
return withContext(coroutineDispatchers.io) {
|
||||
runCatchingExceptions {
|
||||
pendingOAuthAuthorizationData?.use {
|
||||
currentClient?.abortOidcAuth(it)
|
||||
currentClient?.abortOauthAuth(it)
|
||||
}
|
||||
pendingOAuthAuthorizationData = null
|
||||
}.mapFailure { failure ->
|
||||
|
|
@ -304,7 +304,7 @@ class RustMatrixAuthenticationService(
|
|||
runCatchingExceptions {
|
||||
val client = currentClient ?: error("You need to call `setHomeserver()` first")
|
||||
val currentSessionPaths = sessionPaths ?: error("You need to call `setHomeserver()` first")
|
||||
client.loginWithOidcCallback(
|
||||
client.loginWithOauthCallback(
|
||||
callbackUrl = callbackUrl,
|
||||
)
|
||||
// Free the pending data since we won't use it to abort the flow anymore
|
||||
|
|
@ -368,7 +368,7 @@ class RustMatrixAuthenticationService(
|
|||
qrCodeData = sdkQrCodeLoginData,
|
||||
)
|
||||
client.newLoginWithQrCodeHandler(
|
||||
oidcConfiguration = oidcConfiguration,
|
||||
oauthConfiguration = oidcConfiguration,
|
||||
).use {
|
||||
it.scan(
|
||||
qrCodeData = qrCodeData.rustQrCodeData,
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ object QrErrorMapper {
|
|||
is RustHumanQrLoginException.OtherDeviceNotSignedIn -> QrLoginException.OtherDeviceNotSignedIn
|
||||
is RustHumanQrLoginException.LinkingNotSupported -> QrLoginException.LinkingNotSupported
|
||||
is RustHumanQrLoginException.Unknown -> QrLoginException.Unknown
|
||||
is RustHumanQrLoginException.OidcMetadataInvalid -> QrLoginException.OidcMetadataInvalid
|
||||
is RustHumanQrLoginException.OAuthMetadataInvalid -> QrLoginException.OidcMetadataInvalid
|
||||
is RustHumanQrLoginException.SlidingSyncNotAvailable -> QrLoginException.SlidingSyncNotAvailable
|
||||
is RustHumanQrLoginException.CheckCodeAlreadySent -> QrLoginException.CheckCodeAlreadySent
|
||||
is RustHumanQrLoginException.CheckCodeCannotBeSent -> QrLoginException.CheckCodeCannotBeSent
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ object RustIdentityResetHandleFactory {
|
|||
return runCatchingExceptions {
|
||||
identityResetHandle?.let {
|
||||
when (val authType = identityResetHandle.authType()) {
|
||||
is CrossSigningResetAuthType.Oidc -> RustOidcIdentityResetHandle(identityResetHandle, authType.info.approvalUrl)
|
||||
is CrossSigningResetAuthType.OAuth -> RustOidcIdentityResetHandle(identityResetHandle, authType.info.approvalUrl)
|
||||
// User interactive authentication (user + password)
|
||||
CrossSigningResetAuthType.Uiaa -> RustPasswordIdentityResetHandle(userId, identityResetHandle)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ internal fun Session.toSessionData(
|
|||
accessToken = accessToken,
|
||||
refreshToken = refreshToken,
|
||||
homeserverUrl = homeserverUrl ?: this.homeserverUrl,
|
||||
oidcData = oidcData,
|
||||
oidcData = oauthData,
|
||||
loginTimestamp = Date(),
|
||||
isTokenValid = isTokenValid,
|
||||
loginType = loginType,
|
||||
|
|
|
|||
|
|
@ -516,10 +516,10 @@ class JoinedRustRoom(
|
|||
return innerRoom.liveLocationSharesFlow().timedByExpiry(systemClock::epochMillis)
|
||||
}
|
||||
|
||||
override suspend fun startLiveLocationShare(durationMillis: Long): Result<Unit> = withContext(roomDispatcher) {
|
||||
override suspend fun startLiveLocationShare(durationMillis: Long): Result<EventId> = withContext(roomDispatcher) {
|
||||
runCatchingExceptions {
|
||||
innerRoom.startLiveLocationShare(durationMillis.toULong())
|
||||
}
|
||||
}.map(::EventId)
|
||||
}
|
||||
|
||||
override suspend fun stopLiveLocationShare(): Result<Unit> = withContext(roomDispatcher) {
|
||||
|
|
@ -538,7 +538,7 @@ class JoinedRustRoom(
|
|||
|
||||
override fun destroy() {
|
||||
baseRoom.destroy()
|
||||
liveInnerTimeline.destroy()
|
||||
liveTimeline.close()
|
||||
threadsListService.destroy()
|
||||
Timber.d("Room $roomId destroyed")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ import kotlinx.coroutines.channels.awaitClose
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.buffer
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import org.matrix.rustcomponents.sdk.LiveLocationShareListener
|
||||
import org.matrix.rustcomponents.sdk.LiveLocationShareUpdate
|
||||
import org.matrix.rustcomponents.sdk.LiveLocationsListener
|
||||
import org.matrix.rustcomponents.sdk.RoomInterface
|
||||
import org.matrix.rustcomponents.sdk.LiveLocationShare as RustLiveLocationShare
|
||||
|
||||
|
|
@ -41,9 +41,9 @@ fun RoomInterface.liveLocationSharesFlow(): Flow<List<LiveLocationShare>> {
|
|||
}
|
||||
}
|
||||
return callbackFlow {
|
||||
val liveLocationShares = liveLocationShares()
|
||||
val liveLocationShares = liveLocationsObserver()
|
||||
val shares: MutableList<LiveLocationShare> = ArrayList()
|
||||
val taskHandle = liveLocationShares.subscribe(object : LiveLocationShareListener {
|
||||
val taskHandle = liveLocationShares.subscribe(object : LiveLocationsListener {
|
||||
override fun onUpdate(updates: List<LiveLocationShareUpdate>) {
|
||||
for (update in updates) {
|
||||
shares.applyUpdate(update)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import com.google.common.truth.Truth.assertThat
|
|||
import io.element.android.libraries.matrix.api.auth.AuthenticationException
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.ClientBuildException
|
||||
import org.matrix.rustcomponents.sdk.OidcException
|
||||
import org.matrix.rustcomponents.sdk.OAuthException
|
||||
|
||||
class AuthenticationExceptionMappingTest {
|
||||
@Test
|
||||
|
|
@ -65,15 +65,15 @@ class AuthenticationExceptionMappingTest {
|
|||
|
||||
@Test
|
||||
fun `mapping Oidc exceptions map to the Oidc Kotlin`() {
|
||||
assertThat(OidcException.Generic("Generic").mapAuthenticationException())
|
||||
assertThat(OAuthException.Generic("Generic").mapAuthenticationException())
|
||||
.isException<AuthenticationException.Oidc>("Generic")
|
||||
assertThat(OidcException.CallbackUrlInvalid("CallbackUrlInvalid").mapAuthenticationException())
|
||||
assertThat(OAuthException.CallbackUrlInvalid("CallbackUrlInvalid").mapAuthenticationException())
|
||||
.isException<AuthenticationException.Oidc>("CallbackUrlInvalid")
|
||||
assertThat(OidcException.Cancelled("Cancelled").mapAuthenticationException())
|
||||
assertThat(OAuthException.Cancelled("Cancelled").mapAuthenticationException())
|
||||
.isException<AuthenticationException.Oidc>("Cancelled")
|
||||
assertThat(OidcException.MetadataInvalid("MetadataInvalid").mapAuthenticationException())
|
||||
assertThat(OAuthException.MetadataInvalid("MetadataInvalid").mapAuthenticationException())
|
||||
.isException<AuthenticationException.Oidc>("MetadataInvalid")
|
||||
assertThat(OidcException.NotSupported("NotSupported").mapAuthenticationException())
|
||||
assertThat(OAuthException.NotSupported("NotSupported").mapAuthenticationException())
|
||||
.isException<AuthenticationException.Oidc>("NotSupported")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class QrErrorMapperTest {
|
|||
assertThat(QrErrorMapper.map(RustHumanQrLoginException.OtherDeviceNotSignedIn())).isEqualTo(QrLoginException.OtherDeviceNotSignedIn)
|
||||
assertThat(QrErrorMapper.map(RustHumanQrLoginException.LinkingNotSupported())).isEqualTo(QrLoginException.LinkingNotSupported)
|
||||
assertThat(QrErrorMapper.map(RustHumanQrLoginException.Unknown())).isEqualTo(QrLoginException.Unknown)
|
||||
assertThat(QrErrorMapper.map(RustHumanQrLoginException.OidcMetadataInvalid())).isEqualTo(QrLoginException.OidcMetadataInvalid)
|
||||
assertThat(QrErrorMapper.map(RustHumanQrLoginException.OAuthMetadataInvalid())).isEqualTo(QrLoginException.OidcMetadataInvalid)
|
||||
assertThat(QrErrorMapper.map(RustHumanQrLoginException.SlidingSyncNotAvailable())).isEqualTo(QrLoginException.SlidingSyncNotAvailable)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
package io.element.android.libraries.matrix.impl.fixtures.factories
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.ThreadId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiTimelineEvent
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||
import io.element.android.libraries.matrix.test.A_USER_NAME
|
||||
|
|
@ -68,6 +69,8 @@ internal fun aRustNotificationRoomInfo(
|
|||
isDirect: Boolean = false,
|
||||
joinRule: JoinRule? = null,
|
||||
isSpace: Boolean = false,
|
||||
serviceMembers: List<UserId> = emptyList(),
|
||||
activeServiceMemberCount: Int = 0,
|
||||
) = NotificationRoomInfo(
|
||||
displayName = displayName,
|
||||
avatarUrl = avatarUrl,
|
||||
|
|
@ -78,6 +81,8 @@ internal fun aRustNotificationRoomInfo(
|
|||
isDirect = isDirect,
|
||||
joinRule = joinRule,
|
||||
isSpace = isSpace,
|
||||
serviceMembers = serviceMembers.map { it.value },
|
||||
activeServiceMembersCount = activeServiceMemberCount.toULong(),
|
||||
)
|
||||
|
||||
internal fun aRustNotificationEventTimeline(
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ internal fun aRustRoomInfo(
|
|||
serviceMembers: List<String> = emptyList(),
|
||||
isLowPriority: Boolean = false,
|
||||
activeRoomCallConsensusIntent: RtcCallIntentConsensus = RtcCallIntentConsensus.None,
|
||||
activeServiceMembersCount: Int = 0,
|
||||
) = RoomInfo(
|
||||
id = id,
|
||||
displayName = displayName,
|
||||
|
|
@ -101,4 +102,5 @@ internal fun aRustRoomInfo(
|
|||
serviceMembers = serviceMembers,
|
||||
isLowPriority = isLowPriority,
|
||||
activeRoomCallConsensusIntent = activeRoomCallConsensusIntent,
|
||||
activeServiceMembersCount = activeServiceMembersCount.toULong(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,6 @@ internal fun aRustSession(
|
|||
userId = A_USER_ID.value,
|
||||
deviceId = A_DEVICE_ID.value,
|
||||
homeserverUrl = A_HOMESERVER_URL,
|
||||
oidcData = null,
|
||||
oauthData = null,
|
||||
slidingSyncVersion = proxy,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class FakeFfiHomeserverLoginDetails(
|
|||
private val supportsSsoLogin: Boolean = false,
|
||||
) : HomeserverLoginDetails(NoHandle) {
|
||||
override fun url(): String = url
|
||||
override fun supportsOidcLogin(): Boolean = supportsOidcLogin
|
||||
override fun supportsOauthLogin(): Boolean = supportsOidcLogin
|
||||
override fun supportsPasswordLogin(): Boolean = supportsPasswordLogin
|
||||
override fun supportsSsoLogin(): Boolean = supportsSsoLogin
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ 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.widget.MatrixWidgetDriver
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
|
||||
import io.element.android.libraries.matrix.test.room.threads.FakeThreadsListService
|
||||
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
||||
|
|
@ -238,8 +239,8 @@ class FakeJoinedRoom(
|
|||
return liveLocationSharesFlow
|
||||
}
|
||||
|
||||
override suspend fun startLiveLocationShare(durationMillis: Long): Result<Unit> = simulateLongTask {
|
||||
startLiveLocationShareResult(durationMillis)
|
||||
override suspend fun startLiveLocationShare(durationMillis: Long): Result<EventId> = simulateLongTask {
|
||||
startLiveLocationShareResult(durationMillis).map { AN_EVENT_ID }
|
||||
}
|
||||
|
||||
override suspend fun stopLiveLocationShare(): Result<Unit> = simulateLongTask {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c8dab1dc964cea9a76dc7130d8ac2bfe5d3c866fd6d8d969101eb50e828775d3
|
||||
size 31915
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ec67e3fef25c57331cb2425f8fc46a733e16a98c9945d0a4e5b950843c42fa34
|
||||
size 31087
|
||||
|
|
@ -163,7 +163,9 @@
|
|||
"screen_qr_code_login_connection_note_secure_state.*",
|
||||
"screen_qr_code_login_unknown_error_description",
|
||||
"screen_qr_code_login_invalid_scan_state_.*",
|
||||
"screen_qr_code_login_no_camera_permission_state_.*"
|
||||
"screen_qr_code_login_no_camera_permission_state_.*",
|
||||
"screen_qr_code_login_device_code_.*",
|
||||
"screen_qr_code_login_verify_code_loading"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue