diff --git a/apps/admin/data/fitai.db b/apps/admin/data/fitai.db index 64f2d21..896526b 100644 Binary files a/apps/admin/data/fitai.db and b/apps/admin/data/fitai.db differ diff --git a/apps/mobile/android/.gitignore b/apps/mobile/android/.gitignore deleted file mode 100644 index 8a6be07..0000000 --- a/apps/mobile/android/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -# OSX -# -.DS_Store - -# Android/IntelliJ -# -build/ -.idea -.gradle -local.properties -*.iml -*.hprof -.cxx/ - -# Bundle artifacts -*.jsbundle diff --git a/apps/mobile/android/app/build.gradle b/apps/mobile/android/app/build.gradle deleted file mode 100644 index 3ebb1b0..0000000 --- a/apps/mobile/android/app/build.gradle +++ /dev/null @@ -1,182 +0,0 @@ -apply plugin: "com.android.application" -apply plugin: "org.jetbrains.kotlin.android" -apply plugin: "com.facebook.react" - -def projectRoot = rootDir.getAbsoluteFile().getParentFile().getAbsolutePath() - -/** - * This is the configuration block to customize your React Native Android app. - * By default you don't need to apply any configuration, just uncomment the lines you need. - */ -react { - entryFile = file(["node", "-e", "require('expo/scripts/resolveAppEntry')", projectRoot, "android", "absolute"].execute(null, rootDir).text.trim()) - reactNativeDir = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile() - hermesCommand = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/sdks/hermesc/%OS-BIN%/hermesc" - codegenDir = new File(["node", "--print", "require.resolve('@react-native/codegen/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile() - - enableBundleCompression = (findProperty('android.enableBundleCompression') ?: false).toBoolean() - // Use Expo CLI to bundle the app, this ensures the Metro config - // works correctly with Expo projects. - cliFile = new File(["node", "--print", "require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })"].execute(null, rootDir).text.trim()) - bundleCommand = "export:embed" - - /* Folders */ - // The root of your project, i.e. where "package.json" lives. Default is '../..' - // root = file("../../") - // The folder where the react-native NPM package is. Default is ../../node_modules/react-native - // reactNativeDir = file("../../node_modules/react-native") - // The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen - // codegenDir = file("../../node_modules/@react-native/codegen") - - /* Variants */ - // The list of variants to that are debuggable. For those we're going to - // skip the bundling of the JS bundle and the assets. By default is just 'debug'. - // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. - // debuggableVariants = ["liteDebug", "prodDebug"] - - /* Bundling */ - // A list containing the node command and its flags. Default is just 'node'. - // nodeExecutableAndArgs = ["node"] - - // - // The path to the CLI configuration file. Default is empty. - // bundleConfig = file(../rn-cli.config.js) - // - // The name of the generated asset file containing your JS bundle - // bundleAssetName = "MyApplication.android.bundle" - // - // The entry file for bundle generation. Default is 'index.android.js' or 'index.js' - // entryFile = file("../js/MyApplication.android.js") - // - // A list of extra flags to pass to the 'bundle' commands. - // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle - // extraPackagerArgs = [] - - /* Hermes Commands */ - // The hermes compiler command to run. By default it is 'hermesc' - // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc" - // - // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" - // hermesFlags = ["-O", "-output-source-map"] - - /* Autolinking */ - autolinkLibrariesWithApp() -} - -/** - * Set this to true in release builds to optimize the app using [R8](https://developer.android.com/topic/performance/app-optimization/enable-app-optimization). - */ -def enableMinifyInReleaseBuilds = (findProperty('android.enableMinifyInReleaseBuilds') ?: false).toBoolean() - -/** - * The preferred build flavor of JavaScriptCore (JSC) - * - * For example, to use the international variant, you can use: - * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` - * - * The international variant includes ICU i18n library and necessary data - * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that - * give correct results when using with locales other than en-US. Note that - * this variant is about 6MiB larger per architecture than default. - */ -def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+' - -android { - ndkVersion rootProject.ext.ndkVersion - - buildToolsVersion rootProject.ext.buildToolsVersion - compileSdk rootProject.ext.compileSdkVersion - - namespace "com.fitai" - defaultConfig { - applicationId "com.fitai" - minSdkVersion rootProject.ext.minSdkVersion - targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 1 - versionName "1.0" - - buildConfigField "String", "REACT_NATIVE_RELEASE_LEVEL", "\"${findProperty('reactNativeReleaseLevel') ?: 'stable'}\"" - } - signingConfigs { - debug { - storeFile file('debug.keystore') - storePassword 'android' - keyAlias 'androiddebugkey' - keyPassword 'android' - } - } - buildTypes { - debug { - signingConfig signingConfigs.debug - } - release { - // Caution! In production, you need to generate your own keystore file. - // see https://reactnative.dev/docs/signed-apk-android. - signingConfig signingConfigs.debug - def enableShrinkResources = findProperty('android.enableShrinkResourcesInReleaseBuilds') ?: 'false' - shrinkResources enableShrinkResources.toBoolean() - minifyEnabled enableMinifyInReleaseBuilds - proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" - def enablePngCrunchInRelease = findProperty('android.enablePngCrunchInReleaseBuilds') ?: 'true' - crunchPngs enablePngCrunchInRelease.toBoolean() - } - } - packagingOptions { - jniLibs { - def enableLegacyPackaging = findProperty('expo.useLegacyPackaging') ?: 'false' - useLegacyPackaging enableLegacyPackaging.toBoolean() - } - } - androidResources { - ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~' - } -} - -// Apply static values from `gradle.properties` to the `android.packagingOptions` -// Accepts values in comma delimited lists, example: -// android.packagingOptions.pickFirsts=/LICENSE,**/picasa.ini -["pickFirsts", "excludes", "merges", "doNotStrip"].each { prop -> - // Split option: 'foo,bar' -> ['foo', 'bar'] - def options = (findProperty("android.packagingOptions.$prop") ?: "").split(","); - // Trim all elements in place. - for (i in 0.. 0) { - println "android.packagingOptions.$prop += $options ($options.length)" - // Ex: android.packagingOptions.pickFirsts += '**/SCCS/**' - options.each { - android.packagingOptions[prop] += it - } - } -} - -dependencies { - // The version of react-native is set by the React Native Gradle Plugin - implementation("com.facebook.react:react-android") - - def isGifEnabled = (findProperty('expo.gif.enabled') ?: "") == "true"; - def isWebpEnabled = (findProperty('expo.webp.enabled') ?: "") == "true"; - def isWebpAnimatedEnabled = (findProperty('expo.webp.animated') ?: "") == "true"; - - if (isGifEnabled) { - // For animated gif support - implementation("com.facebook.fresco:animated-gif:${expoLibs.versions.fresco.get()}") - } - - if (isWebpEnabled) { - // For webp support - implementation("com.facebook.fresco:webpsupport:${expoLibs.versions.fresco.get()}") - if (isWebpAnimatedEnabled) { - // Animated webp support - implementation("com.facebook.fresco:animated-webp:${expoLibs.versions.fresco.get()}") - } - } - - if (hermesEnabled.toBoolean()) { - implementation("com.facebook.react:hermes-android") - } else { - implementation jscFlavor - } -} diff --git a/apps/mobile/android/app/debug.keystore b/apps/mobile/android/app/debug.keystore deleted file mode 100644 index 364e105..0000000 Binary files a/apps/mobile/android/app/debug.keystore and /dev/null differ diff --git a/apps/mobile/android/app/proguard-rules.pro b/apps/mobile/android/app/proguard-rules.pro deleted file mode 100644 index 551eb41..0000000 --- a/apps/mobile/android/app/proguard-rules.pro +++ /dev/null @@ -1,14 +0,0 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# react-native-reanimated --keep class com.swmansion.reanimated.** { *; } --keep class com.facebook.react.turbomodule.** { *; } - -# Add any project specific keep options here: diff --git a/apps/mobile/android/app/src/debug/AndroidManifest.xml b/apps/mobile/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index 3ec2507..0000000 --- a/apps/mobile/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/apps/mobile/android/app/src/debugOptimized/AndroidManifest.xml b/apps/mobile/android/app/src/debugOptimized/AndroidManifest.xml deleted file mode 100644 index 3ec2507..0000000 --- a/apps/mobile/android/app/src/debugOptimized/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/apps/mobile/android/app/src/main/AndroidManifest.xml b/apps/mobile/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index 150248b..0000000 --- a/apps/mobile/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/mobile/android/app/src/main/java/com/anonymous/fitai/MainActivity.kt b/apps/mobile/android/app/src/main/java/com/anonymous/fitai/MainActivity.kt deleted file mode 100644 index 0fa6a55..0000000 --- a/apps/mobile/android/app/src/main/java/com/anonymous/fitai/MainActivity.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.anonymous.fitai - -import android.os.Build -import android.os.Bundle - -import com.facebook.react.ReactActivity -import com.facebook.react.ReactActivityDelegate -import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled -import com.facebook.react.defaults.DefaultReactActivityDelegate - -import expo.modules.ReactActivityDelegateWrapper - -class MainActivity : ReactActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - // Set the theme to AppTheme BEFORE onCreate to support - // coloring the background, status bar, and navigation bar. - // This is required for expo-splash-screen. - setTheme(R.style.AppTheme); - super.onCreate(null) - } - - /** - * Returns the name of the main component registered from JavaScript. This is used to schedule - * rendering of the component. - */ - override fun getMainComponentName(): String = "main" - - /** - * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] - * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] - */ - override fun createReactActivityDelegate(): ReactActivityDelegate { - return ReactActivityDelegateWrapper( - this, - BuildConfig.IS_NEW_ARCHITECTURE_ENABLED, - object : DefaultReactActivityDelegate( - this, - mainComponentName, - fabricEnabled - ){}) - } - - /** - * Align the back button behavior with Android S - * where moving root activities to background instead of finishing activities. - * @see onBackPressed - */ - override fun invokeDefaultOnBackPressed() { - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) { - if (!moveTaskToBack(false)) { - // For non-root activities, use the default implementation to finish them. - super.invokeDefaultOnBackPressed() - } - return - } - - // Use the default back button implementation on Android S - // because it's doing more than [Activity.moveTaskToBack] in fact. - super.invokeDefaultOnBackPressed() - } -} diff --git a/apps/mobile/android/app/src/main/java/com/anonymous/fitai/MainApplication.kt b/apps/mobile/android/app/src/main/java/com/anonymous/fitai/MainApplication.kt deleted file mode 100644 index 676cd4c..0000000 --- a/apps/mobile/android/app/src/main/java/com/anonymous/fitai/MainApplication.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.anonymous.fitai - -import android.app.Application -import android.content.res.Configuration - -import com.facebook.react.PackageList -import com.facebook.react.ReactApplication -import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative -import com.facebook.react.ReactNativeHost -import com.facebook.react.ReactPackage -import com.facebook.react.ReactHost -import com.facebook.react.common.ReleaseLevel -import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint -import com.facebook.react.defaults.DefaultReactNativeHost - -import expo.modules.ApplicationLifecycleDispatcher -import expo.modules.ReactNativeHostWrapper - -class MainApplication : Application(), ReactApplication { - - override val reactNativeHost: ReactNativeHost = ReactNativeHostWrapper( - this, - object : DefaultReactNativeHost(this) { - override fun getPackages(): List = - PackageList(this).packages.apply { - // Packages that cannot be autolinked yet can be added manually here, for example: - // add(MyReactNativePackage()) - } - - override fun getJSMainModuleName(): String = ".expo/.virtual-metro-entry" - - override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG - - override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED - } - ) - - override val reactHost: ReactHost - get() = ReactNativeHostWrapper.createReactHost(applicationContext, reactNativeHost) - - override fun onCreate() { - super.onCreate() - DefaultNewArchitectureEntryPoint.releaseLevel = try { - ReleaseLevel.valueOf(BuildConfig.REACT_NATIVE_RELEASE_LEVEL.uppercase()) - } catch (e: IllegalArgumentException) { - ReleaseLevel.STABLE - } - loadReactNative(this) - ApplicationLifecycleDispatcher.onApplicationCreate(this) - } - - override fun onConfigurationChanged(newConfig: Configuration) { - super.onConfigurationChanged(newConfig) - ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig) - } -} diff --git a/apps/mobile/android/app/src/main/res/drawable-hdpi/splashscreen_logo.png b/apps/mobile/android/app/src/main/res/drawable-hdpi/splashscreen_logo.png deleted file mode 100644 index 31df827..0000000 Binary files a/apps/mobile/android/app/src/main/res/drawable-hdpi/splashscreen_logo.png and /dev/null differ diff --git a/apps/mobile/android/app/src/main/res/drawable-mdpi/splashscreen_logo.png b/apps/mobile/android/app/src/main/res/drawable-mdpi/splashscreen_logo.png deleted file mode 100644 index ef243aa..0000000 Binary files a/apps/mobile/android/app/src/main/res/drawable-mdpi/splashscreen_logo.png and /dev/null differ diff --git a/apps/mobile/android/app/src/main/res/drawable-xhdpi/splashscreen_logo.png b/apps/mobile/android/app/src/main/res/drawable-xhdpi/splashscreen_logo.png deleted file mode 100644 index e9d5474..0000000 Binary files a/apps/mobile/android/app/src/main/res/drawable-xhdpi/splashscreen_logo.png and /dev/null differ diff --git a/apps/mobile/android/app/src/main/res/drawable-xxhdpi/splashscreen_logo.png b/apps/mobile/android/app/src/main/res/drawable-xxhdpi/splashscreen_logo.png deleted file mode 100644 index d61da15..0000000 Binary files a/apps/mobile/android/app/src/main/res/drawable-xxhdpi/splashscreen_logo.png and /dev/null differ diff --git a/apps/mobile/android/app/src/main/res/drawable-xxxhdpi/splashscreen_logo.png b/apps/mobile/android/app/src/main/res/drawable-xxxhdpi/splashscreen_logo.png deleted file mode 100644 index 4aeed11..0000000 Binary files a/apps/mobile/android/app/src/main/res/drawable-xxxhdpi/splashscreen_logo.png and /dev/null differ diff --git a/apps/mobile/android/app/src/main/res/drawable/ic_launcher_background.xml b/apps/mobile/android/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 883b2a0..0000000 --- a/apps/mobile/android/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/apps/mobile/android/app/src/main/res/drawable/rn_edit_text_material.xml b/apps/mobile/android/app/src/main/res/drawable/rn_edit_text_material.xml deleted file mode 100644 index 5c25e72..0000000 --- a/apps/mobile/android/app/src/main/res/drawable/rn_edit_text_material.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - diff --git a/apps/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/apps/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp deleted file mode 100644 index a2f5908..0000000 Binary files a/apps/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp and /dev/null differ diff --git a/apps/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/apps/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp deleted file mode 100644 index 1b52399..0000000 Binary files a/apps/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and /dev/null differ diff --git a/apps/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/apps/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp deleted file mode 100644 index ff10afd..0000000 Binary files a/apps/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp and /dev/null differ diff --git a/apps/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/apps/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp deleted file mode 100644 index 115a4c7..0000000 Binary files a/apps/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and /dev/null differ diff --git a/apps/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/apps/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp deleted file mode 100644 index dcd3cd8..0000000 Binary files a/apps/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and /dev/null differ diff --git a/apps/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/apps/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp deleted file mode 100644 index 459ca60..0000000 Binary files a/apps/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/apps/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/apps/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp deleted file mode 100644 index 8ca12fe..0000000 Binary files a/apps/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and /dev/null differ diff --git a/apps/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/apps/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp deleted file mode 100644 index 8e19b41..0000000 Binary files a/apps/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/apps/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/apps/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp deleted file mode 100644 index b824ebd..0000000 Binary files a/apps/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and /dev/null differ diff --git a/apps/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/apps/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp deleted file mode 100644 index 4c19a13..0000000 Binary files a/apps/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/apps/mobile/android/app/src/main/res/values/colors.xml b/apps/mobile/android/app/src/main/res/values/colors.xml deleted file mode 100644 index 21cc155..0000000 --- a/apps/mobile/android/app/src/main/res/values/colors.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #FFFFFF - \ No newline at end of file diff --git a/apps/mobile/android/app/src/main/res/values/strings.xml b/apps/mobile/android/app/src/main/res/values/strings.xml deleted file mode 100644 index 8829cd4..0000000 --- a/apps/mobile/android/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - FitAI - diff --git a/apps/mobile/android/app/src/main/res/values/styles.xml b/apps/mobile/android/app/src/main/res/values/styles.xml deleted file mode 100644 index 26f3404..0000000 --- a/apps/mobile/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - diff --git a/apps/mobile/android/build.gradle b/apps/mobile/android/build.gradle deleted file mode 100644 index 0554dd1..0000000 --- a/apps/mobile/android/build.gradle +++ /dev/null @@ -1,24 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. - -buildscript { - repositories { - google() - mavenCentral() - } - dependencies { - classpath('com.android.tools.build:gradle') - classpath('com.facebook.react:react-native-gradle-plugin') - classpath('org.jetbrains.kotlin:kotlin-gradle-plugin') - } -} - -allprojects { - repositories { - google() - mavenCentral() - maven { url 'https://www.jitpack.io' } - } -} - -apply plugin: "expo-root-project" -apply plugin: "com.facebook.react.rootproject" diff --git a/apps/mobile/android/gradle.properties b/apps/mobile/android/gradle.properties deleted file mode 100644 index 97271b9..0000000 --- a/apps/mobile/android/gradle.properties +++ /dev/null @@ -1,61 +0,0 @@ -# Project-wide Gradle settings. - -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. - -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html - -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m -org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m - -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -org.gradle.parallel=true - -# AndroidX package structure to make it clearer which packages are bundled with the -# Android operating system, and which are packaged with your app's APK -# https://developer.android.com/topic/libraries/support-library/androidx-rn -android.useAndroidX=true - -# Enable AAPT2 PNG crunching -android.enablePngCrunchInReleaseBuilds=true - -# Use this property to specify which architecture you want to build. -# You can also override it from the CLI using -# ./gradlew -PreactNativeArchitectures=x86_64 -reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 - -# Use this property to enable support to the new architecture. -# This will allow you to use TurboModules and the Fabric render in -# your application. You should enable this flag either if you want -# to write custom TurboModules/Fabric components OR use libraries that -# are providing them. -newArchEnabled=true - -# Use this property to enable or disable the Hermes JS engine. -# If set to false, you will be using JSC instead. -hermesEnabled=true - -# Use this property to enable edge-to-edge display support. -# This allows your app to draw behind system bars for an immersive UI. -# Note: Only works with ReactActivity and should not be used with custom Activity. -edgeToEdgeEnabled=true - -# Enable GIF support in React Native images (~200 B increase) -expo.gif.enabled=true -# Enable webp support in React Native images (~85 KB increase) -expo.webp.enabled=true -# Enable animated webp support (~3.4 MB increase) -# Disabled by default because iOS doesn't support animated webp -expo.webp.animated=false - -# Enable network inspector -EX_DEV_CLIENT_NETWORK_INSPECTOR=true - -# Use legacy packaging to compress native libraries in the resulting APK. -expo.useLegacyPackaging=false diff --git a/apps/mobile/android/settings.gradle b/apps/mobile/android/settings.gradle deleted file mode 100644 index a3d64a9..0000000 --- a/apps/mobile/android/settings.gradle +++ /dev/null @@ -1,39 +0,0 @@ -pluginManagement { - def reactNativeGradlePlugin = new File( - providers.exec { - workingDir(rootDir) - commandLine("node", "--print", "require.resolve('@react-native/gradle-plugin/package.json', { paths: [require.resolve('react-native/package.json')] })") - }.standardOutput.asText.get().trim() - ).getParentFile().absolutePath - includeBuild(reactNativeGradlePlugin) - - def expoPluginsPath = new File( - providers.exec { - workingDir(rootDir) - commandLine("node", "--print", "require.resolve('expo-modules-autolinking/package.json', { paths: [require.resolve('expo/package.json')] })") - }.standardOutput.asText.get().trim(), - "../android/expo-gradle-plugin" - ).absolutePath - includeBuild(expoPluginsPath) -} - -plugins { - id("com.facebook.react.settings") - id("expo-autolinking-settings") -} - -extensions.configure(com.facebook.react.ReactSettingsExtension) { ex -> - if (System.getenv('EXPO_USE_COMMUNITY_AUTOLINKING') == '1') { - ex.autolinkLibrariesFromCommand() - } else { - ex.autolinkLibrariesFromCommand(expoAutolinking.rnConfigCommand) - } -} -expoAutolinking.useExpoModules() - -rootProject.name = 'FitAI' - -expoAutolinking.useExpoVersionCatalog() - -include ':app' -includeBuild(expoAutolinking.reactNativeGradlePlugin) diff --git a/apps/mobile/app.json b/apps/mobile/app.json index 50311fd..454b8da 100644 --- a/apps/mobile/app.json +++ b/apps/mobile/app.json @@ -16,7 +16,8 @@ "supportsTablet": true, "infoPlist": { "NSCameraUsageDescription": "This app uses the camera to scan food barcodes and identify nutritional information.", - "NSUserNotificationsUsageDescription": "This app uses notifications to keep you updated on your fitness progress, recommendation approvals, and important reminders." + "NSUserNotificationsUsageDescription": "This app uses notifications to keep you updated on your fitness progress, recommendation approvals, and important reminders.", + "NSMotionUsageDescription": "This app uses motion data to track your daily steps and activity progress." } }, "android": { @@ -27,7 +28,8 @@ "permissions": [ "CAMERA", "POST_NOTIFICATIONS", - "android.permission.CAMERA" + "android.permission.CAMERA", + "android.permission.ACTIVITY_RECOGNITION" ], "package": "com.anonymous.fitai" }, diff --git a/apps/mobile/ios/FitAI.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/apps/mobile/ios/FitAI.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a..0000000 --- a/apps/mobile/ios/FitAI.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/apps/mobile/ios/FitAI.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/apps/mobile/ios/FitAI.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/apps/mobile/ios/FitAI.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/apps/mobile/package-lock.json b/apps/mobile/package-lock.json index 5c087ac..7d952db 100644 --- a/apps/mobile/package-lock.json +++ b/apps/mobile/package-lock.json @@ -32,6 +32,7 @@ "expo-notifications": "~0.32.0", "expo-router": "~6.0.14", "expo-secure-store": "~15.0.7", + "expo-sensors": "~14.1.4", "expo-status-bar": "^3.0.8", "expo-web-browser": "^15.0.10", "react": "19.1.0", @@ -7604,6 +7605,19 @@ "expo": "*" } }, + "node_modules/expo-sensors": { + "version": "14.1.4", + "resolved": "https://registry.npmjs.org/expo-sensors/-/expo-sensors-14.1.4.tgz", + "integrity": "sha512-KHROi5C8dhXedMwx7fZ5eyv9p382F5XOIex4a+GpdOTL3OY4xyk08kt7x64FtMeeoT87gYD3mb9LrBpHyNubkg==", + "license": "MIT", + "dependencies": { + "invariant": "^2.2.4" + }, + "peerDependencies": { + "expo": "*", + "react-native": "*" + } + }, "node_modules/expo-server": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/expo-server/-/expo-server-1.0.4.tgz", diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 08f0824..de9ebc1 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -38,6 +38,7 @@ "expo-notifications": "~0.32.0", "expo-router": "~6.0.14", "expo-secure-store": "~15.0.7", + "expo-sensors": "~14.1.4", "expo-status-bar": "^3.0.8", "expo-web-browser": "^15.0.10", "react": "19.1.0", diff --git a/apps/mobile/src/app/(tabs)/index.tsx b/apps/mobile/src/app/(tabs)/index.tsx index cc1cdb1..c4728dc 100644 --- a/apps/mobile/src/app/(tabs)/index.tsx +++ b/apps/mobile/src/app/(tabs)/index.tsx @@ -10,7 +10,7 @@ import { Alert, AppState, } from "react-native"; -import { useUser } from "@clerk/clerk-expo"; +import { useAuth, useUser } from "@clerk/clerk-expo"; import { useState, useCallback, useEffect, useRef, useMemo } from "react"; import { useFocusEffect } from "@react-navigation/native"; import AsyncStorage from "@react-native-async-storage/async-storage"; @@ -27,6 +27,8 @@ import { AddWaterModal } from "../../components/AddWaterModal"; import { ScanFoodModal } from "../../components/ScanFoodModal"; import { ActivityRing } from "../../components/ActivityRing"; import { useMembership } from "../../hooks/useMembership"; +import { attendanceApi, type Attendance } from "../../api/attendance"; +import { useDailySteps } from "../../hooks/useDailySteps"; import { checkInsToActivities, completedGoalsToActivities, @@ -74,7 +76,15 @@ const getRandomMotivation = () => { export default function HomeScreen() { const { user } = useUser(); + const { getToken } = useAuth(); const { colors, typography } = useTheme(); + const { + steps, + goal: stepsGoal, + loading: stepsLoading, + supported: stepsSupported, + permissionGranted: stepsPermissionGranted, + } = useDailySteps(); const { features, membershipType } = useMembership(); const { refetchStatistics, forceRefresh, statistics, loading } = useStatistics(); @@ -85,6 +95,9 @@ export default function HomeScreen() { const [scanFoodModalVisible, setScanFoodModalVisible] = useState(false); const [calories, setCalories] = useState(0); const [waterIntake, setWaterIntake] = useState(0); + const [activeWorkoutSession, setActiveWorkoutSession] = + useState(null); + const [workoutActionLoading, setWorkoutActionLoading] = useState(false); const [motivationalMessage, setMotivationalMessage] = useState( "Let's crush it today! 💪", ); @@ -181,14 +194,78 @@ export default function HomeScreen() { }, getMillisecondsUntilNextMidnight() + 50); }, [reconcileDailyMetrics]); + const fetchActiveWorkoutSession = useCallback(async () => { + try { + const token = await getToken(); + if (!token) { + setActiveWorkoutSession(null); + return; + } + + const history = await attendanceApi.getHistory(token); + if (history.length > 0 && !history[0].checkOutTime) { + setActiveWorkoutSession(history[0]); + } else { + setActiveWorkoutSession(null); + } + } catch { + setActiveWorkoutSession(null); + } + }, [getToken]); + useFocusEffect( useCallback(() => { void reconcileDailyMetrics(); + void fetchActiveWorkoutSession(); refetchStatistics(); refetchGoals(); - }, [reconcileDailyMetrics, refetchStatistics, refetchGoals]), + }, [ + fetchActiveWorkoutSession, + reconcileDailyMetrics, + refetchStatistics, + refetchGoals, + ]), ); + const handleWorkoutAction = useCallback(async () => { + try { + setWorkoutActionLoading(true); + const token = await getToken(); + if (!token) { + Alert.alert("Sign in required", "Please sign in to log your workout."); + return; + } + + if (activeWorkoutSession) { + await attendanceApi.checkOut(token); + Alert.alert("Workout logged", "Session ended successfully."); + } else { + await attendanceApi.checkIn("gym", token); + Alert.alert("Workout started", "Session started successfully."); + } + + await Promise.all([ + fetchActiveWorkoutSession(), + forceRefresh(), + refetchStatistics(), + ]); + } catch (error: unknown) { + const message = + error instanceof Error + ? error.message + : "Unable to update workout session. Please try again."; + Alert.alert("Workout action failed", message); + } finally { + setWorkoutActionLoading(false); + } + }, [ + activeWorkoutSession, + fetchActiveWorkoutSession, + forceRefresh, + getToken, + refetchStatistics, + ]); + const onRefresh = useCallback(async () => { setRefreshing(true); await Promise.all([forceRefresh(), refetchGoals()]); @@ -491,6 +568,57 @@ export default function HomeScreen() { + {/* Steps */} + + + + + + + 👣 + + + + Steps + + + {stepsLoading + ? "Loading steps..." + : !stepsSupported + ? "Step tracking not supported on this device" + : !stepsPermissionGranted + ? "Enable motion access in settings" + : steps >= stepsGoal + ? "Daily step goal reached!" + : `${Math.max(0, stepsGoal - steps)} steps remaining`} + + + + + {stepsLoading ? "--" : `${steps}/${stepsGoal}`} + + + + + + {/* Quick Actions */} console.log("Log workout")} + onPress={handleWorkoutAction} + disabled={workoutActionLoading} activeOpacity={0.85} style={[ styles.quickActionCard, - { backgroundColor: colors.primary }, + { + backgroundColor: activeWorkoutSession + ? colors.warning + : colors.primary, + opacity: workoutActionLoading ? 0.7 : 1, + }, ]} > - + - Workout + {activeWorkoutSession ? "End Workout" : "Start Workout"} - Log your session + {activeWorkoutSession + ? `In session since ${new Date( + activeWorkoutSession.checkInTime, + ).toLocaleTimeString([], { + hour: "2-digit", + minute: "2-digit", + })}` + : workoutActionLoading + ? "Updating session..." + : "Log your session"} @@ -931,7 +1078,12 @@ const styles = StyleSheet.create({ justifyContent: "space-between", alignItems: "center", }, - progressLabelRow: { flexDirection: "row", alignItems: "center" }, + progressLabelRow: { + flexDirection: "row", + alignItems: "center", + flex: 1, + minWidth: 0, + }, progressIcon: { width: 48, height: 48, diff --git a/apps/mobile/src/hooks/useDailySteps.ts b/apps/mobile/src/hooks/useDailySteps.ts new file mode 100644 index 0000000..31ecd27 --- /dev/null +++ b/apps/mobile/src/hooks/useDailySteps.ts @@ -0,0 +1,136 @@ +import { useAuth } from "@clerk/clerk-expo"; +import { Pedometer } from "expo-sensors"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { AppState } from "react-native"; + +interface DailyStepsState { + steps: number; + goal: number; + loading: boolean; + supported: boolean; + permissionGranted: boolean; + refresh: () => Promise; +} + +const DEFAULT_DAILY_STEPS_GOAL = 8000; + +function startOfToday(): Date { + const now = new Date(); + return new Date(now.getFullYear(), now.getMonth(), now.getDate()); +} + +function millisecondsUntilNextMidnight(): number { + const now = new Date(); + const nextMidnight = new Date(now); + nextMidnight.setHours(24, 0, 0, 0); + return Math.max(1000, nextMidnight.getTime() - now.getTime()); +} + +export function useDailySteps(): DailyStepsState { + const { isSignedIn } = useAuth(); + const [steps, setSteps] = useState(0); + const [loading, setLoading] = useState(true); + const [supported, setSupported] = useState(true); + const [permissionGranted, setPermissionGranted] = useState(true); + const pedometerSubscriptionRef = useRef | null>(null); + const midnightTimerRef = useRef | null>(null); + + const fetchDailySteps = useCallback(async () => { + if (!isSignedIn) { + setSteps(0); + setLoading(false); + return; + } + + try { + setLoading(true); + const isAvailable = await Pedometer.isAvailableAsync(); + setSupported(Boolean(isAvailable)); + + if (!isAvailable) { + setSteps(0); + setPermissionGranted(false); + return; + } + + const start = startOfToday(); + const end = new Date(); + const result = await Pedometer.getStepCountAsync(start, end); + + setSteps(Math.max(0, result.steps ?? 0)); + setPermissionGranted(true); + } catch { + setPermissionGranted(false); + setSteps(0); + } finally { + setLoading(false); + } + }, [isSignedIn]); + + const resetAtMidnight = useCallback(() => { + if (midnightTimerRef.current) { + clearTimeout(midnightTimerRef.current); + } + + midnightTimerRef.current = setTimeout(() => { + void fetchDailySteps(); + resetAtMidnight(); + }, millisecondsUntilNextMidnight() + 100); + }, [fetchDailySteps]); + + useEffect(() => { + void fetchDailySteps(); + }, [fetchDailySteps]); + + useEffect(() => { + if (pedometerSubscriptionRef.current) { + pedometerSubscriptionRef.current.remove(); + pedometerSubscriptionRef.current = null; + } + + if (!isSignedIn || !supported || !permissionGranted) { + return; + } + + pedometerSubscriptionRef.current = Pedometer.watchStepCount( + (result: { steps: number }) => { + setSteps((prev) => Math.max(0, prev + Math.max(0, result.steps ?? 0))); + }, + ); + + return () => { + if (pedometerSubscriptionRef.current) { + pedometerSubscriptionRef.current.remove(); + pedometerSubscriptionRef.current = null; + } + }; + }, [isSignedIn, permissionGranted, supported]); + + useEffect(() => { + const subscription = AppState.addEventListener("change", (state) => { + if (state === "active") { + void fetchDailySteps(); + } + }); + + resetAtMidnight(); + + return () => { + subscription.remove(); + if (midnightTimerRef.current) { + clearTimeout(midnightTimerRef.current); + } + }; + }, [fetchDailySteps, resetAtMidnight]); + + return { + steps, + goal: DEFAULT_DAILY_STEPS_GOAL, + loading, + supported, + permissionGranted, + refresh: fetchDailySteps, + }; +}