Add catchingExceptions method to replace runCatching (#4797)

- Add `runCatchingExceptions` and `mapCatchingExceptions` to replace `runCatching` and `mapCatching`.
- Make `tryOrNull { ... }` catch only exceptions too.
- Apply the changes to the whole project.
- Add new Rust fakes for tests to handle the code that's now unblocked - previously it just threw an `UnsatisfiedLinkError` which we ignored.
- Add a new `detekt-rules` project with a `RunCatchingRule` to prevent `runCatching` and `mapCatching` usages.
This commit is contained in:
Jorge Martin Espinosa 2025-06-04 09:02:26 +02:00 committed by GitHub
parent 01d6012760
commit 58a3ea8b1f
144 changed files with 716 additions and 375 deletions

View file

@ -21,6 +21,7 @@ import im.vector.app.features.analytics.plan.CryptoSessionStateChange
import im.vector.app.features.analytics.plan.UserProperties import im.vector.app.features.analytics.plan.UserProperties
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
@ -125,7 +126,7 @@ class LoggedInPresenter @Inject constructor(
} }
// Force the user to log out if they were using the proxy sliding sync as it's no longer supported by the SDK // Force the user to log out if they were using the proxy sliding sync as it's no longer supported by the SDK
private suspend fun MatrixClient.needsForcedNativeSlidingSyncMigration(): Result<Boolean> = runCatching { private suspend fun MatrixClient.needsForcedNativeSlidingSyncMigration(): Result<Boolean> = runCatchingExceptions {
val currentSlidingSyncVersion = currentSlidingSyncVersion().getOrThrow() val currentSlidingSyncVersion = currentSlidingSyncVersion().getOrThrow()
currentSlidingSyncVersion == SlidingSyncVersion.Proxy currentSlidingSyncVersion == SlidingSyncVersion.Proxy
} }

View file

