aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-29 08:37:25 -0700
committerAdam Soltys <[email protected]>2026-09-29 08:37:25 -0700
commit62b8e37109464aca875e306269c181faf6c64ae3 (patch)
treec6f2dddec0c4db898f2c34de72ac67f24fb94b5e
parent6ee2e4e000c9513d09c98554ed0162a97a4f7824 (diff)
downloadcoinosv3-62b8e37109464aca875e306269c181faf6c64ae3.tar.gz
coinosv3-62b8e37109464aca875e306269c181faf6c64ae3.zip
Confirm discarding substantial post drafts
-rw-r--r--src/features/messages.js26
-rw-r--r--src/i18n.js4
-rw-r--r--tools/post-cancel-test.js7
3 files changed, 34 insertions, 3 deletions
diff --git a/src/features/messages.js b/src/features/messages.js
index 10773c8..89fe55a 100644
--- a/src/features/messages.js
+++ b/src/features/messages.js
@@ -7627,12 +7627,35 @@ export function messagesFeature(ctx) {
};
function cancelPost() {
postAttachGeneration++;
+ ui.postCancelConfirm = false;
ui.profCompose = null;
ui.postMedia = [];
ui.postPreview = false;
discardDraft(POST_DRAFT);
render();
}
+ function requestCancelPost() {
+ if (composeText().length > 20 || ui.postUploading || (ui.postMedia || []).length) {
+ ui.postCancelConfirm = true;
+ render();
+ return;
+ }
+ cancelPost();
+ }
+ function postCancelWarning() {
+ if (!ui.postCancelConfirm) return null;
+ const keep = () => { ui.postCancelConfirm = false; render(); };
+ return h('div', {
+ class: 'confirm-pop-backdrop',
+ onClick: (event) => { if (event.target === event.currentTarget) keep(); },
+ },
+ h('div', { class: 'card col confirm-pop', style: 'gap:10px' },
+ h('h3', { style: 'margin:0' }, t('postDiscardTitle')),
+ h('div', { class: 'small muted' }, t('postDiscardBody')),
+ h('div', { class: 'row gap6' },
+ h('button', { class: 'btn-ghost grow', onClick: keep }, t('postKeepEditing')),
+ h('button', { class: 'btn-danger grow', onClick: cancelPost }, t('postDiscard')))));
+ }
const CLIP = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display:block"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg>';
const EYE = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display:block"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>';
@@ -7703,7 +7726,8 @@ export function messagesFeature(ctx) {
type: 'file', id: 'post-file', accept: 'image/*,video/*', style: 'display:none',
onChange: async (e) => { const f = e.target.files && e.target.files[0]; e.target.value = ''; await attachMedia(f); },
}),
- h('button', { class: 'btn-ghost', onClick: cancelPost }, t('cancel'))));
+ h('button', { class: 'btn-ghost', onClick: requestCancelPost }, t('cancel'))),
+ postCancelWarning());
}
// ---- the feed view --------------------------------------------------------
diff --git a/src/i18n.js b/src/i18n.js
index 475f919..41ccb36 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -365,6 +365,10 @@ const STR = {
postBoostedAlready: 'You already boosted that',
postQuote: 'Quote',
postMore: 'More',
+ postDiscardTitle: 'Discard this draft?',
+ postDiscardBody: 'You’ll lose this draft and any attachments.',
+ postKeepEditing: 'Keep editing',
+ postDiscard: 'Discard draft',
postMute: 'Mute',
postUnmute: 'Unmute',
postMuted: 'Muted. Their posts are hidden',
diff --git a/tools/post-cancel-test.js b/tools/post-cancel-test.js
index 5527bf2..217e743 100644
--- a/tools/post-cancel-test.js
+++ b/tools/post-cancel-test.js
@@ -12,6 +12,9 @@ assert(cancel.includes('ui.profCompose = null'), 'composer is closed');
assert(cancel.includes('ui.postMedia = []'), 'pending attachment metadata is removed');
assert(cancel.includes('ui.postPreview = false'), 'preview state is removed');
assert(cancel.includes('discardDraft(POST_DRAFT)'), 'persisted draft is removed immediately');
-assert(source.includes("onClick: cancelPost }, t('cancel')"), 'Cancel button uses the discard handler');
+assert(source.includes("composeText().length > 20"), 'drafts over 20 characters require confirmation');
+assert(source.includes("ui.postUploading || (ui.postMedia || []).length"), 'attachments require confirmation');
+assert(source.includes("onClick: requestCancelPost }, t('cancel')"), 'Cancel button uses the guarded discard handler');
+assert(source.includes("onClick: cancelPost }, t('postDiscard')"), 'warning can confirm the discard');
-console.log('✓ Cancel closes the new-post form and removes its complete saved draft');
+console.log('✓ Cancel warns for substantial drafts, then closes the form and removes its complete saved draft');