mirror of
https://github.com/element-hq/element-android
synced 2024-11-27 03:48:12 +03:00
Merge branch 'develop' into feature/ons/toggle_ip_address_visibility
# Conflicts: # vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt
This commit is contained in:
commit
b81fc4f8f1
27 changed files with 170 additions and 395 deletions
|
@ -60,8 +60,8 @@ jobs:
|
||||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||||
query: |
|
query: |
|
||||||
mutation add_to_project($projectid:ID!, $contentid:ID!) {
|
mutation add_to_project($projectid:ID!, $contentid:ID!) {
|
||||||
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
|
addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) {
|
||||||
projectNextItem {
|
item {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,8 +129,8 @@ jobs:
|
||||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||||
query: |
|
query: |
|
||||||
mutation add_to_project($projectid:ID!, $contentid:ID!) {
|
mutation add_to_project($projectid:ID!, $contentid:ID!) {
|
||||||
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
|
addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) {
|
||||||
projectNextItem {
|
item {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
Changes in Element v1.5.7 (2022-11-07)
|
||||||
|
======================================
|
||||||
|
|
||||||
|
Bugfixes 🐛
|
||||||
|
----------
|
||||||
|
- Fix regression when syncing with homeserver < 1.4. ([#7534](https://github.com/vector-im/element-android/issues/7534))
|
||||||
|
|
||||||
Changes in Element v1.5.6 (2022-11-02)
|
Changes in Element v1.5.6 (2022-11-02)
|
||||||
======================================
|
======================================
|
||||||
|
|
||||||
|
|
1
changelog.d/7501.bugfix
Normal file
1
changelog.d/7501.bugfix
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Fix duplicated mention pills in some cases
|
1
changelog.d/7509.bugfix
Normal file
1
changelog.d/7509.bugfix
Normal file
|
@ -0,0 +1 @@
|
||||||
|
When joining a room, the message composer is displayed once the room is loaded.
|
|
@ -26,7 +26,7 @@ def jjwt = "0.11.5"
|
||||||
// Temporary version to unblock #6929. Once 0.16.0 is released we should use it, and revert
|
// Temporary version to unblock #6929. Once 0.16.0 is released we should use it, and revert
|
||||||
// the whole commit which set version 0.16.0-SNAPSHOT
|
// the whole commit which set version 0.16.0-SNAPSHOT
|
||||||
def vanniktechEmoji = "0.16.0-SNAPSHOT"
|
def vanniktechEmoji = "0.16.0-SNAPSHOT"
|
||||||
def sentry = "6.6.0"
|
def sentry = "6.7.0"
|
||||||
def fragment = "1.5.4"
|
def fragment = "1.5.4"
|
||||||
// Testing
|
// Testing
|
||||||
def mockk = "1.12.3" // We need to use 1.12.3 to have mocking in androidTest until a new version is released: https://github.com/mockk/mockk/issues/819
|
def mockk = "1.12.3" // We need to use 1.12.3 to have mocking in androidTest until a new version is released: https://github.com/mockk/mockk/issues/819
|
||||||
|
|
2
fastlane/metadata/android/en-US/changelogs/40105070.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40105070.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Main changes in this version: new UI for selecting an attachment.
|
||||||
|
Full changelog: https://github.com/vector-im/element-android/releases
|
|
@ -67,7 +67,9 @@ internal object FilterFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createElementTimelineFilter(): RoomEventFilter? {
|
private fun createElementTimelineFilter(): RoomEventFilter? {
|
||||||
return RoomEventFilter(enableUnreadThreadNotifications = true)
|
// we need to check if homeserver supports thread notifications before setting this param
|
||||||
|
// return RoomEventFilter(enableUnreadThreadNotifications = true)
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createElementStateFilter(): RoomEventFilter {
|
private fun createElementStateFilter(): RoomEventFilter {
|
||||||
|
|
|
@ -132,7 +132,7 @@ dependencies {
|
||||||
implementation libs.androidx.biometric
|
implementation libs.androidx.biometric
|
||||||
|
|
||||||
api "org.threeten:threetenbp:1.4.0:no-tzdb"
|
api "org.threeten:threetenbp:1.4.0:no-tzdb"
|
||||||
api "com.gabrielittner.threetenbp:lazythreetenbp:0.11.0"
|
api "com.gabrielittner.threetenbp:lazythreetenbp:0.12.0"
|
||||||
|
|
||||||
implementation libs.squareup.moshi
|
implementation libs.squareup.moshi
|
||||||
kapt libs.squareup.moshiKotlin
|
kapt libs.squareup.moshiKotlin
|
||||||
|
|
|
@ -24,26 +24,6 @@ package im.vector.app.features.analytics.plan
|
||||||
* definition. These properties must all be device independent.
|
* definition. These properties must all be device independent.
|
||||||
*/
|
*/
|
||||||
data class UserProperties(
|
data class UserProperties(
|
||||||
/**
|
|
||||||
* Whether the user has the favourites space enabled.
|
|
||||||
*/
|
|
||||||
val webMetaSpaceFavouritesEnabled: Boolean? = null,
|
|
||||||
/**
|
|
||||||
* Whether the user has the home space set to all rooms.
|
|
||||||
*/
|
|
||||||
val webMetaSpaceHomeAllRooms: Boolean? = null,
|
|
||||||
/**
|
|
||||||
* Whether the user has the home space enabled.
|
|
||||||
*/
|
|
||||||
val webMetaSpaceHomeEnabled: Boolean? = null,
|
|
||||||
/**
|
|
||||||
* Whether the user has the other rooms space enabled.
|
|
||||||
*/
|
|
||||||
val webMetaSpaceOrphansEnabled: Boolean? = null,
|
|
||||||
/**
|
|
||||||
* Whether the user has the people space enabled.
|
|
||||||
*/
|
|
||||||
val webMetaSpacePeopleEnabled: Boolean? = null,
|
|
||||||
/**
|
/**
|
||||||
* The active filter in the All Chats screen.
|
* The active filter in the All Chats screen.
|
||||||
*/
|
*/
|
||||||
|
@ -109,11 +89,6 @@ data class UserProperties(
|
||||||
|
|
||||||
fun getProperties(): Map<String, Any>? {
|
fun getProperties(): Map<String, Any>? {
|
||||||
return mutableMapOf<String, Any>().apply {
|
return mutableMapOf<String, Any>().apply {
|
||||||
webMetaSpaceFavouritesEnabled?.let { put("WebMetaSpaceFavouritesEnabled", it) }
|
|
||||||
webMetaSpaceHomeAllRooms?.let { put("WebMetaSpaceHomeAllRooms", it) }
|
|
||||||
webMetaSpaceHomeEnabled?.let { put("WebMetaSpaceHomeEnabled", it) }
|
|
||||||
webMetaSpaceOrphansEnabled?.let { put("WebMetaSpaceOrphansEnabled", it) }
|
|
||||||
webMetaSpacePeopleEnabled?.let { put("WebMetaSpacePeopleEnabled", it) }
|
|
||||||
allChatsActiveFilter?.let { put("allChatsActiveFilter", it.name) }
|
allChatsActiveFilter?.let { put("allChatsActiveFilter", it.name) }
|
||||||
ftueUseCaseSelection?.let { put("ftueUseCaseSelection", it.name) }
|
ftueUseCaseSelection?.let { put("ftueUseCaseSelection", it.name) }
|
||||||
numFavouriteRooms?.let { put("numFavouriteRooms", it) }
|
numFavouriteRooms?.let { put("numFavouriteRooms", it) }
|
||||||
|
|
|
@ -1169,6 +1169,9 @@ class TimelineFragment :
|
||||||
lazyLoadedViews.inviteView(false)?.isVisible = false
|
lazyLoadedViews.inviteView(false)?.isVisible = false
|
||||||
|
|
||||||
if (mainState.tombstoneEvent == null) {
|
if (mainState.tombstoneEvent == null) {
|
||||||
|
views.composerContainer.isInvisible = !messageComposerState.isComposerVisible
|
||||||
|
views.voiceMessageRecorderContainer.isVisible = messageComposerState.isVoiceMessageRecorderVisible
|
||||||
|
|
||||||
when (messageComposerState.canSendMessage) {
|
when (messageComposerState.canSendMessage) {
|
||||||
CanSendStatus.Allowed -> {
|
CanSendStatus.Allowed -> {
|
||||||
NotificationAreaView.State.Hidden
|
NotificationAreaView.State.Hidden
|
||||||
|
@ -1224,6 +1227,7 @@ class TimelineFragment :
|
||||||
|
|
||||||
private fun FragmentTimelineBinding.hideComposerViews() {
|
private fun FragmentTimelineBinding.hideComposerViews() {
|
||||||
composerContainer.isVisible = false
|
composerContainer.isVisible = false
|
||||||
|
voiceMessageRecorderContainer.isVisible = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderTypingMessageNotification(roomSummary: RoomSummary?, state: RoomDetailViewState) {
|
private fun renderTypingMessageNotification(roomSummary: RoomSummary?, state: RoomDetailViewState) {
|
||||||
|
|
|
@ -83,6 +83,20 @@ class PillsPostProcessor @AssistedInject constructor(
|
||||||
val pillSpan = linkSpan.createPillSpan(roomId) ?: return@forEach
|
val pillSpan = linkSpan.createPillSpan(roomId) ?: return@forEach
|
||||||
val startSpan = renderedText.getSpanStart(linkSpan)
|
val startSpan = renderedText.getSpanStart(linkSpan)
|
||||||
val endSpan = renderedText.getSpanEnd(linkSpan)
|
val endSpan = renderedText.getSpanEnd(linkSpan)
|
||||||
|
// GlideImagesPlugin causes duplicated pills if we have a nested spans in the pill span,
|
||||||
|
// such as images or italic text.
|
||||||
|
// Accordingly, it's better to remove all spans that are contained in this span before rendering.
|
||||||
|
renderedText.getSpans(startSpan, endSpan, Any::class.java).forEach remove@{
|
||||||
|
if (it !is LinkSpan) {
|
||||||
|
// Make sure to only remove spans that are contained in this link, and not are bigger than this link, e.g. like reply-blocks
|
||||||
|
val start = renderedText.getSpanStart(it)
|
||||||
|
if (start < startSpan) return@remove
|
||||||
|
val end = renderedText.getSpanEnd(it)
|
||||||
|
if (end > endSpan) return@remove
|
||||||
|
|
||||||
|
renderedText.removeSpan(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
addPillSpan(renderedText, pillSpan, startSpan, endSpan)
|
addPillSpan(renderedText, pillSpan, startSpan, endSpan)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,20 +31,16 @@ import im.vector.app.features.auth.PendingAuthHandler
|
||||||
import im.vector.app.features.settings.VectorPreferences
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
|
import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
|
||||||
import im.vector.app.features.settings.devices.v2.signout.InterceptSignoutFlowResponseUseCase
|
import im.vector.app.features.settings.devices.v2.signout.InterceptSignoutFlowResponseUseCase
|
||||||
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionResult
|
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionsReAuthNeeded
|
||||||
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionsUseCase
|
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionsUseCase
|
||||||
import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase
|
import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase
|
||||||
import im.vector.app.features.settings.devices.v2.verification.GetCurrentSessionCrossSigningInfoUseCase
|
import im.vector.app.features.settings.devices.v2.verification.GetCurrentSessionCrossSigningInfoUseCase
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
|
||||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth
|
import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import kotlin.coroutines.Continuation
|
|
||||||
|
|
||||||
class DevicesViewModel @AssistedInject constructor(
|
class DevicesViewModel @AssistedInject constructor(
|
||||||
@Assisted initialState: DevicesViewState,
|
@Assisted initialState: DevicesViewState,
|
||||||
|
@ -166,16 +162,14 @@ class DevicesViewModel @AssistedInject constructor(
|
||||||
if (deviceIds.isEmpty()) {
|
if (deviceIds.isEmpty()) {
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
val signoutResult = signout(deviceIds)
|
val result = signout(deviceIds)
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
|
||||||
if (signoutResult.isSuccess) {
|
val error = result.exceptionOrNull()
|
||||||
|
if (error == null) {
|
||||||
onSignoutSuccess()
|
onSignoutSuccess()
|
||||||
} else {
|
} else {
|
||||||
when (val failure = signoutResult.exceptionOrNull()) {
|
onSignoutFailure(error)
|
||||||
null -> onSignoutSuccess()
|
|
||||||
else -> onSignoutFailure(failure)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -187,16 +181,9 @@ class DevicesViewModel @AssistedInject constructor(
|
||||||
.orEmpty()
|
.orEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun signout(deviceIds: List<String>) = signoutSessionsUseCase.execute(deviceIds, object : UserInteractiveAuthInterceptor {
|
private suspend fun signout(deviceIds: List<String>) = signoutSessionsUseCase.execute(deviceIds, this::onReAuthNeeded)
|
||||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
|
||||||
when (val result = interceptSignoutFlowResponseUseCase.execute(flowResponse, errCode, promise)) {
|
|
||||||
is SignoutSessionResult.ReAuthNeeded -> onReAuthNeeded(result)
|
|
||||||
is SignoutSessionResult.Completed -> Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
private fun onReAuthNeeded(reAuthNeeded: SignoutSessionResult.ReAuthNeeded) {
|
private fun onReAuthNeeded(reAuthNeeded: SignoutSessionsReAuthNeeded) {
|
||||||
Timber.d("onReAuthNeeded")
|
Timber.d("onReAuthNeeded")
|
||||||
pendingAuthHandler.pendingAuth = DefaultBaseAuth(session = reAuthNeeded.flowResponse.session)
|
pendingAuthHandler.pendingAuth = DefaultBaseAuth(session = reAuthNeeded.flowResponse.session)
|
||||||
pendingAuthHandler.uiaContinuation = reAuthNeeded.uiaContinuation
|
pendingAuthHandler.uiaContinuation = reAuthNeeded.uiaContinuation
|
||||||
|
|
|
@ -33,24 +33,18 @@ import im.vector.app.features.settings.devices.v2.GetDeviceFullInfoListUseCase
|
||||||
import im.vector.app.features.settings.devices.v2.RefreshDevicesUseCase
|
import im.vector.app.features.settings.devices.v2.RefreshDevicesUseCase
|
||||||
import im.vector.app.features.settings.devices.v2.VectorSessionsListViewModel
|
import im.vector.app.features.settings.devices.v2.VectorSessionsListViewModel
|
||||||
import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
|
import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
|
||||||
import im.vector.app.features.settings.devices.v2.signout.InterceptSignoutFlowResponseUseCase
|
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionsReAuthNeeded
|
||||||
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionResult
|
|
||||||
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionsUseCase
|
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionsUseCase
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
|
||||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
|
||||||
import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth
|
import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import kotlin.coroutines.Continuation
|
|
||||||
|
|
||||||
class OtherSessionsViewModel @AssistedInject constructor(
|
class OtherSessionsViewModel @AssistedInject constructor(
|
||||||
@Assisted private val initialState: OtherSessionsViewState,
|
@Assisted private val initialState: OtherSessionsViewState,
|
||||||
activeSessionHolder: ActiveSessionHolder,
|
activeSessionHolder: ActiveSessionHolder,
|
||||||
private val getDeviceFullInfoListUseCase: GetDeviceFullInfoListUseCase,
|
private val getDeviceFullInfoListUseCase: GetDeviceFullInfoListUseCase,
|
||||||
private val signoutSessionsUseCase: SignoutSessionsUseCase,
|
private val signoutSessionsUseCase: SignoutSessionsUseCase,
|
||||||
private val interceptSignoutFlowResponseUseCase: InterceptSignoutFlowResponseUseCase,
|
|
||||||
private val pendingAuthHandler: PendingAuthHandler,
|
private val pendingAuthHandler: PendingAuthHandler,
|
||||||
refreshDevicesUseCase: RefreshDevicesUseCase,
|
refreshDevicesUseCase: RefreshDevicesUseCase,
|
||||||
@DefaultPreferences
|
@DefaultPreferences
|
||||||
|
@ -193,16 +187,14 @@ class OtherSessionsViewModel @AssistedInject constructor(
|
||||||
if (deviceIds.isEmpty()) {
|
if (deviceIds.isEmpty()) {
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
val signoutResult = signout(deviceIds)
|
val result = signout(deviceIds)
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
|
||||||
if (signoutResult.isSuccess) {
|
val error = result.exceptionOrNull()
|
||||||
|
if (error == null) {
|
||||||
onSignoutSuccess()
|
onSignoutSuccess()
|
||||||
} else {
|
} else {
|
||||||
when (val failure = signoutResult.exceptionOrNull()) {
|
onSignoutFailure(error)
|
||||||
null -> onSignoutSuccess()
|
|
||||||
else -> onSignoutFailure(failure)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,16 +207,9 @@ class OtherSessionsViewModel @AssistedInject constructor(
|
||||||
}.mapNotNull { it.deviceInfo.deviceId }
|
}.mapNotNull { it.deviceInfo.deviceId }
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun signout(deviceIds: List<String>) = signoutSessionsUseCase.execute(deviceIds, object : UserInteractiveAuthInterceptor {
|
private suspend fun signout(deviceIds: List<String>) = signoutSessionsUseCase.execute(deviceIds, this::onReAuthNeeded)
|
||||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
|
||||||
when (val result = interceptSignoutFlowResponseUseCase.execute(flowResponse, errCode, promise)) {
|
|
||||||
is SignoutSessionResult.ReAuthNeeded -> onReAuthNeeded(result)
|
|
||||||
is SignoutSessionResult.Completed -> Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
private fun onReAuthNeeded(reAuthNeeded: SignoutSessionResult.ReAuthNeeded) {
|
private fun onReAuthNeeded(reAuthNeeded: SignoutSessionsReAuthNeeded) {
|
||||||
Timber.d("onReAuthNeeded")
|
Timber.d("onReAuthNeeded")
|
||||||
pendingAuthHandler.pendingAuth = DefaultBaseAuth(session = reAuthNeeded.flowResponse.session)
|
pendingAuthHandler.pendingAuth = DefaultBaseAuth(session = reAuthNeeded.flowResponse.session)
|
||||||
pendingAuthHandler.uiaContinuation = reAuthNeeded.uiaContinuation
|
pendingAuthHandler.uiaContinuation = reAuthNeeded.uiaContinuation
|
||||||
|
|
|
@ -34,28 +34,24 @@ import im.vector.app.features.settings.devices.v2.VectorSessionsListViewModel
|
||||||
import im.vector.app.features.settings.devices.v2.notification.GetNotificationsStatusUseCase
|
import im.vector.app.features.settings.devices.v2.notification.GetNotificationsStatusUseCase
|
||||||
import im.vector.app.features.settings.devices.v2.notification.TogglePushNotificationUseCase
|
import im.vector.app.features.settings.devices.v2.notification.TogglePushNotificationUseCase
|
||||||
import im.vector.app.features.settings.devices.v2.signout.InterceptSignoutFlowResponseUseCase
|
import im.vector.app.features.settings.devices.v2.signout.InterceptSignoutFlowResponseUseCase
|
||||||
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionResult
|
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionsReAuthNeeded
|
||||||
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionUseCase
|
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionsUseCase
|
||||||
import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase
|
import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
|
||||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
|
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
|
||||||
import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth
|
import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import kotlin.coroutines.Continuation
|
|
||||||
|
|
||||||
class SessionOverviewViewModel @AssistedInject constructor(
|
class SessionOverviewViewModel @AssistedInject constructor(
|
||||||
@Assisted val initialState: SessionOverviewViewState,
|
@Assisted val initialState: SessionOverviewViewState,
|
||||||
private val getDeviceFullInfoUseCase: GetDeviceFullInfoUseCase,
|
private val getDeviceFullInfoUseCase: GetDeviceFullInfoUseCase,
|
||||||
private val checkIfCurrentSessionCanBeVerifiedUseCase: CheckIfCurrentSessionCanBeVerifiedUseCase,
|
private val checkIfCurrentSessionCanBeVerifiedUseCase: CheckIfCurrentSessionCanBeVerifiedUseCase,
|
||||||
private val signoutSessionUseCase: SignoutSessionUseCase,
|
private val signoutSessionsUseCase: SignoutSessionsUseCase,
|
||||||
private val interceptSignoutFlowResponseUseCase: InterceptSignoutFlowResponseUseCase,
|
private val interceptSignoutFlowResponseUseCase: InterceptSignoutFlowResponseUseCase,
|
||||||
private val pendingAuthHandler: PendingAuthHandler,
|
private val pendingAuthHandler: PendingAuthHandler,
|
||||||
private val activeSessionHolder: ActiveSessionHolder,
|
private val activeSessionHolder: ActiveSessionHolder,
|
||||||
|
@ -174,30 +170,21 @@ class SessionOverviewViewModel @AssistedInject constructor(
|
||||||
private fun handleSignoutOtherSession(deviceId: String) {
|
private fun handleSignoutOtherSession(deviceId: String) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
val signoutResult = signout(deviceId)
|
val result = signout(deviceId)
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
|
||||||
if (signoutResult.isSuccess) {
|
val error = result.exceptionOrNull()
|
||||||
|
if (error == null) {
|
||||||
onSignoutSuccess()
|
onSignoutSuccess()
|
||||||
} else {
|
} else {
|
||||||
when (val failure = signoutResult.exceptionOrNull()) {
|
onSignoutFailure(error)
|
||||||
null -> onSignoutSuccess()
|
|
||||||
else -> onSignoutFailure(failure)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun signout(deviceId: String) = signoutSessionUseCase.execute(deviceId, object : UserInteractiveAuthInterceptor {
|
private suspend fun signout(deviceId: String) = signoutSessionsUseCase.execute(listOf(deviceId), this::onReAuthNeeded)
|
||||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
|
||||||
when (val result = interceptSignoutFlowResponseUseCase.execute(flowResponse, errCode, promise)) {
|
|
||||||
is SignoutSessionResult.ReAuthNeeded -> onReAuthNeeded(result)
|
|
||||||
is SignoutSessionResult.Completed -> Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
private fun onReAuthNeeded(reAuthNeeded: SignoutSessionResult.ReAuthNeeded) {
|
private fun onReAuthNeeded(reAuthNeeded: SignoutSessionsReAuthNeeded) {
|
||||||
Timber.d("onReAuthNeeded")
|
Timber.d("onReAuthNeeded")
|
||||||
pendingAuthHandler.pendingAuth = DefaultBaseAuth(session = reAuthNeeded.flowResponse.session)
|
pendingAuthHandler.pendingAuth = DefaultBaseAuth(session = reAuthNeeded.flowResponse.session)
|
||||||
pendingAuthHandler.uiaContinuation = reAuthNeeded.uiaContinuation
|
pendingAuthHandler.uiaContinuation = reAuthNeeded.uiaContinuation
|
||||||
|
|
|
@ -37,17 +37,16 @@ class InterceptSignoutFlowResponseUseCase @Inject constructor(
|
||||||
flowResponse: RegistrationFlowResponse,
|
flowResponse: RegistrationFlowResponse,
|
||||||
errCode: String?,
|
errCode: String?,
|
||||||
promise: Continuation<UIABaseAuth>
|
promise: Continuation<UIABaseAuth>
|
||||||
): SignoutSessionResult {
|
): SignoutSessionsReAuthNeeded? {
|
||||||
return if (flowResponse.nextUncompletedStage() == LoginFlowTypes.PASSWORD && reAuthHelper.data != null && errCode == null) {
|
return if (flowResponse.nextUncompletedStage() == LoginFlowTypes.PASSWORD && reAuthHelper.data != null && errCode == null) {
|
||||||
UserPasswordAuth(
|
UserPasswordAuth(
|
||||||
session = null,
|
session = null,
|
||||||
user = activeSessionHolder.getActiveSession().myUserId,
|
user = activeSessionHolder.getActiveSession().myUserId,
|
||||||
password = reAuthHelper.data
|
password = reAuthHelper.data
|
||||||
).let { promise.resume(it) }
|
).let { promise.resume(it) }
|
||||||
|
null
|
||||||
SignoutSessionResult.Completed
|
|
||||||
} else {
|
} else {
|
||||||
SignoutSessionResult.ReAuthNeeded(
|
SignoutSessionsReAuthNeeded(
|
||||||
pendingAuth = DefaultBaseAuth(session = flowResponse.session),
|
pendingAuth = DefaultBaseAuth(session = flowResponse.session),
|
||||||
uiaContinuation = promise,
|
uiaContinuation = promise,
|
||||||
flowResponse = flowResponse,
|
flowResponse = flowResponse,
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 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 im.vector.app.features.settings.devices.v2.signout
|
|
||||||
|
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
|
||||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
|
||||||
import org.matrix.android.sdk.api.util.awaitCallback
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use case to signout a single session.
|
|
||||||
*/
|
|
||||||
class SignoutSessionUseCase @Inject constructor(
|
|
||||||
private val activeSessionHolder: ActiveSessionHolder,
|
|
||||||
) {
|
|
||||||
|
|
||||||
suspend fun execute(deviceId: String, userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor): Result<Unit> {
|
|
||||||
return deleteDevice(deviceId, userInteractiveAuthInterceptor)
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun deleteDevice(deviceId: String, userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor) = runCatching {
|
|
||||||
awaitCallback { matrixCallback ->
|
|
||||||
activeSessionHolder.getActiveSession()
|
|
||||||
.cryptoService()
|
|
||||||
.deleteDevice(deviceId, userInteractiveAuthInterceptor, matrixCallback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -20,13 +20,9 @@ import org.matrix.android.sdk.api.auth.UIABaseAuth
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.coroutines.Continuation
|
||||||
|
|
||||||
sealed class SignoutSessionResult {
|
data class SignoutSessionsReAuthNeeded(
|
||||||
data class ReAuthNeeded(
|
val pendingAuth: UIABaseAuth,
|
||||||
val pendingAuth: UIABaseAuth,
|
val uiaContinuation: Continuation<UIABaseAuth>,
|
||||||
val uiaContinuation: Continuation<UIABaseAuth>,
|
val flowResponse: RegistrationFlowResponse,
|
||||||
val flowResponse: RegistrationFlowResponse,
|
val errCode: String?
|
||||||
val errCode: String?
|
)
|
||||||
) : SignoutSessionResult()
|
|
||||||
|
|
||||||
object Completed : SignoutSessionResult()
|
|
||||||
}
|
|
|
@ -16,27 +16,42 @@
|
||||||
|
|
||||||
package im.vector.app.features.settings.devices.v2.signout
|
package im.vector.app.features.settings.devices.v2.signout
|
||||||
|
|
||||||
|
import androidx.annotation.Size
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
|
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
||||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||||
|
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||||
import org.matrix.android.sdk.api.util.awaitCallback
|
import org.matrix.android.sdk.api.util.awaitCallback
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import kotlin.coroutines.Continuation
|
||||||
|
|
||||||
/**
|
|
||||||
* Use case to signout several sessions.
|
|
||||||
*/
|
|
||||||
class SignoutSessionsUseCase @Inject constructor(
|
class SignoutSessionsUseCase @Inject constructor(
|
||||||
private val activeSessionHolder: ActiveSessionHolder,
|
private val activeSessionHolder: ActiveSessionHolder,
|
||||||
|
private val interceptSignoutFlowResponseUseCase: InterceptSignoutFlowResponseUseCase,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun execute(deviceIds: List<String>, userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor): Result<Unit> {
|
suspend fun execute(
|
||||||
return deleteDevices(deviceIds, userInteractiveAuthInterceptor)
|
@Size(min = 1) deviceIds: List<String>,
|
||||||
|
onReAuthNeeded: (SignoutSessionsReAuthNeeded) -> Unit,
|
||||||
|
): Result<Unit> = runCatching {
|
||||||
|
Timber.d("start execute with ${deviceIds.size} deviceIds")
|
||||||
|
|
||||||
|
val authInterceptor = object : UserInteractiveAuthInterceptor {
|
||||||
|
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||||
|
val result = interceptSignoutFlowResponseUseCase.execute(flowResponse, errCode, promise)
|
||||||
|
result?.let(onReAuthNeeded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteDevices(deviceIds, authInterceptor)
|
||||||
|
Timber.d("end execute")
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun deleteDevices(deviceIds: List<String>, userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor) = runCatching {
|
private suspend fun deleteDevices(deviceIds: List<String>, userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor) =
|
||||||
awaitCallback { matrixCallback ->
|
awaitCallback { matrixCallback ->
|
||||||
activeSessionHolder.getActiveSession()
|
activeSessionHolder.getActiveSession()
|
||||||
.cryptoService()
|
.cryptoService()
|
||||||
.deleteDevices(deviceIds, userInteractiveAuthInterceptor, matrixCallback)
|
.deleteDevices(deviceIds, userInteractiveAuthInterceptor, matrixCallback)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -232,7 +232,7 @@ class DevicesViewModelTest {
|
||||||
// Given
|
// Given
|
||||||
val expectedViewState = givenInitialViewState(deviceId1 = A_DEVICE_ID_1, deviceId2 = A_CURRENT_DEVICE_ID)
|
val expectedViewState = givenInitialViewState(deviceId1 = A_DEVICE_ID_1, deviceId2 = A_CURRENT_DEVICE_ID)
|
||||||
// signout all devices except the current device
|
// signout all devices except the current device
|
||||||
fakeSignoutSessionsUseCase.givenSignoutSuccess(listOf(A_DEVICE_ID_1), fakeInterceptSignoutFlowResponseUseCase)
|
fakeSignoutSessionsUseCase.givenSignoutSuccess(listOf(A_DEVICE_ID_1))
|
||||||
|
|
||||||
// When
|
// When
|
||||||
val viewModel = createViewModel()
|
val viewModel = createViewModel()
|
||||||
|
@ -279,7 +279,7 @@ class DevicesViewModelTest {
|
||||||
@Test
|
@Test
|
||||||
fun `given reAuth is needed during multiSignout when handling multiSignout action then requestReAuth is sent and pending auth is stored`() {
|
fun `given reAuth is needed during multiSignout when handling multiSignout action then requestReAuth is sent and pending auth is stored`() {
|
||||||
// Given
|
// Given
|
||||||
val reAuthNeeded = fakeSignoutSessionsUseCase.givenSignoutReAuthNeeded(listOf(A_DEVICE_ID_1, A_DEVICE_ID_2), fakeInterceptSignoutFlowResponseUseCase)
|
val reAuthNeeded = fakeSignoutSessionsUseCase.givenSignoutReAuthNeeded(listOf(A_DEVICE_ID_1, A_DEVICE_ID_2))
|
||||||
val expectedPendingAuth = DefaultBaseAuth(session = reAuthNeeded.flowResponse.session)
|
val expectedPendingAuth = DefaultBaseAuth(session = reAuthNeeded.flowResponse.session)
|
||||||
val expectedReAuthEvent = DevicesViewEvent.RequestReAuth(reAuthNeeded.flowResponse, reAuthNeeded.errCode)
|
val expectedReAuthEvent = DevicesViewEvent.RequestReAuth(reAuthNeeded.flowResponse, reAuthNeeded.errCode)
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,6 @@ import im.vector.app.features.settings.devices.v2.DeviceFullInfo
|
||||||
import im.vector.app.features.settings.devices.v2.GetDeviceFullInfoListUseCase
|
import im.vector.app.features.settings.devices.v2.GetDeviceFullInfoListUseCase
|
||||||
import im.vector.app.features.settings.devices.v2.RefreshDevicesUseCase
|
import im.vector.app.features.settings.devices.v2.RefreshDevicesUseCase
|
||||||
import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
|
import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
|
||||||
import im.vector.app.features.settings.devices.v2.signout.InterceptSignoutFlowResponseUseCase
|
|
||||||
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
||||||
import im.vector.app.test.fakes.FakePendingAuthHandler
|
import im.vector.app.test.fakes.FakePendingAuthHandler
|
||||||
import im.vector.app.test.fakes.FakeSharedPreferences
|
import im.vector.app.test.fakes.FakeSharedPreferences
|
||||||
|
@ -67,7 +66,6 @@ class OtherSessionsViewModelTest {
|
||||||
private val fakeGetDeviceFullInfoListUseCase = mockk<GetDeviceFullInfoListUseCase>()
|
private val fakeGetDeviceFullInfoListUseCase = mockk<GetDeviceFullInfoListUseCase>()
|
||||||
private val fakeRefreshDevicesUseCase = mockk<RefreshDevicesUseCase>(relaxed = true)
|
private val fakeRefreshDevicesUseCase = mockk<RefreshDevicesUseCase>(relaxed = true)
|
||||||
private val fakeSignoutSessionsUseCase = FakeSignoutSessionsUseCase()
|
private val fakeSignoutSessionsUseCase = FakeSignoutSessionsUseCase()
|
||||||
private val fakeInterceptSignoutFlowResponseUseCase = mockk<InterceptSignoutFlowResponseUseCase>()
|
|
||||||
private val fakePendingAuthHandler = FakePendingAuthHandler()
|
private val fakePendingAuthHandler = FakePendingAuthHandler()
|
||||||
private val fakeSharedPreferences = FakeSharedPreferences()
|
private val fakeSharedPreferences = FakeSharedPreferences()
|
||||||
|
|
||||||
|
@ -77,7 +75,6 @@ class OtherSessionsViewModelTest {
|
||||||
activeSessionHolder = fakeActiveSessionHolder.instance,
|
activeSessionHolder = fakeActiveSessionHolder.instance,
|
||||||
getDeviceFullInfoListUseCase = fakeGetDeviceFullInfoListUseCase,
|
getDeviceFullInfoListUseCase = fakeGetDeviceFullInfoListUseCase,
|
||||||
signoutSessionsUseCase = fakeSignoutSessionsUseCase.instance,
|
signoutSessionsUseCase = fakeSignoutSessionsUseCase.instance,
|
||||||
interceptSignoutFlowResponseUseCase = fakeInterceptSignoutFlowResponseUseCase,
|
|
||||||
pendingAuthHandler = fakePendingAuthHandler.instance,
|
pendingAuthHandler = fakePendingAuthHandler.instance,
|
||||||
refreshDevicesUseCase = fakeRefreshDevicesUseCase,
|
refreshDevicesUseCase = fakeRefreshDevicesUseCase,
|
||||||
sharedPreferences = fakeSharedPreferences,
|
sharedPreferences = fakeSharedPreferences,
|
||||||
|
@ -325,7 +322,7 @@ class OtherSessionsViewModelTest {
|
||||||
val devices: List<DeviceFullInfo> = listOf(deviceFullInfo1, deviceFullInfo2)
|
val devices: List<DeviceFullInfo> = listOf(deviceFullInfo1, deviceFullInfo2)
|
||||||
givenGetDeviceFullInfoListReturns(filterType = defaultArgs.defaultFilter, devices)
|
givenGetDeviceFullInfoListReturns(filterType = defaultArgs.defaultFilter, devices)
|
||||||
// signout only selected devices
|
// signout only selected devices
|
||||||
fakeSignoutSessionsUseCase.givenSignoutSuccess(listOf(A_DEVICE_ID_2), fakeInterceptSignoutFlowResponseUseCase)
|
fakeSignoutSessionsUseCase.givenSignoutSuccess(listOf(A_DEVICE_ID_2))
|
||||||
val expectedViewState = OtherSessionsViewState(
|
val expectedViewState = OtherSessionsViewState(
|
||||||
devices = Success(listOf(deviceFullInfo1, deviceFullInfo2)),
|
devices = Success(listOf(deviceFullInfo1, deviceFullInfo2)),
|
||||||
currentFilter = defaultArgs.defaultFilter,
|
currentFilter = defaultArgs.defaultFilter,
|
||||||
|
@ -361,7 +358,7 @@ class OtherSessionsViewModelTest {
|
||||||
val devices: List<DeviceFullInfo> = listOf(deviceFullInfo1, deviceFullInfo2)
|
val devices: List<DeviceFullInfo> = listOf(deviceFullInfo1, deviceFullInfo2)
|
||||||
givenGetDeviceFullInfoListReturns(filterType = defaultArgs.defaultFilter, devices)
|
givenGetDeviceFullInfoListReturns(filterType = defaultArgs.defaultFilter, devices)
|
||||||
// signout all devices
|
// signout all devices
|
||||||
fakeSignoutSessionsUseCase.givenSignoutSuccess(listOf(A_DEVICE_ID_1, A_DEVICE_ID_2), fakeInterceptSignoutFlowResponseUseCase)
|
fakeSignoutSessionsUseCase.givenSignoutSuccess(listOf(A_DEVICE_ID_1, A_DEVICE_ID_2))
|
||||||
val expectedViewState = OtherSessionsViewState(
|
val expectedViewState = OtherSessionsViewState(
|
||||||
devices = Success(listOf(deviceFullInfo1, deviceFullInfo2)),
|
devices = Success(listOf(deviceFullInfo1, deviceFullInfo2)),
|
||||||
currentFilter = defaultArgs.defaultFilter,
|
currentFilter = defaultArgs.defaultFilter,
|
||||||
|
@ -426,7 +423,7 @@ class OtherSessionsViewModelTest {
|
||||||
val deviceFullInfo2 = aDeviceFullInfo(A_DEVICE_ID_2, isSelected = true)
|
val deviceFullInfo2 = aDeviceFullInfo(A_DEVICE_ID_2, isSelected = true)
|
||||||
val devices: List<DeviceFullInfo> = listOf(deviceFullInfo1, deviceFullInfo2)
|
val devices: List<DeviceFullInfo> = listOf(deviceFullInfo1, deviceFullInfo2)
|
||||||
givenGetDeviceFullInfoListReturns(filterType = defaultArgs.defaultFilter, devices)
|
givenGetDeviceFullInfoListReturns(filterType = defaultArgs.defaultFilter, devices)
|
||||||
val reAuthNeeded = fakeSignoutSessionsUseCase.givenSignoutReAuthNeeded(listOf(A_DEVICE_ID_1, A_DEVICE_ID_2), fakeInterceptSignoutFlowResponseUseCase)
|
val reAuthNeeded = fakeSignoutSessionsUseCase.givenSignoutReAuthNeeded(listOf(A_DEVICE_ID_1, A_DEVICE_ID_2))
|
||||||
val expectedPendingAuth = DefaultBaseAuth(session = reAuthNeeded.flowResponse.session)
|
val expectedPendingAuth = DefaultBaseAuth(session = reAuthNeeded.flowResponse.session)
|
||||||
val expectedReAuthEvent = OtherSessionsViewEvents.RequestReAuth(reAuthNeeded.flowResponse, reAuthNeeded.errCode)
|
val expectedReAuthEvent = OtherSessionsViewEvents.RequestReAuth(reAuthNeeded.flowResponse, reAuthNeeded.errCode)
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSes
|
||||||
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
||||||
import im.vector.app.test.fakes.FakePendingAuthHandler
|
import im.vector.app.test.fakes.FakePendingAuthHandler
|
||||||
import im.vector.app.test.fakes.FakeSharedPreferences
|
import im.vector.app.test.fakes.FakeSharedPreferences
|
||||||
import im.vector.app.test.fakes.FakeSignoutSessionUseCase
|
import im.vector.app.test.fakes.FakeSignoutSessionsUseCase
|
||||||
import im.vector.app.test.fakes.FakeTogglePushNotificationUseCase
|
import im.vector.app.test.fakes.FakeTogglePushNotificationUseCase
|
||||||
import im.vector.app.test.fakes.FakeVerificationService
|
import im.vector.app.test.fakes.FakeVerificationService
|
||||||
import im.vector.app.test.test
|
import im.vector.app.test.test
|
||||||
|
@ -71,7 +71,7 @@ class SessionOverviewViewModelTest {
|
||||||
private val getDeviceFullInfoUseCase = mockk<GetDeviceFullInfoUseCase>(relaxed = true)
|
private val getDeviceFullInfoUseCase = mockk<GetDeviceFullInfoUseCase>(relaxed = true)
|
||||||
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
|
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
|
||||||
private val checkIfCurrentSessionCanBeVerifiedUseCase = mockk<CheckIfCurrentSessionCanBeVerifiedUseCase>()
|
private val checkIfCurrentSessionCanBeVerifiedUseCase = mockk<CheckIfCurrentSessionCanBeVerifiedUseCase>()
|
||||||
private val fakeSignoutSessionUseCase = FakeSignoutSessionUseCase()
|
private val fakeSignoutSessionsUseCase = FakeSignoutSessionsUseCase()
|
||||||
private val interceptSignoutFlowResponseUseCase = mockk<InterceptSignoutFlowResponseUseCase>()
|
private val interceptSignoutFlowResponseUseCase = mockk<InterceptSignoutFlowResponseUseCase>()
|
||||||
private val fakePendingAuthHandler = FakePendingAuthHandler()
|
private val fakePendingAuthHandler = FakePendingAuthHandler()
|
||||||
private val refreshDevicesUseCase = mockk<RefreshDevicesUseCase>(relaxed = true)
|
private val refreshDevicesUseCase = mockk<RefreshDevicesUseCase>(relaxed = true)
|
||||||
|
@ -84,7 +84,7 @@ class SessionOverviewViewModelTest {
|
||||||
initialState = SessionOverviewViewState(args),
|
initialState = SessionOverviewViewState(args),
|
||||||
getDeviceFullInfoUseCase = getDeviceFullInfoUseCase,
|
getDeviceFullInfoUseCase = getDeviceFullInfoUseCase,
|
||||||
checkIfCurrentSessionCanBeVerifiedUseCase = checkIfCurrentSessionCanBeVerifiedUseCase,
|
checkIfCurrentSessionCanBeVerifiedUseCase = checkIfCurrentSessionCanBeVerifiedUseCase,
|
||||||
signoutSessionUseCase = fakeSignoutSessionUseCase.instance,
|
signoutSessionsUseCase = fakeSignoutSessionsUseCase.instance,
|
||||||
interceptSignoutFlowResponseUseCase = interceptSignoutFlowResponseUseCase,
|
interceptSignoutFlowResponseUseCase = interceptSignoutFlowResponseUseCase,
|
||||||
pendingAuthHandler = fakePendingAuthHandler.instance,
|
pendingAuthHandler = fakePendingAuthHandler.instance,
|
||||||
activeSessionHolder = fakeActiveSessionHolder.instance,
|
activeSessionHolder = fakeActiveSessionHolder.instance,
|
||||||
|
@ -252,7 +252,7 @@ class SessionOverviewViewModelTest {
|
||||||
val deviceFullInfo = mockk<DeviceFullInfo>()
|
val deviceFullInfo = mockk<DeviceFullInfo>()
|
||||||
every { deviceFullInfo.isCurrentDevice } returns false
|
every { deviceFullInfo.isCurrentDevice } returns false
|
||||||
every { getDeviceFullInfoUseCase.execute(A_SESSION_ID_1) } returns flowOf(deviceFullInfo)
|
every { getDeviceFullInfoUseCase.execute(A_SESSION_ID_1) } returns flowOf(deviceFullInfo)
|
||||||
fakeSignoutSessionUseCase.givenSignoutSuccess(A_SESSION_ID_1, interceptSignoutFlowResponseUseCase)
|
fakeSignoutSessionsUseCase.givenSignoutSuccess(listOf(A_SESSION_ID_1))
|
||||||
val signoutAction = SessionOverviewAction.SignoutOtherSession
|
val signoutAction = SessionOverviewAction.SignoutOtherSession
|
||||||
givenCurrentSessionIsTrusted()
|
givenCurrentSessionIsTrusted()
|
||||||
val expectedViewState = SessionOverviewViewState(
|
val expectedViewState = SessionOverviewViewState(
|
||||||
|
@ -289,7 +289,7 @@ class SessionOverviewViewModelTest {
|
||||||
every { deviceFullInfo.isCurrentDevice } returns false
|
every { deviceFullInfo.isCurrentDevice } returns false
|
||||||
every { getDeviceFullInfoUseCase.execute(A_SESSION_ID_1) } returns flowOf(deviceFullInfo)
|
every { getDeviceFullInfoUseCase.execute(A_SESSION_ID_1) } returns flowOf(deviceFullInfo)
|
||||||
val error = Exception()
|
val error = Exception()
|
||||||
fakeSignoutSessionUseCase.givenSignoutError(A_SESSION_ID_1, error)
|
fakeSignoutSessionsUseCase.givenSignoutError(listOf(A_SESSION_ID_1), error)
|
||||||
val signoutAction = SessionOverviewAction.SignoutOtherSession
|
val signoutAction = SessionOverviewAction.SignoutOtherSession
|
||||||
givenCurrentSessionIsTrusted()
|
givenCurrentSessionIsTrusted()
|
||||||
val expectedViewState = SessionOverviewViewState(
|
val expectedViewState = SessionOverviewViewState(
|
||||||
|
@ -322,7 +322,7 @@ class SessionOverviewViewModelTest {
|
||||||
val deviceFullInfo = mockk<DeviceFullInfo>()
|
val deviceFullInfo = mockk<DeviceFullInfo>()
|
||||||
every { deviceFullInfo.isCurrentDevice } returns false
|
every { deviceFullInfo.isCurrentDevice } returns false
|
||||||
every { getDeviceFullInfoUseCase.execute(A_SESSION_ID_1) } returns flowOf(deviceFullInfo)
|
every { getDeviceFullInfoUseCase.execute(A_SESSION_ID_1) } returns flowOf(deviceFullInfo)
|
||||||
val reAuthNeeded = fakeSignoutSessionUseCase.givenSignoutReAuthNeeded(A_SESSION_ID_1, interceptSignoutFlowResponseUseCase)
|
val reAuthNeeded = fakeSignoutSessionsUseCase.givenSignoutReAuthNeeded(listOf(A_SESSION_ID_1))
|
||||||
val signoutAction = SessionOverviewAction.SignoutOtherSession
|
val signoutAction = SessionOverviewAction.SignoutOtherSession
|
||||||
givenCurrentSessionIsTrusted()
|
givenCurrentSessionIsTrusted()
|
||||||
val expectedPendingAuth = DefaultBaseAuth(session = reAuthNeeded.flowResponse.session)
|
val expectedPendingAuth = DefaultBaseAuth(session = reAuthNeeded.flowResponse.session)
|
||||||
|
|
|
@ -24,8 +24,8 @@ import io.mockk.mockk
|
||||||
import io.mockk.mockkStatic
|
import io.mockk.mockkStatic
|
||||||
import io.mockk.runs
|
import io.mockk.runs
|
||||||
import io.mockk.unmockkAll
|
import io.mockk.unmockkAll
|
||||||
|
import org.amshove.kluent.shouldBe
|
||||||
import org.amshove.kluent.shouldBeEqualTo
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
import org.amshove.kluent.shouldBeInstanceOf
|
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
@ -63,7 +63,7 @@ class InterceptSignoutFlowResponseUseCaseTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given no error and a stored password and a next stage as password when intercepting then promise is resumed and success is returned`() {
|
fun `given no error and a stored password and a next stage as password when intercepting then promise is resumed and null is returned`() {
|
||||||
// Given
|
// Given
|
||||||
val registrationFlowResponse = givenNextUncompletedStage(LoginFlowTypes.PASSWORD, A_SESSION_ID)
|
val registrationFlowResponse = givenNextUncompletedStage(LoginFlowTypes.PASSWORD, A_SESSION_ID)
|
||||||
fakeReAuthHelper.givenStoredPassword(A_PASSWORD)
|
fakeReAuthHelper.givenStoredPassword(A_PASSWORD)
|
||||||
|
@ -84,7 +84,7 @@ class InterceptSignoutFlowResponseUseCaseTest {
|
||||||
)
|
)
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
result shouldBeInstanceOf (SignoutSessionResult.Completed::class)
|
result shouldBe null
|
||||||
every {
|
every {
|
||||||
promise.resume(expectedAuth)
|
promise.resume(expectedAuth)
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ class InterceptSignoutFlowResponseUseCaseTest {
|
||||||
fakeReAuthHelper.givenStoredPassword(A_PASSWORD)
|
fakeReAuthHelper.givenStoredPassword(A_PASSWORD)
|
||||||
val errorCode = AN_ERROR_CODE
|
val errorCode = AN_ERROR_CODE
|
||||||
val promise = mockk<Continuation<UIABaseAuth>>()
|
val promise = mockk<Continuation<UIABaseAuth>>()
|
||||||
val expectedResult = SignoutSessionResult.ReAuthNeeded(
|
val expectedResult = SignoutSessionsReAuthNeeded(
|
||||||
pendingAuth = DefaultBaseAuth(session = A_SESSION_ID),
|
pendingAuth = DefaultBaseAuth(session = A_SESSION_ID),
|
||||||
uiaContinuation = promise,
|
uiaContinuation = promise,
|
||||||
flowResponse = registrationFlowResponse,
|
flowResponse = registrationFlowResponse,
|
||||||
|
@ -122,7 +122,7 @@ class InterceptSignoutFlowResponseUseCaseTest {
|
||||||
fakeReAuthHelper.givenStoredPassword(A_PASSWORD)
|
fakeReAuthHelper.givenStoredPassword(A_PASSWORD)
|
||||||
val errorCode: String? = null
|
val errorCode: String? = null
|
||||||
val promise = mockk<Continuation<UIABaseAuth>>()
|
val promise = mockk<Continuation<UIABaseAuth>>()
|
||||||
val expectedResult = SignoutSessionResult.ReAuthNeeded(
|
val expectedResult = SignoutSessionsReAuthNeeded(
|
||||||
pendingAuth = DefaultBaseAuth(session = A_SESSION_ID),
|
pendingAuth = DefaultBaseAuth(session = A_SESSION_ID),
|
||||||
uiaContinuation = promise,
|
uiaContinuation = promise,
|
||||||
flowResponse = registrationFlowResponse,
|
flowResponse = registrationFlowResponse,
|
||||||
|
@ -147,7 +147,7 @@ class InterceptSignoutFlowResponseUseCaseTest {
|
||||||
fakeReAuthHelper.givenStoredPassword(null)
|
fakeReAuthHelper.givenStoredPassword(null)
|
||||||
val errorCode: String? = null
|
val errorCode: String? = null
|
||||||
val promise = mockk<Continuation<UIABaseAuth>>()
|
val promise = mockk<Continuation<UIABaseAuth>>()
|
||||||
val expectedResult = SignoutSessionResult.ReAuthNeeded(
|
val expectedResult = SignoutSessionsReAuthNeeded(
|
||||||
pendingAuth = DefaultBaseAuth(session = A_SESSION_ID),
|
pendingAuth = DefaultBaseAuth(session = A_SESSION_ID),
|
||||||
uiaContinuation = promise,
|
uiaContinuation = promise,
|
||||||
flowResponse = registrationFlowResponse,
|
flowResponse = registrationFlowResponse,
|
||||||
|
|
|
@ -1,79 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 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 im.vector.app.features.settings.devices.v2.signout
|
|
||||||
|
|
||||||
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
|
||||||
import io.mockk.every
|
|
||||||
import io.mockk.mockk
|
|
||||||
import kotlinx.coroutines.test.runTest
|
|
||||||
import org.amshove.kluent.shouldBe
|
|
||||||
import org.junit.Test
|
|
||||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
|
||||||
|
|
||||||
private const val A_DEVICE_ID = "device-id"
|
|
||||||
|
|
||||||
class SignoutSessionUseCaseTest {
|
|
||||||
|
|
||||||
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
|
|
||||||
|
|
||||||
private val signoutSessionUseCase = SignoutSessionUseCase(
|
|
||||||
activeSessionHolder = fakeActiveSessionHolder.instance
|
|
||||||
)
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `given a device id when signing out with success then success result is returned`() = runTest {
|
|
||||||
// Given
|
|
||||||
val interceptor = givenAuthInterceptor()
|
|
||||||
fakeActiveSessionHolder.fakeSession
|
|
||||||
.fakeCryptoService
|
|
||||||
.givenDeleteDeviceSucceeds(A_DEVICE_ID)
|
|
||||||
|
|
||||||
// When
|
|
||||||
val result = signoutSessionUseCase.execute(A_DEVICE_ID, interceptor)
|
|
||||||
|
|
||||||
// Then
|
|
||||||
result.isSuccess shouldBe true
|
|
||||||
every {
|
|
||||||
fakeActiveSessionHolder.fakeSession
|
|
||||||
.fakeCryptoService
|
|
||||||
.deleteDevice(A_DEVICE_ID, interceptor, any())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `given a device id when signing out with error then failure result is returned`() = runTest {
|
|
||||||
// Given
|
|
||||||
val interceptor = givenAuthInterceptor()
|
|
||||||
val error = mockk<Exception>()
|
|
||||||
fakeActiveSessionHolder.fakeSession
|
|
||||||
.fakeCryptoService
|
|
||||||
.givenDeleteDeviceFailsWithError(A_DEVICE_ID, error)
|
|
||||||
|
|
||||||
// When
|
|
||||||
val result = signoutSessionUseCase.execute(A_DEVICE_ID, interceptor)
|
|
||||||
|
|
||||||
// Then
|
|
||||||
result.isFailure shouldBe true
|
|
||||||
every {
|
|
||||||
fakeActiveSessionHolder.fakeSession
|
|
||||||
.fakeCryptoService
|
|
||||||
.deleteDevice(A_DEVICE_ID, interceptor, any())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun givenAuthInterceptor() = mockk<UserInteractiveAuthInterceptor>()
|
|
||||||
}
|
|
|
@ -19,10 +19,10 @@ package im.vector.app.features.settings.devices.v2.signout
|
||||||
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
import io.mockk.verify
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.amshove.kluent.shouldBe
|
import org.amshove.kluent.shouldBe
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
|
||||||
|
|
||||||
private const val A_DEVICE_ID_1 = "device-id-1"
|
private const val A_DEVICE_ID_1 = "device-id-1"
|
||||||
private const val A_DEVICE_ID_2 = "device-id-2"
|
private const val A_DEVICE_ID_2 = "device-id-2"
|
||||||
|
@ -30,36 +30,38 @@ private const val A_DEVICE_ID_2 = "device-id-2"
|
||||||
class SignoutSessionsUseCaseTest {
|
class SignoutSessionsUseCaseTest {
|
||||||
|
|
||||||
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
|
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
|
||||||
|
private val fakeInterceptSignoutFlowResponseUseCase = mockk<InterceptSignoutFlowResponseUseCase>()
|
||||||
|
|
||||||
private val signoutSessionsUseCase = SignoutSessionsUseCase(
|
private val signoutSessionsUseCase = SignoutSessionsUseCase(
|
||||||
activeSessionHolder = fakeActiveSessionHolder.instance
|
activeSessionHolder = fakeActiveSessionHolder.instance,
|
||||||
|
interceptSignoutFlowResponseUseCase = fakeInterceptSignoutFlowResponseUseCase,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given a list of device ids when signing out with success then success result is returned`() = runTest {
|
fun `given a list of device ids when signing out with success then success result is returned`() = runTest {
|
||||||
// Given
|
// Given
|
||||||
val interceptor = givenAuthInterceptor()
|
val callback = givenOnReAuthCallback()
|
||||||
val deviceIds = listOf(A_DEVICE_ID_1, A_DEVICE_ID_2)
|
val deviceIds = listOf(A_DEVICE_ID_1, A_DEVICE_ID_2)
|
||||||
fakeActiveSessionHolder.fakeSession
|
fakeActiveSessionHolder.fakeSession
|
||||||
.fakeCryptoService
|
.fakeCryptoService
|
||||||
.givenDeleteDevicesSucceeds(deviceIds)
|
.givenDeleteDevicesSucceeds(deviceIds)
|
||||||
|
|
||||||
// When
|
// When
|
||||||
val result = signoutSessionsUseCase.execute(deviceIds, interceptor)
|
val result = signoutSessionsUseCase.execute(deviceIds, callback)
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
result.isSuccess shouldBe true
|
result.isSuccess shouldBe true
|
||||||
every {
|
verify {
|
||||||
fakeActiveSessionHolder.fakeSession
|
fakeActiveSessionHolder.fakeSession
|
||||||
.fakeCryptoService
|
.fakeCryptoService
|
||||||
.deleteDevices(deviceIds, interceptor, any())
|
.deleteDevices(deviceIds, any(), any())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given a list of device ids when signing out with error then failure result is returned`() = runTest {
|
fun `given a list of device ids when signing out with error then failure result is returned`() = runTest {
|
||||||
// Given
|
// Given
|
||||||
val interceptor = givenAuthInterceptor()
|
val interceptor = givenOnReAuthCallback()
|
||||||
val deviceIds = listOf(A_DEVICE_ID_1, A_DEVICE_ID_2)
|
val deviceIds = listOf(A_DEVICE_ID_1, A_DEVICE_ID_2)
|
||||||
val error = mockk<Exception>()
|
val error = mockk<Exception>()
|
||||||
fakeActiveSessionHolder.fakeSession
|
fakeActiveSessionHolder.fakeSession
|
||||||
|
@ -71,12 +73,41 @@ class SignoutSessionsUseCaseTest {
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
result.isFailure shouldBe true
|
result.isFailure shouldBe true
|
||||||
every {
|
verify {
|
||||||
fakeActiveSessionHolder.fakeSession
|
fakeActiveSessionHolder.fakeSession
|
||||||
.fakeCryptoService
|
.fakeCryptoService
|
||||||
.deleteDevices(deviceIds, interceptor, any())
|
.deleteDevices(deviceIds, any(), any())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun givenAuthInterceptor() = mockk<UserInteractiveAuthInterceptor>()
|
@Test
|
||||||
|
fun `given a list of device ids when signing out with reAuth needed then callback is called`() = runTest {
|
||||||
|
// Given
|
||||||
|
val callback = givenOnReAuthCallback()
|
||||||
|
val deviceIds = listOf(A_DEVICE_ID_1, A_DEVICE_ID_2)
|
||||||
|
fakeActiveSessionHolder.fakeSession
|
||||||
|
.fakeCryptoService
|
||||||
|
.givenDeleteDevicesNeedsUIAuth(deviceIds)
|
||||||
|
val reAuthNeeded = SignoutSessionsReAuthNeeded(
|
||||||
|
pendingAuth = mockk(),
|
||||||
|
uiaContinuation = mockk(),
|
||||||
|
flowResponse = mockk(),
|
||||||
|
errCode = "errorCode"
|
||||||
|
)
|
||||||
|
every { fakeInterceptSignoutFlowResponseUseCase.execute(any(), any(), any()) } returns reAuthNeeded
|
||||||
|
|
||||||
|
// When
|
||||||
|
val result = signoutSessionsUseCase.execute(deviceIds, callback)
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.isSuccess shouldBe true
|
||||||
|
verify {
|
||||||
|
fakeActiveSessionHolder.fakeSession
|
||||||
|
.fakeCryptoService
|
||||||
|
.deleteDevices(deviceIds, any(), any())
|
||||||
|
callback(reAuthNeeded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun givenOnReAuthCallback(): (SignoutSessionsReAuthNeeded) -> Unit = {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.slot
|
import io.mockk.slot
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
import org.matrix.android.sdk.api.MatrixCallback
|
||||||
|
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||||
import org.matrix.android.sdk.api.session.crypto.CryptoService
|
import org.matrix.android.sdk.api.session.crypto.CryptoService
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
|
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
|
||||||
|
@ -70,30 +71,21 @@ class FakeCryptoService(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun givenDeleteDeviceSucceeds(deviceId: String) {
|
fun givenDeleteDevicesSucceeds(deviceIds: List<String>) {
|
||||||
val matrixCallback = slot<MatrixCallback<Unit>>()
|
every { deleteDevices(deviceIds, any(), any()) } answers {
|
||||||
every { deleteDevice(deviceId, any(), capture(matrixCallback)) } answers {
|
|
||||||
thirdArg<MatrixCallback<Unit>>().onSuccess(Unit)
|
thirdArg<MatrixCallback<Unit>>().onSuccess(Unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun givenDeleteDeviceFailsWithError(deviceId: String, error: Exception) {
|
fun givenDeleteDevicesNeedsUIAuth(deviceIds: List<String>) {
|
||||||
val matrixCallback = slot<MatrixCallback<Unit>>()
|
every { deleteDevices(deviceIds, any(), any()) } answers {
|
||||||
every { deleteDevice(deviceId, any(), capture(matrixCallback)) } answers {
|
secondArg<UserInteractiveAuthInterceptor>().performStage(mockk(), "", mockk())
|
||||||
thirdArg<MatrixCallback<Unit>>().onFailure(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun givenDeleteDevicesSucceeds(deviceIds: List<String>) {
|
|
||||||
val matrixCallback = slot<MatrixCallback<Unit>>()
|
|
||||||
every { deleteDevices(deviceIds, any(), capture(matrixCallback)) } answers {
|
|
||||||
thirdArg<MatrixCallback<Unit>>().onSuccess(Unit)
|
thirdArg<MatrixCallback<Unit>>().onSuccess(Unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun givenDeleteDevicesFailsWithError(deviceIds: List<String>, error: Exception) {
|
fun givenDeleteDevicesFailsWithError(deviceIds: List<String>, error: Exception) {
|
||||||
val matrixCallback = slot<MatrixCallback<Unit>>()
|
every { deleteDevices(deviceIds, any(), any()) } answers {
|
||||||
every { deleteDevices(deviceIds, any(), capture(matrixCallback)) } answers {
|
|
||||||
thirdArg<MatrixCallback<Unit>>().onFailure(error)
|
thirdArg<MatrixCallback<Unit>>().onFailure(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 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 im.vector.app.test.fakes
|
|
||||||
|
|
||||||
import im.vector.app.features.settings.devices.v2.signout.InterceptSignoutFlowResponseUseCase
|
|
||||||
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionResult
|
|
||||||
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionUseCase
|
|
||||||
import io.mockk.coEvery
|
|
||||||
import io.mockk.every
|
|
||||||
import io.mockk.mockk
|
|
||||||
import io.mockk.slot
|
|
||||||
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
|
||||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
|
||||||
import kotlin.coroutines.Continuation
|
|
||||||
|
|
||||||
class FakeSignoutSessionUseCase {
|
|
||||||
|
|
||||||
val instance = mockk<SignoutSessionUseCase>()
|
|
||||||
|
|
||||||
fun givenSignoutSuccess(
|
|
||||||
deviceId: String,
|
|
||||||
interceptSignoutFlowResponseUseCase: InterceptSignoutFlowResponseUseCase,
|
|
||||||
) {
|
|
||||||
val interceptor = slot<UserInteractiveAuthInterceptor>()
|
|
||||||
val flowResponse = mockk<RegistrationFlowResponse>()
|
|
||||||
val errorCode = "errorCode"
|
|
||||||
val promise = mockk<Continuation<UIABaseAuth>>()
|
|
||||||
every { interceptSignoutFlowResponseUseCase.execute(flowResponse, errorCode, promise) } returns SignoutSessionResult.Completed
|
|
||||||
coEvery { instance.execute(deviceId, capture(interceptor)) } coAnswers {
|
|
||||||
secondArg<UserInteractiveAuthInterceptor>().performStage(flowResponse, errorCode, promise)
|
|
||||||
Result.success(Unit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun givenSignoutReAuthNeeded(
|
|
||||||
deviceId: String,
|
|
||||||
interceptSignoutFlowResponseUseCase: InterceptSignoutFlowResponseUseCase,
|
|
||||||
): SignoutSessionResult.ReAuthNeeded {
|
|
||||||
val interceptor = slot<UserInteractiveAuthInterceptor>()
|
|
||||||
val flowResponse = mockk<RegistrationFlowResponse>()
|
|
||||||
every { flowResponse.session } returns "a-session-id"
|
|
||||||
val errorCode = "errorCode"
|
|
||||||
val promise = mockk<Continuation<UIABaseAuth>>()
|
|
||||||
val reAuthNeeded = SignoutSessionResult.ReAuthNeeded(
|
|
||||||
pendingAuth = mockk(),
|
|
||||||
uiaContinuation = promise,
|
|
||||||
flowResponse = flowResponse,
|
|
||||||
errCode = errorCode,
|
|
||||||
)
|
|
||||||
every { interceptSignoutFlowResponseUseCase.execute(flowResponse, errorCode, promise) } returns reAuthNeeded
|
|
||||||
coEvery { instance.execute(deviceId, capture(interceptor)) } coAnswers {
|
|
||||||
secondArg<UserInteractiveAuthInterceptor>().performStage(flowResponse, errorCode, promise)
|
|
||||||
Result.success(Unit)
|
|
||||||
}
|
|
||||||
|
|
||||||
return reAuthNeeded
|
|
||||||
}
|
|
||||||
|
|
||||||
fun givenSignoutError(deviceId: String, error: Throwable) {
|
|
||||||
coEvery { instance.execute(deviceId, any()) } returns Result.failure(error)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,55 +16,33 @@
|
||||||
|
|
||||||
package im.vector.app.test.fakes
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
import im.vector.app.features.settings.devices.v2.signout.InterceptSignoutFlowResponseUseCase
|
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionsReAuthNeeded
|
||||||
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionResult
|
|
||||||
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionsUseCase
|
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionsUseCase
|
||||||
import io.mockk.coEvery
|
import io.mockk.coEvery
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.slot
|
|
||||||
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
|
||||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||||
import kotlin.coroutines.Continuation
|
|
||||||
|
|
||||||
class FakeSignoutSessionsUseCase {
|
class FakeSignoutSessionsUseCase {
|
||||||
|
|
||||||
val instance = mockk<SignoutSessionsUseCase>()
|
val instance = mockk<SignoutSessionsUseCase>()
|
||||||
|
|
||||||
fun givenSignoutSuccess(
|
fun givenSignoutSuccess(deviceIds: List<String>) {
|
||||||
deviceIds: List<String>,
|
coEvery { instance.execute(deviceIds, any()) } returns Result.success(Unit)
|
||||||
interceptSignoutFlowResponseUseCase: InterceptSignoutFlowResponseUseCase,
|
|
||||||
) {
|
|
||||||
val interceptor = slot<UserInteractiveAuthInterceptor>()
|
|
||||||
val flowResponse = mockk<RegistrationFlowResponse>()
|
|
||||||
val errorCode = "errorCode"
|
|
||||||
val promise = mockk<Continuation<UIABaseAuth>>()
|
|
||||||
every { interceptSignoutFlowResponseUseCase.execute(flowResponse, errorCode, promise) } returns SignoutSessionResult.Completed
|
|
||||||
coEvery { instance.execute(deviceIds, capture(interceptor)) } coAnswers {
|
|
||||||
secondArg<UserInteractiveAuthInterceptor>().performStage(flowResponse, errorCode, promise)
|
|
||||||
Result.success(Unit)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun givenSignoutReAuthNeeded(
|
fun givenSignoutReAuthNeeded(deviceIds: List<String>): SignoutSessionsReAuthNeeded {
|
||||||
deviceIds: List<String>,
|
|
||||||
interceptSignoutFlowResponseUseCase: InterceptSignoutFlowResponseUseCase,
|
|
||||||
): SignoutSessionResult.ReAuthNeeded {
|
|
||||||
val interceptor = slot<UserInteractiveAuthInterceptor>()
|
|
||||||
val flowResponse = mockk<RegistrationFlowResponse>()
|
val flowResponse = mockk<RegistrationFlowResponse>()
|
||||||
every { flowResponse.session } returns "a-session-id"
|
every { flowResponse.session } returns "a-session-id"
|
||||||
val errorCode = "errorCode"
|
val errorCode = "errorCode"
|
||||||
val promise = mockk<Continuation<UIABaseAuth>>()
|
val reAuthNeeded = SignoutSessionsReAuthNeeded(
|
||||||
val reAuthNeeded = SignoutSessionResult.ReAuthNeeded(
|
|
||||||
pendingAuth = mockk(),
|
pendingAuth = mockk(),
|
||||||
uiaContinuation = promise,
|
uiaContinuation = mockk(),
|
||||||
flowResponse = flowResponse,
|
flowResponse = flowResponse,
|
||||||
errCode = errorCode,
|
errCode = errorCode,
|
||||||
)
|
)
|
||||||
every { interceptSignoutFlowResponseUseCase.execute(flowResponse, errorCode, promise) } returns reAuthNeeded
|
coEvery { instance.execute(deviceIds, any()) } coAnswers {
|
||||||
coEvery { instance.execute(deviceIds, capture(interceptor)) } coAnswers {
|
secondArg<(SignoutSessionsReAuthNeeded) -> Unit>().invoke(reAuthNeeded)
|
||||||
secondArg<UserInteractiveAuthInterceptor>().performStage(flowResponse, errorCode, promise)
|
|
||||||
Result.success(Unit)
|
Result.success(Unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue