@expensify/react-native-live-markdown 0.1.44 → 0.1.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/RNLiveMarkdown.podspec +13 -0
  2. package/android/build.gradle +12 -0
  3. package/android/proguard-rules.pro +1 -0
  4. package/android/src/main/java/com/expensify/livemarkdown/CustomFabricUIManager.java +47 -0
  5. package/android/src/main/java/com/expensify/livemarkdown/CustomMountingManager.java +248 -0
  6. package/android/src/main/java/com/expensify/livemarkdown/LiveMarkdownModule.java +27 -0
  7. package/android/src/main/java/com/expensify/livemarkdown/LiveMarkdownPackage.java +30 -3
  8. package/android/src/main/java/com/expensify/livemarkdown/MarkdownFontFamilySpan.java +7 -1
  9. package/android/src/main/java/com/expensify/livemarkdown/MarkdownFontSizeSpan.java +3 -1
  10. package/android/src/main/java/com/expensify/livemarkdown/MarkdownUtils.java +2 -6
  11. package/android/src/main/new_arch/CMakeLists.txt +82 -0
  12. package/android/src/main/new_arch/MarkdownCommitHook.cpp +173 -0
  13. package/android/src/main/new_arch/MarkdownCommitHook.h +52 -0
  14. package/android/src/main/new_arch/NativeProxy.cpp +38 -0
  15. package/android/src/main/new_arch/NativeProxy.h +37 -0
  16. package/android/src/main/new_arch/OnLoad.cpp +8 -0
  17. package/android/src/main/new_arch/RNLiveMarkdownSpec.cpp +44 -0
  18. package/android/src/main/new_arch/RNLiveMarkdownSpec.h +37 -0
  19. package/android/src/newarch/NativeProxy.java +24 -0
  20. package/android/src/oldarch/NativeLiveMarkdownModuleSpec.java +37 -0
  21. package/android/src/oldarch/NativeProxy.java +9 -0
  22. package/cpp/react/renderer/components/RNLiveMarkdownSpec/MarkdownShadowFamilyRegistry.cpp +58 -0
  23. package/cpp/react/renderer/components/RNLiveMarkdownSpec/MarkdownShadowFamilyRegistry.h +35 -0
  24. package/cpp/react/renderer/components/RNLiveMarkdownSpec/MarkdownTextInputDecoratorShadowNode.cpp +38 -0
  25. package/cpp/react/renderer/components/RNLiveMarkdownSpec/MarkdownTextInputDecoratorShadowNode.h +48 -0
  26. package/cpp/react/renderer/components/RNLiveMarkdownSpec/MarkdownTextInputDecoratorState.h +34 -0
  27. package/cpp/react/renderer/components/RNLiveMarkdownSpec/MarkdownTextInputDecoratorViewComponentDescriptor.h +17 -0
  28. package/ios/MarkdownCommitHook.h +42 -0
  29. package/ios/MarkdownCommitHook.mm +195 -0
  30. package/ios/MarkdownTextInputDecoratorComponentView.mm +24 -1
  31. package/ios/MarkdownTextInputDecoratorView.mm +5 -1
  32. package/ios/RCTBackedTextFieldDelegateAdapter+Markdown.m +1 -1
  33. package/ios/RCTBaseTextInputView+Markdown.m +2 -2
  34. package/ios/RCTLiveMarkdownModule.h +12 -0
  35. package/ios/RCTLiveMarkdownModule.mm +48 -0
  36. package/ios/RCTMarkdownUtils.h +1 -3
  37. package/ios/RCTMarkdownUtils.mm +129 -136
  38. package/ios/RCTTextInputComponentView+Markdown.mm +18 -4
  39. package/ios/RCTUITextView+Markdown.mm +1 -1
  40. package/lib/commonjs/MarkdownTextInput.js +4 -0
  41. package/lib/commonjs/MarkdownTextInput.js.map +1 -1
  42. package/lib/commonjs/MarkdownTextInputDecoratorViewNativeComponent.js +3 -1
  43. package/lib/commonjs/MarkdownTextInputDecoratorViewNativeComponent.js.map +1 -1
  44. package/lib/commonjs/NativeLiveMarkdownModule.js +9 -0
  45. package/lib/commonjs/NativeLiveMarkdownModule.js.map +1 -0
  46. package/lib/module/MarkdownTextInput.js +4 -0
  47. package/lib/module/MarkdownTextInput.js.map +1 -1
  48. package/lib/module/MarkdownTextInputDecoratorViewNativeComponent.js +3 -1
  49. package/lib/module/MarkdownTextInputDecoratorViewNativeComponent.js.map +1 -1
  50. package/lib/module/NativeLiveMarkdownModule.js +3 -0
  51. package/lib/module/NativeLiveMarkdownModule.js.map +1 -0
  52. package/lib/typescript/src/MarkdownTextInput.d.ts.map +1 -1
  53. package/lib/typescript/src/MarkdownTextInputDecoratorViewNativeComponent.d.ts.map +1 -1
  54. package/lib/typescript/src/NativeLiveMarkdownModule.d.ts +7 -0
  55. package/lib/typescript/src/NativeLiveMarkdownModule.d.ts.map +1 -0
  56. package/package.json +3 -2
  57. package/react-native.config.js +12 -0
  58. package/src/MarkdownTextInput.tsx +5 -0
  59. package/src/MarkdownTextInputDecoratorViewNativeComponent.ts +3 -1
  60. package/src/NativeLiveMarkdownModule.ts +8 -0
