lv 1 lună în urmă
părinte
comite
093909690b

+ 0 - 1
.yarnrc.yml

@@ -1 +0,0 @@
-nodeLinker: node-modules

+ 4 - 0
android/app/build.gradle

@@ -3,6 +3,10 @@ apply plugin: "org.jetbrains.kotlin.android"
 apply plugin: "com.facebook.react"
 
 def projectRoot = rootDir.getAbsoluteFile().getParentFile().getAbsolutePath()
+/**
+ * Run Proguard to shrink the Java bytecode in release builds.
+ */
+def enableProguardInReleaseBuilds = true
 
 /**
  * This is the configuration block to customize your React Native Android app.

+ 1 - 1
android/app/src/main/AndroidManifest.xml

@@ -19,7 +19,7 @@
     <meta-data android:name="expo.modules.updates.CODE_SIGNING_METADATA" android:value="{&quot;keyid&quot;:&quot;main&quot;,&quot;alg&quot;:&quot;rsa-v1_5-sha256&quot;}"/>
     <meta-data android:name="expo.modules.updates.ENABLED" android:value="true"/>
     <meta-data android:name="expo.modules.updates.EXPO_RUNTIME_VERSION" android:value="@string/expo_runtime_version"/>
-    <meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
+    <meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="NEVER"/>
     <meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="30000"/>
     <meta-data android:name="expo.modules.updates.EXPO_UPDATE_URL" android:value="https://updates-loan.ewaga.com/api/manifest"/>
     <activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode|smallestScreenSize" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true" android:screenOrientation="portrait">

+ 6 - 1
android/gradle.properties

@@ -60,4 +60,9 @@ EX_DEV_CLIENT_NETWORK_INSPECTOR=true
 # Use legacy packaging to compress native libraries in the resulting APK.
 expo.useLegacyPackaging=false
 
-ndk.dir=~/lv/Library/Android/sdk/ndk/27.1.12297006
+ndk.dir=~/lv/Library/Android/sdk/ndk/27.1.12297006
+android.compileSdkVersion=36
+android.targetSdkVersion=36
+android.buildToolsVersion=36.0.0
+android.enableMinifyInReleaseBuilds=true
+android.enableBundleCompression=true

+ 10 - 0
app.json

@@ -55,6 +55,15 @@
             "imageWidth": 76
           }
         }
+      ],
+      [
+      "expo-build-properties",
+        {
+          "android": {
+            "enableMinifyInReleaseBuilds": true,
+            "enableBundleCompression": true
+          }
+        }
       ]
     ],
     "experiments": {
@@ -64,6 +73,7 @@
     },
     "updates": {
       "enabled": true,
+      "checkAutomatically": "NEVER",
       "url": "https://updates-loan.ewaga.com/api/manifest",
       "fallbackToCacheTimeout": 30000,
       "codeSigningCertificate": "./code-signing/certificate.pem",

+ 8 - 4
ios/LoanAssistant.xcodeproj/project.pbxproj

@@ -301,10 +301,12 @@
 				"${PODS_ROOT}/Target Support Files/Pods-LoanAssistant/Pods-LoanAssistant-resources.sh",
 				"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle",
 				"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle",
+				"${PODS_CONFIGURATION_BUILD_DIR}/EXUpdates/EXUpdates.bundle",
 				"${PODS_CONFIGURATION_BUILD_DIR}/ExpoDevice/ExpoDevice_privacy.bundle",
 				"${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle",
 				"${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle",
 				"${PODS_CONFIGURATION_BUILD_DIR}/RNFSTurbo/RNFSTurbo_PrivacyInfo.bundle",
+				"${PODS_CONFIGURATION_BUILD_DIR}/ReachabilitySwift/ReachabilitySwift.bundle",
 				"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle",
 				"${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle",
 				"${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.bundle",
@@ -313,10 +315,12 @@
 			outputPaths = (
 				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle",
 				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXUpdates.bundle",
 				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoDevice_privacy.bundle",
 				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle",
 				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle",
 				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNFSTurbo_PrivacyInfo.bundle",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ReachabilitySwift.bundle",
 				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle",
 				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-cxxreact_privacy.bundle",
 				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SDWebImage.bundle",
@@ -368,11 +372,11 @@
 				);
 				OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
 				PRODUCT_BUNDLE_IDENTIFIER = com.cdloan.assistant;
-				PRODUCT_NAME = "LoanAssistant";
+				PRODUCT_NAME = LoanAssistant;
 				SWIFT_OBJC_BRIDGING_HEADER = "LoanAssistant/LoanAssistant-Bridging-Header.h";
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				SWIFT_VERSION = 5.0;
-				TARGETED_DEVICE_FAMILY = "1";
+				TARGETED_DEVICE_FAMILY = 1;
 				VERSIONING_SYSTEM = "apple-generic";
 			};
 			name = Debug;
@@ -399,10 +403,10 @@
 				);
 				OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
 				PRODUCT_BUNDLE_IDENTIFIER = com.cdloan.assistant;
-				PRODUCT_NAME = "LoanAssistant";
+				PRODUCT_NAME = LoanAssistant;
 				SWIFT_OBJC_BRIDGING_HEADER = "LoanAssistant/LoanAssistant-Bridging-Header.h";
 				SWIFT_VERSION = 5.0;
-				TARGETED_DEVICE_FAMILY = "1";
+				TARGETED_DEVICE_FAMILY = 1;
 				VERSIONING_SYSTEM = "apple-generic";
 			};
 			name = Release;

+ 1 - 1
ios/LoanAssistant/Supporting/Expo.plist

@@ -3,7 +3,7 @@
 <plist version="1.0">
   <dict>
     <key>EXUpdatesCheckOnLaunch</key>
-    <string>ALWAYS</string>
+    <string>NEVER</string>
     <key>EXUpdatesCodeSigningCertificate</key>
     <string>-----BEGIN CERTIFICATE-----&#xD;
 MIICzTCCAbWgAwIBAgIJVRhEHlaBbd3/MA0GCSqGSIb3DQEBCwUAMBAxDjAMBgNV&#xD;

+ 63 - 0
ios/Podfile.lock

@@ -1,6 +1,11 @@
 PODS:
+  - EASClient (55.0.5):
+    - ExpoModulesCore
   - EXConstants (55.0.12):
     - ExpoModulesCore
+  - EXJSONUtils (55.0.2)
+  - EXManifests (55.0.15):
+    - ExpoModulesCore
   - Expo (55.0.12):
     - ExpoModulesCore
     - hermes-engine
@@ -92,6 +97,37 @@ PODS:
     - ExpoModulesCore
   - ExpoWebBrowser (55.0.13):
     - ExpoModulesCore
+  - EXStructuredHeaders (55.0.2)
+  - EXUpdates (55.0.20):
+    - EASClient
+    - EXManifests
+    - ExpoModulesCore
+    - EXStructuredHeaders
+    - EXUpdatesInterface
+    - hermes-engine
+    - RCTRequired
+    - RCTTypeSafety
+    - ReachabilitySwift
+    - React-Core
+    - React-Core-prebuilt
+    - React-debug
+    - React-Fabric
+    - React-featureflags
+    - React-graphics
+    - React-ImageManager
+    - React-jsi
+    - React-NativeModulesApple
+    - React-RCTFabric
+    - React-renderercss
+    - React-rendererdebug
+    - React-utils
+    - ReactCodegen
+    - ReactCommon/turbomodule/bridging
+    - ReactCommon/turbomodule/core
+    - ReactNativeDependencies
+    - Yoga
+  - EXUpdatesInterface (55.1.5):
+    - ExpoModulesCore
   - FBLazyVector (0.83.4)
   - hermes-engine (0.14.1):
     - hermes-engine/Pre-built (= 0.14.1)
@@ -171,6 +207,7 @@ PODS:
     - FBLazyVector (= 0.83.4)
     - RCTRequired (= 0.83.4)
     - React-Core (= 0.83.4)
+  - ReachabilitySwift (5.2.4)
   - React (0.83.4):
     - React-Core (= 0.83.4)
     - React-Core/DevSupport (= 0.83.4)
@@ -2258,7 +2295,10 @@ PODS:
   - Yoga (0.0.0)
 
 DEPENDENCIES:
+  - EASClient (from `../node_modules/expo-eas-client/ios`)
   - EXConstants (from `../node_modules/expo-constants/ios`)
+  - EXJSONUtils (from `../node_modules/expo-json-utils/ios`)
+  - EXManifests (from `../node_modules/expo-manifests/ios`)
   - Expo (from `../node_modules/expo`)
   - ExpoAsset (from `../node_modules/expo-asset/ios`)
   - ExpoDevice (from `../node_modules/expo-device/ios`)
@@ -2277,6 +2317,9 @@ DEPENDENCIES:
   - ExpoSymbols (from `../node_modules/expo-symbols/ios`)
   - ExpoSystemUI (from `../node_modules/expo-system-ui/ios`)
   - ExpoWebBrowser (from `../node_modules/expo-web-browser/ios`)
+  - EXStructuredHeaders (from `../node_modules/expo-structured-headers/ios`)
+  - EXUpdates (from `../node_modules/expo-updates/ios`)
+  - EXUpdatesInterface (from `../node_modules/expo-updates-interface/ios`)
   - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
   - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
   - NitroMmkv (from `../node_modules/react-native-mmkv`)
@@ -2366,14 +2409,21 @@ SPEC REPOS:
     - libdav1d
     - libwebp
     - MMKVCore
+    - ReachabilitySwift
     - SDWebImage
     - SDWebImageAVIFCoder
     - SDWebImageSVGCoder
     - SDWebImageWebPCoder
 
 EXTERNAL SOURCES:
+  EASClient:
+    :path: "../node_modules/expo-eas-client/ios"
   EXConstants:
     :path: "../node_modules/expo-constants/ios"
+  EXJSONUtils:
+    :path: "../node_modules/expo-json-utils/ios"
+  EXManifests:
+    :path: "../node_modules/expo-manifests/ios"
   Expo:
     :path: "../node_modules/expo"
   ExpoAsset:
@@ -2410,6 +2460,12 @@ EXTERNAL SOURCES:
     :path: "../node_modules/expo-system-ui/ios"
   ExpoWebBrowser:
     :path: "../node_modules/expo-web-browser/ios"
+  EXStructuredHeaders:
+    :path: "../node_modules/expo-structured-headers/ios"
+  EXUpdates:
+    :path: "../node_modules/expo-updates/ios"
+  EXUpdatesInterface:
+    :path: "../node_modules/expo-updates-interface/ios"
   FBLazyVector:
     :path: "../node_modules/react-native/Libraries/FBLazyVector"
   hermes-engine:
@@ -2575,7 +2631,10 @@ EXTERNAL SOURCES:
     :path: "../node_modules/react-native/ReactCommon/yoga"
 
 SPEC CHECKSUMS:
+  EASClient: 8077eb9af46cc7ad3ad216cec12d2bf5763cca13
   EXConstants: 97e4a5b18e38331acce952f0e4a6817b0418408f
+  EXJSONUtils: 04bc3807d351331a5fe154ce1da596ec15d99169
+  EXManifests: a4e214e6a66372e662ef38adf2afca773bbd0c18
   Expo: 04f5391a7affe866de03b928cf40d683ec4a3463
   ExpoAsset: 3636e70a874487efd0a677f6c064dbc9fc8f174b
   ExpoDevice: 221ea185adcd21845c5911d370b03af84a493413
@@ -2594,6 +2653,9 @@ SPEC CHECKSUMS:
   ExpoSymbols: 8b63e859ba013df1f2fc666f535fddb3d5270569
   ExpoSystemUI: 8f1d5aac902dada8df8cc2944563c56e2179fcb9
   ExpoWebBrowser: f81f8e35afd7f61ec809622ef354c579a3b580e2
+  EXStructuredHeaders: 93ad4f31a2eec124428675e28aca11de243ee1e4
+  EXUpdates: 1457ed285cd7c5e9d4c388ace518af55fa4b21ec
+  EXUpdatesInterface: 39d05400a9226c438565dbaaa8447b5f0672ba45
   FBLazyVector: 061f518bbd81677ed8a8317e2ae60b8779495808
   hermes-engine: e4ecc5957f671df3fa4828a33b435bd6955c25a5
   libavif: 84bbb62fb232c3018d6f1bab79beea87e35de7b7
@@ -2607,6 +2669,7 @@ SPEC CHECKSUMS:
   RCTSwiftUI: 395b65655229fa2006415207adcfcb6e35dc78ed
   RCTSwiftUIWrapper: 91351441a592e07e09a2f94d2cbdf088fde7e2e1
   RCTTypeSafety: dfd539968f0b82892da72bb32b7d8aa2f5789622
+  ReachabilitySwift: 32793e867593cfc1177f5d16491e3a197d2fccda
   React: 3e14066ac707b3e369d09e2e923d8bee7f8c33ff
   React-callinvoker: bd7959a24564feaf5e4c8c11789e64884da13482
   React-Core: f060f4e14e9301685ce63ea65fbe903bf3397e45

+ 3 - 1
ios/Podfile.properties.json

@@ -1,4 +1,6 @@
 {
   "expo.jsEngine": "hermes",
-  "EX_DEV_CLIENT_NETWORK_INSPECTOR": "true"
+  "EX_DEV_CLIENT_NETWORK_INSPECTOR": "true",
+  "ios.forceStaticLinking": "[]",
+  "apple.privacyManifestAggregationEnabled": "true"
 }

+ 1 - 0
package.json

@@ -24,6 +24,7 @@
     "@react-navigation/native": "^7.1.33",
     "axios": "^1.14.0",
     "expo": "~55.0.12",
+    "expo-build-properties": "~55.0.13",
     "expo-constants": "~55.0.12",
     "expo-device": "~55.0.13",
     "expo-font": "~55.0.6",

Fișier diff suprimat deoarece este prea mare
+ 5 - 0
patches/@ant-design__react-native.patch


+ 22 - 2
pnpm-lock.yaml

@@ -4,6 +4,11 @@ settings:
   autoInstallPeers: true
   excludeLinksFromLockfile: false
 
+patchedDependencies:
+  '@ant-design/react-native':
+    hash: 7965687d5a5085e368e3df572427f090ef12d472bfb5c04c49709a146aa6639c
+    path: patches/@ant-design__react-native.patch
+
 importers:
 
   .:
@@ -13,7 +18,7 @@ importers:
         version: 2.3.2(react@19.2.0)
       '@ant-design/react-native':
         specifier: ^5.4.3
-        version: 5.4.3(@ant-design/icons-react-native@2.3.2(react@19.2.0))(@babel/preset-env@7.29.2(@babel/core@7.29.0))(react-dom@19.2.0(react@19.2.0))(react-native-gesture-handler@2.30.1(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-reanimated@4.2.1(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)
+        version: 5.4.3(patch_hash=7965687d5a5085e368e3df572427f090ef12d472bfb5c04c49709a146aa6639c)(@ant-design/icons-react-native@2.3.2(react@19.2.0))(@babel/preset-env@7.29.2(@babel/core@7.29.0))(react-dom@19.2.0(react@19.2.0))(react-native-gesture-handler@2.30.1(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-reanimated@4.2.1(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)
       '@react-navigation/bottom-tabs':
         specifier: ^7.15.5
         version: 7.15.9(@react-navigation/native@7.2.2(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-screens@4.23.0(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)
@@ -29,6 +34,9 @@ importers:
       expo:
         specifier: ~55.0.12
         version: 55.0.12(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.9)(expo-router@55.0.11)(react-dom@19.2.0(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3)
+      expo-build-properties:
+        specifier: ~55.0.13
+        version: 55.0.13(expo@55.0.12)
       expo-constants:
         specifier: ~55.0.12
         version: 55.0.12(expo@55.0.12)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(typescript@5.9.3)
@@ -2643,6 +2651,11 @@ packages:
       react: '*'
       react-native: '*'
 
+  expo-build-properties@55.0.13:
+    resolution: {integrity: sha512-UYZhUKyh7YQhbJdkBvo68WUQ7fOtZeSV7F8kfYkjEiN/ADRHG0WfEIiddvGfi9cH/5iwpptv/+Lu5cx6uvfegA==}
+    peerDependencies:
+      expo: '*'
+
   expo-constants@55.0.12:
     resolution: {integrity: sha512-e2oxzvPyBv0t51o/lNuiiBtYFQcv3rWnTUvIH0GXRjHkg8LHHePly1vJ5oGg5KO2v8qprleDp9g6s5YD0MIUtQ==}
     peerDependencies:
@@ -5198,7 +5211,7 @@ snapshots:
     dependencies:
       react: 19.2.0
 
-  '@ant-design/react-native@5.4.3(@ant-design/icons-react-native@2.3.2(react@19.2.0))(@babel/preset-env@7.29.2(@babel/core@7.29.0))(react-dom@19.2.0(react@19.2.0))(react-native-gesture-handler@2.30.1(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-reanimated@4.2.1(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)':
+  '@ant-design/react-native@5.4.3(patch_hash=7965687d5a5085e368e3df572427f090ef12d472bfb5c04c49709a146aa6639c)(@ant-design/icons-react-native@2.3.2(react@19.2.0))(@babel/preset-env@7.29.2(@babel/core@7.29.0))(react-dom@19.2.0(react@19.2.0))(react-native-gesture-handler@2.30.1(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-reanimated@4.2.1(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)':
     dependencies:
       '@ant-design/icons-react-native': 2.3.2(react@19.2.0)
       '@bang88/react-native-ultimate-listview': 4.1.1
@@ -8429,6 +8442,13 @@ snapshots:
       - supports-color
       - typescript
 
+  expo-build-properties@55.0.13(expo@55.0.12):
+    dependencies:
+      '@expo/schema-utils': 55.0.3
+      expo: 55.0.12(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.9)(expo-router@55.0.11)(react-dom@19.2.0(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3)
+      resolve-from: 5.0.0
+      semver: 7.7.4
+
   expo-constants@55.0.12(expo@55.0.12)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(typescript@5.9.3):
     dependencies:
       '@expo/config': 55.0.13(typescript@5.9.3)

+ 3 - 0
pnpm-workspace.yaml

@@ -1 +1,4 @@
 nodeLinker: hoisted
+
+patchedDependencies:
+  '@ant-design/react-native': patches/@ant-design__react-native.patch

+ 2 - 2
src/app/(tabs)/index.tsx

@@ -257,7 +257,7 @@ export default function HomeScreen() {
         </View>
       </ScrollView>
 
-      <View className="absolute bottom-8 right-7">
+      <View className="absolute bottom-8 ios:bottom-28 right-7">
         <Pressable
           className="h-16 w-16 items-center justify-center rounded-full bg-primary-container shadow-xl"
           style={({ pressed }) => ({
@@ -265,7 +265,7 @@ export default function HomeScreen() {
             transform: [{ scale: pressed ? 0.94 : 1 }],
           })}
         >
-          <Ionicons name="add" size={28} color="#ffffff" />
+          <Ionicons name="sparkles" size={28} color="#ffffff" />
         </Pressable>
       </View>
     </SafeAreaView>

+ 3 - 6
src/app/+html.tsx

@@ -29,6 +29,7 @@ export default function Root({ children }: PropsWithChildren) {
 body {
   min-height: 100%;
   margin: 0;
+  
 }
 
 * {
@@ -41,22 +42,18 @@ body {
     height: 0;
   }
 
+  @media (min-width: 448px) {
   body {
-    min-height: 100vh;
     background:
       radial-gradient(circle at top, rgba(96, 165, 250, 0.18), transparent 32%),
       linear-gradient(180deg, #172554 0%, #0f172a 55%, #020617 100%);
-  }
-
+    }
   #root {
     max-width: 448px;
     margin: 0 auto;
     background: #ffffff;
-  }
 
-  @media (min-width: 448px) {
 
-    #root {
       min-height: 100vh;
       overflow: hidden;
       border-radius: 24px;

+ 19 - 8
src/app/_layout.tsx

@@ -8,7 +8,7 @@ import * as updates from 'expo-updates';
 
 import { AnimatedSplashOverlay } from '@/components/animated-icon';
 import { AuthProvider } from '@/utils/auth';
-import { Provider, Toast } from '@ant-design/react-native';
+import { Modal, Provider, Toast } from '@ant-design/react-native';
 import antdDefaultTheme from '@ant-design/react-native/lib/style/themes/default';
 import { useFonts } from 'expo-font';
 import { Stack } from 'expo-router';
@@ -38,7 +38,6 @@ export default function RootLayout() {
     antoutline: require('@ant-design/icons-react-native/fonts/antoutline.ttf'),
   });
   useEffect(() => {
-const  l = Toast.loading("正在更新 ...");
     
     (async ()=> {
       
@@ -47,14 +46,26 @@ const  l = Toast.loading("正在更新 ...");
       const res = await updates.checkForUpdateAsync();
       // alert(JSON.stringify(res))
       if (res.isAvailable) {
-        const u = await updates.fetchUpdateAsync();
-        await updates.reloadAsync();
-      } else {
-        Toast.remove(l);
+
+        Modal.alert("提示", "发现新版本", [
+          {
+            text: '确认',
+            onPress: async ()=> {
+              const  l = Toast.loading("正在更新 ...");
+              try {
+                await updates.fetchUpdateAsync();
+                await updates.reloadAsync();
+              } catch(e) {
+                Modal.alert("提示", "更新遇到问题" );
+              }
+              Toast.remove(l);
+            }
+          }
+        ], ()=>false);
       }
+        
       } catch(e) {
-Toast.remove(l);
-        alert(e?.message || e+"");
+        // alert(e?.message || e+"");
       }
       setIniting(false);
       

+ 12 - 24
src/app/sign-in.tsx

@@ -1,5 +1,5 @@
-import { signIn, useAuthContext } from '@/utils/auth';
-import { Toast } from '@ant-design/react-native';
+import { smsSignIn, useAuthContext } from '@/utils/auth';
+import { Button, Toast } from '@ant-design/react-native';
 import { Ionicons } from '@expo/vector-icons';
 import { router, useLocalSearchParams } from 'expo-router';
 import React, { useRef, useState } from 'react';
@@ -32,7 +32,7 @@ export default function SignInScreen({
   const [agreed, setAgreed] = useState(false);
   const [flushAgree, setFlushAgree] = useState(false);
   const [loading, setLoading] = useState(false);
-  const [phone, setPhone] = useState('');
+  const [mobile, setMobile] = useState('');
   const [code, setCode] = useState('');
   const [password, setPassword] = useState('');
 
@@ -40,7 +40,7 @@ export default function SignInScreen({
   const { setToken } = useAuthContext();
   const scrollView = useRef<ScrollView>(null);
   const handleSendCode = () => {
-    if (phone.trim().length !== 11) {
+    if (mobile.trim().length !== 11) {
       Toast.fail('请先输入 11 位手机号');
       return;
     }
@@ -50,7 +50,7 @@ export default function SignInScreen({
   const handleLogin = async () => {
    
     setFlushAgree(false);
-    if (phone.trim().length !== 11) {
+    if (mobile.trim().length !== 11) {
       Toast.fail('请输入正确的手机号');
       callback?.(false);
       return;
@@ -81,7 +81,7 @@ export default function SignInScreen({
     const toastKey = Toast.loading('正在登录...');
 
     try {
-      const token = await signIn(1);
+      const token = await smsSignIn(mobile, code);
       setToken(token);
       Toast.success('登录成功');
       callback?.(true);
@@ -126,7 +126,7 @@ export default function SignInScreen({
                   <Ionicons name="business" size={30} color="#ffffff" />
                 </View>
                 <Text className="mb-2 text-4xl font-extrabold tracking-tight text-on-surface">
-                  欢迎-回来 ****
+                  欢迎回来
                 </Text>
                 <Text className="text-base font-medium leading-7 text-on-surface-variant">
                   登录贷款助手,开启您的
@@ -174,8 +174,8 @@ export default function SignInScreen({
                       style={{ width: StyleSheet.hairlineWidth }}
                     />
                     <TextInput
-                      value={phone}
-                      onChangeText={setPhone}
+                      value={mobile}
+                      onChangeText={setMobile}
                       editable={!loading}
                       keyboardType="phone-pad"
                       maxLength={11}
@@ -228,22 +228,10 @@ export default function SignInScreen({
               </View>
 
               <View className="mb-8 gap-4">
-                <Pressable
-                  disabled={loading}
-                  onPress={handleLogin}
-                  className="items-center justify-center rounded-2xl bg-primary-container py-5 shadow-lg"
-                  style={({ pressed }) => ({
-                    opacity: loading ? 0.6 : pressed ? 0.88 : 1,
-                    transform: [{ scale: pressed ? 0.985 : 1 }],
-                  })}
-                >
-                  <Text className="text-2xl font-extrabold text-on-primary">
-                    {loading ? '登录中...' : '立即登录'}
-                  </Text>
-                </Pressable>
+                <Button type='primary' onPress={handleLogin}>登录</Button>
 
                 <View className="flex-row items-center justify-between px-3">
-                  <Pressable hitSlop={8}>
+                  <Pressable hitSlop={8} onPress={() => router.push('/sign-up')}>
                     <Text className="text-lg font-medium text-on-surface-variant">
                       注册账号
                     </Text>
@@ -288,7 +276,7 @@ export default function SignInScreen({
                 ))}
               </View>
 
-              <View className={`flex-row items-start gap-3 px-2 rounded-md border-transparent border-2 transition-colors delay-300 ${flushAgree ? ' border-primary/50' : ''}`}>
+              <View className={`flex-row items-start gap-3 px-2 rounded-md border-transparent border-2 transition delay-300 ${flushAgree ? ' border-primary/50' : ''}`}>
                 <Pressable
                   onPress={() => setAgreed((value) => !value)}
                   hitSlop={8}

+ 264 - 0
src/app/sign-up.tsx

@@ -0,0 +1,264 @@
+import { Button, Toast } from '@ant-design/react-native';
+import { Ionicons } from '@expo/vector-icons';
+import { router } from 'expo-router';
+import React, { useRef, useState } from 'react';
+import {
+  KeyboardAvoidingView,
+  Platform,
+  Pressable,
+  ScrollView,
+  StyleSheet,
+  Text,
+  TextInput,
+  View,
+} from 'react-native';
+import { SafeAreaView } from 'react-native-safe-area-context';
+
+function FieldLabel({ children }: { children: React.ReactNode }) {
+  return (
+    <Text className="mb-2 ml-1 text-xs font-bold uppercase tracking-widest text-outline">
+      {children}
+    </Text>
+  );
+}
+
+export default function SignUpScreen() {
+  const [mobile, setMobile] = useState('');
+  const [password, setPassword] = useState('');
+  const [email, setEmail] = useState('');
+  const [name, setName] = useState('');
+  const [organization, setOrganization] = useState('');
+  const [agreed, setAgreed] = useState(false);
+  const [flushAgree, setFlushAgree] = useState(false);
+  const [loading, setLoading] = useState(false);
+  const scrollView = useRef<ScrollView>(null);
+
+  const handleRegister = async () => {
+    setFlushAgree(false);
+
+    if (mobile.trim().length !== 11) {
+      Toast.fail('请输入正确的手机号');
+      return;
+    }
+
+    if (password.trim().length < 6) {
+      Toast.fail('请输入不少于 6 位的登录密码');
+      return;
+    }
+
+    if (!email.includes('@')) {
+      Toast.fail('请输入正确的邮箱');
+      return;
+    }
+
+    if (!name.trim()) {
+      Toast.fail('请输入姓名');
+      return;
+    }
+
+    if (!organization.trim()) {
+      Toast.fail('请输入所属机构');
+      return;
+    }
+
+    if (!agreed) {
+      Toast.fail('请先阅读并同意协议');
+      setFlushAgree(true);
+      scrollView.current?.scrollToEnd({ animated: true });
+      return;
+    }
+
+    setLoading(true);
+    const toastKey = Toast.loading('正在创建账号...');
+
+    try {
+      await new Promise((resolve) => setTimeout(resolve, 600));
+      Toast.success('注册成功,请登录');
+      router.replace('/sign-in');
+    } catch (error) {
+      console.error('注册失败:', error);
+      Toast.fail('注册失败,请稍后重试');
+    } finally {
+      setLoading(false);
+      Toast.remove(toastKey);
+    }
+  };
+
+  return (
+    <SafeAreaView className="flex-1 bg-surface">
+      <KeyboardAvoidingView
+        className="flex-1"
+        behavior={Platform.OS === 'ios' ? 'padding' : undefined}
+      >
+        <ScrollView
+          ref={scrollView}
+          className="flex-1"
+          contentContainerClassName="px-8 pt-12 pb-10"
+          contentContainerStyle={{ flexGrow: 1 }}
+          keyboardShouldPersistTaps="handled"
+          showsVerticalScrollIndicator={false}
+        >
+          <View className="absolute -top-24 -right-24 h-96 w-96 rounded-full bg-primary-container/10" />
+          <View className="absolute top-32 -left-20 h-64 w-64 rounded-full bg-secondary-container/20" />
+          <View className="absolute -bottom-16 right-0 h-48 w-48 rounded-full bg-primary-fixed/50" />
+
+          <View className="flex-1 justify-between">
+            <View>
+              <View className="mb-12">
+                <View className="mb-6 h-16 w-16 items-center justify-center rounded-2xl bg-primary-container shadow-lg">
+                  <Ionicons name="sparkles" size={30} color="#ffffff" />
+                </View>
+                <Text className="mb-2 text-4xl font-extrabold tracking-tight text-on-surface">
+                  创建账号
+                </Text>
+                <Text className="text-base font-medium leading-7 text-on-surface-variant">
+                  完善基础信息,开启您的智能借贷助手工作台
+                </Text>
+              </View>
+
+              <View className="mb-8 rounded-2xl bg-surface-container-lowest p-3 shadow-sm">
+                <View className="px-2 pb-2">
+                  <FieldLabel>手机号码</FieldLabel>
+                  <View className="mb-5 flex-row items-center rounded-2xl bg-surface-container-low px-5 py-4">
+                    <Text className="text-2xl font-bold text-on-surface">+86</Text>
+                    <View
+                      className="mx-4 h-5 bg-outline-variant/40"
+                      style={{ width: StyleSheet.hairlineWidth }}
+                    />
+                    <TextInput
+                      value={mobile}
+                      onChangeText={setMobile}
+                      editable={!loading}
+                      keyboardType="phone-pad"
+                      maxLength={11}
+                      placeholder="请输入手机号"
+                      placeholderTextColor="#9ca3af"
+                      className="flex-1 p-0 text-xl font-medium text-on-surface"
+                    />
+                    <Ionicons
+                      name="phone-portrait-outline"
+                      size={22}
+                      color="#c3c6d7"
+                    />
+                  </View>
+
+                  <FieldLabel>登录密码</FieldLabel>
+                  <View className="mb-5 flex-row items-center rounded-2xl bg-surface-container-low px-5 py-4">
+                    <TextInput
+                      value={password}
+                      onChangeText={setPassword}
+                      editable={!loading}
+                      secureTextEntry
+                      maxLength={20}
+                      placeholder="请输入不少于 6 位的密码"
+                      placeholderTextColor="#9ca3af"
+                      className="flex-1 p-0 text-xl font-medium text-on-surface"
+                    />
+                    <Ionicons name="lock-closed-outline" size={22} color="#c3c6d7" />
+                  </View>
+
+                  <FieldLabel>邮箱</FieldLabel>
+                  <View className="mb-5 flex-row items-center rounded-2xl bg-surface-container-low px-5 py-4">
+                    <TextInput
+                      value={email}
+                      onChangeText={setEmail}
+                      editable={!loading}
+                      keyboardType="email-address"
+                      autoCapitalize="none"
+                      placeholder="请输入常用邮箱"
+                      placeholderTextColor="#9ca3af"
+                      className="flex-1 p-0 text-xl font-medium text-on-surface"
+                    />
+                    <Ionicons name="mail-outline" size={22} color="#c3c6d7" />
+                  </View>
+
+                  <FieldLabel>姓名</FieldLabel>
+                  <View className="mb-5 flex-row items-center rounded-2xl bg-surface-container-low px-5 py-4">
+                    <TextInput
+                      value={name}
+                      onChangeText={setName}
+                      editable={!loading}
+                      placeholder="请输入姓名"
+                      placeholderTextColor="#9ca3af"
+                      className="flex-1 p-0 text-xl font-medium text-on-surface"
+                    />
+                    <Ionicons name="person-outline" size={22} color="#c3c6d7" />
+                  </View>
+
+                  <FieldLabel>所属机构</FieldLabel>
+                  <View className="flex-row items-center rounded-2xl bg-surface-container-low px-5 py-4">
+                    <TextInput
+                      value={organization}
+                      onChangeText={setOrganization}
+                      editable={!loading}
+                      placeholder="请输入所属机构"
+                      placeholderTextColor="#9ca3af"
+                      className="flex-1 p-0 text-xl font-medium text-on-surface"
+                    />
+                    <Ionicons name="business-outline" size={22} color="#c3c6d7" />
+                  </View>
+                </View>
+              </View>
+
+              <View className="mb-8 gap-4">
+                <Button type="primary" loading={loading} onPress={handleRegister}>
+                  注册账号
+                </Button>
+
+                <View className="flex-row items-center justify-between px-3">
+                  <Pressable hitSlop={8} onPress={() => router.back()}>
+                    <Text className="text-lg font-medium text-on-surface-variant">
+                      返回登录
+                    </Text>
+                  </Pressable>
+                  <Pressable hitSlop={8}>
+                    <Text className="text-lg font-medium text-on-surface-variant">
+                      遇到问题?
+                    </Text>
+                  </Pressable>
+                </View>
+              </View>
+            </View>
+
+            <View className="pt-4">
+              <View
+                className={`flex-row items-start gap-3 rounded-md border-2 border-transparent px-2 transition delay-300 ${
+                  flushAgree ? 'border-primary/50' : ''
+                }`}
+              >
+                <Pressable
+                  onPress={() => setAgreed((value) => !value)}
+                  hitSlop={8}
+                  className="pt-1"
+                >
+                  <View
+                    className={`h-6 w-6 items-center justify-center rounded-full ${
+                      agreed
+                        ? 'border-primary bg-primary'
+                        : 'border-outline-variant bg-surface-container-low'
+                    }`}
+                  >
+                    {agreed ? (
+                      <Ionicons name="checkmark" size={14} color="#ffffff" />
+                    ) : null}
+                  </View>
+                </Pressable>
+                <Text className="flex-1 text-sm leading-7 text-on-surface-variant">
+                  注册即代表您已阅读并同意
+                  <Text className="cursor-pointer font-semibold text-primary">
+                    《用户服务协议》
+                  </Text>
+                  、
+                  <Text className="cursor-pointer font-semibold text-primary">
+                    《隐私政策》
+                  </Text>
+                  ,并授权该应用用于账户创建与服务通知。
+                </Text>
+              </View>
+            </View>
+          </View>
+        </ScrollView>
+      </KeyboardAvoidingView>
+    </SafeAreaView>
+  );
+}

+ 3 - 4
src/components/app-tabs.tsx

@@ -1,18 +1,17 @@
 import { NativeTabs } from 'expo-router/unstable-native-tabs';
 import React from 'react';
-import { useColorScheme } from 'react-native';
 
 import { Colors } from '@/constants/theme';
 
 export default function AppTabs() {
-  const scheme = useColorScheme();
-  const colors = Colors[scheme === 'unspecified' ? 'light' : scheme];
+  const colors = Colors;
 
   return (
     <NativeTabs
       backgroundColor={colors.background}
       indicatorColor={colors.backgroundElement}
-      labelStyle={{ selected: { color: colors.text } }}>
+      tintColor={colors.brand_primary}
+>
       <NativeTabs.Trigger name="index">
         <NativeTabs.Trigger.Label>首页</NativeTabs.Trigger.Label>
         <NativeTabs.Trigger.Icon

+ 3 - 3
src/components/app-tabs.web.tsx

@@ -12,7 +12,7 @@ import { Pressable, StyleSheet, View } from 'react-native';
 import { ThemedText } from './themed-text';
 import { ThemedView } from './themed-view';
 
-import { Colors, MaxContentWidth, Spacing } from '@/constants/theme';
+import { MaxContentWidth, Spacing } from '@/constants/theme';
 import { Image, ImageSource } from 'expo-image';
 
 import antdDefaultTheme from '@ant-design/react-native/lib/style/themes/default';
@@ -21,7 +21,7 @@ import antdDefaultTheme from '@ant-design/react-native/lib/style/themes/default'
 export default function AppTabs() {
   return (
     <Tabs>
-      <TabSlot style={{ height: '100%', paddingBottom: 44 }} />
+      <TabSlot detachInactiveScreens style={{ height: '100%', paddingBottom: 44 }} />
       <TabList asChild>
         <CustomTabList>
           <TabTrigger name="home" href="/" asChild>
@@ -62,7 +62,7 @@ export function TabButton({ children, isFocused, icon, ...props }: TabTriggerSlo
 
 export function CustomTabList(props: TabListProps) {
   // const scheme = "light"
-  const colors = Colors['light'];
+
 
   return (
     <View {...props} style={styles.tabListContainer}>

+ 1 - 1
src/config.json

@@ -1,7 +1,7 @@
 
 {
     "api": {
-        "url": "http://localhost:8080/v1/",
+        "url": "https://loan.ewaga.com/api/v1/",
         "timeout": 15000
     },
     

+ 10 - 16
src/constants/theme.ts

@@ -7,24 +7,18 @@ import '@/global.css';
 
 import { Platform } from 'react-native';
 
+
+import antdDefaultTheme from '@ant-design/react-native/lib/style/themes/default';
 export const Colors = {
-  light: {
-    text: '#000000',
-    background: '#ffffff',
-    backgroundElement: '#F0F0F3',
-    backgroundSelected: '#E0E1E6',
-    textSecondary: '#60646C',
-  },
-  dark: {
-    text: '#ffffff',
-    background: '#000000',
-    backgroundElement: '#212225',
-    backgroundSelected: '#2E3135',
-    textSecondary: '#B0B4BA',
-  },
-} as const;
+    text: antdDefaultTheme.color_text_base,
+    background: antdDefaultTheme.fill_base,
+    backgroundElement:  antdDefaultTheme.fill_body,
+    backgroundSelected: antdDefaultTheme.fill_tap,
+    textSecondary: antdDefaultTheme.color_text_paragraph,
+    ...antdDefaultTheme,
+  } as const;
 
-export type ThemeColor = keyof typeof Colors.light & keyof typeof Colors.dark;
+export type ThemeColor = keyof typeof Colors;
 
 export const Fonts = Platform.select({
   ios: {

+ 53 - 33
src/utils/api.ts

@@ -1,24 +1,26 @@
 // import { getAccessToken } from '@/apis/auth';
-const {api: apiConfig, jsVersion} = require('@/config') as AppConfig;
 import axios from 'axios';
 import Constants from 'expo-constants';
 import { Platform } from 'react-native';
 import { getAccessToken } from './auth';
+const {api: apiConfig, jsVersion} = require('@/config.json') as AppConfig;
 
 
 
 
-
-export class ApiError extends Error {
+export class HttpError {
     public get data() {
         return this._data;
     }
     name: string = 'ApiError';
-    constructor(message: string, public code: number, private _data?: any) {
+    constructor(private message: string, public code: number, private _data?: any) {
         if (__DEV__) {
             message = `${message} (code: ${code})`;
         }
-        super(message);
+    }
+
+    toString() {
+        return this.message;
     }
 
     static is(err: any) {
@@ -33,6 +35,15 @@ export class ApiError extends Error {
     static is3xx(err: any) {
         return err instanceof ApiError && err.code >= 300 && err.code < 400 ? err as ApiError : null;
     }
+
+    static isApiError(err: any) {
+        return err instanceof ApiError;
+    }
+}
+
+
+export class ApiError extends HttpError {
+    
 }
 
 
@@ -123,49 +134,58 @@ if (__DEV__) {
 }
 
 
-export async function request<T>(api: string, method: 'get' | 'post' | 'put' | 'delete', params: Record<string, any>, data: any): Promise<T> {
+async function request<T>(url: string, method: 'get' | 'post' | 'put' | 'delete', params: Record<string, any>, data: any): Promise<T> {
     const headers: Record<string, string> = {};
     let token = getAccessToken();
-    if (token?.accessToken) {
-        headers['Authorization'] = `Bearer ${token.accessToken}`;
+    if (token?.token) {
+        headers['Authorization'] = `Bearer ${token.token}`;
     }
     try {
-    const response = await apiClient.request<ApiResponse<T>>({
-        url: api,
-        method,
-        params,
-        data,
-        headers,
-    });
-
-    const res = response.data;
-    if (`${res?.code}` !== '1') {
-        throw new ApiError(res?.message || response.statusText, res?.code || response.status, res?.data);
-    }
-    return res?.data as T;
-} catch (error) {
-    if (error instanceof ApiError) {
-        throw error;
-    }
+        const response = await apiClient.request<ApiResponse<T>>({
+            url,
+            method,
+            params,
+            data,
+            headers,
+        });
+        if (200 !== response.status) {
+            throw new HttpError(response.statusText  || 'http error', response.status);
+        }
+        const res = response.data;
+        
 
-    // @ts-ignore
-    throw new ApiError(error?.message || error?.toString() || "unknown error", 500);
-}
+        if (`${res?.code}` !== '1') {
+            throw new ApiError(res?.message || response.statusText, res?.code||0, res?.data);
+        }
+        return res?.data as T;
+    } catch (error) {
+        if (error instanceof ApiError) {
+            throw error;
+        }
+
+        // @ts-ignore
+        throw new HttpError(error?.message || (`${error}`) || "unknown error", 500);
+    }
 }
 
 
-export async function get<T>(api: string, params: Record<string, any>): Promise<T> {
+async function get<T>(api: string, params: Record<string, any>): Promise<T> {
     return await request<T>(api, 'get', params, undefined);
 }
 
-export async function post<T>(api: string, data: any): Promise<T> {
+async function post<T>(api: string, data: any): Promise<T> {
     return await request<T>(api, 'post', {}, data);
 }
 
-export async function put<T>(api: string, data: any): Promise<T> {
+async function put<T>(api: string, data: any): Promise<T> {
     return await request<T>(api, 'put', {}, data);
 }
 
-export async function deleted<T>(api: string): Promise<T> {
+async function deleted<T>(api: string): Promise<T> {
     return await request<T>(api, 'delete', {}, undefined);
-}
+} const api = {
+    post,get,put,deleted
+}
+
+
+export default api;

+ 36 - 15
src/utils/auth.tsx

@@ -1,16 +1,18 @@
 import React, { createContext, useContext, useState } from 'react';
+import api from './api';
 
 interface AccessToken {
-  accessToken: string;
+  token: string;
   expiresIn: number;
-  tokenType: string;
-  scope: string;
+  tokenType?: string;
+  scope?: string;
+  expiresAt: number;
 }
 
 interface AuthContextType {
   token: AccessToken | null;
   isAuthenticated: boolean;
-  setToken: (token: AccessToken | null) => void;
+  setToken: (token: AccessToken | null) => boolean;
 }
 
 let tokenCache: AccessToken | null = null;
@@ -21,8 +23,18 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
   const [token, setTokenState] = useState<AccessToken | null>(tokenCache);
 
   const setToken = (nextToken: AccessToken | null) => {
+    // tokenCache = nextToken;
+    if (nextToken && nextToken.token && nextToken.expiresIn) {
+      // if (!tokenCache.expiresAt) {
+        nextToken.expiresAt = nextToken.expiresIn + Date.now() / 1000;
+      // }
+
+    } else {
+      nextToken = null;
+    }
     tokenCache = nextToken;
     setTokenState(nextToken);
+    return true;
   };
 
   return (
@@ -47,6 +59,13 @@ export function useAuthContext() {
 }
 
 export function getAccessToken() {
+  if (!tokenCache) {
+    return null;
+  }
+  const now = Date.now() / 1000;
+  if (tokenCache.expiresAt < now - 30) {
+    tokenCache = null;
+  }
   return tokenCache;
 }
 
@@ -54,18 +73,20 @@ export function useAuth() {
   return useAuthContext();
 }
 
-export async function signIn(_: number) {
-  return new Promise<AccessToken>((resolve) => {
-    setTimeout(() => {
-      tokenCache = {
-        accessToken: 'mocked-access-token',
-        expiresIn: 3600,
-        tokenType: 'Bearer',
-        scope: 'read write',
-      };
-      resolve(tokenCache);
-    }, 3000);
+export async function smsSignIn(mobile: string, captcha: string) {
+  const {
+    token,
+    expires_in
+  } = await api.post<{
+    token: string,
+    expires_in: number,
+  }>("/user/mobilelogin", {
+    mobile, captcha
   });
+  return {
+    token,
+    expiresIn: expires_in
+  } as AccessToken;
 }
 
 export type { AccessToken };

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff