Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .maestro/enrichedInput/flows/empty_element_parsing.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
appId: swmansion.enriched.example
tags:
- android-only
---
# PR #284 - fix: parsing empty elements
- launchApp
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
96 changes: 64 additions & 32 deletions ios/htmlParser/HtmlParser.mm
Original file line number Diff line number Diff line change
Expand Up @@ -409,34 +409,6 @@ + (NSString *_Nullable)initiallyProcessHtml:(NSString *_Nonnull)html
inString:fixedHtml
leading:NO
trailing:YES];

// this is more like a hack but for some reason the last <br> in
// <blockquote> and <codeblock> are not properly changed into zero width
// space so we do that manually here
fixedHtml = [fixedHtml
stringByReplacingOccurrencesOfString:@"<br>\n</blockquote>"
withString:@"<p>\u200B</p>\n</blockquote>"];
fixedHtml = [fixedHtml
stringByReplacingOccurrencesOfString:@"<br>\n</codeblock>"
withString:@"<p>\u200B</p>\n</codeblock>"];

// The same like above for (blockquote and codeblock) this is more like a
// hack but for some reason the last <li></li> in <ul> and <ol> are not
// properly changed into zero width space so we do that manually here
// TODO: investigate this further, issue is already described here:
// https://github.com/software-mansion/react-native-enriched/issues/505
fixedHtml = [fixedHtml
stringByReplacingOccurrencesOfString:@"<li></li>\n</ul>"
withString:@"<li>\u200B</li>\n</ul>"];
fixedHtml = [fixedHtml
stringByReplacingOccurrencesOfString:@"<li></li>\n</ol>"
withString:@"<li>\u200B</li>\n</ol>"];

// replace "<br>" at the end with "<br>\n" if input is not empty to properly
// handle last <br> in html
if ([fixedHtml hasSuffix:@"<br>"] && fixedHtml.length != 4) {
fixedHtml = [fixedHtml stringByAppendingString:@"\n"];
}
}

return fixedHtml;
Expand All @@ -455,6 +427,7 @@ + (NSArray *_Nonnull)getTextAndStylesFromHtml:(NSString *_Nonnull)fixedHtml {
BOOL gettingTagName = NO;
BOOL gettingTagParams = NO;
BOOL closingTag = NO;
BOOL lastTagWasBr = NO;
NSMutableString *currentTagName =
[[NSMutableString alloc] initWithString:@""];
NSMutableString *currentTagParams =
Expand Down Expand Up @@ -490,12 +463,36 @@ + (NSArray *_Nonnull)getTextAndStylesFromHtml:(NSString *_Nonnull)fixedHtml {
}

if ([currentTagName isEqualToString:@"br"]) {
lastTagWasBr = YES;
// do nothing, we don't include these tags in styles
} else if ([currentTagName isEqualToString:@"li"]) {
// Only track checkbox state if we're inside a checkbox list
if (insideCheckboxList && !closingTag) {
BOOL isChecked = [currentTagParams containsString:@"checked"];
checkboxStates[@(plainText.length)] = @(isChecked);
if (!closingTag) {
// Opening tag <li>
// Track checkbox state if we're inside a checkbox list
if (insideCheckboxList) {
BOOL isChecked = [currentTagParams containsString:@"checked"];
checkboxStates[@(plainText.length)] = @(isChecked);
}
// Record the start location so we can check if it's empty when
// closing
ongoingTags[@"li"] = @[ @(plainText.length) ];
} else {
// Closing tag </li>
NSArray *tagData = ongoingTags[@"li"];
if (tagData != nil) {
NSInteger tagLocation = [((NSNumber *)tagData[0]) intValue];
NSString *innerContent = [plainText substringFromIndex:tagLocation];

// If the li is completely empty (or just contains layout newlines),
// inject ZWS
if ([innerContent
stringByTrimmingCharactersInSet:[NSCharacterSet
newlineCharacterSet]]
.length == 0) {
[plainText appendString:@"\u200B"];
}
[ongoingTags removeObjectForKey:@"li"];
}
}
} else if (!closingTag) {
BOOL isPlainParagraph = [currentTagName isEqualToString:@"p"] &&
Expand Down Expand Up @@ -529,6 +526,12 @@ + (NSArray *_Nonnull)getTextAndStylesFromHtml:(NSString *_Nonnull)fixedHtml {
i += 1;
}

if ([currentTagName isEqualToString:@"img"]) {
// Images have no inner text, so we manually break the <br> streak
// here.
lastTagWasBr = NO;
}

if (isSelfClosing) {
[self finalizeTagEntry:currentTagName
ongoingTags:ongoingTags
Expand All @@ -549,12 +552,36 @@ + (NSArray *_Nonnull)getTextAndStylesFromHtml:(NSString *_Nonnull)fixedHtml {

BOOL isBlockTag = [self isBlockTag:currentTagName];

// ZWS logic for blockquote and codeblock
BOOL needsZWS = [currentTagName isEqualToString:@"blockquote"] ||
[currentTagName isEqualToString:@"codeblock"];
BOOL isEmptyBlock = NO;
if (needsZWS) {
NSArray *tagData = ongoingTags[currentTagName];
if (tagData != nil) {
NSInteger tagLoc = [tagData[0] intValue];
NSString *inner = [plainText substringFromIndex:tagLoc];
if ([inner stringByTrimmingCharactersInSet:[NSCharacterSet
newlineCharacterSet]]
.length == 0) {
isEmptyBlock = YES;
}
}
}

// skip one newline if it was added before some closing tags that are
// in separate lines
if (isBlockTag && plainText.length > 0 &&
[[NSCharacterSet newlineCharacterSet]
characterIsMember:[plainText
characterAtIndex:plainText.length - 1]]) {

// If the last thing processed was a <br>, or the block is totally
// empty, inject a \u200B before trimming the trailing newline to save
// the empty line.
if (lastTagWasBr || isEmptyBlock) {
[plainText insertString:@"\u200B" atIndex:plainText.length - 1];
}
plainText = [[plainText
substringWithRange:NSMakeRange(0, plainText.length - 1)]
mutableCopy];
Expand Down Expand Up @@ -589,6 +616,11 @@ + (NSArray *_Nonnull)getTextAndStylesFromHtml:(NSString *_Nonnull)fixedHtml {
i += escaped.length - 1;
} else {
[plainText appendString:currentCharacterStr];
// Any typed character that isn't a newline breaks the <br> streak
if (![[NSCharacterSet newlineCharacterSet]
characterIsMember:currentCharacterChar]) {
lastTagWasBr = NO;
}
}
} else {
if (gettingTagName) {
Expand Down
Loading