Bug 1139677 - Display the user's FxA profile image in the Sync Pref pane r=markh,rfeeley

This commit is contained in:
Zachary Carter 2015-03-27 02:37:55 -07:00
parent 1511bd4c8e
commit 012fd3ed00
9 changed files with 145 additions and 42 deletions

View File

@ -95,7 +95,9 @@ let gSyncPane = {
"weave:service:start-over:finish",
"weave:service:setup-complete",
"weave:service:logout:finish",
FxAccountsCommon.ONVERIFIED_NOTIFICATION];
FxAccountsCommon.ONVERIFIED_NOTIFICATION,
FxAccountsCommon.ON_PROFILE_CHANGE_NOTIFICATION,
];
let migrateTopic = "fxa-migration:state-changed";
// Add the observers now and remove them on unload
@ -123,6 +125,8 @@ let gSyncPane = {
}),
this.updateWeavePrefs();
this._initProfileImageUI();
},
_setupEventListeners: function() {
@ -224,6 +228,14 @@ let gSyncPane = {
});
},
_initProfileImageUI: function () {
try {
if (Services.prefs.getBoolPref("identity.fxaccounts.profile_image.enabled")) {
document.getElementById("fxaProfileImage").hidden = false;
}
} catch (e) { }
},
updateWeavePrefs: function () {
// ask the migration module to broadcast its current state (and nothing will
// happen if it's not loaded - which is good, as that means no migration
@ -244,10 +256,11 @@ let gSyncPane = {
}
// determine the fxa status...
this.page = PAGE_PLEASE_WAIT;
fxAccounts.getSignedInUser().then(data => {
if (!data) {
this.page = FXA_PAGE_LOGGED_OUT;
return;
return false;
}
this.page = FXA_PAGE_LOGGED_IN;
// We are logged in locally, but maybe we are in a state where the
@ -281,7 +294,36 @@ let gSyncPane = {
for (let checkbox of engines.querySelectorAll("checkbox")) {
checkbox.disabled = enginesListDisabled;
}
// Clear the profile image (if any) of the previously logged in account.
document.getElementById("fxaProfileImage").style.removeProperty("background-image");
// If the account is verified the next promise in the chain will
// fetch profile data.
return data.verified;
}).then(shouldGetProfile => {
if (shouldGetProfile) {
return fxAccounts.getSignedInUserProfile();
}
}).then(data => {
if (data && data.avatar) {
// Make sure the image is available before displaying it,
// as we don't want to overwrite the default profile image
// with a broken/unavailable image
let img = new Image();
img.onload = () => {
let bgImage = "url('" + data.avatar + "')";
document.getElementById("fxaProfileImage").style.backgroundImage = bgImage;
};
img.src = data.avatar;
}
}, err => {
FxAccountsCommon.log.error(err);
}).catch(err => {
// If we get here something's really busted
Cu.reportError(String(err));
});
// If fxAccountEnabled is false and we are in a "not configured" state,
// then fxAccounts is probably fully disabled rather than just unconfigured,
// so handle this case. This block can be removed once we remove support
@ -521,6 +563,15 @@ let gSyncPane = {
});
},
openChangeProfileImage: function() {
fxAccounts.promiseAccountsChangeProfileURI("avatar")
.then(url => {
this.openContentInBrowser(url, {
replaceQueryString: true
});
});
},
manageFirefoxAccount: function() {
let url = Services.prefs.getCharPref("identity.fxaccounts.settings.uri");
this.openContentInBrowser(url);

View File

@ -233,7 +233,11 @@
<!-- logged in and verified and all is good -->
<hbox id="fxaLoginVerified"
align="center">
<label id="fxaEmailAddress1"/>
<hbox align="center">
<image id="fxaProfileImage"
onclick="gSyncPane.openChangeProfileImage();" hidden="true"/>
<label id="fxaEmailAddress1"/>
</hbox>
<spacer flex="1"/>
<button id="verifiedManage"
label="&manage.label;"/>

View File

@ -179,6 +179,7 @@ browser.jar:
skin/classic/browser/preferences/in-content/favicon.ico (../shared/incontentprefs/favicon.ico)
skin/classic/browser/preferences/in-content/icons.svg (../shared/incontentprefs/icons.svg)
skin/classic/browser/preferences/in-content/search.css (../shared/incontentprefs/search.css)
skin/classic/browser/preferences/in-content/default-profile-image.svg (../shared/incontentprefs/default-profile-image.svg)
skin/classic/browser/preferences/applications.css (preferences/applications.css)
skin/classic/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css)
skin/classic/browser/preferences/search.css (preferences/search.css)

