Add option to delete a poll while editing the poll (#1895)

---------

Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
jonnyandrew 2023-11-28 13:25:18 +00:00 committed by GitHub
parent 7bc0b29603
commit a0548dfdc4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 153 additions and 24 deletions

1
changelog.d/1895.feature Normal file
View file

@ -0,0 +1 @@
Add option to delete a poll while editing the poll

View file

@ -20,6 +20,7 @@ import io.element.android.libraries.matrix.api.poll.PollKind
sealed interface CreatePollEvents {
data object Save : CreatePollEvents
data class Delete(val confirmed: Boolean) : CreatePollEvents
data class SetQuestion(val question: String) : CreatePollEvents
data class SetAnswer(val index: Int, val text: String) : CreatePollEvents
data object AddAnswer : CreatePollEvents

View file

@ -67,7 +67,8 @@ class CreatePollPresenter @AssistedInject constructor(
var question: String by rememberSaveable { mutableStateOf("") }
var answers: List<String> by rememberSaveable { mutableStateOf(listOf("", "")) }
var pollKind: PollKind by rememberSaveable(saver = pollKindSaver) { mutableStateOf(PollKind.Disclosed) }
var showConfirmation: Boolean by rememberSaveable { mutableStateOf(false) }
var showBackConfirmation: Boolean by rememberSaveable { mutableStateOf(false) }
var showDeleteConfirmation: Boolean by rememberSaveable { mutableStateOf(false) }
LaunchedEffect(Unit) {
if (mode is CreatePollMode.EditPoll) {
@ -114,6 +115,22 @@ class CreatePollPresenter @AssistedInject constructor(
Timber.d("Cannot create poll")
}
}
is CreatePollEvents.Delete -> {
if (mode !is CreatePollMode.EditPoll) {
return
}
if (!event.confirmed) {
showDeleteConfirmation = true
return
}
scope.launch {
showDeleteConfirmation = false
repository.deletePoll(mode.eventId)
navigateUp()
}
}
is CreatePollEvents.AddAnswer -> {
answers = answers + ""
}
@ -137,12 +154,15 @@ class CreatePollPresenter @AssistedInject constructor(
CreatePollEvents.ConfirmNavBack -> {
val shouldConfirm = question.isNotBlank() || answers.any { it.isNotBlank() }
if (shouldConfirm) {
showConfirmation = true
showBackConfirmation = true
} else {
navigateUp()
}
}
is CreatePollEvents.HideConfirmation -> showConfirmation = false
is CreatePollEvents.HideConfirmation -> {
showBackConfirmation = false
showDeleteConfirmation = false
}
}
}
@ -156,7 +176,8 @@ class CreatePollPresenter @AssistedInject constructor(
question = question,
answers = immutableAnswers,
pollKind = pollKind,
showConfirmation = showConfirmation,
showBackConfirmation = showBackConfirmation,
showDeleteConfirmation = showDeleteConfirmation,
eventSink = ::handleEvents,
)
}

View file

@ -26,13 +26,16 @@ data class CreatePollState(
val question: String,
val answers: ImmutableList<Answer>,
val pollKind: PollKind,
val showConfirmation: Boolean,
val showBackConfirmation: Boolean,
val showDeleteConfirmation: Boolean,
val eventSink: (CreatePollEvents) -> Unit,
) {
enum class Mode {
New,
Edit,
}
val canDelete: Boolean = mode == Mode.Edit
}
data class Answer(

View file

@ -34,7 +34,8 @@ class CreatePollStateProvider : PreviewParameterProvider<CreatePollState> {
Answer("", false)
),
pollKind = PollKind.Disclosed,
showConfirmation = false,
showBackConfirmation = false,
showDeleteConfirmation = false,
),
aCreatePollState(
mode = CreatePollState.Mode.New,
@ -45,7 +46,8 @@ class CreatePollStateProvider : PreviewParameterProvider<CreatePollState> {
Answer("Italian \uD83C\uDDEE\uD83C\uDDF9", false),
Answer("Chinese \uD83C\uDDE8\uD83C\uDDF3", false),
),
showConfirmation = false,
showBackConfirmation = false,
showDeleteConfirmation = false,
pollKind = PollKind.Undisclosed,
),
aCreatePollState(
@ -57,7 +59,8 @@ class CreatePollStateProvider : PreviewParameterProvider<CreatePollState> {
Answer("Italian \uD83C\uDDEE\uD83C\uDDF9", false),
Answer("Chinese \uD83C\uDDE8\uD83C\uDDF3", false),
),
showConfirmation = true,
showBackConfirmation = true,
showDeleteConfirmation = false,
pollKind = PollKind.Undisclosed,
),
aCreatePollState(
@ -71,7 +74,8 @@ class CreatePollStateProvider : PreviewParameterProvider<CreatePollState> {
Answer("Brazilian \uD83C\uDDE7\uD83C\uDDF7", true),
Answer("French \uD83C\uDDEB\uD83C\uDDF7", true),
),
showConfirmation = false,
showBackConfirmation = false,
showDeleteConfirmation = false,
pollKind = PollKind.Undisclosed,
),
aCreatePollState(
@ -101,7 +105,8 @@ class CreatePollStateProvider : PreviewParameterProvider<CreatePollState> {
Answer("19", true),
Answer("20", true),
),
showConfirmation = false,
showBackConfirmation = false,
showDeleteConfirmation = false,
pollKind = PollKind.Undisclosed,
),
aCreatePollState(
@ -124,7 +129,8 @@ class CreatePollStateProvider : PreviewParameterProvider<CreatePollState> {
false
),
),
showConfirmation = false,
showBackConfirmation = false,
showDeleteConfirmation = false,
pollKind = PollKind.Undisclosed,
),
aCreatePollState(
@ -137,7 +143,21 @@ class CreatePollStateProvider : PreviewParameterProvider<CreatePollState> {
Answer("", false)
),
pollKind = PollKind.Disclosed,
showConfirmation = false,
showDeleteConfirmation = false,
showBackConfirmation = false,
),
aCreatePollState(
mode = CreatePollState.Mode.Edit,
canCreate = false,
canAddAnswer = true,
question = "",
answers = persistentListOf(
Answer("", false),
Answer("", false)
),
pollKind = PollKind.Disclosed,
showDeleteConfirmation = true,
showBackConfirmation = false,
),
)
}
@ -148,7 +168,8 @@ private fun aCreatePollState(
canAddAnswer: Boolean,
question: String,
answers: PersistentList<Answer>,
showConfirmation: Boolean,
showBackConfirmation: Boolean,
showDeleteConfirmation: Boolean,
pollKind: PollKind
): CreatePollState {
return CreatePollState(
@ -157,7 +178,8 @@ private fun aCreatePollState(
canAddAnswer = canAddAnswer,
question = question,
answers = answers,
showConfirmation = showConfirmation,
showBackConfirmation = showBackConfirmation,
showDeleteConfirmation = showDeleteConfirmation,
pollKind = pollKind,
eventSink = {}
)

View file

@ -76,11 +76,19 @@ fun CreatePollView(
val navBack = { state.eventSink(CreatePollEvents.ConfirmNavBack) }
BackHandler(onBack = navBack)
if (state.showConfirmation) ConfirmationDialog(
if (state.showBackConfirmation) ConfirmationDialog(
content = stringResource(id = R.string.screen_create_poll_cancel_confirmation_content_android),
onSubmitClicked = { state.eventSink(CreatePollEvents.NavBack) },
onDismiss = { state.eventSink(CreatePollEvents.HideConfirmation) }
)
if (state.showDeleteConfirmation) {
ConfirmationDialog(
title = stringResource(id = R.string.screen_edit_poll_delete_confirmation_title),
content = stringResource(id = R.string.screen_edit_poll_delete_confirmation),
onSubmitClicked = { state.eventSink(CreatePollEvents.Delete(confirmed = true)) },
onDismiss = { state.eventSink(CreatePollEvents.HideConfirmation) }
)
}
val questionFocusRequester = remember { FocusRequester() }
val answerFocusRequester = remember { FocusRequester() }
LaunchedEffect(Unit) {
@ -191,6 +199,13 @@ fun CreatePollView(
onChange = { state.eventSink(CreatePollEvents.SetPollKind(if (it) PollKind.Undisclosed else PollKind.Disclosed)) },
),
)
if (state.canDelete) {
ListItem(
headlineContent = { Text(text = stringResource(id = CommonStrings.action_delete_poll)) },
style = ListItemStyle.Destructive,
onClick = { state.eventSink(CreatePollEvents.Delete(confirmed = false)) },
)
}
}
}
}

View file

@ -59,4 +59,11 @@ class PollRepository @Inject constructor(
pollKind = pollKind,
)
}
suspend fun deletePoll(
pollStartId: EventId,
): Result<Unit> =
room.redactEvent(
eventId = pollStartId,
)
}

