Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RNMobile] [Embed block] Detect when an embeddable URL is pasted into an empty paragraph #35204

Merged
merged 33 commits into from Oct 13, 2021
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
8e7ae99
added unstableEmbedURLOnPaste prop to activate embed detection.
jd-alexander Sep 29, 2021
3df07fa
reintegrated Embed transformation that was disabled - testing required.
jd-alexander Sep 29, 2021
1010372
Added functionality that would convert the URL to a link.
jd-alexander Sep 29, 2021
4130c4b
integrated the bottom sheet component that displays Create embed/link.
jd-alexander Sep 29, 2021
1739f62
The EmbedHandlerPicker was extracted to a standalone component.
jd-alexander Sep 30, 2021
e8854be
Updated changelog.
jd-alexander Sep 30, 2021
7390971
Merge branch 'trunk' into rnmobile/embed-block-paste-url
jd-alexander Oct 5, 2021
1b41af9
added onChange to the embed logic so that the url is visible.
jd-alexander Oct 5, 2021
8f38899
only process URLs on empty paragraphs.
jd-alexander Oct 5, 2021
be43155
utilized useCallback to lessen re-renders.
jd-alexander Oct 5, 2021
579def2
added pickerOptions to the dependencies array.
jd-alexander Oct 5, 2021
eaec369
if there is content within a paragraph then insert the link.
jd-alexander Oct 6, 2021
00a4f9d
utilized isEmpty to check the text property of value.
jd-alexander Oct 6, 2021
74da669
memoized the EmbedHandlerPicker
jd-alexander Oct 6, 2021
7975a76
Added a comment to clarify changes.
jd-alexander Oct 6, 2021
0334b75
Fixed issue with release notes that doesn't follow format.
jd-alexander Oct 6, 2021
b961e49
Merge branch 'trunk' into rnmobile/embed-block-paste-url
jd-alexander Oct 6, 2021
6cd4591
refactored embed paste logic.
jd-alexander Oct 6, 2021
9d3a548
disables onReplace on non embed rich-text instances.
jd-alexander Oct 6, 2021
f59cff0
Merge branch 'trunk' into rnmobile/embed-block-paste-url
jd-alexander Oct 6, 2021
b508ae8
Fixed CHANGELOG formatting issue.
jd-alexander Oct 7, 2021
9e14dd3
Fixed formatting issue in CHANGELOG.
jd-alexander Oct 7, 2021
6752e72
refactored the Embed pasting logic so that it is centralized
jd-alexander Oct 8, 2021
e0ce9a3
Changed the variable to something more meaningful.
jd-alexander Oct 8, 2021
cf9d168
Removed unnecessary content length check since this is already checked.
jd-alexander Oct 8, 2021
9b8a322
Merge branch 'trunk' into rnmobile/embed-block-paste-url
jd-alexander Oct 8, 2021
7d22100
if block is not derived then we check if content is a string url.
jd-alexander Oct 11, 2021
1683920
Refactor embed paste logic
fluiddot Oct 11, 2021
b5174f3
Merge branch 'rnmobile/embed-block-paste-url' of github.com:WordPress…
fluiddot Oct 11, 2021
f3f4603
Removed the __unstableEmbedURLOnPaste flag from mode logic for Heading
jd-alexander Oct 11, 2021
5eadfc9
Resolved isPastedURL logic issues to fix HEADING bug.
jd-alexander Oct 13, 2021
a6e9141
Merge branch 'trunk' into rnmobile/embed-block-paste-url
jd-alexander Oct 13, 2021
5a15e99
Fixed typo in CHANGELOG.
jd-alexander Oct 13, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,67 @@
/**
* External dependencies
*/
import { noop } from 'lodash';

/**
* WordPress dependencies
*/
import {
forwardRef,
useRef,
useImperativeHandle,
memo,
useCallback,
} from '@wordpress/element';
import { Picker } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

const DEFAULT_PICKER_OPTIONS = [
{
id: 'createEmbed',
label: __( 'Create embed' ),
value: 'createEmbed',
onSelect: noop,
},
{
id: 'createLink',
label: __( 'Create link' ),
value: 'createLink',
onSelect: noop,
},
];

const EmbedHandlerPicker = forwardRef( ( {}, ref ) => {
const pickerRef = useRef();
const pickerOptions = useRef( DEFAULT_PICKER_OPTIONS ).current;

const onPickerSelect = useCallback(
( value ) => {
const selectedItem = pickerOptions.find(
( item ) => item.value === value
);
selectedItem.onSelect();
},
[ pickerOptions ]
);

useImperativeHandle( ref, () => ( {
presentPicker: ( { createEmbed, createLink } ) => {
pickerOptions[ 0 ].onSelect = createEmbed;
pickerOptions[ 1 ].onSelect = createLink;
pickerRef.current?.presentPicker();
},
} ) );

return (
<Picker
ref={ pickerRef }
fluiddot marked this conversation as resolved.
Show resolved Hide resolved
options={ pickerOptions }
onChange={ onPickerSelect }
hideCancelButton
leftAlign
/>
);
} );

export default memo( EmbedHandlerPicker );
47 changes: 38 additions & 9 deletions packages/block-editor/src/components/rich-text/index.native.js
Expand Up @@ -55,7 +55,9 @@ import {
getMultilineTag,
getAllowedFormats,
isShortcode,
createLinkInParagraph,
} from './utils';
import EmbedHandlerPicker from './embed-handler-picker';

