Files
Eric FELIXINE e30ae8ed09 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
2026-06-01 18:00:35 -04:00

325 lines
11 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "Shareables.h"
using namespace facebook;
namespace reanimated {
jsi::Function getValueUnpacker(jsi::Runtime &rt) {
auto valueUnpacker = rt.global().getProperty(rt, "__valueUnpacker");
assert(valueUnpacker.isObject() && "valueUnpacker not found");
return valueUnpacker.asObject(rt).asFunction(rt);
}
#ifndef NDEBUG
static const auto callGuardLambda = [](facebook::jsi::Runtime &rt,
const facebook::jsi::Value &thisVal,
const facebook::jsi::Value *args,
size_t count) {
return args[0].asObject(rt).asFunction(rt).call(rt, args + 1, count - 1);
};
jsi::Function getCallGuard(jsi::Runtime &rt) {
auto callGuard = rt.global().getProperty(rt, "__callGuardDEV");
if (callGuard.isObject()) {
// Use JS implementation if `__callGuardDEV` has already been installed.
// This is the desired behavior.
return callGuard.asObject(rt).asFunction(rt);
}
// Otherwise, fallback to C++ JSI implementation. This is necessary so that we
// can install `__callGuardDEV` itself and should happen only once. Note that
// the C++ implementation doesn't intercept errors and simply throws them as
// C++ exceptions which crashes the app. We assume that installing the guard
// doesn't throw any errors.
return jsi::Function::createFromHostFunction(
rt, jsi::PropNameID::forAscii(rt, "callGuard"), 1, callGuardLambda);
}
#endif // NDEBUG
jsi::Value makeShareableClone(
jsi::Runtime &rt,
const jsi::Value &value,
const jsi::Value &shouldRetainRemote,
const jsi::Value &nativeStateSource) {
std::shared_ptr<Shareable> shareable;
if (value.isObject()) {
auto object = value.asObject(rt);
if (!object.getProperty(rt, "__workletHash").isUndefined()) {
shareable = std::make_shared<ShareableWorklet>(rt, object);
} else if (!object.getProperty(rt, "__init").isUndefined()) {
shareable = std::make_shared<ShareableHandle>(rt, object);
} else if (object.isFunction(rt)) {
auto function = object.asFunction(rt);
if (function.isHostFunction(rt)) {
shareable =
std::make_shared<ShareableHostFunction>(rt, std::move(function));
} else {
shareable =
std::make_shared<ShareableRemoteFunction>(rt, std::move(function));
}
} else if (object.isArray(rt)) {
if (shouldRetainRemote.isBool() && shouldRetainRemote.getBool()) {
shareable = std::make_shared<RetainingShareable<ShareableArray>>(
rt, object.asArray(rt));
} else {
shareable = std::make_shared<ShareableArray>(rt, object.asArray(rt));
}
} else if (object.isArrayBuffer(rt)) {
shareable =
std::make_shared<ShareableArrayBuffer>(rt, object.getArrayBuffer(rt));
} else if (object.isHostObject(rt)) {
if (object.isHostObject<ShareableJSRef>(rt)) {
return object;
}
shareable =
std::make_shared<ShareableHostObject>(rt, object.getHostObject(rt));
} else {
if (shouldRetainRemote.isBool() && shouldRetainRemote.getBool()) {
shareable = std::make_shared<RetainingShareable<ShareableObject>>(
rt, object, nativeStateSource);
} else {
shareable =
std::make_shared<ShareableObject>(rt, object, nativeStateSource);
}
}
} else if (value.isString()) {
shareable = std::make_shared<ShareableString>(value.asString(rt).utf8(rt));
} else if (value.isUndefined()) {
shareable = std::make_shared<ShareableScalar>();
} else if (value.isNull()) {
shareable = std::make_shared<ShareableScalar>(nullptr);
} else if (value.isBool()) {
shareable = std::make_shared<ShareableScalar>(value.getBool());
} else if (value.isNumber()) {
shareable = std::make_shared<ShareableScalar>(value.getNumber());
#if REACT_NATIVE_MINOR_VERSION >= 71
} else if (value.isBigInt()) {
shareable = std::make_shared<ShareableBigInt>(rt, value.getBigInt(rt));
#endif
} else if (value.isSymbol()) {
// TODO: this is only a placeholder implementation, here we replace symbols
// with strings in order to make certain objects to be captured. There isn't
// yet any usecase for using symbols on the UI runtime so it is fine to keep
// it like this for now.
shareable =
std::make_shared<ShareableString>(value.getSymbol(rt).toString(rt));
} else {
throw std::runtime_error(
"[Reanimated] Attempted to convert an unsupported value type.");
}
return ShareableJSRef::newHostObject(rt, shareable);
}
std::shared_ptr<Shareable> extractShareableOrThrow(
jsi::Runtime &rt,
const jsi::Value &maybeShareableValue,
const std::string &errorMessage) {
if (maybeShareableValue.isObject()) {
auto object = maybeShareableValue.asObject(rt);
if (object.isHostObject<ShareableJSRef>(rt)) {
return object.getHostObject<ShareableJSRef>(rt)->value();
}
throw std::runtime_error(
"[Reanimated] Attempted to extract from a HostObject that wasn't converted to a Shareable.");
} else if (maybeShareableValue.isUndefined()) {
return Shareable::undefined();
}
throw std::runtime_error(errorMessage);
}
Shareable::~Shareable() {}
std::shared_ptr<Shareable> Shareable::undefined() {
static auto undefined = std::make_shared<ShareableScalar>();
return undefined;
}
template <typename BaseClass>
jsi::Value RetainingShareable<BaseClass>::getJSValue(jsi::Runtime &rt) {
if (&rt == primaryRuntime_) {
// TODO: it is suboptimal to generate new object every time getJS is
// called on host runtime the objects we are generating already exists
// and we should possibly just grab a hold of such object and use it here
// instead of creating a new JS representation. As far as I understand the
// only case where it can be realistically called this way is when a
// shared value is created and then accessed on the same runtime
return BaseClass::toJSValue(rt);
}
if (secondaryValue_ == nullptr) {
auto value = BaseClass::toJSValue(rt);
secondaryValue_ = std::make_unique<jsi::Value>(rt, value);
secondaryRuntime_ = &rt;
return value;
}
if (&rt == secondaryRuntime_) {
return jsi::Value(rt, *secondaryValue_);
}
return BaseClass::toJSValue(rt);
}
ShareableJSRef::~ShareableJSRef() {}
ShareableArray::ShareableArray(jsi::Runtime &rt, const jsi::Array &array)
: Shareable(ArrayType) {
auto size = array.size(rt);
data_.reserve(size);
for (size_t i = 0; i < size; i++) {
data_.push_back(extractShareableOrThrow(rt, array.getValueAtIndex(rt, i)));
}
}
jsi::Value ShareableArray::toJSValue(jsi::Runtime &rt) {
auto size = data_.size();
auto ary = jsi::Array(rt, size);
for (size_t i = 0; i < size; i++) {
ary.setValueAtIndex(rt, i, data_[i]->getJSValue(rt));
}
return ary;
}
jsi::Value ShareableArrayBuffer::toJSValue(jsi::Runtime &rt) {
auto size = static_cast<int>(data_.size());
auto arrayBuffer = rt.global()
.getPropertyAsFunction(rt, "ArrayBuffer")
.callAsConstructor(rt, size)
.getObject(rt)
.getArrayBuffer(rt);
memcpy(arrayBuffer.data(rt), data_.data(), size);
return arrayBuffer;
}
ShareableObject::ShareableObject(jsi::Runtime &rt, const jsi::Object &object)
: Shareable(ObjectType) {
auto propertyNames = object.getPropertyNames(rt);
auto size = propertyNames.size(rt);
data_.reserve(size);
for (size_t i = 0; i < size; i++) {
auto key = propertyNames.getValueAtIndex(rt, i).asString(rt);
auto value = extractShareableOrThrow(rt, object.getProperty(rt, key));
data_.emplace_back(key.utf8(rt), value);
}
#if REACT_NATIVE_MINOR_VERSION >= 71
if (object.hasNativeState(rt)) {
nativeState_ = object.getNativeState(rt);
}
#endif
}
ShareableObject::ShareableObject(
jsi::Runtime &rt,
const jsi::Object &object,
const jsi::Value &nativeStateSource)
: ShareableObject(rt, object) {
#if REACT_NATIVE_MINOR_VERSION >= 71
if (nativeStateSource.isObject() &&
nativeStateSource.asObject(rt).hasNativeState(rt)) {
nativeState_ = nativeStateSource.asObject(rt).getNativeState(rt);
}
#endif
}
jsi::Value ShareableObject::toJSValue(jsi::Runtime &rt) {
auto obj = jsi::Object(rt);
for (size_t i = 0, size = data_.size(); i < size; i++) {
obj.setProperty(
rt, data_[i].first.c_str(), data_[i].second->getJSValue(rt));
}
#if REACT_NATIVE_MINOR_VERSION >= 71
if (nativeState_ != nullptr) {
obj.setNativeState(rt, nativeState_);
}
#endif
return obj;
}
jsi::Value ShareableHostObject::toJSValue(jsi::Runtime &rt) {
return jsi::Object::createFromHostObject(rt, hostObject_);
}
jsi::Value ShareableHostFunction::toJSValue(jsi::Runtime &rt) {
return jsi::Function::createFromHostFunction(
rt, jsi::PropNameID::forUtf8(rt, name_), paramCount_, hostFunction_);
}
jsi::Value ShareableWorklet::toJSValue(jsi::Runtime &rt) {
assert(
std::any_of(
data_.cbegin(),
data_.cend(),
[](const auto &item) { return item.first == "__workletHash"; }) &&
"ShareableWorklet doesn't have `__workletHash` property");
jsi::Value obj = ShareableObject::toJSValue(rt);
return getValueUnpacker(rt).call(
rt, obj, jsi::String::createFromAscii(rt, "Worklet"));
}
jsi::Value ShareableRemoteFunction::toJSValue(jsi::Runtime &rt) {
if (&rt == runtime_) {
return jsi::Value(rt, *function_);
} else {
#ifndef NDEBUG
return getValueUnpacker(rt).call(
rt,
ShareableJSRef::newHostObject(rt, shared_from_this()),
jsi::String::createFromAscii(rt, "RemoteFunction"),
jsi::String::createFromUtf8(rt, name_));
#else
return ShareableJSRef::newHostObject(rt, shared_from_this());
#endif
}
}
jsi::Value ShareableHandle::toJSValue(jsi::Runtime &rt) {
if (remoteValue_ == nullptr) {
auto initObj = initializer_->getJSValue(rt);
auto value = std::make_unique<jsi::Value>(getValueUnpacker(rt).call(
rt, initObj, jsi::String::createFromAscii(rt, "Handle")));
// We are locking the initialization here since the thread that is
// initalizing can be pre-empted on runtime lock. E.g.
// UI thread can be pre-empted on initialization of a shared value and then
// JS thread can try to access the shared value, locking the whole runtime.
// If we put the lock on `getValueUnpacker` part (basically any part that
// requires runtime) we would get a deadlock since UI thread would never
// release it.
std::unique_lock<std::mutex> lock(initializationMutex_);
if (remoteValue_ == nullptr) {
remoteValue_ = std::move(value);
remoteRuntime_ = &rt;
}
}
return jsi::Value(rt, *remoteValue_);
}
jsi::Value ShareableString::toJSValue(jsi::Runtime &rt) {
return jsi::String::createFromUtf8(rt, data_);
}
#if REACT_NATIVE_MINOR_VERSION >= 71
jsi::Value ShareableBigInt::toJSValue(jsi::Runtime &rt) {
return rt.global()
.getPropertyAsFunction(rt, "BigInt")
.call(rt, jsi::String::createFromUtf8(rt, string_));
}
#endif
jsi::Value ShareableScalar::toJSValue(jsi::Runtime &) {
switch (valueType_) {
case Shareable::UndefinedType:
return jsi::Value();
case Shareable::NullType:
return jsi::Value(nullptr);
case Shareable::BooleanType:
return jsi::Value(data_.boolean);
case Shareable::NumberType:
return jsi::Value(data_.number);
default:
throw std::runtime_error(
"[Reanimated] Attempted to convert object that's not of a scalar type.");
}
}
} /* namespace reanimated */