feat(smart-app): implement complete mobile app MVP

- App.tsx: full navigation (Auth stack + Main tabs with 5 screens)
- Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen
- HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors
- MapScreen: interactive map with layer toggles (6 layers)
- MarketplaceScreen: categories (6), products (5), search
- ChatScreen: AI chat with quick prompts (4), bot responses
- ProfileScreen: user info, stats, menu (9 items), logout
- AlertsScreen: alert list with severity, acknowledge
- SensorsScreen: sensor list with type filters (6 types), search
- ZonesScreen: zone cards with stats
- SettingsScreen: language picker (FR/EN/ES/DE), privacy, about
- Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n
- Hooks: useSensors, useAlerts, useNotifications, useLocation
- Components: Card, Button, LoadingSpinner, ErrorBoundary, Header
- Services: iotService, notificationService (with axios API client)
- Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN)
- Theme: colors.ts with full design system (Blue Ocean palette)
- Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
This commit is contained in:
Eric FELIXINE
2026-06-01 18:00:35 -04:00
parent 08ca495bde
commit e30ae8ed09
35578 changed files with 3703534 additions and 43 deletions

View File

@@ -0,0 +1,173 @@
/*
* 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 "AttributedString.h"
#include <react/renderer/debug/DebugStringConvertibleItem.h>
namespace facebook::react {
using Fragment = AttributedString::Fragment;
using Fragments = AttributedString::Fragments;
#pragma mark - Fragment
std::string Fragment::AttachmentCharacter() {
// C++20 makes char8_t a distinct type from char, and u8 string literals
// consist of char8_t instead of char, which in turn requires std::u8string,
// etc. Here we were assuming char was UTF-8 anyway, so just cast to that
// (which is valid because char* is allowed to alias anything).
return reinterpret_cast<const char*>(
u8"\uFFFC"); // Unicode `OBJECT REPLACEMENT CHARACTER`
}
bool Fragment::isAttachment() const {
return string == AttachmentCharacter();
}
bool Fragment::operator==(const Fragment& rhs) const {
return std::tie(
string,
textAttributes,
parentShadowView.tag,
parentShadowView.layoutMetrics) ==
std::tie(
rhs.string,
rhs.textAttributes,
rhs.parentShadowView.tag,
rhs.parentShadowView.layoutMetrics);
}
bool Fragment::isContentEqual(const Fragment& rhs) const {
return std::tie(string, textAttributes) ==
std::tie(rhs.string, rhs.textAttributes);
}
bool Fragment::operator!=(const Fragment& rhs) const {
return !(*this == rhs);
}
#pragma mark - AttributedString
void AttributedString::appendFragment(const Fragment& fragment) {
ensureUnsealed();
if (fragment.string.empty()) {
return;
}
fragments_.push_back(fragment);
}
void AttributedString::prependFragment(const Fragment& fragment) {
ensureUnsealed();
if (fragment.string.empty()) {
return;
}
fragments_.insert(fragments_.begin(), fragment);
}
void AttributedString::appendAttributedString(
const AttributedString& attributedString) {
ensureUnsealed();
fragments_.insert(
fragments_.end(),
attributedString.fragments_.begin(),
attributedString.fragments_.end());
}
void AttributedString::prependAttributedString(
const AttributedString& attributedString) {
ensureUnsealed();
fragments_.insert(
fragments_.begin(),
attributedString.fragments_.begin(),
attributedString.fragments_.end());
}
const Fragments& AttributedString::getFragments() const {
return fragments_;
}
Fragments& AttributedString::getFragments() {
return fragments_;
}
std::string AttributedString::getString() const {
auto string = std::string{};
for (const auto& fragment : fragments_) {
string += fragment.string;
}
return string;
}
bool AttributedString::isEmpty() const {
return fragments_.empty();
}
bool AttributedString::compareTextAttributesWithoutFrame(
const AttributedString& rhs) const {
if (fragments_.size() != rhs.fragments_.size()) {
return false;
}
for (size_t i = 0; i < fragments_.size(); i++) {
if (fragments_[i].textAttributes != rhs.fragments_[i].textAttributes ||
fragments_[i].string != rhs.fragments_[i].string) {
return false;
}
}
return true;
}
bool AttributedString::operator==(const AttributedString& rhs) const {
return fragments_ == rhs.fragments_;
}
bool AttributedString::operator!=(const AttributedString& rhs) const {
return !(*this == rhs);
}
bool AttributedString::isContentEqual(const AttributedString& rhs) const {
if (fragments_.size() != rhs.fragments_.size()) {
return false;
}
for (size_t i = 0; i < fragments_.size(); i++) {
if (!fragments_[i].isContentEqual(rhs.fragments_[i])) {
return false;
}
}
return true;
}
#pragma mark - DebugStringConvertible
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList AttributedString::getDebugChildren() const {
auto list = SharedDebugStringConvertibleList{};
for (auto&& fragment : fragments_) {
auto propsList =
fragment.textAttributes.DebugStringConvertible::getDebugProps();
list.push_back(std::make_shared<DebugStringConvertibleItem>(
"Fragment",
fragment.string,
SharedDebugStringConvertibleList(),
propsList));
}
return list;
}
#endif
} // namespace facebook::react

View File

@@ -0,0 +1,146 @@
/*
* 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.
*/
#pragma once
#include <memory>
#include <react/renderer/attributedstring/TextAttributes.h>
#include <react/renderer/core/Sealable.h>
#include <react/renderer/core/ShadowNode.h>
#include <react/renderer/debug/DebugStringConvertible.h>
#include <react/renderer/mounting/ShadowView.h>
#include <react/utils/hash_combine.h>
namespace facebook::react {
class AttributedString;
using SharedAttributedString = std::shared_ptr<const AttributedString>;
/*
* Simple, cross-platform, React-specific implementation of attributed string
* (aka spanned string).
* `AttributedString` is basically a list of `Fragments` which have `string` and
* `textAttributes` + `shadowNode` associated with the `string`.
*/
class AttributedString : public Sealable, public DebugStringConvertible {
public:
class Fragment {
public:
static std::string AttachmentCharacter();
std::string string;
TextAttributes textAttributes;
ShadowView parentShadowView;
/*
* Returns true is the Fragment represents an attachment.
* Equivalent to `string == AttachmentCharacter()`.
*/
bool isAttachment() const;
/*
* Returns whether the underlying text and attributes are equal,
* disregarding layout or other information.
*/
bool isContentEqual(const Fragment& rhs) const;
bool operator==(const Fragment& rhs) const;
bool operator!=(const Fragment& rhs) const;
};
class Range {
public:
int location{0};
int length{0};
};
using Fragments = std::vector<Fragment>;
/*
* Appends and prepends a `fragment` to the string.
*/
void appendFragment(const Fragment& fragment);
void prependFragment(const Fragment& fragment);
/*
* Appends and prepends an `attributedString` (all its fragments) to
* the string.
*/
void appendAttributedString(const AttributedString& attributedString);
void prependAttributedString(const AttributedString& attributedString);
/*
* Returns a read-only reference to a list of fragments.
*/
const Fragments& getFragments() const;
/*
* Returns a reference to a list of fragments.
*/
Fragments& getFragments();
/*
* Returns a string constructed from all strings in all fragments.
*/
std::string getString() const;
/*
* Returns `true` if the string is empty (has no any fragments).
*/
bool isEmpty() const;
/**
* Compares equality of TextAttributes of all Fragments on both sides.
*/
bool compareTextAttributesWithoutFrame(const AttributedString& rhs) const;
bool isContentEqual(const AttributedString& rhs) const;
bool operator==(const AttributedString& rhs) const;
bool operator!=(const AttributedString& rhs) const;
#pragma mark - DebugStringConvertible
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList getDebugChildren() const override;
#endif
private:
Fragments fragments_;
};
} // namespace facebook::react
namespace std {
template <>
struct hash<facebook::react::AttributedString::Fragment> {
size_t operator()(
const facebook::react::AttributedString::Fragment& fragment) const {
return facebook::react::hash_combine(
fragment.string,
fragment.textAttributes,
fragment.parentShadowView,
fragment.parentShadowView.layoutMetrics);
}
};
template <>
struct hash<facebook::react::AttributedString> {
size_t operator()(
const facebook::react::AttributedString& attributedString) const {
auto seed = size_t{0};
for (const auto& fragment : attributedString.getFragments()) {
facebook::react::hash_combine(seed, fragment);
}
return seed;
}
};
} // namespace std

