mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 16:02:32 +00:00
* fix(mobile): quantize chat pinch font scale so a zoom stops re-measuring the list every frame
A user bubble on a 390pt iPhone painted five lines inside a frame that
reserved six, with the last painted line cut through a glyph at the content
edge and "no longer needed." gone.
The paint is React Native's: a `<Text>` with no `numberOfLines` gets a text
container whose `lineBreakMode` is `NSLineBreakByClipping`
(RCTTextLayoutManager.mm). Measure lays out into `{width, CGFLOAT_MAX}`, paint
lays out into the mounted content frame — so a frame one line short does not
re-wrap, it dumps the remainder onto the last fitting line and clips it, with
no ellipsis. Reproduced on-device against the real component with the message
text held constant, so five painted lines can only be truncation.
Two conditions are each necessary, and removing either makes it vanish over
~6000 measured bubble renders: a pooled `RCTParagraphComponentView` carrying a
shorter row's content frame (`prepareForRecycle` clears `state` but not
`_textView.layoutMetrics`), and whole-list re-measure churn while rows enter
and leave that pool.
The churn was ours. `renderItem` closes over `fontScale`, and the pinch handler
committed a new scale on every gesture frame, so one zoom drove hundreds of
full-list re-measures. The pinch is composed `Simultaneous` with the list's own
scroll, so a stray second finger during a scroll started that storm at scales
the user cannot see — matching the report, whose glyph metrics are `fontScale`
1.0 exactly.
`quantizeFontScale` snaps commits to a 5% grid. React bails out of a same-value
`setState`, so gesture noise now commits nothing and a full-range pinch commits
at most ~20 times. Under the churn that produced 67 defects in 6636 bubble
renders, the quantized build measured 0 in 6064 — with a forced-defect control
bubble flagged in 100% of frames of both runs to prove the detector was live.
This removes the trigger we own; it does not close the RN recycling window
itself. That needs a one-line reset in `prepareForRecycle`, which cannot land
here without refreshing the `patchedDependencies` hash under `mobile/`.
* fix(mobile): reset recycled paragraph layout before reuse
61 lines
3.0 KiB
Diff
61 lines
3.0 KiB
Diff
diff --git a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm
|
|
index d02e003a439e410ddab597b2c036256c80db0395..3caff21b0d00bfbc6dea3f8470e812b8343ac3a2 100644
|
|
--- a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm
|
|
+++ b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm
|
|
@@ -703,6 +703,7 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
|
|
{
|
|
return {
|
|
.text = RCTStringFromNSString(_backedTextInputView.attributedText.string),
|
|
+ .isComposing = _backedTextInputView.markedTextRange != nil,
|
|
.selectionRange = [self _selectionRange],
|
|
.eventCount = static_cast<int>(_mostRecentEventCount),
|
|
.contentOffset = RCTPointFromCGPoint(_backedTextInputView.contentOffset),
|
|
diff --git a/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.cpp b/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.cpp
|
|
index a9bc219f8bf729111e907423ca61b991fd712b46..60e9c6f54edb295295d6e48d1d554499106b7e57 100644
|
|
--- a/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.cpp
|
|
+++ b/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.cpp
|
|
@@ -24,6 +24,10 @@ static jsi::Value textInputMetricsPayload(
|
|
|
|
payload.setProperty(runtime, "eventCount", textInputMetrics.eventCount);
|
|
|
|
+ if (textInputMetrics.isComposing.has_value()) {
|
|
+ payload.setProperty(runtime, "isComposing", *textInputMetrics.isComposing);
|
|
+ }
|
|
+
|
|
if (includeSelectionState) {
|
|
auto selection = jsi::Object(runtime);
|
|
selection.setProperty(
|
|
diff --git a/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.h b/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.h
|
|
index a3f2e01c130c1a53aee0b39e72927d21f2a6973d..c9d34f8b42644800b5c85e5be45b6ebfbe94a54d 100644
|
|
--- a/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.h
|
|
+++ b/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.h
|
|
@@ -7,6 +7,8 @@
|
|
|
|
#pragma once
|
|
|
|
+#include <optional>
|
|
+
|
|
#include <react/renderer/attributedstring/AttributedString.h>
|
|
#include <react/renderer/components/view/ViewEventEmitter.h>
|
|
|
|
@@ -18,6 +20,7 @@ class TextInputEventEmitter : public ViewEventEmitter {
|
|
|
|
struct Metrics {
|
|
std::string text;
|
|
+ std::optional<bool> isComposing;
|
|
AttributedString::Range selectionRange;
|
|
// ScrollView-like metrics
|
|
Size contentSize;
|
|
diff --git a/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm b/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm
|
|
index 79ee7ffe43..956f189a1c 100644
|
|
--- a/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm
|
|
+++ b/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm
|
|
@@ -136,6 +136,7 @@ using namespace facebook::react;
|
|
- (void)prepareForRecycle
|
|
{
|
|
[super prepareForRecycle];
|
|
_textView.state = nullptr;
|
|
+ _textView.layoutMetrics = EmptyLayoutMetrics;
|
|
_accessibilityProvider = nil;
|
|
}
|