mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-05 04:15:12 +00:00
Merge branch 'develop' into fix/1874-empty-formatted-body
This commit is contained in:
+2
-2
@@ -8,6 +8,6 @@
|
||||
|
||||
package io.element.android.features.analytics.api
|
||||
|
||||
sealed interface AnalyticsOptInEvents {
|
||||
data class EnableAnalytics(val isEnabled: Boolean) : AnalyticsOptInEvents
|
||||
sealed interface AnalyticsOptInEvent {
|
||||
data class EnableAnalytics(val isEnabled: Boolean) : AnalyticsOptInEvent
|
||||
}
|
||||
+2
-2
@@ -8,11 +8,11 @@
|
||||
|
||||
package io.element.android.features.analytics.api.preferences
|
||||
|
||||
import io.element.android.features.analytics.api.AnalyticsOptInEvents
|
||||
import io.element.android.features.analytics.api.AnalyticsOptInEvent
|
||||
|
||||
data class AnalyticsPreferencesState(
|
||||
val applicationName: String,
|
||||
val isEnabled: Boolean,
|
||||
val policyUrl: String,
|
||||
val eventSink: (AnalyticsOptInEvents) -> Unit,
|
||||
val eventSink: (AnalyticsOptInEvent) -> Unit,
|
||||
)
|
||||
|
||||
+2
-2
@@ -13,7 +13,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import io.element.android.features.analytics.api.AnalyticsOptInEvents
|
||||
import io.element.android.features.analytics.api.AnalyticsOptInEvent
|
||||
import io.element.android.features.analytics.api.R
|
||||
import io.element.android.libraries.designsystem.components.LINK_TAG
|
||||
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
||||
@@ -30,7 +30,7 @@ fun AnalyticsPreferencesView(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
fun onEnabledChanged(isEnabled: Boolean) {
|
||||
state.eventSink(AnalyticsOptInEvents.EnableAnalytics(isEnabled = isEnabled))
|
||||
state.eventSink(AnalyticsOptInEvent.EnableAnalytics(isEnabled = isEnabled))
|
||||
}
|
||||
|
||||
val supportingText = stringResource(
|
||||
|
||||
+3
-3
@@ -12,7 +12,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.appconfig.AnalyticsConfig
|
||||
import io.element.android.features.analytics.api.AnalyticsOptInEvents
|
||||
import io.element.android.features.analytics.api.AnalyticsOptInEvent
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.core.meta.BuildMeta
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
@@ -28,9 +28,9 @@ class AnalyticsOptInPresenter(
|
||||
override fun present(): AnalyticsOptInState {
|
||||
val localCoroutineScope = rememberCoroutineScope()
|
||||
|
||||
fun handleEvent(event: AnalyticsOptInEvents) {
|
||||
fun handleEvent(event: AnalyticsOptInEvent) {
|
||||
when (event) {
|
||||
is AnalyticsOptInEvents.EnableAnalytics -> localCoroutineScope.setIsEnabled(event.isEnabled)
|
||||
is AnalyticsOptInEvent.EnableAnalytics -> localCoroutineScope.setIsEnabled(event.isEnabled)
|
||||
}
|
||||
localCoroutineScope.launch {
|
||||
analyticsService.setDidAskUserConsent()
|
||||
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
|
||||
package io.element.android.features.analytics.impl
|
||||
|
||||
import io.element.android.features.analytics.api.AnalyticsOptInEvents
|
||||
import io.element.android.features.analytics.api.AnalyticsOptInEvent
|
||||
|
||||
data class AnalyticsOptInState(
|
||||
val applicationName: String,
|
||||
val hasPolicyLink: Boolean,
|
||||
val eventSink: (AnalyticsOptInEvents) -> Unit
|
||||
val eventSink: (AnalyticsOptInEvent) -> Unit
|
||||
)
|
||||
|
||||
+3
-3
@@ -28,7 +28,7 @@ import androidx.compose.ui.unit.dp
|
||||
import io.element.android.appconfig.AnalyticsConfig
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.features.analytics.api.AnalyticsOptInEvents
|
||||
import io.element.android.features.analytics.api.AnalyticsOptInEvent
|
||||
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.pages.HeaderFooterPage
|
||||
@@ -55,11 +55,11 @@ fun AnalyticsOptInView(
|
||||
val eventSink = state.eventSink
|
||||
|
||||
fun onAcceptTerms() {
|
||||
eventSink(AnalyticsOptInEvents.EnableAnalytics(true))
|
||||
eventSink(AnalyticsOptInEvent.EnableAnalytics(true))
|
||||
}
|
||||
|
||||
fun onDeclineTerms() {
|
||||
eventSink(AnalyticsOptInEvents.EnableAnalytics(false))
|
||||
eventSink(AnalyticsOptInEvent.EnableAnalytics(false))
|
||||
}
|
||||
|
||||
BackHandler(onBack = ::onDeclineTerms)
|
||||
|
||||
+3
-3
@@ -13,7 +13,7 @@ import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.appconfig.AnalyticsConfig
|
||||
import io.element.android.features.analytics.api.AnalyticsOptInEvents
|
||||
import io.element.android.features.analytics.api.AnalyticsOptInEvent
|
||||
import io.element.android.features.analytics.api.preferences.AnalyticsPreferencesState
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.core.meta.BuildMeta
|
||||
@@ -31,9 +31,9 @@ class AnalyticsPreferencesPresenter(
|
||||
val localCoroutineScope = rememberCoroutineScope()
|
||||
val isEnabled = analyticsService.userConsentFlow.collectAsState(initial = false)
|
||||
|
||||
fun handleEvent(event: AnalyticsOptInEvents) {
|
||||
fun handleEvent(event: AnalyticsOptInEvent) {
|
||||
when (event) {
|
||||
is AnalyticsOptInEvents.EnableAnalytics -> localCoroutineScope.setIsEnabled(event.isEnabled)
|
||||
is AnalyticsOptInEvent.EnableAnalytics -> localCoroutineScope.setIsEnabled(event.isEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -12,7 +12,7 @@ import app.cash.molecule.RecompositionMode
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.analytics.api.AnalyticsOptInEvents
|
||||
import io.element.android.features.analytics.api.AnalyticsOptInEvent
|
||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
@@ -37,7 +37,7 @@ class AnalyticsOptInPresenterTest {
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(analyticsService.didAskUserConsentFlow.first()).isFalse()
|
||||
initialState.eventSink.invoke(AnalyticsOptInEvents.EnableAnalytics(true))
|
||||
initialState.eventSink.invoke(AnalyticsOptInEvent.EnableAnalytics(true))
|
||||
assertThat(analyticsService.didAskUserConsentFlow.first()).isTrue()
|
||||
assertThat(analyticsService.userConsentFlow.first()).isTrue()
|
||||
}
|
||||
@@ -55,7 +55,7 @@ class AnalyticsOptInPresenterTest {
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(analyticsService.didAskUserConsentFlow.first()).isFalse()
|
||||
initialState.eventSink.invoke(AnalyticsOptInEvents.EnableAnalytics(false))
|
||||
initialState.eventSink.invoke(AnalyticsOptInEvent.EnableAnalytics(false))
|
||||
assertThat(analyticsService.didAskUserConsentFlow.first()).isTrue()
|
||||
assertThat(analyticsService.userConsentFlow.first()).isFalse()
|
||||
}
|
||||
|
||||
+3
-3
@@ -13,7 +13,7 @@ import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.appconfig.AnalyticsConfig
|
||||
import io.element.android.features.analytics.api.AnalyticsOptInEvents
|
||||
import io.element.android.features.analytics.api.AnalyticsOptInEvent
|
||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
@@ -67,9 +67,9 @@ class AnalyticsPreferencesPresenterTest {
|
||||
skipItems(1)
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.isEnabled).isTrue()
|
||||
initialState.eventSink.invoke(AnalyticsOptInEvents.EnableAnalytics(false))
|
||||
initialState.eventSink.invoke(AnalyticsOptInEvent.EnableAnalytics(false))
|
||||
assertThat(awaitItem().isEnabled).isFalse()
|
||||
initialState.eventSink.invoke(AnalyticsOptInEvents.EnableAnalytics(true))
|
||||
initialState.eventSink.invoke(AnalyticsOptInEvent.EnableAnalytics(true))
|
||||
assertThat(awaitItem().isEnabled).isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -21,7 +21,7 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.features.createroom.impl.R
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleEvents
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleEvent
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleState
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleStatePreviewParam
|
||||
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
|
||||
@@ -56,7 +56,7 @@ fun AddPeopleView(
|
||||
Button(
|
||||
text = stringResource(CommonStrings.action_finish),
|
||||
onClick = {
|
||||
state.eventSink(InvitePeopleEvents.SendInvites)
|
||||
state.eventSink(InvitePeopleEvent.SendInvites)
|
||||
},
|
||||
enabled = state.canInvite,
|
||||
modifier = Modifier
|
||||
|
||||
+9
-9
@@ -11,13 +11,13 @@ package io.element.android.features.createroom.impl.configureroom
|
||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
||||
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
||||
|
||||
sealed interface ConfigureRoomEvents {
|
||||
data class RoomNameChanged(val name: String) : ConfigureRoomEvents
|
||||
data class TopicChanged(val topic: String) : ConfigureRoomEvents
|
||||
data class JoinRuleChanged(val joinRuleItem: JoinRuleItem) : ConfigureRoomEvents
|
||||
data class RoomAddressChanged(val roomAddress: String) : ConfigureRoomEvents
|
||||
data object CreateRoom : ConfigureRoomEvents
|
||||
data class HandleAvatarAction(val action: AvatarAction) : ConfigureRoomEvents
|
||||
data class SetParentSpace(val space: SpaceRoom?) : ConfigureRoomEvents
|
||||
data object CancelCreateRoom : ConfigureRoomEvents
|
||||
sealed interface ConfigureRoomEvent {
|
||||
data class RoomNameChanged(val name: String) : ConfigureRoomEvent
|
||||
data class TopicChanged(val topic: String) : ConfigureRoomEvent
|
||||
data class JoinRuleChanged(val joinRuleItem: JoinRuleItem) : ConfigureRoomEvent
|
||||
data class RoomAddressChanged(val roomAddress: String) : ConfigureRoomEvent
|
||||
data object CreateRoom : ConfigureRoomEvent
|
||||
data class HandleAvatarAction(val action: AvatarAction) : ConfigureRoomEvent
|
||||
data class SetParentSpace(val space: SpaceRoom?) : ConfigureRoomEvent
|
||||
data object CancelCreateRoom : ConfigureRoomEvent
|
||||
}
|
||||
+9
-9
@@ -188,14 +188,14 @@ class ConfigureRoomPresenter(
|
||||
localCoroutineScope.createRoom(config, createRoomAction)
|
||||
}
|
||||
|
||||
fun handleEvent(event: ConfigureRoomEvents) {
|
||||
fun handleEvent(event: ConfigureRoomEvent) {
|
||||
when (event) {
|
||||
is ConfigureRoomEvents.RoomNameChanged -> dataStore.setRoomName(event.name)
|
||||
is ConfigureRoomEvents.TopicChanged -> dataStore.setTopic(event.topic)
|
||||
is ConfigureRoomEvents.JoinRuleChanged -> dataStore.setJoinRule(event.joinRuleItem)
|
||||
is ConfigureRoomEvents.RoomAddressChanged -> dataStore.setRoomAddress(event.roomAddress)
|
||||
is ConfigureRoomEvents.CreateRoom -> createRoom(createRoomConfig)
|
||||
is ConfigureRoomEvents.HandleAvatarAction -> {
|
||||
is ConfigureRoomEvent.RoomNameChanged -> dataStore.setRoomName(event.name)
|
||||
is ConfigureRoomEvent.TopicChanged -> dataStore.setTopic(event.topic)
|
||||
is ConfigureRoomEvent.JoinRuleChanged -> dataStore.setJoinRule(event.joinRuleItem)
|
||||
is ConfigureRoomEvent.RoomAddressChanged -> dataStore.setRoomAddress(event.roomAddress)
|
||||
is ConfigureRoomEvent.CreateRoom -> createRoom(createRoomConfig)
|
||||
is ConfigureRoomEvent.HandleAvatarAction -> {
|
||||
when (event.action) {
|
||||
AvatarAction.ChoosePhoto -> galleryImagePicker.launch()
|
||||
AvatarAction.TakePhoto -> if (cameraPermissionState.permissionGranted) {
|
||||
@@ -207,10 +207,10 @@ class ConfigureRoomPresenter(
|
||||
AvatarAction.Remove -> dataStore.setAvatarUri(uri = null)
|
||||
}
|
||||
}
|
||||
is ConfigureRoomEvents.SetParentSpace -> {
|
||||
is ConfigureRoomEvent.SetParentSpace -> {
|
||||
dataStore.setParentSpace(event.space, false)
|
||||
}
|
||||
ConfigureRoomEvents.CancelCreateRoom -> {
|
||||
ConfigureRoomEvent.CancelCreateRoom -> {
|
||||
createRoomAction.value = AsyncAction.Uninitialized
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ data class ConfigureRoomState(
|
||||
val homeserverName: String,
|
||||
val availableJoinRules: ImmutableList<JoinRuleItem>,
|
||||
val spaces: ImmutableList<SpaceRoom>,
|
||||
val eventSink: (ConfigureRoomEvents) -> Unit
|
||||
val eventSink: (ConfigureRoomEvent) -> Unit
|
||||
) {
|
||||
val isValid: Boolean = config.roomName?.isNotEmpty() == true &&
|
||||
(config.visibilityState is RoomVisibilityState.Private || roomAddressValidity == RoomAddressValidity.Valid) &&
|
||||
|
||||
+1
-1
@@ -143,7 +143,7 @@ fun aConfigureRoomState(
|
||||
)
|
||||
},
|
||||
spaces: List<SpaceRoom> = emptyList(),
|
||||
eventSink: (ConfigureRoomEvents) -> Unit = { },
|
||||
eventSink: (ConfigureRoomEvent) -> Unit = { },
|
||||
) = ConfigureRoomState(
|
||||
config = config,
|
||||
isSpace = isSpace,
|
||||
|
||||
+9
-9
@@ -96,7 +96,7 @@ fun ConfigureRoomView(
|
||||
onBackClick = onBackClick,
|
||||
onNextClick = {
|
||||
focusManager.clearFocus()
|
||||
state.eventSink(ConfigureRoomEvents.CreateRoom)
|
||||
state.eventSink(ConfigureRoomEvent.CreateRoom)
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -114,20 +114,20 @@ fun ConfigureRoomView(
|
||||
avatarUri = state.config.avatarUri,
|
||||
roomName = state.config.roomName.orEmpty(),
|
||||
onAvatarClick = ::onAvatarClick,
|
||||
onChangeRoomName = { state.eventSink(ConfigureRoomEvents.RoomNameChanged(it)) },
|
||||
onChangeRoomName = { state.eventSink(ConfigureRoomEvent.RoomNameChanged(it)) },
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
RoomTopic(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
topic = state.config.topic.orEmpty(),
|
||||
onTopicChange = { state.eventSink(ConfigureRoomEvents.TopicChanged(it)) },
|
||||
onTopicChange = { state.eventSink(ConfigureRoomEvent.TopicChanged(it)) },
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
if (!state.isSpace && state.spaces.isNotEmpty()) {
|
||||
SelectParentSpaceOptions(
|
||||
spaces = state.spaces,
|
||||
selectedSpace = state.config.parentSpace,
|
||||
onSelectSpace = { state.eventSink(ConfigureRoomEvents.SetParentSpace(it)) },
|
||||
onSelectSpace = { state.eventSink(ConfigureRoomEvent.SetParentSpace(it)) },
|
||||
)
|
||||
}
|
||||
RoomJoinRuleOptions(
|
||||
@@ -136,7 +136,7 @@ fun ConfigureRoomView(
|
||||
parentSpace = state.config.parentSpace,
|
||||
onOptionClick = {
|
||||
focusManager.clearFocus()
|
||||
state.eventSink(ConfigureRoomEvents.JoinRuleChanged(it))
|
||||
state.eventSink(ConfigureRoomEvent.JoinRuleChanged(it))
|
||||
},
|
||||
)
|
||||
if (state.config.visibilityState !is RoomVisibilityState.Private) {
|
||||
@@ -146,7 +146,7 @@ fun ConfigureRoomView(
|
||||
address = state.config.visibilityState.roomAddress().getOrNull().orEmpty(),
|
||||
homeserverName = state.homeserverName,
|
||||
addressValidity = state.roomAddressValidity,
|
||||
onAddressChange = { state.eventSink(ConfigureRoomEvents.RoomAddressChanged(it)) },
|
||||
onAddressChange = { state.eventSink(ConfigureRoomEvent.RoomAddressChanged(it)) },
|
||||
label = null,
|
||||
supportingText = stringResource(R.string.screen_create_room_room_address_section_footer),
|
||||
)
|
||||
@@ -158,7 +158,7 @@ fun ConfigureRoomView(
|
||||
actions = state.avatarActions,
|
||||
isVisible = isAvatarActionsSheetVisible.value,
|
||||
onDismiss = { isAvatarActionsSheetVisible.value = false },
|
||||
onSelectAction = { state.eventSink(ConfigureRoomEvents.HandleAvatarAction(it)) }
|
||||
onSelectAction = { state.eventSink(ConfigureRoomEvent.HandleAvatarAction(it)) }
|
||||
)
|
||||
|
||||
AsyncActionView(
|
||||
@@ -170,8 +170,8 @@ fun ConfigureRoomView(
|
||||
},
|
||||
onSuccess = { onCreateRoomSuccess(it) },
|
||||
errorMessage = { stringResource(if (isSpace) R.string.screen_create_room_error_creating_space else R.string.screen_create_room_error_creating_room) },
|
||||
onRetry = { state.eventSink(ConfigureRoomEvents.CreateRoom) },
|
||||
onErrorDismiss = { state.eventSink(ConfigureRoomEvents.CancelCreateRoom) },
|
||||
onRetry = { state.eventSink(ConfigureRoomEvent.CreateRoom) },
|
||||
onErrorDismiss = { state.eventSink(ConfigureRoomEvent.CancelCreateRoom) },
|
||||
)
|
||||
|
||||
PermissionsView(
|
||||
|
||||
+36
-36
@@ -12,7 +12,7 @@ import androidx.core.net.toUri
|
||||
import app.cash.turbine.TurbineTestContext
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import im.vector.app.features.analytics.plan.CreatedRoom
|
||||
import io.element.android.features.createroom.impl.configureroom.ConfigureRoomEvents
|
||||
import io.element.android.features.createroom.impl.configureroom.ConfigureRoomEvent
|
||||
import io.element.android.features.createroom.impl.configureroom.ConfigureRoomPresenter
|
||||
import io.element.android.features.createroom.impl.configureroom.ConfigureRoomState
|
||||
import io.element.android.features.createroom.impl.configureroom.CreateRoomConfig
|
||||
@@ -106,14 +106,14 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
|
||||
assertThat(initialState.isValid).isFalse()
|
||||
|
||||
// Room name not empty
|
||||
initialState.eventSink(ConfigureRoomEvents.RoomNameChanged(A_ROOM_NAME))
|
||||
initialState.eventSink(ConfigureRoomEvent.RoomNameChanged(A_ROOM_NAME))
|
||||
var newState: ConfigureRoomState = awaitItem()
|
||||
config = config.copy(roomName = A_ROOM_NAME)
|
||||
assertThat(newState.config).isEqualTo(config)
|
||||
assertThat(newState.isValid).isTrue()
|
||||
|
||||
// Clear room name
|
||||
newState.eventSink(ConfigureRoomEvents.RoomNameChanged(""))
|
||||
newState.eventSink(ConfigureRoomEvent.RoomNameChanged(""))
|
||||
newState = awaitItem()
|
||||
config = config.copy(roomName = null)
|
||||
assertThat(newState.config).isEqualTo(config)
|
||||
@@ -136,13 +136,13 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
|
||||
var expectedConfig = CreateRoomConfig()
|
||||
assertThat(initialState.config).isEqualTo(expectedConfig)
|
||||
// Room name
|
||||
initialState.eventSink(ConfigureRoomEvents.RoomNameChanged(A_ROOM_NAME))
|
||||
initialState.eventSink(ConfigureRoomEvent.RoomNameChanged(A_ROOM_NAME))
|
||||
var newState = awaitItem()
|
||||
expectedConfig = expectedConfig.copy(roomName = A_ROOM_NAME)
|
||||
assertThat(newState.config).isEqualTo(expectedConfig)
|
||||
|
||||
// Room topic
|
||||
newState.eventSink(ConfigureRoomEvents.TopicChanged(A_MESSAGE))
|
||||
newState.eventSink(ConfigureRoomEvent.TopicChanged(A_MESSAGE))
|
||||
newState = awaitItem()
|
||||
expectedConfig = expectedConfig.copy(topic = A_MESSAGE)
|
||||
assertThat(newState.config).isEqualTo(expectedConfig)
|
||||
@@ -153,7 +153,7 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
|
||||
// From gallery
|
||||
val uriFromGallery = AN_URI_FROM_GALLERY
|
||||
pickerProvider.givenResult(uriFromGallery.toUri())
|
||||
newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
||||
newState.eventSink(ConfigureRoomEvent.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
||||
newState = awaitItem()
|
||||
expectedConfig = expectedConfig.copy(avatarUri = uriFromGallery)
|
||||
assertThat(newState.config).isEqualTo(expectedConfig)
|
||||
@@ -161,7 +161,7 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
|
||||
val uriFromCamera = AN_URI_FROM_CAMERA
|
||||
pickerProvider.givenResult(uriFromCamera.toUri())
|
||||
assertThat(newState.cameraPermissionState.permissionGranted).isFalse()
|
||||
newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.TakePhoto))
|
||||
newState.eventSink(ConfigureRoomEvent.HandleAvatarAction(AvatarAction.TakePhoto))
|
||||
newState = awaitItem()
|
||||
assertThat(newState.cameraPermissionState.showDialog).isTrue()
|
||||
permissionsPresenter.setPermissionGranted()
|
||||
@@ -173,18 +173,18 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
|
||||
// Do it again, no permission is requested
|
||||
val uriFromCamera2 = AN_URI_FROM_CAMERA_2
|
||||
pickerProvider.givenResult(uriFromCamera2.toUri())
|
||||
newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.TakePhoto))
|
||||
newState.eventSink(ConfigureRoomEvent.HandleAvatarAction(AvatarAction.TakePhoto))
|
||||
newState = awaitItem()
|
||||
expectedConfig = expectedConfig.copy(avatarUri = uriFromCamera2)
|
||||
assertThat(newState.config).isEqualTo(expectedConfig)
|
||||
// Remove
|
||||
newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.Remove))
|
||||
newState.eventSink(ConfigureRoomEvent.HandleAvatarAction(AvatarAction.Remove))
|
||||
newState = awaitItem()
|
||||
expectedConfig = expectedConfig.copy(avatarUri = null)
|
||||
assertThat(newState.config).isEqualTo(expectedConfig)
|
||||
|
||||
// Room privacy
|
||||
newState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
|
||||
newState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
|
||||
newState = awaitItem()
|
||||
expectedConfig = expectedConfig.copy(
|
||||
visibilityState = RoomVisibilityState.Public(
|
||||
@@ -208,7 +208,7 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
|
||||
|
||||
matrixClient.givenCreateRoomResult(createRoomResult)
|
||||
|
||||
initialState.eventSink(ConfigureRoomEvents.CreateRoom)
|
||||
initialState.eventSink(ConfigureRoomEvent.CreateRoom)
|
||||
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
val stateAfterCreateRoom = awaitItem()
|
||||
assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Success::class.java)
|
||||
@@ -239,13 +239,13 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
|
||||
|
||||
// Use a public parent space so AskToJoin is a valid option
|
||||
val parentSpace = aSpaceRoom(joinRule = JoinRule.Public)
|
||||
initialState.eventSink(ConfigureRoomEvents.SetParentSpace(parentSpace))
|
||||
initialState.eventSink(ConfigureRoomEvent.SetParentSpace(parentSpace))
|
||||
assertThat(awaitItem().config.parentSpace).isEqualTo(parentSpace)
|
||||
|
||||
initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.AskToJoin))
|
||||
initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.AskToJoin))
|
||||
assertThat(awaitItem().config.visibilityState.joinRuleItem).isEqualTo(JoinRuleItem.PublicVisibility.AskToJoin)
|
||||
|
||||
initialState.eventSink(ConfigureRoomEvents.CreateRoom)
|
||||
initialState.eventSink(ConfigureRoomEvent.CreateRoom)
|
||||
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
val stateAfterCreateRoom = awaitItem()
|
||||
|
||||
@@ -282,13 +282,13 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
|
||||
|
||||
// Use a public parent space so AskToJoin is a valid option
|
||||
val parentSpace = aSpaceRoom(joinRule = JoinRule.Public)
|
||||
initialState.eventSink(ConfigureRoomEvents.SetParentSpace(parentSpace))
|
||||
initialState.eventSink(ConfigureRoomEvent.SetParentSpace(parentSpace))
|
||||
assertThat(awaitItem().config.parentSpace).isEqualTo(parentSpace)
|
||||
|
||||
initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.AskToJoin))
|
||||
initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.AskToJoin))
|
||||
assertThat(awaitItem().config.visibilityState.joinRuleItem).isEqualTo(JoinRuleItem.PublicVisibility.AskToJoin)
|
||||
|
||||
initialState.eventSink(ConfigureRoomEvents.CreateRoom)
|
||||
initialState.eventSink(ConfigureRoomEvent.CreateRoom)
|
||||
|
||||
// We immediately receive the room power levels info needed for adding the child to a space
|
||||
val powerLevels = RoomPowerLevels(
|
||||
@@ -339,7 +339,7 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
|
||||
|
||||
matrixClient.givenCreateRoomResult(createRoomResult)
|
||||
|
||||
initialState.eventSink(ConfigureRoomEvents.CreateRoom)
|
||||
initialState.eventSink(ConfigureRoomEvent.CreateRoom)
|
||||
skipItems(2)
|
||||
|
||||
val analyticsEvent = analyticsService.capturedEvents.filterIsInstance<CreatedRoom>().firstOrNull()
|
||||
@@ -369,14 +369,14 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
|
||||
mediaPreProcessor.givenResult(Result.success(MediaUploadInfo.Image(file, mockk(), mockk())))
|
||||
matrixClient.givenUploadMediaResult(Result.failure(AN_EXCEPTION))
|
||||
|
||||
initialState.eventSink(ConfigureRoomEvents.CreateRoom)
|
||||
initialState.eventSink(ConfigureRoomEvent.CreateRoom)
|
||||
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
val stateAfterCreateRoom = awaitItem()
|
||||
assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
assertThat(analyticsService.capturedEvents.filterIsInstance<CreatedRoom>()).isEmpty()
|
||||
|
||||
matrixClient.givenUploadMediaResult(Result.success(AN_AVATAR_URL))
|
||||
stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom)
|
||||
stateAfterCreateRoom.eventSink(ConfigureRoomEvent.CreateRoom)
|
||||
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Success::class.java)
|
||||
@@ -399,14 +399,14 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
|
||||
fakeMatrixClient.givenCreateRoomResult(createRoomResult)
|
||||
|
||||
// Create
|
||||
initialState.eventSink(ConfigureRoomEvents.CreateRoom)
|
||||
initialState.eventSink(ConfigureRoomEvent.CreateRoom)
|
||||
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
val stateAfterCreateRoom = awaitItem()
|
||||
assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
assertThat((stateAfterCreateRoom.createRoomAction as? AsyncAction.Failure)?.error).isEqualTo(createRoomResult.exceptionOrNull())
|
||||
|
||||
// Retry
|
||||
stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom)
|
||||
stateAfterCreateRoom.eventSink(ConfigureRoomEvent.CreateRoom)
|
||||
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
val stateAfterRetry = awaitItem()
|
||||
@@ -414,7 +414,7 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
|
||||
assertThat((stateAfterRetry.createRoomAction as? AsyncAction.Failure)?.error).isEqualTo(createRoomResult.exceptionOrNull())
|
||||
|
||||
// Cancel
|
||||
stateAfterRetry.eventSink(ConfigureRoomEvents.CancelCreateRoom)
|
||||
stateAfterRetry.eventSink(ConfigureRoomEvent.CancelCreateRoom)
|
||||
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||
}
|
||||
}
|
||||
@@ -430,9 +430,9 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = initialState()
|
||||
initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
|
||||
initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
|
||||
skipItems(1)
|
||||
initialState.eventSink(ConfigureRoomEvents.RoomAddressChanged("invalid address"))
|
||||
initialState.eventSink(ConfigureRoomEvent.RoomAddressChanged("invalid address"))
|
||||
skipItems(1)
|
||||
advanceUntilIdle()
|
||||
awaitItem().also { state ->
|
||||
@@ -450,9 +450,9 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = initialState()
|
||||
initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
|
||||
initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
|
||||
skipItems(1)
|
||||
initialState.eventSink(ConfigureRoomEvents.RoomAddressChanged("address"))
|
||||
initialState.eventSink(ConfigureRoomEvent.RoomAddressChanged("address"))
|
||||
skipItems(1)
|
||||
advanceUntilIdle()
|
||||
awaitItem().also { state ->
|
||||
@@ -470,9 +470,9 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = initialState()
|
||||
initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
|
||||
initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
|
||||
skipItems(1)
|
||||
initialState.eventSink(ConfigureRoomEvents.RoomAddressChanged("address"))
|
||||
initialState.eventSink(ConfigureRoomEvent.RoomAddressChanged("address"))
|
||||
skipItems(1)
|
||||
advanceUntilIdle()
|
||||
awaitItem().also { state ->
|
||||
@@ -488,22 +488,22 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
|
||||
val initialState = initialState()
|
||||
|
||||
// First change the join rule to public
|
||||
initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
|
||||
initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
|
||||
assertThat(awaitItem().config.visibilityState).isInstanceOf(RoomVisibilityState.Public::class.java)
|
||||
|
||||
// Then check changing the parent space resets it to private
|
||||
// (via LaunchedEffect fallback since Public is not in availableJoinRules for non-public parent)
|
||||
initialState.eventSink(ConfigureRoomEvents.SetParentSpace(aSpaceRoom()))
|
||||
initialState.eventSink(ConfigureRoomEvent.SetParentSpace(aSpaceRoom()))
|
||||
skipItems(1) // Skip intermediate state
|
||||
assertThat(awaitItem().config.visibilityState).isEqualTo(RoomVisibilityState.Private(JoinRuleItem.PrivateVisibility.Private))
|
||||
|
||||
// If we change the join rule back to public
|
||||
initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
|
||||
initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
|
||||
skipItems(1) // Skip intermediate state (Public is still invalid)
|
||||
assertThat(awaitItem().config.visibilityState).isEqualTo(RoomVisibilityState.Private(JoinRuleItem.PrivateVisibility.Private))
|
||||
|
||||
// Then remove the parent space, the join rule stays private
|
||||
initialState.eventSink(ConfigureRoomEvents.SetParentSpace(null))
|
||||
initialState.eventSink(ConfigureRoomEvent.SetParentSpace(null))
|
||||
assertThat(awaitItem().config.visibilityState).isEqualTo(RoomVisibilityState.Private(JoinRuleItem.PrivateVisibility.Private))
|
||||
}
|
||||
}
|
||||
@@ -514,7 +514,7 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
|
||||
presenter.test {
|
||||
val initialState = initialState()
|
||||
|
||||
initialState.eventSink(ConfigureRoomEvents.SetParentSpace(aSpaceRoom()))
|
||||
initialState.eventSink(ConfigureRoomEvent.SetParentSpace(aSpaceRoom()))
|
||||
|
||||
assertThat(awaitError())
|
||||
}
|
||||
@@ -534,8 +534,8 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
|
||||
presenter.test {
|
||||
val initialState = initialState()
|
||||
|
||||
initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PrivateVisibility.Private))
|
||||
initialState.eventSink(ConfigureRoomEvents.CreateRoom)
|
||||
initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PrivateVisibility.Private))
|
||||
initialState.eventSink(ConfigureRoomEvent.CreateRoom)
|
||||
|
||||
assertThat(awaitItem().createRoomAction.isLoading()).isTrue()
|
||||
assertThat(awaitItem().createRoomAction.isSuccess()).isTrue()
|
||||
|
||||
+5
-5
@@ -8,9 +8,9 @@
|
||||
|
||||
package io.element.android.features.logout.impl
|
||||
|
||||
sealed interface AccountDeactivationEvents {
|
||||
data class SetEraseData(val eraseData: Boolean) : AccountDeactivationEvents
|
||||
data class SetPassword(val password: String) : AccountDeactivationEvents
|
||||
data class DeactivateAccount(val isRetry: Boolean) : AccountDeactivationEvents
|
||||
data object CloseDialogs : AccountDeactivationEvents
|
||||
sealed interface AccountDeactivationEvent {
|
||||
data class SetEraseData(val eraseData: Boolean) : AccountDeactivationEvent
|
||||
data class SetPassword(val password: String) : AccountDeactivationEvent
|
||||
data class DeactivateAccount(val isRetry: Boolean) : AccountDeactivationEvent
|
||||
data object CloseDialogs : AccountDeactivationEvent
|
||||
}
|
||||
+5
-5
@@ -34,19 +34,19 @@ class AccountDeactivationPresenter(
|
||||
|
||||
val formState = remember { mutableStateOf(DeactivateFormState.Default) }
|
||||
|
||||
fun handleEvent(event: AccountDeactivationEvents) {
|
||||
fun handleEvent(event: AccountDeactivationEvent) {
|
||||
when (event) {
|
||||
is AccountDeactivationEvents.SetEraseData -> {
|
||||
is AccountDeactivationEvent.SetEraseData -> {
|
||||
updateFormState(formState) {
|
||||
copy(eraseData = event.eraseData)
|
||||
}
|
||||
}
|
||||
is AccountDeactivationEvents.SetPassword -> {
|
||||
is AccountDeactivationEvent.SetPassword -> {
|
||||
updateFormState(formState) {
|
||||
copy(password = event.password)
|
||||
}
|
||||
}
|
||||
is AccountDeactivationEvents.DeactivateAccount ->
|
||||
is AccountDeactivationEvent.DeactivateAccount ->
|
||||
if (action.value.isConfirming() || event.isRetry) {
|
||||
localCoroutineScope.deactivateAccount(
|
||||
formState = formState.value,
|
||||
@@ -55,7 +55,7 @@ class AccountDeactivationPresenter(
|
||||
} else {
|
||||
action.value = AsyncAction.ConfirmingNoParams
|
||||
}
|
||||
AccountDeactivationEvents.CloseDialogs -> {
|
||||
AccountDeactivationEvent.CloseDialogs -> {
|
||||
action.value = AsyncAction.Uninitialized
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ import kotlinx.parcelize.Parcelize
|
||||
data class AccountDeactivationState(
|
||||
val deactivateFormState: DeactivateFormState,
|
||||
val accountDeactivationAction: AsyncAction<Unit>,
|
||||
val eventSink: (AccountDeactivationEvents) -> Unit,
|
||||
val eventSink: (AccountDeactivationEvent) -> Unit,
|
||||
) {
|
||||
val submitEnabled: Boolean
|
||||
get() = accountDeactivationAction is AsyncAction.Uninitialized &&
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ internal fun aDeactivateFormState(
|
||||
internal fun anAccountDeactivationState(
|
||||
deactivateFormState: DeactivateFormState = aDeactivateFormState(),
|
||||
accountDeactivationAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
eventSink: (AccountDeactivationEvents) -> Unit = {},
|
||||
eventSink: (AccountDeactivationEvent) -> Unit = {},
|
||||
) = AccountDeactivationState(
|
||||
deactivateFormState = deactivateFormState,
|
||||
accountDeactivationAction = accountDeactivationAction,
|
||||
|
||||
+7
-7
@@ -102,14 +102,14 @@ fun AccountDeactivationView(
|
||||
Content(
|
||||
state = state,
|
||||
onSubmitClick = {
|
||||
eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false))
|
||||
eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false))
|
||||
}
|
||||
)
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Buttons(
|
||||
state = state,
|
||||
onSubmitClick = {
|
||||
eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false))
|
||||
eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false))
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -117,13 +117,13 @@ fun AccountDeactivationView(
|
||||
AccountDeactivationActionDialog(
|
||||
state.accountDeactivationAction,
|
||||
onConfirmClick = {
|
||||
eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false))
|
||||
eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false))
|
||||
},
|
||||
onRetryClick = {
|
||||
eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = true))
|
||||
eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = true))
|
||||
},
|
||||
onDismissDialog = {
|
||||
eventSink(AccountDeactivationEvents.CloseDialogs)
|
||||
eventSink(AccountDeactivationEvent.CloseDialogs)
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -239,7 +239,7 @@ private fun Content(
|
||||
headline = stringResource(R.string.screen_deactivate_account_delete_all_messages),
|
||||
value = eraseData,
|
||||
onChange = {
|
||||
eventSink(AccountDeactivationEvents.SetEraseData(it))
|
||||
eventSink(AccountDeactivationEvent.SetEraseData(it))
|
||||
},
|
||||
enabled = !isLoading,
|
||||
)
|
||||
@@ -275,7 +275,7 @@ private fun Content(
|
||||
onValueChange = {
|
||||
val sanitized = it.sanitize()
|
||||
passwordFieldState = sanitized
|
||||
eventSink(AccountDeactivationEvents.SetPassword(sanitized))
|
||||
eventSink(AccountDeactivationEvent.SetPassword(sanitized))
|
||||
},
|
||||
placeholder = stringResource(CommonStrings.common_password),
|
||||
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
|
||||
|
||||
+15
-15
@@ -47,11 +47,11 @@ class AccountDeactivationPresenterTest {
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.deactivateFormState).isEqualTo(DeactivateFormState.Default)
|
||||
initialState.eventSink(AccountDeactivationEvents.SetEraseData(true))
|
||||
initialState.eventSink(AccountDeactivationEvent.SetEraseData(true))
|
||||
val updatedState = awaitItem()
|
||||
assertThat(updatedState.deactivateFormState).isEqualTo(DeactivateFormState.Default.copy(eraseData = true))
|
||||
assertThat(updatedState.submitEnabled).isFalse()
|
||||
updatedState.eventSink(AccountDeactivationEvents.SetPassword("password"))
|
||||
updatedState.eventSink(AccountDeactivationEvent.SetPassword("password"))
|
||||
val updatedState2 = awaitItem()
|
||||
assertThat(updatedState2.deactivateFormState).isEqualTo(DeactivateFormState(password = "password", eraseData = true))
|
||||
assertThat(updatedState2.submitEnabled).isTrue()
|
||||
@@ -71,12 +71,12 @@ class AccountDeactivationPresenterTest {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(AccountDeactivationEvents.SetPassword("password"))
|
||||
initialState.eventSink(AccountDeactivationEvent.SetPassword("password"))
|
||||
skipItems(1)
|
||||
initialState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false))
|
||||
initialState.eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false))
|
||||
val updatedState = awaitItem()
|
||||
assertThat(updatedState.accountDeactivationAction).isEqualTo(AsyncAction.ConfirmingNoParams)
|
||||
updatedState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false))
|
||||
updatedState.eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false))
|
||||
val updatedState2 = awaitItem()
|
||||
assertThat(updatedState2.accountDeactivationAction).isEqualTo(AsyncAction.Loading)
|
||||
val finalState = awaitItem()
|
||||
@@ -98,20 +98,20 @@ class AccountDeactivationPresenterTest {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(AccountDeactivationEvents.SetPassword("password"))
|
||||
initialState.eventSink(AccountDeactivationEvents.SetEraseData(true))
|
||||
initialState.eventSink(AccountDeactivationEvent.SetPassword("password"))
|
||||
initialState.eventSink(AccountDeactivationEvent.SetEraseData(true))
|
||||
skipItems(2)
|
||||
initialState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false))
|
||||
initialState.eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false))
|
||||
val updatedState = awaitItem()
|
||||
assertThat(updatedState.accountDeactivationAction).isEqualTo(AsyncAction.ConfirmingNoParams)
|
||||
updatedState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false))
|
||||
updatedState.eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false))
|
||||
val updatedState2 = awaitItem()
|
||||
assertThat(updatedState2.accountDeactivationAction).isEqualTo(AsyncAction.Loading)
|
||||
val finalState = awaitItem()
|
||||
assertThat(finalState.accountDeactivationAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION))
|
||||
recorder.assertions().isCalledOnce().with(value("password"), value(true))
|
||||
// Retry
|
||||
finalState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = true))
|
||||
finalState.eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = true))
|
||||
val finalState2 = awaitItem()
|
||||
assertThat(finalState2.accountDeactivationAction).isEqualTo(AsyncAction.Loading)
|
||||
assertThat(awaitItem().accountDeactivationAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION))
|
||||
@@ -131,20 +131,20 @@ class AccountDeactivationPresenterTest {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(AccountDeactivationEvents.SetPassword("password"))
|
||||
initialState.eventSink(AccountDeactivationEvents.SetEraseData(true))
|
||||
initialState.eventSink(AccountDeactivationEvent.SetPassword("password"))
|
||||
initialState.eventSink(AccountDeactivationEvent.SetEraseData(true))
|
||||
skipItems(2)
|
||||
initialState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false))
|
||||
initialState.eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false))
|
||||
val updatedState = awaitItem()
|
||||
assertThat(updatedState.accountDeactivationAction).isEqualTo(AsyncAction.ConfirmingNoParams)
|
||||
updatedState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false))
|
||||
updatedState.eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false))
|
||||
val updatedState2 = awaitItem()
|
||||
assertThat(updatedState2.accountDeactivationAction).isEqualTo(AsyncAction.Loading)
|
||||
val finalState = awaitItem()
|
||||
assertThat(finalState.accountDeactivationAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION))
|
||||
recorder.assertions().isCalledOnce().with(value("password"), value(true))
|
||||
// Cancel
|
||||
finalState.eventSink(AccountDeactivationEvents.CloseDialogs)
|
||||
finalState.eventSink(AccountDeactivationEvent.CloseDialogs)
|
||||
val finalState2 = awaitItem()
|
||||
assertThat(finalState2.accountDeactivationAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
}
|
||||
|
||||
+13
-13
@@ -35,7 +35,7 @@ import org.robolectric.annotation.Config
|
||||
class AccountDeactivationViewTest : RobolectricTest() {
|
||||
@Test
|
||||
fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<AccountDeactivationEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<AccountDeactivationEvent>(expectEvents = false)
|
||||
ensureCalledOnce {
|
||||
setAccountDeactivationView(
|
||||
state = anAccountDeactivationState(eventSink = eventsRecorder),
|
||||
@@ -48,7 +48,7 @@ class AccountDeactivationViewTest : RobolectricTest() {
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `clicking on Deactivate emits the expected Event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<AccountDeactivationEvents>()
|
||||
val eventsRecorder = EventsRecorder<AccountDeactivationEvent>()
|
||||
setAccountDeactivationView(
|
||||
state = anAccountDeactivationState(
|
||||
deactivateFormState = aDeactivateFormState(
|
||||
@@ -58,12 +58,12 @@ class AccountDeactivationViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
clickOn(CommonStrings.action_delete)
|
||||
eventsRecorder.assertSingle(AccountDeactivationEvents.DeactivateAccount(false))
|
||||
eventsRecorder.assertSingle(AccountDeactivationEvent.DeactivateAccount(false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on Deactivate on the confirmation dialog emits the expected Event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<AccountDeactivationEvents>()
|
||||
val eventsRecorder = EventsRecorder<AccountDeactivationEvent>()
|
||||
setAccountDeactivationView(
|
||||
state = anAccountDeactivationState(
|
||||
deactivateFormState = aDeactivateFormState(
|
||||
@@ -74,12 +74,12 @@ class AccountDeactivationViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
pressTag(TestTags.dialogPositive.value)
|
||||
eventsRecorder.assertSingle(AccountDeactivationEvents.DeactivateAccount(false))
|
||||
eventsRecorder.assertSingle(AccountDeactivationEvent.DeactivateAccount(false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on retry on the confirmation dialog emits the expected Event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<AccountDeactivationEvents>()
|
||||
val eventsRecorder = EventsRecorder<AccountDeactivationEvent>()
|
||||
setAccountDeactivationView(
|
||||
state = anAccountDeactivationState(
|
||||
deactivateFormState = aDeactivateFormState(
|
||||
@@ -90,24 +90,24 @@ class AccountDeactivationViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
clickOn(CommonStrings.action_retry)
|
||||
eventsRecorder.assertSingle(AccountDeactivationEvents.DeactivateAccount(true))
|
||||
eventsRecorder.assertSingle(AccountDeactivationEvent.DeactivateAccount(true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `switching on the erase all switch emits the expected Event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<AccountDeactivationEvents>()
|
||||
val eventsRecorder = EventsRecorder<AccountDeactivationEvent>()
|
||||
setAccountDeactivationView(
|
||||
state = anAccountDeactivationState(
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
clickOn(R.string.screen_deactivate_account_delete_all_messages)
|
||||
eventsRecorder.assertSingle(AccountDeactivationEvents.SetEraseData(true))
|
||||
eventsRecorder.assertSingle(AccountDeactivationEvent.SetEraseData(true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `switching off the erase all switch emits the expected Event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<AccountDeactivationEvents>()
|
||||
val eventsRecorder = EventsRecorder<AccountDeactivationEvent>()
|
||||
setAccountDeactivationView(
|
||||
state = anAccountDeactivationState(
|
||||
deactivateFormState = aDeactivateFormState(
|
||||
@@ -117,13 +117,13 @@ class AccountDeactivationViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
clickOn(R.string.screen_deactivate_account_delete_all_messages)
|
||||
eventsRecorder.assertSingle(AccountDeactivationEvents.SetEraseData(false))
|
||||
eventsRecorder.assertSingle(AccountDeactivationEvent.SetEraseData(false))
|
||||
}
|
||||
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `typing text in the password field emits the expected Event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<AccountDeactivationEvents>()
|
||||
val eventsRecorder = EventsRecorder<AccountDeactivationEvent>()
|
||||
setAccountDeactivationView(
|
||||
state = anAccountDeactivationState(
|
||||
deactivateFormState = aDeactivateFormState(
|
||||
@@ -133,7 +133,7 @@ class AccountDeactivationViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
onNodeWithTag(TestTags.loginPassword.value).performTextInput("A")
|
||||
eventsRecorder.assertSingle(AccountDeactivationEvents.SetPassword("A$A_PASSWORD"))
|
||||
eventsRecorder.assertSingle(AccountDeactivationEvent.SetPassword("A$A_PASSWORD"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.enterprise.api
|
||||
|
||||
import android.app.Activity
|
||||
|
||||
/**
|
||||
* A hook that is called during app startup, from the `MainActivity`.
|
||||
*
|
||||
* Note that the hook is run in the background: it will not block startup of the app.
|
||||
*/
|
||||
fun interface AppStartupHook {
|
||||
/** @param activity The activity which is active while the app starts (i.e. the `MainActivity`). */
|
||||
suspend fun onAppStartup(activity: Activity)
|
||||
}
|
||||
+2
-2
@@ -8,6 +8,6 @@
|
||||
|
||||
package io.element.android.features.forward.impl
|
||||
|
||||
sealed interface ForwardMessagesEvents {
|
||||
data object ClearError : ForwardMessagesEvents
|
||||
sealed interface ForwardMessagesEvent {
|
||||
data object ClearError : ForwardMessagesEvent
|
||||
}
|
||||
+2
-2
@@ -48,9 +48,9 @@ class ForwardMessagesPresenter(
|
||||
|
||||
@Composable
|
||||
override fun present(): ForwardMessagesState {
|
||||
fun handleEvent(event: ForwardMessagesEvents) {
|
||||
fun handleEvent(event: ForwardMessagesEvent) {
|
||||
when (event) {
|
||||
ForwardMessagesEvents.ClearError -> forwardingActionState.value = AsyncAction.Uninitialized
|
||||
ForwardMessagesEvent.ClearError -> forwardingActionState.value = AsyncAction.Uninitialized
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -13,5 +13,5 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
||||
data class ForwardMessagesState(
|
||||
val forwardAction: AsyncAction<List<RoomId>>,
|
||||
val eventSink: (ForwardMessagesEvents) -> Unit
|
||||
val eventSink: (ForwardMessagesEvent) -> Unit
|
||||
)
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ open class ForwardMessagesStatePreviewParam : PreviewParameterProvider<ForwardMe
|
||||
|
||||
fun aForwardMessagesState(
|
||||
forwardAction: AsyncAction<List<RoomId>> = AsyncAction.Uninitialized,
|
||||
eventSink: (ForwardMessagesEvents) -> Unit = {}
|
||||
eventSink: (ForwardMessagesEvent) -> Unit = {}
|
||||
) = ForwardMessagesState(
|
||||
forwardAction = forwardAction,
|
||||
eventSink = eventSink
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ fun ForwardMessagesView(
|
||||
stringResource(id = CommonStrings.error_unknown)
|
||||
},
|
||||
onErrorDismiss = {
|
||||
state.eventSink(ForwardMessagesEvents.ClearError)
|
||||
state.eventSink(ForwardMessagesEvent.ClearError)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ class ForwardMessagesPresenterTest {
|
||||
val failedForwardState = awaitItem()
|
||||
assertThat(failedForwardState.forwardAction.isFailure()).isTrue()
|
||||
// Then clear error
|
||||
failedForwardState.eventSink(ForwardMessagesEvents.ClearError)
|
||||
failedForwardState.eventSink(ForwardMessagesEvent.ClearError)
|
||||
assertThat(awaitItem().forwardAction.isUninitialized()).isTrue()
|
||||
forwardEventLambda.assertions().isCalledOnce()
|
||||
}
|
||||
|
||||
+3
-3
@@ -29,7 +29,7 @@ import org.junit.Test
|
||||
class ForwardMessagesViewTest : RobolectricTest() {
|
||||
@Test
|
||||
fun `cancel error emits the expected event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<ForwardMessagesEvents>()
|
||||
val eventsRecorder = EventsRecorder<ForwardMessagesEvent>()
|
||||
setForwardMessagesView(
|
||||
aForwardMessagesState(
|
||||
forwardAction = AsyncAction.Failure(AN_EXCEPTION),
|
||||
@@ -37,13 +37,13 @@ class ForwardMessagesViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
pressTag(TestTags.dialogPositive.value)
|
||||
eventsRecorder.assertSingle(ForwardMessagesEvents.ClearError)
|
||||
eventsRecorder.assertSingle(ForwardMessagesEvent.ClearError)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `success invokes onForwardSuccess`() = runAndroidComposeUiTest {
|
||||
val data = listOf(A_ROOM_ID)
|
||||
val eventsRecorder = EventsRecorder<ForwardMessagesEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<ForwardMessagesEvent>(expectEvents = false)
|
||||
ensureCalledOnceWithParam<List<RoomId>?>(data) { callback ->
|
||||
setForwardMessagesView(
|
||||
aForwardMessagesState(
|
||||
|
||||
+3
-3
@@ -8,7 +8,7 @@
|
||||
|
||||
package io.element.android.features.ftue.impl.notifications
|
||||
|
||||
sealed interface NotificationsOptInEvents {
|
||||
data object ContinueClicked : NotificationsOptInEvents
|
||||
data object NotNowClicked : NotificationsOptInEvents
|
||||
sealed interface NotificationsOptInEvent {
|
||||
data object ContinueClicked : NotificationsOptInEvent
|
||||
data object NotNowClicked : NotificationsOptInEvent
|
||||
}
|
||||
+3
-3
@@ -52,16 +52,16 @@ class NotificationsOptInPresenter(
|
||||
override fun present(): NotificationsOptInState {
|
||||
val notificationsPermissionsState = postNotificationPermissionsPresenter.present()
|
||||
|
||||
fun handleEvent(event: NotificationsOptInEvents) {
|
||||
fun handleEvent(event: NotificationsOptInEvent) {
|
||||
when (event) {
|
||||
NotificationsOptInEvents.ContinueClicked -> {
|
||||
NotificationsOptInEvent.ContinueClicked -> {
|
||||
if (notificationsPermissionsState.permissionGranted) {
|
||||
callback.onNotificationsOptInFinished()
|
||||
} else {
|
||||
notificationsPermissionsState.eventSink(PermissionsEvent.RequestPermissions)
|
||||
}
|
||||
}
|
||||
NotificationsOptInEvents.NotNowClicked -> {
|
||||
NotificationsOptInEvent.NotNowClicked -> {
|
||||
if (buildVersionSdkIntProvider.isAtLeast(Build.VERSION_CODES.TIRAMISU)) {
|
||||
appCoroutineScope.setPermissionDenied()
|
||||
}
|
||||
|
||||
+1
-1
@@ -12,5 +12,5 @@ import io.element.android.libraries.permissions.api.PermissionsState
|
||||
|
||||
data class NotificationsOptInState(
|
||||
val notificationsPermissionState: PermissionsState,
|
||||
val eventSink: (NotificationsOptInEvents) -> Unit
|
||||
val eventSink: (NotificationsOptInEvent) -> Unit
|
||||
)
|
||||
|
||||
+2
-2
@@ -86,14 +86,14 @@ private fun NotificationsOptInFooter(state: NotificationsOptInState) {
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(CommonStrings.action_ok),
|
||||
onClick = {
|
||||
state.eventSink(NotificationsOptInEvents.ContinueClicked)
|
||||
state.eventSink(NotificationsOptInEvent.ContinueClicked)
|
||||
}
|
||||
)
|
||||
TextButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(CommonStrings.action_not_now),
|
||||
onClick = {
|
||||
state.eventSink(NotificationsOptInEvents.NotNowClicked)
|
||||
state.eventSink(NotificationsOptInEvent.NotNowClicked)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
+2
-2
@@ -15,7 +15,7 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutEvents
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutEvent
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutState
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
@@ -67,7 +67,7 @@ class ChooseSelfVerificationModePresenter(
|
||||
|
||||
fun handleEvent(event: ChooseSelfVerificationModeEvent) {
|
||||
when (event) {
|
||||
ChooseSelfVerificationModeEvent.SignOut -> directLogoutState.eventSink(DirectLogoutEvents.Logout(ignoreSdkError = false))
|
||||
ChooseSelfVerificationModeEvent.SignOut -> directLogoutState.eventSink(DirectLogoutEvent.Logout(ignoreSdkError = false))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -55,7 +55,7 @@ class NotificationsOptInPresenterTest {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(NotificationsOptInEvents.ContinueClicked)
|
||||
initialState.eventSink(NotificationsOptInEvent.ContinueClicked)
|
||||
assertThat(awaitItem().notificationsPermissionState.showDialog).isTrue()
|
||||
}
|
||||
}
|
||||
@@ -70,7 +70,7 @@ class NotificationsOptInPresenterTest {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(NotificationsOptInEvents.ContinueClicked)
|
||||
initialState.eventSink(NotificationsOptInEvent.ContinueClicked)
|
||||
assertThat(isFinished).isTrue()
|
||||
}
|
||||
}
|
||||
@@ -86,7 +86,7 @@ class NotificationsOptInPresenterTest {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(NotificationsOptInEvents.NotNowClicked)
|
||||
initialState.eventSink(NotificationsOptInEvent.NotNowClicked)
|
||||
assertThat(isFinished).isTrue()
|
||||
}
|
||||
}
|
||||
@@ -105,7 +105,7 @@ class NotificationsOptInPresenterTest {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(NotificationsOptInEvents.NotNowClicked)
|
||||
initialState.eventSink(NotificationsOptInEvent.NotNowClicked)
|
||||
|
||||
// Allow background coroutines to run
|
||||
runCurrent()
|
||||
|
||||
+3
-3
@@ -9,7 +9,7 @@
|
||||
package io.element.android.features.ftue.impl.sessionverification.choosemode
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutEvents
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutEvent
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutState
|
||||
import io.element.android.features.logout.api.direct.aDirectLogoutState
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
@@ -112,7 +112,7 @@ class ChooseSessionVerificationModePresenterTest {
|
||||
|
||||
@Test
|
||||
fun `sing out action triggers a direct logout`() = runTest {
|
||||
val logoutEventRecorder = lambdaRecorder<DirectLogoutEvents, Unit> {}
|
||||
val logoutEventRecorder = lambdaRecorder<DirectLogoutEvent, Unit> {}
|
||||
val logoutPresenter = Presenter<DirectLogoutState> {
|
||||
aDirectLogoutState(eventSink = logoutEventRecorder)
|
||||
}
|
||||
@@ -121,7 +121,7 @@ class ChooseSessionVerificationModePresenterTest {
|
||||
val initial = awaitItem()
|
||||
initial.eventSink(ChooseSelfVerificationModeEvent.SignOut)
|
||||
logoutEventRecorder.assertions().isCalledOnce()
|
||||
.with(value(DirectLogoutEvents.Logout(ignoreSdkError = false)))
|
||||
.with(value(DirectLogoutEvent.Logout(ignoreSdkError = false)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -16,7 +16,7 @@ import io.element.android.libraries.designsystem.components.Announcement
|
||||
import io.element.android.libraries.designsystem.components.AnnouncementType
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.push.api.battery.BatteryOptimizationEvents
|
||||
import io.element.android.libraries.push.api.battery.BatteryOptimizationEvent
|
||||
import io.element.android.libraries.push.api.battery.BatteryOptimizationState
|
||||
import io.element.android.libraries.push.api.battery.aBatteryOptimizationState
|
||||
|
||||
@@ -31,8 +31,8 @@ internal fun BatteryOptimizationBanner(
|
||||
description = stringResource(R.string.banner_battery_optimization_content_android),
|
||||
type = AnnouncementType.Actionable(
|
||||
actionText = stringResource(R.string.banner_battery_optimization_submit_android),
|
||||
onActionClick = { state.eventSink(BatteryOptimizationEvents.RequestDisableOptimizations) },
|
||||
onDismissClick = { state.eventSink(BatteryOptimizationEvents.Dismiss) },
|
||||
onActionClick = { state.eventSink(BatteryOptimizationEvent.RequestDisableOptimizations) },
|
||||
onDismissClick = { state.eventSink(BatteryOptimizationEvent.Dismiss) },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
+3
-3
@@ -16,7 +16,7 @@ import io.element.android.libraries.designsystem.components.Announcement
|
||||
import io.element.android.libraries.designsystem.components.AnnouncementType
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsEvents
|
||||
import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsEvent
|
||||
import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState
|
||||
import io.element.android.libraries.fullscreenintent.api.aFullScreenIntentPermissionsState
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
@@ -31,8 +31,8 @@ fun FullScreenIntentPermissionBanner(
|
||||
description = stringResource(R.string.full_screen_intent_banner_message),
|
||||
type = AnnouncementType.Actionable(
|
||||
actionText = stringResource(CommonStrings.action_continue),
|
||||
onDismissClick = { state.eventSink(FullScreenIntentPermissionsEvents.Dismiss) },
|
||||
onActionClick = { state.eventSink(FullScreenIntentPermissionsEvents.OpenSettings) },
|
||||
onDismissClick = { state.eventSink(FullScreenIntentPermissionsEvent.Dismiss) },
|
||||
onActionClick = { state.eventSink(FullScreenIntentPermissionsEvent.OpenSettings) },
|
||||
),
|
||||
modifier = modifier.roomListBannerPadding(),
|
||||
)
|
||||
|
||||
+55
-49
@@ -53,8 +53,6 @@ import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.heading
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
@@ -80,7 +78,7 @@ import io.element.android.libraries.designsystem.modifiers.backgroundVerticalGra
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE
|
||||
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
|
||||
import io.element.android.libraries.designsystem.text.AdaptativeTitle
|
||||
import io.element.android.libraries.designsystem.theme.components.DropdownMenu
|
||||
import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
@@ -141,15 +139,21 @@ fun HomeTopBar(
|
||||
else -> stringResource(selectedNavigationItem.labelRes)
|
||||
}
|
||||
}
|
||||
HomeNavigationBarItem.Spaces -> stringResource(selectedNavigationItem.labelRes)
|
||||
HomeNavigationBarItem.Spaces -> null
|
||||
}
|
||||
displayTitle?.let {
|
||||
val style = when (spaceFiltersState) {
|
||||
// Space name
|
||||
is SpaceFiltersState.Selected -> ElementTheme.typography.fontHeadingSmMedium
|
||||
// "Chats"
|
||||
else -> ElementTheme.typography.fontHeadingLgBold
|
||||
}
|
||||
AdaptativeTitle(
|
||||
title = displayTitle,
|
||||
style = style,
|
||||
twoLinesStyle = ElementTheme.typography.fontHeadingSmMedium,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.semantics {
|
||||
heading()
|
||||
},
|
||||
style = ElementTheme.typography.aliasScreenTitle,
|
||||
text = displayTitle,
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
NavigationIcon(
|
||||
@@ -179,7 +183,9 @@ fun HomeTopBar(
|
||||
TopAppBarScrollBehaviorLayout(scrollBehavior = scrollBehavior) {
|
||||
RoomListFiltersView(
|
||||
state = filtersState,
|
||||
modifier = Modifier.padding(bottom = 16.dp).padding(contentPadding)
|
||||
modifier = Modifier
|
||||
.padding(bottom = 16.dp)
|
||||
.padding(contentPadding)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -348,45 +354,45 @@ private fun AccountIcon(
|
||||
),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
val avatarData by remember(matrixUser) {
|
||||
derivedStateOf {
|
||||
matrixUser.getAvatarData(size = AvatarSize.CurrentUserTopBar)
|
||||
}
|
||||
val avatarData by remember(matrixUser) {
|
||||
derivedStateOf {
|
||||
matrixUser.getAvatarData(size = AvatarSize.CurrentUserTopBar)
|
||||
}
|
||||
val statusEmoji = matrixUser.displayedStatus?.toEmojiText()
|
||||
val avatarModifier = if (statusEmoji != null) {
|
||||
Modifier.eraseStatusEmojiBackground(
|
||||
parentSize = AvatarSize.CurrentUserTopBar.dp,
|
||||
layoutDirection = LocalLayoutDirection.current,
|
||||
)
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
Avatar(
|
||||
avatarData = avatarData,
|
||||
avatarType = AvatarType.User,
|
||||
modifier = avatarModifier,
|
||||
contentDescription = if (isCurrentAccount) {
|
||||
if (showAvatarIndicator) {
|
||||
stringResource(CommonStrings.a11y_settings_with_required_action)
|
||||
} else {
|
||||
stringResource(CommonStrings.common_settings)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
}
|
||||
val statusEmoji = matrixUser.displayedStatus?.toEmojiText()
|
||||
val avatarModifier = if (statusEmoji != null) {
|
||||
Modifier.eraseStatusEmojiBackground(
|
||||
parentSize = AvatarSize.CurrentUserTopBar.dp,
|
||||
layoutDirection = LocalLayoutDirection.current,
|
||||
)
|
||||
if (statusEmoji != null) {
|
||||
StatusEmojiBadge(
|
||||
emoji = statusEmoji,
|
||||
modifier = Modifier.align(Alignment.BottomEnd),
|
||||
)
|
||||
}
|
||||
if (showAvatarIndicator) {
|
||||
RedIndicatorAtom(
|
||||
modifier = Modifier.align(Alignment.TopEnd)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
Avatar(
|
||||
avatarData = avatarData,
|
||||
avatarType = AvatarType.User,
|
||||
modifier = avatarModifier,
|
||||
contentDescription = if (isCurrentAccount) {
|
||||
if (showAvatarIndicator) {
|
||||
stringResource(CommonStrings.a11y_settings_with_required_action)
|
||||
} else {
|
||||
stringResource(CommonStrings.common_settings)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
if (statusEmoji != null) {
|
||||
StatusEmojiBadge(
|
||||
emoji = statusEmoji,
|
||||
modifier = Modifier.align(Alignment.BottomEnd),
|
||||
)
|
||||
}
|
||||
if (showAvatarIndicator) {
|
||||
RedIndicatorAtom(
|
||||
modifier = Modifier.align(Alignment.TopEnd)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -19,6 +19,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.eventformatter.api.RoomLatestEventFormatter
|
||||
import io.element.android.libraries.matrix.api.room.CallIntentConsensus
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.RoomInfo
|
||||
import io.element.android.libraries.matrix.api.roomlist.LatestEventValue
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import io.element.android.libraries.matrix.ui.model.dmUserStatus
|
||||
@@ -47,7 +48,7 @@ class RoomListRoomSummaryFactory(
|
||||
mode = DateFormatterMode.TimeOrDate,
|
||||
useRelative = true,
|
||||
),
|
||||
latestEvent = computeLatestEvent(roomSummary.latestEvent, roomInfo.isDm),
|
||||
latestEvent = computeLatestEvent(roomSummary.latestEvent, roomInfo.hasOnlyTwoMembers()),
|
||||
avatarData = avatarData,
|
||||
userDefinedNotificationMode = roomInfo.userDefinedNotificationMode,
|
||||
hasRoomCall = roomInfo.hasRoomCall,
|
||||
@@ -81,6 +82,10 @@ class RoomListRoomSummaryFactory(
|
||||
)
|
||||
}
|
||||
|
||||
private fun RoomInfo.hasOnlyTwoMembers(): Boolean {
|
||||
return isDm || activeMembersCount <= 2
|
||||
}
|
||||
|
||||
private fun computeLatestEvent(latestEvent: LatestEventValue, dm: Boolean): LatestEvent {
|
||||
return when (latestEvent) {
|
||||
is LatestEventValue.None -> {
|
||||
|
||||
+2
-2
@@ -37,8 +37,8 @@ import io.element.android.features.home.impl.spacefilters.SpaceFiltersState
|
||||
import io.element.android.features.home.impl.spacefilters.into
|
||||
import io.element.android.features.home.impl.spacefilters.selectedFilter
|
||||
import io.element.android.features.invite.api.SeenInvitesStore
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents.AcceptInvite
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents.DeclineInvite
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvent.AcceptInvite
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvent.DeclineInvite
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomEvent
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomState
|
||||
|
||||
-3
@@ -7,9 +7,6 @@
|
||||
|
||||
package io.element.android.features.home.impl.search
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
|
||||
@Immutable
|
||||
sealed interface GlobalSearchEvent {
|
||||
object ClearQuery : GlobalSearchEvent
|
||||
object ToggleSearchVisibility : GlobalSearchEvent
|
||||
|
||||
+1
-1
@@ -8,4 +8,4 @@
|
||||
|
||||
package io.element.android.features.home.impl.spaces
|
||||
|
||||
sealed interface HomeSpacesEvents
|
||||
sealed interface HomeSpacesEvent
|
||||
+1
-1
@@ -39,7 +39,7 @@ class HomeSpacesPresenter(
|
||||
seenInvitesStore.seenRoomIds().map { it.toImmutableSet() }
|
||||
}.collectAsState(persistentSetOf())
|
||||
|
||||
fun handleEvent(event: HomeSpacesEvents) {
|
||||
fun handleEvent(event: HomeSpacesEvent) {
|
||||
// when (event) { }
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ data class HomeSpacesState(
|
||||
val seenSpaceInvites: ImmutableSet<RoomId>,
|
||||
val hideInvitesAvatar: Boolean,
|
||||
val canExploreSpaces: Boolean,
|
||||
val eventSink: (HomeSpacesEvents) -> Unit,
|
||||
val eventSink: (HomeSpacesEvent) -> Unit,
|
||||
)
|
||||
|
||||
sealed interface CurrentSpace {
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ internal fun aHomeSpacesState(
|
||||
seenSpaceInvites: Set<RoomId> = emptySet(),
|
||||
hideInvitesAvatar: Boolean = false,
|
||||
canExploreSpaces: Boolean = true,
|
||||
eventSink: (HomeSpacesEvents) -> Unit = {},
|
||||
eventSink: (HomeSpacesEvent) -> Unit = {},
|
||||
) = HomeSpacesState(
|
||||
space = space,
|
||||
spaceRooms = spaceRooms.toImmutableList(),
|
||||
|
||||
+1
-1
@@ -143,7 +143,7 @@ private fun EmptySpaceHomeView(
|
||||
)
|
||||
Text(
|
||||
text = stringResource(CommonStrings.screen_space_list_empty_state_title),
|
||||
style = ElementTheme.typography.fontHeadingLgBold,
|
||||
style = ElementTheme.typography.fontHeadingMdBold,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
|
||||
+26
@@ -8,10 +8,36 @@
|
||||
|
||||
package io.element.android.features.home.impl.datasource
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.dateformatter.api.DateFormatter
|
||||
import io.element.android.libraries.dateformatter.test.FakeDateFormatter
|
||||
import io.element.android.libraries.eventformatter.api.RoomLatestEventFormatter
|
||||
import io.element.android.libraries.eventformatter.test.FakeRoomLatestEventFormatter
|
||||
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||
import io.element.android.libraries.matrix.test.room.aRoomSummary
|
||||
import org.junit.Test
|
||||
|
||||
class RoomListRoomSummaryFactoryTest {
|
||||
@Test
|
||||
fun `a room with two members does not prefix its preview with the sender`() {
|
||||
val formatter = FakeRoomLatestEventFormatter()
|
||||
val factory = aRoomListRoomSummaryFactory(roomLatestEventFormatter = formatter)
|
||||
|
||||
factory.create(aRoomSummary(info = aRoomInfo(isDm = false, activeMembersCount = 2)))
|
||||
|
||||
assertThat(formatter.lastIsDmRoom).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a room with more than two members prefixes its preview with the sender`() {
|
||||
val formatter = FakeRoomLatestEventFormatter()
|
||||
val factory = aRoomListRoomSummaryFactory(roomLatestEventFormatter = formatter)
|
||||
|
||||
factory.create(aRoomSummary(info = aRoomInfo(isDm = false, activeMembersCount = 3)))
|
||||
|
||||
assertThat(formatter.lastIsDmRoom).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
fun aRoomListRoomSummaryFactory(
|
||||
dateFormatter: DateFormatter = FakeDateFormatter { _, _, _ -> "Today" },
|
||||
|
||||
+4
-4
@@ -25,7 +25,7 @@ import io.element.android.features.home.impl.search.aRoomListSearchState
|
||||
import io.element.android.features.home.impl.spacefilters.SpaceFiltersState
|
||||
import io.element.android.features.home.impl.spacefilters.aDisabledSpaceFiltersState
|
||||
import io.element.android.features.invite.api.SeenInvitesStore
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvent
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.acceptdecline.anAcceptDeclineInviteState
|
||||
import io.element.android.features.invite.test.InMemorySeenInvitesStore
|
||||
@@ -474,7 +474,7 @@ class RoomListPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - when a room is invited then accept and decline events are sent to acceptDeclinePresenter`() = runTest {
|
||||
val eventSinkRecorder = lambdaRecorder { _: AcceptDeclineInviteEvents -> }
|
||||
val eventSinkRecorder = lambdaRecorder { _: AcceptDeclineInviteEvent -> }
|
||||
val acceptDeclinePresenter = Presenter {
|
||||
anAcceptDeclineInviteState(eventSink = eventSinkRecorder)
|
||||
}
|
||||
@@ -513,8 +513,8 @@ class RoomListPresenterTest {
|
||||
assert(eventSinkRecorder)
|
||||
.isCalledExactly(2)
|
||||
.withSequence(
|
||||
listOf(value(AcceptDeclineInviteEvents.AcceptInvite(inviteData))),
|
||||
listOf(value(AcceptDeclineInviteEvents.DeclineInvite(inviteData, blockUser = false, shouldConfirm = false))),
|
||||
listOf(value(AcceptDeclineInviteEvent.AcceptInvite(inviteData))),
|
||||
listOf(value(AcceptDeclineInviteEvent.DeclineInvite(inviteData, blockUser = false, shouldConfirm = false))),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -13,7 +13,7 @@ import io.element.android.features.invite.api.InviteData
|
||||
/**
|
||||
* Events sent to the accept/decline invite presenter, from whichever screen is showing the invitation.
|
||||
*/
|
||||
interface AcceptDeclineInviteEvents {
|
||||
data class AcceptInvite(val invite: InviteData) : AcceptDeclineInviteEvents
|
||||
data class DeclineInvite(val invite: InviteData, val blockUser: Boolean, val shouldConfirm: Boolean) : AcceptDeclineInviteEvents
|
||||
interface AcceptDeclineInviteEvent {
|
||||
data class AcceptInvite(val invite: InviteData) : AcceptDeclineInviteEvent
|
||||
data class DeclineInvite(val invite: InviteData, val blockUser: Boolean, val shouldConfirm: Boolean) : AcceptDeclineInviteEvent
|
||||
}
|
||||
+1
-1
@@ -14,5 +14,5 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
||||
data class AcceptDeclineInviteState(
|
||||
val acceptAction: AsyncAction<RoomId>,
|
||||
val declineAction: AsyncAction<RoomId>,
|
||||
val eventSink: (AcceptDeclineInviteEvents) -> Unit,
|
||||
val eventSink: (AcceptDeclineInviteEvent) -> Unit,
|
||||
)
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
||||
fun anAcceptDeclineInviteState(
|
||||
acceptAction: AsyncAction<RoomId> = AsyncAction.Uninitialized,
|
||||
declineAction: AsyncAction<RoomId> = AsyncAction.Uninitialized,
|
||||
eventSink: (AcceptDeclineInviteEvents) -> Unit = {},
|
||||
eventSink: (AcceptDeclineInviteEvent) -> Unit = {},
|
||||
) = AcceptDeclineInviteState(
|
||||
acceptAction = acceptAction,
|
||||
declineAction = declineAction,
|
||||
|
||||
+6
-6
@@ -15,7 +15,7 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.features.invite.api.InviteData
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvent
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.acceptdecline.ConfirmingDeclineInvite
|
||||
import io.element.android.features.invite.impl.AcceptInvite
|
||||
@@ -40,13 +40,13 @@ class AcceptDeclineInvitePresenter(
|
||||
val declinedAction: MutableState<AsyncAction<RoomId>> =
|
||||
remember { mutableStateOf(AsyncAction.Uninitialized) }
|
||||
|
||||
fun handleEvent(event: AcceptDeclineInviteEvents) {
|
||||
fun handleEvent(event: AcceptDeclineInviteEvent) {
|
||||
when (event) {
|
||||
is AcceptDeclineInviteEvents.AcceptInvite -> {
|
||||
is AcceptDeclineInviteEvent.AcceptInvite -> {
|
||||
localCoroutineScope.acceptInvite(event.invite.roomId, acceptedAction)
|
||||
}
|
||||
|
||||
is AcceptDeclineInviteEvents.DeclineInvite -> {
|
||||
is AcceptDeclineInviteEvent.DeclineInvite -> {
|
||||
val inviteData = event.invite
|
||||
if (event.shouldConfirm) {
|
||||
declinedAction.value = ConfirmingDeclineInvite(inviteData, event.blockUser)
|
||||
@@ -58,11 +58,11 @@ class AcceptDeclineInvitePresenter(
|
||||
)
|
||||
}
|
||||
}
|
||||
is InternalAcceptDeclineInviteEvents.ClearAcceptActionState -> {
|
||||
is InternalAcceptDeclineInviteEvent.ClearAcceptActionState -> {
|
||||
acceptedAction.value = AsyncAction.Uninitialized
|
||||
}
|
||||
|
||||
is InternalAcceptDeclineInviteEvents.ClearDeclineActionState -> {
|
||||
is InternalAcceptDeclineInviteEvent.ClearDeclineActionState -> {
|
||||
declinedAction.value = AsyncAction.Uninitialized
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -14,7 +14,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import io.element.android.features.invite.api.InviteData
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvent
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.acceptdecline.ConfirmingDeclineInvite
|
||||
import io.element.android.features.invite.impl.AcceptInvite
|
||||
@@ -37,11 +37,11 @@ fun AcceptDeclineInviteView(
|
||||
AsyncActionView(
|
||||
async = state.acceptAction,
|
||||
onSuccess = { roomId ->
|
||||
state.eventSink(InternalAcceptDeclineInviteEvents.ClearAcceptActionState)
|
||||
state.eventSink(InternalAcceptDeclineInviteEvent.ClearAcceptActionState)
|
||||
onAcceptInviteSuccess(roomId)
|
||||
},
|
||||
onErrorDismiss = {
|
||||
state.eventSink(InternalAcceptDeclineInviteEvents.ClearAcceptActionState)
|
||||
state.eventSink(InternalAcceptDeclineInviteEvent.ClearAcceptActionState)
|
||||
},
|
||||
errorTitle = {
|
||||
stringResource(CommonStrings.common_something_went_wrong)
|
||||
@@ -57,11 +57,11 @@ fun AcceptDeclineInviteView(
|
||||
AsyncActionView(
|
||||
async = state.declineAction,
|
||||
onSuccess = { roomId ->
|
||||
state.eventSink(InternalAcceptDeclineInviteEvents.ClearDeclineActionState)
|
||||
state.eventSink(InternalAcceptDeclineInviteEvent.ClearDeclineActionState)
|
||||
onDeclineInviteSuccess(roomId)
|
||||
},
|
||||
onErrorDismiss = {
|
||||
state.eventSink(InternalAcceptDeclineInviteEvents.ClearDeclineActionState)
|
||||
state.eventSink(InternalAcceptDeclineInviteEvent.ClearDeclineActionState)
|
||||
},
|
||||
errorTitle = {
|
||||
stringResource(CommonStrings.common_something_went_wrong)
|
||||
@@ -77,7 +77,7 @@ fun AcceptDeclineInviteView(
|
||||
blockUser = confirming.blockUser,
|
||||
onConfirmClick = {
|
||||
state.eventSink(
|
||||
AcceptDeclineInviteEvents.DeclineInvite(
|
||||
AcceptDeclineInviteEvent.DeclineInvite(
|
||||
confirming.inviteData,
|
||||
blockUser = confirming.blockUser,
|
||||
shouldConfirm = false
|
||||
@@ -85,7 +85,7 @@ fun AcceptDeclineInviteView(
|
||||
)
|
||||
},
|
||||
onDismissClick = {
|
||||
state.eventSink(InternalAcceptDeclineInviteEvents.ClearDeclineActionState)
|
||||
state.eventSink(InternalAcceptDeclineInviteEvent.ClearDeclineActionState)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
+4
-4
@@ -8,9 +8,9 @@
|
||||
|
||||
package io.element.android.features.invite.impl.acceptdecline
|
||||
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvent
|
||||
|
||||
sealed interface InternalAcceptDeclineInviteEvents : AcceptDeclineInviteEvents {
|
||||
data object ClearAcceptActionState : InternalAcceptDeclineInviteEvents
|
||||
data object ClearDeclineActionState : InternalAcceptDeclineInviteEvents
|
||||
sealed interface InternalAcceptDeclineInviteEvent : AcceptDeclineInviteEvent {
|
||||
data object ClearAcceptActionState : InternalAcceptDeclineInviteEvent
|
||||
data object ClearDeclineActionState : InternalAcceptDeclineInviteEvent
|
||||
}
|
||||
+6
-6
@@ -8,10 +8,10 @@
|
||||
|
||||
package io.element.android.features.invite.impl.declineandblock
|
||||
|
||||
sealed interface DeclineAndBlockEvents {
|
||||
data class UpdateReportReason(val reason: String) : DeclineAndBlockEvents
|
||||
data object ToggleReportRoom : DeclineAndBlockEvents
|
||||
data object ToggleBlockUser : DeclineAndBlockEvents
|
||||
data object Decline : DeclineAndBlockEvents
|
||||
data object ClearDeclineAction : DeclineAndBlockEvents
|
||||
sealed interface DeclineAndBlockEvent {
|
||||
data class UpdateReportReason(val reason: String) : DeclineAndBlockEvent
|
||||
data object ToggleReportRoom : DeclineAndBlockEvent
|
||||
data object ToggleBlockUser : DeclineAndBlockEvent
|
||||
data object Decline : DeclineAndBlockEvent
|
||||
data object ClearDeclineAction : DeclineAndBlockEvent
|
||||
}
|
||||
+6
-6
@@ -49,13 +49,13 @@ class DeclineAndBlockPresenter(
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
fun handleEvent(event: DeclineAndBlockEvents) {
|
||||
fun handleEvent(event: DeclineAndBlockEvent) {
|
||||
when (event) {
|
||||
DeclineAndBlockEvents.ClearDeclineAction -> declineAction.value = AsyncAction.Uninitialized
|
||||
DeclineAndBlockEvents.Decline -> coroutineScope.decline(reportReason, blockUser, reportRoom, declineAction)
|
||||
DeclineAndBlockEvents.ToggleBlockUser -> blockUser = !blockUser
|
||||
DeclineAndBlockEvents.ToggleReportRoom -> reportRoom = !reportRoom
|
||||
is DeclineAndBlockEvents.UpdateReportReason -> reportReason = event.reason
|
||||
DeclineAndBlockEvent.ClearDeclineAction -> declineAction.value = AsyncAction.Uninitialized
|
||||
DeclineAndBlockEvent.Decline -> coroutineScope.decline(reportReason, blockUser, reportRoom, declineAction)
|
||||
DeclineAndBlockEvent.ToggleBlockUser -> blockUser = !blockUser
|
||||
DeclineAndBlockEvent.ToggleReportRoom -> reportRoom = !reportRoom
|
||||
is DeclineAndBlockEvent.UpdateReportReason -> reportReason = event.reason
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ data class DeclineAndBlockState(
|
||||
val reportReason: String,
|
||||
val blockUser: Boolean,
|
||||
val declineAction: AsyncAction<Unit>,
|
||||
val eventSink: (DeclineAndBlockEvents) -> Unit
|
||||
val eventSink: (DeclineAndBlockEvent) -> Unit
|
||||
) {
|
||||
val canDecline = blockUser || reportRoom && reportReason.isNotEmpty()
|
||||
}
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ fun aDeclineAndBlockState(
|
||||
reportReason: String = "",
|
||||
blockUser: Boolean = false,
|
||||
declineAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
eventSink: (DeclineAndBlockEvents) -> Unit = {},
|
||||
eventSink: (DeclineAndBlockEvent) -> Unit = {},
|
||||
) = DeclineAndBlockState(
|
||||
reportRoom = reportRoom,
|
||||
reportReason = reportReason,
|
||||
|
||||
+6
-6
@@ -56,8 +56,8 @@ fun DeclineAndBlockView(
|
||||
async = state.declineAction,
|
||||
onSuccess = { onDeclineSuccess() },
|
||||
errorMessage = { stringResource(CommonStrings.error_unknown) },
|
||||
onRetry = { state.eventSink(DeclineAndBlockEvents.Decline) },
|
||||
onErrorDismiss = { state.eventSink(DeclineAndBlockEvents.ClearDeclineAction) }
|
||||
onRetry = { state.eventSink(DeclineAndBlockEvent.Decline) },
|
||||
onErrorDismiss = { state.eventSink(DeclineAndBlockEvent.ClearDeclineAction) }
|
||||
)
|
||||
|
||||
Scaffold(
|
||||
@@ -89,7 +89,7 @@ fun DeclineAndBlockView(
|
||||
Text(text = stringResource(R.string.screen_decline_and_block_block_user_option_description))
|
||||
},
|
||||
onClick = {
|
||||
state.eventSink(DeclineAndBlockEvents.ToggleBlockUser)
|
||||
state.eventSink(DeclineAndBlockEvent.ToggleBlockUser)
|
||||
},
|
||||
trailingContent = ListItemContent.Switch(checked = state.blockUser)
|
||||
)
|
||||
@@ -104,7 +104,7 @@ fun DeclineAndBlockView(
|
||||
Text(text = stringResource(R.string.screen_decline_and_block_report_user_option_description))
|
||||
},
|
||||
onClick = {
|
||||
state.eventSink(DeclineAndBlockEvents.ToggleReportRoom)
|
||||
state.eventSink(DeclineAndBlockEvent.ToggleReportRoom)
|
||||
},
|
||||
trailingContent = ListItemContent.Switch(checked = state.reportRoom)
|
||||
)
|
||||
@@ -113,7 +113,7 @@ fun DeclineAndBlockView(
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
TextField(
|
||||
value = state.reportReason,
|
||||
onValueChange = { state.eventSink(DeclineAndBlockEvents.UpdateReportReason(it)) },
|
||||
onValueChange = { state.eventSink(DeclineAndBlockEvent.UpdateReportReason(it)) },
|
||||
placeholder = stringResource(R.string.screen_decline_and_block_report_user_reason_placeholder),
|
||||
minLines = 3,
|
||||
enabled = !isDeclining,
|
||||
@@ -132,7 +132,7 @@ fun DeclineAndBlockView(
|
||||
enabled = !isDeclining && state.canDecline,
|
||||
onClick = {
|
||||
focusManager.clearFocus(force = true)
|
||||
state.eventSink(DeclineAndBlockEvents.Decline)
|
||||
state.eventSink(DeclineAndBlockEvent.Decline)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
||||
+11
-11
@@ -10,7 +10,7 @@ package io.element.android.features.invite.impl.acceptdecline
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.invite.api.InviteData
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvent
|
||||
import io.element.android.features.invite.api.acceptdecline.ConfirmingDeclineInvite
|
||||
import io.element.android.features.invite.impl.AcceptInvite
|
||||
import io.element.android.features.invite.impl.DeclineInvite
|
||||
@@ -51,13 +51,13 @@ class AcceptDeclineInvitePresenterTest {
|
||||
val inviteData = anInviteData()
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(
|
||||
AcceptDeclineInviteEvents.DeclineInvite(inviteData, blockUser = false, shouldConfirm = true)
|
||||
AcceptDeclineInviteEvent.DeclineInvite(inviteData, blockUser = false, shouldConfirm = true)
|
||||
)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.declineAction).isEqualTo(ConfirmingDeclineInvite(inviteData, false))
|
||||
state.eventSink(
|
||||
InternalAcceptDeclineInviteEvents.ClearDeclineActionState
|
||||
InternalAcceptDeclineInviteEvent.ClearDeclineActionState
|
||||
)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
@@ -78,20 +78,20 @@ class AcceptDeclineInvitePresenterTest {
|
||||
val inviteData = anInviteData()
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(
|
||||
AcceptDeclineInviteEvents.DeclineInvite(inviteData, blockUser = false, shouldConfirm = true)
|
||||
AcceptDeclineInviteEvent.DeclineInvite(inviteData, blockUser = false, shouldConfirm = true)
|
||||
)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.declineAction).isEqualTo(ConfirmingDeclineInvite(inviteData, false))
|
||||
state.eventSink(
|
||||
AcceptDeclineInviteEvents.DeclineInvite(inviteData, blockUser = false, shouldConfirm = false)
|
||||
AcceptDeclineInviteEvent.DeclineInvite(inviteData, blockUser = false, shouldConfirm = false)
|
||||
)
|
||||
}
|
||||
assertThat(awaitItem().declineAction.isLoading()).isTrue()
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.declineAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
state.eventSink(
|
||||
InternalAcceptDeclineInviteEvents.ClearDeclineActionState
|
||||
InternalAcceptDeclineInviteEvent.ClearDeclineActionState
|
||||
)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
@@ -114,13 +114,13 @@ class AcceptDeclineInvitePresenterTest {
|
||||
val inviteData = anInviteData()
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(
|
||||
AcceptDeclineInviteEvents.DeclineInvite(inviteData, blockUser = false, shouldConfirm = true)
|
||||
AcceptDeclineInviteEvent.DeclineInvite(inviteData, blockUser = false, shouldConfirm = true)
|
||||
)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.declineAction).isEqualTo(ConfirmingDeclineInvite(inviteData, blockUser = false))
|
||||
state.eventSink(
|
||||
AcceptDeclineInviteEvents.DeclineInvite(inviteData, blockUser = false, shouldConfirm = false)
|
||||
AcceptDeclineInviteEvent.DeclineInvite(inviteData, blockUser = false, shouldConfirm = false)
|
||||
)
|
||||
}
|
||||
assertThat(awaitItem().declineAction.isLoading()).isTrue()
|
||||
@@ -146,7 +146,7 @@ class AcceptDeclineInvitePresenterTest {
|
||||
val inviteData = anInviteData()
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(
|
||||
AcceptDeclineInviteEvents.AcceptInvite(inviteData)
|
||||
AcceptDeclineInviteEvent.AcceptInvite(inviteData)
|
||||
)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
@@ -155,7 +155,7 @@ class AcceptDeclineInvitePresenterTest {
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.acceptAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
state.eventSink(
|
||||
InternalAcceptDeclineInviteEvents.ClearAcceptActionState
|
||||
InternalAcceptDeclineInviteEvent.ClearAcceptActionState
|
||||
)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
@@ -178,7 +178,7 @@ class AcceptDeclineInvitePresenterTest {
|
||||
val inviteData = anInviteData()
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(
|
||||
AcceptDeclineInviteEvents.AcceptInvite(inviteData)
|
||||
AcceptDeclineInviteEvent.AcceptInvite(inviteData)
|
||||
)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
|
||||
+7
-7
@@ -53,21 +53,21 @@ class DeclineAndBlockPresenterTest {
|
||||
assertThat(state.blockUser).isTrue()
|
||||
assertThat(state.reportReason).isEmpty()
|
||||
assertThat(state.canDecline).isTrue()
|
||||
state.eventSink(DeclineAndBlockEvents.ToggleBlockUser)
|
||||
state.eventSink(DeclineAndBlockEvent.ToggleBlockUser)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.reportRoom).isFalse()
|
||||
assertThat(state.blockUser).isFalse()
|
||||
assertThat(state.reportReason).isEmpty()
|
||||
assertThat(state.canDecline).isFalse()
|
||||
state.eventSink(DeclineAndBlockEvents.ToggleReportRoom)
|
||||
state.eventSink(DeclineAndBlockEvent.ToggleReportRoom)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.reportRoom).isTrue()
|
||||
assertThat(state.blockUser).isFalse()
|
||||
assertThat(state.reportReason).isEmpty()
|
||||
assertThat(state.canDecline).isFalse()
|
||||
state.eventSink(DeclineAndBlockEvents.UpdateReportReason("Spam"))
|
||||
state.eventSink(DeclineAndBlockEvent.UpdateReportReason("Spam"))
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.reportRoom).isTrue()
|
||||
@@ -87,7 +87,7 @@ class DeclineAndBlockPresenterTest {
|
||||
)
|
||||
presenter.test {
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(DeclineAndBlockEvents.Decline)
|
||||
state.eventSink(DeclineAndBlockEvent.Decline)
|
||||
}
|
||||
assertThat(awaitItem().declineAction.isLoading()).isTrue()
|
||||
awaitItem().also { state ->
|
||||
@@ -110,12 +110,12 @@ class DeclineAndBlockPresenterTest {
|
||||
)
|
||||
presenter.test {
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(DeclineAndBlockEvents.Decline)
|
||||
state.eventSink(DeclineAndBlockEvent.Decline)
|
||||
}
|
||||
assertThat(awaitItem().declineAction.isLoading()).isTrue()
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.declineAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
state.eventSink(DeclineAndBlockEvents.ClearDeclineAction)
|
||||
state.eventSink(DeclineAndBlockEvent.ClearDeclineAction)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.declineAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||
@@ -137,7 +137,7 @@ class DeclineAndBlockPresenterTest {
|
||||
)
|
||||
presenter.test {
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(DeclineAndBlockEvents.Decline)
|
||||
state.eventSink(DeclineAndBlockEvent.Decline)
|
||||
}
|
||||
assertThat(awaitItem().declineAction.isLoading()).isTrue()
|
||||
awaitItem().also { state ->
|
||||
|
||||
+11
-11
@@ -30,7 +30,7 @@ import org.junit.Test
|
||||
class DeclineAndBlockViewTest : RobolectricTest() {
|
||||
@Test
|
||||
fun `clicking on back invoke the expected callback`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<DeclineAndBlockEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<DeclineAndBlockEvent>(expectEvents = false)
|
||||
ensureCalledOnce {
|
||||
setDeclineAndBlockView(
|
||||
aDeclineAndBlockState(
|
||||
@@ -44,7 +44,7 @@ class DeclineAndBlockViewTest : RobolectricTest() {
|
||||
|
||||
@Test
|
||||
fun `a successful decline invokes the expected callback`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<DeclineAndBlockEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<DeclineAndBlockEvent>(expectEvents = false)
|
||||
ensureCalledOnce {
|
||||
setDeclineAndBlockView(
|
||||
aDeclineAndBlockState(
|
||||
@@ -58,7 +58,7 @@ class DeclineAndBlockViewTest : RobolectricTest() {
|
||||
|
||||
@Test
|
||||
fun `clicking on decline when enabled emits the expected event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<DeclineAndBlockEvents>()
|
||||
val eventsRecorder = EventsRecorder<DeclineAndBlockEvent>()
|
||||
setDeclineAndBlockView(
|
||||
aDeclineAndBlockState(
|
||||
blockUser = true,
|
||||
@@ -66,12 +66,12 @@ class DeclineAndBlockViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
clickOn(CommonStrings.action_decline)
|
||||
eventsRecorder.assertSingle(DeclineAndBlockEvents.Decline)
|
||||
eventsRecorder.assertSingle(DeclineAndBlockEvent.Decline)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on decline when disabled does not emit event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<DeclineAndBlockEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<DeclineAndBlockEvent>(expectEvents = false)
|
||||
setDeclineAndBlockView(
|
||||
aDeclineAndBlockState(
|
||||
blockUser = false,
|
||||
@@ -85,7 +85,7 @@ class DeclineAndBlockViewTest : RobolectricTest() {
|
||||
|
||||
@Test
|
||||
fun `clicking on block option emits the expected event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<DeclineAndBlockEvents>()
|
||||
val eventsRecorder = EventsRecorder<DeclineAndBlockEvent>()
|
||||
setDeclineAndBlockView(
|
||||
aDeclineAndBlockState(
|
||||
blockUser = true,
|
||||
@@ -93,12 +93,12 @@ class DeclineAndBlockViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
clickOn(R.string.screen_decline_and_block_block_user_option_title)
|
||||
eventsRecorder.assertSingle(DeclineAndBlockEvents.ToggleBlockUser)
|
||||
eventsRecorder.assertSingle(DeclineAndBlockEvent.ToggleBlockUser)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on report room option emits the expected event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<DeclineAndBlockEvents>()
|
||||
val eventsRecorder = EventsRecorder<DeclineAndBlockEvent>()
|
||||
setDeclineAndBlockView(
|
||||
aDeclineAndBlockState(
|
||||
reportRoom = true,
|
||||
@@ -106,12 +106,12 @@ class DeclineAndBlockViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
clickOn(CommonStrings.action_report_room)
|
||||
eventsRecorder.assertSingle(DeclineAndBlockEvents.ToggleReportRoom)
|
||||
eventsRecorder.assertSingle(DeclineAndBlockEvent.ToggleReportRoom)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `typing text in the reason field emits the expected Event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<DeclineAndBlockEvents>()
|
||||
val eventsRecorder = EventsRecorder<DeclineAndBlockEvent>()
|
||||
setDeclineAndBlockView(
|
||||
aDeclineAndBlockState(
|
||||
reportRoom = true,
|
||||
@@ -120,7 +120,7 @@ class DeclineAndBlockViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
onNodeWithText("").performTextInput("Spam!")
|
||||
eventsRecorder.assertSingle(DeclineAndBlockEvents.UpdateReportReason("Spam!"))
|
||||
eventsRecorder.assertSingle(DeclineAndBlockEvent.UpdateReportReason("Spam!"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -11,8 +11,8 @@ package io.element.android.features.invitepeople.api
|
||||
/**
|
||||
* Events the invite people UI sends to its presenter through [InvitePeopleState.eventSink].
|
||||
*/
|
||||
interface InvitePeopleEvents {
|
||||
data object SendInvites : InvitePeopleEvents
|
||||
data object CloseSearch : InvitePeopleEvents
|
||||
data object ClearError : InvitePeopleEvents
|
||||
interface InvitePeopleEvent {
|
||||
data object SendInvites : InvitePeopleEvent
|
||||
data object CloseSearch : InvitePeopleEvent
|
||||
data object ClearError : InvitePeopleEvent
|
||||
}
|
||||
+1
-1
@@ -28,5 +28,5 @@ interface InvitePeopleState {
|
||||
val createRoomFromDmAction: AsyncAction<RoomId>
|
||||
|
||||
/** Where the UI sends its events. */
|
||||
val eventSink: (InvitePeopleEvents) -> Unit
|
||||
val eventSink: (InvitePeopleEvent) -> Unit
|
||||
}
|
||||
|
||||
+2
-2
@@ -27,7 +27,7 @@ private data class PreviewInvitePeopleState(
|
||||
override val isSearchActive: Boolean,
|
||||
override val sendInvitesAction: AsyncAction<Unit>,
|
||||
override val createRoomFromDmAction: AsyncAction<RoomId>,
|
||||
override val eventSink: (InvitePeopleEvents) -> Unit,
|
||||
override val eventSink: (InvitePeopleEvent) -> Unit,
|
||||
) : InvitePeopleState
|
||||
|
||||
private fun aPreviewInvitePeopleState(
|
||||
@@ -35,7 +35,7 @@ private fun aPreviewInvitePeopleState(
|
||||
isSearchActive: Boolean = false,
|
||||
sendInvitesAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
createRoomFromDmAction: AsyncAction<RoomId> = AsyncAction.Uninitialized,
|
||||
eventSink: (InvitePeopleEvents) -> Unit = {},
|
||||
eventSink: (InvitePeopleEvent) -> Unit = {},
|
||||
) = PreviewInvitePeopleState(
|
||||
canInvite = canInvite,
|
||||
isSearchActive = isSearchActive,
|
||||
|
||||
+6
-6
@@ -8,12 +8,12 @@
|
||||
|
||||
package io.element.android.features.invitepeople.impl
|
||||
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleEvents
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleEvent
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
|
||||
sealed interface DefaultInvitePeopleEvents : InvitePeopleEvents {
|
||||
data class ToggleUser(val user: MatrixUser) : DefaultInvitePeopleEvents
|
||||
data class OnSearchActiveChanged(val active: Boolean) : DefaultInvitePeopleEvents
|
||||
data object DismissUnknownUsersModal : DefaultInvitePeopleEvents
|
||||
data object RemoveUnknownUsers : DefaultInvitePeopleEvents
|
||||
sealed interface DefaultInvitePeopleEvent : InvitePeopleEvent {
|
||||
data class ToggleUser(val user: MatrixUser) : DefaultInvitePeopleEvent
|
||||
data class OnSearchActiveChanged(val active: Boolean) : DefaultInvitePeopleEvent
|
||||
data object DismissUnknownUsersModal : DefaultInvitePeopleEvent
|
||||
data object RemoveUnknownUsers : DefaultInvitePeopleEvent
|
||||
}
|
||||
+10
-10
@@ -24,7 +24,7 @@ import dev.zacsweers.metro.Assisted
|
||||
import dev.zacsweers.metro.AssistedFactory
|
||||
import dev.zacsweers.metro.AssistedInject
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleEvents
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleEvent
|
||||
import io.element.android.features.invitepeople.api.InvitePeoplePresenter
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleState
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
@@ -179,26 +179,26 @@ class DefaultInvitePeoplePresenter(
|
||||
)
|
||||
}
|
||||
|
||||
fun handleEvent(event: InvitePeopleEvents) {
|
||||
fun handleEvent(event: InvitePeopleEvent) {
|
||||
when (event) {
|
||||
// Dedicated `when` for exhaustivity.
|
||||
is DefaultInvitePeopleEvents -> when (event) {
|
||||
is DefaultInvitePeopleEvents.OnSearchActiveChanged -> {
|
||||
is DefaultInvitePeopleEvent -> when (event) {
|
||||
is DefaultInvitePeopleEvent.OnSearchActiveChanged -> {
|
||||
searchActive = event.active
|
||||
if (!event.active) {
|
||||
queryState.clearText()
|
||||
}
|
||||
}
|
||||
|
||||
is DefaultInvitePeopleEvents.ToggleUser -> {
|
||||
is DefaultInvitePeopleEvent.ToggleUser -> {
|
||||
selectedUsers.toggleUser(event.user)
|
||||
searchResults.toggleUser(event.user)
|
||||
// suggestions will automatically update via derivedStateOf when selectedUsers changes
|
||||
}
|
||||
is DefaultInvitePeopleEvents.DismissUnknownUsersModal -> {
|
||||
is DefaultInvitePeopleEvent.DismissUnknownUsersModal -> {
|
||||
sendInvitesAction.value = AsyncAction.Uninitialized
|
||||
}
|
||||
is DefaultInvitePeopleEvents.RemoveUnknownUsers -> {
|
||||
is DefaultInvitePeopleEvent.RemoveUnknownUsers -> {
|
||||
val usersToRemove = selectedUsers.value.filter { it in unknownUsers }
|
||||
usersToRemove.forEach { user ->
|
||||
selectedUsers.toggleUser(user)
|
||||
@@ -207,7 +207,7 @@ class DefaultInvitePeoplePresenter(
|
||||
sendInvitesAction.value = AsyncAction.Uninitialized
|
||||
}
|
||||
}
|
||||
is InvitePeopleEvents.SendInvites -> {
|
||||
is InvitePeopleEvent.SendInvites -> {
|
||||
if (unknownUsers.isNotEmpty() && sendInvitesAction.value !is ConfirmingUnknownUserInvitation) {
|
||||
sendInvitesAction.value = ConfirmingUnknownUserInvitation(
|
||||
unknownUsers
|
||||
@@ -224,11 +224,11 @@ class DefaultInvitePeoplePresenter(
|
||||
}
|
||||
}
|
||||
}
|
||||
is InvitePeopleEvents.CloseSearch -> {
|
||||
is InvitePeopleEvent.CloseSearch -> {
|
||||
searchActive = false
|
||||
queryState.clearText()
|
||||
}
|
||||
is InvitePeopleEvents.ClearError -> {
|
||||
is InvitePeopleEvent.ClearError -> {
|
||||
sendInvitesAction.value = AsyncAction.Uninitialized
|
||||
createRoomFromDmAction.value = AsyncAction.Uninitialized
|
||||
}
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@
|
||||
package io.element.android.features.invitepeople.impl
|
||||
|
||||
import androidx.compose.foundation.text.input.TextFieldState
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleEvents
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleEvent
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleState
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
@@ -29,5 +29,5 @@ data class DefaultInvitePeopleState(
|
||||
override val sendInvitesAction: AsyncAction<Unit>,
|
||||
override val createRoomFromDmAction: AsyncAction<RoomId>,
|
||||
val suggestions: ImmutableList<InvitableUser>,
|
||||
override val eventSink: (InvitePeopleEvents) -> Unit
|
||||
override val eventSink: (InvitePeopleEvent) -> Unit
|
||||
) : InvitePeopleState
|
||||
|
||||
+7
-7
@@ -30,7 +30,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleEvents
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleEvent
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.designsystem.atomic.molecules.ButtonRowMolecule
|
||||
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
|
||||
@@ -102,7 +102,7 @@ private fun InvitePeopleContentView(
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
fun toggleUser(user: MatrixUser) {
|
||||
state.eventSink(DefaultInvitePeopleEvents.ToggleUser(user))
|
||||
state.eventSink(DefaultInvitePeopleEvent.ToggleUser(user))
|
||||
}
|
||||
|
||||
InvitePeopleSearchBar(
|
||||
@@ -114,7 +114,7 @@ private fun InvitePeopleContentView(
|
||||
active = state.isSearchActive,
|
||||
onActiveChange = {
|
||||
state.eventSink(
|
||||
DefaultInvitePeopleEvents.OnSearchActiveChanged(
|
||||
DefaultInvitePeopleEvent.OnSearchActiveChanged(
|
||||
it
|
||||
)
|
||||
)
|
||||
@@ -144,7 +144,7 @@ private fun InvitePeopleContentView(
|
||||
CheckableUserRow(
|
||||
checked = invitableUser.isSelected,
|
||||
onCheckedChange = {
|
||||
state.eventSink(DefaultInvitePeopleEvents.ToggleUser(invitableUser.matrixUser))
|
||||
state.eventSink(DefaultInvitePeopleEvent.ToggleUser(invitableUser.matrixUser))
|
||||
},
|
||||
data = CheckableUserRowData.Resolved(
|
||||
avatarData = invitableUser.matrixUser.getAvatarData(AvatarSize.UserListItem),
|
||||
@@ -163,9 +163,9 @@ private fun InvitePeopleContentView(
|
||||
if (state.sendInvitesAction is ConfirmingUnknownUserInvitation) {
|
||||
InvitePeopleConfirmModal(
|
||||
users = state.sendInvitesAction.users,
|
||||
onDismiss = { state.eventSink.invoke(DefaultInvitePeopleEvents.DismissUnknownUsersModal) },
|
||||
onInvite = { state.eventSink.invoke(InvitePeopleEvents.SendInvites) },
|
||||
onRemove = { state.eventSink.invoke(DefaultInvitePeopleEvents.RemoveUnknownUsers) }
|
||||
onDismiss = { state.eventSink.invoke(DefaultInvitePeopleEvent.DismissUnknownUsersModal) },
|
||||
onInvite = { state.eventSink.invoke(InvitePeopleEvent.SendInvites) },
|
||||
onRemove = { state.eventSink.invoke(DefaultInvitePeopleEvent.RemoveUnknownUsers) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+29
-29
@@ -11,7 +11,7 @@ package io.element.android.features.invitepeople.impl
|
||||
import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
|
||||
import app.cash.turbine.ReceiveTurbine
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleEvents
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleEvent
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
||||
@@ -88,13 +88,13 @@ internal class DefaultInvitePeoplePresenterTest {
|
||||
val initialState = awaitItem()
|
||||
skipItems(1)
|
||||
|
||||
initialState.eventSink(DefaultInvitePeopleEvents.OnSearchActiveChanged(true))
|
||||
initialState.eventSink(DefaultInvitePeopleEvent.OnSearchActiveChanged(true))
|
||||
|
||||
val resultState = awaitItemAsDefault()
|
||||
assertThat(resultState.isSearchActive).isTrue()
|
||||
resultState.searchQuery.setTextAndPlaceCursorAtEnd("some query")
|
||||
assertThat(awaitItemAsDefault().searchQuery.text.toString()).isEqualTo("some query")
|
||||
resultState.eventSink(InvitePeopleEvents.CloseSearch)
|
||||
resultState.eventSink(InvitePeopleEvent.CloseSearch)
|
||||
skipItems(1)
|
||||
awaitItemAsDefault().also {
|
||||
assertThat(it.isSearchActive).isFalse()
|
||||
@@ -290,18 +290,18 @@ internal class DefaultInvitePeoplePresenterTest {
|
||||
skipItems(1)
|
||||
|
||||
// When we toggle a user not in the list, they are added
|
||||
initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(aMatrixUser()))
|
||||
initialState.eventSink(DefaultInvitePeopleEvent.ToggleUser(aMatrixUser()))
|
||||
assertThat(awaitItemAsDefault().selectedUsers).containsExactly(aMatrixUser())
|
||||
|
||||
// Toggling a different user also adds them
|
||||
initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(aMatrixUser(id = A_USER_ID_2.value)))
|
||||
initialState.eventSink(DefaultInvitePeopleEvent.ToggleUser(aMatrixUser(id = A_USER_ID_2.value)))
|
||||
assertThat(awaitItemAsDefault().selectedUsers).containsExactly(
|
||||
aMatrixUser(),
|
||||
aMatrixUser(id = A_USER_ID_2.value)
|
||||
)
|
||||
|
||||
// Toggling the first user removes them
|
||||
initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(aMatrixUser()))
|
||||
initialState.eventSink(DefaultInvitePeopleEvent.ToggleUser(aMatrixUser()))
|
||||
assertThat(awaitItemAsDefault().selectedUsers).containsExactly(aMatrixUser(id = A_USER_ID_2.value))
|
||||
}
|
||||
}
|
||||
@@ -319,7 +319,7 @@ internal class DefaultInvitePeoplePresenterTest {
|
||||
|
||||
val selectedUser = aMatrixUser(displayName = "John Doe")
|
||||
|
||||
initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(selectedUser))
|
||||
initialState.eventSink(DefaultInvitePeopleEvent.ToggleUser(selectedUser))
|
||||
|
||||
initialState.searchQuery.setTextAndPlaceCursorAtEnd("some query")
|
||||
skipItems(1)
|
||||
@@ -373,7 +373,7 @@ internal class DefaultInvitePeoplePresenterTest {
|
||||
}
|
||||
|
||||
// And then a user is toggled
|
||||
initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(selectedUser))
|
||||
initialState.eventSink(DefaultInvitePeopleEvent.ToggleUser(selectedUser))
|
||||
skipItems(1)
|
||||
val resultState = awaitItemAsDefault()
|
||||
|
||||
@@ -414,13 +414,13 @@ internal class DefaultInvitePeoplePresenterTest {
|
||||
repository.emitStateWithUsers(users = aMatrixUserList() + selectedUser)
|
||||
skipItems(1)
|
||||
// And then a user is toggled
|
||||
initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(selectedUser))
|
||||
initialState.eventSink(DefaultInvitePeopleEvent.ToggleUser(selectedUser))
|
||||
skipItems(1)
|
||||
val resultState = awaitItemAsDefault()
|
||||
// The results are updated...
|
||||
assertThat(resultState.searchResults).isInstanceOf(SearchBarResultState.Results::class.java)
|
||||
// Send invites
|
||||
initialState.eventSink(InvitePeopleEvents.SendInvites)
|
||||
initialState.eventSink(InvitePeopleEvent.SendInvites)
|
||||
|
||||
// Can't invite in the loading state
|
||||
awaitItem().run {
|
||||
@@ -468,13 +468,13 @@ internal class DefaultInvitePeoplePresenterTest {
|
||||
repository.emitStateWithUsers(users = aMatrixUserList() + selectedUser)
|
||||
skipItems(1)
|
||||
// And then a user is toggled
|
||||
initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(selectedUser))
|
||||
initialState.eventSink(DefaultInvitePeopleEvent.ToggleUser(selectedUser))
|
||||
skipItems(1)
|
||||
val resultState = awaitItemAsDefault()
|
||||
// The results are updated...
|
||||
assertThat(resultState.searchResults).isInstanceOf(SearchBarResultState.Results::class.java)
|
||||
// Send invites
|
||||
initialState.eventSink(InvitePeopleEvents.SendInvites)
|
||||
initialState.eventSink(InvitePeopleEvent.SendInvites)
|
||||
|
||||
// Can't invite in the loading state
|
||||
awaitItem().run {
|
||||
@@ -645,17 +645,17 @@ internal class DefaultInvitePeoplePresenterTest {
|
||||
skipItems(1)
|
||||
|
||||
// When we toggle a user not in the list, they are added, and we fetch their identity.
|
||||
initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(alice))
|
||||
initialState.eventSink(DefaultInvitePeopleEvent.ToggleUser(alice))
|
||||
delay(100)
|
||||
awaitItemAsDefault().eventSink(DefaultInvitePeopleEvents.ToggleUser(bob))
|
||||
awaitItemAsDefault().eventSink(DefaultInvitePeopleEvent.ToggleUser(bob))
|
||||
delay(100)
|
||||
awaitItemAsDefault().eventSink(DefaultInvitePeopleEvents.ToggleUser(charlie))
|
||||
awaitItemAsDefault().eventSink(DefaultInvitePeopleEvent.ToggleUser(charlie))
|
||||
delay(100)
|
||||
|
||||
// If we do not have their identity cached, or fail to fetch it, we should mark them as unknown.
|
||||
awaitItemAsDefault().run {
|
||||
assertThat(selectedUsers).containsExactly(alice, bob, charlie)
|
||||
eventSink(InvitePeopleEvents.SendInvites)
|
||||
eventSink(InvitePeopleEvent.SendInvites)
|
||||
}
|
||||
|
||||
getUserIdentityResult.assertions().isCalledExactly(3).withSequence(
|
||||
@@ -668,7 +668,7 @@ internal class DefaultInvitePeoplePresenterTest {
|
||||
awaitItemAsDefault().run {
|
||||
assertThat(sendInvitesAction).isInstanceOf(ConfirmingUnknownUserInvitation::class.java)
|
||||
assertThat(canInvite).isTrue()
|
||||
eventSink(InvitePeopleEvents.SendInvites)
|
||||
eventSink(InvitePeopleEvent.SendInvites)
|
||||
}
|
||||
|
||||
delay(1_000)
|
||||
@@ -712,11 +712,11 @@ internal class DefaultInvitePeoplePresenterTest {
|
||||
skipItems(1)
|
||||
|
||||
// When we toggle a user not in the list, they are added, and we fetch their identity.
|
||||
initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(alice))
|
||||
initialState.eventSink(DefaultInvitePeopleEvent.ToggleUser(alice))
|
||||
delay(100)
|
||||
awaitItemAsDefault().eventSink(DefaultInvitePeopleEvents.ToggleUser(bob))
|
||||
awaitItemAsDefault().eventSink(DefaultInvitePeopleEvent.ToggleUser(bob))
|
||||
delay(100)
|
||||
awaitItemAsDefault().eventSink(DefaultInvitePeopleEvents.ToggleUser(charlie))
|
||||
awaitItemAsDefault().eventSink(DefaultInvitePeopleEvent.ToggleUser(charlie))
|
||||
delay(100)
|
||||
|
||||
// And the search is matching Alice and Bob
|
||||
@@ -738,7 +738,7 @@ internal class DefaultInvitePeoplePresenterTest {
|
||||
searchResults.users().map { Pair(it.matrixUser, it.isSelected) }
|
||||
).containsExactly(Pair(alice, true), Pair(bob, true))
|
||||
|
||||
eventSink(InvitePeopleEvents.SendInvites)
|
||||
eventSink(InvitePeopleEvent.SendInvites)
|
||||
}
|
||||
|
||||
getUserIdentityResult.assertions().isCalledExactly(3).withSequence(
|
||||
@@ -751,7 +751,7 @@ internal class DefaultInvitePeoplePresenterTest {
|
||||
awaitItemAsDefault().run {
|
||||
assertThat(sendInvitesAction).isInstanceOf(ConfirmingUnknownUserInvitation::class.java)
|
||||
assertThat(canInvite).isTrue()
|
||||
eventSink(DefaultInvitePeopleEvents.RemoveUnknownUsers)
|
||||
eventSink(DefaultInvitePeopleEvent.RemoveUnknownUsers)
|
||||
}
|
||||
|
||||
// Selecting "remove" should remove all unknown users, but keeps those who are known.
|
||||
@@ -794,16 +794,16 @@ internal class DefaultInvitePeoplePresenterTest {
|
||||
skipItems(1)
|
||||
|
||||
// When we toggle a user not in the list, they are added, and we fetch their identity.
|
||||
initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(alice))
|
||||
initialState.eventSink(DefaultInvitePeopleEvent.ToggleUser(alice))
|
||||
delay(100)
|
||||
awaitItemAsDefault().eventSink(DefaultInvitePeopleEvents.ToggleUser(bob))
|
||||
awaitItemAsDefault().eventSink(DefaultInvitePeopleEvent.ToggleUser(bob))
|
||||
delay(100)
|
||||
awaitItemAsDefault().eventSink(DefaultInvitePeopleEvents.ToggleUser(charlie))
|
||||
awaitItemAsDefault().eventSink(DefaultInvitePeopleEvent.ToggleUser(charlie))
|
||||
delay(100)
|
||||
|
||||
awaitItemAsDefault().run {
|
||||
assertThat(selectedUsers).containsExactly(alice, bob, charlie)
|
||||
eventSink(InvitePeopleEvents.SendInvites)
|
||||
eventSink(InvitePeopleEvent.SendInvites)
|
||||
}
|
||||
|
||||
getUserIdentityResult.assertions().isCalledExactly(3).withSequence(
|
||||
@@ -816,7 +816,7 @@ internal class DefaultInvitePeoplePresenterTest {
|
||||
awaitItemAsDefault().run {
|
||||
assertThat(sendInvitesAction).isInstanceOf(ConfirmingUnknownUserInvitation::class.java)
|
||||
assertThat(canInvite).isTrue()
|
||||
eventSink(DefaultInvitePeopleEvents.DismissUnknownUsersModal)
|
||||
eventSink(DefaultInvitePeopleEvent.DismissUnknownUsersModal)
|
||||
}
|
||||
|
||||
// Dismissing should not modify the selection at all
|
||||
@@ -853,10 +853,10 @@ internal class DefaultInvitePeoplePresenterTest {
|
||||
skipItems(1)
|
||||
|
||||
// We want to add a new user to a DM
|
||||
initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(alice))
|
||||
initialState.eventSink(DefaultInvitePeopleEvent.ToggleUser(alice))
|
||||
|
||||
// And we send the invites
|
||||
initialState.eventSink(InvitePeopleEvents.SendInvites)
|
||||
initialState.eventSink(InvitePeopleEvent.SendInvites)
|
||||
|
||||
skipItems(1)
|
||||
|
||||
|
||||
+11
-11
@@ -10,15 +10,15 @@ package io.element.android.features.joinroom.impl
|
||||
|
||||
import io.element.android.features.invite.api.InviteData
|
||||
|
||||
sealed interface JoinRoomEvents {
|
||||
data object RetryFetchingContent : JoinRoomEvents
|
||||
data object DismissErrorAndHideContent : JoinRoomEvents
|
||||
data object JoinRoom : JoinRoomEvents
|
||||
data object KnockRoom : JoinRoomEvents
|
||||
data object ForgetRoom : JoinRoomEvents
|
||||
data class CancelKnock(val requiresConfirmation: Boolean) : JoinRoomEvents
|
||||
data class UpdateKnockMessage(val message: String) : JoinRoomEvents
|
||||
data object ClearActionStates : JoinRoomEvents
|
||||
data class AcceptInvite(val inviteData: InviteData) : JoinRoomEvents
|
||||
data class DeclineInvite(val inviteData: InviteData, val blockUser: Boolean) : JoinRoomEvents
|
||||
sealed interface JoinRoomEvent {
|
||||
data object RetryFetchingContent : JoinRoomEvent
|
||||
data object DismissErrorAndHideContent : JoinRoomEvent
|
||||
data object JoinRoom : JoinRoomEvent
|
||||
data object KnockRoom : JoinRoomEvent
|
||||
data object ForgetRoom : JoinRoomEvent
|
||||
data class CancelKnock(val requiresConfirmation: Boolean) : JoinRoomEvent
|
||||
data class UpdateKnockMessage(val message: String) : JoinRoomEvent
|
||||
data object ClearActionStates : JoinRoomEvent
|
||||
data class AcceptInvite(val inviteData: InviteData) : JoinRoomEvent
|
||||
data class DeclineInvite(val inviteData: InviteData, val blockUser: Boolean) : JoinRoomEvent
|
||||
}
|
||||
+14
-14
@@ -26,7 +26,7 @@ import dev.zacsweers.metro.AssistedInject
|
||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||
import io.element.android.features.invite.api.InviteData
|
||||
import io.element.android.features.invite.api.SeenInvitesStore
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvent
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.toInviteData
|
||||
import io.element.android.features.joinroom.impl.di.CancelKnockRoom
|
||||
@@ -151,37 +151,37 @@ class JoinRoomPresenter(
|
||||
contentState.markRoomInviteAsSeen()
|
||||
}
|
||||
|
||||
fun handleEvent(event: JoinRoomEvents) {
|
||||
fun handleEvent(event: JoinRoomEvent) {
|
||||
when (event) {
|
||||
JoinRoomEvents.JoinRoom -> coroutineScope.joinRoom(joinAction)
|
||||
is JoinRoomEvents.KnockRoom -> coroutineScope.knockRoom(knockAction, knockMessage)
|
||||
is JoinRoomEvents.AcceptInvite -> {
|
||||
JoinRoomEvent.JoinRoom -> coroutineScope.joinRoom(joinAction)
|
||||
is JoinRoomEvent.KnockRoom -> coroutineScope.knockRoom(knockAction, knockMessage)
|
||||
is JoinRoomEvent.AcceptInvite -> {
|
||||
acceptDeclineInviteState.eventSink(
|
||||
AcceptDeclineInviteEvents.AcceptInvite(event.inviteData)
|
||||
AcceptDeclineInviteEvent.AcceptInvite(event.inviteData)
|
||||
)
|
||||
}
|
||||
is JoinRoomEvents.DeclineInvite -> {
|
||||
is JoinRoomEvent.DeclineInvite -> {
|
||||
acceptDeclineInviteState.eventSink(
|
||||
AcceptDeclineInviteEvents.DeclineInvite(invite = event.inviteData, blockUser = event.blockUser, shouldConfirm = true)
|
||||
AcceptDeclineInviteEvent.DeclineInvite(invite = event.inviteData, blockUser = event.blockUser, shouldConfirm = true)
|
||||
)
|
||||
}
|
||||
is JoinRoomEvents.CancelKnock -> coroutineScope.cancelKnockRoom(event.requiresConfirmation, cancelKnockAction)
|
||||
JoinRoomEvents.RetryFetchingContent -> {
|
||||
is JoinRoomEvent.CancelKnock -> coroutineScope.cancelKnockRoom(event.requiresConfirmation, cancelKnockAction)
|
||||
JoinRoomEvent.RetryFetchingContent -> {
|
||||
retryCount++
|
||||
}
|
||||
JoinRoomEvents.ClearActionStates -> {
|
||||
JoinRoomEvent.ClearActionStates -> {
|
||||
knockAction.value = AsyncAction.Uninitialized
|
||||
joinAction.value = AsyncAction.Uninitialized
|
||||
cancelKnockAction.value = AsyncAction.Uninitialized
|
||||
forgetRoomAction.value = AsyncAction.Uninitialized
|
||||
}
|
||||
is JoinRoomEvents.UpdateKnockMessage -> {
|
||||
is JoinRoomEvent.UpdateKnockMessage -> {
|
||||
knockMessage = event.message.take(MAX_KNOCK_MESSAGE_LENGTH)
|
||||
}
|
||||
JoinRoomEvents.DismissErrorAndHideContent -> {
|
||||
JoinRoomEvent.DismissErrorAndHideContent -> {
|
||||
isDismissingContent = true
|
||||
}
|
||||
JoinRoomEvents.ForgetRoom -> coroutineScope.forgetRoom(forgetRoomAction)
|
||||
JoinRoomEvent.ForgetRoom -> coroutineScope.forgetRoom(forgetRoomAction)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ data class JoinRoomState(
|
||||
val knockMessage: String,
|
||||
val hideInviteAvatars: Boolean,
|
||||
val canReportRoom: Boolean,
|
||||
val eventSink: (JoinRoomEvents) -> Unit
|
||||
val eventSink: (JoinRoomEvent) -> Unit
|
||||
) {
|
||||
val isJoinActionUnauthorized = joinAction is AsyncAction.Failure && joinAction.error is JoinRoom.Failures.UnauthorizedJoin
|
||||
val joinAuthorisationStatus = when (contentState) {
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@ fun aJoinRoomState(
|
||||
knockMessage: String = "",
|
||||
hideInviteAvatars: Boolean = false,
|
||||
canReportRoom: Boolean = true,
|
||||
eventSink: (JoinRoomEvents) -> Unit = {}
|
||||
eventSink: (JoinRoomEvent) -> Unit = {}
|
||||
) = JoinRoomState(
|
||||
roomIdOrAlias = roomIdOrAlias,
|
||||
contentState = contentState,
|
||||
|
||||
+15
-15
@@ -115,33 +115,33 @@ fun JoinRoomView(
|
||||
contentState = state.contentState,
|
||||
knockMessage = state.knockMessage,
|
||||
hideAvatarsImages = state.hideAvatarsImages,
|
||||
onKnockMessageUpdate = { state.eventSink(JoinRoomEvents.UpdateKnockMessage(it)) },
|
||||
onKnockMessageUpdate = { state.eventSink(JoinRoomEvent.UpdateKnockMessage(it)) },
|
||||
)
|
||||
},
|
||||
footer = {
|
||||
JoinRoomFooter(
|
||||
joinAuthorisationStatus = state.joinAuthorisationStatus,
|
||||
onAcceptInvite = { inviteData ->
|
||||
state.eventSink(JoinRoomEvents.AcceptInvite(inviteData))
|
||||
state.eventSink(JoinRoomEvent.AcceptInvite(inviteData))
|
||||
},
|
||||
onDeclineInvite = { inviteData, blockUser ->
|
||||
if (state.canReportRoom && blockUser) {
|
||||
onDeclineInviteAndBlockUser(inviteData)
|
||||
} else {
|
||||
state.eventSink(JoinRoomEvents.DeclineInvite(inviteData, blockUser = blockUser))
|
||||
state.eventSink(JoinRoomEvent.DeclineInvite(inviteData, blockUser = blockUser))
|
||||
}
|
||||
},
|
||||
onJoinRoom = {
|
||||
state.eventSink(JoinRoomEvents.JoinRoom)
|
||||
state.eventSink(JoinRoomEvent.JoinRoom)
|
||||
},
|
||||
onKnockRoom = {
|
||||
state.eventSink(JoinRoomEvents.KnockRoom)
|
||||
state.eventSink(JoinRoomEvent.KnockRoom)
|
||||
},
|
||||
onCancelKnock = {
|
||||
state.eventSink(JoinRoomEvents.CancelKnock(requiresConfirmation = true))
|
||||
state.eventSink(JoinRoomEvent.CancelKnock(requiresConfirmation = true))
|
||||
},
|
||||
onForgetRoom = {
|
||||
state.eventSink(JoinRoomEvents.ForgetRoom)
|
||||
state.eventSink(JoinRoomEvent.ForgetRoom)
|
||||
},
|
||||
onGoBack = onBackClick,
|
||||
)
|
||||
@@ -152,9 +152,9 @@ fun JoinRoomView(
|
||||
RetryDialog(
|
||||
title = stringResource(R.string.screen_join_room_loading_alert_title),
|
||||
content = stringResource(CommonStrings.error_network_or_server_issue),
|
||||
onRetry = { state.eventSink(JoinRoomEvents.RetryFetchingContent) },
|
||||
onRetry = { state.eventSink(JoinRoomEvent.RetryFetchingContent) },
|
||||
onDismiss = {
|
||||
state.eventSink(JoinRoomEvents.DismissErrorAndHideContent)
|
||||
state.eventSink(JoinRoomEvent.DismissErrorAndHideContent)
|
||||
onBackClick()
|
||||
}
|
||||
)
|
||||
@@ -166,7 +166,7 @@ fun JoinRoomView(
|
||||
errorTitle = { stringResource(CommonStrings.common_something_went_wrong) },
|
||||
errorMessage = { stringResource(CommonStrings.error_network_or_server_issue) },
|
||||
onSuccess = { onJoinSuccess() },
|
||||
onErrorDismiss = { state.eventSink(JoinRoomEvents.ClearActionStates) },
|
||||
onErrorDismiss = { state.eventSink(JoinRoomEvent.ClearActionStates) },
|
||||
)
|
||||
}
|
||||
AsyncActionView(
|
||||
@@ -174,19 +174,19 @@ fun JoinRoomView(
|
||||
errorTitle = { stringResource(CommonStrings.common_something_went_wrong) },
|
||||
errorMessage = { stringResource(CommonStrings.error_network_or_server_issue) },
|
||||
onSuccess = { onKnockSuccess() },
|
||||
onErrorDismiss = { state.eventSink(JoinRoomEvents.ClearActionStates) },
|
||||
onErrorDismiss = { state.eventSink(JoinRoomEvent.ClearActionStates) },
|
||||
)
|
||||
AsyncActionView(
|
||||
async = state.forgetAction,
|
||||
errorTitle = { stringResource(CommonStrings.common_something_went_wrong) },
|
||||
errorMessage = { stringResource(CommonStrings.error_network_or_server_issue) },
|
||||
onSuccess = { onForgetSuccess() },
|
||||
onErrorDismiss = { state.eventSink(JoinRoomEvents.ClearActionStates) },
|
||||
onErrorDismiss = { state.eventSink(JoinRoomEvent.ClearActionStates) },
|
||||
)
|
||||
AsyncActionView(
|
||||
async = state.cancelKnockAction,
|
||||
onSuccess = { onCancelKnockSuccess() },
|
||||
onErrorDismiss = { state.eventSink(JoinRoomEvents.ClearActionStates) },
|
||||
onErrorDismiss = { state.eventSink(JoinRoomEvent.ClearActionStates) },
|
||||
errorTitle = { stringResource(CommonStrings.common_something_went_wrong) },
|
||||
errorMessage = { stringResource(CommonStrings.error_network_or_server_issue) },
|
||||
confirmationDialog = {
|
||||
@@ -195,8 +195,8 @@ fun JoinRoomView(
|
||||
title = stringResource(R.string.screen_join_room_cancel_knock_alert_title),
|
||||
submitText = stringResource(R.string.screen_join_room_cancel_knock_alert_confirmation),
|
||||
cancelText = stringResource(CommonStrings.action_no),
|
||||
onSubmitClick = { state.eventSink(JoinRoomEvents.CancelKnock(requiresConfirmation = false)) },
|
||||
onDismiss = { state.eventSink(JoinRoomEvents.ClearActionStates) },
|
||||
onSubmitClick = { state.eventSink(JoinRoomEvent.CancelKnock(requiresConfirmation = false)) },
|
||||
onDismiss = { state.eventSink(JoinRoomEvent.ClearActionStates) },
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
+18
-18
@@ -12,7 +12,7 @@ import com.google.common.truth.Truth.assertThat
|
||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||
import io.element.android.features.invite.api.InviteData
|
||||
import io.element.android.features.invite.api.SeenInvitesStore
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvent
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.acceptdecline.anAcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.toInviteData
|
||||
@@ -365,7 +365,7 @@ class JoinRoomPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - when room is invited then accept and decline events are sent to acceptDeclinePresenter`() = runTest {
|
||||
val eventSinkRecorder = lambdaRecorder { _: AcceptDeclineInviteEvents -> }
|
||||
val eventSinkRecorder = lambdaRecorder { _: AcceptDeclineInviteEvent -> }
|
||||
val acceptDeclinePresenter = Presenter {
|
||||
anAcceptDeclineInviteState(eventSink = eventSinkRecorder)
|
||||
}
|
||||
@@ -388,14 +388,14 @@ class JoinRoomPresenterTest {
|
||||
skipItems(1)
|
||||
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(JoinRoomEvents.AcceptInvite(inviteData))
|
||||
state.eventSink(JoinRoomEvents.DeclineInvite(inviteData, false))
|
||||
state.eventSink(JoinRoomEvent.AcceptInvite(inviteData))
|
||||
state.eventSink(JoinRoomEvent.DeclineInvite(inviteData, false))
|
||||
|
||||
assert(eventSinkRecorder)
|
||||
.isCalledExactly(2)
|
||||
.withSequence(
|
||||
listOf(value(AcceptDeclineInviteEvents.AcceptInvite(inviteData))),
|
||||
listOf(value(AcceptDeclineInviteEvents.DeclineInvite(inviteData, blockUser = false, shouldConfirm = true))),
|
||||
listOf(value(AcceptDeclineInviteEvent.AcceptInvite(inviteData))),
|
||||
listOf(value(AcceptDeclineInviteEvent.DeclineInvite(inviteData, blockUser = false, shouldConfirm = true))),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -422,7 +422,7 @@ class JoinRoomPresenterTest {
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(JoinRoomEvents.JoinRoom)
|
||||
state.eventSink(JoinRoomEvent.JoinRoom)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.joinAction).isEqualTo(AsyncAction.Loading)
|
||||
@@ -453,14 +453,14 @@ class JoinRoomPresenterTest {
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(JoinRoomEvents.JoinRoom)
|
||||
state.eventSink(JoinRoomEvent.JoinRoom)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.joinAction).isEqualTo(AsyncAction.Loading)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.joinAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION))
|
||||
state.eventSink(JoinRoomEvents.ClearActionStates)
|
||||
state.eventSink(JoinRoomEvent.ClearActionStates)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.joinAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
@@ -480,7 +480,7 @@ class JoinRoomPresenterTest {
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(JoinRoomEvents.JoinRoom)
|
||||
state.eventSink(JoinRoomEvent.JoinRoom)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.joinAction).isEqualTo(AsyncAction.Loading)
|
||||
@@ -683,17 +683,17 @@ class JoinRoomPresenterTest {
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(JoinRoomEvents.UpdateKnockMessage(knockMessage))
|
||||
state.eventSink(JoinRoomEvent.UpdateKnockMessage(knockMessage))
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(JoinRoomEvents.KnockRoom)
|
||||
state.eventSink(JoinRoomEvent.KnockRoom)
|
||||
}
|
||||
|
||||
assertThat(awaitItem().knockAction).isEqualTo(AsyncAction.Loading)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.knockAction).isEqualTo(AsyncAction.Success(Unit))
|
||||
fakeKnockRoom.lambda = knockRoomFailure
|
||||
state.eventSink(JoinRoomEvents.KnockRoom)
|
||||
state.eventSink(JoinRoomEvent.KnockRoom)
|
||||
}
|
||||
|
||||
assertThat(awaitItem().knockAction).isEqualTo(AsyncAction.Loading)
|
||||
@@ -731,17 +731,17 @@ class JoinRoomPresenterTest {
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(JoinRoomEvents.CancelKnock(true))
|
||||
state.eventSink(JoinRoomEvent.CancelKnock(true))
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.cancelKnockAction).isEqualTo(AsyncAction.ConfirmingNoParams)
|
||||
state.eventSink(JoinRoomEvents.CancelKnock(false))
|
||||
state.eventSink(JoinRoomEvent.CancelKnock(false))
|
||||
}
|
||||
assertThat(awaitItem().cancelKnockAction).isEqualTo(AsyncAction.Loading)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.cancelKnockAction).isEqualTo(AsyncAction.Success(Unit))
|
||||
cancelKnockRoom.lambda = cancelKnockRoomFailure
|
||||
state.eventSink(JoinRoomEvents.CancelKnock(false))
|
||||
state.eventSink(JoinRoomEvent.CancelKnock(false))
|
||||
}
|
||||
assertThat(awaitItem().cancelKnockAction).isEqualTo(AsyncAction.Loading)
|
||||
awaitItem().also { state ->
|
||||
@@ -778,14 +778,14 @@ class JoinRoomPresenterTest {
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(JoinRoomEvents.ForgetRoom)
|
||||
state.eventSink(JoinRoomEvent.ForgetRoom)
|
||||
}
|
||||
|
||||
assertThat(awaitItem().forgetAction).isEqualTo(AsyncAction.Loading)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.forgetAction).isEqualTo(AsyncAction.Success(Unit))
|
||||
fakeForgetRoom.lambda = forgetRoomFailure
|
||||
state.eventSink(JoinRoomEvents.ForgetRoom)
|
||||
state.eventSink(JoinRoomEvent.ForgetRoom)
|
||||
}
|
||||
|
||||
assertThat(awaitItem().forgetAction).isEqualTo(AsyncAction.Loading)
|
||||
|
||||
+26
-26
@@ -34,7 +34,7 @@ import org.junit.Test
|
||||
class JoinRoomViewTest : RobolectricTest() {
|
||||
@Test
|
||||
fun `clicking on back invoke the expected callback`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvent>(expectEvents = false)
|
||||
ensureCalledOnce {
|
||||
setJoinRoomView(
|
||||
aJoinRoomState(
|
||||
@@ -48,7 +48,7 @@ class JoinRoomViewTest : RobolectricTest() {
|
||||
|
||||
@Test
|
||||
fun `clicking on Join room on CanJoin room emits the expected Event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>()
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvent>()
|
||||
setJoinRoomView(
|
||||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.CanJoin),
|
||||
@@ -56,12 +56,12 @@ class JoinRoomViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
clickOn(R.string.screen_join_room_join_action)
|
||||
eventsRecorder.assertSingle(JoinRoomEvents.JoinRoom)
|
||||
eventsRecorder.assertSingle(JoinRoomEvent.JoinRoom)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on Knock room on CanKnock room emits the expected Event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>()
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvent>()
|
||||
setJoinRoomView(
|
||||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.CanKnock),
|
||||
@@ -70,12 +70,12 @@ class JoinRoomViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
clickOn(R.string.screen_join_room_knock_action)
|
||||
eventsRecorder.assertSingle(JoinRoomEvents.KnockRoom)
|
||||
eventsRecorder.assertSingle(JoinRoomEvent.KnockRoom)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on closing Knock error emits the expected Event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>()
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvent>()
|
||||
setJoinRoomView(
|
||||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.CanKnock),
|
||||
@@ -84,12 +84,12 @@ class JoinRoomViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
clickOn(CommonStrings.action_ok)
|
||||
eventsRecorder.assertSingle(JoinRoomEvents.ClearActionStates)
|
||||
eventsRecorder.assertSingle(JoinRoomEvent.ClearActionStates)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on cancel knock request emit the expected Event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>()
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvent>()
|
||||
setJoinRoomView(
|
||||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsKnocked),
|
||||
@@ -97,12 +97,12 @@ class JoinRoomViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
clickOn(R.string.screen_join_room_cancel_knock_action)
|
||||
eventsRecorder.assertSingle(JoinRoomEvents.CancelKnock(true))
|
||||
eventsRecorder.assertSingle(JoinRoomEvent.CancelKnock(true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on closing Cancel Knock error emits the expected Event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>()
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvent>()
|
||||
setJoinRoomView(
|
||||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsKnocked),
|
||||
@@ -111,12 +111,12 @@ class JoinRoomViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
clickOn(CommonStrings.action_ok)
|
||||
eventsRecorder.assertSingle(JoinRoomEvents.ClearActionStates)
|
||||
eventsRecorder.assertSingle(JoinRoomEvent.ClearActionStates)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on closing Join error emits the expected Event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>()
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvent>()
|
||||
setJoinRoomView(
|
||||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.CanKnock),
|
||||
@@ -125,12 +125,12 @@ class JoinRoomViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
clickOn(CommonStrings.action_ok)
|
||||
eventsRecorder.assertSingle(JoinRoomEvents.ClearActionStates)
|
||||
eventsRecorder.assertSingle(JoinRoomEvent.ClearActionStates)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when joining room is successful, the expected callback is invoked`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvent>(expectEvents = false)
|
||||
ensureCalledOnce {
|
||||
setJoinRoomView(
|
||||
aJoinRoomState(
|
||||
@@ -144,7 +144,7 @@ class JoinRoomViewTest : RobolectricTest() {
|
||||
|
||||
@Test
|
||||
fun `clicking on Accept when JoinAuthorisationStatus is IsInvited emits the expected Event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>()
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvent>()
|
||||
val inviteData = anInviteData()
|
||||
setJoinRoomView(
|
||||
aJoinRoomState(
|
||||
@@ -153,12 +153,12 @@ class JoinRoomViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
clickOn(CommonStrings.action_accept)
|
||||
eventsRecorder.assertSingle(JoinRoomEvents.AcceptInvite(inviteData))
|
||||
eventsRecorder.assertSingle(JoinRoomEvent.AcceptInvite(inviteData))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on Decline when JoinAuthorisationStatus is IsInvited emits the expected Event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>()
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvent>()
|
||||
val inviteData = anInviteData()
|
||||
setJoinRoomView(
|
||||
aJoinRoomState(
|
||||
@@ -167,13 +167,13 @@ class JoinRoomViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
clickOn(CommonStrings.action_decline)
|
||||
eventsRecorder.assertSingle(JoinRoomEvents.DeclineInvite(inviteData, false))
|
||||
eventsRecorder.assertSingle(JoinRoomEvent.DeclineInvite(inviteData, false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on Decline and block when JoinAuthorisationStatus is IsInvited and can report room, the expected callback is invoked`() {
|
||||
runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvent>(expectEvents = false)
|
||||
val inviteData = anInviteData()
|
||||
val joinRoomState = aJoinRoomState(
|
||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(inviteData, aRoomMember().toInviteSender())),
|
||||
@@ -192,7 +192,7 @@ class JoinRoomViewTest : RobolectricTest() {
|
||||
|
||||
@Test
|
||||
fun `clicking on Decline and block when JoinAuthorisationStatus is IsInvited and cant report room, emits the expected Event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>()
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvent>()
|
||||
val inviteData = anInviteData()
|
||||
val joinRoomState = aJoinRoomState(
|
||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(inviteData, aRoomMember().toInviteSender())),
|
||||
@@ -201,12 +201,12 @@ class JoinRoomViewTest : RobolectricTest() {
|
||||
)
|
||||
setJoinRoomView(state = joinRoomState)
|
||||
clickOn(R.string.screen_join_room_decline_and_block_button_title)
|
||||
eventsRecorder.assertSingle(JoinRoomEvents.DeclineInvite(inviteData, true))
|
||||
eventsRecorder.assertSingle(JoinRoomEvent.DeclineInvite(inviteData, true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on Retry when an error occurs emits the expected Event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>()
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvent>()
|
||||
setJoinRoomView(
|
||||
aJoinRoomState(
|
||||
contentState = aFailureContentState(),
|
||||
@@ -214,12 +214,12 @@ class JoinRoomViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
clickOn(CommonStrings.action_retry)
|
||||
eventsRecorder.assertSingle(JoinRoomEvents.RetryFetchingContent)
|
||||
eventsRecorder.assertSingle(JoinRoomEvent.RetryFetchingContent)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on ok when user is unauthorized the expected callback`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvent>(expectEvents = false)
|
||||
ensureCalledOnce {
|
||||
setJoinRoomView(
|
||||
aJoinRoomState(
|
||||
@@ -235,7 +235,7 @@ class JoinRoomViewTest : RobolectricTest() {
|
||||
|
||||
@Test
|
||||
fun `clicking on forget when user is banned invokes the expected callback`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>()
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvent>()
|
||||
setJoinRoomView(
|
||||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsBanned(null, null)),
|
||||
@@ -243,7 +243,7 @@ class JoinRoomViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
clickOn(R.string.screen_join_room_forget_action)
|
||||
eventsRecorder.assertSingle(JoinRoomEvents.ForgetRoom)
|
||||
eventsRecorder.assertSingle(JoinRoomEvent.ForgetRoom)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -8,7 +8,7 @@
|
||||
|
||||
package io.element.android.features.knockrequests.impl.banner
|
||||
|
||||
sealed interface KnockRequestsBannerEvents {
|
||||
data object AcceptSingleRequest : KnockRequestsBannerEvents
|
||||
data object Dismiss : KnockRequestsBannerEvents
|
||||
sealed interface KnockRequestsBannerEvent {
|
||||
data object AcceptSingleRequest : KnockRequestsBannerEvent
|
||||
data object Dismiss : KnockRequestsBannerEvent
|
||||
}
|
||||
+3
-3
@@ -53,15 +53,15 @@ class KnockRequestsBannerPresenter(
|
||||
}
|
||||
}
|
||||
|
||||
fun handleEvent(event: KnockRequestsBannerEvents) {
|
||||
fun handleEvent(event: KnockRequestsBannerEvent) {
|
||||
when (event) {
|
||||
is KnockRequestsBannerEvents.AcceptSingleRequest -> {
|
||||
is KnockRequestsBannerEvent.AcceptSingleRequest -> {
|
||||
sessionCoroutineScope.acceptSingleKnockRequest(
|
||||
knockRequests = knockRequests,
|
||||
displayAcceptError = showAcceptError,
|
||||
)
|
||||
}
|
||||
is KnockRequestsBannerEvents.Dismiss -> {
|
||||
is KnockRequestsBannerEvent.Dismiss -> {
|
||||
sessionCoroutineScope.launch {
|
||||
knockRequestsService.markAllKnockRequestsAsSeen()
|
||||
}
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ data class KnockRequestsBannerState(
|
||||
val knockRequests: ImmutableList<KnockRequestPresentable>,
|
||||
val displayAcceptError: Boolean,
|
||||
val canAccept: Boolean,
|
||||
val eventSink: (KnockRequestsBannerEvents) -> Unit,
|
||||
val eventSink: (KnockRequestsBannerEvent) -> Unit,
|
||||
) {
|
||||
val subtitle = knockRequests.singleOrNull()?.userId?.value
|
||||
val reason = knockRequests.singleOrNull()?.reason
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ fun aKnockRequestsBannerState(
|
||||
displayAcceptError: Boolean = false,
|
||||
canAccept: Boolean = true,
|
||||
isVisible: Boolean = true,
|
||||
eventSink: (KnockRequestsBannerEvents) -> Unit = {}
|
||||
eventSink: (KnockRequestsBannerEvent) -> Unit = {}
|
||||
) = KnockRequestsBannerState(
|
||||
knockRequests = knockRequests.toImmutableList(),
|
||||
displayAcceptError = displayAcceptError,
|
||||
|
||||
+2
-2
@@ -109,11 +109,11 @@ private fun KnockRequestsBannerContent(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
fun onDismissClick() {
|
||||
state.eventSink(KnockRequestsBannerEvents.Dismiss)
|
||||
state.eventSink(KnockRequestsBannerEvent.Dismiss)
|
||||
}
|
||||
|
||||
fun onAcceptClick() {
|
||||
state.eventSink(KnockRequestsBannerEvents.AcceptSingleRequest)
|
||||
state.eventSink(KnockRequestsBannerEvent.AcceptSingleRequest)
|
||||
}
|
||||
|
||||
Column(
|
||||
|
||||
+8
-8
@@ -10,12 +10,12 @@ package io.element.android.features.knockrequests.impl.list
|
||||
|
||||
import io.element.android.features.knockrequests.impl.data.KnockRequestPresentable
|
||||
|
||||
sealed interface KnockRequestsListEvents {
|
||||
data class Accept(val knockRequest: KnockRequestPresentable) : KnockRequestsListEvents
|
||||
data class Decline(val knockRequest: KnockRequestPresentable) : KnockRequestsListEvents
|
||||
data class DeclineAndBan(val knockRequest: KnockRequestPresentable) : KnockRequestsListEvents
|
||||
data object AcceptAll : KnockRequestsListEvents
|
||||
data object ResetCurrentAction : KnockRequestsListEvents
|
||||
data object RetryCurrentAction : KnockRequestsListEvents
|
||||
data object ConfirmCurrentAction : KnockRequestsListEvents
|
||||
sealed interface KnockRequestsListEvent {
|
||||
data class Accept(val knockRequest: KnockRequestPresentable) : KnockRequestsListEvent
|
||||
data class Decline(val knockRequest: KnockRequestPresentable) : KnockRequestsListEvent
|
||||
data class DeclineAndBan(val knockRequest: KnockRequestPresentable) : KnockRequestsListEvent
|
||||
data object AcceptAll : KnockRequestsListEvent
|
||||
data object ResetCurrentAction : KnockRequestsListEvent
|
||||
data object RetryCurrentAction : KnockRequestsListEvent
|
||||
data object ConfirmCurrentAction : KnockRequestsListEvent
|
||||
}
|
||||
+8
-8
@@ -39,28 +39,28 @@ class KnockRequestsListPresenter(
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
fun handleEvent(event: KnockRequestsListEvents) {
|
||||
fun handleEvent(event: KnockRequestsListEvent) {
|
||||
when (event) {
|
||||
KnockRequestsListEvents.AcceptAll -> {
|
||||
KnockRequestsListEvent.AcceptAll -> {
|
||||
currentAction = KnockRequestsAction.AcceptAll
|
||||
}
|
||||
is KnockRequestsListEvents.Accept -> {
|
||||
is KnockRequestsListEvent.Accept -> {
|
||||
currentAction = KnockRequestsAction.Accept(event.knockRequest)
|
||||
}
|
||||
is KnockRequestsListEvents.Decline -> {
|
||||
is KnockRequestsListEvent.Decline -> {
|
||||
currentAction = KnockRequestsAction.Decline(event.knockRequest)
|
||||
}
|
||||
is KnockRequestsListEvents.DeclineAndBan -> {
|
||||
is KnockRequestsListEvent.DeclineAndBan -> {
|
||||
currentAction = KnockRequestsAction.DeclineAndBan(event.knockRequest)
|
||||
}
|
||||
KnockRequestsListEvents.ResetCurrentAction -> {
|
||||
KnockRequestsListEvent.ResetCurrentAction -> {
|
||||
asyncAction.value = AsyncAction.Uninitialized
|
||||
currentAction = KnockRequestsAction.None
|
||||
}
|
||||
KnockRequestsListEvents.RetryCurrentAction -> {
|
||||
KnockRequestsListEvent.RetryCurrentAction -> {
|
||||
coroutineScope.executeAction(currentAction, asyncAction, isActionConfirmed = true)
|
||||
}
|
||||
KnockRequestsListEvents.ConfirmCurrentAction -> {
|
||||
KnockRequestsListEvent.ConfirmCurrentAction -> {
|
||||
coroutineScope.executeAction(currentAction, asyncAction, isActionConfirmed = true)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ data class KnockRequestsListState(
|
||||
val currentAction: KnockRequestsAction,
|
||||
val asyncAction: AsyncAction<Unit>,
|
||||
val permissions: KnockRequestPermissions,
|
||||
val eventSink: (KnockRequestsListEvents) -> Unit,
|
||||
val eventSink: (KnockRequestsListEvent) -> Unit,
|
||||
) {
|
||||
val canAcceptAll = permissions.canAccept && knockRequests is AsyncData.Success && knockRequests.data.size > 1
|
||||
}
|
||||
|
||||
+1
-1
@@ -140,7 +140,7 @@ fun aKnockRequestsListState(
|
||||
canDecline = true,
|
||||
canBan = true,
|
||||
),
|
||||
eventSink: (KnockRequestsListEvents) -> Unit = {},
|
||||
eventSink: (KnockRequestsListEvent) -> Unit = {},
|
||||
) = KnockRequestsListState(
|
||||
knockRequests = knockRequests,
|
||||
currentAction = currentAction,
|
||||
|
||||
+7
-7
@@ -108,15 +108,15 @@ private fun KnockRequestsListContent(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
fun onAcceptClick(knockRequest: KnockRequestPresentable) {
|
||||
state.eventSink(KnockRequestsListEvents.Accept(knockRequest))
|
||||
state.eventSink(KnockRequestsListEvent.Accept(knockRequest))
|
||||
}
|
||||
|
||||
fun onDeclineClick(knockRequest: KnockRequestPresentable) {
|
||||
state.eventSink(KnockRequestsListEvents.Decline(knockRequest))
|
||||
state.eventSink(KnockRequestsListEvent.Decline(knockRequest))
|
||||
}
|
||||
|
||||
fun onBanClick(knockRequest: KnockRequestPresentable) {
|
||||
state.eventSink(KnockRequestsListEvents.DeclineAndBan(knockRequest))
|
||||
state.eventSink(KnockRequestsListEvent.DeclineAndBan(knockRequest))
|
||||
}
|
||||
|
||||
var bottomPaddingInPixels by remember { mutableIntStateOf(0) }
|
||||
@@ -160,19 +160,19 @@ private fun KnockRequestsListContent(
|
||||
currentAction = state.currentAction,
|
||||
asyncAction = state.asyncAction,
|
||||
onConfirm = {
|
||||
state.eventSink(KnockRequestsListEvents.ConfirmCurrentAction)
|
||||
state.eventSink(KnockRequestsListEvent.ConfirmCurrentAction)
|
||||
},
|
||||
onRetry = {
|
||||
state.eventSink(KnockRequestsListEvents.RetryCurrentAction)
|
||||
state.eventSink(KnockRequestsListEvent.RetryCurrentAction)
|
||||
},
|
||||
onDismiss = {
|
||||
state.eventSink(KnockRequestsListEvents.ResetCurrentAction)
|
||||
state.eventSink(KnockRequestsListEvent.ResetCurrentAction)
|
||||
},
|
||||
)
|
||||
if (state.canAcceptAll) {
|
||||
KnockRequestsAcceptAll(
|
||||
onClick = {
|
||||
state.eventSink(KnockRequestsListEvents.AcceptAll)
|
||||
state.eventSink(KnockRequestsListEvent.AcceptAll)
|
||||
},
|
||||
onHeightChange = { height ->
|
||||
bottomPaddingInPixels = height
|
||||
|
||||
+3
-3
@@ -146,7 +146,7 @@ import org.junit.Test
|
||||
presenter.test {
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(KnockRequestsBannerEvents.AcceptSingleRequest)
|
||||
state.eventSink(KnockRequestsBannerEvent.AcceptSingleRequest)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.isVisible).isFalse()
|
||||
@@ -182,7 +182,7 @@ import org.junit.Test
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.knockRequests).hasSize(1)
|
||||
state.eventSink(KnockRequestsBannerEvents.AcceptSingleRequest)
|
||||
state.eventSink(KnockRequestsBannerEvent.AcceptSingleRequest)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.isVisible).isFalse()
|
||||
@@ -206,7 +206,7 @@ import org.junit.Test
|
||||
presenter.test {
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(KnockRequestsBannerEvents.Dismiss)
|
||||
state.eventSink(KnockRequestsBannerEvent.Dismiss)
|
||||
}
|
||||
advanceUntilIdle()
|
||||
assert(markAsSeenLambda).isCalledExactly(3)
|
||||
|
||||
+6
-6
@@ -29,7 +29,7 @@ import org.junit.Test
|
||||
class KnockRequestsBannerViewTest : RobolectricTest() {
|
||||
@Test
|
||||
fun `clicking on view on single request invoke the expected callback`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsBannerEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsBannerEvent>(expectEvents = false)
|
||||
ensureCalledOnce {
|
||||
setKnockRequestsBannerView(
|
||||
state = aKnockRequestsBannerState(
|
||||
@@ -43,7 +43,7 @@ class KnockRequestsBannerViewTest : RobolectricTest() {
|
||||
|
||||
@Test
|
||||
fun `clicking on view all when multiple requests invoke the expected callback`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsBannerEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsBannerEvent>(expectEvents = false)
|
||||
ensureCalledOnce {
|
||||
setKnockRequestsBannerView(
|
||||
state = aKnockRequestsBannerState(
|
||||
@@ -62,19 +62,19 @@ class KnockRequestsBannerViewTest : RobolectricTest() {
|
||||
|
||||
@Test
|
||||
fun `clicking on accept on a single request emit the expected event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsBannerEvents>()
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsBannerEvent>()
|
||||
setKnockRequestsBannerView(
|
||||
state = aKnockRequestsBannerState(
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
clickOn(CommonStrings.action_accept)
|
||||
eventsRecorder.assertSingle(KnockRequestsBannerEvents.AcceptSingleRequest)
|
||||
eventsRecorder.assertSingle(KnockRequestsBannerEvent.AcceptSingleRequest)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on dismiss emit the expected event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsBannerEvents>()
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsBannerEvent>()
|
||||
setKnockRequestsBannerView(
|
||||
state = aKnockRequestsBannerState(
|
||||
eventSink = eventsRecorder,
|
||||
@@ -82,7 +82,7 @@ class KnockRequestsBannerViewTest : RobolectricTest() {
|
||||
)
|
||||
val close = activity!!.getString(CommonStrings.action_close)
|
||||
onNodeWithContentDescription(close).performClick()
|
||||
eventsRecorder.assertSingle(KnockRequestsBannerEvents.Dismiss)
|
||||
eventsRecorder.assertSingle(KnockRequestsBannerEvent.Dismiss)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-17
@@ -65,7 +65,7 @@ class KnockRequestsListPresenterTest {
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
val knockRequestPresentable = state.knockRequests.dataOrNull()?.first()!!
|
||||
state.eventSink(KnockRequestsListEvents.Accept(knockRequestPresentable))
|
||||
state.eventSink(KnockRequestsListEvent.Accept(knockRequestPresentable))
|
||||
}
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
@@ -75,7 +75,7 @@ class KnockRequestsListPresenterTest {
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.asyncAction).isInstanceOf(AsyncAction.Success::class.java)
|
||||
state.eventSink(KnockRequestsListEvents.ResetCurrentAction)
|
||||
state.eventSink(KnockRequestsListEvent.ResetCurrentAction)
|
||||
}
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
@@ -99,7 +99,7 @@ class KnockRequestsListPresenterTest {
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
val knockRequestPresentable = state.knockRequests.dataOrNull()?.first()!!
|
||||
state.eventSink(KnockRequestsListEvents.Accept(knockRequestPresentable))
|
||||
state.eventSink(KnockRequestsListEvent.Accept(knockRequestPresentable))
|
||||
}
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
@@ -109,14 +109,14 @@ class KnockRequestsListPresenterTest {
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.asyncAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
state.eventSink(KnockRequestsListEvents.RetryCurrentAction)
|
||||
state.eventSink(KnockRequestsListEvent.RetryCurrentAction)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.asyncAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.asyncAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
state.eventSink(KnockRequestsListEvents.ResetCurrentAction)
|
||||
state.eventSink(KnockRequestsListEvent.ResetCurrentAction)
|
||||
}
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
@@ -140,21 +140,21 @@ class KnockRequestsListPresenterTest {
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
val knockRequestPresentable = state.knockRequests.dataOrNull()?.first()!!
|
||||
state.eventSink(KnockRequestsListEvents.Decline(knockRequestPresentable))
|
||||
state.eventSink(KnockRequestsListEvent.Decline(knockRequestPresentable))
|
||||
}
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
val knockRequestPresentable = state.knockRequests.dataOrNull()?.first()!!
|
||||
assertThat(state.currentAction).isEqualTo(KnockRequestsAction.Decline(knockRequestPresentable))
|
||||
assertThat(state.asyncAction).isInstanceOf(AsyncAction.ConfirmingNoParams::class.java)
|
||||
state.eventSink(KnockRequestsListEvents.ConfirmCurrentAction)
|
||||
state.eventSink(KnockRequestsListEvent.ConfirmCurrentAction)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.asyncAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.asyncAction).isInstanceOf(AsyncAction.Success::class.java)
|
||||
state.eventSink(KnockRequestsListEvents.ResetCurrentAction)
|
||||
state.eventSink(KnockRequestsListEvent.ResetCurrentAction)
|
||||
}
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
@@ -178,21 +178,21 @@ class KnockRequestsListPresenterTest {
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
val knockRequestPresentable = state.knockRequests.dataOrNull()?.first()!!
|
||||
state.eventSink(KnockRequestsListEvents.DeclineAndBan(knockRequestPresentable))
|
||||
state.eventSink(KnockRequestsListEvent.DeclineAndBan(knockRequestPresentable))
|
||||
}
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
val knockRequestPresentable = state.knockRequests.dataOrNull()?.first()!!
|
||||
assertThat(state.currentAction).isEqualTo(KnockRequestsAction.DeclineAndBan(knockRequestPresentable))
|
||||
assertThat(state.asyncAction).isInstanceOf(AsyncAction.ConfirmingNoParams::class.java)
|
||||
state.eventSink(KnockRequestsListEvents.ConfirmCurrentAction)
|
||||
state.eventSink(KnockRequestsListEvent.ConfirmCurrentAction)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.asyncAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.asyncAction).isInstanceOf(AsyncAction.Success::class.java)
|
||||
state.eventSink(KnockRequestsListEvents.ResetCurrentAction)
|
||||
state.eventSink(KnockRequestsListEvent.ResetCurrentAction)
|
||||
}
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
@@ -220,20 +220,20 @@ class KnockRequestsListPresenterTest {
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.canAcceptAll).isTrue()
|
||||
state.eventSink(KnockRequestsListEvents.AcceptAll)
|
||||
state.eventSink(KnockRequestsListEvent.AcceptAll)
|
||||
}
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.currentAction).isEqualTo(KnockRequestsAction.AcceptAll)
|
||||
assertThat(state.asyncAction).isInstanceOf(AsyncAction.ConfirmingNoParams::class.java)
|
||||
state.eventSink(KnockRequestsListEvents.ConfirmCurrentAction)
|
||||
state.eventSink(KnockRequestsListEvent.ConfirmCurrentAction)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.asyncAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.asyncAction).isInstanceOf(AsyncAction.Success::class.java)
|
||||
state.eventSink(KnockRequestsListEvents.ResetCurrentAction)
|
||||
state.eventSink(KnockRequestsListEvent.ResetCurrentAction)
|
||||
}
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
@@ -262,20 +262,20 @@ class KnockRequestsListPresenterTest {
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.canAcceptAll).isTrue()
|
||||
state.eventSink(KnockRequestsListEvents.AcceptAll)
|
||||
state.eventSink(KnockRequestsListEvent.AcceptAll)
|
||||
}
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.currentAction).isEqualTo(KnockRequestsAction.AcceptAll)
|
||||
assertThat(state.asyncAction).isInstanceOf(AsyncAction.ConfirmingNoParams::class.java)
|
||||
state.eventSink(KnockRequestsListEvents.ConfirmCurrentAction)
|
||||
state.eventSink(KnockRequestsListEvent.ConfirmCurrentAction)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.asyncAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.asyncAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
state.eventSink(KnockRequestsListEvents.ResetCurrentAction)
|
||||
state.eventSink(KnockRequestsListEvent.ResetCurrentAction)
|
||||
}
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
|
||||
+15
-15
@@ -31,7 +31,7 @@ import org.junit.Test
|
||||
class KnockRequestsListViewTest : RobolectricTest() {
|
||||
@Test
|
||||
fun `clicking on back invoke the expected callback`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsListEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsListEvent>(expectEvents = false)
|
||||
ensureCalledOnce {
|
||||
setKnockRequestsListView(
|
||||
aKnockRequestsListState(
|
||||
@@ -45,7 +45,7 @@ class KnockRequestsListViewTest : RobolectricTest() {
|
||||
|
||||
@Test
|
||||
fun `clicking on accept emit the expected event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsListEvents>()
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsListEvent>()
|
||||
val knockRequest = aKnockRequestPresentable()
|
||||
setKnockRequestsListView(
|
||||
aKnockRequestsListState(
|
||||
@@ -54,12 +54,12 @@ class KnockRequestsListViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
clickOn(CommonStrings.action_accept)
|
||||
eventsRecorder.assertSingle(KnockRequestsListEvents.Accept(knockRequest))
|
||||
eventsRecorder.assertSingle(KnockRequestsListEvent.Accept(knockRequest))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on decline emit the expected event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsListEvents>()
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsListEvent>()
|
||||
val knockRequest = aKnockRequestPresentable()
|
||||
setKnockRequestsListView(
|
||||
aKnockRequestsListState(
|
||||
@@ -68,12 +68,12 @@ class KnockRequestsListViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
clickOn(CommonStrings.action_decline)
|
||||
eventsRecorder.assertSingle(KnockRequestsListEvents.Decline(knockRequest))
|
||||
eventsRecorder.assertSingle(KnockRequestsListEvent.Decline(knockRequest))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on decline and ban emit the expected event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsListEvents>()
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsListEvent>()
|
||||
val knockRequest = aKnockRequestPresentable()
|
||||
setKnockRequestsListView(
|
||||
aKnockRequestsListState(
|
||||
@@ -82,12 +82,12 @@ class KnockRequestsListViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
clickOn(R.string.screen_knock_requests_list_decline_and_ban_action_title)
|
||||
eventsRecorder.assertSingle(KnockRequestsListEvents.DeclineAndBan(knockRequest))
|
||||
eventsRecorder.assertSingle(KnockRequestsListEvent.DeclineAndBan(knockRequest))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on accept all emit the expected event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsListEvents>()
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsListEvent>()
|
||||
val knockRequests = persistentListOf(aKnockRequestPresentable(), aKnockRequestPresentable())
|
||||
setKnockRequestsListView(
|
||||
aKnockRequestsListState(
|
||||
@@ -96,12 +96,12 @@ class KnockRequestsListViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
clickOn(R.string.screen_knock_requests_list_accept_all_button_title)
|
||||
eventsRecorder.assertSingle(KnockRequestsListEvents.AcceptAll)
|
||||
eventsRecorder.assertSingle(KnockRequestsListEvent.AcceptAll)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `retry on async view retry emit the expected event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsListEvents>()
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsListEvent>()
|
||||
val knockRequests = persistentListOf(aKnockRequestPresentable(), aKnockRequestPresentable())
|
||||
setKnockRequestsListView(
|
||||
aKnockRequestsListState(
|
||||
@@ -112,12 +112,12 @@ class KnockRequestsListViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
clickOn(CommonStrings.action_retry)
|
||||
eventsRecorder.assertSingle(KnockRequestsListEvents.RetryCurrentAction)
|
||||
eventsRecorder.assertSingle(KnockRequestsListEvent.RetryCurrentAction)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `canceling async view emit the expected event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsListEvents>()
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsListEvent>()
|
||||
val knockRequests = persistentListOf(aKnockRequestPresentable(), aKnockRequestPresentable())
|
||||
setKnockRequestsListView(
|
||||
aKnockRequestsListState(
|
||||
@@ -128,12 +128,12 @@ class KnockRequestsListViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
clickOn(CommonStrings.action_cancel)
|
||||
eventsRecorder.assertSingle(KnockRequestsListEvents.ResetCurrentAction)
|
||||
eventsRecorder.assertSingle(KnockRequestsListEvent.ResetCurrentAction)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `confirming async view emit the expected event`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsListEvents>()
|
||||
val eventsRecorder = EventsRecorder<KnockRequestsListEvent>()
|
||||
val knockRequests = persistentListOf(aKnockRequestPresentable(), aKnockRequestPresentable())
|
||||
setKnockRequestsListView(
|
||||
aKnockRequestsListState(
|
||||
@@ -144,7 +144,7 @@ class KnockRequestsListViewTest : RobolectricTest() {
|
||||
),
|
||||
)
|
||||
clickOn(R.string.screen_knock_requests_list_accept_all_alert_confirm_button_title)
|
||||
eventsRecorder.assertSingle(KnockRequestsListEvents.ConfirmCurrentAction)
|
||||
eventsRecorder.assertSingle(KnockRequestsListEvent.ConfirmCurrentAction)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -41,9 +41,9 @@ class DefaultPermissionsPresenter(
|
||||
permissionsRequested = true
|
||||
}
|
||||
|
||||
fun handleEvent(event: PermissionsEvents) {
|
||||
fun handleEvent(event: PermissionsEvent) {
|
||||
when (event) {
|
||||
PermissionsEvents.RequestPermissions -> {
|
||||
PermissionsEvent.RequestPermissions -> {
|
||||
multiplePermissionsState.launchMultiplePermissionRequest()
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -8,6 +8,6 @@
|
||||
|
||||
package io.element.android.features.location.impl.common.permissions
|
||||
|
||||
sealed interface PermissionsEvents {
|
||||
data object RequestPermissions : PermissionsEvents
|
||||
sealed interface PermissionsEvent {
|
||||
data object RequestPermissions : PermissionsEvent
|
||||
}
|
||||
+1
-1
@@ -12,7 +12,7 @@ data class PermissionsState(
|
||||
val permissions: Permissions,
|
||||
val shouldShowRationale: Boolean,
|
||||
val permissionsAlreadyRequested: Boolean,
|
||||
val eventSink: (PermissionsEvents) -> Unit,
|
||||
val eventSink: (PermissionsEvent) -> Unit,
|
||||
) {
|
||||
sealed interface Permissions {
|
||||
data object AllGranted : Permissions
|
||||
|
||||
+4
-4
@@ -29,7 +29,7 @@ import io.element.android.features.location.impl.common.MapDefaults
|
||||
import io.element.android.features.location.impl.common.SendLiveLocationPermissions
|
||||
import io.element.android.features.location.impl.common.actions.LocationActions
|
||||
import io.element.android.features.location.impl.common.checkLocationConstraints
|
||||
import io.element.android.features.location.impl.common.permissions.PermissionsEvents
|
||||
import io.element.android.features.location.impl.common.permissions.PermissionsEvent
|
||||
import io.element.android.features.location.impl.common.permissions.PermissionsPresenter
|
||||
import io.element.android.features.location.impl.common.permissions.PermissionsState
|
||||
import io.element.android.features.location.impl.common.sendLiveLocationPermissions
|
||||
@@ -111,7 +111,7 @@ class ShareLocationPresenter(
|
||||
sendLiveLocationPermissions = SendLiveLocationPermissions.GRANTED
|
||||
)
|
||||
if (locationConstraints is LocationConstraintsCheck.PermissionShouldBeRequested) {
|
||||
permissionsState.eventSink(PermissionsEvents.RequestPermissions)
|
||||
permissionsState.eventSink(PermissionsEvent.RequestPermissions)
|
||||
}
|
||||
trackUserPosition = locationConstraints is LocationConstraintsCheck.Success
|
||||
dialogState = ShareLocationState.Dialog.Constraints(locationConstraints.toDialogState())
|
||||
@@ -137,7 +137,7 @@ class ShareLocationPresenter(
|
||||
}
|
||||
else -> {
|
||||
if (locationConstraints is LocationConstraintsCheck.PermissionShouldBeRequested) {
|
||||
permissionsState.eventSink(PermissionsEvents.RequestPermissions)
|
||||
permissionsState.eventSink(PermissionsEvent.RequestPermissions)
|
||||
}
|
||||
dialogState = ShareLocationState.Dialog.Constraints(locationConstraints.toDialogState())
|
||||
}
|
||||
@@ -195,7 +195,7 @@ class ShareLocationPresenter(
|
||||
}
|
||||
ShareLocationEvent.RequestPermissions -> {
|
||||
dialogState = ShareLocationState.Dialog.None
|
||||
permissionsState.eventSink(PermissionsEvents.RequestPermissions)
|
||||
permissionsState.eventSink(PermissionsEvent.RequestPermissions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -32,7 +32,7 @@ import io.element.android.features.location.impl.common.MapDefaults
|
||||
import io.element.android.features.location.impl.common.SendLiveLocationPermissions
|
||||
import io.element.android.features.location.impl.common.actions.LocationActions
|
||||
import io.element.android.features.location.impl.common.checkLocationConstraints
|
||||
import io.element.android.features.location.impl.common.permissions.PermissionsEvents
|
||||
import io.element.android.features.location.impl.common.permissions.PermissionsEvent
|
||||
import io.element.android.features.location.impl.common.permissions.PermissionsPresenter
|
||||
import io.element.android.features.location.impl.common.permissions.PermissionsState
|
||||
import io.element.android.features.location.impl.common.toDialogState
|
||||
@@ -99,7 +99,7 @@ class ShowLocationPresenter(
|
||||
sendLiveLocationPermissions = SendLiveLocationPermissions.GRANTED
|
||||
)
|
||||
if (locationConstraints is LocationConstraintsCheck.PermissionShouldBeRequested) {
|
||||
permissionsState.eventSink(PermissionsEvents.RequestPermissions)
|
||||
permissionsState.eventSink(PermissionsEvent.RequestPermissions)
|
||||
}
|
||||
isTrackMyLocation = locationConstraints is LocationConstraintsCheck.Success
|
||||
dialogState = locationConstraints.toDialogState()
|
||||
@@ -130,7 +130,7 @@ class ShowLocationPresenter(
|
||||
locationActions.openLocationSettings()
|
||||
dialogState = LocationConstraintsDialogState.None
|
||||
}
|
||||
ShowLocationEvent.RequestPermissions -> permissionsState.eventSink(PermissionsEvents.RequestPermissions)
|
||||
ShowLocationEvent.RequestPermissions -> permissionsState.eventSink(PermissionsEvent.RequestPermissions)
|
||||
ShowLocationEvent.StopLocationSharing -> coroutineScope.launch {
|
||||
liveLocationShareManager.stopShare(joinedRoom.roomId)
|
||||
}
|
||||
|
||||
+2
-2
@@ -11,9 +11,9 @@ package io.element.android.features.location.impl.common.permissions
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
class FakePermissionsPresenter : PermissionsPresenter {
|
||||
val events = mutableListOf<PermissionsEvents>()
|
||||
val events = mutableListOf<PermissionsEvent>()
|
||||
|
||||
private fun handleEvent(event: PermissionsEvents) {
|
||||
private fun handleEvent(event: PermissionsEvent) {
|
||||
events += event
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user