View File

@ -284,6 +284,7 @@ browser.jar:
skin/classic/browser/preferences/in-content/favicon.ico (../shared/incontentprefs/favicon.ico)
skin/classic/browser/preferences/in-content/icons.svg (../shared/incontentprefs/icons.svg)
skin/classic/browser/preferences/in-content/search.css (../shared/incontentprefs/search.css)
skin/classic/browser/preferences/in-content/default-profile-image.svg (../shared/incontentprefs/default-profile-image.svg)
skin/classic/browser/preferences/applications.css (preferences/applications.css)
skin/classic/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css)
skin/classic/browser/preferences/search.css (preferences/search.css)

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
<path fill="#C3CFD8" d="M500-0.3c276.1,0,500,223.9,500,500s-223.9,500-500,500S0,775.8,0,499.7C0,223.5,223.9-0.3,500-0.3z"/>
<circle fill="#FFFFFF" cx="500" cy="317" r="139.1"/>
<path fill="#FFFFFF" d="M751.8,643.6L751.8,643.6c0.1-2.3,0.2-4.6,0.2-6.9c0-68-55.3-127-136.2-156.3L505.9,590.4h0
c-0.4,29.8-1.4,58.8-2.8,86.6c-1,0.1-2,0.3-3.1,0.3s-2-0.2-3.1-0.3c-1.4-27.9-2.4-56.9-2.8-86.7h0L384.3,480.4
C303.3,509.7,248,568.7,248,636.7c0,2.3,0.1,4.6,0.2,6.9l7.4,49.7c57.1,72,145.4,118.2,244.4,118.2c99,0,187.3-46.2,244.4-118.2
L751.8,643.6z"/>
</svg>

After

Width:  |  Height:  |  Size: 993 B

View File

@ -216,6 +216,22 @@ description > html|a {
cursor: pointer;
}
#fxaProfileImage {
width: 60px;
height: 60px;
border-radius: 50%;
border-width: 5px;
border-color: red;
background-image: url(chrome://browser/skin/preferences/in-content/default-profile-image.svg);
background-size: contain;
cursor: pointer;
-moz-margin-end: 15px;
}
#fxaProfileImage:hover {
border-color: blue;
}
#noFxaAccount {
/* Overriding the margins from the base preferences.css theme file.
These overrides can be simplified by fixing bug 1027174 */

View File

@ -207,6 +207,7 @@ browser.jar:
skin/classic/browser/preferences/in-content/favicon.ico (../shared/incontentprefs/favicon.ico)
skin/classic/browser/preferences/in-content/icons.svg (../shared/incontentprefs/icons.svg)
skin/classic/browser/preferences/in-content/search.css (../shared/incontentprefs/search.css)
skin/classic/browser/preferences/in-content/default-profile-image.svg (../shared/incontentprefs/default-profile-image.svg)
skin/classic/browser/preferences/applications.css (preferences/applications.css)
skin/classic/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css)
skin/classic/browser/preferences/search.css (preferences/search.css)

View File

