Allow confirmation on closing reply form
This commit is contained in:
parent
aa59adc81f
commit
f2bdc1c563
|
@ -0,0 +1,48 @@
|
|||
import DialogModal from 'src/components/dialog_modal/dialog_modal.vue'
|
||||
|
||||
const DraftCloser = {
|
||||
data () {
|
||||
return {
|
||||
showing: false
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DialogModal
|
||||
},
|
||||
emits: [
|
||||
'save',
|
||||
'discard'
|
||||
],
|
||||
computed: {
|
||||
action () {
|
||||
return this.$store.getters.mergedConfig.unsavedPostAction
|
||||
},
|
||||
shouldConfirm () {
|
||||
return this.action === 'confirm'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
requestClose () {
|
||||
if (this.shouldConfirm) {
|
||||
this.showing = true
|
||||
} else if (this.action === 'save') {
|
||||
this.save()
|
||||
} else {
|
||||
this.discard()
|
||||
}
|
||||
},
|
||||
save () {
|
||||
this.$emit('save')
|
||||
this.showing = false
|
||||
},
|
||||
discard () {
|
||||
this.$emit('discard')
|
||||
this.showing = false
|
||||
},
|
||||
cancel () {
|
||||
this.showing = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default DraftCloser
|
|
@ -0,0 +1,43 @@
|
|||
<template>
|
||||
<teleport to="#modal">
|
||||
<dialog-modal
|
||||
v-if="showing"
|
||||
v-body-scroll-lock="true"
|
||||
class="confirm-modal"
|
||||
:on-cancel="cancel"
|
||||
>
|
||||
<template #header>
|
||||
<span>
|
||||
{{ $t('post_status.close_confirm_title') }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
{{ $t('post_status.close_confirm') }}
|
||||
|
||||
<template #footer>
|
||||
<button
|
||||
class="btn button-default"
|
||||
@click.prevent="save"
|
||||
>
|
||||
{{ $t('post_status.close_confirm_save_button') }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="btn button-default"
|
||||
@click.prevent="discard"
|
||||
>
|
||||
{{ $t('post_status.close_confirm_discard_button') }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="btn button-default"
|
||||
@click.prevent="cancel"
|
||||
>
|
||||
{{ $t('post_status.close_confirm_continue_composing_button') }}
|
||||
</button>
|
||||
</template>
|
||||
</dialog-modal>
|
||||
</teleport>
|
||||
</template>
|
||||
|
||||
<script src="./draft_closer.js"></script>
|
|
@ -15,6 +15,7 @@ import suggestor from '../emoji_input/suggestor.js'
|
|||
import { mapGetters, mapState } from 'vuex'
|
||||
import Checkbox from '../checkbox/checkbox.vue'
|
||||
import Select from '../select/select.vue'
|
||||
import DraftCloser from 'src/components/draft_closer/draft_closer.vue'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
@ -104,7 +105,8 @@ const PostStatusForm = {
|
|||
'posted',
|
||||
'resize',
|
||||
'mediaplay',
|
||||
'mediapause'
|
||||
'mediapause',
|
||||
'can-close'
|
||||
],
|
||||
components: {
|
||||
MediaUpload,
|
||||
|
@ -115,7 +117,8 @@ const PostStatusForm = {
|
|||
Select,
|
||||
Attachment,
|
||||
StatusContent,
|
||||
Gallery
|
||||
Gallery,
|
||||
DraftCloser
|
||||
},
|
||||
mounted () {
|
||||
this.updateIdempotencyKey()
|
||||
|
@ -199,7 +202,8 @@ const PostStatusForm = {
|
|||
previewLoading: false,
|
||||
emojiInputShown: false,
|
||||
idempotencyKey: '',
|
||||
saveInhibited: true
|
||||
saveInhibited: true,
|
||||
savable: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -290,9 +294,9 @@ const PostStatusForm = {
|
|||
isEdit () {
|
||||
return typeof this.statusId !== 'undefined' && this.statusId.trim() !== ''
|
||||
},
|
||||
debouncedSaveDraft () {
|
||||
return debounce(this.saveDraft, 3000)
|
||||
},
|
||||
// debouncedSaveDraft () {
|
||||
// return debounce(this.saveDraft, 3000)
|
||||
// },
|
||||
pollFormVisible () {
|
||||
return this.newStatus.hasPoll
|
||||
},
|
||||
|
@ -310,13 +314,14 @@ const PostStatusForm = {
|
|||
}
|
||||
},
|
||||
beforeUnmount () {
|
||||
this.saveDraft()
|
||||
// this.saveDraft()
|
||||
},
|
||||
methods: {
|
||||
statusChanged () {
|
||||
this.autoPreview()
|
||||
this.updateIdempotencyKey()
|
||||
this.debouncedSaveDraft()
|
||||
// this.debouncedSaveDraft()
|
||||
this.savable = true
|
||||
this.saveInhibited = false
|
||||
},
|
||||
clearStatus () {
|
||||
|
@ -344,6 +349,7 @@ const PostStatusForm = {
|
|||
el.style.height = undefined
|
||||
this.error = null
|
||||
if (this.preview) this.previewStatus()
|
||||
this.savable = false
|
||||
},
|
||||
async postStatus (event, newStatus, opts = {}) {
|
||||
if (this.posting && !this.optimisticPosting) { return }
|
||||
|
@ -678,16 +684,18 @@ const PostStatusForm = {
|
|||
this.newStatus.files?.length ||
|
||||
this.newStatus.hasPoll
|
||||
)) {
|
||||
this.$store.dispatch('addOrSaveDraft', { draft: this.newStatus })
|
||||
return this.$store.dispatch('addOrSaveDraft', { draft: this.newStatus })
|
||||
.then(id => {
|
||||
if (this.newStatus.id !== id) {
|
||||
this.newStatus.id = id
|
||||
this.savable = false
|
||||
}
|
||||
})
|
||||
}
|
||||
return Promise.resolve()
|
||||
},
|
||||
abandonDraft () {
|
||||
this.$store.dispatch('abandonDraft', { id: this.newStatus.id })
|
||||
return this.$store.dispatch('abandonDraft', { id: this.newStatus.id })
|
||||
},
|
||||
getDraft (statusType, refId) {
|
||||
const maybeDraft = this.$store.state.drafts.drafts[this.draftId]
|
||||
|
@ -701,6 +709,23 @@ const PostStatusForm = {
|
|||
}
|
||||
}
|
||||
// No draft available, fall back
|
||||
},
|
||||
requestClose () {
|
||||
if (!this.savable) {
|
||||
this.$emit('can-close')
|
||||
} else {
|
||||
this.$refs.draftCloser.requestClose()
|
||||
}
|
||||
},
|
||||
saveAndCloseDraft () {
|
||||
this.saveDraft().then(() => {
|
||||
this.$emit('can-close')
|
||||
})
|
||||
},
|
||||
discardAndCloseDraft () {
|
||||
this.abandonDraft().then(() => {
|
||||
this.$emit('can-close')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -339,6 +339,11 @@
|
|||
</Checkbox>
|
||||
</div>
|
||||
</form>
|
||||
<DraftCloser
|
||||
ref="draftCloser"
|
||||
@save="saveAndCloseDraft"
|
||||
@discard="discardAndCloseDraft"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -50,6 +50,11 @@ const GeneralTab = {
|
|||
value: mode,
|
||||
label: this.$t(`settings.user_popover_avatar_action_${mode}`)
|
||||
})),
|
||||
unsavedPostActionOptions: ['save', 'discard', 'confirm'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.unsaved_post_action_${mode}`)
|
||||
})),
|
||||
loopSilentAvailable:
|
||||
// Firefox
|
||||
Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') ||
|
||||
|
|
|
@ -518,6 +518,15 @@
|
|||
{{ $t('settings.autocomplete_select_first') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<ChoiceSetting
|
||||
id="unsavedPostAction"
|
||||
path="unsavedPostAction"
|
||||
:options="unsavedPostActionOptions"
|
||||
>
|
||||
{{ $t('settings.unsaved_post_action') }}
|
||||
</ChoiceSetting>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -423,6 +423,13 @@ const Status = {
|
|||
this.error = undefined
|
||||
},
|
||||
toggleReplying () {
|
||||
if (this.replying) {
|
||||
this.$refs.postStatusForm.requestClose()
|
||||
} else {
|
||||
this.doToggleReplying()
|
||||
}
|
||||
},
|
||||
doToggleReplying () {
|
||||
controlledOrUncontrolledToggle(this, 'replying')
|
||||
},
|
||||
gotoOriginal (id) {
|
||||
|
|
|
@ -496,13 +496,15 @@
|
|||
class="status-container reply-form"
|
||||
>
|
||||
<PostStatusForm
|
||||
ref="postStatusForm"
|
||||
class="reply-body"
|
||||
:reply-to="status.id"
|
||||
:attentions="status.attentions"
|
||||
:replied-user="status.user"
|
||||
:copy-message-scope="status.visibility"
|
||||
:subject="replySubject"
|
||||
@posted="toggleReplying"
|
||||
@posted="doToggleReplying"
|
||||
@can-close="doToggleReplying"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -296,7 +296,12 @@
|
|||
"private": "Followers-only - post to followers only",
|
||||
"public": "Public - post to public timelines",
|
||||
"unlisted": "Unlisted - do not post to public timelines"
|
||||
}
|
||||
},
|
||||
"close_confirm_title": "Closing post form",
|
||||
"close_confirm": "What do you want to do with your current writing?",
|
||||
"close_confirm_save_button": "Save",
|
||||
"close_confirm_discard_button": "Discard",
|
||||
"close_confirm_continue_composing_button": "Continue composing"
|
||||
},
|
||||
"registration": {
|
||||
"bio_optional": "Bio (optional)",
|
||||
|
@ -467,6 +472,10 @@
|
|||
"avatar_size_instruction": "The recommended minimum size for avatar images is 150x150 pixels.",
|
||||
"pad_emoji": "Pad emoji with spaces when adding from picker",
|
||||
"autocomplete_select_first": "Automatically select the first candidate when autocomplete results are available",
|
||||
"unsaved_post_action": "When you try to close an unsaved posting form",
|
||||
"unsaved_post_action_save": "Save it to drafts",
|
||||
"unsaved_post_action_discard": "Discard it",
|
||||
"unsaved_post_action_confirm": "Ask every time",
|
||||
"emoji_reactions_on_timeline": "Show emoji reactions on timeline",
|
||||
"emoji_reactions_scale": "Reactions scale factor",
|
||||
"export_theme": "Save preset",
|
||||
|
|
|
@ -18,7 +18,8 @@ export const multiChoiceProperties = [
|
|||
'conversationDisplay', // tree | linear
|
||||
'conversationOtherRepliesButton', // below | inside
|
||||
'mentionLinkDisplay', // short | full_for_remote | full
|
||||
'userPopoverAvatarAction' // close | zoom | open
|
||||
'userPopoverAvatarAction', // close | zoom | open
|
||||
'unsavedPostAction' // save | discard | confirm
|
||||
]
|
||||
|
||||
export const defaultState = {
|
||||
|
@ -117,7 +118,8 @@ export const defaultState = {
|
|||
conversationOtherRepliesButton: undefined, // instance default
|
||||
conversationTreeFadeAncestors: undefined, // instance default
|
||||
maxDepthInThread: undefined, // instance default
|
||||
autocompleteSelect: undefined // instance default
|
||||
autocompleteSelect: undefined, // instance default
|
||||
unsavedPostAction: undefined // instance default
|
||||
}
|
||||
|
||||
// caching the instance default properties
|
||||
|
|
|
@ -105,6 +105,7 @@ const defaultState = {
|
|||
conversationTreeFadeAncestors: false,
|
||||
maxDepthInThread: 6,
|
||||
autocompleteSelect: false,
|
||||
unsavedPostAction: 'confirm',
|
||||
|
||||
// Nasty stuff
|
||||
customEmoji: [],
|
||||
|
|
Loading…
Reference in New Issue