Bug 1440499 - Implement the payerName/payerEmail/payerPhone contact picker
MozReview-Commit-ID: GJbAT1ABlXd
--- a/toolkit/components/payments/content/paymentDialogWrapper.js
+++ b/toolkit/components/payments/content/paymentDialogWrapper.js
@@ -42,16 +42,43 @@ var paymentDialogWrapper = {
Ci.nsIObserver,
Ci.nsISupportsWeakReference,
]),
/**
* Note: This method is async because profileStorage plans to become async.
*
* @param {string} guid
+ * @returns {object} containing only the requested payer values.
+ */
+ async _convertProfileAddressToPayerData(guid) {
+ let addressData = profileStorage.addresses.get(guid);
+ if (!addressData) {
+ throw new Error(`Payer address not found: ${guid}`);
+ }
+
+ let {
+ requestPayerName,
+ requestPayerEmail,
+ requestPayerPhone,
+ } = this.request.paymentOptions;
+
+ let payerData = {
+ payerName: requestPayerName ? addressData.name : "",
+ payerEmail: requestPayerEmail ? addressData.email : "",
+ payerPhone: requestPayerPhone ? addressData.tel : "",
+ };
+
+ return payerData;
+ },
+
+ /**
+ * Note: This method is async because profileStorage plans to become async.
+ *
+ * @param {string} guid
* @returns {nsIPaymentAddress}
*/
async _convertProfileAddressToPaymentAddress(guid) {
let addressData = profileStorage.addresses.get(guid);
if (!addressData) {
throw new Error(`Shipping address not found: ${guid}`);
}
@@ -373,32 +400,42 @@ var paymentDialogWrapper = {
const showResponse = this.createShowResponse({
acceptStatus: Ci.nsIPaymentActionResponse.PAYMENT_REJECTED,
});
paymentSrv.respondPayment(showResponse);
window.close();
},
async onPay({
+ selectedPayerAddressGUID: payerGUID,
selectedPaymentCardGUID: paymentCardGUID,
selectedPaymentCardSecurityCode: cardSecurityCode,
}) {
let methodData = await this._convertProfileBasicCardToPaymentMethodData(paymentCardGUID,
cardSecurityCode);
if (!methodData) {
// TODO (Bug 1429265/Bug 1429205): Handle when a user hits cancel on the
// Master Password dialog.
Cu.reportError("Bug 1429265/Bug 1429205: User canceled master password entry");
return;
}
+ let {
+ payerName,
+ payerEmail,
+ payerPhone,
+ } = await this._convertProfileAddressToPayerData(payerGUID);
+
this.pay({
methodName: "basic-card",
methodData,
+ payerName,
+ payerEmail,
+ payerPhone,
});
},
pay({
payerName,
payerEmail,
payerPhone,
methodName,
--- a/toolkit/components/payments/res/components/address-option.css
+++ b/toolkit/components/payments/res/components/address-option.css
@@ -12,16 +12,22 @@ address-option {
rich-select[open] > .rich-select-popup-box > address-option {
grid-template-areas:
"name name "
"street-address street-address"
"email tel ";
}
+address-option[hide-address] {
+ grid-template-areas:
+ "name name"
+ "tel email";
+}
+
address-option > .name {
grid-area: name;
}
address-option > .street-address {
grid-area: street-address;
}
@@ -35,12 +41,13 @@ address-option > .tel {
address-option > .name,
address-option > .street-address,
address-option > .email,
address-option > .tel {
white-space: nowrap;
}
-.rich-select-selected-clone > .email,
-.rich-select-selected-clone > .tel {
+address-option[hide-address] > .street-address,
+.rich-select-selected-clone:not([hide-address]) > .email,
+.rich-select-selected-clone:not([hide-address]) > .tel {
display: none;
}
--- a/toolkit/components/payments/res/components/address-option.js
+++ b/toolkit/components/payments/res/components/address-option.js
@@ -21,16 +21,17 @@
/* global ObservedPropertiesMixin, RichOption */
class AddressOption extends ObservedPropertiesMixin(RichOption) {
static get observedAttributes() {
return RichOption.observedAttributes.concat([
"address-level1",
"address-level2",
"country",
+ "hide-address",
"email",
"guid",
"name",
"postal-code",
"street-address",
"tel",
]);
}
--- a/toolkit/components/payments/res/containers/address-picker.js
+++ b/toolkit/components/payments/res/containers/address-picker.js
@@ -21,26 +21,28 @@ class AddressPicker extends PaymentState
connectedCallback() {
this.appendChild(this.dropdown);
super.connectedCallback();
}
render(state) {
let {savedAddresses} = state;
+ let hideAddress = this.hasAttribute("hide-address"); // TODO
let desiredOptions = [];
for (let [guid, address] of Object.entries(savedAddresses)) {
let optionEl = this.dropdown.getOptionByValue(guid);
if (!optionEl) {
optionEl = document.createElement("address-option");
optionEl.value = guid;
}
for (let [key, val] of Object.entries(address)) {
optionEl.setAttribute(key, val);
}
+ optionEl.hideAddress = hideAddress;
desiredOptions.push(optionEl);
}
let el = null;
while ((el = this.dropdown.popupBox.querySelector(":scope > address-option"))) {
el.remove();
}
for (let option of desiredOptions) {
this.dropdown.popupBox.appendChild(option);
--- a/toolkit/components/payments/res/containers/payment-dialog.js
+++ b/toolkit/components/payments/res/containers/payment-dialog.js
@@ -28,16 +28,17 @@ class PaymentDialog extends PaymentState
this._payButton.addEventListener("click", this);
this._viewAllButton = contents.querySelector("#view-all");
this._viewAllButton.addEventListener("click", this);
this._orderDetailsOverlay = contents.querySelector("#order-details-overlay");
this._shippingTypeLabel = contents.querySelector("#shipping-type-label");
this._shippingRelatedEls = contents.querySelectorAll(".shipping-related");
+ this._payerRelatedEls = contents.querySelectorAll(".payer-related");
this._errorText = contents.querySelector("#error-text");
this._disabledOverlay = contents.getElementById("disabled-overlay");
this.appendChild(contents);
super.connectedCallback();
}
@@ -64,21 +65,23 @@ class PaymentDialog extends PaymentState
}
cancelRequest() {
paymentRequest.cancel();
}
pay() {
let {
+ selectedPayerAddress,
selectedPaymentCard,
selectedPaymentCardSecurityCode,
} = this.requestStore.getState();
paymentRequest.pay({
+ selectedPayerAddressGUID: selectedPayerAddress,
selectedPaymentCardGUID: selectedPaymentCard,
selectedPaymentCardSecurityCode,
});
}
changeShippingAddress(shippingAddressGUID) {
paymentRequest.changeShippingAddress({
shippingAddressGUID,
@@ -101,16 +104,17 @@ class PaymentDialog extends PaymentState
setStateFromParent(state) {
this.requestStore.setState(state);
// Check if any foreign-key constraints were invalidated.
state = this.requestStore.getState();
let {
savedAddresses,
savedBasicCards,
+ selectedPayerAddress,
selectedPaymentCard,
selectedShippingAddress,
selectedShippingOption,
} = state;
let shippingOptions = state.request.paymentDetails.shippingOptions;
// Ensure `selectedShippingAddress` never refers to a deleted address and refers
// to an address if one exists.
@@ -143,16 +147,26 @@ class PaymentDialog extends PaymentState
if (!selectedShippingOption && shippingOptions.length) {
selectedShippingOption = shippingOptions[0].id;
}
this._cachedState.selectedShippingOption = selectedShippingOption;
this.requestStore.setState({
selectedShippingOption,
});
}
+
+
+ // Ensure `selectedPayerAddress` never refers to a deleted address and refers
+ // to an address if one exists.
+ if (!savedAddresses[selectedPayerAddress]) {
+ this.requestStore.setState({
+ selectedPayerAddress: Object.keys(savedAddresses)[0] || null,
+ });
+ }
+
}
_renderPayButton(state) {
this._payButton.disabled = state.changesPrevented;
switch (state.completionState) {
case "initial":
case "processing":
break;
@@ -193,16 +207,20 @@ class PaymentDialog extends PaymentState
totalAmountEl.currency = totalItem.amount.currency;
this._orderDetailsOverlay.hidden = !state.orderDetailsShowing;
this._errorText.textContent = paymentDetails.error;
let paymentOptions = request.paymentOptions;
for (let element of this._shippingRelatedEls) {
element.hidden = !paymentOptions.requestShipping;
}
+ let payerRequested = paymentOptions.requestPayerName || paymentOptions.requestPayerEmail || paymentOptions.requestPayerPhone;
+ for (let element of this._payerRelatedEls) {
+ element.hidden = !payerRequested;
+ }
let shippingType = paymentOptions.shippingType || "shipping";
this._shippingTypeLabel.querySelector("label").textContent =
this._shippingTypeLabel.dataset[shippingType + "AddressLabel"];
this._renderPayButton(state);
let {
changesPrevented,
--- a/toolkit/components/payments/res/debugging.js
+++ b/toolkit/components/payments/res/debugging.js
@@ -142,16 +142,17 @@ let REQUEST_CONTACT_NO_SHIPPING = {
},
};
let ADDRESSES_1 = {
"48bnds6854t": {
"address-level1": "MI",
"address-level2": "Some City",
"country": "US",
+ "email": "foo@bar.com",
"guid": "48bnds6854t",
"name": "Mr. Foo",
"postal-code": "90210",
"street-address": "123 Sesame Street,\nApt 40",
"tel": "+1 519 555-5555",
},
"68gjdh354j": {
"address-level1": "CA",
--- a/toolkit/components/payments/res/mixins/PaymentStateSubscriberMixin.js
+++ b/toolkit/components/payments/res/mixins/PaymentStateSubscriberMixin.js
@@ -33,16 +33,17 @@ let requestStore = new PaymentsStore({
paymentOptions: {
requestPayerName: false,
requestPayerEmail: false,
requestPayerPhone: false,
requestShipping: false,
shippingType: "shipping",
},
},
+ selectedPayerAddress: null,
selectedPaymentCard: null,
selectedPaymentCardSecurityCode: null,
selectedShippingAddress: null,
selectedShippingOption: null,
savedAddresses: {},
savedBasicCards: {},
});
--- a/toolkit/components/payments/res/paymentRequest.xhtml
+++ b/toolkit/components/payments/res/paymentRequest.xhtml
@@ -5,16 +5,17 @@
<!DOCTYPE html [
<!ENTITY viewAllItems "View All Items">
<!ENTITY paymentSummaryTitle "Your Payment">
<!ENTITY shippingAddressLabel "Shipping Address">
<!ENTITY deliveryAddressLabel "Delivery Address">
<!ENTITY pickupAddressLabel "Pickup Address">
<!ENTITY shippingOptionsLabel "Shipping Options">
<!ENTITY paymentMethodsLabel "Payment Method">
+ <!ENTITY payerLabel "Contact Information">
<!ENTITY cancelPaymentButton.label "Cancel">
<!ENTITY approvePaymentButton.label "Pay">
<!ENTITY processingPaymentButton.label "Processing">
<!ENTITY orderDetailsLabel "Order Details">
<!ENTITY orderTotalLabel "Total">
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
@@ -62,27 +63,33 @@
</div>
</header>
<div id="main-container">
<section id="payment-summary">
<h1>&paymentSummaryTitle;</h1>
<section>
+ <div id="error-text"></div>
+
<div class="shipping-related"
id="shipping-type-label"
data-shipping-address-label="&shippingAddressLabel;"
data-delivery-address-label="&deliveryAddressLabel;"
data-pickup-address-label="&pickupAddressLabel;"><label></label></div>
<address-picker class="shipping-related" selected-state-key="selectedShippingAddress"></address-picker>
<div class="shipping-related"><label>&shippingOptionsLabel;</label></div>
<shipping-option-picker class="shipping-related"></shipping-option-picker>
<div><label>&paymentMethodsLabel;</label></div>
<payment-method-picker selected-state-key="selectedPaymentCard"></payment-method-picker>
- <div id="error-text"></div>
+
+ <div class="payer-related"><label>&payerLabel;</label></div>
+ <address-picker class="payer-related"
+ hide-address=""
+ selected-state-key="selectedPayerAddress"></address-picker>
</section>
<footer id="controls-container">
<button id="cancel">&cancelPaymentButton.label;</button>
<button id="pay"
data-initial-label="&approvePaymentButton.label;"
data-processing-label="&processingPaymentButton.label;"></button>
</footer>