Improve prompts when draft is empty (#3699)
When the user is closing the compose view, if it's new and empty, don't show a prompt. if it's an existing draft and now empty, ask if the user wants to delete it or continue editing. I don't think there is much value in saving an empty draft. ---------
This commit is contained in:
parent
eedf6abf91
commit
485a4c364e
3 changed files with 69 additions and 13 deletions
|
@ -78,6 +78,7 @@ import com.keylesspalace.tusky.R
|
|||
import com.keylesspalace.tusky.adapter.EmojiAdapter
|
||||
import com.keylesspalace.tusky.adapter.LocaleAdapter
|
||||
import com.keylesspalace.tusky.adapter.OnEmojiSelectedListener
|
||||
import com.keylesspalace.tusky.components.compose.ComposeViewModel.ConfirmationKind
|
||||
import com.keylesspalace.tusky.components.compose.dialog.CaptionDialog
|
||||
import com.keylesspalace.tusky.components.compose.dialog.makeFocusDialog
|
||||
import com.keylesspalace.tusky.components.compose.dialog.showAddPollDialog
|
||||
|
@ -1126,17 +1127,20 @@ class ComposeActivity :
|
|||
private fun handleCloseButton() {
|
||||
val contentText = binding.composeEditField.text.toString()
|
||||
val contentWarning = binding.composeContentWarningField.text.toString()
|
||||
if (viewModel.didChange(contentText, contentWarning)) {
|
||||
when (viewModel.composeKind) {
|
||||
ComposeKind.NEW -> getSaveAsDraftOrDiscardDialog(contentText, contentWarning)
|
||||
ComposeKind.EDIT_DRAFT -> getUpdateDraftOrDiscardDialog(contentText, contentWarning)
|
||||
ComposeKind.EDIT_POSTED -> getContinueEditingOrDiscardDialog()
|
||||
ComposeKind.EDIT_SCHEDULED -> getContinueEditingOrDiscardDialog()
|
||||
}.show()
|
||||
} else {
|
||||
when (viewModel.handleCloseButton(contentText, contentWarning)) {
|
||||
ConfirmationKind.NONE -> {
|
||||
viewModel.stopUploads()
|
||||
finishWithoutSlideOutAnimation()
|
||||
}
|
||||
ConfirmationKind.SAVE_OR_DISCARD ->
|
||||
getSaveAsDraftOrDiscardDialog(contentText, contentWarning).show()
|
||||
ConfirmationKind.UPDATE_OR_DISCARD ->
|
||||
getUpdateDraftOrDiscardDialog(contentText, contentWarning).show()
|
||||
ConfirmationKind.CONTINUE_EDITING_OR_DISCARD_CHANGES ->
|
||||
getContinueEditingOrDiscardDialog().show()
|
||||
ConfirmationKind.CONTINUE_EDITING_OR_DISCARD_DRAFT ->
|
||||
getDeleteEmptyDraftOrContinueEditing().show()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1200,6 +1204,23 @@ class ComposeActivity :
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* User is editing an existing draft and making it empty.
|
||||
* The user can either delete the empty draft or go back to editing.
|
||||
*/
|
||||
private fun getDeleteEmptyDraftOrContinueEditing(): AlertDialog.Builder {
|
||||
return AlertDialog.Builder(this)
|
||||
.setMessage(R.string.compose_delete_draft)
|
||||
.setPositiveButton(R.string.action_delete) { _, _ ->
|
||||
viewModel.deleteDraft()
|
||||
viewModel.stopUploads()
|
||||
finishWithoutSlideOutAnimation()
|
||||
}
|
||||
.setNegativeButton(R.string.action_continue_edit) { _, _ ->
|
||||
// Do nothing, dialog will dismiss, user can continue editing
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteDraftAndFinish() {
|
||||
viewModel.deleteDraft()
|
||||
finishWithoutSlideOutAnimation()
|
||||
|
|
|
@ -21,6 +21,7 @@ import androidx.core.net.toUri
|
|||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import at.connyduck.calladapter.networkresult.fold
|
||||
import com.keylesspalace.tusky.components.compose.ComposeActivity.ComposeKind
|
||||
import com.keylesspalace.tusky.components.compose.ComposeActivity.QueuedMedia
|
||||
import com.keylesspalace.tusky.components.compose.ComposeAutoCompleteAdapter.AutocompleteResult
|
||||
import com.keylesspalace.tusky.components.drafts.DraftHelper
|
||||
|
@ -94,7 +95,7 @@ class ComposeViewModel @Inject constructor(
|
|||
val media: MutableStateFlow<List<QueuedMedia>> = MutableStateFlow(emptyList())
|
||||
val uploadError = MutableSharedFlow<Throwable>(replay = 0, extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
||||
|
||||
lateinit var composeKind: ComposeActivity.ComposeKind
|
||||
lateinit var composeKind: ComposeKind
|
||||
|
||||
// Used in ComposeActivity to pass state to result function when cropImage contract inflight
|
||||
var cropImageItemOld: QueuedMedia? = null
|
||||
|
@ -213,7 +214,28 @@ class ComposeViewModel @Inject constructor(
|
|||
this.markMediaAsSensitive.value = this.markMediaAsSensitive.value != true
|
||||
}
|
||||
|
||||
fun didChange(content: String?, contentWarning: String?): Boolean {
|
||||
fun handleCloseButton(contentText: String?, contentWarning: String?): ConfirmationKind {
|
||||
return if (didChange(contentText, contentWarning)) {
|
||||
when (composeKind) {
|
||||
ComposeKind.NEW -> if (isEmpty(contentText, contentWarning)) {
|
||||
ConfirmationKind.NONE
|
||||
} else {
|
||||
ConfirmationKind.SAVE_OR_DISCARD
|
||||
}
|
||||
ComposeKind.EDIT_DRAFT -> if (isEmpty(contentText, contentWarning)) {
|
||||
ConfirmationKind.CONTINUE_EDITING_OR_DISCARD_DRAFT
|
||||
} else {
|
||||
ConfirmationKind.UPDATE_OR_DISCARD
|
||||
}
|
||||
ComposeKind.EDIT_POSTED -> ConfirmationKind.CONTINUE_EDITING_OR_DISCARD_CHANGES
|
||||
ComposeKind.EDIT_SCHEDULED -> ConfirmationKind.CONTINUE_EDITING_OR_DISCARD_CHANGES
|
||||
}
|
||||
} else {
|
||||
ConfirmationKind.NONE
|
||||
}
|
||||
}
|
||||
|
||||
private fun didChange(content: String?, contentWarning: String?): Boolean {
|
||||
val textChanged = content.orEmpty() != startingText.orEmpty()
|
||||
val contentWarningChanged = contentWarning.orEmpty() != startingContentWarning
|
||||
val mediaChanged = media.value.isNotEmpty()
|
||||
|
@ -223,6 +245,10 @@ class ComposeViewModel @Inject constructor(
|
|||
return modifiedInitialState || textChanged || contentWarningChanged || mediaChanged || pollChanged || didScheduledTimeChange
|
||||
}
|
||||
|
||||
private fun isEmpty(content: String?, contentWarning: String?): Boolean {
|
||||
return !modifiedInitialState && (content.isNullOrBlank() && contentWarning.isNullOrBlank() && media.value.isEmpty() && poll.value == null)
|
||||
}
|
||||
|
||||
fun contentWarningChanged(value: Boolean) {
|
||||
showContentWarning.value = value
|
||||
contentWarningStateChanged = true
|
||||
|
@ -390,7 +416,7 @@ class ComposeViewModel @Inject constructor(
|
|||
return
|
||||
}
|
||||
|
||||
composeKind = composeOptions?.kind ?: ComposeActivity.ComposeKind.NEW
|
||||
composeKind = composeOptions?.kind ?: ComposeKind.NEW
|
||||
|
||||
val preferredVisibility = accountManager.activeAccount!!.defaultPostPrivacy
|
||||
|
||||
|
@ -486,6 +512,14 @@ class ComposeViewModel @Inject constructor(
|
|||
private companion object {
|
||||
const val TAG = "ComposeViewModel"
|
||||
}
|
||||
|
||||
enum class ConfirmationKind {
|
||||
NONE, // just close
|
||||
SAVE_OR_DISCARD,
|
||||
UPDATE_OR_DISCARD,
|
||||
CONTINUE_EDITING_OR_DISCARD_CHANGES, // editing post
|
||||
CONTINUE_EDITING_OR_DISCARD_DRAFT // edit draft
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -481,6 +481,7 @@
|
|||
<string name="action_remove">Remove</string>
|
||||
<string name="lock_account_label">Lock account</string>
|
||||
<string name="lock_account_label_description">Requires you to manually approve followers</string>
|
||||
<string name="compose_delete_draft">Delete draft?</string>
|
||||
<string name="compose_save_draft">Save draft?</string>
|
||||
<string name="compose_save_draft_loses_media">Save draft? (Attachments will be uploaded again when you restore the draft.)</string>
|
||||
<string name="compose_unsaved_changes">You have unsaved changes.</string>
|
||||
|
|
Loading…
Reference in a new issue