@ -50,6 +50,7 @@ allprojects {
} }
dependencies { dependencies {
detektPlugins("io.nlopez.compose.rules:detekt:0.4.22") detektPlugins("io.nlopez.compose.rules:detekt:0.4.22")
detektPlugins(project(":tests:detekt-rules"))
} }
tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach { tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {

View file

@ -10,6 +10,7 @@ package io.element.android.features.cachecleaner.impl
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.features.cachecleaner.api.CacheCleaner import io.element.android.features.cachecleaner.api.CacheCleaner
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.CacheDirectory import io.element.android.libraries.di.CacheDirectory
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -33,7 +34,7 @@ class DefaultCacheCleaner @Inject constructor(
override fun clearCache() { override fun clearCache() {
scope.launch(dispatchers.io) { scope.launch(dispatchers.io) {
runCatching { runCatchingExceptions {
SUBDIRS_TO_CLEANUP.forEach { SUBDIRS_TO_CLEANUP.forEach {
File(cacheDir.path, it).apply { File(cacheDir.path, it).apply {
if (exists()) { if (exists()) {

View file

@ -24,6 +24,7 @@ import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.IconCompat import androidx.core.graphics.drawable.IconCompat
import io.element.android.features.call.impl.R import io.element.android.features.call.impl.R
import io.element.android.features.call.impl.ui.ElementCallActivity import io.element.android.features.call.impl.ui.ElementCallActivity
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.push.api.notifications.ForegroundServiceType import io.element.android.libraries.push.api.notifications.ForegroundServiceType
import io.element.android.libraries.push.api.notifications.NotificationIdProvider import io.element.android.libraries.push.api.notifications.NotificationIdProvider
@ -78,7 +79,7 @@ class CallForegroundService : Service() {
} else { } else {
0 0
} }
runCatching { runCatchingExceptions {
ServiceCompat.startForeground(this, notificationId, notification, serviceType) ServiceCompat.startForeground(this, notificationId, notification, serviceType)
}.onFailure { }.onFailure {
Timber.e(it, "Failed to start ongoing call foreground service") Timber.e(it, "Failed to start ongoing call foreground service")

View file

@ -21,6 +21,7 @@ import io.element.android.features.call.api.CallType
import io.element.android.features.call.api.CurrentCall import io.element.android.features.call.api.CurrentCall
import io.element.android.features.call.impl.notifications.CallNotificationData import io.element.android.features.call.impl.notifications.CallNotificationData
import io.element.android.features.call.impl.notifications.RingingCallNotificationCreator import io.element.android.features.call.impl.notifications.RingingCallNotificationCreator
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
@ -218,7 +219,7 @@ class DefaultActiveCallManager @Inject constructor(
timestamp = notificationData.timestamp, timestamp = notificationData.timestamp,
textContent = notificationData.textContent, textContent = notificationData.textContent,
) ?: return ) ?: return
runCatching { runCatchingExceptions {
notificationManagerCompat.notify( notificationManagerCompat.notify(
NotificationIdProvider.getForegroundServiceNotificationId(ForegroundServiceType.INCOMING_CALL), NotificationIdProvider.getForegroundServiceNotificationId(ForegroundServiceType.INCOMING_CALL),
notification, notification,

View file

@ -8,6 +8,7 @@
package io.element.android.features.call.impl.utils package io.element.android.features.call.impl.utils
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
@ -33,7 +34,7 @@ class DefaultCallWidgetProvider @Inject constructor(
clientId: String, clientId: String,
languageTag: String?, languageTag: String?,
theme: String?, theme: String?,
): Result<CallWidgetProvider.GetWidgetResult> = runCatching { ): Result<CallWidgetProvider.GetWidgetResult> = runCatchingExceptions {
val matrixClient = matrixClientsProvider.getOrRestore(sessionId).getOrThrow() val matrixClient = matrixClientsProvider.getOrRestore(sessionId).getOrThrow()
val room = activeRoomsHolder.getActiveRoomMatching(sessionId, roomId) val room = activeRoomsHolder.getActiveRoomMatching(sessionId, roomId)
?: matrixClient.getJoinedRoom(roomId) ?: matrixClient.getJoinedRoom(roomId)

View file

@ -8,13 +8,14 @@
package io.element.android.features.call.impl.utils package io.element.android.features.call.impl.utils
import io.element.android.features.call.impl.data.WidgetMessage import io.element.android.features.call.impl.data.WidgetMessage
import io.element.android.libraries.core.extensions.runCatchingExceptions
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
object WidgetMessageSerializer { object WidgetMessageSerializer {
private val coder = Json { ignoreUnknownKeys = true } private val coder = Json { ignoreUnknownKeys = true }
fun deserialize(message: String): Result<WidgetMessage> { fun deserialize(message: String): Result<WidgetMessage> {
return runCatching { coder.decodeFromString(WidgetMessage.serializer(), message) } return runCatchingExceptions { coder.decodeFromString(WidgetMessage.serializer(), message) }
} }
fun serialize(message: WidgetMessage): String { fun serialize(message: WidgetMessage): String {

View file

@ -35,7 +35,7 @@ open class CreateRoomRootStateProvider : PreviewParameterProvider<CreateRoomRoot
} }
), ),
aCreateRoomRootState( aCreateRoomRootState(
startDmAction = AsyncAction.Failure(Throwable("error")), startDmAction = AsyncAction.Failure(RuntimeException("error")),
userListState = aMatrixUser().let { userListState = aMatrixUser().let {
aUserListState().copy( aUserListState().copy(
searchQuery = it.userId.value, searchQuery = it.userId.value,

View file

@ -14,8 +14,8 @@ import io.element.android.features.createroom.api.ConfirmingStartDmWithMatrixUse
import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_THROWABLE
import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.matrix.ui.components.aMatrixUser
import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.api.AnalyticsService
@ -70,13 +70,13 @@ class DefaultStartDMActionTest {
fun `when dm creation fails, assert state is updated with given error`() = runTest { fun `when dm creation fails, assert state is updated with given error`() = runTest {
val matrixClient = FakeMatrixClient().apply { val matrixClient = FakeMatrixClient().apply {
givenFindDmResult(null) givenFindDmResult(null)
givenCreateDmResult(Result.failure(A_THROWABLE)) givenCreateDmResult(Result.failure(AN_EXCEPTION))
} }
val analyticsService = FakeAnalyticsService() val analyticsService = FakeAnalyticsService()
val action = createStartDMAction(matrixClient, analyticsService) val action = createStartDMAction(matrixClient, analyticsService)
val state = mutableStateOf<AsyncAction<RoomId>>(AsyncAction.Uninitialized) val state = mutableStateOf<AsyncAction<RoomId>>(AsyncAction.Uninitialized)
action.execute(aMatrixUser(), true, state) action.execute(aMatrixUser(), true, state)
assertThat(state.value).isEqualTo(AsyncAction.Failure(A_THROWABLE)) assertThat(state.value).isEqualTo(AsyncAction.Failure(AN_EXCEPTION))
assertThat(analyticsService.capturedEvents).isEmpty() assertThat(analyticsService.capturedEvents).isEmpty()
} }

View file

@ -22,10 +22,10 @@ import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias
import io.element.android.libraries.matrix.api.room.alias.RoomAliasHelper import io.element.android.libraries.matrix.api.room.alias.RoomAliasHelper
import io.element.android.libraries.matrix.test.AN_AVATAR_URL import io.element.android.libraries.matrix.test.AN_AVATAR_URL
import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.A_MESSAGE import io.element.android.libraries.matrix.test.A_MESSAGE
import io.element.android.libraries.matrix.test.A_ROOM_ID 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_ROOM_NAME
import io.element.android.libraries.matrix.test.A_THROWABLE
import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.room.alias.FakeRoomAliasHelper import io.element.android.libraries.matrix.test.room.alias.FakeRoomAliasHelper
import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.matrix.ui.components.aMatrixUser
@ -274,7 +274,7 @@ class ConfigureBaseRoomPresenterTest {
createRoomDataStore.setAvatarUri(Uri.parse(AN_URI_FROM_GALLERY)) createRoomDataStore.setAvatarUri(Uri.parse(AN_URI_FROM_GALLERY))
skipItems(1) skipItems(1)
mediaPreProcessor.givenResult(Result.success(MediaUploadInfo.Image(mockk(), mockk(), mockk()))) mediaPreProcessor.givenResult(Result.success(MediaUploadInfo.Image(mockk(), mockk(), mockk())))
matrixClient.givenUploadMediaResult(Result.failure(A_THROWABLE)) matrixClient.givenUploadMediaResult(Result.failure(AN_EXCEPTION))
initialState.eventSink(ConfigureRoomEvents.CreateRoom) initialState.eventSink(ConfigureRoomEvents.CreateRoom)
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java)
@ -298,7 +298,7 @@ class ConfigureBaseRoomPresenterTest {
) )
presenter.test { presenter.test {
val initialState = initialState() val initialState = initialState()
val createRoomResult = Result.failure<RoomId>(A_THROWABLE) val createRoomResult = Result.failure<RoomId>(AN_EXCEPTION)
fakeMatrixClient.givenCreateRoomResult(createRoomResult) fakeMatrixClient.givenCreateRoomResult(createRoomResult)

View file

@ -24,8 +24,8 @@ import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_THROWABLE
import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.core.aBuildMeta
import io.element.android.libraries.usersearch.test.FakeUserRepository import io.element.android.libraries.usersearch.test.FakeUserRepository
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
@ -42,7 +42,7 @@ class CreateBaseRoomRootPresenterTest {
@Test @Test
fun `present - start DM action failure scenario`() = runTest { fun `present - start DM action failure scenario`() = runTest {
val startDMFailureResult = AsyncAction.Failure(A_THROWABLE) val startDMFailureResult = AsyncAction.Failure(AN_EXCEPTION)
val executeResult = lambdaRecorder<MatrixUser, Boolean, MutableState<AsyncAction<RoomId>>, Unit> { _, _, actionState -> val executeResult = lambdaRecorder<MatrixUser, Boolean, MutableState<AsyncAction<RoomId>>, Unit> { _, _, actionState ->
actionState.value = startDMFailureResult actionState.value = startDMFailureResult
} }

View file

@ -29,10 +29,10 @@ open class AcceptDeclineInviteStateProvider : PreviewParameterProvider<AcceptDec
), ),
), ),
anAcceptDeclineInviteState( anAcceptDeclineInviteState(
acceptAction = AsyncAction.Failure(Throwable("Error while accepting invite")), acceptAction = AsyncAction.Failure(RuntimeException("Error while accepting invite")),
), ),
anAcceptDeclineInviteState( anAcceptDeclineInviteState(
declineAction = AsyncAction.Failure(Throwable("Error while declining invite")), declineAction = AsyncAction.Failure(RuntimeException("Error while declining invite")),
), ),
) )
} }

View file

@ -69,7 +69,7 @@ class DefaultAcceptInviteTest {
fun `accept invite failure scenario`() = runTest { fun `accept invite failure scenario`() = runTest {
val joinRoomLambda = val joinRoomLambda =
lambdaRecorder<RoomIdOrAlias, List<String>, JoinedRoom.Trigger, Result<Unit>> { _, _, _ -> lambdaRecorder<RoomIdOrAlias, List<String>, JoinedRoom.Trigger, Result<Unit>> { _, _, _ ->
Result.failure(Throwable("Join room failed")) Result.failure(RuntimeException("Join room failed"))
} }
val acceptInvite = DefaultAcceptInvite( val acceptInvite = DefaultAcceptInvite(

View file

@ -89,7 +89,7 @@ open class KnockRequestsListStateProvider : PreviewParameterProvider<KnockReques
canBan = true, canBan = true,
), ),
currentAction = KnockRequestsAction.AcceptAll, currentAction = KnockRequestsAction.AcceptAll,
asyncAction = AsyncAction.Failure(Throwable("Failed to accept all")), asyncAction = AsyncAction.Failure(RuntimeException("Failed to accept all")),
), ),
aKnockRequestsListState( aKnockRequestsListState(
knockRequests = AsyncData.Success( knockRequests = AsyncData.Success(

View file

@ -108,7 +108,7 @@ class KnockRequestsListViewTest {
rule.setKnockRequestsListView( rule.setKnockRequestsListView(
aKnockRequestsListState( aKnockRequestsListState(
knockRequests = AsyncData.Success(knockRequests), knockRequests = AsyncData.Success(knockRequests),
asyncAction = AsyncAction.Failure(Throwable("Failed to accept all")), asyncAction = AsyncAction.Failure(RuntimeException("Failed to accept all")),
currentAction = KnockRequestsAction.AcceptAll, currentAction = KnockRequestsAction.AcceptAll,
eventSink = eventsRecorder, eventSink = eventsRecorder,
), ),
@ -124,7 +124,7 @@ class KnockRequestsListViewTest {
rule.setKnockRequestsListView( rule.setKnockRequestsListView(
aKnockRequestsListState( aKnockRequestsListState(
knockRequests = AsyncData.Success(knockRequests), knockRequests = AsyncData.Success(knockRequests),
asyncAction = AsyncAction.Failure(Throwable("Failed to accept all")), asyncAction = AsyncAction.Failure(RuntimeException("Failed to accept all")),
currentAction = KnockRequestsAction.AcceptAll, currentAction = KnockRequestsAction.AcceptAll,
eventSink = eventsRecorder, eventSink = eventsRecorder,
), ),

View file

@ -17,6 +17,7 @@ import io.element.android.features.licenses.impl.LicensesProvider
import io.element.android.features.licenses.impl.model.DependencyLicenseItem import io.element.android.features.licenses.impl.model.DependencyLicenseItem
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.extensions.runCatchingExceptions
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toPersistentList import kotlinx.collections.immutable.toPersistentList
import javax.inject.Inject import javax.inject.Inject
@ -34,7 +35,7 @@ class DependencyLicensesListPresenter @Inject constructor(
} }
var filter by remember { mutableStateOf("") } var filter by remember { mutableStateOf("") }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
runCatching { runCatchingExceptions {
licenses = AsyncData.Success(licensesProvider.provides().toPersistentList()) licenses = AsyncData.Success(licensesProvider.provides().toPersistentList())
}.onFailure { }.onFailure {
licenses = AsyncData.Failure(it) licenses = AsyncData.Failure(it)

View file

@ -15,6 +15,7 @@ import androidx.core.net.toUri
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.features.location.api.Location import io.element.android.features.location.api.Location
import io.element.android.libraries.androidutils.system.openAppSettingsPage import io.element.android.libraries.androidutils.system.openAppSettingsPage
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.ApplicationContext
import timber.log.Timber import timber.log.Timber
@ -26,7 +27,7 @@ class AndroidLocationActions @Inject constructor(
@ApplicationContext private val context: Context @ApplicationContext private val context: Context
) : LocationActions { ) : LocationActions {
override fun share(location: Location, label: String?) { override fun share(location: Location, label: String?) {
runCatching { runCatchingExceptions {
val uri = buildUrl(location, label).toUri() val uri = buildUrl(location, label).toUri()
val showMapsIntent = Intent(Intent.ACTION_VIEW).setData(uri) val showMapsIntent = Intent(Intent.ACTION_VIEW).setData(uri)
val chooserIntent = Intent.createChooser(showMapsIntent, null) val chooserIntent = Intent.createChooser(showMapsIntent, null)

View file

@ -13,6 +13,7 @@ import androidx.biometric.BiometricPrompt.CryptoObject
import androidx.biometric.BiometricPrompt.PromptInfo import androidx.biometric.BiometricPrompt.PromptInfo
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.cryptography.api.EncryptionDecryptionService import io.element.android.libraries.cryptography.api.EncryptionDecryptionService
import io.element.android.libraries.cryptography.api.SecretKeyRepository import io.element.android.libraries.cryptography.api.SecretKeyRepository
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
@ -119,7 +120,7 @@ private class AuthenticationCallback(
private fun Cipher?.isValid(): Boolean { private fun Cipher?.isValid(): Boolean {
if (this == null) return false if (this == null) return false
return runCatching { return runCatchingExceptions {
doFinal("biometric_challenge".toByteArray()) doFinal("biometric_challenge".toByteArray())
}.isSuccess }.isSuccess
} }

View file

@ -21,6 +21,7 @@ import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.core.extensions.flatMap import io.element.android.libraries.core.extensions.flatMap
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
@ -74,7 +75,7 @@ class CreateAccountPresenter @AssistedInject constructor(
private fun CoroutineScope.importSession(message: String, loggedInState: MutableState<AsyncAction<SessionId>>) = launch { private fun CoroutineScope.importSession(message: String, loggedInState: MutableState<AsyncAction<SessionId>>) = launch {
loggedInState.value = AsyncAction.Loading loggedInState.value = AsyncAction.Loading
runCatching { runCatchingExceptions {
messageParser.parse(message) messageParser.parse(message)
}.flatMap { externalSession -> }.flatMap { externalSession ->
authenticationService.importCreatedSession(externalSession) authenticationService.importCreatedSession(externalSession)

View file

@ -17,7 +17,7 @@ open class CreateAccountStateProvider : PreviewParameterProvider<CreateAccountSt
aCreateAccountState(), aCreateAccountState(),
aCreateAccountState(pageProgress = 33), aCreateAccountState(pageProgress = 33),
aCreateAccountState(createAction = AsyncAction.Loading), aCreateAccountState(createAction = AsyncAction.Loading),
aCreateAccountState(createAction = AsyncAction.Failure(Throwable("Failed to create account"))), aCreateAccountState(createAction = AsyncAction.Failure(RuntimeException("Failed to create account"))),
) )
} }

View file

@ -17,7 +17,7 @@ class ErrorFormatterTest {
// region loginError // region loginError
@Test @Test
fun `loginError - invalid unknown error returns unknown error message`() { fun `loginError - invalid unknown error returns unknown error message`() {
val error = Throwable("Some unknown error") val error = RuntimeException("Some unknown error")
assertThat(loginError(error)).isEqualTo(CommonStrings.error_unknown) assertThat(loginError(error)).isEqualTo(CommonStrings.error_unknown)
} }

View file

@ -17,7 +17,7 @@ import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.core.uri.ensureProtocol import io.element.android.libraries.core.uri.ensureProtocol
import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER_2 import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER_2
import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER_3 import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER_3
import io.element.android.libraries.matrix.test.A_THROWABLE import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.test import io.element.android.tests.testutils.test
@ -113,7 +113,7 @@ class ChooseAccountProviderPresenterTest {
} }
awaitItem().also { awaitItem().also {
assertThat(it.selectedAccountProvider).isEqualTo(accountProvider1) assertThat(it.selectedAccountProvider).isEqualTo(accountProvider1)
authenticationService.givenChangeServerError(A_THROWABLE) authenticationService.givenChangeServerError(AN_EXCEPTION)
it.eventSink(ChooseAccountProviderEvents.Continue) it.eventSink(ChooseAccountProviderEvents.Continue)
skipItems(1) // Loading skipItems(1) // Loading

View file

@ -22,9 +22,9 @@ import io.element.android.features.login.impl.web.FakeWebClientUrlForAuthenticat
import io.element.android.features.login.impl.web.WebClientUrlForAuthenticationRetriever import io.element.android.features.login.impl.web.WebClientUrlForAuthenticationRetriever
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.A_HOMESERVER import io.element.android.libraries.matrix.test.A_HOMESERVER
import io.element.android.libraries.matrix.test.A_HOMESERVER_OIDC import io.element.android.libraries.matrix.test.A_HOMESERVER_OIDC
import io.element.android.libraries.matrix.test.A_THROWABLE
import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService
import io.element.android.libraries.oidc.api.OidcAction import io.element.android.libraries.oidc.api.OidcAction
import io.element.android.libraries.oidc.api.OidcActionFlow import io.element.android.libraries.oidc.api.OidcActionFlow
@ -118,7 +118,7 @@ class ConfirmAccountProviderPresenterTest {
assertThat(successState.submitEnabled).isFalse() assertThat(successState.submitEnabled).isFalse()
assertThat(successState.loginMode).isInstanceOf(AsyncData.Success::class.java) assertThat(successState.loginMode).isInstanceOf(AsyncData.Success::class.java)
assertThat(successState.loginMode.dataOrNull()).isInstanceOf(LoginMode.Oidc::class.java) assertThat(successState.loginMode.dataOrNull()).isInstanceOf(LoginMode.Oidc::class.java)
authenticationService.givenOidcCancelError(A_THROWABLE) authenticationService.givenOidcCancelError(AN_EXCEPTION)
defaultOidcActionFlow.post(OidcAction.GoBack) defaultOidcActionFlow.post(OidcAction.GoBack)
val cancelFailureState = awaitItem() val cancelFailureState = awaitItem()
assertThat(cancelFailureState.loginMode).isInstanceOf(AsyncData.Failure::class.java) assertThat(cancelFailureState.loginMode).isInstanceOf(AsyncData.Failure::class.java)
@ -173,7 +173,7 @@ class ConfirmAccountProviderPresenterTest {
assertThat(successState.submitEnabled).isFalse() assertThat(successState.submitEnabled).isFalse()
assertThat(successState.loginMode).isInstanceOf(AsyncData.Success::class.java) assertThat(successState.loginMode).isInstanceOf(AsyncData.Success::class.java)
assertThat(successState.loginMode.dataOrNull()).isInstanceOf(LoginMode.Oidc::class.java) assertThat(successState.loginMode.dataOrNull()).isInstanceOf(LoginMode.Oidc::class.java)
authenticationService.givenLoginError(A_THROWABLE) authenticationService.givenLoginError(AN_EXCEPTION)
defaultOidcActionFlow.post(OidcAction.Success("aUrl")) defaultOidcActionFlow.post(OidcAction.Success("aUrl"))
val cancelLoadingState = awaitItem() val cancelLoadingState = awaitItem()
assertThat(cancelLoadingState.loginMode).isInstanceOf(AsyncData.Loading::class.java) assertThat(cancelLoadingState.loginMode).isInstanceOf(AsyncData.Loading::class.java)
@ -225,7 +225,7 @@ class ConfirmAccountProviderPresenterTest {
presenter.present() presenter.present()
}.test { }.test {
val initialState = awaitItem() val initialState = awaitItem()
authenticationService.givenChangeServerError(Throwable()) authenticationService.givenChangeServerError(RuntimeException())
initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue) initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue)
skipItems(1) // Loading skipItems(1) // Loading
val failureState = awaitItem() val failureState = awaitItem()
@ -246,7 +246,7 @@ class ConfirmAccountProviderPresenterTest {
val initialState = awaitItem() val initialState = awaitItem()
// Submit will return an error // Submit will return an error
authenticationService.givenChangeServerError(A_THROWABLE) authenticationService.givenChangeServerError(AN_EXCEPTION)
initialState.eventSink(ConfirmAccountProviderEvents.Continue) initialState.eventSink(ConfirmAccountProviderEvents.Continue)
skipItems(1) // Loading skipItems(1) // Loading

View file

@ -16,11 +16,14 @@ import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.api.auth.external.ExternalSession import io.element.android.libraries.matrix.api.auth.external.ExternalSession
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService
import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.core.aBuildMeta
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.lambda.value
@ -89,12 +92,16 @@ class CreateAccountPresenterTest {
defaultLoginUserStory.setLoginFlowIsDone(false) defaultLoginUserStory.setLoginFlowIsDone(false)
assertThat(defaultLoginUserStory.loginFlowIsDone.value).isFalse() assertThat(defaultLoginUserStory.loginFlowIsDone.value).isFalse()
val lambda = lambdaRecorder<String, ExternalSession> { _ -> anExternalSession() } val lambda = lambdaRecorder<String, ExternalSession> { _ -> anExternalSession() }
val sessionVerificationService = FakeSessionVerificationService()
val client = FakeMatrixClient(sessionVerificationService = sessionVerificationService)
val clientProvider = FakeMatrixClientProvider(getClient = { Result.success(client) })
val presenter = createPresenter( val presenter = createPresenter(
authenticationService = FakeMatrixAuthenticationService( authenticationService = FakeMatrixAuthenticationService(
importCreatedSessionLambda = { Result.success(A_SESSION_ID) } importCreatedSessionLambda = { Result.success(A_SESSION_ID) }
), ),
messageParser = FakeMessageParser(lambda), messageParser = FakeMessageParser(lambda),
defaultLoginUserStory = defaultLoginUserStory, defaultLoginUserStory = defaultLoginUserStory,
clientProvider = clientProvider,
) )
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -102,6 +109,7 @@ class CreateAccountPresenterTest {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink(CreateAccountEvents.OnMessageReceived("aMessage")) initialState.eventSink(CreateAccountEvents.OnMessageReceived("aMessage"))
assertThat(awaitItem().createAction.isLoading()).isTrue() assertThat(awaitItem().createAction.isLoading()).isTrue()
sessionVerificationService.emitVerifiedStatus(SessionVerifiedStatus.Verified)
assertThat(awaitItem().createAction.dataOrNull()).isEqualTo(A_SESSION_ID) assertThat(awaitItem().createAction.dataOrNull()).isEqualTo(A_SESSION_ID)
} }
lambda.assertions().isCalledOnce().with(value("aMessage")) lambda.assertions().isCalledOnce().with(value("aMessage"))

View file

@ -14,10 +14,10 @@ import io.element.android.features.login.impl.DefaultLoginUserStory
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.A_HOMESERVER import io.element.android.libraries.matrix.test.A_HOMESERVER
import io.element.android.libraries.matrix.test.A_PASSWORD import io.element.android.libraries.matrix.test.A_PASSWORD
import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.matrix.test.A_THROWABLE
import io.element.android.libraries.matrix.test.A_USER_NAME import io.element.android.libraries.matrix.test.A_USER_NAME
import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
@ -96,12 +96,12 @@ class LoginPasswordPresenterTest {
initialState.eventSink.invoke(LoginPasswordEvents.SetPassword(A_PASSWORD)) initialState.eventSink.invoke(LoginPasswordEvents.SetPassword(A_PASSWORD))
skipItems(1) skipItems(1)
val loginAndPasswordState = awaitItem() val loginAndPasswordState = awaitItem()
authenticationService.givenLoginError(A_THROWABLE) authenticationService.givenLoginError(AN_EXCEPTION)
loginAndPasswordState.eventSink.invoke(LoginPasswordEvents.Submit) loginAndPasswordState.eventSink.invoke(LoginPasswordEvents.Submit)
val submitState = awaitItem() val submitState = awaitItem()
assertThat(submitState.loginAction).isInstanceOf(AsyncData.Loading::class.java) assertThat(submitState.loginAction).isInstanceOf(AsyncData.Loading::class.java)
val loggedInState = awaitItem() val loggedInState = awaitItem()
assertThat(loggedInState.loginAction).isEqualTo(AsyncData.Failure<SessionId>(A_THROWABLE)) assertThat(loggedInState.loginAction).isEqualTo(AsyncData.Failure<SessionId>(AN_EXCEPTION))
} }
} }
@ -117,13 +117,13 @@ class LoginPasswordPresenterTest {
initialState.eventSink.invoke(LoginPasswordEvents.SetPassword(A_PASSWORD)) initialState.eventSink.invoke(LoginPasswordEvents.SetPassword(A_PASSWORD))
skipItems(1) skipItems(1)
val loginAndPasswordState = awaitItem() val loginAndPasswordState = awaitItem()
authenticationService.givenLoginError(A_THROWABLE) authenticationService.givenLoginError(AN_EXCEPTION)
loginAndPasswordState.eventSink.invoke(LoginPasswordEvents.Submit) loginAndPasswordState.eventSink.invoke(LoginPasswordEvents.Submit)
val submitState = awaitItem() val submitState = awaitItem()
assertThat(submitState.loginAction).isInstanceOf(AsyncData.Loading::class.java) assertThat(submitState.loginAction).isInstanceOf(AsyncData.Loading::class.java)
val loggedInState = awaitItem() val loggedInState = awaitItem()
// Check an error was returned // Check an error was returned
assertThat(loggedInState.loginAction).isEqualTo(AsyncData.Failure<SessionId>(A_THROWABLE)) assertThat(loggedInState.loginAction).isEqualTo(AsyncData.Failure<SessionId>(AN_EXCEPTION))
// Assert the error is then cleared // Assert the error is then cleared
loggedInState.eventSink(LoginPasswordEvents.ClearError) loggedInState.eventSink(LoginPasswordEvents.ClearError)
val clearedState = awaitItem() val clearedState = awaitItem()

View file

@ -24,9 +24,9 @@ import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER
import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER_2 import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER_2
import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER_3 import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER_3
import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
import io.element.android.libraries.matrix.test.A_LOGIN_HINT import io.element.android.libraries.matrix.test.A_LOGIN_HINT
import io.element.android.libraries.matrix.test.A_THROWABLE
import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService
import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.core.aBuildMeta
import io.element.android.libraries.oidc.api.OidcActionFlow import io.element.android.libraries.oidc.api.OidcActionFlow
@ -192,7 +192,7 @@ class OnBoardingPresenterTest {
skipItems(3) skipItems(3)
awaitItem().also { awaitItem().also {
assertThat(it.defaultAccountProvider).isEqualTo(A_HOMESERVER_URL) assertThat(it.defaultAccountProvider).isEqualTo(A_HOMESERVER_URL)
authenticationService.givenChangeServerError(A_THROWABLE) authenticationService.givenChangeServerError(AN_EXCEPTION)
it.eventSink(OnBoardingEvents.OnSignIn(A_HOMESERVER_URL)) it.eventSink(OnBoardingEvents.OnSignIn(A_HOMESERVER_URL))
skipItems(1) // Loading skipItems(1) // Loading

View file

@ -18,7 +18,7 @@ import io.element.android.libraries.matrix.api.encryption.BackupState
import io.element.android.libraries.matrix.api.encryption.BackupUploadState import io.element.android.libraries.matrix.api.encryption.BackupUploadState
import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.encryption.EncryptionService
import io.element.android.libraries.matrix.api.encryption.RecoveryState import io.element.android.libraries.matrix.api.encryption.RecoveryState
import io.element.android.libraries.matrix.test.A_THROWABLE import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
@ -165,7 +165,7 @@ class LogoutPresenterTest {
fun `present - logout with error then cancel`() = runTest { fun `present - logout with error then cancel`() = runTest {
val matrixClient = FakeMatrixClient().apply { val matrixClient = FakeMatrixClient().apply {
logoutLambda = { _, _ -> logoutLambda = { _, _ ->
throw A_THROWABLE throw AN_EXCEPTION
} }
} }
val presenter = createLogoutPresenter( val presenter = createLogoutPresenter(
@ -182,7 +182,7 @@ class LogoutPresenterTest {
val loadingState = awaitItem() val loadingState = awaitItem()
assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java) assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java)
val errorState = awaitItem() val errorState = awaitItem()
assertThat(errorState.logoutAction).isEqualTo(AsyncAction.Failure(A_THROWABLE)) assertThat(errorState.logoutAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION))
errorState.eventSink.invoke(LogoutEvents.CloseDialogs) errorState.eventSink.invoke(LogoutEvents.CloseDialogs)
val finalState = awaitItem() val finalState = awaitItem()
assertThat(finalState.logoutAction).isEqualTo(AsyncAction.Uninitialized) assertThat(finalState.logoutAction).isEqualTo(AsyncAction.Uninitialized)
@ -194,9 +194,7 @@ class LogoutPresenterTest {
val matrixClient = FakeMatrixClient().apply { val matrixClient = FakeMatrixClient().apply {
logoutLambda = { ignoreSdkError, _ -> logoutLambda = { ignoreSdkError, _ ->
if (!ignoreSdkError) { if (!ignoreSdkError) {
throw A_THROWABLE throw AN_EXCEPTION
} else {
null
} }
} }
} }
@ -214,7 +212,7 @@ class LogoutPresenterTest {
val loadingState = awaitItem() val loadingState = awaitItem()
assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java) assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java)
val errorState = awaitItem() val errorState = awaitItem()
assertThat(errorState.logoutAction).isEqualTo(AsyncAction.Failure(A_THROWABLE)) assertThat(errorState.logoutAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION))
errorState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = true)) errorState.eventSink.invoke(LogoutEvents.Logout(ignoreSdkError = true))
val loadingState2 = awaitItem() val loadingState2 = awaitItem()
assertThat(loadingState2.logoutAction).isInstanceOf(AsyncAction.Loading::class.java) assertThat(loadingState2.logoutAction).isInstanceOf(AsyncAction.Loading::class.java)

View file

@ -17,7 +17,7 @@ import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.encryption.BackupUploadState import io.element.android.libraries.matrix.api.encryption.BackupUploadState
import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.encryption.EncryptionService
import io.element.android.libraries.matrix.test.A_THROWABLE import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
@ -117,7 +117,7 @@ class DirectLogoutPresenterTest {
fun `present - logout with error then cancel`() = runTest { fun `present - logout with error then cancel`() = runTest {
val matrixClient = FakeMatrixClient().apply { val matrixClient = FakeMatrixClient().apply {
logoutLambda = { _, _ -> logoutLambda = { _, _ ->
throw A_THROWABLE throw AN_EXCEPTION
} }
} }
val presenter = createDirectLogoutPresenter( val presenter = createDirectLogoutPresenter(
@ -134,7 +134,7 @@ class DirectLogoutPresenterTest {
val loadingState = awaitItem() val loadingState = awaitItem()
assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java) assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java)
val errorState = awaitItem() val errorState = awaitItem()
assertThat(errorState.logoutAction).isEqualTo(AsyncAction.Failure(A_THROWABLE)) assertThat(errorState.logoutAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION))
errorState.eventSink.invoke(DirectLogoutEvents.CloseDialogs) errorState.eventSink.invoke(DirectLogoutEvents.CloseDialogs)
val finalState = awaitItem() val finalState = awaitItem()
assertThat(finalState.logoutAction).isEqualTo(AsyncAction.Uninitialized) assertThat(finalState.logoutAction).isEqualTo(AsyncAction.Uninitialized)
@ -146,9 +146,7 @@ class DirectLogoutPresenterTest {
val matrixClient = FakeMatrixClient().apply { val matrixClient = FakeMatrixClient().apply {
logoutLambda = { ignoreSdkError, _ -> logoutLambda = { ignoreSdkError, _ ->
if (!ignoreSdkError) { if (!ignoreSdkError) {
throw A_THROWABLE throw AN_EXCEPTION
} else {
null
} }
} }
} }
@ -166,7 +164,7 @@ class DirectLogoutPresenterTest {
val loadingState = awaitItem() val loadingState = awaitItem()
assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java) assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java)
val errorState = awaitItem() val errorState = awaitItem()
assertThat(errorState.logoutAction).isEqualTo(AsyncAction.Failure(A_THROWABLE)) assertThat(errorState.logoutAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION))
errorState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = true)) errorState.eventSink.invoke(DirectLogoutEvents.Logout(ignoreSdkError = true))
val loadingState2 = awaitItem() val loadingState2 = awaitItem()
assertThat(loadingState2.logoutAction).isInstanceOf(AsyncAction.Loading::class.java) assertThat(loadingState2.logoutAction).isInstanceOf(AsyncAction.Loading::class.java)

View file

@ -56,6 +56,7 @@ import io.element.android.libraries.androidutils.clipboard.ClipboardHelper
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
@ -387,7 +388,7 @@ class MessagesPresenter @AssistedInject constructor(
private fun CoroutineScope.reinviteOtherUser(inviteProgress: MutableState<AsyncData<Unit>>) = launch(dispatchers.io) { private fun CoroutineScope.reinviteOtherUser(inviteProgress: MutableState<AsyncData<Unit>>) = launch(dispatchers.io) {
inviteProgress.value = AsyncData.Loading() inviteProgress.value = AsyncData.Loading()
runCatching { runCatchingExceptions {
val memberList = when (val memberState = room.membersStateFlow.value) { val memberList = when (val memberState = room.membersStateFlow.value) {
is RoomMembersState.Ready -> memberState.roomMembers is RoomMembersState.Ready -> memberState.roomMembers
is RoomMembersState.Error -> memberState.prevRoomMembers.orEmpty() is RoomMembersState.Error -> memberState.prevRoomMembers.orEmpty()

View file

@ -27,6 +27,7 @@ import io.element.android.libraries.androidutils.file.safeDelete
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.coroutine.firstInstanceOf import io.element.android.libraries.core.coroutine.firstInstanceOf
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.di.annotations.SessionCoroutineScope import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.api.FeatureFlags
@ -240,7 +241,7 @@ class AttachmentsPreviewPresenter @AssistedInject constructor(
sendActionState: MutableState<SendActionState>, sendActionState: MutableState<SendActionState>,
dismissAfterSend: Boolean, dismissAfterSend: Boolean,
replyParameters: ReplyParameters?, replyParameters: ReplyParameters?,
) = runCatching { ) = runCatchingExceptions {
val context = coroutineContext val context = coroutineContext
val progressCallback = object : ProgressCallback { val progressCallback = object : ProgressCallback {
override fun onProgress(current: Long, total: Long) { override fun onProgress(current: Long, total: Long) {

View file

@ -24,7 +24,7 @@ open class ForwardMessagesStateProvider : PreviewParameterProvider<ForwardMessag
) )
), ),
aForwardMessagesState( aForwardMessagesState(
forwardAction = AsyncAction.Failure(Throwable("error")), forwardAction = AsyncAction.Failure(RuntimeException("error")),
), ),
) )
} }

View file

@ -41,6 +41,7 @@ import io.element.android.features.messages.impl.messagecomposer.suggestions.Sug
import io.element.android.features.messages.impl.timeline.TimelineController import io.element.android.features.messages.impl.timeline.TimelineController
import io.element.android.features.messages.impl.utils.TextPillificationHelper import io.element.android.features.messages.impl.utils.TextPillificationHelper
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlagService
@ -512,7 +513,7 @@ class MessageComposerPresenter @AssistedInject constructor(
private suspend fun sendMedia( private suspend fun sendMedia(
uri: Uri, uri: Uri,
mimeType: String, mimeType: String,
) = runCatching { ) = runCatchingExceptions {
mediaSender.sendMedia( mediaSender.sendMedia(
uri = uri, uri = uri,
mimeType = mimeType, mimeType = mimeType,

View file

@ -17,7 +17,7 @@ open class ReportMessageStateProvider : PreviewParameterProvider<ReportMessageSt
aReportMessageState(reason = "This user is making the chat very toxic."), aReportMessageState(reason = "This user is making the chat very toxic."),
aReportMessageState(reason = "This user is making the chat very toxic.", blockUser = true), aReportMessageState(reason = "This user is making the chat very toxic.", blockUser = true),
aReportMessageState(reason = "This user is making the chat very toxic.", blockUser = true, result = AsyncAction.Loading), aReportMessageState(reason = "This user is making the chat very toxic.", blockUser = true, result = AsyncAction.Loading),
aReportMessageState(reason = "This user is making the chat very toxic.", blockUser = true, result = AsyncAction.Failure(Throwable("error"))), aReportMessageState(reason = "This user is making the chat very toxic.", blockUser = true, result = AsyncAction.Failure(RuntimeException("error"))),
aReportMessageState(reason = "This user is making the chat very toxic.", blockUser = true, result = AsyncAction.Success(Unit)), aReportMessageState(reason = "This user is making the chat very toxic.", blockUser = true, result = AsyncAction.Success(Unit)),
// Add other states here // Add other states here
) )

View file

@ -62,11 +62,11 @@ import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransa
import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId
import io.element.android.libraries.matrix.test.AN_AVATAR_URL import io.element.android.libraries.matrix.test.AN_AVATAR_URL
import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.A_CAPTION import io.element.android.libraries.matrix.test.A_CAPTION
import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.matrix.test.A_SESSION_ID_2 import io.element.android.libraries.matrix.test.A_SESSION_ID_2
import io.element.android.libraries.matrix.test.A_THROWABLE
import io.element.android.libraries.matrix.test.A_USER_ID 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_ID_2
import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.core.aBuildMeta
@ -744,7 +744,7 @@ class MessagesPresenterTest {
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
), ),
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
inviteUserResult = { Result.failure(Throwable("Oops!")) }, inviteUserResult = { Result.failure(RuntimeException("Oops!")) },
) )
room.givenRoomMembersState( room.givenRoomMembersState(
RoomMembersState.Ready( RoomMembersState.Ready(
@ -892,7 +892,7 @@ class MessagesPresenterTest {
@Test @Test
fun `present - handle action pin`() = runTest { fun `present - handle action pin`() = runTest {
val successPinEventLambda = lambdaRecorder { _: EventId -> Result.success(true) } val successPinEventLambda = lambdaRecorder { _: EventId -> Result.success(true) }
val failurePinEventLambda = lambdaRecorder { _: EventId -> Result.failure<Boolean>(A_THROWABLE) } val failurePinEventLambda = lambdaRecorder { _: EventId -> Result.failure<Boolean>(AN_EXCEPTION) }
val analyticsService = FakeAnalyticsService() val analyticsService = FakeAnalyticsService()
val timeline = FakeTimeline() val timeline = FakeTimeline()
val room = FakeJoinedRoom( val room = FakeJoinedRoom(
@ -932,7 +932,7 @@ class MessagesPresenterTest {
@Test @Test
fun `present - handle action unpin`() = runTest { fun `present - handle action unpin`() = runTest {
val successUnpinEventLambda = lambdaRecorder { _: EventId -> Result.success(true) } val successUnpinEventLambda = lambdaRecorder { _: EventId -> Result.success(true) }
val failureUnpinEventLambda = lambdaRecorder { _: EventId -> Result.failure<Boolean>(A_THROWABLE) } val failureUnpinEventLambda = lambdaRecorder { _: EventId -> Result.failure<Boolean>(AN_EXCEPTION) }
val timeline = FakeTimeline() val timeline = FakeTimeline()
val analyticsService = FakeAnalyticsService() val analyticsService = FakeAnalyticsService()
val room = FakeJoinedRoom( val room = FakeJoinedRoom(

View file

@ -25,7 +25,7 @@ import io.element.android.libraries.matrix.api.sync.SyncService
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.A_THROWABLE import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.A_UNIQUE_ID import io.element.android.libraries.matrix.test.A_UNIQUE_ID
import io.element.android.libraries.matrix.test.room.FakeBaseRoom import io.element.android.libraries.matrix.test.room.FakeBaseRoom
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
@ -157,7 +157,7 @@ class PinnedMessagesListPresenterTest {
@Test @Test
fun `present - unpin event`() = runTest { fun `present - unpin event`() = runTest {
val successUnpinEventLambda = lambdaRecorder { _: EventId? -> Result.success(true) } val successUnpinEventLambda = lambdaRecorder { _: EventId? -> Result.success(true) }
val failureUnpinEventLambda = lambdaRecorder { _: EventId? -> Result.failure<Boolean>(A_THROWABLE) } val failureUnpinEventLambda = lambdaRecorder { _: EventId? -> Result.failure<Boolean>(AN_EXCEPTION) }
val pinnedEventsTimeline = createPinnedMessagesTimeline() val pinnedEventsTimeline = createPinnedMessagesTimeline()
val analyticsService = FakeAnalyticsService() val analyticsService = FakeAnalyticsService()
val room = FakeJoinedRoom( val room = FakeJoinedRoom(

View file

@ -12,6 +12,7 @@ import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.junit4.createComposeRule
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.features.messages.impl.utils.FakeMentionSpanFormatter import io.element.android.features.messages.impl.utils.FakeMentionSpanFormatter
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser
import io.element.android.libraries.textcomposer.mentions.MentionSpanProvider import io.element.android.libraries.textcomposer.mentions.MentionSpanProvider
@ -35,7 +36,7 @@ class DefaultHtmlConverterProviderTest {
@Test @Test
fun `calling provide without calling Update first should throw an exception`() { fun `calling provide without calling Update first should throw an exception`() {
val exception = runCatching { provider.provide() }.exceptionOrNull() val exception = runCatchingExceptions { provider.provide() }.exceptionOrNull()
assertThat(exception).isInstanceOf(IllegalStateException::class.java) assertThat(exception).isInstanceOf(IllegalStateException::class.java)
} }
@ -47,7 +48,7 @@ class DefaultHtmlConverterProviderTest {
provider.Update() provider.Update()
} }
} }
val htmlConverter = runCatching { provider.provide() }.getOrNull() val htmlConverter = runCatchingExceptions { provider.provide() }.getOrNull()
assertThat(htmlConverter).isNotNull() assertThat(htmlConverter).isNotNull()
} }

View file

@ -562,7 +562,7 @@ import kotlin.time.Duration.Companion.seconds
liveTimeline = FakeTimeline( liveTimeline = FakeTimeline(
timelineItems = flowOf(emptyList()), timelineItems = flowOf(emptyList()),
), ),
createTimelineResult = { Result.failure(Throwable("An error")) }, createTimelineResult = { Result.failure(RuntimeException("An error")) },
baseRoom = FakeBaseRoom(canUserSendMessageResult = { _, _ -> Result.success(true) }), baseRoom = FakeBaseRoom(canUserSendMessageResult = { _, _ -> Result.success(true) }),
) )
) )

View file

@ -9,6 +9,7 @@ package io.element.android.features.migration.impl.migrations
import android.content.Context import android.content.Context
import com.squareup.anvil.annotations.ContributesMultibinding import com.squareup.anvil.annotations.ContributesMultibinding
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.ApplicationContext
import javax.inject.Inject import javax.inject.Inject
@ -26,6 +27,6 @@ class AppMigration04 @Inject constructor(
override val order: Int = 4 override val order: Int = 4
override suspend fun migrate() { override suspend fun migrate() {
runCatching { context.getDatabasePath(NOTIFICATION_FILE_NAME).delete() } runCatchingExceptions { context.getDatabasePath(NOTIFICATION_FILE_NAME).delete() }
} }
} }

View file

@ -7,6 +7,7 @@
package io.element.android.features.poll.impl.data package io.element.android.features.poll.impl.data
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.poll.PollKind import io.element.android.libraries.matrix.api.poll.PollKind
import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.JoinedRoom
@ -22,7 +23,7 @@ class PollRepository @Inject constructor(
private val room: JoinedRoom, private val room: JoinedRoom,
private val timelineProvider: TimelineProvider, private val timelineProvider: TimelineProvider,
) { ) {
suspend fun getPoll(eventId: EventId): Result<PollContent> = runCatching { suspend fun getPoll(eventId: EventId): Result<PollContent> = runCatchingExceptions {
timelineProvider timelineProvider
.getActiveTimeline() .getActiveTimeline()
.timelineItems .timelineItems

View file

@ -21,7 +21,7 @@ class BlockedUsersStateProvider : PreviewParameterProvider<BlockedUsersState> {
aBlockedUsersState(blockedUsers = emptyList()), aBlockedUsersState(blockedUsers = emptyList()),
aBlockedUsersState(unblockUserAction = AsyncAction.ConfirmingNoParams), aBlockedUsersState(unblockUserAction = AsyncAction.ConfirmingNoParams),
aBlockedUsersState(unblockUserAction = AsyncAction.Loading), aBlockedUsersState(unblockUserAction = AsyncAction.Loading),
aBlockedUsersState(unblockUserAction = AsyncAction.Failure(Throwable("Failed to unblock user"))), aBlockedUsersState(unblockUserAction = AsyncAction.Failure(RuntimeException("Failed to unblock user"))),
aBlockedUsersState(unblockUserAction = AsyncAction.Success(Unit)), aBlockedUsersState(unblockUserAction = AsyncAction.Success(Unit)),
) )
} }

View file

@ -29,6 +29,7 @@ import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runCatchingUpdatingState import io.element.android.libraries.architecture.runCatchingUpdatingState
import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.core.meta.BuildType import io.element.android.libraries.core.meta.BuildType
import io.element.android.libraries.featureflag.api.Feature import io.element.android.libraries.featureflag.api.Feature
@ -201,8 +202,8 @@ class DeveloperSettingsPresenter @Inject constructor(
} }
private fun customElementCallUrlValidator(url: String?): Boolean { private fun customElementCallUrlValidator(url: String?): Boolean {
return runCatching { return runCatchingExceptions {
if (url.isNullOrEmpty()) return@runCatching if (url.isNullOrEmpty()) return@runCatchingExceptions
val parsedUrl = URL(url) val parsedUrl = URL(url)
if (parsedUrl.protocol !in listOf("http", "https")) error("Incorrect protocol") if (parsedUrl.protocol !in listOf("http", "https")) error("Incorrect protocol")
if (parsedUrl.host.isNullOrBlank()) error("Missing host") if (parsedUrl.host.isNullOrBlank()) error("Missing host")

View file

@ -22,6 +22,7 @@ import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runUpdatingStateNoSuccess import io.element.android.libraries.architecture.runUpdatingStateNoSuccess
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
@ -209,7 +210,7 @@ class NotificationSettingsPresenter @Inject constructor(
} }
private fun CoroutineScope.fixConfigurationMismatch(target: MutableState<NotificationSettingsState.MatrixSettings>) = launch { private fun CoroutineScope.fixConfigurationMismatch(target: MutableState<NotificationSettingsState.MatrixSettings>) = launch {
runCatching { runCatchingExceptions {
val groupDefaultMode = notificationSettingsService.getDefaultRoomNotificationMode(isEncrypted = false, isOneToOne = false).getOrThrow() val groupDefaultMode = notificationSettingsService.getDefaultRoomNotificationMode(isEncrypted = false, isOneToOne = false).getOrThrow()
val encryptedGroupDefaultMode = notificationSettingsService.getDefaultRoomNotificationMode(isEncrypted = true, isOneToOne = false).getOrThrow() val encryptedGroupDefaultMode = notificationSettingsService.getDefaultRoomNotificationMode(isEncrypted = true, isOneToOne = false).getOrThrow()

View file

@ -23,10 +23,10 @@ open class NotificationSettingsStateProvider : PreviewParameterProvider<Notifica
aValidNotificationSettingsState(systemNotificationsEnabled = false), aValidNotificationSettingsState(systemNotificationsEnabled = false),
aValidNotificationSettingsState(), aValidNotificationSettingsState(),
aValidNotificationSettingsState(changeNotificationSettingAction = AsyncAction.Loading), aValidNotificationSettingsState(changeNotificationSettingAction = AsyncAction.Loading),
aValidNotificationSettingsState(changeNotificationSettingAction = AsyncAction.Failure(Throwable("error"))), aValidNotificationSettingsState(changeNotificationSettingAction = AsyncAction.Failure(RuntimeException("error"))),
aValidNotificationSettingsState( aValidNotificationSettingsState(
availablePushDistributors = listOf(aDistributor("Firebase")), availablePushDistributors = listOf(aDistributor("Firebase")),
changeNotificationSettingAction = AsyncAction.Failure(Throwable("error")), changeNotificationSettingAction = AsyncAction.Failure(RuntimeException("error")),
), ),
aValidNotificationSettingsState(availablePushDistributors = listOf(aDistributor("Firebase"))), aValidNotificationSettingsState(availablePushDistributors = listOf(aDistributor("Firebase"))),
aValidNotificationSettingsState(showChangePushProviderDialog = true), aValidNotificationSettingsState(showChangePushProviderDialog = true),

View file

@ -21,7 +21,7 @@ open class EditDefaultNotificationSettingStateProvider : PreviewParameterProvide
anEditDefaultNotificationSettingsState(), anEditDefaultNotificationSettingsState(),
anEditDefaultNotificationSettingsState(isOneToOne = true), anEditDefaultNotificationSettingsState(isOneToOne = true),
anEditDefaultNotificationSettingsState(changeNotificationSettingAction = AsyncAction.Loading), anEditDefaultNotificationSettingsState(changeNotificationSettingAction = AsyncAction.Loading),
anEditDefaultNotificationSettingsState(changeNotificationSettingAction = AsyncAction.Failure(Throwable("error"))), anEditDefaultNotificationSettingsState(changeNotificationSettingAction = AsyncAction.Failure(RuntimeException("error"))),
anEditDefaultNotificationSettingsState(displayMentionsOnlyDisclaimer = true), anEditDefaultNotificationSettingsState(displayMentionsOnlyDisclaimer = true),
) )
} }

View file

@ -26,6 +26,7 @@ import io.element.android.libraries.androidutils.file.TemporaryUriDeleter
import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runCatchingUpdatingState import io.element.android.libraries.architecture.runCatchingUpdatingState
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser
@ -168,7 +169,7 @@ class EditUserProfilePresenter @AssistedInject constructor(
} }
private suspend fun updateAvatar(avatarUri: Uri?): Result<Unit> { private suspend fun updateAvatar(avatarUri: Uri?): Result<Unit> {
return runCatching { return runCatchingExceptions {
if (avatarUri != null) { if (avatarUri != null) {
val preprocessed = mediaPreProcessor.process( val preprocessed = mediaPreProcessor.process(
uri = avatarUri, uri = avatarUri,

View file

@ -14,7 +14,7 @@ import com.google.common.truth.Truth.assertThat
import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingPresenter import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingPresenter
import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingStateEvents import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingStateEvents
import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.room.RoomNotificationMode
import io.element.android.libraries.matrix.test.A_THROWABLE import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
import io.element.android.libraries.matrix.test.room.aRoomSummary import io.element.android.libraries.matrix.test.room.aRoomSummary
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
@ -81,7 +81,7 @@ class EditDefaultNotificationSettingsPresenterTest {
fun `present - edit default notification setting failed`() = runTest { fun `present - edit default notification setting failed`() = runTest {
val notificationSettingsService = FakeNotificationSettingsService() val notificationSettingsService = FakeNotificationSettingsService()
val presenter = createEditDefaultNotificationSettingPresenter(notificationSettingsService) val presenter = createEditDefaultNotificationSettingPresenter(notificationSettingsService)
notificationSettingsService.givenSetDefaultNotificationModeError(A_THROWABLE) notificationSettingsService.givenSetDefaultNotificationModeError(AN_EXCEPTION)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {

View file

@ -16,7 +16,7 @@ import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermiss
import io.element.android.libraries.fullscreenintent.api.aFullScreenIntentPermissionsState import io.element.android.libraries.fullscreenintent.api.aFullScreenIntentPermissionsState
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.room.RoomNotificationMode
import io.element.android.libraries.matrix.test.A_THROWABLE import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
import io.element.android.libraries.push.api.PushService import io.element.android.libraries.push.api.PushService
@ -208,7 +208,7 @@ class NotificationSettingsPresenterTest {
fun `present - clear notification settings change error`() = runTest { fun `present - clear notification settings change error`() = runTest {
val notificationSettingsService = FakeNotificationSettingsService() val notificationSettingsService = FakeNotificationSettingsService()
val presenter = createNotificationSettingsPresenter(notificationSettingsService) val presenter = createNotificationSettingsPresenter(notificationSettingsService)
notificationSettingsService.givenSetAtRoomError(A_THROWABLE) notificationSettingsService.givenSetAtRoomError(AN_EXCEPTION)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {

View file

@ -393,7 +393,7 @@ class EditUserProfilePresenterTest {
), ),
) )
fakePickerProvider.givenResult(anotherAvatarUri) fakePickerProvider.givenResult(anotherAvatarUri)
fakeMediaPreProcessor.givenResult(Result.failure(Throwable("Oh no"))) fakeMediaPreProcessor.givenResult(Result.failure(RuntimeException("Oh no")))
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -410,7 +410,7 @@ class EditUserProfilePresenterTest {
fun `present - sets save action to failure if name update fails`() = runTest { fun `present - sets save action to failure if name update fails`() = runTest {
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
val matrixClient = FakeMatrixClient().apply { val matrixClient = FakeMatrixClient().apply {
givenSetDisplayNameResult(Result.failure(Throwable("!"))) givenSetDisplayNameResult(Result.failure(RuntimeException("!")))
} }
saveAndAssertFailure(user, matrixClient, EditUserProfileEvents.UpdateDisplayName("New name")) saveAndAssertFailure(user, matrixClient, EditUserProfileEvents.UpdateDisplayName("New name"))
} }
@ -419,7 +419,7 @@ class EditUserProfilePresenterTest {
fun `present - sets save action to failure if removing avatar fails`() = runTest { fun `present - sets save action to failure if removing avatar fails`() = runTest {
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
val matrixClient = FakeMatrixClient().apply { val matrixClient = FakeMatrixClient().apply {
givenRemoveAvatarResult(Result.failure(Throwable("!"))) givenRemoveAvatarResult(Result.failure(RuntimeException("!")))
} }
saveAndAssertFailure(user, matrixClient, EditUserProfileEvents.HandleAvatarAction(AvatarAction.Remove)) saveAndAssertFailure(user, matrixClient, EditUserProfileEvents.HandleAvatarAction(AvatarAction.Remove))
} }
@ -429,7 +429,7 @@ class EditUserProfilePresenterTest {
givenPickerReturnsFile() givenPickerReturnsFile()
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
val matrixClient = FakeMatrixClient().apply { val matrixClient = FakeMatrixClient().apply {
givenUploadAvatarResult(Result.failure(Throwable("!"))) givenUploadAvatarResult(Result.failure(RuntimeException("!")))
} }
saveAndAssertFailure(user, matrixClient, EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) saveAndAssertFailure(user, matrixClient, EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
} }
@ -439,7 +439,7 @@ class EditUserProfilePresenterTest {
givenPickerReturnsFile() givenPickerReturnsFile()
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
val matrixClient = FakeMatrixClient().apply { val matrixClient = FakeMatrixClient().apply {
givenSetDisplayNameResult(Result.failure(Throwable("!"))) givenSetDisplayNameResult(Result.failure(RuntimeException("!")))
} }
val presenter = createEditUserProfilePresenter(matrixUser = user, matrixClient = matrixClient) val presenter = createEditUserProfilePresenter(matrixUser = user, matrixClient = matrixClient)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {

View file

@ -308,7 +308,7 @@ class DefaultBugReporter @Inject constructor(
*/ */
private fun getLogFiles(): List<File> { private fun getLogFiles(): List<File> {
return tryOrNull( return tryOrNull(
onError = { Timber.e(it, "## getLogFiles() failed") } onException = { Timber.e(it, "## getLogFiles() failed") }
) { ) {
val logDirectory = logDirectory() val logDirectory = logDirectory()
logDirectory.listFiles()?.toList() logDirectory.listFiles()?.toList()

View file

@ -24,6 +24,7 @@ import io.element.android.libraries.androidutils.file.TemporaryUriDeleter
import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runCatchingUpdatingState import io.element.android.libraries.architecture.runCatchingUpdatingState
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.api.room.StateEventType import io.element.android.libraries.matrix.api.room.StateEventType
@ -216,7 +217,7 @@ class RoomDetailsEditPresenter @Inject constructor(
} }
private suspend fun updateAvatar(avatarUri: Uri?): Result<Unit> { private suspend fun updateAvatar(avatarUri: Uri?): Result<Unit> {
return runCatching { return runCatchingExceptions {
if (avatarUri != null) { if (avatarUri != null) {
val preprocessed = mediaPreProcessor.process( val preprocessed = mediaPreProcessor.process(
uri = avatarUri, uri = avatarUri,

View file

@ -27,7 +27,7 @@ open class RoomDetailsEditStateProvider : PreviewParameterProvider<RoomDetailsEd
aRoomDetailsEditState(canChangeName = true, canChangeTopic = false, canChangeAvatar = true, saveButtonEnabled = false), aRoomDetailsEditState(canChangeName = true, canChangeTopic = false, canChangeAvatar = true, saveButtonEnabled = false),
aRoomDetailsEditState(canChangeName = false, canChangeTopic = true, canChangeAvatar = false, saveButtonEnabled = false), aRoomDetailsEditState(canChangeName = false, canChangeTopic = true, canChangeAvatar = false, saveButtonEnabled = false),
aRoomDetailsEditState(saveAction = AsyncAction.Loading), aRoomDetailsEditState(saveAction = AsyncAction.Loading),
aRoomDetailsEditState(saveAction = AsyncAction.Failure(Throwable("Whelp"))), aRoomDetailsEditState(saveAction = AsyncAction.Failure(RuntimeException("Whelp"))),
) )
} }

View file

@ -19,9 +19,9 @@ internal class RoomNotificationSettingsStateProvider : PreviewParameterProvider<
aRoomNotificationSettingsState(), aRoomNotificationSettingsState(),
aRoomNotificationSettingsState(isDefault = false), aRoomNotificationSettingsState(isDefault = false),
aRoomNotificationSettingsState(setNotificationSettingAction = AsyncAction.Loading), aRoomNotificationSettingsState(setNotificationSettingAction = AsyncAction.Loading),
aRoomNotificationSettingsState(setNotificationSettingAction = AsyncAction.Failure(Throwable("error"))), aRoomNotificationSettingsState(setNotificationSettingAction = AsyncAction.Failure(RuntimeException("error"))),
aRoomNotificationSettingsState(restoreDefaultAction = AsyncAction.Loading), aRoomNotificationSettingsState(restoreDefaultAction = AsyncAction.Loading),
aRoomNotificationSettingsState(restoreDefaultAction = AsyncAction.Failure(Throwable("error"))), aRoomNotificationSettingsState(restoreDefaultAction = AsyncAction.Failure(RuntimeException("error"))),
aRoomNotificationSettingsState(displayMentionsOnlyDisclaimer = true) aRoomNotificationSettingsState(displayMentionsOnlyDisclaimer = true)
) )

View file

@ -258,7 +258,7 @@ class RoomDetailsPresenterTest {
@Test @Test
fun `present - initial state when canInvite errors`() = runTest { fun `present - initial state when canInvite errors`() = runTest {
val room = aJoinedRoom( val room = aJoinedRoom(
canInviteResult = { Result.failure(Throwable("Whoops")) }, canInviteResult = { Result.failure(RuntimeException("Whoops")) },
canUserJoinCallResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) },
canSendStateResult = { _, _ -> Result.success(true) }, canSendStateResult = { _, _ -> Result.success(true) },
) )
@ -277,7 +277,7 @@ class RoomDetailsPresenterTest {
when (stateEventType) { when (stateEventType) {
StateEventType.ROOM_TOPIC -> Result.success(true) StateEventType.ROOM_TOPIC -> Result.success(true)
StateEventType.ROOM_NAME -> Result.success(false) StateEventType.ROOM_NAME -> Result.success(false)
else -> Result.failure(Throwable("Whelp")) else -> Result.failure(RuntimeException("Whelp"))
} }
}, },
canBanResult = { Result.success(false) }, canBanResult = { Result.success(false) },
@ -306,7 +306,7 @@ class RoomDetailsPresenterTest {
StateEventType.ROOM_TOPIC, StateEventType.ROOM_TOPIC,
StateEventType.ROOM_NAME, StateEventType.ROOM_NAME,
StateEventType.ROOM_AVATAR -> Result.success(true) StateEventType.ROOM_AVATAR -> Result.success(true)
else -> Result.failure(Throwable("Whelp")) else -> Result.failure(RuntimeException("Whelp"))
} }
}, },
canKickResult = { Result.success(false) }, canKickResult = { Result.success(false) },
@ -357,7 +357,7 @@ class RoomDetailsPresenterTest {
StateEventType.ROOM_AVATAR, StateEventType.ROOM_AVATAR,
StateEventType.ROOM_TOPIC, StateEventType.ROOM_TOPIC,
StateEventType.ROOM_NAME -> Result.success(true) StateEventType.ROOM_NAME -> Result.success(true)
else -> Result.failure(Throwable("Whelp")) else -> Result.failure(RuntimeException("Whelp"))
} }
}, },
userDisplayNameResult = { Result.success(A_USER_NAME) }, userDisplayNameResult = { Result.success(A_USER_NAME) },
@ -403,7 +403,7 @@ class RoomDetailsPresenterTest {
StateEventType.ROOM_TOPIC, StateEventType.ROOM_TOPIC,
StateEventType.ROOM_NAME, StateEventType.ROOM_NAME,
StateEventType.ROOM_AVATAR -> Result.success(true) StateEventType.ROOM_AVATAR -> Result.success(true)
else -> Result.failure(Throwable("Whelp")) else -> Result.failure(RuntimeException("Whelp"))
} }
}, },
canKickResult = { canKickResult = {
@ -436,7 +436,7 @@ class RoomDetailsPresenterTest {
StateEventType.ROOM_TOPIC, StateEventType.ROOM_TOPIC,
StateEventType.ROOM_NAME, StateEventType.ROOM_NAME,
StateEventType.ROOM_AVATAR -> Result.success(false) StateEventType.ROOM_AVATAR -> Result.success(false)
else -> Result.failure(Throwable("Whelp")) else -> Result.failure(RuntimeException("Whelp"))
} }
}, },
canBanResult = { canBanResult = {
@ -468,7 +468,7 @@ class RoomDetailsPresenterTest {
StateEventType.ROOM_AVATAR, StateEventType.ROOM_AVATAR,
StateEventType.ROOM_NAME -> Result.success(true) StateEventType.ROOM_NAME -> Result.success(true)
StateEventType.ROOM_TOPIC -> Result.success(false) StateEventType.ROOM_TOPIC -> Result.success(false)
else -> Result.failure(Throwable("Whelp")) else -> Result.failure(RuntimeException("Whelp"))
} }
}, },
canKickResult = { canKickResult = {
@ -500,7 +500,7 @@ class RoomDetailsPresenterTest {
StateEventType.ROOM_AVATAR, StateEventType.ROOM_AVATAR,
StateEventType.ROOM_TOPIC, StateEventType.ROOM_TOPIC,
StateEventType.ROOM_NAME -> Result.success(true) StateEventType.ROOM_NAME -> Result.success(true)
else -> Result.failure(Throwable("Whelp")) else -> Result.failure(RuntimeException("Whelp"))
} }
}, },
canKickResult = { canKickResult = {

View file

@ -124,7 +124,7 @@ class RoomDetailsEditPresenterTest {
when (stateEventType) { when (stateEventType) {
StateEventType.ROOM_NAME -> Result.success(true) StateEventType.ROOM_NAME -> Result.success(true)
StateEventType.ROOM_AVATAR -> Result.success(false) StateEventType.ROOM_AVATAR -> Result.success(false)
StateEventType.ROOM_TOPIC -> Result.failure(Throwable("Oops")) StateEventType.ROOM_TOPIC -> Result.failure(RuntimeException("Oops"))
else -> lambdaError() else -> lambdaError()
} }
}, },
@ -157,7 +157,7 @@ class RoomDetailsEditPresenterTest {
when (stateEventType) { when (stateEventType) {
StateEventType.ROOM_NAME -> Result.success(false) StateEventType.ROOM_NAME -> Result.success(false)
StateEventType.ROOM_AVATAR -> Result.success(true) StateEventType.ROOM_AVATAR -> Result.success(true)
StateEventType.ROOM_TOPIC -> Result.failure(Throwable("Oops")) StateEventType.ROOM_TOPIC -> Result.failure(RuntimeException("Oops"))
else -> lambdaError() else -> lambdaError()
} }
} }
@ -188,7 +188,7 @@ class RoomDetailsEditPresenterTest {
canSendStateResult = { _, stateEventType -> canSendStateResult = { _, stateEventType ->
when (stateEventType) { when (stateEventType) {
StateEventType.ROOM_NAME -> Result.success(false) StateEventType.ROOM_NAME -> Result.success(false)
StateEventType.ROOM_AVATAR -> Result.failure(Throwable("Oops")) StateEventType.ROOM_AVATAR -> Result.failure(RuntimeException("Oops"))
StateEventType.ROOM_TOPIC -> Result.success(true) StateEventType.ROOM_TOPIC -> Result.success(true)
else -> lambdaError() else -> lambdaError()
} }
@ -559,7 +559,7 @@ class RoomDetailsEditPresenterTest {
canSendStateResult = { _, _ -> Result.success(true) } canSendStateResult = { _, _ -> Result.success(true) }
) )
fakePickerProvider.givenResult(anotherAvatarUri) fakePickerProvider.givenResult(anotherAvatarUri)
fakeMediaPreProcessor.givenResult(Result.failure(Throwable("Oh no"))) fakeMediaPreProcessor.givenResult(Result.failure(RuntimeException("Oh no")))
val deleteCallback = lambdaRecorder<Uri?, Unit> {} val deleteCallback = lambdaRecorder<Uri?, Unit> {}
val presenter = createRoomDetailsEditPresenter( val presenter = createRoomDetailsEditPresenter(
room = room, room = room,
@ -580,7 +580,7 @@ class RoomDetailsEditPresenterTest {
topic = "My topic", topic = "My topic",
displayName = "Name", displayName = "Name",
avatarUrl = AN_AVATAR_URL, avatarUrl = AN_AVATAR_URL,
setNameResult = { Result.failure(Throwable("!")) }, setNameResult = { Result.failure(RuntimeException("!")) },
canSendStateResult = { _, _ -> Result.success(true) } canSendStateResult = { _, _ -> Result.success(true) }
) )
saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomName("New name"), deleteCallbackNumberOfInvocation = 1) saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomName("New name"), deleteCallbackNumberOfInvocation = 1)
@ -592,7 +592,7 @@ class RoomDetailsEditPresenterTest {
topic = "My topic", topic = "My topic",
displayName = "Name", displayName = "Name",
avatarUrl = AN_AVATAR_URL, avatarUrl = AN_AVATAR_URL,
setTopicResult = { Result.failure(Throwable("!")) }, setTopicResult = { Result.failure(RuntimeException("!")) },
canSendStateResult = { _, _ -> Result.success(true) } canSendStateResult = { _, _ -> Result.success(true) }
) )
saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomTopic("New topic"), deleteCallbackNumberOfInvocation = 1) saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomTopic("New topic"), deleteCallbackNumberOfInvocation = 1)
@ -604,7 +604,7 @@ class RoomDetailsEditPresenterTest {
topic = "My topic", topic = "My topic",
displayName = "Name", displayName = "Name",
avatarUrl = AN_AVATAR_URL, avatarUrl = AN_AVATAR_URL,
removeAvatarResult = { Result.failure(Throwable("!")) }, removeAvatarResult = { Result.failure(RuntimeException("!")) },
canSendStateResult = { _, _ -> Result.success(true) } canSendStateResult = { _, _ -> Result.success(true) }
) )
saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove), deleteCallbackNumberOfInvocation = 2) saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove), deleteCallbackNumberOfInvocation = 2)
@ -617,7 +617,7 @@ class RoomDetailsEditPresenterTest {
topic = "My topic", topic = "My topic",
displayName = "Name", displayName = "Name",
avatarUrl = AN_AVATAR_URL, avatarUrl = AN_AVATAR_URL,
updateAvatarResult = { _, _ -> Result.failure(Throwable("!")) }, updateAvatarResult = { _, _ -> Result.failure(RuntimeException("!")) },
canSendStateResult = { _, _ -> Result.success(true) } canSendStateResult = { _, _ -> Result.success(true) }
) )
saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto), deleteCallbackNumberOfInvocation = 2) saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto), deleteCallbackNumberOfInvocation = 2)
@ -630,7 +630,7 @@ class RoomDetailsEditPresenterTest {
topic = "My topic", topic = "My topic",
displayName = "Name", displayName = "Name",
avatarUrl = AN_AVATAR_URL, avatarUrl = AN_AVATAR_URL,
setTopicResult = { Result.failure(Throwable("!")) }, setTopicResult = { Result.failure(RuntimeException("!")) },
canSendStateResult = { _, _ -> Result.success(true) } canSendStateResult = { _, _ -> Result.success(true) }
) )
val deleteCallback = lambdaRecorder<Uri?, Unit> {} val deleteCallback = lambdaRecorder<Uri?, Unit> {}

View file

@ -205,7 +205,7 @@ class RoomDetailsEditViewTest {
rule.setRoomDetailsEditView( rule.setRoomDetailsEditView(
aRoomDetailsEditState( aRoomDetailsEditState(
eventSink = eventsRecorder, eventSink = eventsRecorder,
saveAction = AsyncAction.Failure(Throwable("Whelp")), saveAction = AsyncAction.Failure(RuntimeException("Whelp")),
), ),
) )
rule.clickOn(CommonStrings.action_ok) rule.clickOn(CommonStrings.action_ok)

View file

@ -186,7 +186,7 @@ class RoomMemberListPresenterTest {
val presenter = createPresenter( val presenter = createPresenter(
joinedRoom = FakeJoinedRoom( joinedRoom = FakeJoinedRoom(
baseRoom = FakeBaseRoom( baseRoom = FakeBaseRoom(
canInviteResult = { Result.failure(Throwable("Eek")) }, canInviteResult = { Result.failure(RuntimeException("Eek")) },
updateMembersResult = { Result.success(Unit) } updateMembersResult = { Result.success(Unit) }
) )
) )

View file

@ -78,8 +78,8 @@ class RoomMemberDetailsPresenterTest {
avatarUrl = "Alice Avatar url", avatarUrl = "Alice Avatar url",
) )
val room = aJoinedRoom( val room = aJoinedRoom(
userDisplayNameResult = { Result.failure(Throwable()) }, userDisplayNameResult = { Result.failure(RuntimeException()) },
userAvatarUrlResult = { Result.failure(Throwable()) }, userAvatarUrlResult = { Result.failure(RuntimeException()) },
getUpdatedMemberResult = { Result.failure(AN_EXCEPTION) }, getUpdatedMemberResult = { Result.failure(AN_EXCEPTION) },
).apply { ).apply {
givenRoomMembersState(RoomMembersState.Ready(persistentListOf(roomMember))) givenRoomMembersState(RoomMembersState.Ready(persistentListOf(roomMember)))

View file

@ -13,8 +13,8 @@ import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.features.roomdetails.impl.aJoinedRoom import io.element.android.features.roomdetails.impl.aJoinedRoom
import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.room.RoomNotificationMode
import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_THROWABLE
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
import io.element.android.tests.testutils.awaitLastSequentialItem import io.element.android.tests.testutils.awaitLastSequentialItem
@ -71,7 +71,7 @@ class RoomNotificationSettingsPresenterTest {
@Test @Test
fun `present - notification settings set custom failed`() = runTest { fun `present - notification settings set custom failed`() = runTest {
val notificationSettingsService = FakeNotificationSettingsService() val notificationSettingsService = FakeNotificationSettingsService()
notificationSettingsService.givenSetNotificationModeError(A_THROWABLE) notificationSettingsService.givenSetNotificationModeError(AN_EXCEPTION)
val presenter = createRoomNotificationSettingsPresenter(notificationSettingsService) val presenter = createRoomNotificationSettingsPresenter(notificationSettingsService)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -131,7 +131,7 @@ class RoomNotificationSettingsPresenterTest {
@Test @Test
fun `present - notification settings restore default failed`() = runTest { fun `present - notification settings restore default failed`() = runTest {
val notificationSettingsService = FakeNotificationSettingsService() val notificationSettingsService = FakeNotificationSettingsService()
notificationSettingsService.givenRestoreDefaultNotificationModeError(A_THROWABLE) notificationSettingsService.givenRestoreDefaultNotificationModeError(AN_EXCEPTION)
val presenter = createRoomNotificationSettingsPresenter(notificationSettingsService) val presenter = createRoomNotificationSettingsPresenter(notificationSettingsService)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()

View file

@ -17,6 +17,7 @@ import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.api.room.toMatrixUser import io.element.android.libraries.matrix.api.room.toMatrixUser
@ -41,7 +42,7 @@ class ChangeRolesViewTest {
@Test @Test
fun `passing a 'USER' role throws an exception`() { fun `passing a 'USER' role throws an exception`() {
val exception = runCatching { val exception = runCatchingExceptions {
rule.setChangeRolesContent( rule.setChangeRolesContent(
state = aChangeRolesState( state = aChangeRolesState(
role = RoomMember.Role.USER, role = RoomMember.Role.USER,

View file

@ -27,6 +27,7 @@ import io.element.android.libraries.preferences.api.store.SessionPreferencesStor
import io.element.android.services.appnavstate.api.ActiveRoomsHolder import io.element.android.services.appnavstate.api.ActiveRoomsHolder
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.coroutines.cancellation.CancellationException
class SharePresenter @AssistedInject constructor( class SharePresenter @AssistedInject constructor(
@Assisted private val intent: Intent, @Assisted private val intent: Intent,
@ -89,12 +90,21 @@ class SharePresenter @AssistedInject constructor(
) )
filesToShare filesToShare
.map { fileToShare -> .map { fileToShare ->
mediaSender.sendMedia( val result = mediaSender.sendMedia(
uri = fileToShare.uri, uri = fileToShare.uri,
mimeType = fileToShare.mimeType, mimeType = fileToShare.mimeType,
).isSuccess )
// If the coroutine was cancelled, destroy the room and rethrow the exception
val cancellationException = result.exceptionOrNull() as? CancellationException
if (cancellationException != null) {
if (activeRoomsHolder.getActiveRoomMatching(matrixClient.sessionId, roomId) == null) {
room.destroy()
}
throw cancellationException
}
result.isSuccess
} }
.all { it } .all { isSuccess -> isSuccess }
.also { .also {
if (activeRoomsHolder.getActiveRoomMatching(matrixClient.sessionId, roomId) == null) { if (activeRoomsHolder.getActiveRoomMatching(matrixClient.sessionId, roomId) == null) {
room.destroy() room.destroy()

View file

@ -24,7 +24,7 @@ open class ShareStateProvider : PreviewParameterProvider<ShareState> {
) )
), ),
aShareState( aShareState(
shareAction = AsyncAction.Failure(Throwable("error")), shareAction = AsyncAction.Failure(RuntimeException("error")),
), ),
) )
} }

View file

@ -30,7 +30,6 @@ import io.element.android.libraries.matrix.api.encryption.identity.IdentityState
import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_THROWABLE
import io.element.android.libraries.matrix.test.A_USER_ID 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_ID_2
import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClient
@ -215,7 +214,7 @@ class UserProfilePresenterTest {
@Test @Test
fun `present - BlockUser with error`() = runTest { fun `present - BlockUser with error`() = runTest {
val matrixClient = createFakeMatrixClient( val matrixClient = createFakeMatrixClient(
ignoreUserResult = { Result.failure(A_THROWABLE) } ignoreUserResult = { Result.failure(AN_EXCEPTION) }
) )
val presenter = createUserProfilePresenter(client = matrixClient) val presenter = createUserProfilePresenter(client = matrixClient)
presenter.test { presenter.test {
@ -223,7 +222,7 @@ class UserProfilePresenterTest {
initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = false)) initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = false))
assertThat(awaitItem().isBlocked.isLoading()).isTrue() assertThat(awaitItem().isBlocked.isLoading()).isTrue()
val errorState = awaitItem() val errorState = awaitItem()
assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(A_THROWABLE) assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(AN_EXCEPTION)
// Clear error // Clear error
initialState.eventSink(UserProfileEvents.ClearBlockUserError) initialState.eventSink(UserProfileEvents.ClearBlockUserError)
assertThat(awaitItem().isBlocked).isEqualTo(AsyncData.Success(false)) assertThat(awaitItem().isBlocked).isEqualTo(AsyncData.Success(false))
@ -233,7 +232,7 @@ class UserProfilePresenterTest {
@Test @Test
fun `present - UnblockUser with error`() = runTest { fun `present - UnblockUser with error`() = runTest {
val matrixClient = createFakeMatrixClient( val matrixClient = createFakeMatrixClient(
unIgnoreUserResult = { Result.failure(A_THROWABLE) } unIgnoreUserResult = { Result.failure(AN_EXCEPTION) }
) )
val presenter = createUserProfilePresenter(client = matrixClient) val presenter = createUserProfilePresenter(client = matrixClient)
presenter.test { presenter.test {
@ -241,7 +240,7 @@ class UserProfilePresenterTest {
initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = false)) initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = false))
assertThat(awaitItem().isBlocked.isLoading()).isTrue() assertThat(awaitItem().isBlocked.isLoading()).isTrue()
val errorState = awaitItem() val errorState = awaitItem()
assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(A_THROWABLE) assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(AN_EXCEPTION)
// Clear error // Clear error
initialState.eventSink(UserProfileEvents.ClearBlockUserError) initialState.eventSink(UserProfileEvents.ClearBlockUserError)
assertThat(awaitItem().isBlocked).isEqualTo(AsyncData.Success(true)) assertThat(awaitItem().isBlocked).isEqualTo(AsyncData.Success(true))
@ -265,7 +264,7 @@ class UserProfilePresenterTest {
@Test @Test
fun `present - start DM action failure scenario`() = runTest { fun `present - start DM action failure scenario`() = runTest {
val startDMFailureResult = AsyncAction.Failure(A_THROWABLE) val startDMFailureResult = AsyncAction.Failure(AN_EXCEPTION)
val executeResult = lambdaRecorder<MatrixUser, Boolean, MutableState<AsyncAction<RoomId>>, Unit> { _, _, actionState -> val executeResult = lambdaRecorder<MatrixUser, Boolean, MutableState<AsyncAction<RoomId>>, Unit> { _, _, actionState ->
actionState.value = startDMFailureResult actionState.value = startDMFailureResult
} }

View file

@ -206,6 +206,7 @@ class OutgoingVerificationPresenterTest {
) )
) )
service.emitVerificationFlowState(VerificationFlowState.DidFinish) service.emitVerificationFlowState(VerificationFlowState.DidFinish)
service.emitVerifiedStatus(SessionVerifiedStatus.Verified)
assertThat(awaitItem().step).isEqualTo(Step.Completed) assertThat(awaitItem().step).isEqualTo(Step.Completed)
} }
} }

View file

@ -9,6 +9,7 @@ package io.element.android.features.viewfolder.impl.file
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File import java.io.File
@ -23,7 +24,7 @@ class DefaultFileContentReader @Inject constructor(
private val dispatchers: CoroutineDispatchers, private val dispatchers: CoroutineDispatchers,
) : FileContentReader { ) : FileContentReader {
override suspend fun getLines(path: String): Result<List<String>> = withContext(dispatchers.io) { override suspend fun getLines(path: String): Result<List<String>> = withContext(dispatchers.io) {
runCatching { runCatchingExceptions {
File(path).readLines() File(path).readLines()
} }
} }

View file

@ -16,6 +16,7 @@ import androidx.annotation.RequiresApi
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.androidutils.system.toast import io.element.android.libraries.androidutils.system.toast
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.ApplicationContext
@ -40,7 +41,7 @@ class DefaultFileSave @Inject constructor(
path: String, path: String,
) { ) {
withContext(dispatchers.io) { withContext(dispatchers.io) {
runCatching { runCatchingExceptions {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
saveOnDiskUsingMediaStore(path) saveOnDiskUsingMediaStore(path)
} else { } else {

View file

@ -13,6 +13,7 @@ import android.net.Uri
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
@ -37,7 +38,7 @@ class DefaultFileShare @Inject constructor(
override suspend fun share( override suspend fun share(
path: String, path: String,
) { ) {
runCatching { runCatchingExceptions {
val file = File(path) val file = File(path)
val shareableUri = file.toShareableUri() val shareableUri = file.toShareableUri()
val shareMediaIntent = Intent(Intent.ACTION_SEND) val shareMediaIntent = Intent(Intent.ACTION_SEND)

View file

@ -37,6 +37,7 @@ datetime = "0.6.2"
serialization_json = "1.8.1" serialization_json = "1.8.1"
#other #other
detekt = "1.23.8"
coil = "3.1.0" coil = "3.1.0"
showkase = "1.0.3" showkase = "1.0.3"
appyx = "1.7.1" appyx = "1.7.1"
@ -152,6 +153,8 @@ test_parameter_injector = "com.google.testparameterinjector:test-parameter-injec
test_robolectric = "org.robolectric:robolectric:4.14.1" test_robolectric = "org.robolectric:robolectric:4.14.1"
test_appyx_junit = { module = "com.bumble.appyx:testing-junit4", version.ref = "appyx" } test_appyx_junit = { module = "com.bumble.appyx:testing-junit4", version.ref = "appyx" }
test_composable_preview_scanner = "io.github.sergio-sastre.ComposablePreviewScanner:android:0.6.1" test_composable_preview_scanner = "io.github.sergio-sastre.ComposablePreviewScanner:android:0.6.1"
test_detekt_api = { module = "io.gitlab.arturbosch.detekt:detekt-api", version.ref = "detekt" }
test_detekt_test = { module = "io.gitlab.arturbosch.detekt:detekt-test", version.ref = "detekt" }
# Others # Others
coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" } coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" }
@ -231,7 +234,7 @@ kotlin_serialization = { id = "org.jetbrains.kotlin.plugin.serialization", versi
kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
anvil = { id = "dev.zacsweers.anvil", version.ref = "anvil" } anvil = { id = "dev.zacsweers.anvil", version.ref = "anvil" }
detekt = "io.gitlab.arturbosch.detekt:1.23.8" detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
ktlint = "org.jlleitschuh.gradle.ktlint:12.3.0" ktlint = "org.jlleitschuh.gradle.ktlint:12.3.0"
dependencygraph = "com.savvasdalkitsis.module-dependency-graph:0.12" dependencygraph = "com.savvasdalkitsis.module-dependency-graph:0.12"
dependencycheck = "org.owasp.dependencycheck:12.1.1" dependencycheck = "org.owasp.dependencycheck:12.1.1"

View file

@ -41,7 +41,7 @@ fun AudioManager.enableExternalAudioDevice() {
selectedDevice?.let { device -> selectedDevice?.let { device ->
Timber.d("Audio device selected, type: ${device.type}") Timber.d("Audio device selected, type: ${device.type}")
tryOrNull( tryOrNull(
onError = { failure -> onException = { failure ->
Timber.e(failure, "Audio: exception when setting communication device") Timber.e(failure, "Audio: exception when setting communication device")
} }
) { ) {

View file

@ -12,6 +12,7 @@ import android.content.Context
import android.net.Uri import android.net.Uri
import android.provider.OpenableColumns import android.provider.OpenableColumns
import androidx.core.net.toFile import androidx.core.net.toFile
import io.element.android.libraries.core.extensions.runCatchingExceptions
fun Context.getMimeType(uri: Uri): String? = when (uri.scheme) { fun Context.getMimeType(uri: Uri): String? = when (uri.scheme) {
ContentResolver.SCHEME_CONTENT -> contentResolver.getType(uri) ContentResolver.SCHEME_CONTENT -> contentResolver.getType(uri)
@ -32,14 +33,14 @@ fun Context.getFileSize(uri: Uri): Long {
} ?: 0 } ?: 0
} }
private fun Context.getContentFileSize(uri: Uri): Long? = runCatching { private fun Context.getContentFileSize(uri: Uri): Long? = runCatchingExceptions {
contentResolver.query(uri, null, null, null, null)?.use { cursor -> contentResolver.query(uri, null, null, null, null)?.use { cursor ->
cursor.moveToFirst() cursor.moveToFirst()
return@use cursor.getColumnIndexOrThrow(OpenableColumns.SIZE).let(cursor::getLong) return@use cursor.getColumnIndexOrThrow(OpenableColumns.SIZE).let(cursor::getLong)
} }
}.getOrNull() }.getOrNull()
private fun Context.getContentFileName(uri: Uri): String? = runCatching { private fun Context.getContentFileName(uri: Uri): String? = runCatchingExceptions {
contentResolver.query(uri, null, null, null, null)?.use { cursor -> contentResolver.query(uri, null, null, null, null)?.use { cursor ->
cursor.moveToFirst() cursor.moveToFirst()
return@use cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME).let(cursor::getString) return@use cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME).let(cursor::getString)

View file

@ -17,7 +17,7 @@ import java.util.UUID
fun File.safeDelete() { fun File.safeDelete() {
if (exists().not()) return if (exists().not()) return
tryOrNull( tryOrNull(
onError = { onException = {
Timber.e(it, "Error, unable to delete file $path") Timber.e(it, "Error, unable to delete file $path")
}, },
operation = { operation = {
@ -30,7 +30,7 @@ fun File.safeDelete() {
fun File.safeRenameTo(dest: File) { fun File.safeRenameTo(dest: File) {
tryOrNull( tryOrNull(
onError = { onException = {
Timber.e(it, "Error, unable to rename file $path to ${dest.path}") Timber.e(it, "Error, unable to rename file $path to ${dest.path}")
}, },
operation = { operation = {

View file

@ -13,6 +13,7 @@ import android.text.util.Linkify
import androidx.core.text.getSpans import androidx.core.text.getSpans
import androidx.core.text.toSpannable import androidx.core.text.toSpannable
import androidx.core.text.util.LinkifyCompat import androidx.core.text.util.LinkifyCompat
import io.element.android.libraries.core.extensions.runCatchingExceptions
import timber.log.Timber import timber.log.Timber
import kotlin.collections.component1 import kotlin.collections.component1
import kotlin.collections.component2 import kotlin.collections.component2
@ -48,7 +49,7 @@ object LinkifyHelper {
// Try to avoid including trailing punctuation in the link. // Try to avoid including trailing punctuation in the link.
// Since this might fail in some edge cases, we catch the exception and just use the original end index. // Since this might fail in some edge cases, we catch the exception and just use the original end index.
val newEnd = runCatching { val newEnd = runCatchingExceptions {
adjustLinkifiedUrlSpanEndIndex(spannable, start, end) adjustLinkifiedUrlSpanEndIndex(spannable, start, end)
}.onFailure { }.onFailure {
Timber.e(it, "Failed to adjust end index for link span") Timber.e(it, "Failed to adjust end index for link span")

View file

@ -15,6 +15,7 @@ android {
dependencies { dependencies {
api(projects.libraries.di) api(projects.libraries.di)
api(projects.libraries.core)
api(libs.dagger) api(libs.dagger)
api(libs.appyx.core) api(libs.appyx.core)
api(libs.androidx.lifecycle.runtime) api(libs.androidx.lifecycle.runtime)

View file

@ -9,6 +9,7 @@ package io.element.android.libraries.architecture
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import io.element.android.libraries.core.extensions.runCatchingExceptions
import kotlin.contracts.ExperimentalContracts import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind import kotlin.contracts.InvocationKind
import kotlin.contracts.contract import kotlin.contracts.contract
@ -90,7 +91,7 @@ suspend inline fun <T> MutableState<AsyncAction<T>>.runCatchingUpdatingState(
state = this, state = this,
errorTransform = errorTransform, errorTransform = errorTransform,
resultBlock = { resultBlock = {
runCatching { runCatchingExceptions {
block() block()
} }
}, },
@ -103,7 +104,7 @@ suspend inline fun <T> (suspend () -> T).runCatchingUpdatingState(
state = state, state = state,
errorTransform = errorTransform, errorTransform = errorTransform,
resultBlock = { resultBlock = {
runCatching { runCatchingExceptions {
this() this()
} }
}, },

View file

@ -9,6 +9,7 @@ package io.element.android.libraries.architecture
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import io.element.android.libraries.core.extensions.runCatchingExceptions
import kotlin.contracts.ExperimentalContracts import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind import kotlin.contracts.InvocationKind
import kotlin.contracts.contract import kotlin.contracts.contract
@ -93,7 +94,7 @@ suspend inline fun <T> MutableState<AsyncData<T>>.runCatchingUpdatingState(
state = this, state = this,
errorTransform = errorTransform, errorTransform = errorTransform,
resultBlock = { resultBlock = {
runCatching { runCatchingExceptions {
block() block()
} }
}, },
@ -106,7 +107,7 @@ suspend inline fun <T> (suspend () -> T).runCatchingUpdatingState(
state = state, state = state,
errorTransform = errorTransform, errorTransform = errorTransform,
resultBlock = { resultBlock = {
runCatching { runCatchingExceptions {
this() this()
} }
}, },

View file

@ -38,15 +38,15 @@ class AsyncDataKtTest {
val result = runUpdatingState(state) { val result = runUpdatingState(state) {
delay(1) delay(1)
Result.failure(MyThrowable("hello")) Result.failure(MyException("hello"))
} }
assertThat(result.isFailure).isTrue() assertThat(result.isFailure).isTrue()
assertThat(result.exceptionOrNull()).isEqualTo(MyThrowable("hello")) assertThat(result.exceptionOrNull()).isEqualTo(MyException("hello"))
assertThat(state.popFirst()).isEqualTo(AsyncData.Uninitialized) assertThat(state.popFirst()).isEqualTo(AsyncData.Uninitialized)
assertThat(state.popFirst()).isEqualTo(AsyncData.Loading(null)) assertThat(state.popFirst()).isEqualTo(AsyncData.Loading(null))
assertThat(state.popFirst()).isEqualTo(AsyncData.Failure<Int>(MyThrowable("hello"))) assertThat(state.popFirst()).isEqualTo(AsyncData.Failure<Int>(MyException("hello")))
state.assertNoMoreValues() state.assertNoMoreValues()
} }
@ -54,17 +54,17 @@ class AsyncDataKtTest {
fun `updates state when block returns failure transforming the error`() = runTest { fun `updates state when block returns failure transforming the error`() = runTest {
val state = TestableMutableState<AsyncData<Int>>(AsyncData.Uninitialized) val state = TestableMutableState<AsyncData<Int>>(AsyncData.Uninitialized)
val result = runUpdatingState(state, { MyThrowable(it.message + " world") }) { val result = runUpdatingState(state, { MyException(it.message + " world") }) {
delay(1) delay(1)
Result.failure(MyThrowable("hello")) Result.failure(MyException("hello"))
} }
assertThat(result.isFailure).isTrue() assertThat(result.isFailure).isTrue()
assertThat(result.exceptionOrNull()).isEqualTo(MyThrowable("hello world")) assertThat(result.exceptionOrNull()).isEqualTo(MyException("hello world"))
assertThat(state.popFirst()).isEqualTo(AsyncData.Uninitialized) assertThat(state.popFirst()).isEqualTo(AsyncData.Uninitialized)
assertThat(state.popFirst()).isEqualTo(AsyncData.Loading(null)) assertThat(state.popFirst()).isEqualTo(AsyncData.Loading(null))
assertThat(state.popFirst()).isEqualTo(AsyncData.Failure<Int>(MyThrowable("hello world"))) assertThat(state.popFirst()).isEqualTo(AsyncData.Failure<Int>(MyException("hello world")))
state.assertNoMoreValues() state.assertNoMoreValues()
} }
} }
@ -101,4 +101,4 @@ private class TestableMutableState<T>(
/** /**
* An exception that is also a data class so we can compare it using equals. * An exception that is also a data class so we can compare it using equals.
*/ */
private data class MyThrowable(val myMessage: String) : Throwable(myMessage) private data class MyException(val myMessage: String) : Exception(myMessage)

View file

@ -7,11 +7,20 @@
package io.element.android.libraries.core.data package io.element.android.libraries.core.data
inline fun <A> tryOrNull(onError: ((Throwable) -> Unit) = { }, operation: () -> A): A? { import kotlin.coroutines.cancellation.CancellationException
/**
* Can be used to catch [Exception]s in a block of code, returning `null` if an exception occurs.
*
* If the block throws a [CancellationException], it will be rethrown.
*/
inline fun <A> tryOrNull(onException: ((Exception) -> Unit) = { }, operation: () -> A): A? {
return try { return try {
operation() operation()
} catch (any: Throwable) { } catch (e: CancellationException) {
onError.invoke(any) throw e
} catch (e: Exception) {
onException.invoke(e)
null null
} }
} }

View file

@ -7,6 +7,61 @@
package io.element.android.libraries.core.extensions package io.element.android.libraries.core.extensions
import kotlin.coroutines.cancellation.CancellationException
/**
* Can be used to catch exceptions in a block of code and return a [Result].
* If the block throws a [CancellationException], it will be rethrown.
* If it throws any other exception, it will be wrapped in a [Result.failure].
*
* [Error]s are not caught by this function, as they are not meant to be caught in normal application flow.
*/
inline fun <T> runCatchingExceptions(
block: () -> T
): Result<T> {
return try {
Result.success(block())
} catch (e: CancellationException) {
throw e
} catch (e: Exception) {
Result.failure(e)
}
}
/**
* Can be used to catch exceptions in a block of code and return a [Result].
* If the block throws a [CancellationException], it will be rethrown.
* If it throws any other exception, it will be wrapped in a [Result.failure].
*
* [Error]s are not caught by this function, as they are not meant to be caught in normal application flow.
*/
inline fun <T, R> T.runCatchingExceptions(
block: T.() -> R
): Result<R> {
return try {
Result.success(block())
} catch (e: CancellationException) {
throw e
} catch (e: Exception) {
Result.failure(e)
}
}
/**
* Can be used to transform a [Result] into another [Result] by applying a [block] to the value if it is successful.
* If the original [Result] is a failure, the exception will be wrapped in a new [Result.failure].
*
* This is a safer version of [Result.mapCatching].
*/
inline fun <R, T> Result<T>.mapCatchingExceptions(
block: (T) -> R,
): Result<R> {
return fold(
onSuccess = { value -> runCatchingExceptions { block(value) } },
onFailure = { exception -> Result.failure(exception) }
)
}
/** /**
* Can be used to transform some Throwable into some other. * Can be used to transform some Throwable into some other.
*/ */
@ -33,12 +88,16 @@ inline fun <R, T> Result<T>.flatMap(transform: (T) -> Result<R>): Result<R> {
* @return The result of the transform or a caught exception wrapped in a [Result]. * @return The result of the transform or a caught exception wrapped in a [Result].
*/ */
inline fun <R, T> Result<T>.flatMapCatching(transform: (T) -> Result<R>): Result<R> { inline fun <R, T> Result<T>.flatMapCatching(transform: (T) -> Result<R>): Result<R> {
return mapCatching(transform).fold( return mapCatchingExceptions(transform).fold(
onSuccess = { it }, onSuccess = { it },
onFailure = { Result.failure(it) } onFailure = { Result.failure(it) }
) )
} }
/**
* Can be used to execute a block of code after the [Result] has been processed, regardless of whether it was successful or not.
* The block receives the exception if there was one, or `null` if the result was successful.
*/
inline fun <T> Result<T>.finally(block: (exception: Throwable?) -> Unit): Result<T> { inline fun <T> Result<T>.finally(block: (exception: Throwable?) -> Unit): Result<T> {
onSuccess { block(null) } onSuccess { block(null) }
onFailure(block) onFailure(block)

View file

@ -7,6 +7,7 @@
package io.element.android.libraries.matrix.api.room.powerlevels package io.element.android.libraries.matrix.api.room.powerlevels
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.matrix.api.room.BaseRoom import io.element.android.libraries.matrix.api.room.BaseRoom
import io.element.android.libraries.matrix.api.room.MessageEventType import io.element.android.libraries.matrix.api.room.MessageEventType
import io.element.android.libraries.matrix.api.room.StateEventType import io.element.android.libraries.matrix.api.room.StateEventType
@ -60,7 +61,7 @@ suspend fun BaseRoom.canRedactOther(): Result<Boolean> = canUserRedactOther(sess
/** /**
* Shortcut for checking if current user can handle knock requests. * Shortcut for checking if current user can handle knock requests.
*/ */
suspend fun BaseRoom.canHandleKnockRequests(): Result<Boolean> = runCatching { suspend fun BaseRoom.canHandleKnockRequests(): Result<Boolean> = runCatchingExceptions {
canInvite().getOrThrow() || canBan().getOrThrow() || canKick().getOrThrow() canInvite().getOrThrow() || canBan().getOrThrow() || canKick().getOrThrow()
} }

View file

@ -13,6 +13,7 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.coroutine.childScope import io.element.android.libraries.core.coroutine.childScope
import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.core.extensions.mapFailure import io.element.android.libraries.core.extensions.mapFailure
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.DeviceId
@ -250,7 +251,7 @@ class RustMatrixClient(
} }
override fun userIdServerName(): String { override fun userIdServerName(): String {
return runCatching { return runCatchingExceptions {
innerClient.userIdServerName() innerClient.userIdServerName()
} }
.onFailure { .onFailure {
@ -261,7 +262,7 @@ class RustMatrixClient(
} }
override suspend fun getUrl(url: String): Result<String> = withContext(sessionDispatcher) { override suspend fun getUrl(url: String): Result<String> = withContext(sessionDispatcher) {
runCatching { runCatchingExceptions {
innerClient.getUrl(url) innerClient.getUrl(url)
} }
} }
@ -301,19 +302,19 @@ class RustMatrixClient(
} }
override suspend fun ignoreUser(userId: UserId): Result<Unit> = withContext(sessionDispatcher) { override suspend fun ignoreUser(userId: UserId): Result<Unit> = withContext(sessionDispatcher) {
runCatching { runCatchingExceptions {
innerClient.ignoreUser(userId.value) innerClient.ignoreUser(userId.value)
} }
} }
override suspend fun unignoreUser(userId: UserId): Result<Unit> = withContext(sessionDispatcher) { override suspend fun unignoreUser(userId: UserId): Result<Unit> = withContext(sessionDispatcher) {
runCatching { runCatchingExceptions {
innerClient.unignoreUser(userId.value) innerClient.unignoreUser(userId.value)
} }
} }
override suspend fun createRoom(createRoomParams: CreateRoomParameters): Result<RoomId> = withContext(sessionDispatcher) { override suspend fun createRoom(createRoomParams: CreateRoomParameters): Result<RoomId> = withContext(sessionDispatcher) {
runCatching { runCatchingExceptions {
val rustParams = RustCreateRoomParameters( val rustParams = RustCreateRoomParameters(
name = createRoomParams.name, name = createRoomParams.name,
topic = createRoomParams.topic, topic = createRoomParams.topic,
@ -363,7 +364,7 @@ class RustMatrixClient(
} }
override suspend fun getProfile(userId: UserId): Result<MatrixUser> = withContext(sessionDispatcher) { override suspend fun getProfile(userId: UserId): Result<MatrixUser> = withContext(sessionDispatcher) {
runCatching { runCatchingExceptions {
innerClient.getProfile(userId.value).let(UserProfileMapper::map) innerClient.getProfile(userId.value).let(UserProfileMapper::map)
} }
} }
@ -373,28 +374,28 @@ class RustMatrixClient(
override suspend fun searchUsers(searchTerm: String, limit: Long): Result<MatrixSearchUserResults> = override suspend fun searchUsers(searchTerm: String, limit: Long): Result<MatrixSearchUserResults> =
withContext(sessionDispatcher) { withContext(sessionDispatcher) {
runCatching { runCatchingExceptions {
innerClient.searchUsers(searchTerm, limit.toULong()).let(UserSearchResultMapper::map) innerClient.searchUsers(searchTerm, limit.toULong()).let(UserSearchResultMapper::map)
} }
} }
override suspend fun setDisplayName(displayName: String): Result<Unit> = override suspend fun setDisplayName(displayName: String): Result<Unit> =
withContext(sessionDispatcher) { withContext(sessionDispatcher) {
runCatching { innerClient.setDisplayName(displayName) } runCatchingExceptions { innerClient.setDisplayName(displayName) }
} }
override suspend fun uploadAvatar(mimeType: String, data: ByteArray): Result<Unit> = override suspend fun uploadAvatar(mimeType: String, data: ByteArray): Result<Unit> =
withContext(sessionDispatcher) { withContext(sessionDispatcher) {
runCatching { innerClient.uploadAvatar(mimeType, data) } runCatchingExceptions { innerClient.uploadAvatar(mimeType, data) }
} }
override suspend fun removeAvatar(): Result<Unit> = override suspend fun removeAvatar(): Result<Unit> =
withContext(sessionDispatcher) { withContext(sessionDispatcher) {
runCatching { innerClient.removeAvatar() } runCatchingExceptions { innerClient.removeAvatar() }
} }
override suspend fun joinRoom(roomId: RoomId): Result<RoomSummary?> = withContext(sessionDispatcher) { override suspend fun joinRoom(roomId: RoomId): Result<RoomSummary?> = withContext(sessionDispatcher) {
runCatching { runCatchingExceptions {
innerClient.joinRoomById(roomId.value).destroy() innerClient.joinRoomById(roomId.value).destroy()
try { try {
awaitRoom(roomId.toRoomIdOrAlias(), 10.seconds, CurrentUserMembership.JOINED) awaitRoom(roomId.toRoomIdOrAlias(), 10.seconds, CurrentUserMembership.JOINED)
@ -406,7 +407,7 @@ class RustMatrixClient(
}.mapFailure { it.mapClientException() } }.mapFailure { it.mapClientException() }
override suspend fun joinRoomByIdOrAlias(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>): Result<RoomSummary?> = withContext(sessionDispatcher) { override suspend fun joinRoomByIdOrAlias(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>): Result<RoomSummary?> = withContext(sessionDispatcher) {
runCatching { runCatchingExceptions {
innerClient.joinRoomByIdOrAlias( innerClient.joinRoomByIdOrAlias(
roomIdOrAlias = roomIdOrAlias.identifier, roomIdOrAlias = roomIdOrAlias.identifier,
serverNames = serverNames, serverNames = serverNames,
@ -423,7 +424,7 @@ class RustMatrixClient(
override suspend fun knockRoom(roomIdOrAlias: RoomIdOrAlias, message: String, serverNames: List<String>): Result<RoomSummary?> = withContext( override suspend fun knockRoom(roomIdOrAlias: RoomIdOrAlias, message: String, serverNames: List<String>): Result<RoomSummary?> = withContext(
sessionDispatcher sessionDispatcher
) { ) {
runCatching { runCatchingExceptions {
innerClient.knock(roomIdOrAlias.identifier, message, serverNames).destroy() innerClient.knock(roomIdOrAlias.identifier, message, serverNames).destroy()
try { try {
awaitRoom(roomIdOrAlias, 10.seconds, CurrentUserMembership.KNOCKED) awaitRoom(roomIdOrAlias, 10.seconds, CurrentUserMembership.KNOCKED)
@ -435,19 +436,19 @@ class RustMatrixClient(
} }
override suspend fun trackRecentlyVisitedRoom(roomId: RoomId): Result<Unit> = withContext(sessionDispatcher) { override suspend fun trackRecentlyVisitedRoom(roomId: RoomId): Result<Unit> = withContext(sessionDispatcher) {
runCatching { runCatchingExceptions {
innerClient.trackRecentlyVisitedRoom(roomId.value) innerClient.trackRecentlyVisitedRoom(roomId.value)
} }
} }
override suspend fun getRecentlyVisitedRooms(): Result<List<RoomId>> = withContext(sessionDispatcher) { override suspend fun getRecentlyVisitedRooms(): Result<List<RoomId>> = withContext(sessionDispatcher) {
runCatching { runCatchingExceptions {
innerClient.getRecentlyVisitedRooms().map(::RoomId) innerClient.getRecentlyVisitedRooms().map(::RoomId)
} }
} }
override suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result<Optional<ResolvedRoomAlias>> = withContext(sessionDispatcher) { override suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result<Optional<ResolvedRoomAlias>> = withContext(sessionDispatcher) {
runCatching { runCatchingExceptions {
val result = innerClient.resolveRoomAlias(roomAlias.value)?.let { val result = innerClient.resolveRoomAlias(roomAlias.value)?.let {
ResolvedRoomAlias( ResolvedRoomAlias(
roomId = RoomId(it.roomId), roomId = RoomId(it.roomId),
@ -459,7 +460,7 @@ class RustMatrixClient(
} }
override suspend fun getRoomPreview(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>): Result<NotJoinedRoom> = withContext(sessionDispatcher) { override suspend fun getRoomPreview(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>): Result<NotJoinedRoom> = withContext(sessionDispatcher) {
runCatching { runCatchingExceptions {
when (roomIdOrAlias) { when (roomIdOrAlias) {
is RoomIdOrAlias.Alias -> { is RoomIdOrAlias.Alias -> {
val roomId = innerClient.resolveRoomAlias(roomIdOrAlias.roomAlias.value)?.roomId?.let { RoomId(it) } val roomId = innerClient.resolveRoomAlias(roomIdOrAlias.roomAlias.value)?.roomId?.let { RoomId(it) }
@ -556,7 +557,7 @@ class RustMatrixClient(
} }
override fun canDeactivateAccount(): Boolean { override fun canDeactivateAccount(): Boolean {
return runCatching { return runCatchingExceptions {
innerClient.canDeactivateAccount() innerClient.canDeactivateAccount()
} }
.getOrNull() .getOrNull()
@ -568,9 +569,9 @@ class RustMatrixClient(
// Remove current delegate so we don't receive an auth error // Remove current delegate so we don't receive an auth error
clientDelegateTaskHandle?.cancelAndDestroy() clientDelegateTaskHandle?.cancelAndDestroy()
clientDelegateTaskHandle = null clientDelegateTaskHandle = null
runCatching { runCatchingExceptions {
// First call without AuthData, should fail // First call without AuthData, should fail
val firstAttempt = runCatching { val firstAttempt = runCatchingExceptions {
innerClient.deactivateAccount( innerClient.deactivateAccount(
authData = null, authData = null,
eraseData = eraseData, eraseData = eraseData,
@ -579,7 +580,7 @@ class RustMatrixClient(
if (firstAttempt.isFailure) { if (firstAttempt.isFailure) {
Timber.w(firstAttempt.exceptionOrNull(), "Expected failure, try again") Timber.w(firstAttempt.exceptionOrNull(), "Expected failure, try again")
// This is expected, try again with the password // This is expected, try again with the password
runCatching { runCatchingExceptions {
innerClient.deactivateAccount( innerClient.deactivateAccount(
authData = AuthData.Password( authData = AuthData.Password(
passwordDetails = AuthDataPasswordDetails( passwordDetails = AuthDataPasswordDetails(
@ -606,13 +607,13 @@ class RustMatrixClient(
override suspend fun getAccountManagementUrl(action: AccountManagementAction?): Result<String?> = withContext(sessionDispatcher) { override suspend fun getAccountManagementUrl(action: AccountManagementAction?): Result<String?> = withContext(sessionDispatcher) {
val rustAction = action?.toRustAction() val rustAction = action?.toRustAction()
runCatching { runCatchingExceptions {
innerClient.accountUrl(rustAction) innerClient.accountUrl(rustAction)
} }
} }
override suspend fun uploadMedia(mimeType: String, data: ByteArray, progressCallback: ProgressCallback?): Result<String> = withContext(sessionDispatcher) { override suspend fun uploadMedia(mimeType: String, data: ByteArray, progressCallback: ProgressCallback?): Result<String> = withContext(sessionDispatcher) {
runCatching { runCatchingExceptions {
innerClient.uploadMedia(mimeType, data, progressCallback?.toProgressWatcher()) innerClient.uploadMedia(mimeType, data, progressCallback?.toProgressWatcher())
} }
} }
@ -654,19 +655,19 @@ class RustMatrixClient(
}.buffer(Channel.UNLIMITED) }.buffer(Channel.UNLIMITED)
override suspend fun availableSlidingSyncVersions(): Result<List<SlidingSyncVersion>> = withContext(sessionDispatcher) { override suspend fun availableSlidingSyncVersions(): Result<List<SlidingSyncVersion>> = withContext(sessionDispatcher) {
runCatching { runCatchingExceptions {
innerClient.availableSlidingSyncVersions().map { it.map() } innerClient.availableSlidingSyncVersions().map { it.map() }
} }
} }
override suspend fun currentSlidingSyncVersion(): Result<SlidingSyncVersion> = withContext(sessionDispatcher) { override suspend fun currentSlidingSyncVersion(): Result<SlidingSyncVersion> = withContext(sessionDispatcher) {
runCatching { runCatchingExceptions {
innerClient.session().slidingSyncVersion.map() innerClient.session().slidingSyncVersion.map()
} }
} }
override suspend fun canReportRoom(): Boolean = withContext(sessionDispatcher) { override suspend fun canReportRoom(): Boolean = withContext(sessionDispatcher) {
runCatching { runCatchingExceptions {
innerClient.isReportRoomApiSupported() innerClient.isReportRoomApiSupported()
}.getOrDefault(false) }.getOrDefault(false)
} }

View file

@ -10,6 +10,7 @@ package io.element.android.libraries.matrix.impl.auth
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.extensions.mapFailure import io.element.android.libraries.core.extensions.mapFailure
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
@ -91,7 +92,7 @@ class RustMatrixAuthenticationService @Inject constructor(
} }
override suspend fun restoreSession(sessionId: SessionId): Result<MatrixClient> = withContext(coroutineDispatchers.io) { override suspend fun restoreSession(sessionId: SessionId): Result<MatrixClient> = withContext(coroutineDispatchers.io) {
runCatching { runCatchingExceptions {
val sessionData = sessionStore.getSession(sessionId.value) val sessionData = sessionStore.getSession(sessionId.value)
if (sessionData != null) { if (sessionData != null) {
if (sessionData.isTokenValid) { if (sessionData.isTokenValid) {
@ -126,7 +127,7 @@ class RustMatrixAuthenticationService @Inject constructor(
override suspend fun setHomeserver(homeserver: String): Result<Unit> = override suspend fun setHomeserver(homeserver: String): Result<Unit> =
withContext(coroutineDispatchers.io) { withContext(coroutineDispatchers.io) {
val emptySessionPath = rotateSessionPath() val emptySessionPath = rotateSessionPath()
runCatching { runCatchingExceptions {
val client = makeClient(sessionPaths = emptySessionPath) { val client = makeClient(sessionPaths = emptySessionPath) {
serverNameOrHomeserverUrl(homeserver) serverNameOrHomeserverUrl(homeserver)
} }
@ -144,7 +145,7 @@ class RustMatrixAuthenticationService @Inject constructor(
override suspend fun login(username: String, password: String): Result<SessionId> = override suspend fun login(username: String, password: String): Result<SessionId> =
withContext(coroutineDispatchers.io) { withContext(coroutineDispatchers.io) {
runCatching { runCatchingExceptions {
val client = currentClient ?: error("You need to call `setHomeserver()` first") val client = currentClient ?: error("You need to call `setHomeserver()` first")
val currentSessionPaths = sessionPaths ?: error("You need to call `setHomeserver()` first") val currentSessionPaths = sessionPaths ?: error("You need to call `setHomeserver()` first")
client.login(username, password, "Element X Android", null) client.login(username, password, "Element X Android", null)
@ -170,7 +171,7 @@ class RustMatrixAuthenticationService @Inject constructor(
override suspend fun importCreatedSession(externalSession: ExternalSession): Result<SessionId> = override suspend fun importCreatedSession(externalSession: ExternalSession): Result<SessionId> =
withContext(coroutineDispatchers.io) { withContext(coroutineDispatchers.io) {
runCatching { runCatchingExceptions {
currentClient ?: error("You need to call `setHomeserver()` first") currentClient ?: error("You need to call `setHomeserver()` first")
val currentSessionPaths = sessionPaths ?: error("You need to call `setHomeserver()` first") val currentSessionPaths = sessionPaths ?: error("You need to call `setHomeserver()` first")
val sessionData = externalSession.toSessionData( val sessionData = externalSession.toSessionData(
@ -192,7 +193,7 @@ class RustMatrixAuthenticationService @Inject constructor(
loginHint: String?, loginHint: String?,
): Result<OidcDetails> { ): Result<OidcDetails> {
return withContext(coroutineDispatchers.io) { return withContext(coroutineDispatchers.io) {
runCatching { runCatchingExceptions {
val client = currentClient ?: error("You need to call `setHomeserver()` first") val client = currentClient ?: error("You need to call `setHomeserver()` first")
val oAuthAuthorizationData = client.urlForOidc( val oAuthAuthorizationData = client.urlForOidc(
oidcConfiguration = oidcConfigurationProvider.get(), oidcConfiguration = oidcConfigurationProvider.get(),
@ -211,7 +212,7 @@ class RustMatrixAuthenticationService @Inject constructor(
override suspend fun cancelOidcLogin(): Result<Unit> { override suspend fun cancelOidcLogin(): Result<Unit> {
return withContext(coroutineDispatchers.io) { return withContext(coroutineDispatchers.io) {
runCatching { runCatchingExceptions {
pendingOAuthAuthorizationData?.use { pendingOAuthAuthorizationData?.use {
currentClient?.abortOidcAuth(it) currentClient?.abortOidcAuth(it)
} }
@ -228,7 +229,7 @@ class RustMatrixAuthenticationService @Inject constructor(
*/ */
override suspend fun loginWithOidc(callbackUrl: String): Result<SessionId> { override suspend fun loginWithOidc(callbackUrl: String): Result<SessionId> {
return withContext(coroutineDispatchers.io) { return withContext(coroutineDispatchers.io) {
runCatching { runCatchingExceptions {
val client = currentClient ?: error("You need to call `setHomeserver()` first") val client = currentClient ?: error("You need to call `setHomeserver()` first")
val currentSessionPaths = sessionPaths ?: error("You need to call `setHomeserver()` first") val currentSessionPaths = sessionPaths ?: error("You need to call `setHomeserver()` first")
client.loginWithOidcCallback(callbackUrl) client.loginWithOidcCallback(callbackUrl)
@ -268,7 +269,7 @@ class RustMatrixAuthenticationService @Inject constructor(
progress(state.toStep()) progress(state.toStep())
} }
} }
runCatching { runCatchingExceptions {
val client = makeQrCodeLoginClient( val client = makeQrCodeLoginClient(
sessionPaths = emptySessionPaths, sessionPaths = emptySessionPaths,
passphrase = pendingPassphrase, passphrase = pendingPassphrase,

View file

@ -8,6 +8,7 @@
package io.element.android.libraries.matrix.impl.auth.qrlogin package io.element.android.libraries.matrix.impl.auth.qrlogin
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData
import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginDataFactory import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginDataFactory
@ -17,6 +18,6 @@ import javax.inject.Inject
@ContributesBinding(AppScope::class) @ContributesBinding(AppScope::class)
class RustQrCodeLoginDataFactory @Inject constructor() : MatrixQrCodeLoginDataFactory { class RustQrCodeLoginDataFactory @Inject constructor() : MatrixQrCodeLoginDataFactory {
override fun parseQrCodeData(data: ByteArray): Result<MatrixQrCodeLoginData> { override fun parseQrCodeData(data: ByteArray): Result<MatrixQrCodeLoginData> {
return runCatching { SdkQrCodeLoginData(QrCodeData.fromBytes(data)) } return runCatchingExceptions { SdkQrCodeLoginData(QrCodeData.fromBytes(data)) }
} }
} }

View file

@ -8,6 +8,7 @@
package io.element.android.libraries.matrix.impl.call package io.element.android.libraries.matrix.impl.call
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import org.matrix.rustcomponents.sdk.ElementWellKnown import org.matrix.rustcomponents.sdk.ElementWellKnown
import org.matrix.rustcomponents.sdk.makeElementWellKnown import org.matrix.rustcomponents.sdk.makeElementWellKnown
@ -20,7 +21,7 @@ interface ElementWellKnownParser {
@ContributesBinding(AppScope::class) @ContributesBinding(AppScope::class)
class RustElementWellKnownParser @Inject constructor() : ElementWellKnownParser { class RustElementWellKnownParser @Inject constructor() : ElementWellKnownParser {
override fun parse(str: String): Result<ElementWellKnown> { override fun parse(str: String): Result<ElementWellKnown> {
return runCatching { return runCatchingExceptions {
makeElementWellKnown(str) makeElementWellKnown(str)
} }
} }

View file

@ -7,13 +7,14 @@
package io.element.android.libraries.matrix.impl.core package io.element.android.libraries.matrix.impl.core
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.matrix.api.core.SendHandle import io.element.android.libraries.matrix.api.core.SendHandle
class RustSendHandle( class RustSendHandle(
val inner: org.matrix.rustcomponents.sdk.SendHandle, val inner: org.matrix.rustcomponents.sdk.SendHandle,
) : SendHandle { ) : SendHandle {
override suspend fun retry(): Result<Unit> { override suspend fun retry(): Result<Unit> {
return runCatching { return runCatchingExceptions {
inner.tryResend() inner.tryResend()
} }
} }

View file

@ -10,6 +10,7 @@ package io.element.android.libraries.matrix.impl.encryption
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.extensions.flatMap import io.element.android.libraries.core.extensions.flatMap
import io.element.android.libraries.core.extensions.mapFailure import io.element.android.libraries.core.extensions.mapFailure
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.encryption.BackupState import io.element.android.libraries.matrix.api.encryption.BackupState
@ -96,7 +97,7 @@ internal class RustEncryptionService(
.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, false) .stateIn(sessionCoroutineScope, SharingStarted.Eagerly, false)
override suspend fun enableBackups(): Result<Unit> = withContext(dispatchers.io) { override suspend fun enableBackups(): Result<Unit> = withContext(dispatchers.io) {
runCatching { runCatchingExceptions {
service.enableBackups() service.enableBackups()
}.mapFailure { }.mapFailure {
it.mapRecoveryException() it.mapRecoveryException()
@ -106,7 +107,7 @@ internal class RustEncryptionService(
override suspend fun enableRecovery( override suspend fun enableRecovery(
waitForBackupsToUpload: Boolean, waitForBackupsToUpload: Boolean,
): Result<Unit> = withContext(dispatchers.io) { ): Result<Unit> = withContext(dispatchers.io) {
runCatching { runCatchingExceptions {
service.enableRecovery( service.enableRecovery(
waitForBackupsToUpload = waitForBackupsToUpload, waitForBackupsToUpload = waitForBackupsToUpload,
progressListener = object : EnableRecoveryProgressListener { progressListener = object : EnableRecoveryProgressListener {
@ -124,14 +125,14 @@ internal class RustEncryptionService(
} }
override suspend fun doesBackupExistOnServer(): Result<Boolean> = withContext(dispatchers.io) { override suspend fun doesBackupExistOnServer(): Result<Boolean> = withContext(dispatchers.io) {
runCatching { runCatchingExceptions {
service.backupExistsOnServer() service.backupExistsOnServer()
} }
} }
override fun waitForBackupUploadSteadyState(): Flow<BackupUploadState> { override fun waitForBackupUploadSteadyState(): Flow<BackupUploadState> {
return callbackFlow { return callbackFlow {
runCatching { runCatchingExceptions {
service.waitForBackupUploadSteadyState( service.waitForBackupUploadSteadyState(
progressListener = object : BackupSteadyStateListener { progressListener = object : BackupSteadyStateListener {
override fun onUpdate(status: RustBackupUploadState) { override fun onUpdate(status: RustBackupUploadState) {
@ -155,7 +156,7 @@ internal class RustEncryptionService(
} }
override suspend fun disableRecovery(): Result<Unit> = withContext(dispatchers.io) { override suspend fun disableRecovery(): Result<Unit> = withContext(dispatchers.io) {
runCatching { runCatchingExceptions {
service.disableRecovery() service.disableRecovery()
}.mapFailure { }.mapFailure {
it.mapRecoveryException() it.mapRecoveryException()
@ -163,7 +164,7 @@ internal class RustEncryptionService(
} }
private suspend fun isLastDevice(): Result<Boolean> = withContext(dispatchers.io) { private suspend fun isLastDevice(): Result<Boolean> = withContext(dispatchers.io) {
runCatching { runCatchingExceptions {
service.isLastDevice() service.isLastDevice()
}.mapFailure { }.mapFailure {
it.mapRecoveryException() it.mapRecoveryException()
@ -171,7 +172,7 @@ internal class RustEncryptionService(
} }
override suspend fun resetRecoveryKey(): Result<String> = withContext(dispatchers.io) { override suspend fun resetRecoveryKey(): Result<String> = withContext(dispatchers.io) {
runCatching { runCatchingExceptions {
service.resetRecoveryKey() service.resetRecoveryKey()
}.mapFailure { }.mapFailure {
it.mapRecoveryException() it.mapRecoveryException()
@ -179,7 +180,7 @@ internal class RustEncryptionService(
} }
override suspend fun recover(recoveryKey: String): Result<Unit> = withContext(dispatchers.io) { override suspend fun recover(recoveryKey: String): Result<Unit> = withContext(dispatchers.io) {
runCatching { runCatchingExceptions {
service.recover(recoveryKey) service.recover(recoveryKey)
}.mapFailure { }.mapFailure {
it.mapRecoveryException() it.mapRecoveryException()
@ -187,34 +188,34 @@ internal class RustEncryptionService(
} }
override suspend fun deviceCurve25519(): String? { override suspend fun deviceCurve25519(): String? {
return runCatching { service.curve25519Key() }.getOrNull() return runCatchingExceptions { service.curve25519Key() }.getOrNull()
} }
override suspend fun deviceEd25519(): String? { override suspend fun deviceEd25519(): String? {
return runCatching { service.ed25519Key() }.getOrNull() return runCatchingExceptions { service.ed25519Key() }.getOrNull()
} }
override suspend fun startIdentityReset(): Result<IdentityResetHandle?> { override suspend fun startIdentityReset(): Result<IdentityResetHandle?> {
return runCatching { return runCatchingExceptions {
service.resetIdentity() service.resetIdentity()
}.flatMap { handle -> }.flatMap { handle ->
RustIdentityResetHandleFactory.create(sessionId, handle) RustIdentityResetHandleFactory.create(sessionId, handle)
} }
} }
override suspend fun isUserVerified(userId: UserId): Result<Boolean> = runCatching { override suspend fun isUserVerified(userId: UserId): Result<Boolean> = runCatchingExceptions {
getUserIdentityInternal(userId).isVerified() getUserIdentityInternal(userId).isVerified()
} }
override suspend fun pinUserIdentity(userId: UserId): Result<Unit> = runCatching { override suspend fun pinUserIdentity(userId: UserId): Result<Unit> = runCatchingExceptions {
getUserIdentityInternal(userId).pin() getUserIdentityInternal(userId).pin()
} }
override suspend fun withdrawVerification(userId: UserId): Result<Unit> = runCatching { override suspend fun withdrawVerification(userId: UserId): Result<Unit> = runCatchingExceptions {
getUserIdentityInternal(userId).withdrawVerification() getUserIdentityInternal(userId).withdrawVerification()
} }
override suspend fun getUserIdentity(userId: UserId): Result<IdentityState?> = runCatching { override suspend fun getUserIdentity(userId: UserId): Result<IdentityState?> = runCatchingExceptions {
val identity = getUserIdentityInternal(userId) val identity = getUserIdentityInternal(userId)
val isVerified = identity.isVerified() val isVerified = identity.isVerified()
when { when {

View file

@ -7,6 +7,7 @@
package io.element.android.libraries.matrix.impl.encryption package io.element.android.libraries.matrix.impl.encryption
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.encryption.IdentityOidcResetHandle import io.element.android.libraries.matrix.api.encryption.IdentityOidcResetHandle
import io.element.android.libraries.matrix.api.encryption.IdentityPasswordResetHandle import io.element.android.libraries.matrix.api.encryption.IdentityPasswordResetHandle
@ -20,7 +21,7 @@ object RustIdentityResetHandleFactory {
userId: UserId, userId: UserId,
identityResetHandle: org.matrix.rustcomponents.sdk.IdentityResetHandle? identityResetHandle: org.matrix.rustcomponents.sdk.IdentityResetHandle?
): Result<IdentityResetHandle?> { ): Result<IdentityResetHandle?> {
return runCatching { return runCatchingExceptions {
identityResetHandle?.let { identityResetHandle?.let {
when (val authType = identityResetHandle.authType()) { when (val authType = identityResetHandle.authType()) {
is CrossSigningResetAuthType.Oidc -> RustOidcIdentityResetHandle(identityResetHandle, authType.info.approvalUrl) is CrossSigningResetAuthType.Oidc -> RustOidcIdentityResetHandle(identityResetHandle, authType.info.approvalUrl)
@ -37,7 +38,7 @@ class RustPasswordIdentityResetHandle(
private val identityResetHandle: org.matrix.rustcomponents.sdk.IdentityResetHandle, private val identityResetHandle: org.matrix.rustcomponents.sdk.IdentityResetHandle,
) : IdentityPasswordResetHandle { ) : IdentityPasswordResetHandle {
override suspend fun resetPassword(password: String): Result<Unit> { override suspend fun resetPassword(password: String): Result<Unit> {
return runCatching { identityResetHandle.reset(AuthData.Password(AuthDataPasswordDetails(userId.value, password))) } return runCatchingExceptions { identityResetHandle.reset(AuthData.Password(AuthDataPasswordDetails(userId.value, password))) }
} }
override suspend fun cancel() { override suspend fun cancel() {
@ -50,7 +51,7 @@ class RustOidcIdentityResetHandle(
override val url: String, override val url: String,
) : IdentityOidcResetHandle { ) : IdentityOidcResetHandle {
override suspend fun resetOidc(): Result<Unit> { override suspend fun resetOidc(): Result<Unit> {
return runCatching { identityResetHandle.reset(null) } return runCatchingExceptions { identityResetHandle.reset(null) }
} }
override suspend fun cancel() { override suspend fun cancel() {

View file

@ -8,6 +8,7 @@
package io.element.android.libraries.matrix.impl.media package io.element.android.libraries.matrix.impl.media
import io.element.android.libraries.androidutils.file.safeDelete import io.element.android.libraries.androidutils.file.safeDelete
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.matrix.api.media.MediaUploadHandler import io.element.android.libraries.matrix.api.media.MediaUploadHandler
import org.matrix.rustcomponents.sdk.SendAttachmentJoinHandle import org.matrix.rustcomponents.sdk.SendAttachmentJoinHandle
import java.io.File import java.io.File
@ -17,7 +18,7 @@ class MediaUploadHandlerImpl(
private val sendAttachmentJoinHandle: SendAttachmentJoinHandle, private val sendAttachmentJoinHandle: SendAttachmentJoinHandle,
) : MediaUploadHandler { ) : MediaUploadHandler {
override suspend fun await(): Result<Unit> = override suspend fun await(): Result<Unit> =
runCatching { runCatchingExceptions {
sendAttachmentJoinHandle.join() sendAttachmentJoinHandle.join()
} }
.also { cleanUpFiles() } .also { cleanUpFiles() }

View file

@ -8,6 +8,7 @@
package io.element.android.libraries.matrix.impl.media package io.element.android.libraries.matrix.impl.media
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
import io.element.android.libraries.matrix.api.media.MediaFile import io.element.android.libraries.matrix.api.media.MediaFile
@ -34,7 +35,7 @@ class RustMediaLoader(
@OptIn(ExperimentalUnsignedTypes::class) @OptIn(ExperimentalUnsignedTypes::class)
override suspend fun loadMediaContent(source: MediaSource): Result<ByteArray> = override suspend fun loadMediaContent(source: MediaSource): Result<ByteArray> =
withContext(mediaDispatcher) { withContext(mediaDispatcher) {
runCatching { runCatchingExceptions {
source.toRustMediaSource().use { source -> source.toRustMediaSource().use { source ->
innerClient.getMediaContent(source) innerClient.getMediaContent(source)
} }
@ -48,7 +49,7 @@ class RustMediaLoader(
height: Long height: Long
): Result<ByteArray> = ): Result<ByteArray> =
withContext(mediaDispatcher) { withContext(mediaDispatcher) {
runCatching { runCatchingExceptions {
source.toRustMediaSource().use { mediaSource -> source.toRustMediaSource().use { mediaSource ->
innerClient.getMediaThumbnail( innerClient.getMediaThumbnail(
mediaSource = mediaSource, mediaSource = mediaSource,
@ -66,7 +67,7 @@ class RustMediaLoader(
useCache: Boolean, useCache: Boolean,
): Result<MediaFile> = ): Result<MediaFile> =
withContext(mediaDispatcher) { withContext(mediaDispatcher) {
runCatching { runCatchingExceptions {
source.toRustMediaSource().use { mediaSource -> source.toRustMediaSource().use { mediaSource ->
val mediaFile = innerClient.getMediaFile( val mediaFile = innerClient.getMediaFile(
mediaSource = mediaSource, mediaSource = mediaSource,

View file

@ -8,6 +8,7 @@
package io.element.android.libraries.matrix.impl.notification package io.element.android.libraries.matrix.impl.notification
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.matrix.api.core.EventId 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.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
@ -30,7 +31,7 @@ class RustNotificationService(
override suspend fun getNotifications( override suspend fun getNotifications(
ids: Map<RoomId, List<EventId>> ids: Map<RoomId, List<EventId>>
): Result<Map<EventId, NotificationData>> = withContext(dispatchers.io) { ): Result<Map<EventId, NotificationData>> = withContext(dispatchers.io) {
runCatching { runCatchingExceptions {
val requests = ids.map { (roomId, eventIds) -> val requests = ids.map { (roomId, eventIds) ->
NotificationItemsRequest( NotificationItemsRequest(
roomId = roomId.value, roomId = roomId.value,

View file

@ -9,6 +9,7 @@ package io.element.android.libraries.matrix.impl.notificationsettings
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.coroutine.suspendLazy import io.element.android.libraries.core.coroutine.suspendLazy
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.room.RoomNotificationMode
@ -48,12 +49,12 @@ class RustNotificationSettingsService(
} }
override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result<RoomNotificationSettings> = override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result<RoomNotificationSettings> =
runCatching { runCatchingExceptions {
notificationSettings.await().getRoomNotificationSettings(roomId.value, isEncrypted, isOneToOne).let(RoomNotificationSettingsMapper::map) notificationSettings.await().getRoomNotificationSettings(roomId.value, isEncrypted, isOneToOne).let(RoomNotificationSettingsMapper::map)
} }
override suspend fun getDefaultRoomNotificationMode(isEncrypted: Boolean, isOneToOne: Boolean): Result<RoomNotificationMode> = override suspend fun getDefaultRoomNotificationMode(isEncrypted: Boolean, isOneToOne: Boolean): Result<RoomNotificationMode> =
runCatching { runCatchingExceptions {
notificationSettings.await().getDefaultRoomNotificationMode(isEncrypted, isOneToOne).let(RoomNotificationSettingsMapper::mapMode) notificationSettings.await().getDefaultRoomNotificationMode(isEncrypted, isOneToOne).let(RoomNotificationSettingsMapper::mapMode)
} }
@ -62,7 +63,7 @@ class RustNotificationSettingsService(
mode: RoomNotificationMode, mode: RoomNotificationMode,
isOneToOne: Boolean isOneToOne: Boolean
): Result<Unit> = withContext(dispatchers.io) { ): Result<Unit> = withContext(dispatchers.io) {
runCatching { runCatchingExceptions {
try { try {
notificationSettings.await().setDefaultRoomNotificationMode(isEncrypted, isOneToOne, mode.let(RoomNotificationSettingsMapper::mapMode)) notificationSettings.await().setDefaultRoomNotificationMode(isEncrypted, isOneToOne, mode.let(RoomNotificationSettingsMapper::mapMode))
} catch (exception: NotificationSettingsException.RuleNotFound) { } catch (exception: NotificationSettingsException.RuleNotFound) {
@ -74,13 +75,13 @@ class RustNotificationSettingsService(
} }
override suspend fun setRoomNotificationMode(roomId: RoomId, mode: RoomNotificationMode): Result<Unit> = withContext(dispatchers.io) { override suspend fun setRoomNotificationMode(roomId: RoomId, mode: RoomNotificationMode): Result<Unit> = withContext(dispatchers.io) {
runCatching { runCatchingExceptions {
notificationSettings.await().setRoomNotificationMode(roomId.value, mode.let(RoomNotificationSettingsMapper::mapMode)) notificationSettings.await().setRoomNotificationMode(roomId.value, mode.let(RoomNotificationSettingsMapper::mapMode))
} }
} }
override suspend fun restoreDefaultRoomNotificationMode(roomId: RoomId): Result<Unit> = withContext(dispatchers.io) { override suspend fun restoreDefaultRoomNotificationMode(roomId: RoomId): Result<Unit> = withContext(dispatchers.io) {
runCatching { runCatchingExceptions {
notificationSettings.await().restoreDefaultRoomNotificationMode(roomId.value) notificationSettings.await().restoreDefaultRoomNotificationMode(roomId.value)
} }
} }
@ -88,54 +89,54 @@ class RustNotificationSettingsService(
override suspend fun muteRoom(roomId: RoomId): Result<Unit> = setRoomNotificationMode(roomId, RoomNotificationMode.MUTE) override suspend fun muteRoom(roomId: RoomId): Result<Unit> = setRoomNotificationMode(roomId, RoomNotificationMode.MUTE)
override suspend fun unmuteRoom(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean) = withContext(dispatchers.io) { override suspend fun unmuteRoom(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean) = withContext(dispatchers.io) {
runCatching { runCatchingExceptions {
notificationSettings.await().unmuteRoom(roomId.value, isEncrypted, isOneToOne) notificationSettings.await().unmuteRoom(roomId.value, isEncrypted, isOneToOne)
} }
} }
override suspend fun isRoomMentionEnabled(): Result<Boolean> = withContext(dispatchers.io) { override suspend fun isRoomMentionEnabled(): Result<Boolean> = withContext(dispatchers.io) {
runCatching { runCatchingExceptions {
notificationSettings.await().isRoomMentionEnabled() notificationSettings.await().isRoomMentionEnabled()
} }
} }
override suspend fun setRoomMentionEnabled(enabled: Boolean): Result<Unit> = withContext(dispatchers.io) { override suspend fun setRoomMentionEnabled(enabled: Boolean): Result<Unit> = withContext(dispatchers.io) {
runCatching { runCatchingExceptions {
notificationSettings.await().setRoomMentionEnabled(enabled) notificationSettings.await().setRoomMentionEnabled(enabled)
} }
} }
override suspend fun isCallEnabled(): Result<Boolean> = withContext(dispatchers.io) { override suspend fun isCallEnabled(): Result<Boolean> = withContext(dispatchers.io) {
runCatching { runCatchingExceptions {
notificationSettings.await().isCallEnabled() notificationSettings.await().isCallEnabled()
} }
} }
override suspend fun setCallEnabled(enabled: Boolean): Result<Unit> = withContext(dispatchers.io) { override suspend fun setCallEnabled(enabled: Boolean): Result<Unit> = withContext(dispatchers.io) {
runCatching { runCatchingExceptions {
notificationSettings.await().setCallEnabled(enabled) notificationSettings.await().setCallEnabled(enabled)
} }
} }
override suspend fun isInviteForMeEnabled(): Result<Boolean> = withContext(dispatchers.io) { override suspend fun isInviteForMeEnabled(): Result<Boolean> = withContext(dispatchers.io) {
runCatching { runCatchingExceptions {
notificationSettings.await().isInviteForMeEnabled() notificationSettings.await().isInviteForMeEnabled()
} }
} }
override suspend fun setInviteForMeEnabled(enabled: Boolean): Result<Unit> = withContext(dispatchers.io) { override suspend fun setInviteForMeEnabled(enabled: Boolean): Result<Unit> = withContext(dispatchers.io) {
runCatching { runCatchingExceptions {
notificationSettings.await().setInviteForMeEnabled(enabled) notificationSettings.await().setInviteForMeEnabled(enabled)
} }
} }
override suspend fun getRoomsWithUserDefinedRules(): Result<List<String>> = override suspend fun getRoomsWithUserDefinedRules(): Result<List<String>> =
runCatching { runCatchingExceptions {
notificationSettings.await().getRoomsWithUserDefinedRules(enabled = true) notificationSettings.await().getRoomsWithUserDefinedRules(enabled = true)
} }
override suspend fun canHomeServerPushEncryptedEventsToDevice(): Result<Boolean> = override suspend fun canHomeServerPushEncryptedEventsToDevice(): Result<Boolean> =
runCatching { runCatchingExceptions {
notificationSettings.await().canPushEncryptedEventToDevice() notificationSettings.await().canPushEncryptedEventToDevice()
} }
} }

View file

@ -8,6 +8,7 @@
package io.element.android.libraries.matrix.impl.permalink package io.element.android.libraries.matrix.impl.permalink
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.api.core.MatrixPatterns import io.element.android.libraries.matrix.api.core.MatrixPatterns
import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomAlias
@ -24,7 +25,7 @@ class DefaultPermalinkBuilder @Inject constructor() : PermalinkBuilder {
if (!MatrixPatterns.isUserId(userId.value)) { if (!MatrixPatterns.isUserId(userId.value)) {
return Result.failure(PermalinkBuilderError.InvalidData) return Result.failure(PermalinkBuilderError.InvalidData)
} }
return runCatching { return runCatchingExceptions {
matrixToUserPermalink(userId.value) matrixToUserPermalink(userId.value)
} }
} }
@ -33,7 +34,7 @@ class DefaultPermalinkBuilder @Inject constructor() : PermalinkBuilder {
if (!MatrixPatterns.isRoomAlias(roomAlias.value)) { if (!MatrixPatterns.isRoomAlias(roomAlias.value)) {
return Result.failure(PermalinkBuilderError.InvalidData) return Result.failure(PermalinkBuilderError.InvalidData)
} }
return runCatching { return runCatchingExceptions {
matrixToRoomAliasPermalink(roomAlias.value) matrixToRoomAliasPermalink(roomAlias.value)
} }
} }

View file

@ -9,6 +9,7 @@ package io.element.android.libraries.matrix.impl.permalink
import androidx.core.net.toUri import androidx.core.net.toUri
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomAlias
@ -44,7 +45,7 @@ class DefaultPermalinkParser @Inject constructor(
// so convert URI to matrix.to to simplify parsing process // so convert URI to matrix.to to simplify parsing process
val matrixToUri = matrixToConverter.convert(uri) ?: return PermalinkData.FallbackLink(uri) val matrixToUri = matrixToConverter.convert(uri) ?: return PermalinkData.FallbackLink(uri)
val result = runCatching { val result = runCatchingExceptions {
parseMatrixEntityFrom(matrixToUri.toString()) parseMatrixEntityFrom(matrixToUri.toString())
}.getOrNull() }.getOrNull()
return if (result == null) { return if (result == null) {

View file

@ -9,6 +9,7 @@ package io.element.android.libraries.matrix.impl.pushers
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.extensions.mapFailure import io.element.android.libraries.core.extensions.mapFailure
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.matrix.api.pusher.PushersService import io.element.android.libraries.matrix.api.pusher.PushersService
import io.element.android.libraries.matrix.api.pusher.SetHttpPusherData import io.element.android.libraries.matrix.api.pusher.SetHttpPusherData
import io.element.android.libraries.matrix.api.pusher.UnsetHttpPusherData import io.element.android.libraries.matrix.api.pusher.UnsetHttpPusherData
@ -26,7 +27,7 @@ class RustPushersService(
) : PushersService { ) : PushersService {
override suspend fun setHttpPusher(setHttpPusherData: SetHttpPusherData): Result<Unit> { override suspend fun setHttpPusher(setHttpPusherData: SetHttpPusherData): Result<Unit> {
return withContext(dispatchers.io) { return withContext(dispatchers.io) {
runCatching { runCatchingExceptions {
client.setPusher( client.setPusher(
identifiers = PusherIdentifiers( identifiers = PusherIdentifiers(
pushkey = setHttpPusherData.pushKey, pushkey = setHttpPusherData.pushKey,
@ -51,7 +52,7 @@ class RustPushersService(
override suspend fun unsetHttpPusher(unsetHttpPusherData: UnsetHttpPusherData): Result<Unit> { override suspend fun unsetHttpPusher(unsetHttpPusherData: UnsetHttpPusherData): Result<Unit> {
return withContext(dispatchers.io) { return withContext(dispatchers.io) {
runCatching { runCatchingExceptions {
client.deletePusher( client.deletePusher(
identifiers = PusherIdentifiers( identifiers = PusherIdentifiers(
pushkey = unsetHttpPusherData.pushKey, pushkey = unsetHttpPusherData.pushKey,

View file

@ -10,6 +10,7 @@ package io.element.android.libraries.matrix.impl.room
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.coroutine.childScope import io.element.android.libraries.core.coroutine.childScope
import io.element.android.libraries.core.extensions.mapFailure import io.element.android.libraries.core.extensions.mapFailure
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.featureflag.api.FeatureFlagService 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.DeviceId
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
@ -204,7 +205,7 @@ class JoinedRustRoom(
// Track read receipts only for focused timeline for performance optimization // Track read receipts only for focused timeline for performance optimization
val trackReadReceipts = createTimelineParams is CreateTimelineParams.Focused val trackReadReceipts = createTimelineParams is CreateTimelineParams.Focused
runCatching { runCatchingExceptions {
innerRoom.timelineWithConfiguration( innerRoom.timelineWithConfiguration(
configuration = TimelineConfiguration( configuration = TimelineConfiguration(
focus = focus, focus = focus,
@ -243,7 +244,7 @@ class JoinedRustRoom(
htmlBody: String?, htmlBody: String?,
intentionalMentions: List<IntentionalMention> intentionalMentions: List<IntentionalMention>
): Result<Unit> = withContext(roomDispatcher) { ): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
MessageEventContent.from(body, htmlBody, intentionalMentions).use { newContent -> MessageEventContent.from(body, htmlBody, intentionalMentions).use { newContent ->
innerRoom.edit(eventId.value, newContent) innerRoom.edit(eventId.value, newContent)
} }
@ -251,43 +252,43 @@ class JoinedRustRoom(
} }
override suspend fun typingNotice(isTyping: Boolean) = withContext(roomDispatcher) { override suspend fun typingNotice(isTyping: Boolean) = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.typingNotice(isTyping) innerRoom.typingNotice(isTyping)
} }
} }
override suspend fun inviteUserById(id: UserId): Result<Unit> = withContext(roomDispatcher) { override suspend fun inviteUserById(id: UserId): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.inviteUserById(id.value) innerRoom.inviteUserById(id.value)
} }
} }
override suspend fun updateAvatar(mimeType: String, data: ByteArray): Result<Unit> = withContext(roomDispatcher) { override suspend fun updateAvatar(mimeType: String, data: ByteArray): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.uploadAvatar(mimeType, data, null) innerRoom.uploadAvatar(mimeType, data, null)
} }
} }
override suspend fun removeAvatar(): Result<Unit> = withContext(roomDispatcher) { override suspend fun removeAvatar(): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.removeAvatar() innerRoom.removeAvatar()
} }
} }
override suspend fun setName(name: String): Result<Unit> = withContext(roomDispatcher) { override suspend fun setName(name: String): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.setName(name) innerRoom.setName(name)
} }
} }
override suspend fun setTopic(topic: String): Result<Unit> = withContext(roomDispatcher) { override suspend fun setTopic(topic: String): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.setTopic(topic) innerRoom.setTopic(topic)
} }
} }
override suspend fun reportContent(eventId: EventId, reason: String, blockUserId: UserId?): Result<Unit> = withContext(roomDispatcher) { override suspend fun reportContent(eventId: EventId, reason: String, blockUserId: UserId?): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.reportContent(eventId = eventId.value, score = null, reason = reason) innerRoom.reportContent(eventId = eventId.value, score = null, reason = reason)
if (blockUserId != null) { if (blockUserId != null) {
innerRoom.ignoreUser(blockUserId.value) innerRoom.ignoreUser(blockUserId.value)
@ -299,7 +300,7 @@ class JoinedRustRoom(
val currentState = roomNotificationSettingsStateFlow.value val currentState = roomNotificationSettingsStateFlow.value
val currentRoomNotificationSettings = currentState.roomNotificationSettings() val currentRoomNotificationSettings = currentState.roomNotificationSettings()
roomNotificationSettingsStateFlow.value = RoomNotificationSettingsState.Pending(prevRoomNotificationSettings = currentRoomNotificationSettings) roomNotificationSettingsStateFlow.value = RoomNotificationSettingsState.Pending(prevRoomNotificationSettings = currentRoomNotificationSettings)
runCatching { runCatchingExceptions {
val isEncrypted = roomInfoFlow.value.isEncrypted ?: getUpdatedIsEncrypted().getOrThrow() val isEncrypted = roomInfoFlow.value.isEncrypted ?: getUpdatedIsEncrypted().getOrThrow()
notificationSettingsService.getRoomNotificationSettings(roomId, isEncrypted, isOneToOne).getOrThrow() notificationSettingsService.getRoomNotificationSettings(roomId, isEncrypted, isOneToOne).getOrThrow()
}.map { }.map {
@ -313,56 +314,56 @@ class JoinedRustRoom(
} }
override suspend fun updateCanonicalAlias(canonicalAlias: RoomAlias?, alternativeAliases: List<RoomAlias>): Result<Unit> = withContext(roomDispatcher) { override suspend fun updateCanonicalAlias(canonicalAlias: RoomAlias?, alternativeAliases: List<RoomAlias>): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.updateCanonicalAlias(canonicalAlias?.value, alternativeAliases.map { it.value }) innerRoom.updateCanonicalAlias(canonicalAlias?.value, alternativeAliases.map { it.value })
} }
} }
override suspend fun publishRoomAliasInRoomDirectory(roomAlias: RoomAlias): Result<Boolean> = withContext(roomDispatcher) { override suspend fun publishRoomAliasInRoomDirectory(roomAlias: RoomAlias): Result<Boolean> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.publishRoomAliasInRoomDirectory(roomAlias.value) innerRoom.publishRoomAliasInRoomDirectory(roomAlias.value)
} }
} }
override suspend fun removeRoomAliasFromRoomDirectory(roomAlias: RoomAlias): Result<Boolean> = withContext(roomDispatcher) { override suspend fun removeRoomAliasFromRoomDirectory(roomAlias: RoomAlias): Result<Boolean> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.removeRoomAliasFromRoomDirectory(roomAlias.value) innerRoom.removeRoomAliasFromRoomDirectory(roomAlias.value)
} }
} }
override suspend fun updateRoomVisibility(roomVisibility: RoomVisibility): Result<Unit> = withContext(roomDispatcher) { override suspend fun updateRoomVisibility(roomVisibility: RoomVisibility): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.updateRoomVisibility(roomVisibility.map()) innerRoom.updateRoomVisibility(roomVisibility.map())
} }
} }
override suspend fun updateHistoryVisibility(historyVisibility: RoomHistoryVisibility): Result<Unit> = withContext(roomDispatcher) { override suspend fun updateHistoryVisibility(historyVisibility: RoomHistoryVisibility): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.updateHistoryVisibility(historyVisibility.map()) innerRoom.updateHistoryVisibility(historyVisibility.map())
} }
} }
override suspend fun enableEncryption(): Result<Unit> = withContext(roomDispatcher) { override suspend fun enableEncryption(): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.enableEncryption() innerRoom.enableEncryption()
} }
} }
override suspend fun updateJoinRule(joinRule: JoinRule): Result<Unit> = withContext(roomDispatcher) { override suspend fun updateJoinRule(joinRule: JoinRule): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.updateJoinRules(joinRule.map()) innerRoom.updateJoinRules(joinRule.map())
} }
} }
override suspend fun updateUsersRoles(changes: List<UserRoleChange>): Result<Unit> { override suspend fun updateUsersRoles(changes: List<UserRoleChange>): Result<Unit> {
return runCatching { return runCatchingExceptions {
val powerLevelChanges = changes.map { UserPowerLevelUpdate(it.userId.value, it.powerLevel) } val powerLevelChanges = changes.map { UserPowerLevelUpdate(it.userId.value, it.powerLevel) }
innerRoom.updatePowerLevelsForUsers(powerLevelChanges) innerRoom.updatePowerLevelsForUsers(powerLevelChanges)
} }
} }
override suspend fun updatePowerLevels(roomPowerLevels: RoomPowerLevels): Result<Unit> = withContext(roomDispatcher) { override suspend fun updatePowerLevels(roomPowerLevels: RoomPowerLevels): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
val changes = RoomPowerLevelChanges( val changes = RoomPowerLevelChanges(
ban = roomPowerLevels.ban, ban = roomPowerLevels.ban,
invite = roomPowerLevels.invite, invite = roomPowerLevels.invite,
@ -378,25 +379,25 @@ class JoinedRustRoom(
} }
override suspend fun resetPowerLevels(): Result<RoomPowerLevels> = withContext(roomDispatcher) { override suspend fun resetPowerLevels(): Result<RoomPowerLevels> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
RoomPowerLevelsMapper.map(innerRoom.resetPowerLevels()) RoomPowerLevelsMapper.map(innerRoom.resetPowerLevels())
} }
} }
override suspend fun kickUser(userId: UserId, reason: String?): Result<Unit> = withContext(roomDispatcher) { override suspend fun kickUser(userId: UserId, reason: String?): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.kickUser(userId.value, reason) innerRoom.kickUser(userId.value, reason)
} }
} }
override suspend fun banUser(userId: UserId, reason: String?): Result<Unit> = withContext(roomDispatcher) { override suspend fun banUser(userId: UserId, reason: String?): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.banUser(userId.value, reason) innerRoom.banUser(userId.value, reason)
} }
} }
override suspend fun unbanUser(userId: UserId, reason: String?): Result<Unit> = withContext(roomDispatcher) { override suspend fun unbanUser(userId: UserId, reason: String?): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.unbanUser(userId.value, reason) innerRoom.unbanUser(userId.value, reason)
} }
} }
@ -407,13 +408,13 @@ class JoinedRustRoom(
languageTag: String?, languageTag: String?,
theme: String?, theme: String?,
) = withContext(roomDispatcher) { ) = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
widgetSettings.generateWidgetWebViewUrl(innerRoom, clientId, languageTag, theme) widgetSettings.generateWidgetWebViewUrl(innerRoom, clientId, languageTag, theme)
} }
} }
override fun getWidgetDriver(widgetSettings: MatrixWidgetSettings): Result<MatrixWidgetDriver> { override fun getWidgetDriver(widgetSettings: MatrixWidgetSettings): Result<MatrixWidgetDriver> {
return runCatching { return runCatchingExceptions {
RustWidgetDriver( RustWidgetDriver(
widgetSettings = widgetSettings, widgetSettings = widgetSettings,
room = innerRoom, room = innerRoom,
@ -427,7 +428,7 @@ class JoinedRustRoom(
} }
override suspend fun sendCallNotificationIfNeeded(): Result<Unit> = withContext(roomDispatcher) { override suspend fun sendCallNotificationIfNeeded(): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.sendCallNotificationIfNeeded() innerRoom.sendCallNotificationIfNeeded()
} }
} }
@ -435,14 +436,14 @@ class JoinedRustRoom(
override suspend fun setSendQueueEnabled(enabled: Boolean) { override suspend fun setSendQueueEnabled(enabled: Boolean) {
withContext(roomDispatcher) { withContext(roomDispatcher) {
Timber.d("setSendQueuesEnabled: $enabled") Timber.d("setSendQueuesEnabled: $enabled")
runCatching { runCatchingExceptions {
innerRoom.enableSendQueue(enabled) innerRoom.enableSendQueue(enabled)
} }
} }
} }
override suspend fun ignoreDeviceTrustAndResend(devices: Map<UserId, List<DeviceId>>, sendHandle: SendHandle) = withContext(roomDispatcher) { override suspend fun ignoreDeviceTrustAndResend(devices: Map<UserId, List<DeviceId>>, sendHandle: SendHandle) = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.ignoreDeviceTrustAndResend( innerRoom.ignoreDeviceTrustAndResend(
devices = devices.entries.associate { entry -> devices = devices.entries.associate { entry ->
entry.key.value to entry.value.map { it.value } entry.key.value to entry.value.map { it.value }
@ -453,7 +454,7 @@ class JoinedRustRoom(
} }
override suspend fun withdrawVerificationAndResend(userIds: List<UserId>, sendHandle: SendHandle) = withContext(roomDispatcher) { override suspend fun withdrawVerificationAndResend(userIds: List<UserId>, sendHandle: SendHandle) = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.withdrawVerificationAndResend( innerRoom.withdrawVerificationAndResend(
userIds = userIds.map { it.value }, userIds = userIds.map { it.value },
sendHandle = (sendHandle as RustSendHandle).inner, sendHandle = (sendHandle as RustSendHandle).inner,

View file

@ -8,6 +8,7 @@
package io.element.android.libraries.matrix.impl.room package io.element.android.libraries.matrix.impl.room
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.room.NotJoinedRoom import io.element.android.libraries.matrix.api.room.NotJoinedRoom
import io.element.android.libraries.matrix.api.room.RoomMembershipDetails import io.element.android.libraries.matrix.api.room.RoomMembershipDetails
@ -20,8 +21,8 @@ class NotJoinedRustRoom(
override val localRoom: RustBaseRoom?, override val localRoom: RustBaseRoom?,
override val previewInfo: RoomPreviewInfo, override val previewInfo: RoomPreviewInfo,
) : NotJoinedRoom { ) : NotJoinedRoom {
override suspend fun membershipDetails(): Result<RoomMembershipDetails?> = runCatching { override suspend fun membershipDetails(): Result<RoomMembershipDetails?> = runCatchingExceptions {
val room = localRoom?.innerRoom ?: return@runCatching null val room = localRoom?.innerRoom ?: return@runCatchingExceptions null
val (ownMember, senderInfo) = room.memberWithSenderInfo(sessionId.value) val (ownMember, senderInfo) = room.memberWithSenderInfo(sessionId.value)
RoomMembershipDetails( RoomMembershipDetails(
currentUserMember = RoomMemberMapper.map(ownMember), currentUserMember = RoomMemberMapper.map(ownMember),

View file

@ -8,6 +8,7 @@
package io.element.android.libraries.matrix.impl.room package io.element.android.libraries.matrix.impl.room
import io.element.android.libraries.core.coroutine.parallelMap import io.element.android.libraries.core.coroutine.parallelMap
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.matrix.api.core.EventId 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.core.RoomId
import io.element.android.libraries.matrix.api.room.ForwardEventException import io.element.android.libraries.matrix.api.room.ForwardEventException
@ -52,7 +53,7 @@ class RoomContentForwarder(
val failedForwardingTo = mutableSetOf<RoomId>() val failedForwardingTo = mutableSetOf<RoomId>()
targetRooms.parallelMap { room -> targetRooms.parallelMap { room ->
room.use { targetRoom -> room.use { targetRoom ->
runCatching { runCatchingExceptions {
// Sending a message requires a registered timeline listener // Sending a message requires a registered timeline listener
targetRoom.timeline().runWithTimelineListenerRegistered { targetRoom.timeline().runWithTimelineListenerRegistered {
withTimeout(timeoutMs.milliseconds) { withTimeout(timeoutMs.milliseconds) {

View file

@ -9,6 +9,7 @@ package io.element.android.libraries.matrix.impl.room
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.coroutine.childScope import io.element.android.libraries.core.coroutine.childScope
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.matrix.api.core.DeviceId 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.EventId
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
@ -90,7 +91,7 @@ class RustBaseRoom(
} }
override suspend fun getMembers(limit: Int) = withContext(roomDispatcher) { override suspend fun getMembers(limit: Int) = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.members().use { innerRoom.members().use {
it.nextChunk(limit.toUInt()).orEmpty().map { roomMember -> it.nextChunk(limit.toUInt()).orEmpty().map { roomMember ->
RoomMemberMapper.map(roomMember) RoomMemberMapper.map(roomMember)
@ -100,7 +101,7 @@ class RustBaseRoom(
} }
override suspend fun getUpdatedMember(userId: UserId): Result<RoomMember> = withContext(roomDispatcher) { override suspend fun getUpdatedMember(userId: UserId): Result<RoomMember> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
RoomMemberMapper.map(innerRoom.member(userId.value)) RoomMemberMapper.map(innerRoom.member(userId.value))
} }
} }
@ -113,32 +114,32 @@ class RustBaseRoom(
} }
override suspend fun userDisplayName(userId: UserId): Result<String?> = withContext(roomDispatcher) { override suspend fun userDisplayName(userId: UserId): Result<String?> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.memberDisplayName(userId.value) innerRoom.memberDisplayName(userId.value)
} }
} }
override suspend fun userRole(userId: UserId): Result<RoomMember.Role> = withContext(roomDispatcher) { override suspend fun userRole(userId: UserId): Result<RoomMember.Role> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
RoomMemberMapper.mapRole(innerRoom.suggestedRoleForUser(userId.value)) RoomMemberMapper.mapRole(innerRoom.suggestedRoleForUser(userId.value))
} }
} }
override suspend fun powerLevels(): Result<RoomPowerLevels> = withContext(roomDispatcher) { override suspend fun powerLevels(): Result<RoomPowerLevels> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
RoomPowerLevelsMapper.map(innerRoom.getPowerLevels()) RoomPowerLevelsMapper.map(innerRoom.getPowerLevels())
} }
} }
override suspend fun userAvatarUrl(userId: UserId): Result<String?> = withContext(roomDispatcher) { override suspend fun userAvatarUrl(userId: UserId): Result<String?> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.memberAvatarUrl(userId.value) innerRoom.memberAvatarUrl(userId.value)
} }
} }
override suspend fun leave(): Result<Unit> = withContext(roomDispatcher) { override suspend fun leave(): Result<Unit> = withContext(roomDispatcher) {
val membershipBeforeLeft = roomInfoFlow.value.currentUserMembership val membershipBeforeLeft = roomInfoFlow.value.currentUserMembership
runCatching { runCatchingExceptions {
innerRoom.leave() innerRoom.leave()
}.onSuccess { }.onSuccess {
roomMembershipObserver.notifyUserLeftRoom(roomId, membershipBeforeLeft) roomMembershipObserver.notifyUserLeftRoom(roomId, membershipBeforeLeft)
@ -146,142 +147,142 @@ class RustBaseRoom(
} }
override suspend fun join(): Result<Unit> = withContext(roomDispatcher) { override suspend fun join(): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.join() innerRoom.join()
} }
} }
override suspend fun forget(): Result<Unit> = withContext(roomDispatcher) { override suspend fun forget(): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.forget() innerRoom.forget()
} }
} }
override suspend fun canUserInvite(userId: UserId): Result<Boolean> = withContext(roomDispatcher) { override suspend fun canUserInvite(userId: UserId): Result<Boolean> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.canUserInvite(userId.value) innerRoom.canUserInvite(userId.value)
} }
} }
override suspend fun canUserKick(userId: UserId): Result<Boolean> = withContext(roomDispatcher) { override suspend fun canUserKick(userId: UserId): Result<Boolean> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.canUserKick(userId.value) innerRoom.canUserKick(userId.value)
} }
} }
override suspend fun canUserBan(userId: UserId): Result<Boolean> = withContext(roomDispatcher) { override suspend fun canUserBan(userId: UserId): Result<Boolean> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.canUserBan(userId.value) innerRoom.canUserBan(userId.value)
} }
} }
override suspend fun canUserRedactOwn(userId: UserId): Result<Boolean> = withContext(roomDispatcher) { override suspend fun canUserRedactOwn(userId: UserId): Result<Boolean> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.canUserRedactOwn(userId.value) innerRoom.canUserRedactOwn(userId.value)
} }
} }
override suspend fun canUserRedactOther(userId: UserId): Result<Boolean> = withContext(roomDispatcher) { override suspend fun canUserRedactOther(userId: UserId): Result<Boolean> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.canUserRedactOther(userId.value) innerRoom.canUserRedactOther(userId.value)
} }
} }
override suspend fun canUserSendState(userId: UserId, type: StateEventType): Result<Boolean> = withContext(roomDispatcher) { override suspend fun canUserSendState(userId: UserId, type: StateEventType): Result<Boolean> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.canUserSendState(userId.value, type.map()) innerRoom.canUserSendState(userId.value, type.map())
} }
} }
override suspend fun canUserSendMessage(userId: UserId, type: MessageEventType): Result<Boolean> = withContext(roomDispatcher) { override suspend fun canUserSendMessage(userId: UserId, type: MessageEventType): Result<Boolean> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.canUserSendMessage(userId.value, type.map()) innerRoom.canUserSendMessage(userId.value, type.map())
} }
} }
override suspend fun canUserTriggerRoomNotification(userId: UserId): Result<Boolean> = withContext(roomDispatcher) { override suspend fun canUserTriggerRoomNotification(userId: UserId): Result<Boolean> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.canUserTriggerRoomNotification(userId.value) innerRoom.canUserTriggerRoomNotification(userId.value)
} }
} }
override suspend fun canUserPinUnpin(userId: UserId): Result<Boolean> = withContext(roomDispatcher) { override suspend fun canUserPinUnpin(userId: UserId): Result<Boolean> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.canUserPinUnpin(userId.value) innerRoom.canUserPinUnpin(userId.value)
} }
} }
override suspend fun clearEventCacheStorage(): Result<Unit> = withContext(roomDispatcher) { override suspend fun clearEventCacheStorage(): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.clearEventCacheStorage() innerRoom.clearEventCacheStorage()
} }
} }
override suspend fun setIsFavorite(isFavorite: Boolean): Result<Unit> = withContext(roomDispatcher) { override suspend fun setIsFavorite(isFavorite: Boolean): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.setIsFavourite(isFavorite, null) innerRoom.setIsFavourite(isFavorite, null)
} }
} }
override suspend fun markAsRead(receiptType: ReceiptType): Result<Unit> = withContext(roomDispatcher) { override suspend fun markAsRead(receiptType: ReceiptType): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.markAsRead(receiptType.toRustReceiptType()) innerRoom.markAsRead(receiptType.toRustReceiptType())
} }
} }
override suspend fun setUnreadFlag(isUnread: Boolean): Result<Unit> = withContext(roomDispatcher) { override suspend fun setUnreadFlag(isUnread: Boolean): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.setUnreadFlag(isUnread) innerRoom.setUnreadFlag(isUnread)
} }
} }
override suspend fun getPermalink(): Result<String> = withContext(roomDispatcher) { override suspend fun getPermalink(): Result<String> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.matrixToPermalink() innerRoom.matrixToPermalink()
} }
} }
override suspend fun getPermalinkFor(eventId: EventId): Result<String> = withContext(roomDispatcher) { override suspend fun getPermalinkFor(eventId: EventId): Result<String> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.matrixToEventPermalink(eventId.value) innerRoom.matrixToEventPermalink(eventId.value)
} }
} }
override suspend fun getRoomVisibility(): Result<RoomVisibility> = withContext(roomDispatcher) { override suspend fun getRoomVisibility(): Result<RoomVisibility> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.getRoomVisibility().map() innerRoom.getRoomVisibility().map()
} }
} }
override suspend fun getUpdatedIsEncrypted(): Result<Boolean> = withContext(roomDispatcher) { override suspend fun getUpdatedIsEncrypted(): Result<Boolean> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
innerRoom.latestEncryptionState() == EncryptionState.ENCRYPTED innerRoom.latestEncryptionState() == EncryptionState.ENCRYPTED
} }
} }
override suspend fun saveComposerDraft(composerDraft: ComposerDraft): Result<Unit> = withContext(roomDispatcher) { override suspend fun saveComposerDraft(composerDraft: ComposerDraft): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
Timber.d("saveComposerDraft: $composerDraft into $roomId") Timber.d("saveComposerDraft: $composerDraft into $roomId")
innerRoom.saveComposerDraft(composerDraft.into()) innerRoom.saveComposerDraft(composerDraft.into())
} }
} }
override suspend fun loadComposerDraft(): Result<ComposerDraft?> = withContext(roomDispatcher) { override suspend fun loadComposerDraft(): Result<ComposerDraft?> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
Timber.d("loadComposerDraft for $roomId") Timber.d("loadComposerDraft for $roomId")
innerRoom.loadComposerDraft()?.into() innerRoom.loadComposerDraft()?.into()
} }
} }
override suspend fun clearComposerDraft(): Result<Unit> = withContext(roomDispatcher) { override suspend fun clearComposerDraft(): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
Timber.d("clearComposerDraft for $roomId") Timber.d("clearComposerDraft for $roomId")
innerRoom.clearComposerDraft() innerRoom.clearComposerDraft()
} }
} }
override suspend fun reportRoom(reason: String?): Result<Unit> = withContext(roomDispatcher) { override suspend fun reportRoom(reason: String?): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatchingExceptions {
Timber.d("reportRoom $roomId") Timber.d("reportRoom $roomId")
innerRoom.reportRoom(reason) innerRoom.reportRoom(reason)
} }

View file

@ -7,6 +7,7 @@
package io.element.android.libraries.matrix.impl.room.knock package io.element.android.libraries.matrix.impl.room.knock
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.matrix.api.core.EventId 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.core.UserId
import io.element.android.libraries.matrix.api.room.knock.KnockRequest import io.element.android.libraries.matrix.api.room.knock.KnockRequest
@ -23,19 +24,19 @@ class RustKnockRequest(
override val timestamp: Long? = inner.timestamp?.toLong() override val timestamp: Long? = inner.timestamp?.toLong()
override val isSeen: Boolean = inner.isSeen override val isSeen: Boolean = inner.isSeen
override suspend fun accept(): Result<Unit> = runCatching { override suspend fun accept(): Result<Unit> = runCatchingExceptions {
inner.actions.accept() inner.actions.accept()
} }
override suspend fun decline(reason: String?): Result<Unit> = runCatching { override suspend fun decline(reason: String?): Result<Unit> = runCatchingExceptions {
inner.actions.decline(reason) inner.actions.decline(reason)
} }
override suspend fun declineAndBan(reason: String?): Result<Unit> = runCatching { override suspend fun declineAndBan(reason: String?): Result<Unit> = runCatchingExceptions {
inner.actions.declineAndBan(reason) inner.actions.declineAndBan(reason)
} }
override suspend fun markAsSeen(): Result<Unit> = runCatching { override suspend fun markAsSeen(): Result<Unit> = runCatchingExceptions {
inner.actions.markAsSeen() inner.actions.markAsSeen()
} }
} }

View file

@ -116,7 +116,7 @@ internal fun RoomListServiceInterface.syncIndicator(): Flow<RoomListServiceSyncI
internal fun RoomListServiceInterface.roomOrNull(roomId: String): Room? { internal fun RoomListServiceInterface.roomOrNull(roomId: String): Room? {
return tryOrNull( return tryOrNull(
onError = { Timber.e(it, "Failed finding room with id=$roomId.") } onException = { Timber.e(it, "Failed finding room with id=$roomId.") }
) { ) {
room(roomId) room(roomId)
} }

View file

@ -8,6 +8,7 @@
package io.element.android.libraries.matrix.impl.sync package io.element.android.libraries.matrix.impl.sync
import io.element.android.libraries.core.coroutine.mapState import io.element.android.libraries.core.coroutine.mapState
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.matrix.api.sync.SyncService
import io.element.android.libraries.matrix.api.sync.SyncState import io.element.android.libraries.matrix.api.sync.SyncState
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
@ -33,10 +34,10 @@ class RustSyncService(
private val isServiceReady = AtomicBoolean(true) private val isServiceReady = AtomicBoolean(true)
override suspend fun startSync() = withContext(dispatcher) { override suspend fun startSync() = withContext(dispatcher) {
runCatching { runCatchingExceptions {
if (!isServiceReady.get()) { if (!isServiceReady.get()) {
Timber.d("Can't start sync: service is not ready") Timber.d("Can't start sync: service is not ready")
return@runCatching return@runCatchingExceptions
} }
Timber.i("Start sync") Timber.i("Start sync")
inner.start() inner.start()
@ -46,10 +47,10 @@ class RustSyncService(
} }
override suspend fun stopSync() = withContext(dispatcher) { override suspend fun stopSync() = withContext(dispatcher) {
runCatching { runCatchingExceptions {
if (!isServiceReady.get()) { if (!isServiceReady.get()) {
Timber.d("Can't stop sync: service is not ready") Timber.d("Can't stop sync: service is not ready")
return@runCatching return@runCatchingExceptions
} }
Timber.i("Stop sync") Timber.i("Stop sync")
inner.stop() inner.stop()

View file

@ -7,6 +7,7 @@
package io.element.android.libraries.matrix.impl.timeline package io.element.android.libraries.matrix.impl.timeline
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
@ -165,7 +166,7 @@ class RustTimeline(
override val membershipChangeEventReceived: Flow<Unit> = timelineDiffProcessor.membershipChangeEventReceived override val membershipChangeEventReceived: Flow<Unit> = timelineDiffProcessor.membershipChangeEventReceived
override suspend fun sendReadReceipt(eventId: EventId, receiptType: ReceiptType): Result<Unit> = withContext(dispatcher) { override suspend fun sendReadReceipt(eventId: EventId, receiptType: ReceiptType): Result<Unit> = withContext(dispatcher) {
runCatching { runCatchingExceptions {
inner.sendReadReceipt(receiptType.toRustReceiptType(), eventId.value) inner.sendReadReceipt(receiptType.toRustReceiptType(), eventId.value)
} }
} }
@ -181,7 +182,7 @@ class RustTimeline(
override suspend fun paginate(direction: Timeline.PaginationDirection): Result<Boolean> = withContext(NonCancellable) { override suspend fun paginate(direction: Timeline.PaginationDirection): Result<Boolean> = withContext(NonCancellable) {
withContext(dispatcher) { withContext(dispatcher) {
initLatch.await() initLatch.await()
runCatching { runCatchingExceptions {
if (!canPaginate(direction)) throw TimelineException.CannotPaginate if (!canPaginate(direction)) throw TimelineException.CannotPaginate
updatePaginationStatus(direction) { it.copy(isPaginating = true) } updatePaginationStatus(direction) { it.copy(isPaginating = true) }
when (direction) { when (direction) {
@ -275,14 +276,14 @@ class RustTimeline(
intentionalMentions: List<IntentionalMention>, intentionalMentions: List<IntentionalMention>,
): Result<Unit> = withContext(dispatcher) { ): Result<Unit> = withContext(dispatcher) {
MessageEventContent.from(body, htmlBody, intentionalMentions).use { content -> MessageEventContent.from(body, htmlBody, intentionalMentions).use { content ->
runCatching<Unit> { runCatchingExceptions<Unit> {
inner.send(content) inner.send(content)
} }
} }
} }
override suspend fun redactEvent(eventOrTransactionId: EventOrTransactionId, reason: String?): Result<Unit> = withContext(dispatcher) { override suspend fun redactEvent(eventOrTransactionId: EventOrTransactionId, reason: String?): Result<Unit> = withContext(dispatcher) {
runCatching { runCatchingExceptions {
inner.redactEvent( inner.redactEvent(
eventOrTransactionId = eventOrTransactionId.toRustEventOrTransactionId(), eventOrTransactionId = eventOrTransactionId.toRustEventOrTransactionId(),
reason = reason, reason = reason,
@ -296,7 +297,7 @@ class RustTimeline(
htmlBody: String?, htmlBody: String?,
intentionalMentions: List<IntentionalMention>, intentionalMentions: List<IntentionalMention>,
): Result<Unit> = withContext(dispatcher) { ): Result<Unit> = withContext(dispatcher) {
runCatching { runCatchingExceptions {
val editedContent = EditedContent.RoomMessage( val editedContent = EditedContent.RoomMessage(
content = MessageEventContent.from( content = MessageEventContent.from(
body = body, body = body,
@ -316,7 +317,7 @@ class RustTimeline(
caption: String?, caption: String?,
formattedCaption: String?, formattedCaption: String?,
): Result<Unit> = withContext(dispatcher) { ): Result<Unit> = withContext(dispatcher) {
runCatching<Unit> { runCatchingExceptions<Unit> {
val editedContent = EditedContent.MediaCaption( val editedContent = EditedContent.MediaCaption(
caption = caption, caption = caption,
formattedCaption = formattedCaption?.let { formattedCaption = formattedCaption?.let {
@ -340,7 +341,7 @@ class RustTimeline(
intentionalMentions: List<IntentionalMention>, intentionalMentions: List<IntentionalMention>,
fromNotification: Boolean, fromNotification: Boolean,
): Result<Unit> = withContext(dispatcher) { ): Result<Unit> = withContext(dispatcher) {
runCatching { runCatchingExceptions {
val msg = MessageEventContent.from(body, htmlBody, intentionalMentions) val msg = MessageEventContent.from(body, htmlBody, intentionalMentions)
inner.sendReply( inner.sendReply(
msg = msg, msg = msg,
@ -462,7 +463,7 @@ class RustTimeline(
} }
override suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result<Unit> = withContext(dispatcher) { override suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result<Unit> = withContext(dispatcher) {
runCatching { runCatchingExceptions {
inner.toggleReaction( inner.toggleReaction(
key = emoji, key = emoji,
itemId = eventOrTransactionId.toRustEventOrTransactionId(), itemId = eventOrTransactionId.toRustEventOrTransactionId(),
@ -471,7 +472,7 @@ class RustTimeline(
} }
override suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit> = withContext(dispatcher) { override suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit> = withContext(dispatcher) {
runCatching { runCatchingExceptions {
roomContentForwarder.forward(fromTimeline = inner, eventId = eventId, toRoomIds = roomIds) roomContentForwarder.forward(fromTimeline = inner, eventId = eventId, toRoomIds = roomIds)
}.onFailure { }.onFailure {
Timber.e(it) Timber.e(it)
@ -485,7 +486,7 @@ class RustTimeline(
zoomLevel: Int?, zoomLevel: Int?,
assetType: AssetType?, assetType: AssetType?,
): Result<Unit> = withContext(dispatcher) { ): Result<Unit> = withContext(dispatcher) {
runCatching { runCatchingExceptions {
inner.sendLocation( inner.sendLocation(
body = body, body = body,
geoUri = geoUri, geoUri = geoUri,
@ -528,7 +529,7 @@ class RustTimeline(
maxSelections: Int, maxSelections: Int,
pollKind: PollKind, pollKind: PollKind,
): Result<Unit> = withContext(dispatcher) { ): Result<Unit> = withContext(dispatcher) {
runCatching { runCatchingExceptions {
inner.createPoll( inner.createPoll(
question = question, question = question,
answers = answers, answers = answers,
@ -545,7 +546,7 @@ class RustTimeline(
maxSelections: Int, maxSelections: Int,
pollKind: PollKind, pollKind: PollKind,
): Result<Unit> = withContext(dispatcher) { ): Result<Unit> = withContext(dispatcher) {
runCatching { runCatchingExceptions {
val editedContent = EditedContent.PollStart( val editedContent = EditedContent.PollStart(
pollData = PollData( pollData = PollData(
question = question, question = question,
@ -565,7 +566,7 @@ class RustTimeline(
pollStartId: EventId, pollStartId: EventId,
answers: List<String> answers: List<String>
): Result<Unit> = withContext(dispatcher) { ): Result<Unit> = withContext(dispatcher) {
runCatching { runCatchingExceptions {
inner.sendPollResponse( inner.sendPollResponse(
pollStartEventId = pollStartId.value, pollStartEventId = pollStartId.value,
answers = answers, answers = answers,
@ -577,7 +578,7 @@ class RustTimeline(
pollStartId: EventId, pollStartId: EventId,
text: String text: String
): Result<Unit> = withContext(dispatcher) { ): Result<Unit> = withContext(dispatcher) {
runCatching { runCatchingExceptions {
inner.endPoll( inner.endPoll(
pollStartEventId = pollStartId.value, pollStartEventId = pollStartId.value,
text = text, text = text,
@ -586,7 +587,7 @@ class RustTimeline(
} }
private fun sendAttachment(files: List<File>, handle: () -> SendAttachmentJoinHandle): Result<MediaUploadHandler> { private fun sendAttachment(files: List<File>, handle: () -> SendAttachmentJoinHandle): Result<MediaUploadHandler> {
return runCatching { return runCatchingExceptions {
MediaUploadHandlerImpl(files, handle()) MediaUploadHandlerImpl(files, handle())
} }
} }
@ -609,19 +610,19 @@ class RustTimeline(
} }
override suspend fun pinEvent(eventId: EventId): Result<Boolean> = withContext(dispatcher) { override suspend fun pinEvent(eventId: EventId): Result<Boolean> = withContext(dispatcher) {
runCatching { runCatchingExceptions {
inner.pinEvent(eventId = eventId.value) inner.pinEvent(eventId = eventId.value)
} }
} }
override suspend fun unpinEvent(eventId: EventId): Result<Boolean> = withContext(dispatcher) { override suspend fun unpinEvent(eventId: EventId): Result<Boolean> = withContext(dispatcher) {
runCatching { runCatchingExceptions {
inner.unpinEvent(eventId = eventId.value) inner.unpinEvent(eventId = eventId.value)
} }
} }
private suspend fun fetchDetailsForEvent(eventId: EventId): Result<Unit> = withContext(dispatcher) { private suspend fun fetchDetailsForEvent(eventId: EventId): Result<Unit> = withContext(dispatcher) {
runCatching { runCatchingExceptions {
inner.fetchDetailsForEvent(eventId.value) inner.fetchDetailsForEvent(eventId.value)
} }
} }

Some files were not shown because too many files have changed in this diff Show more