@expensify/react-native-live-markdown 0.1.184 → 0.1.185
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.
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
#include <memory>
|
|
9
9
|
|
|
10
10
|
#include "MarkdownTextInputDecoratorShadowNode.h"
|
|
11
|
+
#include "RCTMarkdownUtils.h"
|
|
11
12
|
|
|
12
13
|
using namespace facebook::react;
|
|
13
14
|
|
|
@@ -35,6 +36,10 @@ public:
|
|
|
35
36
|
RootShadowNode::Unshared const &newRootShadowNode) noexcept override;
|
|
36
37
|
|
|
37
38
|
private:
|
|
39
|
+
static RCTMarkdownUtils *
|
|
40
|
+
getMarkdownUtils(const MarkdownTextInputDecoratorShadowNode &decorator);
|
|
41
|
+
static RCTMarkdownUtils *getOrCreateMarkdownUtils(
|
|
42
|
+
const MarkdownTextInputDecoratorShadowNode &decorator);
|
|
38
43
|
const std::shared_ptr<UIManager> uiManager_;
|
|
39
44
|
};
|
|
40
45
|
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
#include <React/RCTUtils.h>
|
|
4
4
|
#include <react/renderer/core/ComponentDescriptor.h>
|
|
5
5
|
#include <react/renderer/textlayoutmanager/RCTAttributedTextUtils.h>
|
|
6
|
+
#include <react/utils/ManagedObjectWrapper.h>
|
|
6
7
|
|
|
7
8
|
#include "MarkdownCommitHook.h"
|
|
8
9
|
#include "MarkdownShadowFamilyRegistry.h"
|
|
9
10
|
#include "RCTMarkdownStyle.h"
|
|
10
|
-
#include "RCTMarkdownUtils.h"
|
|
11
11
|
|
|
12
12
|
using namespace facebook::react;
|
|
13
13
|
|
|
@@ -24,6 +24,27 @@ MarkdownCommitHook::~MarkdownCommitHook() noexcept {
|
|
|
24
24
|
uiManager_->unregisterCommitHook(*this);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
RCTMarkdownUtils *MarkdownCommitHook::getMarkdownUtils(
|
|
28
|
+
const MarkdownTextInputDecoratorShadowNode &decorator) {
|
|
29
|
+
const auto decoratorState = std::static_pointer_cast<
|
|
30
|
+
const ConcreteState<MarkdownTextInputDecoratorState>>(
|
|
31
|
+
decorator.getState());
|
|
32
|
+
const auto memoizedUtils = (RCTMarkdownUtils *)unwrapManagedObject(
|
|
33
|
+
decoratorState->getData().markdownUtils);
|
|
34
|
+
return memoizedUtils;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
RCTMarkdownUtils *MarkdownCommitHook::getOrCreateMarkdownUtils(
|
|
38
|
+
const MarkdownTextInputDecoratorShadowNode &decorator) {
|
|
39
|
+
const auto memoizedUtils = MarkdownCommitHook::getMarkdownUtils(decorator);
|
|
40
|
+
|
|
41
|
+
if (memoizedUtils != nullptr) {
|
|
42
|
+
return memoizedUtils;
|
|
43
|
+
} else {
|
|
44
|
+
return [[RCTMarkdownUtils alloc] init];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
27
48
|
RootShadowNode::Unshared MarkdownCommitHook::shadowTreeWillCommit(
|
|
28
49
|
ShadowTree const &, RootShadowNode::Shared const &,
|
|
29
50
|
RootShadowNode::Unshared const &newRootShadowNode) noexcept {
|
|
@@ -94,6 +115,8 @@ RootShadowNode::Unshared MarkdownCommitHook::shadowTreeWillCommit(
|
|
|
94
115
|
const auto fontSizeMultiplier =
|
|
95
116
|
newRootShadowNode->getConcreteProps().layoutContext.fontSizeMultiplier;
|
|
96
117
|
|
|
118
|
+
RCTMarkdownUtils *usedUtils = nil;
|
|
119
|
+
|
|
97
120
|
// We only want to update the shadow node when the attributed string is
|
|
98
121
|
// stored by value If it's stored by pointer, the markdown formatting should
|
|
99
122
|
// already by applied to it, since the only source of that pointer (besides
|
|
@@ -115,7 +138,7 @@ RootShadowNode::Unshared MarkdownCommitHook::shadowTreeWillCommit(
|
|
|
115
138
|
nodes.textInput->getTag())) {
|
|
116
139
|
rootNode = rootNode->cloneTree(
|
|
117
140
|
nodes.textInput->getFamily(),
|
|
118
|
-
[&nodes, &textInputState, &stateData,
|
|
141
|
+
[&nodes, &textInputState, &stateData, &usedUtils,
|
|
119
142
|
fontSizeMultiplier](const ShadowNode &node) {
|
|
120
143
|
const auto &markdownProps = *std::static_pointer_cast<
|
|
121
144
|
MarkdownTextInputDecoratorViewProps const>(
|
|
@@ -132,8 +155,9 @@ RootShadowNode::Unshared MarkdownCommitHook::shadowTreeWillCommit(
|
|
|
132
155
|
// this can possibly be optimized
|
|
133
156
|
RCTMarkdownStyle *markdownStyle = [[RCTMarkdownStyle alloc]
|
|
134
157
|
initWithStruct:markdownProps.markdownStyle];
|
|
135
|
-
|
|
136
|
-
|
|
158
|
+
usedUtils =
|
|
159
|
+
MarkdownCommitHook::getOrCreateMarkdownUtils(*nodes.decorator);
|
|
160
|
+
[usedUtils setMarkdownStyle:markdownStyle];
|
|
137
161
|
|
|
138
162
|
// convert the attibuted string stored in state to
|
|
139
163
|
// NSAttributedString
|
|
@@ -142,14 +166,15 @@ RootShadowNode::Unshared MarkdownCommitHook::shadowTreeWillCommit(
|
|
|
142
166
|
stateData.attributedStringBox);
|
|
143
167
|
|
|
144
168
|
// Handles the first render, where the text stored in props is
|
|
145
|
-
// different than the one stored in state. The one in state is
|
|
146
|
-
// while the one in props is passed from JS. If we don't
|
|
147
|
-
// state here, we'll end up with a one-default-line-sized
|
|
169
|
+
// different than the one stored in state. The one in state is
|
|
170
|
+
// empty, while the one in props is passed from JS. If we don't
|
|
171
|
+
// update the state here, we'll end up with a one-default-line-sized
|
|
172
|
+
// text input
|
|
148
173
|
if (textInputState.getRevision() == State::initialRevisionValue) {
|
|
149
|
-
|
|
150
|
-
|
|
174
|
+
auto plainStringFromState =
|
|
175
|
+
std::string([[nsAttributedString string] UTF8String]);
|
|
151
176
|
|
|
152
|
-
|
|
177
|
+
if (plainStringFromState != textInputProps.text) {
|
|
153
178
|
// creates new AttributedString from props, adapted from
|
|
154
179
|
// TextInputShadowNode (ios one, text inputs are
|
|
155
180
|
// platform-specific)
|
|
@@ -164,14 +189,15 @@ RootShadowNode::Unshared MarkdownCommitHook::shadowTreeWillCommit(
|
|
|
164
189
|
|
|
165
190
|
// convert the newly created attributed string to
|
|
166
191
|
// NSAttributedString
|
|
167
|
-
nsAttributedString =
|
|
168
|
-
|
|
169
|
-
|
|
192
|
+
nsAttributedString =
|
|
193
|
+
RCTNSAttributedStringFromAttributedStringBox(
|
|
194
|
+
AttributedStringBox{attributedString});
|
|
195
|
+
}
|
|
170
196
|
}
|
|
171
197
|
|
|
172
198
|
// apply markdown
|
|
173
|
-
auto newString = [
|
|
174
|
-
|
|
199
|
+
auto newString = [usedUtils parseMarkdown:nsAttributedString
|
|
200
|
+
withAttributes:defaultNSTextAttributes];
|
|
175
201
|
|
|
176
202
|
// create a clone of the old TextInputState and update the
|
|
177
203
|
// attributed string box to point to the string with markdown
|
|
@@ -187,10 +213,10 @@ RootShadowNode::Unshared MarkdownCommitHook::shadowTreeWillCommit(
|
|
|
187
213
|
});
|
|
188
214
|
});
|
|
189
215
|
} else if (stateData.attributedStringBox.getMode() ==
|
|
190
|
-
|
|
216
|
+
AttributedStringBox::Mode::OpaquePointer) {
|
|
191
217
|
rootNode = rootNode->cloneTree(
|
|
192
218
|
nodes.textInput->getFamily(),
|
|
193
|
-
[&nodes, &textInputState, &stateData,
|
|
219
|
+
[&nodes, &textInputState, &stateData, &usedUtils,
|
|
194
220
|
fontSizeMultiplier](const ShadowNode &node) {
|
|
195
221
|
const auto &markdownProps = *std::static_pointer_cast<
|
|
196
222
|
MarkdownTextInputDecoratorViewProps const>(
|
|
@@ -207,8 +233,9 @@ RootShadowNode::Unshared MarkdownCommitHook::shadowTreeWillCommit(
|
|
|
207
233
|
// this can possibly be optimized
|
|
208
234
|
RCTMarkdownStyle *markdownStyle = [[RCTMarkdownStyle alloc]
|
|
209
235
|
initWithStruct:markdownProps.markdownStyle];
|
|
210
|
-
|
|
211
|
-
|
|
236
|
+
usedUtils =
|
|
237
|
+
MarkdownCommitHook::getOrCreateMarkdownUtils(*nodes.decorator);
|
|
238
|
+
[usedUtils setMarkdownStyle:markdownStyle];
|
|
212
239
|
|
|
213
240
|
// convert the attibuted string stored in state to
|
|
214
241
|
// NSAttributedString
|
|
@@ -217,8 +244,8 @@ RootShadowNode::Unshared MarkdownCommitHook::shadowTreeWillCommit(
|
|
|
217
244
|
stateData.attributedStringBox);
|
|
218
245
|
|
|
219
246
|
// apply markdown
|
|
220
|
-
auto newString = [
|
|
221
|
-
|
|
247
|
+
auto newString = [usedUtils parseMarkdown:nsAttributedString
|
|
248
|
+
withAttributes:defaultNSTextAttributes];
|
|
222
249
|
|
|
223
250
|
// create a clone of the old TextInputState and update the
|
|
224
251
|
// attributed string box to point to the string with markdown
|
|
@@ -234,6 +261,39 @@ RootShadowNode::Unshared MarkdownCommitHook::shadowTreeWillCommit(
|
|
|
234
261
|
});
|
|
235
262
|
});
|
|
236
263
|
}
|
|
264
|
+
|
|
265
|
+
// if usedUtils is not nil, then we did some work on the TextInput
|
|
266
|
+
// ShadowNode, we may need to update the decorator as well
|
|
267
|
+
if (usedUtils != nil) {
|
|
268
|
+
const auto ancestors =
|
|
269
|
+
nodes.decorator->getFamily().getAncestors(*rootNode);
|
|
270
|
+
const auto parentInfo = ancestors.back();
|
|
271
|
+
const auto decoratorNode =
|
|
272
|
+
std::static_pointer_cast<const MarkdownTextInputDecoratorShadowNode>(
|
|
273
|
+
parentInfo.first.get().getChildren().at(parentInfo.second));
|
|
274
|
+
|
|
275
|
+
// if usedUtils is defferent from the one kept in the decorator state, it
|
|
276
|
+
// needs to be updated to ensure memoization works correctly
|
|
277
|
+
if (usedUtils != MarkdownCommitHook::getMarkdownUtils(*decoratorNode)) {
|
|
278
|
+
const auto oldDecoratorState = *std::static_pointer_cast<
|
|
279
|
+
const ConcreteState<MarkdownTextInputDecoratorState>>(
|
|
280
|
+
decoratorNode->getState());
|
|
281
|
+
const auto newDecoratorState =
|
|
282
|
+
std::make_shared<const MarkdownTextInputDecoratorState>(
|
|
283
|
+
oldDecoratorState.getData().decoratorFamily,
|
|
284
|
+
wrapManagedObject(usedUtils));
|
|
285
|
+
const auto newDecoratorNode = decoratorNode->clone(
|
|
286
|
+
{.state = std::make_shared<
|
|
287
|
+
const ConcreteState<MarkdownTextInputDecoratorState>>(
|
|
288
|
+
newDecoratorState, oldDecoratorState)});
|
|
289
|
+
|
|
290
|
+
// since we did clone the path to the text input, parent node is mutable
|
|
291
|
+
// at this point - no need to clone the entire path
|
|
292
|
+
const auto mutableParent =
|
|
293
|
+
const_cast<ShadowNode *>(&parentInfo.first.get());
|
|
294
|
+
mutableParent->replaceChild(*decoratorNode, newDecoratorNode);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
237
297
|
}
|
|
238
298
|
|
|
239
299
|
return std::static_pointer_cast<RootShadowNode>(rootNode);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
|
+
#include <memory>
|
|
3
4
|
#include <react/renderer/core/ShadowNodeFamily.h>
|
|
4
5
|
|
|
5
6
|
namespace facebook {
|
|
@@ -9,18 +10,25 @@ class JSI_EXPORT MarkdownTextInputDecoratorState final {
|
|
|
9
10
|
public:
|
|
10
11
|
using Shared = std::shared_ptr<const MarkdownTextInputDecoratorState>;
|
|
11
12
|
|
|
12
|
-
MarkdownTextInputDecoratorState()
|
|
13
|
+
MarkdownTextInputDecoratorState()
|
|
14
|
+
: decoratorFamily(nullptr), markdownUtils(nullptr){};
|
|
13
15
|
MarkdownTextInputDecoratorState(
|
|
14
16
|
const ShadowNodeFamily::Shared textInputFamily_)
|
|
15
|
-
: decoratorFamily(textInputFamily_){};
|
|
17
|
+
: decoratorFamily(textInputFamily_), markdownUtils(nullptr){};
|
|
18
|
+
MarkdownTextInputDecoratorState(
|
|
19
|
+
const ShadowNodeFamily::Shared textInputFamily_,
|
|
20
|
+
const std::shared_ptr<void> markdownUtils_)
|
|
21
|
+
: decoratorFamily(textInputFamily_), markdownUtils(markdownUtils_){};
|
|
16
22
|
|
|
17
23
|
#ifdef ANDROID
|
|
18
24
|
MarkdownTextInputDecoratorState(
|
|
19
25
|
MarkdownTextInputDecoratorState const &previousState, folly::dynamic data)
|
|
20
|
-
: decoratorFamily(previousState.decoratorFamily)
|
|
26
|
+
: decoratorFamily(previousState.decoratorFamily),
|
|
27
|
+
markdownUtils(nullptr){};
|
|
21
28
|
#endif
|
|
22
29
|
|
|
23
30
|
const ShadowNodeFamily::Shared decoratorFamily;
|
|
31
|
+
const std::shared_ptr<void> markdownUtils;
|
|
24
32
|
|
|
25
33
|
#ifdef ANDROID
|
|
26
34
|
folly::dynamic getDynamic() const {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expensify/react-native-live-markdown",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.185",
|
|
4
4
|
"description": "Drop-in replacement for React Native's TextInput component with Markdown formatting.",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|