View file

@ -371,7 +371,7 @@ class CreatePollPresenterTest {
}.test {
val initial = awaitItem()
Truth.assertThat(navUpInvocationsCount).isEqualTo(0)
Truth.assertThat(initial.showConfirmation).isFalse()
Truth.assertThat(initial.showBackConfirmation).isFalse()
initial.eventSink(CreatePollEvents.ConfirmNavBack)
Truth.assertThat(navUpInvocationsCount).isEqualTo(1)
}
@ -386,12 +386,59 @@ class CreatePollPresenterTest {
val initial = awaitItem()
initial.eventSink(CreatePollEvents.SetQuestion("Non blank"))
Truth.assertThat(navUpInvocationsCount).isEqualTo(0)
Truth.assertThat(awaitItem().showConfirmation).isFalse()
Truth.assertThat(awaitItem().showBackConfirmation).isFalse()
initial.eventSink(CreatePollEvents.ConfirmNavBack)
Truth.assertThat(navUpInvocationsCount).isEqualTo(0)
Truth.assertThat(awaitItem().showConfirmation).isTrue()
Truth.assertThat(awaitItem().showBackConfirmation).isTrue()
initial.eventSink(CreatePollEvents.HideConfirmation)
Truth.assertThat(awaitItem().showConfirmation).isFalse()
Truth.assertThat(awaitItem().showBackConfirmation).isFalse()
}
}
@Test
fun `delete confirms`() = runTest {
val presenter = createCreatePollPresenter(mode = CreatePollMode.EditPoll(pollEventId))
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
awaitDefaultItem()
awaitPollLoaded().eventSink(CreatePollEvents.Delete(confirmed = false))
awaitDeleteConfirmation()
Truth.assertThat(fakeMatrixRoom.redactEventEventIdParam).isNull()
}
}
@Test
fun `delete can be cancelled`() = runTest {
val presenter = createCreatePollPresenter(mode = CreatePollMode.EditPoll(pollEventId))
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
awaitDefaultItem()
awaitPollLoaded().eventSink(CreatePollEvents.Delete(confirmed = false))
Truth.assertThat(fakeMatrixRoom.redactEventEventIdParam).isNull()
awaitDeleteConfirmation().eventSink(CreatePollEvents.HideConfirmation)
awaitPollLoaded().apply {
Truth.assertThat(showDeleteConfirmation).isFalse()
}
Truth.assertThat(fakeMatrixRoom.redactEventEventIdParam).isNull()
}
}
@Test
fun `delete can be confirmed`() = runTest {
val presenter = createCreatePollPresenter(mode = CreatePollMode.EditPoll(pollEventId))
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
awaitDefaultItem()
awaitPollLoaded().eventSink(CreatePollEvents.Delete(confirmed = false))
Truth.assertThat(fakeMatrixRoom.redactEventEventIdParam).isNull()
awaitDeleteConfirmation().eventSink(CreatePollEvents.Delete(confirmed = true))
awaitPollLoaded().apply {
Truth.assertThat(showDeleteConfirmation).isFalse()
}
Truth.assertThat(fakeMatrixRoom.redactEventEventIdParam).isEqualTo(pollEventId)
}
}
@ -402,7 +449,13 @@ class CreatePollPresenterTest {
Truth.assertThat(question).isEmpty()
Truth.assertThat(answers).isEqualTo(listOf(Answer("", false), Answer("", false)))
Truth.assertThat(pollKind).isEqualTo(PollKind.Disclosed)
Truth.assertThat(showConfirmation).isFalse()
Truth.assertThat(showBackConfirmation).isFalse()
Truth.assertThat(showDeleteConfirmation).isFalse()
}
private suspend fun TurbineTestContext<CreatePollState>.awaitDeleteConfirmation() =
awaitItem().apply {
Truth.assertThat(showDeleteConfirmation).isTrue()
}
private suspend fun TurbineTestContext<CreatePollState>.awaitPollLoaded(

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3f9afba1a28a22dface99d2359ca4a29134b3f8c9768ac71f7a492ffb07af356
size 33406
oid sha256:8e545dca45645bf7874d183697250ca6f8f916121e5a72a07409917a62d1dc94
size 35440

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:362bcdd3b7e32613da7b9f1a6a2dc23a097e8e9b671d185220d9a243326c5b4f
size 37556

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e51bd3544012bde205e05b3b3116a0a6a682b043ac9270a91a4bff3eaa6c91fd
size 31627
oid sha256:0ccb4cce1db9083561047204aa570727c870a2d1974534fbfc8482b3eaf4fa53
size 33628

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9fd2419fbcfbf5df4847e47416f3717c1176c5e9e5b529503916f6413c47e5cf
size 32712