const wrapperClasses = 'block-editor-rich-text';
const classes = 'block-editor-rich-text__editable';
Expand Down Expand Up @@ -118,6 +120,7 @@ function RichTextWrapper(
const fallbackRef = useRef();
const { clientId, isSelected: blockIsSelected } = useBlockEditContext();
const nativeProps = useNativeProps();
const embedHandlerPickerRef = useRef();
const selector = ( select ) => {
const {
isCaretWithinFormattedText,
Expand Down Expand Up @@ -439,21 +442,14 @@ function RichTextWrapper(
mode = 'BLOCKS';
}

if (
__unstableEmbedURLOnPaste &&
isEmpty( value ) &&
isURL( plainText.trim() )
) {
mode = 'BLOCKS';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should preserve this line to be executed (if the conditions of line 448 are satisfied) because it's used in the pasteHandler call below. Maybe this has to do with the issue you encountered in this comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yes. Good eye!

}

const content = pasteHandler( {
HTML: html,
plainText,
mode,
tagName,
preserveWhiteSpace,
} );
const isPastedURL = isURL( plainText.trim() );

if ( typeof content === 'string' ) {
let valueToInsert = create( { html: content } );
Expand All @@ -472,7 +468,39 @@ function RichTextWrapper(

onChange( insert( value, valueToInsert ) );
} else if ( content.length > 0 ) {
if ( onReplace && isEmpty( value ) ) {
if ( isPastedURL ) {
onChange( insert( value, create( { text: plainText } ) ) );

if ( ! isEmpty( value ) ) {
return;
}

const derivedBlock = content[ 0 ];
const { name } = derivedBlock;

// When an URL is pasted in an empty paragraph then the EmbedHandlerPicker should showcase options allowing the transformation of that URL
// into either an Embed block or a link within the target paragraph. If the paragraph is non-empty, the URL is pasted as text.
if ( __unstableEmbedURLOnPaste && name === 'core/embed' ) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On Android, this logic fails because it is not resolving the pasted URL as a block. The content that is parsed by the PasteHandler is a string. Further details on the differences in the clipboard behavior as expounded on this comment #35204 (comment) refactoring to accommodate this case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should handle the else case of this if block, probably we could call the same as in line 504:

onReplace( content, content.length - 1, -1 );

This way if we copy, for example, a Heading block that contains an URL, it will paste that block.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@carlosgprim we may have to tag-team on resolving the final Heading block issue with the Embed Paste URL. This is what is taking place.

There are cases where copying a URL from a Heading block results in a string when pasted instead of being pasted as a block when it’s processed by the PasteHandler therefore the bottom sheet is still shown. I am not sure how we can figure out the source of the copy/paste if only a string is returned. Still investigating.

mode = 'BLOCKS';
// Embed handler
embedHandlerPickerRef.current?.presentPicker( {
createEmbed: () => {
if ( onReplace ) {
jd-alexander marked this conversation as resolved.
Show resolved Hide resolved
onReplace(
content,
content.length - 1,
-1
);
}
},
createLink: () =>
createLinkInParagraph(
plainText.trim(),
onReplace
),
} );
}
} else if ( onReplace && isEmpty( value ) ) {
onReplace( content, content.length - 1, -1 );
} else {
splitValue( value, content );
Expand Down Expand Up @@ -650,6 +678,7 @@ function RichTextWrapper(
/>
) }
</Autocomplete>
<EmbedHandlerPicker ref={ embedHandlerPickerRef } />
</>
) }
</RichText>
Expand Down
16 changes: 16 additions & 0 deletions packages/block-editor/src/components/rich-text/utils.js
Expand Up @@ -3,6 +3,8 @@
*/
import { regexp } from '@wordpress/shortcode';
import deprecated from '@wordpress/deprecated';
import { renderToString } from '@wordpress/element';
import { createBlock } from '@wordpress/blocks';

export function addActiveFormats( value, activeFormats ) {
if ( activeFormats?.length ) {
Expand Down Expand Up @@ -60,3 +62,17 @@ export function getAllowedFormats( {
getAllowedFormats.EMPTY_ARRAY = [];

export const isShortcode = ( text ) => regexp( '.*' ).test( text );

/**
* Creates a link from pasted URL.
* Creates a paragraph block containing a link to the URL, and calls `onReplace`.
*
* @param {string} url The URL that could not be embedded.
* @param {Function} onReplace Function to call with the created fallback block.
*/
export function createLinkInParagraph( url, onReplace ) {
const link = <a href={ url }>{ url }</a>;
onReplace(
createBlock( 'core/paragraph', { content: renderToString( link ) } )
);
}
2 changes: 2 additions & 0 deletions packages/block-library/src/embed/transforms.native.js
@@ -1,9 +1,11 @@
/**
* Internal dependencies
*/
import webTransforms from './transforms.js';
import transformationCategories from '../transformationCategories';

const transforms = {
...webTransforms,
jd-alexander marked this conversation as resolved.
Show resolved Hide resolved
supportedMobileTransforms: transformationCategories.media,
};

Expand Down
1 change: 1 addition & 0 deletions packages/block-library/src/paragraph/edit.native.js
Expand Up @@ -83,6 +83,7 @@ function ParagraphBlock( {
onRemove={ onReplace ? () => onReplace( [] ) : undefined }
placeholder={ placeholder || __( 'Start writing…' ) }
textAlign={ align }
__unstableEmbedURLOnPaste
/>
</>
);
Expand Down
5 changes: 3 additions & 2 deletions packages/react-native-editor/CHANGELOG.md
Expand Up @@ -11,6 +11,7 @@ For each user feature we should also add a importance categorization label to i

## Unreleased
- [*] [Embed block] Fix inline preview cut-off when editing URL [#35321]
- [**] [Embed block] Detect when an embeddable URL is pasted into an empty paragraph. [#35204]
- [*] [Unsupported Block Editor] Fix text selection bug for Android [#34668]

## 1.63.0
Expand All @@ -22,8 +23,8 @@ For each user feature we should also add a importance categorization label to i
- Same as 1.62.1 but with the changelog.

## 1.62.1
- [**] Image block: fix height and border regression. [https://github.com/WordPress/gutenberg/pull/34957]
- [**] Column block: fix width float attribute cut off. [#35061]
- [**] Image block: fix height and border regression. [#34957]
- [**] Column block: fix width attribute flout cutoff. [#34604]

## 1.62.0
- [**] [Embed block] Implement WP embed preview component [#34004]
Expand Down