-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[RNMobile] Embed block: Unavailable preview fallback (#33266)
* Add /oembed path to supported endpoints * Check embed preview status differently on native * Use ActivityIndicator in embed loading * Add loading and link styles * Render different view in placholder if link cannot be embedded * Show thumbnail or cannot embed text in preview * Update embed loading style * Fix spinner on Android * Remove todo * Fix loading dark background * Fix thumbnail aspectRatio * Add embed no preview component and refactor styles * Add requestReview to RN bridge (iOS) * Add requestReview to RN bridge (Android) * Add request preview to Gutenberg demo * [TEST] Enable embed block * Update texts of embed no preview component * Add help icon to embed no preview component * Add dark mode styles to help icon * Add native version of embed edit component * Add embed preview image style * Fix style import in embed preview * Remove platform related code from embed block * Disable embed block (enabled only for dev) * Update a comment in native version of embed block * Show toast text on preview request call in demo project * Move onSubmit to inline callbacks Co-authored-by: Ceyhun Ozugur <ceyhunozugur@gmail.com>
- Loading branch information
Showing
16 changed files
with
582 additions
and
122 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
/** | ||
* Internal dependencies | ||
*/ | ||
import { | ||
createUpgradedEmbedBlock, | ||
getAttributesFromPreview, | ||
getEmbedInfoByProvider, | ||
} from './util'; | ||
import EmbedControls from './embed-controls'; | ||
import { embedContentIcon } from './icons'; | ||
import EmbedLoading from './embed-loading'; | ||
import EmbedPlaceholder from './embed-placeholder'; | ||
import EmbedPreview from './embed-preview'; | ||
import EmbedBottomSheet from './embed-bottom-sheet'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { _x } from '@wordpress/i18n'; | ||
import { useState, useEffect } from '@wordpress/element'; | ||
import { useSelect } from '@wordpress/data'; | ||
import { | ||
useBlockProps, | ||
store as blockEditorStore, | ||
} from '@wordpress/block-editor'; | ||
import { store as coreStore } from '@wordpress/core-data'; | ||
import { View } from '@wordpress/primitives'; | ||
|
||
const EmbedEdit = ( props ) => { | ||
const { | ||
attributes: { | ||
providerNameSlug, | ||
previewable, | ||
responsive, | ||
url: attributesUrl, | ||
}, | ||
attributes, | ||
isSelected, | ||
onReplace, | ||
setAttributes, | ||
insertBlocksAfter, | ||
onFocus, | ||
clientId, | ||
} = props; | ||
|
||
const defaultEmbedInfo = { | ||
title: _x( 'Embed', 'block title' ), | ||
icon: embedContentIcon, | ||
}; | ||
const { icon, title } = | ||
getEmbedInfoByProvider( providerNameSlug ) || defaultEmbedInfo; | ||
|
||
const [ url, setURL ] = useState( attributesUrl ); | ||
|
||
const { wasBlockJustInserted } = useSelect( | ||
( select ) => ( { | ||
wasBlockJustInserted: select( | ||
blockEditorStore | ||
).wasBlockJustInserted( clientId, 'inserter_menu' ), | ||
} ), | ||
[ clientId ] | ||
); | ||
const [ isEditingURL, setIsEditingURL ] = useState( | ||
isSelected && wasBlockJustInserted && ! url | ||
); | ||
|
||
const { preview, fetching, cannotEmbed } = useSelect( | ||
( select ) => { | ||
const { | ||
getEmbedPreview, | ||
isPreviewEmbedFallback, | ||
isRequestingEmbedPreview, | ||
} = select( coreStore ); | ||
if ( ! attributesUrl ) { | ||
return { fetching: false, cannotEmbed: false }; | ||
} | ||
|
||
const embedPreview = getEmbedPreview( attributesUrl ); | ||
const previewIsFallback = isPreviewEmbedFallback( attributesUrl ); | ||
|
||
// The external oEmbed provider does not exist. We got no type info and no html. | ||
const badEmbedProvider = | ||
embedPreview?.html === false && | ||
embedPreview?.type === undefined; | ||
// Some WordPress URLs that can't be embedded will cause the API to return | ||
// a valid JSON response with no HTML and `code` set to 404, rather | ||
// than generating a fallback response as other embeds do. | ||
const wordpressCantEmbed = embedPreview?.code === '404'; | ||
const validPreview = | ||
!! embedPreview && ! badEmbedProvider && ! wordpressCantEmbed; | ||
return { | ||
preview: validPreview ? embedPreview : undefined, | ||
fetching: isRequestingEmbedPreview( attributesUrl ), | ||
cannotEmbed: ! validPreview || previewIsFallback, | ||
}; | ||
}, | ||
[ attributesUrl ] | ||
); | ||
|
||
/** | ||
* @return {Object} Attributes derived from the preview, merged with the current attributes. | ||
*/ | ||
const getMergedAttributes = () => { | ||
const { allowResponsive, className } = attributes; | ||
return { | ||
...attributes, | ||
...getAttributesFromPreview( | ||
preview, | ||
title, | ||
className, | ||
responsive, | ||
allowResponsive | ||
), | ||
}; | ||
}; | ||
|
||
useEffect( () => { | ||
if ( ! preview?.html || ! cannotEmbed || fetching ) { | ||
return; | ||
} | ||
// At this stage, we're not fetching the preview and know it can't be embedded, | ||
// so try removing any trailing slash, and resubmit. | ||
const newURL = attributesUrl.replace( /\/$/, '' ); | ||
setURL( newURL ); | ||
setIsEditingURL( false ); | ||
setAttributes( { url: newURL } ); | ||
}, [ preview?.html, attributesUrl ] ); | ||
|
||
// Handle incoming preview | ||
useEffect( () => { | ||
if ( preview && ! isEditingURL ) { | ||
// Even though we set attributes that get derived from the preview, | ||
// we don't access them directly because for the initial render, | ||
// the `setAttributes` call will not have taken effect. If we're | ||
// rendering responsive content, setting the responsive classes | ||
// after the preview has been rendered can result in unwanted | ||
// clipping or scrollbars. The `getAttributesFromPreview` function | ||
// that `getMergedAttributes` uses is memoized so that we're not | ||
// calculating them on every render. | ||
setAttributes( getMergedAttributes() ); | ||
if ( onReplace ) { | ||
const upgradedBlock = createUpgradedEmbedBlock( | ||
props, | ||
getMergedAttributes() | ||
); | ||
|
||
if ( upgradedBlock ) { | ||
onReplace( upgradedBlock ); | ||
} | ||
} | ||
} | ||
}, [ preview, isEditingURL ] ); | ||
|
||
const blockProps = useBlockProps(); | ||
|
||
if ( fetching ) { | ||
return ( | ||
<View { ...blockProps }> | ||
<EmbedLoading /> | ||
</View> | ||
); | ||
} | ||
|
||
const showEmbedPlaceholder = ! preview || cannotEmbed; | ||
|
||
return ( | ||
<> | ||
{ showEmbedPlaceholder ? ( | ||
<View { ...blockProps }> | ||
<EmbedPlaceholder | ||
icon={ icon } | ||
isSelected={ isSelected } | ||
label={ title } | ||
onPress={ ( event ) => { | ||
onFocus( event ); | ||
setIsEditingURL( true ); | ||
} } | ||
cannotEmbed={ cannotEmbed } | ||
/> | ||
</View> | ||
) : ( | ||
<> | ||
<EmbedControls | ||
showEditButton={ preview && ! cannotEmbed } | ||
switchBackToURLInput={ () => setIsEditingURL( true ) } | ||
/> | ||
<View { ...blockProps }> | ||
<EmbedPreview | ||
clientId={ clientId } | ||
icon={ icon } | ||
insertBlocksAfter={ insertBlocksAfter } | ||
isSelected={ isSelected } | ||
label={ title } | ||
preview={ preview } | ||
previewable={ previewable } | ||
url={ url } | ||
/> | ||
</View> | ||
</> | ||
) } | ||
<EmbedBottomSheet | ||
value={ url } | ||
isVisible={ isEditingURL } | ||
onClose={ () => setIsEditingURL( false ) } | ||
onSubmit={ ( value ) => { | ||
// On native, the URL change is only notified when submitting, | ||
// and not via 'onChange', so we have to explicitly set the URL. | ||
setURL( value ); | ||
|
||
setIsEditingURL( false ); | ||
setAttributes( { url: value } ); | ||
} } | ||
/> | ||
</> | ||
); | ||
}; | ||
|
||
export default EmbedEdit; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { __ } from '@wordpress/i18n'; | ||
import { ToolbarButton, ToolbarGroup } from '@wordpress/components'; | ||
import { BlockControls } from '@wordpress/block-editor'; | ||
import { edit } from '@wordpress/icons'; | ||
|
||
const EmbedControls = ( { showEditButton, switchBackToURLInput } ) => ( | ||
<BlockControls> | ||
<ToolbarGroup> | ||
{ showEditButton && ( | ||
<ToolbarButton | ||
label={ __( 'Edit URL' ) } | ||
icon={ edit } | ||
onClick={ switchBackToURLInput } | ||
/> | ||
) } | ||
</ToolbarGroup> | ||
</BlockControls> | ||
); | ||
|
||
export default EmbedControls; |
Oops, something went wrong.