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,52 @@
#include "AsyncQueue.h"
#include <utility>
namespace reanimated {
AsyncQueue::AsyncQueue(std::string name)
: state_(std::make_shared<AsyncQueueState>()) {
auto thread = std::thread([name, state = state_] {
#if __APPLE__
pthread_setname_np(name.c_str());
#endif
while (state->running) {
std::unique_lock<std::mutex> lock(state->mutex);
state->cv.wait(
lock, [state] { return !state->queue.empty() || !state->running; });
if (!state->running) {
return;
}
if (state->queue.empty()) {
continue;
}
auto job = std::move(state->queue.front());
state->queue.pop();
lock.unlock();
job();
}
});
#ifdef ANDROID
pthread_setname_np(thread.native_handle(), name.c_str());
#endif
thread.detach();
}
AsyncQueue::~AsyncQueue() {
{
std::unique_lock<std::mutex> lock(state_->mutex);
state_->running = false;
state_->queue = {};
}
state_->cv.notify_all();
}
void AsyncQueue::push(std::function<void()> &&job) {
{
std::unique_lock<std::mutex> lock(state_->mutex);
state_->queue.emplace(job);
}
state_->cv.notify_one();
}
} // namespace reanimated

View File

@@ -0,0 +1,35 @@
#pragma once
#include <jsi/jsi.h>
#include <atomic>
#include <condition_variable>
#include <memory>
#include <queue>
#include <string>
#include <thread>
#include <utility>
#include <vector>
namespace reanimated {
struct AsyncQueueState {
std::atomic_bool running{true};
std::mutex mutex;
std::condition_variable cv;
std::queue<std::function<void()>> queue;
};
class AsyncQueue {
public:
explicit AsyncQueue(std::string name);
~AsyncQueue();
void push(std::function<void()> &&job);
private:
const std::shared_ptr<AsyncQueueState> state_;
};
} // namespace reanimated

View File

@@ -0,0 +1,14 @@
#pragma once
#include <algorithm>
namespace reanimated {
namespace collection {
template <class CollectionType, class ValueType>
inline bool contains(const CollectionType &collection, const ValueType &value) {
return collection.find(value) != collection.end();
}
} // namespace collection
} // namespace reanimated

View File

