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,3 @@
declare const _default: any;
export default _default;
//# sourceMappingURL=ExpoSecureStore.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ExpoSecureStore.d.ts","sourceRoot":"","sources":["../src/ExpoSecureStore.ts"],"names":[],"mappings":";AACA,wBAAsD"}

View File

@@ -0,0 +1,3 @@
import { requireNativeModule } from 'expo-modules-core';
export default requireNativeModule('ExpoSecureStore');
//# sourceMappingURL=ExpoSecureStore.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ExpoSecureStore.js","sourceRoot":"","sources":["../src/ExpoSecureStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,eAAe,mBAAmB,CAAC,iBAAiB,CAAC,CAAC","sourcesContent":["import { requireNativeModule } from 'expo-modules-core';\nexport default requireNativeModule('ExpoSecureStore');\n"]}

View File

@@ -0,0 +1,3 @@
declare const _default: {};
export default _default;
//# sourceMappingURL=ExpoSecureStore.web.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ExpoSecureStore.web.d.ts","sourceRoot":"","sources":["../src/ExpoSecureStore.web.ts"],"names":[],"mappings":";AAAA,wBAAkB"}

View File

@@ -0,0 +1,2 @@
export default {};
//# sourceMappingURL=ExpoSecureStore.web.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ExpoSecureStore.web.js","sourceRoot":"","sources":["../src/ExpoSecureStore.web.ts"],"names":[],"mappings":"AAAA,eAAe,EAAE,CAAC","sourcesContent":["export default {};\n"]}

View File

