blob: c4253185da2dc5604a5310c7b5fc5261b63653d5 [file] [log] [blame]
Dave Borowitz8cdc76b2018-03-26 10:04:27 -04001/**
2 * @license
3 * Copyright (C) 2016 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +010017import '../../../scripts/bundled-polymer.js';
Wyatt Allen767b7602016-06-15 16:51:20 -070018
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +010019import '@polymer/iron-input/iron-input.js';
20import '../../../behaviors/fire-behavior/fire-behavior.js';
21import '../../shared/gr-avatar/gr-avatar.js';
22import '../../shared/gr-date-formatter/gr-date-formatter.js';
23import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
24import '../../../styles/gr-form-styles.js';
25import '../../../styles/shared-styles.js';
26import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js';
27import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
28import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
29import {PolymerElement} from '@polymer/polymer/polymer-element.js';
30import {htmlTemplate} from './gr-account-info_html.js';
31
32/**
33 * @appliesMixin Gerrit.FireMixin
34 * @extends Polymer.Element
35 */
36class GrAccountInfo extends mixinBehaviors( [
37 Gerrit.FireBehavior,
38], GestureEventListeners(
39 LegacyElementMixin(
40 PolymerElement))) {
41 static get template() { return htmlTemplate; }
42
43 static get is() { return 'gr-account-info'; }
Dmitrii Filippov3fd2b102019-11-15 16:16:46 +010044 /**
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +010045 * Fired when account details are changed.
46 *
47 * @event account-detail-update
Tao Zhou9a076812019-12-17 09:59:28 +010048 */
Logan Hanks25f49af2016-10-10 17:26:43 -070049
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +010050 static get properties() {
51 return {
52 usernameMutable: {
53 type: Boolean,
54 notify: true,
55 computed: '_computeUsernameMutable(_serverConfig, _account.username)',
56 },
57 nameMutable: {
58 type: Boolean,
59 notify: true,
60 computed: '_computeNameMutable(_serverConfig)',
61 },
62 hasUnsavedChanges: {
63 type: Boolean,
64 notify: true,
65 computed: '_computeHasUnsavedChanges(_hasNameChange, ' +
66 '_hasUsernameChange, _hasStatusChange)',
67 },
Wyatt Allen767b7602016-06-15 16:51:20 -070068
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +010069 _hasNameChange: Boolean,
70 _hasUsernameChange: Boolean,
71 _hasStatusChange: Boolean,
72 _loading: {
73 type: Boolean,
74 value: false,
75 },
76 _saving: {
77 type: Boolean,
78 value: false,
79 },
80 /** @type {?} */
81 _account: Object,
82 _serverConfig: Object,
83 _username: {
84 type: String,
85 observer: '_usernameChanged',
86 },
87 _avatarChangeUrl: {
88 type: String,
89 value: '',
90 },
91 };
92 }
93
94 static get observers() {
95 return [
96 '_nameChanged(_account.name)',
97 '_statusChanged(_account.status)',
98 ];
99 }
100
101 loadData() {
102 const promises = [];
103
104 this._loading = true;
105
106 promises.push(this.$.restAPI.getConfig().then(config => {
107 this._serverConfig = config;
108 }));
109
110 promises.push(this.$.restAPI.getAccount().then(account => {
111 this._hasNameChange = false;
112 this._hasUsernameChange = false;
113 this._hasStatusChange = false;
114 // Provide predefined value for username to trigger computation of
115 // username mutability.
116 account.username = account.username || '';
117 this._account = account;
118 this._username = account.username;
119 }));
120
121 promises.push(this.$.restAPI.getAvatarChangeUrl().then(url => {
122 this._avatarChangeUrl = url;
123 }));
124
125 return Promise.all(promises).then(() => {
126 this._loading = false;
127 });
128 }
129
130 save() {
131 if (!this.hasUnsavedChanges) {
132 return Promise.resolve();
Dmitrii Filippov3fd2b102019-11-15 16:16:46 +0100133 }
Wyatt Allen767b7602016-06-15 16:51:20 -0700134
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100135 this._saving = true;
136 // Set only the fields that have changed.
137 // Must be done in sequence to avoid race conditions (@see Issue 5721)
138 return this._maybeSetName()
139 .then(this._maybeSetUsername.bind(this))
140 .then(this._maybeSetStatus.bind(this))
141 .then(() => {
142 this._hasNameChange = false;
143 this._hasStatusChange = false;
144 this._saving = false;
145 this.fire('account-detail-update');
146 });
147 }
148
149 _maybeSetName() {
150 return this._hasNameChange && this.nameMutable ?
151 this.$.restAPI.setAccountName(this._account.name) :
152 Promise.resolve();
153 }
154
155 _maybeSetUsername() {
156 return this._hasUsernameChange && this.usernameMutable ?
157 this.$.restAPI.setAccountUsername(this._username) :
158 Promise.resolve();
159 }
160
161 _maybeSetStatus() {
162 return this._hasStatusChange ?
163 this.$.restAPI.setAccountStatus(this._account.status) :
164 Promise.resolve();
165 }
166
167 _computeHasUnsavedChanges(nameChanged, usernameChanged, statusChanged) {
168 return nameChanged || usernameChanged || statusChanged;
169 }
170
171 _computeUsernameMutable(config, username) {
172 // Polymer 2: check for undefined
173 if ([
174 config,
175 username,
176 ].some(arg => arg === undefined)) {
177 return undefined;
Dmitrii Filippov3fd2b102019-11-15 16:16:46 +0100178 }
Wyatt Allen767b7602016-06-15 16:51:20 -0700179
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100180 // Username may not be changed once it is set.
181 return config.auth.editable_account_fields.includes('USER_NAME') &&
182 !username;
183 }
Wyatt Allen767b7602016-06-15 16:51:20 -0700184
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100185 _computeNameMutable(config) {
186 return config.auth.editable_account_fields.includes('FULL_NAME');
187 }
Wyatt Allen767b7602016-06-15 16:51:20 -0700188
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100189 _statusChanged() {
190 if (this._loading) { return; }
191 this._hasStatusChange = true;
192 }
Wyatt Allen767b7602016-06-15 16:51:20 -0700193
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100194 _usernameChanged() {
195 if (this._loading || !this._account) { return; }
196 this._hasUsernameChange =
197 (this._account.username || '') !== (this._username || '');
198 }
Wyatt Allen767b7602016-06-15 16:51:20 -0700199
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100200 _nameChanged() {
201 if (this._loading) { return; }
202 this._hasNameChange = true;
203 }
Paladox none7e4726c2018-10-07 14:29:45 +0000204
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100205 _handleKeydown(e) {
206 if (e.keyCode === 13) { // Enter
207 e.stopPropagation();
208 this.save();
Dmitrii Filippov3fd2b102019-11-15 16:16:46 +0100209 }
210 }
211
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100212 _hideAvatarChangeUrl(avatarChangeUrl) {
213 if (!avatarChangeUrl) {
214 return 'hide';
215 }
216
217 return '';
218 }
219}
220
221customElements.define(GrAccountInfo.is, GrAccountInfo);