Skip to content

Commit 5c634fd

Browse files
feat: add support for inline image on iOS (#271)
1 parent 571fa10 commit 5c634fd

File tree

9 files changed

+277
-8
lines changed

9 files changed

+277
-8
lines changed

example/src/App.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
Text,
55
type NativeSyntheticEvent,
66
ScrollView,
7+
Platform,
78
} from 'react-native';
89
import {
910
EnrichedTextInput,
@@ -202,7 +203,11 @@ export default function App() {
202203
selectionLimit: 1,
203204
});
204205

205-
const imageUri = response.assets?.[0]?.originalPath;
206+
const imageUri =
207+
Platform.OS === 'android'
208+
? response.assets?.[0]?.originalPath
209+
: response.assets?.[0]?.uri;
210+
206211
if (!imageUri) return;
207212

208213
ref.current?.setImage(imageUri);

ios/EnrichedTextInputView.mm

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ - (void)setDefaults {
9494
@([UnorderedListStyle getStyleType]): [[UnorderedListStyle alloc] initWithInput:self],
9595
@([OrderedListStyle getStyleType]): [[OrderedListStyle alloc] initWithInput:self],
9696
@([BlockQuoteStyle getStyleType]): [[BlockQuoteStyle alloc] initWithInput:self],
97-
@([CodeBlockStyle getStyleType]): [[CodeBlockStyle alloc] initWithInput:self]
97+
@([CodeBlockStyle getStyleType]): [[CodeBlockStyle alloc] initWithInput:self],
98+
@([ImageStyle getStyleType]): [[ImageStyle alloc] initWithInput:self]
9899
};
99100

100101
conflictingStyles = @{
@@ -112,24 +113,26 @@ - (void)setDefaults {
112113
@([OrderedListStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([UnorderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]), @([CodeBlockStyle getStyleType])],
113114
@([BlockQuoteStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([CodeBlockStyle getStyleType])],
114115
@([CodeBlockStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]),
115-
@([BoldStyle getStyleType]), @([ItalicStyle getStyleType]), @([UnderlineStyle getStyleType]), @([StrikethroughStyle getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]), @([InlineCodeStyle getStyleType]), @([MentionStyle getStyleType]), @([LinkStyle getStyleType])]
116+
@([BoldStyle getStyleType]), @([ItalicStyle getStyleType]), @([UnderlineStyle getStyleType]), @([StrikethroughStyle getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]), @([InlineCodeStyle getStyleType]), @([MentionStyle getStyleType]), @([LinkStyle getStyleType])],
117+
@([ImageStyle getStyleType]) : @[@([LinkStyle getStyleType]), @([MentionStyle getStyleType])]
116118
};
117119

118120
blockingStyles = @{
119121
@([BoldStyle getStyleType]) : @[@([CodeBlockStyle getStyleType])],
120122
@([ItalicStyle getStyleType]) : @[@([CodeBlockStyle getStyleType])],
121123
@([UnderlineStyle getStyleType]) : @[@([CodeBlockStyle getStyleType])],
122124
@([StrikethroughStyle getStyleType]) : @[@([CodeBlockStyle getStyleType])],
123-
@([InlineCodeStyle getStyleType]) : @[@([CodeBlockStyle getStyleType])],
124-
@([LinkStyle getStyleType]): @[@([CodeBlockStyle getStyleType])],
125-
@([MentionStyle getStyleType]): @[@([CodeBlockStyle getStyleType])],
125+
@([InlineCodeStyle getStyleType]) : @[@([CodeBlockStyle getStyleType]), @([ImageStyle getStyleType])],
126+
@([LinkStyle getStyleType]): @[@([CodeBlockStyle getStyleType]), @([ImageStyle getStyleType])],
127+
@([MentionStyle getStyleType]): @[@([CodeBlockStyle getStyleType]), @([ImageStyle getStyleType])],
126128
@([H1Style getStyleType]): @[],
127129
@([H2Style getStyleType]): @[],
128130
@([H3Style getStyleType]): @[],
129131
@([UnorderedListStyle getStyleType]): @[],
130132
@([OrderedListStyle getStyleType]): @[],
131133
@([BlockQuoteStyle getStyleType]): @[],
132134
@([CodeBlockStyle getStyleType]): @[],
135+
@([ImageStyle getStyleType]) : @[@([InlineCodeStyle getStyleType])]
133136
};
134137

135138
parser = [[InputParser alloc] initWithInput:self];
@@ -368,6 +371,16 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &
368371
stylePropChanged = YES;
369372
}
370373