@@ -0,0 +1,135 @@
export type KeychainAccessibilityConstant = number;
/**
* The data in the keychain item cannot be accessed after a restart until the device has been
* unlocked once by the user. This may be useful if you need to access the item when the phone
* is locked.
*/
export declare const AFTER_FIRST_UNLOCK: KeychainAccessibilityConstant;
/**
* Similar to `AFTER_FIRST_UNLOCK`, except the entry is not migrated to a new device when restoring
* from a backup.
*/
export declare const AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY: KeychainAccessibilityConstant;
/**
* The data in the keychain item can always be accessed regardless of whether the device is locked.
* This is the least secure option.
*/
export declare const ALWAYS: KeychainAccessibilityConstant;
/**
* Similar to `WHEN_UNLOCKED_THIS_DEVICE_ONLY`, except the user must have set a passcode in order to
* store an entry. If the user removes their passcode, the entry will be deleted.
*/
export declare const WHEN_PASSCODE_SET_THIS_DEVICE_ONLY: KeychainAccessibilityConstant;
/**
* Similar to `ALWAYS`, except the entry is not migrated to a new device when restoring from a backup.
*/
export declare const ALWAYS_THIS_DEVICE_ONLY: KeychainAccessibilityConstant;
/**
* The data in the keychain item can be accessed only while the device is unlocked by the user.
*/
export declare const WHEN_UNLOCKED: KeychainAccessibilityConstant;
/**
* Similar to `WHEN_UNLOCKED`, except the entry is not migrated to a new device when restoring from
* a backup.
*/
export declare const WHEN_UNLOCKED_THIS_DEVICE_ONLY: KeychainAccessibilityConstant;
export type SecureStoreOptions = {
/**
* - Android: Equivalent of the public/private key pair `Alias`.
* - iOS: The item's service, equivalent to [`kSecAttrService`](https://developer.apple.com/documentation/security/ksecattrservice/).
* > If the item is set with the `keychainService` option, it will be required to later fetch the value.
*/
keychainService?: string;
/**
* Option responsible for enabling the usage of the user authentication methods available on the device while
* accessing data stored in SecureStore.
* - Android: Equivalent to [`setUserAuthenticationRequired(true)`](https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.Builder#setUserAuthenticationRequired(boolean))
* (requires API 23).
* - iOS: Equivalent to [`kSecAccessControlBiometryCurrentSet`](https://developer.apple.com/documentation/security/secaccesscontrolcreateflags/ksecaccesscontrolbiometrycurrentset/).
* Complete functionality is unlocked only with a freshly generated key - this would not work in tandem with the `keychainService`
* value used for the others non-authenticated operations.
*
* Warning: This option is not supported in Expo Go when biometric authentication is available due to a missing NSFaceIDUsageDescription.
* In release builds or when using continuous native generation, make sure to use the `expo-secure-store` config plugin.
*
*/
requireAuthentication?: boolean;
/**
* Custom message displayed to the user while `requireAuthentication` option is turned on.
*/
authenticationPrompt?: string;
/**
* Specifies when the stored entry is accessible, using iOS's `kSecAttrAccessible` property.
* @see Apple's documentation on [keychain item accessibility](https://developer.apple.com/documentation/security/ksecattraccessible/).
* @default SecureStore.WHEN_UNLOCKED
* @platform ios
*/
keychainAccessible?: KeychainAccessibilityConstant;
};
/**
* Returns whether the SecureStore API is enabled on the current device. This does not check the app
* permissions.
*
* @return Promise which fulfils witch `boolean`, indicating whether the SecureStore API is available
* on the current device. Currently, this resolves `true` on Android and iOS only.
*/
export declare function isAvailableAsync(): Promise<boolean>;
/**
* Delete the value associated with the provided key.
*
* @param key The key that was used to store the associated value.
* @param options An [`SecureStoreOptions`](#securestoreoptions) object.
*
* @return A promise that will reject if the value couldn't be deleted.
*/
export declare function deleteItemAsync(key: string, options?: SecureStoreOptions): Promise<void>;
/**
* Reads the stored value associated with the provided key.
*
* @param key The key that was used to store the associated value.
* @param options An [`SecureStoreOptions`](#securestoreoptions) object.
*
* @return A promise that resolves to the previously stored value. It will return `null` if there is no entry
* for the given key or if the key has been invalidated. It will reject if an error occurs while retrieving the value.
*
* > Keys are invalidated by the system when biometrics change, such as adding a new fingerprint or changing the face profile used for face recognition.
* > After a key has been invalidated, it becomes impossible to read its value.
* > This only applies to values stored with `requireAuthentication` set to `true`.
*/
export declare function getItemAsync(key: string, options?: SecureStoreOptions): Promise<string | null>;
/**
* Stores a keyvalue pair.
*
* @param key The key to associate with the stored value. Keys may contain alphanumeric characters, `.`, `-`, and `_`.
* @param value The value to store. Size limit is 2048 bytes.
* @param options An [`SecureStoreOptions`](#securestoreoptions) object.
*
* @return A promise that will reject if value cannot be stored on the device.
*/
export declare function setItemAsync(key: string, value: string, options?: SecureStoreOptions): Promise<void>;
/**
* Stores a keyvalue pair synchronously.
* > **Note:** This function blocks the JavaScript thread, so the application may not be interactive when the `requireAuthentication` option is set to `true` until the user authenticates.
*
* @param key The key to associate with the stored value. Keys may contain alphanumeric characters, `.`, `-`, and `_`.
* @param value The value to store. Size limit is 2048 bytes.
* @param options An [`SecureStoreOptions`](#securestoreoptions) object.
*
*/
export declare function setItem(key: string, value: string, options?: SecureStoreOptions): void;
/**
* Synchronously reads the stored value associated with the provided key.
* > **Note:** This function blocks the JavaScript thread, so the application may not be interactive when reading a value with `requireAuthentication`
* > option set to `true` until the user authenticates.
* @param key The key that was used to store the associated value.
* @param options An [`SecureStoreOptions`](#securestoreoptions) object.
*
* @return Previously stored value. It will return `null` if there is no entry for the given key or if the key has been invalidated.
*/
export declare function getItem(key: string, options?: SecureStoreOptions): string | null;
/**
* Checks if the value can be saved with `requireAuthentication` option enabled.
* @return `true` if the device supports biometric authentication and the enrolled method is sufficiently secure. Otherwise, returns `false`.
*/
export declare function canUseBiometricAuthentication(): boolean;
//# sourceMappingURL=SecureStore.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"SecureStore.d.ts","sourceRoot":"","sources":["../src/SecureStore.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,6BAA6B,GAAG,MAAM,CAAC;AAGnD;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,EAAE,6BAAkE,CAAC;AAGpG;;;GAGG;AACH,eAAO,MAAM,mCAAmC,EAAE,6BACG,CAAC;AAGtD;;;GAGG;AACH,eAAO,MAAM,MAAM,EAAE,6BAAsD,CAAC;AAG5E;;;GAGG;AACH,eAAO,MAAM,kCAAkC,EAAE,6BACG,CAAC;AAGrD;;GAEG;AACH,eAAO,MAAM,uBAAuB,EAAE,6BACG,CAAC;AAG1C;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,6BAA6D,CAAC;AAG1F;;;GAGG;AACH,eAAO,MAAM,8BAA8B,EAAE,6BACG,CAAC;AAKjD,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;;;;;;OAYG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;OAEG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,6BAA6B,CAAC;CACpD,CAAC;AAGF;;;;;;GAMG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CAEzD;AAGD;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAIf;AAGD;;;;;;;;;;;;GAYG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGxB;AAGD;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CASf;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,IAAI,CAS1F;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,MAAM,GAAG,IAAI,CAGpF;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,IAAI,OAAO,CAEvD"}

