Skip to content

Commit ddb3b27

Browse files
committed
added draft delete indicator & ensured cancelling user profile edit discards the draft
1 parent c0852c1 commit ddb3b27

2 files changed

Lines changed: 80 additions & 37 deletions

File tree

app/assets/javascripts/posts.js

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,26 @@ $(() => {
131131
$postField.val($postField.val()?.toString().replace(placeholder, ''));
132132
});
133133

134+
/**
135+
* Temporarily displays the draft's status with a given message
136+
* @param {JQuery<Element>} $field draftable field
137+
* @param {string} message draft status message
138+
*/
139+
const flashDraftStatus = ($field, message) => {
140+
const $statusEl = $field.parents('.widget').find('.js-post-draft-status');
141+
142+
$statusEl.text(message);
143+
$statusEl.removeClass('transparent');
144+
145+
setTimeout(() => {
146+
$statusEl.addClass('transparent');
147+
}, 1500);
148+
};
149+
134150
/**
135151
* Attempts to save a post draft
136152
* @param {QPixelDraft} draft post draft
137-
* @param {JQuery<Element>} $field body input element
153+
* @param {JQuery<Element>} $field draftable field
138154
* @param {boolean} [manual] whether manual draft saving is enabled
139155
* @returns {Promise<void>}
140156
*/
@@ -148,45 +164,46 @@ $(() => {
148164
const data = await QPixel.saveDraft(draft);
149165

150166
QPixel.handleJSONResponse(data, () => {
151-
const $statusEl = $field.parents('.widget').find('.js-post-draft-status');
152-
153-
$statusEl.removeClass('transparent');
154-
155-
setTimeout(() => {
156-
$statusEl.addClass('transparent');
157-
}, 1500);
167+
flashDraftStatus($field, 'draft saved');
158168
});
159169
};
160170

161171
/**
162-
* @typedef {{
163-
* removeNotice?: boolean
164-
* }} DeleteDraftOptions
165-
*
166172
* Attempts to remove a post draft
167-
* @param {DeleteDraftOptions} [options]
173+
* @param {JQuery<Element>} $field draftable field
168174
* @returns {Promise<boolean>}
169175
*/
170-
const deleteDraft = async (options = {}) => {
176+
const deleteDraft = async ($field) => {
171177
const data = await QPixel.deleteDraft();
172178

173179
return QPixel.handleJSONResponse(data, () => {
174-
if (options.removeNotice) {
175-
$('.js-draft-notice').remove();
176-
}
180+
flashDraftStatus($field, 'draft deleted');
177181
});
178182
}
179183

184+
/**
185+
*
186+
* @param {EventTarget} target post field or one of the draft buttons
187+
* @returns {{
188+
* $form: JQuery<HTMLFormElement>,
189+
* $field: JQuery<HTMLElement>
190+
* }}
191+
*/
192+
const getDraftElements = (target) => {
193+
const $tgt = $(target);
194+
const $form = $tgt.parents('form');
195+
const $field = $form.find('.js-post-field');
196+
return { $form, $field };
197+
};
198+
180199
/**
181200
* Extracts draft info from a given target
182-
* @param {EventTarget} target post input field or "save draft" button
201+
* @param {EventTarget} target post field or one of the draft buttons
183202
* @returns {{ draft: QPixelDraft, field: any }}
184203
*/
185204
const parseDraft = (target) => {
186-
const $tgt = $(target);
187-
const $form = $tgt.parents('form');
205+
const { $field: $bodyField, $form } = getDraftElements(target);
188206

189-
const $bodyField = $form.find('.js-post-field');
190207
const $licenseField = $form.find('.js-license-select');
191208
const $excerptField = $form.find('.js-tag-excerpt');
192209

@@ -217,10 +234,9 @@ $(() => {
217234
return { draft, field: $bodyField };
218235
};
219236

220-
$('.js-delete-draft').on('click', async () => {
221-
await deleteDraft({
222-
removeNotice: true
223-
});
237+
$('.js-delete-draft').on('click', async (ev) => {
238+
const { $field } = getDraftElements(ev.target);
239+
await deleteDraft($field);
224240
});
225241

226242
$('.js-save-draft').on('click', async (ev) => {
@@ -330,19 +346,20 @@ $(() => {
330346

331347
$postFields.parents('form').on('submit', async (ev) => {
332348
const $tgt = $(ev.target);
333-
const field = $tgt.find('.post-field');
349+
const $field = $tgt.find('.js-post-field');
334350

335351
const draftDeleted = $tgt.attr('data-draft-deleted') === 'true';
336352
const isValidated = $tgt.attr('data-validated') === 'true';
337353

338354
if (draftDeleted && isValidated) {
339355
return;
340356
}
357+
341358
ev.preventDefault();
342359

343360
// Draft handling
344361
if (!draftDeleted) {
345-
const status = await deleteDraft();
362+
const status = await deleteDraft($field);
346363

347364
if (status) {
348365
$tgt.attr('data-draft-deleted', 'true');
@@ -359,7 +376,7 @@ $(() => {
359376

360377
// Validation
361378
if (!isValidated) {
362-
const text = $(field).val()?.toString();
379+
const text = $field.val()?.toString();
363380
const validated = QPixel.validatePost(text);
364381
if (validated[0] === true) {
365382
$tgt.attr('data-validated', 'true');
@@ -488,7 +505,9 @@ $(() => {
488505
return;
489506
}
490507

491-
await deleteDraft();
508+
const { $field } = getDraftElements(ev.target);
509+
510+
await deleteDraft($field);
492511

493512
location.href = $btn.attr('href');
494513
});

app/views/users/edit_profile.html.erb

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@
2222

2323
<%= form_for current_user, url: update_user_profile_path do |f| %>
2424
<div class="form-group has-padding-2">
25-
<img alt="user avatar" class="has-float-right has-margin-2 avatar-64" title="Current avatar" src="<%= avatar_url(current_user, 64) %>" height="64" width="64" />
25+
<img alt="user avatar"
26+
class="has-float-right has-margin-2 avatar-64"
27+
title="Current avatar"
28+
src="<%= avatar_url(current_user, 64) %>"
29+
height="64"
30+
width="64" />
2631
<%= f.label :avatar, class: "form-element" %>
2732
<div class="form-caption">
2833
An optional profile picture. Max file size <%= SiteSetting['MaxUploadSize'] %>.
@@ -34,28 +39,43 @@
3439
<div class="form-group has-padding-2">
3540
<%= f.label :username, class: "form-element" %>
3641
<div class="form-caption">What other people call you.</div>
37-
<%= f.text_field :username, class: 'form-element', autocomplete: 'off', data: { character_count: '.js-character-count-user-name' } %>
42+
<%= f.text_field :username,
43+
class: 'form-element',
44+
autocomplete: 'off',
45+
data: { character_count: '.js-character-count-user-name' } %>
3846
<%= render 'shared/char_count', type: 'user-name', cur: current_user.username&.length, min: 3, max: 50 %>
3947
</div>
4048

41-
<%= render 'shared/body_field', f: f, field_name: :profile_markdown, field_label: 'Profile', post: current_user,
42-
cur_length: current_user.profile_markdown&.length, min_length: 0 %>
49+
<%= render 'shared/body_field',
50+
f: f,
51+
field_name: :profile_markdown,
52+
field_label: 'Profile',
53+
post: current_user,
54+
cur_length: current_user.profile_markdown&.length,
55+
min_length: 0 %>
4356

4457
<% unless current_user.community_user.privilege?('unrestricted') %>
4558
<p>Note: Links are not shown publicly until you have earned the Participate Everywhere ability.</p>
4659
<% end %>
4760
<div class="post-preview"></div>
4861

4962
<div>
50-
<p>Extra fields -- your web site, GitHub profile, social-media usernames, whatever you want. Only values that begin with "http" are rendered as links.</p>
63+
<p>Extra fields -- your web site, GitHub profile, social-media usernames, whatever you want.<br/>
64+
Only values that begin with "http" are rendered as links.</p>
5165
<div class="grid">
5266
<%= f.fields_for :user_websites do |w| %>
5367
<div class="grid grid--cell is-12 is-12-sm">
5468
<div class="grid grid--cell is-3 is-3-sm">
55-
<div class="grid--cell is-12"><%= w.text_field :label, class: 'form-element', autocomplete: 'off', placeholder: 'label' %></div>
69+
<div class="grid--cell is-12"><%= w.text_field :label,
70+
class: 'form-element',
71+
autocomplete: 'off',
72+
placeholder: 'label' %></div>
5673
</div>
5774
<div class="grid grid--cell is-6 is-9-sm">
58-
<div class="grid--cell is-12"><%= w.text_field :url, class: 'form-element', autocomplete: 'off', placeholder: 'https://...' %></div>
75+
<div class="grid--cell is-12"><%= w.text_field :url,
76+
class: 'form-element',
77+
autocomplete: 'off',
78+
placeholder: 'https://...' %></div>
5979
</div>
6080
</div>
6181
<% end %>
@@ -70,7 +90,11 @@
7090

7191

7292
<%= f.submit 'Save', class: 'button is-filled' %>
73-
<%= link_to 'Cancel', users_me_path, class: 'button is-muted is-outlined', role: 'button' %>
93+
<%= link_to 'Cancel',
94+
users_me_path,
95+
class: 'button is-muted is-outlined js-cancel-edit',
96+
data: { question_body: t('posts.unsaved_changes_confirmation') },
97+
role: 'button' %>
7498
<% end %>
7599

76100
<% if SiteSetting['AllowContentTransfer'] && current_user.se_acct_id.nil? %>

0 commit comments

Comments
 (0)