Skip to content

Conversation

@war-in
Copy link
Collaborator

@war-in war-in commented Aug 13, 2025

Details

This PR adds support for borderRadius prop in mentions styles on iOS & Android

image

Related Issues

Expensify/App#19299

Manual Tests

iOS & Android:

  1. Verify if singleline mentions have rounded edges
  2. Verify if multiline mentions have rounded edges only at the beginning and the end
  3. Verify mentions work correctly in blockquotes

Linked PRs

@war-in war-in changed the title Add mention-user border radius [iOS] Add mention-user border radius Aug 13, 2025
@war-in war-in changed the title [iOS] Add mention-user border radius [iOS] Enable borderRadius in all mention types Aug 14, 2025
Comment on lines 114 to 120
CGFloat marginLeft = _markdownUtils.markdownStyle.blockquoteMarginLeft;
CGFloat borderWidth = _markdownUtils.markdownStyle.blockquoteBorderWidth;
CGFloat paddingLeft = _markdownUtils.markdownStyle.blockquotePaddingLeft;
CGFloat shift = marginLeft + borderWidth + paddingLeft;

fragmentTextBounds.origin.x -= (paddingLeft + borderWidth) + shift * ([_depth unsignedIntValue] - 1);
fragmentTextBounds.size.width = borderWidth + shift * ([_depth unsignedIntValue] - 1);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Looks like the blockquote logic is now duplicated, can we somehow dedupe it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It was already :/
I could create a helper function to get the shift to make it look better but the logic would stay unchanged

I think we need it to be that way because boundingRect is also used in renderingSurfaceBounds

@war-in war-in requested a review from tomekzaw August 19, 2025 14:49
@war-in war-in changed the title [iOS] Enable borderRadius in all mention types [iOS & Android] Enable borderRadius in all mention types Aug 20, 2025
@war-in war-in marked this pull request as ready for review August 20, 2025 14:48
@war-in war-in requested a review from jmusial August 21, 2025 10:24
@war-in
Copy link
Collaborator Author

war-in commented Aug 22, 2025

I fixed last known iOS issue in this commit - 28b6a13

Before
image

After
image

@war-in war-in requested a review from tomekzaw August 22, 2025 09:33
Copy link
Collaborator

@jmusial jmusial left a comment

Choose a reason for hiding this comment

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