@@ -0,0 +1,34 @@
1
+ #pragma once
2
+
3
+ #include <react/renderer/core/ShadowNodeFamily.h>
4
+
5
+ namespace facebook {
6
+ namespace react {
7
+
8
+ class JSI_EXPORT MarkdownTextInputDecoratorState final {
9
+ public:
10
+ using Shared = std::shared_ptr<const MarkdownTextInputDecoratorState>;
11
+
12
+ MarkdownTextInputDecoratorState() : decoratorFamily(nullptr){};
13
+ MarkdownTextInputDecoratorState(
14
+ const ShadowNodeFamily::Shared textInputFamily_)
15
+ : decoratorFamily(textInputFamily_){};
16
+
17
+ #ifdef ANDROID
18
+ MarkdownTextInputDecoratorState(
19
+ MarkdownTextInputDecoratorState const &previousState, folly::dynamic data)
20
+ : decoratorFamily(previousState.decoratorFamily){};
21
+ #endif
22
+
23
+ const ShadowNodeFamily::Shared decoratorFamily;
24
+
25
+ #ifdef ANDROID
26
+ folly::dynamic getDynamic() const {
27
+ return folly::dynamic::object("decoratorFamily", "pointer should be here?");
28
+ }
29
+ MapBuffer getMapBuffer() const { return MapBufferBuilder::EMPTY(); };
30
+ #endif
31
+ };
32
+
33
+ } // namespace react
34
+ } // namespace facebook
@@ -0,0 +1,17 @@
1
+ #pragma once
2
+
3
+ #include "MarkdownTextInputDecoratorShadowNode.h"
4
+ #include <react/debug/react_native_assert.h>
5
+ #include <react/renderer/core/ConcreteComponentDescriptor.h>
6
+
7
+ namespace facebook {
8
+ namespace react {
9
+
10
+ class MarkdownTextInputDecoratorViewComponentDescriptor final
11
+ : public ConcreteComponentDescriptor<MarkdownTextInputDecoratorShadowNode> {
12
+ public:
13
+ using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
14
+ };
15
+
16
+ } // namespace react
17
+ } // namespace facebook
@@ -0,0 +1,42 @@
1
+ #pragma once
2
+ #ifdef RCT_NEW_ARCH_ENABLED
3
+
4
+ #include <react/renderer/components/iostextinput/TextInputShadowNode.h>
5
+ #include <react/renderer/uimanager/UIManager.h>
6
+ #include <react/renderer/uimanager/UIManagerCommitHook.h>
7
+
8
+ #include <memory>
9
+
10
+ #include "MarkdownTextInputDecoratorShadowNode.h"
11
+
12
+ using namespace facebook::react;
13
+
14
+ namespace livemarkdown {
15
+
16
+ struct MarkdownTextInputDecoratorPair {
17
+ const std::shared_ptr<const TextInputShadowNode> textInput;
18
+ const std::shared_ptr<const MarkdownTextInputDecoratorShadowNode> decorator;
19
+ };
20
+
21
+ class MarkdownCommitHook : public UIManagerCommitHook {
22
+ public:
23
+ MarkdownCommitHook(const std::shared_ptr<UIManager> &uiManager);
24
+
25
+ ~MarkdownCommitHook() noexcept override;
26
+
27
+ void commitHookWasRegistered(UIManager const &) noexcept override {}
28
+
29
+ void commitHookWasUnregistered(UIManager const &) noexcept override {}
30
+
31
+ RootShadowNode::Unshared shadowTreeWillCommit(
32
+ ShadowTree const &shadowTree,
33
+ RootShadowNode::Shared const &oldRootShadowNode,
34
+ RootShadowNode::Unshared const &newRootShadowNode) noexcept override;
35
+
36
+ private:
37
+ const std::shared_ptr<UIManager> uiManager_;
38
+ };
39
+
40
+ } // namespace livemarkdown
41
+
42
+ #endif // RCT_NEW_ARCH_ENABLED
@@ -0,0 +1,195 @@
1
+ #ifdef RCT_NEW_ARCH_ENABLED
2
+
3
+ #include <React/RCTUtils.h>
4
+ #include <react/renderer/core/ComponentDescriptor.h>
5
+ #include <react/renderer/textlayoutmanager/RCTAttributedTextUtils.h>
6
+
7
+ #include "MarkdownCommitHook.h"
8
+ #include "MarkdownShadowFamilyRegistry.h"
9
+ #include "RCTMarkdownStyle.h"
10
+ #include "RCTMarkdownUtils.h"
11
+
12
+ using namespace facebook::react;
13
+
14
+ namespace livemarkdown {
15
+
16
+ MarkdownCommitHook::MarkdownCommitHook(
17
+ const std::shared_ptr<UIManager> &uiManager)
18
+ : uiManager_(uiManager) {
19
+ uiManager_->registerCommitHook(*this);
20
+ }
21
+
22
+ MarkdownCommitHook::~MarkdownCommitHook() noexcept {
23
+ uiManager_->unregisterCommitHook(*this);
24
+ }
25
+
26
+ RootShadowNode::Unshared MarkdownCommitHook::shadowTreeWillCommit(
27
+ ShadowTree const &, RootShadowNode::Shared const &,
28
+ RootShadowNode::Unshared const &newRootShadowNode) noexcept {
29
+ auto rootNode = newRootShadowNode->ShadowNode::clone(ShadowNodeFragment{});
30
+
31
+ // A preface to why we do the weird thing below:
32
+ // On the new architecture there are two ways of measuring text on iOS: by
33
+ // value and by pointer. When done by value, the attributed string to be
34
+ // measured is created on the c++ side. We cannot modify this process as we do
35
+ // not extend TextInputShadowNode. We also cannot really change the layout
36
+ // manager used to do this, since it's a private field (ok, we can but in a
37
+ // not very nice way). But also, the logic for parsing and applying markdown
38
+ // is written in JS/ObjC and we really wouldn't want to reimplement it in c++.
39
+ //
40
+ // Nice thing is that it can also be done by pointer to NSAttributedString,
41
+ // which is the platform's way to handle styled text, and is also used by Live
42
+ // Markdown. On this path, the measurement is done by the OS APIs. The thing
43
+ // we want to make sure of, is that markdown-decorated text input always uses
44
+ // this path and uses a pointer to a string with markdown styles applied.
45
+ // Thankfully, RN provides nice utility functions that allow to convert
46
+ // between the RN's AttributedString and iOS's NSAttributedString. The logic
47
+ // below does exactly that.
48
+
49
+ // In order to properly apply markdown formatting to the text input, we need
50
+ // to update the TextInputShadowNode's state with styled string, but we only
51
+ // have access to the ShadowNodeFamilies of the decorator components. We also
52
+ // know that a markdown decorator is always preceded with the TextInput to
53
+ // decorate, so we need to take the sibling.
54
+ std::vector<MarkdownTextInputDecoratorPair> nodesToUpdate;
55
+ MarkdownShadowFamilyRegistry::runForEveryFamily([&rootNode, &nodesToUpdate](
56
+ ShadowNodeFamily::Shared
57
+ family) {
58
+ // get the path from the root to the node from the decorator family
59
+ const auto ancestors = family->getAncestors(*rootNode);
60
+
61
+ if (!ancestors.empty()) {
62
+ auto &parentNode = ancestors.back().first.get();
63
+ auto index = ancestors.back().second;
64
+
65
+ // this is node represented by one of the registered families and since we
66
+ // only register markdown decorator shadow families, static casting should
67
+ // be safe here
68
+ const auto &decoratorNode =
69
+ std::static_pointer_cast<const MarkdownTextInputDecoratorShadowNode>(
70
+ parentNode.getChildren().at(index));
71
+ // text input always precedes the decorator component
72
+ const auto &previousSibling = parentNode.getChildren().at(index - 1);
73
+
74
+ if (const auto &textInputNode =
75
+ std::dynamic_pointer_cast<const TextInputShadowNode>(
76
+ previousSibling)) {
77
+ // store the pair of text input and decorator to update in the next step
78
+ // we need both, decorator to get markdown style and text input to
79
+ // update it
80
+ nodesToUpdate.push_back({
81
+ textInputNode,
82
+ decoratorNode,
83
+ });
84
+ }
85
+ }
86
+ });
87
+
88
+ for (const auto &nodes : nodesToUpdate) {
89
+ const auto &textInputState =
90
+ *std::static_pointer_cast<const ConcreteState<TextInputState>>(
91
+ nodes.textInput->getState());
92
+ const auto &stateData = textInputState.getData();
93
+ const auto fontSizeMultiplier =
94
+ newRootShadowNode->getConcreteProps().layoutContext.fontSizeMultiplier;
95
+
96
+ // We only want to update the shadow node when the attributed string is
97
+ // stored by value If it's stored by pointer, the markdown formatting should
98
+ // already by applied to it, since the only source of that pointer (besides
99
+ // this commit hook) is RCTTextInputComponentView, which has the relevant
100
+ // method swizzled to make sure the markdown styles are always applied
101
+ // before updating state. There are two caveats:
102
+ // 1. On the first render the swizzled method will not apply markdown since
103
+ // the native component
104
+ // is not mounted yet. In that case we save the tag to update in the
105
+ // method applying markdown formatting and apply it here instead,
106
+ // preventing wrong layout on reloads.
107
+ // 2. When the markdown style prop is changed, the native state needs to be
108
+ // updated to reflect
109
+ // them. In that case the relevant tag is saved in the registry when the
110
+ // new shadow node is created.
111
+ if (stateData.attributedStringBox.getMode() ==
112
+ AttributedStringBox::Mode::Value ||
113
+ MarkdownShadowFamilyRegistry::shouldForceUpdate(
114
+ nodes.textInput->getTag())) {
115
+ rootNode = rootNode->cloneTree(
116
+ nodes.textInput->getFamily(),
117
+ [&nodes, &textInputState, &stateData,
118
+ fontSizeMultiplier](const ShadowNode &node) {
119
+ const auto &markdownProps = *std::static_pointer_cast<
120
+ MarkdownTextInputDecoratorViewProps const>(
121
+ nodes.decorator->getProps());
122
+ const auto &textInputProps =
123
+ *std::static_pointer_cast<TextInputProps const>(
124
+ nodes.textInput->getProps());
125
+
126
+ const auto defaultTextAttributes =
127
+ textInputProps.getEffectiveTextAttributes(fontSizeMultiplier);
128
+ const auto defaultNSTextAttributes =
129
+ RCTNSTextAttributesFromTextAttributes(defaultTextAttributes);
130
+
131
+ // this can possibly be optimized
132
+ RCTMarkdownStyle *markdownStyle = [[RCTMarkdownStyle alloc]
133
+ initWithStruct:markdownProps.markdownStyle];
134
+ RCTMarkdownUtils *utils = [[RCTMarkdownUtils alloc] init];
135
+ [utils setMarkdownStyle:markdownStyle];
136
+
137
+ // convert the attibuted string stored in state to
138
+ // NSAttributedString
139
+ auto nsAttributedString =
140
+ RCTNSAttributedStringFromAttributedStringBox(
141
+ stateData.attributedStringBox);
142
+
143
+ // Handles the first render, where the text stored in props is
144
+ // different than the one stored in state The one in state is empty,
145
+ // while the one in props is passed from JS If we don't update the
146
+ // state here, we'll end up with a one-default-line-sized text
147
+ // input. A better condition to do that can be probably chosen, but
148
+ // this seems to work
149
+ auto plainString =
150
+ std::string([[nsAttributedString string] UTF8String]);
151
+ if (plainString != textInputProps.text) {
152
+ // creates new AttributedString from props, adapted from
153
+ // TextInputShadowNode (ios one, text inputs are
154
+ // platform-specific)
155
+ auto attributedString = AttributedString{};
156
+ attributedString.appendFragment(AttributedString::Fragment{
157
+ textInputProps.text, defaultTextAttributes});
158
+
159
+ auto attachments = BaseTextShadowNode::Attachments{};
160
+ BaseTextShadowNode::buildAttributedString(
161
+ defaultTextAttributes, *nodes.textInput, attributedString,
162
+ attachments);
163
+
164
+ // convert the newly created attributed string to
165
+ // NSAttributedString
166
+ nsAttributedString = RCTNSAttributedStringFromAttributedStringBox(
167
+ AttributedStringBox{attributedString});
168
+ }
169
+
170
+ // apply markdown
171
+ auto newString = [utils parseMarkdown:nsAttributedString
172
+ withAttributes:defaultNSTextAttributes];
173
+
174
+ // create a clone of the old TextInputState and update the
175
+ // attributed string box to point to the string with markdown
176
+ // applied
177
+ auto newStateData = std::make_shared<TextInputState>(stateData);
178
+ newStateData->attributedStringBox =
179
+ RCTAttributedStringBoxFromNSAttributedString(newString);
180
+
181
+ // clone the text input with the new state
182
+ return node.clone({
183
+ .state = std::make_shared<const ConcreteState<TextInputState>>(
184
+ newStateData, textInputState),
185
+ });
186
+ });
187
+ }
188
+ }
189
+
190
+ return std::static_pointer_cast<RootShadowNode>(rootNode);
191
+ }
192
+
193
+ } // namespace livemarkdown
194
+
195
+ #endif // RCT_NEW_ARCH_ENABLED
@@ -1,18 +1,20 @@
1
1
  // This guard prevent this file to be compiled in the old architecture.
