diff --git a/core-blocks/embed/index.js b/core-blocks/embed/index.js index 2004bc7b01274..e80bc4bb1398b 100644 --- a/core-blocks/embed/index.js +++ b/core-blocks/embed/index.js @@ -87,7 +87,7 @@ function getEmbedBlockSettings( { title, description, icon, category = 'embed', const { url } = ownProps.attributes; // Preview is undefined if we don't know the status of it, false if it failed, // otherwise it will be an object containing the embed response. - const preview = url ? select( 'core/block-data' ).getPreview( url ) : undefined; + const preview = url ? select( 'core' ).getPreview( url ) : undefined; return { preview, }; diff --git a/core-blocks/index.js b/core-blocks/index.js index f7f09fb84bb83..75f6b509eed70 100644 --- a/core-blocks/index.js +++ b/core-blocks/index.js @@ -12,7 +12,6 @@ import deprecated from '@wordpress/deprecated'; * Internal dependencies */ import './style.scss'; -import './store'; import * as paragraph from './paragraph'; import * as image from './image'; import * as heading from './heading'; diff --git a/core-blocks/store/actions.js b/core-blocks/store/actions.js deleted file mode 100644 index b00b6256c8605..0000000000000 --- a/core-blocks/store/actions.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Returns an action object used in signalling that the preview data for - * a given URl has been received. - * - * @param {string} url URL to preview the embed for. - * @param {Mixed} preview Preview data. - * - * @return {Object} Action object. - */ -export function receivePreview( url, preview ) { - return { - type: 'RECEIVE_EMBED_PREVIEW', - url, - preview, - }; -} diff --git a/core-blocks/store/index.js b/core-blocks/store/index.js deleted file mode 100644 index 409648c58f965..0000000000000 --- a/core-blocks/store/index.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * WordPress Dependencies - */ -import { registerStore } from '@wordpress/data'; - -/** - * Internal dependencies - */ -import reducer from './reducer'; -import * as selectors from './selectors'; -import * as actions from './actions'; -import * as resolvers from './resolvers'; - -const store = registerStore( 'core/block-data', { - reducer, - actions, - selectors, - resolvers, -} ); - -export default store; diff --git a/core-blocks/store/reducer.js b/core-blocks/store/reducer.js deleted file mode 100644 index c5abf92b5d9f8..0000000000000 --- a/core-blocks/store/reducer.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * WordPress dependencies - */ -import { combineReducers } from '@wordpress/data'; - -export function embedPreviews( state = {}, action ) { - switch ( action.type ) { - case 'RECEIVE_EMBED_PREVIEW': - const { url, preview } = action; - return { - ...state, - [ url ]: preview, - }; - } - return state; -} - -export default combineReducers( { - embedPreviews, -} ); diff --git a/core-blocks/store/resolvers.js b/core-blocks/store/resolvers.js deleted file mode 100644 index 6a9dc69303916..0000000000000 --- a/core-blocks/store/resolvers.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * WordPress dependencies - */ -import apiRequest from '@wordpress/api-request'; - -/** - * External dependencies - */ -import { stringify } from 'querystring'; - -/** - * Internal dependencies - */ -import { - receivePreview, -} from './actions'; - -/** - * Requests a preview from the from the Embed API. - * - * @param {Object} state State tree - * @param {string} url URL to get the preview for. - */ -export async function* getPreview( state, url ) { - try { - const embedProxyResponse = await apiRequest( { path: `/oembed/1.0/proxy?${ stringify( { url } ) }` } ); - yield receivePreview( url, embedProxyResponse ); - } catch ( error ) { - // Embed API 404s if the URL cannot be embedded, so we have to catch the error from the apiRequest here. - yield receivePreview( url, false ); - } -} diff --git a/core-blocks/store/selectors.js b/core-blocks/store/selectors.js deleted file mode 100644 index 10f90024501b4..0000000000000 --- a/core-blocks/store/selectors.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Returns the embed for the given URL. - * - * @param {Object} state Data state. - * @param {string} url Embedded URL. - * - * @return {Mixed} Undefined if the preview has not been fetched, false if the URL cannot be embedded, array of embed preview data if the preview has been fetched. - */ -export function getPreview( state, url ) { - const preview = state.embedPreviews[ url ]; - - if ( ! preview ) { - return preview; - } - - const oEmbedLinkCheck = '' + url + ''; - - if ( oEmbedLinkCheck === preview.html ) { - // just a link to the url, it's oEmbed being helpful and creating a link for us, not actually embedding content - return false; - } - - return preview; -} diff --git a/core-blocks/store/test/reducer.js b/core-blocks/store/test/reducer.js deleted file mode 100644 index 99359a5751d60..0000000000000 --- a/core-blocks/store/test/reducer.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * External dependencies - */ -import deepFreeze from 'deep-freeze'; - -/** - * Internal dependencies - */ -import { embedPreviews } from '../reducer'; - -describe( 'embedPreviews()', () => { - it( 'returns an empty object by default', () => { - const state = embedPreviews( undefined, {} ); - - expect( state ).toEqual( {} ); - } ); - - it( 'returns with received preview', () => { - const originalState = deepFreeze( {} ); - const state = embedPreviews( originalState, { - type: 'RECEIVE_EMBED_PREVIEW', - url: 'http://twitter.com/notnownikki', - preview: { data: 42 }, - } ); - - expect( state ).toEqual( { - 'http://twitter.com/notnownikki': { data: 42 }, - } ); - } ); -} ); diff --git a/core-blocks/store/test/resolvers.js b/core-blocks/store/test/resolvers.js deleted file mode 100644 index bb40b747ca0fa..0000000000000 --- a/core-blocks/store/test/resolvers.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * WordPress dependencies - */ -import apiRequest from '@wordpress/api-request'; - -/** - * External dependencies - */ -import { stringify } from 'querystring'; - -/** - * Internal dependencies - */ -import { getPreview } from '../resolvers'; -import { receivePreview } from '../actions'; - -jest.mock( '@wordpress/api-request' ); - -describe( 'getPreview', () => { - const SUCCESSFUL_EMBED_RESPONSE = { data: '

some html

' }; - const UNEMBEDDABLE_RESPONSE = false; - const EMBEDDABLE_URL = 'http://twitter.com/notnownikki'; - const UNEMBEDDABLE_URL = 'http://example.com/'; - - beforeAll( () => { - apiRequest.mockImplementation( ( options ) => { - if ( options.path === `/oembed/1.0/proxy?${ stringify( { url: EMBEDDABLE_URL } ) }` ) { - return Promise.resolve( SUCCESSFUL_EMBED_RESPONSE ); - } - throw 404; - } ); - } ); - - it( 'yields with fetched embed preview', async () => { - const fulfillment = getPreview( {}, EMBEDDABLE_URL ); - const received = ( await fulfillment.next() ).value; - expect( received ).toEqual( receivePreview( EMBEDDABLE_URL, SUCCESSFUL_EMBED_RESPONSE ) ); - } ); - - it( 'yields false if the URL cannot be embedded', async () => { - const fulfillment = getPreview( {}, UNEMBEDDABLE_URL ); - const received = ( await fulfillment.next() ).value; - expect( received ).toEqual( receivePreview( UNEMBEDDABLE_URL, UNEMBEDDABLE_RESPONSE ) ); - } ); -} ); diff --git a/core-blocks/store/test/selectors.js b/core-blocks/store/test/selectors.js deleted file mode 100644 index 57643a1a9245e..0000000000000 --- a/core-blocks/store/test/selectors.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * External dependencies - */ -import deepFreeze from 'deep-freeze'; - -/** - * Internal dependencies - */ -import { getPreview } from '../selectors'; - -describe( 'getPreview()', () => { - it( 'returns preview stored for url', () => { - let state = deepFreeze( { - embedPreviews: {}, - } ); - expect( getPreview( state, 'http://example.com/' ) ).toBe( undefined ); - - state = deepFreeze( { - embedPreviews: { - 'http://example.com/': { data: 42 }, - }, - } ); - expect( getPreview( state, 'http://example.com/' ) ).toEqual( { data: 42 } ); - } ); - - it( 'returns false if the preview html is just a single link', () => { - const state = deepFreeze( { - embedPreviews: { - 'http://example.com/': { html: 'http://example.com/' }, - }, - } ); - expect( getPreview( state, 'http://example.com/' ) ).toEqual( false ); - } ); -} ); diff --git a/core-data/actions.js b/core-data/actions.js index e25d714ca113a..a79161b7fe42d 100644 --- a/core-data/actions.js +++ b/core-data/actions.js @@ -67,3 +67,20 @@ export function receiveThemeSupportsFromIndex( index ) { themeSupports: index.theme_supports, }; } + +/** + * Returns an action object used in signalling that the preview data for + * a given URl has been received. + * + * @param {string} url URL to preview the embed for. + * @param {Mixed} preview Preview data. + * + * @return {Object} Action object. + */ +export function receivePreview( url, preview ) { + return { + type: 'RECEIVE_EMBED_PREVIEW', + url, + preview, + }; +} diff --git a/core-data/reducer.js b/core-data/reducer.js index a26a5bbccfab7..a846db2951dfe 100644 --- a/core-data/reducer.js +++ b/core-data/reducer.js @@ -135,6 +135,26 @@ function entity( entityConfig ) { }; } +/** + * Reducer managing embed preview data. + * + * @param {Object} state Current state. + * @param {Object} action Dispatched action. + * + * @return {Object} Updated state. + */ +export function embedPreviews( state = {}, action ) { + switch ( action.type ) { + case 'RECEIVE_EMBED_PREVIEW': + const { url, preview } = action; + return { + ...state, + [ url ]: preview, + }; + } + return state; +} + const entitiesByKind = groupBy( entitiesConfig, 'kind' ); export const entities = combineReducers( Object.entries( entitiesByKind ).reduce( ( memo, [ kind, subEntities ] ) => { const kindReducer = combineReducers( subEntities.reduce( @@ -155,4 +175,5 @@ export default combineReducers( { taxonomies, themeSupports, entities, + embedPreviews, } ); diff --git a/core-data/resolvers.js b/core-data/resolvers.js index 83bbf41c8307b..52c8d47106540 100644 --- a/core-data/resolvers.js +++ b/core-data/resolvers.js @@ -3,6 +3,11 @@ */ import apiRequest from '@wordpress/api-request'; +/** + * External dependencies + */ +import { stringify } from 'querystring'; + /** * Internal dependencies */ @@ -11,6 +16,7 @@ import { receiveUserQuery, receiveEntityRecords, receiveThemeSupportsFromIndex, + receivePreview, } from './actions'; import { getEntity } from './entities'; @@ -65,3 +71,19 @@ export async function* getThemeSupports() { const index = await apiRequest( { path: '/' } ); yield receiveThemeSupportsFromIndex( index ); } + +/** + * Requests a preview from the from the Embed API. + * + * @param {Object} state State tree + * @param {string} url URL to get the preview for. + */ +export async function* getPreview( state, url ) { + try { + const embedProxyResponse = await apiRequest( { path: `/oembed/1.0/proxy?${ stringify( { url } ) }` } ); + yield receivePreview( url, embedProxyResponse ); + } catch ( error ) { + // Embed API 404s if the URL cannot be embedded, so we have to catch the error from the apiRequest here. + yield receivePreview( url, false ); + } +} diff --git a/core-data/selectors.js b/core-data/selectors.js index a7f09297bf609..af5ed8a68b752 100644 --- a/core-data/selectors.js +++ b/core-data/selectors.js @@ -145,3 +145,28 @@ export const getEntityRecords = createSelector( export function getThemeSupports( state ) { return state.themeSupports; } + +/** + * Returns the embed preview for the given URL. + * + * @param {Object} state Data state. + * @param {string} url Embedded URL. + * + * @return {Mixed} Undefined if the preview has not been fetched, false if the URL cannot be embedded, array of embed preview data if the preview has been fetched. + */ +export function getPreview( state, url ) { + const preview = state.embedPreviews[ url ]; + + if ( ! preview ) { + return preview; + } + + const oEmbedLinkCheck = '' + url + ''; + + if ( oEmbedLinkCheck === preview.html ) { + // just a link to the url, it's oEmbed being helpful and creating a link for us, not actually embedding content + return false; + } + + return preview; +} diff --git a/test/__mocks__/@wordpress/api-request.js b/core-data/test/__mocks__/@wordpress/api-request.js similarity index 100% rename from test/__mocks__/@wordpress/api-request.js rename to core-data/test/__mocks__/@wordpress/api-request.js diff --git a/core-data/test/reducer.js b/core-data/test/reducer.js index 5d608b025910c..c434d0694b578 100644 --- a/core-data/test/reducer.js +++ b/core-data/test/reducer.js @@ -6,7 +6,7 @@ import deepFreeze from 'deep-freeze'; /** * Internal dependencies */ -import { terms, entities } from '../reducer'; +import { terms, entities, embedPreviews } from '../reducer'; describe( 'terms()', () => { it( 'returns an empty object by default', () => { @@ -78,3 +78,24 @@ describe( 'entities', () => { } ); } ); } ); + +describe( 'embedPreviews()', () => { + it( 'returns an empty object by default', () => { + const state = embedPreviews( undefined, {} ); + + expect( state ).toEqual( {} ); + } ); + + it( 'returns with received preview', () => { + const originalState = deepFreeze( {} ); + const state = embedPreviews( originalState, { + type: 'RECEIVE_EMBED_PREVIEW', + url: 'http://twitter.com/notnownikki', + preview: { data: 42 }, + } ); + + expect( state ).toEqual( { + 'http://twitter.com/notnownikki': { data: 42 }, + } ); + } ); +} ); diff --git a/core-data/test/resolvers.js b/core-data/test/resolvers.js index a2e3660c92b58..f56666b673bea 100644 --- a/core-data/test/resolvers.js +++ b/core-data/test/resolvers.js @@ -3,11 +3,16 @@ */ import apiRequest from '@wordpress/api-request'; +/** + * External dependencies + */ +import { stringify } from 'querystring'; + /** * Internal dependencies */ -import { getCategories, getEntityRecord, getEntityRecords } from '../resolvers'; -import { receiveTerms, receiveEntityRecords } from '../actions'; +import { getCategories, getEntityRecord, getEntityRecords, getPreview } from '../resolvers'; +import { receiveTerms, receiveEntityRecords, receivePreview } from '../actions'; jest.mock( '@wordpress/api-request' ); @@ -67,3 +72,31 @@ describe( 'getEntityRecords', () => { expect( received ).toEqual( receiveEntityRecords( 'root', 'postType', Object.values( POST_TYPES ) ) ); } ); } ); + +describe( 'getPreview', () => { + const SUCCESSFUL_EMBED_RESPONSE = { data: '

some html

' }; + const UNEMBEDDABLE_RESPONSE = false; + const EMBEDDABLE_URL = 'http://twitter.com/notnownikki'; + const UNEMBEDDABLE_URL = 'http://example.com/'; + + beforeAll( () => { + apiRequest.mockImplementation( ( options ) => { + if ( options.path === `/oembed/1.0/proxy?${ stringify( { url: EMBEDDABLE_URL } ) }` ) { + return Promise.resolve( SUCCESSFUL_EMBED_RESPONSE ); + } + throw 404; + } ); + } ); + + it( 'yields with fetched embed preview', async () => { + const fulfillment = getPreview( {}, EMBEDDABLE_URL ); + const received = ( await fulfillment.next() ).value; + expect( received ).toEqual( receivePreview( EMBEDDABLE_URL, SUCCESSFUL_EMBED_RESPONSE ) ); + } ); + + it( 'yields false if the URL cannot be embedded', async () => { + const fulfillment = getPreview( {}, UNEMBEDDABLE_URL ); + const received = ( await fulfillment.next() ).value; + expect( received ).toEqual( receivePreview( UNEMBEDDABLE_URL, UNEMBEDDABLE_RESPONSE ) ); + } ); +} ); diff --git a/core-data/test/selectors.js b/core-data/test/selectors.js index ab508091f8922..ce0a553407085 100644 --- a/core-data/test/selectors.js +++ b/core-data/test/selectors.js @@ -6,7 +6,7 @@ import deepFreeze from 'deep-freeze'; /** * Internal dependencies */ -import { getTerms, isRequestingCategories, getEntityRecord, getEntityRecords } from '../selectors'; +import { getTerms, isRequestingCategories, getEntityRecord, getEntityRecords, getPreview } from '../selectors'; import { select } from '@wordpress/data'; jest.mock( '@wordpress/data', () => ( { @@ -130,3 +130,28 @@ describe( 'getEntityRecords', () => { ] ); } ); } ); + +describe( 'getPreview()', () => { + it( 'returns preview stored for url', () => { + let state = deepFreeze( { + embedPreviews: {}, + } ); + expect( getPreview( state, 'http://example.com/' ) ).toBe( undefined ); + + state = deepFreeze( { + embedPreviews: { + 'http://example.com/': { data: 42 }, + }, + } ); + expect( getPreview( state, 'http://example.com/' ) ).toEqual( { data: 42 } ); + } ); + + it( 'returns false if the preview html is just a single link', () => { + const state = deepFreeze( { + embedPreviews: { + 'http://example.com/': { html: 'http://example.com/' }, + }, + } ); + expect( getPreview( state, 'http://example.com/' ) ).toEqual( false ); + } ); +} );