Dunno if those are connected to this PR, or just existing bugs. In all cases, it's just on mobile devices

  1. android, switching to single line breaks highlights :(
Screen.Recording.2025-08-22.at.13.04.20.mov
  1. android, Cursor not visible when within codeblock or mention
Screen.Recording.2025-08-22.at.13.08.38.mov

@jmusial
Copy link
Collaborator

jmusial commented Aug 22, 2025

Very much a nitpick, but when having a multiline mention starting bottom border radius and finish top, should not apply. Same issue for web

image

@jmusial
Copy link
Collaborator

jmusial commented Aug 22, 2025

Na ios dzieją się rzeczy niestworzone :( Ale to też single - line

to jest 17.5, nie wiem czy to ma znaczenie

Simulator.Screen.Recording.-.iPhone.15.Pro.-.2025-08-22.at.14.34.03.mp4

@war-in
Copy link
Collaborator Author

war-in commented Aug 26, 2025

Very much a nitpick, but when having a multiline mention starting bottom border radius and finish top, should not apply. Same issue for web

We can try to fix it in a separate PR but I think we need to consult with the design team. I'll leave it as an open idea because it's not in the scope of this PR

Thanks for noticing!

@war-in
Copy link
Collaborator Author

war-in commented Aug 26, 2025

  1. android, switching to single line breaks highlights :(

@jmusial should be fixed with the latest commit 🎉 98dafda

  1. android, Cursor not visible when within codeblock or mention

this is expected behaviour. We discussed it with the design team and there is no plan to support it in the near future

@tomekzaw
Copy link
Collaborator

  1. android, Cursor not visible when within codeblock or mention

For the context, this was discussed on Slack: https://swmansion.slack.com/archives/C01GTK53T8Q/p1755699466781499

Comment on lines +104 to +106
NSTextStorage *textStorage = [_textField valueForKey:@"_textStorage"];
NSTextContainer *textContainer = [_textField valueForKey:@"_textContainer"];
NSTextLayoutManager *textLayoutManager = [textContainer valueForKey:@"_textLayoutManager"];
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm not sure if accessing UITextField's private fields could cause some rejections on app store. We should probably verify that

cc @tomekzaw any ideas on how to do that?

Copy link
Collaborator

@tomekzaw tomekzaw Nov 3, 2025

Choose a reason for hiding this comment

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

We need to obfuscate all the selectors here, e.g. "_text" + "tuoyal".reversed() + Manager"

@war-in
Copy link
Collaborator Author

war-in commented Oct 29, 2025

@tomekzaw @parasharrajat @Skalakid PR's ready for review 🎉

Please review it carefully and ask me any questions because the solution is quite complicated in a few places. If you think adding comments would be valuable let me know!

Copy link
Contributor

@Skalakid Skalakid left a comment

Choose a reason for hiding this comment

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

Wow, amazing work! Left some comments, but overall LGTM

Comment on lines +56 to +57
boolean mentionStarts = lineStart <= relativeMentionStart;
boolean mentionEnds = lineEnd >= relativeMentionEnd;
Copy link
Contributor

Choose a reason for hiding this comment

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

How about renaming these variables to something for example like: startsInThisLine and endsInThisLine

Comment on lines +95 to +105
if (@available(iOS 16.0, *)) {
RCTMarkdownTextBackground *textBackground = [[RCTMarkdownTextBackground alloc] init];
textBackground.color = markdownStyle.mentionHereBackgroundColor;
textBackground.borderRadius = markdownStyle.mentionHereBorderRadius;

[attributedString addAttribute:RCTLiveMarkdownTextBackgroundAttributeName
value:textBackground
range:range];
} else {
[attributedString addAttribute:NSBackgroundColorAttributeName value:markdownStyle.mentionHereBackgroundColor range:range];
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I see that there is a lot of similar code. How about moving it to a separate function with textColor, backgroundColor, and borderRadius as props (etc)

Copy link
Collaborator

@tomekzaw tomekzaw Nov 3, 2025

Choose a reason for hiding this comment

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

Helper function for applying mention-related attributes sounds good!

NSRange intersection = NSIntersectionRange(lineRange, mentionRange);
CGPoint startLocation = [lineFragment locationForCharacterAtIndex:intersection.location];
if (isSingleline && startLocation.x == 0 && intersection.location > 0) {
// singleline: mention starts off screen, no need to draw background
Copy link
Contributor

Choose a reason for hiding this comment

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

Please add an explanation that it happens when the input isn't focused

Copy link
Contributor

Choose a reason for hiding this comment

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

The amount of different start and end variables is a bit overwhelming there 😓. Maybe let's add some general comments or something?

mPreBackgroundColor = parseColor(map, "pre", "backgroundColor", context);
mMentionHereColor = parseColor(map, "mentionHere", "color", context);
mMentionHereBackgroundColor = parseColor(map, "mentionHere", "backgroundColor", context);
mMentionHereBorderRadius = parseFloat(map, "mentionHere", "borderRadius") * screenDensity;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd rather keep the original value of borderRadius in MarkdownStyle and multiply it by screenDensity near the drawing logic.


@property (nonnull, atomic) RCTMarkdownUtils *markdownUtils;

@property NSUInteger depth;
Copy link
Collaborator

@tomekzaw tomekzaw Nov 3, 2025

Choose a reason for hiding this comment

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

At some point we might wanna rename depth to blockquoteDepth as this is unrelated to mentions. This can be done in a separate PR (to be merged before or after this one).

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd suggest moving these two classes to separate files, i.e. RCTMarkdownTextBackground.h and RCTMarkdownTextBackground.mm (and the same for RCTMarkdownTextBackgroundWithRange).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants