Rename Async to AsyncData
This commit is contained in:
parent
0e4cb679e1
commit
bd91dd64e2
139 changed files with 745 additions and 745 deletions
|
|
@ -34,7 +34,7 @@ import io.element.android.features.call.data.WidgetMessage
|
||||||
import io.element.android.features.call.utils.CallWidgetProvider
|
import io.element.android.features.call.utils.CallWidgetProvider
|
||||||
import io.element.android.features.call.utils.WidgetMessageInterceptor
|
import io.element.android.features.call.utils.WidgetMessageInterceptor
|
||||||
import io.element.android.features.call.utils.WidgetMessageSerializer
|
import io.element.android.features.call.utils.WidgetMessageSerializer
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
|
|
@ -75,7 +75,7 @@ class CallScreenPresenter @AssistedInject constructor(
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): CallScreenState {
|
override fun present(): CallScreenState {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
val urlState = remember { mutableStateOf<Async<String>>(Async.Uninitialized) }
|
val urlState = remember { mutableStateOf<AsyncData<String>>(AsyncData.Uninitialized) }
|
||||||
val callWidgetDriver = remember { mutableStateOf<MatrixWidgetDriver?>(null) }
|
val callWidgetDriver = remember { mutableStateOf<MatrixWidgetDriver?>(null) }
|
||||||
val messageInterceptor = remember { mutableStateOf<WidgetMessageInterceptor?>(null) }
|
val messageInterceptor = remember { mutableStateOf<WidgetMessageInterceptor?>(null) }
|
||||||
var isJoinedCall by rememberSaveable { mutableStateOf(false) }
|
var isJoinedCall by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
@ -154,7 +154,7 @@ class CallScreenPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
private fun CoroutineScope.loadUrl(
|
private fun CoroutineScope.loadUrl(
|
||||||
inputs: CallType,
|
inputs: CallType,
|
||||||
urlState: MutableState<Async<String>>,
|
urlState: MutableState<AsyncData<String>>,
|
||||||
callWidgetDriver: MutableState<MatrixWidgetDriver?>,
|
callWidgetDriver: MutableState<MatrixWidgetDriver?>,
|
||||||
) = launch {
|
) = launch {
|
||||||
urlState.runCatchingUpdatingState {
|
urlState.runCatchingUpdatingState {
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,10 @@
|
||||||
|
|
||||||
package io.element.android.features.call.ui
|
package io.element.android.features.call.ui
|
||||||
|
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
|
|
||||||
data class CallScreenState(
|
data class CallScreenState(
|
||||||
val urlState: Async<String>,
|
val urlState: AsyncData<String>,
|
||||||
val userAgent: String,
|
val userAgent: String,
|
||||||
val isInWidgetMode: Boolean,
|
val isInWidgetMode: Boolean,
|
||||||
val eventSink: (CallScreenEvents) -> Unit,
|
val eventSink: (CallScreenEvents) -> Unit,
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ import androidx.compose.ui.viewinterop.AndroidView
|
||||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||||
import io.element.android.features.call.R
|
import io.element.android.features.call.R
|
||||||
import io.element.android.features.call.utils.WebViewWidgetMessageInterceptor
|
import io.element.android.features.call.utils.WebViewWidgetMessageInterceptor
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
|
|
@ -96,7 +96,7 @@ internal fun CallScreenView(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun CallWebView(
|
private fun CallWebView(
|
||||||
url: Async<String>,
|
url: AsyncData<String>,
|
||||||
userAgent: String,
|
userAgent: String,
|
||||||
onPermissionsRequested: (PermissionRequest) -> Unit,
|
onPermissionsRequested: (PermissionRequest) -> Unit,
|
||||||
onWebViewCreated: (WebView) -> Unit,
|
onWebViewCreated: (WebView) -> Unit,
|
||||||
|
|
@ -116,7 +116,7 @@ private fun CallWebView(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
update = { webView ->
|
update = { webView ->
|
||||||
if (url is Async.Success && webView.url != url.data) {
|
if (url is AsyncData.Success && webView.url != url.data) {
|
||||||
webView.loadUrl(url.data)
|
webView.loadUrl(url.data)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -161,7 +161,7 @@ internal fun CallScreenViewPreview() {
|
||||||
ElementPreview {
|
ElementPreview {
|
||||||
CallScreenView(
|
CallScreenView(
|
||||||
state = CallScreenState(
|
state = CallScreenState(
|
||||||
urlState = Async.Success("https://call.element.io/some-actual-call?with=parameters"),
|
urlState = AsyncData.Success("https://call.element.io/some-actual-call?with=parameters"),
|
||||||
isInWidgetMode = false,
|
isInWidgetMode = false,
|
||||||
userAgent = "",
|
userAgent = "",
|
||||||
eventSink = {},
|
eventSink = {},
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.call.CallType
|
import io.element.android.features.call.CallType
|
||||||
import io.element.android.features.call.utils.FakeCallWidgetProvider
|
import io.element.android.features.call.utils.FakeCallWidgetProvider
|
||||||
import io.element.android.features.call.utils.FakeWidgetMessageInterceptor
|
import io.element.android.features.call.utils.FakeWidgetMessageInterceptor
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.libraries.matrix.api.sync.SyncState
|
import io.element.android.libraries.matrix.api.sync.SyncState
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||||
|
|
@ -62,7 +62,7 @@ class CallScreenPresenterTest {
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.urlState).isEqualTo(Async.Success("https://call.element.io"))
|
assertThat(initialState.urlState).isEqualTo(AsyncData.Success("https://call.element.io"))
|
||||||
assertThat(initialState.isInWidgetMode).isFalse()
|
assertThat(initialState.isInWidgetMode).isFalse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -83,7 +83,7 @@ class CallScreenPresenterTest {
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.urlState).isInstanceOf(Async.Success::class.java)
|
assertThat(initialState.urlState).isInstanceOf(AsyncData.Success::class.java)
|
||||||
assertThat(initialState.isInWidgetMode).isTrue()
|
assertThat(initialState.isInWidgetMode).isTrue()
|
||||||
assertThat(widgetProvider.getWidgetCalled).isTrue()
|
assertThat(widgetProvider.getWidgetCalled).isTrue()
|
||||||
assertThat(widgetDriver.runCalledCount).isEqualTo(1)
|
assertThat(widgetDriver.runCalledCount).isEqualTo(1)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
package io.element.android.features.createroom.api
|
package io.element.android.features.createroom.api
|
||||||
|
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
|
|
||||||
|
|
@ -27,5 +27,5 @@ interface StartDMAction {
|
||||||
* @param userId The user to start a DM with.
|
* @param userId The user to start a DM with.
|
||||||
* @param actionState The state to update with the result of the action.
|
* @param actionState The state to update with the result of the action.
|
||||||
*/
|
*/
|
||||||
suspend fun execute(userId: UserId, actionState: MutableState<Async<RoomId>>)
|
suspend fun execute(userId: UserId, actionState: MutableState<AsyncData<RoomId>>)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import androidx.compose.runtime.MutableState
|
||||||
import com.squareup.anvil.annotations.ContributesBinding
|
import com.squareup.anvil.annotations.ContributesBinding
|
||||||
import im.vector.app.features.analytics.plan.CreatedRoom
|
import im.vector.app.features.analytics.plan.CreatedRoom
|
||||||
import io.element.android.features.createroom.api.StartDMAction
|
import io.element.android.features.createroom.api.StartDMAction
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.di.SessionScope
|
import io.element.android.libraries.di.SessionScope
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
@ -36,17 +36,17 @@ class DefaultStartDMAction @Inject constructor(
|
||||||
private val analyticsService: AnalyticsService,
|
private val analyticsService: AnalyticsService,
|
||||||
) : StartDMAction {
|
) : StartDMAction {
|
||||||
|
|
||||||
override suspend fun execute(userId: UserId, actionState: MutableState<Async<RoomId>>) {
|
override suspend fun execute(userId: UserId, actionState: MutableState<AsyncData<RoomId>>) {
|
||||||
actionState.value = Async.Loading()
|
actionState.value = AsyncData.Loading()
|
||||||
when (val result = matrixClient.startDM(userId)) {
|
when (val result = matrixClient.startDM(userId)) {
|
||||||
is StartDMResult.Success -> {
|
is StartDMResult.Success -> {
|
||||||
if (result.isNew) {
|
if (result.isNew) {
|
||||||
analyticsService.capture(CreatedRoom(isDM = true))
|
analyticsService.capture(CreatedRoom(isDM = true))
|
||||||
}
|
}
|
||||||
actionState.value = Async.Success(result.roomId)
|
actionState.value = AsyncData.Success(result.roomId)
|
||||||
}
|
}
|
||||||
is StartDMResult.Failure -> {
|
is StartDMResult.Failure -> {
|
||||||
actionState.value = Async.Failure(result.throwable)
|
actionState.value = AsyncData.Failure(result.throwable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import im.vector.app.features.analytics.plan.CreatedRoom
|
import im.vector.app.features.analytics.plan.CreatedRoom
|
||||||
import io.element.android.features.createroom.impl.CreateRoomConfig
|
import io.element.android.features.createroom.impl.CreateRoomConfig
|
||||||
import io.element.android.features.createroom.impl.CreateRoomDataStore
|
import io.element.android.features.createroom.impl.CreateRoomDataStore
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
|
|
@ -91,10 +91,10 @@ class ConfigureRoomPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
val localCoroutineScope = rememberCoroutineScope()
|
val localCoroutineScope = rememberCoroutineScope()
|
||||||
val createRoomAction: MutableState<Async<RoomId>> = remember { mutableStateOf(Async.Uninitialized) }
|
val createRoomAction: MutableState<AsyncData<RoomId>> = remember { mutableStateOf(AsyncData.Uninitialized) }
|
||||||
|
|
||||||
fun createRoom(config: CreateRoomConfig) {
|
fun createRoom(config: CreateRoomConfig) {
|
||||||
createRoomAction.value = Async.Uninitialized
|
createRoomAction.value = AsyncData.Uninitialized
|
||||||
localCoroutineScope.createRoom(config, createRoomAction)
|
localCoroutineScope.createRoom(config, createRoomAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,7 +118,7 @@ class ConfigureRoomPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigureRoomEvents.CancelCreateRoom -> createRoomAction.value = Async.Uninitialized
|
ConfigureRoomEvents.CancelCreateRoom -> createRoomAction.value = AsyncData.Uninitialized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,7 +133,7 @@ class ConfigureRoomPresenter @Inject constructor(
|
||||||
|
|
||||||
private fun CoroutineScope.createRoom(
|
private fun CoroutineScope.createRoom(
|
||||||
config: CreateRoomConfig,
|
config: CreateRoomConfig,
|
||||||
createRoomAction: MutableState<Async<RoomId>>
|
createRoomAction: MutableState<AsyncData<RoomId>>
|
||||||
) = launch {
|
) = launch {
|
||||||
suspend {
|
suspend {
|
||||||
val avatarUrl = config.avatarUri?.let { uploadAvatar(it) }
|
val avatarUrl = config.avatarUri?.let { uploadAvatar(it) }
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ package io.element.android.features.createroom.impl.configureroom
|
||||||
|
|
||||||
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
||||||
import io.element.android.features.createroom.impl.CreateRoomConfig
|
import io.element.android.features.createroom.impl.CreateRoomConfig
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.permissions.api.PermissionsState
|
import io.element.android.libraries.permissions.api.PermissionsState
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
@ -26,7 +26,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||||
data class ConfigureRoomState(
|
data class ConfigureRoomState(
|
||||||
val config: CreateRoomConfig,
|
val config: CreateRoomConfig,
|
||||||
val avatarActions: ImmutableList<AvatarAction>,
|
val avatarActions: ImmutableList<AvatarAction>,
|
||||||
val createRoomAction: Async<RoomId>,
|
val createRoomAction: AsyncData<RoomId>,
|
||||||
val cameraPermissionState: PermissionsState,
|
val cameraPermissionState: PermissionsState,
|
||||||
val eventSink: (ConfigureRoomEvents) -> Unit
|
val eventSink: (ConfigureRoomEvents) -> Unit
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ package io.element.android.features.createroom.impl.configureroom
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.features.createroom.impl.CreateRoomConfig
|
import io.element.android.features.createroom.impl.CreateRoomConfig
|
||||||
import io.element.android.features.createroom.impl.userlist.aListOfSelectedUsers
|
import io.element.android.features.createroom.impl.userlist.aListOfSelectedUsers
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.permissions.api.aPermissionsState
|
import io.element.android.libraries.permissions.api.aPermissionsState
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
||||||
|
|
@ -41,7 +41,7 @@ open class ConfigureRoomStateProvider : PreviewParameterProvider<ConfigureRoomSt
|
||||||
fun aConfigureRoomState() = ConfigureRoomState(
|
fun aConfigureRoomState() = ConfigureRoomState(
|
||||||
config = CreateRoomConfig(),
|
config = CreateRoomConfig(),
|
||||||
avatarActions = persistentListOf(),
|
avatarActions = persistentListOf(),
|
||||||
createRoomAction = Async.Uninitialized,
|
createRoomAction = AsyncData.Uninitialized,
|
||||||
cameraPermissionState = aPermissionsState(showDialog = false),
|
cameraPermissionState = aPermissionsState(showDialog = false),
|
||||||
eventSink = { },
|
eventSink = { },
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import io.element.android.features.createroom.impl.userlist.SelectionMode
|
||||||
import io.element.android.features.createroom.impl.userlist.UserListDataStore
|
import io.element.android.features.createroom.impl.userlist.UserListDataStore
|
||||||
import io.element.android.features.createroom.impl.userlist.UserListPresenter
|
import io.element.android.features.createroom.impl.userlist.UserListPresenter
|
||||||
import io.element.android.features.createroom.impl.userlist.UserListPresenterArgs
|
import io.element.android.features.createroom.impl.userlist.UserListPresenterArgs
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.core.meta.BuildMeta
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
@ -55,14 +55,14 @@ class CreateRoomRootPresenter @Inject constructor(
|
||||||
val userListState = presenter.present()
|
val userListState = presenter.present()
|
||||||
|
|
||||||
val localCoroutineScope = rememberCoroutineScope()
|
val localCoroutineScope = rememberCoroutineScope()
|
||||||
val startDmActionState: MutableState<Async<RoomId>> = remember { mutableStateOf(Async.Uninitialized) }
|
val startDmActionState: MutableState<AsyncData<RoomId>> = remember { mutableStateOf(AsyncData.Uninitialized) }
|
||||||
|
|
||||||
fun handleEvents(event: CreateRoomRootEvents) {
|
fun handleEvents(event: CreateRoomRootEvents) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is CreateRoomRootEvents.StartDM -> localCoroutineScope.launch {
|
is CreateRoomRootEvents.StartDM -> localCoroutineScope.launch {
|
||||||
startDMAction.execute(event.matrixUser.userId, startDmActionState)
|
startDMAction.execute(event.matrixUser.userId, startDmActionState)
|
||||||
}
|
}
|
||||||
CreateRoomRootEvents.CancelStartDM -> startDmActionState.value = Async.Uninitialized
|
CreateRoomRootEvents.CancelStartDM -> startDmActionState.value = AsyncData.Uninitialized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,12 @@
|
||||||
package io.element.android.features.createroom.impl.root
|
package io.element.android.features.createroom.impl.root
|
||||||
|
|
||||||
import io.element.android.features.createroom.impl.userlist.UserListState
|
import io.element.android.features.createroom.impl.userlist.UserListState
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
||||||
data class CreateRoomRootState(
|
data class CreateRoomRootState(
|
||||||
val applicationName: String,
|
val applicationName: String,
|
||||||
val userListState: UserListState,
|
val userListState: UserListState,
|
||||||
val startDmAction: Async<RoomId>,
|
val startDmAction: AsyncData<RoomId>,
|
||||||
val eventSink: (CreateRoomRootEvents) -> Unit,
|
val eventSink: (CreateRoomRootEvents) -> Unit,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ package io.element.android.features.createroom.impl.root
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.features.createroom.impl.userlist.aUserListState
|
import io.element.android.features.createroom.impl.userlist.aUserListState
|
||||||
|
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
||||||
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
||||||
import io.element.android.libraries.usersearch.api.UserSearchResult
|
import io.element.android.libraries.usersearch.api.UserSearchResult
|
||||||
|
|
@ -30,7 +30,7 @@ open class CreateRoomRootStateProvider : PreviewParameterProvider<CreateRoomRoot
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
aCreateRoomRootState(),
|
aCreateRoomRootState(),
|
||||||
aCreateRoomRootState().copy(
|
aCreateRoomRootState().copy(
|
||||||
startDmAction = Async.Loading(),
|
startDmAction = AsyncData.Loading(),
|
||||||
userListState = aMatrixUser().let {
|
userListState = aMatrixUser().let {
|
||||||
aUserListState().copy(
|
aUserListState().copy(
|
||||||
searchQuery = it.userId.value,
|
searchQuery = it.userId.value,
|
||||||
|
|
@ -41,7 +41,7 @@ open class CreateRoomRootStateProvider : PreviewParameterProvider<CreateRoomRoot
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
aCreateRoomRootState().copy(
|
aCreateRoomRootState().copy(
|
||||||
startDmAction = Async.Failure(Throwable("error")),
|
startDmAction = AsyncData.Failure(Throwable("error")),
|
||||||
userListState = aMatrixUser().let {
|
userListState = aMatrixUser().let {
|
||||||
aUserListState().copy(
|
aUserListState().copy(
|
||||||
searchQuery = it.userId.value,
|
searchQuery = it.userId.value,
|
||||||
|
|
@ -57,6 +57,6 @@ open class CreateRoomRootStateProvider : PreviewParameterProvider<CreateRoomRoot
|
||||||
fun aCreateRoomRootState() = CreateRoomRootState(
|
fun aCreateRoomRootState() = CreateRoomRootState(
|
||||||
eventSink = {},
|
eventSink = {},
|
||||||
applicationName = "Element X Preview",
|
applicationName = "Element X Preview",
|
||||||
startDmAction = Async.Uninitialized,
|
startDmAction = AsyncData.Uninitialized,
|
||||||
userListState = aUserListState(),
|
userListState = aUserListState(),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ package io.element.android.features.createroom.impl
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import im.vector.app.features.analytics.plan.CreatedRoom
|
import im.vector.app.features.analytics.plan.CreatedRoom
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||||
|
|
@ -39,9 +39,9 @@ class DefaultStartDMActionTests {
|
||||||
givenFindDmResult(A_ROOM_ID)
|
givenFindDmResult(A_ROOM_ID)
|
||||||
}
|
}
|
||||||
val action = createStartDMAction(matrixClient)
|
val action = createStartDMAction(matrixClient)
|
||||||
val state = mutableStateOf<Async<RoomId>>(Async.Uninitialized)
|
val state = mutableStateOf<AsyncData<RoomId>>(AsyncData.Uninitialized)
|
||||||
action.execute(A_USER_ID, state)
|
action.execute(A_USER_ID, state)
|
||||||
assertThat(state.value).isEqualTo(Async.Success(A_ROOM_ID))
|
assertThat(state.value).isEqualTo(AsyncData.Success(A_ROOM_ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -52,9 +52,9 @@ class DefaultStartDMActionTests {
|
||||||
}
|
}
|
||||||
val analyticsService = FakeAnalyticsService()
|
val analyticsService = FakeAnalyticsService()
|
||||||
val action = createStartDMAction(matrixClient, analyticsService)
|
val action = createStartDMAction(matrixClient, analyticsService)
|
||||||
val state = mutableStateOf<Async<RoomId>>(Async.Uninitialized)
|
val state = mutableStateOf<AsyncData<RoomId>>(AsyncData.Uninitialized)
|
||||||
action.execute(A_USER_ID, state)
|
action.execute(A_USER_ID, state)
|
||||||
assertThat(state.value).isEqualTo(Async.Success(A_ROOM_ID))
|
assertThat(state.value).isEqualTo(AsyncData.Success(A_ROOM_ID))
|
||||||
assertThat(analyticsService.capturedEvents).containsExactly(CreatedRoom(isDM = true))
|
assertThat(analyticsService.capturedEvents).containsExactly(CreatedRoom(isDM = true))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,9 +65,9 @@ class DefaultStartDMActionTests {
|
||||||
givenCreateDmResult(Result.failure(A_THROWABLE))
|
givenCreateDmResult(Result.failure(A_THROWABLE))
|
||||||
}
|
}
|
||||||
val action = createStartDMAction(matrixClient)
|
val action = createStartDMAction(matrixClient)
|
||||||
val state = mutableStateOf<Async<RoomId>>(Async.Uninitialized)
|
val state = mutableStateOf<AsyncData<RoomId>>(AsyncData.Uninitialized)
|
||||||
action.execute(A_USER_ID, state)
|
action.execute(A_USER_ID, state)
|
||||||
assertThat(state.value).isEqualTo(Async.Failure<RoomId>(A_THROWABLE))
|
assertThat(state.value).isEqualTo(AsyncData.Failure<RoomId>(A_THROWABLE))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createStartDMAction(
|
private fun createStartDMAction(
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import im.vector.app.features.analytics.plan.CreatedRoom
|
||||||
import io.element.android.features.createroom.impl.CreateRoomConfig
|
import io.element.android.features.createroom.impl.CreateRoomConfig
|
||||||
import io.element.android.features.createroom.impl.CreateRoomDataStore
|
import io.element.android.features.createroom.impl.CreateRoomDataStore
|
||||||
import io.element.android.features.createroom.impl.userlist.UserListDataStore
|
import io.element.android.features.createroom.impl.userlist.UserListDataStore
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||||
import io.element.android.libraries.matrix.test.A_MESSAGE
|
import io.element.android.libraries.matrix.test.A_MESSAGE
|
||||||
|
|
@ -234,9 +234,9 @@ class ConfigureRoomPresenterTests {
|
||||||
fakeMatrixClient.givenCreateRoomResult(createRoomResult)
|
fakeMatrixClient.givenCreateRoomResult(createRoomResult)
|
||||||
|
|
||||||
initialState.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config))
|
initialState.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config))
|
||||||
assertThat(awaitItem().createRoomAction).isInstanceOf(Async.Loading::class.java)
|
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
val stateAfterCreateRoom = awaitItem()
|
val stateAfterCreateRoom = awaitItem()
|
||||||
assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(Async.Success::class.java)
|
assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncData.Success::class.java)
|
||||||
assertThat(stateAfterCreateRoom.createRoomAction.dataOrNull()).isEqualTo(createRoomResult.getOrNull())
|
assertThat(stateAfterCreateRoom.createRoomAction.dataOrNull()).isEqualTo(createRoomResult.getOrNull())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -272,16 +272,16 @@ class ConfigureRoomPresenterTests {
|
||||||
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config))
|
initialState.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config))
|
||||||
assertThat(awaitItem().createRoomAction).isInstanceOf(Async.Loading::class.java)
|
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
val stateAfterCreateRoom = awaitItem()
|
val stateAfterCreateRoom = awaitItem()
|
||||||
assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(Async.Failure::class.java)
|
assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncData.Failure::class.java)
|
||||||
assertThat(fakeAnalyticsService.capturedEvents.filterIsInstance<CreatedRoom>()).isEmpty()
|
assertThat(fakeAnalyticsService.capturedEvents.filterIsInstance<CreatedRoom>()).isEmpty()
|
||||||
|
|
||||||
fakeMatrixClient.givenUploadMediaResult(Result.success(AN_AVATAR_URL))
|
fakeMatrixClient.givenUploadMediaResult(Result.success(AN_AVATAR_URL))
|
||||||
stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config))
|
stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config))
|
||||||
assertThat(awaitItem().createRoomAction).isInstanceOf(Async.Uninitialized::class.java)
|
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncData.Uninitialized::class.java)
|
||||||
assertThat(awaitItem().createRoomAction).isInstanceOf(Async.Loading::class.java)
|
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
assertThat(awaitItem().createRoomAction).isInstanceOf(Async.Success::class.java)
|
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncData.Success::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -297,22 +297,22 @@ class ConfigureRoomPresenterTests {
|
||||||
|
|
||||||
// Create
|
// Create
|
||||||
initialState.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config))
|
initialState.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config))
|
||||||
assertThat(awaitItem().createRoomAction).isInstanceOf(Async.Loading::class.java)
|
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
val stateAfterCreateRoom = awaitItem()
|
val stateAfterCreateRoom = awaitItem()
|
||||||
assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(Async.Failure::class.java)
|
assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncData.Failure::class.java)
|
||||||
assertThat((stateAfterCreateRoom.createRoomAction as? Async.Failure)?.error).isEqualTo(createRoomResult.exceptionOrNull())
|
assertThat((stateAfterCreateRoom.createRoomAction as? AsyncData.Failure)?.error).isEqualTo(createRoomResult.exceptionOrNull())
|
||||||
|
|
||||||
// Retry
|
// Retry
|
||||||
stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config))
|
stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config))
|
||||||
assertThat(awaitItem().createRoomAction).isInstanceOf(Async.Uninitialized::class.java)
|
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncData.Uninitialized::class.java)
|
||||||
assertThat(awaitItem().createRoomAction).isInstanceOf(Async.Loading::class.java)
|
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
val stateAfterRetry = awaitItem()
|
val stateAfterRetry = awaitItem()
|
||||||
assertThat(stateAfterRetry.createRoomAction).isInstanceOf(Async.Failure::class.java)
|
assertThat(stateAfterRetry.createRoomAction).isInstanceOf(AsyncData.Failure::class.java)
|
||||||
assertThat((stateAfterRetry.createRoomAction as? Async.Failure)?.error).isEqualTo(createRoomResult.exceptionOrNull())
|
assertThat((stateAfterRetry.createRoomAction as? AsyncData.Failure)?.error).isEqualTo(createRoomResult.exceptionOrNull())
|
||||||
|
|
||||||
// Cancel
|
// Cancel
|
||||||
stateAfterRetry.eventSink(ConfigureRoomEvents.CancelCreateRoom)
|
stateAfterRetry.eventSink(ConfigureRoomEvents.CancelCreateRoom)
|
||||||
assertThat(awaitItem().createRoomAction).isInstanceOf(Async.Uninitialized::class.java)
|
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncData.Uninitialized::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import io.element.android.features.createroom.impl.userlist.FakeUserListPresente
|
||||||
import io.element.android.features.createroom.impl.userlist.FakeUserListPresenterFactory
|
import io.element.android.features.createroom.impl.userlist.FakeUserListPresenterFactory
|
||||||
import io.element.android.features.createroom.impl.userlist.UserListDataStore
|
import io.element.android.features.createroom.impl.userlist.UserListDataStore
|
||||||
import io.element.android.features.createroom.test.FakeStartDMAction
|
import io.element.android.features.createroom.test.FakeStartDMAction
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
|
|
@ -52,20 +52,20 @@ class CreateRoomRootPresenterTests {
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
||||||
assertThat(initialState.startDmAction).isInstanceOf(Async.Uninitialized::class.java)
|
assertThat(initialState.startDmAction).isInstanceOf(AsyncData.Uninitialized::class.java)
|
||||||
assertThat(initialState.applicationName).isEqualTo(aBuildMeta().applicationName)
|
assertThat(initialState.applicationName).isEqualTo(aBuildMeta().applicationName)
|
||||||
assertThat(initialState.userListState.selectedUsers).isEmpty()
|
assertThat(initialState.userListState.selectedUsers).isEmpty()
|
||||||
assertThat(initialState.userListState.isSearchActive).isFalse()
|
assertThat(initialState.userListState.isSearchActive).isFalse()
|
||||||
assertThat(initialState.userListState.isMultiSelectionEnabled).isFalse()
|
assertThat(initialState.userListState.isMultiSelectionEnabled).isFalse()
|
||||||
|
|
||||||
val matrixUser = MatrixUser(UserId("@name:domain"))
|
val matrixUser = MatrixUser(UserId("@name:domain"))
|
||||||
val startDMSuccessResult = Async.Success(A_ROOM_ID)
|
val startDMSuccessResult = AsyncData.Success(A_ROOM_ID)
|
||||||
val startDMFailureResult = Async.Failure<RoomId>(A_THROWABLE)
|
val startDMFailureResult = AsyncData.Failure<RoomId>(A_THROWABLE)
|
||||||
|
|
||||||
// Failure
|
// Failure
|
||||||
startDMAction.givenExecuteResult(startDMFailureResult)
|
startDMAction.givenExecuteResult(startDMFailureResult)
|
||||||
initialState.eventSink(CreateRoomRootEvents.StartDM(matrixUser))
|
initialState.eventSink(CreateRoomRootEvents.StartDM(matrixUser))
|
||||||
assertThat(awaitItem().startDmAction).isInstanceOf(Async.Loading::class.java)
|
assertThat(awaitItem().startDmAction).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
awaitItem().also { state ->
|
awaitItem().also { state ->
|
||||||
assertThat(state.startDmAction).isEqualTo(startDMFailureResult)
|
assertThat(state.startDmAction).isEqualTo(startDMFailureResult)
|
||||||
state.eventSink(CreateRoomRootEvents.CancelStartDM)
|
state.eventSink(CreateRoomRootEvents.CancelStartDM)
|
||||||
|
|
@ -74,10 +74,10 @@ class CreateRoomRootPresenterTests {
|
||||||
// Success
|
// Success
|
||||||
startDMAction.givenExecuteResult(startDMSuccessResult)
|
startDMAction.givenExecuteResult(startDMSuccessResult)
|
||||||
awaitItem().also { state ->
|
awaitItem().also { state ->
|
||||||
assertThat(state.startDmAction).isEqualTo(Async.Uninitialized)
|
assertThat(state.startDmAction).isEqualTo(AsyncData.Uninitialized)
|
||||||
state.eventSink(CreateRoomRootEvents.StartDM(matrixUser))
|
state.eventSink(CreateRoomRootEvents.StartDM(matrixUser))
|
||||||
}
|
}
|
||||||
assertThat(awaitItem().startDmAction).isInstanceOf(Async.Loading::class.java)
|
assertThat(awaitItem().startDmAction).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
awaitItem().also { state ->
|
awaitItem().also { state ->
|
||||||
assertThat(state.startDmAction).isEqualTo(startDMSuccessResult)
|
assertThat(state.startDmAction).isEqualTo(startDMSuccessResult)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ package io.element.android.features.createroom.test
|
||||||
|
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import io.element.android.features.createroom.api.StartDMAction
|
import io.element.android.features.createroom.api.StartDMAction
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||||
|
|
@ -26,14 +26,14 @@ import kotlinx.coroutines.delay
|
||||||
|
|
||||||
class FakeStartDMAction : StartDMAction {
|
class FakeStartDMAction : StartDMAction {
|
||||||
|
|
||||||
private var executeResult: Async<RoomId> = Async.Success(A_ROOM_ID)
|
private var executeResult: AsyncData<RoomId> = AsyncData.Success(A_ROOM_ID)
|
||||||
|
|
||||||
fun givenExecuteResult(result: Async<RoomId>) {
|
fun givenExecuteResult(result: AsyncData<RoomId>) {
|
||||||
executeResult = result
|
executeResult = result
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun execute(userId: UserId, actionState: MutableState<Async<RoomId>>) {
|
override suspend fun execute(userId: UserId, actionState: MutableState<AsyncData<RoomId>>) {
|
||||||
actionState.value = Async.Loading()
|
actionState.value = AsyncData.Loading()
|
||||||
delay(1)
|
delay(1)
|
||||||
actionState.value = executeResult
|
actionState.value = executeResult
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ import im.vector.app.features.analytics.plan.JoinedRoom
|
||||||
import io.element.android.features.invitelist.api.SeenInvitesStore
|
import io.element.android.features.invitelist.api.SeenInvitesStore
|
||||||
import io.element.android.features.invitelist.impl.model.InviteListInviteSummary
|
import io.element.android.features.invitelist.impl.model.InviteListInviteSummary
|
||||||
import io.element.android.features.invitelist.impl.model.InviteSender
|
import io.element.android.features.invitelist.impl.model.InviteSender
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
|
|
@ -77,14 +77,14 @@ class InviteListPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
val localCoroutineScope = rememberCoroutineScope()
|
val localCoroutineScope = rememberCoroutineScope()
|
||||||
val acceptedAction: MutableState<Async<RoomId>> = remember { mutableStateOf(Async.Uninitialized) }
|
val acceptedAction: MutableState<AsyncData<RoomId>> = remember { mutableStateOf(AsyncData.Uninitialized) }
|
||||||
val declinedAction: MutableState<Async<Unit>> = remember { mutableStateOf(Async.Uninitialized) }
|
val declinedAction: MutableState<AsyncData<Unit>> = remember { mutableStateOf(AsyncData.Uninitialized) }
|
||||||
val decliningInvite: MutableState<InviteListInviteSummary?> = remember { mutableStateOf(null) }
|
val decliningInvite: MutableState<InviteListInviteSummary?> = remember { mutableStateOf(null) }
|
||||||
|
|
||||||
fun handleEvent(event: InviteListEvents) {
|
fun handleEvent(event: InviteListEvents) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is InviteListEvents.AcceptInvite -> {
|
is InviteListEvents.AcceptInvite -> {
|
||||||
acceptedAction.value = Async.Uninitialized
|
acceptedAction.value = AsyncData.Uninitialized
|
||||||
localCoroutineScope.acceptInvite(event.invite.roomId, acceptedAction)
|
localCoroutineScope.acceptInvite(event.invite.roomId, acceptedAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,7 +93,7 @@ class InviteListPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
is InviteListEvents.ConfirmDeclineInvite -> {
|
is InviteListEvents.ConfirmDeclineInvite -> {
|
||||||
declinedAction.value = Async.Uninitialized
|
declinedAction.value = AsyncData.Uninitialized
|
||||||
decliningInvite.value?.let {
|
decliningInvite.value?.let {
|
||||||
localCoroutineScope.declineInvite(it.roomId, declinedAction)
|
localCoroutineScope.declineInvite(it.roomId, declinedAction)
|
||||||
}
|
}
|
||||||
|
|
@ -105,11 +105,11 @@ class InviteListPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
is InviteListEvents.DismissAcceptError -> {
|
is InviteListEvents.DismissAcceptError -> {
|
||||||
acceptedAction.value = Async.Uninitialized
|
acceptedAction.value = AsyncData.Uninitialized
|
||||||
}
|
}
|
||||||
|
|
||||||
is InviteListEvents.DismissDeclineError -> {
|
is InviteListEvents.DismissDeclineError -> {
|
||||||
declinedAction.value = Async.Uninitialized
|
declinedAction.value = AsyncData.Uninitialized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -137,7 +137,7 @@ class InviteListPresenter @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.acceptInvite(roomId: RoomId, acceptedAction: MutableState<Async<RoomId>>) = launch {
|
private fun CoroutineScope.acceptInvite(roomId: RoomId, acceptedAction: MutableState<AsyncData<RoomId>>) = launch {
|
||||||
suspend {
|
suspend {
|
||||||
client.getRoom(roomId)?.use {
|
client.getRoom(roomId)?.use {
|
||||||
it.join().getOrThrow()
|
it.join().getOrThrow()
|
||||||
|
|
@ -148,7 +148,7 @@ class InviteListPresenter @Inject constructor(
|
||||||
}.runCatchingUpdatingState(acceptedAction)
|
}.runCatchingUpdatingState(acceptedAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.declineInvite(roomId: RoomId, declinedAction: MutableState<Async<Unit>>) = launch {
|
private fun CoroutineScope.declineInvite(roomId: RoomId, declinedAction: MutableState<AsyncData<Unit>>) = launch {
|
||||||
suspend {
|
suspend {
|
||||||
client.getRoom(roomId)?.use {
|
client.getRoom(roomId)?.use {
|
||||||
it.leave().getOrThrow()
|
it.leave().getOrThrow()
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ package io.element.android.features.invitelist.impl
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import io.element.android.features.invitelist.impl.model.InviteListInviteSummary
|
import io.element.android.features.invitelist.impl.model.InviteListInviteSummary
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
|
|
@ -26,8 +26,8 @@ import kotlinx.collections.immutable.ImmutableList
|
||||||
data class InviteListState(
|
data class InviteListState(
|
||||||
val inviteList: ImmutableList<InviteListInviteSummary>,
|
val inviteList: ImmutableList<InviteListInviteSummary>,
|
||||||
val declineConfirmationDialog: InviteDeclineConfirmationDialog,
|
val declineConfirmationDialog: InviteDeclineConfirmationDialog,
|
||||||
val acceptedAction: Async<RoomId>,
|
val acceptedAction: AsyncData<RoomId>,
|
||||||
val declinedAction: Async<Unit>,
|
val declinedAction: AsyncData<Unit>,
|
||||||
val eventSink: (InviteListEvents) -> Unit
|
val eventSink: (InviteListEvents) -> Unit
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ package io.element.android.features.invitelist.impl
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.features.invitelist.impl.model.InviteListInviteSummary
|
import io.element.android.features.invitelist.impl.model.InviteListInviteSummary
|
||||||
import io.element.android.features.invitelist.impl.model.InviteSender
|
import io.element.android.features.invitelist.impl.model.InviteSender
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
@ -32,16 +32,16 @@ open class InviteListStateProvider : PreviewParameterProvider<InviteListState> {
|
||||||
aInviteListState().copy(inviteList = persistentListOf()),
|
aInviteListState().copy(inviteList = persistentListOf()),
|
||||||
aInviteListState().copy(declineConfirmationDialog = InviteDeclineConfirmationDialog.Visible(true, "Alice")),
|
aInviteListState().copy(declineConfirmationDialog = InviteDeclineConfirmationDialog.Visible(true, "Alice")),
|
||||||
aInviteListState().copy(declineConfirmationDialog = InviteDeclineConfirmationDialog.Visible(false, "Some Room")),
|
aInviteListState().copy(declineConfirmationDialog = InviteDeclineConfirmationDialog.Visible(false, "Some Room")),
|
||||||
aInviteListState().copy(acceptedAction = Async.Failure(Throwable("Whoops"))),
|
aInviteListState().copy(acceptedAction = AsyncData.Failure(Throwable("Whoops"))),
|
||||||
aInviteListState().copy(declinedAction = Async.Failure(Throwable("Whoops"))),
|
aInviteListState().copy(declinedAction = AsyncData.Failure(Throwable("Whoops"))),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun aInviteListState() = InviteListState(
|
internal fun aInviteListState() = InviteListState(
|
||||||
inviteList = aInviteListInviteSummaryList(),
|
inviteList = aInviteListInviteSummaryList(),
|
||||||
declineConfirmationDialog = InviteDeclineConfirmationDialog.Hidden,
|
declineConfirmationDialog = InviteDeclineConfirmationDialog.Hidden,
|
||||||
acceptedAction = Async.Uninitialized,
|
acceptedAction = AsyncData.Uninitialized,
|
||||||
declinedAction = Async.Uninitialized,
|
declinedAction = AsyncData.Uninitialized,
|
||||||
eventSink = {},
|
eventSink = {},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.features.invitelist.impl.components.InviteSummaryRow
|
import io.element.android.features.invitelist.impl.components.InviteSummaryRow
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||||
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
|
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
|
||||||
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
||||||
|
|
@ -56,7 +56,7 @@ fun InviteListView(
|
||||||
onInviteAccepted: (RoomId) -> Unit,
|
onInviteAccepted: (RoomId) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
if (state.acceptedAction is Async.Success) {
|
if (state.acceptedAction is AsyncData.Success) {
|
||||||
LaunchedEffect(state.acceptedAction) {
|
LaunchedEffect(state.acceptedAction) {
|
||||||
onInviteAccepted(state.acceptedAction.data)
|
onInviteAccepted(state.acceptedAction.data)
|
||||||
}
|
}
|
||||||
|
|
@ -89,7 +89,7 @@ fun InviteListView(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.acceptedAction is Async.Failure) {
|
if (state.acceptedAction is AsyncData.Failure) {
|
||||||
ErrorDialog(
|
ErrorDialog(
|
||||||
content = stringResource(CommonStrings.error_unknown),
|
content = stringResource(CommonStrings.error_unknown),
|
||||||
title = stringResource(CommonStrings.common_error),
|
title = stringResource(CommonStrings.common_error),
|
||||||
|
|
@ -98,7 +98,7 @@ fun InviteListView(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.declinedAction is Async.Failure) {
|
if (state.declinedAction is AsyncData.Failure) {
|
||||||
ErrorDialog(
|
ErrorDialog(
|
||||||
content = stringResource(CommonStrings.error_unknown),
|
content = stringResource(CommonStrings.error_unknown),
|
||||||
title = stringResource(CommonStrings.common_error),
|
title = stringResource(CommonStrings.common_error),
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.invitelist.api.SeenInvitesStore
|
import io.element.android.features.invitelist.api.SeenInvitesStore
|
||||||
import io.element.android.features.invitelist.test.FakeSeenInvitesStore
|
import io.element.android.features.invitelist.test.FakeSeenInvitesStore
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
|
|
@ -245,7 +245,7 @@ class InviteListPresenterTests {
|
||||||
|
|
||||||
val newState = awaitItem()
|
val newState = awaitItem()
|
||||||
|
|
||||||
assertThat(newState.declinedAction).isEqualTo(Async.Failure<Unit>(ex))
|
assertThat(newState.declinedAction).isEqualTo(AsyncData.Failure<Unit>(ex))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -277,7 +277,7 @@ class InviteListPresenterTests {
|
||||||
|
|
||||||
val newState = awaitItem()
|
val newState = awaitItem()
|
||||||
|
|
||||||
assertThat(newState.declinedAction).isEqualTo(Async.Uninitialized)
|
assertThat(newState.declinedAction).isEqualTo(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -300,7 +300,7 @@ class InviteListPresenterTests {
|
||||||
|
|
||||||
val newState = awaitItem()
|
val newState = awaitItem()
|
||||||
|
|
||||||
assertThat(newState.acceptedAction).isEqualTo(Async.Success(A_ROOM_ID))
|
assertThat(newState.acceptedAction).isEqualTo(AsyncData.Success(A_ROOM_ID))
|
||||||
assertThat(fakeNotificationDrawerManager.getClearMembershipNotificationForRoomCount(client.sessionId, A_ROOM_ID)).isEqualTo(1)
|
assertThat(fakeNotificationDrawerManager.getClearMembershipNotificationForRoomCount(client.sessionId, A_ROOM_ID)).isEqualTo(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -323,7 +323,7 @@ class InviteListPresenterTests {
|
||||||
val originalState = awaitItem()
|
val originalState = awaitItem()
|
||||||
originalState.eventSink(InviteListEvents.AcceptInvite(originalState.inviteList[0]))
|
originalState.eventSink(InviteListEvents.AcceptInvite(originalState.inviteList[0]))
|
||||||
|
|
||||||
assertThat(awaitItem().acceptedAction).isEqualTo(Async.Failure<RoomId>(ex))
|
assertThat(awaitItem().acceptedAction).isEqualTo(AsyncData.Failure<RoomId>(ex))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -350,7 +350,7 @@ class InviteListPresenterTests {
|
||||||
originalState.eventSink(InviteListEvents.DismissAcceptError)
|
originalState.eventSink(InviteListEvents.DismissAcceptError)
|
||||||
|
|
||||||
val newState = awaitItem()
|
val newState = awaitItem()
|
||||||
assertThat(newState.acceptedAction).isEqualTo(Async.Uninitialized)
|
assertThat(newState.acceptedAction).isEqualTo(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ import io.element.android.features.lockscreen.impl.biometric.BiometricUnlockMana
|
||||||
import io.element.android.features.lockscreen.impl.pin.PinCodeManager
|
import io.element.android.features.lockscreen.impl.pin.PinCodeManager
|
||||||
import io.element.android.features.lockscreen.impl.pin.model.PinEntry
|
import io.element.android.features.lockscreen.impl.pin.model.PinEntry
|
||||||
import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypadModel
|
import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypadModel
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||||
import io.element.android.libraries.core.bool.orFalse
|
import io.element.android.libraries.core.bool.orFalse
|
||||||
|
|
@ -49,11 +49,11 @@ class PinUnlockPresenter @Inject constructor(
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): PinUnlockState {
|
override fun present(): PinUnlockState {
|
||||||
val pinEntryState = remember {
|
val pinEntryState = remember {
|
||||||
mutableStateOf<Async<PinEntry>>(Async.Uninitialized)
|
mutableStateOf<AsyncData<PinEntry>>(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
val pinEntry by pinEntryState
|
val pinEntry by pinEntryState
|
||||||
var remainingAttempts by remember {
|
var remainingAttempts by remember {
|
||||||
mutableStateOf<Async<Int>>(Async.Uninitialized)
|
mutableStateOf<AsyncData<Int>>(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
var showWrongPinTitle by rememberSaveable {
|
var showWrongPinTitle by rememberSaveable {
|
||||||
mutableStateOf(false)
|
mutableStateOf(false)
|
||||||
|
|
@ -62,7 +62,7 @@ class PinUnlockPresenter @Inject constructor(
|
||||||
mutableStateOf(false)
|
mutableStateOf(false)
|
||||||
}
|
}
|
||||||
val signOutAction = remember {
|
val signOutAction = remember {
|
||||||
mutableStateOf<Async<String?>>(Async.Uninitialized)
|
mutableStateOf<AsyncData<String?>>(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
var biometricUnlockResult by remember {
|
var biometricUnlockResult by remember {
|
||||||
mutableStateOf<BiometricUnlock.AuthenticationResult?>(null)
|
mutableStateOf<BiometricUnlock.AuthenticationResult?>(null)
|
||||||
|
|
@ -91,7 +91,7 @@ class PinUnlockPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val remainingAttemptsNumber = pinCodeManager.getRemainingPinCodeAttemptsNumber()
|
val remainingAttemptsNumber = pinCodeManager.getRemainingPinCodeAttemptsNumber()
|
||||||
remainingAttempts = Async.Success(remainingAttemptsNumber)
|
remainingAttempts = AsyncData.Success(remainingAttemptsNumber)
|
||||||
if (remainingAttemptsNumber == 0) {
|
if (remainingAttemptsNumber == 0) {
|
||||||
showSignOutPrompt = true
|
showSignOutPrompt = true
|
||||||
}
|
}
|
||||||
|
|
@ -139,46 +139,46 @@ class PinUnlockPresenter @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Async<PinEntry>.isComplete(): Boolean {
|
private fun AsyncData<PinEntry>.isComplete(): Boolean {
|
||||||
return dataOrNull()?.isComplete().orFalse()
|
return dataOrNull()?.isComplete().orFalse()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Async<PinEntry>.toText(): String {
|
private fun AsyncData<PinEntry>.toText(): String {
|
||||||
return dataOrNull()?.toText() ?: ""
|
return dataOrNull()?.toText() ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Async<PinEntry>.clear(): Async<PinEntry> {
|
private fun AsyncData<PinEntry>.clear(): AsyncData<PinEntry> {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
is Async.Success -> Async.Success(data.clear())
|
is AsyncData.Success -> AsyncData.Success(data.clear())
|
||||||
else -> this
|
else -> this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Async<PinEntry>.process(pinKeypadModel: PinKeypadModel): Async<PinEntry> {
|
private fun AsyncData<PinEntry>.process(pinKeypadModel: PinKeypadModel): AsyncData<PinEntry> {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
is Async.Success -> {
|
is AsyncData.Success -> {
|
||||||
val pinEntry = when (pinKeypadModel) {
|
val pinEntry = when (pinKeypadModel) {
|
||||||
PinKeypadModel.Back -> data.deleteLast()
|
PinKeypadModel.Back -> data.deleteLast()
|
||||||
is PinKeypadModel.Number -> data.addDigit(pinKeypadModel.number)
|
is PinKeypadModel.Number -> data.addDigit(pinKeypadModel.number)
|
||||||
PinKeypadModel.Empty -> data
|
PinKeypadModel.Empty -> data
|
||||||
}
|
}
|
||||||
Async.Success(pinEntry)
|
AsyncData.Success(pinEntry)
|
||||||
}
|
}
|
||||||
else -> this
|
else -> this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Async<PinEntry>.process(pinEntryAsText: String): Async<PinEntry> {
|
private fun AsyncData<PinEntry>.process(pinEntryAsText: String): AsyncData<PinEntry> {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
is Async.Success -> {
|
is AsyncData.Success -> {
|
||||||
val pinEntry = data.fillWith(pinEntryAsText)
|
val pinEntry = data.fillWith(pinEntryAsText)
|
||||||
Async.Success(pinEntry)
|
AsyncData.Success(pinEntry)
|
||||||
}
|
}
|
||||||
else -> this
|
else -> this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.signOut(signOutAction: MutableState<Async<String?>>) = launch {
|
private fun CoroutineScope.signOut(signOutAction: MutableState<AsyncData<String?>>) = launch {
|
||||||
suspend {
|
suspend {
|
||||||
matrixClient.logout(ignoreSdkError = true)
|
matrixClient.logout(ignoreSdkError = true)
|
||||||
}.runCatchingUpdatingState(signOutAction)
|
}.runCatchingUpdatingState(signOutAction)
|
||||||
|
|
|
||||||
|
|
@ -19,21 +19,21 @@ package io.element.android.features.lockscreen.impl.unlock
|
||||||
import io.element.android.features.lockscreen.impl.biometric.BiometricUnlock
|
import io.element.android.features.lockscreen.impl.biometric.BiometricUnlock
|
||||||
import io.element.android.features.lockscreen.impl.biometric.BiometricUnlockError
|
import io.element.android.features.lockscreen.impl.biometric.BiometricUnlockError
|
||||||
import io.element.android.features.lockscreen.impl.pin.model.PinEntry
|
import io.element.android.features.lockscreen.impl.pin.model.PinEntry
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
|
|
||||||
data class PinUnlockState(
|
data class PinUnlockState(
|
||||||
val pinEntry: Async<PinEntry>,
|
val pinEntry: AsyncData<PinEntry>,
|
||||||
val showWrongPinTitle: Boolean,
|
val showWrongPinTitle: Boolean,
|
||||||
val remainingAttempts: Async<Int>,
|
val remainingAttempts: AsyncData<Int>,
|
||||||
val showSignOutPrompt: Boolean,
|
val showSignOutPrompt: Boolean,
|
||||||
val signOutAction: Async<String?>,
|
val signOutAction: AsyncData<String?>,
|
||||||
val showBiometricUnlock: Boolean,
|
val showBiometricUnlock: Boolean,
|
||||||
val isUnlocked: Boolean,
|
val isUnlocked: Boolean,
|
||||||
val biometricUnlockResult: BiometricUnlock.AuthenticationResult?,
|
val biometricUnlockResult: BiometricUnlock.AuthenticationResult?,
|
||||||
val eventSink: (PinUnlockEvents) -> Unit
|
val eventSink: (PinUnlockEvents) -> Unit
|
||||||
) {
|
) {
|
||||||
val isSignOutPromptCancellable = when (remainingAttempts) {
|
val isSignOutPromptCancellable = when (remainingAttempts) {
|
||||||
is Async.Success -> remainingAttempts.data > 0
|
is AsyncData.Success -> remainingAttempts.data > 0
|
||||||
else -> true
|
else -> true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ package io.element.android.features.lockscreen.impl.unlock
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.features.lockscreen.impl.biometric.BiometricUnlock
|
import io.element.android.features.lockscreen.impl.biometric.BiometricUnlock
|
||||||
import io.element.android.features.lockscreen.impl.pin.model.PinEntry
|
import io.element.android.features.lockscreen.impl.pin.model.PinEntry
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
|
|
||||||
open class PinUnlockStateProvider : PreviewParameterProvider<PinUnlockState> {
|
open class PinUnlockStateProvider : PreviewParameterProvider<PinUnlockState> {
|
||||||
override val values: Sequence<PinUnlockState>
|
override val values: Sequence<PinUnlockState>
|
||||||
|
|
@ -30,7 +30,7 @@ open class PinUnlockStateProvider : PreviewParameterProvider<PinUnlockState> {
|
||||||
aPinUnlockState(showSignOutPrompt = true),
|
aPinUnlockState(showSignOutPrompt = true),
|
||||||
aPinUnlockState(showBiometricUnlock = false),
|
aPinUnlockState(showBiometricUnlock = false),
|
||||||
aPinUnlockState(showSignOutPrompt = true, remainingAttempts = 0),
|
aPinUnlockState(showSignOutPrompt = true, remainingAttempts = 0),
|
||||||
aPinUnlockState(signOutAction = Async.Loading()),
|
aPinUnlockState(signOutAction = AsyncData.Loading()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,11 +42,11 @@ fun aPinUnlockState(
|
||||||
showBiometricUnlock: Boolean = true,
|
showBiometricUnlock: Boolean = true,
|
||||||
biometricUnlockResult: BiometricUnlock.AuthenticationResult? = null,
|
biometricUnlockResult: BiometricUnlock.AuthenticationResult? = null,
|
||||||
isUnlocked: Boolean = false,
|
isUnlocked: Boolean = false,
|
||||||
signOutAction: Async<String?> = Async.Uninitialized,
|
signOutAction: AsyncData<String?> = AsyncData.Uninitialized,
|
||||||
) = PinUnlockState(
|
) = PinUnlockState(
|
||||||
pinEntry = Async.Success(pinEntry),
|
pinEntry = AsyncData.Success(pinEntry),
|
||||||
showWrongPinTitle = showWrongPinTitle,
|
showWrongPinTitle = showWrongPinTitle,
|
||||||
remainingAttempts = Async.Success(remainingAttempts),
|
remainingAttempts = AsyncData.Success(remainingAttempts),
|
||||||
showSignOutPrompt = showSignOutPrompt,
|
showSignOutPrompt = showSignOutPrompt,
|
||||||
showBiometricUnlock = showBiometricUnlock,
|
showBiometricUnlock = showBiometricUnlock,
|
||||||
signOutAction = signOutAction,
|
signOutAction = signOutAction,
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ import io.element.android.features.lockscreen.impl.components.PinEntryTextField
|
||||||
import io.element.android.features.lockscreen.impl.pin.model.PinDigit
|
import io.element.android.features.lockscreen.impl.pin.model.PinDigit
|
||||||
import io.element.android.features.lockscreen.impl.pin.model.PinEntry
|
import io.element.android.features.lockscreen.impl.pin.model.PinEntry
|
||||||
import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypad
|
import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypad
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtom
|
import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtom
|
||||||
import io.element.android.libraries.designsystem.components.ProgressDialog
|
import io.element.android.libraries.designsystem.components.ProgressDialog
|
||||||
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
|
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
|
||||||
|
|
@ -92,7 +92,7 @@ fun PinUnlockView(
|
||||||
onDismiss = { state.eventSink(PinUnlockEvents.ClearSignOutPrompt) },
|
onDismiss = { state.eventSink(PinUnlockEvents.ClearSignOutPrompt) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (state.signOutAction is Async.Loading) {
|
if (state.signOutAction is AsyncData.Loading) {
|
||||||
ProgressDialog(text = stringResource(id = R.string.screen_signout_in_progress_dialog_content))
|
ProgressDialog(text = stringResource(id = R.string.screen_signout_in_progress_dialog_content))
|
||||||
}
|
}
|
||||||
if (state.showBiometricUnlockError) {
|
if (state.showBiometricUnlockError) {
|
||||||
|
|
@ -335,7 +335,7 @@ private fun PinUnlockHeader(
|
||||||
style = ElementTheme.typography.fontBodyMdRegular,
|
style = ElementTheme.typography.fontBodyMdRegular,
|
||||||
color = subtitleColor,
|
color = subtitleColor,
|
||||||
)
|
)
|
||||||
if (!isInAppUnlock && state.pinEntry is Async.Success) {
|
if (!isInAppUnlock && state.pinEntry is AsyncData.Success) {
|
||||||
Spacer(Modifier.height(24.dp))
|
Spacer(Modifier.height(24.dp))
|
||||||
PinDotsRow(state.pinEntry.data)
|
PinDotsRow(state.pinEntry.data)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ import io.element.android.features.lockscreen.impl.pin.PinCodeManager
|
||||||
import io.element.android.features.lockscreen.impl.pin.model.PinEntry
|
import io.element.android.features.lockscreen.impl.pin.model.PinEntry
|
||||||
import io.element.android.features.lockscreen.impl.pin.model.assertText
|
import io.element.android.features.lockscreen.impl.pin.model.assertText
|
||||||
import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypadModel
|
import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypadModel
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||||
import io.element.android.tests.testutils.awaitLastSequentialItem
|
import io.element.android.tests.testutils.awaitLastSequentialItem
|
||||||
import io.element.android.tests.testutils.consumeItemsUntilPredicate
|
import io.element.android.tests.testutils.consumeItemsUntilPredicate
|
||||||
|
|
@ -48,15 +48,15 @@ class PinUnlockPresenterTest {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
awaitItem().also { state ->
|
awaitItem().also { state ->
|
||||||
assertThat(state.pinEntry).isInstanceOf(Async.Uninitialized::class.java)
|
assertThat(state.pinEntry).isInstanceOf(AsyncData.Uninitialized::class.java)
|
||||||
assertThat(state.showWrongPinTitle).isFalse()
|
assertThat(state.showWrongPinTitle).isFalse()
|
||||||
assertThat(state.showSignOutPrompt).isFalse()
|
assertThat(state.showSignOutPrompt).isFalse()
|
||||||
assertThat(state.isUnlocked).isFalse()
|
assertThat(state.isUnlocked).isFalse()
|
||||||
assertThat(state.signOutAction).isInstanceOf(Async.Uninitialized::class.java)
|
assertThat(state.signOutAction).isInstanceOf(AsyncData.Uninitialized::class.java)
|
||||||
assertThat(state.remainingAttempts).isInstanceOf(Async.Uninitialized::class.java)
|
assertThat(state.remainingAttempts).isInstanceOf(AsyncData.Uninitialized::class.java)
|
||||||
}
|
}
|
||||||
consumeItemsUntilPredicate {
|
consumeItemsUntilPredicate {
|
||||||
it.pinEntry is Async.Success && it.remainingAttempts is Async.Success
|
it.pinEntry is AsyncData.Success && it.remainingAttempts is AsyncData.Success
|
||||||
}.last().also { state ->
|
}.last().also { state ->
|
||||||
state.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Number('1')))
|
state.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Number('1')))
|
||||||
state.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Number('2')))
|
state.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Number('2')))
|
||||||
|
|
@ -83,7 +83,7 @@ class PinUnlockPresenterTest {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = consumeItemsUntilPredicate {
|
val initialState = consumeItemsUntilPredicate {
|
||||||
it.pinEntry is Async.Success && it.remainingAttempts is Async.Success
|
it.pinEntry is AsyncData.Success && it.remainingAttempts is AsyncData.Success
|
||||||
}.last()
|
}.last()
|
||||||
val numberOfAttempts = initialState.remainingAttempts.dataOrNull() ?: 0
|
val numberOfAttempts = initialState.remainingAttempts.dataOrNull() ?: 0
|
||||||
repeat(numberOfAttempts) {
|
repeat(numberOfAttempts) {
|
||||||
|
|
@ -107,7 +107,7 @@ class PinUnlockPresenterTest {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
consumeItemsUntilPredicate {
|
consumeItemsUntilPredicate {
|
||||||
it.pinEntry is Async.Success && it.remainingAttempts is Async.Success
|
it.pinEntry is AsyncData.Success && it.remainingAttempts is AsyncData.Success
|
||||||
}.last().also { state ->
|
}.last().also { state ->
|
||||||
state.eventSink(PinUnlockEvents.OnForgetPin)
|
state.eventSink(PinUnlockEvents.OnForgetPin)
|
||||||
}
|
}
|
||||||
|
|
@ -125,12 +125,12 @@ class PinUnlockPresenterTest {
|
||||||
state.eventSink(PinUnlockEvents.SignOut)
|
state.eventSink(PinUnlockEvents.SignOut)
|
||||||
}
|
}
|
||||||
consumeItemsUntilPredicate { state ->
|
consumeItemsUntilPredicate { state ->
|
||||||
state.signOutAction is Async.Success
|
state.signOutAction is AsyncData.Success
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Async<PinEntry>.assertText(text: String) {
|
private fun AsyncData<PinEntry>.assertText(text: String) {
|
||||||
dataOrNull()?.assertText(text)
|
dataOrNull()?.assertText(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import io.element.android.features.login.impl.accountprovider.AccountProvider
|
import io.element.android.features.login.impl.accountprovider.AccountProvider
|
||||||
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
||||||
import io.element.android.features.login.impl.error.ChangeServerError
|
import io.element.android.features.login.impl.error.ChangeServerError
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||||
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
||||||
|
|
@ -41,14 +41,14 @@ class ChangeServerPresenter @Inject constructor(
|
||||||
override fun present(): ChangeServerState {
|
override fun present(): ChangeServerState {
|
||||||
val localCoroutineScope = rememberCoroutineScope()
|
val localCoroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
val changeServerAction: MutableState<Async<Unit>> = remember {
|
val changeServerAction: MutableState<AsyncData<Unit>> = remember {
|
||||||
mutableStateOf(Async.Uninitialized)
|
mutableStateOf(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleEvents(event: ChangeServerEvents) {
|
fun handleEvents(event: ChangeServerEvents) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is ChangeServerEvents.ChangeServer -> localCoroutineScope.changeServer(event.accountProvider, changeServerAction)
|
is ChangeServerEvents.ChangeServer -> localCoroutineScope.changeServer(event.accountProvider, changeServerAction)
|
||||||
ChangeServerEvents.ClearError -> changeServerAction.value = Async.Uninitialized
|
ChangeServerEvents.ClearError -> changeServerAction.value = AsyncData.Uninitialized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,7 +60,7 @@ class ChangeServerPresenter @Inject constructor(
|
||||||
|
|
||||||
private fun CoroutineScope.changeServer(
|
private fun CoroutineScope.changeServer(
|
||||||
data: AccountProvider,
|
data: AccountProvider,
|
||||||
changeServerAction: MutableState<Async<Unit>>,
|
changeServerAction: MutableState<AsyncData<Unit>>,
|
||||||
) = launch {
|
) = launch {
|
||||||
suspend {
|
suspend {
|
||||||
authenticationService.setHomeserver(data.url).map {
|
authenticationService.setHomeserver(data.url).map {
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,9 @@
|
||||||
|
|
||||||
package io.element.android.features.login.impl.changeserver
|
package io.element.android.features.login.impl.changeserver
|
||||||
|
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
|
|
||||||
data class ChangeServerState(
|
data class ChangeServerState(
|
||||||
val changeServerAction: Async<Unit>,
|
val changeServerAction: AsyncData<Unit>,
|
||||||
val eventSink: (ChangeServerEvents) -> Unit
|
val eventSink: (ChangeServerEvents) -> Unit
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
package io.element.android.features.login.impl.changeserver
|
package io.element.android.features.login.impl.changeserver
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
|
|
||||||
open class ChangeServerStateProvider : PreviewParameterProvider<ChangeServerState> {
|
open class ChangeServerStateProvider : PreviewParameterProvider<ChangeServerState> {
|
||||||
override val values: Sequence<ChangeServerState>
|
override val values: Sequence<ChangeServerState>
|
||||||
|
|
@ -27,6 +27,6 @@ open class ChangeServerStateProvider : PreviewParameterProvider<ChangeServerStat
|
||||||
}
|
}
|
||||||
|
|
||||||
fun aChangeServerState() = ChangeServerState(
|
fun aChangeServerState() = ChangeServerState(
|
||||||
changeServerAction = Async.Uninitialized,
|
changeServerAction = AsyncData.Uninitialized,
|
||||||
eventSink = {}
|
eventSink = {}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import io.element.android.features.login.impl.dialogs.SlidingSyncNotSupportedDialog
|
import io.element.android.features.login.impl.dialogs.SlidingSyncNotSupportedDialog
|
||||||
import io.element.android.features.login.impl.error.ChangeServerError
|
import io.element.android.features.login.impl.error.ChangeServerError
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.designsystem.components.ProgressDialog
|
import io.element.android.libraries.designsystem.components.ProgressDialog
|
||||||
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
|
|
@ -37,7 +37,7 @@ fun ChangeServerView(
|
||||||
) {
|
) {
|
||||||
val eventSink = state.eventSink
|
val eventSink = state.eventSink
|
||||||
when (state.changeServerAction) {
|
when (state.changeServerAction) {
|
||||||
is Async.Failure -> {
|
is AsyncData.Failure -> {
|
||||||
when (val error = state.changeServerAction.error) {
|
when (val error = state.changeServerAction.error) {
|
||||||
is ChangeServerError.Error -> {
|
is ChangeServerError.Error -> {
|
||||||
ErrorDialog(
|
ErrorDialog(
|
||||||
|
|
@ -60,11 +60,11 @@ fun ChangeServerView(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Async.Loading -> ProgressDialog()
|
is AsyncData.Loading -> ProgressDialog()
|
||||||
is Async.Success -> LaunchedEffect(state.changeServerAction) {
|
is AsyncData.Success -> LaunchedEffect(state.changeServerAction) {
|
||||||
onDone()
|
onDone()
|
||||||
}
|
}
|
||||||
Async.Uninitialized -> Unit
|
AsyncData.Uninitialized -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.features.login.api.oidc.OidcAction
|
import io.element.android.features.login.api.oidc.OidcAction
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
||||||
import io.element.android.libraries.matrix.api.auth.OidcDetails
|
import io.element.android.libraries.matrix.api.auth.OidcDetails
|
||||||
|
|
@ -44,33 +44,33 @@ class OidcPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): OidcState {
|
override fun present(): OidcState {
|
||||||
var requestState: Async<Unit> by remember {
|
var requestState: AsyncData<Unit> by remember {
|
||||||
mutableStateOf(Async.Uninitialized)
|
mutableStateOf(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
val localCoroutineScope = rememberCoroutineScope()
|
val localCoroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
fun handleCancel() {
|
fun handleCancel() {
|
||||||
requestState = Async.Loading()
|
requestState = AsyncData.Loading()
|
||||||
localCoroutineScope.launch {
|
localCoroutineScope.launch {
|
||||||
authenticationService.cancelOidcLogin()
|
authenticationService.cancelOidcLogin()
|
||||||
.fold(
|
.fold(
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
// Then go back
|
// Then go back
|
||||||
requestState = Async.Success(Unit)
|
requestState = AsyncData.Success(Unit)
|
||||||
},
|
},
|
||||||
onFailure = {
|
onFailure = {
|
||||||
requestState = Async.Failure(it)
|
requestState = AsyncData.Failure(it)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleSuccess(url: String) {
|
fun handleSuccess(url: String) {
|
||||||
requestState = Async.Loading()
|
requestState = AsyncData.Loading()
|
||||||
localCoroutineScope.launch {
|
localCoroutineScope.launch {
|
||||||
authenticationService.loginWithOidc(url)
|
authenticationService.loginWithOidc(url)
|
||||||
.onFailure {
|
.onFailure {
|
||||||
requestState = Async.Failure(it)
|
requestState = AsyncData.Failure(it)
|
||||||
}
|
}
|
||||||
// On success, the node tree will be updated, there is nothing to do
|
// On success, the node tree will be updated, there is nothing to do
|
||||||
}
|
}
|
||||||
|
|
@ -87,7 +87,7 @@ class OidcPresenter @AssistedInject constructor(
|
||||||
when (event) {
|
when (event) {
|
||||||
OidcEvents.Cancel -> handleCancel()
|
OidcEvents.Cancel -> handleCancel()
|
||||||
is OidcEvents.OidcActionEvent -> handleAction(event.oidcAction)
|
is OidcEvents.OidcActionEvent -> handleAction(event.oidcAction)
|
||||||
OidcEvents.ClearError -> requestState = Async.Uninitialized
|
OidcEvents.ClearError -> requestState = AsyncData.Uninitialized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,11 @@
|
||||||
|
|
||||||
package io.element.android.features.login.impl.oidc.webview
|
package io.element.android.features.login.impl.oidc.webview
|
||||||
|
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.auth.OidcDetails
|
import io.element.android.libraries.matrix.api.auth.OidcDetails
|
||||||
|
|
||||||
data class OidcState(
|
data class OidcState(
|
||||||
val oidcDetails: OidcDetails,
|
val oidcDetails: OidcDetails,
|
||||||
val requestState: Async<Unit>,
|
val requestState: AsyncData<Unit>,
|
||||||
val eventSink: (OidcEvents) -> Unit
|
val eventSink: (OidcEvents) -> Unit
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -17,20 +17,20 @@
|
||||||
package io.element.android.features.login.impl.oidc.webview
|
package io.element.android.features.login.impl.oidc.webview
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.auth.OidcDetails
|
import io.element.android.libraries.matrix.api.auth.OidcDetails
|
||||||
|
|
||||||
open class OidcStateProvider : PreviewParameterProvider<OidcState> {
|
open class OidcStateProvider : PreviewParameterProvider<OidcState> {
|
||||||
override val values: Sequence<OidcState>
|
override val values: Sequence<OidcState>
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
aOidcState(),
|
aOidcState(),
|
||||||
aOidcState().copy(requestState = Async.Loading()),
|
aOidcState().copy(requestState = AsyncData.Loading()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun aOidcState() = OidcState(
|
fun aOidcState() = OidcState(
|
||||||
oidcDetails = aOidcDetails(),
|
oidcDetails = aOidcDetails(),
|
||||||
requestState = Async.Uninitialized,
|
requestState = AsyncData.Uninitialized,
|
||||||
eventSink = {}
|
eventSink = {}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ import io.element.android.features.login.impl.DefaultLoginUserStory
|
||||||
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
||||||
import io.element.android.features.login.impl.error.ChangeServerError
|
import io.element.android.features.login.impl.error.ChangeServerError
|
||||||
import io.element.android.features.login.impl.oidc.customtab.DefaultOidcActionFlow
|
import io.element.android.features.login.impl.oidc.customtab.DefaultOidcActionFlow
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||||
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
||||||
|
|
@ -61,8 +61,8 @@ class ConfirmAccountProviderPresenter @AssistedInject constructor(
|
||||||
val accountProvider by accountProviderDataSource.flow().collectAsState()
|
val accountProvider by accountProviderDataSource.flow().collectAsState()
|
||||||
val localCoroutineScope = rememberCoroutineScope()
|
val localCoroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
val loginFlowAction: MutableState<Async<LoginFlow>> = remember {
|
val loginFlowAction: MutableState<AsyncData<LoginFlow>> = remember {
|
||||||
mutableStateOf(Async.Uninitialized)
|
mutableStateOf(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
|
|
@ -78,7 +78,7 @@ class ConfirmAccountProviderPresenter @AssistedInject constructor(
|
||||||
ConfirmAccountProviderEvents.Continue -> {
|
ConfirmAccountProviderEvents.Continue -> {
|
||||||
localCoroutineScope.submit(accountProvider.url, loginFlowAction)
|
localCoroutineScope.submit(accountProvider.url, loginFlowAction)
|
||||||
}
|
}
|
||||||
ConfirmAccountProviderEvents.ClearError -> loginFlowAction.value = Async.Uninitialized
|
ConfirmAccountProviderEvents.ClearError -> loginFlowAction.value = AsyncData.Uninitialized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,7 +92,7 @@ class ConfirmAccountProviderPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
private fun CoroutineScope.submit(
|
private fun CoroutineScope.submit(
|
||||||
homeserverUrl: String,
|
homeserverUrl: String,
|
||||||
loginFlowAction: MutableState<Async<LoginFlow>>,
|
loginFlowAction: MutableState<AsyncData<LoginFlow>>,
|
||||||
) = launch {
|
) = launch {
|
||||||
suspend {
|
suspend {
|
||||||
authenticationService.setHomeserver(homeserverUrl).map {
|
authenticationService.setHomeserver(homeserverUrl).map {
|
||||||
|
|
@ -111,17 +111,17 @@ class ConfirmAccountProviderPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
private suspend fun onOidcAction(
|
private suspend fun onOidcAction(
|
||||||
oidcAction: OidcAction,
|
oidcAction: OidcAction,
|
||||||
loginFlowAction: MutableState<Async<LoginFlow>>,
|
loginFlowAction: MutableState<AsyncData<LoginFlow>>,
|
||||||
) {
|
) {
|
||||||
loginFlowAction.value = Async.Loading()
|
loginFlowAction.value = AsyncData.Loading()
|
||||||
when (oidcAction) {
|
when (oidcAction) {
|
||||||
OidcAction.GoBack -> {
|
OidcAction.GoBack -> {
|
||||||
authenticationService.cancelOidcLogin()
|
authenticationService.cancelOidcLogin()
|
||||||
.onSuccess {
|
.onSuccess {
|
||||||
loginFlowAction.value = Async.Uninitialized
|
loginFlowAction.value = AsyncData.Uninitialized
|
||||||
}
|
}
|
||||||
.onFailure { failure ->
|
.onFailure { failure ->
|
||||||
loginFlowAction.value = Async.Failure(failure)
|
loginFlowAction.value = AsyncData.Failure(failure)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is OidcAction.Success -> {
|
is OidcAction.Success -> {
|
||||||
|
|
@ -130,7 +130,7 @@ class ConfirmAccountProviderPresenter @AssistedInject constructor(
|
||||||
defaultLoginUserStory.setLoginFlowIsDone(true)
|
defaultLoginUserStory.setLoginFlowIsDone(true)
|
||||||
}
|
}
|
||||||
.onFailure { failure ->
|
.onFailure { failure ->
|
||||||
loginFlowAction.value = Async.Failure(failure)
|
loginFlowAction.value = AsyncData.Failure(failure)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,17 +17,17 @@
|
||||||
package io.element.android.features.login.impl.screens.confirmaccountprovider
|
package io.element.android.features.login.impl.screens.confirmaccountprovider
|
||||||
|
|
||||||
import io.element.android.features.login.impl.accountprovider.AccountProvider
|
import io.element.android.features.login.impl.accountprovider.AccountProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.auth.OidcDetails
|
import io.element.android.libraries.matrix.api.auth.OidcDetails
|
||||||
|
|
||||||
// Do not use default value, so no member get forgotten in the presenters.
|
// Do not use default value, so no member get forgotten in the presenters.
|
||||||
data class ConfirmAccountProviderState(
|
data class ConfirmAccountProviderState(
|
||||||
val accountProvider: AccountProvider,
|
val accountProvider: AccountProvider,
|
||||||
val isAccountCreation: Boolean,
|
val isAccountCreation: Boolean,
|
||||||
val loginFlow: Async<LoginFlow>,
|
val loginFlow: AsyncData<LoginFlow>,
|
||||||
val eventSink: (ConfirmAccountProviderEvents) -> Unit
|
val eventSink: (ConfirmAccountProviderEvents) -> Unit
|
||||||
) {
|
) {
|
||||||
val submitEnabled: Boolean get() = accountProvider.url.isNotEmpty() && (loginFlow is Async.Uninitialized || loginFlow is Async.Loading)
|
val submitEnabled: Boolean get() = accountProvider.url.isNotEmpty() && (loginFlow is AsyncData.Uninitialized || loginFlow is AsyncData.Loading)
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed interface LoginFlow {
|
sealed interface LoginFlow {
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ package io.element.android.features.login.impl.screens.confirmaccountprovider
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.features.login.impl.accountprovider.anAccountProvider
|
import io.element.android.features.login.impl.accountprovider.anAccountProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
|
|
||||||
open class ConfirmAccountProviderStateProvider : PreviewParameterProvider<ConfirmAccountProviderState> {
|
open class ConfirmAccountProviderStateProvider : PreviewParameterProvider<ConfirmAccountProviderState> {
|
||||||
override val values: Sequence<ConfirmAccountProviderState>
|
override val values: Sequence<ConfirmAccountProviderState>
|
||||||
|
|
@ -31,6 +31,6 @@ open class ConfirmAccountProviderStateProvider : PreviewParameterProvider<Confir
|
||||||
fun aConfirmAccountProviderState() = ConfirmAccountProviderState(
|
fun aConfirmAccountProviderState() = ConfirmAccountProviderState(
|
||||||
accountProvider = anAccountProvider(),
|
accountProvider = anAccountProvider(),
|
||||||
isAccountCreation = false,
|
isAccountCreation = false,
|
||||||
loginFlow = Async.Uninitialized,
|
loginFlow = AsyncData.Uninitialized,
|
||||||
eventSink = {}
|
eventSink = {}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.features.login.impl.R
|
import io.element.android.features.login.impl.R
|
||||||
import io.element.android.features.login.impl.dialogs.SlidingSyncNotSupportedDialog
|
import io.element.android.features.login.impl.dialogs.SlidingSyncNotSupportedDialog
|
||||||
import io.element.android.features.login.impl.error.ChangeServerError
|
import io.element.android.features.login.impl.error.ChangeServerError
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
|
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
|
||||||
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
|
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
|
||||||
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
|
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
|
||||||
|
|
@ -56,7 +56,7 @@ fun ConfirmAccountProviderView(
|
||||||
) {
|
) {
|
||||||
val isLoading by remember(state.loginFlow) {
|
val isLoading by remember(state.loginFlow) {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
state.loginFlow is Async.Loading
|
state.loginFlow is AsyncData.Loading
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val eventSink = state.eventSink
|
val eventSink = state.eventSink
|
||||||
|
|
@ -107,7 +107,7 @@ fun ConfirmAccountProviderView(
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
when (state.loginFlow) {
|
when (state.loginFlow) {
|
||||||
is Async.Failure -> {
|
is AsyncData.Failure -> {
|
||||||
when (val error = state.loginFlow.error) {
|
when (val error = state.loginFlow.error) {
|
||||||
is ChangeServerError.Error -> {
|
is ChangeServerError.Error -> {
|
||||||
ErrorDialog(
|
ErrorDialog(
|
||||||
|
|
@ -127,14 +127,14 @@ fun ConfirmAccountProviderView(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Async.Loading -> Unit // The Continue button shows the loading state
|
is AsyncData.Loading -> Unit // The Continue button shows the loading state
|
||||||
is Async.Success -> {
|
is AsyncData.Success -> {
|
||||||
when (val loginFlowState = state.loginFlow.data) {
|
when (val loginFlowState = state.loginFlow.data) {
|
||||||
is LoginFlow.OidcFlow -> onOidcDetails(loginFlowState.oidcDetails)
|
is LoginFlow.OidcFlow -> onOidcDetails(loginFlowState.oidcDetails)
|
||||||
LoginFlow.PasswordLogin -> onLoginPasswordNeeded()
|
LoginFlow.PasswordLogin -> onLoginPasswordNeeded()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Async.Uninitialized -> Unit
|
AsyncData.Uninitialized -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import io.element.android.features.login.impl.DefaultLoginUserStory
|
import io.element.android.features.login.impl.DefaultLoginUserStory
|
||||||
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
|
|
@ -43,8 +43,8 @@ class LoginPasswordPresenter @Inject constructor(
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): LoginPasswordState {
|
override fun present(): LoginPasswordState {
|
||||||
val localCoroutineScope = rememberCoroutineScope()
|
val localCoroutineScope = rememberCoroutineScope()
|
||||||
val loginAction: MutableState<Async<SessionId>> = remember {
|
val loginAction: MutableState<AsyncData<SessionId>> = remember {
|
||||||
mutableStateOf(Async.Uninitialized)
|
mutableStateOf(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
|
|
||||||
val formState = rememberSaveable {
|
val formState = rememberSaveable {
|
||||||
|
|
@ -63,7 +63,7 @@ class LoginPasswordPresenter @Inject constructor(
|
||||||
LoginPasswordEvents.Submit -> {
|
LoginPasswordEvents.Submit -> {
|
||||||
localCoroutineScope.submit(formState.value, loginAction)
|
localCoroutineScope.submit(formState.value, loginAction)
|
||||||
}
|
}
|
||||||
LoginPasswordEvents.ClearError -> loginAction.value = Async.Uninitialized
|
LoginPasswordEvents.ClearError -> loginAction.value = AsyncData.Uninitialized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,16 +75,16 @@ class LoginPasswordPresenter @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.submit(formState: LoginFormState, loggedInState: MutableState<Async<SessionId>>) = launch {
|
private fun CoroutineScope.submit(formState: LoginFormState, loggedInState: MutableState<AsyncData<SessionId>>) = launch {
|
||||||
loggedInState.value = Async.Loading()
|
loggedInState.value = AsyncData.Loading()
|
||||||
authenticationService.login(formState.login.trim(), formState.password)
|
authenticationService.login(formState.login.trim(), formState.password)
|
||||||
.onSuccess { sessionId ->
|
.onSuccess { sessionId ->
|
||||||
// We will not navigate to the WaitList screen, so the login user story is done
|
// We will not navigate to the WaitList screen, so the login user story is done
|
||||||
defaultLoginUserStory.setLoginFlowIsDone(true)
|
defaultLoginUserStory.setLoginFlowIsDone(true)
|
||||||
loggedInState.value = Async.Success(sessionId)
|
loggedInState.value = AsyncData.Success(sessionId)
|
||||||
}
|
}
|
||||||
.onFailure { failure ->
|
.onFailure { failure ->
|
||||||
loggedInState.value = Async.Failure(failure)
|
loggedInState.value = AsyncData.Failure(failure)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,18 +18,18 @@ package io.element.android.features.login.impl.screens.loginpassword
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import io.element.android.features.login.impl.accountprovider.AccountProvider
|
import io.element.android.features.login.impl.accountprovider.AccountProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
data class LoginPasswordState(
|
data class LoginPasswordState(
|
||||||
val accountProvider: AccountProvider,
|
val accountProvider: AccountProvider,
|
||||||
val formState: LoginFormState,
|
val formState: LoginFormState,
|
||||||
val loginAction: Async<SessionId>,
|
val loginAction: AsyncData<SessionId>,
|
||||||
val eventSink: (LoginPasswordEvents) -> Unit
|
val eventSink: (LoginPasswordEvents) -> Unit
|
||||||
) {
|
) {
|
||||||
val submitEnabled: Boolean
|
val submitEnabled: Boolean
|
||||||
get() = loginAction !is Async.Failure &&
|
get() = loginAction !is AsyncData.Failure &&
|
||||||
formState.login.isNotEmpty() &&
|
formState.login.isNotEmpty() &&
|
||||||
formState.password.isNotEmpty()
|
formState.password.isNotEmpty()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,22 +18,22 @@ package io.element.android.features.login.impl.screens.loginpassword
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.features.login.impl.accountprovider.anAccountProvider
|
import io.element.android.features.login.impl.accountprovider.anAccountProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
|
|
||||||
open class LoginPasswordStateProvider : PreviewParameterProvider<LoginPasswordState> {
|
open class LoginPasswordStateProvider : PreviewParameterProvider<LoginPasswordState> {
|
||||||
override val values: Sequence<LoginPasswordState>
|
override val values: Sequence<LoginPasswordState>
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
aLoginPasswordState(),
|
aLoginPasswordState(),
|
||||||
// Loading
|
// Loading
|
||||||
aLoginPasswordState().copy(loginAction = Async.Loading()),
|
aLoginPasswordState().copy(loginAction = AsyncData.Loading()),
|
||||||
// Error
|
// Error
|
||||||
aLoginPasswordState().copy(loginAction = Async.Failure(Exception("An error occurred"))),
|
aLoginPasswordState().copy(loginAction = AsyncData.Failure(Exception("An error occurred"))),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun aLoginPasswordState() = LoginPasswordState(
|
fun aLoginPasswordState() = LoginPasswordState(
|
||||||
accountProvider = anAccountProvider(),
|
accountProvider = anAccountProvider(),
|
||||||
formState = LoginFormState.Default,
|
formState = LoginFormState.Default,
|
||||||
loginAction = Async.Uninitialized,
|
loginAction = AsyncData.Uninitialized,
|
||||||
eventSink = {}
|
eventSink = {}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ import io.element.android.compound.tokens.generated.CompoundIcons
|
||||||
import io.element.android.features.login.impl.R
|
import io.element.android.features.login.impl.R
|
||||||
import io.element.android.features.login.impl.error.isWaitListError
|
import io.element.android.features.login.impl.error.isWaitListError
|
||||||
import io.element.android.features.login.impl.error.loginError
|
import io.element.android.features.login.impl.error.loginError
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
|
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
|
||||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||||
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
||||||
|
|
@ -84,7 +84,7 @@ fun LoginPasswordView(
|
||||||
) {
|
) {
|
||||||
val isLoading by remember(state.loginAction) {
|
val isLoading by remember(state.loginAction) {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
state.loginAction is Async.Loading
|
state.loginAction is AsyncData.Loading
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val focusManager = LocalFocusManager.current
|
val focusManager = LocalFocusManager.current
|
||||||
|
|
@ -148,7 +148,7 @@ fun LoginPasswordView(
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(60.dp))
|
Spacer(modifier = Modifier.height(60.dp))
|
||||||
|
|
||||||
if (state.loginAction is Async.Failure) {
|
if (state.loginAction is AsyncData.Failure) {
|
||||||
when {
|
when {
|
||||||
state.loginAction.error.isWaitListError() -> {
|
state.loginAction.error.isWaitListError() -> {
|
||||||
onWaitListError(state.formState)
|
onWaitListError(state.formState)
|
||||||
|
|
@ -224,7 +224,7 @@ private fun LoginForm(
|
||||||
)
|
)
|
||||||
|
|
||||||
var passwordVisible by remember { mutableStateOf(false) }
|
var passwordVisible by remember { mutableStateOf(false) }
|
||||||
if (state.loginAction is Async.Loading) {
|
if (state.loginAction is AsyncData.Loading) {
|
||||||
// Ensure password is hidden when user submits the form
|
// Ensure password is hidden when user submits the form
|
||||||
passwordVisible = false
|
passwordVisible = false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ import androidx.compose.runtime.setValue
|
||||||
import io.element.android.features.login.impl.changeserver.ChangeServerPresenter
|
import io.element.android.features.login.impl.changeserver.ChangeServerPresenter
|
||||||
import io.element.android.features.login.impl.resolver.HomeserverData
|
import io.element.android.features.login.impl.resolver.HomeserverData
|
||||||
import io.element.android.features.login.impl.resolver.HomeserverResolver
|
import io.element.android.features.login.impl.resolver.HomeserverResolver
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
|
@ -46,8 +46,8 @@ class SearchAccountProviderPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
val changeServerState = changeServerPresenter.present()
|
val changeServerState = changeServerPresenter.present()
|
||||||
|
|
||||||
val data: MutableState<Async<List<HomeserverData>>> = remember {
|
val data: MutableState<AsyncData<List<HomeserverData>>> = remember {
|
||||||
mutableStateOf(Async.Uninitialized)
|
mutableStateOf(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(userInput) {
|
LaunchedEffect(userInput) {
|
||||||
|
|
@ -70,16 +70,16 @@ class SearchAccountProviderPresenter @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.onUserInput(userInput: String, data: MutableState<Async<List<HomeserverData>>>) = launch {
|
private fun CoroutineScope.onUserInput(userInput: String, data: MutableState<AsyncData<List<HomeserverData>>>) = launch {
|
||||||
data.value = Async.Uninitialized
|
data.value = AsyncData.Uninitialized
|
||||||
// Debounce
|
// Debounce
|
||||||
delay(300)
|
delay(300)
|
||||||
data.value = Async.Loading()
|
data.value = AsyncData.Loading()
|
||||||
homeserverResolver.resolve(userInput).collect {
|
homeserverResolver.resolve(userInput).collect {
|
||||||
data.value = Async.Success(it)
|
data.value = AsyncData.Success(it)
|
||||||
}
|
}
|
||||||
if (data.value !is Async.Success) {
|
if (data.value !is AsyncData.Success) {
|
||||||
data.value = Async.Uninitialized
|
data.value = AsyncData.Uninitialized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,12 @@ package io.element.android.features.login.impl.screens.searchaccountprovider
|
||||||
|
|
||||||
import io.element.android.features.login.impl.changeserver.ChangeServerState
|
import io.element.android.features.login.impl.changeserver.ChangeServerState
|
||||||
import io.element.android.features.login.impl.resolver.HomeserverData
|
import io.element.android.features.login.impl.resolver.HomeserverData
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
|
|
||||||
// Do not use default value, so no member get forgotten in the presenters.
|
// Do not use default value, so no member get forgotten in the presenters.
|
||||||
data class SearchAccountProviderState(
|
data class SearchAccountProviderState(
|
||||||
val userInput: String,
|
val userInput: String,
|
||||||
val userInputResult: Async<List<HomeserverData>>,
|
val userInputResult: AsyncData<List<HomeserverData>>,
|
||||||
val changeServerState: ChangeServerState,
|
val changeServerState: ChangeServerState,
|
||||||
val eventSink: (SearchAccountProviderEvents) -> Unit
|
val eventSink: (SearchAccountProviderEvents) -> Unit
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -20,20 +20,20 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.appconfig.AuthenticationConfig
|
import io.element.android.appconfig.AuthenticationConfig
|
||||||
import io.element.android.features.login.impl.changeserver.aChangeServerState
|
import io.element.android.features.login.impl.changeserver.aChangeServerState
|
||||||
import io.element.android.features.login.impl.resolver.HomeserverData
|
import io.element.android.features.login.impl.resolver.HomeserverData
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
|
|
||||||
open class SearchAccountProviderStateProvider : PreviewParameterProvider<SearchAccountProviderState> {
|
open class SearchAccountProviderStateProvider : PreviewParameterProvider<SearchAccountProviderState> {
|
||||||
override val values: Sequence<SearchAccountProviderState>
|
override val values: Sequence<SearchAccountProviderState>
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
aSearchAccountProviderState(),
|
aSearchAccountProviderState(),
|
||||||
aSearchAccountProviderState(userInputResult = Async.Success(aHomeserverDataList())),
|
aSearchAccountProviderState(userInputResult = AsyncData.Success(aHomeserverDataList())),
|
||||||
// Add other state here
|
// Add other state here
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun aSearchAccountProviderState(
|
fun aSearchAccountProviderState(
|
||||||
userInput: String = "",
|
userInput: String = "",
|
||||||
userInputResult: Async<List<HomeserverData>> = Async.Uninitialized,
|
userInputResult: AsyncData<List<HomeserverData>> = AsyncData.Uninitialized,
|
||||||
) = SearchAccountProviderState(
|
) = SearchAccountProviderState(
|
||||||
userInput = userInput,
|
userInput = userInput,
|
||||||
userInputResult = userInputResult,
|
userInputResult = userInputResult,
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ import io.element.android.features.login.impl.accountprovider.AccountProviderVie
|
||||||
import io.element.android.features.login.impl.changeserver.ChangeServerEvents
|
import io.element.android.features.login.impl.changeserver.ChangeServerEvents
|
||||||
import io.element.android.features.login.impl.changeserver.ChangeServerView
|
import io.element.android.features.login.impl.changeserver.ChangeServerView
|
||||||
import io.element.android.features.login.impl.resolver.HomeserverData
|
import io.element.android.features.login.impl.resolver.HomeserverData
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
|
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
|
||||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||||
import io.element.android.libraries.designsystem.components.form.textFieldState
|
import io.element.android.libraries.designsystem.components.form.textFieldState
|
||||||
|
|
@ -152,10 +152,10 @@ fun SearchAccountProviderView(
|
||||||
}
|
}
|
||||||
|
|
||||||
when (state.userInputResult) {
|
when (state.userInputResult) {
|
||||||
is Async.Failure -> {
|
is AsyncData.Failure -> {
|
||||||
// Ignore errors (let the user type more chars)
|
// Ignore errors (let the user type more chars)
|
||||||
}
|
}
|
||||||
is Async.Loading -> {
|
is AsyncData.Loading -> {
|
||||||
item {
|
item {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -167,7 +167,7 @@ fun SearchAccountProviderView(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Async.Success -> {
|
is AsyncData.Success -> {
|
||||||
items(state.userInputResult.data) { homeserverData ->
|
items(state.userInputResult.data) { homeserverData ->
|
||||||
val item = homeserverData.toAccountProvider()
|
val item = homeserverData.toAccountProvider()
|
||||||
AccountProviderView(
|
AccountProviderView(
|
||||||
|
|
@ -178,7 +178,7 @@ fun SearchAccountProviderView(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Async.Uninitialized -> Unit
|
AsyncData.Uninitialized -> Unit
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
Spacer(Modifier.height(32.dp))
|
Spacer(Modifier.height(32.dp))
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.features.login.impl.DefaultLoginUserStory
|
import io.element.android.features.login.impl.DefaultLoginUserStory
|
||||||
import io.element.android.features.login.impl.screens.loginpassword.LoginFormState
|
import io.element.android.features.login.impl.screens.loginpassword.LoginFormState
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.core.meta.BuildMeta
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
||||||
|
|
@ -55,8 +55,8 @@ class WaitListPresenter @AssistedInject constructor(
|
||||||
authenticationService.getHomeserverDetails().value?.url ?: "server"
|
authenticationService.getHomeserverDetails().value?.url ?: "server"
|
||||||
}
|
}
|
||||||
|
|
||||||
val loginAction: MutableState<Async<SessionId>> = remember {
|
val loginAction: MutableState<AsyncData<SessionId>> = remember {
|
||||||
mutableStateOf(Async.Uninitialized)
|
mutableStateOf(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
|
|
||||||
val attemptNumber = remember { mutableIntStateOf(0) }
|
val attemptNumber = remember { mutableIntStateOf(0) }
|
||||||
|
|
@ -70,7 +70,7 @@ class WaitListPresenter @AssistedInject constructor(
|
||||||
coroutineScope.loginAttempt(formState, loginAction)
|
coroutineScope.loginAttempt(formState, loginAction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WaitListEvents.ClearError -> loginAction.value = Async.Uninitialized
|
WaitListEvents.ClearError -> loginAction.value = AsyncData.Uninitialized
|
||||||
WaitListEvents.Continue -> defaultLoginUserStory.setLoginFlowIsDone(true)
|
WaitListEvents.Continue -> defaultLoginUserStory.setLoginFlowIsDone(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -83,15 +83,15 @@ class WaitListPresenter @AssistedInject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.loginAttempt(formState: LoginFormState, loggedInState: MutableState<Async<SessionId>>) = launch {
|
private fun CoroutineScope.loginAttempt(formState: LoginFormState, loggedInState: MutableState<AsyncData<SessionId>>) = launch {
|
||||||
Timber.w("Attempt to login...")
|
Timber.w("Attempt to login...")
|
||||||
loggedInState.value = Async.Loading()
|
loggedInState.value = AsyncData.Loading()
|
||||||
authenticationService.login(formState.login.trim(), formState.password)
|
authenticationService.login(formState.login.trim(), formState.password)
|
||||||
.onSuccess { sessionId ->
|
.onSuccess { sessionId ->
|
||||||
loggedInState.value = Async.Success(sessionId)
|
loggedInState.value = AsyncData.Success(sessionId)
|
||||||
}
|
}
|
||||||
.onFailure { failure ->
|
.onFailure { failure ->
|
||||||
loggedInState.value = Async.Failure(failure)
|
loggedInState.value = AsyncData.Failure(failure)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,13 @@
|
||||||
|
|
||||||
package io.element.android.features.login.impl.screens.waitlistscreen
|
package io.element.android.features.login.impl.screens.waitlistscreen
|
||||||
|
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
|
|
||||||
// Do not use default value, so no member get forgotten in the presenters.
|
// Do not use default value, so no member get forgotten in the presenters.
|
||||||
data class WaitListState(
|
data class WaitListState(
|
||||||
val appName: String,
|
val appName: String,
|
||||||
val serverName: String,
|
val serverName: String,
|
||||||
val loginAction: Async<SessionId>,
|
val loginAction: AsyncData<SessionId>,
|
||||||
val eventSink: (WaitListEvents) -> Unit
|
val eventSink: (WaitListEvents) -> Unit
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -17,17 +17,17 @@
|
||||||
package io.element.android.features.login.impl.screens.waitlistscreen
|
package io.element.android.features.login.impl.screens.waitlistscreen
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
|
|
||||||
open class WaitListStateProvider : PreviewParameterProvider<WaitListState> {
|
open class WaitListStateProvider : PreviewParameterProvider<WaitListState> {
|
||||||
override val values: Sequence<WaitListState>
|
override val values: Sequence<WaitListState>
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
aWaitListState(loginAction = Async.Uninitialized),
|
aWaitListState(loginAction = AsyncData.Uninitialized),
|
||||||
aWaitListState(loginAction = Async.Loading()),
|
aWaitListState(loginAction = AsyncData.Loading()),
|
||||||
aWaitListState(loginAction = Async.Failure(Throwable("error"))),
|
aWaitListState(loginAction = AsyncData.Failure(Throwable("error"))),
|
||||||
aWaitListState(loginAction = Async.Failure(Throwable(message = "IO_ELEMENT_X_WAIT_LIST"))),
|
aWaitListState(loginAction = AsyncData.Failure(Throwable(message = "IO_ELEMENT_X_WAIT_LIST"))),
|
||||||
aWaitListState(loginAction = Async.Success(SessionId("@alice:element.io"))),
|
aWaitListState(loginAction = AsyncData.Success(SessionId("@alice:element.io"))),
|
||||||
// Add other state here
|
// Add other state here
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -35,7 +35,7 @@ open class WaitListStateProvider : PreviewParameterProvider<WaitListState> {
|
||||||
fun aWaitListState(
|
fun aWaitListState(
|
||||||
appName: String = "Element X",
|
appName: String = "Element X",
|
||||||
serverName: String = "server.org",
|
serverName: String = "server.org",
|
||||||
loginAction: Async<SessionId> = Async.Uninitialized,
|
loginAction: AsyncData<SessionId> = AsyncData.Uninitialized,
|
||||||
) = WaitListState(
|
) = WaitListState(
|
||||||
appName = appName,
|
appName = appName,
|
||||||
serverName = serverName,
|
serverName = serverName,
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ import androidx.lifecycle.Lifecycle
|
||||||
import io.element.android.features.login.impl.R
|
import io.element.android.features.login.impl.R
|
||||||
import io.element.android.features.login.impl.error.isWaitListError
|
import io.element.android.features.login.impl.error.isWaitListError
|
||||||
import io.element.android.features.login.impl.error.loginError
|
import io.element.android.features.login.impl.error.loginError
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.designsystem.atomic.pages.SunsetPage
|
import io.element.android.libraries.designsystem.atomic.pages.SunsetPage
|
||||||
import io.element.android.libraries.designsystem.components.dialogs.RetryDialog
|
import io.element.android.libraries.designsystem.components.dialogs.RetryDialog
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
|
|
@ -89,12 +89,12 @@ private fun WaitListContent(
|
||||||
) {
|
) {
|
||||||
val title = stringResource(
|
val title = stringResource(
|
||||||
when (state.loginAction) {
|
when (state.loginAction) {
|
||||||
is Async.Success -> R.string.screen_waitlist_title_success
|
is AsyncData.Success -> R.string.screen_waitlist_title_success
|
||||||
else -> R.string.screen_waitlist_title
|
else -> R.string.screen_waitlist_title
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
val subtitle = when (state.loginAction) {
|
val subtitle = when (state.loginAction) {
|
||||||
is Async.Success -> stringResource(
|
is AsyncData.Success -> stringResource(
|
||||||
id = R.string.screen_waitlist_message_success,
|
id = R.string.screen_waitlist_message_success,
|
||||||
state.appName,
|
state.appName,
|
||||||
)
|
)
|
||||||
|
|
@ -122,7 +122,7 @@ private fun OverallContent(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Box(modifier = modifier.fillMaxSize()) {
|
Box(modifier = modifier.fillMaxSize()) {
|
||||||
if (state.loginAction !is Async.Success) {
|
if (state.loginAction !is AsyncData.Success) {
|
||||||
CompositionLocalProvider(LocalContentColor provides Color.Black) {
|
CompositionLocalProvider(LocalContentColor provides Color.Black) {
|
||||||
TextButton(
|
TextButton(
|
||||||
text = stringResource(CommonStrings.action_cancel),
|
text = stringResource(CommonStrings.action_cancel),
|
||||||
|
|
@ -130,7 +130,7 @@ private fun OverallContent(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (state.loginAction is Async.Success) {
|
if (state.loginAction is AsyncData.Success) {
|
||||||
Button(
|
Button(
|
||||||
text = stringResource(id = CommonStrings.action_continue),
|
text = stringResource(id = CommonStrings.action_continue),
|
||||||
onClick = { state.eventSink.invoke(WaitListEvents.Continue) },
|
onClick = { state.eventSink.invoke(WaitListEvents.Continue) },
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.login.impl.accountprovider.AccountProvider
|
import io.element.android.features.login.impl.accountprovider.AccountProvider
|
||||||
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.test.A_HOMESERVER
|
import io.element.android.libraries.matrix.test.A_HOMESERVER
|
||||||
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
|
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
|
||||||
import io.element.android.libraries.matrix.test.auth.FakeAuthenticationService
|
import io.element.android.libraries.matrix.test.auth.FakeAuthenticationService
|
||||||
|
|
@ -46,7 +46,7 @@ class ChangeServerPresenterTest {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.changeServerAction).isEqualTo(Async.Uninitialized)
|
assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,13 +61,13 @@ class ChangeServerPresenterTest {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.changeServerAction).isEqualTo(Async.Uninitialized)
|
assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized)
|
||||||
authenticationService.givenHomeserver(A_HOMESERVER)
|
authenticationService.givenHomeserver(A_HOMESERVER)
|
||||||
initialState.eventSink.invoke(ChangeServerEvents.ChangeServer(AccountProvider(url = A_HOMESERVER_URL)))
|
initialState.eventSink.invoke(ChangeServerEvents.ChangeServer(AccountProvider(url = A_HOMESERVER_URL)))
|
||||||
val loadingState = awaitItem()
|
val loadingState = awaitItem()
|
||||||
assertThat(loadingState.changeServerAction).isInstanceOf(Async.Loading::class.java)
|
assertThat(loadingState.changeServerAction).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
val successState = awaitItem()
|
val successState = awaitItem()
|
||||||
assertThat(successState.changeServerAction).isEqualTo(Async.Success(Unit))
|
assertThat(successState.changeServerAction).isEqualTo(AsyncData.Success(Unit))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,16 +82,16 @@ class ChangeServerPresenterTest {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.changeServerAction).isEqualTo(Async.Uninitialized)
|
assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized)
|
||||||
initialState.eventSink.invoke(ChangeServerEvents.ChangeServer(AccountProvider(url = A_HOMESERVER_URL)))
|
initialState.eventSink.invoke(ChangeServerEvents.ChangeServer(AccountProvider(url = A_HOMESERVER_URL)))
|
||||||
val loadingState = awaitItem()
|
val loadingState = awaitItem()
|
||||||
assertThat(loadingState.changeServerAction).isInstanceOf(Async.Loading::class.java)
|
assertThat(loadingState.changeServerAction).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
val failureState = awaitItem()
|
val failureState = awaitItem()
|
||||||
assertThat(failureState.changeServerAction).isInstanceOf(Async.Failure::class.java)
|
assertThat(failureState.changeServerAction).isInstanceOf(AsyncData.Failure::class.java)
|
||||||
// Clear error
|
// Clear error
|
||||||
failureState.eventSink.invoke(ChangeServerEvents.ClearError)
|
failureState.eventSink.invoke(ChangeServerEvents.ClearError)
|
||||||
val finalState = awaitItem()
|
val finalState = awaitItem()
|
||||||
assertThat(finalState.changeServerAction).isEqualTo(Async.Uninitialized)
|
assertThat(finalState.changeServerAction).isEqualTo(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import app.cash.molecule.moleculeFlow
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.login.api.oidc.OidcAction
|
import io.element.android.features.login.api.oidc.OidcAction
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.test.A_THROWABLE
|
import io.element.android.libraries.matrix.test.A_THROWABLE
|
||||||
import io.element.android.libraries.matrix.test.auth.A_OIDC_DATA
|
import io.element.android.libraries.matrix.test.auth.A_OIDC_DATA
|
||||||
import io.element.android.libraries.matrix.test.auth.FakeAuthenticationService
|
import io.element.android.libraries.matrix.test.auth.FakeAuthenticationService
|
||||||
|
|
@ -49,7 +49,7 @@ class OidcPresenterTest {
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.oidcDetails).isEqualTo(A_OIDC_DATA)
|
assertThat(initialState.oidcDetails).isEqualTo(A_OIDC_DATA)
|
||||||
assertThat(initialState.requestState).isEqualTo(Async.Uninitialized)
|
assertThat(initialState.requestState).isEqualTo(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,9 +65,9 @@ class OidcPresenterTest {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink.invoke(OidcEvents.Cancel)
|
initialState.eventSink.invoke(OidcEvents.Cancel)
|
||||||
val loadingState = awaitItem()
|
val loadingState = awaitItem()
|
||||||
assertThat(loadingState.requestState).isEqualTo(Async.Loading<Unit>())
|
assertThat(loadingState.requestState).isEqualTo(AsyncData.Loading<Unit>())
|
||||||
val finalState = awaitItem()
|
val finalState = awaitItem()
|
||||||
assertThat(finalState.requestState).isEqualTo(Async.Success(Unit))
|
assertThat(finalState.requestState).isEqualTo(AsyncData.Success(Unit))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -85,9 +85,9 @@ class OidcPresenterTest {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink.invoke(OidcEvents.Cancel)
|
initialState.eventSink.invoke(OidcEvents.Cancel)
|
||||||
val loadingState = awaitItem()
|
val loadingState = awaitItem()
|
||||||
assertThat(loadingState.requestState).isEqualTo(Async.Loading<Unit>())
|
assertThat(loadingState.requestState).isEqualTo(AsyncData.Loading<Unit>())
|
||||||
val finalState = awaitItem()
|
val finalState = awaitItem()
|
||||||
assertThat(finalState.requestState).isEqualTo(Async.Failure<Unit>(A_THROWABLE))
|
assertThat(finalState.requestState).isEqualTo(AsyncData.Failure<Unit>(A_THROWABLE))
|
||||||
// Note: in real life I do not think this can happen, and the app should not block the user.
|
// Note: in real life I do not think this can happen, and the app should not block the user.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -104,9 +104,9 @@ class OidcPresenterTest {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink.invoke(OidcEvents.OidcActionEvent(OidcAction.GoBack))
|
initialState.eventSink.invoke(OidcEvents.OidcActionEvent(OidcAction.GoBack))
|
||||||
val loadingState = awaitItem()
|
val loadingState = awaitItem()
|
||||||
assertThat(loadingState.requestState).isEqualTo(Async.Loading<Unit>())
|
assertThat(loadingState.requestState).isEqualTo(AsyncData.Loading<Unit>())
|
||||||
val finalState = awaitItem()
|
val finalState = awaitItem()
|
||||||
assertThat(finalState.requestState).isEqualTo(Async.Success(Unit))
|
assertThat(finalState.requestState).isEqualTo(AsyncData.Success(Unit))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,7 +122,7 @@ class OidcPresenterTest {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink.invoke(OidcEvents.OidcActionEvent(OidcAction.Success("A_URL")))
|
initialState.eventSink.invoke(OidcEvents.OidcActionEvent(OidcAction.Success("A_URL")))
|
||||||
val loadingState = awaitItem()
|
val loadingState = awaitItem()
|
||||||
assertThat(loadingState.requestState).isEqualTo(Async.Loading<Unit>())
|
assertThat(loadingState.requestState).isEqualTo(AsyncData.Loading<Unit>())
|
||||||
// In this case, no success, the session is created and the node get destroyed.
|
// In this case, no success, the session is created and the node get destroyed.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -141,12 +141,12 @@ class OidcPresenterTest {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink.invoke(OidcEvents.OidcActionEvent(OidcAction.Success("A_URL")))
|
initialState.eventSink.invoke(OidcEvents.OidcActionEvent(OidcAction.Success("A_URL")))
|
||||||
val loadingState = awaitItem()
|
val loadingState = awaitItem()
|
||||||
assertThat(loadingState.requestState).isEqualTo(Async.Loading<Unit>())
|
assertThat(loadingState.requestState).isEqualTo(AsyncData.Loading<Unit>())
|
||||||
val errorState = awaitItem()
|
val errorState = awaitItem()
|
||||||
assertThat(errorState.requestState).isEqualTo(Async.Failure<Unit>(A_THROWABLE))
|
assertThat(errorState.requestState).isEqualTo(AsyncData.Failure<Unit>(A_THROWABLE))
|
||||||
errorState.eventSink.invoke(OidcEvents.ClearError)
|
errorState.eventSink.invoke(OidcEvents.ClearError)
|
||||||
val finalState = awaitItem()
|
val finalState = awaitItem()
|
||||||
assertThat(finalState.requestState).isEqualTo(Async.Uninitialized)
|
assertThat(finalState.requestState).isEqualTo(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import io.element.android.features.login.impl.DefaultLoginUserStory
|
||||||
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
||||||
import io.element.android.features.login.impl.oidc.customtab.DefaultOidcActionFlow
|
import io.element.android.features.login.impl.oidc.customtab.DefaultOidcActionFlow
|
||||||
import io.element.android.features.login.impl.util.defaultAccountProvider
|
import io.element.android.features.login.impl.util.defaultAccountProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
||||||
import io.element.android.libraries.matrix.test.A_HOMESERVER
|
import io.element.android.libraries.matrix.test.A_HOMESERVER
|
||||||
import io.element.android.libraries.matrix.test.A_HOMESERVER_OIDC
|
import io.element.android.libraries.matrix.test.A_HOMESERVER_OIDC
|
||||||
|
|
@ -52,7 +52,7 @@ class ConfirmAccountProviderPresenterTest {
|
||||||
assertThat(initialState.isAccountCreation).isFalse()
|
assertThat(initialState.isAccountCreation).isFalse()
|
||||||
assertThat(initialState.submitEnabled).isTrue()
|
assertThat(initialState.submitEnabled).isTrue()
|
||||||
assertThat(initialState.accountProvider).isEqualTo(defaultAccountProvider)
|
assertThat(initialState.accountProvider).isEqualTo(defaultAccountProvider)
|
||||||
assertThat(initialState.loginFlow).isEqualTo(Async.Uninitialized)
|
assertThat(initialState.loginFlow).isEqualTo(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,10 +70,10 @@ class ConfirmAccountProviderPresenterTest {
|
||||||
initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue)
|
initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue)
|
||||||
val loadingState = awaitItem()
|
val loadingState = awaitItem()
|
||||||
assertThat(loadingState.submitEnabled).isTrue()
|
assertThat(loadingState.submitEnabled).isTrue()
|
||||||
assertThat(loadingState.loginFlow).isInstanceOf(Async.Loading::class.java)
|
assertThat(loadingState.loginFlow).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
val successState = awaitItem()
|
val successState = awaitItem()
|
||||||
assertThat(successState.submitEnabled).isFalse()
|
assertThat(successState.submitEnabled).isFalse()
|
||||||
assertThat(successState.loginFlow).isInstanceOf(Async.Success::class.java)
|
assertThat(successState.loginFlow).isInstanceOf(AsyncData.Success::class.java)
|
||||||
assertThat(successState.loginFlow.dataOrNull()).isEqualTo(LoginFlow.PasswordLogin)
|
assertThat(successState.loginFlow.dataOrNull()).isEqualTo(LoginFlow.PasswordLogin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -92,10 +92,10 @@ class ConfirmAccountProviderPresenterTest {
|
||||||
initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue)
|
initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue)
|
||||||
val loadingState = awaitItem()
|
val loadingState = awaitItem()
|
||||||
assertThat(loadingState.submitEnabled).isTrue()
|
assertThat(loadingState.submitEnabled).isTrue()
|
||||||
assertThat(loadingState.loginFlow).isInstanceOf(Async.Loading::class.java)
|
assertThat(loadingState.loginFlow).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
val successState = awaitItem()
|
val successState = awaitItem()
|
||||||
assertThat(successState.submitEnabled).isFalse()
|
assertThat(successState.submitEnabled).isFalse()
|
||||||
assertThat(successState.loginFlow).isInstanceOf(Async.Success::class.java)
|
assertThat(successState.loginFlow).isInstanceOf(AsyncData.Success::class.java)
|
||||||
assertThat(successState.loginFlow.dataOrNull()).isInstanceOf(LoginFlow.OidcFlow::class.java)
|
assertThat(successState.loginFlow.dataOrNull()).isInstanceOf(LoginFlow.OidcFlow::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -116,15 +116,15 @@ class ConfirmAccountProviderPresenterTest {
|
||||||
initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue)
|
initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue)
|
||||||
val loadingState = awaitItem()
|
val loadingState = awaitItem()
|
||||||
assertThat(loadingState.submitEnabled).isTrue()
|
assertThat(loadingState.submitEnabled).isTrue()
|
||||||
assertThat(loadingState.loginFlow).isInstanceOf(Async.Loading::class.java)
|
assertThat(loadingState.loginFlow).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
val successState = awaitItem()
|
val successState = awaitItem()
|
||||||
assertThat(successState.submitEnabled).isFalse()
|
assertThat(successState.submitEnabled).isFalse()
|
||||||
assertThat(successState.loginFlow).isInstanceOf(Async.Success::class.java)
|
assertThat(successState.loginFlow).isInstanceOf(AsyncData.Success::class.java)
|
||||||
assertThat(successState.loginFlow.dataOrNull()).isInstanceOf(LoginFlow.OidcFlow::class.java)
|
assertThat(successState.loginFlow.dataOrNull()).isInstanceOf(LoginFlow.OidcFlow::class.java)
|
||||||
authenticationService.givenOidcCancelError(A_THROWABLE)
|
authenticationService.givenOidcCancelError(A_THROWABLE)
|
||||||
defaultOidcActionFlow.post(OidcAction.GoBack)
|
defaultOidcActionFlow.post(OidcAction.GoBack)
|
||||||
val cancelFailureState = awaitItem()
|
val cancelFailureState = awaitItem()
|
||||||
assertThat(cancelFailureState.loginFlow).isInstanceOf(Async.Failure::class.java)
|
assertThat(cancelFailureState.loginFlow).isInstanceOf(AsyncData.Failure::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,14 +144,14 @@ class ConfirmAccountProviderPresenterTest {
|
||||||
initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue)
|
initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue)
|
||||||
val loadingState = awaitItem()
|
val loadingState = awaitItem()
|
||||||
assertThat(loadingState.submitEnabled).isTrue()
|
assertThat(loadingState.submitEnabled).isTrue()
|
||||||
assertThat(loadingState.loginFlow).isInstanceOf(Async.Loading::class.java)
|
assertThat(loadingState.loginFlow).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
val successState = awaitItem()
|
val successState = awaitItem()
|
||||||
assertThat(successState.submitEnabled).isFalse()
|
assertThat(successState.submitEnabled).isFalse()
|
||||||
assertThat(successState.loginFlow).isInstanceOf(Async.Success::class.java)
|
assertThat(successState.loginFlow).isInstanceOf(AsyncData.Success::class.java)
|
||||||
assertThat(successState.loginFlow.dataOrNull()).isInstanceOf(LoginFlow.OidcFlow::class.java)
|
assertThat(successState.loginFlow.dataOrNull()).isInstanceOf(LoginFlow.OidcFlow::class.java)
|
||||||
defaultOidcActionFlow.post(OidcAction.GoBack)
|
defaultOidcActionFlow.post(OidcAction.GoBack)
|
||||||
val cancelFinalState = awaitItem()
|
val cancelFinalState = awaitItem()
|
||||||
assertThat(cancelFinalState.loginFlow).isInstanceOf(Async.Uninitialized::class.java)
|
assertThat(cancelFinalState.loginFlow).isInstanceOf(AsyncData.Uninitialized::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,17 +171,17 @@ class ConfirmAccountProviderPresenterTest {
|
||||||
initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue)
|
initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue)
|
||||||
val loadingState = awaitItem()
|
val loadingState = awaitItem()
|
||||||
assertThat(loadingState.submitEnabled).isTrue()
|
assertThat(loadingState.submitEnabled).isTrue()
|
||||||
assertThat(loadingState.loginFlow).isInstanceOf(Async.Loading::class.java)
|
assertThat(loadingState.loginFlow).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
val successState = awaitItem()
|
val successState = awaitItem()
|
||||||
assertThat(successState.submitEnabled).isFalse()
|
assertThat(successState.submitEnabled).isFalse()
|
||||||
assertThat(successState.loginFlow).isInstanceOf(Async.Success::class.java)
|
assertThat(successState.loginFlow).isInstanceOf(AsyncData.Success::class.java)
|
||||||
assertThat(successState.loginFlow.dataOrNull()).isInstanceOf(LoginFlow.OidcFlow::class.java)
|
assertThat(successState.loginFlow.dataOrNull()).isInstanceOf(LoginFlow.OidcFlow::class.java)
|
||||||
authenticationService.givenLoginError(A_THROWABLE)
|
authenticationService.givenLoginError(A_THROWABLE)
|
||||||
defaultOidcActionFlow.post(OidcAction.Success("aUrl"))
|
defaultOidcActionFlow.post(OidcAction.Success("aUrl"))
|
||||||
val cancelLoadingState = awaitItem()
|
val cancelLoadingState = awaitItem()
|
||||||
assertThat(cancelLoadingState.loginFlow).isInstanceOf(Async.Loading::class.java)
|
assertThat(cancelLoadingState.loginFlow).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
val cancelFailureState = awaitItem()
|
val cancelFailureState = awaitItem()
|
||||||
assertThat(cancelFailureState.loginFlow).isInstanceOf(Async.Failure::class.java)
|
assertThat(cancelFailureState.loginFlow).isInstanceOf(AsyncData.Failure::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -205,15 +205,15 @@ class ConfirmAccountProviderPresenterTest {
|
||||||
initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue)
|
initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue)
|
||||||
val loadingState = awaitItem()
|
val loadingState = awaitItem()
|
||||||
assertThat(loadingState.submitEnabled).isTrue()
|
assertThat(loadingState.submitEnabled).isTrue()
|
||||||
assertThat(loadingState.loginFlow).isInstanceOf(Async.Loading::class.java)
|
assertThat(loadingState.loginFlow).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
val successState = awaitItem()
|
val successState = awaitItem()
|
||||||
assertThat(successState.submitEnabled).isFalse()
|
assertThat(successState.submitEnabled).isFalse()
|
||||||
assertThat(successState.loginFlow).isInstanceOf(Async.Success::class.java)
|
assertThat(successState.loginFlow).isInstanceOf(AsyncData.Success::class.java)
|
||||||
assertThat(successState.loginFlow.dataOrNull()).isInstanceOf(LoginFlow.OidcFlow::class.java)
|
assertThat(successState.loginFlow.dataOrNull()).isInstanceOf(LoginFlow.OidcFlow::class.java)
|
||||||
assertThat(defaultLoginUserStory.loginFlowIsDone.value).isFalse()
|
assertThat(defaultLoginUserStory.loginFlowIsDone.value).isFalse()
|
||||||
defaultOidcActionFlow.post(OidcAction.Success("aUrl"))
|
defaultOidcActionFlow.post(OidcAction.Success("aUrl"))
|
||||||
val successSuccessState = awaitItem()
|
val successSuccessState = awaitItem()
|
||||||
assertThat(successSuccessState.loginFlow).isInstanceOf(Async.Loading::class.java)
|
assertThat(successSuccessState.loginFlow).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
waitForPredicate { defaultLoginUserStory.loginFlowIsDone.value }
|
waitForPredicate { defaultLoginUserStory.loginFlowIsDone.value }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -233,7 +233,7 @@ class ConfirmAccountProviderPresenterTest {
|
||||||
skipItems(1) // Loading
|
skipItems(1) // Loading
|
||||||
val failureState = awaitItem()
|
val failureState = awaitItem()
|
||||||
assertThat(failureState.submitEnabled).isFalse()
|
assertThat(failureState.submitEnabled).isFalse()
|
||||||
assertThat(failureState.loginFlow).isInstanceOf(Async.Failure::class.java)
|
assertThat(failureState.loginFlow).isInstanceOf(AsyncData.Failure::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -256,12 +256,12 @@ class ConfirmAccountProviderPresenterTest {
|
||||||
|
|
||||||
// Check an error was returned
|
// Check an error was returned
|
||||||
val submittedState = awaitItem()
|
val submittedState = awaitItem()
|
||||||
assertThat(submittedState.loginFlow).isInstanceOf(Async.Failure::class.java)
|
assertThat(submittedState.loginFlow).isInstanceOf(AsyncData.Failure::class.java)
|
||||||
|
|
||||||
// Assert the error is then cleared
|
// Assert the error is then cleared
|
||||||
submittedState.eventSink(ConfirmAccountProviderEvents.ClearError)
|
submittedState.eventSink(ConfirmAccountProviderEvents.ClearError)
|
||||||
val clearedState = awaitItem()
|
val clearedState = awaitItem()
|
||||||
assertThat(clearedState.loginFlow).isEqualTo(Async.Uninitialized)
|
assertThat(clearedState.loginFlow).isEqualTo(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.login.impl.DefaultLoginUserStory
|
import io.element.android.features.login.impl.DefaultLoginUserStory
|
||||||
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
||||||
import io.element.android.features.login.impl.util.defaultAccountProvider
|
import io.element.android.features.login.impl.util.defaultAccountProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
import io.element.android.libraries.matrix.test.A_HOMESERVER
|
import io.element.android.libraries.matrix.test.A_HOMESERVER
|
||||||
import io.element.android.libraries.matrix.test.A_PASSWORD
|
import io.element.android.libraries.matrix.test.A_PASSWORD
|
||||||
|
|
@ -57,7 +57,7 @@ class LoginPasswordPresenterTest {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.accountProvider).isEqualTo(defaultAccountProvider)
|
assertThat(initialState.accountProvider).isEqualTo(defaultAccountProvider)
|
||||||
assertThat(initialState.formState).isEqualTo(LoginFormState.Default)
|
assertThat(initialState.formState).isEqualTo(LoginFormState.Default)
|
||||||
assertThat(initialState.loginAction).isEqualTo(Async.Uninitialized)
|
assertThat(initialState.loginAction).isEqualTo(AsyncData.Uninitialized)
|
||||||
assertThat(initialState.submitEnabled).isFalse()
|
assertThat(initialState.submitEnabled).isFalse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -110,9 +110,9 @@ class LoginPasswordPresenterTest {
|
||||||
val loginAndPasswordState = awaitItem()
|
val loginAndPasswordState = awaitItem()
|
||||||
loginAndPasswordState.eventSink.invoke(LoginPasswordEvents.Submit)
|
loginAndPasswordState.eventSink.invoke(LoginPasswordEvents.Submit)
|
||||||
val submitState = awaitItem()
|
val submitState = awaitItem()
|
||||||
assertThat(submitState.loginAction).isInstanceOf(Async.Loading::class.java)
|
assertThat(submitState.loginAction).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
val loggedInState = awaitItem()
|
val loggedInState = awaitItem()
|
||||||
assertThat(loggedInState.loginAction).isEqualTo(Async.Success(A_SESSION_ID))
|
assertThat(loggedInState.loginAction).isEqualTo(AsyncData.Success(A_SESSION_ID))
|
||||||
assertThat(loginUserStory.loginFlowIsDone.value).isTrue()
|
assertThat(loginUserStory.loginFlowIsDone.value).isTrue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -139,9 +139,9 @@ class LoginPasswordPresenterTest {
|
||||||
authenticationService.givenLoginError(A_THROWABLE)
|
authenticationService.givenLoginError(A_THROWABLE)
|
||||||
loginAndPasswordState.eventSink.invoke(LoginPasswordEvents.Submit)
|
loginAndPasswordState.eventSink.invoke(LoginPasswordEvents.Submit)
|
||||||
val submitState = awaitItem()
|
val submitState = awaitItem()
|
||||||
assertThat(submitState.loginAction).isInstanceOf(Async.Loading::class.java)
|
assertThat(submitState.loginAction).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
val loggedInState = awaitItem()
|
val loggedInState = awaitItem()
|
||||||
assertThat(loggedInState.loginAction).isEqualTo(Async.Failure<SessionId>(A_THROWABLE))
|
assertThat(loggedInState.loginAction).isEqualTo(AsyncData.Failure<SessionId>(A_THROWABLE))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -167,14 +167,14 @@ class LoginPasswordPresenterTest {
|
||||||
authenticationService.givenLoginError(A_THROWABLE)
|
authenticationService.givenLoginError(A_THROWABLE)
|
||||||
loginAndPasswordState.eventSink.invoke(LoginPasswordEvents.Submit)
|
loginAndPasswordState.eventSink.invoke(LoginPasswordEvents.Submit)
|
||||||
val submitState = awaitItem()
|
val submitState = awaitItem()
|
||||||
assertThat(submitState.loginAction).isInstanceOf(Async.Loading::class.java)
|
assertThat(submitState.loginAction).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
val loggedInState = awaitItem()
|
val loggedInState = awaitItem()
|
||||||
// Check an error was returned
|
// Check an error was returned
|
||||||
assertThat(loggedInState.loginAction).isEqualTo(Async.Failure<SessionId>(A_THROWABLE))
|
assertThat(loggedInState.loginAction).isEqualTo(AsyncData.Failure<SessionId>(A_THROWABLE))
|
||||||
// Assert the error is then cleared
|
// Assert the error is then cleared
|
||||||
loggedInState.eventSink(LoginPasswordEvents.ClearError)
|
loggedInState.eventSink(LoginPasswordEvents.ClearError)
|
||||||
val clearedState = awaitItem()
|
val clearedState = awaitItem()
|
||||||
assertThat(clearedState.loginAction).isEqualTo(Async.Uninitialized)
|
assertThat(clearedState.loginAction).isEqualTo(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ import io.element.android.features.login.impl.resolver.network.FakeWellknownRequ
|
||||||
import io.element.android.features.login.impl.resolver.network.WellKnown
|
import io.element.android.features.login.impl.resolver.network.WellKnown
|
||||||
import io.element.android.features.login.impl.resolver.network.WellKnownBaseConfig
|
import io.element.android.features.login.impl.resolver.network.WellKnownBaseConfig
|
||||||
import io.element.android.features.login.impl.resolver.network.WellKnownSlidingSyncConfig
|
import io.element.android.features.login.impl.resolver.network.WellKnownSlidingSyncConfig
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
|
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
|
||||||
import io.element.android.libraries.matrix.test.auth.FakeAuthenticationService
|
import io.element.android.libraries.matrix.test.auth.FakeAuthenticationService
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
|
|
@ -57,7 +57,7 @@ class SearchAccountProviderPresenterTest {
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.userInput).isEmpty()
|
assertThat(initialState.userInput).isEmpty()
|
||||||
assertThat(initialState.userInputResult).isEqualTo(Async.Uninitialized)
|
assertThat(initialState.userInputResult).isEqualTo(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,9 +79,9 @@ class SearchAccountProviderPresenterTest {
|
||||||
initialState.eventSink.invoke(SearchAccountProviderEvents.UserInput("test"))
|
initialState.eventSink.invoke(SearchAccountProviderEvents.UserInput("test"))
|
||||||
val withInputState = awaitItem()
|
val withInputState = awaitItem()
|
||||||
assertThat(withInputState.userInput).isEqualTo("test")
|
assertThat(withInputState.userInput).isEqualTo("test")
|
||||||
assertThat(initialState.userInputResult).isEqualTo(Async.Uninitialized)
|
assertThat(initialState.userInputResult).isEqualTo(AsyncData.Uninitialized)
|
||||||
assertThat(awaitItem().userInputResult).isInstanceOf(Async.Loading::class.java)
|
assertThat(awaitItem().userInputResult).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
assertThat(awaitItem().userInputResult).isEqualTo(Async.Uninitialized)
|
assertThat(awaitItem().userInputResult).isEqualTo(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,10 +103,10 @@ class SearchAccountProviderPresenterTest {
|
||||||
initialState.eventSink.invoke(SearchAccountProviderEvents.UserInput("https://test.org"))
|
initialState.eventSink.invoke(SearchAccountProviderEvents.UserInput("https://test.org"))
|
||||||
val withInputState = awaitItem()
|
val withInputState = awaitItem()
|
||||||
assertThat(withInputState.userInput).isEqualTo("https://test.org")
|
assertThat(withInputState.userInput).isEqualTo("https://test.org")
|
||||||
assertThat(initialState.userInputResult).isEqualTo(Async.Uninitialized)
|
assertThat(initialState.userInputResult).isEqualTo(AsyncData.Uninitialized)
|
||||||
assertThat(awaitItem().userInputResult).isInstanceOf(Async.Loading::class.java)
|
assertThat(awaitItem().userInputResult).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
assertThat(awaitItem().userInputResult).isEqualTo(
|
assertThat(awaitItem().userInputResult).isEqualTo(
|
||||||
Async.Success(
|
AsyncData.Success(
|
||||||
listOf(
|
listOf(
|
||||||
aHomeserverData(homeserverUrl = "https://test.org", isWellknownValid = false, supportSlidingSync = false)
|
aHomeserverData(homeserverUrl = "https://test.org", isWellknownValid = false, supportSlidingSync = false)
|
||||||
)
|
)
|
||||||
|
|
@ -138,10 +138,10 @@ class SearchAccountProviderPresenterTest {
|
||||||
initialState.eventSink.invoke(SearchAccountProviderEvents.UserInput("test"))
|
initialState.eventSink.invoke(SearchAccountProviderEvents.UserInput("test"))
|
||||||
val withInputState = awaitItem()
|
val withInputState = awaitItem()
|
||||||
assertThat(withInputState.userInput).isEqualTo("test")
|
assertThat(withInputState.userInput).isEqualTo("test")
|
||||||
assertThat(initialState.userInputResult).isEqualTo(Async.Uninitialized)
|
assertThat(initialState.userInputResult).isEqualTo(AsyncData.Uninitialized)
|
||||||
assertThat(awaitItem().userInputResult).isInstanceOf(Async.Loading::class.java)
|
assertThat(awaitItem().userInputResult).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
assertThat(awaitItem().userInputResult).isEqualTo(
|
assertThat(awaitItem().userInputResult).isEqualTo(
|
||||||
Async.Success(
|
AsyncData.Success(
|
||||||
listOf(
|
listOf(
|
||||||
aHomeserverData(homeserverUrl = "https://test.org", isWellknownValid = true, supportSlidingSync = false)
|
aHomeserverData(homeserverUrl = "https://test.org", isWellknownValid = true, supportSlidingSync = false)
|
||||||
)
|
)
|
||||||
|
|
@ -173,10 +173,10 @@ class SearchAccountProviderPresenterTest {
|
||||||
initialState.eventSink.invoke(SearchAccountProviderEvents.UserInput("test"))
|
initialState.eventSink.invoke(SearchAccountProviderEvents.UserInput("test"))
|
||||||
val withInputState = awaitItem()
|
val withInputState = awaitItem()
|
||||||
assertThat(withInputState.userInput).isEqualTo("test")
|
assertThat(withInputState.userInput).isEqualTo("test")
|
||||||
assertThat(initialState.userInputResult).isEqualTo(Async.Uninitialized)
|
assertThat(initialState.userInputResult).isEqualTo(AsyncData.Uninitialized)
|
||||||
assertThat(awaitItem().userInputResult).isInstanceOf(Async.Loading::class.java)
|
assertThat(awaitItem().userInputResult).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
assertThat(awaitItem().userInputResult).isEqualTo(
|
assertThat(awaitItem().userInputResult).isEqualTo(
|
||||||
Async.Success(
|
AsyncData.Success(
|
||||||
listOf(
|
listOf(
|
||||||
aHomeserverData(homeserverUrl = "https://test.io")
|
aHomeserverData(homeserverUrl = "https://test.io")
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.login.impl.DefaultLoginUserStory
|
import io.element.android.features.login.impl.DefaultLoginUserStory
|
||||||
import io.element.android.features.login.impl.screens.loginpassword.LoginFormState
|
import io.element.android.features.login.impl.screens.loginpassword.LoginFormState
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
import io.element.android.libraries.matrix.test.A_HOMESERVER
|
import io.element.android.libraries.matrix.test.A_HOMESERVER
|
||||||
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
|
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
|
||||||
|
|
@ -58,7 +58,7 @@ class WaitListPresenterTest {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.appName).isEqualTo("Application Name")
|
assertThat(initialState.appName).isEqualTo("Application Name")
|
||||||
assertThat(initialState.serverName).isEqualTo(A_HOMESERVER_URL)
|
assertThat(initialState.serverName).isEqualTo(A_HOMESERVER_URL)
|
||||||
assertThat(initialState.loginAction).isEqualTo(Async.Uninitialized)
|
assertThat(initialState.loginAction).isEqualTo(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,13 +83,13 @@ class WaitListPresenterTest {
|
||||||
expectNoEvents()
|
expectNoEvents()
|
||||||
initialState.eventSink.invoke(WaitListEvents.AttemptLogin)
|
initialState.eventSink.invoke(WaitListEvents.AttemptLogin)
|
||||||
val submitState = awaitItem()
|
val submitState = awaitItem()
|
||||||
assertThat(submitState.loginAction).isInstanceOf(Async.Loading::class.java)
|
assertThat(submitState.loginAction).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
val errorState = awaitItem()
|
val errorState = awaitItem()
|
||||||
assertThat(errorState.loginAction).isEqualTo(Async.Failure<SessionId>(A_THROWABLE))
|
assertThat(errorState.loginAction).isEqualTo(AsyncData.Failure<SessionId>(A_THROWABLE))
|
||||||
// Assert the error can be cleared
|
// Assert the error can be cleared
|
||||||
errorState.eventSink(WaitListEvents.ClearError)
|
errorState.eventSink(WaitListEvents.ClearError)
|
||||||
val clearedState = awaitItem()
|
val clearedState = awaitItem()
|
||||||
assertThat(clearedState.loginAction).isEqualTo(Async.Uninitialized)
|
assertThat(clearedState.loginAction).isEqualTo(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,9 +113,9 @@ class WaitListPresenterTest {
|
||||||
expectNoEvents()
|
expectNoEvents()
|
||||||
initialState.eventSink.invoke(WaitListEvents.AttemptLogin)
|
initialState.eventSink.invoke(WaitListEvents.AttemptLogin)
|
||||||
val submitState = awaitItem()
|
val submitState = awaitItem()
|
||||||
assertThat(submitState.loginAction).isInstanceOf(Async.Loading::class.java)
|
assertThat(submitState.loginAction).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
val successState = awaitItem()
|
val successState = awaitItem()
|
||||||
assertThat(successState.loginAction).isEqualTo(Async.Success(A_USER_ID))
|
assertThat(successState.loginAction).isEqualTo(AsyncData.Success(A_USER_ID))
|
||||||
assertThat(loginUserStory.loginFlowIsDone.value).isFalse()
|
assertThat(loginUserStory.loginFlowIsDone.value).isFalse()
|
||||||
successState.eventSink.invoke(WaitListEvents.Continue)
|
successState.eventSink.invoke(WaitListEvents.Continue)
|
||||||
assertThat(loginUserStory.loginFlowIsDone.value).isTrue()
|
assertThat(loginUserStory.loginFlowIsDone.value).isTrue()
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||||
|
|
@ -75,8 +75,8 @@ class LogoutPresenter @Inject constructor(
|
||||||
val backupState by encryptionService.backupStateStateFlow.collectAsState()
|
val backupState by encryptionService.backupStateStateFlow.collectAsState()
|
||||||
val recoveryState by encryptionService.recoveryStateStateFlow.collectAsState()
|
val recoveryState by encryptionService.recoveryStateStateFlow.collectAsState()
|
||||||
|
|
||||||
val doesBackupExistOnServerAction: MutableState<Async<Boolean>> = remember {
|
val doesBackupExistOnServerAction: MutableState<AsyncData<Boolean>> = remember {
|
||||||
mutableStateOf(Async.Uninitialized)
|
mutableStateOf(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(backupState) {
|
LaunchedEffect(backupState) {
|
||||||
|
|
@ -111,7 +111,7 @@ class LogoutPresenter @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.getKeyBackupStatus(action: MutableState<Async<Boolean>>) = launch {
|
private fun CoroutineScope.getKeyBackupStatus(action: MutableState<AsyncData<Boolean>>) = launch {
|
||||||
suspend {
|
suspend {
|
||||||
encryptionService.doesBackupExistOnServer().getOrThrow()
|
encryptionService.doesBackupExistOnServer().getOrThrow()
|
||||||
}.runCatchingUpdatingState(action)
|
}.runCatchingUpdatingState(action)
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ import io.element.android.features.networkmonitor.api.NetworkMonitor
|
||||||
import io.element.android.features.networkmonitor.api.NetworkStatus
|
import io.element.android.features.networkmonitor.api.NetworkStatus
|
||||||
import io.element.android.features.preferences.api.store.PreferencesStore
|
import io.element.android.features.preferences.api.store.PreferencesStore
|
||||||
import io.element.android.libraries.androidutils.clipboard.ClipboardHelper
|
import io.element.android.libraries.androidutils.clipboard.ClipboardHelper
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.libraries.core.meta.BuildMeta
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
|
|
@ -141,11 +141,11 @@ class MessagesPresenter @AssistedInject constructor(
|
||||||
val userHasPermissionToSendMessage by room.canSendMessageAsState(type = MessageEventType.ROOM_MESSAGE, updateKey = syncUpdateFlow.value)
|
val userHasPermissionToSendMessage by room.canSendMessageAsState(type = MessageEventType.ROOM_MESSAGE, updateKey = syncUpdateFlow.value)
|
||||||
val userHasPermissionToRedact by room.canRedactAsState(updateKey = syncUpdateFlow.value)
|
val userHasPermissionToRedact by room.canRedactAsState(updateKey = syncUpdateFlow.value)
|
||||||
val userHasPermissionToSendReaction by room.canSendMessageAsState(type = MessageEventType.REACTION_SENT, updateKey = syncUpdateFlow.value)
|
val userHasPermissionToSendReaction by room.canSendMessageAsState(type = MessageEventType.REACTION_SENT, updateKey = syncUpdateFlow.value)
|
||||||
val roomName: Async<String> by remember {
|
val roomName: AsyncData<String> by remember {
|
||||||
derivedStateOf { roomInfo?.name?.let { Async.Success(it) } ?: Async.Uninitialized }
|
derivedStateOf { roomInfo?.name?.let { AsyncData.Success(it) } ?: AsyncData.Uninitialized }
|
||||||
}
|
}
|
||||||
val roomAvatar: Async<AvatarData> by remember {
|
val roomAvatar: AsyncData<AvatarData> by remember {
|
||||||
derivedStateOf { roomInfo?.avatarData()?.let { Async.Success(it) } ?: Async.Uninitialized }
|
derivedStateOf { roomInfo?.avatarData()?.let { AsyncData.Success(it) } ?: AsyncData.Uninitialized }
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasDismissedInviteDialog by rememberSaveable {
|
var hasDismissedInviteDialog by rememberSaveable {
|
||||||
|
|
@ -162,7 +162,7 @@ class MessagesPresenter @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val inviteProgress = remember { mutableStateOf<Async<Unit>>(Async.Uninitialized) }
|
val inviteProgress = remember { mutableStateOf<AsyncData<Unit>>(AsyncData.Uninitialized) }
|
||||||
var showReinvitePrompt by remember { mutableStateOf(false) }
|
var showReinvitePrompt by remember { mutableStateOf(false) }
|
||||||
LaunchedEffect(hasDismissedInviteDialog, composerState.hasFocus, syncUpdateFlow) {
|
LaunchedEffect(hasDismissedInviteDialog, composerState.hasFocus, syncUpdateFlow) {
|
||||||
withContext(dispatchers.io) {
|
withContext(dispatchers.io) {
|
||||||
|
|
@ -279,8 +279,8 @@ class MessagesPresenter @AssistedInject constructor(
|
||||||
.onFailure { Timber.e(it) }
|
.onFailure { Timber.e(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.reinviteOtherUser(inviteProgress: MutableState<Async<Unit>>) = launch(dispatchers.io) {
|
private fun CoroutineScope.reinviteOtherUser(inviteProgress: MutableState<AsyncData<Unit>>) = launch(dispatchers.io) {
|
||||||
inviteProgress.value = Async.Loading()
|
inviteProgress.value = AsyncData.Loading()
|
||||||
runCatching {
|
runCatching {
|
||||||
room.updateMembers()
|
room.updateMembers()
|
||||||
|
|
||||||
|
|
@ -296,10 +296,10 @@ class MessagesPresenter @AssistedInject constructor(
|
||||||
}.getOrThrow()
|
}.getOrThrow()
|
||||||
}.fold(
|
}.fold(
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
inviteProgress.value = Async.Success(Unit)
|
inviteProgress.value = AsyncData.Success(Unit)
|
||||||
},
|
},
|
||||||
onFailure = {
|
onFailure = {
|
||||||
inviteProgress.value = Async.Failure(it)
|
inviteProgress.value = AsyncData.Failure(it)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import io.element.android.features.messages.impl.timeline.components.reactionsum
|
||||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState
|
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState
|
||||||
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuState
|
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuState
|
||||||
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState
|
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
@ -33,8 +33,8 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
@Immutable
|
@Immutable
|
||||||
data class MessagesState(
|
data class MessagesState(
|
||||||
val roomId: RoomId,
|
val roomId: RoomId,
|
||||||
val roomName: Async<String>,
|
val roomName: AsyncData<String>,
|
||||||
val roomAvatar: Async<AvatarData>,
|
val roomAvatar: AsyncData<AvatarData>,
|
||||||
val userHasPermissionToSendMessage: Boolean,
|
val userHasPermissionToSendMessage: Boolean,
|
||||||
val userHasPermissionToRedact: Boolean,
|
val userHasPermissionToRedact: Boolean,
|
||||||
val userHasPermissionToSendReaction: Boolean,
|
val userHasPermissionToSendReaction: Boolean,
|
||||||
|
|
@ -48,7 +48,7 @@ data class MessagesState(
|
||||||
val readReceiptBottomSheetState: ReadReceiptBottomSheetState,
|
val readReceiptBottomSheetState: ReadReceiptBottomSheetState,
|
||||||
val hasNetworkConnection: Boolean,
|
val hasNetworkConnection: Boolean,
|
||||||
val snackbarMessage: SnackbarMessage?,
|
val snackbarMessage: SnackbarMessage?,
|
||||||
val inviteProgress: Async<Unit>,
|
val inviteProgress: AsyncData<Unit>,
|
||||||
val showReinvitePrompt: Boolean,
|
val showReinvitePrompt: Boolean,
|
||||||
val enableTextFormatting: Boolean,
|
val enableTextFormatting: Boolean,
|
||||||
val enableVoiceMessages: Boolean,
|
val enableVoiceMessages: Boolean,
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ import io.element.android.features.messages.impl.timeline.components.retrysendme
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
|
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
|
||||||
import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessageComposerState
|
import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessageComposerState
|
||||||
import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessagePreviewState
|
import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessagePreviewState
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
@ -47,8 +47,8 @@ open class MessagesStateProvider : PreviewParameterProvider<MessagesState> {
|
||||||
aMessagesState().copy(userHasPermissionToSendMessage = false),
|
aMessagesState().copy(userHasPermissionToSendMessage = false),
|
||||||
aMessagesState().copy(showReinvitePrompt = true),
|
aMessagesState().copy(showReinvitePrompt = true),
|
||||||
aMessagesState().copy(
|
aMessagesState().copy(
|
||||||
roomName = Async.Uninitialized,
|
roomName = AsyncData.Uninitialized,
|
||||||
roomAvatar = Async.Uninitialized,
|
roomAvatar = AsyncData.Uninitialized,
|
||||||
),
|
),
|
||||||
aMessagesState().copy(composerState = aMessageComposerState().copy(showTextFormatting = true)),
|
aMessagesState().copy(composerState = aMessageComposerState().copy(showTextFormatting = true)),
|
||||||
aMessagesState().copy(
|
aMessagesState().copy(
|
||||||
|
|
@ -83,8 +83,8 @@ open class MessagesStateProvider : PreviewParameterProvider<MessagesState> {
|
||||||
|
|
||||||
fun aMessagesState() = MessagesState(
|
fun aMessagesState() = MessagesState(
|
||||||
roomId = RoomId("!id:domain"),
|
roomId = RoomId("!id:domain"),
|
||||||
roomName = Async.Success("Room name"),
|
roomName = AsyncData.Success("Room name"),
|
||||||
roomAvatar = Async.Success(AvatarData("!id:domain", "Room name", size = AvatarSize.TimelineRoom)),
|
roomAvatar = AsyncData.Success(AvatarData("!id:domain", "Room name", size = AvatarSize.TimelineRoom)),
|
||||||
userHasPermissionToSendMessage = true,
|
userHasPermissionToSendMessage = true,
|
||||||
userHasPermissionToRedact = false,
|
userHasPermissionToRedact = false,
|
||||||
userHasPermissionToSendReaction = true,
|
userHasPermissionToSendReaction = true,
|
||||||
|
|
@ -117,7 +117,7 @@ fun aMessagesState() = MessagesState(
|
||||||
),
|
),
|
||||||
hasNetworkConnection = true,
|
hasNetworkConnection = true,
|
||||||
snackbarMessage = null,
|
snackbarMessage = null,
|
||||||
inviteProgress = Async.Uninitialized,
|
inviteProgress = AsyncData.Uninitialized,
|
||||||
showReinvitePrompt = false,
|
showReinvitePrompt = false,
|
||||||
enableTextFormatting = true,
|
enableTextFormatting = true,
|
||||||
enableVoiceMessages = true,
|
enableVoiceMessages = true,
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import androidx.compose.runtime.remember
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
@ -48,7 +48,7 @@ class ForwardMessagesPresenter @AssistedInject constructor(
|
||||||
fun create(eventId: String): ForwardMessagesPresenter
|
fun create(eventId: String): ForwardMessagesPresenter
|
||||||
}
|
}
|
||||||
|
|
||||||
private val forwardingActionState: MutableState<Async<ImmutableList<RoomId>>> = mutableStateOf(Async.Uninitialized)
|
private val forwardingActionState: MutableState<AsyncData<ImmutableList<RoomId>>> = mutableStateOf(AsyncData.Uninitialized)
|
||||||
|
|
||||||
fun onRoomSelected(roomIds: List<RoomId>) {
|
fun onRoomSelected(roomIds: List<RoomId>) {
|
||||||
matrixCoroutineScope.forwardEvent(eventId, roomIds.toPersistentList(), forwardingActionState)
|
matrixCoroutineScope.forwardEvent(eventId, roomIds.toPersistentList(), forwardingActionState)
|
||||||
|
|
@ -62,13 +62,13 @@ class ForwardMessagesPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
fun handleEvents(event: ForwardMessagesEvents) {
|
fun handleEvents(event: ForwardMessagesEvents) {
|
||||||
when (event) {
|
when (event) {
|
||||||
ForwardMessagesEvents.ClearError -> forwardingActionState.value = Async.Uninitialized
|
ForwardMessagesEvents.ClearError -> forwardingActionState.value = AsyncData.Uninitialized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ForwardMessagesState(
|
return ForwardMessagesState(
|
||||||
isForwarding = forwardingActionState.value.isLoading(),
|
isForwarding = forwardingActionState.value.isLoading(),
|
||||||
error = (forwardingActionState.value as? Async.Failure)?.error,
|
error = (forwardingActionState.value as? AsyncData.Failure)?.error,
|
||||||
forwardingSucceeded = forwardingSucceeded,
|
forwardingSucceeded = forwardingSucceeded,
|
||||||
eventSink = { handleEvents(it) }
|
eventSink = { handleEvents(it) }
|
||||||
)
|
)
|
||||||
|
|
@ -77,12 +77,12 @@ class ForwardMessagesPresenter @AssistedInject constructor(
|
||||||
private fun CoroutineScope.forwardEvent(
|
private fun CoroutineScope.forwardEvent(
|
||||||
eventId: EventId,
|
eventId: EventId,
|
||||||
roomIds: ImmutableList<RoomId>,
|
roomIds: ImmutableList<RoomId>,
|
||||||
isForwardMessagesState: MutableState<Async<ImmutableList<RoomId>>>,
|
isForwardMessagesState: MutableState<AsyncData<ImmutableList<RoomId>>>,
|
||||||
) = launch {
|
) = launch {
|
||||||
isForwardMessagesState.value = Async.Loading()
|
isForwardMessagesState.value = AsyncData.Loading()
|
||||||
room.forwardEvent(eventId, roomIds).fold(
|
room.forwardEvent(eventId, roomIds).fold(
|
||||||
{ isForwardMessagesState.value = Async.Success(roomIds) },
|
{ isForwardMessagesState.value = AsyncData.Success(roomIds) },
|
||||||
{ isForwardMessagesState.value = Async.Failure(it) }
|
{ isForwardMessagesState.value = AsyncData.Failure(it) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ import androidx.compose.runtime.setValue
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.runUpdatingState
|
import io.element.android.libraries.architecture.runUpdatingState
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||||
|
|
@ -60,14 +60,14 @@ class ReportMessagePresenter @AssistedInject constructor(
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
var reason by rememberSaveable { mutableStateOf("") }
|
var reason by rememberSaveable { mutableStateOf("") }
|
||||||
var blockUser by rememberSaveable { mutableStateOf(false) }
|
var blockUser by rememberSaveable { mutableStateOf(false) }
|
||||||
var result: MutableState<Async<Unit>> = remember { mutableStateOf(Async.Uninitialized) }
|
var result: MutableState<AsyncData<Unit>> = remember { mutableStateOf(AsyncData.Uninitialized) }
|
||||||
|
|
||||||
fun handleEvents(event: ReportMessageEvents) {
|
fun handleEvents(event: ReportMessageEvents) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is ReportMessageEvents.UpdateReason -> reason = event.reason
|
is ReportMessageEvents.UpdateReason -> reason = event.reason
|
||||||
ReportMessageEvents.ToggleBlockUser -> blockUser = !blockUser
|
ReportMessageEvents.ToggleBlockUser -> blockUser = !blockUser
|
||||||
ReportMessageEvents.Report -> coroutineScope.report(inputs.eventId, inputs.senderId, reason, blockUser, result)
|
ReportMessageEvents.Report -> coroutineScope.report(inputs.eventId, inputs.senderId, reason, blockUser, result)
|
||||||
ReportMessageEvents.ClearError -> result.value = Async.Uninitialized
|
ReportMessageEvents.ClearError -> result.value = AsyncData.Uninitialized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,7 +84,7 @@ class ReportMessagePresenter @AssistedInject constructor(
|
||||||
userId: UserId,
|
userId: UserId,
|
||||||
reason: String,
|
reason: String,
|
||||||
blockUser: Boolean,
|
blockUser: Boolean,
|
||||||
result: MutableState<Async<Unit>>,
|
result: MutableState<AsyncData<Unit>>,
|
||||||
) = launch {
|
) = launch {
|
||||||
result.runUpdatingState {
|
result.runUpdatingState {
|
||||||
val userIdToBlock = userId.takeIf { blockUser }
|
val userIdToBlock = userId.takeIf { blockUser }
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,11 @@
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.report
|
package io.element.android.features.messages.impl.report
|
||||||
|
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
|
|
||||||
data class ReportMessageState(
|
data class ReportMessageState(
|
||||||
val reason: String,
|
val reason: String,
|
||||||
val blockUser: Boolean,
|
val blockUser: Boolean,
|
||||||
val result: Async<Unit>,
|
val result: AsyncData<Unit>,
|
||||||
val eventSink: (ReportMessageEvents) -> Unit
|
val eventSink: (ReportMessageEvents) -> Unit
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
package io.element.android.features.messages.impl.report
|
package io.element.android.features.messages.impl.report
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
|
|
||||||
open class ReportMessageStateProvider : PreviewParameterProvider<ReportMessageState> {
|
open class ReportMessageStateProvider : PreviewParameterProvider<ReportMessageState> {
|
||||||
override val values: Sequence<ReportMessageState>
|
override val values: Sequence<ReportMessageState>
|
||||||
|
|
@ -25,9 +25,9 @@ open class ReportMessageStateProvider : PreviewParameterProvider<ReportMessageSt
|
||||||
aReportMessageState(),
|
aReportMessageState(),
|
||||||
aReportMessageState(reason = "This user is making the chat very toxic."),
|
aReportMessageState(reason = "This user is making the chat very toxic."),
|
||||||
aReportMessageState(reason = "This user is making the chat very toxic.", blockUser = true),
|
aReportMessageState(reason = "This user is making the chat very toxic.", blockUser = true),
|
||||||
aReportMessageState(reason = "This user is making the chat very toxic.", blockUser = true, result = Async.Loading()),
|
aReportMessageState(reason = "This user is making the chat very toxic.", blockUser = true, result = AsyncData.Loading()),
|
||||||
aReportMessageState(reason = "This user is making the chat very toxic.", blockUser = true, result = Async.Failure(Throwable("error"))),
|
aReportMessageState(reason = "This user is making the chat very toxic.", blockUser = true, result = AsyncData.Failure(Throwable("error"))),
|
||||||
aReportMessageState(reason = "This user is making the chat very toxic.", blockUser = true, result = Async.Success(Unit)),
|
aReportMessageState(reason = "This user is making the chat very toxic.", blockUser = true, result = AsyncData.Success(Unit)),
|
||||||
// Add other states here
|
// Add other states here
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -35,7 +35,7 @@ open class ReportMessageStateProvider : PreviewParameterProvider<ReportMessageSt
|
||||||
fun aReportMessageState(
|
fun aReportMessageState(
|
||||||
reason: String = "",
|
reason: String = "",
|
||||||
blockUser: Boolean = false,
|
blockUser: Boolean = false,
|
||||||
result: Async<Unit> = Async.Uninitialized,
|
result: AsyncData<Unit> = AsyncData.Uninitialized,
|
||||||
) = ReportMessageState(
|
) = ReportMessageState(
|
||||||
reason = reason,
|
reason = reason,
|
||||||
blockUser = blockUser,
|
blockUser = blockUser,
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.features.messages.impl.R
|
import io.element.android.features.messages.impl.R
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.designsystem.components.async.AsyncView
|
import io.element.android.libraries.designsystem.components.async.AsyncView
|
||||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
|
|
@ -62,7 +62,7 @@ fun ReportMessageView(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val focusManager = LocalFocusManager.current
|
val focusManager = LocalFocusManager.current
|
||||||
val isSending = state.result is Async.Loading
|
val isSending = state.result is AsyncData.Loading
|
||||||
AsyncView(
|
AsyncView(
|
||||||
async = state.result,
|
async = state.result,
|
||||||
showProgressDialog = false,
|
showProgressDialog = false,
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ import io.element.android.features.messages.impl.timeline.di.TimelineItemEventCo
|
||||||
import io.element.android.features.messages.impl.timeline.di.TimelineItemPresenterFactory
|
import io.element.android.features.messages.impl.timeline.di.TimelineItemPresenterFactory
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
|
||||||
import io.element.android.features.messages.impl.voicemessages.VoiceMessageException
|
import io.element.android.features.messages.impl.voicemessages.VoiceMessageException
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.runUpdatingState
|
import io.element.android.libraries.architecture.runUpdatingState
|
||||||
import io.element.android.libraries.di.RoomScope
|
import io.element.android.libraries.di.RoomScope
|
||||||
|
|
@ -71,7 +71,7 @@ class VoiceMessagePresenter @AssistedInject constructor(
|
||||||
body = content.body,
|
body = content.body,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val play = mutableStateOf<Async<Unit>>(Async.Uninitialized)
|
private val play = mutableStateOf<AsyncData<Unit>>(AsyncData.Uninitialized)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): VoiceMessageState {
|
override fun present(): VoiceMessageState {
|
||||||
|
|
@ -91,8 +91,8 @@ class VoiceMessagePresenter @AssistedInject constructor(
|
||||||
when {
|
when {
|
||||||
content.eventId == null -> VoiceMessageState.Button.Disabled
|
content.eventId == null -> VoiceMessageState.Button.Disabled
|
||||||
playerState.isPlaying -> VoiceMessageState.Button.Pause
|
playerState.isPlaying -> VoiceMessageState.Button.Pause
|
||||||
play.value is Async.Loading -> VoiceMessageState.Button.Downloading
|
play.value is AsyncData.Loading -> VoiceMessageState.Button.Downloading
|
||||||
play.value is Async.Failure -> VoiceMessageState.Button.Retry
|
play.value is AsyncData.Failure -> VoiceMessageState.Button.Retry
|
||||||
else -> VoiceMessageState.Button.Play
|
else -> VoiceMessageState.Button.Play
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ import io.element.android.features.networkmonitor.test.FakeNetworkMonitor
|
||||||
import io.element.android.features.poll.test.actions.FakeEndPollAction
|
import io.element.android.features.poll.test.actions.FakeEndPollAction
|
||||||
import io.element.android.features.poll.test.actions.FakeSendPollResponseAction
|
import io.element.android.features.poll.test.actions.FakeSendPollResponseAction
|
||||||
import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper
|
import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
|
|
@ -117,14 +117,14 @@ class MessagesPresenterTest {
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = consumeItemsUntilTimeout().last()
|
val initialState = consumeItemsUntilTimeout().last()
|
||||||
assertThat(initialState.roomId).isEqualTo(A_ROOM_ID)
|
assertThat(initialState.roomId).isEqualTo(A_ROOM_ID)
|
||||||
assertThat(initialState.roomName).isEqualTo(Async.Success(""))
|
assertThat(initialState.roomName).isEqualTo(AsyncData.Success(""))
|
||||||
assertThat(initialState.roomAvatar)
|
assertThat(initialState.roomAvatar)
|
||||||
.isEqualTo(Async.Success(AvatarData(id = A_ROOM_ID.value, name = "", url = AN_AVATAR_URL, size = AvatarSize.TimelineRoom)))
|
.isEqualTo(AsyncData.Success(AvatarData(id = A_ROOM_ID.value, name = "", url = AN_AVATAR_URL, size = AvatarSize.TimelineRoom)))
|
||||||
assertThat(initialState.userHasPermissionToSendMessage).isTrue()
|
assertThat(initialState.userHasPermissionToSendMessage).isTrue()
|
||||||
assertThat(initialState.userHasPermissionToRedact).isFalse()
|
assertThat(initialState.userHasPermissionToRedact).isFalse()
|
||||||
assertThat(initialState.hasNetworkConnection).isTrue()
|
assertThat(initialState.hasNetworkConnection).isTrue()
|
||||||
assertThat(initialState.snackbarMessage).isNull()
|
assertThat(initialState.snackbarMessage).isNull()
|
||||||
assertThat(initialState.inviteProgress).isEqualTo(Async.Uninitialized)
|
assertThat(initialState.inviteProgress).isEqualTo(AsyncData.Uninitialized)
|
||||||
assertThat(initialState.showReinvitePrompt).isFalse()
|
assertThat(initialState.showReinvitePrompt).isFalse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import app.cash.molecule.RecompositionMode
|
||||||
import app.cash.molecule.moleculeFlow
|
import app.cash.molecule.moleculeFlow
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||||
|
|
@ -45,7 +45,7 @@ class ReportMessagePresenterTests {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.reason).isEmpty()
|
assertThat(initialState.reason).isEmpty()
|
||||||
assertThat(initialState.blockUser).isFalse()
|
assertThat(initialState.blockUser).isFalse()
|
||||||
assertThat(initialState.result).isInstanceOf(Async.Uninitialized::class.java)
|
assertThat(initialState.result).isInstanceOf(AsyncData.Uninitialized::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,8 +91,8 @@ class ReportMessagePresenterTests {
|
||||||
initialState.eventSink(ReportMessageEvents.ToggleBlockUser)
|
initialState.eventSink(ReportMessageEvents.ToggleBlockUser)
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
initialState.eventSink(ReportMessageEvents.Report)
|
initialState.eventSink(ReportMessageEvents.Report)
|
||||||
assertThat(awaitItem().result).isInstanceOf(Async.Loading::class.java)
|
assertThat(awaitItem().result).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
assertThat(awaitItem().result).isInstanceOf(Async.Success::class.java)
|
assertThat(awaitItem().result).isInstanceOf(AsyncData.Success::class.java)
|
||||||
assertThat(room.reportedContentCount).isEqualTo(1)
|
assertThat(room.reportedContentCount).isEqualTo(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -106,8 +106,8 @@ class ReportMessagePresenterTests {
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink(ReportMessageEvents.Report)
|
initialState.eventSink(ReportMessageEvents.Report)
|
||||||
assertThat(awaitItem().result).isInstanceOf(Async.Loading::class.java)
|
assertThat(awaitItem().result).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
assertThat(awaitItem().result).isInstanceOf(Async.Success::class.java)
|
assertThat(awaitItem().result).isInstanceOf(AsyncData.Success::class.java)
|
||||||
assertThat(room.reportedContentCount).isEqualTo(1)
|
assertThat(room.reportedContentCount).isEqualTo(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -123,13 +123,13 @@ class ReportMessagePresenterTests {
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink(ReportMessageEvents.Report)
|
initialState.eventSink(ReportMessageEvents.Report)
|
||||||
assertThat(awaitItem().result).isInstanceOf(Async.Loading::class.java)
|
assertThat(awaitItem().result).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
val resultState = awaitItem()
|
val resultState = awaitItem()
|
||||||
assertThat(resultState.result).isInstanceOf(Async.Failure::class.java)
|
assertThat(resultState.result).isInstanceOf(AsyncData.Failure::class.java)
|
||||||
assertThat(room.reportedContentCount).isEqualTo(1)
|
assertThat(room.reportedContentCount).isEqualTo(1)
|
||||||
|
|
||||||
resultState.eventSink(ReportMessageEvents.ClearError)
|
resultState.eventSink(ReportMessageEvents.ClearError)
|
||||||
assertThat(awaitItem().result).isInstanceOf(Async.Uninitialized::class.java)
|
assertThat(awaitItem().result).isInstanceOf(AsyncData.Uninitialized::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ import io.element.android.features.preferences.api.store.PreferencesStore
|
||||||
import io.element.android.features.preferences.impl.tasks.ClearCacheUseCase
|
import io.element.android.features.preferences.impl.tasks.ClearCacheUseCase
|
||||||
import io.element.android.features.preferences.impl.tasks.ComputeCacheSizeUseCase
|
import io.element.android.features.preferences.impl.tasks.ComputeCacheSizeUseCase
|
||||||
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesPresenter
|
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesPresenter
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||||
import io.element.android.libraries.core.bool.orFalse
|
import io.element.android.libraries.core.bool.orFalse
|
||||||
|
|
@ -65,10 +65,10 @@ class DeveloperSettingsPresenter @Inject constructor(
|
||||||
mutableStateMapOf<String, Boolean>()
|
mutableStateMapOf<String, Boolean>()
|
||||||
}
|
}
|
||||||
val cacheSize = remember {
|
val cacheSize = remember {
|
||||||
mutableStateOf<Async<String>>(Async.Uninitialized)
|
mutableStateOf<AsyncData<String>>(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
val clearCacheAction = remember {
|
val clearCacheAction = remember {
|
||||||
mutableStateOf<Async<Unit>>(Async.Uninitialized)
|
mutableStateOf<AsyncData<Unit>>(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
val customElementCallBaseUrl by preferencesStore
|
val customElementCallBaseUrl by preferencesStore
|
||||||
.getCustomElementCallBaseUrlFlow()
|
.getCustomElementCallBaseUrlFlow()
|
||||||
|
|
@ -154,13 +154,13 @@ class DeveloperSettingsPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.computeCacheSize(cacheSize: MutableState<Async<String>>) = launch {
|
private fun CoroutineScope.computeCacheSize(cacheSize: MutableState<AsyncData<String>>) = launch {
|
||||||
suspend {
|
suspend {
|
||||||
computeCacheSizeUseCase()
|
computeCacheSizeUseCase()
|
||||||
}.runCatchingUpdatingState(cacheSize)
|
}.runCatchingUpdatingState(cacheSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.clearCache(clearCacheAction: MutableState<Async<Unit>>) = launch {
|
private fun CoroutineScope.clearCache(clearCacheAction: MutableState<AsyncData<Unit>>) = launch {
|
||||||
suspend {
|
suspend {
|
||||||
clearCacheUseCase()
|
clearCacheUseCase()
|
||||||
}.runCatchingUpdatingState(clearCacheAction)
|
}.runCatchingUpdatingState(clearCacheAction)
|
||||||
|
|
|
||||||
|
|
@ -17,15 +17,15 @@
|
||||||
package io.element.android.features.preferences.impl.developer
|
package io.element.android.features.preferences.impl.developer
|
||||||
|
|
||||||
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesState
|
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesState
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.featureflag.ui.model.FeatureUiModel
|
import io.element.android.libraries.featureflag.ui.model.FeatureUiModel
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
data class DeveloperSettingsState(
|
data class DeveloperSettingsState(
|
||||||
val features: ImmutableList<FeatureUiModel>,
|
val features: ImmutableList<FeatureUiModel>,
|
||||||
val cacheSize: Async<String>,
|
val cacheSize: AsyncData<String>,
|
||||||
val rageshakeState: RageshakePreferencesState,
|
val rageshakeState: RageshakePreferencesState,
|
||||||
val clearCacheAction: Async<Unit>,
|
val clearCacheAction: AsyncData<Unit>,
|
||||||
val customElementCallBaseUrlState: CustomElementCallBaseUrlState,
|
val customElementCallBaseUrlState: CustomElementCallBaseUrlState,
|
||||||
val eventSink: (DeveloperSettingsEvents) -> Unit
|
val eventSink: (DeveloperSettingsEvents) -> Unit
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,14 @@ package io.element.android.features.preferences.impl.developer
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.features.rageshake.api.preferences.aRageshakePreferencesState
|
import io.element.android.features.rageshake.api.preferences.aRageshakePreferencesState
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.featureflag.ui.model.aFeatureUiModelList
|
import io.element.android.libraries.featureflag.ui.model.aFeatureUiModelList
|
||||||
|
|
||||||
open class DeveloperSettingsStateProvider : PreviewParameterProvider<DeveloperSettingsState> {
|
open class DeveloperSettingsStateProvider : PreviewParameterProvider<DeveloperSettingsState> {
|
||||||
override val values: Sequence<DeveloperSettingsState>
|
override val values: Sequence<DeveloperSettingsState>
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
aDeveloperSettingsState(),
|
aDeveloperSettingsState(),
|
||||||
aDeveloperSettingsState().copy(clearCacheAction = Async.Loading()),
|
aDeveloperSettingsState().copy(clearCacheAction = AsyncData.Loading()),
|
||||||
aDeveloperSettingsState().copy(
|
aDeveloperSettingsState().copy(
|
||||||
customElementCallBaseUrlState = CustomElementCallBaseUrlState(
|
customElementCallBaseUrlState = CustomElementCallBaseUrlState(
|
||||||
baseUrl = "https://call.element.ahoy",
|
baseUrl = "https://call.element.ahoy",
|
||||||
|
|
@ -39,8 +39,8 @@ open class DeveloperSettingsStateProvider : PreviewParameterProvider<DeveloperSe
|
||||||
fun aDeveloperSettingsState() = DeveloperSettingsState(
|
fun aDeveloperSettingsState() = DeveloperSettingsState(
|
||||||
features = aFeatureUiModelList(),
|
features = aFeatureUiModelList(),
|
||||||
rageshakeState = aRageshakePreferencesState(),
|
rageshakeState = aRageshakePreferencesState(),
|
||||||
cacheSize = Async.Success("1.2 MB"),
|
cacheSize = AsyncData.Success("1.2 MB"),
|
||||||
clearCacheAction = Async.Uninitialized,
|
clearCacheAction = AsyncData.Uninitialized,
|
||||||
customElementCallBaseUrlState = CustomElementCallBaseUrlState(baseUrl = null, defaultUrl = "https://call.element.io", validator = { true }),
|
customElementCallBaseUrlState = CustomElementCallBaseUrlState(baseUrl = null, defaultUrl = "https://call.element.io", validator = { true }),
|
||||||
eventSink = {}
|
eventSink = {}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
|
|
@ -52,7 +52,7 @@ class NotificationSettingsPresenter @Inject constructor(
|
||||||
val systemNotificationsEnabled: MutableState<Boolean> = remember {
|
val systemNotificationsEnabled: MutableState<Boolean> = remember {
|
||||||
mutableStateOf(systemNotificationsEnabledProvider.notificationsEnabled())
|
mutableStateOf(systemNotificationsEnabledProvider.notificationsEnabled())
|
||||||
}
|
}
|
||||||
val changeNotificationSettingAction: MutableState<Async<Unit>> = remember { mutableStateOf(Async.Uninitialized) }
|
val changeNotificationSettingAction: MutableState<AsyncData<Unit>> = remember { mutableStateOf(AsyncData.Uninitialized) }
|
||||||
|
|
||||||
val localCoroutineScope = rememberCoroutineScope()
|
val localCoroutineScope = rememberCoroutineScope()
|
||||||
val appNotificationsEnabled = userPushStore
|
val appNotificationsEnabled = userPushStore
|
||||||
|
|
@ -87,7 +87,7 @@ class NotificationSettingsPresenter @Inject constructor(
|
||||||
NotificationSettingsEvents.RefreshSystemNotificationsEnabled -> {
|
NotificationSettingsEvents.RefreshSystemNotificationsEnabled -> {
|
||||||
systemNotificationsEnabled.value = systemNotificationsEnabledProvider.notificationsEnabled()
|
systemNotificationsEnabled.value = systemNotificationsEnabledProvider.notificationsEnabled()
|
||||||
}
|
}
|
||||||
NotificationSettingsEvents.ClearNotificationChangeError -> changeNotificationSettingAction.value = Async.Uninitialized
|
NotificationSettingsEvents.ClearNotificationChangeError -> changeNotificationSettingAction.value = AsyncData.Uninitialized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -168,19 +168,19 @@ class NotificationSettingsPresenter @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.setAtRoomNotificationsEnabled(enabled: Boolean, action: MutableState<Async<Unit>>) = launch {
|
private fun CoroutineScope.setAtRoomNotificationsEnabled(enabled: Boolean, action: MutableState<AsyncData<Unit>>) = launch {
|
||||||
suspend {
|
suspend {
|
||||||
notificationSettingsService.setRoomMentionEnabled(enabled).getOrThrow()
|
notificationSettingsService.setRoomMentionEnabled(enabled).getOrThrow()
|
||||||
}.runCatchingUpdatingState(action)
|
}.runCatchingUpdatingState(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.setCallNotificationsEnabled(enabled: Boolean, action: MutableState<Async<Unit>>) = launch {
|
private fun CoroutineScope.setCallNotificationsEnabled(enabled: Boolean, action: MutableState<AsyncData<Unit>>) = launch {
|
||||||
suspend {
|
suspend {
|
||||||
notificationSettingsService.setCallEnabled(enabled).getOrThrow()
|
notificationSettingsService.setCallEnabled(enabled).getOrThrow()
|
||||||
}.runCatchingUpdatingState(action)
|
}.runCatchingUpdatingState(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.setInviteForMeNotificationsEnabled(enabled: Boolean, action: MutableState<Async<Unit>>) = launch {
|
private fun CoroutineScope.setInviteForMeNotificationsEnabled(enabled: Boolean, action: MutableState<AsyncData<Unit>>) = launch {
|
||||||
suspend {
|
suspend {
|
||||||
notificationSettingsService.setInviteForMeEnabled(enabled).getOrThrow()
|
notificationSettingsService.setInviteForMeEnabled(enabled).getOrThrow()
|
||||||
}.runCatchingUpdatingState(action)
|
}.runCatchingUpdatingState(action)
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,14 @@
|
||||||
package io.element.android.features.preferences.impl.notifications
|
package io.element.android.features.preferences.impl.notifications
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class NotificationSettingsState(
|
data class NotificationSettingsState(
|
||||||
val matrixSettings: MatrixSettings,
|
val matrixSettings: MatrixSettings,
|
||||||
val appSettings: AppSettings,
|
val appSettings: AppSettings,
|
||||||
val changeNotificationSettingAction: Async<Unit>,
|
val changeNotificationSettingAction: AsyncData<Unit>,
|
||||||
val eventSink: (NotificationSettingsEvents) -> Unit,
|
val eventSink: (NotificationSettingsEvents) -> Unit,
|
||||||
) {
|
) {
|
||||||
sealed interface MatrixSettings {
|
sealed interface MatrixSettings {
|
||||||
|
|
|
||||||
|
|
@ -17,20 +17,20 @@
|
||||||
package io.element.android.features.preferences.impl.notifications
|
package io.element.android.features.preferences.impl.notifications
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
|
|
||||||
open class NotificationSettingsStateProvider : PreviewParameterProvider<NotificationSettingsState> {
|
open class NotificationSettingsStateProvider : PreviewParameterProvider<NotificationSettingsState> {
|
||||||
override val values: Sequence<NotificationSettingsState>
|
override val values: Sequence<NotificationSettingsState>
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
aNotificationSettingsState(),
|
aNotificationSettingsState(),
|
||||||
aNotificationSettingsState(changeNotificationSettingAction = Async.Loading(Unit)),
|
aNotificationSettingsState(changeNotificationSettingAction = AsyncData.Loading(Unit)),
|
||||||
aNotificationSettingsState(changeNotificationSettingAction = Async.Failure(Throwable("error"))),
|
aNotificationSettingsState(changeNotificationSettingAction = AsyncData.Failure(Throwable("error"))),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun aNotificationSettingsState(
|
fun aNotificationSettingsState(
|
||||||
changeNotificationSettingAction: Async<Unit> = Async.Uninitialized,
|
changeNotificationSettingAction: AsyncData<Unit> = AsyncData.Uninitialized,
|
||||||
) = NotificationSettingsState(
|
) = NotificationSettingsState(
|
||||||
matrixSettings = NotificationSettingsState.MatrixSettings.Valid(
|
matrixSettings = NotificationSettingsState.MatrixSettings.Valid(
|
||||||
atRoomNotificationsEnabled = true,
|
atRoomNotificationsEnabled = true,
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ import androidx.compose.runtime.setValue
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
|
|
@ -63,7 +63,7 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor(
|
||||||
mutableStateOf(null)
|
mutableStateOf(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
val changeNotificationSettingAction: MutableState<Async<Unit>> = remember { mutableStateOf(Async.Uninitialized) }
|
val changeNotificationSettingAction: MutableState<AsyncData<Unit>> = remember { mutableStateOf(AsyncData.Uninitialized) }
|
||||||
|
|
||||||
val roomsWithUserDefinedMode: MutableState<List<RoomSummary.Filled>> = remember {
|
val roomsWithUserDefinedMode: MutableState<List<RoomSummary.Filled>> = remember {
|
||||||
mutableStateOf(listOf())
|
mutableStateOf(listOf())
|
||||||
|
|
@ -82,7 +82,7 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor(
|
||||||
is EditDefaultNotificationSettingStateEvents.SetNotificationMode -> {
|
is EditDefaultNotificationSettingStateEvents.SetNotificationMode -> {
|
||||||
localCoroutineScope.setDefaultNotificationMode(event.mode, changeNotificationSettingAction)
|
localCoroutineScope.setDefaultNotificationMode(event.mode, changeNotificationSettingAction)
|
||||||
}
|
}
|
||||||
EditDefaultNotificationSettingStateEvents.ClearError -> changeNotificationSettingAction.value = Async.Uninitialized
|
EditDefaultNotificationSettingStateEvents.ClearError -> changeNotificationSettingAction.value = AsyncData.Uninitialized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,7 +137,7 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor(
|
||||||
roomsWithUserDefinedMode.value = sortedSummaries
|
roomsWithUserDefinedMode.value = sortedSummaries
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.setDefaultNotificationMode(mode: RoomNotificationMode, action: MutableState<Async<Unit>>) = launch {
|
private fun CoroutineScope.setDefaultNotificationMode(mode: RoomNotificationMode, action: MutableState<AsyncData<Unit>>) = launch {
|
||||||
suspend {
|
suspend {
|
||||||
// On modern clients, we don't have different settings for encrypted and non-encrypted rooms (Legacy clients did).
|
// On modern clients, we don't have different settings for encrypted and non-encrypted rooms (Legacy clients did).
|
||||||
notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = true, mode = mode, isOneToOne = isOneToOne).getOrThrow()
|
notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = true, mode = mode, isOneToOne = isOneToOne).getOrThrow()
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package io.element.android.features.preferences.impl.notifications.edit
|
package io.element.android.features.preferences.impl.notifications.edit
|
||||||
|
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
@ -25,7 +25,7 @@ data class EditDefaultNotificationSettingState(
|
||||||
val isOneToOne: Boolean,
|
val isOneToOne: Boolean,
|
||||||
val mode: RoomNotificationMode?,
|
val mode: RoomNotificationMode?,
|
||||||
val roomsWithUserDefinedMode: ImmutableList<RoomSummary.Filled>,
|
val roomsWithUserDefinedMode: ImmutableList<RoomSummary.Filled>,
|
||||||
val changeNotificationSettingAction: Async<Unit>,
|
val changeNotificationSettingAction: AsyncData<Unit>,
|
||||||
val displayMentionsOnlyDisclaimer: Boolean,
|
val displayMentionsOnlyDisclaimer: Boolean,
|
||||||
val eventSink: (EditDefaultNotificationSettingStateEvents) -> Unit,
|
val eventSink: (EditDefaultNotificationSettingStateEvents) -> Unit,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
package io.element.android.features.preferences.impl.notifications.edit
|
package io.element.android.features.preferences.impl.notifications.edit
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||||
|
|
@ -29,15 +29,15 @@ open class EditDefaultNotificationSettingStateProvider: PreviewParameterProvider
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
anEditDefaultNotificationSettingsState(),
|
anEditDefaultNotificationSettingsState(),
|
||||||
anEditDefaultNotificationSettingsState(isOneToOne = true),
|
anEditDefaultNotificationSettingsState(isOneToOne = true),
|
||||||
anEditDefaultNotificationSettingsState(changeNotificationSettingAction = Async.Loading(Unit)),
|
anEditDefaultNotificationSettingsState(changeNotificationSettingAction = AsyncData.Loading(Unit)),
|
||||||
anEditDefaultNotificationSettingsState(changeNotificationSettingAction = Async.Failure(Throwable("error"))),
|
anEditDefaultNotificationSettingsState(changeNotificationSettingAction = AsyncData.Failure(Throwable("error"))),
|
||||||
anEditDefaultNotificationSettingsState(displayMentionsOnlyDisclaimer = true),
|
anEditDefaultNotificationSettingsState(displayMentionsOnlyDisclaimer = true),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun anEditDefaultNotificationSettingsState(
|
private fun anEditDefaultNotificationSettingsState(
|
||||||
isOneToOne: Boolean = false,
|
isOneToOne: Boolean = false,
|
||||||
changeNotificationSettingAction: Async<Unit> = Async.Uninitialized,
|
changeNotificationSettingAction: AsyncData<Unit> = AsyncData.Uninitialized,
|
||||||
displayMentionsOnlyDisclaimer: Boolean = false,
|
displayMentionsOnlyDisclaimer: Boolean = false,
|
||||||
) = EditDefaultNotificationSettingState(
|
) = EditDefaultNotificationSettingState(
|
||||||
isOneToOne = isOneToOne,
|
isOneToOne = isOneToOne,
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ import androidx.core.net.toUri
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
|
|
@ -92,7 +92,7 @@ class EditUserProfilePresenter @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val saveAction: MutableState<Async<Unit>> = remember { mutableStateOf(Async.Uninitialized) }
|
val saveAction: MutableState<AsyncData<Unit>> = remember { mutableStateOf(AsyncData.Uninitialized) }
|
||||||
val localCoroutineScope = rememberCoroutineScope()
|
val localCoroutineScope = rememberCoroutineScope()
|
||||||
fun handleEvents(event: EditUserProfileEvents) {
|
fun handleEvents(event: EditUserProfileEvents) {
|
||||||
when (event) {
|
when (event) {
|
||||||
|
|
@ -111,7 +111,7 @@ class EditUserProfilePresenter @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
is EditUserProfileEvents.UpdateDisplayName -> userDisplayName = event.name
|
is EditUserProfileEvents.UpdateDisplayName -> userDisplayName = event.name
|
||||||
EditUserProfileEvents.CancelSaveChanges -> saveAction.value = Async.Uninitialized
|
EditUserProfileEvents.CancelSaveChanges -> saveAction.value = AsyncData.Uninitialized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,7 +126,7 @@ class EditUserProfilePresenter @AssistedInject constructor(
|
||||||
displayName = userDisplayName.orEmpty(),
|
displayName = userDisplayName.orEmpty(),
|
||||||
userAvatarUrl = userAvatarUri,
|
userAvatarUrl = userAvatarUri,
|
||||||
avatarActions = avatarActions,
|
avatarActions = avatarActions,
|
||||||
saveButtonEnabled = canSave && saveAction.value !is Async.Loading,
|
saveButtonEnabled = canSave && saveAction.value !is AsyncData.Loading,
|
||||||
saveAction = saveAction.value,
|
saveAction = saveAction.value,
|
||||||
cameraPermissionState = cameraPermissionState,
|
cameraPermissionState = cameraPermissionState,
|
||||||
eventSink = { handleEvents(it) },
|
eventSink = { handleEvents(it) },
|
||||||
|
|
@ -140,7 +140,7 @@ class EditUserProfilePresenter @AssistedInject constructor(
|
||||||
// Need to call `toUri()?.toString()` to make the test pass (we mockk Uri)
|
// Need to call `toUri()?.toString()` to make the test pass (we mockk Uri)
|
||||||
avatarUri?.toString()?.trim() != currentUser.avatarUrl?.toUri()?.toString()?.trim()
|
avatarUri?.toString()?.trim() != currentUser.avatarUrl?.toUri()?.toString()?.trim()
|
||||||
|
|
||||||
private fun CoroutineScope.saveChanges(name: String?, avatarUri: Uri?, currentUser: MatrixUser, action: MutableState<Async<Unit>>) = launch {
|
private fun CoroutineScope.saveChanges(name: String?, avatarUri: Uri?, currentUser: MatrixUser, action: MutableState<AsyncData<Unit>>) = launch {
|
||||||
val results = mutableListOf<Result<Unit>>()
|
val results = mutableListOf<Result<Unit>>()
|
||||||
suspend {
|
suspend {
|
||||||
if (!name.isNullOrEmpty() && name.trim() != currentUser.displayName.orEmpty().trim()) {
|
if (!name.isNullOrEmpty() && name.trim() != currentUser.displayName.orEmpty().trim()) {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
package io.element.android.features.preferences.impl.user.editprofile
|
package io.element.android.features.preferences.impl.user.editprofile
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
||||||
import io.element.android.libraries.permissions.api.PermissionsState
|
import io.element.android.libraries.permissions.api.PermissionsState
|
||||||
|
|
@ -29,7 +29,7 @@ data class EditUserProfileState(
|
||||||
val userAvatarUrl: Uri?,
|
val userAvatarUrl: Uri?,
|
||||||
val avatarActions: ImmutableList<AvatarAction>,
|
val avatarActions: ImmutableList<AvatarAction>,
|
||||||
val saveButtonEnabled: Boolean,
|
val saveButtonEnabled: Boolean,
|
||||||
val saveAction: Async<Unit>,
|
val saveAction: AsyncData<Unit>,
|
||||||
val cameraPermissionState: PermissionsState,
|
val cameraPermissionState: PermissionsState,
|
||||||
val eventSink: (EditUserProfileEvents) -> Unit
|
val eventSink: (EditUserProfileEvents) -> Unit
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
package io.element.android.features.preferences.impl.user.editprofile
|
package io.element.android.features.preferences.impl.user.editprofile
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.permissions.api.aPermissionsState
|
import io.element.android.libraries.permissions.api.aPermissionsState
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
@ -35,7 +35,7 @@ fun aEditUserProfileState() = EditUserProfileState(
|
||||||
displayName = "John Doe",
|
displayName = "John Doe",
|
||||||
userAvatarUrl = null,
|
userAvatarUrl = null,
|
||||||
avatarActions = persistentListOf(),
|
avatarActions = persistentListOf(),
|
||||||
saveAction = Async.Uninitialized,
|
saveAction = AsyncData.Uninitialized,
|
||||||
saveButtonEnabled = true,
|
saveButtonEnabled = true,
|
||||||
cameraPermissionState = aPermissionsState(showDialog = false),
|
cameraPermissionState = aPermissionsState(showDialog = false),
|
||||||
eventSink = {}
|
eventSink = {}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import io.element.android.features.preferences.impl.tasks.FakeComputeCacheSizeUs
|
||||||
import io.element.android.features.rageshake.impl.preferences.DefaultRageshakePreferencesPresenter
|
import io.element.android.features.rageshake.impl.preferences.DefaultRageshakePreferencesPresenter
|
||||||
import io.element.android.features.rageshake.test.rageshake.FakeRageShake
|
import io.element.android.features.rageshake.test.rageshake.FakeRageShake
|
||||||
import io.element.android.features.rageshake.test.rageshake.FakeRageshakeDataStore
|
import io.element.android.features.rageshake.test.rageshake.FakeRageshakeDataStore
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||||
import io.element.android.libraries.featureflag.test.InMemoryPreferencesStore
|
import io.element.android.libraries.featureflag.test.InMemoryPreferencesStore
|
||||||
|
|
@ -49,8 +49,8 @@ class DeveloperSettingsPresenterTest {
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.features).isEmpty()
|
assertThat(initialState.features).isEmpty()
|
||||||
assertThat(initialState.clearCacheAction).isEqualTo(Async.Uninitialized)
|
assertThat(initialState.clearCacheAction).isEqualTo(AsyncData.Uninitialized)
|
||||||
assertThat(initialState.cacheSize).isEqualTo(Async.Uninitialized)
|
assertThat(initialState.cacheSize).isEqualTo(AsyncData.Uninitialized)
|
||||||
assertThat(initialState.customElementCallBaseUrlState).isNotNull()
|
assertThat(initialState.customElementCallBaseUrlState).isNotNull()
|
||||||
assertThat(initialState.customElementCallBaseUrlState.baseUrl).isNull()
|
assertThat(initialState.customElementCallBaseUrlState.baseUrl).isNull()
|
||||||
val loadedState = awaitItem()
|
val loadedState = awaitItem()
|
||||||
|
|
@ -105,9 +105,9 @@ class DeveloperSettingsPresenterTest {
|
||||||
assertThat(clearCacheUseCase.executeHasBeenCalled).isFalse()
|
assertThat(clearCacheUseCase.executeHasBeenCalled).isFalse()
|
||||||
initialState.eventSink(DeveloperSettingsEvents.ClearCache)
|
initialState.eventSink(DeveloperSettingsEvents.ClearCache)
|
||||||
val stateAfterEvent = awaitItem()
|
val stateAfterEvent = awaitItem()
|
||||||
assertThat(stateAfterEvent.clearCacheAction).isInstanceOf(Async.Loading::class.java)
|
assertThat(stateAfterEvent.clearCacheAction).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
assertThat(awaitItem().clearCacheAction).isInstanceOf(Async.Success::class.java)
|
assertThat(awaitItem().clearCacheAction).isInstanceOf(AsyncData.Success::class.java)
|
||||||
assertThat(clearCacheUseCase.executeHasBeenCalled).isTrue()
|
assertThat(clearCacheUseCase.executeHasBeenCalled).isTrue()
|
||||||
cancelAndIgnoreRemainingEvents()
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import app.cash.molecule.RecompositionMode
|
||||||
import app.cash.molecule.moleculeFlow
|
import app.cash.molecule.moleculeFlow
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||||
|
|
@ -110,7 +110,7 @@ class EditUserProfilePresenterTest {
|
||||||
AvatarAction.Remove
|
AvatarAction.Remove
|
||||||
)
|
)
|
||||||
assertThat(initialState.saveButtonEnabled).isFalse()
|
assertThat(initialState.saveButtonEnabled).isFalse()
|
||||||
assertThat(initialState.saveAction).isInstanceOf(Async.Uninitialized::class.java)
|
assertThat(initialState.saveAction).isInstanceOf(AsyncData.Uninitialized::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -359,7 +359,7 @@ class EditUserProfilePresenterTest {
|
||||||
initialState.eventSink(EditUserProfileEvents.Save)
|
initialState.eventSink(EditUserProfileEvents.Save)
|
||||||
skipItems(2)
|
skipItems(2)
|
||||||
assertThat(matrixClient.uploadAvatarCalled).isFalse()
|
assertThat(matrixClient.uploadAvatarCalled).isFalse()
|
||||||
assertThat(awaitItem().saveAction).isInstanceOf(Async.Failure::class.java)
|
assertThat(awaitItem().saveAction).isInstanceOf(AsyncData.Failure::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -406,9 +406,9 @@ class EditUserProfilePresenterTest {
|
||||||
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("foo"))
|
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("foo"))
|
||||||
initialState.eventSink(EditUserProfileEvents.Save)
|
initialState.eventSink(EditUserProfileEvents.Save)
|
||||||
skipItems(2)
|
skipItems(2)
|
||||||
assertThat(awaitItem().saveAction).isInstanceOf(Async.Failure::class.java)
|
assertThat(awaitItem().saveAction).isInstanceOf(AsyncData.Failure::class.java)
|
||||||
initialState.eventSink(EditUserProfileEvents.CancelSaveChanges)
|
initialState.eventSink(EditUserProfileEvents.CancelSaveChanges)
|
||||||
assertThat(awaitItem().saveAction).isInstanceOf(Async.Uninitialized::class.java)
|
assertThat(awaitItem().saveAction).isInstanceOf(AsyncData.Uninitialized::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -421,8 +421,8 @@ class EditUserProfilePresenterTest {
|
||||||
initialState.eventSink(event)
|
initialState.eventSink(event)
|
||||||
initialState.eventSink(EditUserProfileEvents.Save)
|
initialState.eventSink(EditUserProfileEvents.Save)
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
assertThat(awaitItem().saveAction).isInstanceOf(Async.Loading::class.java)
|
assertThat(awaitItem().saveAction).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
assertThat(awaitItem().saveAction).isInstanceOf(Async.Failure::class.java)
|
assertThat(awaitItem().saveAction).isInstanceOf(AsyncData.Failure::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ import io.element.android.features.rageshake.api.reporter.BugReporter
|
||||||
import io.element.android.features.rageshake.api.reporter.BugReporterListener
|
import io.element.android.features.rageshake.api.reporter.BugReporterListener
|
||||||
import io.element.android.features.rageshake.api.screenshot.ScreenshotHolder
|
import io.element.android.features.rageshake.api.screenshot.ScreenshotHolder
|
||||||
import io.element.android.features.rageshake.impl.logs.VectorFileLogger
|
import io.element.android.features.rageshake.impl.logs.VectorFileLogger
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
@ -45,27 +45,27 @@ class BugReportPresenter @Inject constructor(
|
||||||
|
|
||||||
private class BugReporterUploadListener(
|
private class BugReporterUploadListener(
|
||||||
private val sendingProgress: MutableFloatState,
|
private val sendingProgress: MutableFloatState,
|
||||||
private val sendingAction: MutableState<Async<Unit>>
|
private val sendingAction: MutableState<AsyncData<Unit>>
|
||||||
) : BugReporterListener {
|
) : BugReporterListener {
|
||||||
|
|
||||||
override fun onUploadCancelled() {
|
override fun onUploadCancelled() {
|
||||||
sendingProgress.floatValue = 0f
|
sendingProgress.floatValue = 0f
|
||||||
sendingAction.value = Async.Uninitialized
|
sendingAction.value = AsyncData.Uninitialized
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUploadFailed(reason: String?) {
|
override fun onUploadFailed(reason: String?) {
|
||||||
sendingProgress.floatValue = 0f
|
sendingProgress.floatValue = 0f
|
||||||
sendingAction.value = Async.Failure(Exception(reason))
|
sendingAction.value = AsyncData.Failure(Exception(reason))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onProgress(progress: Int) {
|
override fun onProgress(progress: Int) {
|
||||||
sendingProgress.floatValue = progress.toFloat() / 100
|
sendingProgress.floatValue = progress.toFloat() / 100
|
||||||
sendingAction.value = Async.Loading()
|
sendingAction.value = AsyncData.Loading()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUploadSucceed() {
|
override fun onUploadSucceed() {
|
||||||
sendingProgress.floatValue = 0f
|
sendingProgress.floatValue = 0f
|
||||||
sendingAction.value = Async.Success(Unit)
|
sendingAction.value = AsyncData.Success(Unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,8 +83,8 @@ class BugReportPresenter @Inject constructor(
|
||||||
val sendingProgress = remember {
|
val sendingProgress = remember {
|
||||||
mutableFloatStateOf(0f)
|
mutableFloatStateOf(0f)
|
||||||
}
|
}
|
||||||
val sendingAction: MutableState<Async<Unit>> = remember {
|
val sendingAction: MutableState<AsyncData<Unit>> = remember {
|
||||||
mutableStateOf(Async.Uninitialized)
|
mutableStateOf(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
val formState: MutableState<BugReportFormState> = remember {
|
val formState: MutableState<BugReportFormState> = remember {
|
||||||
mutableStateOf(BugReportFormState.Default)
|
mutableStateOf(BugReportFormState.Default)
|
||||||
|
|
@ -109,7 +109,7 @@ class BugReportPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
BugReportEvents.ClearError -> {
|
BugReportEvents.ClearError -> {
|
||||||
sendingProgress.floatValue = 0f
|
sendingProgress.floatValue = 0f
|
||||||
sendingAction.value = Async.Uninitialized
|
sendingAction.value = AsyncData.Uninitialized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
package io.element.android.features.rageshake.impl.bugreport
|
package io.element.android.features.rageshake.impl.bugreport
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
data class BugReportState(
|
data class BugReportState(
|
||||||
|
|
@ -25,11 +25,11 @@ data class BugReportState(
|
||||||
val hasCrashLogs: Boolean,
|
val hasCrashLogs: Boolean,
|
||||||
val screenshotUri: String?,
|
val screenshotUri: String?,
|
||||||
val sendingProgress: Float,
|
val sendingProgress: Float,
|
||||||
val sending: Async<Unit>,
|
val sending: AsyncData<Unit>,
|
||||||
val eventSink: (BugReportEvents) -> Unit
|
val eventSink: (BugReportEvents) -> Unit
|
||||||
) {
|
) {
|
||||||
val submitEnabled =
|
val submitEnabled =
|
||||||
formState.description.length > 10 && sending !is Async.Loading
|
formState.description.length > 10 && sending !is AsyncData.Loading
|
||||||
}
|
}
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
package io.element.android.features.rageshake.impl.bugreport
|
package io.element.android.features.rageshake.impl.bugreport
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
|
|
||||||
open class BugReportStateProvider : PreviewParameterProvider<BugReportState> {
|
open class BugReportStateProvider : PreviewParameterProvider<BugReportState> {
|
||||||
override val values: Sequence<BugReportState>
|
override val values: Sequence<BugReportState>
|
||||||
|
|
@ -31,8 +31,8 @@ open class BugReportStateProvider : PreviewParameterProvider<BugReportState> {
|
||||||
hasCrashLogs = true,
|
hasCrashLogs = true,
|
||||||
screenshotUri = "aUri"
|
screenshotUri = "aUri"
|
||||||
),
|
),
|
||||||
aBugReportState().copy(sending = Async.Loading()),
|
aBugReportState().copy(sending = AsyncData.Loading()),
|
||||||
aBugReportState().copy(sending = Async.Success(Unit)),
|
aBugReportState().copy(sending = AsyncData.Success(Unit)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -41,6 +41,6 @@ fun aBugReportState() = BugReportState(
|
||||||
hasCrashLogs = false,
|
hasCrashLogs = false,
|
||||||
screenshotUri = null,
|
screenshotUri = null,
|
||||||
sendingProgress = 0F,
|
sendingProgress = 0F,
|
||||||
sending = Async.Uninitialized,
|
sending = AsyncData.Uninitialized,
|
||||||
eventSink = {}
|
eventSink = {}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ import androidx.compose.ui.unit.dp
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import io.element.android.features.rageshake.impl.R
|
import io.element.android.features.rageshake.impl.R
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.designsystem.components.async.AsyncView
|
import io.element.android.libraries.designsystem.components.async.AsyncView
|
||||||
import io.element.android.libraries.designsystem.components.form.textFieldState
|
import io.element.android.libraries.designsystem.components.form.textFieldState
|
||||||
import io.element.android.libraries.designsystem.components.preferences.PreferencePage
|
import io.element.android.libraries.designsystem.components.preferences.PreferencePage
|
||||||
|
|
@ -67,7 +67,7 @@ fun BugReportView(
|
||||||
title = stringResource(id = CommonStrings.common_report_a_problem),
|
title = stringResource(id = CommonStrings.common_report_a_problem),
|
||||||
onBackPressed = onBackPressed
|
onBackPressed = onBackPressed
|
||||||
) {
|
) {
|
||||||
val isFormEnabled = state.sending !is Async.Loading
|
val isFormEnabled = state.sending !is AsyncData.Loading
|
||||||
var descriptionFieldState by textFieldState(
|
var descriptionFieldState by textFieldState(
|
||||||
stateValue = state.formState.description
|
stateValue = state.formState.description
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import io.element.android.features.rageshake.test.crash.A_CRASH_DATA
|
||||||
import io.element.android.features.rageshake.test.crash.FakeCrashDataStore
|
import io.element.android.features.rageshake.test.crash.FakeCrashDataStore
|
||||||
import io.element.android.features.rageshake.test.screenshot.A_SCREENSHOT_URI
|
import io.element.android.features.rageshake.test.screenshot.A_SCREENSHOT_URI
|
||||||
import io.element.android.features.rageshake.test.screenshot.FakeScreenshotHolder
|
import io.element.android.features.rageshake.test.screenshot.FakeScreenshotHolder
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.test.A_FAILURE_REASON
|
import io.element.android.libraries.matrix.test.A_FAILURE_REASON
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
|
|
@ -53,7 +53,7 @@ class BugReportPresenterTest {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.hasCrashLogs).isFalse()
|
assertThat(initialState.hasCrashLogs).isFalse()
|
||||||
assertThat(initialState.formState).isEqualTo(BugReportFormState.Default)
|
assertThat(initialState.formState).isEqualTo(BugReportFormState.Default)
|
||||||
assertThat(initialState.sending).isEqualTo(Async.Uninitialized)
|
assertThat(initialState.sending).isEqualTo(AsyncData.Uninitialized)
|
||||||
assertThat(initialState.screenshotUri).isNull()
|
assertThat(initialState.screenshotUri).isNull()
|
||||||
assertThat(initialState.sendingProgress).isEqualTo(0f)
|
assertThat(initialState.sendingProgress).isEqualTo(0f)
|
||||||
assertThat(initialState.submitEnabled).isFalse()
|
assertThat(initialState.submitEnabled).isFalse()
|
||||||
|
|
@ -174,13 +174,13 @@ class BugReportPresenterTest {
|
||||||
initialState.eventSink.invoke(BugReportEvents.SendBugReport)
|
initialState.eventSink.invoke(BugReportEvents.SendBugReport)
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val progressState = awaitItem()
|
val progressState = awaitItem()
|
||||||
assertThat(progressState.sending).isEqualTo(Async.Loading(null))
|
assertThat(progressState.sending).isEqualTo(AsyncData.Loading(null))
|
||||||
assertThat(progressState.sendingProgress).isEqualTo(0f)
|
assertThat(progressState.sendingProgress).isEqualTo(0f)
|
||||||
assertThat(progressState.submitEnabled).isFalse()
|
assertThat(progressState.submitEnabled).isFalse()
|
||||||
assertThat(awaitItem().sendingProgress).isEqualTo(0.5f)
|
assertThat(awaitItem().sendingProgress).isEqualTo(0.5f)
|
||||||
assertThat(awaitItem().sendingProgress).isEqualTo(1f)
|
assertThat(awaitItem().sendingProgress).isEqualTo(1f)
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
assertThat(awaitItem().sending).isEqualTo(Async.Success(Unit))
|
assertThat(awaitItem().sending).isEqualTo(AsyncData.Success(Unit))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -199,17 +199,17 @@ class BugReportPresenterTest {
|
||||||
initialState.eventSink.invoke(BugReportEvents.SendBugReport)
|
initialState.eventSink.invoke(BugReportEvents.SendBugReport)
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val progressState = awaitItem()
|
val progressState = awaitItem()
|
||||||
assertThat(progressState.sending).isEqualTo(Async.Loading(null))
|
assertThat(progressState.sending).isEqualTo(AsyncData.Loading(null))
|
||||||
assertThat(progressState.sendingProgress).isEqualTo(0f)
|
assertThat(progressState.sendingProgress).isEqualTo(0f)
|
||||||
assertThat(awaitItem().sendingProgress).isEqualTo(0.5f)
|
assertThat(awaitItem().sendingProgress).isEqualTo(0.5f)
|
||||||
// Failure
|
// Failure
|
||||||
assertThat(awaitItem().sendingProgress).isEqualTo(0f)
|
assertThat(awaitItem().sendingProgress).isEqualTo(0f)
|
||||||
assertThat((awaitItem().sending as Async.Failure).error.message).isEqualTo(A_FAILURE_REASON)
|
assertThat((awaitItem().sending as AsyncData.Failure).error.message).isEqualTo(A_FAILURE_REASON)
|
||||||
// Reset failure
|
// Reset failure
|
||||||
initialState.eventSink.invoke(BugReportEvents.ClearError)
|
initialState.eventSink.invoke(BugReportEvents.ClearError)
|
||||||
val lastItem = awaitItem()
|
val lastItem = awaitItem()
|
||||||
assertThat(lastItem.sendingProgress).isEqualTo(0f)
|
assertThat(lastItem.sendingProgress).isEqualTo(0f)
|
||||||
assertThat(lastItem.sending).isInstanceOf(Async.Uninitialized::class.java)
|
assertThat(lastItem.sending).isInstanceOf(AsyncData.Uninitialized::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -228,12 +228,12 @@ class BugReportPresenterTest {
|
||||||
initialState.eventSink.invoke(BugReportEvents.SendBugReport)
|
initialState.eventSink.invoke(BugReportEvents.SendBugReport)
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val progressState = awaitItem()
|
val progressState = awaitItem()
|
||||||
assertThat(progressState.sending).isEqualTo(Async.Loading(null))
|
assertThat(progressState.sending).isEqualTo(AsyncData.Loading(null))
|
||||||
assertThat(progressState.sendingProgress).isEqualTo(0f)
|
assertThat(progressState.sendingProgress).isEqualTo(0f)
|
||||||
assertThat(awaitItem().sendingProgress).isEqualTo(0.5f)
|
assertThat(awaitItem().sendingProgress).isEqualTo(0.5f)
|
||||||
// Cancelled
|
// Cancelled
|
||||||
assertThat(awaitItem().sendingProgress).isEqualTo(0f)
|
assertThat(awaitItem().sendingProgress).isEqualTo(0f)
|
||||||
assertThat(awaitItem().sending).isEqualTo(Async.Uninitialized)
|
assertThat(awaitItem().sending).isEqualTo(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import io.element.android.compound.tokens.generated.CompoundIcons
|
||||||
import io.element.android.features.roomdetails.impl.R
|
import io.element.android.features.roomdetails.impl.R
|
||||||
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsEvents
|
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsEvents
|
||||||
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState
|
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.core.bool.orFalse
|
import io.element.android.libraries.core.bool.orFalse
|
||||||
import io.element.android.libraries.designsystem.components.dialogs.RetryDialog
|
import io.element.android.libraries.designsystem.components.dialogs.RetryDialog
|
||||||
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
||||||
|
|
@ -42,13 +42,13 @@ import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
internal fun BlockUserSection(state: RoomMemberDetailsState, modifier: Modifier = Modifier) {
|
internal fun BlockUserSection(state: RoomMemberDetailsState, modifier: Modifier = Modifier) {
|
||||||
PreferenceCategory(showDivider = false, modifier = modifier) {
|
PreferenceCategory(showDivider = false, modifier = modifier) {
|
||||||
when (state.isBlocked) {
|
when (state.isBlocked) {
|
||||||
is Async.Failure -> PreferenceBlockUser(isBlocked = state.isBlocked.prevData, isLoading = false, eventSink = state.eventSink)
|
is AsyncData.Failure -> PreferenceBlockUser(isBlocked = state.isBlocked.prevData, isLoading = false, eventSink = state.eventSink)
|
||||||
is Async.Loading -> PreferenceBlockUser(isBlocked = state.isBlocked.prevData, isLoading = true, eventSink = state.eventSink)
|
is AsyncData.Loading -> PreferenceBlockUser(isBlocked = state.isBlocked.prevData, isLoading = true, eventSink = state.eventSink)
|
||||||
is Async.Success -> PreferenceBlockUser(isBlocked = state.isBlocked.data, isLoading = false, eventSink = state.eventSink)
|
is AsyncData.Success -> PreferenceBlockUser(isBlocked = state.isBlocked.data, isLoading = false, eventSink = state.eventSink)
|
||||||
Async.Uninitialized -> PreferenceBlockUser(isBlocked = null, isLoading = true, eventSink = state.eventSink)
|
AsyncData.Uninitialized -> PreferenceBlockUser(isBlocked = null, isLoading = true, eventSink = state.eventSink)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (state.isBlocked is Async.Failure) {
|
if (state.isBlocked is AsyncData.Failure) {
|
||||||
RetryDialog(
|
RetryDialog(
|
||||||
content = stringResource(CommonStrings.error_unknown),
|
content = stringResource(CommonStrings.error_unknown),
|
||||||
onDismiss = { state.eventSink(RoomMemberDetailsEvents.ClearBlockUserError) },
|
onDismiss = { state.eventSink(RoomMemberDetailsEvents.ClearBlockUserError) },
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
|
|
@ -116,7 +116,7 @@ class RoomDetailsEditPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val saveAction: MutableState<Async<Unit>> = remember { mutableStateOf(Async.Uninitialized) }
|
val saveAction: MutableState<AsyncData<Unit>> = remember { mutableStateOf(AsyncData.Uninitialized) }
|
||||||
val localCoroutineScope = rememberCoroutineScope()
|
val localCoroutineScope = rememberCoroutineScope()
|
||||||
fun handleEvents(event: RoomDetailsEditEvents) {
|
fun handleEvents(event: RoomDetailsEditEvents) {
|
||||||
when (event) {
|
when (event) {
|
||||||
|
|
@ -136,7 +136,7 @@ class RoomDetailsEditPresenter @Inject constructor(
|
||||||
|
|
||||||
is RoomDetailsEditEvents.UpdateRoomName -> roomName = event.name
|
is RoomDetailsEditEvents.UpdateRoomName -> roomName = event.name
|
||||||
is RoomDetailsEditEvents.UpdateRoomTopic -> roomTopic = event.topic.takeUnless { it.isEmpty() }
|
is RoomDetailsEditEvents.UpdateRoomTopic -> roomTopic = event.topic.takeUnless { it.isEmpty() }
|
||||||
RoomDetailsEditEvents.CancelSaveChanges -> saveAction.value = Async.Uninitialized
|
RoomDetailsEditEvents.CancelSaveChanges -> saveAction.value = AsyncData.Uninitialized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -156,7 +156,7 @@ class RoomDetailsEditPresenter @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.saveChanges(name: String, topic: String?, avatarUri: Uri?, action: MutableState<Async<Unit>>) = launch {
|
private fun CoroutineScope.saveChanges(name: String, topic: String?, avatarUri: Uri?, action: MutableState<AsyncData<Unit>>) = launch {
|
||||||
val results = mutableListOf<Result<Unit>>()
|
val results = mutableListOf<Result<Unit>>()
|
||||||
suspend {
|
suspend {
|
||||||
if (topic.orEmpty().trim() != room.topic.orEmpty().trim()) {
|
if (topic.orEmpty().trim() != room.topic.orEmpty().trim()) {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
package io.element.android.features.roomdetails.impl.edit
|
package io.element.android.features.roomdetails.impl.edit
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
||||||
import io.element.android.libraries.permissions.api.PermissionsState
|
import io.element.android.libraries.permissions.api.PermissionsState
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
@ -32,7 +32,7 @@ data class RoomDetailsEditState(
|
||||||
val canChangeAvatar: Boolean,
|
val canChangeAvatar: Boolean,
|
||||||
val avatarActions: ImmutableList<AvatarAction>,
|
val avatarActions: ImmutableList<AvatarAction>,
|
||||||
val saveButtonEnabled: Boolean,
|
val saveButtonEnabled: Boolean,
|
||||||
val saveAction: Async<Unit>,
|
val saveAction: AsyncData<Unit>,
|
||||||
val cameraPermissionState: PermissionsState,
|
val cameraPermissionState: PermissionsState,
|
||||||
val eventSink: (RoomDetailsEditEvents) -> Unit
|
val eventSink: (RoomDetailsEditEvents) -> Unit
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ package io.element.android.features.roomdetails.impl.edit
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.permissions.api.aPermissionsState
|
import io.element.android.libraries.permissions.api.aPermissionsState
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
||||||
|
|
@ -30,8 +30,8 @@ open class RoomDetailsEditStateProvider : PreviewParameterProvider<RoomDetailsEd
|
||||||
aRoomDetailsEditState().copy(roomAvatarUrl = Uri.parse("example://uri")),
|
aRoomDetailsEditState().copy(roomAvatarUrl = Uri.parse("example://uri")),
|
||||||
aRoomDetailsEditState().copy(canChangeName = true, canChangeTopic = false, canChangeAvatar = true, saveButtonEnabled = false),
|
aRoomDetailsEditState().copy(canChangeName = true, canChangeTopic = false, canChangeAvatar = true, saveButtonEnabled = false),
|
||||||
aRoomDetailsEditState().copy(canChangeName = false, canChangeTopic = true, canChangeAvatar = false, saveButtonEnabled = false),
|
aRoomDetailsEditState().copy(canChangeName = false, canChangeTopic = true, canChangeAvatar = false, saveButtonEnabled = false),
|
||||||
aRoomDetailsEditState().copy(saveAction = Async.Loading()),
|
aRoomDetailsEditState().copy(saveAction = AsyncData.Loading()),
|
||||||
aRoomDetailsEditState().copy(saveAction = Async.Failure(Throwable("Whelp")))
|
aRoomDetailsEditState().copy(saveAction = AsyncData.Failure(Throwable("Whelp")))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,7 +45,7 @@ fun aRoomDetailsEditState() = RoomDetailsEditState(
|
||||||
canChangeAvatar = true,
|
canChangeAvatar = true,
|
||||||
avatarActions = persistentListOf(),
|
avatarActions = persistentListOf(),
|
||||||
saveButtonEnabled = true,
|
saveButtonEnabled = true,
|
||||||
saveAction = Async.Uninitialized,
|
saveAction = AsyncData.Uninitialized,
|
||||||
cameraPermissionState = aPermissionsState(showDialog = false),
|
cameraPermissionState = aPermissionsState(showDialog = false),
|
||||||
eventSink = {}
|
eventSink = {}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import io.element.android.features.roomdetails.impl.members.RoomMemberListDataSource
|
import io.element.android.features.roomdetails.impl.members.RoomMemberListDataSource
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
|
|
@ -48,7 +48,7 @@ class RoomInviteMembersPresenter @Inject constructor(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): RoomInviteMembersState {
|
override fun present(): RoomInviteMembersState {
|
||||||
val roomMembers = remember { mutableStateOf<Async<ImmutableList<RoomMember>>>(Async.Loading()) }
|
val roomMembers = remember { mutableStateOf<AsyncData<ImmutableList<RoomMember>>>(AsyncData.Loading()) }
|
||||||
val selectedUsers = remember { mutableStateOf<ImmutableList<MatrixUser>>(persistentListOf()) }
|
val selectedUsers = remember { mutableStateOf<ImmutableList<MatrixUser>>(persistentListOf()) }
|
||||||
val searchResults = remember { mutableStateOf<SearchBarResultState<ImmutableList<InvitableUser>>>(SearchBarResultState.NotSearching()) }
|
val searchResults = remember { mutableStateOf<SearchBarResultState<ImmutableList<InvitableUser>>>(SearchBarResultState.NotSearching()) }
|
||||||
var searchQuery by rememberSaveable { mutableStateOf("") }
|
var searchQuery by rememberSaveable { mutableStateOf("") }
|
||||||
|
|
@ -115,7 +115,7 @@ class RoomInviteMembersPresenter @Inject constructor(
|
||||||
|
|
||||||
private suspend fun performSearch(
|
private suspend fun performSearch(
|
||||||
searchResults: MutableState<SearchBarResultState<ImmutableList<InvitableUser>>>,
|
searchResults: MutableState<SearchBarResultState<ImmutableList<InvitableUser>>>,
|
||||||
roomMembers: MutableState<Async<ImmutableList<RoomMember>>>,
|
roomMembers: MutableState<AsyncData<ImmutableList<RoomMember>>>,
|
||||||
selectedUsers: MutableState<ImmutableList<MatrixUser>>,
|
selectedUsers: MutableState<ImmutableList<MatrixUser>>,
|
||||||
searchQuery: String,
|
searchQuery: String,
|
||||||
) = withContext(coroutineDispatchers.io) {
|
) = withContext(coroutineDispatchers.io) {
|
||||||
|
|
@ -142,7 +142,7 @@ class RoomInviteMembersPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun fetchMembers(roomMembers: MutableState<Async<ImmutableList<RoomMember>>>) {
|
private suspend fun fetchMembers(roomMembers: MutableState<AsyncData<ImmutableList<RoomMember>>>) {
|
||||||
suspend {
|
suspend {
|
||||||
withContext(coroutineDispatchers.io) {
|
withContext(coroutineDispatchers.io) {
|
||||||
roomMemberListDataSource.search("").toImmutableList()
|
roomMemberListDataSource.search("").toImmutableList()
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import androidx.compose.runtime.produceState
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
||||||
|
|
@ -44,7 +44,7 @@ class RoomMemberListPresenter @Inject constructor(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): RoomMemberListState {
|
override fun present(): RoomMemberListState {
|
||||||
var roomMembers by remember { mutableStateOf<Async<RoomMembers>>(Async.Loading()) }
|
var roomMembers by remember { mutableStateOf<AsyncData<RoomMembers>>(AsyncData.Loading()) }
|
||||||
var searchQuery by rememberSaveable { mutableStateOf("") }
|
var searchQuery by rememberSaveable { mutableStateOf("") }
|
||||||
var searchResults by remember {
|
var searchResults by remember {
|
||||||
mutableStateOf<SearchBarResultState<RoomMembers>>(SearchBarResultState.NotSearching())
|
mutableStateOf<SearchBarResultState<RoomMembers>>(SearchBarResultState.NotSearching())
|
||||||
|
|
@ -59,7 +59,7 @@ class RoomMemberListPresenter @Inject constructor(
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
withContext(coroutineDispatchers.io) {
|
withContext(coroutineDispatchers.io) {
|
||||||
val members = roomMemberListDataSource.search("").groupBy { it.membership }
|
val members = roomMemberListDataSource.search("").groupBy { it.membership }
|
||||||
roomMembers = Async.Success(
|
roomMembers = AsyncData.Success(
|
||||||
RoomMembers(
|
RoomMembers(
|
||||||
invited = members.getOrDefault(RoomMembershipState.INVITE, emptyList()).toImmutableList(),
|
invited = members.getOrDefault(RoomMembershipState.INVITE, emptyList()).toImmutableList(),
|
||||||
joined = members.getOrDefault(RoomMembershipState.JOIN, emptyList()).toImmutableList(),
|
joined = members.getOrDefault(RoomMembershipState.JOIN, emptyList()).toImmutableList(),
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,13 @@
|
||||||
|
|
||||||
package io.element.android.features.roomdetails.impl.members
|
package io.element.android.features.roomdetails.impl.members
|
||||||
|
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
data class RoomMemberListState(
|
data class RoomMemberListState(
|
||||||
val roomMembers: Async<RoomMembers>,
|
val roomMembers: AsyncData<RoomMembers>,
|
||||||
val searchQuery: String,
|
val searchQuery: String,
|
||||||
val searchResults: SearchBarResultState<RoomMembers>,
|
val searchResults: SearchBarResultState<RoomMembers>,
|
||||||
val isSearchActive: Boolean,
|
val isSearchActive: Boolean,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
package io.element.android.features.roomdetails.impl.members
|
package io.element.android.features.roomdetails.impl.members
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||||
|
|
@ -28,14 +28,14 @@ internal class RoomMemberListStateProvider : PreviewParameterProvider<RoomMember
|
||||||
override val values: Sequence<RoomMemberListState>
|
override val values: Sequence<RoomMemberListState>
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
aRoomMemberListState(
|
aRoomMemberListState(
|
||||||
roomMembers = Async.Success(
|
roomMembers = AsyncData.Success(
|
||||||
RoomMembers(
|
RoomMembers(
|
||||||
invited = persistentListOf(aVictor(), aWalter()),
|
invited = persistentListOf(aVictor(), aWalter()),
|
||||||
joined = persistentListOf(anAlice(), aBob()),
|
joined = persistentListOf(anAlice(), aBob()),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
aRoomMemberListState(roomMembers = Async.Loading()),
|
aRoomMemberListState(roomMembers = AsyncData.Loading()),
|
||||||
aRoomMemberListState().copy(canInvite = true),
|
aRoomMemberListState().copy(canInvite = true),
|
||||||
aRoomMemberListState().copy(isSearchActive = false),
|
aRoomMemberListState().copy(isSearchActive = false),
|
||||||
aRoomMemberListState().copy(isSearchActive = true),
|
aRoomMemberListState().copy(isSearchActive = true),
|
||||||
|
|
@ -59,7 +59,7 @@ internal class RoomMemberListStateProvider : PreviewParameterProvider<RoomMember
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun aRoomMemberListState(
|
internal fun aRoomMemberListState(
|
||||||
roomMembers: Async<RoomMembers> = Async.Uninitialized,
|
roomMembers: AsyncData<RoomMembers> = AsyncData.Uninitialized,
|
||||||
searchResults: SearchBarResultState<RoomMembers> = SearchBarResultState.NotSearching(),
|
searchResults: SearchBarResultState<RoomMembers> = SearchBarResultState.NotSearching(),
|
||||||
) = RoomMemberListState(
|
) = RoomMemberListState(
|
||||||
roomMembers = roomMembers,
|
roomMembers = roomMembers,
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.features.roomdetails.impl.R
|
import io.element.android.features.roomdetails.impl.R
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
|
|
@ -103,7 +103,7 @@ fun RoomMemberListView(
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!state.isSearchActive) {
|
if (!state.isSearchActive) {
|
||||||
if (state.roomMembers is Async.Success) {
|
if (state.roomMembers is AsyncData.Success) {
|
||||||
RoomMemberList(
|
RoomMemberList(
|
||||||
roomMembers = state.roomMembers.data,
|
roomMembers = state.roomMembers.data,
|
||||||
showMembersCount = true,
|
showMembersCount = true,
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ import im.vector.app.features.analytics.plan.MobileScreen
|
||||||
import io.element.android.anvilannotations.ContributesNode
|
import io.element.android.anvilannotations.ContributesNode
|
||||||
import io.element.android.features.roomdetails.impl.R
|
import io.element.android.features.roomdetails.impl.R
|
||||||
import io.element.android.libraries.androidutils.system.startSharePlainTextIntent
|
import io.element.android.libraries.androidutils.system.startSharePlainTextIntent
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.NodeInputs
|
import io.element.android.libraries.architecture.NodeInputs
|
||||||
import io.element.android.libraries.architecture.inputs
|
import io.element.android.libraries.architecture.inputs
|
||||||
import io.element.android.libraries.di.RoomScope
|
import io.element.android.libraries.di.RoomScope
|
||||||
|
|
@ -95,7 +95,7 @@ class RoomMemberDetailsNode @AssistedInject constructor(
|
||||||
val state = presenter.present()
|
val state = presenter.present()
|
||||||
|
|
||||||
LaunchedEffect(state.startDmActionState) {
|
LaunchedEffect(state.startDmActionState) {
|
||||||
if (state.startDmActionState is Async.Success) {
|
if (state.startDmActionState is AsyncData.Success) {
|
||||||
onStartDM(state.startDmActionState.data)
|
onStartDM(state.startDmActionState.data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.features.createroom.api.StartDMAction
|
import io.element.android.features.createroom.api.StartDMAction
|
||||||
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState.ConfirmationDialog
|
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState.ConfirmationDialog
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.core.bool.orFalse
|
import io.element.android.libraries.core.bool.orFalse
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
|
|
@ -56,14 +56,14 @@ class RoomMemberDetailsPresenter @AssistedInject constructor(
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
var confirmationDialog by remember { mutableStateOf<ConfirmationDialog?>(null) }
|
var confirmationDialog by remember { mutableStateOf<ConfirmationDialog?>(null) }
|
||||||
val roomMember by room.getRoomMemberAsState(roomMemberId)
|
val roomMember by room.getRoomMemberAsState(roomMemberId)
|
||||||
val startDmActionState: MutableState<Async<RoomId>> = remember { mutableStateOf(Async.Uninitialized) }
|
val startDmActionState: MutableState<AsyncData<RoomId>> = remember { mutableStateOf(AsyncData.Uninitialized) }
|
||||||
// the room member is not really live...
|
// the room member is not really live...
|
||||||
val isBlocked: MutableState<Async<Boolean>> = remember(roomMember) {
|
val isBlocked: MutableState<AsyncData<Boolean>> = remember(roomMember) {
|
||||||
val isIgnored = roomMember?.isIgnored
|
val isIgnored = roomMember?.isIgnored
|
||||||
if (isIgnored == null) {
|
if (isIgnored == null) {
|
||||||
mutableStateOf(Async.Uninitialized)
|
mutableStateOf(AsyncData.Uninitialized)
|
||||||
} else {
|
} else {
|
||||||
mutableStateOf(Async.Success(isIgnored))
|
mutableStateOf(AsyncData.Success(isIgnored))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
|
|
@ -90,7 +90,7 @@ class RoomMemberDetailsPresenter @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
RoomMemberDetailsEvents.ClearConfirmationDialog -> confirmationDialog = null
|
RoomMemberDetailsEvents.ClearConfirmationDialog -> confirmationDialog = null
|
||||||
RoomMemberDetailsEvents.ClearBlockUserError -> {
|
RoomMemberDetailsEvents.ClearBlockUserError -> {
|
||||||
isBlocked.value = Async.Success(isBlocked.value.dataOrNull().orFalse())
|
isBlocked.value = AsyncData.Success(isBlocked.value.dataOrNull().orFalse())
|
||||||
}
|
}
|
||||||
RoomMemberDetailsEvents.StartDM -> {
|
RoomMemberDetailsEvents.StartDM -> {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
|
|
@ -98,7 +98,7 @@ class RoomMemberDetailsPresenter @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RoomMemberDetailsEvents.ClearStartDMState -> {
|
RoomMemberDetailsEvents.ClearStartDMState -> {
|
||||||
startDmActionState.value = Async.Uninitialized
|
startDmActionState.value = AsyncData.Uninitialized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -127,30 +127,30 @@ class RoomMemberDetailsPresenter @AssistedInject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.blockUser(userId: UserId, isBlockedState: MutableState<Async<Boolean>>) = launch {
|
private fun CoroutineScope.blockUser(userId: UserId, isBlockedState: MutableState<AsyncData<Boolean>>) = launch {
|
||||||
isBlockedState.value = Async.Loading(false)
|
isBlockedState.value = AsyncData.Loading(false)
|
||||||
client.ignoreUser(userId)
|
client.ignoreUser(userId)
|
||||||
.fold(
|
.fold(
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
isBlockedState.value = Async.Success(true)
|
isBlockedState.value = AsyncData.Success(true)
|
||||||
room.updateMembers()
|
room.updateMembers()
|
||||||
},
|
},
|
||||||
onFailure = {
|
onFailure = {
|
||||||
isBlockedState.value = Async.Failure(it, false)
|
isBlockedState.value = AsyncData.Failure(it, false)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.unblockUser(userId: UserId, isBlockedState: MutableState<Async<Boolean>>) = launch {
|
private fun CoroutineScope.unblockUser(userId: UserId, isBlockedState: MutableState<AsyncData<Boolean>>) = launch {
|
||||||
isBlockedState.value = Async.Loading(true)
|
isBlockedState.value = AsyncData.Loading(true)
|
||||||
client.unignoreUser(userId)
|
client.unignoreUser(userId)
|
||||||
.fold(
|
.fold(
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
isBlockedState.value = Async.Success(false)
|
isBlockedState.value = AsyncData.Success(false)
|
||||||
room.updateMembers()
|
room.updateMembers()
|
||||||
},
|
},
|
||||||
onFailure = {
|
onFailure = {
|
||||||
isBlockedState.value = Async.Failure(it, true)
|
isBlockedState.value = AsyncData.Failure(it, true)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,15 +16,15 @@
|
||||||
|
|
||||||
package io.element.android.features.roomdetails.impl.members.details
|
package io.element.android.features.roomdetails.impl.members.details
|
||||||
|
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
||||||
data class RoomMemberDetailsState(
|
data class RoomMemberDetailsState(
|
||||||
val userId: String,
|
val userId: String,
|
||||||
val userName: String?,
|
val userName: String?,
|
||||||
val avatarUrl: String?,
|
val avatarUrl: String?,
|
||||||
val isBlocked: Async<Boolean>,
|
val isBlocked: AsyncData<Boolean>,
|
||||||
val startDmActionState: Async<RoomId>,
|
val startDmActionState: AsyncData<RoomId>,
|
||||||
val displayConfirmationDialog: ConfirmationDialog?,
|
val displayConfirmationDialog: ConfirmationDialog?,
|
||||||
val isCurrentUser: Boolean,
|
val isCurrentUser: Boolean,
|
||||||
val eventSink: (RoomMemberDetailsEvents) -> Unit
|
val eventSink: (RoomMemberDetailsEvents) -> Unit
|
||||||
|
|
|
||||||
|
|
@ -17,18 +17,18 @@
|
||||||
package io.element.android.features.roomdetails.impl.members.details
|
package io.element.android.features.roomdetails.impl.members.details
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
|
|
||||||
open class RoomMemberDetailsStateProvider : PreviewParameterProvider<RoomMemberDetailsState> {
|
open class RoomMemberDetailsStateProvider : PreviewParameterProvider<RoomMemberDetailsState> {
|
||||||
override val values: Sequence<RoomMemberDetailsState>
|
override val values: Sequence<RoomMemberDetailsState>
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
aRoomMemberDetailsState(),
|
aRoomMemberDetailsState(),
|
||||||
aRoomMemberDetailsState().copy(userName = null),
|
aRoomMemberDetailsState().copy(userName = null),
|
||||||
aRoomMemberDetailsState().copy(isBlocked = Async.Success(true)),
|
aRoomMemberDetailsState().copy(isBlocked = AsyncData.Success(true)),
|
||||||
aRoomMemberDetailsState().copy(displayConfirmationDialog = RoomMemberDetailsState.ConfirmationDialog.Block),
|
aRoomMemberDetailsState().copy(displayConfirmationDialog = RoomMemberDetailsState.ConfirmationDialog.Block),
|
||||||
aRoomMemberDetailsState().copy(displayConfirmationDialog = RoomMemberDetailsState.ConfirmationDialog.Unblock),
|
aRoomMemberDetailsState().copy(displayConfirmationDialog = RoomMemberDetailsState.ConfirmationDialog.Unblock),
|
||||||
aRoomMemberDetailsState().copy(isBlocked = Async.Loading(true)),
|
aRoomMemberDetailsState().copy(isBlocked = AsyncData.Loading(true)),
|
||||||
aRoomMemberDetailsState().copy(startDmActionState = Async.Loading()),
|
aRoomMemberDetailsState().copy(startDmActionState = AsyncData.Loading()),
|
||||||
// Add other states here
|
// Add other states here
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -37,8 +37,8 @@ fun aRoomMemberDetailsState() = RoomMemberDetailsState(
|
||||||
userId = "@daniel:domain.com",
|
userId = "@daniel:domain.com",
|
||||||
userName = "Daniel",
|
userName = "Daniel",
|
||||||
avatarUrl = null,
|
avatarUrl = null,
|
||||||
isBlocked = Async.Success(false),
|
isBlocked = AsyncData.Success(false),
|
||||||
startDmActionState = Async.Uninitialized,
|
startDmActionState = AsyncData.Uninitialized,
|
||||||
displayConfirmationDialog = null,
|
displayConfirmationDialog = null,
|
||||||
isCurrentUser = false,
|
isCurrentUser = false,
|
||||||
eventSink = {},
|
eventSink = {},
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ import androidx.compose.runtime.setValue
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||||
|
|
@ -61,11 +61,11 @@ class RoomNotificationSettingsPresenter @AssistedInject constructor(
|
||||||
mutableStateOf(null)
|
mutableStateOf(null)
|
||||||
}
|
}
|
||||||
val localCoroutineScope = rememberCoroutineScope()
|
val localCoroutineScope = rememberCoroutineScope()
|
||||||
val setNotificationSettingAction: MutableState<Async<Unit>> = remember { mutableStateOf(Async.Uninitialized) }
|
val setNotificationSettingAction: MutableState<AsyncData<Unit>> = remember { mutableStateOf(AsyncData.Uninitialized) }
|
||||||
val restoreDefaultAction: MutableState<Async<Unit>> = remember { mutableStateOf(Async.Uninitialized) }
|
val restoreDefaultAction: MutableState<AsyncData<Unit>> = remember { mutableStateOf(AsyncData.Uninitialized) }
|
||||||
|
|
||||||
val roomNotificationSettings: MutableState<Async<RoomNotificationSettings>> = remember {
|
val roomNotificationSettings: MutableState<AsyncData<RoomNotificationSettings>> = remember {
|
||||||
mutableStateOf(Async.Uninitialized)
|
mutableStateOf(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We store state of which mode the user has set via the notification service before the new push settings have been updated.
|
// We store state of which mode the user has set via the notification service before the new push settings have been updated.
|
||||||
|
|
@ -111,10 +111,10 @@ class RoomNotificationSettingsPresenter @AssistedInject constructor(
|
||||||
localCoroutineScope.restoreDefaultRoomNotificationMode(restoreDefaultAction, pendingSetDefault)
|
localCoroutineScope.restoreDefaultRoomNotificationMode(restoreDefaultAction, pendingSetDefault)
|
||||||
}
|
}
|
||||||
RoomNotificationSettingsEvents.ClearSetNotificationError -> {
|
RoomNotificationSettingsEvents.ClearSetNotificationError -> {
|
||||||
setNotificationSettingAction.value = Async.Uninitialized
|
setNotificationSettingAction.value = AsyncData.Uninitialized
|
||||||
}
|
}
|
||||||
RoomNotificationSettingsEvents.ClearRestoreDefaultError -> {
|
RoomNotificationSettingsEvents.ClearRestoreDefaultError -> {
|
||||||
restoreDefaultAction.value = Async.Uninitialized
|
restoreDefaultAction.value = AsyncData.Uninitialized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -136,7 +136,7 @@ class RoomNotificationSettingsPresenter @AssistedInject constructor(
|
||||||
@OptIn(FlowPreview::class)
|
@OptIn(FlowPreview::class)
|
||||||
private fun CoroutineScope.observeNotificationSettings(
|
private fun CoroutineScope.observeNotificationSettings(
|
||||||
pendingModeState: MutableState<RoomNotificationMode?>,
|
pendingModeState: MutableState<RoomNotificationMode?>,
|
||||||
roomNotificationSettings: MutableState<Async<RoomNotificationSettings>>
|
roomNotificationSettings: MutableState<AsyncData<RoomNotificationSettings>>
|
||||||
) {
|
) {
|
||||||
notificationSettingsService.notificationSettingsChangeFlow
|
notificationSettingsService.notificationSettingsChangeFlow
|
||||||
.debounce(0.5.seconds)
|
.debounce(0.5.seconds)
|
||||||
|
|
@ -148,7 +148,7 @@ class RoomNotificationSettingsPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
private fun CoroutineScope.fetchNotificationSettings(
|
private fun CoroutineScope.fetchNotificationSettings(
|
||||||
pendingModeState: MutableState<RoomNotificationMode?>,
|
pendingModeState: MutableState<RoomNotificationMode?>,
|
||||||
roomNotificationSettings: MutableState<Async<RoomNotificationSettings>>
|
roomNotificationSettings: MutableState<AsyncData<RoomNotificationSettings>>
|
||||||
) = launch {
|
) = launch {
|
||||||
suspend {
|
suspend {
|
||||||
pendingModeState.value = null
|
pendingModeState.value = null
|
||||||
|
|
@ -169,7 +169,7 @@ class RoomNotificationSettingsPresenter @AssistedInject constructor(
|
||||||
mode: RoomNotificationMode,
|
mode: RoomNotificationMode,
|
||||||
pendingModeState: MutableState<RoomNotificationMode?>,
|
pendingModeState: MutableState<RoomNotificationMode?>,
|
||||||
pendingDefaultState: MutableState<Boolean?>,
|
pendingDefaultState: MutableState<Boolean?>,
|
||||||
action: MutableState<Async<Unit>>
|
action: MutableState<AsyncData<Unit>>
|
||||||
) = launch {
|
) = launch {
|
||||||
suspend {
|
suspend {
|
||||||
pendingModeState.value = mode
|
pendingModeState.value = mode
|
||||||
|
|
@ -184,7 +184,7 @@ class RoomNotificationSettingsPresenter @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.restoreDefaultRoomNotificationMode(
|
private fun CoroutineScope.restoreDefaultRoomNotificationMode(
|
||||||
action: MutableState<Async<Unit>>,
|
action: MutableState<AsyncData<Unit>>,
|
||||||
pendingDefaultState: MutableState<Boolean?>
|
pendingDefaultState: MutableState<Boolean?>
|
||||||
) = launch {
|
) = launch {
|
||||||
suspend {
|
suspend {
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue