Skip to content

Commit e474d07

Browse files
committed
Merge branch 'main' into @wolewicki/compat-77
2 parents 55ee161 + 958dbd9 commit e474d07

File tree

17 files changed

+108
-58
lines changed

17 files changed

+108
-58
lines changed

android/src/main/java/com/expensify/livemarkdown/MarkdownTextInputDecoratorView.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
import androidx.annotation.Nullable;
44

55
import android.content.Context;
6-
import android.content.res.AssetManager;
6+
import android.text.Editable;
7+
import android.text.SpannableStringBuilder;
78
import android.text.TextWatcher;
89
import android.util.AttributeSet;
910

@@ -93,11 +94,11 @@ protected void setParserId(int parserId) {
9394
}
9495

9596
protected void applyNewStyles() {
96-
if (mReactEditText != null) {
97-
int selectionStart = mReactEditText.getSelectionStart();
98-
int selectionEnd = mReactEditText.getSelectionEnd();
99-
mReactEditText.setText(mReactEditText.getText()); // trigger update
100-
mReactEditText.setSelection(selectionStart, selectionEnd);
97+
if (mReactEditText != null && mMarkdownUtils != null) {
98+
Editable editable = mReactEditText.getText();
99+
if (editable instanceof SpannableStringBuilder ssb) {
100+
mMarkdownUtils.applyMarkdownFormatting(ssb);
101+
}
101102
}
102103
}
103104
}

apple/MarkdownParser.mm

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,15 @@ @implementation MarkdownParser {
2323
const auto &markdownRuntime = expensify::livemarkdown::getMarkdownRuntime();
2424
jsi::Runtime &rt = markdownRuntime->getJSIRuntime();
2525

26-
const auto &markdownWorklet = expensify::livemarkdown::getMarkdownWorklet([parserId intValue]);
26+
std::shared_ptr<ShareableWorklet> markdownWorklet;
27+
try {
28+
markdownWorklet = expensify::livemarkdown::getMarkdownWorklet([parserId intValue]);
29+
} catch (const std::out_of_range &error) {
30+
_prevText = text;
31+
_prevParserId = parserId;
32+
_prevMarkdownRanges = @[];
33+
return _prevMarkdownRanges;
34+
}
2735

2836
const auto &input = jsi::String::createFromUtf8(rt, [text UTF8String]);
2937

cpp/MarkdownGlobal.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,7 @@ void unregisterMarkdownWorklet(const int parserId) {
3636

3737
std::shared_ptr<ShareableWorklet> getMarkdownWorklet(const int parserId) {
3838
std::unique_lock<std::mutex> lock(globalMarkdownShareableWorkletsMutex);
39-
const auto &worklet = globalMarkdownShareableWorklets[parserId];
40-
assert(worklet != nullptr);
41-
return worklet;
39+
return globalMarkdownShareableWorklets.at(parserId);
4240
}
4341

4442
} // namespace livemarkdown

example/ios/Podfile.lock

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1509,7 +1509,7 @@ PODS:
15091509
- React-logger (= 0.76.3)
15101510
- React-perflogger (= 0.76.3)
15111511
- React-utils (= 0.76.3)
1512-
- RNLiveMarkdown (0.1.218):
1512+
- RNLiveMarkdown (0.1.229):
15131513
- DoubleConversion
15141514
- glog
15151515
- hermes-engine
@@ -1529,10 +1529,10 @@ PODS:
15291529
- ReactCodegen
15301530
- ReactCommon/turbomodule/bridging
15311531
- ReactCommon/turbomodule/core
1532-
- RNLiveMarkdown/newarch (= 0.1.218)
1532+
- RNLiveMarkdown/newarch (= 0.1.229)
15331533
- RNReanimated/worklets
15341534
- Yoga
1535-
- RNLiveMarkdown/newarch (0.1.218):
1535+
- RNLiveMarkdown/newarch (0.1.229):
15361536
- DoubleConversion
15371537
- glog
15381538
- hermes-engine
@@ -1913,10 +1913,10 @@ SPEC CHECKSUMS:
19131913
React-utils: 2bcaf4f4dfe361344bce2fae428603d518488630
19141914
ReactCodegen: 3a85e3cb68c92f16b2ffcf192e30364d78f8ccb9
19151915
ReactCommon: 89c87b343deacc8610b099ac764848f0ce937e3e
1916-
RNLiveMarkdown: cc674559a93c1f503ed6aac341a66346f05ef153
1916+
RNLiveMarkdown: 11cf762d7ab0c9535f0a66e5b2dbd7faeda35a45
19171917
RNReanimated: 97d6090ccdf33859f28cc6d394fb4fd799e75d29
19181918
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
1919-
Yoga: f6dc1b6029519815d5516a1241821c6a9074af6d
1919+
Yoga: 3deb2471faa9916c8a82dda2a22d3fba2620ad37
19201920

19211921
PODFILE CHECKSUM: 9b81b0f7bfba9e6fb4fa10efe8319f7860794e08
19221922

example/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"start": "react-native start"
1010
},
1111
"dependencies": {
12-
"expensify-common": "2.0.108",
12+
"expensify-common": "2.0.115",
1313
"react": "18.3.1",
1414
"react-native": "0.76.3",
1515
"react-native-reanimated": "3.16.4"

package-lock.json

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@expensify/react-native-live-markdown",
3-
"version": "0.1.219",
3+
"version": "0.1.231",
44
"description": "Drop-in replacement for React Native's TextInput component with Markdown formatting.",
55
"main": "lib/commonjs/index",
66
"module": "lib/module/index",
@@ -87,7 +87,7 @@
8787
"eslint-plugin-prettier": "^4.0.0",
8888
"eslint-plugin-promise": "^6.1.1",
8989
"eslint-plugin-tsdoc": "^0.2.17",
90-
"expensify-common": "2.0.108",
90+
"expensify-common": "2.0.115",
9191
"jest": "^29.6.3",
9292
"jest-environment-jsdom": "^29.7.0",
9393
"nodemon": "^3.1.3",
@@ -103,7 +103,7 @@
103103
"typescript": "~5.3.3"
104104
},
105105
"peerDependencies": {
106-
"expensify-common": ">=2.0.108",
106+
"expensify-common": ">=2.0.115",
107107
"react": "*",
108108
"react-native": "*",
109109
"react-native-reanimated": ">=3.16.4"

src/MarkdownTextInput.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ declare global {
2424
let initialized = false;
2525
let workletRuntime: WorkletRuntime | undefined;
2626

27+
function getWorkletRuntime(): WorkletRuntime {
28+
if (workletRuntime === undefined) {
29+
throw new Error(
30+
"[react-native-live-markdown] Worklet runtime hasn't been created yet. Please avoid calling `getWorkletRuntime()` in top-level scope. Instead, call `getWorkletRuntime()` directly in `runOnRuntime` arguments list.",
31+
);
32+
}
33+
return workletRuntime;
34+
}
35+
2736
function initializeLiveMarkdownIfNeeded() {
2837
if (initialized) {
2938
return;
@@ -96,8 +105,7 @@ const MarkdownTextInput = React.forwardRef<MarkdownTextInput, MarkdownTextInputP
96105

97106
const parserId = React.useMemo(() => {
98107
return registerParser(props.parser);
99-
// eslint-disable-next-line react-hooks/exhaustive-deps
100-
}, [workletHash]);
108+
}, [props.parser]);
101109

102110
React.useEffect(() => {
103111
return () => unregisterParser(parserId);
@@ -132,3 +140,5 @@ const styles = StyleSheet.create({
132140
export type {PartialMarkdownStyle as MarkdownStyle, MarkdownTextInputProps};
133141

134142
export default MarkdownTextInput;
143+
144+
export {getWorkletRuntime};

src/MarkdownTextInput.web.tsx

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ interface MarkdownTextInputProps extends TextInputProps, InlineImagesInputProps
3838

3939
interface MarkdownNativeEvent extends Event {
4040
inputType?: string;
41+
isComposing?: boolean;
42+
keyCode?: number;
4143
}
4244

4345
type MarkdownTextInput = TextInput & React.Component<MarkdownTextInputProps>;
@@ -120,7 +122,6 @@ const MarkdownTextInput = React.forwardRef<MarkdownTextInput, MarkdownTextInputP
120122
throw new Error('[react-native-live-markdown] `parser` is not a function');
121123
}
122124

123-
const compositionRef = useRef<boolean>(false);
124125
const divRef = useRef<MarkdownTextInputElement | null>(null);
125126
const currentlyFocusedField = useRef<HTMLDivElement | null>(null);
126127
const contentSelection = useRef<Selection | null>(null);
@@ -349,6 +350,7 @@ const MarkdownTextInput = React.forwardRef<MarkdownTextInput, MarkdownTextInputP
349350
}
350351
const nativeEvent = e.nativeEvent as MarkdownNativeEvent;
351352
const inputType = nativeEvent.inputType;
353+
const isComposing = isEventComposing(nativeEvent);
352354

353355
updateTextColor(divRef.current, e.target.textContent ?? '');
354356
const previousText = divRef.current.value;
@@ -370,7 +372,7 @@ const MarkdownTextInput = React.forwardRef<MarkdownTextInput, MarkdownTextInputP
370372
? Math.max(contentSelection.current.start, 0) // Don't move the caret when deleting forward with no characters selected
371373
: Math.max(Math.max(contentSelection.current.end, 0) + (parsedText.length - previousText.length), 0);
372374

373-
if (compositionRef.current) {
375+
if (isComposing) {
374376
updateTextColor(divRef.current, parsedText);
375377
updateSelection(e, {
376378
start: newCursorPosition,
@@ -657,13 +659,8 @@ const MarkdownTextInput = React.forwardRef<MarkdownTextInput, MarkdownTextInputP
657659
[insertText],
658660
);
659661

660-
const startComposition = useCallback(() => {
661-
compositionRef.current = true;
662-
}, []);
663-
664662
const endComposition = useCallback(
665663
(e: React.CompositionEvent<HTMLDivElement>) => {
666-
compositionRef.current = false;
667664
handleOnChangeText(e);
668665
},
669666
[handleOnChangeText],
@@ -788,7 +785,6 @@ const MarkdownTextInput = React.forwardRef<MarkdownTextInput, MarkdownTextInputP
788785
autoCapitalize={autoCapitalize}
789786
className={className}
790787
onKeyDown={handleKeyPress}
791-
onCompositionStart={startComposition}
792788
onCompositionEnd={endComposition}
793789
onInput={handleOnChangeText}
794790
onClick={handleClick}
@@ -829,4 +825,10 @@ const styles = StyleSheet.create({
829825

830826
export default MarkdownTextInput;
831827

832-
export type {MarkdownTextInputProps, MarkdownTextInputElement, HTMLMarkdownElement};
828+
export type {MarkdownNativeEvent, MarkdownTextInputProps, MarkdownTextInputElement, HTMLMarkdownElement};
829+
830+
function getWorkletRuntime() {
831+
throw new Error('[react-native-live-markdown] `getWorkletRuntime` is not available on web. Please make sure to use it only on native Android or iOS.');
832+
}
833+
834+
export {getWorkletRuntime};

src/__tests__/parseExpensiMark.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -371,10 +371,8 @@ describe('trailing whitespace', () => {
371371

372372
test('with whitespace between syntax', () => {
373373
expect('> > > Hello world').toBeParsedAs([
374-
{type: 'blockquote', start: 0, length: 17, depth: 3},
374+
{type: 'blockquote', start: 0, length: 17},
375375
{type: 'syntax', start: 0, length: 1},
376-
{type: 'syntax', start: 2, length: 1},
377-
{type: 'syntax', start: 4, length: 1},
378376
]);
379377
});
380378

0 commit comments

Comments
 (0)