374+
if(newViewProps.htmlStyle.img.width != oldViewProps.htmlStyle.img.width) {
375+
[newConfig setImageWidth:newViewProps.htmlStyle.img.width];
376+
stylePropChanged = YES;
377+
}
378+
379+
if(newViewProps.htmlStyle.img.height != oldViewProps.htmlStyle.img.height) {
380+
[newConfig setImageHeight:newViewProps.htmlStyle.img.height];
381+
stylePropChanged = YES;
382+
}
383+
371384
if(newViewProps.htmlStyle.a.textDecorationLine != oldViewProps.htmlStyle.a.textDecorationLine) {
372385
NSString *objcString = [NSString fromCppString:newViewProps.htmlStyle.a.textDecorationLine];
373386
if([objcString isEqualToString:DecorationUnderline]) {
@@ -723,7 +736,7 @@ - (void)tryUpdatingActiveStyles {
723736
.isOrderedList = [_activeStyles containsObject: @([OrderedListStyle getStyleType])],
724737
.isBlockQuote = [_activeStyles containsObject: @([BlockQuoteStyle getStyleType])],
725738
.isCodeBlock = [_activeStyles containsObject: @([CodeBlockStyle getStyleType])],
726-
.isImage = NO // [_activeStyles containsObject: @([ImageStyle getStyleType]])],
739+
.isImage = [_activeStyles containsObject: @([ImageStyle getStyleType])],
727740
});
728741
}
729742
}
@@ -793,6 +806,9 @@ - (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args {
793806
[self toggleParagraphStyle:[BlockQuoteStyle getStyleType]];
794807
} else if([commandName isEqualToString:@"toggleCodeBlock"]) {
795808
[self toggleParagraphStyle:[CodeBlockStyle getStyleType]];
809+
} else if([commandName isEqualToString:@"addImage"]) {
810+
NSString *uri = (NSString *)args[0];
811+
[self addImage:uri];
796812
}
797813
}
798814

@@ -939,6 +955,17 @@ - (void)addMention:(NSString *)indicator text:(NSString *)text attributes:(NSStr
939955
}
940956
}
941957

