diff --git a/CLAUDE.md b/CLAUDE.md index e701cef..5cf00f3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - **Run on iOS**: `npm run ios` ## Architecture -- **Framework**: React Native (Expo) with TypeScript using Expo Router for file-based navigation +- **Framework**: React Native (Expo Prebuild/Ejected) with TypeScript using Expo Router for file-based navigation - **State Management**: Redux Toolkit with domain-specific slices (`store/`) and typed hooks (`hooks/redux.ts`) - **Authentication**: Custom auth guard system with `useAuthGuard` hook for protected navigation - **Navigation**: diff --git a/android/.gitignore b/android/.gitignore deleted file mode 100644 index 8a6be07..0000000 --- a/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/android/app/build.gradle b/android/app/build.gradle deleted file mode 100644 index e66bff4..0000000 --- a/android/app/build.gradle +++ /dev/null @@ -1,177 +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 to Run Proguard on Release builds to minify the Java bytecode. - */ -def enableProguardInReleaseBuilds = (findProperty('android.enableProguardInReleaseBuilds') ?: 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.anonymous.digitalpilates' - defaultConfig { - applicationId 'com.anonymous.digitalpilates' - minSdkVersion rootProject.ext.minSdkVersion - targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 2 - versionName "1.0.2" - } - 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 - shrinkResources (findProperty('android.enableShrinkResourcesInReleaseBuilds')?.toBoolean() ?: false) - minifyEnabled enableProguardInReleaseBuilds - proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" - crunchPngs (findProperty('android.enablePngCrunchInReleaseBuilds')?.toBoolean() ?: true) - } - } - packagingOptions { - jniLibs { - useLegacyPackaging (findProperty('expo.useLegacyPackaging')?.toBoolean() ?: false) - } - } - 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/android/app/debug.keystore b/android/app/debug.keystore deleted file mode 100644 index 364e105..0000000 Binary files a/android/app/debug.keystore and /dev/null differ diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro deleted file mode 100644 index 551eb41..0000000 --- a/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/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index 3ec2507..0000000 --- a/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index 6300be6..0000000 --- a/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/android/app/src/main/java/com/anonymous/digitalpilates/MainActivity.kt b/android/app/src/main/java/com/anonymous/digitalpilates/MainActivity.kt deleted file mode 100644 index deee6b3..0000000 --- a/android/app/src/main/java/com/anonymous/digitalpilates/MainActivity.kt +++ /dev/null @@ -1,65 +0,0 @@ -package com.anonymous.digitalpilates -import expo.modules.splashscreen.SplashScreenManager - -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); - // @generated begin expo-splashscreen - expo prebuild (DO NOT MODIFY) sync-f3ff59a738c56c9a6119210cb55f0b613eb8b6af - SplashScreenManager.registerOnActivity(this) - // @generated end expo-splashscreen - 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/android/app/src/main/java/com/anonymous/digitalpilates/MainApplication.kt b/android/app/src/main/java/com/anonymous/digitalpilates/MainApplication.kt deleted file mode 100644 index b7a3dd3..0000000 --- a/android/app/src/main/java/com/anonymous/digitalpilates/MainApplication.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.anonymous.digitalpilates - -import android.app.Application -import android.content.res.Configuration - -import com.facebook.react.PackageList -import com.facebook.react.ReactApplication -import com.facebook.react.ReactNativeHost -import com.facebook.react.ReactPackage -import com.facebook.react.ReactHost -import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load -import com.facebook.react.defaults.DefaultReactNativeHost -import com.facebook.react.soloader.OpenSourceMergedSoMapping -import com.facebook.soloader.SoLoader - -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 { - val packages = PackageList(this).packages - // Packages that cannot be autolinked yet can be added manually here, for example: - // packages.add(MyReactNativePackage()) - return packages - } - - 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 isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED - } - ) - - override val reactHost: ReactHost - get() = ReactNativeHostWrapper.createReactHost(applicationContext, reactNativeHost) - - override fun onCreate() { - super.onCreate() - SoLoader.init(this, OpenSourceMergedSoMapping) - if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { - // If you opted-in for the New Architecture, we load the native entry point for this app. - load() - } - ApplicationLifecycleDispatcher.onApplicationCreate(this) - } - - override fun onConfigurationChanged(newConfig: Configuration) { - super.onConfigurationChanged(newConfig) - ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig) - } -} diff --git a/android/app/src/main/res/drawable-hdpi/splashscreen_logo.png b/android/app/src/main/res/drawable-hdpi/splashscreen_logo.png deleted file mode 100644 index 31df827..0000000 Binary files a/android/app/src/main/res/drawable-hdpi/splashscreen_logo.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-mdpi/splashscreen_logo.png b/android/app/src/main/res/drawable-mdpi/splashscreen_logo.png deleted file mode 100644 index ef243aa..0000000 Binary files a/android/app/src/main/res/drawable-mdpi/splashscreen_logo.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-xhdpi/splashscreen_logo.png b/android/app/src/main/res/drawable-xhdpi/splashscreen_logo.png deleted file mode 100644 index e9d5474..0000000 Binary files a/android/app/src/main/res/drawable-xhdpi/splashscreen_logo.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-xxhdpi/splashscreen_logo.png b/android/app/src/main/res/drawable-xxhdpi/splashscreen_logo.png deleted file mode 100644 index d61da15..0000000 Binary files a/android/app/src/main/res/drawable-xxhdpi/splashscreen_logo.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/splashscreen_logo.png b/android/app/src/main/res/drawable-xxxhdpi/splashscreen_logo.png deleted file mode 100644 index 4aeed11..0000000 Binary files a/android/app/src/main/res/drawable-xxxhdpi/splashscreen_logo.png and /dev/null differ diff --git a/android/app/src/main/res/drawable/ic_launcher_background.xml b/android/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 883b2a0..0000000 --- a/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/android/app/src/main/res/drawable/rn_edit_text_material.xml b/android/app/src/main/res/drawable/rn_edit_text_material.xml deleted file mode 100644 index 5c25e72..0000000 --- a/android/app/src/main/res/drawable/rn_edit_text_material.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 3941bea..0000000 --- a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 3941bea..0000000 --- a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp deleted file mode 100644 index 7fae0cc..0000000 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp deleted file mode 100644 index ac03dbf..0000000 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp deleted file mode 100644 index afa0a4e..0000000 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp deleted file mode 100644 index 78aaf45..0000000 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp deleted file mode 100644 index e1173a9..0000000 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp deleted file mode 100644 index c4f6e10..0000000 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp deleted file mode 100644 index 7a0f085..0000000 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp deleted file mode 100644 index ff086fd..0000000 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp deleted file mode 100644 index 6c2d40b..0000000 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp deleted file mode 100644 index 730e3fa..0000000 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp deleted file mode 100644 index f7f1d06..0000000 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp deleted file mode 100644 index 3452615..0000000 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp deleted file mode 100644 index b11a322..0000000 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp deleted file mode 100644 index 49a464e..0000000 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp deleted file mode 100644 index b51fd15..0000000 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android/app/src/main/res/values-night/colors.xml b/android/app/src/main/res/values-night/colors.xml deleted file mode 100644 index 3c05de5..0000000 --- a/android/app/src/main/res/values-night/colors.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml deleted file mode 100644 index f387b90..0000000 --- a/android/app/src/main/res/values/colors.xml +++ /dev/null @@ -1,6 +0,0 @@ - - #ffffff - #ffffff - #023c69 - #ffffff - \ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml deleted file mode 100644 index b879908..0000000 --- a/android/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - digital-pilates - automatic - contain - false - \ No newline at end of file diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml deleted file mode 100644 index b3c7754..0000000 --- a/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle deleted file mode 100644 index fa7b11e..0000000 --- a/android/build.gradle +++ /dev/null @@ -1,37 +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') - } -} - -def reactNativeAndroidDir = new File( - providers.exec { - workingDir(rootDir) - commandLine("node", "--print", "require.resolve('react-native/package.json')") - }.standardOutput.asText.get().trim(), - "../android" -) - -allprojects { - repositories { - maven { - // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm - url(reactNativeAndroidDir) - } - - google() - mavenCentral() - maven { url 'https://www.jitpack.io' } - } -} - -apply plugin: "expo-root-project" -apply plugin: "com.facebook.react.rootproject" diff --git a/android/gradle.properties b/android/gradle.properties deleted file mode 100644 index 38e3ce3..0000000 --- a/android/gradle.properties +++ /dev/null @@ -1,59 +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=false - -# 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 - -# Whether the app is configured to use edge-to-edge via the app config or `react-native-edge-to-edge` plugin -expo.edgeToEdgeEnabled=true \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index a4b76b9..0000000 Binary files a/android/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 37f853b..0000000 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/android/gradlew b/android/gradlew deleted file mode 100755 index f3b75f3..0000000 --- a/android/gradlew +++ /dev/null @@ -1,251 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat deleted file mode 100644 index 9b42019..0000000 --- a/android/gradlew.bat +++ /dev/null @@ -1,94 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/android/settings.gradle b/android/settings.gradle deleted file mode 100644 index ef069a4..0000000 --- a/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 = 'digital-pilates' - -expoAutolinking.useExpoVersionCatalog() - -include ':app' -includeBuild(expoAutolinking.reactNativeGradlePlugin) diff --git a/app.json b/app.json index 71386ef..7bd5ea3 100644 --- a/app.json +++ b/app.json @@ -68,8 +68,7 @@ "./assets/sounds/notification.wav" ] } - ], - "expo-background-task" + ] ], "experiments": { "typedRoutes": true diff --git a/app/(tabs)/statistics.tsx b/app/(tabs)/statistics.tsx index d39eeff..cc10ffd 100644 --- a/app/(tabs)/statistics.tsx +++ b/app/(tabs)/statistics.tsx @@ -13,9 +13,7 @@ import { Colors } from '@/constants/Colors'; import { getTabBarBottomPadding } from '@/constants/TabBar'; import { useAppDispatch, useAppSelector } from '@/hooks/redux'; import { useAuthGuard } from '@/hooks/useAuthGuard'; -import { useBackgroundTasks } from '@/hooks/useBackgroundTasks'; import { notificationService } from '@/services/notifications'; -import { backgroundTaskManager } from '@/services/backgroundTaskManager'; import { selectHealthDataByDate, setHealthData } from '@/store/healthSlice'; import { fetchDailyMoodCheckins, selectLatestMoodRecordByDate } from '@/store/moodSlice'; import { fetchDailyNutritionData, selectNutritionSummaryByDate } from '@/store/nutritionSlice'; @@ -38,8 +36,8 @@ import { ScrollView, StyleSheet, Text, - View, - TouchableOpacity + TouchableOpacity, + View } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; @@ -150,7 +148,6 @@ export default function ExploreScreen() { }); }, [userProfile]); - const { registerTask, isInitialized } = useBackgroundTasks(); // 心情相关状态 const dispatch = useAppDispatch(); const [isMoodLoading, setIsMoodLoading] = useState(false); @@ -416,49 +413,6 @@ export default function ExploreScreen() { }; }, [loadAllData, currentSelectedDate]); - useEffect(() => { - // 只有在后台任务管理器初始化完成后才注册任务 - if (isInitialized) { - console.log('后台任务管理器已初始化,开始注册健康数据任务...'); - registerTask({ - id: 'health-data-task', - name: 'health-data-task', - handler: async () => { - try { - console.log('后台任务:更新健康数据和检查压力水平...'); - - // 发送测试通知,验证后台任务是否执行 - await notificationService.sendImmediateNotification({ - title: '后台任务测试 🔔', - body: `任务执行时间: ${new Date().toLocaleTimeString('zh-CN')}`, - data: { - type: 'background_task_test', - timestamp: new Date().toISOString(), - }, - sound: true, - priority: 'high' - }); - - // 后台任务只更新健康数据,强制刷新以获取最新数据 - await loadHealthData(undefined, true); - - // 执行压力检查 - await checkStressLevelAndNotify(); - - // 执行喝水目标检查 - await checkWaterGoalAndNotify(); - } catch (error) { - console.error('健康数据任务执行失败:', error); - } - }, - }).then(() => { - console.log('健康数据任务注册成功'); - }).catch((error) => { - console.error('健康数据任务注册失败:', error); - }); - } - }, [isInitialized]); - // 检查压力水平并发送通知 const checkStressLevelAndNotify = React.useCallback(async () => { try { @@ -627,7 +581,7 @@ export default function ExploreScreen() { style={styles.debugButton} onPress={async () => { console.log('🔧 手动触发后台任务测试...'); - await backgroundTaskManager.debugExecuteBackgroundTask(); + // await backgroundTaskManager.triggerTaskForTesting(); }} > 🔧 diff --git a/app/_layout.tsx b/app/_layout.tsx index 6f9ffd0..4e63f06 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -8,7 +8,6 @@ import 'react-native-reanimated'; import PrivacyConsentModal from '@/components/PrivacyConsentModal'; import { useAppDispatch, useAppSelector } from '@/hooks/redux'; import { clearAiCoachSessionCache } from '@/services/aiCoachSession'; -import { backgroundTaskManager } from '@/services/backgroundTaskManager'; import { notificationService } from '@/services/notifications'; import { store } from '@/store'; import { rehydrateUser, setPrivacyAgreed } from '@/store/userSlice'; @@ -31,16 +30,6 @@ function Bootstrapper({ children }: { children: React.ReactNode }) { await dispatch(rehydrateUser()); setUserDataLoaded(true); }; - - const initializeBackgroundTasks = async () => { - try { - await backgroundTaskManager.initialize(); - console.log('后台任务管理器初始化成功'); - } catch (error) { - console.error('后台任务管理器初始化失败:', error); - } - }; - const initializeNotifications = async () => { try { // 初始化通知服务 @@ -52,7 +41,6 @@ function Bootstrapper({ children }: { children: React.ReactNode }) { }; loadUserData(); - initializeBackgroundTasks(); initializeNotifications(); // 冷启动时清空 AI 教练会话缓存 clearAiCoachSessionCache(); diff --git a/app/food/analysis-result.tsx b/app/food/analysis-result.tsx index 2b68f28..6b7e884 100644 --- a/app/food/analysis-result.tsx +++ b/app/food/analysis-result.tsx @@ -7,13 +7,14 @@ import { addDietRecord, type CreateDietRecordDto, type MealType } from '@/servic import { selectFoodRecognitionResult } from '@/store/foodRecognitionSlice'; import { Ionicons } from '@expo/vector-icons'; import dayjs from 'dayjs'; +import { Image } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; import { useLocalSearchParams, useRouter } from 'expo-router'; import React, { useEffect, useState } from 'react'; + import { ActivityIndicator, BackHandler, - Image, Modal, Pressable, ScrollView, @@ -297,7 +298,7 @@ export default function FoodAnalysisResultScreen() { {/* 预览提示图标 */} diff --git a/app/mood/calendar.tsx b/app/mood/calendar.tsx index 1958040..3fdbf16 100644 --- a/app/mood/calendar.tsx +++ b/app/mood/calendar.tsx @@ -3,21 +3,19 @@ import { Colors } from '@/constants/Colors'; import { useAppDispatch, useAppSelector } from '@/hooks/redux'; import { useColorScheme } from '@/hooks/useColorScheme'; import { useMoodData } from '@/hooks/useMoodData'; -import { getMoodOptions, MoodOption } from '@/services/moodCheckins'; +import { getMoodOptions } from '@/services/moodCheckins'; import { selectLatestMoodRecordByDate } from '@/store/moodSlice'; -import { Image } from 'react-native'; import dayjs from 'dayjs'; import { LinearGradient } from 'expo-linear-gradient'; import { router, useFocusEffect, useLocalSearchParams } from 'expo-router'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import { - Dimensions, - SafeAreaView, + Dimensions, Image, SafeAreaView, ScrollView, StyleSheet, Text, TouchableOpacity, - View, + View } from 'react-native'; const { width } = Dimensions.get('window'); @@ -60,7 +58,7 @@ export default function MoodCalendarScreen() { // 使用 useRef 来存储函数引用,避免依赖循环 const fetchMoodRecordsRef = useRef(fetchMoodRecords); const fetchMoodHistoryRecordsRef = useRef(fetchMoodHistoryRecords); - + // 更新 ref 值 fetchMoodRecordsRef.current = fetchMoodRecords; fetchMoodHistoryRecordsRef.current = fetchMoodHistoryRecords; @@ -73,7 +71,7 @@ export default function MoodCalendarScreen() { // 使用 Redux store 中的数据 const moodRecords = useAppSelector(state => state.mood.moodRecords); - + // 获取选中日期的数据 const selectedDateMood = useAppSelector(state => { if (!selectedDay) return null; @@ -191,46 +189,30 @@ export default function MoodCalendarScreen() { const moodRecord = dayRecords.length > 0 ? dayRecords[0] : null; const isToday = day === new Date().getDate() && - month === new Date().getMonth() + 1 && - year === new Date().getFullYear(); + month === new Date().getMonth() + 1 && + year === new Date().getFullYear(); if (moodRecord) { const mood = moodOptions.find(m => m.type === moodRecord.moodType); - const intensity = moodRecord.intensity; - const color = mood?.color || '#7a5af8'; - - // 计算圆环的填充比例 (0-1) - const fillRatio = intensity / 10; - + return ( - - - - - {intensity} - + + + ); } return ( - - + ); }; - // 使用统一的渐变背景色 - const backgroundGradientColors = [colorTokens.backgroundGradientStart, colorTokens.backgroundGradientEnd] as const; - return ( - + - + - + ); } @@ -104,8 +104,7 @@ export function FloatingFoodOverlay({ visible, onClose, mealType = 'dinner' }: F const styles = StyleSheet.create({ overlay: { flex: 1, - backgroundColor: 'rgba(0, 0, 0, 0.4)', - justifyContent: 'center', + justifyContent: 'flex-end', alignItems: 'center', }, backdrop: { @@ -117,6 +116,7 @@ const styles = StyleSheet.create({ }, container: { alignItems: 'center', + marginBottom: 40, }, blurContainer: { borderRadius: 20, @@ -132,9 +132,9 @@ const styles = StyleSheet.create({ alignItems: 'center', }, title: { - fontSize: 16, + fontSize: 12, fontWeight: '600', - color: '#333', + color: '#636161ff', }, menuGrid: { flexDirection: 'row', @@ -146,9 +146,9 @@ const styles = StyleSheet.create({ flex: 1, }, iconContainer: { - width: 48, - height: 48, - borderRadius: 24, + width: 40, + height: 40, + borderRadius: 20, alignItems: 'center', justifyContent: 'center', marginBottom: 8, @@ -162,7 +162,7 @@ const styles = StyleSheet.create({ elevation: 4, }, iconText: { - fontSize: 22, + fontSize: 16, }, menuText: { fontSize: 13, diff --git a/components/NutritionRecordCard.tsx b/components/NutritionRecordCard.tsx index 6174e2a..d83178c 100644 --- a/components/NutritionRecordCard.tsx +++ b/components/NutritionRecordCard.tsx @@ -3,10 +3,12 @@ import { useThemeColor } from '@/hooks/useThemeColor'; import { DietRecord } from '@/services/dietRecords'; import { Ionicons } from '@expo/vector-icons'; import dayjs from 'dayjs'; +import { Image } from 'expo-image'; import React, { useMemo, useRef, useState } from 'react'; -import { Alert, Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; +import { Alert, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import { RectButton, Swipeable } from 'react-native-gesture-handler'; + export type NutritionRecordCardProps = { record: DietRecord; onPress?: () => void; @@ -162,7 +164,11 @@ export function NutritionRecordCard({ {/* 左侧:食物图片 */} {record.imageUrl ? ( - + ) : ( )} diff --git a/docs/background-tasks-implementation.md b/docs/background-tasks-implementation.md deleted file mode 100644 index 1b4736d..0000000 --- a/docs/background-tasks-implementation.md +++ /dev/null @@ -1,284 +0,0 @@ -# 后台任务系统实现文档 - -## 概述 - -本项目已成功集成iOS后台任务支持,使用Expo官方的 `expo-task-manager` 和 `expo-background-task` 库。该系统提供了完整的后台任务管理功能,支持任务注册、执行、状态监控等。 - -## 技术栈 - -- **expo-task-manager**: Expo官方后台任务管理库 -- **expo-background-task**: Expo官方后台任务库 -- **React Native**: 跨平台移动应用框架 -- **TypeScript**: 类型安全的JavaScript超集 - -## 文件结构 - -``` -services/ -├── backgroundTaskManager.ts # 后台任务管理器核心逻辑 -├── backgroundTasks.ts # 示例任务定义 -hooks/ -├── useBackgroundTasks.ts # 后台任务自定义Hook -components/ -├── BackgroundTaskTest.tsx # 后台任务测试组件 -``` - -## 核心功能 - -### 1. 后台任务管理器 (services/backgroundTaskManager.ts) - -#### 主要特性 -- **单例模式**: 确保全局只有一个任务管理器实例 -- **任务注册**: 支持注册自定义后台任务 -- **状态管理**: 完整的任务状态跟踪和持久化 -- **错误处理**: 完善的错误处理和日志记录 -- **后台获取**: 自动注册后台获取任务 - -#### 核心方法 - -```typescript -// 初始化后台任务管理器 -await backgroundTaskManager.initialize(); - -// 注册自定义任务 -await backgroundTaskManager.registerTask({ - id: 'my-task', - name: '我的任务', - handler: async (data) => { - // 您的任务逻辑 - console.log('执行任务:', data); - }, - options: { - minimumInterval: 300, // 5分钟最小间隔 - stopOnTerminate: false, - startOnBoot: true, - } -}); - -// 手动执行任务 -await backgroundTaskManager.executeTask('my-task', { customData: 'value' }); - -// 执行所有任务 -const results = await backgroundTaskManager.executeAllTasks(); - -// 获取任务状态 -const status = backgroundTaskManager.getTaskStatus('my-task'); -``` - -### 2. 自定义Hook (hooks/useBackgroundTasks.ts) - -#### 主要特性 -- **状态管理**: 管理任务状态和初始化状态 -- **自动初始化**: 组件挂载时自动初始化任务管理器 -- **便捷接口**: 提供简化的任务操作方法 -- **实时更新**: 任务状态实时更新 - -#### 使用示例 - -```typescript -const { - isInitialized, - taskStatuses, - registeredTasks, - registerTask, - executeTask, - executeAllTasks, -} = useBackgroundTasks(); - -// 注册任务 -await registerTask({ - id: 'data-sync', - name: '数据同步', - handler: async () => { - // 数据同步逻辑 - } -}); - -// 执行任务 -await executeTask('data-sync'); -``` - -### 3. 示例任务 (services/backgroundTasks.ts) - -#### 预定义任务类型 -- **数据同步任务**: 同步用户数据、运动记录等 -- **健康数据更新任务**: 更新步数、心率等健康数据 -- **通知检查任务**: 检查是否需要发送通知 -- **缓存清理任务**: 清理过期缓存文件 -- **用户行为分析任务**: 分析用户使用模式 - -#### 创建自定义任务 - -```typescript -import { createCustomTask } from '@/services/backgroundTasks'; - -const myTask = createCustomTask( - 'my-custom-task', - '我的自定义任务', - async (data) => { - // 您的任务逻辑 - console.log('执行自定义任务:', data); - }, - { - minimumInterval: 120, // 2分钟 - stopOnTerminate: false, - startOnBoot: true, - } -); -``` - -## 使用指南 - -### 1. 基本使用 - -```typescript -import { useBackgroundTasks } from '@/hooks/useBackgroundTasks'; -import { createCustomTask } from '@/services/backgroundTasks'; - -const MyComponent = () => { - const { registerTask, executeTask } = useBackgroundTasks(); - - const handleCreateTask = async () => { - const task = createCustomTask( - 'my-task', - '我的任务', - async (data) => { - // 实现您的后台任务逻辑 - console.log('后台任务执行中...'); - - // 例如:数据同步 - await syncUserData(); - - // 例如:健康数据更新 - await updateHealthData(); - - // 例如:发送通知 - await checkAndSendNotifications(); - } - ); - - await registerTask(task); - }; - - const handleExecuteTask = async () => { - await executeTask('my-task', { customData: 'value' }); - }; - - return ( - -