View File

@@ -0,0 +1,176 @@
import ExpoSecureStore from './ExpoSecureStore';
// @needsAudit
/**
* The data in the keychain item cannot be accessed after a restart until the device has been
* unlocked once by the user. This may be useful if you need to access the item when the phone
* is locked.
*/
export const AFTER_FIRST_UNLOCK = ExpoSecureStore.AFTER_FIRST_UNLOCK;
// @needsAudit
/**
* Similar to `AFTER_FIRST_UNLOCK`, except the entry is not migrated to a new device when restoring
* from a backup.
*/
export const AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY = ExpoSecureStore.AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY;
// @needsAudit
/**
* The data in the keychain item can always be accessed regardless of whether the device is locked.
* This is the least secure option.
*/
export const ALWAYS = ExpoSecureStore.ALWAYS;
// @needsAudit
/**
* Similar to `WHEN_UNLOCKED_THIS_DEVICE_ONLY`, except the user must have set a passcode in order to
* store an entry. If the user removes their passcode, the entry will be deleted.
*/
export const WHEN_PASSCODE_SET_THIS_DEVICE_ONLY = ExpoSecureStore.WHEN_PASSCODE_SET_THIS_DEVICE_ONLY;
// @needsAudit
/**
* Similar to `ALWAYS`, except the entry is not migrated to a new device when restoring from a backup.
*/
export const ALWAYS_THIS_DEVICE_ONLY = ExpoSecureStore.ALWAYS_THIS_DEVICE_ONLY;
// @needsAudit
/**
* The data in the keychain item can be accessed only while the device is unlocked by the user.
*/
export const WHEN_UNLOCKED = ExpoSecureStore.WHEN_UNLOCKED;
// @needsAudit
/**
* Similar to `WHEN_UNLOCKED`, except the entry is not migrated to a new device when restoring from
* a backup.
*/
export const WHEN_UNLOCKED_THIS_DEVICE_ONLY = ExpoSecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY;
const VALUE_BYTES_LIMIT = 2048;
// @needsAudit
/**
* Returns whether the SecureStore API is enabled on the current device. This does not check the app
* permissions.
*
* @return Promise which fulfils witch `boolean`, indicating whether the SecureStore API is available
* on the current device. Currently, this resolves `true` on Android and iOS only.
*/
export async function isAvailableAsync() {
return !!ExpoSecureStore.getValueWithKeyAsync;
}
// @needsAudit
/**
* Delete the value associated with the provided key.
*
* @param key The key that was used to store the associated value.
* @param options An [`SecureStoreOptions`](#securestoreoptions) object.
*
* @return A promise that will reject if the value couldn't be deleted.
*/
export async function deleteItemAsync(key, options = {}) {
ensureValidKey(key);
await ExpoSecureStore.deleteValueWithKeyAsync(key, options);
}
// @needsAudit
/**
* Reads the stored value associated with the provided key.
*
* @param key The key that was used to store the associated value.
* @param options An [`SecureStoreOptions`](#securestoreoptions) object.
*
* @return A promise that resolves to the previously stored value. It will return `null` if there is no entry
* for the given key or if the key has been invalidated. It will reject if an error occurs while retrieving the value.
*
* > Keys are invalidated by the system when biometrics change, such as adding a new fingerprint or changing the face profile used for face recognition.
* > After a key has been invalidated, it becomes impossible to read its value.
* > This only applies to values stored with `requireAuthentication` set to `true`.
*/
export async function getItemAsync(key, options = {}) {
ensureValidKey(key);
return await ExpoSecureStore.getValueWithKeyAsync(key, options);
}
// @needsAudit
/**
* Stores a keyvalue pair.
*
* @param key The key to associate with the stored value. Keys may contain alphanumeric characters, `.`, `-`, and `_`.
* @param value The value to store. Size limit is 2048 bytes.
* @param options An [`SecureStoreOptions`](#securestoreoptions) object.
*
* @return A promise that will reject if value cannot be stored on the device.
*/
export async function setItemAsync(key, value, options = {}) {
ensureValidKey(key);
if (!isValidValue(value)) {
throw new Error(`Invalid value provided to SecureStore. Values must be strings; consider JSON-encoding your values if they are serializable.`);
}
await ExpoSecureStore.setValueWithKeyAsync(value, key, options);
}
/**
* Stores a keyvalue pair synchronously.
* > **Note:** This function blocks the JavaScript thread, so the application may not be interactive when the `requireAuthentication` option is set to `true` until the user authenticates.
*
* @param key The key to associate with the stored value. Keys may contain alphanumeric characters, `.`, `-`, and `_`.
* @param value The value to store. Size limit is 2048 bytes.
* @param options An [`SecureStoreOptions`](#securestoreoptions) object.
*
*/
export function setItem(key, value, options = {}) {
ensureValidKey(key);
if (!isValidValue(value)) {
throw new Error(`Invalid value provided to SecureStore. Values must be strings; consider JSON-encoding your values if they are serializable.`);
}
return ExpoSecureStore.setValueWithKeySync(value, key, options);
}
/**
* Synchronously reads the stored value associated with the provided key.
* > **Note:** This function blocks the JavaScript thread, so the application may not be interactive when reading a value with `requireAuthentication`
* > option set to `true` until the user authenticates.
* @param key The key that was used to store the associated value.
* @param options An [`SecureStoreOptions`](#securestoreoptions) object.
*
* @return Previously stored value. It will return `null` if there is no entry for the given key or if the key has been invalidated.
*/
export function getItem(key, options = {}) {
ensureValidKey(key);
return ExpoSecureStore.getValueWithKeySync(key, options);
}
/**
* Checks if the value can be saved with `requireAuthentication` option enabled.
* @return `true` if the device supports biometric authentication and the enrolled method is sufficiently secure. Otherwise, returns `false`.
*/
export function canUseBiometricAuthentication() {
return ExpoSecureStore.canUseBiometricAuthentication();
}
function ensureValidKey(key) {
if (!isValidKey(key)) {
throw new Error(`Invalid key provided to SecureStore. Keys must not be empty and contain only alphanumeric characters, ".", "-", and "_".`);
}
}
function isValidKey(key) {
return typeof key === 'string' && /^[\w.-]+$/.test(key);
}
function isValidValue(value) {
if (typeof value !== 'string') {
return false;
}
if (byteCount(value) > VALUE_BYTES_LIMIT) {
console.warn('Value being stored in SecureStore is larger than 2048 bytes and it may not be stored successfully. In a future SDK version, this call may throw an error.');
}
return true;
}
// copy-pasted from https://stackoverflow.com/a/39488643
function byteCount(value) {
let bytes = 0;
for (let i = 0; i < value.length; i++) {
const codePoint = value.charCodeAt(i);
// Lone surrogates cannot be passed to encodeURI
if (codePoint >= 0xd800 && codePoint < 0xe000) {
if (codePoint < 0xdc00 && i + 1 < value.length) {
const next = value.charCodeAt(i + 1);
if (next >= 0xdc00 && next < 0xe000) {
bytes += 4;
i++;
continue;
}
}
}
bytes += codePoint < 0x80 ? 1 : codePoint < 0x800 ? 2 : 3;
}
return bytes;
}
//# sourceMappingURL=SecureStore.js.map

File diff suppressed because one or more lines are too long