Browse Source

Merge pull request #883 from vector-im/fix/jme/880-sliding-sync-loop-expires-and-restarts

Fix sliding sync loop restarts due to expirations
pull/893/head
Benoit Marty 1 year ago committed by GitHub
parent
commit
9ec0c888c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 70
      appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt
  2. 52
      appnav/src/main/kotlin/io/element/android/appnav/di/MatrixClientsHolder.kt
  3. 6
      appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt
  4. 32
      appnav/src/main/kotlin/io/element/android/appnav/root/RootNavState.kt
  5. 99
      appnav/src/main/kotlin/io/element/android/appnav/root/RootNavStateFlowFactory.kt
  6. 1
      build.gradle.kts
  7. 1
      changelog.d/880.bugfix
  8. 1
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ClearCacheUseCase.kt
  9. 28
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClientProvider.kt
  10. 22
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/exception/ClientException.kt
  11. 2
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt
  12. 4
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt
  13. 3
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt
  14. 27
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/exception/ClientException.kt
  15. 11
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt
  16. 22
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt

70
appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt

@ -40,12 +40,11 @@ import io.element.android.anvilannotations.ContributesNode @@ -40,12 +40,11 @@ import io.element.android.anvilannotations.ContributesNode
import io.element.android.appnav.di.MatrixClientsHolder
import io.element.android.appnav.intent.IntentResolver
import io.element.android.appnav.intent.ResolvedIntent
import io.element.android.appnav.root.RootNavStateFlowFactory
import io.element.android.appnav.root.RootPresenter
import io.element.android.appnav.root.RootView
import io.element.android.features.login.api.LoginUserStory
import io.element.android.features.login.api.oidc.OidcAction
import io.element.android.features.login.api.oidc.OidcActionFlow
import io.element.android.features.preferences.api.CacheService
import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint
import io.element.android.libraries.architecture.BackstackNode
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
@ -57,29 +56,22 @@ import io.element.android.libraries.di.AppScope @@ -57,29 +56,22 @@ import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.api.core.SessionId
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.parcelize.Parcelize
import timber.log.Timber
import java.util.UUID
@ContributesNode(AppScope::class)
class RootFlowNode @AssistedInject constructor(
@Assisted val buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
private val authenticationService: MatrixAuthenticationService,
private val cacheService: CacheService,
private val navStateFlowFactory: RootNavStateFlowFactory,
private val matrixClientsHolder: MatrixClientsHolder,
private val presenter: RootPresenter,
private val bugReportEntryPoint: BugReportEntryPoint,
private val intentResolver: IntentResolver,
private val oidcActionFlow: OidcActionFlow,
private val loginUserStory: LoginUserStory,
) :
BackstackNode<RootFlowNode.NavTarget>(
backstack = BackStack(
@ -91,26 +83,25 @@ class RootFlowNode @AssistedInject constructor( @@ -91,26 +83,25 @@ class RootFlowNode @AssistedInject constructor(
) {
override fun onBuilt() {
matrixClientsHolder.restore(buildContext.savedStateMap)
matrixClientsHolder.restoreWithSavedState(buildContext.savedStateMap)
super.onBuilt()
observeLoggedInState()
observeNavState()
}
override fun onSaveInstanceState(state: MutableSavedStateMap) {
super.onSaveInstanceState(state)
matrixClientsHolder.save(state)
matrixClientsHolder.saveIntoSavedState(state)
navStateFlowFactory.saveIntoSavedState(state)
}
private fun observeLoggedInState() {
combine(
cacheService.onClearedCacheEventFlow(),
isUserLoggedInFlow(),
) { _, isLoggedIn -> isLoggedIn }
.onEach { isLoggedIn ->
Timber.v("isLoggedIn=$isLoggedIn")
if (isLoggedIn) {
private fun observeNavState() {
navStateFlowFactory.create(buildContext.savedStateMap)
.distinctUntilChanged()
.onEach { navState ->
Timber.v("navState=$navState")
if (navState.isLoggedIn) {
tryToRestoreLatestSession(
onSuccess = { switchToLoggedInFlow(it) },
onSuccess = { sessionId -> switchToLoggedInFlow(sessionId, navState.cacheIndex) },
onFailure = { switchToNotLoggedInFlow() }
)
} else {
@ -120,19 +111,8 @@ class RootFlowNode @AssistedInject constructor( @@ -120,19 +111,8 @@ class RootFlowNode @AssistedInject constructor(
.launchIn(lifecycleScope)
}
private fun switchToLoggedInFlow(sessionId: SessionId) {
backstack.safeRoot(NavTarget.LoggedInFlow(sessionId))
}
private fun isUserLoggedInFlow(): Flow<Boolean> {
return combine(
authenticationService.isLoggedIn(),
loginUserStory.loginFlowIsDone
) { isLoggedIn, loginFlowIsDone ->
isLoggedIn && loginFlowIsDone
}
.distinctUntilChanged()
private fun switchToLoggedInFlow(sessionId: SessionId, navId: Int) {
backstack.safeRoot(NavTarget.LoggedInFlow(sessionId, navId))
}
private fun switchToNotLoggedInFlow() {
@ -145,14 +125,8 @@ class RootFlowNode @AssistedInject constructor( @@ -145,14 +125,8 @@ class RootFlowNode @AssistedInject constructor(
onFailure: () -> Unit = {},
onSuccess: (SessionId) -> Unit = {},
) {
// If the session is already known it'll be restored by the node hierarchy
if (matrixClientsHolder.knowSession(sessionId)) {
Timber.v("Session $sessionId already alive, no need to restore.")
return
}
authenticationService.restoreSession(sessionId)
.onSuccess { matrixClient ->
matrixClientsHolder.add(matrixClient)
matrixClientsHolder.getOrRestore(sessionId)
.onSuccess {
Timber.v("Succeed to restore session $sessionId")
onSuccess(sessionId)
}
@ -204,7 +178,7 @@ class RootFlowNode @AssistedInject constructor( @@ -204,7 +178,7 @@ class RootFlowNode @AssistedInject constructor(
@Parcelize
data class LoggedInFlow(
val sessionId: SessionId,
val navId: UUID = UUID.randomUUID(),
val navId: Int
) : NavTarget
@Parcelize
@ -278,11 +252,5 @@ class RootFlowNode @AssistedInject constructor( @@ -278,11 +252,5 @@ class RootFlowNode @AssistedInject constructor(
navTarget is NavTarget.LoggedInFlow && navTarget.sessionId == sessionId
}
}
private fun CacheService.onClearedCacheEventFlow(): Flow<Unit> {
return clearedCacheEventFlow
.onEach { sessionId -> matrixClientsHolder.remove(sessionId) }
.map { }
.onStart { emit((Unit)) }
}
}

52
appnav/src/main/kotlin/io/element/android/appnav/di/MatrixClientsHolder.kt

@ -18,23 +18,28 @@ package io.element.android.appnav.di @@ -18,23 +18,28 @@ package io.element.android.appnav.di
import com.bumble.appyx.core.state.MutableSavedStateMap
import com.bumble.appyx.core.state.SavedStateMap
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.api.core.SessionId
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import timber.log.Timber
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
private const val SAVE_INSTANCE_KEY = "io.element.android.x.di.MatrixClientsHolder.SaveInstanceKey"
class MatrixClientsHolder @Inject constructor(private val authenticationService: MatrixAuthenticationService) {
@SingleIn(AppScope::class)
@ContributesBinding(AppScope::class)
class MatrixClientsHolder @Inject constructor(private val authenticationService: MatrixAuthenticationService) : MatrixClientProvider {
private val sessionIdsToMatrixClient = ConcurrentHashMap<SessionId, MatrixClient>()
fun add(matrixClient: MatrixClient) {
sessionIdsToMatrixClient[matrixClient.sessionId] = matrixClient
}
private val restoreMutex = Mutex()
fun removeAll() {
sessionIdsToMatrixClient.clear()
@ -44,16 +49,21 @@ class MatrixClientsHolder @Inject constructor(private val authenticationService: @@ -44,16 +49,21 @@ class MatrixClientsHolder @Inject constructor(private val authenticationService:
sessionIdsToMatrixClient.remove(sessionId)
}
fun isEmpty(): Boolean = sessionIdsToMatrixClient.isEmpty()
fun knowSession(sessionId: SessionId): Boolean = sessionIdsToMatrixClient.containsKey(sessionId)
fun getOrNull(sessionId: SessionId): MatrixClient? {
return sessionIdsToMatrixClient[sessionId]
}
override suspend fun getOrRestore(sessionId: SessionId): Result<MatrixClient> {
return restoreMutex.withLock {
when (val matrixClient = getOrNull(sessionId)) {
null -> restore(sessionId)
else -> Result.success(matrixClient)
}
}
}
@Suppress("UNCHECKED_CAST")
fun restore(state: SavedStateMap?) {
fun restoreWithSavedState(state: SavedStateMap?) {
Timber.d("Restore state")
if (state == null || sessionIdsToMatrixClient.isNotEmpty()) return Unit.also {
Timber.w("Restore with non-empty map")
@ -64,21 +74,25 @@ class MatrixClientsHolder @Inject constructor(private val authenticationService: @@ -64,21 +74,25 @@ class MatrixClientsHolder @Inject constructor(private val authenticationService:
// Not ideal but should only happens in case of process recreation. This ensure we restore all the active sessions before restoring the node graphs.
runBlocking {
sessionIds.forEach { sessionId ->
Timber.d("Restore matrix session: $sessionId")
authenticationService.restoreSession(sessionId)
.onSuccess { matrixClient ->
add(matrixClient)
}
.onFailure {
Timber.e("Fail to restore session")
}
restore(sessionId)
}
}
}
fun save(state: MutableSavedStateMap) {
fun saveIntoSavedState(state: MutableSavedStateMap) {
val sessionKeys = sessionIdsToMatrixClient.keys.toTypedArray()
Timber.d("Save matrix session keys = ${sessionKeys.map { it.value }}")
state[SAVE_INSTANCE_KEY] = sessionKeys
}
private suspend fun restore(sessionId: SessionId): Result<MatrixClient> {
Timber.d("Restore matrix session: $sessionId")
return authenticationService.restoreSession(sessionId)
.onSuccess { matrixClient ->
sessionIdsToMatrixClient[matrixClient.sessionId] = matrixClient
}
.onFailure {
Timber.e("Fail to restore session")
}
}
}

6
appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt

@ -32,11 +32,11 @@ import com.bumble.appyx.core.node.node @@ -32,11 +32,11 @@ import com.bumble.appyx.core.node.node
import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.newRoot
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.appnav.NodeLifecycleCallback
import io.element.android.appnav.safeRoot
import io.element.android.features.networkmonitor.api.NetworkMonitor
import io.element.android.features.networkmonitor.api.NetworkStatus
import io.element.android.libraries.architecture.BackstackNode
@ -92,9 +92,9 @@ class RoomFlowNode @AssistedInject constructor( @@ -92,9 +92,9 @@ class RoomFlowNode @AssistedInject constructor(
.distinctUntilChanged()
.onEach { isLoaded ->
if (isLoaded) {
backstack.safeRoot(NavTarget.Loaded)
backstack.newRoot(NavTarget.Loaded)
} else {
backstack.safeRoot(NavTarget.Loading)
backstack.newRoot(NavTarget.Loading)
}
}.launchIn(lifecycleScope)
}

32
appnav/src/main/kotlin/io/element/android/appnav/root/RootNavState.kt

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.appnav.root
/**
* [RootNavState] produced by [RootNavStateFlowFactory].
*/
data class RootNavState(
/**
* This value is incremented when a clear cache is done.
* Can be useful to track to force ui state to re-render
*/
val cacheIndex: Int,
/**
* true if we are currently loggedIn.
*/
val isLoggedIn: Boolean
)

99
appnav/src/main/kotlin/io/element/android/appnav/root/RootNavStateFlowFactory.kt

@ -0,0 +1,99 @@ @@ -0,0 +1,99 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.appnav.root
import com.bumble.appyx.core.state.MutableSavedStateMap
import com.bumble.appyx.core.state.SavedStateMap
import io.element.android.appnav.di.MatrixClientsHolder
import io.element.android.features.login.api.LoginUserStory
import io.element.android.features.preferences.api.CacheService
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
import javax.inject.Inject
private const val SAVE_INSTANCE_KEY = "io.element.android.x.RootNavStateFlowFactory.SAVE_INSTANCE_KEY"
/**
* This class is responsible for creating a flow of [RootNavState].
* It gathers data from multiple datasource and creates a unique one.
*/
class RootNavStateFlowFactory @Inject constructor(
private val authenticationService: MatrixAuthenticationService,
private val cacheService: CacheService,
private val matrixClientsHolder: MatrixClientsHolder,
private val loginUserStory: LoginUserStory,
) {
private var currentCacheIndex = 0
fun create(savedStateMap: SavedStateMap?): Flow<RootNavState> {
return combine(
cacheIndexFlow(savedStateMap),
isUserLoggedInFlow(),
) { cacheIndex, isLoggedIn ->
RootNavState(cacheIndex = cacheIndex, isLoggedIn = isLoggedIn)
}
}
fun saveIntoSavedState(stateMap: MutableSavedStateMap) {
stateMap[SAVE_INSTANCE_KEY] = currentCacheIndex
}
/**
* @return a flow of integer, where each time a clear cache is done, we have a new incremented value.
*/
private fun cacheIndexFlow(savedStateMap: SavedStateMap?): Flow<Int> {
val initialCacheIndex = savedStateMap.getCacheIndexOrDefault()
return cacheService.clearedCacheEventFlow
.onEach { sessionId ->
matrixClientsHolder.remove(sessionId)
}
.toIndexFlow(initialCacheIndex)
.onEach { cacheIndex ->
currentCacheIndex = cacheIndex
}
}
private fun isUserLoggedInFlow(): Flow<Boolean> {
return combine(
authenticationService.isLoggedIn(),
loginUserStory.loginFlowIsDone
) { isLoggedIn, loginFlowIsDone ->
isLoggedIn && loginFlowIsDone
}
.distinctUntilChanged()
}
/**
* @return a flow of integer that increments the value by one each time a new element is emitted upstream.
*/
private fun Flow<Any>.toIndexFlow(initialValue: Int): Flow<Int> = flow {
var index = initialValue
emit(initialValue)
collect {
emit(++index)
}
}
private fun SavedStateMap?.getCacheIndexOrDefault(): Int {
return this?.get(SAVE_INSTANCE_KEY) as? Int ?: 0
}
}

1
build.gradle.kts

@ -247,6 +247,7 @@ koverMerged { @@ -247,6 +247,7 @@ koverMerged {
target = kotlinx.kover.api.VerificationTarget.CLASS
overrideClassFilter {
includes += "^*State$"
excludes += "io.element.android.appnav.root.RootNavState*"
excludes += "io.element.android.libraries.matrix.api.timeline.item.event.OtherState$*"
excludes += "io.element.android.libraries.matrix.api.timeline.item.event.EventSendState$*"
excludes += "io.element.android.libraries.matrix.api.room.RoomMembershipState*"

1
changelog.d/880.bugfix

@ -0,0 +1 @@ @@ -0,0 +1 @@
Fix sliding sync loop restarts due to expirations.

1
features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ClearCacheUseCase.kt

@ -27,7 +27,6 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers @@ -27,7 +27,6 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.MatrixClient
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import javax.inject.Inject

28
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClientProvider.kt

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.matrix.api
import io.element.android.libraries.matrix.api.core.SessionId
interface MatrixClientProvider {
/**
* Can be used to get or restore a MatrixClient with the given [SessionId].
* If a [MatrixClient] is already in memory, it'll return it. Otherwise it'll try to restore one.
* Most of the time you want to use injected constructor instead of retrieving a MatrixClient with this provider.
*/
suspend fun getOrRestore(sessionId: SessionId): Result<MatrixClient>
}

22
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/exception/ClientException.kt

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.matrix.api.exception
sealed class ClientException(message: String) : Exception(message) {
class Generic(message: String) : ClientException(message)
class Other(message: String) : ClientException(message)
}

2
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt

@ -165,8 +165,10 @@ class RustMatrixClient constructor( @@ -165,8 +165,10 @@ class RustMatrixClient constructor(
val cachedRoomListItem = roomListService.roomOrNull(roomId.value)
val fullRoom = cachedRoomListItem?.fullRoom()
if (cachedRoomListItem == null || fullRoom == null) {
Timber.d("No room cached for $roomId")
null
} else {
Timber.d("Found room cached for $roomId")
Pair(cachedRoomListItem, fullRoom)
}
}

4
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt

@ -19,7 +19,7 @@ package io.element.android.libraries.matrix.impl.auth @@ -19,7 +19,7 @@ package io.element.android.libraries.matrix.impl.auth
import io.element.android.libraries.matrix.api.auth.AuthenticationException
import org.matrix.rustcomponents.sdk.AuthenticationException as RustAuthenticationException
fun Throwable.mapAuthenticationException(): Throwable {
fun Throwable.mapAuthenticationException(): AuthenticationException {
return when (this) {
is RustAuthenticationException.ClientMissing -> AuthenticationException.ClientMissing(this.message!!)
is RustAuthenticationException.Generic -> AuthenticationException.Generic(this.message!!)
@ -35,6 +35,6 @@ fun Throwable.mapAuthenticationException(): Throwable { @@ -35,6 +35,6 @@ fun Throwable.mapAuthenticationException(): Throwable {
is RustAuthenticationException.OidcNotSupported -> AuthenticationException.OidcError("OidcNotSupported", message!!)
*/
else -> this
else -> AuthenticationException.Generic(this.message ?: "Unknown error")
}
}

3
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt

@ -29,6 +29,7 @@ import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails @@ -29,6 +29,7 @@ import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
import io.element.android.libraries.matrix.api.auth.OidcDetails
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.impl.RustMatrixClient
import io.element.android.libraries.matrix.impl.exception.mapClientException
import io.element.android.libraries.network.useragent.UserAgentProvider
import io.element.android.libraries.sessionstorage.api.SessionData
import io.element.android.libraries.sessionstorage.api.SessionStore
@ -95,7 +96,7 @@ class RustMatrixAuthenticationService @Inject constructor( @@ -95,7 +96,7 @@ class RustMatrixAuthenticationService @Inject constructor(
throw IllegalStateException("No session to restore with id $sessionId")
}
}.mapFailure { failure ->
failure.mapAuthenticationException()
failure.mapClientException()
}
}

27
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/exception/ClientException.kt

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.matrix.impl.exception
import io.element.android.libraries.matrix.api.exception.ClientException
import org.matrix.rustcomponents.sdk.ClientException as RustClientException
fun Throwable.mapClientException(): ClientException {
return when (this) {
is RustClientException.Generic -> ClientException.Generic(msg)
else -> ClientException.Other(message ?: "Unknown error")
}
}

11
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt

@ -23,12 +23,12 @@ import io.element.android.libraries.core.data.tryOrNull @@ -23,12 +23,12 @@ import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
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.SessionId
import io.element.android.libraries.matrix.api.core.ThreadId
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.push.api.notifications.NotificationDrawerManager
import io.element.android.libraries.push.api.store.PushDataStore
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
@ -57,7 +57,7 @@ class DefaultNotificationDrawerManager @Inject constructor( @@ -57,7 +57,7 @@ class DefaultNotificationDrawerManager @Inject constructor(
private val coroutineScope: CoroutineScope,
private val dispatchers: CoroutineDispatchers,
private val buildMeta: BuildMeta,
private val matrixAuthenticationService: MatrixAuthenticationService,
private val matrixClientProvider: MatrixClientProvider,
) : NotificationDrawerManager {
/**
* Lazily initializes the NotificationState as we rely on having a current session in order to fetch the persisted queue of events.
@ -251,11 +251,10 @@ class DefaultNotificationDrawerManager @Inject constructor( @@ -251,11 +251,10 @@ class DefaultNotificationDrawerManager @Inject constructor(
val currentUser = tryOrNull(
onError = { Timber.e(it, "Unable to retrieve info for user ${sessionId.value}") },
operation = {
val client = matrixAuthenticationService.restoreSession(sessionId).getOrNull()
val client = matrixClientProvider.getOrRestore(sessionId).getOrThrow()
// myUserDisplayName cannot be empty else NotificationCompat.MessagingStyle() will crash
val myUserDisplayName = client?.loadUserDisplayName()?.getOrNull() ?: sessionId.value
val userAvatarUrl = client?.loadUserAvatarURLString()?.getOrNull()
val myUserDisplayName = client.loadUserDisplayName().getOrNull() ?: sessionId.value
val userAvatarUrl = client.loadUserAvatarURLString().getOrNull()
MatrixUser(
userId = sessionId,
displayName = myUserDisplayName,

22
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt

@ -18,7 +18,6 @@ package io.element.android.libraries.push.impl.notifications @@ -18,7 +18,6 @@ package io.element.android.libraries.push.impl.notifications
import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
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.SessionId
@ -35,6 +34,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessage @@ -35,6 +34,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessage
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.push.impl.R
import io.element.android.libraries.push.impl.log.pushLoggerTag
import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent
@ -59,25 +59,25 @@ class NotifiableEventResolver @Inject constructor( @@ -59,25 +59,25 @@ class NotifiableEventResolver @Inject constructor(
private val stringProvider: StringProvider,
// private val noticeEventFormatter: NoticeEventFormatter,
// private val displayableEventFormatter: DisplayableEventFormatter,
private val matrixAuthenticationService: MatrixAuthenticationService,
private val buildMeta: BuildMeta,
private val clock: SystemClock,
private val matrixClientProvider: MatrixClientProvider,
) {
suspend fun resolveEvent(sessionId: SessionId, roomId: RoomId, eventId: EventId): NotifiableEvent? {
// Restore session
val session = matrixAuthenticationService.restoreSession(sessionId).getOrNull() ?: return null
val notificationService = session.notificationService()
val client = matrixClientProvider.getOrRestore(sessionId).getOrNull() ?: return null
val notificationService = client.notificationService()
val notificationData = notificationService.getNotification(
userId = sessionId,
roomId = roomId,
eventId = eventId,
userId = sessionId,
roomId = roomId,
eventId = eventId,
// FIXME should be true in the future, but right now it's broken
// (https://github.com/vector-im/element-x-android/issues/640#issuecomment-1612913658)
filterByPushRules = false,
).onFailure {
Timber.tag(loggerTag.value).e(it, "Unable to resolve event: $eventId.")
}.getOrNull()
filterByPushRules = false,
).onFailure {
Timber.tag(loggerTag.value).e(it, "Unable to resolve event: $eventId.")
}.getOrNull()
// TODO this notificationData is not always valid at the moment, sometimes the Rust SDK can't fetch the matching event
return notificationData?.asNotifiableEvent(sessionId)

Loading…
Cancel
Save