Skip to content

Commit

Permalink
Merge MarkdownTextInputDecoratorView into `MarkdownTextInputDecorat…
Browse files Browse the repository at this point in the history
…orComponentView` on iOS
  • Loading branch information
tomekzaw committed Mar 10, 2025
1 parent 17461c5 commit 1da1996
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 152 deletions.
126 changes: 112 additions & 14 deletions apple/MarkdownTextInputDecoratorComponentView.mm
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
#import <react/debug/react_native_assert.h>
#import <react/renderer/components/RNLiveMarkdownSpec/Props.h>
#import <React/RCTFabricComponentsPlugins.h>
#import <React/RCTUITextField.h>

#import <RNLiveMarkdown/MarkdownLayoutManager.h>
#import <RNLiveMarkdown/MarkdownShadowFamilyRegistry.h>
#import <RNLiveMarkdown/MarkdownTextInputDecoratorComponentView.h>
#import <RNLiveMarkdown/MarkdownTextInputDecoratorView.h>
#import <RNLiveMarkdown/MarkdownTextInputDecoratorViewComponentDescriptor.h>
#import <RNLiveMarkdown/RCTBackedTextFieldDelegateAdapter+Markdown.h>
#import <RNLiveMarkdown/RCTMarkdownStyle.h>
#import <RNLiveMarkdown/RCTTextInputComponentView+Markdown.h>
#import <RNLiveMarkdown/RCTUITextView+Markdown.h>

#import <RNLiveMarkdown/MarkdownTextInputDecoratorViewComponentDescriptor.h>
#import "MarkdownShadowFamilyRegistry.h"
#import "RCTFabricComponentsPlugins.h"
#import <objc/runtime.h>

using namespace facebook::react;

@implementation MarkdownTextInputDecoratorComponentView {
MarkdownTextInputDecoratorView *_view;
RCTMarkdownUtils *_markdownUtils;
RCTMarkdownStyle *_markdownStyle;
NSNumber *_parserId;
__weak RCTTextInputComponentView *_textInput;
__weak UIView<RCTBackedTextInputViewProtocol> *_backedTextInputView;
__weak RCTBackedTextFieldDelegateAdapter *_adapter;
__weak RCTUITextView *_textView;
ShadowNodeFamily::Shared _decoratorFamily;
}

Expand All @@ -31,10 +43,6 @@ - (instancetype)initWithFrame:(CGRect)frame
if (self = [super initWithFrame:frame]) {
static const auto defaultProps = std::make_shared<const MarkdownTextInputDecoratorViewProps>();
_props = defaultProps;

_view = [[MarkdownTextInputDecoratorView alloc] init];

self.contentView = _view;
}

return self;
Expand All @@ -43,11 +51,11 @@ - (instancetype)initWithFrame:(CGRect)frame
- (void)updateState:(const facebook::react::State::Shared &)state oldState:(const facebook::react::State::Shared &)oldState
{
auto data = std::static_pointer_cast<MarkdownTextInputDecoratorShadowNode::ConcreteState const>(state)->getData();

if (_decoratorFamily != nullptr) {
MarkdownShadowFamilyRegistry::unregisterFamilyForUpdates(_decoratorFamily);
}

_decoratorFamily = data.decoratorFamily;
MarkdownShadowFamilyRegistry::registerFamilyForUpdates(_decoratorFamily);
}
Expand All @@ -57,26 +65,116 @@ - (void)willMoveToSuperview:(UIView *)newSuperview {
MarkdownShadowFamilyRegistry::unregisterFamilyForUpdates(_decoratorFamily);
_decoratorFamily = nullptr;
}

[super willMoveToSuperview:newSuperview];
}

- (void)didMoveToWindow {
if (self.superview == nil) {
return;
}

NSArray *viewsArray = self.superview.subviews;
NSUInteger currentIndex = [viewsArray indexOfObject:self];

react_native_assert(currentIndex != 0 && currentIndex != NSNotFound && "Error while finding current component.");
UIView *view = [viewsArray objectAtIndex:currentIndex - 1];

react_native_assert([view isKindOfClass:[RCTTextInputComponentView class]] && "Previous sibling component is not an instance of RCTTextInputComponentView.");
_textInput = (RCTTextInputComponentView *)view;
_backedTextInputView = [_textInput valueForKey:@"_backedTextInputView"];

_markdownUtils = [[RCTMarkdownUtils alloc] init];
react_native_assert(_markdownStyle != nil);
[_markdownUtils setMarkdownStyle:_markdownStyle];
[_markdownUtils setParserId:_parserId];

[_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];
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)
// Consider removing this fix if it is no longer needed after migrating to TextKit 2.
CGSize contentSize = _textView.contentSize;
CGRect textBounds = [layoutManager usedRectForTextContainer:_textView.textContainer];
contentSize.height = textBounds.size.height + _textView.textContainerInset.top + _textView.textContainerInset.bottom;
[_textView setContentSize:contentSize];

