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,27 @@
/*
* 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 <jsireact/JSIExecutor.h>
namespace facebook::react {
class JSCExecutorFactory : public JSExecutorFactory {
public:
explicit JSCExecutorFactory(JSIExecutor::RuntimeInstaller runtimeInstaller)
: runtimeInstaller_(std::move(runtimeInstaller)) {}
std::unique_ptr<JSExecutor> createJSExecutor(
std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> jsQueue) override;
private:
JSIExecutor::RuntimeInstaller runtimeInstaller_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,24 @@
/*
* 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 "JSCExecutorFactory.h"
#import <jsc/JSCRuntime.h>
#import <memory>
namespace facebook::react {
std::unique_ptr<JSExecutor> JSCExecutorFactory::createJSExecutor(
std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> __unused jsQueue)
{
return std::make_unique<JSIExecutor>(
facebook::jsc::makeJSCRuntime(), delegate, JSIExecutor::defaultTimeoutInvoker, runtimeInstaller_);
}
} // namespace facebook::react

View File

@@ -0,0 +1,40 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
#include <cxxreact/JSBigString.h>
namespace facebook::react {
class NSDataBigString : public JSBigString {
public:
// The NSData passed in must be be null-terminated.
NSDataBigString(NSData *data);
// The ASCII optimization is not enabled on iOS
bool isAscii() const override
{
return false;
}
const char *c_str() const override
{
return (const char *)[m_data bytes];
}
size_t size() const override
{
return m_length;
}
private:
NSData *m_data;
size_t m_length;
};
} // namespace facebook::react

View File

@@ -0,0 +1,41 @@
/*
* 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.
*/
#import "NSDataBigString.h"
namespace facebook::react {
static NSData *ensureNullTerminated(NSData *source)
{
if (!source || source.length == 0) {
return nil;
}
NSUInteger sourceLength = source.length;
unsigned char lastByte;
[source getBytes:&lastByte range:NSMakeRange(sourceLength - 1, 1)];
// TODO: bundles from the packager should always include a NULL byte
// or we should we relax this requirement and only read as much from the
// buffer as length indicates
if (lastByte == '\0') {
return source;
} else {
NSMutableData *data = [source mutableCopy];
unsigned char nullByte = '\0';
[data appendBytes:&nullByte length:1];
return data;
}
}
NSDataBigString::NSDataBigString(NSData *data)
{
m_length = [data length];
m_data = ensureNullTerminated(data);
}
} // namespace facebook::react

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
/*
* 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>
#import <React/RCTBridgeDelegate.h>
namespace facebook::react {
class JSExecutorFactory;
} // namespace facebook::react
// This is a separate class so non-C++ implementations don't need to
// take a C++ dependency.
@protocol RCTCxxBridgeDelegate <RCTBridgeDelegate>
/**
* In the RCTCxxBridge, if this method is implemented, return a
* ExecutorFactory instance which can be used to create the executor.
* If not implemented, or returns an empty pointer, JSIExecutorFactory
* will be used with a JSCRuntime.
*/
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge;
@end

View File

@@ -0,0 +1,21 @@
/*
* 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 <jsireact/JSIExecutor.h>
namespace facebook::react {
/**
* Creates a lambda used to bind a JSIRuntime in the context of
* Apple platforms, such as console logging, performance metrics, etc.
*/
JSIExecutor::RuntimeInstaller RCTJSIExecutorRuntimeInstaller(
JSIExecutor::RuntimeInstaller runtimeInstallerToWrap);
} // namespace facebook::react

View File

@@ -0,0 +1,30 @@
/*
* 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 "RCTJSIExecutorRuntimeInstaller.h"
#import <React/RCTLog.h>
#include <chrono>
namespace facebook::react {
JSIExecutor::RuntimeInstaller RCTJSIExecutorRuntimeInstaller(JSIExecutor::RuntimeInstaller runtimeInstallerToWrap)
{
return [runtimeInstaller = runtimeInstallerToWrap](jsi::Runtime &runtime) {
Logger iosLoggingBinder = [](const std::string &message, unsigned int logLevel) {
_RCTLogJavaScriptInternal(static_cast<RCTLogLevel>(logLevel), [NSString stringWithUTF8String:message.c_str()]);
};
bindNativeLogger(runtime, iosLoggingBinder);
// Wrap over the original runtimeInstaller
if (runtimeInstaller) {
runtimeInstaller(runtime);
}
};
}
} // namespace facebook::react

View File

@@ -0,0 +1,39 @@
/*
* 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.
*/
#import <memory>
#import <string>
#import <Foundation/Foundation.h>
#import <React/RCTJavaScriptExecutor.h>
#import <cxxreact/MessageQueueThread.h>
#import <atomic>
namespace facebook::react {
class RCTMessageThread : public MessageQueueThread,
public std::enable_shared_from_this<RCTMessageThread> {
public:
RCTMessageThread(NSRunLoop* runLoop, RCTJavaScriptCompleteBlock errorBlock);
~RCTMessageThread() override;
void runOnQueue(std::function<void()>&&) override;
void runOnQueueSync(std::function<void()>&&) override;
void quitSynchronous() override;
void setRunLoop(NSRunLoop* runLoop);
private:
void tryFunc(const std::function<void()>& func);
void runAsync(std::function<void()> func);
void runSync(std::function<void()> func);
CFRunLoopRef m_cfRunLoop;
RCTJavaScriptCompleteBlock m_errorBlock;
std::atomic_bool m_shutdown;
};
} // namespace facebook::react

