diff --git a/client/components/data/query-geo/README.md b/client/components/data/query-geo/README.md deleted file mode 100644 index cb2418932506e..0000000000000 --- a/client/components/data/query-geo/README.md +++ /dev/null @@ -1,18 +0,0 @@ -Query Sites -=========== - -`` is a React component used in managing network requests for browser IP geolocation. - -## Usage - -Render the component. The component does not accept any children, nor does it render any of its own. - -```jsx -function MyComponent() { - return -} -``` - -## Props - -This component does not accept any props. diff --git a/client/components/data/query-geo/index.jsx b/client/components/data/query-geo/index.jsx deleted file mode 100644 index 5f243262c129d..0000000000000 --- a/client/components/data/query-geo/index.jsx +++ /dev/null @@ -1,36 +0,0 @@ -/** @format */ - -/** - * External dependencies - */ - -import PropTypes from 'prop-types'; -import { Component } from 'react'; -import { connect } from 'react-redux'; - -/** - * Internal dependencies - */ -import { isRequestingGeo } from 'state/geo/selectors'; -import { requestGeo } from 'state/geo/actions'; - -class QueryGeo extends Component { - componentWillMount() { - if ( ! this.props.requesting ) { - this.props.requestGeo(); - } - } - - render() { - return null; - } -} - -QueryGeo.propTypes = { - requesting: PropTypes.bool, - requestGeo: PropTypes.func, -}; - -export default connect( state => ( { requesting: isRequestingGeo( state ) } ), { requestGeo } )( - QueryGeo -); diff --git a/client/components/language-picker/index.jsx b/client/components/language-picker/index.jsx index 4cd0b1e275463..cf7f30957f002 100644 --- a/client/components/language-picker/index.jsx +++ b/client/components/language-picker/index.jsx @@ -14,10 +14,9 @@ import { find, isString, noop } from 'lodash'; /** * Internal dependencies */ -import { getGeoCountryShort } from 'state/geo/selectors'; -import QueryGeo from 'components/data/query-geo'; import LanguagePickerModal from './modal'; import QueryLanguageNames from 'components/data/query-language-names'; +import { requestGeoLocation } from 'state/data-layer/http-data/getters'; import { getLanguageCodeLabels } from './utils'; export class LanguagePicker extends PureComponent { @@ -174,13 +173,12 @@ export class LanguagePicker extends PureComponent { { this.renderModal( language.langSlug ) } - ); } } -export default connect( state => ( { - countryCode: getGeoCountryShort( state ), -} ) )( localize( LanguagePicker ) ); +export default connect( () => ( { countryCode: requestGeoLocation().data } ) )( + localize( LanguagePicker ) +); diff --git a/client/lib/abtest/README.md b/client/lib/abtest/README.md index b00c8ee6b6932..d120d69d7180d 100644 --- a/client/lib/abtest/README.md +++ b/client/lib/abtest/README.md @@ -68,7 +68,7 @@ Also, the user's variation is saved in local storage. You can see this in Chrome Here's another example with country targeting: ```jsx -const userCountryCode = getGeoCountryShort( state ); +const userCountryCode = requestGeoLocation().data; let buttonWording; if ( abtest( 'freeTrialButtonWordingForIndia', userCountryCode ) === 'startFreeTrial' ) { @@ -161,7 +161,7 @@ You would need to update [config/default.json](https://github.com/Automattic/wp- "knownABTestKeys": [ "freeTrialButtonWording" ] - + "overrideABTests": [ [ "freeTrialButtonWording_201502160", "startFreeTrial" ] ] diff --git a/client/my-sites/checkout/checkout/checkout.jsx b/client/my-sites/checkout/checkout/checkout.jsx index 2b2c01a4c2b09..665853d0d0f05 100644 --- a/client/my-sites/checkout/checkout/checkout.jsx +++ b/client/my-sites/checkout/checkout/checkout.jsx @@ -32,7 +32,6 @@ import { managePurchase } from 'me/purchases/paths'; import SubscriptionLengthPicker from 'blocks/subscription-length-picker'; import QueryContactDetailsCache from 'components/data/query-contact-details-cache'; import QueryStoredCards from 'components/data/query-stored-cards'; -import QueryGeo from 'components/data/query-geo'; import QuerySitePlans from 'components/data/query-site-plans'; import QueryPlans from 'components/data/query-plans'; import SecurePaymentForm from './secure-payment-form'; @@ -610,7 +609,6 @@ export class Checkout extends React.Component { - { this.content() } diff --git a/client/state/data-layer/http-data/getters.js b/client/state/data-layer/http-data/getters.js new file mode 100644 index 0000000000000..6b299505d237a --- /dev/null +++ b/client/state/data-layer/http-data/getters.js @@ -0,0 +1,38 @@ +/** @format */ +/** + * Internal dependencies + */ +import makeJsonSchemaParser from 'lib/make-json-schema-parser'; +import { http as rawHttp } from 'state/http/actions'; +import { requestHttpData } from 'state/data-layer/http-data/common'; + +export const requestGeoLocation = () => + requestHttpData( + 'geo', + rawHttp( { + method: 'GET', + url: 'https://public-api.wordpress.com/geo/', + } ), + { + fromApi: makeJsonSchemaParser( + { + type: 'object', + properties: { + body: { + type: [ 'object', 'null' ], + properties: { + latitude: { type: 'string' }, + longitude: { type: 'string' }, + country_short: { type: 'string' }, + country_long: { type: 'string' }, + region: { type: 'string' }, + city: { type: 'string' }, + }, + }, + }, + }, + // we only use the short code currently + ( { body: { country_short } } ) => [ [ 'geo', country_short ] ] + ), + } + ); diff --git a/client/state/data-layer/http-data/index.js b/client/state/data-layer/http-data/index.js index 6c3117eb59c61..d08303087006f 100644 --- a/client/state/data-layer/http-data/index.js +++ b/client/state/data-layer/http-data/index.js @@ -105,8 +105,10 @@ const parseResponse = ( data, fromApi ) => { }; const onSuccess = ( action, apiData ) => { - const fromApi = 'function' === typeof action.fromApi && action.fromApi(); - const [ error, data ] = fromApi ? parseResponse( apiData, fromApi ) : [ undefined, [] ]; + const [ error, data ] = + 'function' === typeof action.fromApi + ? parseResponse( apiData, action.fromApi ) + : [ undefined, [] ]; if ( undefined !== error ) { return onError( action, error ); diff --git a/client/state/geo/actions.js b/client/state/geo/actions.js deleted file mode 100644 index ea141ed27478b..0000000000000 --- a/client/state/geo/actions.js +++ /dev/null @@ -1,57 +0,0 @@ -/** @format */ - -/** - * Internal dependencies - */ - -import request from 'superagent'; -import { - GEO_RECEIVE, - GEO_REQUEST, - GEO_REQUEST_FAILURE, - GEO_REQUEST_SUCCESS, -} from 'state/action-types'; - -/** - * Constants - */ -export const GEO_ENDPOINT = 'https://public-api.wordpress.com/geo/'; - -/** - * Returns an action object used in signalling that the current browser IP - * geolocation has been received. - * - * @param {Object} geo Geolocation data - * @return {Object} Action object - */ -export function receiveGeo( geo ) { - return { - type: GEO_RECEIVE, - geo, - }; -} - -/** - * Returns a function which, when invoked, triggers a network request to fetch - * browser IP geolocation. - * - * @return {Function} Action thunk - */ -export function requestGeo() { - return dispatch => { - dispatch( { type: GEO_REQUEST } ); - - return request - .get( GEO_ENDPOINT ) - .then( ( { body: geo } ) => { - dispatch( { type: GEO_REQUEST_SUCCESS } ); - dispatch( receiveGeo( geo ) ); - } ) - .catch( error => { - dispatch( { - type: GEO_REQUEST_FAILURE, - error, - } ); - } ); - }; -} diff --git a/client/state/geo/reducer.js b/client/state/geo/reducer.js deleted file mode 100644 index 4e7b9e5ac41f9..0000000000000 --- a/client/state/geo/reducer.js +++ /dev/null @@ -1,49 +0,0 @@ -/** @format */ - -/** - * Internal dependencies - */ - -import { - GEO_RECEIVE, - GEO_REQUEST, - GEO_REQUEST_FAILURE, - GEO_REQUEST_SUCCESS, -} from 'state/action-types'; -import { combineReducers, createReducer } from 'state/utils'; -import { geoSchema } from './schema'; - -/** - * Returns the updated requesting state after an action has been dispatched. - * Requesting state tracks whether a geolocation request is in progress. - * - * @param {Object} state Current state - * @param {Object} action Action object - * @return {Object} Updated state - */ -export const requesting = createReducer( false, { - [ GEO_REQUEST ]: () => true, - [ GEO_REQUEST_FAILURE ]: () => false, - [ GEO_REQUEST_SUCCESS ]: () => false, -} ); - -/** - * Returns the updated requesting state after an action has been dispatched. - * Requesting state tracks current browser IP geolocation. - * - * @param {?Object} state Current state - * @param {Object} action Action object - * @return {?Object} Updated state - */ -export const geo = createReducer( - null, - { - [ GEO_RECEIVE ]: ( state, action ) => action.geo, - }, - geoSchema -); - -export default combineReducers( { - requesting, - geo, -} ); diff --git a/client/state/geo/schema.js b/client/state/geo/schema.js deleted file mode 100644 index 62299916d68d6..0000000000000 --- a/client/state/geo/schema.js +++ /dev/null @@ -1,12 +0,0 @@ -/** @format */ -export const geoSchema = { - type: [ 'object', 'null' ], - properties: { - latitude: { type: 'string' }, - longitude: { type: 'string' }, - country_short: { type: 'string' }, - country_long: { type: 'string' }, - region: { type: 'string' }, - city: { type: 'string' }, - }, -}; diff --git a/client/state/geo/selectors.js b/client/state/geo/selectors.js deleted file mode 100644 index 3dd7f151b703c..0000000000000 --- a/client/state/geo/selectors.js +++ /dev/null @@ -1,47 +0,0 @@ -/** @format */ - -/** - * External dependencies - */ - -import { get } from 'lodash'; - -/** - * Returns whether a browser IP geolocation request is in progress. - * - * @param {Object} state Global state tree - * @return {Boolean} Whether request is in progress - */ -export function isRequestingGeo( state ) { - return state.geo.requesting; -} - -/** - * Returns the current browser IP geolocation data. - * - * @param {Object} state Global state tree - * @return {?Object} Current browser IP geolocation data - */ -export function getGeo( state ) { - return state.geo.geo; -} - -/** - * Returns the current browser IP geolocation full country name. - * - * @param {Object} state Global state tree - * @return {?String} Current browser IP geolocation data - */ -export function getGeoCountry( state ) { - return get( getGeo( state ), 'country_long', null ); -} - -/** - * Returns the current browser IP geolocation abbreviated country name. - * - * @param {Object} state Global state tree - * @return {?String} Current browser IP geolocation short country name - */ -export function getGeoCountryShort( state ) { - return get( getGeo( state ), 'country_short', null ); -} diff --git a/client/state/geo/test/actions.js b/client/state/geo/test/actions.js deleted file mode 100644 index d47d74c63f889..0000000000000 --- a/client/state/geo/test/actions.js +++ /dev/null @@ -1,116 +0,0 @@ -/** @format */ -/** - * External dependencies - */ -import { expect } from 'chai'; -import { match } from 'sinon'; - -/** - * Internal dependencies - */ -import { receiveGeo, requestGeo } from '../actions'; -import { - GEO_RECEIVE, - GEO_REQUEST, - GEO_REQUEST_SUCCESS, - GEO_REQUEST_FAILURE, -} from 'state/action-types'; -import useNock from 'test/helpers/use-nock'; -import { useSandbox } from 'test/helpers/use-sinon'; - -describe( 'actions', () => { - let spy; - useSandbox( sandbox => ( spy = sandbox.spy() ) ); - - describe( 'receiveGeo()', () => { - test( 'should return an action object', () => { - const action = receiveGeo( { - latitude: '39.36006', - longitude: '-84.30994', - country_short: 'US', - country_long: 'United States', - region: 'Ohio', - city: 'Mason', - } ); - - expect( action ).to.eql( { - type: GEO_RECEIVE, - geo: { - latitude: '39.36006', - longitude: '-84.30994', - country_short: 'US', - country_long: 'United States', - region: 'Ohio', - city: 'Mason', - }, - } ); - } ); - } ); - - describe( 'requestGeo()', () => { - test( 'should dispatch fetch action when thunk triggered', () => { - requestGeo()( spy ); - - expect( spy ).to.have.been.calledWith( { - type: GEO_REQUEST, - } ); - } ); - - describe( 'success', () => { - useNock( nock => { - nock( 'https://public-api.wordpress.com:443' ) - .persist() - .get( '/geo/' ) - .reply( 200, { - latitude: '39.36006', - longitude: '-84.30994', - country_short: 'US', - country_long: 'United States', - region: 'Ohio', - city: 'Mason', - } ); - } ); - - test( 'should dispatch receive action when request completes', () => { - return requestGeo()( spy ).then( () => { - expect( spy ).to.have.been.calledWith( - receiveGeo( { - latitude: '39.36006', - longitude: '-84.30994', - country_short: 'US', - country_long: 'United States', - region: 'Ohio', - city: 'Mason', - } ) - ); - } ); - } ); - - test( 'should dispatch request success action when request completes', () => { - return requestGeo()( spy ).then( () => { - expect( spy ).to.have.been.calledWith( { - type: GEO_REQUEST_SUCCESS, - } ); - } ); - } ); - } ); - - describe( 'failure', () => { - useNock( nock => { - nock( 'https://public-api.wordpress.com:443' ) - .persist() - .get( '/geo/' ) - .reply( 500 ); - } ); - - test( 'should dispatch fail action when request fails', () => { - return requestGeo()( spy ).then( () => { - expect( spy ).to.have.been.calledWith( { - type: GEO_REQUEST_FAILURE, - error: match( { message: 'Internal Server Error' } ), - } ); - } ); - } ); - } ); - } ); -} ); diff --git a/client/state/geo/test/reducer.js b/client/state/geo/test/reducer.js deleted file mode 100644 index 512cdfe47a95a..0000000000000 --- a/client/state/geo/test/reducer.js +++ /dev/null @@ -1,121 +0,0 @@ -/** @format */ - -/** - * External dependencies - */ -import { expect } from 'chai'; -import deepFreeze from 'deep-freeze'; - -/** - * Internal dependencies - */ -import reducer, { requesting, geo } from '../reducer'; -import { - GEO_RECEIVE, - GEO_REQUEST, - GEO_REQUEST_FAILURE, - GEO_REQUEST_SUCCESS, - DESERIALIZE, -} from 'state/action-types'; -import { useSandbox } from 'test/helpers/use-sinon'; - -describe( 'reducer', () => { - useSandbox( sandbox => sandbox.stub( console, 'warn' ) ); - - test( 'should include expected keys in return value', () => { - expect( reducer( undefined, {} ) ).to.have.keys( [ 'requesting', 'geo' ] ); - } ); - - describe( 'requesting()', () => { - test( 'should default to false', () => { - const state = requesting( undefined, {} ); - - expect( state ).to.be.false; - } ); - - test( 'should set site ID to true value if request in progress', () => { - const state = requesting( undefined, { - type: GEO_REQUEST, - } ); - - expect( state ).to.eql( true ); - } ); - - test( 'should set site ID to false if request succeeds', () => { - const state = requesting( true, { - type: GEO_REQUEST_SUCCESS, - } ); - - expect( state ).to.eql( false ); - } ); - - test( 'should set site ID to false if request fails', () => { - const state = requesting( true, { - type: GEO_REQUEST_FAILURE, - } ); - - expect( state ).to.eql( false ); - } ); - } ); - - describe( 'geo()', () => { - test( 'should default to null', () => { - const state = geo( undefined, {} ); - - expect( state ).to.be.null; - } ); - - test( 'should track received geo data', () => { - const state = geo( undefined, { - type: GEO_RECEIVE, - geo: { - latitude: '39.36006', - longitude: '-84.30994', - country_short: 'US', - country_long: 'United States', - region: 'Ohio', - city: 'Mason', - }, - } ); - - expect( state ).to.eql( { - latitude: '39.36006', - longitude: '-84.30994', - country_short: 'US', - country_long: 'United States', - region: 'Ohio', - city: 'Mason', - } ); - } ); - - test( 'should load valid persisted state', () => { - const original = deepFreeze( { - latitude: '39.36006', - longitude: '-84.30994', - country_short: 'US', - country_long: 'United States', - region: 'Ohio', - city: 'Mason', - } ); - const state = geo( original, { type: DESERIALIZE } ); - - expect( state ).to.eql( { - latitude: '39.36006', - longitude: '-84.30994', - country_short: 'US', - country_long: 'United States', - region: 'Ohio', - city: 'Mason', - } ); - } ); - - test( 'should not load invalid persisted state', () => { - const original = deepFreeze( { - country_short: true, - } ); - const state = geo( original, { type: DESERIALIZE } ); - - expect( state ).to.be.null; - } ); - } ); -} ); diff --git a/client/state/geo/test/selectors.js b/client/state/geo/test/selectors.js deleted file mode 100644 index 7e46f681d5329..0000000000000 --- a/client/state/geo/test/selectors.js +++ /dev/null @@ -1,109 +0,0 @@ -/** @format */ - -/** - * External dependencies - */ -import { expect } from 'chai'; - -/** - * Internal dependencies - */ -import { isRequestingGeo, getGeo, getGeoCountry, getGeoCountryShort } from '../selectors'; - -describe( 'selectors', () => { - describe( 'isRequestingGeo()', () => { - test( 'should return requesting state', () => { - const isRequesting = isRequestingGeo( { - geo: { - requesting: true, - }, - } ); - - expect( isRequesting ).to.be.true; - } ); - } ); - - describe( 'getGeo()', () => { - test( 'should return geo data state', () => { - const geo = getGeo( { - geo: { - geo: { - latitude: '39.36006', - longitude: '-84.30994', - country_short: 'US', - country_long: 'United States', - region: 'Ohio', - city: 'Mason', - }, - }, - } ); - - expect( geo ).to.eql( { - latitude: '39.36006', - longitude: '-84.30994', - country_short: 'US', - country_long: 'United States', - region: 'Ohio', - city: 'Mason', - } ); - } ); - } ); - - describe( 'getGeoCountry()', () => { - test( 'should return null if no geo data state', () => { - const country = getGeoCountry( { - geo: { - geo: null, - }, - } ); - - expect( country ).to.be.null; - } ); - - test( 'should return full country name of geo data state', () => { - const country = getGeoCountry( { - geo: { - geo: { - latitude: '39.36006', - longitude: '-84.30994', - country_short: 'US', - country_long: 'United States', - region: 'Ohio', - city: 'Mason', - }, - }, - } ); - - expect( country ).to.equal( 'United States' ); - } ); - } ); - - describe( 'getGeoCountryShort()', () => { - test( 'should return null if no geo data state', () => { - const country = getGeoCountryShort( { - geo: { - geo: null, - }, - } ); - - expect( country ).to.be.null; - } ); - - test( 'should return abbreviated country name of geo data state', () => { - const country = getGeoCountryShort( { - geo: { - geo: { - latitude: '39.36006', - longitude: '-84.30994', - country_short: 'US', - country_long: 'United States', - region: 'Ohio', - city: 'Mason', - }, - }, - } ); - - expect( country ).to.equal( 'US' ); - } ); - } ); -} ); diff --git a/client/state/index.js b/client/state/index.js index dea22121fbee2..64147c1912611 100644 --- a/client/state/index.js +++ b/client/state/index.js @@ -40,7 +40,6 @@ import currentUser from './current-user/reducer'; import { reducer as dataRequests } from './data-layer/wpcom-http/utils'; import documentHead from './document-head/reducer'; import domains from './domains/reducer'; -import geo from './geo/reducer'; import googleAppsUsers from './google-apps-users/reducer'; import googleMyBusiness from './google-my-business/reducer'; import help from './help/reducer'; @@ -131,7 +130,6 @@ const reducers = { domains, extensions, form, - geo, googleAppsUsers, googleMyBusiness, happinessEngineers, diff --git a/client/state/selectors/get-current-user-payment-methods.js b/client/state/selectors/get-current-user-payment-methods.js index e554ce5880ab5..21293ca47111c 100644 --- a/client/state/selectors/get-current-user-payment-methods.js +++ b/client/state/selectors/get-current-user-payment-methods.js @@ -10,7 +10,7 @@ import { lowerCase, upperCase } from 'lodash'; * Internal dependencies */ import { getCurrentUserLocale } from 'state/current-user/selectors'; -import { getGeoCountryShort } from 'state/geo/selectors'; +import { requestGeoLocation } from 'state/data-layer/http-data/getters'; /** * Constants @@ -44,7 +44,7 @@ const paymentMethods = { * @return {Array} Preferred payment methods */ export default function getCurrentUserPaymentMethods( state ) { - const countryCode = getGeoCountryShort( state ); + const countryCode = requestGeoLocation().data; const wpcomLang = getCurrentUserLocale( state ); const generatedLocale = lowerCase( wpcomLang ) + '-' + upperCase( countryCode );