settings: Separate "Your account" section in two different sections.

We separate "Your account" section to two different sections -
"Profile" section for user name, custom profile fields, and avatar
and "Account & Security" section for email, password, role, api-key
and deactivating button.

Another important change here is that the modal for changing name
is removed and now the name has a simple input text box and it
behaves similar to inputs for custom-profile-fields.

Fixes #18848.
This commit is contained in:
sahil839
2021-06-17 22:12:31 +05:30
committed by Tim Abbott
parent 5f74e78bee
commit bb816e1998
14 changed files with 102 additions and 142 deletions

View File

@@ -22,7 +22,7 @@ function test(label, f) {
} }
test("basics", () => { test("basics", () => {
const hash1 = "#settings/your-account"; const hash1 = "#settings/profile";
const hash2 = "#narrow/is/private"; const hash2 = "#narrow/is/private";
browser_history.go_to_location(hash1); browser_history.go_to_location(hash1);
assert.equal(location.hash, hash1); assert.equal(location.hash, hash1);

View File

@@ -86,26 +86,26 @@ run_test("test_get_hash_category", () => {
assert.deepEqual(hash_util.get_hash_category("#drafts"), "drafts"); assert.deepEqual(hash_util.get_hash_category("#drafts"), "drafts");
assert.deepEqual(hash_util.get_hash_category("invites"), "invites"); assert.deepEqual(hash_util.get_hash_category("invites"), "invites");
location.hash = "#settings/your-account"; location.hash = "#settings/profile";
assert.deepEqual(hash_util.get_current_hash_category(), "settings"); assert.deepEqual(hash_util.get_current_hash_category(), "settings");
}); });
run_test("test_get_hash_section", () => { run_test("test_get_hash_section", () => {
assert.equal(hash_util.get_hash_section("streams/subscribed"), "subscribed"); assert.equal(hash_util.get_hash_section("streams/subscribed"), "subscribed");
assert.equal(hash_util.get_hash_section("#settings/your-account"), "your-account"); assert.equal(hash_util.get_hash_section("#settings/profile"), "profile");
assert.equal(hash_util.get_hash_section("settings/10/general/"), "10"); assert.equal(hash_util.get_hash_section("settings/10/general/"), "10");
assert.equal(hash_util.get_hash_section("#drafts"), ""); assert.equal(hash_util.get_hash_section("#drafts"), "");
assert.equal(hash_util.get_hash_section(""), ""); assert.equal(hash_util.get_hash_section(""), "");
location.hash = "#settings/your-account"; location.hash = "#settings/profile";
assert.deepEqual(hash_util.get_current_hash_section(), "your-account"); assert.deepEqual(hash_util.get_current_hash_section(), "profile");
}); });
run_test("build_reload_url", () => { run_test("build_reload_url", () => {
location.hash = "#settings/your-account"; location.hash = "#settings/profile";
assert.equal(hash_util.build_reload_url(), "+oldhash=settings%2Fyour-account"); assert.equal(hash_util.build_reload_url(), "+oldhash=settings%2Fprofile");
location.hash = "#test"; location.hash = "#test";
assert.equal(hash_util.build_reload_url(), "+oldhash=test"); assert.equal(hash_util.build_reload_url(), "+oldhash=test");

View File

@@ -27,7 +27,7 @@ async function open_settings(page: Page): Promise<void> {
await page.waitForSelector(settings_selector, {visible: true}); await page.waitForSelector(settings_selector, {visible: true});
await page.click(settings_selector); await page.click(settings_selector);
await page.waitForSelector("#settings_content .account-settings-form", {visible: true}); await page.waitForSelector("#settings_content .profile-settings-form", {visible: true});
const page_url = await common.page_url_with_fragment(page); const page_url = await common.page_url_with_fragment(page);
assert.ok( assert.ok(
page_url.includes("/#settings/"), page_url.includes("/#settings/"),
@@ -36,23 +36,18 @@ async function open_settings(page: Page): Promise<void> {
} }
async function test_change_full_name(page: Page): Promise<void> { async function test_change_full_name(page: Page): Promise<void> {
await page.click("#change_full_name"); await page.click("#full_name");
const change_full_name_button_selector = "#change_full_name_button";
await page.waitForSelector(change_full_name_button_selector, {visible: true});
const full_name_input_selector = 'input[name="full_name"]'; const full_name_input_selector = 'input[name="full_name"]';
await page.$eval(full_name_input_selector, (el) => { await common.clear_and_type(page, full_name_input_selector, "New name");
(el as HTMLInputElement).value = "";
}); await page.click("#settings_content .profile-settings-form");
await page.waitForFunction(() => $(":focus").attr("id") === "change_full_name_modal"); await page.waitForSelector(".full-name-change-form .alert-success", {visible: true});
await page.type(full_name_input_selector, "New name"); await page.waitForFunction(() => $("#full_name").val() === "New name");
await page.click(change_full_name_button_selector);
await page.waitForFunction(() => $("#change_full_name").text().trim() === "New name");
await common.wait_for_modal_to_close(page);
} }
async function test_change_password(page: Page): Promise<void> { async function test_change_password(page: Page): Promise<void> {
await page.click('[data-section="account-and-privacy"]');
await page.click("#change_password"); await page.click("#change_password");
const change_password_button_selector = "#change_password_button"; const change_password_button_selector = "#change_password_button";

View File

@@ -95,7 +95,7 @@ export function build_user_avatar_widget(upload_function) {
}, },
}); });
} }
const modal_parent = $("#account-settings"); const modal_parent = $("#profile-settings");
const html_body = render_confirm_delete_user_avatar(); const html_body = render_confirm_delete_user_avatar();

View File

@@ -12,7 +12,7 @@ export function get_hash_category(hash) {
} }
export function get_hash_section(hash) { export function get_hash_section(hash) {
// given "#settings/your-account", returns "your-account" // given "#settings/profile", returns "profile"
// given '#streams/5/social", returns "5" // given '#streams/5/social", returns "5"
if (!hash) { if (!hash) {
return ""; return "";

View File

@@ -36,15 +36,10 @@ export function update_email(new_email) {
} }
export function update_full_name(new_full_name) { export function update_full_name(new_full_name) {
const full_name_field = $("#full_name_value");
if (full_name_field) {
full_name_field.text(new_full_name);
}
// Arguably, this should work more like how the `update_email` // Arguably, this should work more like how the `update_email`
// flow works, where we update the name in the modal on open, // flow works, where we update the name in the modal on open,
// rather than updating it here, but this works. // rather than updating it here, but this works.
const full_name_input = $(".full_name_change_container input[name='full_name']"); const full_name_input = $(".full-name-change-form input[name='full_name']");
if (full_name_input) { if (full_name_input) {
full_name_input.val(new_full_name); full_name_input.val(new_full_name);
} }
@@ -279,13 +274,8 @@ export function add_custom_profile_fields_to_settings() {
return; return;
} }
const element_id = "#account-settings .custom-profile-fields-form"; const element_id = "#profile-settings .custom-profile-fields-form";
$(element_id).html(""); $(element_id).html("");
if (page_params.custom_profile_fields.length > 0) {
$("#account-settings #custom-field-header").show();
} else {
$("#account-settings #custom-field-header").hide();
}
append_custom_profile_fields(element_id, people.my_current_user_id()); append_custom_profile_fields(element_id, people.my_current_user_id());
initialize_custom_user_type_fields(element_id, people.my_current_user_id(), true, true); initialize_custom_user_type_fields(element_id, people.my_current_user_id(), true, true);
@@ -403,15 +393,6 @@ export function set_up() {
clear_password_change(); clear_password_change();
$("#change_full_name").on("click", (e) => {
e.preventDefault();
e.stopPropagation();
if (settings_data.user_can_change_name()) {
$("#change_full_name_modal").find("input[name='full_name']").val(page_params.full_name);
overlays.open_modal("#change_full_name_modal");
}
});
$("#change_password").on("click", async (e) => { $("#change_password").on("click", async (e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
@@ -487,28 +468,18 @@ export function set_up() {
password_quality?.(field.val(), $("#pw_strength .bar"), field); password_quality?.(field.val(), $("#pw_strength .bar"), field);
}); });
$("#change_full_name_button").on("click", (e) => { $("#full_name").on("change", (e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
const change_full_name_error = $("#change_full_name_modal")
.find(".change_full_name_info")
.expectOne();
const data = {}; const data = {};
data.full_name = $(".full_name_change_container").find("input[name='full_name']").val(); data.full_name = $("#full_name").val();
const opts = {
success_continuation() {
overlays.close_modal("#change_full_name_modal");
},
error_msg_element: change_full_name_error,
};
settings_ui.do_settings_change( settings_ui.do_settings_change(
channel.patch, channel.patch,
"/json/settings", "/json/settings",
data, data,
$("#account-settings-status").expectOne(), $(".full-name-status").expectOne(),
opts,
); );
}); });
@@ -556,7 +527,7 @@ export function set_up() {
} }
}); });
$("#account-settings").on("click", ".custom_user_field .remove_date", (e) => { $("#profile-settings").on("click", ".custom_user_field .remove_date", (e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
const field = $(e.target).closest(".custom_user_field").expectOne(); const field = $(e.target).closest(".custom_user_field").expectOne();
@@ -564,7 +535,7 @@ export function set_up() {
update_user_custom_profile_fields([field_id], channel.del); update_user_custom_profile_fields([field_id], channel.del);
}); });
$("#account-settings").on("change", ".custom_user_field_value", function (e) { $("#profile-settings").on("change", ".custom_user_field_value", function (e) {
const fields = []; const fields = [];
const value = $(this).val(); const value = $(this).val();
const field_id = Number.parseInt( const field_id = Number.parseInt(

View File

@@ -38,6 +38,10 @@ export function get_group(section) {
case "deactivated-users-admin": case "deactivated-users-admin":
return "org_users"; return "org_users";
case "profile":
case "account-and-privacy":
return "your-account";
default: default:
return section; return section;
} }

View File

@@ -62,7 +62,8 @@ h3 .fa-question-circle-o {
text-align: left; text-align: left;
} }
} }
#account-settings { #account-settings,
#profile-field-settings {
.grid { .grid {
label { label {
min-width: 120px; min-width: 120px;
@@ -108,8 +109,7 @@ h3 .fa-question-circle-o {
} }
#change_password_modal, #change_password_modal,
#change_email_modal, #change_email_modal {
#change_full_name_modal {
max-width: 480px; max-width: 480px;
} }
@@ -587,12 +587,14 @@ input[type="checkbox"] {
} }
} }
#account-settings { #profile-settings {
.custom-profile-fields-form .custom_user_field label { .custom-profile-fields-form .custom_user_field label,
.full-name-change-form label {
min-width: fit-content; min-width: fit-content;
} }
.alert-notification.custom-field-status { .alert-notification.custom-field-status,
.alert-notification.full-name-status {
padding-top: 0; padding-top: 0;
padding-bottom: 0; padding-bottom: 0;
margin-top: 0; margin-top: 0;
@@ -1511,7 +1513,6 @@ input[type="checkbox"] {
width: 245px; width: 245px;
} }
#change_full_name_modal .change_full_name_info,
#change_email_modal .change_email_info, #change_email_modal .change_email_info,
#change_password_modal .change_password_info, #change_password_modal .change_password_info,
#api_key_modal #api_key_status { #api_key_modal #api_key_status {
@@ -1519,7 +1520,7 @@ input[type="checkbox"] {
} }
} }
#account-settings, #profile-settings,
#edit-user-form { #edit-user-form {
.user-role button { .user-role button {
cursor: default; cursor: default;

View File

@@ -1,9 +1,9 @@
<div id="account-settings" class="settings-section show" data-name="your-account"> <div id="account-settings" class="settings-section show" data-name="account-and-privacy">
<div class="alert" id="dev-account-settings-status"></div> <div class="alert" id="dev-account-settings-status"></div>
<div class="account-settings-form"> <div class="account-settings-form">
<div class="inline-block"> <div class="inline-block">
<div id="user_details_section"> <div id="user_details_section">
<h3 class="inline-block">{{t "User settings" }}</h3> <h3 class="inline-block">{{t "Account" }}</h3>
<form class="email-change-form grid"> <form class="email-change-form grid">
<div class="alert-notification" id="account-settings-status"></div> <div class="alert-notification" id="account-settings-status"></div>
<div class="input-group"> <div class="input-group">
@@ -43,41 +43,6 @@
</p> </p>
{{/if}} {{/if}}
<form class="form-horizontal full-name-change-form">
<div class="input-group inline-block grid user-name-parent">
<div class="user-name-section inline-block">
<label for="full_name" class="inline-block title">{{t "Full name" }}</label>
<button type="button" id='change_full_name' class="button btn-link small white rounded inline-block" id="full_name"
{{#unless user_can_change_name}}disabled="disabled"{{/unless}}>
<span id="full_name_value">{{page_params.full_name}}</span>
<i class="fa fa-pencil"></i>
</button>
<i class="fa fa-question-circle change_name_tooltip tippy-zulip-tooltip settings-info-icon"
{{#if user_can_change_name}}style="display:none"{{/if}}
data-tippy-content="{{t 'Name changes are disabled in this organization. Contact an administrator to change your name.' }}">
</i>
</div>
<div id="change_full_name_modal" class="modal modal-bg hide fade" tabindex="-1" role="dialog"
aria-labelledby="change_full_name_modal_label" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="{{t 'Close' }}"><span aria-hidden="true">&times;</span></button>
<h3 class="inline-block" id="change_full_name_modal_label">{{t "Change full name" }}</h3>
<div class="alert-notification change_full_name_info"></div>
</div>
<div class="modal-body">
<div class="input-group full_name_change_container">
<label for="email">{{t "New full name" }}</label>
<input type="text" name="full_name" value="{{ page_params.full_name }}" autocomplete="off" spellcheck="false" autofocus="autofocus"/>
</div>
</div>
<div class="modal-footer">
<button class="button white rounded" type="button" data-dismiss="modal">{{t "Cancel" }}</button>
<button id='change_full_name_button' class="button rounded sea-green" data-dismiss="modal">{{t "Change" }}</button>
</div>
</div>
</div>
</form>
<form class="password-change-form grid"> <form class="password-change-form grid">
{{#if page_params.realm_email_auth_enabled}} {{#if page_params.realm_email_auth_enabled}}
<div> <div>
@@ -126,44 +91,14 @@
<label for="user_role" class="inline-block title">{{t "Role" }}</label> <label for="user_role" class="inline-block title">{{t "Role" }}</label>
<span>{{user_role_text}}</span> <span>{{user_role_text}}</span>
</div> </div>
<div class="input-group">
<form class="deactivate_account grid"> <button type="submit" class="button rounded btn-danger" id="user_deactivate_account_button">
<div class="input-group"> {{t 'Deactivate account' }}
<button type="submit" class="button rounded btn-danger" id="user_deactivate_account_button"> </button>
{{t 'Deactivate account' }} </div>
</button>
</div>
</form>
</div> </div>
</div> </div>
<div class="inline-block user-avatar-section">
<h3>
{{t "Profile picture" }}
<i class="fa fa-question-circle change_name_tooltip tippy-zulip-tooltip settings-info-icon"
{{#if user_can_change_avatar}}style="display:none"{{/if}}
data-tippy-content="{{t 'Avatar changes are disabled in this organization.' }}">
</i>
</h3>
{{> image_upload_widget
widget = "user-avatar"
upload_text = (t "Upload new profile picture")
delete_text = (t "Delete profile picture")
is_editable_by_current_user = user_can_change_avatar
image = page_params.avatar_url_medium}}
<div id="user-avatar-source">
<a href="https://en.gravatar.com/" target="_blank" rel="noopener noreferrer">{{t "Avatar from Gravatar" }}</a>
</div>
</div>
<div class="clear-float"></div>
<h3 class="inline-block" id="custom-field-header" {{#unless page_params.custom_profile_fields}}style="display: none"{{/unless}}>{{t "Profile" }}</h3>
<form class="form-horizontal custom-profile-fields-form grid"></form>
<button class="button rounded sea-green w-200 block" id="show_my_user_profile_modal">
{{t 'Preview profile' }}
<i class="fa fa-external-link" aria-hidden="true" title="{{t 'Preview profile' }}"></i>
</button>
<hr class="settings_separator" /> <hr class="settings_separator" />
<div class="form-horizontal" id="api_key_button_box"> <div class="form-horizontal" id="api_key_button_box">

View File

@@ -0,0 +1,48 @@
<div id="profile-settings" class="settings-section show" data-name="profile">
<div class="profile-settings-form">
<div class="inline-block">
<h3 class="inline-block hide" id="user-profile-header">{{t "Profile" }}</h3>
<div id="user_details_section">
<form class="form-horizontal full-name-change-form">
<div class="input-group inline-block grid user-name-parent">
<div class="user-name-section inline-block">
<label for="full_name" class="title inline-block">{{t "Full name" }}</label>
<i class="fa fa-question-circle change_name_tooltip tippy-zulip-tooltip settings-info-icon"
{{#if user_can_change_name}}style="display:none"{{/if}}
data-tippy-content="{{t 'Name changes are disabled in this organization. Contact an administrator to change your name.' }}">
</i>
<div class="alert-notification full-name-status"></div>
<div class="name-input">
<input id="full_name" name="full_name" type="text" value="{{ page_params.full_name }}" {{#unless user_can_change_name}}disabled="disabled"{{/unless}} maxlength="60" />
</div>
</div>
</div>
</form>
<form class="form-horizontal custom-profile-fields-form grid"></form>
<button class="button rounded sea-green w-200 block" id="show_my_user_profile_modal">
{{t 'Preview profile' }}
<i class="fa fa-external-link" aria-hidden="true" title="{{t 'Preview profile' }}"></i>
</button>
</div>
</div>
<div class="inline-block user-avatar-section">
<h3>
{{t "Profile picture" }}
<i class="fa fa-question-circle change_name_tooltip tippy-zulip-tooltip settings-info-icon"
{{#if user_can_change_avatar}}style="display:none"{{/if}}
data-tippy-content="{{t 'Avatar changes are disabled in this organization.' }}">
</i>
</h3>
{{> image_upload_widget
widget = "user-avatar"
upload_text = (t "Upload new profile picture")
delete_text = (t "Delete profile picture")
is_editable_by_current_user = user_can_change_avatar
image = page_params.avatar_url_medium}}
<div id="user-avatar-source">
<a href="https://en.gravatar.com/" target="_blank" rel="noopener noreferrer">{{t "Avatar from Gravatar" }}</a>
</div>
</div>
</div>
</div>

View File

@@ -1,4 +1,6 @@
<div id="settings-change-box" class="new-style"> <div id="settings-change-box" class="new-style">
{{> settings/profile_settings }}
{{> settings/account_settings }} {{> settings/account_settings }}
{{> settings/display_settings }} {{> settings/display_settings }}

View File

@@ -129,7 +129,7 @@
{{/unless}} {{/unless}}
{{#if is_me}} {{#if is_me}}
<li> <li>
<a href="/#settings/your-account"> <a href="/#settings/profile">
<i class="fa fa-edit" aria-hidden="true"></i> {{#tr}}Edit your profile{{/tr}} <i class="fa fa-edit" aria-hidden="true"></i> {{#tr}}Edit your profile{{/tr}}
</a> </a>
</li> </li>

View File

@@ -7,7 +7,7 @@
<div id="name"> <div id="name">
{{full_name}} {{full_name}}
{{#if is_me}} {{#if is_me}}
<a href="/#settings/your-account"> <a href="/#settings/profile">
<i class="fa fa-edit" id="edit-button" aria-hidden="true"></i> <i class="fa fa-edit" id="edit-button" aria-hidden="true"></i>
</a> </a>
{{/if}} {{/if}}

View File

@@ -11,9 +11,13 @@
<div class="sidebar-list dark-grey small-text"> <div class="sidebar-list dark-grey small-text">
<div class="center tab-container"></div> <div class="center tab-container"></div>
<ul class="normal-settings-list"> <ul class="normal-settings-list">
<li tabindex="0" data-section="your-account"> <li tabindex="0" data-section="profile">
<i class="icon fa fa-user" aria-hidden="true"></i> <i class="icon fa fa-user" aria-hidden="true"></i>
<div class="text">{{ _('Your account') }}</div> <div class="text">{{ _('Profile') }}</div>
</li>
<li tabindex="0" data-section="account-and-privacy">
<i class="icon fa fa-lock" aria-hidden="true"></i>
<div class="text">{{ _('Account & privacy') }}</div>
</li> </li>
<li tabindex="0" data-section="display-settings"> <li tabindex="0" data-section="display-settings">
<i class="icon fa fa-clock-o" aria-hidden="true"></i> <i class="icon fa fa-clock-o" aria-hidden="true"></i>