diff --git a/client/index.js b/client/index.js
index d4440f45053..5266289317a 100644
--- a/client/index.js
+++ b/client/index.js
@@ -10,6 +10,7 @@ import { addFilter } from '@wordpress/hooks';
import './style.scss';
import { HelloWorld } from 'hello-world';
import TransactionsPage from 'transactions';
+import 'payments-api/payments-data-store';
const DepositsPage = () => Hello from the deposits page;
const DisputesPage = () => Hello from the disputes page;
diff --git a/client/payments-api/api-spec/payments-rest-api.js b/client/payments-api/api-spec/payments-rest-api.js
new file mode 100644
index 00000000000..10693269bed
--- /dev/null
+++ b/client/payments-api/api-spec/payments-rest-api.js
@@ -0,0 +1,31 @@
+/** @format */
+
+/**
+ * Internal dependencies.
+ */
+import transactions from './transactions';
+
+function createPaymentsApiSpec() {
+ return {
+ name: 'wcPaymentsApi',
+ mutations: {},
+ selectors: {
+ ...transactions.selectors,
+ },
+ operations: {
+ read( resourceNames ) {
+ return [
+ ...transactions.operations.read( resourceNames ),
+ ];
+ },
+ update( resourceNames, data ) {
+ return [];
+ },
+ updateLocally( resourceNames, data ) {
+ return [];
+ },
+ },
+ };
+}
+
+export default createPaymentsApiSpec();
diff --git a/client/payments-api/api-spec/transactions/index.js b/client/payments-api/api-spec/transactions/index.js
new file mode 100644
index 00000000000..024fe6a8757
--- /dev/null
+++ b/client/payments-api/api-spec/transactions/index.js
@@ -0,0 +1,12 @@
+/** @format */
+
+/**
+ * Internal dependencies.
+ */
+import selectors from './selectors';
+import operations from './operations';
+
+export default {
+ selectors,
+ operations,
+};
diff --git a/client/payments-api/api-spec/transactions/operations.js b/client/payments-api/api-spec/transactions/operations.js
new file mode 100644
index 00000000000..dc8b7707e69
--- /dev/null
+++ b/client/payments-api/api-spec/transactions/operations.js
@@ -0,0 +1,44 @@
+/** @format */
+
+/**
+ * External dependencies.
+ */
+import apiFetch from '@wordpress/api-fetch';
+import { includes } from 'lodash';
+
+/**
+ * Internal dependencies.
+ */
+import { NAMESPACE } from '../../constants';
+
+function read( resourceNames, fetch = apiFetch, dataToResources = transactionsToResources ) {
+ return readTransactions( resourceNames, fetch, dataToResources );
+}
+
+export function readTransactions( resourceNames, fetch, dataToResources ) {
+ if ( includes( resourceNames, 'transactions-list' ) ) {
+ const url = `${ NAMESPACE }/payments/transactions`;
+
+ return [
+ fetch( { path: url } )
+ .then( dataToResources )
+ .catch( error => {
+ return { [ resourceName ]: { error } };
+ } )
+ ];
+ }
+
+ return [];
+}
+
+export function transactionsToResources( transactions ) {
+ return {
+ [ 'transactions-list' ]: {
+ data: transactions,
+ }
+ };
+}
+
+export default {
+ read,
+};
diff --git a/client/payments-api/api-spec/transactions/selectors.js b/client/payments-api/api-spec/transactions/selectors.js
new file mode 100644
index 00000000000..3f7839f4c21
--- /dev/null
+++ b/client/payments-api/api-spec/transactions/selectors.js
@@ -0,0 +1,42 @@
+/** @format */
+
+/**
+ * External dependencies.
+ */
+import { isNil } from 'lodash';
+
+/**
+ * Internal dependencies.
+ */
+import { DEFAULT_REQUIREMENT } from '../../constants';
+
+const getTransactions = ( getResource, requireResource ) => (
+ requirement = DEFAULT_REQUIREMENT
+) => {
+ return requireResource( requirement, 'transactions-list' ).data || {};
+}
+
+const isWaitingForInitialLoad = ( getResource ) => () => {
+ const resourceName = 'transactions-list';
+ const transactionsResource = getResource( resourceName );
+
+ return transactionsResource.lastReceived === undefined;
+}
+
+const getTransactionsIsLoading = ( getResource ) => () => {
+ const resourceName = 'transactions-list';
+ const transactionsResource = getResource( resourceName );
+
+ return transactionsResource.lastRequested > transactionsResource.lastReceived;
+}
+
+const showTransactionsPlaceholder = ( getResource ) => () => {
+ return isWaitingForInitialLoad( getResource )();
+}
+
+export default {
+ getTransactions,
+ getTransactionsIsLoading,
+ isWaitingForInitialLoad,
+ showTransactionsPlaceholder,
+};
diff --git a/client/payments-api/constants.js b/client/payments-api/constants.js
new file mode 100644
index 00000000000..84268735807
--- /dev/null
+++ b/client/payments-api/constants.js
@@ -0,0 +1,12 @@
+
+/**
+ * External dependencies.
+ */
+import { MINUTE } from '@fresh-data/framework';
+
+export const DEFAULT_REQUIREMENT = {
+ timeout: 1 * MINUTE,
+ freshness: 30 * MINUTE,
+};
+
+export const NAMESPACE = '/wc/v3';
diff --git a/client/payments-api/payments-data-store/create-api-client.js b/client/payments-api/payments-data-store/create-api-client.js
new file mode 100644
index 00000000000..95ff21da014
--- /dev/null
+++ b/client/payments-api/payments-data-store/create-api-client.js
@@ -0,0 +1,45 @@
+
+/**
+ * External dependencies.
+ */
+import { ApiClient } from '@fresh-data/framework';
+
+/**
+ * Internal dependencies.
+ */
+import createStore from './create-store';
+
+function createDataHandlers( store ) {
+ return {
+ dataRequested: resourceNames => {
+ store.dispatch( {
+ type: 'FRESH_DATA_REQUESTED',
+ resourceNames,
+ time: new Date(),
+ } );
+ },
+ dataReceived: resources => {
+ store.dispatch( {
+ type: 'FRESH_DATA_RECEIVED',
+ resources,
+ time: new Date(),
+ } );
+ },
+ };
+}
+
+function createApiClient( name, apiSpec ) {
+ const store = createStore( name );
+ const dataHandlers = createDataHandlers( store );
+ const apiClient = new ApiClient( apiSpec );
+ apiClient.setDataHandlers( dataHandlers );
+
+ const storeChanged = () => {
+ apiClient.setState( store.getState() );
+ }
+ store.subscribe( storeChanged );
+
+ return apiClient;
+}
+
+export default createApiClient;
diff --git a/client/payments-api/payments-data-store/create-store.js b/client/payments-api/payments-data-store/create-store.js
new file mode 100644
index 00000000000..2d84ff5a287
--- /dev/null
+++ b/client/payments-api/payments-data-store/create-store.js
@@ -0,0 +1,16 @@
+
+/**
+ * External dependencies.
+ */
+import { createStore } from 'redux';
+
+/**
+ * Internal dependencies.
+ */
+import reducer from './reducer';
+
+export default name => {
+ const devTools = window.__REDUX_DEVTOOLS_EXTENSION__;
+
+ return createStore( reducer, devTools && devTools( { name: name, instanceId: name } ) );
+}
diff --git a/client/payments-api/payments-data-store/index.js b/client/payments-api/payments-data-store/index.js
new file mode 100644
index 00000000000..75f53c1d9c6
--- /dev/null
+++ b/client/payments-api/payments-data-store/index.js
@@ -0,0 +1,31 @@
+
+/**
+ * External dependencies.
+ */
+import { registerGenericStore } from '@wordpress/data';
+
+/**
+ * Internal dependencies.
+ */
+import createApiClient from './create-api-client';
+import paymentsApiSpec from '../api-spec/payments-rest-api';
+
+if ( 'development' === process.env.NODE_ENV ) {
+ window.__FRESH_DATA_DEV_INFO__ = true;
+}
+
+function createPaymentsApiStore() {
+ const apiClient = createApiClient( 'wc-payments-api', paymentsApiSpec );
+
+ return {
+ getSelectors: () => {
+ return apiClient.getSelectors();
+ },
+ getActions: () => {
+ return apiClient.getMutations();
+ },
+ subscribe: apiClient.subscribe,
+ };
+}
+
+registerGenericStore( 'wc-payments-api', createPaymentsApiStore() );
diff --git a/client/payments-api/payments-data-store/reducer.js b/client/payments-api/payments-data-store/reducer.js
new file mode 100644
index 00000000000..40900f5a21c
--- /dev/null
+++ b/client/payments-api/payments-data-store/reducer.js
@@ -0,0 +1,52 @@
+
+const defaultState = {
+ resources: {},
+};
+
+export default function reducer( state = defaultState, action ) {
+ switch ( action.type ) {
+ case 'FRESH_DATA_REQUESTED':
+ return reduceRequested( state, action );
+ case 'FRESH_DATA_RECEIVED':
+ return reduceReceived( state, action );
+ default:
+ return state;
+ }
+}
+
+export function reduceRequested( state, action ) {
+ const newResources = action.resourceNames.reduce( ( resources, name ) => {
+ resources[ name ] = { lastRequested: action.time };
+ return resources;
+ }, {} );
+ return reduceResources( state, newResources );
+}
+
+export function reduceReceived( state, action ) {
+ const newResources = Object.keys( action.resources ).reduce( ( resources, name ) => {
+ const resource = { ...action.resources[ name ] };
+ if ( resource.data ) {
+ resource.lastReceived = action.time;
+ }
+ if ( resource.error ) {
+ resource.lastError = action.time;
+ }
+ resources[ name ] = resource;
+ return resources;
+ }, {} );
+ return reduceResources( state, newResources );
+}
+
+export function reduceResources( state, newResources ) {
+ const updatedResources = Object.keys( newResources ).reduce(
+ ( resources, resourceName ) => {
+ const resource = resources[ resourceName ];
+ const newResource = newResources[ resourceName ];
+ resources[ resourceName ] = { ...resource, ...newResource };
+ return resources;
+ },
+ { ...state.resources }
+ );
+
+ return { ...state, resources: updatedResources };
+}
diff --git a/client/payments-api/test/api-spec/transactions/index.js b/client/payments-api/test/api-spec/transactions/index.js
new file mode 100644
index 00000000000..3463a18c576
--- /dev/null
+++ b/client/payments-api/test/api-spec/transactions/index.js
@@ -0,0 +1,3 @@
+/** @format */
+import './operations';
+import './selectors';
diff --git a/client/payments-api/test/api-spec/transactions/operations.js b/client/payments-api/test/api-spec/transactions/operations.js
new file mode 100644
index 00000000000..328607b8281
--- /dev/null
+++ b/client/payments-api/test/api-spec/transactions/operations.js
@@ -0,0 +1,69 @@
+/** @format */
+
+/**
+ * Internal dependencies.
+ */
+import { readTransactions, transactionsToResources } from '../../../api-spec/transactions/operations';
+import { NAMESPACE } from '../../../constants';
+
+describe( 'Transactions operations', () => {
+ describe( 'readTransactions()', () => {
+ const expectedUrl = `${ NAMESPACE }/payments/transactions`;
+
+ it( 'Returns a list with one promise when correct resource names are supplied', () => {
+ const mockData = [ {}, {}, {} ];
+ const expectedResolvedPromise = {
+ [ "transactions-list" ]: {
+ data: mockData,
+ },
+ };
+
+ const mockToResources = jest.fn();
+ mockToResources.mockReturnValue( expectedResolvedPromise );
+
+ const mockPromise = new Promise( () => mockData, () => {} );
+ const expectedPromises = [ mockPromise ];
+
+ const mockFetch = jest.fn();
+ mockFetch.mockReturnValue( mockPromise );
+
+ // Perform read operation.
+ const promises = readTransactions( [ 'transactions-list' ], mockFetch, mockToResources );
+
+ expect( mockFetch ).toHaveBeenCalledTimes( 1 );
+ expect( mockFetch ).toHaveBeenCalledWith( { path: expectedUrl } );
+ expect( promises ).toStrictEqual( expectedPromises );
+ promises[0].then( result => {
+ expect( mockToResources ).toHaveBeenCalledTimes( 1 );
+ expect( mockToResources ).toHaveBeenCalledWith( mockData );
+ expect( result ).toBe( expectedResolvedPromise );
+ } );
+ } );
+
+ it( 'Returns an empty list when wrong resource names are supplied', () => {
+ const expected = [];
+
+ const mockFetch = jest.fn();
+
+ // Perform read operation.
+ const promises = readTransactions( [ 'wrong', 'resource', 'names' ] );
+
+ expect( mockFetch ).not.toHaveBeenCalled();
+ expect( promises ).toStrictEqual( expected );
+ } );
+ } );
+
+ describe( 'transactionsToResources()', () => {
+ it( 'Transactions list is correctly converted to resources', () => {
+ const mockData = [ {}, {}, {} ];
+ const expected = {
+ [ 'transactions-list' ]: {
+ data: mockData,
+ },
+ };
+
+ const resources = transactionsToResources( mockData );
+ expect( resources ).toStrictEqual( expected );
+ } );
+ } );
+} );
diff --git a/client/payments-api/test/api-spec/transactions/selectors.js b/client/payments-api/test/api-spec/transactions/selectors.js
new file mode 100644
index 00000000000..ae400ad07ae
--- /dev/null
+++ b/client/payments-api/test/api-spec/transactions/selectors.js
@@ -0,0 +1,172 @@
+/** @format */
+
+/**
+ * Internal dependencies.
+ */
+import transactionsSelectors from '../../../api-spec/transactions/selectors';
+import { DEFAULT_REQUIREMENT } from '../../../constants';
+
+
+describe( 'Transactions selectors', () => {
+ const expectedResourceName = 'transactions-list';
+ const now = Date.now();
+ const second_before_now = now - 1000;
+
+ describe( 'getTransactions()', () => {
+ it( 'Returns empty list before a read operation', () => {
+ const expected = {};
+
+ const mockGetResource = jest.fn();
+ const mockRequireResource = jest.fn();
+
+ mockRequireResource.mockReturnValue( {} );
+ const transactions = transactionsSelectors.getTransactions( mockGetResource, mockRequireResource )();
+
+ expect( mockGetResource ).not.toHaveBeenCalled();
+ expect( mockRequireResource ).toHaveBeenCalledTimes( 1 );
+ expect( mockRequireResource ).toHaveBeenCalledWith( DEFAULT_REQUIREMENT, expectedResourceName );
+ expect( transactions ).toStrictEqual( expected );
+ } );
+
+ it( 'Returns the expected transactions list after a read operation', () => {
+ const expected = { data: [ {}, {}, {} ] };
+
+ const mockGetResource = jest.fn();
+ const mockRequireResource = jest.fn();
+
+ mockRequireResource.mockReturnValue( { data: expected } );
+ const transactions = transactionsSelectors.getTransactions( mockGetResource, mockRequireResource )();
+
+ expect( mockGetResource ).not.toHaveBeenCalled();
+ expect( mockRequireResource ).toHaveBeenCalledTimes( 1 );
+ expect( mockRequireResource ).toHaveBeenCalledWith( DEFAULT_REQUIREMENT, expectedResourceName );
+ expect( transactions ).toBe( expected );
+ } );
+ } );
+
+ describe( 'getTransactionsIsLoading()', () => {
+ it( "Returns false when a read operation isn't in flight", () => {
+ const expected = false;
+
+ const mockGetResource = jest.fn();
+
+ mockGetResource.mockReturnValue( {
+ lastRequested: second_before_now,
+ lastReceived: now,
+ } );
+ const isLoading = transactionsSelectors.getTransactionsIsLoading( mockGetResource )();
+
+ expect( mockGetResource ).toHaveBeenCalledTimes( 1 );
+ expect( mockGetResource ).toHaveBeenCalledWith( expectedResourceName );
+ expect( isLoading ).toStrictEqual( expected );
+ } );
+
+ it( 'Returns true when a read operation is in flight', () => {
+ const expected = true;
+
+ const mockGetResource = jest.fn();
+
+ mockGetResource.mockReturnValue( {
+ lastRequested: now,
+ lastReceived: second_before_now,
+ } );
+ const isLoading = transactionsSelectors.getTransactionsIsLoading( mockGetResource )();
+
+ expect( mockGetResource ).toHaveBeenCalledTimes( 1 );
+ expect( mockGetResource ).toHaveBeenCalledWith( expectedResourceName );
+ expect( isLoading ).toStrictEqual( expected );
+ } );
+ } );
+
+ describe( 'isWaitingForInitialLoad()', () => {
+ it( 'Returns false when transactions are initialized', () => {
+ const expected = false;
+
+ const mockGetResource = jest.fn();
+ mockGetResource.mockReturnValue( {
+ lastRequested: second_before_now,
+ lastReceived: now,
+ } );
+
+ const initStatus = transactionsSelectors.isWaitingForInitialLoad( mockGetResource )();
+
+ expect( mockGetResource ).toHaveBeenCalledTimes( 1 );
+ expect( mockGetResource ).toHaveBeenCalledWith( expectedResourceName );
+ expect( initStatus ).toBe( expected );
+ } );
+
+ it( "Returns true when transactions aren't initialized", () => {
+ const expected = true;
+
+ const mockGetResource = jest.fn();
+ mockGetResource.mockReturnValue( {} );
+
+ const initStatus = transactionsSelectors.isWaitingForInitialLoad( mockGetResource )();
+
+ expect( mockGetResource ).toHaveBeenCalledTimes( 1 );
+ expect( mockGetResource ).toHaveBeenCalledWith( expectedResourceName );
+ expect( initStatus ).toBe( expected );
+ } );
+
+ it( "Returns true when first request in flight", () => {
+ const expected = true;
+
+ const mockGetResource = jest.fn();
+ mockGetResource.mockReturnValue( {
+ lastRequested: second_before_now,
+ } );
+
+ const initStatus = transactionsSelectors.isWaitingForInitialLoad( mockGetResource )();
+
+ expect( mockGetResource ).toHaveBeenCalledTimes( 1 );
+ expect( mockGetResource ).toHaveBeenCalledWith( expectedResourceName );
+ expect( initStatus ).toBe( expected );
+ } );
+ } );
+
+ describe( 'showTransactionsPlaceholder()', () => {
+ it( "Returns true when transactions aren't initialized", () => {
+ const expected = true;
+
+ const mockGetResource = jest.fn();
+ mockGetResource.mockReturnValue( {} );
+
+ const showPlaceholder = transactionsSelectors.showTransactionsPlaceholder( mockGetResource )();
+
+ expect( mockGetResource ).toHaveBeenCalledTimes( 1 );
+ expect( mockGetResource ).toHaveBeenCalledWith( expectedResourceName );
+ expect( showPlaceholder ).toBe( expected );
+ } );
+
+ it( "Returns true when first request in flight", () => {
+ const expected = true;
+
+ const mockGetResource = jest.fn();
+ mockGetResource.mockReturnValue( {
+ lastRequested: second_before_now,
+ } );
+
+ const initStatus = transactionsSelectors.showTransactionsPlaceholder( mockGetResource )();
+
+ expect( mockGetResource ).toHaveBeenCalledTimes( 1 );
+ expect( mockGetResource ).toHaveBeenCalledWith( expectedResourceName );
+ expect( initStatus ).toBe( expected );
+ } );
+
+ it( "Returns false when transactions are initialized", () => {
+ const expected = false;
+
+ const mockGetResource = jest.fn();
+ mockGetResource.mockReturnValue( {
+ lastRequested: second_before_now,
+ lastReceived: now,
+ } );
+
+ const showPlaceholder = transactionsSelectors.showTransactionsPlaceholder( mockGetResource )();
+
+ expect( mockGetResource ).toHaveBeenCalledTimes( 1 );
+ expect( mockGetResource ).toHaveBeenCalledWith( expectedResourceName );
+ expect( showPlaceholder ).toBe( expected );
+ } );
+ } );
+} );
diff --git a/client/payments-api/test/index.js b/client/payments-api/test/index.js
new file mode 100644
index 00000000000..8b4e515764a
--- /dev/null
+++ b/client/payments-api/test/index.js
@@ -0,0 +1,3 @@
+/** @format */
+
+import './api-spec/transactions';
diff --git a/client/payments-api/with-select.js b/client/payments-api/with-select.js
new file mode 100644
index 00000000000..d330f87210a
--- /dev/null
+++ b/client/payments-api/with-select.js
@@ -0,0 +1,195 @@
+/**
+ * NOTE: This is temporary code. It exists only until a version of `@wordpress/data`
+ * is released which supports this functionality.
+ *
+ * @todo Remove this and use `@wordpress/data` `withSelect` instead after
+ * this PR is merged: https://github.com/WordPress/gutenberg/pull/11460
+ *
+ * @format
+ */
+
+/**
+ * External dependencies
+ */
+import { isFunction } from 'lodash';
+import { Component } from '@wordpress/element';
+import isShallowEqual from '@wordpress/is-shallow-equal';
+import { createHigherOrderComponent } from '@wordpress/compose';
+import { RegistryConsumer } from '@wordpress/data';
+
+/**
+ * Higher-order component used to inject state-derived props using registered
+ * selectors.
+ *
+ * @param {Function} mapSelectToProps Function called on every state change,
+ * expected to return object of props to
+ * merge with the component's own props.
+ *
+ * @return {Component} Enhanced component with merged state data props.
+ */
+const withSelect = mapSelectToProps =>
+ createHigherOrderComponent( WrappedComponent => {
+ /**
+ * Default merge props. A constant value is used as the fallback since it
+ * can be more efficiently shallow compared in case component is repeatedly
+ * rendered without its own merge props.
+ *
+ * @type {Object}
+ */
+ const DEFAULT_MERGE_PROPS = {};
+
+ class ComponentWithSelect extends Component {
+ constructor( props ) {
+ super( props );
+
+ this.onStoreChange = this.onStoreChange.bind( this );
+ this.subscribe( props.registry );
+
+ this.onUnmounts = {};
+ this.mergeProps = this.getNextMergeProps( props );
+ }
+
+ /**
+ * Given a props object, returns the next merge props by mapSelectToProps.
+ *
+ * @param {Object} props Props to pass as argument to mapSelectToProps.
+ *
+ * @return {Object} Props to merge into rendered wrapped element.
+ */
+ getNextMergeProps( props ) {
+ const storeSelectors = {};
+ const onCompletes = [];
+ const componentContext = { component: this };
+
+ const getStoreFromRegistry = ( key, registry, context ) => {
+ // This is our first time selecting from this store.
+ // Do some lazy-loading of handling at this time.
+ const selectorsForKey = registry.select( key );
+
+ if ( isFunction( selectorsForKey ) ) {
+ // This store has special handling for its selectors.
+ // We give it a context, and we check for a "resolve"
+ const { selectors, onComplete, onUnmount } = selectorsForKey( context );
+ onComplete && onCompletes.push( onComplete );
+ onUnmount && ( this.onUnmounts[ key ] = onUnmount );
+ storeSelectors[ key ] = selectors;
+ } else {
+ storeSelectors[ key ] = selectorsForKey;
+ }
+ };
+
+ const select = key => {
+ if ( ! storeSelectors[ key ] ) {
+ // TODO: make this more functional in nature, e.g. by having
+ // getStoreFromRegistry return a value, and making the side
+ // effects from calling the function clearer?
+ getStoreFromRegistry( key, props.registry, componentContext );
+ }
+
+ return storeSelectors[ key ];
+ };
+
+ const selectedProps = mapSelectToProps( select, props.ownProps ) || DEFAULT_MERGE_PROPS;
+
+ // Complete the select for those stores which support it.
+ onCompletes.forEach( onComplete => onComplete() );
+ return selectedProps;
+ }
+
+ componentDidMount() {
+ this.canRunSelection = true;
+
+ // A state change may have occurred between the constructor and
+ // mount of the component (e.g. during the wrapped components own
+ // constructor), in which case selection should be rerun.
+ if ( this.hasQueuedSelection ) {
+ this.hasQueuedSelection = false;
+ this.onStoreChange();
+ }
+ }
+
+ componentWillUnmount() {
+ this.canRunSelection = false;
+ this.unsubscribe();
+ Object.keys( this.onUnmounts ).forEach( key => this.onUnmounts[ key ]() );
+ }
+
+ shouldComponentUpdate( nextProps, nextState ) {
+ // Cycle subscription if registry changes.
+ const hasRegistryChanged = nextProps.registry !== this.props.registry;
+ if ( hasRegistryChanged ) {
+ this.unsubscribe();
+ this.subscribe( nextProps.registry );
+ }
+
+ // Treat a registry change as equivalent to `ownProps`, to reflect
+ // `mergeProps` to rendered component if and only if updated.
+ const hasPropsChanged =
+ hasRegistryChanged || ! isShallowEqual( this.props.ownProps, nextProps.ownProps );
+
+ // Only render if props have changed or merge props have been updated
+ // from the store subscriber.
+ if ( this.state === nextState && ! hasPropsChanged ) {
+ return false;
+ }
+
+ if ( hasPropsChanged ) {
+ const nextMergeProps = this.getNextMergeProps( nextProps );
+ if ( ! isShallowEqual( this.mergeProps, nextMergeProps ) ) {
+ // If merge props change as a result of the incoming props,
+ // they should be reflected as such in the upcoming render.
+ // While side effects are discouraged in lifecycle methods,
+ // this component is used heavily, and prior efforts to use
+ // `getDerivedStateFromProps` had demonstrated miserable
+ // performance.
+ this.mergeProps = nextMergeProps;
+ }
+
+ // Regardless whether merge props are changing, fall through to
+ // incur the render since the component will need to receive
+ // the changed `ownProps`.
+ }
+
+ return true;
+ }
+
+ onStoreChange() {
+ if ( ! this.canRunSelection ) {
+ this.hasQueuedSelection = true;
+ return;
+ }
+
+ const nextMergeProps = this.getNextMergeProps( this.props );
+ if ( isShallowEqual( this.mergeProps, nextMergeProps ) ) {
+ return;
+ }
+
+ this.mergeProps = nextMergeProps;
+
+ // Schedule an update. Merge props are not assigned to state since
+ // derivation of merge props from incoming props occurs within
+ // shouldComponentUpdate, where setState is not allowed. setState
+ // is used here instead of forceUpdate because forceUpdate bypasses
+ // shouldComponentUpdate altogether, which isn't desireable if both
+ // state and props change within the same render. Unfortunately,
+ // this requires that next merge props are generated twice.
+ this.setState( {} );
+ }
+
+ subscribe( registry ) {
+ this.unsubscribe = registry.subscribe( this.onStoreChange );
+ }
+
+ render() {
+ return ;
+ }
+ }
+
+ return ownProps => (
+
+ { registry => }
+
+ )
+ }, 'withSelect' );
+
+export default withSelect;
diff --git a/client/transactions/index.js b/client/transactions/index.js
index c9b2200c75b..c59f5a6a2fd 100644
--- a/client/transactions/index.js
+++ b/client/transactions/index.js
@@ -1,14 +1,17 @@
/**
* External dependencies
*/
-import { useState, useEffect } from '@wordpress/element';
-import apiFetch from '@wordpress/api-fetch';
import { dateI18n } from '@wordpress/date';
import moment from 'moment';
import { formatCurrency } from '@woocommerce/currency';
import { TableCard } from '@woocommerce/components';
import { capitalize } from 'lodash';
+/**
+ * Internal dependencies.
+ */
+import withSelect from 'payments-api/with-select';
+
const headers = [
{ key: 'created', label: 'Date / Time', required: true, isLeftAligned: true, defaultSort: true, defaultOrder: 'desc' },
{ key: 'type', label: 'Type', required: true },
@@ -24,25 +27,12 @@ const headers = [
{ key: 'risk_level', label: 'Risk Level', hiddenByDefault: true },
];
-const TransactionsPage = () => {
- const [ transactions, setTransactions ] = useState( [] );
- const [ loading, setLoading ] = useState( false );
-
- useEffect( () => {
- setLoading( true );
- apiFetch( { path: '/wc/v3/payments/transactions' } ).then( ( response ) => {
- const { data } = response;
- if ( data ) {
- setTransactions( data );
- } else {
- console.error( response );
- }
+const TransactionsList = ( props ) => {
+ const { transactions, showPlaceholder } = props;
+ const transactionsData = transactions.data || [];
+ // Do not display table loading view if data is already available.
- setLoading( false );
- } );
- }, [] );
-
- const rows = transactions.map( ( txn ) => {
+ const rows = transactionsData.map( ( txn ) => {
const charge = txn.source.object === 'charge' ? txn.source : null;
const order_url = txn.order ? #{ txn.order.number } : –;
@@ -50,7 +40,6 @@ const TransactionsPage = () => {
const billing_details = charge ? charge.billing_details : null;
const outcome = charge ? charge.outcome : null;
const payment_method_details = charge ? charge.payment_method_details : null;
-
const address = billing_details ? billing_details.address : null;
const card = payment_method_details ? payment_method_details.card : null;
@@ -76,7 +65,7 @@ const TransactionsPage = () => {
return (
{
);
};
-export default TransactionsPage;
+export default withSelect( select => {
+ const { getTransactions, showTransactionsPlaceholder } = select( 'wc-payments-api' );
+ const transactions = getTransactions();
+ const showPlaceholder = showTransactionsPlaceholder();
+
+ return { transactions, showPlaceholder };
+} )( TransactionsList );
diff --git a/package-lock.json b/package-lock.json
index 54945ba1af9..d8bafb3ab78 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -336,9 +336,9 @@
}
},
"@babel/plugin-proposal-object-rest-spread": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.5.4.tgz",
- "integrity": "sha512-KCx0z3y7y8ipZUMAEEJOyNi11lMb/FOPUjjB113tfowgw0c16EGYos7worCKBcUAh2oG+OBnoUhsnTSoLpV9uA==",
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.5.5.tgz",
+ "integrity": "sha512-F2DxJJSQ7f64FyTVl5cw/9MWn6naXGdk3Q3UhDbFEEHv+EilCPoeRD3Zh/Utx1CJz4uyKlQ4uH+bJPbEhMV7Zw==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0",
@@ -826,7 +826,6 @@
"version": "7.4.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz",
"integrity": "sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==",
- "dev": true,
"requires": {
"regenerator-runtime": "^0.13.2"
}
@@ -915,6 +914,14 @@
"minimist": "^1.2.0"
}
},
+ "@fresh-data/framework": {
+ "version": "0.7.0-beta.0",
+ "resolved": "https://registry.npmjs.org/@fresh-data/framework/-/framework-0.7.0-beta.0.tgz",
+ "integrity": "sha512-EunC210+m/pDlF8ZmGzV0UA3UKXsP+qqaJRHU9Y/dULEW7xki0kvp6P52ykRptcYQquayUPEORn/90qBMDI/cQ==",
+ "requires": {
+ "@babel/runtime": "^7.4.2"
+ }
+ },
"@jest/console": {
"version": "24.7.1",
"resolved": "https://registry.npmjs.org/@jest/console/-/console-24.7.1.tgz",
@@ -1728,6 +1735,12 @@
"integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==",
"dev": true
},
+ "lodash": {
+ "version": "4.17.11",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
+ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
+ "dev": true
+ },
"moment": {
"version": "2.22.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.22.1.tgz",
@@ -1792,6 +1805,14 @@
"@babel/runtime-corejs2": "7.4.5",
"@woocommerce/number": "1.0.2",
"lodash": "4.17.11"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "4.17.11",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
+ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
+ "dev": true
+ }
}
},
"@woocommerce/date": {
@@ -1821,6 +1842,12 @@
"tannin": "^1.0.1"
}
},
+ "lodash": {
+ "version": "4.17.11",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
+ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
+ "dev": true
+ },
"moment": {
"version": "2.22.2",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz",
@@ -1863,6 +1890,12 @@
"integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==",
"dev": true
},
+ "lodash": {
+ "version": "4.17.11",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
+ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
+ "dev": true
+ },
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
@@ -1897,6 +1930,12 @@
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz",
"integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==",
"dev": true
+ },
+ "lodash": {
+ "version": "4.17.11",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
+ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
+ "dev": true
}
}
},
@@ -3388,6 +3427,21 @@
"type-is": "~1.6.17"
},
"dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
@@ -4659,12 +4713,11 @@
"dev": true
},
"debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
- "ms": "2.0.0"
+ "ms": "^2.1.1"
}
},
"decamelize": {
@@ -5561,6 +5614,15 @@
"to-regex": "^3.0.1"
},
"dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
"define-property": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
@@ -5578,6 +5640,12 @@
"requires": {
"is-extendable": "^0.1.0"
}
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
}
}
},
@@ -5648,6 +5716,21 @@
"vary": "~1.1.2"
},
"dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
@@ -5769,6 +5852,23 @@
"debug": "2.6.9",
"mkdirp": "0.5.1",
"yauzl": "2.4.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
}
},
"extsprintf": {
@@ -5923,6 +6023,23 @@
"parseurl": "~1.3.3",
"statuses": "~1.5.0",
"unpipe": "~1.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
}
},
"find-cache-dir": {
@@ -6019,6 +6136,23 @@
"chalk": "^2.0.1",
"commander": "^2.11.0",
"debug": "^2.6.8"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
}
},
"find-up": {
@@ -7225,10 +7359,12 @@
"dev": true
},
"hoist-non-react-statics": {
- "version": "2.5.5",
- "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
- "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==",
- "dev": true
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz",
+ "integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==",
+ "requires": {
+ "react-is": "^16.7.0"
+ }
},
"homedir-polyfill": {
"version": "1.0.3",
@@ -7688,7 +7824,6 @@
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
- "dev": true,
"requires": {
"loose-envify": "^1.0.0"
}
@@ -8881,8 +9016,7 @@
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"js-yaml": {
"version": "3.13.1",
@@ -9148,10 +9282,9 @@
"dev": true
},
"lodash": {
- "version": "4.17.11",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
- "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
- "dev": true
+ "version": "4.17.14",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz",
+ "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw=="
},
"lodash._basecallback": {
"version": "3.3.1",
@@ -9316,7 +9449,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
- "dev": true,
"requires": {
"js-tokens": "^3.0.0 || ^4.0.0"
}
@@ -9829,10 +9961,9 @@
}
},
"ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
- "dev": true
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"mute-stream": {
"version": "0.0.7",
@@ -10384,8 +10515,7 @@
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
- "dev": true
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"object-copy": {
"version": "0.1.0",
@@ -10952,6 +11082,21 @@
"resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
"integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
"dev": true
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
}
}
},
@@ -11209,7 +11354,6 @@
"version": "15.7.2",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
- "dev": true,
"requires": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
@@ -11489,6 +11633,14 @@
"dev": true,
"requires": {
"hoist-non-react-statics": "^2.1.1"
+ },
+ "dependencies": {
+ "hoist-non-react-statics": {
+ "version": "2.5.5",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
+ "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==",
+ "dev": true
+ }
}
},
"react-dates": {
@@ -11527,8 +11679,7 @@
"react-is": {
"version": "16.8.6",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz",
- "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==",
- "dev": true
+ "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA=="
},
"react-lifecycles-compat": {
"version": "3.0.4",
@@ -11567,6 +11718,19 @@
"prop-types": "^15.5.8"
}
},
+ "react-redux": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-6.0.1.tgz",
+ "integrity": "sha512-T52I52Kxhbqy/6TEfBv85rQSDz6+Y28V/pf52vDWs1YRXG19mcFOGfHnY2HsNFHyhP+ST34Aih98fvt6tqwVcQ==",
+ "requires": {
+ "@babel/runtime": "^7.3.1",
+ "hoist-non-react-statics": "^3.3.0",
+ "invariant": "^2.2.4",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.7.2",
+ "react-is": "^16.8.2"
+ }
+ },
"react-router": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.0.1.tgz",
@@ -11664,6 +11828,14 @@
"object.assign": "^4.1.0",
"object.values": "^1.0.4",
"prop-types": "^15.6.0"
+ },
+ "dependencies": {
+ "hoist-non-react-statics": {
+ "version": "2.5.5",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
+ "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==",
+ "dev": true
+ }
}
},
"react-with-styles": {
@@ -11799,7 +11971,6 @@
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/redux/-/redux-4.0.4.tgz",
"integrity": "sha512-vKv4WdiJxOWKxK0yRoaK3Y4pxxB0ilzVx6dszU2W8wLxlb2yikRph4iV/ymtdJ6ZxpBLFbyrxklnT5yBbQSl3Q==",
- "dev": true,
"requires": {
"loose-envify": "^1.4.0",
"symbol-observable": "^1.2.0"
@@ -11829,8 +12000,7 @@
"regenerator-runtime": {
"version": "0.13.2",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz",
- "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==",
- "dev": true
+ "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA=="
},
"regenerator-transform": {
"version": "0.14.0",
@@ -12395,6 +12565,23 @@
"statuses": "~1.5.0"
},
"dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
@@ -12453,6 +12640,11 @@
"requires": {
"is-extendable": "^0.1.0"
}
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
@@ -12582,6 +12774,15 @@
"use": "^3.1.0"
},
"dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
"define-property": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
@@ -12599,6 +12800,12 @@
"requires": {
"is-extendable": "^0.1.0"
}
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
}
}
},
@@ -13473,8 +13680,7 @@
"symbol-observable": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
- "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==",
- "dev": true
+ "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
},
"symbol-tree": {
"version": "3.2.4",
@@ -14694,6 +14900,21 @@
"supports-color": "^2.0.0"
}
},
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
diff --git a/package.json b/package.json
index 7feae092397..6cb18a3c4c4 100644
--- a/package.json
+++ b/package.json
@@ -21,9 +21,15 @@
}
},
"dependencies": {
- "css-loader": "^2.1.1"
+ "@fresh-data/framework": "^0.7.0-beta",
+ "css-loader": "^2.1.1",
+ "debug": "^4.1.1",
+ "lodash": "^4.17.14",
+ "react-redux": "^6.0.1",
+ "redux": "^4.0.4"
},
"devDependencies": {
+ "@babel/plugin-proposal-object-rest-spread": "^7.5.5",
"@woocommerce/components": "^3.1.0",
"@woocommerce/currency": "^1.1.2",
"@woocommerce/date": "^1.2.0",