Remove folly::dynamic AttributedString storage (#44512)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/44512

We only ever go through MapBuffer now, so we can remove the code related to storing text fragments in folly::dynamic.

Changelog: [Internal]

Reviewed By: rshest

Differential Revision: D56963463

fbshipit-source-id: 98bce8aa4ccad134ce18bf35028e1b7b5082c3ca
This commit is contained in:
Nick Gerleman
2024-05-13 13:40:53 -07:00
committed by Facebook GitHub Bot
parent 082e29ed4e
commit fd8f1f5274
7 changed files with 5 additions and 311 deletions
@@ -813,176 +813,6 @@ inline std::string toString(const AttributedString::Range& range) {
#ifdef ANDROID
inline folly::dynamic toDynamic(
const ParagraphAttributes& paragraphAttributes) {
auto values = folly::dynamic::object();
values("maximumNumberOfLines", paragraphAttributes.maximumNumberOfLines);
values("ellipsizeMode", toString(paragraphAttributes.ellipsizeMode));
values("textBreakStrategy", toString(paragraphAttributes.textBreakStrategy));
values("adjustsFontSizeToFit", paragraphAttributes.adjustsFontSizeToFit);
values("includeFontPadding", paragraphAttributes.includeFontPadding);
values(
"android_hyphenationFrequency",
toString(paragraphAttributes.android_hyphenationFrequency));
return values;
}
inline folly::dynamic toDynamic(const FontVariant& fontVariant) {
auto result = folly::dynamic::array();
if ((int)fontVariant & (int)FontVariant::SmallCaps) {
result.push_back("small-caps");
}
if ((int)fontVariant & (int)FontVariant::OldstyleNums) {
result.push_back("oldstyle-nums");
}
if ((int)fontVariant & (int)FontVariant::LiningNums) {
result.push_back("lining-nums");
}
if ((int)fontVariant & (int)FontVariant::TabularNums) {
result.push_back("tabular-nums");
}
if ((int)fontVariant & (int)FontVariant::ProportionalNums) {
result.push_back("proportional-nums");
}
return result;
}
inline folly::dynamic toDynamic(const TextAttributes& textAttributes) {
auto _textAttributes = folly::dynamic::object();
if (textAttributes.foregroundColor) {
_textAttributes(
"foregroundColor", toAndroidRepr(textAttributes.foregroundColor));
}
if (textAttributes.backgroundColor) {
_textAttributes(
"backgroundColor", toAndroidRepr(textAttributes.backgroundColor));
}
if (!std::isnan(textAttributes.opacity)) {
_textAttributes("opacity", textAttributes.opacity);
}
if (!textAttributes.fontFamily.empty()) {
_textAttributes("fontFamily", textAttributes.fontFamily);
}
if (!std::isnan(textAttributes.fontSize)) {
_textAttributes("fontSize", textAttributes.fontSize);
}
if (!std::isnan(textAttributes.fontSizeMultiplier)) {
_textAttributes("fontSizeMultiplier", textAttributes.fontSizeMultiplier);
}
if (textAttributes.fontWeight.has_value()) {
_textAttributes("fontWeight", toString(*textAttributes.fontWeight));
}
if (textAttributes.fontStyle.has_value()) {
_textAttributes("fontStyle", toString(*textAttributes.fontStyle));
}
if (textAttributes.fontVariant.has_value()) {
_textAttributes("fontVariant", toDynamic(*textAttributes.fontVariant));
}
if (textAttributes.allowFontScaling.has_value()) {
_textAttributes("allowFontScaling", *textAttributes.allowFontScaling);
}
if (!std::isnan(textAttributes.letterSpacing)) {
_textAttributes("letterSpacing", textAttributes.letterSpacing);
}
if (textAttributes.textTransform.has_value()) {
_textAttributes("textTransform", toString(*textAttributes.textTransform));
}
if (!std::isnan(textAttributes.lineHeight)) {
_textAttributes("lineHeight", textAttributes.lineHeight);
}
if (textAttributes.alignment.has_value()) {
_textAttributes("alignment", toString(*textAttributes.alignment));
}
if (textAttributes.baseWritingDirection.has_value()) {
_textAttributes(
"baseWritingDirection", toString(*textAttributes.baseWritingDirection));
}
if (textAttributes.lineBreakStrategy.has_value()) {
_textAttributes(
"lineBreakStrategyIOS", toString(*textAttributes.lineBreakStrategy));
}
// Decoration
if (textAttributes.textDecorationColor) {
_textAttributes(
"textDecorationColor",
toAndroidRepr(textAttributes.textDecorationColor));
}
if (textAttributes.textDecorationLineType.has_value()) {
_textAttributes(
"textDecorationLine", toString(*textAttributes.textDecorationLineType));
}
if (textAttributes.textDecorationStyle.has_value()) {
_textAttributes(
"textDecorationStyle", toString(*textAttributes.textDecorationStyle));
}
// Shadow
// textShadowOffset = textAttributes.textShadowOffset.has_value() ?
// textAttributes.textShadowOffset.value() : textShadowOffset;
if (!std::isnan(textAttributes.textShadowRadius)) {
_textAttributes("textShadowRadius", textAttributes.textShadowRadius);
}
if (textAttributes.textShadowColor) {
_textAttributes(
"textShadowColor", toAndroidRepr(textAttributes.textShadowColor));
}
// Special
if (textAttributes.isHighlighted.has_value()) {
_textAttributes("isHighlighted", *textAttributes.isHighlighted);
}
if (textAttributes.layoutDirection.has_value()) {
_textAttributes(
"layoutDirection", toString(*textAttributes.layoutDirection));
}
if (textAttributes.accessibilityRole.has_value()) {
_textAttributes(
"accessibilityRole", toString(*textAttributes.accessibilityRole));
}
if (textAttributes.textAlignVertical.has_value()) {
_textAttributes(
"textAlignVertical", toString(*textAttributes.textAlignVertical));
}
return _textAttributes;
}
inline folly::dynamic toDynamic(const AttributedString::Fragment& fragment) {
folly::dynamic value = folly::dynamic::object();
value["string"] = fragment.string;
if (fragment.parentShadowView.componentHandle) {
value["reactTag"] = fragment.parentShadowView.tag;
}
if (fragment.isAttachment()) {
value["isAttachment"] = true;
value["width"] = fragment.parentShadowView.layoutMetrics.frame.size.width;
value["height"] = fragment.parentShadowView.layoutMetrics.frame.size.height;
}
value["textAttributes"] = toDynamic(fragment.textAttributes);
return value;
}
inline folly::dynamic toDynamic(const AttributedString& attributedString) {
auto value = folly::dynamic::object();
auto fragments = folly::dynamic::array();
for (auto fragment : attributedString.getFragments()) {
fragments.push_back(toDynamic(fragment));
}
value("fragments", fragments);
value(
"hash", std::hash<facebook::react::AttributedString>{}(attributedString));
value("string", attributedString.getString());
return value;
}
inline folly::dynamic toDynamic(const AttributedString::Range& range) {
folly::dynamic dynamicValue = folly::dynamic::object();
dynamicValue["location"] = range.location;
dynamicValue["length"] = range.length;
return dynamicValue;
}
// constants for AttributedString serialization
constexpr static MapBuffer::Key AS_KEY_HASH = 0;
constexpr static MapBuffer::Key AS_KEY_STRING = 1;
@@ -1,45 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <gtest/gtest.h>
#include <react/renderer/attributedstring/TextAttributes.h>
#include <react/renderer/attributedstring/conversions.h>
#include <react/renderer/attributedstring/primitives.h>
#include <react/renderer/core/graphicsConversions.h>
namespace facebook::react {
#ifdef ANDROID
TEST(AttributedStringTest, testToDynamic) {
auto attributedString = AttributedString{};
auto fragment = AttributedString::Fragment{};
fragment.string = "test";
auto text = TextAttributes{};
text.foregroundColor = {
colorFromComponents({100 / 255.0, 153 / 255.0, 200 / 255.0, 1.0})};
text.opacity = 0.5;
text.fontStyle = FontStyle::Italic;
text.fontWeight = FontWeight::Thin;
text.fontVariant = FontVariant::TabularNums;
fragment.textAttributes = text;
attributedString.appendFragment(fragment);
auto result = toDynamic(attributedString);
EXPECT_EQ(result["string"], fragment.string);
auto textAttribute = result["fragments"][0]["textAttributes"];
EXPECT_EQ(textAttribute["foregroundColor"], toDynamic(text.foregroundColor));
EXPECT_EQ(textAttribute["opacity"], text.opacity);
EXPECT_EQ(textAttribute["fontStyle"], toString(text.fontStyle.value()));
EXPECT_EQ(textAttribute["fontWeight"], toString(text.fontWeight.value()));
}
#endif
} // namespace facebook::react
@@ -1,34 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <gtest/gtest.h>
#include <react/renderer/attributedstring/ParagraphAttributes.h>
#include <react/renderer/attributedstring/conversions.h>
#include <react/renderer/attributedstring/primitives.h>
namespace facebook::react {
#ifdef ANDROID
TEST(ParagraphAttributesTest, testToDynamic) {
auto paragraphAttributes = ParagraphAttributes{};
paragraphAttributes.maximumNumberOfLines = 2;
paragraphAttributes.adjustsFontSizeToFit = false;
paragraphAttributes.ellipsizeMode = EllipsizeMode::Middle;
auto result = toDynamic(paragraphAttributes);
EXPECT_EQ(
result["maximumNumberOfLines"], paragraphAttributes.maximumNumberOfLines);
EXPECT_EQ(
result["adjustsFontSizeToFit"], paragraphAttributes.adjustsFontSizeToFit);
EXPECT_EQ(
result["ellipsizeMode"], toString(paragraphAttributes.ellipsizeMode));
}
#endif
} // namespace facebook::react
@@ -1,37 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <gtest/gtest.h>
#include <react/renderer/attributedstring/TextAttributes.h>
#include <react/renderer/attributedstring/conversions.h>
#include <react/renderer/attributedstring/primitives.h>
#include <react/renderer/core/graphicsConversions.h>
namespace facebook::react {
#ifdef ANDROID
TEST(TextAttributesTest, testToDynamic) {
auto textAttributes = TextAttributes{};
textAttributes.foregroundColor = {
colorFromComponents({200 / 255.0, 153 / 255.0, 100 / 255.0, 1.0})};
textAttributes.opacity = 0.5;
textAttributes.fontStyle = FontStyle::Italic;
textAttributes.fontWeight = FontWeight::Thin;
textAttributes.fontVariant = FontVariant::TabularNums;
auto result = toDynamic(textAttributes);
EXPECT_EQ(
result["foregroundColor"], toDynamic(textAttributes.foregroundColor));
EXPECT_EQ(result["opacity"], textAttributes.opacity);
EXPECT_EQ(result["fontStyle"], toString(textAttributes.fontStyle.value()));
EXPECT_EQ(result["fontWeight"], toString(textAttributes.fontWeight.value()));
}
#endif
} // namespace facebook::react
@@ -14,7 +14,7 @@ namespace facebook::react {
#ifdef ANDROID
folly::dynamic ParagraphState::getDynamic() const {
return toDynamic(*this);
LOG(FATAL) << "ParagraphState may only be serialized to MapBuffer";
}
MapBuffer ParagraphState::getMapBuffer() const {
@@ -18,15 +18,6 @@
namespace facebook::react {
#ifdef ANDROID
inline folly::dynamic toDynamic(const ParagraphState& paragraphState) {
folly::dynamic newState = folly::dynamic::object();
newState["attributedString"] = toDynamic(paragraphState.attributedString);
newState["paragraphAttributes"] =
toDynamic(paragraphState.paragraphAttributes);
newState["hash"] = newState["attributedString"]["hash"];
return newState;
}
inline MapBuffer toMapBuffer(const ParagraphState& paragraphState) {
auto builder = MapBufferBuilder();
auto attStringMapBuffer = toMapBuffer(paragraphState.attributedString);
@@ -66,27 +66,16 @@ AndroidTextInputState::AndroidTextInputState(
.getDouble()){};
folly::dynamic AndroidTextInputState::getDynamic() const {
// Java doesn't need all fields, so we don't pass them all along.
folly::dynamic newState = folly::dynamic::object();
LOG(FATAL) << "Android TextInput state should only be read using MapBuffer";
}
MapBuffer AndroidTextInputState::getMapBuffer() const {
auto builder = MapBufferBuilder();
// If we have a `cachedAttributedStringId` we know that we're (1) not trying
// to set a new string, so we don't need to pass it along; (2) setState was
// called from Java to trigger a relayout with a `cachedAttributedStringId`,
// so Java has all up-to-date information and we should pass an empty map
// through.
if (cachedAttributedStringId == 0) {
newState["mostRecentEventCount"] = mostRecentEventCount;
newState["attributedString"] = toDynamic(attributedString);
newState["hash"] = newState["attributedString"]["hash"];
newState["paragraphAttributes"] =
toDynamic(paragraphAttributes); // TODO: can we memoize this in Java?
}
return newState;
}
MapBuffer AndroidTextInputState::getMapBuffer() const {
auto builder = MapBufferBuilder();
// See comment in getDynamic block.
if (cachedAttributedStringId == 0) {
// TODO truncation
builder.putInt(