@@ -0,0 +1,5 @@
#include "FeaturesConfig.h"
namespace reanimated {
bool FeaturesConfig::_isLayoutAnimationEnabled = false;
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include <string>
namespace reanimated {
class FeaturesConfig {
public:
static inline bool isLayoutAnimationEnabled() {
return _isLayoutAnimationEnabled;
}
static inline void setLayoutAnimationEnabled(bool isLayoutAnimationEnabled) {
_isLayoutAnimationEnabled = isLayoutAnimationEnabled;
}
private:
static bool _isLayoutAnimationEnabled;
};
} // namespace reanimated

View File

@@ -0,0 +1,340 @@
#include "JSISerializer.h"
#include <cxxabi.h>
#include <iostream>
#include <sstream>
const std::vector<std::string> SUPPORTED_ERROR_TYPES = {
"Error",
"AggregateError",
"EvalError",
"RangeError",
"ReferenceError",
"SyntaxError",
"TypeError",
"URIError",
"InternalError"};
const std::vector<std::string> SUPPORTED_INDEXED_COLLECTION_TYPES = {
"Int8Array",
"Uint8Array",
"Uint8ClampedArray",
"Int16Array",
"Uint16Array",
"Int32Array",
"Uint32Array",
"BigInt64Array",
"BigUint64Array",
"Float32Array",
"Float64Array",
};
const std::vector<std::string> SUPPORTED_STRUCTURED_DATA_TYPES = {
"ArrayBuffer",
"SharedArrayBuffer",
"DataView",
"Atomics",
"JSON",
};
const std::vector<std::string> SUPPORTED_MANAGING_MEMORY_TYPES = {
"WeakRef",
"FinalizationRegistry",
};
const std::vector<std::string> SUPPORTED_ABSTRACTION_OBJECT_TYPES = {
"Iterator",
"AsyncIterator",
"Promise",
"GeneratorFunction",
"AsyncGeneratorFunction",
"Generator",
"AsyncGenerator",
"AsyncFunction",
};
const std::vector<std::string> SUPPORTED_REFLECTION_TYPES = {
"Reflect",
"Proxy",
};
static inline std::string getObjectTypeName(
jsi::Runtime &rt,
const jsi::Object &object) {
return object.getPropertyAsObject(rt, "constructor")
.getProperty(rt, "name")
.toString(rt)
.utf8(rt);
}
static inline bool isInstanceOf(
jsi::Runtime &rt,
const jsi::Object &object,
const std::string &type) {
return getObjectTypeName(rt, object) == type;
}
static inline bool isInstanceOfAny(
jsi::Runtime &rt,
const jsi::Object &object,
const std::vector<std::string> &supportedTypes) {
auto instanceType = getObjectTypeName(rt, object);
return std::find(
supportedTypes.begin(), supportedTypes.end(), instanceType) !=
supportedTypes.end();
}
JSISerializer::JSISerializer(jsi::Runtime &rt)
: rt_(rt),
visitedNodes_(rt_.global()
.getPropertyAsFunction(rt_, "Set")
.callAsConstructor(rt_)
.asObject(rt_)) {}
std::string JSISerializer::stringifyWithName(const jsi::Object &object) {
std::stringstream ss;
ss << '[' << getObjectTypeName(rt_, object) << ']';
return ss.str();
}
std::string JSISerializer::stringifyArray(const jsi::Array &arr) {
std::stringstream ss;
ss << '[';
for (size_t i = 0, length = arr.size(rt_); i < length; i++) {
jsi::Value element = arr.getValueAtIndex(rt_, i);
ss << stringifyJSIValueRecursively(element);
if (i != length - 1) {
ss << ", ";
}
}
ss << ']';
return ss.str();
}
std::string JSISerializer::stringifyFunction(const jsi::Function &func) {
std::stringstream ss;
auto kind = (func.isHostFunction(rt_) ? "jsi::HostFunction" : "Function");
auto name = func.getProperty(rt_, "name").toString(rt_).utf8(rt_);
name = name.empty() ? "anonymous" : name;
ss << '[' << kind << ' ' << name << ']';
return ss.str();
}
std::string JSISerializer::stringifyHostObject(jsi::HostObject &hostObject) {
int status = -1;
char *hostObjClassName =
abi::__cxa_demangle(typeid(hostObject).name(), NULL, NULL, &status);
if (status != 0) {
return "[jsi::HostObject]";
}
std::stringstream ss;
ss << "[jsi::HostObject(" << hostObjClassName << ")";
std::free(hostObjClassName);
auto props = hostObject.getPropertyNames(rt_);
auto propsCount = props.size();
if (propsCount > 0) {
ss << " {";
auto lastKey = props.back().utf8(rt_);
for (const auto &key : props) {
auto formattedKey = key.utf8(rt_);
auto value = hostObject.get(rt_, key);
ss << '"' << formattedKey << '"' << ": "
<< stringifyJSIValueRecursively(value);
if (formattedKey != lastKey) {
ss << ", ";
}
}
ss << '}';
}
ss << ']';
return ss.str();
}
std::string JSISerializer::stringifyObject(const jsi::Object &object) {
std::stringstream ss;
ss << '{';
auto props = object.getPropertyNames(rt_);
for (size_t i = 0, propsCount = props.size(rt_); i < propsCount; i++) {
jsi::String propName = props.getValueAtIndex(rt_, i).toString(rt_);
ss << '"' << propName.utf8(rt_) << '"' << ": "
<< stringifyJSIValueRecursively(object.getProperty(rt_, propName));
if (i != propsCount - 1) {
ss << ", ";
}
}
ss << '}';
return ss.str();
}
std::string JSISerializer::stringifyError(const jsi::Object &object) {
std::stringstream ss;
ss << '[' << object.getProperty(rt_, "name").toString(rt_).utf8(rt_) << ": "
<< object.getProperty(rt_, "message").toString(rt_).utf8(rt_) << ']';
return ss.str();
}
std::string JSISerializer::stringifySet(const jsi::Object &object) {
std::stringstream ss;
jsi::Function arrayFrom = rt_.global()
.getPropertyAsObject(rt_, "Array")
.getPropertyAsFunction(rt_, "from");
jsi::Object result = arrayFrom.call(rt_, object).asObject(rt_);
if (!result.isArray(rt_)) {
return "[Set]";
}
auto arr = result.asArray(rt_);
ss << "Set {";
for (size_t i = 0, length = arr.size(rt_); i < length; i++) {
ss << stringifyJSIValueRecursively(arr.getValueAtIndex(rt_, i));
if (i != length - 1) {
ss << ", ";
}
}
ss << '}';
return ss.str();
}
std::string JSISerializer::stringifyMap(const jsi::Object &object) {
std::stringstream ss;
jsi::Function arrayFrom = rt_.global()
.getPropertyAsObject(rt_, "Array")
.getPropertyAsFunction(rt_, "from");
jsi::Object result = arrayFrom.call(rt_, object).asObject(rt_);
if (!result.isArray(rt_)) {
return "[Map]";
}
auto arr = result.asArray(rt_);
ss << "Map {";
for (size_t i = 0, length = arr.size(rt_); i < length; i++) {
auto pair = arr.getValueAtIndex(rt_, i).asObject(rt_).getArray(rt_);
auto key = pair.getValueAtIndex(rt_, 0);
auto value = pair.getValueAtIndex(rt_, 1);
ss << stringifyJSIValueRecursively(key) << ": "
<< stringifyJSIValueRecursively(value);
if (i != length - 1) {
ss << ", ";
}
}
ss << '}';
return ss.str();
}
std::string JSISerializer::stringifyRecursiveType(const jsi::Object &object) {
auto type = getObjectTypeName(rt_, object);
if (type == "Array") {
return "[...]";
}
if (type == "Object") {
return "{...}";
}
return "...";
}
std::string JSISerializer::stringifyWithToString(const jsi::Object &object) {
return object.getPropertyAsFunction(rt_, "toString")
.callWithThis(rt_, object)
.toString(rt_)
.utf8(rt_);
}
std::string JSISerializer::stringifyJSIValueRecursively(
const jsi::Value &value,
bool isTopLevel) {
if (value.isBool() || value.isNumber()) {
return value.toString(rt_).utf8(rt_);
}
if (value.isString()) {
return isTopLevel ? value.getString(rt_).utf8(rt_)
: '"' + value.getString(rt_).utf8(rt_) + '"';
}
if (value.isSymbol()) {
return value.getSymbol(rt_).toString(rt_);
}
#if REACT_NATIVE_MINOR_VERSION >= 71
if (value.isBigInt()) {
return value.getBigInt(rt_).toString(rt_).utf8(rt_) + 'n';
}
#endif
if (value.isUndefined()) {
return "undefined";
}
if (value.isNull()) {
return "null";
}
if (value.isObject()) {
jsi::Object object = value.asObject(rt_);
if (hasBeenVisited(object)) {
return stringifyRecursiveType(object);
}
markAsVisited(object);
if (object.isArray(rt_)) {
return stringifyArray(object.getArray(rt_));
}
if (object.isFunction(rt_)) {
return stringifyFunction(object.getFunction(rt_));
}
if (object.isHostObject(rt_)) {
return stringifyHostObject(*object.getHostObject(rt_));
}
if (isInstanceOfAny(rt_, object, SUPPORTED_ERROR_TYPES)) {
return stringifyError(object);
}
if (isInstanceOfAny(rt_, object, SUPPORTED_INDEXED_COLLECTION_TYPES) ||
isInstanceOfAny(rt_, object, SUPPORTED_STRUCTURED_DATA_TYPES) ||
isInstanceOfAny(rt_, object, SUPPORTED_MANAGING_MEMORY_TYPES) ||
isInstanceOfAny(rt_, object, SUPPORTED_ABSTRACTION_OBJECT_TYPES) ||
isInstanceOfAny(rt_, object, SUPPORTED_REFLECTION_TYPES) ||
isInstanceOf(rt_, object, "Intl") ||
isInstanceOf(rt_, object, "WeakMap") ||
isInstanceOf(rt_, object, "WeakSet")) {
// TODO: Consider extending this log info
return stringifyWithName(object);
}
if (isInstanceOf(rt_, object, "Date") ||
isInstanceOf(rt_, object, "RegExp")) {
return stringifyWithToString(object);
}
if (isInstanceOf(rt_, object, "Map")) {
return stringifyMap(object);
}
if (isInstanceOf(rt_, object, "Set")) {
return stringifySet(object);
}
return stringifyObject(object);
}
throw std::runtime_error("[Reanimated] Unsupported value type.");
}
std::string stringifyJSIValue(jsi::Runtime &rt, const jsi::Value &value) {
JSISerializer serializer(rt);
return serializer.stringifyJSIValueRecursively(value, true);
}

View File

@@ -0,0 +1,45 @@
#pragma once
#include <jsi/jsi.h>
#include <string>
#include <vector>
using namespace facebook;
namespace {
class JSISerializer {
public:
explicit JSISerializer(jsi::Runtime &rt);
std::string stringifyJSIValueRecursively(
const jsi::Value &value,
bool isTopLevel = false);
private:
std::string stringifyArray(const jsi::Array &arr);
std::string stringifyFunction(const jsi::Function &func);
std::string stringifyHostObject(jsi::HostObject &hostObject);
std::string stringifyObject(const jsi::Object &object);
std::string stringifyError(const jsi::Object &object);
std::string stringifySet(const jsi::Object &object);
std::string stringifyMap(const jsi::Object &object);
std::string stringifyWithName(const jsi::Object &object);
std::string stringifyWithToString(const jsi::Object &object);
std::string stringifyRecursiveType(const jsi::Object &object);
bool hasBeenVisited(const jsi::Object &object) {
return visitedNodes_.getPropertyAsFunction(rt_, "has")
.callWithThis(rt_, visitedNodes_, object)
.getBool();
}
void markAsVisited(const jsi::Object &object) {
visitedNodes_.getPropertyAsFunction(rt_, "add")
.callWithThis(rt_, visitedNodes_, object);
}
jsi::Runtime &rt_;
jsi::Object visitedNodes_;
};
} // namespace
std::string stringifyJSIValue(jsi::Runtime &rt, const jsi::Value &value);

View File

@@ -0,0 +1,16 @@
#include "JSLogger.h"
#include <memory>
namespace reanimated {
void JSLogger::warnOnJS(const std::string &warning) const {
#ifndef NDEBUG
jsScheduler_->scheduleOnJS([warning](jsi::Runtime &rt) {
auto console = rt.global().getPropertyAsObject(rt, "console");
auto warn = console.getPropertyAsFunction(rt, "warn");
warn.call(rt, jsi::String::createFromUtf8(rt, warning));
});
#endif // NDEBUG
}
} // namespace reanimated

View File

@@ -0,0 +1,20 @@
#pragma once
#include "JSScheduler.h"
#include <memory>
#include <string>
namespace reanimated {
class JSLogger {
public:
explicit JSLogger(const std::shared_ptr<JSScheduler> &jsScheduler)
: jsScheduler_(jsScheduler) {}
void warnOnJS(const std::string &warning) const;
private:
const std::shared_ptr<JSScheduler> jsScheduler_;
};
} // namespace reanimated

View File

@@ -0,0 +1,36 @@
#include "JSScheduler.h"
using namespace facebook;
using namespace react;
namespace reanimated {
JSScheduler::JSScheduler(
jsi::Runtime &rnRuntime,
const std::shared_ptr<CallInvoker> &jsCallInvoker)
: scheduleOnJS([&](Job job) {
jsCallInvoker_->invokeAsync(
[job = std::move(job), &rt = rnRuntime_] { job(rt); });
}),
rnRuntime_(rnRuntime),
jsCallInvoker_(jsCallInvoker) {}
#if REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED)
// With `runtimeExecutor`.
JSScheduler::JSScheduler(
jsi::Runtime &rnRuntime,
RuntimeExecutor runtimeExecutor)
: scheduleOnJS([&](Job job) {
runtimeExecutor_(
[job = std::move(job)](jsi::Runtime &runtime) { job(runtime); });
}),
rnRuntime_(rnRuntime),
runtimeExecutor_(runtimeExecutor) {}
#endif // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED
const std::shared_ptr<CallInvoker> JSScheduler::getJSCallInvoker() const {
assert(
jsCallInvoker_ != nullptr &&
"[Reanimated] Expected jsCallInvoker, got nullptr instead.");
return jsCallInvoker_;
}
} // namespace reanimated

View File

@@ -0,0 +1,42 @@
#pragma once
#include <ReactCommon/CallInvoker.h>
#include <ReactCommon/RuntimeExecutor.h>
#include <jsi/jsi.h>
#include <memory>
#include <utility>
using namespace facebook;
using namespace react;
using Job = std::function<void(jsi::Runtime &rt)>;
namespace reanimated {
class JSScheduler {
public:
// With `jsCallInvoker`.
explicit JSScheduler(
jsi::Runtime &rnRuntime,
const std::shared_ptr<CallInvoker> &jsCallInvoker);
#if REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED)
// With `runtimeExecutor`.
explicit JSScheduler(
jsi::Runtime &rnRuntime,
RuntimeExecutor runtimeExecutor);
#endif // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED
const std::function<void(Job)> scheduleOnJS = nullptr;
const std::shared_ptr<CallInvoker> getJSCallInvoker() const;
protected:
jsi::Runtime &rnRuntime_;
#if REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED)
RuntimeExecutor runtimeExecutor_ = nullptr;
#endif // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED
const std::shared_ptr<CallInvoker> jsCallInvoker_ = nullptr;
};
} // namespace reanimated

View File

@@ -0,0 +1,102 @@
#pragma once
#include <jsi/jsi.h>
#ifdef RCT_NEW_ARCH_ENABLED
#include <react/renderer/core/ReactPrimitives.h>
#endif
#include <string>
#include <utility>
#include <vector>
using namespace facebook;
#ifdef RCT_NEW_ARCH_ENABLED
using namespace react;
#endif
namespace reanimated {
#ifdef RCT_NEW_ARCH_ENABLED
using SynchronouslyUpdateUIPropsFunction =
std::function<void(jsi::Runtime &rt, Tag tag, const jsi::Object &props)>;
using UpdatePropsFunction =
std::function<void(jsi::Runtime &rt, const jsi::Value &operations)>;
using RemoveFromPropsRegistryFunction =
std::function<void(jsi::Runtime &rt, const jsi::Value &viewTags)>;
using ObtainPropFunction = std::function<jsi::Value(
jsi::Runtime &rt,
const jsi::Value &shadowNodeWrapper,
const jsi::Value &propName)>;
using DispatchCommandFunction = std::function<void(
jsi::Runtime &rt,
const jsi::Value &shadowNodeValue,
const jsi::Value &commandNameValue,
const jsi::Value &argsValue)>;
using MeasureFunction = std::function<
jsi::Value(jsi::Runtime &rt, const jsi::Value &shadowNodeValue)>;
#else
using UpdatePropsFunction =
std::function<void(jsi::Runtime &rt, const jsi::Value &operations)>;
using ScrollToFunction = std::function<void(int, double, double, bool)>;
using DispatchCommandFunction = std::function<void(
jsi::Runtime &rt,
const int viewTag,
const jsi::Value &commandNameValue,
const jsi::Value &argsValue)>;
using MeasureFunction =
std::function<std::vector<std::pair<std::string, double>>(int)>;
using ObtainPropFunction =
std::function<jsi::Value(jsi::Runtime &, const int, const jsi::Value &)>;
#endif // RCT_NEW_ARCH_ENABLED
using RequestRenderFunction =
std::function<void(std::function<void(const double)>, jsi::Runtime &)>;
using GetAnimationTimestampFunction = std::function<double(void)>;
using ProgressLayoutAnimationFunction =
std::function<void(jsi::Runtime &, int, jsi::Object, bool)>;
using EndLayoutAnimationFunction = std::function<void(int, bool)>;
using RegisterSensorFunction =
std::function<int(int, int, int, std::function<void(double[], int)>)>;
using UnregisterSensorFunction = std::function<void(int)>;
using SetGestureStateFunction = std::function<void(int, int)>;
using ConfigurePropsFunction = std::function<void(
jsi::Runtime &rt,
const jsi::Value &uiProps,
const jsi::Value &nativeProps)>;
using KeyboardEventSubscribeFunction =
std::function<int(std::function<void(int, int)>, bool)>;
using KeyboardEventUnsubscribeFunction = std::function<void(int)>;
using MaybeFlushUIUpdatesQueueFunction = std::function<void()>;
struct PlatformDepMethodsHolder {
RequestRenderFunction requestRender;
#ifdef RCT_NEW_ARCH_ENABLED
SynchronouslyUpdateUIPropsFunction synchronouslyUpdateUIPropsFunction;
#else
UpdatePropsFunction updatePropsFunction;
ScrollToFunction scrollToFunction;
DispatchCommandFunction dispatchCommandFunction;
MeasureFunction measureFunction;
ConfigurePropsFunction configurePropsFunction;
ObtainPropFunction obtainPropFunction;
#endif
GetAnimationTimestampFunction getAnimationTimestamp;
ProgressLayoutAnimationFunction progressLayoutAnimation;
EndLayoutAnimationFunction endLayoutAnimation;
RegisterSensorFunction registerSensor;
UnregisterSensorFunction unregisterSensor;
SetGestureStateFunction setGestureStateFunction;
KeyboardEventSubscribeFunction subscribeForKeyboardEvents;
KeyboardEventUnsubscribeFunction unsubscribeFromKeyboardEvents;
MaybeFlushUIUpdatesQueueFunction maybeFlushUIUpdatesQueueFunction;
};
} // namespace reanimated

View File

@@ -0,0 +1,11 @@
#pragma once
#ifdef ANDROID
#include "Logger.h"
#include "LoggerInterface.h"
#include "SpeedChecker.h"
#else
#include "Common/cpp/hidden_headers/Logger.h"
#include "Common/cpp/hidden_headers/LoggerInterface.h"
#include "Common/cpp/hidden_headers/SpeedChecker.h"
#endif

View File

@@ -0,0 +1,26 @@
#include "ReanimatedJSIUtils.h"
#include <vector>
using namespace facebook;
namespace reanimated::jsi_utils {
jsi::Array convertStringToArray(
jsi::Runtime &rt,
const std::string &value,
const unsigned int expectedSize) {
std::vector<float> transformMatrixList;
std::istringstream stringStream(value);
std::copy(
std::istream_iterator<float>(stringStream),
std::istream_iterator<float>(),
std::back_inserter(transformMatrixList));
assert(transformMatrixList.size() == expectedSize);
jsi::Array matrix(rt, expectedSize);
for (unsigned int i = 0; i < expectedSize; i++) {
matrix.setValueAtIndex(rt, i, transformMatrixList[i]);
}
return matrix;
}
} // namespace reanimated::jsi_utils

View File

@@ -0,0 +1,176 @@
#pragma once
#include <jsi/jsi.h>
#include <sstream>
#include <string>
#include <tuple>
#include <utility>
using namespace facebook;
namespace reanimated {
namespace jsi_utils {
// `get` functions take a pointer to `jsi::Value` and
// call an appropriate method to cast to the native type
template <typename T>
inline T get(jsi::Runtime &rt, const jsi::Value *value);
template <>
inline double get<double>(jsi::Runtime &, const jsi::Value *value) {
return value->asNumber();
}
template <>
inline int get<int>(jsi::Runtime &, const jsi::Value *value) {
return value->asNumber();
}
template <>
inline bool get<bool>(jsi::Runtime &, const jsi::Value *value) {
if (!value->isBool()) {
throw jsi::JSINativeException("[Reanimated] Expected a boolean.");
}
return value->getBool();
}
template <>
inline jsi::Object get<jsi::Object>(jsi::Runtime &rt, const jsi::Value *value) {
return value->asObject(rt);
}
template <>
inline jsi::Value const &get<jsi::Value const &>(
jsi::Runtime &,
const jsi::Value *value) {
return *value;
}
// `convertArgs` functions take a variadic template parameter of target (C++)
// argument types `Targs` and a `jsi::Value` array `args`, and converts `args`
// to a tuple of typed C++ arguments to be passed to the native implementation.
// This is accomplished by dispatching (at compile time) to the correct
// implementation based on the first type of `Targs`, using SFINAE to select the
// correct specialization, and concatenating with the result of recursion on the
// rest of `Targs`
// BEGIN implementations for `convertArgs` specializations.
// specialization for empty `Targs` - returns an empty tuple
template <typename... Args>
inline std::enable_if_t<(sizeof...(Args) == 0), std::tuple<>> convertArgs(
jsi::Runtime &,
const jsi::Value *) {
return std::make_tuple();
}
// calls `get<First>` on the first argument to retrieve the native type,
// then calls recursively on the rest of `args`
// and returns the concatenation of results
template <typename T, typename... Rest>
inline std::tuple<T, Rest...> convertArgs(
jsi::Runtime &rt,
const jsi::Value *args) {
auto arg = std::tuple<T>(get<T>(rt, args));
auto rest = convertArgs<Rest...>(rt, std::next(args));
return std::tuple_cat(std::move(arg), std::move(rest));
}
// END implementations for `convertArgs` specializations.
// returns a tuple with the result of casting `args` to appropriate
// native C++ types needed to call `function`
template <typename Ret, typename... Args>
std::tuple<Args...> getArgsForFunction(
std::function<Ret(Args...)>,
jsi::Runtime &rt,
const jsi::Value *args,
const size_t count) {
assert(sizeof...(Args) == count);
return convertArgs<Args...>(rt, args);
}
// returns a tuple with the result of casting `args` to appropriate
// native C++ types needed to call `function`,
// passing `rt` as the first argument
template <typename Ret, typename... Args>
std::tuple<jsi::Runtime &, Args...> getArgsForFunction(
std::function<Ret(jsi::Runtime &, Args...)>,
jsi::Runtime &rt,
const jsi::Value *args,
const size_t count) {
assert(sizeof...(Args) == count);
return std::tuple_cat(std::tie(rt), convertArgs<Args...>(rt, args));
}
// calls `function` with `args`
template <typename Ret, typename... Args>
inline jsi::Value apply(
std::function<Ret(Args...)> function,
std::tuple<Args...> args) {
return std::apply(function, std::move(args));
}
// calls void-returning `function` with `args`,
// and returns `undefined`
template <typename... Args>
inline jsi::Value apply(
std::function<void(Args...)> function,
std::tuple<Args...> args) {
std::apply(function, std::move(args));
return jsi::Value::undefined();
}
// returns a function with JSI calling convention
// from a native function `function`
template <typename Fun>
jsi::HostFunctionType createHostFunction(Fun function) {
return [function](
jsi::Runtime &rt,
const jsi::Value &,
const jsi::Value *args,
const size_t count) {
auto argz = getArgsForFunction(function, rt, args, count);
return apply(function, std::move(argz));
};
}
// used to determine if `function<Ret(Args...)>`
// takes `Runtime &` as its first argument
template <typename... Args>
struct takes_runtime {
static constexpr size_t value = 0;
};
// specialization for `function<Ret(Runtime &, Rest...)`
template <typename... Rest>
struct takes_runtime<jsi::Runtime &, Rest...> {
static constexpr size_t value = 1;
};
// creates a JSI compatible function from `function`
// and installs it as a global function named `name`
// in the `rt` JS runtime
template <typename Ret, typename... Args>
void installJsiFunction(
jsi::Runtime &rt,
std::string_view name,
std::function<Ret(Args...)> function) {
auto clb = createHostFunction(function);
auto argsCount = sizeof...(Args) - takes_runtime<Args...>::value;
jsi::Value jsiFunction = jsi::Function::createFromHostFunction(
rt, jsi::PropNameID::forAscii(rt, name.data()), argsCount, clb);
rt.global().setProperty(rt, name.data(), jsiFunction);
}
// this should take care of passing types convertible to `function`
template <typename Fun>
void installJsiFunction(jsi::Runtime &rt, std::string_view name, Fun function) {
installJsiFunction(rt, name, std::function(std::forward<Fun>(function)));
}
jsi::Array convertStringToArray(
jsi::Runtime &rt,
const std::string &value,
const unsigned int expectedSize);
} // namespace jsi_utils
} // namespace reanimated

View File

@@ -0,0 +1,94 @@
#include "ReanimatedVersion.h"
#include <memory>
#include <regex>
#include <string>
#include "JSLogger.h"
#ifdef REANIMATED_VERSION
#define STRINGIZE(x) #x
#define STRINGIZE2(x) STRINGIZE(x)
#define REANIMATED_VERSION_STRING STRINGIZE2(REANIMATED_VERSION)
#endif // REANIMATED_VERSION
using namespace facebook;
namespace reanimated {
std::string getReanimatedCppVersion() {
return std::string(REANIMATED_VERSION_STRING);
}
void injectReanimatedCppVersion(jsi::Runtime &rnRuntime) {
auto version = getReanimatedCppVersion();
rnRuntime.global().setProperty(
rnRuntime,
"_REANIMATED_VERSION_CPP",
jsi::String::createFromUtf8(rnRuntime, version));
}
#ifndef NDEBUG
// This function is pretty much a copy of
// `src/reanimated2/platform-specific/checkVersion.ts`.
bool matchVersion(const std::string &version1, const std::string &version2) {
std::regex pattern("^\\d+\\.\\d+\\.\\d+$");
if (std::regex_match(version1, pattern) &&
std::regex_match(version2, pattern)) {
auto majorPattern = std::regex("^\\d+");
auto major1 = std::regex_search(version1, majorPattern);
auto major2 = std::regex_search(version2, majorPattern);
if (major1 != major2) {
return false;
}
auto minorPattern = std::regex("\\.\\d+\\.");
auto minor1 = std::regex_search(version1, minorPattern);
auto minor2 = std::regex_search(version2, minorPattern);
if (minor1 != minor2) {
return false;
}
return true;
} else {
return version1 == version2;
}
}
void checkJSVersion(
jsi::Runtime &rnRuntime,
const std::shared_ptr<JSLogger> &jsLogger) {
auto cppVersion = getReanimatedCppVersion();
auto maybeJSVersion =
rnRuntime.global().getProperty(rnRuntime, "_REANIMATED_VERSION_JS");
if (maybeJSVersion.isUndefined()) {
jsLogger->warnOnJS(
std::string(
"[Reanimated] C++ side failed to resolve JavaScript code version\n") +
"See `https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#c-side-failed-to-resolve-javascript-code-version` for more details.");
return;
}
auto jsVersion = maybeJSVersion.asString(rnRuntime).utf8(rnRuntime);
if (!matchVersion(cppVersion, jsVersion)) {
jsLogger->warnOnJS(
std::string(
"[Reanimated] Mismatch between C++ code version and JavaScript code version (") +
cppVersion + " vs. " + jsVersion + " respectively).\n" +
"See `https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#mismatch-between-c-code-version-and-javascript-code-version` for more details.");
return;
}
}
#else
void checkJSVersion(
jsi::Runtime &rnRuntime,
const std::shared_ptr<JSLogger> &jsLogger) {
// In release builds we don't check the version, hence
// this function is a NOOP.
}
bool matchVersion(const std::string &version1, const std::string &version2) {
// Stub implementation for release builds.
return true;
}
#endif // NDEBUG
}; // namespace reanimated

View File

@@ -0,0 +1,17 @@
#pragma once
#include <jsi/jsi.h>
#include <memory>
#include <string>
#include "JSLogger.h"
using namespace facebook;
namespace reanimated {
std::string getReanimatedCppVersion();
void injectReanimatedCppVersion(jsi::Runtime &);
bool matchVersion(const std::string &, const std::string &);
void checkJSVersion(jsi::Runtime &, const std::shared_ptr<JSLogger> &);
}; // namespace reanimated

View File

@@ -0,0 +1,71 @@
#pragma once
#ifndef NDEBUG
#include <cxxabi.h>
#include <atomic>
#include <iostream>
#include <string>
#ifdef ANDROID
#include <android/log.h>
#endif
namespace reanimated {
// This is a class that counts how many instances of a different class there
// are. It is meant only to be used with classes that should only have one
// instance.
template <class T>
class SingleInstanceChecker {
public:
SingleInstanceChecker();
~SingleInstanceChecker();
private:
void assertWithMessage(bool condition, std::string message) {
if (!condition) {
#ifdef ANDROID
__android_log_print(
ANDROID_LOG_WARN, "Reanimated", "%s", message.c_str());
#else
std::cerr << "[Reanimated] " << message << std::endl;
#endif
#ifdef IS_REANIMATED_EXAMPLE_APP
assert(false);
#endif
}
}
// A static field will exist separately for every class template.
// This has to be inline for automatic initialization.
inline static std::atomic<int> instanceCount_;
};
template <class T>
SingleInstanceChecker<T>::SingleInstanceChecker() {
int status = 0;
std::string className =
__cxxabiv1::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status);
// Only one instance should exist, but it is possible for two instances
// to co-exist during a reload.
assertWithMessage(
instanceCount_ <= 1,
"[Reanimated] More than one instance of " + className +
" present. This may indicate a memory leak due to a retain cycle.");
instanceCount_++;
}
template <class T>
SingleInstanceChecker<T>::~SingleInstanceChecker() {
instanceCount_--;
}
} // namespace reanimated
#endif // NDEBUG

