Skip to content
This repository has been archived by the owner on Mar 28, 2023. It is now read-only.

Commit

Permalink
Merge pull request #1402 from OpenBazaar/retainShippingOptionOnRefresh
Browse files Browse the repository at this point in the history
Retain shipping option on refresh
  • Loading branch information
rmisio committed Jun 19, 2018
2 parents e8c9ca8 + 47e25ec commit 60698f6
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 89 deletions.
2 changes: 1 addition & 1 deletion js/templates/modals/purchase/shipping.html
Expand Up @@ -8,7 +8,7 @@ <h2 class="h4 required flexExpand"><%= ob.polyT('purchase.shippingTitle') %></h2
<% if (ob.userAddresses.length) { %>
<select id="shippingAddress">
<% ob.userAddresses.forEach((a, i) => { %>
<option value="<%= i %>">
<option value="<%= i %>" <% if (ob.selectedAddressIndex === i) print('selected') %>>
<% const addr = [];
addr.push(a.name);
if (a.company) addr.push(a.company);
Expand Down
47 changes: 23 additions & 24 deletions js/templates/modals/purchase/shippingOptions.html
@@ -1,30 +1,29 @@
<% if (ob.validShippingOptions.length) { %>
<% if (ob.validOptions.length) { %>
<div class="flexColRows boxList border borderStacked clrP clrBr">
<% ob.validShippingOptions.forEach((option, i) => {
option.services.forEach((service, j) => { %>
<div>
<div class="btnRadio width100">
<input type="radio"
class="js-shippingOption"
id="<%= `${i}${j}` %>"
<% if (i===0 && j === 0) print('checked') %>
data-name="<%= option.name %>"
data-type="<%= option.type %>"
data-service="<%= service.name %>"
name="shippingOption">
<label for="<%= `${i}${j}` %>" class="flex gutterH pad">
<div>
<div class="tx5 rowSm txB"><%= option.name %>: <%= service.name %></div>
<div class="tx5b clrT2 txUnb">
<% const price = ob.currencyMod.convertAndFormatCurrency(service.price, ob.metadata.pricingCurrency, ob.displayCurrency) %>
<% if (service.estimatedDelivery) print(ob.polyT('purchase.serviceDetails', { price, delivery: service.estimatedDelivery })) %>
</div>
<% ob.validOptions.forEach((service, i) => {
const selected = ob.selectedOption && ob.selectedOption.name === service.name && ob.selectedOption.service === service.service;
%>
<div>
<div class="btnRadio width100">
<input type="radio"
class="js-shippingOption"
id="<%= `option${i}` %>"
<% if (selected) print('checked') %>
data-name="<%= service.name %>"
data-service="<%= service.service%>"
name="shippingOption">
<label for="<%= `option${i}` %>" class="flex gutterH pad">
<div>
<div class="tx5 rowSm txB"><%= service.name %>: <%= service.service %></div>
<div class="tx5b clrT2 txUnb">
<% const price = ob.currencyMod.convertAndFormatCurrency(service.price, ob.metadata.pricingCurrency, ob.displayCurrency) %>
<% if (service.estimatedDelivery) print(ob.polyT('purchase.serviceDetails', { price, delivery: service.estimatedDelivery })) %>
</div>
</label>
</div>
</div>
</label>
</div>
<% }); %>
<% }); %>
</div>
<% }); %>
</div>
<% } else { %>
<div class="padGi flexCent">
Expand Down
139 changes: 115 additions & 24 deletions js/views/modals/purchase/Shipping.js
@@ -1,10 +1,12 @@
import $ from 'jquery';
import _ from 'underscore';
import '../../../lib/select2';
import app from '../../../app';
import loadTemplate from '../../../utils/loadTemplate';
import baseView from '../../baseVw';
import Listing from '../../../models/listing/Listing';
import ShippingOptions from './ShippingOptions';
import ShippingAddress from '../../../models/settings/ShippingAddress';

export default class extends baseView {
constructor(options = {}) {
Expand All @@ -14,16 +16,22 @@ export default class extends baseView {
if (!this.model || !(this.model instanceof Listing)) {
throw new Error('Please provide a listing model');
}
this.shippingOptions = this.createChild(ShippingOptions, {
model: this.model,
});

this.listenTo(this.shippingOptions, 'shippingOptionSelected', ((opts) => {
this.trigger('shippingOptionSelected', opts);
}));
this.listenTo(app.settings.get('shippingAddresses'), 'update', (col) => {
// if all the addresses were deleted, update with blank values
if (!col.models.length) this.trigger('shippingOptionSelected', { name: '', service: '' });
this.validOptions = [];

const userAddresses = app.settings.get('shippingAddresses');
this.selectedAddress = userAddresses.at(0) || '';

this.listenTo(userAddresses, 'update', col => {
// If all the addresses were deleted, set the selection to blank.
if (!col.models.length) {
this.selectedAddress = '';
} else {
// If the old selected address doesn't exist any more, select the first address and set the
// selection to the first valid value.
this.selectedAddress = userAddresses.get(this.selectedAddress) ?
this.selectedAddress : userAddresses.at(0);
}
this.render();
});
}
Expand All @@ -38,39 +46,122 @@ export default class extends baseView {
};
}

get selectedOption() {
return this._selectedOption;
}

set selectedOption(opts) {
if (!_.isEqual(this._selectedOption, opts)) {
this._selectedOption = opts;
this.trigger('shippingOptionSelected', opts);
}
}

extractValidOptions(address) {
// Any time the country is changed, the options valid for that country need to be extracted.
if (address !== '' && !(address instanceof ShippingAddress)) {
throw new Error('The address must be blank or an instance of the ShippingAddress model.');
}

const validOptions = [];
const countryCode = address ? address.get('country') : '';

const extractedOptions = this.model.get('shippingOptions').toJSON().filter(option =>
option.regions.includes(countryCode) || option.regions.includes('ALL'));

extractedOptions.forEach(option => {
if (option.type === 'LOCAL_PICKUP') {
// local pickup options need a service with a name and price
option.services[0] = { name: app.polyglot.t('purchase.localPickup'), price: 0 };
}
option.services = _.sortBy(option.services, 'price');
option.services.forEach(optionService => {
validOptions.push({
...optionService,
name: option.name,
service: optionService.name,
});
});
});

return validOptions;
}

get selectedAddress() {
return this._selectedAddress;
}

set selectedAddress(address) {
if (this._selectedAddress && _.isEqual(this._selectedAddress, address)) return;

let validOptions = [];

// if the selected address has a new country, extract the valid shipping options.
if (address && address.get('country') !== this.country) {
validOptions = this.extractValidOptions(address);
}

this._selectedAddress = address;

if (validOptions.length) {
// If the previously selected shipping option is no longer valid, select the first valid
// shipping option. this.selectionOption only has a name and service, as that's the expected
// data for the server, the validOptions have additional data in them.
const isSelectedValid = this.selectedOption && this.selectedOption.name &&
!!validOptions.filter(option => option.name === this.selectedOption.name &&
option.service === this.selectedOption.service).length;

if (!isSelectedValid) {
this.selectedOption = {
name: validOptions[0].name,
service: validOptions[0].service,
};
}
} else {
this.selectedOption = { name: '', service: '' };
}

if (this.shippingOptions) {
this.shippingOptions.validOptions = validOptions;
this.shippingOptions.selectedOption = this.selectedOption;
this.shippingOptions.render();
}

this.validOptions = validOptions;
}

changeShippingAddress(e) {
const index = $(e.target).val();
this.selectedAddress = app.settings.get('shippingAddresses').at(index);
const code = this.selectedAddress.get('country');
// if an address with the same country is chosen, don't re-render the options
if (code !== this.countryCode) {
this.countryCode = code;
this.shippingOptions.countryCode = code;
this.shippingOptions.render();
}
}

render() {
const userAddresses = app.settings.get('shippingAddresses');

const selectedAddressIndex = this.selectedAddress && userAddresses.length ?
userAddresses.indexOf(this.selectedAddress) : '';

loadTemplate('modals/purchase/shipping.html', t => {
this.$el.html(t({
userAddresses: userAddresses.toJSON(),
selectedAddressIndex,
}));
});
this.$('#shippingAddress').select2({
// disables the search box
minimumResultsForSearch: Infinity,
});

if (userAddresses.length) {
this.selectedAddress = userAddresses.at(0);
this.shippingOptions.countryCode = this.selectedAddress.get('country');
this.shippingOptions.delegateEvents();
this.$('.js-shippingOptionsWrapper').html(this.shippingOptions.render().el);
} else {
this.selectedAddress = null;
}
if (this.shippingOptions) this.shippingOptions.remove();
this.shippingOptions = this.createChild(ShippingOptions, {
model: this.model,
validOptions: this.validOptions,
selectedOption: this.selectedOption,
});
this.listenTo(this.shippingOptions, 'shippingOptionSelected',
opts => (this.selectedOption = opts));

this.$('.js-shippingOptionsWrapper').append(this.shippingOptions.render().el);

return this;
}
Expand Down
53 changes: 13 additions & 40 deletions js/views/modals/purchase/ShippingOptions.js
@@ -1,5 +1,4 @@
import $ from 'jquery';
import _ from 'underscore';
import app from '../../../app';
import '../../../lib/select2';
import loadTemplate from '../../../utils/loadTemplate';
Expand All @@ -11,11 +10,12 @@ export default class extends baseView {
super(options);
this.options = options;

this._countryCode = options.countryCode || '';

if (!this.model || !(this.model instanceof Listing)) {
throw new Error('Please provide a listing model');
}

this._selectedOption = options.selectedOption || {};
this.validOptions = options.validOptions || [];
}

className() {
Expand All @@ -24,56 +24,29 @@ export default class extends baseView {

events() {
return {
'click .js-shippingOption': 'changeShippingOption',
'click .js-shippingOption': 'onSelectShippingOption',
};
}

changeShippingOption(e) {
const option = $(e.target);
const sOpts = {};
sOpts.name = option.attr('data-name');
if (option.attr('data-type') !== 'LOCAL_PICKUP') {
sOpts.service = option.attr('data-service');
}
this.trigger('shippingOptionSelected', sOpts);
get selectedOption() {
return this._selectedOption;
}

get countryCode() {
return this._countryCode;
set selectedOption(opts) {
this._selectedOption = opts;
this.trigger('shippingOptionSelected', opts);
}

set countryCode(code) {
this._countryCode = code;
onSelectShippingOption(e) {
this.selectedOption = $(e.target).data();
}

render() {
const validShippingOptions = this.model.get('shippingOptions').toJSON().filter((option) =>
option.regions.indexOf(this.countryCode) !== -1 || option.regions.indexOf('ALL') !== -1);

if (validShippingOptions.length) {
validShippingOptions.forEach(option => {
if (option.type === 'LOCAL_PICKUP') {
// local pickup options need a name and price
option.services[0] = { name: app.polyglot.t('purchase.localPickup'), price: 0 };
}
option.services = _.sortBy(option.services, 'price');
});

const sOpts = {};
sOpts.name = validShippingOptions[0].name;
if (validShippingOptions[0].type !== 'LOCAL_PICKUP') {
sOpts.service = validShippingOptions[0].services[0].name;
}
this.trigger('shippingOptionSelected', sOpts);
} else {
// if no valid option is available, set it to blank
this.trigger('shippingOptionSelected', { name: '', service: '' });
}

loadTemplate('modals/purchase/shippingOptions.html', t => {
this.$el.html(t({
...this.model.toJSON(),
validShippingOptions,
validOptions: this.validOptions,
selectedOption: this.selectedOption,
displayCurrency: app.settings.get('localCurrency'),
}));
});
Expand Down

0 comments on commit 60698f6

Please sign in to comment.