BugReportEvents -> BugReportEvent

This commit is contained in:
Benoit Marty
2026-08-25 10:13:33 +02:00
parent 5d8ca663df
commit 859b0e1b60
5 changed files with 54 additions and 54 deletions
@@ -8,15 +8,15 @@
package io.element.android.features.rageshake.impl.bugreport package io.element.android.features.rageshake.impl.bugreport
sealed interface BugReportEvents { sealed interface BugReportEvent {
data object SendBugReport : BugReportEvents data object SendBugReport : BugReportEvent
data object ResetAll : BugReportEvents data object ResetAll : BugReportEvent
data object ClearError : BugReportEvents data object ClearError : BugReportEvent
data class SetDescription(val description: String) : BugReportEvents data class SetDescription(val description: String) : BugReportEvent
data class SetSendLog(val sendLog: Boolean) : BugReportEvents data class SetSendLog(val sendLog: Boolean) : BugReportEvent
data class SetCanContact(val canContact: Boolean) : BugReportEvents data class SetCanContact(val canContact: Boolean) : BugReportEvent
data class SetSendScreenshot(val sendScreenshot: Boolean) : BugReportEvents data class SetSendScreenshot(val sendScreenshot: Boolean) : BugReportEvent
data class SetSendPushRules(val sendPushRules: Boolean) : BugReportEvents data class SetSendPushRules(val sendPushRules: Boolean) : BugReportEvent
data class SetGhIssueNumber(val ghIssueNumber: Int?) : BugReportEvents data class SetGhIssueNumber(val ghIssueNumber: Int?) : BugReportEvent
} }
@@ -83,9 +83,9 @@ class BugReportPresenter(
} }
val uploadListener = BugReporterUploadListener(sendingProgress, sendingAction) val uploadListener = BugReporterUploadListener(sendingProgress, sendingAction)
fun handleEvent(event: BugReportEvents) { fun handleEvent(event: BugReportEvent) {
when (event) { when (event) {
BugReportEvents.SendBugReport -> { BugReportEvent.SendBugReport -> {
if (formState.value.description.length < 10 && formState.value.ghIssueNumber == null) { if (formState.value.description.length < 10 && formState.value.ghIssueNumber == null) {
sendingAction.value = AsyncAction.Failure(BugReportFormError.DescriptionTooShort) sendingAction.value = AsyncAction.Failure(BugReportFormError.DescriptionTooShort)
} else { } else {
@@ -93,27 +93,27 @@ class BugReportPresenter(
appCoroutineScope.sendBugReport(formState.value, crashInfo.isNotEmpty(), uploadListener) appCoroutineScope.sendBugReport(formState.value, crashInfo.isNotEmpty(), uploadListener)
} }
} }
BugReportEvents.ResetAll -> appCoroutineScope.resetAll() BugReportEvent.ResetAll -> appCoroutineScope.resetAll()
is BugReportEvents.SetDescription -> updateFormState(formState) { is BugReportEvent.SetDescription -> updateFormState(formState) {
copy(description = event.description) copy(description = event.description)
} }
is BugReportEvents.SetCanContact -> updateFormState(formState) { is BugReportEvent.SetCanContact -> updateFormState(formState) {
copy(canContact = event.canContact) copy(canContact = event.canContact)
} }
is BugReportEvents.SetSendLog -> updateFormState(formState) { is BugReportEvent.SetSendLog -> updateFormState(formState) {
copy(sendLogs = event.sendLog) copy(sendLogs = event.sendLog)
} }
is BugReportEvents.SetSendScreenshot -> updateFormState(formState) { is BugReportEvent.SetSendScreenshot -> updateFormState(formState) {
copy(sendScreenshot = event.sendScreenshot) copy(sendScreenshot = event.sendScreenshot)
} }
is BugReportEvents.SetSendPushRules -> updateFormState(formState) { is BugReportEvent.SetSendPushRules -> updateFormState(formState) {
copy(sendPushRules = event.sendPushRules) copy(sendPushRules = event.sendPushRules)
} }
BugReportEvents.ClearError -> { BugReportEvent.ClearError -> {
sendingProgress.floatValue = 0f sendingProgress.floatValue = 0f
sendingAction.value = AsyncAction.Uninitialized sendingAction.value = AsyncAction.Uninitialized
} }
is BugReportEvents.SetGhIssueNumber -> updateFormState(formState) { is BugReportEvent.SetGhIssueNumber -> updateFormState(formState) {
copy(ghIssueNumber = event.ghIssueNumber) copy(ghIssueNumber = event.ghIssueNumber)
} }
} }
@@ -18,7 +18,7 @@ data class BugReportState(
val screenshotUri: String?, val screenshotUri: String?,
val sendingProgress: Float, val sendingProgress: Float,
val sending: AsyncAction<Unit>, val sending: AsyncAction<Unit>,
val eventSink: (BugReportEvents) -> Unit val eventSink: (BugReportEvent) -> Unit
) { ) {
val submitEnabled = sending !is AsyncAction.Loading val submitEnabled = sending !is AsyncAction.Loading
val isDescriptionInError = sending is AsyncAction.Failure && val isDescriptionInError = sending is AsyncAction.Failure &&
@@ -84,7 +84,7 @@ fun BugReportView(
supportingText = stringResource(id = R.string.screen_bug_report_editor_description), supportingText = stringResource(id = R.string.screen_bug_report_editor_description),
onValueChange = { onValueChange = {
descriptionFieldState = it descriptionFieldState = it
eventSink(BugReportEvents.SetDescription(it)) eventSink(BugReportEvent.SetDescription(it))
}, },
keyboardOptions = KeyboardOptions( keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Sentences, capitalization = KeyboardCapitalization.Sentences,
@@ -110,14 +110,14 @@ fun BugReportView(
PreferenceDivider() PreferenceDivider()
PreferenceSwitch( PreferenceSwitch(
isChecked = state.formState.sendLogs, isChecked = state.formState.sendLogs,
onCheckedChange = { eventSink(BugReportEvents.SetSendLog(it)) }, onCheckedChange = { eventSink(BugReportEvent.SetSendLog(it)) },
enabled = isFormEnabled, enabled = isFormEnabled,
title = stringResource(id = R.string.screen_bug_report_include_logs), title = stringResource(id = R.string.screen_bug_report_include_logs),
subtitle = stringResource(id = R.string.screen_bug_report_logs_description), subtitle = stringResource(id = R.string.screen_bug_report_logs_description),
) )
PreferenceSwitch( PreferenceSwitch(
isChecked = state.formState.canContact, isChecked = state.formState.canContact,
onCheckedChange = { eventSink(BugReportEvents.SetCanContact(it)) }, onCheckedChange = { eventSink(BugReportEvent.SetCanContact(it)) },
enabled = isFormEnabled, enabled = isFormEnabled,
title = stringResource(id = R.string.screen_bug_report_contact_me_title), title = stringResource(id = R.string.screen_bug_report_contact_me_title),
subtitle = stringResource(id = R.string.screen_bug_report_contact_me), subtitle = stringResource(id = R.string.screen_bug_report_contact_me),
@@ -125,7 +125,7 @@ fun BugReportView(
if (state.screenshotUri != null) { if (state.screenshotUri != null) {
PreferenceSwitch( PreferenceSwitch(
isChecked = state.formState.sendScreenshot, isChecked = state.formState.sendScreenshot,
onCheckedChange = { eventSink(BugReportEvents.SetSendScreenshot(it)) }, onCheckedChange = { eventSink(BugReportEvent.SetSendScreenshot(it)) },
enabled = isFormEnabled, enabled = isFormEnabled,
title = stringResource(id = R.string.screen_bug_report_include_screenshot) title = stringResource(id = R.string.screen_bug_report_include_screenshot)
) )
@@ -151,7 +151,7 @@ fun BugReportView(
} }
PreferenceSwitch( PreferenceSwitch(
isChecked = state.formState.sendPushRules, isChecked = state.formState.sendPushRules,
onCheckedChange = { eventSink(BugReportEvents.SetSendPushRules(it)) }, onCheckedChange = { eventSink(BugReportEvent.SetSendPushRules(it)) },
enabled = isFormEnabled, enabled = isFormEnabled,
title = stringResource(R.string.screen_bug_report_send_notification_settings_title), title = stringResource(R.string.screen_bug_report_send_notification_settings_title),
subtitle = stringResource(R.string.screen_bug_report_send_notification_settings_description), subtitle = stringResource(R.string.screen_bug_report_send_notification_settings_description),
@@ -179,12 +179,12 @@ fun BugReportView(
onValueChange = { onValueChange = {
if (it.isEmpty()) { if (it.isEmpty()) {
ghIssueNumberState = "" ghIssueNumberState = ""
eventSink(BugReportEvents.SetGhIssueNumber(null)) eventSink(BugReportEvent.SetGhIssueNumber(null))
} else { } else {
val number = it.toIntOrNull()?.takeIf { ghInt -> ghInt in 1..99_999 } val number = it.toIntOrNull()?.takeIf { ghInt -> ghInt in 1..99_999 }
number?.let { ghIssueNumber -> number?.let { ghIssueNumber ->
ghIssueNumberState = ghIssueNumber.toString() ghIssueNumberState = ghIssueNumber.toString()
eventSink(BugReportEvents.SetGhIssueNumber(ghIssueNumber)) eventSink(BugReportEvent.SetGhIssueNumber(ghIssueNumber))
} }
} }
}, },
@@ -202,7 +202,7 @@ fun BugReportView(
PreferenceRow { PreferenceRow {
Button( Button(
text = stringResource(id = CommonStrings.action_send), text = stringResource(id = CommonStrings.action_send),
onClick = { eventSink(BugReportEvents.SendBugReport) }, onClick = { eventSink(BugReportEvent.SendBugReport) },
enabled = state.submitEnabled, enabled = state.submitEnabled,
showProgress = state.sending.isLoading(), showProgress = state.sending.isLoading(),
modifier = Modifier modifier = Modifier
@@ -216,7 +216,7 @@ fun BugReportView(
async = state.sending, async = state.sending,
progressDialog = { }, progressDialog = { },
onSuccess = { onSuccess = {
eventSink(BugReportEvents.ResetAll) eventSink(BugReportEvent.ResetAll)
onSuccess() onSuccess()
}, },
errorMessage = { error -> errorMessage = { error ->
@@ -225,7 +225,7 @@ fun BugReportView(
else -> error.message ?: error.toString() else -> error.message ?: error.toString()
} }
}, },
onErrorDismiss = { eventSink(BugReportEvents.ClearError) }, onErrorDismiss = { eventSink(BugReportEvent.ClearError) },
) )
} }
} }
@@ -51,9 +51,9 @@ class BugReportPresenterTest {
val presenter = createPresenter() val presenter = createPresenter()
presenter.test { presenter.test {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink.invoke(BugReportEvents.SetDescription(A_SHORT_DESCRIPTION)) initialState.eventSink.invoke(BugReportEvent.SetDescription(A_SHORT_DESCRIPTION))
assertThat(awaitItem().submitEnabled).isTrue() assertThat(awaitItem().submitEnabled).isTrue()
initialState.eventSink.invoke(BugReportEvents.SetDescription(A_LONG_DESCRIPTION)) initialState.eventSink.invoke(BugReportEvent.SetDescription(A_LONG_DESCRIPTION))
assertThat(awaitItem().submitEnabled).isTrue() assertThat(awaitItem().submitEnabled).isTrue()
} }
} }
@@ -63,9 +63,9 @@ class BugReportPresenterTest {
val presenter = createPresenter() val presenter = createPresenter()
presenter.test { presenter.test {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink.invoke(BugReportEvents.SetCanContact(true)) initialState.eventSink.invoke(BugReportEvent.SetCanContact(true))
assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(canContact = true)) assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(canContact = true))
initialState.eventSink.invoke(BugReportEvents.SetCanContact(false)) initialState.eventSink.invoke(BugReportEvent.SetCanContact(false))
assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(canContact = false)) assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(canContact = false))
} }
} }
@@ -76,9 +76,9 @@ class BugReportPresenterTest {
presenter.test { presenter.test {
val initialState = awaitItem() val initialState = awaitItem()
// Since this is true by default, start by disabling // Since this is true by default, start by disabling
initialState.eventSink.invoke(BugReportEvents.SetSendLog(false)) initialState.eventSink.invoke(BugReportEvent.SetSendLog(false))
assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendLogs = false)) assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendLogs = false))
initialState.eventSink.invoke(BugReportEvents.SetSendLog(true)) initialState.eventSink.invoke(BugReportEvent.SetSendLog(true))
assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendLogs = true)) assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendLogs = true))
} }
} }
@@ -88,9 +88,9 @@ class BugReportPresenterTest {
val presenter = createPresenter() val presenter = createPresenter()
presenter.test { presenter.test {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink.invoke(BugReportEvents.SetSendScreenshot(true)) initialState.eventSink.invoke(BugReportEvent.SetSendScreenshot(true))
assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendScreenshot = true)) assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendScreenshot = true))
initialState.eventSink.invoke(BugReportEvents.SetSendScreenshot(false)) initialState.eventSink.invoke(BugReportEvent.SetSendScreenshot(false))
assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendScreenshot = false)) assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendScreenshot = false))
} }
} }
@@ -100,9 +100,9 @@ class BugReportPresenterTest {
val presenter = createPresenter() val presenter = createPresenter()
presenter.test { presenter.test {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink.invoke(BugReportEvents.SetSendPushRules(true)) initialState.eventSink.invoke(BugReportEvent.SetSendPushRules(true))
assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendPushRules = true)) assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendPushRules = true))
initialState.eventSink.invoke(BugReportEvents.SetSendPushRules(false)) initialState.eventSink.invoke(BugReportEvent.SetSendPushRules(false))
assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendPushRules = false)) assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendPushRules = false))
} }
} }
@@ -112,9 +112,9 @@ class BugReportPresenterTest {
val presenter = createPresenter() val presenter = createPresenter()
presenter.test { presenter.test {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink.invoke(BugReportEvents.SetGhIssueNumber(1)) initialState.eventSink.invoke(BugReportEvent.SetGhIssueNumber(1))
assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(ghIssueNumber = 1)) assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(ghIssueNumber = 1))
initialState.eventSink.invoke(BugReportEvents.SetGhIssueNumber(null)) initialState.eventSink.invoke(BugReportEvent.SetGhIssueNumber(null))
assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(ghIssueNumber = null)) assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(ghIssueNumber = null))
} }
} }
@@ -130,7 +130,7 @@ class BugReportPresenterTest {
val initialState = awaitItem() val initialState = awaitItem()
assertThat(initialState.hasCrashLogs).isTrue() assertThat(initialState.hasCrashLogs).isTrue()
assertThat(initialState.screenshotUri).isEqualTo(A_SCREENSHOT_URI) assertThat(initialState.screenshotUri).isEqualTo(A_SCREENSHOT_URI)
initialState.eventSink.invoke(BugReportEvents.ResetAll) initialState.eventSink.invoke(BugReportEvent.ResetAll)
val resetState = awaitItem() val resetState = awaitItem()
assertThat(resetState.hasCrashLogs).isFalse() assertThat(resetState.hasCrashLogs).isFalse()
// TODO Make it live assertThat(resetState.screenshotUri).isNull() // TODO Make it live assertThat(resetState.screenshotUri).isNull()
@@ -146,9 +146,9 @@ class BugReportPresenterTest {
) )
presenter.test { presenter.test {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink.invoke(BugReportEvents.SetDescription(A_LONG_DESCRIPTION)) initialState.eventSink.invoke(BugReportEvent.SetDescription(A_LONG_DESCRIPTION))
skipItems(1) skipItems(1)
initialState.eventSink.invoke(BugReportEvents.SendBugReport) initialState.eventSink.invoke(BugReportEvent.SendBugReport)
skipItems(1) skipItems(1)
val progressState = awaitItem() val progressState = awaitItem()
assertThat(progressState.sending).isEqualTo(AsyncAction.Loading) assertThat(progressState.sending).isEqualTo(AsyncAction.Loading)
@@ -170,9 +170,9 @@ class BugReportPresenterTest {
) )
presenter.test { presenter.test {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink.invoke(BugReportEvents.SetDescription(A_LONG_DESCRIPTION)) initialState.eventSink.invoke(BugReportEvent.SetDescription(A_LONG_DESCRIPTION))
skipItems(1) skipItems(1)
initialState.eventSink.invoke(BugReportEvents.SendBugReport) initialState.eventSink.invoke(BugReportEvent.SendBugReport)
skipItems(1) skipItems(1)
val progressState = awaitItem() val progressState = awaitItem()
assertThat(progressState.sending).isEqualTo(AsyncAction.Loading) assertThat(progressState.sending).isEqualTo(AsyncAction.Loading)
@@ -182,7 +182,7 @@ class BugReportPresenterTest {
assertThat(awaitItem().sendingProgress).isEqualTo(0f) assertThat(awaitItem().sendingProgress).isEqualTo(0f)
assertThat((awaitItem().sending as AsyncAction.Failure).error.message).isEqualTo(A_FAILURE_REASON) assertThat((awaitItem().sending as AsyncAction.Failure).error.message).isEqualTo(A_FAILURE_REASON)
// Reset failure // Reset failure
initialState.eventSink.invoke(BugReportEvents.ClearError) initialState.eventSink.invoke(BugReportEvent.ClearError)
val lastItem = awaitItem() val lastItem = awaitItem()
assertThat(lastItem.sendingProgress).isEqualTo(0f) assertThat(lastItem.sendingProgress).isEqualTo(0f)
assertThat(lastItem.sending).isInstanceOf(AsyncAction.Uninitialized::class.java) assertThat(lastItem.sending).isInstanceOf(AsyncAction.Uninitialized::class.java)
@@ -194,13 +194,13 @@ class BugReportPresenterTest {
val presenter = createPresenter() val presenter = createPresenter()
presenter.test { presenter.test {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink.invoke(BugReportEvents.SetDescription(A_SHORT_DESCRIPTION)) initialState.eventSink.invoke(BugReportEvent.SetDescription(A_SHORT_DESCRIPTION))
skipItems(1) skipItems(1)
initialState.eventSink.invoke(BugReportEvents.SendBugReport) initialState.eventSink.invoke(BugReportEvent.SendBugReport)
val errorState = awaitItem() val errorState = awaitItem()
assertThat(errorState.sending).isEqualTo(AsyncAction.Failure(BugReportFormError.DescriptionTooShort)) assertThat(errorState.sending).isEqualTo(AsyncAction.Failure(BugReportFormError.DescriptionTooShort))
// Reset failure // Reset failure
initialState.eventSink.invoke(BugReportEvents.ClearError) initialState.eventSink.invoke(BugReportEvent.ClearError)
val lastItem = awaitItem() val lastItem = awaitItem()
assertThat(lastItem.sending).isInstanceOf(AsyncAction.Uninitialized::class.java) assertThat(lastItem.sending).isInstanceOf(AsyncAction.Uninitialized::class.java)
} }
@@ -215,9 +215,9 @@ class BugReportPresenterTest {
) )
presenter.test { presenter.test {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink.invoke(BugReportEvents.SetDescription(A_LONG_DESCRIPTION)) initialState.eventSink.invoke(BugReportEvent.SetDescription(A_LONG_DESCRIPTION))
skipItems(1) skipItems(1)
initialState.eventSink.invoke(BugReportEvents.SendBugReport) initialState.eventSink.invoke(BugReportEvent.SendBugReport)
skipItems(1) skipItems(1)
val progressState = awaitItem() val progressState = awaitItem()
assertThat(progressState.sending).isEqualTo(AsyncAction.Loading) assertThat(progressState.sending).isEqualTo(AsyncAction.Loading)