diff --git a/apple/MarkdownCommitHook.mm b/apple/MarkdownCommitHook.mm index f1923a3d..fcff8127 100644 --- a/apple/MarkdownCommitHook.mm +++ b/apple/MarkdownCommitHook.mm @@ -170,8 +170,8 @@ } // apply markdown - auto newString = [utils parseMarkdown:nsAttributedString - withAttributes:defaultNSTextAttributes]; + NSMutableAttributedString *newString = [nsAttributedString mutableCopy]; + [utils applyFormatting:newString withDefaultTextAttributes:defaultNSTextAttributes]; // create a clone of the old TextInputState and update the // attributed string box to point to the string with markdown @@ -217,8 +217,8 @@ stateData.attributedStringBox); // apply markdown - auto newString = [utils parseMarkdown:nsAttributedString - withAttributes:defaultNSTextAttributes]; + NSMutableAttributedString *newString = [nsAttributedString mutableCopy]; + [utils applyFormatting:newString withDefaultTextAttributes:defaultNSTextAttributes]; // create a clone of the old TextInputState and update the // attributed string box to point to the string with markdown diff --git a/apple/MarkdownTextInputDecoratorView.mm b/apple/MarkdownTextInputDecoratorView.mm index c6ae82fb..1803e4e2 100644 --- a/apple/MarkdownTextInputDecoratorView.mm +++ b/apple/MarkdownTextInputDecoratorView.mm @@ -1,29 +1,18 @@ #import +#import +#import #import "react_native_assert.h" #import #import -#import -#import - -#ifdef RCT_NEW_ARCH_ENABLED -#import -#else -#import -#endif /* RCT_NEW_ARCH_ENABLED */ +#import #import @implementation MarkdownTextInputDecoratorView { RCTMarkdownUtils *_markdownUtils; RCTMarkdownStyle *_markdownStyle; -#ifdef RCT_NEW_ARCH_ENABLED - __weak RCTTextInputComponentView *_textInput; -#else - __weak RCTBaseTextInputView *_textInput; -#endif /* RCT_NEW_ARCH_ENABLED */ - __weak UIView *_backedTextInputView; - __weak RCTBackedTextFieldDelegateAdapter *_adapter; + MarkdownTextStorageDelegate *_markdownTextStorageDelegate; __weak RCTUITextView *_textView; } @@ -51,26 +40,33 @@ - (void)didMoveToWindow { #ifdef RCT_NEW_ARCH_ENABLED react_native_assert([view isKindOfClass:[RCTTextInputComponentView class]] && "Previous sibling component is not an instance of RCTTextInputComponentView."); - _textInput = (RCTTextInputComponentView *)view; - _backedTextInputView = [_textInput valueForKey:@"_backedTextInputView"]; + RCTTextInputComponentView *textInputComponentView = (RCTTextInputComponentView *)view; + UIView *backedTextInputView = [textInputComponentView valueForKey:@"_backedTextInputView"]; #else - react_native_assert([view isKindOfClass:[RCTBaseTextInputView class]] && "Previous sibling component is not an instance of RCTBaseTextInputView."); - _textInput = (RCTBaseTextInputView *)view; - _backedTextInputView = _textInput.backedTextInputView; + // TODO: implement on Paper + react_native_assert(false && "Not implemented on Paper yet"); #endif /* RCT_NEW_ARCH_ENABLED */ _markdownUtils = [[RCTMarkdownUtils alloc] init]; react_native_assert(_markdownStyle != nil); [_markdownUtils setMarkdownStyle:_markdownStyle]; - [_textInput setMarkdownUtils:_markdownUtils]; - if ([_backedTextInputView isKindOfClass:[RCTUITextField class]]) { - RCTUITextField *textField = (RCTUITextField *)_backedTextInputView; - _adapter = [textField valueForKey:@"textInputDelegateAdapter"]; - [_adapter setMarkdownUtils:_markdownUtils]; - } else if ([_backedTextInputView isKindOfClass:[RCTUITextView class]]) { - _textView = (RCTUITextView *)_backedTextInputView; - [_textView setMarkdownUtils:_markdownUtils]; + if ([backedTextInputView isKindOfClass:[RCTUITextField class]]) { + // TODO: implement for singleline input + react_native_assert(false && "Not implemented for singleline input yet"); + } else if ([backedTextInputView isKindOfClass:[RCTUITextView class]]) { + _textView = (RCTUITextView *)backedTextInputView; + + _markdownTextStorageDelegate = [[MarkdownTextStorageDelegate alloc] init]; + _markdownTextStorageDelegate.markdownUtils = _markdownUtils; + _markdownTextStorageDelegate.textView = _textView; + + // register delegate for future edits + _textView.textStorage.delegate = _markdownTextStorageDelegate; + + // format initial value + [_textView.textStorage setAttributedString:_textView.attributedText]; + NSLayoutManager *layoutManager = _textView.layoutManager; // switching to TextKit 1 compatibility mode // Correct content height in TextKit 1 compatibility mode. (See https://github.com/Expensify/App/issues/41567) @@ -90,14 +86,9 @@ - (void)didMoveToWindow { - (void)willMoveToWindow:(UIWindow *)newWindow { - if (_textInput != nil) { - [_textInput setMarkdownUtils:nil]; - } - if (_adapter != nil) { - [_adapter setMarkdownUtils:nil]; - } if (_textView != nil) { - [_textView setMarkdownUtils:nil]; + _textView.textStorage.delegate = nil; + if (_textView.layoutManager != nil && [object_getClass(_textView.layoutManager) isEqual:[MarkdownLayoutManager class]]) { [_textView.layoutManager setValue:nil forKey:@"markdownUtils"]; object_setClass(_textView.layoutManager, [NSLayoutManager class]); @@ -110,17 +101,8 @@ - (void)setMarkdownStyle:(RCTMarkdownStyle *)markdownStyle _markdownStyle = markdownStyle; [_markdownUtils setMarkdownStyle:markdownStyle]; - if (_textView != nil) { - // We want to use `textStorage` for applying markdown when possible. Currently it's only available for UITextView - [_textView textDidChange]; - } else { - // apply new styles -#ifdef RCT_NEW_ARCH_ENABLED - [_textInput _setAttributedString:_backedTextInputView.attributedText]; -#else - [_textInput setAttributedText:_textInput.attributedText]; -#endif /* RCT_NEW_ARCH_ENABLED */ - } + // trigger reformatting + [_textView.textStorage setAttributedString:_textView.attributedText]; } @end diff --git a/apple/MarkdownTextStorageDelegate.h b/apple/MarkdownTextStorageDelegate.h new file mode 100644 index 00000000..bfdebbbd --- /dev/null +++ b/apple/MarkdownTextStorageDelegate.h @@ -0,0 +1,15 @@ +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface MarkdownTextStorageDelegate : NSObject + +@property(nonatomic, nullable) RCTMarkdownUtils *markdownUtils; + +@property(nonatomic, nullable, strong) RCTUITextView *textView; + +@end + +NS_ASSUME_NONNULL_END diff --git a/apple/MarkdownTextStorageDelegate.mm b/apple/MarkdownTextStorageDelegate.mm new file mode 100644 index 00000000..475c8dab --- /dev/null +++ b/apple/MarkdownTextStorageDelegate.mm @@ -0,0 +1,15 @@ +#import + +@implementation MarkdownTextStorageDelegate + +- (void)textStorage:(NSTextStorage *)textStorage didProcessEditing:(NSTextStorageEditActions)editedMask range:(NSRange)editedRange changeInLength:(NSInteger)delta { + react_native_assert(_markdownUtils != nil); + react_native_assert(_textView != nil); + react_native_assert(_textView.defaultTextAttributes != nil); + + [_markdownUtils applyFormatting:textStorage withDefaultTextAttributes:_textView.defaultTextAttributes]; + + // TODO: fix cursor position when adding newline after a blockquote (probably not here though) +} + +@end diff --git a/apple/RCTBackedTextFieldDelegateAdapter+Markdown.h b/apple/RCTBackedTextFieldDelegateAdapter+Markdown.h deleted file mode 100644 index f8ddc1d2..00000000 --- a/apple/RCTBackedTextFieldDelegateAdapter+Markdown.h +++ /dev/null @@ -1,14 +0,0 @@ -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface RCTBackedTextFieldDelegateAdapter (Markdown) - -@property(nonatomic, nullable, getter=getMarkdownUtils) RCTMarkdownUtils *markdownUtils; - -- (void)markdown_textFieldDidChange; - -@end - -NS_ASSUME_NONNULL_END diff --git a/apple/RCTBackedTextFieldDelegateAdapter+Markdown.mm b/apple/RCTBackedTextFieldDelegateAdapter+Markdown.mm deleted file mode 100644 index 11c3baf8..00000000 --- a/apple/RCTBackedTextFieldDelegateAdapter+Markdown.mm +++ /dev/null @@ -1,43 +0,0 @@ -#import -#import -#import -#import - -@implementation RCTBackedTextFieldDelegateAdapter (Markdown) - -- (void)setMarkdownUtils:(RCTMarkdownUtils *)markdownUtils { - objc_setAssociatedObject(self, @selector(getMarkdownUtils), markdownUtils, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -- (RCTMarkdownUtils *)getMarkdownUtils { - return objc_getAssociatedObject(self, @selector(getMarkdownUtils)); -} - -- (void)markdown_textFieldDidChange -{ - RCTMarkdownUtils *markdownUtils = [self getMarkdownUtils]; - if (markdownUtils != nil) { - RCTUITextField *backedTextInputView = [self valueForKey:@"_backedTextInputView"]; - UITextRange *range = backedTextInputView.selectedTextRange; - backedTextInputView.attributedText = [markdownUtils parseMarkdown:backedTextInputView.attributedText withAttributes:backedTextInputView.defaultTextAttributes]; - [backedTextInputView setSelectedTextRange:range notifyDelegate:YES]; - } - - // Call the original method - [self markdown_textFieldDidChange]; -} - -+ (void)load -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - Class cls = [self class]; - SEL originalSelector = @selector(textFieldDidChange); - SEL swizzledSelector = @selector(markdown_textFieldDidChange); - Method originalMethod = class_getInstanceMethod(cls, originalSelector); - Method swizzledMethod = class_getInstanceMethod(cls, swizzledSelector); - method_exchangeImplementations(originalMethod, swizzledMethod); - }); -} - -@end diff --git a/apple/RCTBaseTextInputView+Markdown.h b/apple/RCTBaseTextInputView+Markdown.h deleted file mode 100644 index 3d37adb2..00000000 --- a/apple/RCTBaseTextInputView+Markdown.h +++ /dev/null @@ -1,18 +0,0 @@ -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface RCTBaseTextInputView (Markdown) - -@property(nonatomic, nullable, getter=getMarkdownUtils) RCTMarkdownUtils *markdownUtils; - -- (void)markdown_setAttributedText:(NSAttributedString *)attributedText; - -- (BOOL)markdown_textOf:(NSAttributedString *)newText equals:(NSAttributedString *)oldText; - -- (void)markdown_updateLocalData; - -@end - -NS_ASSUME_NONNULL_END diff --git a/apple/RCTBaseTextInputView+Markdown.mm b/apple/RCTBaseTextInputView+Markdown.mm deleted file mode 100644 index 7662d545..00000000 --- a/apple/RCTBaseTextInputView+Markdown.mm +++ /dev/null @@ -1,101 +0,0 @@ -#import -#import -#import - -@implementation RCTBaseTextInputView (Markdown) - -- (void)setMarkdownUtils:(RCTMarkdownUtils *)markdownUtils { - objc_setAssociatedObject(self, @selector(getMarkdownUtils), markdownUtils, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -- (RCTMarkdownUtils *)getMarkdownUtils { - return objc_getAssociatedObject(self, @selector(getMarkdownUtils)); -} - -- (void)markdown_setAttributedText:(NSAttributedString *)attributedText -{ - RCTMarkdownUtils *markdownUtils = [self getMarkdownUtils]; - if (markdownUtils != nil) { - attributedText = [markdownUtils parseMarkdown:attributedText withAttributes:self.backedTextInputView.defaultTextAttributes]; - } - - // Call the original method - [self markdown_setAttributedText:attributedText]; -} - -- (BOOL)markdown_textOf:(NSAttributedString *)newText equals:(NSAttributedString *)oldText -{ - RCTMarkdownUtils *markdownUtils = [self getMarkdownUtils]; - if (markdownUtils != nil) { - // Emoji characters are automatically assigned an AppleColorEmoji NSFont and the original font is moved to NSOriginalFont - // We need to remove these attributes before comparison - NSMutableAttributedString *newTextCopy = [newText mutableCopy]; - NSMutableAttributedString *oldTextCopy = [oldText mutableCopy]; - [newTextCopy removeAttribute:@"NSFont" range:NSMakeRange(0, newTextCopy.length)]; - [oldTextCopy removeAttribute:@"NSFont" range:NSMakeRange(0, oldTextCopy.length)]; - [oldTextCopy removeAttribute:@"NSOriginalFont" range:NSMakeRange(0, oldTextCopy.length)]; - return [newTextCopy isEqualToAttributedString:oldTextCopy]; - } - - return [self markdown_textOf:newText equals:oldText]; -} - -- (void)markdown_updateLocalData -{ - RCTMarkdownUtils *markdownUtils = [self getMarkdownUtils]; - if (markdownUtils != nil) { - id backedTextInputView = self.backedTextInputView; - NSAttributedString *oldAttributedText = backedTextInputView.attributedText; - NSAttributedString *newAttributedText = [markdownUtils parseMarkdown:oldAttributedText withAttributes:backedTextInputView.defaultTextAttributes]; - UITextRange *range = backedTextInputView.selectedTextRange; - - // update attributed text without emitting onSelectionChange event - id delegate = backedTextInputView.textInputDelegate; - backedTextInputView.textInputDelegate = nil; - [backedTextInputView setAttributedText:newAttributedText]; - backedTextInputView.textInputDelegate = delegate; - - // restore original selection and emit onSelectionChange event - [backedTextInputView setSelectedTextRange:range notifyDelegate:YES]; - } - - // Call the original method - [self markdown_updateLocalData]; -} - -+ (void)load -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - Class cls = [self class]; - - { - // swizzle setAttributedText - SEL originalSelector = @selector(setAttributedText:); - SEL swizzledSelector = @selector(markdown_setAttributedText:); - Method originalMethod = class_getInstanceMethod(cls, originalSelector); - Method swizzledMethod = class_getInstanceMethod(cls, swizzledSelector); - method_exchangeImplementations(originalMethod, swizzledMethod); - } - - { - // swizzle updateLocalData - SEL originalSelector = @selector(updateLocalData); - SEL swizzledSelector = @selector(markdown_updateLocalData); - Method originalMethod = class_getInstanceMethod(cls, originalSelector); - Method swizzledMethod = class_getInstanceMethod(cls, swizzledSelector); - method_exchangeImplementations(originalMethod, swizzledMethod); - } - - { - // swizzle textOf - SEL originalSelector = @selector(textOf:equals:); - SEL swizzledSelector = @selector(markdown_textOf:equals:); - Method originalMethod = class_getInstanceMethod(cls, originalSelector); - Method swizzledMethod = class_getInstanceMethod(cls, swizzledSelector); - method_exchangeImplementations(originalMethod, swizzledMethod); - } - }); -} - -@end diff --git a/apple/RCTMarkdownUtils.h b/apple/RCTMarkdownUtils.h index 4d080bb8..da40ab16 100644 --- a/apple/RCTMarkdownUtils.h +++ b/apple/RCTMarkdownUtils.h @@ -8,7 +8,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) RCTMarkdownStyle *markdownStyle; @property (nonatomic) NSMutableArray *blockquoteRangesAndLevels; -- (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input withAttributes:(nullable NSDictionary*)attributes; +- (void)applyFormatting:(nonnull NSMutableAttributedString *)attributedString withDefaultTextAttributes:(nonnull NSDictionary *)defaultTextAttributes; @end diff --git a/apple/RCTMarkdownUtils.mm b/apple/RCTMarkdownUtils.mm index c22a8784..bf5db0a4 100644 --- a/apple/RCTMarkdownUtils.mm +++ b/apple/RCTMarkdownUtils.mm @@ -8,25 +8,10 @@ using namespace facebook; -@implementation RCTMarkdownUtils { - NSString *_prevInputString; - NSAttributedString *_prevAttributedString; - NSDictionary *_prevTextAttributes; - __weak RCTMarkdownStyle *_prevMarkdownStyle; -} +@implementation RCTMarkdownUtils -- (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input withAttributes:(nullable NSDictionary *)attributes +- (void)applyFormatting:(nonnull NSMutableAttributedString *)attributedString withDefaultTextAttributes:(nonnull NSDictionary *)defaultTextAttributes { - @synchronized (self) { - if (input == nil) { - return nil; - } - - NSString *inputString = [input string]; - if ([inputString isEqualToString:_prevInputString] && [attributes isEqualToDictionary:_prevTextAttributes] && [_markdownStyle isEqual:_prevMarkdownStyle]) { - return _prevAttributedString; - } - static std::shared_ptr runtime; static std::mutex runtimeMutex; auto lock = std::lock_guard(runtimeMutex); @@ -42,24 +27,27 @@ - (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input withA } jsi::Runtime &rt = *runtime; - auto text = jsi::String::createFromUtf8(rt, [inputString UTF8String]); + auto text = jsi::String::createFromUtf8(rt, [attributedString.string UTF8String]); auto func = rt.global().getPropertyAsFunction(rt, "parseExpensiMarkToRanges"); auto output = func.call(rt, text); - if (output.isUndefined()) { - return input; - } + // TODO: memoize parser output const auto &ranges = output.asObject(rt).asArray(rt); - NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:inputString attributes:attributes]; + NSRange fullRange = NSMakeRange(0, attributedString.length); + [attributedString beginEditing]; + [attributedString addAttributes:defaultTextAttributes range:fullRange]; + // If the attributed string ends with underlined text, blurring the single-line input imprints the underline style across the whole string. // It looks like a bug in iOS, as there is no underline style to be found in the attributed string, especially after formatting. // This is a workaround that applies the NSUnderlineStyleNone to the string before iterating over ranges which resolves this problem. - [attributedString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleNone] range:NSMakeRange(0, attributedString.length)]; + [attributedString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleNone] range:fullRange]; + // TODO: confirm if this workaround is still necessary _blockquoteRangesAndLevels = [NSMutableArray new]; + // TODO: use custom attribute to mark blockquotes for (size_t i = 0, n = ranges.size(rt); i < n; ++i) { const auto &item = ranges.getValueAtIndex(rt, i).asObject(rt); @@ -74,14 +62,6 @@ - (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input withA RCTApplyBaselineOffset(attributedString); [attributedString endEditing]; - - _prevInputString = inputString; - _prevAttributedString = attributedString; - _prevTextAttributes = attributes; - _prevMarkdownStyle = _markdownStyle; - - return attributedString; - } } - (void)applyRangeToAttributedString:(NSMutableAttributedString *)attributedString type:(const std::string)type start:(const int)start length:(const int)length depth:(const int)depth { diff --git a/apple/RCTTextInputComponentView+Markdown.h b/apple/RCTTextInputComponentView+Markdown.h deleted file mode 100644 index 346bc711..00000000 --- a/apple/RCTTextInputComponentView+Markdown.h +++ /dev/null @@ -1,23 +0,0 @@ -// This guard prevent this file to be compiled in the old architecture. -#ifdef RCT_NEW_ARCH_ENABLED - -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface RCTTextInputComponentView (Markdown) - -@property(nonatomic, nullable, getter=getMarkdownUtils) RCTMarkdownUtils *markdownUtils; - -- (void)markdown__setAttributedString:(NSAttributedString *)attributedString; - -- (BOOL)markdown__textOf:(NSAttributedString *)newText equals:(NSAttributedString *)oldText; - -- (void)_setAttributedString:(NSAttributedString *)attributedString; - -@end - -NS_ASSUME_NONNULL_END - -#endif /* RCT_NEW_ARCH_ENABLED */ diff --git a/apple/RCTTextInputComponentView+Markdown.mm b/apple/RCTTextInputComponentView+Markdown.mm deleted file mode 100644 index 5ce1e63e..00000000 --- a/apple/RCTTextInputComponentView+Markdown.mm +++ /dev/null @@ -1,97 +0,0 @@ -// This guard prevent this file to be compiled in the old architecture. -#ifdef RCT_NEW_ARCH_ENABLED - -#import -#import -#import -#import - -#import "MarkdownShadowFamilyRegistry.h" - -using namespace expensify::livemarkdown; - -@implementation RCTTextInputComponentView (Markdown) - -- (void)setMarkdownUtils:(RCTMarkdownUtils *)markdownUtils { - objc_setAssociatedObject(self, @selector(getMarkdownUtils), markdownUtils, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - - if (markdownUtils != nil) { - // force Markdown formatting on first render because `_setAttributedText` is called before `setMarkdownUtils` - RCTUITextField *backedTextInputView = [self getBackedTextInputView]; - backedTextInputView.attributedText = [markdownUtils parseMarkdown:backedTextInputView.attributedText withAttributes:backedTextInputView.defaultTextAttributes]; - } -} - -- (RCTMarkdownUtils *)getMarkdownUtils { - return objc_getAssociatedObject(self, @selector(getMarkdownUtils)); -} - -- (RCTUITextField *)getBackedTextInputView { - RCTUITextField *backedTextInputView = [self valueForKey:@"_backedTextInputView"]; - return backedTextInputView; -} - -- (void)markdown__setAttributedString:(NSAttributedString *)attributedString -{ - RCTMarkdownUtils *markdownUtils = [self getMarkdownUtils]; - RCTUITextField *backedTextInputView = [self getBackedTextInputView]; - if (markdownUtils != nil && backedTextInputView != nil) { - attributedString = [markdownUtils parseMarkdown:attributedString withAttributes:backedTextInputView.defaultTextAttributes]; - } else { - // If markdownUtils is undefined, the text input hasn't been mounted yet. It will - // update its state with the unformatted attributed string, we want to prevent displaying - // this state by applying markdown in the commit hook where we can read markdown styles - // from decorator props. - MarkdownShadowFamilyRegistry::forceNextStateUpdate((facebook::react::Tag)self.tag); - } - - // Call the original method - [self markdown__setAttributedString:attributedString]; -} - -- (BOOL)markdown__textOf:(NSAttributedString *)newText equals:(NSAttributedString *)oldText -{ - RCTMarkdownUtils *markdownUtils = [self getMarkdownUtils]; - if (markdownUtils != nil) { - // Emoji characters are automatically assigned an AppleColorEmoji NSFont and the original font is moved to NSOriginalFont - // We need to remove these attributes before comparison - NSMutableAttributedString *newTextCopy = [newText mutableCopy]; - NSMutableAttributedString *oldTextCopy = [oldText mutableCopy]; - [newTextCopy removeAttribute:@"NSFont" range:NSMakeRange(0, newTextCopy.length)]; - [oldTextCopy removeAttribute:@"NSFont" range:NSMakeRange(0, oldTextCopy.length)]; - [oldTextCopy removeAttribute:@"NSOriginalFont" range:NSMakeRange(0, oldTextCopy.length)]; - return [newTextCopy isEqualToAttributedString:oldTextCopy]; - } - - return [self markdown__textOf:newText equals:oldText]; -} - -+ (void)load -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - { - // swizzle _setAttributedString - Class cls = [self class]; - SEL originalSelector = @selector(_setAttributedString:); - SEL swizzledSelector = @selector(markdown__setAttributedString:); - Method originalMethod = class_getInstanceMethod(cls, originalSelector); - Method swizzledMethod = class_getInstanceMethod(cls, swizzledSelector); - method_exchangeImplementations(originalMethod, swizzledMethod); - } - - { - // swizzle _textOf - Class cls = [self class]; - SEL originalSelector = @selector(_textOf:equals:); - SEL swizzledSelector = @selector(markdown__textOf:equals:); - Method originalMethod = class_getInstanceMethod(cls, originalSelector); - Method swizzledMethod = class_getInstanceMethod(cls, swizzledSelector); - method_exchangeImplementations(originalMethod, swizzledMethod); - } - }); -} - -@end - -#endif /* RCT_NEW_ARCH_ENABLED */ diff --git a/apple/RCTUITextView+Markdown.h b/apple/RCTUITextView+Markdown.h deleted file mode 100644 index 40deedad..00000000 --- a/apple/RCTUITextView+Markdown.h +++ /dev/null @@ -1,19 +0,0 @@ -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface RCTUITextView (Private) -- (void)textDidChange; -@end - -@interface RCTUITextView (Markdown) - -@property(nonatomic, nullable, getter=getMarkdownUtils) RCTMarkdownUtils *markdownUtils; - -- (void)markdown_textDidChange; - -@end - -NS_ASSUME_NONNULL_END diff --git a/apple/RCTUITextView+Markdown.mm b/apple/RCTUITextView+Markdown.mm deleted file mode 100644 index 70f2d882..00000000 --- a/apple/RCTUITextView+Markdown.mm +++ /dev/null @@ -1,42 +0,0 @@ -#import -#import -#import - -@implementation RCTUITextView (Markdown) - -- (void)setMarkdownUtils:(RCTMarkdownUtils *)markdownUtils { - objc_setAssociatedObject(self, @selector(getMarkdownUtils), markdownUtils, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -- (RCTMarkdownUtils *)getMarkdownUtils { - return objc_getAssociatedObject(self, @selector(getMarkdownUtils)); -} - -- (void)markdown_textDidChange -{ - RCTMarkdownUtils *markdownUtils = [self getMarkdownUtils]; - if (markdownUtils != nil) { - UITextRange *range = self.selectedTextRange; - super.attributedText = [markdownUtils parseMarkdown:self.attributedText withAttributes:self.defaultTextAttributes]; - [super setSelectedTextRange:range]; // prevents cursor from jumping at the end when typing in the middle of the text - self.typingAttributes = self.defaultTextAttributes; // removes indent in new line when typing after blockquote - } - - // Call the original method - [self markdown_textDidChange]; -} - -+ (void)load -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - Class cls = [self class]; - SEL originalSelector = @selector(textDidChange); - SEL swizzledSelector = @selector(markdown_textDidChange); - Method originalMethod = class_getInstanceMethod(cls, originalSelector); - Method swizzledMethod = class_getInstanceMethod(cls, swizzledSelector); - method_exchangeImplementations(originalMethod, swizzledMethod); - }); -} - -@end diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 14c774cb..743b1a82 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1497,7 +1497,7 @@ PODS: - React-logger (= 0.75.2) - React-perflogger (= 0.75.2) - React-utils (= 0.75.2) - - RNLiveMarkdown (0.1.169): + - RNLiveMarkdown (0.1.172): - DoubleConversion - glog - hermes-engine @@ -1517,9 +1517,9 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNLiveMarkdown/newarch (= 0.1.169) + - RNLiveMarkdown/newarch (= 0.1.172) - Yoga - - RNLiveMarkdown/newarch (0.1.169): + - RNLiveMarkdown/newarch (0.1.172): - DoubleConversion - glog - hermes-engine @@ -1805,7 +1805,7 @@ SPEC CHECKSUMS: React-utils: 81a715d9c0a2a49047e77a86f3a2247408540deb ReactCodegen: 60973d382704c793c605b9be0fc7f31cb279442f ReactCommon: 6ef348087d250257c44c0204461c03f036650e9b - RNLiveMarkdown: 00ab78496be2ae15a15a83f14ba732c01624f02c + RNLiveMarkdown: 0d4f090ee84cfa2d2684b5b079547c5c9d2453b2 SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d Yoga: 2a45d7e59592db061217551fd3bbe2dd993817ae diff --git a/example/src/App.tsx b/example/src/App.tsx index d3d63426..042383e4 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -6,7 +6,9 @@ import * as TEST_CONST from './testConstants'; import {PlatformInfo} from './PlatformInfo'; export default function App() { - const [value, setValue] = React.useState(TEST_CONST.EXAMPLE_CONTENT); + const [value, setValue] = React.useState( + 'Hello *world*!\n> Lorem ipsum\nexample.com\n# Hello world\n# Hello world\n# Hello world', + ); const [textColorState, setTextColorState] = React.useState(false); const [linkColorState, setLinkColorState] = React.useState(false); const [textFontSizeState, setTextFontSizeState] = React.useState(false);