Cleanup and compact code. Also prefer usage of DayNightPreview.
This commit is contained in:
parent
372cc2a16e
commit
574e6199fe
6 changed files with 36 additions and 122 deletions
|
|
@ -95,8 +95,8 @@ class EditUserProfilePresenter @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
val canSave = remember(userDisplayName, userAvatarUri) {
|
val canSave = remember(userDisplayName, userAvatarUri) {
|
||||||
val hasProfileChanged = hasDisplayNameChanged(userDisplayName, matrixUser)
|
val hasProfileChanged = hasDisplayNameChanged(userDisplayName, matrixUser) ||
|
||||||
|| hasAvatarUrlChanged(userAvatarUri, matrixUser)
|
hasAvatarUrlChanged(userAvatarUri, matrixUser)
|
||||||
!userDisplayName.isNullOrBlank() && hasProfileChanged
|
!userDisplayName.isNullOrBlank() && hasProfileChanged
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -139,6 +139,6 @@ class EditUserProfilePresenter @AssistedInject constructor(
|
||||||
} else {
|
} else {
|
||||||
matrixClient.removeAvatar().getOrThrow()
|
matrixClient.removeAvatar().getOrThrow()
|
||||||
}
|
}
|
||||||
}.onFailure { it.printStackTrace() }
|
}.onFailure { Timber.e(it, "Unable to update avatar") }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,6 @@ import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.platform.LocalFocusManager
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.features.preferences.impl.R
|
import io.element.android.features.preferences.impl.R
|
||||||
|
|
@ -50,8 +49,8 @@ import io.element.android.libraries.designsystem.components.ProgressDialog
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||||
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
import io.element.android.libraries.designsystem.preview.DayNightPreviews
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
|
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
|
||||||
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
|
|
@ -135,7 +134,6 @@ fun EditUserProfileView(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(40.dp))
|
Spacer(modifier = Modifier.height(40.dp))
|
||||||
|
|
||||||
LabelledOutlinedTextField(
|
LabelledOutlinedTextField(
|
||||||
label = stringResource(R.string.screen_edit_profile_display_name),
|
label = stringResource(R.string.screen_edit_profile_display_name),
|
||||||
value = state.displayName,
|
value = state.displayName,
|
||||||
|
|
@ -155,7 +153,6 @@ fun EditUserProfileView(
|
||||||
is Async.Loading -> {
|
is Async.Loading -> {
|
||||||
ProgressDialog(text = stringResource(R.string.screen_edit_profile_updating_details))
|
ProgressDialog(text = stringResource(R.string.screen_edit_profile_updating_details))
|
||||||
}
|
}
|
||||||
|
|
||||||
is Async.Failure -> {
|
is Async.Failure -> {
|
||||||
ErrorDialog(
|
ErrorDialog(
|
||||||
title = stringResource(R.string.screen_edit_profile_error_title),
|
title = stringResource(R.string.screen_edit_profile_error_title),
|
||||||
|
|
@ -163,40 +160,31 @@ fun EditUserProfileView(
|
||||||
onDismiss = { state.eventSink(EditUserProfileEvents.CancelSaveChanges) },
|
onDismiss = { state.eventSink(EditUserProfileEvents.CancelSaveChanges) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
is Async.Success -> {
|
is Async.Success -> {
|
||||||
LaunchedEffect(state.saveAction) {
|
LaunchedEffect(state.saveAction) {
|
||||||
onProfileEdited()
|
onProfileEdited()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> Unit
|
else -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier =
|
private fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier =
|
||||||
this.pointerInput(Unit) {
|
pointerInput(Unit) {
|
||||||
detectTapGestures(onTap = {
|
detectTapGestures(onTap = {
|
||||||
focusManager.clearFocus()
|
focusManager.clearFocus()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
@DayNightPreviews
|
||||||
@Composable
|
@Composable
|
||||||
fun EditUserProfileViewLightPreview(@PreviewParameter(EditUserProfileStateProvider::class) state: EditUserProfileState) =
|
internal fun EditUserProfileViewPreview(@PreviewParameter(EditUserProfileStateProvider::class) state: EditUserProfileState) =
|
||||||
ElementPreviewLight { ContentToPreview(state) }
|
ElementPreview {
|
||||||
|
EditUserProfileView(
|
||||||
|
onBackPressed = {},
|
||||||
|
onProfileEdited = {},
|
||||||
|
state = state,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Preview
|
|
||||||
@Composable
|
|
||||||
fun EditUserProfileViewDarkPreview(@PreviewParameter(EditUserProfileStateProvider::class) state: EditUserProfileState) =
|
|
||||||
ElementPreviewDark { ContentToPreview(state) }
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun ContentToPreview(state: EditUserProfileState) {
|
|
||||||
EditUserProfileView(
|
|
||||||
onBackPressed = {},
|
|
||||||
onProfileEdited = {},
|
|
||||||
state = state,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ class EditUserProfilePresenterTest {
|
||||||
unmockkAll()
|
unmockkAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun anEditUserProfilePresenter(
|
private fun createEditUserProfilePresenter(
|
||||||
matrixClient: MatrixClient = FakeMatrixClient(),
|
matrixClient: MatrixClient = FakeMatrixClient(),
|
||||||
matrixUser: MatrixUser = aMatrixUser(),
|
matrixUser: MatrixUser = aMatrixUser(),
|
||||||
): EditUserProfilePresenter {
|
): EditUserProfilePresenter {
|
||||||
|
|
@ -89,8 +89,7 @@ class EditUserProfilePresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state is created from room info`() = runTest {
|
fun `present - initial state is created from room info`() = runTest {
|
||||||
val user = aMatrixUser(avatarUrl = AN_AVATAR_URL)
|
val user = aMatrixUser(avatarUrl = AN_AVATAR_URL)
|
||||||
val presenter = anEditUserProfilePresenter(matrixUser = user)
|
val presenter = createEditUserProfilePresenter(matrixUser = user)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -111,27 +110,23 @@ class EditUserProfilePresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - updates state in response to changes`() = runTest {
|
fun `present - updates state in response to changes`() = runTest {
|
||||||
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
||||||
val presenter = anEditUserProfilePresenter(matrixUser = user)
|
val presenter = createEditUserProfilePresenter(matrixUser = user)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.displayName).isEqualTo("Name")
|
assertThat(initialState.displayName).isEqualTo("Name")
|
||||||
assertThat(initialState.userAvatarUrl).isEqualTo(userAvatarUri)
|
assertThat(initialState.userAvatarUrl).isEqualTo(userAvatarUri)
|
||||||
|
|
||||||
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("Name II"))
|
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("Name II"))
|
||||||
awaitItem().apply {
|
awaitItem().apply {
|
||||||
assertThat(displayName).isEqualTo("Name II")
|
assertThat(displayName).isEqualTo("Name II")
|
||||||
assertThat(userAvatarUrl).isEqualTo(userAvatarUri)
|
assertThat(userAvatarUrl).isEqualTo(userAvatarUri)
|
||||||
}
|
}
|
||||||
|
|
||||||
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("Name III"))
|
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("Name III"))
|
||||||
awaitItem().apply {
|
awaitItem().apply {
|
||||||
assertThat(displayName).isEqualTo("Name III")
|
assertThat(displayName).isEqualTo("Name III")
|
||||||
assertThat(userAvatarUrl).isEqualTo(userAvatarUri)
|
assertThat(userAvatarUrl).isEqualTo(userAvatarUri)
|
||||||
}
|
}
|
||||||
|
|
||||||
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.Remove))
|
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.Remove))
|
||||||
awaitItem().apply {
|
awaitItem().apply {
|
||||||
assertThat(displayName).isEqualTo("Name III")
|
assertThat(displayName).isEqualTo("Name III")
|
||||||
|
|
@ -143,17 +138,13 @@ class EditUserProfilePresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - obtains avatar uris from gallery`() = runTest {
|
fun `present - obtains avatar uris from gallery`() = runTest {
|
||||||
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
||||||
|
|
||||||
fakePickerProvider.givenResult(anotherAvatarUri)
|
fakePickerProvider.givenResult(anotherAvatarUri)
|
||||||
|
val presenter = createEditUserProfilePresenter(matrixUser = user)
|
||||||
val presenter = anEditUserProfilePresenter(matrixUser = user)
|
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.userAvatarUrl).isEqualTo(userAvatarUri)
|
assertThat(initialState.userAvatarUrl).isEqualTo(userAvatarUri)
|
||||||
|
|
||||||
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
||||||
awaitItem().apply {
|
awaitItem().apply {
|
||||||
assertThat(userAvatarUrl).isEqualTo(anotherAvatarUri)
|
assertThat(userAvatarUrl).isEqualTo(anotherAvatarUri)
|
||||||
|
|
@ -164,17 +155,13 @@ class EditUserProfilePresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - obtains avatar uris from camera`() = runTest {
|
fun `present - obtains avatar uris from camera`() = runTest {
|
||||||
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
||||||
|
|
||||||
fakePickerProvider.givenResult(anotherAvatarUri)
|
fakePickerProvider.givenResult(anotherAvatarUri)
|
||||||
|
val presenter = createEditUserProfilePresenter(matrixUser = user)
|
||||||
val presenter = anEditUserProfilePresenter(matrixUser = user)
|
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.userAvatarUrl).isEqualTo(userAvatarUri)
|
assertThat(initialState.userAvatarUrl).isEqualTo(userAvatarUri)
|
||||||
|
|
||||||
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.TakePhoto))
|
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.TakePhoto))
|
||||||
awaitItem().apply {
|
awaitItem().apply {
|
||||||
assertThat(userAvatarUrl).isEqualTo(anotherAvatarUri)
|
assertThat(userAvatarUrl).isEqualTo(anotherAvatarUri)
|
||||||
|
|
@ -185,35 +172,28 @@ class EditUserProfilePresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - updates save button state`() = runTest {
|
fun `present - updates save button state`() = runTest {
|
||||||
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
||||||
|
|
||||||
fakePickerProvider.givenResult(userAvatarUri)
|
fakePickerProvider.givenResult(userAvatarUri)
|
||||||
|
val presenter = createEditUserProfilePresenter(matrixUser = user)
|
||||||
val presenter = anEditUserProfilePresenter(matrixUser = user)
|
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.saveButtonEnabled).isEqualTo(false)
|
assertThat(initialState.saveButtonEnabled).isEqualTo(false)
|
||||||
|
|
||||||
// Once a change is made, the save button is enabled
|
// Once a change is made, the save button is enabled
|
||||||
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("Name II"))
|
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("Name II"))
|
||||||
awaitItem().apply {
|
awaitItem().apply {
|
||||||
assertThat(saveButtonEnabled).isEqualTo(true)
|
assertThat(saveButtonEnabled).isEqualTo(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's reverted then the save disables again
|
// If it's reverted then the save disables again
|
||||||
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("Name"))
|
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("Name"))
|
||||||
awaitItem().apply {
|
awaitItem().apply {
|
||||||
assertThat(saveButtonEnabled).isEqualTo(false)
|
assertThat(saveButtonEnabled).isEqualTo(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a change...
|
// Make a change...
|
||||||
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.Remove))
|
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.Remove))
|
||||||
awaitItem().apply {
|
awaitItem().apply {
|
||||||
assertThat(saveButtonEnabled).isEqualTo(true)
|
assertThat(saveButtonEnabled).isEqualTo(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Revert it...
|
// Revert it...
|
||||||
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
||||||
awaitItem().apply {
|
awaitItem().apply {
|
||||||
|
|
@ -225,35 +205,28 @@ class EditUserProfilePresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - updates save button state when initial values are null`() = runTest {
|
fun `present - updates save button state when initial values are null`() = runTest {
|
||||||
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = null)
|
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = null)
|
||||||
|
|
||||||
fakePickerProvider.givenResult(userAvatarUri)
|
fakePickerProvider.givenResult(userAvatarUri)
|
||||||
|
val presenter = createEditUserProfilePresenter(matrixUser = user)
|
||||||
val presenter = anEditUserProfilePresenter(matrixUser = user)
|
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.saveButtonEnabled).isEqualTo(false)
|
assertThat(initialState.saveButtonEnabled).isEqualTo(false)
|
||||||
|
|
||||||
// Once a change is made, the save button is enabled
|
// Once a change is made, the save button is enabled
|
||||||
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("Name II"))
|
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("Name II"))
|
||||||
awaitItem().apply {
|
awaitItem().apply {
|
||||||
assertThat(saveButtonEnabled).isEqualTo(true)
|
assertThat(saveButtonEnabled).isEqualTo(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's reverted then the save disables again
|
// If it's reverted then the save disables again
|
||||||
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("fallback"))
|
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("fallback"))
|
||||||
awaitItem().apply {
|
awaitItem().apply {
|
||||||
assertThat(saveButtonEnabled).isEqualTo(false)
|
assertThat(saveButtonEnabled).isEqualTo(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a change...
|
// Make a change...
|
||||||
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
||||||
awaitItem().apply {
|
awaitItem().apply {
|
||||||
assertThat(saveButtonEnabled).isEqualTo(true)
|
assertThat(saveButtonEnabled).isEqualTo(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Revert it...
|
// Revert it...
|
||||||
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.Remove))
|
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.Remove))
|
||||||
awaitItem().apply {
|
awaitItem().apply {
|
||||||
|
|
@ -266,26 +239,21 @@ class EditUserProfilePresenterTest {
|
||||||
fun `present - save changes room details if different`() = runTest {
|
fun `present - save changes room details if different`() = runTest {
|
||||||
val matrixClient = FakeMatrixClient()
|
val matrixClient = FakeMatrixClient()
|
||||||
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
||||||
|
val presenter = createEditUserProfilePresenter(
|
||||||
val presenter = anEditUserProfilePresenter(
|
|
||||||
matrixClient = matrixClient,
|
matrixClient = matrixClient,
|
||||||
matrixUser = user
|
matrixUser = user
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
||||||
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("New name"))
|
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("New name"))
|
||||||
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.Remove))
|
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.Remove))
|
||||||
initialState.eventSink(EditUserProfileEvents.Save)
|
initialState.eventSink(EditUserProfileEvents.Save)
|
||||||
skipItems(5)
|
skipItems(5)
|
||||||
|
|
||||||
assertThat(matrixClient.setDisplayNameCalled).isTrue()
|
assertThat(matrixClient.setDisplayNameCalled).isTrue()
|
||||||
assertThat(matrixClient.removeAvatarCalled).isTrue()
|
assertThat(matrixClient.removeAvatarCalled).isTrue()
|
||||||
assertThat(matrixClient.uploadAvatarCalled).isFalse()
|
assertThat(matrixClient.uploadAvatarCalled).isFalse()
|
||||||
|
|
||||||
cancelAndIgnoreRemainingEvents()
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -294,24 +262,19 @@ class EditUserProfilePresenterTest {
|
||||||
fun `present - save doesn't change room details if they're the same trimmed`() = runTest {
|
fun `present - save doesn't change room details if they're the same trimmed`() = runTest {
|
||||||
val matrixClient = FakeMatrixClient()
|
val matrixClient = FakeMatrixClient()
|
||||||
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
||||||
|
val presenter = createEditUserProfilePresenter(
|
||||||
val presenter = anEditUserProfilePresenter(
|
|
||||||
matrixClient = matrixClient,
|
matrixClient = matrixClient,
|
||||||
matrixUser = user
|
matrixUser = user
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
||||||
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName(" Name "))
|
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName(" Name "))
|
||||||
initialState.eventSink(EditUserProfileEvents.Save)
|
initialState.eventSink(EditUserProfileEvents.Save)
|
||||||
|
|
||||||
assertThat(matrixClient.setDisplayNameCalled).isTrue()
|
assertThat(matrixClient.setDisplayNameCalled).isTrue()
|
||||||
assertThat(matrixClient.uploadAvatarCalled).isFalse()
|
assertThat(matrixClient.uploadAvatarCalled).isFalse()
|
||||||
assertThat(matrixClient.removeAvatarCalled).isFalse()
|
assertThat(matrixClient.removeAvatarCalled).isFalse()
|
||||||
|
|
||||||
cancelAndIgnoreRemainingEvents()
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -320,24 +283,19 @@ class EditUserProfilePresenterTest {
|
||||||
fun `present - save doesn't change name if it's now empty`() = runTest {
|
fun `present - save doesn't change name if it's now empty`() = runTest {
|
||||||
val matrixClient = FakeMatrixClient()
|
val matrixClient = FakeMatrixClient()
|
||||||
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
||||||
|
val presenter = createEditUserProfilePresenter(
|
||||||
val presenter = anEditUserProfilePresenter(
|
|
||||||
matrixClient = matrixClient,
|
matrixClient = matrixClient,
|
||||||
matrixUser = user
|
matrixUser = user
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
||||||
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName(""))
|
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName(""))
|
||||||
initialState.eventSink(EditUserProfileEvents.Save)
|
initialState.eventSink(EditUserProfileEvents.Save)
|
||||||
|
|
||||||
assertThat(matrixClient.setDisplayNameCalled).isFalse()
|
assertThat(matrixClient.setDisplayNameCalled).isFalse()
|
||||||
assertThat(matrixClient.uploadAvatarCalled).isFalse()
|
assertThat(matrixClient.uploadAvatarCalled).isFalse()
|
||||||
assertThat(matrixClient.removeAvatarCalled).isFalse()
|
assertThat(matrixClient.removeAvatarCalled).isFalse()
|
||||||
|
|
||||||
cancelAndIgnoreRemainingEvents()
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -346,23 +304,18 @@ class EditUserProfilePresenterTest {
|
||||||
fun `present - save processes and sets avatar when processor returns successfully`() = runTest {
|
fun `present - save processes and sets avatar when processor returns successfully`() = runTest {
|
||||||
val matrixClient = FakeMatrixClient()
|
val matrixClient = FakeMatrixClient()
|
||||||
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
||||||
|
|
||||||
givenPickerReturnsFile()
|
givenPickerReturnsFile()
|
||||||
|
val presenter = createEditUserProfilePresenter(
|
||||||
val presenter = anEditUserProfilePresenter(
|
|
||||||
matrixClient = matrixClient,
|
matrixClient = matrixClient,
|
||||||
matrixUser = user
|
matrixUser = user
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
||||||
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
||||||
initialState.eventSink(EditUserProfileEvents.Save)
|
initialState.eventSink(EditUserProfileEvents.Save)
|
||||||
skipItems(2)
|
skipItems(2)
|
||||||
|
|
||||||
assertThat(matrixClient.uploadAvatarCalled).isTrue()
|
assertThat(matrixClient.uploadAvatarCalled).isTrue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -371,26 +324,20 @@ class EditUserProfilePresenterTest {
|
||||||
fun `present - save does not set avatar data if processor fails`() = runTest {
|
fun `present - save does not set avatar data if processor fails`() = runTest {
|
||||||
val matrixClient = FakeMatrixClient()
|
val matrixClient = FakeMatrixClient()
|
||||||
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
||||||
|
val presenter = createEditUserProfilePresenter(
|
||||||
val presenter = anEditUserProfilePresenter(
|
|
||||||
matrixClient = matrixClient,
|
matrixClient = matrixClient,
|
||||||
matrixUser = user
|
matrixUser = user
|
||||||
)
|
)
|
||||||
|
|
||||||
fakePickerProvider.givenResult(anotherAvatarUri)
|
fakePickerProvider.givenResult(anotherAvatarUri)
|
||||||
fakeMediaPreProcessor.givenResult(Result.failure(Throwable("Oh no")))
|
fakeMediaPreProcessor.givenResult(Result.failure(Throwable("Oh no")))
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
||||||
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
||||||
initialState.eventSink(EditUserProfileEvents.Save)
|
initialState.eventSink(EditUserProfileEvents.Save)
|
||||||
skipItems(2)
|
skipItems(2)
|
||||||
|
|
||||||
assertThat(matrixClient.uploadAvatarCalled).isFalse()
|
assertThat(matrixClient.uploadAvatarCalled).isFalse()
|
||||||
|
|
||||||
assertThat(awaitItem().saveAction).isInstanceOf(Async.Failure::class.java)
|
assertThat(awaitItem().saveAction).isInstanceOf(Async.Failure::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -401,7 +348,6 @@ class EditUserProfilePresenterTest {
|
||||||
val matrixClient = FakeMatrixClient().apply {
|
val matrixClient = FakeMatrixClient().apply {
|
||||||
givenSetDisplayNameResult(Result.failure(Throwable("!")))
|
givenSetDisplayNameResult(Result.failure(Throwable("!")))
|
||||||
}
|
}
|
||||||
|
|
||||||
saveAndAssertFailure(user, matrixClient, EditUserProfileEvents.UpdateDisplayName("New name"))
|
saveAndAssertFailure(user, matrixClient, EditUserProfileEvents.UpdateDisplayName("New name"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -411,61 +357,49 @@ class EditUserProfilePresenterTest {
|
||||||
val matrixClient = FakeMatrixClient().apply {
|
val matrixClient = FakeMatrixClient().apply {
|
||||||
givenRemoveAvatarResult(Result.failure(Throwable("!")))
|
givenRemoveAvatarResult(Result.failure(Throwable("!")))
|
||||||
}
|
}
|
||||||
|
|
||||||
saveAndAssertFailure(user, matrixClient, EditUserProfileEvents.HandleAvatarAction(AvatarAction.Remove))
|
saveAndAssertFailure(user, matrixClient, EditUserProfileEvents.HandleAvatarAction(AvatarAction.Remove))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - sets save action to failure if setting avatar fails`() = runTest {
|
fun `present - sets save action to failure if setting avatar fails`() = runTest {
|
||||||
givenPickerReturnsFile()
|
givenPickerReturnsFile()
|
||||||
|
|
||||||
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
||||||
val matrixClient = FakeMatrixClient().apply {
|
val matrixClient = FakeMatrixClient().apply {
|
||||||
givenUploadAvatarResult(Result.failure(Throwable("!")))
|
givenUploadAvatarResult(Result.failure(Throwable("!")))
|
||||||
}
|
}
|
||||||
|
|
||||||
saveAndAssertFailure(user, matrixClient, EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
saveAndAssertFailure(user, matrixClient, EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - CancelSaveChanges resets save action state`() = runTest {
|
fun `present - CancelSaveChanges resets save action state`() = runTest {
|
||||||
givenPickerReturnsFile()
|
givenPickerReturnsFile()
|
||||||
|
|
||||||
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
||||||
val matrixClient = FakeMatrixClient().apply {
|
val matrixClient = FakeMatrixClient().apply {
|
||||||
givenSetDisplayNameResult(Result.failure(Throwable("!")))
|
givenSetDisplayNameResult(Result.failure(Throwable("!")))
|
||||||
}
|
}
|
||||||
|
val presenter = createEditUserProfilePresenter(matrixUser = user)
|
||||||
val presenter = anEditUserProfilePresenter(matrixUser = user)
|
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
||||||
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("foo"))
|
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("foo"))
|
||||||
initialState.eventSink(EditUserProfileEvents.Save)
|
initialState.eventSink(EditUserProfileEvents.Save)
|
||||||
skipItems(2)
|
skipItems(2)
|
||||||
|
|
||||||
assertThat(awaitItem().saveAction).isInstanceOf(Async.Failure::class.java)
|
assertThat(awaitItem().saveAction).isInstanceOf(Async.Failure::class.java)
|
||||||
|
|
||||||
initialState.eventSink(EditUserProfileEvents.CancelSaveChanges)
|
initialState.eventSink(EditUserProfileEvents.CancelSaveChanges)
|
||||||
assertThat(awaitItem().saveAction).isInstanceOf(Async.Uninitialized::class.java)
|
assertThat(awaitItem().saveAction).isInstanceOf(Async.Uninitialized::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun saveAndAssertFailure(matrixUser: MatrixUser, matrixClient: MatrixClient, event: EditUserProfileEvents) {
|
private suspend fun saveAndAssertFailure(matrixUser: MatrixUser, matrixClient: MatrixClient, event: EditUserProfileEvents) {
|
||||||
val presenter = anEditUserProfilePresenter(matrixUser = matrixUser, matrixClient = matrixClient)
|
val presenter = createEditUserProfilePresenter(matrixUser = matrixUser, matrixClient = matrixClient)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
||||||
initialState.eventSink(event)
|
initialState.eventSink(event)
|
||||||
initialState.eventSink(EditUserProfileEvents.Save)
|
initialState.eventSink(EditUserProfileEvents.Save)
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
|
|
||||||
assertThat(awaitItem().saveAction).isInstanceOf(Async.Loading::class.java)
|
assertThat(awaitItem().saveAction).isInstanceOf(Async.Loading::class.java)
|
||||||
assertThat(awaitItem().saveAction).isInstanceOf(Async.Failure::class.java)
|
assertThat(awaitItem().saveAction).isInstanceOf(Async.Failure::class.java)
|
||||||
}
|
}
|
||||||
|
|
@ -476,7 +410,6 @@ class EditUserProfilePresenterTest {
|
||||||
val processedFile: File = mockk {
|
val processedFile: File = mockk {
|
||||||
every { readBytes() } returns fakeFileContents
|
every { readBytes() } returns fakeFileContents
|
||||||
}
|
}
|
||||||
|
|
||||||
fakePickerProvider.givenResult(anotherAvatarUri)
|
fakePickerProvider.givenResult(anotherAvatarUri)
|
||||||
fakeMediaPreProcessor.givenResult(
|
fakeMediaPreProcessor.givenResult(
|
||||||
Result.success(
|
Result.success(
|
||||||
|
|
|
||||||
|
|
@ -223,7 +223,7 @@ private fun LabelledReadOnlyField(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier =
|
private fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier =
|
||||||
this.pointerInput(Unit) {
|
pointerInput(Unit) {
|
||||||
detectTapGestures(onTap = {
|
detectTapGestures(onTap = {
|
||||||
focusManager.clearFocus()
|
focusManager.clearFocus()
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,9 @@ import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
import io.element.android.libraries.designsystem.preview.DayNightPreviews
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.theme.components.OutlinedTextField
|
import io.element.android.libraries.designsystem.theme.components.OutlinedTextField
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
import io.element.android.libraries.theme.ElementTheme
|
import io.element.android.libraries.theme.ElementTheme
|
||||||
|
|
@ -66,16 +65,9 @@ fun LabelledOutlinedTextField(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
@DayNightPreviews
|
||||||
@Composable
|
@Composable
|
||||||
internal fun LabelledOutlinedTextFieldLightPreview() = ElementPreviewLight { ContentToPreview() }
|
internal fun LabelledOutlinedTextFieldPreview() = ElementPreview {
|
||||||
|
|
||||||
@Preview
|
|
||||||
@Composable
|
|
||||||
internal fun LabelledOutlinedTextFieldDarkPreview() = ElementPreviewDark { ContentToPreview() }
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun ContentToPreview() {
|
|
||||||
Column {
|
Column {
|
||||||
LabelledOutlinedTextField(
|
LabelledOutlinedTextField(
|
||||||
label = "Room name",
|
label = "Room name",
|
||||||
|
|
@ -89,3 +81,4 @@ private fun ContentToPreview() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ anvil {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// implementation(projects.libraries.rustsdk)
|
// implementation(projects.libraries.rustsdk)
|
||||||
implementation(libs.matrix.sdk)
|
implementation(libs.matrix.sdk)
|
||||||
implementation(projects.libraries.di)
|
implementation(projects.libraries.di)
|
||||||
implementation(projects.libraries.androidutils)
|
implementation(projects.libraries.androidutils)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue