summaryrefslogtreecommitdiffstats
path: root/comm/mail/components/preferences/sync.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mail/components/preferences/sync.js')
-rw-r--r--comm/mail/components/preferences/sync.js377
1 files changed, 377 insertions, 0 deletions
diff --git a/comm/mail/components/preferences/sync.js b/comm/mail/components/preferences/sync.js
new file mode 100644
index 0000000000..d8336c6e23
--- /dev/null
+++ b/comm/mail/components/preferences/sync.js
@@ -0,0 +1,377 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from preferences.js */
+
+ChromeUtils.defineESModuleGetters(this, {
+ UIState: "resource://services-sync/UIState.sys.mjs",
+ Weave: "resource://services-sync/main.sys.mjs",
+});
+
+var { FxAccounts, getFxAccountsSingleton } = ChromeUtils.importESModule(
+ "resource://gre/modules/FxAccounts.sys.mjs"
+);
+var fxAccounts = getFxAccountsSingleton();
+
+var gSyncPane = {
+ init() {
+ this._setupEventListeners();
+ this.setupEnginesUI();
+
+ Weave.Svc.Obs.add(UIState.ON_UPDATE, this.updateWeavePrefs, this);
+
+ window.addEventListener("unload", () => {
+ Weave.Svc.Obs.remove(UIState.ON_UPDATE, this.updateWeavePrefs, this);
+ });
+
+ let cachedComputerName = Services.prefs.getStringPref(
+ "identity.fxaccounts.account.device.name",
+ ""
+ );
+ if (cachedComputerName) {
+ this._populateComputerName(cachedComputerName);
+ }
+
+ this.updateWeavePrefs();
+ },
+
+ /**
+ * Update the UI based on the current state.
+ */
+ updateWeavePrefs() {
+ let state = UIState.get();
+
+ let noFxaAccount = document.getElementById("noFxaAccount");
+ let hasFxaAccount = document.getElementById("hasFxaAccount");
+ if (state.status == UIState.STATUS_NOT_CONFIGURED) {
+ noFxaAccount.hidden = false;
+ hasFxaAccount.hidden = true;
+ return;
+ }
+ noFxaAccount.hidden = true;
+ hasFxaAccount.hidden = false;
+
+ let syncReady = false; // Is sync able to actually sync?
+ let fxaLoginUnverified = document.getElementById("fxaLoginUnverified");
+ let fxaLoginRejected = document.getElementById("fxaLoginRejected");
+ let fxaLoginVerified = document.getElementById("fxaLoginVerified");
+ if (state.status == UIState.STATUS_LOGIN_FAILED) {
+ fxaLoginUnverified.hidden = true;
+ fxaLoginRejected.hidden = false;
+ fxaLoginVerified.hidden = true;
+ } else if (state.status == UIState.STATUS_NOT_VERIFIED) {
+ fxaLoginUnverified.hidden = false;
+ fxaLoginRejected.hidden = true;
+ fxaLoginVerified.hidden = true;
+ } else {
+ fxaLoginUnverified.hidden = true;
+ fxaLoginRejected.hidden = true;
+ fxaLoginVerified.hidden = false;
+ syncReady = true;
+ }
+
+ this._populateComputerName(Weave.Service.clientsEngine.localName);
+ for (let elt of document.querySelectorAll(".needs-account-ready")) {
+ elt.disabled = !syncReady;
+ }
+
+ let syncConnected = document.getElementById("syncConnected");
+ let syncDisconnected = document.getElementById("syncDisconnected");
+ syncConnected.hidden = !syncReady || !state.syncEnabled;
+ syncDisconnected.hidden = !syncReady || state.syncEnabled;
+
+ document.l10n.setAttributes(
+ document.getElementById("fxaAccountMailNotVerified"),
+ "sync-pane-email-not-verified",
+ { userEmail: state.email }
+ );
+ document.l10n.setAttributes(
+ document.getElementById("fxaAccountLoginRejected"),
+ "sync-signedin-login-failure",
+ { userEmail: state.email }
+ );
+
+ document.getElementById("fxaAvatar").src =
+ state.avatarURL && !state.avatarIsDefault ? state.avatarURL : "";
+ document.getElementById("fxaDisplayName").textContent = state.displayName;
+ document.getElementById("fxaEmailAddress").textContent = state.email;
+
+ this._updateSyncNow(state.syncing);
+ },
+
+ _toggleComputerNameControls(editMode) {
+ let textbox = document.getElementById("fxaDeviceNameInput");
+ textbox.readOnly = !editMode;
+ document.getElementById("fxaDeviceNameChangeDeviceName").hidden = editMode;
+ document.getElementById("fxaDeviceNameCancel").hidden = !editMode;
+ document.getElementById("fxaDeviceNameSave").hidden = !editMode;
+ },
+
+ _focusComputerNameTextbox() {
+ let textbox = document.getElementById("fxaDeviceNameInput");
+ let valLength = textbox.value.length;
+ textbox.focus();
+ textbox.setSelectionRange(valLength, valLength);
+ },
+
+ _blurComputerNameTextbox() {
+ document.getElementById("fxaDeviceNameInput").blur();
+ },
+
+ _focusAfterComputerNameTextbox() {
+ // Focus the most appropriate element that's *not* the "computer name" box.
+ Services.focus.moveFocus(
+ window,
+ document.getElementById("fxaDeviceNameInput"),
+ Services.focus.MOVEFOCUS_FORWARD,
+ 0
+ );
+ },
+
+ _updateComputerNameValue(save) {
+ if (save) {
+ let textbox = document.getElementById("fxaDeviceNameInput");
+ Weave.Service.clientsEngine.localName = textbox.value;
+ }
+ this._populateComputerName(Weave.Service.clientsEngine.localName);
+ },
+
+ _setupEventListeners() {
+ function setEventListener(id, eventType, callback) {
+ document
+ .getElementById(id)
+ .addEventListener(eventType, callback.bind(gSyncPane));
+ }
+
+ setEventListener("noFxaSignIn", "click", function () {
+ window.browsingContext.topChromeWindow.gSync.initFxA();
+ return false;
+ });
+ setEventListener(
+ "fxaResendVerification",
+ "click",
+ gSyncPane.verifyFirefoxAccount
+ );
+ setEventListener("fxaUnverifiedRemoveAccount", "click", function () {
+ /* No warning as account can't have previously synced. */
+ gSyncPane.unlinkFirefoxAccount(false);
+ });
+ setEventListener("fxaRejectedSignIn", "click", gSyncPane.reSignIn);
+ setEventListener("fxaRejectedRemoveAccount", "click", function () {
+ gSyncPane.unlinkFirefoxAccount(true);
+ });
+ setEventListener("photoButton", "click", function (event) {
+ window.browsingContext.topChromeWindow.gSync.openFxAAvatarPage(
+ "preferences"
+ );
+ });
+ setEventListener("verifiedManage", "click", function (event) {
+ window.browsingContext.topChromeWindow.gSync.openFxAManagePage(
+ "preferences"
+ );
+ event.preventDefault();
+ // Stop attempts to open this link in an external browser.
+ event.stopPropagation();
+ });
+ setEventListener("fxaAccountSignOut", "click", function () {
+ gSyncPane.unlinkFirefoxAccount(true);
+ });
+ setEventListener("fxaDeviceNameCancel", "click", function () {
+ // We explicitly blur the textbox because of bug 75324, then after
+ // changing the state of the buttons, force focus to whatever the focus
+ // manager thinks should be next (which on the mac, depends on an OSX
+ // keyboard access preference)
+ this._blurComputerNameTextbox();
+ this._toggleComputerNameControls(false);
+ this._updateComputerNameValue(false);
+ this._focusAfterComputerNameTextbox();
+ });
+ setEventListener("fxaDeviceNameSave", "click", function () {
+ // Work around bug 75324 - see above.
+ this._blurComputerNameTextbox();
+ this._toggleComputerNameControls(false);
+ this._updateComputerNameValue(true);
+ this._focusAfterComputerNameTextbox();
+ });
+ setEventListener("fxaDeviceNameChangeDeviceName", "click", function () {
+ this._toggleComputerNameControls(true);
+ this._focusComputerNameTextbox();
+ });
+ setEventListener("syncShowSyncedSyncNow", "click", function () {
+ // syncing can take a little time to send the "started" notification, so
+ // pretend we already got it.
+ this._updateSyncNow(true);
+ Weave.Service.sync({ why: "aboutprefs" });
+ });
+ setEventListener("enginesLearnMore", "click", function (event) {
+ // TODO: A real page.
+ window.browsingContext.topChromeWindow.openContentTab(
+ "https://example.org/?page=learnMore"
+ );
+ event.preventDefault();
+ // Stop attempts to open this link in an external browser.
+ event.stopPropagation();
+ });
+ setEventListener("syncChangeOptions", "click", function () {
+ gSyncPane._chooseWhatToSync(true);
+ });
+ setEventListener("syncSetup", "click", function () {
+ gSyncPane._chooseWhatToSync(false);
+ });
+ },
+
+ async _chooseWhatToSync(isAlreadySyncing) {
+ // Assuming another device is syncing and we're not, we update the engines
+ // selection so the correct checkboxes are pre-filled.
+ if (!isAlreadySyncing) {
+ try {
+ await Weave.Service.updateLocalEnginesState();
+ } catch (err) {
+ console.error("Error updating the local engines state", err);
+ }
+ }
+ let params = {};
+ if (isAlreadySyncing) {
+ // If we are already syncing then we also offer to disconnect.
+ params.disconnectFun = () => this.disconnectSync();
+ }
+ gSubDialog.open(
+ "chrome://messenger/content/preferences/syncDialog.xhtml",
+ {
+ features: "resizable=no",
+ closingCallback: event => {
+ if (!isAlreadySyncing && event.detail.button == "accept") {
+ // We weren't syncing but the user has accepted the dialog - so we
+ // want to start!
+ fxAccounts.telemetry
+ .recordConnection(["sync"], "ui")
+ .then(() => {
+ return Weave.Service.configure();
+ })
+ .catch(err => {
+ console.error("Failed to enable sync", err);
+ });
+ }
+ },
+ },
+ params
+ );
+ },
+
+ _updateSyncNow(syncing) {
+ let button = document.getElementById("syncShowSyncedSyncNow");
+ if (syncing) {
+ document.l10n.setAttributes(button, "sync-panel-sync-now-syncing");
+ button.disabled = true;
+ } else {
+ document.l10n.setAttributes(button, "sync-pane-sync-now");
+ button.disabled = false;
+ }
+ },
+
+ /**
+ * If connecting to Firefox Accounts failed, try again.
+ */
+ async reSignIn() {
+ // There's a bit of an edge-case here - we might be forcing reauth when we've
+ // lost the FxA account data - in which case we'll not get a URL as the re-auth
+ // URL embeds account info and the server endpoint complains if we don't
+ // supply it - so we just use the regular "sign in" URL in that case.
+ if (!(await FxAccounts.canConnectAccount())) {
+ return;
+ }
+
+ const url =
+ (await FxAccounts.config.promiseForceSigninURI("preferences")) ||
+ (await FxAccounts.config.promiseConnectAccountURI("preferences"));
+ window.browsingContext.topChromeWindow.openContentTab(url);
+ },
+
+ /**
+ * Send a confirmation email to the account's email address.
+ */
+ verifyFirefoxAccount() {
+ let onError = async () => {
+ let [title, body] = await document.l10n.formatValues([
+ "fxa-verification-not-sent-title",
+ "fxa-verification-not-sent-body",
+ ]);
+ new Notification(title, { body });
+ };
+
+ let onSuccess = async data => {
+ if (data) {
+ let [title, body] = await document.l10n.formatValues([
+ "fxa-verification-sent-title",
+ { id: "fxa-verification-sent-body", args: { userEmail: data.email } },
+ ]);
+ new Notification(title, { body });
+ } else {
+ onError();
+ }
+ };
+
+ fxAccounts
+ .resendVerificationEmail()
+ .then(() => fxAccounts.getSignedInUser(), onError)
+ .then(onSuccess, onError);
+ },
+
+ /**
+ * Disconnect the account, including everything linked.
+ *
+ * @param {boolean} confirm - If true, asks the user if they're sure.
+ */
+ unlinkFirefoxAccount(confirm) {
+ window.browsingContext.topChromeWindow.gSync.disconnect({ confirm });
+ },
+
+ /**
+ * Disconnect sync, leaving the FxA account connected.
+ */
+ disconnectSync() {
+ return window.browsingContext.topChromeWindow.gSync.disconnect({
+ confirm: true,
+ disconnectAccount: false,
+ });
+ },
+
+ _populateComputerName(value) {
+ let textbox = document.getElementById("fxaDeviceNameInput");
+ if (!textbox.hasAttribute("placeholder")) {
+ textbox.setAttribute(
+ "placeholder",
+ fxAccounts.device.getDefaultLocalName()
+ );
+ }
+ textbox.value = value;
+ },
+
+ /**
+ * Arranges to dynamically show or hide sync engine name elements based on
+ * the preferences used for the engines.
+ */
+ setupEnginesUI() {
+ let observe = (element, prefName) => {
+ element.hidden = !Services.prefs.getBoolPref(prefName, false);
+ };
+
+ let engineItems = {
+ showSyncAccount: "services.sync.engine.accounts",
+ showSyncIdentity: "services.sync.engine.identities",
+ showSyncAddress: "services.sync.engine.addressbooks",
+ showSyncCalendar: "services.sync.engine.calendars",
+ showSyncPasswords: "services.sync.engine.passwords",
+ };
+
+ for (let [id, prefName] of Object.entries(engineItems)) {
+ let obs = observe.bind(null, document.getElementById(id), prefName);
+ obs();
+ Services.prefs.addObserver(prefName, obs);
+ window.addEventListener("unload", () => {
+ Services.prefs.removeObserver(prefName, obs);
+ });
+ }
+ },
+};