View File

@@ -0,0 +1,49 @@
#pragma once
#include <condition_variable>
#include <mutex>
#include <queue>
#include <utility>
namespace reanimated {
//
// Copyright (c) 2013 Juan Palacios juan.palacios.puyana@gmail.com
// Subject to the BSD 2-Clause License
// - see < https://opensource.org/license/bsd-2-clause/ >
//
template <typename T>
class ThreadSafeQueue {
public:
T pop() {
std::unique_lock<std::mutex> mlock(mutex_);
while (queue_.empty()) {
cond_.wait(mlock);
}
const auto item = queue_.front();
queue_.pop();
return item;
}
void push(T &&item) {
std::unique_lock<std::mutex> mlock(mutex_);
queue_.push(std::move(item));
mlock.unlock();
cond_.notify_one();
}
bool empty() const {
std::unique_lock<std::mutex> mlock(mutex_);
const auto res = queue_.empty();
mlock.unlock();
cond_.notify_one();
return res;
}
private:
std::queue<T> queue_;
mutable std::mutex mutex_;
mutable std::condition_variable cond_;
};
} // namespace reanimated

View File

@@ -0,0 +1,70 @@
#include "UIRuntimeDecorator.h"
#include "ReanimatedJSIUtils.h"
namespace reanimated {
void UIRuntimeDecorator::decorate(
jsi::Runtime &uiRuntime,
#ifdef RCT_NEW_ARCH_ENABLED
const RemoveFromPropsRegistryFunction removeFromPropsRegistry,
#else
const ScrollToFunction scrollTo,
#endif
const ObtainPropFunction obtainPropFunction,
const UpdatePropsFunction updateProps,
const MeasureFunction measure,
const DispatchCommandFunction dispatchCommand,
const RequestAnimationFrameFunction requestAnimationFrame,
const GetAnimationTimestampFunction getAnimationTimestamp,
const SetGestureStateFunction setGestureState,
const ProgressLayoutAnimationFunction progressLayoutAnimation,
const EndLayoutAnimationFunction endLayoutAnimation,
const MaybeFlushUIUpdatesQueueFunction maybeFlushUIUpdatesQueue) {
uiRuntime.global().setProperty(uiRuntime, "_UI", true);
#ifdef RCT_NEW_ARCH_ENABLED
jsi_utils::installJsiFunction(uiRuntime, "_updatePropsFabric", updateProps);
jsi_utils::installJsiFunction(
uiRuntime, "_removeFromPropsRegistry", removeFromPropsRegistry);
jsi_utils::installJsiFunction(
uiRuntime, "_dispatchCommandFabric", dispatchCommand);
jsi_utils::installJsiFunction(uiRuntime, "_measureFabric", measure);
#else
jsi_utils::installJsiFunction(uiRuntime, "_updatePropsPaper", updateProps);
jsi_utils::installJsiFunction(
uiRuntime, "_dispatchCommandPaper", dispatchCommand);
jsi_utils::installJsiFunction(uiRuntime, "_scrollToPaper", scrollTo);
jsi_utils::installJsiFunction(
uiRuntime,
"_measurePaper",
[measure](jsi::Runtime &rt, int viewTag) -> jsi::Value {
auto result = measure(viewTag);
jsi::Object resultObject(rt);
for (const auto &item : result) {
resultObject.setProperty(rt, item.first.c_str(), item.second);
}
return resultObject;
});
jsi_utils::installJsiFunction(
uiRuntime, "_obtainPropPaper", obtainPropFunction);
#endif // RCT_NEW_ARCH_ENABLED
jsi_utils::installJsiFunction(
uiRuntime, "requestAnimationFrame", requestAnimationFrame);
jsi_utils::installJsiFunction(
uiRuntime, "_getAnimationTimestamp", getAnimationTimestamp);
jsi_utils::installJsiFunction(
uiRuntime, "_notifyAboutProgress", progressLayoutAnimation);
jsi_utils::installJsiFunction(
uiRuntime, "_notifyAboutEnd", endLayoutAnimation);
jsi_utils::installJsiFunction(uiRuntime, "_setGestureState", setGestureState);
jsi_utils::installJsiFunction(
uiRuntime, "_maybeFlushUIUpdatesQueue", maybeFlushUIUpdatesQueue);
jsi_utils::installJsiFunction(
uiRuntime, "_obtainPropFabric", obtainPropFunction);
}
} // namespace reanimated