958+
- (void)addImage:(NSString *)uri
959+
{
960+
ImageStyle *imageStyleClass = (ImageStyle *)stylesDict[@([ImageStyle getStyleType])];
961+
if(imageStyleClass == nullptr) { return; }
962+
963+
if([self handleStyleBlocksAndConflicts:[ImageStyle getStyleType] range:textView.selectedRange]) {
964+
[imageStyleClass addImage:uri];
965+
[self anyTextMayHaveBeenModified];
966+
}
967+
}
968+
942969
- (void)startMentionWithIndicator:(NSString *)indicator {
943970
MentionStyle *mentionStyleClass = (MentionStyle *)stylesDict[@([MentionStyle getStyleType])];
944971
if(mentionStyleClass == nullptr) { return; }

ios/config/InputConfig.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,8 @@
7070
- (void)setCodeBlockBgColor:(UIColor *)newValue;
7171
- (CGFloat)codeBlockBorderRadius;
7272
- (void)setCodeBlockBorderRadius:(CGFloat)newValue;
73+
- (void)setImageWidth:(CGFloat)newValue;
74+
- (CGFloat)imageWidth;
75+
- (void)setImageHeight:(CGFloat)newValue;
76+
- (CGFloat)imageHeight;
7377
@end

ios/config/InputConfig.mm

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ @implementation InputConfig {
3939
UIColor *_codeBlockFgColor;
4040
CGFloat _codeBlockBorderRadius;
4141
UIColor *_codeBlockBgColor;
42+
CGFloat _imageWidth;
43+
CGFloat _imageHeight;
4244
}
4345

4446
- (instancetype) init {
@@ -85,6 +87,8 @@ - (id)copyWithZone:(NSZone *)zone {
8587
copy->_codeBlockFgColor = [_codeBlockFgColor copy];
8688
copy->_codeBlockBgColor = [_codeBlockBgColor copy];
8789
copy->_codeBlockBorderRadius = _codeBlockBorderRadius;
90+
copy->_imageWidth = _imageWidth;
91+
copy->_imageHeight = _imageHeight;
8892
return copy;
8993
}
9094

@@ -409,4 +413,20 @@ - (void)setCodeBlockBorderRadius:(CGFloat)newValue {
409413
_codeBlockBorderRadius = newValue;
410414
}
411415

416+
- (CGFloat)imageWidth {
417+
return _imageWidth;
418+
}
419+
420+
- (void)setImageWidth:(CGFloat)newValue {
421+
_imageWidth = newValue;
422+
}
423+
424+
- (CGFloat)imageHeight {
425+
return _imageHeight;
426+
}
427+
428+
- (void)setImageHeight:(CGFloat)newValue {
429+
_imageHeight = newValue;
430+
}
431+
412432
@end

ios/inputParser/InputParser.mm

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ - (NSString *)parseToHtmlFromRange:(NSRange)range {
8686

8787
// append closing tags
8888
for(NSNumber *style in sortedEndedStyles) {
89+
if ([style isEqualToNumber: @([ImageStyle getStyleType])]) {
90+
continue;
91+
}
8992
NSString *tagContent = [self tagContentForStyle:style openingTag:NO location:currentRange.location];
9093
[result appendString: [NSString stringWithFormat:@"</%@>", tagContent]];
9194
}
@@ -221,6 +224,9 @@ - (NSString *)parseToHtmlFromRange:(NSRange)range {
221224

222225
// append closing tags
223226
for(NSNumber *style in sortedEndedStyles) {
227+
if ([style isEqualToNumber: @([ImageStyle getStyleType])]) {
228+
continue;
229+
}
224230
NSString *tagContent = [self tagContentForStyle:style openingTag:NO location:currentRange.location];
225231
[result appendString: [NSString stringWithFormat:@"</%@>", tagContent]];
226232
}
@@ -233,7 +239,11 @@ - (NSString *)parseToHtmlFromRange:(NSRange)range {
233239
// append opening tags
234240
for(NSNumber *style in sortedNewStyles) {
235241
NSString *tagContent = [self tagContentForStyle:style openingTag:YES location:currentRange.location];
236-
[result appendString: [NSString stringWithFormat:@"<%@>", tagContent]];
242+
if ([style isEqualToNumber: @([ImageStyle getStyleType])]) {
243+
[result appendString: [NSString stringWithFormat:@"<%@/>", tagContent]];
244+
} else {
245+
[result appendString: [NSString stringWithFormat:@"<%@>", tagContent]];
246+
}
237247
}
238248

239249
// append the letter and escape it if needed
@@ -254,6 +264,9 @@ - (NSString *)parseToHtmlFromRange:(NSRange)range {
254264

255265
// append closing tags
256266
for(NSNumber *style in sortedEndedStyles) {
267+
if ([style isEqualToNumber: @([ImageStyle getStyleType])]) {
268+
continue;
269+
}
257270
NSString *tagContent = [self tagContentForStyle:style openingTag:NO location:_input->textView.textStorage.string.length - 1];
258271
[result appendString: [NSString stringWithFormat:@"</%@>", tagContent]];
259272
}
@@ -310,6 +323,19 @@ - (NSString *)tagContentForStyle:(NSNumber *)style openingTag:(BOOL)openingTag l
310323
return @"b";
311324
} else if([style isEqualToNumber: @([ItalicStyle getStyleType])]) {
312325
return @"i";
326+
} else if ([style isEqualToNumber: @([ImageStyle getStyleType])]) {
327+
if(openingTag) {
328+
ImageStyle *imageStyle = (ImageStyle *)_input->stylesDict[@([ImageStyle getStyleType])];
329+
if(imageStyle != nullptr) {
330+
ImageData *data = [imageStyle getImageDataAt:location];
331+
if(data != nullptr && data.uri != nullptr) {
332+
return [NSString stringWithFormat:@"img src=\"%@\"", data.uri];
333+
}
334+
}
335+
return @"img";
336+
} else {
337+
return @"";
338+
}
313339
} else if([style isEqualToNumber: @([UnderlineStyle getStyleType])]) {
314340
return @"u";
315341
} else if([style isEqualToNumber: @([StrikethroughStyle getStyleType])]) {
@@ -642,6 +668,25 @@ - (NSArray *)getTextAndStylesFromHtml:(NSString *)fixedHtml {
642668
[styleArr addObject:@([BoldStyle getStyleType])];
643669
} else if([tagName isEqualToString:@"i"]) {
644670
[styleArr addObject:@([ItalicStyle getStyleType])];
671+
} else if([tagName isEqualToString:@"img"]) {
672+
NSRegularExpression *srcRegex = [NSRegularExpression regularExpressionWithPattern:@"src=\".+\""
673+
options:0
674+
error:nullptr
675+
];
676+
NSTextCheckingResult* match = [srcRegex firstMatchInString:params options:0 range: NSMakeRange(0, params.length)];
677+
678+
if(match == nullptr) {
679+
continue;
680+
}
681+
682+
NSRange srcRange = match.range;
683+
[styleArr addObject:@([ImageStyle getStyleType])];
684+
// cut only the uri from the src="..." string
685+
NSString *uri = [params substringWithRange:NSMakeRange(srcRange.location + 5, srcRange.length - 6)];
686+
ImageData *imageData = [[ImageData alloc] init];
687+
imageData.uri = uri;
688+
689+
stylePair.styleValue = imageData;
645690
} else if([tagName isEqualToString:@"u"]) {
646691
[styleArr addObject:@([UnderlineStyle getStyleType])];
647692
} else if([tagName isEqualToString:@"s"]) {

0 commit comments

Comments
 (0)