View File

@@ -0,0 +1,88 @@
/*
* 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 "AttributedStringBox.h"
#include <react/debug/react_native_assert.h>
#include <utility>
namespace facebook::react {
AttributedStringBox::AttributedStringBox()
: mode_(Mode::Value),
value_(std::make_shared<const AttributedString>(AttributedString{})),
opaquePointer_({}){};
AttributedStringBox::AttributedStringBox(const AttributedString& value)
: mode_(Mode::Value),
value_(std::make_shared<const AttributedString>(value)),
opaquePointer_({}){};
AttributedStringBox::AttributedStringBox(std::shared_ptr<void> opaquePointer)
: mode_(Mode::OpaquePointer),
value_({}),
opaquePointer_(std::move(opaquePointer)) {}
AttributedStringBox::AttributedStringBox(AttributedStringBox&& other) noexcept
: mode_(other.mode_),
value_(std::move(other.value_)),
opaquePointer_(std::move(other.opaquePointer_)) {
other.mode_ = AttributedStringBox::Mode::Value;
other.value_ = std::make_shared<const AttributedString>(AttributedString{});
}
AttributedStringBox::Mode AttributedStringBox::getMode() const {
return mode_;
}
const AttributedString& AttributedStringBox::getValue() const {
react_native_assert(mode_ == AttributedStringBox::Mode::Value);
react_native_assert(value_);
return *value_;
}
std::shared_ptr<void> AttributedStringBox::getOpaquePointer() const {
react_native_assert(mode_ == AttributedStringBox::Mode::OpaquePointer);
react_native_assert(opaquePointer_);
return opaquePointer_;
}
AttributedStringBox& AttributedStringBox::operator=(
AttributedStringBox&& other) noexcept {
if (this != &other) {
mode_ = other.mode_;
value_ = std::move(other.value_);
opaquePointer_ = std::move(other.opaquePointer_);
other.mode_ = AttributedStringBox::Mode::Value;
other.value_ = std::make_shared<const AttributedString>(AttributedString{});
}
return *this;
}
bool operator==(
const AttributedStringBox& lhs,
const AttributedStringBox& rhs) {
if (lhs.getMode() != rhs.getMode()) {
return false;
}
switch (lhs.getMode()) {
case AttributedStringBox::Mode::Value:
return lhs.getValue() == rhs.getValue();
case AttributedStringBox::Mode::OpaquePointer:
return lhs.getOpaquePointer() == rhs.getOpaquePointer();
}
}
bool operator!=(
const AttributedStringBox& lhs,
const AttributedStringBox& rhs) {
return !(lhs == rhs);
}
} // namespace facebook::react

View File

@@ -0,0 +1,78 @@
/*
* 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.
*/
#pragma once
#include <memory>
#include <react/renderer/attributedstring/AttributedString.h>
namespace facebook::react {
/*
* Represents an object storing a shared `AttributedString` or a shared pointer
* to some opaque platform-specific object that can be used as an attributed
* string. The class serves two main purposes:
* - Represent type-erased attributed string entity (which can be
* platform-specific or platform-independent);
* - Represent a container that can be copied with constant complexity.
*/
class AttributedStringBox final {
public:
enum class Mode { Value, OpaquePointer };
/*
* Default constructor constructs an empty string.
*/
AttributedStringBox();
/*
* Custom explicit constructors.
*/
explicit AttributedStringBox(const AttributedString& value);
explicit AttributedStringBox(std::shared_ptr<void> opaquePointer);
/*
* Movable, Copyable, Assignable.
*/
AttributedStringBox(const AttributedStringBox& other) = default;
AttributedStringBox(AttributedStringBox&& other) noexcept;
AttributedStringBox& operator=(const AttributedStringBox& other) = default;
AttributedStringBox& operator=(AttributedStringBox&& other) noexcept;
/*
* Getters.
*/
Mode getMode() const;
const AttributedString& getValue() const;
std::shared_ptr<void> getOpaquePointer() const;
private:
Mode mode_;
std::shared_ptr<const AttributedString> value_;
std::shared_ptr<void> opaquePointer_;
};
bool operator==(const AttributedStringBox& lhs, const AttributedStringBox& rhs);
bool operator!=(const AttributedStringBox& lhs, const AttributedStringBox& rhs);
} // namespace facebook::react
template <>
struct std::hash<facebook::react::AttributedStringBox> {
size_t operator()(
const facebook::react::AttributedStringBox& attributedStringBox) const {
switch (attributedStringBox.getMode()) {
case facebook::react::AttributedStringBox::Mode::Value:
return std::hash<facebook::react::AttributedString>()(
attributedStringBox.getValue());
case facebook::react::AttributedStringBox::Mode::OpaquePointer:
return std::hash<std::shared_ptr<void>>()(
attributedStringBox.getOpaquePointer());
}
}
};

View File

@@ -0,0 +1,35 @@
# 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.
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
add_compile_options(
-fexceptions
-frtti
-std=c++20
-Wall
-Wpedantic
-DLOG_TAG=\"Fabric\")
file(GLOB react_render_attributedstring_SRC CONFIGURE_DEPENDS *.cpp)
add_library(react_render_attributedstring STATIC ${react_render_attributedstring_SRC})
target_include_directories(react_render_attributedstring PUBLIC ${REACT_COMMON_DIR})
target_link_libraries(react_render_attributedstring
folly_runtime
glog
glog_init
react_debug
rrc_view
react_render_core
react_render_debug
react_render_graphics
react_render_mapbuffer
react_utils
rrc_view
yoga
)

View File

@@ -0,0 +1,57 @@
/*
* 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 "ParagraphAttributes.h"
#include <react/renderer/attributedstring/conversions.h>
#include <react/renderer/core/graphicsConversions.h>
#include <react/renderer/debug/debugStringConvertibleUtils.h>
#include <react/utils/FloatComparison.h>
namespace facebook::react {
bool ParagraphAttributes::operator==(const ParagraphAttributes& rhs) const {
return std::tie(
maximumNumberOfLines,
ellipsizeMode,
textBreakStrategy,
adjustsFontSizeToFit,
includeFontPadding,
android_hyphenationFrequency) ==
std::tie(
rhs.maximumNumberOfLines,
rhs.ellipsizeMode,
rhs.textBreakStrategy,
rhs.adjustsFontSizeToFit,
rhs.includeFontPadding,
rhs.android_hyphenationFrequency) &&
floatEquality(minimumFontSize, rhs.minimumFontSize) &&
floatEquality(maximumFontSize, rhs.maximumFontSize);
}
bool ParagraphAttributes::operator!=(const ParagraphAttributes& rhs) const {
return !(*this == rhs);
}
#pragma mark - DebugStringConvertible
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList ParagraphAttributes::getDebugProps() const {
return {
debugStringConvertibleItem("maximumNumberOfLines", maximumNumberOfLines),
debugStringConvertibleItem("ellipsizeMode", ellipsizeMode),
debugStringConvertibleItem("textBreakStrategy", textBreakStrategy),
debugStringConvertibleItem("adjustsFontSizeToFit", adjustsFontSizeToFit),
debugStringConvertibleItem("minimumFontSize", minimumFontSize),
debugStringConvertibleItem("maximumFontSize", maximumFontSize),
debugStringConvertibleItem("includeFontPadding", includeFontPadding),
debugStringConvertibleItem(
"android_hyphenationFrequency", android_hyphenationFrequency)};
}
#endif
} // namespace facebook::react

View File

@@ -0,0 +1,102 @@
/*
* 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.
*/
#pragma once
#include <limits>
#include <react/renderer/attributedstring/primitives.h>
#include <react/renderer/debug/DebugStringConvertible.h>
#include <react/renderer/graphics/Float.h>
#include <react/utils/hash_combine.h>
namespace facebook::react {
class ParagraphAttributes;
using SharedParagraphAttributes = std::shared_ptr<const ParagraphAttributes>;
/*
* Represents all visual attributes of a paragraph of text.
* Two data structures, ParagraphAttributes and AttributedText, should be
* enough to define visual representation of a piece of text on the screen.
*/
class ParagraphAttributes : public DebugStringConvertible {
public:
#pragma mark - Fields
/*
* Maximum number of lines which paragraph can take.
* Zero value represents "no limit".
*/
int maximumNumberOfLines{};
/*
* In case if a text cannot fit given boundaries, defines a place where
* an ellipsize should be placed.
*/
EllipsizeMode ellipsizeMode{};
/*
* (Android only) Break strategy for breaking paragraphs into lines.
*/
TextBreakStrategy textBreakStrategy{TextBreakStrategy::HighQuality};
/*
* Enables font size adjustment to fit constrained boundaries.
*/
bool adjustsFontSizeToFit{};
/*
* (Android only) Leaves enough room for ascenders and descenders instead of
* using the font ascent and descent strictly.
*/
bool includeFontPadding{true};
/*
* (Android only) Frequency of automatic hyphenation to use when determining
* word breaks.
*/
HyphenationFrequency android_hyphenationFrequency{};
/*
* In case of font size adjustment enabled, defines minimum and maximum
* font sizes.
*/
Float minimumFontSize{std::numeric_limits<Float>::quiet_NaN()};
Float maximumFontSize{std::numeric_limits<Float>::quiet_NaN()};
bool operator==(const ParagraphAttributes&) const;
bool operator!=(const ParagraphAttributes&) const;
#pragma mark - DebugStringConvertible
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList getDebugProps() const override;
#endif
};
} // namespace facebook::react
namespace std {
template <>
struct hash<facebook::react::ParagraphAttributes> {
size_t operator()(
const facebook::react::ParagraphAttributes& attributes) const {
return facebook::react::hash_combine(
attributes.maximumNumberOfLines,
attributes.ellipsizeMode,
attributes.textBreakStrategy,
attributes.adjustsFontSizeToFit,
attributes.minimumFontSize,
attributes.maximumFontSize,
attributes.includeFontPadding,
attributes.android_hyphenationFrequency);
}
};
} // namespace std

View File

@@ -0,0 +1,235 @@
/*
* 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 "TextAttributes.h"
#include <react/renderer/attributedstring/conversions.h>
#include <react/renderer/core/conversions.h>
#include <react/renderer/core/graphicsConversions.h>
#include <react/utils/FloatComparison.h>
#include <cmath>
#include <react/renderer/debug/debugStringConvertibleUtils.h>
namespace facebook::react {
void TextAttributes::apply(TextAttributes textAttributes) {
// Color
foregroundColor = textAttributes.foregroundColor
? textAttributes.foregroundColor
: foregroundColor;
backgroundColor = textAttributes.backgroundColor
? textAttributes.backgroundColor
: backgroundColor;
opacity =
!std::isnan(textAttributes.opacity) ? textAttributes.opacity : opacity;
// Font
fontFamily = !textAttributes.fontFamily.empty() ? textAttributes.fontFamily
: fontFamily;
fontSize =
!std::isnan(textAttributes.fontSize) ? textAttributes.fontSize : fontSize;
fontSizeMultiplier = !std::isnan(textAttributes.fontSizeMultiplier)
? textAttributes.fontSizeMultiplier
: fontSizeMultiplier;
fontWeight = textAttributes.fontWeight.has_value() ? textAttributes.fontWeight
: fontWeight;
fontStyle = textAttributes.fontStyle.has_value() ? textAttributes.fontStyle
: fontStyle;
fontVariant = textAttributes.fontVariant.has_value()
? textAttributes.fontVariant
: fontVariant;
allowFontScaling = textAttributes.allowFontScaling.has_value()
? textAttributes.allowFontScaling
: allowFontScaling;
dynamicTypeRamp = textAttributes.dynamicTypeRamp.has_value()
? textAttributes.dynamicTypeRamp
: dynamicTypeRamp;
letterSpacing = !std::isnan(textAttributes.letterSpacing)
? textAttributes.letterSpacing
: letterSpacing;
textTransform = textAttributes.textTransform.has_value()
? textAttributes.textTransform
: textTransform;
// Paragraph Styles
lineHeight = !std::isnan(textAttributes.lineHeight)
? textAttributes.lineHeight
: lineHeight;
alignment = textAttributes.alignment.has_value() ? textAttributes.alignment
: alignment;
baseWritingDirection = textAttributes.baseWritingDirection.has_value()
? textAttributes.baseWritingDirection
: baseWritingDirection;
lineBreakStrategy = textAttributes.lineBreakStrategy.has_value()
? textAttributes.lineBreakStrategy
: lineBreakStrategy;
// Decoration
textDecorationColor = textAttributes.textDecorationColor
? textAttributes.textDecorationColor
: textDecorationColor;
textDecorationLineType = textAttributes.textDecorationLineType.has_value()
? textAttributes.textDecorationLineType
: textDecorationLineType;
textDecorationStyle = textAttributes.textDecorationStyle.has_value()
? textAttributes.textDecorationStyle
: textDecorationStyle;
// Shadow
textShadowOffset = textAttributes.textShadowOffset.has_value()
? textAttributes.textShadowOffset.value()
: textShadowOffset;
textShadowRadius = !std::isnan(textAttributes.textShadowRadius)
? textAttributes.textShadowRadius
: textShadowRadius;
textShadowColor = textAttributes.textShadowColor
? textAttributes.textShadowColor
: textShadowColor;
// Special
isHighlighted = textAttributes.isHighlighted.has_value()
? textAttributes.isHighlighted
: isHighlighted;
// TextAttributes "inherits" the isPressable value from ancestors, so this
// only applies the current node's value for isPressable if it is truthy.
isPressable =
textAttributes.isPressable.has_value() && *textAttributes.isPressable
? textAttributes.isPressable
: isPressable;
layoutDirection = textAttributes.layoutDirection.has_value()
? textAttributes.layoutDirection
: layoutDirection;
accessibilityRole = textAttributes.accessibilityRole.has_value()
? textAttributes.accessibilityRole
: accessibilityRole;
role = textAttributes.role.has_value() ? textAttributes.role : role;
}
#pragma mark - Operators
bool TextAttributes::operator==(const TextAttributes& rhs) const {
return std::tie(
foregroundColor,
backgroundColor,
fontFamily,
fontWeight,
fontStyle,
fontVariant,
allowFontScaling,
dynamicTypeRamp,
alignment,
baseWritingDirection,
lineBreakStrategy,
textDecorationColor,
textDecorationLineType,
textDecorationStyle,
textShadowOffset,
textShadowColor,
isHighlighted,
isPressable,
layoutDirection,
accessibilityRole,
role,
textTransform) ==
std::tie(
rhs.foregroundColor,
rhs.backgroundColor,
rhs.fontFamily,
rhs.fontWeight,
rhs.fontStyle,
rhs.fontVariant,
rhs.allowFontScaling,
rhs.dynamicTypeRamp,
rhs.alignment,
rhs.baseWritingDirection,
rhs.lineBreakStrategy,
rhs.textDecorationColor,
rhs.textDecorationLineType,
rhs.textDecorationStyle,
rhs.textShadowOffset,
rhs.textShadowColor,
rhs.isHighlighted,
rhs.isPressable,
rhs.layoutDirection,
rhs.accessibilityRole,
rhs.role,
rhs.textTransform) &&
floatEquality(opacity, rhs.opacity) &&
floatEquality(fontSize, rhs.fontSize) &&
floatEquality(fontSizeMultiplier, rhs.fontSizeMultiplier) &&
floatEquality(letterSpacing, rhs.letterSpacing) &&
floatEquality(lineHeight, rhs.lineHeight) &&
floatEquality(textShadowRadius, rhs.textShadowRadius);
}
bool TextAttributes::operator!=(const TextAttributes& rhs) const {
return !(*this == rhs);
}
TextAttributes TextAttributes::defaultTextAttributes() {
static auto textAttributes = [] {
auto textAttributes = TextAttributes{};
// Non-obvious (can be different among platforms) default text attributes.
textAttributes.foregroundColor = blackColor();
textAttributes.backgroundColor = clearColor();
textAttributes.fontSize = 14.0;
textAttributes.fontSizeMultiplier = 1.0;
return textAttributes;
}();
return textAttributes;
}
#pragma mark - DebugStringConvertible
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList TextAttributes::getDebugProps() const {
return {
// Color
debugStringConvertibleItem("backgroundColor", backgroundColor),
debugStringConvertibleItem("foregroundColor", foregroundColor),
debugStringConvertibleItem("opacity", opacity),
// Font
debugStringConvertibleItem("fontFamily", fontFamily),
debugStringConvertibleItem("fontSize", fontSize),
debugStringConvertibleItem("fontSizeMultiplier", fontSizeMultiplier),
debugStringConvertibleItem("fontWeight", fontWeight),
debugStringConvertibleItem("fontStyle", fontStyle),
debugStringConvertibleItem("fontVariant", fontVariant),
debugStringConvertibleItem("allowFontScaling", allowFontScaling),
debugStringConvertibleItem("dynamicTypeRamp", dynamicTypeRamp),
debugStringConvertibleItem("letterSpacing", letterSpacing),
// Paragraph Styles
debugStringConvertibleItem("lineHeight", lineHeight),
debugStringConvertibleItem("alignment", alignment),
debugStringConvertibleItem("baseWritingDirection", baseWritingDirection),
debugStringConvertibleItem("lineBreakStrategyIOS", lineBreakStrategy),
// Decoration
debugStringConvertibleItem("textDecorationColor", textDecorationColor),
debugStringConvertibleItem(
"textDecorationLineType", textDecorationLineType),
debugStringConvertibleItem("textDecorationStyle", textDecorationStyle),
// Shadow
debugStringConvertibleItem("textShadowOffset", textShadowOffset),
debugStringConvertibleItem("textShadowRadius", textShadowRadius),
debugStringConvertibleItem("textShadowColor", textShadowColor),
// Special
debugStringConvertibleItem("isHighlighted", isHighlighted),
debugStringConvertibleItem("isPressable", isPressable),
debugStringConvertibleItem("layoutDirection", layoutDirection),
debugStringConvertibleItem("accessibilityRole", accessibilityRole),
debugStringConvertibleItem("role", role),
};
}
#endif
} // namespace facebook::react

View File

@@ -0,0 +1,141 @@
/*
* 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.
*/
#pragma once
#include <functional>
#include <limits>
#include <optional>
#include <react/renderer/attributedstring/primitives.h>
#include <react/renderer/components/view/AccessibilityPrimitives.h>
#include <react/renderer/core/LayoutPrimitives.h>
#include <react/renderer/core/ReactPrimitives.h>
#include <react/renderer/debug/DebugStringConvertible.h>
#include <react/renderer/graphics/Color.h>
#include <react/renderer/graphics/Float.h>
#include <react/renderer/graphics/Size.h>
#include <react/utils/hash_combine.h>
namespace facebook::react {
class TextAttributes;
using SharedTextAttributes = std::shared_ptr<const TextAttributes>;
class TextAttributes : public DebugStringConvertible {
public:
/*
* Returns TextAttribute object which has actual default attribute values
* (e.g. `foregroundColor = black`), in oppose to TextAttribute's default
* constructor which creates an object with nulled attributes.
*/
static TextAttributes defaultTextAttributes();
#pragma mark - Fields
// Color
SharedColor foregroundColor{};
SharedColor backgroundColor{};
Float opacity{std::numeric_limits<Float>::quiet_NaN()};
// Font
std::string fontFamily{""};
Float fontSize{std::numeric_limits<Float>::quiet_NaN()};
Float fontSizeMultiplier{std::numeric_limits<Float>::quiet_NaN()};
std::optional<FontWeight> fontWeight{};
std::optional<FontStyle> fontStyle{};
std::optional<FontVariant> fontVariant{};
std::optional<bool> allowFontScaling{};
std::optional<DynamicTypeRamp> dynamicTypeRamp{};
Float letterSpacing{std::numeric_limits<Float>::quiet_NaN()};
std::optional<TextTransform> textTransform{};
// Paragraph Styles
Float lineHeight{std::numeric_limits<Float>::quiet_NaN()};
std::optional<TextAlignment> alignment{};
std::optional<WritingDirection> baseWritingDirection{};
std::optional<LineBreakStrategy> lineBreakStrategy{};
// Decoration
SharedColor textDecorationColor{};
std::optional<TextDecorationLineType> textDecorationLineType{};
std::optional<TextDecorationStyle> textDecorationStyle{};
// Shadow
// TODO: Use `Point` type instead of `Size` for `textShadowOffset` attribute.
std::optional<Size> textShadowOffset{};
Float textShadowRadius{std::numeric_limits<Float>::quiet_NaN()};
SharedColor textShadowColor{};
// Special
std::optional<bool> isHighlighted{};
std::optional<bool> isPressable{};
// TODO T59221129: document where this value comes from and how it is set.
// It's not clear if this is being used properly, or if it's being set at all.
// Currently, it is intentionally *not* being set as part of BaseTextProps
// construction.
std::optional<LayoutDirection> layoutDirection{};
std::optional<AccessibilityRole> accessibilityRole{};
std::optional<Role> role{};
#pragma mark - Operations
void apply(TextAttributes textAttributes);
#pragma mark - Operators
bool operator==(const TextAttributes& rhs) const;
bool operator!=(const TextAttributes& rhs) const;
#pragma mark - DebugStringConvertible
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList getDebugProps() const override;
#endif
};
} // namespace facebook::react
namespace std {
template <>
struct hash<facebook::react::TextAttributes> {
size_t operator()(
const facebook::react::TextAttributes& textAttributes) const {
return facebook::react::hash_combine(
textAttributes.foregroundColor,
textAttributes.backgroundColor,
textAttributes.opacity,
textAttributes.fontFamily,
textAttributes.fontSize,
textAttributes.fontSizeMultiplier,
textAttributes.fontWeight,
textAttributes.fontStyle,
textAttributes.fontVariant,
textAttributes.allowFontScaling,
textAttributes.letterSpacing,
textAttributes.textTransform,
textAttributes.lineHeight,
textAttributes.alignment,
textAttributes.baseWritingDirection,
textAttributes.lineBreakStrategy,
textAttributes.textDecorationColor,
textAttributes.textDecorationLineType,
textAttributes.textDecorationStyle,
textAttributes.textShadowOffset,
textAttributes.textShadowRadius,
textAttributes.textShadowColor,
textAttributes.isHighlighted,
textAttributes.isPressable,
textAttributes.layoutDirection,
textAttributes.accessibilityRole,
textAttributes.role);
}
};
} // namespace std

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,122 @@
/*
* 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.
*/
#pragma once
#include <functional>
#include <limits>
namespace facebook::react {
enum class FontStyle { Normal, Italic, Oblique };
enum class FontWeight : int {
Weight100 = 100,
UltraLight = 100,
Weight200 = 200,
Thin = 200,
Weight300 = 300,
Light = 300,
Weight400 = 400,
Regular = 400,
Weight500 = 500,
Medium = 500,
Weight600 = 600,
Semibold = 600,
Demibold = 600,
Weight700 = 700,
Bold = 700,
Weight800 = 800,
Heavy = 800,
Weight900 = 900,
Black = 900
};
enum class FontVariant : int {
Default = 0,
SmallCaps = 1 << 1,
OldstyleNums = 1 << 2,
LiningNums = 1 << 3,
TabularNums = 1 << 4,
ProportionalNums = 1 << 5
};
enum class DynamicTypeRamp {
Caption2,
Caption1,
Footnote,
Subheadline,
Callout,
Body,
Headline,
Title3,
Title2,
Title1,
LargeTitle
};
enum class EllipsizeMode {
Clip, // Do not add ellipsize, simply clip.
Head, // Truncate at head of line: "...wxyz".
Tail, // Truncate at tail of line: "abcd...".
Middle // Truncate middle of line: "ab...yz".
};
enum class TextBreakStrategy {
Simple, // Simple strategy.
HighQuality, // High-quality strategy, including hyphenation.
Balanced // Balances line lengths.
};
enum class TextAlignment {
Natural, // Indicates the default alignment for script.
Left, // Visually left aligned.
Center, // Visually centered.
Right, // Visually right aligned.
Justified // Fully-justified. The last line in a paragraph is natural-aligned.
};
enum class WritingDirection {
Natural, // Determines direction using the Unicode Bidi Algorithm rules P2 and
// P3.
LeftToRight, // Left to right writing direction.
RightToLeft // Right to left writing direction.
};
enum class LineBreakStrategy {
None, // Don't use any line break strategies
PushOut, // Use the push out line break strategy.
HangulWordPriority, // When specified, it prohibits breaking between Hangul
// characters.
Standard // Use the same configuration of line break strategies that the
// system uses for standard UI labels.
};
enum class TextDecorationLineType {
None,
Underline,
Strikethrough,
UnderlineStrikethrough
};
enum class TextDecorationStyle { Solid, Double, Dotted, Dashed };
enum class TextTransform {
None,
Uppercase,
Lowercase,
Capitalize,
Unset,
};
enum class HyphenationFrequency {
None, // No hyphenation.
Normal, // Less frequent hyphenation.
Full // Standard amount of hyphenation.
};
} // namespace facebook::react

View File

@@ -0,0 +1,103 @@
/*
* 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 <memory>
#include <gtest/gtest.h>
#include <react/renderer/attributedstring/AttributedStringBox.h>
namespace facebook::react {
TEST(AttributedStringBoxTest, testDefaultConstructor) {
auto attributedStringBox = AttributedStringBox{};
EXPECT_EQ(attributedStringBox.getMode(), AttributedStringBox::Mode::Value);
EXPECT_EQ(attributedStringBox.getValue(), AttributedString{});
}
TEST(AttributedStringBoxTest, testValueConstructor) {
auto attributedString = AttributedString{};
auto fragment = AttributedString::Fragment{};
fragment.string = "test string";
attributedString.appendFragment(fragment);
auto attributedStringBox = AttributedStringBox{attributedString};
EXPECT_EQ(attributedStringBox.getMode(), AttributedStringBox::Mode::Value);
EXPECT_EQ(attributedStringBox.getValue(), attributedString);
}
TEST(AttributedStringBoxTest, testOpaquePointerConstructor) {
auto string = std::make_shared<std::string>("test string");
auto attributedStringBox = AttributedStringBox{string};
EXPECT_EQ(
attributedStringBox.getMode(), AttributedStringBox::Mode::OpaquePointer);
EXPECT_EQ(attributedStringBox.getOpaquePointer(), string);
EXPECT_EQ(string.use_count(), 2);
}
TEST(AttributedStringBoxTest, testMoveConstructor) {
{
auto string = std::make_shared<std::string>("test string");
auto movedFromAttributedStringBox = AttributedStringBox{string};
auto moveToAttributedStringBox =
AttributedStringBox{std::move(movedFromAttributedStringBox)};
EXPECT_EQ(
moveToAttributedStringBox.getMode(),
AttributedStringBox::Mode::OpaquePointer);
EXPECT_EQ(moveToAttributedStringBox.getOpaquePointer(), string);
EXPECT_EQ(string.use_count(), 2);
}
{
auto attributedString = AttributedString{};
auto fragment = AttributedString::Fragment{};
fragment.string = "test string";
attributedString.appendFragment(fragment);
auto movedFromAttributedStringBox = AttributedStringBox{attributedString};
auto moveToAttributedStringBox =
AttributedStringBox{std::move(movedFromAttributedStringBox)};
EXPECT_EQ(
moveToAttributedStringBox.getMode(), AttributedStringBox::Mode::Value);
EXPECT_EQ(moveToAttributedStringBox.getValue(), attributedString);
}
}
TEST(AttributedStringBoxTest, testMoveAssignment) {
{
auto string = std::make_shared<std::string>("test string");
auto movedFromAttributedStringBox = AttributedStringBox{string};
auto movedToAttributedStringBox = AttributedStringBox{};
movedToAttributedStringBox = std::move(movedFromAttributedStringBox);
EXPECT_EQ(
movedToAttributedStringBox.getMode(),
AttributedStringBox::Mode::OpaquePointer);
EXPECT_EQ(movedToAttributedStringBox.getOpaquePointer(), string);
EXPECT_EQ(string.use_count(), 2);
}
{
auto attributedString = AttributedString{};
auto fragment = AttributedString::Fragment{};
fragment.string = "test string";
attributedString.appendFragment(fragment);
auto movedFromAttributedStringBox = AttributedStringBox{attributedString};
auto moveToAttributedStringBox = AttributedStringBox{};
moveToAttributedStringBox = std::move(movedFromAttributedStringBox);
EXPECT_EQ(
moveToAttributedStringBox.getMode(), AttributedStringBox::Mode::Value);
EXPECT_EQ(moveToAttributedStringBox.getValue(), attributedString);
}
}
} // namespace facebook::react

View File

@@ -0,0 +1,45 @@
/*
* 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

View File

@@ -0,0 +1,34 @@
/*
* 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

View File

@@ -0,0 +1,37 @@
/*
* 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