2
2
  #ifdef RCT_NEW_ARCH_ENABLED
3
- #import <react/renderer/components/RNLiveMarkdownSpec/ComponentDescriptors.h>
4
3
  #import <react/renderer/components/RNLiveMarkdownSpec/Props.h>
5
4
 
6
5
  #import <RNLiveMarkdown/MarkdownTextInputDecoratorComponentView.h>
7
6
  #import <RNLiveMarkdown/MarkdownTextInputDecoratorView.h>
8
7
  #import <RNLiveMarkdown/RCTMarkdownStyle.h>
9
8
 
9
+ #import <RNLiveMarkdown/MarkdownTextInputDecoratorViewComponentDescriptor.h>
10
+ #import "MarkdownShadowFamilyRegistry.h"
10
11
  #import "RCTFabricComponentsPlugins.h"
11
12
 
12
13
  using namespace facebook::react;
13
14
 
14
15
  @implementation MarkdownTextInputDecoratorComponentView {
15
16
  MarkdownTextInputDecoratorView *_view;
17
+ ShadowNodeFamily::Shared _decoratorFamily;
16
18
  }
17
19
 
18
20
  + (ComponentDescriptorProvider)componentDescriptorProvider
@@ -34,6 +36,27 @@ using namespace facebook::react;
34
36
  return self;
