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

345 lines
11 KiB
Plaintext

#import <React/RCTBridge+Private.h>
#ifdef RCT_NEW_ARCH_ENABLED
#import <React/RCTFabricSurface.h>
#import <React/RCTScheduler.h>
#import <React/RCTSurface.h>
#import <React/RCTSurfacePresenter.h>
#import <React/RCTSurfacePresenterBridgeAdapter.h>
#import <React/RCTSurfaceView.h>
#if REACT_NATIVE_MINOR_VERSION < 73
#import <React/RCTRuntimeExecutorFromBridge.h>
#endif // REACT_NATIVE_MINOR_VERSION < 73
#endif // RCT_NEW_ARCH_ENABLED
#ifdef RCT_NEW_ARCH_ENABLED
#import <RNReanimated/REAInitializerRCTFabricSurface.h>
#endif // RCT_NEW_ARCH_ENABLED
#import <RNReanimated/NativeProxy.h>
#import <RNReanimated/REAModule.h>
#import <RNReanimated/REANodesManager.h>
#import <RNReanimated/REAUIKit.h>
#import <RNReanimated/RNRuntimeDecorator.h>
#import <RNReanimated/ReanimatedJSIUtils.h>
#import <RNReanimated/SingleInstanceChecker.h>
#import <RNReanimated/WorkletRuntime.h>
#import <RNReanimated/WorkletRuntimeCollector.h>
#if __has_include(<UIKit/UIAccessibility.h>)
#import <UIKit/UIAccessibility.h>
#endif // __has_include(<UIKit/UIAccessibility.h>)
using namespace facebook::react;
using namespace reanimated;
@interface RCTBridge (JSIRuntime)
- (void *)runtime;
@end
@interface RCTBridge (RCTTurboModule)
- (std::shared_ptr<facebook::react::CallInvoker>)jsCallInvoker;
- (void)_tryAndHandleError:(dispatch_block_t)block;
@end
#ifdef RCT_NEW_ARCH_ENABLED
static __strong REAInitializerRCTFabricSurface *reaSurface;
#else
typedef void (^AnimatedOperation)(REANodesManager *nodesManager);
#endif // RCT_NEW_ARCH_ENABLED
@implementation REAModule {
#ifdef RCT_NEW_ARCH_ENABLED
__weak RCTSurfacePresenter *_surfacePresenter;
std::weak_ptr<NativeReanimatedModule> weakNativeReanimatedModule_;
#else
NSMutableArray<AnimatedOperation> *_operations;
#endif // RCT_NEW_ARCH_ENABLED
#ifndef NDEBUG
SingleInstanceChecker<REAModule> singleInstanceChecker_;
#endif // NDEBUG
bool hasListeners;
bool _isBridgeless;
}
@synthesize moduleRegistry = _moduleRegistry;
#if REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED)
@synthesize runtimeExecutor = _runtimeExecutor;
#endif // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED)
RCT_EXPORT_MODULE(ReanimatedModule);
#ifdef RCT_NEW_ARCH_ENABLED
+ (BOOL)requiresMainQueueSetup
{
return YES;
}
#endif // RCT_NEW_ARCH_ENABLED
- (void)invalidate
{
#ifdef RCT_NEW_ARCH_ENABLED
[[NSNotificationCenter defaultCenter] removeObserver:self];
#endif // RCT_NEW_ARCH_ENABLED
[_nodesManager invalidate];
[super invalidate];
}
- (dispatch_queue_t)methodQueue
{
// This module needs to be on the same queue as the UIManager to avoid
// having to lock `_operations` and `_preOperations` since `uiManagerWillPerformMounting`
// will be called from that queue.
return RCTGetUIManagerQueue();
}
#ifdef RCT_NEW_ARCH_ENABLED
- (std::shared_ptr<UIManager>)getUIManager
{
RCTScheduler *scheduler = [_surfacePresenter scheduler];
return scheduler.uiManager;
}
- (void)injectDependencies:(jsi::Runtime &)runtime
{
const auto &uiManager = [self getUIManager];
react_native_assert(uiManager.get() != nil);
if (auto nativeReanimatedModule = weakNativeReanimatedModule_.lock()) {
nativeReanimatedModule->initializeFabric(uiManager);
}
}
#pragma mark-- Initialize
- (void)installReanimatedAfterReload
{
// called from REAInitializerRCTFabricSurface::start
__weak __typeof__(self) weakSelf = self;
_surfacePresenter = self.bridge.surfacePresenter;
[_nodesManager setSurfacePresenter:_surfacePresenter];
// to avoid deadlock we can't use Executor from React Native
// but we can create own and use it because initialization is already synchronized
react_native_assert(self.bridge != nil);
RCTRuntimeExecutorFromBridge(self.bridge)(^(jsi::Runtime &runtime) {
if (__typeof__(self) strongSelf = weakSelf) {
[strongSelf injectDependencies:runtime];
}
});
}
- (void)handleJavaScriptDidLoadNotification:(NSNotification *)notification
{
[self attachReactEventListener];
}
- (void)attachReactEventListener
{
RCTScheduler *scheduler = [_surfacePresenter scheduler];
__weak __typeof__(self) weakSelf = self;
_surfacePresenter.runtimeExecutor(^(jsi::Runtime &runtime) {
__typeof__(self) strongSelf = weakSelf;
if (strongSelf == nil) {
return;
}
if (auto nativeReanimatedModule = strongSelf->weakNativeReanimatedModule_.lock()) {
auto eventListener =
std::make_shared<facebook::react::EventListener>([nativeReanimatedModule](const RawEvent &rawEvent) {
if (!RCTIsMainQueue()) {
// event listener called on the JS thread, let's ignore this event
// as we cannot safely access worklet runtime here
// and also we don't care about topLayout events
return false;
}
return nativeReanimatedModule->handleRawEvent(rawEvent, CACurrentMediaTime() * 1000);
});
[scheduler addEventListener:eventListener];
}
});
}
#pragma mark-- Bridgeless methods
/*
* Taken from RCTNativeAnimatedTurboModule:
* This selector is invoked via BridgelessTurboModuleSetup.
*/
- (void)setSurfacePresenter:(id<RCTSurfacePresenterStub>)surfacePresenter
{
_surfacePresenter = surfacePresenter;
_isBridgeless = true;
}
- (void)setBridge:(RCTBridge *)bridge
{
[super setBridge:bridge];
// only within the first loading `self.bridge.surfacePresenter` exists
// during the reload `self.bridge.surfacePresenter` is null
if (self.bridge.surfacePresenter) {
_surfacePresenter = self.bridge.surfacePresenter;
}
[self setReaSurfacePresenter];
_nodesManager = [[REANodesManager alloc] initWithModule:self bridge:bridge surfacePresenter:_surfacePresenter];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleJavaScriptDidLoadNotification:)
name:RCTJavaScriptDidLoadNotification
object:nil];
[[self.moduleRegistry moduleForName:"EventDispatcher"] addDispatchObserver:self];
}
- (void)setReaSurfacePresenter
{
if (reaSurface == nil) {
// we need only one instance because SurfacePresenter is the same during the application lifetime
reaSurface = [[REAInitializerRCTFabricSurface alloc] init];
[_surfacePresenter registerSurface:reaSurface];
}
reaSurface.reaModule = self;
}
#else // RCT_NEW_ARCH_ENABLED
- (void)setBridge:(RCTBridge *)bridge
{
[super setBridge:bridge];
_nodesManager = [[REANodesManager alloc] initWithModule:self uiManager:self.bridge.uiManager];
_operations = [NSMutableArray new];
[bridge.uiManager.observerCoordinator addObserver:self];
_animationsManager = [[REAAnimationsManager alloc] initWithUIManager:bridge.uiManager];
}
#pragma mark-- Batch handling
- (void)addOperationBlock:(AnimatedOperation)operation
{
[_operations addObject:operation];
}
#pragma mark - RCTUIManagerObserver
- (void)uiManagerWillPerformMounting:(RCTUIManager *)uiManager
{
[_nodesManager maybeFlushUpdateBuffer];
if (_operations.count == 0) {
return;
}
NSArray<AnimatedOperation> *operations = _operations;
_operations = [NSMutableArray new];
REANodesManager *nodesManager = _nodesManager;
[uiManager
addUIBlock:^(__unused RCTUIManager *manager, __unused NSDictionary<NSNumber *, REAUIView *> *viewRegistry) {
for (AnimatedOperation operation in operations) {
operation(nodesManager);
}
[nodesManager operationsBatchDidComplete];
}];
}
#endif // RCT_NEW_ARCH_ENABLED
#pragma mark-- Events
- (NSArray<NSString *> *)supportedEvents
{
return @[ @"onReanimatedCall", @"onReanimatedPropsChange" ];
}
- (void)eventDispatcherWillDispatchEvent:(id<RCTEvent>)event
{
// Events can be dispatched from any queue
[_nodesManager dispatchEvent:event];
}
- (void)startObserving
{
hasListeners = YES;
}
- (void)stopObserving
{
hasListeners = NO;
}
- (void)sendEventWithName:(NSString *)eventName body:(id)body
{
if (hasListeners) {
[super sendEventWithName:eventName body:body];
}
}
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(installTurboModule : (nonnull NSString *)valueUnpackerCode)
{
if (_isBridgeless) {
#if REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED)
RCTCxxBridge *cxxBridge = (RCTCxxBridge *)self.bridge;
auto &rnRuntime = *(jsi::Runtime *)cxxBridge.runtime;
auto executorFunction = ([executor = _runtimeExecutor](std::function<void(jsi::Runtime & runtime)> &&callback) {
// Convert to Objective-C block so it can be captured properly.
__block auto callbackBlock = callback;
[executor execute:^(jsi::Runtime &runtime) {
callbackBlock(runtime);
}];
});
auto nativeReanimatedModule = reanimated::createReanimatedModuleBridgeless(
_moduleRegistry, rnRuntime, std::string([valueUnpackerCode UTF8String]), executorFunction);
[self attachReactEventListener];
[self commonInit:nativeReanimatedModule withRnRuntime:rnRuntime];
#else // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED)
[NSException raise:@"Missing bridge" format:@"[Reanimated] Failed to obtain the bridge."];
#endif // REACT_NATIVE_MINOR_VERSION >= 74 && defined(RCT_NEW_ARCH_ENABLED)
} else {
facebook::jsi::Runtime *jsiRuntime = [self.bridge respondsToSelector:@selector(runtime)]
? reinterpret_cast<facebook::jsi::Runtime *>(self.bridge.runtime)
: nullptr;
if (jsiRuntime) {
auto nativeReanimatedModule = reanimated::createReanimatedModule(
self.bridge, self.bridge.jsCallInvoker, std::string([valueUnpackerCode UTF8String]));
jsi::Runtime &rnRuntime = *jsiRuntime;
[self commonInit:nativeReanimatedModule withRnRuntime:rnRuntime];
}
}
return @YES;
}
#ifdef RCT_NEW_ARCH_ENABLED
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
(const facebook::react::ObjCTurboModule::InitParams &)params
{
return std::make_shared<facebook::react::NativeReanimatedModuleSpecJSI>(params);
}
#endif // RCT_NEW_ARCH_ENABLED
- (void)commonInit:(std::shared_ptr<NativeReanimatedModule>)nativeReanimatedModule
withRnRuntime:(jsi::Runtime &)rnRuntime
{
#if __has_include(<UIKit/UIAccessibility.h>)
auto isReducedMotion = UIAccessibilityIsReduceMotionEnabled();
#else
auto isReducedMotion = NSWorkspace.sharedWorkspace.accessibilityDisplayShouldReduceMotion;
#endif
WorkletRuntimeCollector::install(rnRuntime);
RNRuntimeDecorator::decorate(rnRuntime, nativeReanimatedModule, isReducedMotion);
#ifdef RCT_NEW_ARCH_ENABLED
weakNativeReanimatedModule_ = nativeReanimatedModule;
if (self->_surfacePresenter != nil) {
// reload, uiManager is null right now, we need to wait for `installReanimatedAfterReload`
[self injectDependencies:rnRuntime];
}
#endif // RCT_NEW_ARCH_ENABLED
}
@end