View File

@@ -0,0 +1,35 @@
#pragma once
#include <jsi/jsi.h>
#include "PlatformDepMethodsHolder.h"
using namespace facebook;
namespace reanimated {
using RequestAnimationFrameFunction =
std::function<void(jsi::Runtime &, const jsi::Value &)>;
class UIRuntimeDecorator {
public:
static void decorate(
jsi::Runtime &uiRuntime,
#ifdef RCT_NEW_ARCH_ENABLED
const RemoveFromPropsRegistryFunction removeFromPropsRegistry,
#else
const ScrollToFunction scrollTo,
#endif
const ObtainPropFunction obtainPropFunction,
const UpdatePropsFunction updateProps,
const MeasureFunction measure,
const DispatchCommandFunction dispatchCommand,
const RequestAnimationFrameFunction requestAnimationFrame,
const GetAnimationTimestampFunction getAnimationTimestamp,
const SetGestureStateFunction setGestureState,
const ProgressLayoutAnimationFunction progressLayoutAnimation,
const EndLayoutAnimationFunction endLayoutAnimation,
const MaybeFlushUIUpdatesQueueFunction maybeFlushUIUpdatesQueue);
};
} // namespace reanimated

View File

@@ -0,0 +1,20 @@
#include "UIScheduler.h"
#include "ReanimatedRuntime.h"
#include <utility>
namespace reanimated {
void UIScheduler::scheduleOnUI(std::function<void()> job) {
uiJobs_.push(std::move(job));
}
void UIScheduler::triggerUI() {
scheduledOnUI_ = false;
while (!uiJobs_.empty()) {
const auto job = uiJobs_.pop();
job();
}
}
} // namespace reanimated

