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,19 @@
apply plugin: 'com.android.library'
group = 'host.exp.exponent'
version = '12.0.10'
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
apply from: expoModulesCorePlugin
applyKotlinExpoModulesCorePlugin()
useCoreDependencies()
useDefaultAndroidSdkVersions()
useExpoPublishing()
android {
namespace "expo.modules.font"
defaultConfig {
versionCode 29
versionName "12.0.10"
}
}

View File

@@ -0,0 +1,3 @@
<manifest>
</manifest>

View File

@@ -0,0 +1,74 @@
// Copyright 2015-present 650 Industries. All rights reserved.
package expo.modules.font
import android.content.Context
import android.graphics.Typeface
import android.net.Uri
import expo.modules.interfaces.font.FontManagerInterface
import expo.modules.kotlin.exception.CodedException
import expo.modules.kotlin.exception.Exceptions
import expo.modules.kotlin.modules.Module
import expo.modules.kotlin.modules.ModuleDefinition
import java.io.File
private const val ASSET_SCHEME = "asset://"
private class FontManagerInterfaceNotFoundException :
CodedException("FontManagerInterface not found")
private class FileNotFoundException(uri: String) :
CodedException("File '$uri' doesn't exist")
open class FontLoaderModule : Module() {
private val context: Context
get() = appContext.reactContext ?: throw Exceptions.ReactContextLost()
@Suppress("MemberVisibilityCanBePrivate")
open val prefix = ""
override fun definition() = ModuleDefinition {
Name("ExpoFontLoader")
Constants("customNativeFonts" to queryCustomNativeFonts())
AsyncFunction("loadAsync") { fontFamilyName: String, localUri: String ->
val context = appContext.reactContext ?: throw Exceptions.ReactContextLost()
// TODO(nikki): make sure path is in experience's scope
val typeface: Typeface = if (localUri.startsWith(ASSET_SCHEME)) {
Typeface.createFromAsset(
context.assets, // Also remove the leading slash.
localUri.substring(ASSET_SCHEME.length + 1)
)
} else {
val file = Uri.parse(localUri).path?.let { File(it) }
?: throw FileNotFoundException(localUri)
Typeface.createFromFile(file)
}
val fontManager = appContext.legacyModule<FontManagerInterface>()
?: throw FontManagerInterfaceNotFoundException()
fontManager.setTypeface(prefix + fontFamilyName, Typeface.NORMAL, typeface)
}
}
/**
* Queries custom native font names from the assets.
* Alignment with React Native's implementation at:
* https://github.com/facebook/react-native/blob/363ee484b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/assets/ReactFontManager.java#L146-L161
*/
private fun queryCustomNativeFonts(): List<String> {
val assetManager = context.assets
val fontFileRegex = Regex("^(.+?)(_bold|_italic|_bold_italic)?\\.(ttf|otf)$")
return assetManager.list("fonts/")
?.mapNotNull { fileName ->
fontFileRegex.find(fileName)?.groupValues?.get(1)
}
?.filter { it.isNotBlank() }
.orEmpty()
}
}