@ -44,6 +44,7 @@ let publicProperties = [
"localtimeOffsetMsec",
"now",
"promiseAccountsForceSigninURI",
"promiseAccountsChangeProfileURI",
"resendVerificationEmail",
"setSignedInUser",
"signOut",
@ -961,6 +962,34 @@ FxAccountsInternal.prototype = {
}).then(result => currentState.resolve(result));
},
// Returns a promise that resolves with the URL to use to change
// the current account's profile image.
// if settingToEdit is set, the profile page should hightlight that setting
// for the user to edit.
promiseAccountsChangeProfileURI: function(settingToEdit = null) {
let url = Services.urlFormatter.formatURLPref("identity.fxaccounts.settings.uri");
if (settingToEdit) {
url += (url.indexOf("?") == -1 ? "?" : "&") +
"setting=" + encodeURIComponent(settingToEdit);
}
if (this._requireHttps() && !/^https:/.test(url)) { // Comment to un-break emacs js-mode highlighting
throw new Error("Firefox Accounts server must use HTTPS");
}
let currentState = this.currentAccountState;
// but we need to append the email address onto a query string.
return this.getSignedInUser().then(accountData => {
if (!accountData) {
return null;
}
let newQueryPortion = url.indexOf("?") == -1 ? "?" : "&";
newQueryPortion += "email=" + encodeURIComponent(accountData.email);
newQueryPortion += "&uid=" + encodeURIComponent(accountData.uid);
return url + newQueryPortion;
}).then(result => currentState.resolve(result));
},
/**
* Get an OAuth token for the user
*
@ -1078,30 +1107,13 @@ FxAccountsInternal.prototype = {
getSignedInUserProfile: function () {
let accountState = this.currentAccountState;
return accountState.getProfile()
.then(
(profileData) => {
let profile = JSON.parse(JSON.stringify(profileData));
// profileData doesn't include "verified", but it must be true
// if we've gotten this far.
profile.verified = true;
return accountState.resolve(profile);
},
(error) => {
log.error("Could not retrieve profile data", error);
return this.getSignedInUser().then(data => {
let profile = null;
if (data) {
// If we fail to fetch the profile and have no profile cached
// we resort to using the account data for basic profile data.
profile = {
email: data.email,
uid: data.uid,
verified: data.verified
};
}
return accountState.resolve(profile);
});
.then((profileData) => {
let profile = JSON.parse(JSON.stringify(profileData));
return accountState.resolve(profile);
},
(error) => {
log.error("Could not retrieve profile data", error);
return accountState.reject(error);
})
.then(null, err => this._errorToErrorClass(err));
},

View File

@ -923,7 +923,6 @@ add_test(function test_getSignedInUserProfile_ok() {
fxa.getSignedInUserProfile()
.then(result => {
do_check_eq(result.avatar, "image");
do_check_true(result.verified);
run_next_test();
});
});
@ -946,9 +945,24 @@ add_test(function test_getSignedInUserProfile_error_uses_account_data() {
};
fxa.getSignedInUserProfile()
.then(result => {
do_check_eq(typeof result.avatar, "undefined");
do_check_eq(result.email, "foo@bar.com");
.catch(error => {
do_check_eq(error.message, "UNKNOWN_ERROR");
run_next_test();
});
});
});
add_test(function test_getSignedInUserProfile_unverified_account() {
let fxa = new MockFxAccounts();
let alice = getTestUser("alice");
fxa.setSignedInUser(alice).then(() => {
let accountState = fxa.internal.currentAccountState;
fxa.getSignedInUserProfile()
.catch(error => {
do_check_eq(error.message, "UNVERIFIED_ACCOUNT");
run_next_test();
});
});
@ -958,22 +972,13 @@ add_test(function test_getSignedInUserProfile_error_uses_account_data() {
add_test(function test_getSignedInUserProfile_no_account_data() {
let fxa = new MockFxAccounts();
fxa.internal.getSignedInUser = function () {
return Promise.resolve({ email: "foo@bar.com" });
};
let accountState = fxa.internal.currentAccountState;
accountState.getProfile = function () {
return Promise.reject("boom");
};
fxa.internal.getSignedInUser = function () {
return Promise.resolve(null);
};
fxa.getSignedInUserProfile()
.then(result => {
do_check_eq(result, null);
.catch(error => {
do_check_eq(error.message, "NO_ACCOUNT");
run_next_test();
});