View File

@@ -0,0 +1,23 @@
#pragma once
#include <ReactCommon/CallInvoker.h>
#include <atomic>
#include <memory>
#include "ThreadSafeQueue.h"
namespace reanimated {
class UIScheduler {
public:
virtual void scheduleOnUI(std::function<void()> job);
virtual void triggerUI();
virtual ~UIScheduler() = default;
protected:
std::atomic<bool> scheduledOnUI_{false};
ThreadSafeQueue<std::function<void()>> uiJobs_;
};
} // namespace reanimated

View File

@@ -0,0 +1,29 @@
#include "WorkletEventHandler.h"
namespace reanimated {
void WorkletEventHandler::process(
const std::shared_ptr<WorkletRuntime> &workletRuntime,
const double eventTimestamp,
const jsi::Value &eventValue) const {
workletRuntime->runGuarded(
handlerFunction_, jsi::Value(eventTimestamp), eventValue);
}
uint64_t WorkletEventHandler::getHandlerId() const {
return handlerId_;
}
const std::string &WorkletEventHandler::getEventName() const {
return eventName_;
}
uint64_t WorkletEventHandler::getEmitterReactTag() const {
return emitterReactTag_;
}
bool WorkletEventHandler::shouldIgnoreEmitterReactTag() const {
return emitterReactTag_ == -1;
}
} // namespace reanimated

View File

@@ -0,0 +1,41 @@
#pragma once
#include <jsi/jsi.h>
#include <memory>
#include <string>
#include <utility>
#include "Shareables.h"
#include "WorkletRuntime.h"
using namespace facebook;
namespace reanimated {
class WorkletEventHandler {
const uint64_t handlerId_;
const uint64_t emitterReactTag_;
const std::string eventName_;
const std::shared_ptr<ShareableWorklet> handlerFunction_;
public:
WorkletEventHandler(
const uint64_t handlerId,
const std::string &eventName,
const uint64_t emitterReactTag,
const std::shared_ptr<ShareableWorklet> &handlerFunction)
: handlerId_(handlerId),
emitterReactTag_(emitterReactTag),
eventName_(eventName),
handlerFunction_(handlerFunction) {}
void process(
const std::shared_ptr<WorkletRuntime> &workletRuntime,
double eventTimestamp,
const jsi::Value &eventValue) const;
uint64_t getHandlerId() const;
const std::string &getEventName() const;
uint64_t getEmitterReactTag() const;
bool shouldIgnoreEmitterReactTag() const;
};
} // namespace reanimated