View File

@@ -0,0 +1,112 @@
/*
* 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 "RCTMessageThread.h"
#include <condition_variable>
#include <mutex>
#import <React/RCTCxxUtils.h>
#import <React/RCTUtils.h>
// A note about the implementation: This class is not used
// generically. It's a thin wrapper around a run loop which
// implements a C++ interface, for use by the C++ xplat bridge code.
// This means it can make certain non-generic assumptions. In
// particular, the sync functions are only used for bridge setup and
// teardown, and quitSynchronous is guaranteed to be called.
namespace facebook::react {
RCTMessageThread::RCTMessageThread(NSRunLoop *runLoop, RCTJavaScriptCompleteBlock errorBlock)
: m_cfRunLoop([runLoop getCFRunLoop]), m_errorBlock(errorBlock), m_shutdown(false)
{
CFRetain(m_cfRunLoop);
}
RCTMessageThread::~RCTMessageThread()
{
RCTAssert(m_shutdown, @"RCTMessageThread: quitSynchronous() not called before destructor");
CFRelease(m_cfRunLoop);
}
// This is analogous to dispatch_async
void RCTMessageThread::runAsync(std::function<void()> func)
{
CFRunLoopPerformBlock(m_cfRunLoop, kCFRunLoopCommonModes, ^{
// Create an autorelease pool each run loop to prevent memory footprint from growing too large, which can lead to
// performance problems.
@autoreleasepool {
func();
}
});
CFRunLoopWakeUp(m_cfRunLoop);
}
// This is analogous to dispatch_sync
void RCTMessageThread::runSync(std::function<void()> func)
{
if (m_cfRunLoop == CFRunLoopGetCurrent()) {
func();
return;
}
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
runAsync([func = std::make_shared<std::function<void()>>(std::move(func)), &sema] {
(*func)();
dispatch_semaphore_signal(sema);
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
void RCTMessageThread::tryFunc(const std::function<void()> &func)
{
NSError *error = tryAndReturnError(func);
if (error) {
m_errorBlock(error);
}
}
void RCTMessageThread::runOnQueue(std::function<void()> &&func)
{
if (m_shutdown) {
return;
}
runAsync([sharedThis = shared_from_this(), func = std::make_shared<std::function<void()>>(std::move(func))] {
if (!sharedThis->m_shutdown) {
sharedThis->tryFunc(*func);
}
});
}
void RCTMessageThread::runOnQueueSync(std::function<void()> &&func)
{
if (m_shutdown) {
return;
}
runSync([sharedThis = shared_from_this(), func = std::move(func)] {
if (!sharedThis->m_shutdown) {
sharedThis->tryFunc(func);
}
});
}
void RCTMessageThread::quitSynchronous()
{
m_shutdown = true;
runSync([] {});
CFRunLoopStop(m_cfRunLoop);
}
void RCTMessageThread::setRunLoop(NSRunLoop *runLoop)
{
CFRelease(m_cfRunLoop);
m_cfRunLoop = [runLoop getCFRunLoop];
CFRetain(m_cfRunLoop);
}
} // namespace facebook::react

View File

@@ -0,0 +1,31 @@
/*
* 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 <functional>
#include <memory>
#import <React/RCTDefines.h>
#import <React/RCTJavaScriptExecutor.h>
#import <cxxreact/JSExecutor.h>
namespace facebook::react {
class RCTObjcExecutorFactory : public JSExecutorFactory {
public:
RCTObjcExecutorFactory(
id<RCTJavaScriptExecutor> jse,
RCTJavaScriptCompleteBlock errorBlock);
std::unique_ptr<JSExecutor> createJSExecutor(
std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> jsQueue) override;
private:
id<RCTJavaScriptExecutor> m_jse;
RCTJavaScriptCompleteBlock m_errorBlock;
};
} // 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.
*/
#import "RCTObjcExecutor.h"
#import <React/RCTCxxUtils.h>
#import <React/RCTFollyConvert.h>
#import <React/RCTJavaScriptExecutor.h>
#import <React/RCTLog.h>
#import <React/RCTProfile.h>
#import <React/RCTUtils.h>
#import <cxxreact/JSBigString.h>
#import <cxxreact/JSExecutor.h>
#import <cxxreact/MessageQueueThread.h>
#import <cxxreact/ModuleRegistry.h>
#import <cxxreact/RAMBundleRegistry.h>
#import <folly/json.h>
namespace facebook::react {
namespace {
class JSEException : public std::runtime_error {
public:
JSEException(NSError *error) : runtime_error([[error description] UTF8String]) {}
};
class RCTObjcExecutor : public JSExecutor {
public:
RCTObjcExecutor(
id<RCTJavaScriptExecutor> jse,
RCTJavaScriptCompleteBlock errorBlock,
std::shared_ptr<MessageQueueThread> jsThread,
std::shared_ptr<ExecutorDelegate> delegate)
: m_jse(jse), m_errorBlock(errorBlock), m_delegate(std::move(delegate)), m_jsThread(std::move(jsThread))
{
m_jsCallback = ^(id json, NSError *error) {
if (error) {
// Do not use "m_errorBlock" here as the bridge might be in the middle
// of invalidation as a result of error handling and "this" can be
// already deallocated.
errorBlock(error);
return;
}
m_jsThread->runOnQueue(
[this, json] { m_delegate->callNativeModules(*this, convertIdToFollyDynamic(json), true); });
};
// Synchronously initialize the executor
[jse setUp];
folly::dynamic nativeModuleConfig = folly::dynamic::array;
auto moduleRegistry = m_delegate->getModuleRegistry();
for (const auto &name : moduleRegistry->moduleNames()) {
auto config = moduleRegistry->getConfig(name);
nativeModuleConfig.push_back(config ? config->config : nullptr);
}
folly::dynamic config = folly::dynamic::object("remoteModuleConfig", std::move(nativeModuleConfig));
setGlobalVariable("__fbBatchedBridgeConfig", std::make_unique<JSBigStdString>(folly::toJson(config)));
}
void initializeRuntime() override
{
// We do nothing here since initialization is done in the constructor
}
void loadBundle(std::unique_ptr<const JSBigString> script, std::string sourceURL) override
{
RCTProfileBeginFlowEvent();
[m_jse executeApplicationScript:[NSData dataWithBytes:script->c_str() length:script->size()]
sourceURL:[[NSURL alloc] initWithString:@(sourceURL.c_str())]
onComplete:^(NSError *error) {
RCTProfileEndFlowEvent();
if (error) {
m_errorBlock(error);
return;
}
[m_jse flushedQueue:m_jsCallback];
}];
}
void setBundleRegistry(std::unique_ptr<RAMBundleRegistry>) override
{
RCTAssert(NO, @"RAM bundles are not supported in RCTObjcExecutor");
}
void registerBundle(uint32_t __unused bundleId, const std::string __unused &bundlePath) override
{
RCTAssert(NO, @"RAM bundles are not supported in RCTObjcExecutor");
}
void callFunction(const std::string &module, const std::string &method, const folly::dynamic &arguments) override
{
[m_jse callFunctionOnModule:@(module.c_str())
method:@(method.c_str())
arguments:convertFollyDynamicToId(arguments)
callback:m_jsCallback];
}
void invokeCallback(double callbackId, const folly::dynamic &arguments) override
{
[m_jse invokeCallbackID:@(callbackId) arguments:convertFollyDynamicToId(arguments) callback:m_jsCallback];
}
virtual void setGlobalVariable(std::string propName, std::unique_ptr<const JSBigString> jsonValue) override
{
[m_jse injectJSONText:@(jsonValue->c_str()) asGlobalObjectNamed:@(propName.c_str()) callback:m_errorBlock];
}
virtual std::string getDescription() override
{
return [NSStringFromClass([m_jse class]) UTF8String];
}
private:
id<RCTJavaScriptExecutor> m_jse;
RCTJavaScriptCompleteBlock m_errorBlock;
std::shared_ptr<ExecutorDelegate> m_delegate;
std::shared_ptr<MessageQueueThread> m_jsThread;
RCTJavaScriptCallback m_jsCallback;
};
}
RCTObjcExecutorFactory::RCTObjcExecutorFactory(id<RCTJavaScriptExecutor> jse, RCTJavaScriptCompleteBlock errorBlock)
: m_jse(jse), m_errorBlock(errorBlock)
{
}
std::unique_ptr<JSExecutor> RCTObjcExecutorFactory::createJSExecutor(
std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> jsQueue)
{
return std::unique_ptr<JSExecutor>(new RCTObjcExecutor(m_jse, m_errorBlock, jsQueue, delegate));
}
} // namespace facebook::react