@expensify/react-native-live-markdown 0.1.186 → 0.1.188
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.
- package/README.md +55 -8
- package/RNLiveMarkdown.podspec +2 -4
- package/android/build.gradle +4 -9
- package/android/src/main/cpp/CMakeLists.txt +14 -6
- package/android/src/main/cpp/MarkdownUtils.cpp +13 -24
- package/android/src/main/cpp/MarkdownUtils.h +2 -7
- package/android/src/main/cpp/OnLoad.cpp +6 -0
- package/android/src/main/java/com/expensify/livemarkdown/CustomFabricUIManager.java +2 -2
- package/android/src/main/java/com/expensify/livemarkdown/CustomMountingManager.java +4 -6
- package/android/src/main/java/com/expensify/livemarkdown/LiveMarkdownModule.java +12 -0
- package/android/src/main/java/com/expensify/livemarkdown/MarkdownTextInputDecoratorView.java +17 -3
- package/android/src/main/java/com/expensify/livemarkdown/MarkdownTextInputDecoratorViewManager.java +6 -0
- package/android/src/main/java/com/expensify/livemarkdown/MarkdownUtils.java +21 -34
- package/android/src/main/new_arch/CMakeLists.txt +0 -2
- package/android/src/main/new_arch/MarkdownCommitHook.cpp +7 -4
- package/android/src/main/new_arch/MarkdownCommitHook.h +2 -0
- package/android/src/newarch/LiveMarkdownModuleSpec.java +9 -0
- package/android/src/oldarch/LiveMarkdownModuleSpec.java +12 -0
- package/android/src/oldarch/MarkdownTextInputDecoratorViewManagerSpec.java +1 -0
- package/apple/LiveMarkdownModule.h +16 -0
- package/apple/{RCTLiveMarkdownModule.mm → LiveMarkdownModule.mm} +31 -17
- package/apple/MarkdownCommitHook.mm +2 -0
- package/apple/MarkdownTextInputDecoratorComponentView.mm +4 -0
- package/apple/MarkdownTextInputDecoratorView.h +2 -0
- package/apple/MarkdownTextInputDecoratorView.mm +13 -0
- package/apple/MarkdownTextInputDecoratorViewManager.mm +9 -0
- package/apple/RCTMarkdownUtils.h +1 -0
- package/apple/RCTMarkdownUtils.mm +43 -45
- package/cpp/MarkdownGlobal.cpp +45 -0
- package/cpp/MarkdownGlobal.h +24 -0
- package/cpp/RuntimeDecorator.cpp +43 -0
- package/cpp/RuntimeDecorator.h +13 -0
- package/lib/commonjs/MarkdownTextInput.js +46 -5
- package/lib/commonjs/MarkdownTextInput.js.map +1 -1
- package/lib/commonjs/MarkdownTextInput.web.js +25 -17
- package/lib/commonjs/MarkdownTextInput.web.js.map +1 -1
- package/lib/commonjs/MarkdownTextInputDecoratorViewNativeComponent.js +1 -1
- package/lib/commonjs/MarkdownTextInputDecoratorViewNativeComponent.js.map +1 -1
- package/lib/commonjs/NativeLiveMarkdownModule.js.map +1 -1
- package/lib/commonjs/commonTypes.js.map +1 -1
- package/lib/commonjs/index.js +8 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/parseExpensiMark.js +263 -0
- package/lib/commonjs/parseExpensiMark.js.map +1 -0
- package/lib/commonjs/react-native-web.d.js.map +1 -1
- package/lib/commonjs/styleUtils.js.map +1 -1
- package/lib/commonjs/web/InputHistory.js.map +1 -1
- package/lib/commonjs/web/inputElements/inlineImage.js.map +1 -1
- package/lib/commonjs/web/inputElements/loadingIndicator.js.map +1 -1
- package/lib/commonjs/web/utils/animationUtils.js.map +1 -1
- package/lib/commonjs/web/utils/blockUtils.js.map +1 -1
- package/lib/commonjs/web/utils/browserUtils.js.map +1 -1
- package/lib/commonjs/web/utils/cursorUtils.js.map +1 -1
- package/lib/commonjs/web/utils/inputUtils.js.map +1 -1
- package/lib/commonjs/web/utils/parserUtils.js +3 -3
- package/lib/commonjs/web/utils/parserUtils.js.map +1 -1
- package/lib/commonjs/web/utils/treeUtils.js.map +1 -1
- package/lib/commonjs/web/utils/webStyleUtils.js.map +1 -1
- package/lib/module/MarkdownTextInput.js +45 -4
- package/lib/module/MarkdownTextInput.js.map +1 -1
- package/lib/module/MarkdownTextInput.web.js +23 -15
- package/lib/module/MarkdownTextInput.web.js.map +1 -1
- package/lib/module/MarkdownTextInputDecoratorViewNativeComponent.js.map +1 -1
- package/lib/module/NativeLiveMarkdownModule.js.map +1 -1
- package/lib/module/commonTypes.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/parseExpensiMark.js +258 -0
- package/lib/module/parseExpensiMark.js.map +1 -0
- package/lib/module/react-native-web.d.js.map +1 -1
- package/lib/module/styleUtils.js.map +1 -1
- package/lib/module/web/InputHistory.js.map +1 -1
- package/lib/module/web/inputElements/inlineImage.js.map +1 -1
- package/lib/module/web/inputElements/loadingIndicator.js.map +1 -1
- package/lib/module/web/utils/animationUtils.js.map +1 -1
- package/lib/module/web/utils/blockUtils.js.map +1 -1
- package/lib/module/web/utils/browserUtils.js.map +1 -1
- package/lib/module/web/utils/cursorUtils.js.map +1 -1
- package/lib/module/web/utils/inputUtils.js.map +1 -1
- package/lib/module/web/utils/parserUtils.js +3 -3
- package/lib/module/web/utils/parserUtils.js.map +1 -1
- package/lib/module/web/utils/treeUtils.js.map +1 -1
- package/lib/module/web/utils/webStyleUtils.js.map +1 -1
- package/lib/typescript/src/MarkdownTextInput.d.ts +9 -1
- package/lib/typescript/src/MarkdownTextInput.d.ts.map +1 -1
- package/lib/typescript/src/MarkdownTextInput.web.d.ts +2 -1
- package/lib/typescript/src/MarkdownTextInput.web.d.ts.map +1 -1
- package/lib/typescript/src/MarkdownTextInputDecoratorViewNativeComponent.d.ts +2 -2
- package/lib/typescript/src/MarkdownTextInputDecoratorViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/parseExpensiMark.d.ts +4 -0
- package/lib/typescript/src/parseExpensiMark.d.ts.map +1 -0
- package/lib/typescript/src/web/utils/parserUtils.d.ts +1 -1
- package/lib/typescript/src/web/utils/parserUtils.d.ts.map +1 -1
- package/package.json +20 -23
- package/src/MarkdownTextInput.tsx +62 -2
- package/src/MarkdownTextInput.web.tsx +28 -19
- package/src/MarkdownTextInputDecoratorViewNativeComponent.ts +2 -1
- package/src/index.tsx +2 -0
- package/src/parseExpensiMark.ts +277 -0
- package/src/web/utils/parserUtils.ts +3 -2
- package/apple/RCTLiveMarkdownModule.h +0 -16
- package/lib/parser/react-native-live-markdown-parser.js +0 -29
- package/parser/react-native-live-markdown-parser.js +0 -29
|
@@ -1,40 +1,55 @@
|
|
|
1
|
+
#import "LiveMarkdownModule.h"
|
|
2
|
+
|
|
3
|
+
#import <RNLiveMarkdown/RuntimeDecorator.h>
|
|
4
|
+
|
|
1
5
|
#ifdef RCT_NEW_ARCH_ENABLED
|
|
6
|
+
#import <RNLiveMarkdown/MarkdownCommitHook.h>
|
|
7
|
+
#endif // RCT_NEW_ARCH_ENABLED
|
|
2
8
|
|
|
9
|
+
#import <React/RCTBridge+Private.h>
|
|
10
|
+
|
|
11
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
3
12
|
#import <React/RCTScheduler.h>
|
|
4
13
|
#import <React/RCTSurfacePresenter.h>
|
|
14
|
+
#endif // RCT_NEW_ARCH_ENABLED
|
|
5
15
|
|
|
6
|
-
#import
|
|
7
|
-
#import "MarkdownShadowFamilyRegistry.h"
|
|
8
|
-
#import "RCTLiveMarkdownModule.h"
|
|
16
|
+
#import <jsi/jsi.h>
|
|
9
17
|
|
|
18
|
+
using namespace facebook;
|
|
10
19
|
using namespace expensify::livemarkdown;
|
|
11
20
|
|
|
12
21
|
// A turbomodule used to register the commit hook
|
|
13
22
|
// I think this is the easiest way to access the UIManager, which we need to
|
|
14
23
|
// actually register the hook
|
|
15
24
|
|
|
16
|
-
@implementation
|
|
17
|
-
|
|
25
|
+
@implementation LiveMarkdownModule {
|
|
26
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
18
27
|
std::shared_ptr<MarkdownCommitHook> commitHook_;
|
|
19
28
|
__weak RCTSurfacePresenter *surfacePresenter_;
|
|
29
|
+
#endif // RCT_NEW_ARCH_ENABLED
|
|
20
30
|
}
|
|
21
31
|
|
|
22
|
-
RCT_EXPORT_MODULE(
|
|
32
|
+
RCT_EXPORT_MODULE()
|
|
23
33
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
34
|
+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install)
|
|
35
|
+
{
|
|
36
|
+
RCTCxxBridge *cxxBridge = (RCTCxxBridge *)self.bridge;
|
|
37
|
+
jsi::Runtime &rt = *(jsi::Runtime *)cxxBridge.runtime;
|
|
38
|
+
expensify::livemarkdown::injectJSIBindings(rt);
|
|
27
39
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
40
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
41
|
+
RCTScheduler *scheduler = [surfacePresenter_ scheduler];
|
|
42
|
+
commitHook_ = std::make_shared<MarkdownCommitHook>(scheduler.uiManager);
|
|
43
|
+
#endif // RCT_NEW_ARCH_ENABLED
|
|
44
|
+
|
|
45
|
+
return @(1);
|
|
32
46
|
}
|
|
33
47
|
|
|
48
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
49
|
+
|
|
34
50
|
- (void)handleJavaScriptDidLoadNotification:(NSNotification *)notification
|
|
35
51
|
{
|
|
36
52
|
surfacePresenter_ = self.bridge.surfacePresenter;
|
|
37
|
-
[self install];
|
|
38
53
|
}
|
|
39
54
|
|
|
40
55
|
- (void)setBridge:(RCTBridge *)bridge
|
|
@@ -62,7 +77,6 @@ RCT_EXPORT_MODULE(@"LiveMarkdownModule")
|
|
|
62
77
|
surfacePresenter_ = surfacePresenter;
|
|
63
78
|
}
|
|
64
79
|
|
|
65
|
-
|
|
66
80
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
67
81
|
(const facebook::react::ObjCTurboModule::InitParams &)params {
|
|
68
82
|
return std::make_shared<facebook::react::NativeLiveMarkdownModuleSpecJSI>(
|
|
@@ -75,6 +89,6 @@ RCT_EXPORT_MODULE(@"LiveMarkdownModule")
|
|
|
75
89
|
[super invalidate];
|
|
76
90
|
}
|
|
77
91
|
|
|
78
|
-
@end
|
|
79
|
-
|
|
80
92
|
#endif // RCT_NEW_ARCH_ENABLED
|
|
93
|
+
|
|
94
|
+
@end
|
|
@@ -158,6 +158,7 @@ RootShadowNode::Unshared MarkdownCommitHook::shadowTreeWillCommit(
|
|
|
158
158
|
usedUtils =
|
|
159
159
|
MarkdownCommitHook::getOrCreateMarkdownUtils(*nodes.decorator);
|
|
160
160
|
[usedUtils setMarkdownStyle:markdownStyle];
|
|
161
|
+
[usedUtils setParserId:@(markdownProps.parserId)];
|
|
161
162
|
|
|
162
163
|
// convert the attibuted string stored in state to
|
|
163
164
|
// NSAttributedString
|
|
@@ -236,6 +237,7 @@ RootShadowNode::Unshared MarkdownCommitHook::shadowTreeWillCommit(
|
|
|
236
237
|
usedUtils =
|
|
237
238
|
MarkdownCommitHook::getOrCreateMarkdownUtils(*nodes.decorator);
|
|
238
239
|
[usedUtils setMarkdownStyle:markdownStyle];
|
|
240
|
+
[usedUtils setParserId:@(markdownProps.parserId)];
|
|
239
241
|
|
|
240
242
|
// convert the attibuted string stored in state to
|
|
241
243
|
// NSAttributedString
|
|
@@ -68,6 +68,10 @@ using namespace facebook::react;
|
|
|
68
68
|
const auto &oldViewProps = *std::static_pointer_cast<MarkdownTextInputDecoratorViewProps const>(_props);
|
|
69
69
|
const auto &newViewProps = *std::static_pointer_cast<MarkdownTextInputDecoratorViewProps const>(props);
|
|
70
70
|
|
|
71
|
+
if (oldViewProps.parserId != newViewProps.parserId) {
|
|
72
|
+
[_view setParserId:@(newViewProps.parserId)];
|
|
73
|
+
}
|
|
74
|
+
|
|
71
75
|
// TODO: if (oldViewProps.markdownStyle != newViewProps.markdownStyle)
|
|
72
76
|
RCTMarkdownStyle *markdownStyle = [[RCTMarkdownStyle alloc] initWithStruct:newViewProps.markdownStyle];
|
|
73
77
|
[_view setMarkdownStyle:markdownStyle];
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
@implementation MarkdownTextInputDecoratorView {
|
|
18
18
|
RCTMarkdownUtils *_markdownUtils;
|
|
19
19
|
RCTMarkdownStyle *_markdownStyle;
|
|
20
|
+
NSNumber *_parserId;
|
|
20
21
|
#ifdef RCT_NEW_ARCH_ENABLED
|
|
21
22
|
__weak RCTTextInputComponentView *_textInput;
|
|
22
23
|
#else
|
|
@@ -62,6 +63,7 @@
|
|
|
62
63
|
_markdownUtils = [[RCTMarkdownUtils alloc] init];
|
|
63
64
|
react_native_assert(_markdownStyle != nil);
|
|
64
65
|
[_markdownUtils setMarkdownStyle:_markdownStyle];
|
|
66
|
+
[_markdownUtils setParserId:_parserId];
|
|
65
67
|
|
|
66
68
|
[_textInput setMarkdownUtils:_markdownUtils];
|
|
67
69
|
if ([_backedTextInputView isKindOfClass:[RCTUITextField class]]) {
|
|
@@ -109,7 +111,18 @@
|
|
|
109
111
|
{
|
|
110
112
|
_markdownStyle = markdownStyle;
|
|
111
113
|
[_markdownUtils setMarkdownStyle:markdownStyle];
|
|
114
|
+
[self applyNewStyles];
|
|
115
|
+
}
|
|
112
116
|
|
|
117
|
+
- (void)setParserId:(NSNumber *)parserId
|
|
118
|
+
{
|
|
119
|
+
_parserId = parserId;
|
|
120
|
+
[_markdownUtils setParserId:parserId];
|
|
121
|
+
[self applyNewStyles];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
- (void)applyNewStyles
|
|
125
|
+
{
|
|
113
126
|
if (_textView != nil) {
|
|
114
127
|
// We want to use `textStorage` for applying markdown when possible. Currently it's only available for UITextView
|
|
115
128
|
[_textView textDidChange];
|
|
@@ -20,4 +20,13 @@ RCT_CUSTOM_VIEW_PROPERTY(markdownStyle, NSDictionary, MarkdownTextInputDecorator
|
|
|
20
20
|
#endif /* RCT_NEW_ARCH_ENABLED */
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
RCT_CUSTOM_VIEW_PROPERTY(parserId, NSNumber, MarkdownTextInputDecoratorView)
|
|
24
|
+
{
|
|
25
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
26
|
+
// implemented in MarkdownTextInputDecoratorView updateProps:
|
|
27
|
+
#else
|
|
28
|
+
[view setParserId:json];
|
|
29
|
+
#endif /* RCT_NEW_ARCH_ENABLED */
|
|
30
|
+
}
|
|
31
|
+
|
|
23
32
|
@end
|
package/apple/RCTMarkdownUtils.h
CHANGED
|
@@ -6,6 +6,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
6
6
|
@interface RCTMarkdownUtils : NSObject
|
|
7
7
|
|
|
8
8
|
@property (nonatomic) RCTMarkdownStyle *markdownStyle;
|
|
9
|
+
@property (nonatomic) NSNumber *parserId;
|
|
9
10
|
@property (nonatomic) NSMutableArray<NSDictionary *> *blockquoteRangesAndLevels;
|
|
10
11
|
|
|
11
12
|
- (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input withAttributes:(nullable NSDictionary<NSAttributedStringKey, id>*)attributes;
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
#import <RNLiveMarkdown/RCTMarkdownUtils.h>
|
|
2
|
+
#import <RNLiveMarkdown/MarkdownGlobal.h>
|
|
2
3
|
#import "react_native_assert.h"
|
|
3
4
|
#import <React/RCTAssert.h>
|
|
4
5
|
#import <React/RCTFont.h>
|
|
5
|
-
|
|
6
6
|
#include <jsi/jsi.h>
|
|
7
|
-
#include <hermes/hermes.h>
|
|
8
|
-
|
|
9
|
-
using namespace facebook;
|
|
10
7
|
|
|
11
8
|
@implementation RCTMarkdownUtils {
|
|
12
9
|
NSString *_prevInputString;
|
|
13
10
|
NSAttributedString *_prevAttributedString;
|
|
14
11
|
NSDictionary<NSAttributedStringKey, id> *_prevTextAttributes;
|
|
15
12
|
__weak RCTMarkdownStyle *_prevMarkdownStyle;
|
|
13
|
+
__weak NSNumber *_prevParserId;
|
|
16
14
|
}
|
|
17
15
|
|
|
18
16
|
- (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input withAttributes:(nullable NSDictionary<NSAttributedStringKey,id> *)attributes
|
|
@@ -23,64 +21,64 @@ using namespace facebook;
|
|
|
23
21
|
}
|
|
24
22
|
|
|
25
23
|
NSString *inputString = [input string];
|
|
26
|
-
if ([inputString isEqualToString:_prevInputString] && [attributes isEqualToDictionary:_prevTextAttributes] && [_markdownStyle isEqual:_prevMarkdownStyle]) {
|
|
24
|
+
if ([inputString isEqualToString:_prevInputString] && [attributes isEqualToDictionary:_prevTextAttributes] && [_markdownStyle isEqual:_prevMarkdownStyle] && [_parserId isEqualToNumber:_prevParserId]) {
|
|
27
25
|
return _prevAttributedString;
|
|
28
26
|
}
|
|
29
27
|
|
|
30
|
-
static std::shared_ptr<jsi::Runtime> runtime;
|
|
31
28
|
static std::mutex runtimeMutex;
|
|
32
29
|
auto lock = std::lock_guard<std::mutex>(runtimeMutex);
|
|
33
30
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
assert(path != nil && "[react-native-live-markdown] Markdown parser bundle not found");
|
|
37
|
-
NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
|
|
38
|
-
assert(content != nil && "[react-native-live-markdown] Markdown parser bundle is empty");
|
|
39
|
-
runtime = facebook::hermes::makeHermesRuntime();
|
|
40
|
-
auto codeBuffer = std::make_shared<const jsi::StringBuffer>([content UTF8String]);
|
|
41
|
-
runtime->evaluateJavaScript(codeBuffer, "evaluateJavaScript");
|
|
42
|
-
}
|
|
31
|
+
auto markdownRuntime = expensify::livemarkdown::getMarkdownRuntime();
|
|
32
|
+
jsi::Runtime &rt = markdownRuntime->getJSIRuntime();
|
|
43
33
|
|
|
44
|
-
|
|
45
|
-
auto text = jsi::String::createFromUtf8(rt, [inputString UTF8String]);
|
|
34
|
+
auto markdownWorklet = expensify::livemarkdown::getMarkdownWorklet([_parserId intValue]);
|
|
46
35
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
const auto &ranges = output.asObject(rt).asArray(rt);
|
|
36
|
+
try {
|
|
37
|
+
const auto &text = jsi::String::createFromUtf8(rt, [inputString UTF8String]);
|
|
38
|
+
const auto &output = markdownRuntime->runGuarded(markdownWorklet, text);
|
|
39
|
+
const auto &ranges = output.asObject(rt).asArray(rt);
|
|
53
40
|
|
|
54
|
-
|
|
55
|
-
|
|
41
|
+
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:inputString attributes:attributes];
|
|
42
|
+
[attributedString beginEditing];
|
|
56
43
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
44
|
+
// If the attributed string ends with underlined text, blurring the single-line input imprints the underline style across the whole string.
|
|
45
|
+
// It looks like a bug in iOS, as there is no underline style to be found in the attributed string, especially after formatting.
|
|
46
|
+
// This is a workaround that applies the NSUnderlineStyleNone to the string before iterating over ranges which resolves this problem.
|
|
47
|
+
[attributedString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleNone] range:NSMakeRange(0, attributedString.length)];
|
|
61
48
|
|
|
62
|
-
|
|
49
|
+
_blockquoteRangesAndLevels = [NSMutableArray new];
|
|
63
50
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
51
|
+
for (size_t i = 0, n = ranges.size(rt); i < n; ++i) {
|
|
52
|
+
const auto &item = ranges.getValueAtIndex(rt, i).asObject(rt);
|
|
53
|
+
const auto &type = item.getProperty(rt, "type").asString(rt).utf8(rt);
|
|
54
|
+
const auto &start = static_cast<int>(item.getProperty(rt, "start").asNumber());
|
|
55
|
+
const auto &length = static_cast<int>(item.getProperty(rt, "length").asNumber());
|
|
56
|
+
const auto &depth = item.hasProperty(rt, "depth") ? static_cast<int>(item.getProperty(rt, "depth").asNumber()) : 1;
|
|
70
57
|
|
|
71
|
-
|
|
72
|
-
|
|
58
|
+
[self applyRangeToAttributedString:attributedString type:type start:start length:length depth:depth];
|
|
59
|
+
}
|
|
73
60
|
|
|
74
|
-
|
|
61
|
+
RCTApplyBaselineOffset(attributedString);
|
|
75
62
|
|
|
76
|
-
|
|
63
|
+
[attributedString endEditing];
|
|
77
64
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
65
|
+
_prevInputString = inputString;
|
|
66
|
+
_prevAttributedString = attributedString;
|
|
67
|
+
_prevTextAttributes = attributes;
|
|
68
|
+
_prevMarkdownStyle = _markdownStyle;
|
|
69
|
+
_prevParserId = _parserId;
|
|
82
70
|
|
|
83
|
-
|
|
71
|
+
return attributedString;
|
|
72
|
+
} catch (const jsi::JSError &error) {
|
|
73
|
+
RCTLogWarn(@"[react-native-live-markdown] Incorrect schema of worklet parser output: %s", error.getMessage().c_str());
|
|
74
|
+
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:inputString attributes:attributes];
|
|
75
|
+
_prevInputString = inputString;
|
|
76
|
+
_prevAttributedString = attributedString;
|
|
77
|
+
_prevTextAttributes = attributes;
|
|
78
|
+
_prevMarkdownStyle = _markdownStyle;
|
|
79
|
+
_prevParserId = _parserId;
|
|
80
|
+
return attributedString;
|
|
81
|
+
}
|
|
84
82
|
}
|
|
85
83
|
}
|
|
86
84
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#include "MarkdownGlobal.h"
|
|
2
|
+
|
|
3
|
+
#include <unordered_map>
|
|
4
|
+
|
|
5
|
+
using namespace facebook;
|
|
6
|
+
|
|
7
|
+
namespace expensify {
|
|
8
|
+
namespace livemarkdown {
|
|
9
|
+
|
|
10
|
+
std::shared_ptr<WorkletRuntime> globalMarkdownWorkletRuntime;
|
|
11
|
+
|
|
12
|
+
void setMarkdownRuntime(const std::shared_ptr<WorkletRuntime> &markdownWorkletRuntime) {
|
|
13
|
+
globalMarkdownWorkletRuntime = markdownWorkletRuntime;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
std::shared_ptr<WorkletRuntime> getMarkdownRuntime() {
|
|
17
|
+
return globalMarkdownWorkletRuntime;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
std::unordered_map<int, std::shared_ptr<ShareableWorklet>> globalMarkdownShareableWorklets;
|
|
21
|
+
std::mutex globalMarkdownShareableWorkletsMutex;
|
|
22
|
+
int nextParserId = 1;
|
|
23
|
+
|
|
24
|
+
const int registerMarkdownWorklet(const std::shared_ptr<ShareableWorklet> &markdownWorklet) {
|
|
25
|
+
assert(markdownWorklet != nullptr);
|
|
26
|
+
auto parserId = nextParserId++;
|
|
27
|
+
std::unique_lock<std::mutex> lock(globalMarkdownShareableWorkletsMutex);
|
|
28
|
+
globalMarkdownShareableWorklets[parserId] = markdownWorklet;
|
|
29
|
+
return parserId;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
void unregisterMarkdownWorklet(const int parserId) {
|
|
33
|
+
std::unique_lock<std::mutex> lock(globalMarkdownShareableWorkletsMutex);
|
|
34
|
+
globalMarkdownShareableWorklets.erase(parserId);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
std::shared_ptr<ShareableWorklet> getMarkdownWorklet(const int parserId) {
|
|
38
|
+
std::unique_lock<std::mutex> lock(globalMarkdownShareableWorkletsMutex);
|
|
39
|
+
const auto &worklet = globalMarkdownShareableWorklets[parserId];
|
|
40
|
+
assert(worklet != nullptr);
|
|
41
|
+
return worklet;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
} // namespace livemarkdown
|
|
45
|
+
} // namespace expensify
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <jsi/jsi.h>
|
|
4
|
+
|
|
5
|
+
#include <worklets/WorkletRuntime/WorkletRuntime.h>
|
|
6
|
+
|
|
7
|
+
using namespace facebook;
|
|
8
|
+
using namespace worklets;
|
|
9
|
+
|
|
10
|
+
namespace expensify {
|
|
11
|
+
namespace livemarkdown {
|
|
12
|
+
|
|
13
|
+
void setMarkdownRuntime(const std::shared_ptr<WorkletRuntime> &markdownWorkletRuntime);
|
|
14
|
+
|
|
15
|
+
std::shared_ptr<WorkletRuntime> getMarkdownRuntime();
|
|
16
|
+
|
|
17
|
+
const int registerMarkdownWorklet(const std::shared_ptr<ShareableWorklet> &markdownWorklet);
|
|
18
|
+
|
|
19
|
+
void unregisterMarkdownWorklet(const int parserId);
|
|
20
|
+
|
|
21
|
+
std::shared_ptr<ShareableWorklet> getMarkdownWorklet(const int parserId);
|
|
22
|
+
|
|
23
|
+
} // namespace livemarkdown
|
|
24
|
+
} // namespace expensify
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#include "RuntimeDecorator.h"
|
|
2
|
+
#include "MarkdownGlobal.h"
|
|
3
|
+
|
|
4
|
+
using namespace facebook;
|
|
5
|
+
using namespace worklets;
|
|
6
|
+
|
|
7
|
+
namespace expensify {
|
|
8
|
+
namespace livemarkdown {
|
|
9
|
+
|
|
10
|
+
void injectJSIBindings(jsi::Runtime &rt) {
|
|
11
|
+
|
|
12
|
+
rt.global().setProperty(rt, "jsi_setMarkdownRuntime", jsi::Function::createFromHostFunction(
|
|
13
|
+
rt,
|
|
14
|
+
jsi::PropNameID::forAscii(rt, "jsi_setMarkdownRuntime"),
|
|
15
|
+
1,
|
|
16
|
+
[](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value {
|
|
17
|
+
setMarkdownRuntime(extractWorkletRuntime(rt, args[0]));
|
|
18
|
+
return jsi::Value::undefined();
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
rt.global().setProperty(rt, "jsi_registerMarkdownWorklet", jsi::Function::createFromHostFunction(
|
|
22
|
+
rt,
|
|
23
|
+
jsi::PropNameID::forAscii(rt, "jsi_registerMarkdownWorklet"),
|
|
24
|
+
1,
|
|
25
|
+
[](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value {
|
|
26
|
+
auto parserId = registerMarkdownWorklet(extractShareableOrThrow<ShareableWorklet>(rt, args[0]));
|
|
27
|
+
return jsi::Value(parserId);
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
rt.global().setProperty(rt, "jsi_unregisterMarkdownWorklet", jsi::Function::createFromHostFunction(
|
|
31
|
+
rt,
|
|
32
|
+
jsi::PropNameID::forAscii(rt, "jsi_unregisterMarkdownWorklet"),
|
|
33
|
+
1,
|
|
34
|
+
[](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value {
|
|
35
|
+
auto parserId = static_cast<int>(args[0].asNumber());
|
|
36
|
+
unregisterMarkdownWorklet(parserId);
|
|
37
|
+
return jsi::Value::undefined();
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
} // namespace livemarkdown
|
|
43
|
+
} // namespace expensify
|
|
@@ -6,13 +6,37 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
var _reactNative = require("react-native");
|
|
8
8
|
var _react = _interopRequireDefault(require("react"));
|
|
9
|
+
var _reactNativeReanimated = require("react-native-reanimated");
|
|
9
10
|
var _MarkdownTextInputDecoratorViewNativeComponent = _interopRequireDefault(require("./MarkdownTextInputDecoratorViewNativeComponent"));
|
|
10
11
|
var _NativeLiveMarkdownModule = _interopRequireDefault(require("./NativeLiveMarkdownModule"));
|
|
11
12
|
var _styleUtils = require("./styleUtils");
|
|
12
|
-
function _interopRequireDefault(
|
|
13
|
-
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (
|
|
14
|
-
|
|
13
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
14
|
+
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
15
|
+
let initialized = false;
|
|
16
|
+
let workletRuntime;
|
|
17
|
+
function initializeLiveMarkdownIfNeeded() {
|
|
18
|
+
if (initialized) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (!_NativeLiveMarkdownModule.default) {
|
|
22
|
+
throw new Error('[react-native-live-markdown] NativeLiveMarkdownModule is not available');
|
|
23
|
+
}
|
|
15
24
|
_NativeLiveMarkdownModule.default.install();
|
|
25
|
+
if (!global.jsi_setMarkdownRuntime) {
|
|
26
|
+
throw new Error('[react-native-live-markdown] global.jsi_setMarkdownRuntime is not available');
|
|
27
|
+
}
|
|
28
|
+
workletRuntime = (0, _reactNativeReanimated.createWorkletRuntime)('LiveMarkdownRuntime');
|
|
29
|
+
global.jsi_setMarkdownRuntime(workletRuntime);
|
|
30
|
+
initialized = true;
|
|
31
|
+
}
|
|
32
|
+
function registerParser(parser) {
|
|
33
|
+
initializeLiveMarkdownIfNeeded();
|
|
34
|
+
const shareableWorklet = (0, _reactNativeReanimated.makeShareableCloneRecursive)(parser);
|
|
35
|
+
const parserId = global.jsi_registerMarkdownWorklet(shareableWorklet);
|
|
36
|
+
return parserId;
|
|
37
|
+
}
|
|
38
|
+
function unregisterParser(parserId) {
|
|
39
|
+
global.jsi_unregisterMarkdownWorklet(parserId);
|
|
16
40
|
}
|
|
17
41
|
function processColorsInMarkdownStyle(input) {
|
|
18
42
|
const output = JSON.parse(JSON.stringify(input));
|
|
@@ -32,13 +56,30 @@ function processMarkdownStyle(input) {
|
|
|
32
56
|
return processColorsInMarkdownStyle((0, _styleUtils.mergeMarkdownStyleWithDefault)(input));
|
|
33
57
|
}
|
|
34
58
|
const MarkdownTextInput = /*#__PURE__*/_react.default.forwardRef((props, ref) => {
|
|
35
|
-
const IS_FABRIC =
|
|
59
|
+
const IS_FABRIC = 'nativeFabricUIManager' in global;
|
|
36
60
|
const markdownStyle = _react.default.useMemo(() => processMarkdownStyle(props.markdownStyle), [props.markdownStyle]);
|
|
61
|
+
if (props.parser === undefined) {
|
|
62
|
+
throw new Error('[react-native-live-markdown] `parser` is undefined');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
66
|
+
const workletHash = props.parser.__workletHash;
|
|
67
|
+
if (workletHash === undefined) {
|
|
68
|
+
throw new Error('[react-native-live-markdown] `parser` is not a worklet');
|
|
69
|
+
}
|
|
70
|
+
const parserId = _react.default.useMemo(() => {
|
|
71
|
+
return registerParser(props.parser);
|
|
72
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
73
|
+
}, [workletHash]);
|
|
74
|
+
_react.default.useEffect(() => {
|
|
75
|
+
return () => unregisterParser(parserId);
|
|
76
|
+
}, [parserId]);
|
|
37
77
|
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_reactNative.TextInput, _extends({}, props, {
|
|
38
78
|
ref: ref
|
|
39
79
|
})), /*#__PURE__*/_react.default.createElement(_MarkdownTextInputDecoratorViewNativeComponent.default, {
|
|
40
80
|
style: IS_FABRIC ? styles.farAway : styles.displayNone,
|
|
41
|
-
markdownStyle: markdownStyle
|
|
81
|
+
markdownStyle: markdownStyle,
|
|
82
|
+
parserId: parserId
|
|
42
83
|
}));
|
|
43
84
|
});
|
|
44
85
|
const styles = _reactNative.StyleSheet.create({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_reactNative","require","_react","_interopRequireDefault","_MarkdownTextInputDecoratorViewNativeComponent","_NativeLiveMarkdownModule","_styleUtils","
|
|
1
|
+
{"version":3,"names":["_reactNative","require","_react","_interopRequireDefault","_reactNativeReanimated","_MarkdownTextInputDecoratorViewNativeComponent","_NativeLiveMarkdownModule","_styleUtils","e","__esModule","default","_extends","Object","assign","bind","n","arguments","length","t","r","hasOwnProperty","call","apply","initialized","workletRuntime","initializeLiveMarkdownIfNeeded","NativeLiveMarkdownModule","Error","install","global","jsi_setMarkdownRuntime","createWorkletRuntime","registerParser","parser","shareableWorklet","makeShareableCloneRecursive","parserId","jsi_registerMarkdownWorklet","unregisterParser","jsi_unregisterMarkdownWorklet","processColorsInMarkdownStyle","input","output","JSON","parse","stringify","keys","forEach","key","obj","prop","endsWith","processColor","processMarkdownStyle","mergeMarkdownStyleWithDefault","MarkdownTextInput","React","forwardRef","props","ref","IS_FABRIC","markdownStyle","useMemo","undefined","workletHash","__workletHash","useEffect","createElement","Fragment","TextInput","style","styles","farAway","displayNone","StyleSheet","create","display","position","top","left","_default","exports"],"sourceRoot":"../../src","sources":["MarkdownTextInput.tsx"],"mappings":"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","ignoreList":[]}
|