blob: cba1c3b988ceb21476024b93169f88563972a8a3 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview
* 'settings-sync-account-section' is the settings page containing sign-in
* settings.
*/
cr.exportPath('settings');
/** @const {number} */
settings.MAX_SIGNIN_PROMO_IMPRESSION = 10;
Polymer({
is: 'settings-sync-account-control',
behaviors: [WebUIListenerBehavior],
properties: {
/**
* The current sync status, supplied by parent element.
* @type {!settings.SyncStatus}
*/
syncStatus: Object,
// String to be used as a title when the promo has an account.
promoLabelWithAccount: String,
// String to be used as title of the promo has no account.
promoLabelWithNoAccount: String,
// String to be used as a subtitle when the promo has an account.
promoSecondaryLabelWithAccount: String,
// String to be used as subtitle of the promo has no account.
promoSecondaryLabelWithNoAccount: String,
/**
* Proxy variable for syncStatus.signedIn to shield observer from being
* triggered multiple times whenever syncStatus changes.
* @private {boolean}
*/
signedIn_: {
type: Boolean,
computed: 'computeSignedIn_(syncStatus.signedIn)',
observer: 'onSignedInChanged_',
},
/** @private {!Array<!settings.StoredAccount>} */
storedAccounts_: Object,
/** @private {?settings.StoredAccount} */
shownAccount_: Object,
showingPromo: {
type: Boolean,
value: false,
reflectToAttribute: true,
},
// This property should be set by the parent only and should not change
// after the element is created.
embeddedInSubpage: {
type: Boolean,
reflectToAttribute: true,
},
// This property should be set by the parent only and should not change
// after the element is created.
hideButtons: {
type: Boolean,
value: false,
reflectToAttribute: true,
},
/** @private {boolean} */
shouldShowAvatarRow_: {
type: Boolean,
value: false,
computed: 'computeShouldShowAvatarRow_(storedAccounts_, syncStatus,' +
'storedAccounts_.length, syncStatus.signedIn)',
observer: 'onShouldShowAvatarRowChange_',
},
/** @private */
subLabel_: {
type: String,
computed: 'computeSubLabel_(promoSecondaryLabelWithAccount,' +
'promoSecondaryLabelWithNoAccount, shownAccount_)',
},
unifiedConsentEnabled: Boolean,
/** @private */
showSetupButtons_: {
type: Boolean,
computed: 'computeShowSetupButtons_(unifiedConsentEnabled,' +
'hideButtons, syncStatus.setupInProgress)',
},
},
observers: [
'onShownAccountShouldChange_(storedAccounts_, syncStatus)',
],
/** @private {?settings.SyncBrowserProxy} */
syncBrowserProxy_: null,
created: function() {
this.syncBrowserProxy_ = settings.SyncBrowserProxyImpl.getInstance();
},
/** @override */
attached: function() {
this.syncBrowserProxy_.getStoredAccounts().then(
this.handleStoredAccounts_.bind(this));
this.addWebUIListener(
'stored-accounts-updated', this.handleStoredAccounts_.bind(this));
},
/**
* Records the following user actions:
* - Signin_Impression_FromSettings and
* - Signin_ImpressionWithAccount_FromSettings
* - Signin_ImpressionWithNoAccount_FromSettings
* @private
*/
recordImpressionUserActions_: function() {
assert(!this.syncStatus.signedIn);
assert(this.shownAccount_ !== undefined);
chrome.metricsPrivate.recordUserAction('Signin_Impression_FromSettings');
if (this.shownAccount_) {
chrome.metricsPrivate.recordUserAction(
'Signin_ImpressionWithAccount_FromSettings');
} else {
chrome.metricsPrivate.recordUserAction(
'Signin_ImpressionWithNoAccount_FromSettings');
}
},
/**
* @return {boolean}
* @private
*/
computeSignedIn_: function() {
return !!this.syncStatus.signedIn;
},
/** @private */
onSignedInChanged_: function() {
if (this.embeddedInSubpage) {
this.showingPromo = true;
return;
}
if (!this.showingPromo && !this.syncStatus.signedIn &&
this.syncBrowserProxy_.getPromoImpressionCount() <
settings.MAX_SIGNIN_PROMO_IMPRESSION) {
this.showingPromo = true;
this.syncBrowserProxy_.incrementPromoImpressionCount();
} else {
// Turn off the promo if the user is signed in.
this.showingPromo = false;
}
if (!this.syncStatus.signedIn && this.shownAccount_ !== undefined) {
this.recordImpressionUserActions_();
}
},
/**
* @param {string} labelWithAccount
* @param {string} labelWithNoAccount
* @return {string}
* @private
*/
getLabel_: function(labelWithAccount, labelWithNoAccount) {
return this.shownAccount_ ? labelWithAccount : labelWithNoAccount;
},
/**
* @return {string}
* @private
*/
computeSubLabel_: function() {
return this.getLabel_(this.promoSecondaryLabelWithAccount,
this.promoSecondaryLabelWithNoAccount);
},
/**
* @return {string}
* @private
*/
getPromoHeaderClass_: function() {
return !!this.subLabel_ ? 'two-line': '';
},
/**
* @param {string} label
* @param {string} name
* @return {string}
* @private
*/
getSubstituteLabel_: function(label, name) {
return loadTimeData.substituteString(label, name);
},
/**
* @param {string} label
* @param {string} account
* @return {string}
* @private
*/
getAccountLabel_: function(label, account) {
if(!!this.unifiedConsentEnabled && !!this.syncStatus.setupInProgress) {
return this.syncStatus.statusText || account;
}
return this.syncStatus.signedIn && !this.syncStatus.hasError &&
!this.syncStatus.disabled ?
loadTimeData.substituteString(label, account) :
account;
},
/**
* @param {?string} image
* @return {string}
* @private
*/
getAccountImageSrc_: function(image) {
// image can be undefined if the account has not set an avatar photo.
return image || 'chrome://theme/IDR_PROFILE_AVATAR_PLACEHOLDER_LARGE';
},
/**
* Returns the class of the sync icon.
* @return {string}
* @private
*/
getSyncIconStyle_: function() {
if (!!this.syncStatus.hasUnrecoverableError) {
return 'sync-problem';
}
if (!!this.syncStatus.hasError) {
return this.syncStatus.statusAction ==
settings.StatusAction.REAUTHENTICATE ?
'sync-paused' :
'sync-problem';
}
if (!!this.syncStatus.disabled) {
return 'sync-disabled';
}
return 'sync';
},
/**
* Returned value must match one of iron-icon's settings:(*) icon name.
* @return {string}
* @private
*/
getSyncIcon_: function() {
switch (this.getSyncIconStyle_()) {
case 'sync-problem':
return 'settings:sync-problem';
case 'sync-paused':
return 'settings:sync-disabled';
default:
return 'cr:sync';
}
},
/**
* @return {string}
* @private
*/
getAvatarRowTitle_: function(
accountName, syncErrorLabel, authErrorLabel, disabledLabel) {
switch (this.getSyncIconStyle_()) {
case 'sync-problem':
return syncErrorLabel;
case 'sync-paused':
return authErrorLabel;
case 'sync-disabled':
return disabledLabel;
default:
return accountName;
}
},
/**
* @return {boolean}
* @private
*/
shouldShowTurnOffButton_: function() {
return !this.hideButtons && !this.showSetupButtons_ &&
!!this.syncStatus.signedIn;
},
/**
* @return {boolean}
* @private
*/
shouldShowErrorActionButton_: function() {
if (this.embeddedInSubpage &&
this.syncStatus.statusAction ==
settings.StatusAction.ENTER_PASSPHRASE) {
// In a subpage the passphrase button is not required.
return false;
}
return !this.hideButtons && !this.showSetupButtons_ &&
!!this.syncStatus.signedIn && !!this.syncStatus.hasError &&
this.syncStatus.statusAction != settings.StatusAction.NO_ACTION;
},
/**
* @param {!Array<!settings.StoredAccount>} accounts
* @private
*/
handleStoredAccounts_: function(accounts) {
this.storedAccounts_ = accounts;
},
/**
* @return {boolean}
* @private
*/
computeShouldShowAvatarRow_: function() {
if (this.storedAccounts_ === undefined || this.syncStatus === undefined) {
return false;
}
return this.syncStatus.signedIn || this.storedAccounts_.length > 0;
},
/** @private */
onErrorButtonTap_: function() {
switch (this.syncStatus.statusAction) {
case settings.StatusAction.REAUTHENTICATE:
this.syncBrowserProxy_.startSignIn();
break;
case settings.StatusAction.SIGNOUT_AND_SIGNIN:
if (this.syncStatus.domain) {
settings.navigateTo(settings.routes.SIGN_OUT);
} else {
// Silently sign the user out without deleting their profile and
// prompt them to sign back in.
this.syncBrowserProxy_.signOut(false);
this.syncBrowserProxy_.startSignIn();
}
break;
case settings.StatusAction.UPGRADE_CLIENT:
settings.navigateTo(settings.routes.ABOUT);
break;
case settings.StatusAction.ENTER_PASSPHRASE:
case settings.StatusAction.CONFIRM_SYNC_SETTINGS:
default:
settings.navigateTo(settings.routes.SYNC);
}
},
/** @private */
onSigninTap_: function() {
this.syncBrowserProxy_.startSignIn();
// Need to close here since one menu item also triggers this function.
if (this.$$('#menu')) {
/** @type {!CrActionMenuElement} */ (this.$$('#menu')).close();
}
},
/** @private */
onSignoutTap_: function() {
this.syncBrowserProxy_.signOut(false /* deleteProfile */);
/** @type {!CrActionMenuElement} */ (this.$$('#menu')).close();
},
/** @private */
onSyncButtonTap_: function() {
assert(this.shownAccount_);
assert(this.storedAccounts_.length > 0);
const isDefaultPromoAccount =
(this.shownAccount_.email == this.storedAccounts_[0].email);
this.syncBrowserProxy_.startSyncingWithEmail(
this.shownAccount_.email, isDefaultPromoAccount);
},
/** @private */
onTurnOffButtonTap_: function() {
/* This will route to people_page's disconnect dialog. */
settings.navigateTo(settings.routes.SIGN_OUT);
},
/** @private */
onMenuButtonTap_: function() {
const actionMenu =
/** @type {!CrActionMenuElement} */ (this.$$('#menu'));
actionMenu.showAt(assert(this.$$('#dropdown-arrow')));
},
/** @private */
onShouldShowAvatarRowChange_: function() {
// Close dropdown when avatar-row hides, so if it appears again, the menu
// won't be open by default.
const actionMenu = this.$$('#menu');
if (!this.shouldShowAvatarRow_ && actionMenu && actionMenu.open) {
actionMenu.close();
}
},
/**
* @param {!{model:
* !{item: !settings.StoredAccount},
* }} e
* @private
*/
onAccountTap_: function(e) {
this.shownAccount_ = e.model.item;
/** @type {!CrActionMenuElement} */ (this.$$('#menu')).close();
},
/** @private */
onShownAccountShouldChange_: function() {
if (this.storedAccounts_ === undefined || this.syncStatus === undefined) {
return;
}
if (this.syncStatus.signedIn) {
for (let i = 0; i < this.storedAccounts_.length; i++) {
if (this.storedAccounts_[i].email == this.syncStatus.signedInUsername) {
this.shownAccount_ = this.storedAccounts_[i];
return;
}
}
} else {
const firstStoredAccount =
(this.storedAccounts_.length > 0) ? this.storedAccounts_[0] : null;
// Sign-in impressions should be recorded in the following cases:
// 1. When the promo is first shown, i.e. when |shownAccount_| is
// initialized;
// 2. When the impression account state changes, i.e. promo impression
// state changes (WithAccount -> WithNoAccount) or
// (WithNoAccount -> WithAccount).
const shouldRecordImpression = (this.shownAccount_ === undefined) ||
(!this.shownAccount_ && firstStoredAccount) ||
(this.shownAccount_ && !firstStoredAccount);
this.shownAccount_ = firstStoredAccount;
if (shouldRecordImpression) {
this.recordImpressionUserActions_();
}
}
},
/**
* @return {boolean}
* @private
*/
computeShowSetupButtons_: function() {
return !this.hideButtons && !!this.unifiedConsentEnabled &&
!!this.syncStatus.setupInProgress;
},
/** @private */
onSetupCancel_: function() {
this.fire('sync-setup-done', false);
},
/** @private */
onSetupConfirm_: function() {
this.fire('sync-setup-done', true);
},
});