35
37
  }
36
38
 
39
+ - (void)updateState:(const facebook::react::State::Shared &)state oldState:(const facebook::react::State::Shared &)oldState
40
+ {
41
+ auto data = std::static_pointer_cast<MarkdownTextInputDecoratorShadowNode::ConcreteState const>(state)->getData();
42
+
43
+ if (_decoratorFamily != nullptr) {
44
+ MarkdownShadowFamilyRegistry::unregisterFamilyForUpdates(_decoratorFamily);
45
+ }
46
+
47
+ _decoratorFamily = data.decoratorFamily;
48
+ MarkdownShadowFamilyRegistry::registerFamilyForUpdates(_decoratorFamily);
49
+ }
50
+
51
+ - (void)willMoveToSuperview:(UIView *)newSuperview {
52
+ if (newSuperview == nil) {
53
+ MarkdownShadowFamilyRegistry::unregisterFamilyForUpdates(_decoratorFamily);
54
+ _decoratorFamily = nullptr;
55
+ }
56
+
57
+ [super willMoveToSuperview:newSuperview];
58
+ }
59
+
37
60
  - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
38
61
  {
39
62
  const auto &oldViewProps = *std::static_pointer_cast<MarkdownTextInputDecoratorViewProps const>(_props);
@@ -58,7 +58,7 @@
58
58
  UIView<RCTBackedTextInputViewProtocol> *backedTextInputView = _textInput.backedTextInputView;
59
59
  #endif /* RCT_NEW_ARCH_ENABLED */
60
60
 
61
- _markdownUtils = [[RCTMarkdownUtils alloc] initWithBackedTextInputView:backedTextInputView];
61
+ _markdownUtils = [[RCTMarkdownUtils alloc] init];
62
62
  react_native_assert(_markdownStyle != nil);
63
63
  [_markdownUtils setMarkdownStyle:_markdownStyle];
64
64
 
@@ -100,7 +100,11 @@
100
100
  {
101
101
  _markdownStyle = markdownStyle;
102
102
  [_markdownUtils setMarkdownStyle:markdownStyle];
103
+ #ifdef RCT_NEW_ARCH_ENABLED
104
+ [_textInput textInputDidChange]; // apply new styles
105
+ #else
103
106
  [_textInput setAttributedText:_textInput.attributedText]; // apply new styles
107
+ #endif
104
108
  }
105
109
 
106
110
  @end
@@ -19,7 +19,7 @@
19
19
  if (markdownUtils != nil) {
20
20
  RCTUITextField *backedTextInputView = [self valueForKey:@"_backedTextInputView"];
21
21
  UITextRange *range = backedTextInputView.selectedTextRange;
22
- backedTextInputView.attributedText = [markdownUtils parseMarkdown:backedTextInputView.attributedText];
22
+ backedTextInputView.attributedText = [markdownUtils parseMarkdown:backedTextInputView.attributedText withAttributes:backedTextInputView.defaultTextAttributes];
23
23
  [backedTextInputView setSelectedTextRange:range notifyDelegate:YES];
24
24
  }
25
25
 
@@ -16,7 +16,7 @@
16
16
  {
17
17
  RCTMarkdownUtils *markdownUtils = [self getMarkdownUtils];
18
18
  if (markdownUtils != nil) {
19
- attributedText = [markdownUtils parseMarkdown:attributedText];
19
+ attributedText = [markdownUtils parseMarkdown:attributedText withAttributes:self.backedTextInputView.defaultTextAttributes];
20
20
  }
21
21
 
22
22
  // Call the original method
@@ -29,7 +29,7 @@
29
29
  if (markdownUtils != nil) {
30
30
  id<RCTBackedTextInputViewProtocol> backedTextInputView = self.backedTextInputView;
31
31
  NSAttributedString *oldAttributedText = backedTextInputView.attributedText;
32
- NSAttributedString *newAttributedText = [markdownUtils parseMarkdown:oldAttributedText];
32
+ NSAttributedString *newAttributedText = [markdownUtils parseMarkdown:oldAttributedText withAttributes:backedTextInputView.defaultTextAttributes];
33
33
  UITextRange *range = backedTextInputView.selectedTextRange;
34
34
 
35
35
  // update attributed text without emitting onSelectionChange event
@@ -0,0 +1,12 @@
1
+ #ifdef RCT_NEW_ARCH_ENABLED
2
+
3
+ #import <RNLiveMarkdownSpec/RNLiveMarkdownSpec.h>
4
+ #import <React/RCTEventEmitter.h>
5
+ #import <React/RCTUIManager.h>
6
+
7
+ // Without inheriting after RCTEventEmitter we don't get access to bridge
8
+ @interface RCTLiveMarkdownModule
9
+ : RCTEventEmitter <NativeLiveMarkdownModuleSpec>
10
+ @end
11
+
12
+ #endif // RCT_NEW_ARCH_ENABLED
@@ -0,0 +1,48 @@
1
+ #ifdef RCT_NEW_ARCH_ENABLED
2
+
3
+ #import <React/RCTScheduler.h>
4
+ #import <React/RCTSurfacePresenter.h>
5
+
6
+ #import "MarkdownCommitHook.h"
7
+ #import "MarkdownShadowFamilyRegistry.h"
8
+ #import "RCTLiveMarkdownModule.h"
9
+
10
+ // A turbomodule used to register the commit hook
11
+ // I think this is the easiest way to access the UIManager, which we need to
12
+ // actually register the hook
13
+
14
+ @implementation RCTLiveMarkdownModule {
15
+ BOOL installed_;
16
+ std::shared_ptr<livemarkdown::MarkdownCommitHook> commitHook_;
17
+ }
18
+
19
+ RCT_EXPORT_MODULE(@"LiveMarkdownModule")
20
+
21
+ - (NSNumber *)install {
22
+ if (!installed_) {
23
+ installed_ = YES;
24
+
25
+ RCTBridge *bridge = self.bridge;
26
+ RCTSurfacePresenter *surfacePresenter = bridge.surfacePresenter;
27
+ RCTScheduler *scheduler = [surfacePresenter scheduler];
28
+
29
+ commitHook_ =
30
+ std::make_shared<livemarkdown::MarkdownCommitHook>(scheduler.uiManager);
31
+ }
32
+ return @1;
33
+ }
34
+
35
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
36
+ (const facebook::react::ObjCTurboModule::InitParams &)params {
37
+ return std::make_shared<facebook::react::NativeLiveMarkdownModuleSpecJSI>(
38
+ params);
39
+ }
40
+
41
+ - (void)invalidate {
42
+ MarkdownShadowFamilyRegistry::reset();
43
+ [super invalidate];
44
+ }
45
+
46
+ @end
47
+
48
+ #endif // RCT_NEW_ARCH_ENABLED
@@ -9,9 +9,7 @@ NS_ASSUME_NONNULL_BEGIN
9
9
  @property (nonatomic) NSMutableArray<NSDictionary *> *blockquoteRangesAndLevels;
10
10
  @property (weak, nonatomic) UIView<RCTBackedTextInputViewProtocol> *backedTextInputView;
11
11
 
12
- - (instancetype)initWithBackedTextInputView:(UIView<RCTBackedTextInputViewProtocol> *)backedTextInputView;
13
-
14
- - (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input;
12
+ - (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input withAttributes:(nullable NSDictionary<NSAttributedStringKey, id>*)attributes;
15
13
 
16
14
  @end
17
15