- 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
189 lines
5.6 KiB
Objective-C
189 lines
5.6 KiB
Objective-C
// Copyright 2018-present 650 Industries. All rights reserved.
|
|
|
|
#import <CoreLocation/CLLocationManager.h>
|
|
#import <CoreLocation/CLErrorDomain.h>
|
|
|
|
#import <ExpoModulesCore/EXUtilities.h>
|
|
#import <ExpoModulesCore/EXTaskInterface.h>
|
|
#import <EXLocation/EXLocation.h>
|
|
#import <EXLocation/EXLocationTaskConsumer.h>
|
|
|
|
@interface EXLocationTaskConsumer ()
|
|
|
|
@property (nonatomic, strong) CLLocationManager *locationManager;
|
|
@property (nonatomic, strong) NSMutableArray<CLLocation *> *deferredLocations;
|
|
@property (nonatomic, strong) CLLocation *lastReportedLocation;
|
|
@property (nonatomic, assign) CLLocationDistance deferredDistance;
|
|
|
|
@end
|
|
|
|
@implementation EXLocationTaskConsumer
|
|
|
|
- (instancetype)init
|
|
{
|
|
if (self = [super init]) {
|
|
_deferredLocations = [NSMutableArray new];
|
|
_deferredDistance = 0.0;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[self reset];
|
|
}
|
|
|
|
# pragma mark - EXTaskConsumerInterface
|
|
|
|
- (NSString *)taskType
|
|
{
|
|
return @"location";
|
|
}
|
|
|
|
- (void)didRegisterTask:(id<EXTaskInterface>)task
|
|
{
|
|
[EXUtilities performSynchronouslyOnMainThread:^{
|
|
CLLocationManager *locationManager = [CLLocationManager new];
|
|
|
|
self->_task = task;
|
|
self->_locationManager = locationManager;
|
|
|
|
locationManager.delegate = self;
|
|
locationManager.allowsBackgroundLocationUpdates = YES;
|
|
|
|
// Set options-specific things in location manager.
|
|
[self setOptions:task.options];
|
|
}];
|
|
}
|
|
|
|
- (void)didUnregister
|
|
{
|
|
[self reset];
|
|
}
|
|
|
|
- (void)setOptions:(NSDictionary *)options
|
|
{
|
|
[EXUtilities performSynchronouslyOnMainThread:^{
|
|
CLLocationManager *locationManager = self->_locationManager;
|
|
EXLocationAccuracy accuracy = [options[@"accuracy"] unsignedIntegerValue] ?: EXLocationAccuracyBalanced;
|
|
|
|
locationManager.desiredAccuracy = [EXLocation CLLocationAccuracyFromOption:accuracy];
|
|
locationManager.distanceFilter = [options[@"distanceInterval"] doubleValue] ?: kCLDistanceFilterNone;
|
|
locationManager.activityType = [EXLocation CLActivityTypeFromOption:[options[@"activityType"] integerValue]];
|
|
locationManager.pausesLocationUpdatesAutomatically = [options[@"pausesUpdatesAutomatically"] boolValue];
|
|
|
|
locationManager.showsBackgroundLocationIndicator = [options[@"showsBackgroundLocationIndicator"] boolValue];
|
|
|
|
[locationManager startUpdatingLocation];
|
|
[locationManager startMonitoringSignificantLocationChanges];
|
|
}];
|
|
}
|
|
|
|
# pragma mark - CLLocationManagerDelegate
|
|
|
|
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
|
|
{
|
|
if (_task != nil && locations.count > 0) {
|
|
[self deferLocations:locations];
|
|
[self maybeReportDeferredLocations];
|
|
}
|
|
}
|
|
|
|
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
|
|
{
|
|
if (error.domain == kCLErrorDomain) {
|
|
// This error might happen when the device is not able to find out the location. Try to restart monitoring location.
|
|
[manager stopUpdatingLocation];
|
|
[manager stopMonitoringSignificantLocationChanges];
|
|
[manager startUpdatingLocation];
|
|
[manager startMonitoringSignificantLocationChanges];
|
|
} else {
|
|
[_task executeWithData:nil withError:error];
|
|
}
|
|
}
|
|
|
|
# pragma mark - internal
|
|
|
|
- (void)reset
|
|
{
|
|
[EXUtilities performSynchronouslyOnMainThread:^{
|
|
[self->_locationManager stopUpdatingLocation];
|
|
[self->_locationManager stopMonitoringSignificantLocationChanges];
|
|
[self->_deferredLocations removeAllObjects];
|
|
self->_lastReportedLocation = nil;
|
|
self->_deferredDistance = 0.0;
|
|
self->_locationManager = nil;
|
|
self->_task = nil;
|
|
}];
|
|
}
|
|
|
|
- (void)executeTaskWithDeferredLocations
|
|
{
|
|
// Execute task with deferred locations.
|
|
NSDictionary *data = @{ @"locations": [EXLocationTaskConsumer _exportLocations:_deferredLocations] };
|
|
[_task executeWithData:data withError:nil];
|
|
|
|
// Reset deferring state.
|
|
_lastReportedLocation = _deferredLocations.lastObject;
|
|
_deferredDistance = 0.0;
|
|
[_deferredLocations removeAllObjects];
|
|
}
|
|
|
|
- (void)maybeReportDeferredLocations
|
|
{
|
|
if ([self shouldReportDeferredLocations]) {
|
|
[self executeTaskWithDeferredLocations];
|
|
}
|
|
}
|
|
|
|
- (void)deferLocations:(NSArray<CLLocation *> *)locations
|
|
{
|
|
CLLocation *lastLocation = _deferredLocations.lastObject ?: _lastReportedLocation;
|
|
|
|
for (CLLocation *location in locations) {
|
|
if (lastLocation) {
|
|
_deferredDistance += [location distanceFromLocation:lastLocation];
|
|
}
|
|
lastLocation = location;
|
|
}
|
|
[_deferredLocations addObjectsFromArray:locations];
|
|
}
|
|
|
|
- (BOOL)shouldReportDeferredLocations
|
|
{
|
|
if (_deferredLocations.count <= 0) {
|
|
return NO;
|
|
}
|
|
UIApplicationState appState = [[UIApplication sharedApplication] applicationState];
|
|
|
|
if (appState == UIApplicationStateActive) {
|
|
// Don't defer location updates when app is in foreground state.
|
|
return YES;
|
|
}
|
|
|
|
CLLocation *oldestLocation = _lastReportedLocation ?: _deferredLocations.firstObject;
|
|
CLLocation *newestLocation = _deferredLocations.lastObject;
|
|
NSDictionary *options = _task.options;
|
|
CLLocationDistance distance = [self numberToDouble:options[@"deferredUpdatesDistance"] defaultValue:0];
|
|
NSTimeInterval interval = [self numberToDouble:options[@"deferredUpdatesInterval"] defaultValue:0];
|
|
|
|
return [newestLocation.timestamp timeIntervalSinceDate:oldestLocation.timestamp] >= interval / 1000.0 && _deferredDistance >= distance;
|
|
}
|
|
|
|
- (double)numberToDouble:(NSNumber *)number defaultValue:(double)defaultValue
|
|
{
|
|
return number == nil ? defaultValue : [number doubleValue];
|
|
}
|
|
|
|
+ (NSArray<NSDictionary *> *)_exportLocations:(NSArray<CLLocation *> *)locations
|
|
{
|
|
NSMutableArray<NSDictionary *> *result = [NSMutableArray new];
|
|
|
|
for (CLLocation *location in locations) {
|
|
[result addObject:[EXLocation exportLocation:location]];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@end
|