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

201 lines
5.8 KiB
Plaintext

/*
* 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 <React/RCTSpringAnimation.h>
#import <UIKit/UIKit.h>
#import <React/RCTConvert.h>
#import <React/RCTDefines.h>
#import <React/RCTAnimationUtils.h>
#import <React/RCTValueAnimatedNode.h>
@interface RCTSpringAnimation ()
@property (nonatomic, strong) NSNumber *animationId;
@property (nonatomic, strong) RCTValueAnimatedNode *valueNode;
@property (nonatomic, assign) BOOL animationHasBegun;
@property (nonatomic, assign) BOOL animationHasFinished;
@end
const NSTimeInterval MAX_DELTA_TIME = 0.064;
@implementation RCTSpringAnimation {
CGFloat _toValue;
CGFloat _fromValue;
BOOL _overshootClamping;
CGFloat _restDisplacementThreshold;
CGFloat _restSpeedThreshold;
CGFloat _stiffness;
CGFloat _damping;
CGFloat _mass;
CGFloat _initialVelocity;
NSTimeInterval _animationStartTime;
NSTimeInterval _animationCurrentTime;
RCTResponseSenderBlock _callback;
CGFloat _lastPosition;
CGFloat _lastVelocity;
NSInteger _iterations;
NSInteger _currentLoop;
NSTimeInterval _t; // Current time (startTime + dt)
}
- (instancetype)initWithId:(NSNumber *)animationId
config:(NSDictionary *)config
forNode:(RCTValueAnimatedNode *)valueNode
callBack:(nullable RCTResponseSenderBlock)callback
{
if ((self = [super init])) {
_animationId = animationId;
_lastPosition = valueNode.value;
_valueNode = valueNode;
_lastVelocity = [RCTConvert CGFloat:config[@"initialVelocity"]];
_callback = [callback copy];
[self resetAnimationConfig:config];
}
return self;
}
- (void)resetAnimationConfig:(NSDictionary *)config
{
NSNumber *iterations = [RCTConvert NSNumber:config[@"iterations"]] ?: @1;
_toValue = [RCTConvert CGFloat:config[@"toValue"]];
_overshootClamping = [RCTConvert BOOL:config[@"overshootClamping"]];
_restDisplacementThreshold = [RCTConvert CGFloat:config[@"restDisplacementThreshold"]];
_restSpeedThreshold = [RCTConvert CGFloat:config[@"restSpeedThreshold"]];
_stiffness = [RCTConvert CGFloat:config[@"stiffness"]];
_damping = [RCTConvert CGFloat:config[@"damping"]];
_mass = [RCTConvert CGFloat:config[@"mass"]];
_initialVelocity = _lastVelocity;
_fromValue = _lastPosition;
_fromValue = _lastPosition;
_lastVelocity = _initialVelocity;
_animationHasFinished = iterations.integerValue == 0;
_iterations = iterations.integerValue;
_currentLoop = 1;
_animationStartTime = _animationCurrentTime = -1;
_animationHasBegun = YES;
}
RCT_NOT_IMPLEMENTED(-(instancetype)init)
- (void)startAnimation
{
_animationStartTime = _animationCurrentTime = -1;
_animationHasBegun = YES;
}
- (void)stopAnimation
{
if (_callback) {
_callback(@[ @{@"finished" : @(_animationHasFinished), @"value" : @(_valueNode.value)} ]);
}
_valueNode = nil;
}
- (void)stepAnimationWithTime:(NSTimeInterval)currentTime
{
if (!_animationHasBegun || _animationHasFinished) {
// Animation has not begun or animation has already finished.
return;
}
// calculate delta time
if (_animationStartTime == -1) {
_t = 0.0;
_animationStartTime = currentTime;
} else {
// Handle frame drops, and only advance dt by a max of MAX_DELTA_TIME
NSTimeInterval deltaTime = MIN(MAX_DELTA_TIME, currentTime - _animationCurrentTime);
_t = _t + deltaTime / RCTAnimationDragCoefficient();
}
// store the timestamp
_animationCurrentTime = currentTime;
CGFloat c = _damping;
CGFloat m = _mass;
CGFloat k = _stiffness;
CGFloat v0 = -_initialVelocity;
CGFloat zeta = c / (2 * sqrtf(k * m));
CGFloat omega0 = sqrtf(k / m);
CGFloat omega1 = omega0 * sqrtf(1.0 - (zeta * zeta));
CGFloat x0 = _toValue - _fromValue;
CGFloat position;
CGFloat velocity;
if (zeta < 1) {
// Under damped
CGFloat envelope = expf(-zeta * omega0 * _t);
position = _toValue - envelope * ((v0 + zeta * omega0 * x0) / omega1 * sinf(omega1 * _t) + x0 * cosf(omega1 * _t));
// This looks crazy -- it's actually just the derivative of the
// oscillation function
velocity =
zeta * omega0 * envelope * (sinf(omega1 * _t) * (v0 + zeta * omega0 * x0) / omega1 + x0 * cosf(omega1 * _t)) -
envelope * (cosf(omega1 * _t) * (v0 + zeta * omega0 * x0) - omega1 * x0 * sinf(omega1 * _t));
} else {
CGFloat envelope = expf(-omega0 * _t);
position = _toValue - envelope * (x0 + (v0 + omega0 * x0) * _t);
velocity = envelope * (v0 * (_t * omega0 - 1) + _t * x0 * (omega0 * omega0));
}
_lastPosition = position;
_lastVelocity = velocity;
[self onUpdate:position];
// Conditions for stopping the spring animation
BOOL isOvershooting = NO;
if (_overshootClamping && _stiffness != 0) {
if (_fromValue < _toValue) {
isOvershooting = position > _toValue;
} else {
isOvershooting = position < _toValue;
}
}
BOOL isVelocity = ABS(velocity) <= _restSpeedThreshold;
BOOL isDisplacement = YES;
if (_stiffness != 0) {
isDisplacement = ABS(_toValue - position) <= _restDisplacementThreshold;
}
if (isOvershooting || (isVelocity && isDisplacement)) {
if (_stiffness != 0) {
// Ensure that we end up with a round value
if (_animationHasFinished) {
return;
}
[self onUpdate:_toValue];
}
if (_iterations == -1 || _currentLoop < _iterations) {
_lastPosition = _fromValue;
_lastVelocity = _initialVelocity;
// Set _animationStartTime to -1 to reset instance variables on the next animation step.
_animationStartTime = -1;
_currentLoop++;
[self onUpdate:_fromValue];
} else {
_animationHasFinished = YES;
}
}
}
- (void)onUpdate:(CGFloat)outputValue
{
_valueNode.value = outputValue;
[_valueNode setNeedsUpdate];
}
@end