-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
[Mobile]: Add Image Caption Styling #14883
[Mobile]: Add Image Caption Styling #14883
Conversation
Adds ability to style image caption with bold, italic, links, and strikethrough (wordpress-mobile/gutenberg-mobile#574). Adds ability for image captions to wrap to multiple lines instead of getting cut off (wordpress-mobile/gutenberg-mobile#590).
The previous approach of setting the child component's `isSelected` prop at the time the child was rendered based on `this.props.isSelected && this.state.isCaptionSelected` did not work when: (a) the child was selected (so its `isSelected` prop was true); (b) the child component had no text; and (c) the parent's `isSelected` prop was changed to false (i.e., another block was selected). Because the child component is not rendered when both the parent's `isSelected` prop is false and the caption does not contain any text, the child component's `onBlur` prop function (the `onCaptionBlur function) would not be called and update the `isCaptionSelected` state to be false. This bug shows when a user selects an empty caption, then selects a different block (thereby hiding the caption since it is empty), and then re-selects the image with the empty caption. In that scenario, upon re-selecting the image with the empty caption, the empty caption would appear and immediately be selected instead of just appearing. This bug would not appear if there was any text in the caption, because then the caption would always render and its `onBlur` prop function would be called.
93b7f02
to
a738017
Compare
componentWillReceiveProps( nextProps ) { | ||
this.setState( ( state ) => ( { | ||
isCaptionSelected: nextProps.isSelected && state.isCaptionSelected, | ||
} ) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am guarding the isCaptionSelected
state here with the image block's isSelected
prop in order to avoid a noticeable flicker in the toolbar buttons when changing to another block with buttons. There seems to be a lag between the image block's isSelected
prop being updated to false and its isCaptionSelected
state being updated to false by the onCaptionBlur
function (which is passed as the caption's onBlur
prop). That lag created a split second where the toolbar buttons for both the caption and the newly-selected block were shown at the same time.
My original approach was to just set the caption's isSelected
prop in the image block's render function as this.props.isSelected && this.state.isCaptionSelected
. But this did not work when:
(1) the caption was selected (so the image's isCaptionSelected
state was true);
(2) the caption had no text; and
(3) another block was selected, causing the image's isSelected
prop to change to false.
When the new block is selected in (3), the caption's onBlur
prop function (the onCaptionBlur
function) would not be called and update the image block's isCaptionSelected
state to be false becasuse an empty caption is not even rendered once the image block's isSelected
prop is false:
gutenberg/packages/block-library/src/image/edit.native.js
Lines 420 to 422 in a738017
{ ( ! RichText.isEmpty( caption ) > 0 || isSelected ) && ( | |
<View style={ { padding: 12, flex: 1 } }> | |
<RichText |
Because the isCaptionSelected
state is never updated to false, if the user re-selects the image with the empty caption, the empty caption would appear and immediately be selected even though the user did not tap on the caption (the user couldn't have directly tapped on the caption because empty captions are not shown until the related image is selected). If the caption was not empty, then this bug does not appear because the caption would always render, allowing its onBlur
prop function to update the isCaptionSelected
state to false.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the explanation!
Maybe for this one it would be worth to add a short comment in code?
So someone else (or future us) that sees it will understand the intention easily.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea. I included this info in the commit body, but it makes sense to add a brief explanation here in the code.
I pushed an update addressing this.
@@ -108,6 +117,8 @@ class ImageEdit extends React.Component { | |||
} else if ( attributes.id && ! isURL( attributes.url ) ) { | |||
requestImageFailedRetryDialog( attributes.id ); | |||
} | |||
|
|||
this._caption.blur(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added this so that when an image's caption is selected and the user taps on the image, the caption will lose focus and the toolbar will be updated to only contain the edit image button.
onFocusCaption() { | ||
if ( this.props.onFocus ) { | ||
this.props.onFocus(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This this.props.onFocus()
call avoids two issues:
- Going from another
RichText
component to an image caption would result in the toolbar having the buttons for both blocks (testing scenario 8); and - None of the styling toolbar buttons would appear when switching from an unsupported block to a non-empty image caption (testing scenario 9).
Adding this same logic to the onBlurCaption
to call this.props.onBlur
did not appear to be strictly necessary because this.props.onBlur
was always undefined in my testing, but I thought it was better to add it so that onBlurCaption
and onFocusCaption
would have consistent behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @mchowning for this PR!
Code wise it looks shinny. And thanks for the explanations of your decisions! 🎉
Android:
- All testing scenarios are working perfectly! Nicely done :)
- I did find a small bug pressing the Enter key on the caption field:
The gif is on the example app but I also was able to reproduce it on WPiAndroid.
iOS:
- All testing scenarios are working perfectly too!
- Pressing Enter also has problems, but this one seems to be on the iOS Native side. There are few chances that fixing the Android crash will also fix the iOS crash.
- There is one more issue on iOS, and this one is the one that is worrying me:
- When I tap the links button to show the Links UI, it will try to show it but it will automatically dismiss it right away.
I did some debugging to figure out if it was an iOS Native issue, since it seems to affect only to iOS. This is what I found out:
The Link UI (BottomSheet) is added to the hierarchy by the FormatEdit
component on RichText
. And it's removed from the hierarchy if RichText
is not selected. So far so good.
When the TextInput on the BottomSheet is focused (it is auto focus on iOS), the caption has to loose its focus, that triggers a blur
call. Specifically this one:
That blur
is propagated all the way until the Image
block, and that triggers our new onBlurCaption()
, setting isCaptionSelected
to false, removing FormatEdit
from the hierarchy and effectively dismissing the BottomSheet.
Why this doesn't happen on Android?
On Android I noticed that the blur
event from AztecView
is not triggered when the TextInput on the BottomSheet is focused, so the ImageBlock still thinks that the caption is focused.
After figuring this out, it feels to me that the bug is on Android side, and this implementation works thanks to that bug 😱
I'm not sure if it's expected|required that AztecView._blur
is not called on Android when Aztec looses focus by focusing another TextInput. Maybe @daniloercoli can give us some lights?
What I do know is that on iOS this will always happen, so we will need to find a way to sort this out anyway. It doesn't seem to be simple though 🤔
I wasn't expecting this complication for this task, but that's the beauty of RN and its "multiplatform" 😁
I think it's OK to deal with alignment on a separate PR, but I'd prefer if we get that one fixed before the next release |
I've checked the Android native side, and we're not doing anything special there. There is just a listener that sends an event to the JS side when a I've also checked on |
I agree! I did some more tests, and this difference also happens on the RN |
Good catch @etoledom! I had a bit of time to look into this tonight, and it appears that it is caused by the In particular, in In light of that, it's not surprising that switching the caption's
Obviously I have more investigation to do, but I wanted to put this out there in case anyone has any thoughts on what I've found so far. |
@mchowning - Thanks for clearing up where the problem is! It would be good to clear up what is the behavior we want in place before continuing fixing this. It also looks like on web they use the <!-- wp:image {"id":798} -->
<figure class="wp-block-image">
<img src="https://etoledomatomicsite01.blog/wp-content/uploads/2019/02/image_571855593746995-768x1024.jpg" alt="" class="wp-image-798"/>
<figcaption>Hello<br>world<br>multiline</figcaption>
</figure>
<!-- /wp:image --> |
I'm late to the party 😄, but it definitely looks like a platform difference. Even a new React Native app using either It may be worth keeping an eye on how the work to port |
This change brings mobile's handling of image captinos in line with the web. It also addresses a crash that was occurring when the enter key was tapped to enter a new line in an image caption.
bdb487a
to
e85aa98
Compare
Added a commit addressing the crash when tapping enter to add a new line by updating the One thing to note is that I am not setting the I have noticed that there are some crashes when pressing enter with lines that have extra spaces:
I do not think these issues are directly tied to my changes because a list block has similar issues. I also saw similar crashes when an At a high level, these crashes appear to be occurring because we are sending a |
FWIW, tested out @mchowning 's image_caption_styling/remove_onBlurCaption branch at commit mchowning/gutenberg-mobile@ed80fd1 (Gutenberg branch and commit) and the links UI bottomsheet does stay on when trying to add a link inside the image caption area 🎉. |
On iOS the display of the link UI modal was causing the `onBlurCaption` function to be called, which would update the image caption's `isSelected` prop to false. That would, in turn, immediately remove the just-displayed modal. Restructuring the logic to not like this to not use the image caption's `onBlur` function avoids that issue.
Added 4c4801a to address this issue (thanks for testing this out @hypest). As already discussed, this occurred because showing that modal causes a call to the previously active text field's Issues that must be addressed before merge:
Other issues
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @mchowning - Great job working on the issues! 🎉
The LinksUI problem on iOS is completely solved ✅
The Red Screen on enter is solved (Android) ✅
The app crash on enter is solved (iOS) ✅
Thanks also for creating the tickets for the issues found beyond this PR 🙏
I found one particular case where the multiline caption behaves unexpectedly on Android:
These are the steps:
- Insert an Image on an Image block.
- Write a one line caption.
- Press Enter.
- Write another line.
- Press Enter.
- Press
<-
(Arrow back button) to exit the editor saving a Draft. - Open the recently saved Draft.
- You will see a "Block Validation" error:
What I see when switching to HTML mode is this:
<!-- wp:image {"id":790} -->
<figure class="wp-block-image">
<img src="https://etoledomatomicsite01.blog/wp-content/uploads/2019/02/img_1041.jpg" alt="" class="wp-image-790"/>
<figcaption>
Thanks for<br>I will be<br>Thanks</figcaption><br>
</figcaption>
</figure>
<!-- /wp:image -->
If you notice, there is an extra </figcaption>
by the end of the text content.
On iOS:
On iOS there is also a small issue with multiline, I think that the cause is the same even though it presents itself differently:
<!-- wp:image -->
<figure class="wp-block-image">
<img src="https://cldup.com/cXyG__fTLN.jpg" alt=""/>
<figcaption>
<p>First line</p>Second line<br><br>
</figcaption>
</figure>
<!-- /wp:image -->
As you see, the first line gets wrapped in p
tags. (And it's always just the first line)
Let's focus on the Android side. I'll be helping you all I can with the iOS side. 😃👍
The crash I was seeing in this PR if a new line was added to a caption after a line with "extra" spaces has been resolved by @hypest 's PR. We now avoid updating the |
Explicitly setting the tagName to figcaption caused an invalid block to be saved if a caption ended with an empty line.
@etoledom : Updated to no longer explicitly set No longer setting the |
Hey @mchowning ! Great job fixing the Tested on Android and it works great, adding
Looks good to me. I didn't see regressions and works as expected.
The Interestingly I have made it work properly on iOS by setting RichText with To move forward, I have created a feature branch Then we can start working on the alignment on a new PR. Does it sound good @mchowning ? |
Thanks @etoledom !
I think having a feature branch for this work is a really good idea. I have updated the base branch to
I have updated the target branch for that PR as well. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job @mchowning !
As we discussed, let's merge and continue in a new PR 👍
* [Mobile]: Add Image Caption Styling (#14883) * Add ability to style image caption Adds ability to style image caption with bold, italic, links, and strikethrough (wordpress-mobile/gutenberg-mobile#574). Adds ability for image captions to wrap to multiple lines instead of getting cut off (wordpress-mobile/gutenberg-mobile#590). * Address style errors from linter * Hide image toolbar controls if caption selected * Update state in componentDidMount The previous approach of setting the child component's `isSelected` prop at the time the child was rendered based on `this.props.isSelected && this.state.isCaptionSelected` did not work when: (a) the child was selected (so its `isSelected` prop was true); (b) the child component had no text; and (c) the parent's `isSelected` prop was changed to false (i.e., another block was selected). Because the child component is not rendered when both the parent's `isSelected` prop is false and the caption does not contain any text, the child component's `onBlur` prop function (the `onCaptionBlur function) would not be called and update the `isCaptionSelected` state to be false. This bug shows when a user selects an empty caption, then selects a different block (thereby hiding the caption since it is empty), and then re-selects the image with the empty caption. In that scenario, upon re-selecting the image with the empty caption, the empty caption would appear and immediately be selected instead of just appearing. This bug would not appear if there was any text in the caption, because then the caption would always render and its `onBlur` prop function would be called. * Add comment explaining isSelected guard * Update image caption tagname from p to figcaption This change brings mobile's handling of image captinos in line with the web. It also addresses a crash that was occurring when the enter key was tapped to enter a new line in an image caption. * Remove `onBlurCaption` function On iOS the display of the link UI modal was causing the `onBlurCaption` function to be called, which would update the image caption's `isSelected` prop to false. That would, in turn, immediately remove the just-displayed modal. Restructuring the logic to not like this to not use the image caption's `onBlur` function avoids that issue. * Remove tagName from caption RichText component Explicitly setting the tagName to figcaption caused an invalid block to be saved if a caption ended with an empty line. * iOS: Remove p tags from image caption paragraphs (#15366) * Add center alignment to image caption on mobile (#15257) * Adding missing style to video/style.native.scss This style was fetched from the Image block, and it was removed after replacing the TextInput with RichText on captions
Associated PR updating the gutenberg ref in gutenberg-mobile.
Description
Issue #574 notes that there are no controls for customizing the style or linking of an image caption. This PR adds that feature by converting the caption from a
TextInput
component to aRichText
component. Using aRichText
component enables the use of bold, italic, links, and strikethrough. In addition, this PR also addresses the following bugs:Center Alignment Regression
With this PR, the caption text is no longer aligned to the center because unlike
TextInput
,RichText
does not respect thetext-align: center
style. I believe part of the solution to that may be adding atextAlign
prop to theReactAztecManager
, but I need to do more investigation. My plan is to discuss my findings seperately from this PR because, even without the center alignment, I think that this PR substantially improves the user experience. Just let me know if you think we should hold off on this PR until the center alignment issue is resolved.How has this been tested?
Tested these changes on a Pixel 3 Android device in both the⚠️
gutenberg-mobile
demo app and in aWordPress-Android
WasabiDebug
build. I cannot build for iOS because my personal machine is linux, so I have NOT tested these changes on iOS.Testing Scenarios
RichText
styling buttons (bold, italic, link, strikethrough) in the toolbar with the appropriate active/inactive states and without the edit image (pencil) button in the toolbar.RichText
(paragraphs or headings are good candidates), (3) tap on the caption you edited, and there should only be the toolbar buttons for the caption (no duplicate buttons).-- Additional Test Scenarios based on PR review --
I realize there are quite a few testing scenarios here, but I did manage to break the functionality in most of these scenarios at some point while I was developing this feature (or that functionality was broken in the previous implementation), so I wanted to document them.
Checklist: