diff --git a/ios/RTNMarkdown.mm b/ios/RTNMarkdown.mm index c54f45a..d0a7f30 100644 --- a/ios/RTNMarkdown.mm +++ b/ios/RTNMarkdown.mm @@ -3,11 +3,14 @@ #import "RTNMarkdown.h" #import "RTNMarkdownComponentProps.h" #import "RTNMarkdownFormatter.h" +#import "RTNMarkdownParagraphTextView.h" #import "RTNMarkdownTextContentStorageDelegate.h" #import "RTNMarkdownTextLayoutManagerDelegate.h" #import "RTNMarkdownUITextView.h" #import +#import +#import #import #import @@ -22,7 +25,7 @@ @interface RTNMarkdown () @end @implementation RTNMarkdown { - UIView *_backedTextInputView; + UIView *_backedTextInputView; // s77rt TODO RTNMarkdownFormatter *_formatter; RTNMarkdownTextLayoutManagerDelegate *_textLayoutManagerDelegate; @@ -79,21 +82,29 @@ - (void)updateProps:(Props::Shared const &)props - (void)mountChildComponentView: (UIView *)childComponentView index:(NSInteger)index { - _backedTextInputView = - [childComponentView valueForKey:@"_backedTextInputView"]; - - // Only UITextView (RCTUITextView) exposes a text layout manager - if ([_backedTextInputView isKindOfClass:[RCTUITextView class]]) { - object_setClass((RCTUITextView *)_backedTextInputView, - objc_getClass("RTNMarkdownUITextView")); - - NSTextLayoutManager *textLayoutManager = - ((RTNMarkdownUITextView *)_backedTextInputView).textLayoutManager; - NSTextContentStorage *textContentStorage = - (NSTextContentStorage *)(textLayoutManager.textContentManager); - - textLayoutManager.delegate = _textLayoutManagerDelegate; - textContentStorage.delegate = _textContentStorageDelegate; + if ([childComponentView isKindOfClass:[RCTTextInputComponentView class]]) { + _backedTextInputView = + [childComponentView valueForKey:@"_backedTextInputView"]; + + // Only UITextView (RCTUITextView) exposes a text layout manager + if ([_backedTextInputView isKindOfClass:[RCTUITextView class]]) { + object_setClass((RCTUITextView *)_backedTextInputView, + objc_getClass("RTNMarkdownUITextView")); + + NSTextLayoutManager *textLayoutManager = + ((RTNMarkdownUITextView *)_backedTextInputView).textLayoutManager; + NSTextContentStorage *textContentStorage = + (NSTextContentStorage *)(textLayoutManager.textContentManager); + + textLayoutManager.delegate = _textLayoutManagerDelegate; + textContentStorage.delegate = _textContentStorageDelegate; + } + } else if ([childComponentView + isKindOfClass:[RCTParagraphComponentView class]]) { + object_setClass([childComponentView valueForKey:@"_textView"], + objc_getClass("RTNMarkdownParagraphTextView")); + + NSLog(@"this is a text"); } [super mountChildComponentView:childComponentView index:index]; diff --git a/ios/RTNMarkdownParagraphTextView.h b/ios/RTNMarkdownParagraphTextView.h new file mode 100644 index 0000000..3314ebf --- /dev/null +++ b/ios/RTNMarkdownParagraphTextView.h @@ -0,0 +1,23 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +// Defined here but not exported. Redefine +// https://github.com/facebook/react-native/blob/b028f842333445027f0a4d8c87b429aefcec7239/packages/react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm#L27C1-L35 +// +// ParagraphTextView is an auxiliary view we set as contentView so the drawing +// can happen on top of the layers manipulated by RCTViewComponentView (the +// parent view) +@interface RCTParagraphTextView : UIView + +@property(nonatomic) + facebook::react::ParagraphShadowNode::ConcreteState::Shared state; +@property(nonatomic) facebook::react::ParagraphAttributes paragraphAttributes; +@property(nonatomic) facebook::react::LayoutMetrics layoutMetrics; + +@end + +@interface RTNMarkdownParagraphTextView : RCTParagraphTextView +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/RTNMarkdownParagraphTextView.mm b/ios/RTNMarkdownParagraphTextView.mm new file mode 100644 index 0000000..6bf51a1 --- /dev/null +++ b/ios/RTNMarkdownParagraphTextView.mm @@ -0,0 +1,28 @@ +#import "RTNMarkdownParagraphTextView.h" + +@implementation RTNMarkdownParagraphTextView { + // DO NOT ADD IVARS. + // This class is used for ISA swizzling. +} + +// s77rt should I move state overwritting to the component view? +- (void)drawRect:(CGRect)rect { + NSLog(@"I got you!"); + + auto data = self.state->getData(); + + if (data.attributedString.getString().find("s77rt") == std::string::npos) { + auto fragment = facebook::react::AttributedString::Fragment{}; + fragment.string = "s77rt"; + + data.attributedString.appendFragment(std::move(fragment)); + self.state->updateState(std::move(data)); + + // updating the state will trigger another drawRect + return; + } + + [super drawRect:rect]; +} + +@end diff --git a/js/MarkdownText/index.tsx b/js/MarkdownText/index.tsx new file mode 100644 index 0000000..d8e35cd --- /dev/null +++ b/js/MarkdownText/index.tsx @@ -0,0 +1,31 @@ +import React, { useMemo, forwardRef } from "react"; +import type { ForwardedRef } from "react"; +import { Text } from "react-native"; +import RTNMarkdownNativeComponent from "../RTNMarkdownNativeComponent"; +import type { MarkdownTextProps } from "../types"; +import { processStyles } from "../utils"; + +function MarkdownText( + { + markdownStyles: markdownStylesProp, + children, + ...rest + }: MarkdownTextProps, + ref: ForwardedRef +) { + const markdownStyles = useMemo(() => { + const styles = JSON.parse(JSON.stringify(markdownStylesProp)); + processStyles(styles); + return styles; + }, [markdownStylesProp]); + + return ( + + + {children} + + + ); +} + +export default React.memo(forwardRef(MarkdownText)); diff --git a/js/MarkdownText/index.web.tsx b/js/MarkdownText/index.web.tsx new file mode 100644 index 0000000..ee80a68 --- /dev/null +++ b/js/MarkdownText/index.web.tsx @@ -0,0 +1,21 @@ +import React, { forwardRef } from "react"; +import type { ForwardedRef } from "react"; +import { Text } from "react-native"; +import type { MarkdownTextProps } from "../types"; + +function MarkdownText( + { + markdownStyles: markdownStylesProp, + children, + ...rest + }: MarkdownTextProps, + ref: ForwardedRef +) { + return ( + + {children} + + ); +} + +export default React.memo(forwardRef(MarkdownText)); diff --git a/js/index.tsx b/js/index.tsx index ebac328..0b9a04b 100644 --- a/js/index.tsx +++ b/js/index.tsx @@ -1,2 +1,7 @@ export { default as MarkdownTextInput } from "./MarkdownTextInput"; -export type { MarkdownStyles, MarkdownTextInputProps } from "./types"; +export { default as MarkdownText } from "./MarkdownText"; +export type { + MarkdownStyles, + MarkdownTextInputProps, + MarkdownTextProps, +} from "./types"; diff --git a/js/types.ts b/js/types.ts index 7cf1eef..0682296 100644 --- a/js/types.ts +++ b/js/types.ts @@ -1,4 +1,4 @@ -import type { TextInputProps, ColorValue } from "react-native"; +import type { TextInputProps, TextProps, ColorValue } from "react-native"; type CommonStyle = { backgroundColor?: ColorValue | undefined; @@ -71,5 +71,6 @@ type MarkdownProps = { }; type MarkdownTextInputProps = TextInputProps & MarkdownProps; +type MarkdownTextProps = TextProps & MarkdownProps; -export type { MarkdownStyles, MarkdownTextInputProps }; +export type { MarkdownStyles, MarkdownTextInputProps, MarkdownTextProps };