layoutManager.allowsNonContiguousLayout = NO; // workaround for onScroll issue
object_setClass(layoutManager, [MarkdownLayoutManager class]);
[layoutManager setValue:_markdownUtils forKey:@"markdownUtils"];
} else {
react_native_assert(false && "Cannot enable Markdown for this type of TextInput.");
}
}

- (void)willMoveToWindow:(UIWindow *)newWindow
{
if (_textInput != nil) {
[_textInput setMarkdownUtils:nil];
}
if (_adapter != nil) {
[_adapter setMarkdownUtils:nil];
}
if (_textView != nil) {
[_textView setMarkdownUtils:nil];
if (_textView.layoutManager != nil && [object_getClass(_textView.layoutManager) isEqual:[MarkdownLayoutManager class]]) {
[_textView.layoutManager setValue:nil forKey:@"markdownUtils"];
object_setClass(_textView.layoutManager, [NSLayoutManager class]);
}
}
}

- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &oldViewProps = *std::static_pointer_cast<MarkdownTextInputDecoratorViewProps const>(_props);
const auto &newViewProps = *std::static_pointer_cast<MarkdownTextInputDecoratorViewProps const>(props);

if (oldViewProps.parserId != newViewProps.parserId) {
[_view setParserId:@(newViewProps.parserId)];
[self setParserId:@(newViewProps.parserId)];
}

// TODO: if (oldViewProps.markdownStyle != newViewProps.markdownStyle)
RCTMarkdownStyle *markdownStyle = [[RCTMarkdownStyle alloc] initWithStruct:newViewProps.markdownStyle];
[_view setMarkdownStyle:markdownStyle];
[self setMarkdownStyle:markdownStyle];

[super updateProps:props oldProps:oldProps];
}

- (void)setMarkdownStyle:(RCTMarkdownStyle *)markdownStyle
{
// TODO: move to updateProps method
_markdownStyle = markdownStyle;
[_markdownUtils setMarkdownStyle:markdownStyle];
[self applyNewStyles];
}

- (void)setParserId:(NSNumber *)parserId
{
// TODO: move to updateProps method
_parserId = parserId;
[_markdownUtils setParserId:parserId];
[self applyNewStyles];
}

- (void)applyNewStyles
{
// TODO: call applyNewStyles only once if both markdownStyle and parserId change
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
[_textInput _setAttributedString:_backedTextInputView.attributedText];
}
}

Class<RCTComponentViewProtocol> MarkdownTextInputDecoratorViewCls(void)
{
return MarkdownTextInputDecoratorComponentView.class;
Expand Down
14 changes: 0 additions & 14 deletions apple/MarkdownTextInputDecoratorView.h

This file was deleted.

110 changes: 0 additions & 110 deletions apple/MarkdownTextInputDecoratorView.mm

This file was deleted.

16 changes: 2 additions & 14 deletions apple/MarkdownTextInputDecoratorViewManager.mm
Original file line number Diff line number Diff line change
@@ -1,23 +1,11 @@
#import <RNLiveMarkdown/MarkdownTextInputDecoratorViewManager.h>
#import <RNLiveMarkdown/MarkdownTextInputDecoratorView.h>

@implementation MarkdownTextInputDecoratorViewManager

RCT_EXPORT_MODULE(MarkdownTextInputDecoratorView)

- (UIView *)view
{
return [[MarkdownTextInputDecoratorView alloc] init];
}
RCT_EXPORT_VIEW_PROPERTY(markdownStyle, NSDictionary)

RCT_CUSTOM_VIEW_PROPERTY(markdownStyle, NSDictionary, MarkdownTextInputDecoratorView)
{
// implemented in MarkdownTextInputDecoratorView updateProps:
}

RCT_CUSTOM_VIEW_PROPERTY(parserId, NSNumber, MarkdownTextInputDecoratorView)
{
// implemented in MarkdownTextInputDecoratorView updateProps:
}
RCT_EXPORT_VIEW_PROPERTY(parserId, NSNumber)

@end

0 comments on commit 1da1996

Please sign in to comment.