Quellcode durchsuchen

App refactoring

vbea vor 2 Jahren
Ursprung
Commit
9b50fcb365
100 geänderte Dateien mit 1888 neuen und 2176 gelöschten Zeilen
  1. 2 0
      Strides-APP/.bundle/config
  2. 0 3
      Strides-APP/.editorconfig
  3. 1 1
      Strides-APP/.eslintrc.js
  4. 0 66
      Strides-APP/.flowconfig
  5. 0 3
      Strides-APP/.gitattributes
  6. 0 146
      Strides-APP/App.js
  7. 4 0
      Strides-APP/Gemfile
  8. 6 2
      Strides-APP/README.md
  9. 0 55
      Strides-APP/android/app/BUCK
  10. 62 166
      Strides-APP/android/app/build.gradle
  11. 0 19
      Strides-APP/android/app/build_defs.bzl
  12. 2 2
      Strides-APP/android/app/src/debug/AndroidManifest.xml
  13. 48 43
      Strides-APP/android/app/src/debug/java/com/strides/chargeco/ReactNativeFlipper.java
  14. 5 4
      Strides-APP/android/app/src/main/AndroidManifest.xml
  15. 0 3
      Strides-APP/android/app/src/main/assets/appcenter-config.json
  16. 17 9
      Strides-APP/android/app/src/main/java/com/strides/chargeco/MainActivity.java
  17. 46 75
      Strides-APP/android/app/src/main/java/com/strides/chargeco/MainApplication.java
  18. 36 0
      Strides-APP/android/app/src/main/res/drawable/rn_edit_text_material.xml
  19. 8 0
      Strides-APP/android/app/src/main/res/values-night/colors.xml
  20. 1 0
      Strides-APP/android/app/src/main/res/values/colors.xml
  21. 1 1
      Strides-APP/android/app/src/main/res/values/strings.xml
  22. 13 2
      Strides-APP/android/app/src/main/res/values/styles.xml
  23. 2 2
      Strides-APP/android/app/version.properties
  24. 12 10
      Strides-APP/android/build.gradle
  25. 19 4
      Strides-APP/android/gradle.properties
  26. 2 1
      Strides-APP/android/gradle/wrapper/gradle-wrapper.properties
  27. 165 106
      Strides-APP/android/gradlew
  28. 10 7
      Strides-APP/android/gradlew.bat
  29. 3 3
      Strides-APP/android/settings.gradle
  30. 5 6
      Strides-APP/app.json
  31. 12 7
      Strides-APP/app/components/Button.js
  32. 4 3
      Strides-APP/app/components/CheckBoxText.js
  33. 104 39
      Strides-APP/app/components/CountryIcon.js
  34. 36 27
      Strides-APP/app/components/Dialog.js
  35. 17 13
      Strides-APP/app/components/Dropdown.js
  36. 6 4
      Strides-APP/app/components/HeaderTitle.js
  37. 5 6
      Strides-APP/app/components/ModalPortal.js
  38. 4 0
      Strides-APP/app/components/Switch.js
  39. 28 18
      Strides-APP/app/components/TextView.js
  40. BIN
      Strides-APP/app/images/drawer-logo.png
  41. BIN
      Strides-APP/app/images/icon/search-loading.gif
  42. 26 10
      Strides-APP/app/pages/Launch.js
  43. 1 0
      Strides-APP/app/pages/Router.js
  44. 10 9
      Strides-APP/app/pages/Settings.js
  45. 4 3
      Strides-APP/app/pages/about/About.js
  46. 3 2
      Strides-APP/app/pages/about/Contact.js
  47. 1 1
      Strides-APP/app/pages/alert/Alerts.js
  48. 7 6
      Strides-APP/app/pages/alert/Campaign.js
  49. 7 6
      Strides-APP/app/pages/alert/ItemView.js
  50. 7 6
      Strides-APP/app/pages/alert/Message.js
  51. 13 10
      Strides-APP/app/pages/alert/Notification.js
  52. 2 2
      Strides-APP/app/pages/alert/Promotions.js
  53. 1 1
      Strides-APP/app/pages/bookmark/Bookmarks.js
  54. 44 43
      Strides-APP/app/pages/charge/Charge.js
  55. 16 13
      Strides-APP/app/pages/charge/Charging.js
  56. 2 2
      Strides-APP/app/pages/charge/Details.js
  57. 31 27
      Strides-APP/app/pages/charge/InfoDialog.js
  58. 5 5
      Strides-APP/app/pages/charge/Rating.js
  59. 9 9
      Strides-APP/app/pages/charge/Reserve.js
  60. 15 12
      Strides-APP/app/pages/chargeV2/ChargeAdapter.js
  61. 32 29
      Strides-APP/app/pages/chargeV2/Charging.js
  62. 27 23
      Strides-APP/app/pages/chargeV2/InfoDialog.js
  63. 44 46
      Strides-APP/app/pages/chargeV2/Payment.js
  64. 47 44
      Strides-APP/app/pages/chargeV2/Summary.js
  65. 64 63
      Strides-APP/app/pages/chargeV2/SummaryV2.js
  66. 11 10
      Strides-APP/app/pages/chargeV2/TabInfos.js
  67. 28 27
      Strides-APP/app/pages/chargeV2/TabReserve.js
  68. 19 18
      Strides-APP/app/pages/charging/StationInfoView.js
  69. 4 3
      Strides-APP/app/pages/charging/StepChargeView.js
  70. 25 24
      Strides-APP/app/pages/charging/StepStartView.js
  71. 6 4
      Strides-APP/app/pages/chargingV2/ChargingPage.js
  72. 6 5
      Strides-APP/app/pages/chargingV2/StepAuth.js
  73. 28 27
      Strides-APP/app/pages/chargingV2/StepCharging.js
  74. 20 19
      Strides-APP/app/pages/chargingV2/StepStart.js
  75. 5 4
      Strides-APP/app/pages/chargingV2/StepStop.js
  76. 30 27
      Strides-APP/app/pages/home/Drawer.js
  77. 34 103
      Strides-APP/app/pages/home/Home.js
  78. 128 0
      Strides-APP/app/pages/home/MapUI.js
  79. 0 311
      Strides-APP/app/pages/home/Search.js
  80. 14 13
      Strides-APP/app/pages/home/maps/BottomSiteInfo.js
  81. 23 15
      Strides-APP/app/pages/home/maps/Filter.js
  82. 5 3
      Strides-APP/app/pages/home/maps/LocationPermission.js
  83. 5 3
      Strides-APP/app/pages/member/ApplyMember.js
  84. 9 9
      Strides-APP/app/pages/member/MembersList.js
  85. 19 17
      Strides-APP/app/pages/my/AddVehicle.js
  86. 109 53
      Strides-APP/app/pages/my/EditProfile.js
  87. 13 13
      Strides-APP/app/pages/my/ProfileV2.js
  88. 4 3
      Strides-APP/app/pages/my/VehicleList.js
  89. 27 27
      Strides-APP/app/pages/payment/FormCard.js
  90. 20 20
      Strides-APP/app/pages/payment/PayNow.js
  91. 20 21
      Strides-APP/app/pages/payment/PayPerUse.js
  92. 27 51
      Strides-APP/app/pages/payment/PaymentMethod.js
  93. 3 2
      Strides-APP/app/pages/search/ConnectType.js
  94. 2 2
      Strides-APP/app/pages/search/ListView.js
  95. 6 6
      Strides-APP/app/pages/search/ListViewV2.js
  96. 4 4
      Strides-APP/app/pages/search/Search.js
  97. 4 4
      Strides-APP/app/pages/search/SearchV2.js
  98. 31 20
      Strides-APP/app/pages/sign/Login.js
  99. 19 20
      Strides-APP/app/pages/sign/LoginV2.js
  100. 76 60
      Strides-APP/app/pages/sign/RegisterV2.js

+ 2 - 0
Strides-APP/.bundle/config

@@ -0,0 +1,2 @@
+BUNDLE_PATH: "vendor/bundle"
+BUNDLE_FORCE_RUBY_PLATFORM: 1

+ 0 - 3
Strides-APP/.editorconfig

@@ -1,3 +0,0 @@
-# Windows files
-[*.bat]
-end_of_line = crlf

+ 1 - 1
Strides-APP/.eslintrc.js

@@ -1,4 +1,4 @@
 module.exports = {
   root: true,
-  extends: '@react-native-community',
+  extends: '@react-native',
 };

+ 0 - 66
Strides-APP/.flowconfig

@@ -1,66 +0,0 @@
-[ignore]
-; We fork some components by platform
-.*/*[.]android.js
-
-; Ignore "BUCK" generated dirs
-<PROJECT_ROOT>/\.buckd/
-
-; Ignore polyfills
-node_modules/react-native/Libraries/polyfills/.*
-
-; Flow doesn't support platforms
-.*/Libraries/Utilities/LoadingView.js
-
-[untyped]
-.*/node_modules/@react-native-community/cli/.*/.*
-
-[include]
-
-[libs]
-node_modules/react-native/interface.js
-node_modules/react-native/flow/
-
-[options]
-emoji=true
-
-esproposal.optional_chaining=enable
-esproposal.nullish_coalescing=enable
-
-exact_by_default=true
-
-module.file_ext=.js
-module.file_ext=.json
-module.file_ext=.ios.js
-
-munge_underscores=true
-
-module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/node_modules/react-native/\1'
-module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/node_modules/react-native/Libraries/Image/RelativeImageStub'
-
-suppress_type=$FlowIssue
-suppress_type=$FlowFixMe
-suppress_type=$FlowFixMeProps
-suppress_type=$FlowFixMeState
-
-[lints]
-sketchy-null-number=warn
-sketchy-null-mixed=warn
-sketchy-number=warn
-untyped-type-import=warn
-nonstrict-import=warn
-deprecated-type=warn
-unsafe-getters-setters=warn
-unnecessary-invariant=warn
-signature-verification-failure=warn
-
-[strict]
-deprecated-type
-nonstrict-import
-sketchy-null
-unclear-type
-unsafe-getters-setters
-untyped-import
-untyped-type-import
-
-[version]
-^0.137.0

+ 0 - 3
Strides-APP/.gitattributes

@@ -1,3 +0,0 @@
-# Windows files should use crlf line endings
-# https://help.github.com/articles/dealing-with-line-endings/
-*.bat text eol=crlf

+ 0 - 146
Strides-APP/App.js

@@ -1,146 +0,0 @@
-/**
- * Sample React Native App
- * https://github.com/facebook/react-native
- *
- * @format
- * @flow strict-local
- */
-
-import React from 'react';
-import type {Node} from 'react';
-import {
-  Alert,
-  Button,
-  SafeAreaView,
-  ScrollView,
-  StatusBar,
-  StyleSheet,
-  Text,
-  useColorScheme,
-  View,
-} from 'react-native';
-
-import {
-  Colors,
-  DebugInstructions,
-  Header,
-  LearnMoreLinks,
-  ReloadInstructions,
-} from 'react-native/Libraries/NewAppScreen';
-import Toolbar from './app/components/toolbar';
-import Dialog from './app/components/Dialog';
-
-const Section = ({children, title}): Node => {
-  const isDarkMode = useColorScheme() === 'dark';
-  return (
-    <View style={styles.sectionContainer}>
-      <Text
-        style={[
-          styles.sectionTitle,
-          {
-            color: isDarkMode ? Colors.white : Colors.black,
-          },
-        ]}>
-        {title}
-      </Text>
-      <Text
-        style={[
-          styles.sectionDescription,
-          {
-            color: isDarkMode ? Colors.light : Colors.dark,
-          },
-        ]}>
-        {children}
-      </Text>
-    </View>
-  );
-};
-
-const App: () => Node = () => {
-  const isDarkMode = useColorScheme() === 'dark';
-
-  const backgroundStyle = {
-    backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
-  };
-
-  return (
-    <SafeAreaView style={backgroundStyle}>
-      <Toolbar/>
-      <ScrollView
-        contentInsetAdjustmentBehavior="automatic"
-        style={backgroundStyle}>
-        <Header />
-        <View
-          style={{
-            backgroundColor: isDarkMode ? Colors.black : Colors.white,
-          }}>
-          <Section title="Step One 1">
-            Edit <Text style={styles.highlight}>App.js</Text> to change this
-            screen and then come back to see your edits.
-          </Section>
-          <Button
-            style={{
-              height: 120,
-              backgroundColor: Colors.black,
-            }}
-            onPress={() => {
-              requestAnimationFrame(() => {
-                Dialog.showDialog({title:'邠心', message:'Hello World!', callback:() => {
-                  toastShort('试试就试试')
-                }});
-              });
-            }}
-            title="点我试试"
-          />
-          <View style={{padding: 16}}>
-            <Button onPress={() => {
-              console.log('navigator', navigator)
-              console.log('geolocation', navigator.geolocation)
-              this.navigator.geolocation.getCurrentPosition((location) => {
-                let latlng = {
-                  latitude: location.coords.latitude,
-                  longitude: location.coords.longitude,
-                  latitudeDelta: 0.0922,
-                  longitudeDelta: 0.0421,
-                }
-                Dialog.showDialog({title: 'Geolocation', message: JSON.stringify(latlng)})
-              })
-            }} title='定位'/>
-          </View>
-          
-          <Section title="See Your Changes">
-            <ReloadInstructions />
-          </Section>
-          <Section title="Debug">
-            <DebugInstructions />
-          </Section>
-          <Section title="Learn More">
-            Read the docs to discover what to do next:
-          </Section>
-          <LearnMoreLinks />
-        </View>
-      </ScrollView>
-    </SafeAreaView>
-  );
-};
-
-const styles = StyleSheet.create({
-  sectionContainer: {
-    marginTop: 32,
-    paddingHorizontal: 24,
-  },
-  sectionTitle: {
-    fontSize: 24,
-    fontWeight: '600',
-  },
-  sectionDescription: {
-    marginTop: 8,
-    fontSize: 18,
-    fontWeight: '400',
-  },
-  highlight: {
-    fontWeight: '700',
-  },
-});
-
-export default App;

+ 4 - 0
Strides-APP/Gemfile

@@ -0,0 +1,4 @@
+source 'https://rubygems.org'
+# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
+ruby ">= 2.6.10"
+gem 'cocoapods', '~> 1.12'

+ 6 - 2
Strides-APP/README.md

@@ -6,7 +6,7 @@ Application ID: com.strides.chargeco
 `yarn global add react-native-cli`  
 
 ## 初始化
-`npx react-native init JuicePlus`
+`npx react-native init Strides`
 
 ## 安装库
 `yarn add @react-native-community/art`  
@@ -14,7 +14,7 @@ Application ID: com.strides.chargeco
 `yarn add @react-native-async-storage/async-storage`  
 `yarn add @react-native-community/checkbox`  
 `yarn add @react-native-community/geolocation`  
-`yarn add @react-native-community/masked-view`  
+`yarn add @react-native-masked-view/masked-view`  
 `yarn add @react-native-community/push-notification-ios`  
 `yarn add @react-native-firebase/app`  
 `yarn add @react-native-firebase/messaging`  
@@ -157,3 +157,7 @@ https://www.cnblogs.com/sundaysgarden/p/10357051.html
 ## iOS16专题
 https://github.com/facebook/react-native/issues/32496
 https://stackoverflow.com/questions/71597475/symbols-not-found-for-architecture-arm64-xcode
+
+## RN 0.72踩坑记
+https://stackoverflow.com/questions/71702392/viewproptypes-will-be-removed-from-react-native-migrate-to-viewproptypes-export  
+https://stackoverflow.com/questions/55664673/require-cycles-are-allowed-but-can-result-in-uninitialized-values-consider-ref  

+ 0 - 55
Strides-APP/android/app/BUCK

@@ -1,55 +0,0 @@
-# To learn about Buck see [Docs](https://buckbuild.com/).
-# To run your application with Buck:
-# - install Buck
-# - `npm start` - to start the packager
-# - `cd android`
-# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
-# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
-# - `buck install -r android/app` - compile, install and run application
-#
-
-load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
-
-lib_deps = []
-
-create_aar_targets(glob(["libs/*.aar"]))
-
-create_jar_targets(glob(["libs/*.jar"]))
-
-android_library(
-    name = "all-libs",
-    exported_deps = lib_deps,
-)
-
-android_library(
-    name = "app-code",
-    srcs = glob([
-        "src/main/java/**/*.java",
-    ]),
-    deps = [
-        ":all-libs",
-        ":build_config",
-        ":res",
-    ],
-)
-
-android_build_config(
-    name = "build_config",
-    package = "com.strides.chargeco",
-)
-
-android_resource(
-    name = "res",
-    package = "com.strides.chargeco",
-    res = "src/main/res",
-)
-
-android_binary(
-    name = "app",
-    keystore = "//android/keystores:debug",
-    manifest = "src/main/AndroidManifest.xml",
-    package_type = "debug",
-    deps = [
-        ":app-code",
-    ],
-)

+ 62 - 166
Strides-APP/android/app/build.gradle

@@ -1,131 +1,71 @@
 apply plugin: "com.android.application"
-apply plugin: 'com.google.gms.google-services'
+apply plugin: "com.facebook.react"
+apply plugin: "com.google.gms.google-services"
 
-import com.android.build.OutputFile
-
-def myVersionName = "2.5.0" //★★★★★版本号★★★★★
 /**
- * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
- * and bundleReleaseJsAndAssets).
- * These basically call `react-native bundle` with the correct arguments during the Android build
- * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
- * bundle directly from the development server. Below you can see all the possible configurations
- * and their defaults. If you decide to add a configuration block, make sure to add it before the
- * `apply from: "../../node_modules/react-native/react.gradle"` line.
- *
- * project.ext.react = [
- *   // the name of the generated asset file containing your JS bundle
- *   bundleAssetName: "index.android.bundle",
- *
- *   // the entry file for bundle generation. If none specified and
- *   // "index.android.js" exists, it will be used. Otherwise "index.js" is
- *   // default. Can be overridden with ENTRY_FILE environment variable.
- *   entryFile: "index.android.js",
- *
- *   // https://reactnative.dev/docs/performance#enable-the-ram-format
- *   bundleCommand: "ram-bundle",
- *
- *   // whether to bundle JS and assets in debug mode
- *   bundleInDebug: false,
- *
- *   // whether to bundle JS and assets in release mode
- *   bundleInRelease: true,
- *
- *   // whether to bundle JS and assets in another build variant (if configured).
- *   // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
- *   // The configuration property can be in the following formats
- *   //         'bundleIn${productFlavor}${buildType}'
- *   //         'bundleIn${buildType}'
- *   // bundleInFreeDebug: true,
- *   // bundleInPaidRelease: true,
- *   // bundleInBeta: true,
- *
- *   // whether to disable dev mode in custom build variants (by default only disabled in release)
- *   // for example: to disable dev mode in the staging build type (if configured)
- *   devDisabledInStaging: true,
- *   // The configuration property can be in the following formats
- *   //         'devDisabledIn${productFlavor}${buildType}'
- *   //         'devDisabledIn${buildType}'
- *
- *   // the root of your project, i.e. where "package.json" lives
- *   root: "../../",
- *
- *   // where to put the JS bundle asset in debug mode
- *   jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
- *
- *   // where to put the JS bundle asset in release mode
- *   jsBundleDirRelease: "$buildDir/intermediates/assets/release",
- *
- *   // where to put drawable resources / React Native assets, e.g. the ones you use via
- *   // require('./image.png')), in debug mode
- *   resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
- *
- *   // where to put drawable resources / React Native assets, e.g. the ones you use via
- *   // require('./image.png')), in release mode
- *   resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
- *
- *   // by default the gradle tasks are skipped if none of the JS files or assets change; this means
- *   // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
- *   // date; if you have any other folders that you want to ignore for performance reasons (gradle
- *   // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
- *   // for example, you might want to remove it from here.
- *   inputExcludes: ["android/**", "ios/**"],
- *
- *   // override which node gets called and with what additional arguments
- *   nodeExecutableAndArgs: ["node"],
- *
- *   // supply additional arguments to the packager
- *   extraPackagerArgs: []
- * ]
+ * 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
  */
-
-project.ext.react = [
-    //entryFile: "index.js",
-    //hermesCommand: "../../node_modules/hermes-engine/%OS-BIN%/hermes", // Or whatever path you need
-    enableHermes: true,  // clean and rebuild if changing
-]
-
-apply from: "../../node_modules/react-native/react.gradle"
-apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
-apply from: "../../node_modules/react-native-code-push/android/codepush.gradle"
-
-/**
- * Set this to true to create two separate APKs instead of one:
- *   - An APK that only works on ARM devices
- *   - An APK that only works on x86 devices
- * The advantage is the size of the APK is reduced by about 4MB.
- * Upload all the APKs to the Play Store and people will download
- * the correct one based on the CPU architecture of their device.
- */
-def enableSeparateBuildPerCPUArchitecture = false
+react {
+    /* 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")
+    //   The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js
+    // cliFile = file("../node_modules/react-native/cli.js")
+    /* 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 command to run when bundling. By default is 'bundle'
+    // bundleCommand = "ram-bundle"
+    //
+    //   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"]
+}
 
 /**
- * Run Proguard to shrink the Java bytecode in release builds.
+ * Set this to true to Run Proguard on Release builds to minify the Java bytecode.
  */
-def enableProguardInReleaseBuilds = true
+def enableProguardInReleaseBuilds = false
 
 /**
- * The preferred build flavor of JavaScriptCore.
+ * 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
+ * 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 = 'org.webkit:android-jsc:+'
 
-/**
- * Whether to enable the Hermes VM.
- *
- * This should be set on project.ext.react and mirrored here.  If it is not set
- * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
- * and the benefits of using Hermes will therefore be sharply reduced.
- */
-def enableHermes = project.ext.react.get("enableHermes", false);
-
 def getMyVersionCode() {
     def files = file('version.properties')
     Properties props = new Properties()
@@ -150,28 +90,16 @@ android {
     compileSdkVersion rootProject.ext.compileSdkVersion
     buildToolsVersion rootProject.ext.buildToolsVersion
 
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_8
-        targetCompatibility JavaVersion.VERSION_1_8
-    }
-
+    namespace "com.strides.chargeco"
     defaultConfig {
         applicationId "com.strides.chargeco"
         minSdkVersion rootProject.ext.minSdkVersion
         targetSdkVersion rootProject.ext.targetSdkVersion
         versionCode getMyVersionCode()
-        versionName myVersionName
+        versionName rootProject.ext.versionName
         multiDexEnabled true
         missingDimensionStrategy 'react-native-camera', 'general'
     }
-    splits {
-        abi {
-            reset()
-            enable enableSeparateBuildPerCPUArchitecture
-            universalApk false  // If true, also generate a universal APK
-            include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
-        }
-    }
     signingConfigs {
         debug {
             storeFile file('vbea.keystore')
@@ -193,17 +121,6 @@ android {
             //热更新应用分发Staging key
             resValue "string", "CodePushDeploymentKey", "zu0zkOWpBhXTGQ83pC15FW3puB--tuSG5TvIQ"
         }
-        releaseStaging {
-           signingConfig signingConfigs.release
-            minifyEnabled enableProguardInReleaseBuilds
-            proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
-            ndk {
-                abiFilters "armeabi", "armeabi-v7a", "arm64-v8a"
-            }
-            //热更新应用分发Staging key
-            resValue "string", "CodePushDeploymentKey", "zu0zkOWpBhXTGQ83pC15FW3puB--tuSG5TvIQ"
-            matchingFallbacks = ['release']
-        }
         release {
             // Caution! In production, you need to generate your own keystore file.
             // see https://reactnative.dev/docs/signed-apk-android.
@@ -220,18 +137,6 @@ android {
 
     // applicationVariants are e.g. debug, release
     applicationVariants.all { variant ->
-        variant.outputs.each { output ->
-            // For each separate APK per architecture, set a unique version code as described here:
-            // https://developer.android.com/studio/build/configure-apk-splits.html
-            // Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc.
-            def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
-            def abi = output.getFilter(OutputFile.ABI)
-            if (abi != null) {  // null for the universal-debug, universal-release variants
-                output.versionCodeOverride =
-                        defaultConfig.versionCode * 1000 + versionCodes.get(abi)
-            }
-
-        }
         variant.outputs.all { output ->
             if (variant.buildType.name=="release") {
                 def fileName = "ChargEco-V${variant.versionName}-${variant.versionCode}"
@@ -248,18 +153,16 @@ android {
 
 dependencies {
     implementation fileTree(dir: "libs", include: ["*.jar"])
+    // The version of react-native is set by the React Native Gradle Plugin
+    implementation "com.facebook.react:react-android"
     //noinspection GradleDynamicVersion
-    implementation "com.facebook.react:react-native:0.64.2"  // From node_modules
-    implementation 'com.facebook.fresco:animated-gif:2.0.0' // 如果你需要支持GIF动图
-    implementation "androidx.appcompat:appcompat:1.0.0"
-    implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
+    implementation "com.facebook.fresco:animated-gif:2.5.0" // 如果你需要支持GIF动图
+    implementation "androidx.appcompat:appcompat:1.6.1"
+    implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
 
-    debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
-      exclude group:'com.facebook.fbjni'
-    }
+    debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}")
 
     debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
-        exclude group:'com.facebook.flipper'
         exclude group:'com.squareup.okhttp3', module:'okhttp'
     }
 
@@ -267,10 +170,8 @@ dependencies {
         exclude group:'com.facebook.flipper'
     }
 
-    if (enableHermes) {
-        def hermesPath = "../../node_modules/hermes-engine/android/";
-        debugImplementation files(hermesPath + "hermes-debug.aar")
-        releaseImplementation files(hermesPath + "hermes-release.aar")
+    if (rootProject.ext.enableHermes) {
+        implementation("com.facebook.react:hermes-android")
     } else {
         implementation jscFlavor
     }
@@ -286,11 +187,6 @@ dependencies {
     implementation 'com.google.firebase:firebase-analytics'
 }
 
-// Run this once to be able to run the application with BUCK
-// puts all compile dependencies into folder libs for BUCK to use
-task copyDownloadableDepsToLibs(type: Copy) {
-    from configurations.compile
-    into 'libs'
-}
-
-apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
+apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle")
+apply from: file("../../node_modules/react-native-vector-icons/fonts.gradle")
+applyNativeModulesAppBuildGradle(project)

+ 0 - 19
Strides-APP/android/app/build_defs.bzl

@@ -1,19 +0,0 @@
-"""Helper definitions to glob .aar and .jar targets"""
-
-def create_aar_targets(aarfiles):
-    for aarfile in aarfiles:
-        name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
-        lib_deps.append(":" + name)
-        android_prebuilt_aar(
-            name = name,
-            aar = aarfile,
-        )
-
-def create_jar_targets(jarfiles):
-    for jarfile in jarfiles:
-        name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
-        lib_deps.append(":" + name)
-        prebuilt_jar(
-            name = name,
-            binary_jar = jarfile,
-        )

+ 2 - 2
Strides-APP/android/app/src/debug/AndroidManifest.xml

@@ -8,6 +8,6 @@
         android:usesCleartextTraffic="true"
         tools:targetApi="28"
         tools:ignore="GoogleAppIndexingWarning">
-        <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
+        <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false" />
     </application>
-</manifest>
+</manifest>

+ 48 - 43
Strides-APP/android/app/src/debug/java/com/strides/chargeco/ReactNativeFlipper.java

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) Facebook, Inc. and its affiliates.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  *
  * <p>This source code is licensed under the MIT license found in the LICENSE file in the root
  * directory of this source tree.
@@ -7,6 +7,7 @@
 package com.strides.chargeco;
 
 import android.content.Context;
+
 import com.facebook.flipper.android.AndroidFlipperClient;
 import com.facebook.flipper.android.utils.FlipperUtils;
 import com.facebook.flipper.core.FlipperClient;
@@ -17,56 +18,60 @@ import com.facebook.flipper.plugins.inspector.DescriptorMapping;
 import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
 import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
 import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
-import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
 import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
+import com.facebook.react.ReactInstanceEventListener;
 import com.facebook.react.ReactInstanceManager;
 import com.facebook.react.bridge.ReactContext;
 import com.facebook.react.modules.network.NetworkingModule;
+
 import okhttp3.OkHttpClient;
 
+/**
+ * Class responsible of loading Flipper inside your React Native application. This is the debug
+ * flavor of it. Here you can add your own plugins and customize the Flipper setup.
+ */
 public class ReactNativeFlipper {
-  public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
-    if (FlipperUtils.shouldEnableFlipper(context)) {
-      final FlipperClient client = AndroidFlipperClient.getInstance(context);
+    public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
+        if (FlipperUtils.shouldEnableFlipper(context)) {
+            final FlipperClient client = AndroidFlipperClient.getInstance(context);
 
-      client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
-      client.addPlugin(new ReactFlipperPlugin());
-      client.addPlugin(new DatabasesFlipperPlugin(context));
-      client.addPlugin(new SharedPreferencesFlipperPlugin(context));
-      client.addPlugin(CrashReporterPlugin.getInstance());
+            client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
+            client.addPlugin(new DatabasesFlipperPlugin(context));
+            client.addPlugin(new SharedPreferencesFlipperPlugin(context));
+            client.addPlugin(CrashReporterPlugin.getInstance());
 
-      NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
-      NetworkingModule.setCustomClientBuilder(
-          new NetworkingModule.CustomClientBuilder() {
-            @Override
-            public void apply(OkHttpClient.Builder builder) {
-              builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
-            }
-          });
-      client.addPlugin(networkFlipperPlugin);
-      client.start();
-
-      // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
-      // Hence we run if after all native modules have been initialized
-      ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
-      if (reactContext == null) {
-        reactInstanceManager.addReactInstanceEventListener(
-            new ReactInstanceManager.ReactInstanceEventListener() {
-              @Override
-              public void onReactContextInitialized(ReactContext reactContext) {
-                reactInstanceManager.removeReactInstanceEventListener(this);
-                reactContext.runOnNativeModulesQueueThread(
-                    new Runnable() {
-                      @Override
-                      public void run() {
-                        client.addPlugin(new FrescoFlipperPlugin());
-                      }
+            NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
+            NetworkingModule.setCustomClientBuilder(
+                    new NetworkingModule.CustomClientBuilder() {
+                        @Override
+                        public void apply(OkHttpClient.Builder builder) {
+                            builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
+                        }
                     });
-              }
-            });
-      } else {
-        client.addPlugin(new FrescoFlipperPlugin());
-      }
+            client.addPlugin(networkFlipperPlugin);
+            client.start();
+
+            // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
+            // Hence we run if after all native modules have been initialized
+            ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
+            if (reactContext == null) {
+                reactInstanceManager.addReactInstanceEventListener(
+                        new ReactInstanceEventListener() {
+                            @Override
+                            public void onReactContextInitialized(ReactContext reactContext) {
+                                reactInstanceManager.removeReactInstanceEventListener(this);
+                                reactContext.runOnNativeModulesQueueThread(
+                                        new Runnable() {
+                                            @Override
+                                            public void run() {
+                                                client.addPlugin(new FrescoFlipperPlugin());
+                                            }
+                                        });
+                            }
+                        });
+            } else {
+                client.addPlugin(new FrescoFlipperPlugin());
+            }
+        }
     }
-  }
-}
+}

+ 5 - 4
Strides-APP/android/app/src/main/AndroidManifest.xml

@@ -1,6 +1,6 @@
 <manifest
   xmlns:android="http://schemas.android.com/apk/res/android"
-  package="com.strides.chargeco">
+  xmlns:tools="http://schemas.android.com/tools">
 
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
@@ -11,6 +11,7 @@
     <uses-permission android:name="android.permission.CAMERA" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.RECORD_AUDIO"/>
@@ -22,14 +23,14 @@
       android:icon="@mipmap/ic_launcher"
       android:roundIcon="@mipmap/ic_launcher_round"
       android:allowBackup="false"
-      android:theme="@style/AppTheme"
+      android:theme="@style/AppTheme.Light"
       android:hardwareAccelerated="true"
       android:usesCleartextTraffic="true"
-      android:requestLegacyExternalStorage="true">
+      tools:ignore="GoogleAppIndexingWarning">
         <activity
           android:name=".MainActivity"
           android:label="@string/app_name"
-          android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
+          android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
           android:launchMode="singleTask"
           android:windowSoftInputMode="adjustResize"
           android:exported="true">

+ 0 - 3
Strides-APP/android/app/src/main/assets/appcenter-config.json

@@ -1,3 +0,0 @@
-{
-  "app_secret": "281c5133-a65f-4eff-962f-cb548267b006"
-}

+ 17 - 9
Strides-APP/android/app/src/main/java/com/strides/chargeco/MainActivity.java

@@ -1,22 +1,30 @@
 package com.strides.chargeco;
 
-import android.os.Bundle;
-
 import com.facebook.react.ReactActivity;
+import com.facebook.react.ReactActivityDelegate;
+import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
+import com.facebook.react.defaults.DefaultReactActivityDelegate;
 
 public class MainActivity extends ReactActivity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(null);
-    }
-    
     /**
      * Returns the name of the main component registered from JavaScript. This is used to schedule
      * rendering of the component.
      */
     @Override
     protected String getMainComponentName() {
-        return "JuicePlus";
+        return "Strides";
+    }
+    /**
+     * Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link
+     * DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React
+     * (aka React 18) with two boolean flags.
+     */
+    @Override
+    protected ReactActivityDelegate createReactActivityDelegate() {
+        return new DefaultReactActivityDelegate(
+            this,
+            getMainComponentName(),
+            // If you opted-in for the New Architecture, we enable the Fabric Renderer.
+            DefaultNewArchitectureEntryPoint.getFabricEnabled());
     }
 }

+ 46 - 75
Strides-APP/android/app/src/main/java/com/strides/chargeco/MainApplication.java

@@ -1,96 +1,67 @@
 package com.strides.chargeco;
 
 import android.app.Application;
-import android.content.Context;
-
 import com.facebook.react.PackageList;
 import com.facebook.react.ReactApplication;
-import com.facebook.react.ReactInstanceManager;
 import com.facebook.react.ReactNativeHost;
 import com.facebook.react.ReactPackage;
+import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
+import com.facebook.react.defaults.DefaultReactNativeHost;
 import com.facebook.soloader.SoLoader;
-
-import com.microsoft.codepush.react.CodePush;
-
-import cl.json.ShareApplication;
-
-import java.lang.reflect.InvocationTargetException;
 import java.util.List;
 
-public class MainApplication extends Application implements ShareApplication, ReactApplication {
+public class MainApplication extends Application implements ReactApplication {
 
-  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
-      @Override
-      public boolean getUseDeveloperSupport() {
-        return BuildConfig.DEBUG;
-      }
+    private final ReactNativeHost mReactNativeHost = new DefaultReactNativeHost(this) {
+        @Override
+        public boolean getUseDeveloperSupport() {
+          return BuildConfig.DEBUG;
+        }
 
-      @Override
-      protected List<ReactPackage> getPackages() {
-        @SuppressWarnings("UnnecessaryLocalVariable")
-        List<ReactPackage> packages = new PackageList(this).getPackages();
-        // Packages that cannot be autolinked yet can be added manually here, for example:
-        // packages.add(new MyReactNativePackage());
-        //packages.add(new ClusterMapPackage());
-        return packages;
-      }
+        @Override
+        protected List<ReactPackage> getPackages() {
+          @SuppressWarnings("UnnecessaryLocalVariable")
+          List<ReactPackage> packages = new PackageList(this).getPackages();
+          // Packages that cannot be autolinked yet can be added manually here, for example:
+          // packages.add(new MyReactNativePackage());
+          //packages.add(new ClusterMapPackage());
+          return packages;
+        }
 
-      @Override
-      protected String getJSMainModuleName() {
+        @Override
+        protected String getJSMainModuleName() {
           return "index";
-      }
+        }
 
-      @Override
-      protected String getJSBundleFile() {
-          return CodePush.getJSBundleFile();
-      }
-  };
+        @Override
+        protected boolean isNewArchEnabled() {
+          return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
+        }
 
-  @Override
-  public ReactNativeHost getReactNativeHost() {
-    return mReactNativeHost;
-  }
+        @Override
+        protected Boolean isHermesEnabled() {
+          return BuildConfig.IS_HERMES_ENABLED;
+        }
+    };
 
-  @Override
-  public void onCreate() {
-    super.onCreate();
-    SoLoader.init(this, /* native exopackage */ false);
-    initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
-  }
+    @Override
+    public ReactNativeHost getReactNativeHost() {
+      return mReactNativeHost;
+    }
 
-  /**
-   * Loads Flipper in React Native templates. Call this in the onCreate method with something like
-   * initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
-   *
-   * @param context
-   * @param reactInstanceManager
-   */
-  private static void initializeFlipper(
-      Context context, ReactInstanceManager reactInstanceManager) {
-    if (BuildConfig.DEBUG) {
-      try {
-        /*
-         We use reflection here to pick up the class that initializes Flipper,
-        since Flipper library is not available in release mode
-        */
-        Class<?> aClass = Class.forName("com.strides.chargeco.ReactNativeFlipper");
-        aClass
-            .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
-            .invoke(null, context, reactInstanceManager);
-      } catch (ClassNotFoundException e) {
-        e.printStackTrace();
-      } catch (NoSuchMethodException e) {
-        e.printStackTrace();
-      } catch (IllegalAccessException e) {
-        e.printStackTrace();
-      } catch (InvocationTargetException e) {
-        e.printStackTrace();
-      }
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        SoLoader.init(this, false);/* native exopackage */ 
+        if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
+          // If you opted-in for the New Architecture, we load the native entry point for this app.
+          DefaultNewArchitectureEntryPoint.load();
+        }
+        ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
     }
-  }
 
-  @Override
-  public String getFileProviderAuthority() {
-      return BuildConfig.APPLICATION_ID + ".provider";
-  }
+    /*@Override
+    public String getFileProviderAuthority() {
+        return BuildConfig.APPLICATION_ID + ".provider";
+    }*/
 }

+ 36 - 0
Strides-APP/android/app/src/main/res/drawable/rn_edit_text_material.xml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     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
+
+          http://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.
+-->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+       android:insetLeft="@dimen/abc_edit_text_inset_horizontal_material"
+       android:insetRight="@dimen/abc_edit_text_inset_horizontal_material"
+       android:insetTop="@dimen/abc_edit_text_inset_top_material"
+       android:insetBottom="@dimen/abc_edit_text_inset_bottom_material">
+
+    <selector>
+        <!--
+          This file is a copy of abc_edit_text_material (https://bit.ly/3k8fX7I).
+          The item below with state_pressed="false" and state_focused="false" causes a NullPointerException.
+          NullPointerException:tempt to invoke virtual method 'android.graphics.drawable.Drawable android.graphics.drawable.Drawable$ConstantState.newDrawable(android.content.res.Resources)'
+
+          <item android:state_pressed="false" android:state_focused="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
+
+          For more info, see https://bit.ly/3CdLStv (react-native/pull/29452) and https://bit.ly/3nxOMoR.
+        -->
+        <item android:state_enabled="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
+        <item android:drawable="@drawable/abc_textfield_activated_mtrl_alpha"/>
+    </selector>
+
+</inset>

+ 8 - 0
Strides-APP/android/app/src/main/res/values-night/colors.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+  <color name="white">#FFFFFF</color>
+  <color name="black">#000000</color>
+  <color name="colorPrimary">#A0000000</color>
+  <color name="colorAccent">#A0000000</color>
+  <color name="colorPrimaryDark">#A0000000</color>
+</resources>

+ 1 - 0
Strides-APP/android/app/src/main/res/values/colors.xml

@@ -4,4 +4,5 @@
   <color name="black">#000000</color>
   <color name="colorPrimary">#001489</color>
   <color name="colorAccent">#A3C93A</color>
+  <color name="colorPrimaryDark">#A3C93A</color>
 </resources>

+ 1 - 1
Strides-APP/android/app/src/main/res/values/strings.xml

@@ -1,6 +1,6 @@
 <resources>
     <string name="app_name">ChargEco</string>
-    <string name="main_name">JuicePlus</string>
+    <string name="main_name">Strides</string>
     <string name="default_notification_channel_id">10186</string>
     <string name="appCenterCrashes_whenToSendCrashes" moduleConfig="true" translatable="false">DO_NOT_ASK_JAVASCRIPT</string>
     <string name="appCenterAnalytics_whenToEnableAnalytics" moduleConfig="true" translatable="false">ALWAYS_SEND</string>

+ 13 - 2
Strides-APP/android/app/src/main/res/values/styles.xml

@@ -1,7 +1,7 @@
 <resources>
 
     <!-- Base application theme. -->
-    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
+    <style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
         <!-- Customize your theme here. -->
         <item name="android:textColor">#000000</item>
         <item name="colorPrimary">@color/colorPrimary</item>
@@ -9,7 +9,18 @@
         <item name="colorPrimaryVariant">@color/colorPrimary</item>
         <item name="colorAccent">@color/colorAccent</item>
         <item name="colorOnPrimary">@color/black</item>
-        <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
+        <item name="android:statusBarColor">@color/colorPrimaryDark</item>
+    </style>
+    <style name="AppTheme.Light" parent="Theme.AppCompat.Light.NoActionBar">
+        <!-- Customize your theme here. -->
+        <item name="android:textColor">#000000</item>
+        <item name="colorPrimary">@color/colorPrimary</item>
+        <item name="colorPrimaryDark">@color/colorPrimary</item>
+        <item name="colorPrimaryVariant">@color/colorPrimary</item>
+        <item name="colorAccent">@color/colorAccent</item>
+        <item name="colorOnPrimary">@color/black</item>
+        <item name="android:statusBarColor">@color/colorPrimaryDark</item>
+        <item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
     </style>
 
 </resources>

+ 2 - 2
Strides-APP/android/app/version.properties

@@ -1,2 +1,2 @@
-#Fri Aug 18 17:34:47 CST 2023
-VERSION_CODE=277
+#Mon Sep 04 10:36:50 CST 2023
+VERSION_CODE=304

+ 12 - 10
Strides-APP/android/build.gradle

@@ -1,23 +1,25 @@
 // Top-level build file where you can add configuration options common to all sub-projects/modules.
 buildscript {
     ext {
-        buildToolsVersion = "34.0.0"
-        minSdkVersion = 21
-        compileSdkVersion = 34
-        targetSdkVersion = 34
+        versionName = "3.0.0" //★★★★★版本号★★★★★
+        buildToolsVersion = "33.0.2"
+        minSdkVersion = 23
+        compileSdkVersion = 33
+        targetSdkVersion = 33
         playServicesVersion = "17.0.0"
         firebaseMessagingVersion = "21.1.0"
-        ndkVersion = "20.1.5948944"
+        ndkVersion = "23.1.7779620"
+        enableHermes = true
     }
     repositories {
         google()
-        jcenter()
+        mavenCentral()
     }
     dependencies {
-        classpath("com.android.tools.build:gradle:4.1.1")
-        classpath('com.google.gms:google-services:4.3.15')
-        // NOTE: Do not place your application dependencies here; they belong
-        // in the individual module build.gradle files
+        classpath('com.android.tools.build:gradle')
+        classpath("com.google.gms:google-services:4.3.15")
+        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.22")
+        classpath("com.facebook.react:react-native-gradle-plugin")
     }
 }
 

+ 19 - 4
Strides-APP/android/gradle.properties

@@ -9,8 +9,8 @@
 
 # Specifies the JVM arguments used for the daemon process.
 # The setting is particularly useful for tweaking memory settings.
-# Default value: -Xmx10248m -XX:MaxPermSize=256m
-# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
+org.gradle.jvmargs=-Xmx4196m -XX:MaxMetaspaceSize=512m
 
 # When configured, Gradle will run in incubating parallel mode.
 # This option should only be used with decoupled projects. More details, visit
@@ -25,5 +25,20 @@ android.useAndroidX=true
 android.enableJetifier=true
 
 # Version of flipper SDK to use with React Native
-FLIPPER_VERSION=0.75.1
-org.gradle.jvmargs=-Xmx8192m
+FLIPPER_VERSION=0.182.0
+
+# Use this property to specify which architecture you want to build.
+# You can also override it from the CLI using
+# ./gradlew <task> -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=false
+
+# Use this property to enable or disable the Hermes JS engine.
+# If set to false, you will be using JSC instead.
+hermesEnabled=true

+ 2 - 1
Strides-APP/android/gradle/wrapper/gradle-wrapper.properties

@@ -1,5 +1,6 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-all.zip
+networkTimeout=100000
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists

+ 165 - 106
Strides-APP/android/gradlew

@@ -1,7 +1,7 @@
-#!/usr/bin/env sh
+#!/bin/sh
 
 #
-# Copyright 2015 the original author or authors.
+# 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.
@@ -17,67 +17,101 @@
 #
 
 ##############################################################################
-##
-##  Gradle start up script for UN*X
-##
+#
+#   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/subprojects/plugins/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
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
-    ls=`ls -ld "$PRG"`
-    link=`expr "$ls" : '.*-> \(.*\)$'`
-    if expr "$link" : '/.*' > /dev/null; then
-        PRG="$link"
-    else
-        PRG=`dirname "$PRG"`"/$link"
-    fi
+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
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
 
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
 
 # 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"'
 
 # Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
+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
-    ;;
-  MINGW* )
-    msys=true
-    ;;
-  NONSTOP* )
-    nonstop=true
-    ;;
+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
@@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
 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"
+        JAVACMD=$JAVA_HOME/jre/sh/java
     else
-        JAVACMD="$JAVA_HOME/bin/java"
+        JAVACMD=$JAVA_HOME/bin/java
     fi
     if [ ! -x "$JAVACMD" ] ; then
         die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
 location of your Java installation."
     fi
 else
-    JAVACMD="java"
+    JAVACMD=java
     which java >/dev/null 2>&1 || 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
@@ -106,80 +140,105 @@ location of your Java installation."
 fi
 
 # Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
-    MAX_FD_LIMIT=`ulimit -H -n`
-    if [ $? -eq 0 ] ; then
-        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
-            MAX_FD="$MAX_FD_LIMIT"
-        fi
-        ulimit -n $MAX_FD
-        if [ $? -ne 0 ] ; then
-            warn "Could not set maximum file descriptor limit: $MAX_FD"
-        fi
-    else
-        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
-    fi
+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=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=SC3045 
+        ulimit -n "$MAX_FD" ||
+            warn "Could not set maximum file descriptor limit to $MAX_FD"
+    esac
 fi
 
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
-    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-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" = "true" -o "$msys" = "true" ] ; then
-    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
-    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
-    JAVACMD=`cygpath --unix "$JAVACMD"`
-
-    # We build the pattern for arguments to be converted via cygpath
-    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
-    SEP=""
-    for dir in $ROOTDIRSRAW ; do
-        ROOTDIRS="$ROOTDIRS$SEP$dir"
-        SEP="|"
-    done
-    OURCYGPATTERN="(^($ROOTDIRS))"
-    # Add a user-defined pattern to the cygpath arguments
-    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
-        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
-    fi
+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
-    i=0
-    for arg in "$@" ; do
-        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
-        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
-
-        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
-            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
-        else
-            eval `echo args$i`="\"$arg\""
+    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
-        i=`expr $i + 1`
+        # 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
-    case $i in
-        0) set -- ;;
-        1) set -- "$args0" ;;
-        2) set -- "$args0" "$args1" ;;
-        3) set -- "$args0" "$args1" "$args2" ;;
-        4) set -- "$args0" "$args1" "$args2" "$args3" ;;
-        5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
-        6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
-        7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
-        8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
-        9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
-    esac
 fi
 
-# Escape application args
-save () {
-    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
-    echo " "
-}
-APP_ARGS=`save "$@"`
+# Collect all arguments for the java command;
+#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+#     shell script including quotes and variable substitutions, so put them in
+#     double quotes to make sure that they get re-expanded; and
+#   * put everything else in single quotes, so that it's not re-expanded.
+
+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.
+#
 
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+eval "set -- $(
+        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+        xargs -n1 |
+        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+        tr '\n' ' '
+    )" '"$@"'
 
-exec "$JAVACMD" "$@"
+exec "$JAVACMD" "$@"

+ 10 - 7
Strides-APP/android/gradlew.bat

@@ -14,7 +14,7 @@
 @rem limitations under the License.
 @rem
 
-@if "%DEBUG%" == "" @echo off
+@if "%DEBUG%"=="" @echo off
 @rem ##########################################################################
 @rem
 @rem  Gradle startup script for Windows
@@ -25,7 +25,8 @@
 if "%OS%"=="Windows_NT" setlocal
 
 set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
 set APP_BASE_NAME=%~n0
 set APP_HOME=%DIRNAME%
 
@@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
 
 set JAVA_EXE=java.exe
 %JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto execute
+if %ERRORLEVEL% equ 0 goto execute
 
 echo.
 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -75,15 +76,17 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
 
 :end
 @rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
+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!
-if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
+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
+:omega

+ 3 - 3
Strides-APP/android/settings.gradle

@@ -1,7 +1,7 @@
-rootProject.name = 'JuicePlus'
+rootProject.name = 'Strides'
 apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle");
 applyNativeModulesSettingsGradle(settings)
-include ':app', ':react-native-code-push'
-project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app')
+include ':app'
+includeBuild('../node_modules/@react-native/gradle-plugin')
 // include ':react-native-maps'
 // project(':react-native-maps').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-maps/lib/android')

+ 5 - 6
Strides-APP/app.json

@@ -1,20 +1,19 @@
 {
-  "name": "JuicePlus",
+  "name": "Strides",
   "displayName": "ChargEco",
-  "versionCode": 290,
-  "versionName": "V2.5.0",
+  "versionCode": 300,
+  "versionName": "V3.0.0",
   "product": false,
   "debug": true,
   "isWhitelabel": true,
   "modules": {
     "i18n": false,
     "bookmarks": true,
-    "codePush": false,
     "nationally": false,
     "notifications": true,
     "support": {
-      "phone": "006597285916",
-      "whatsapp": false
+      "phone": "006564775355",
+      "whatsapp": "https://wa.me/6597285916"
     }
   },
   "storeUrl": {

+ 12 - 7
Strides-APP/app/components/Button.js

@@ -1,5 +1,6 @@
-import React, { useEffect } from 'react';
+import React from 'react';
 import { Pressable, StyleSheet, Text, View } from 'react-native';
+import TextView from './TextView';
 
 export default Button = ({
   style = styles.buttonView,
@@ -14,8 +15,10 @@ export default Button = ({
   disabled = false,
   elevation = 0,
   borderRadius = 40,
+  iconLeft,
+  iconRight
 }) => {
-  var start = {}, end = {};
+  //var start = {}, end = {};
   //const isSamsung = BRAND == 'samsung';
   return (
     <View style={getElevation(style, disabled, elevation, borderRadius)}>
@@ -29,13 +32,12 @@ export default Button = ({
         ]}
         disabled={disabled}
         onPress={e => {
-          //if (!isSamsung && onClick) 
-          onClick(e)
+          if (onClick) onClick(e)
         }}
         onLongPress={e => {
           if (onLongClick) {
             onLongClick(e)
-            start = {}
+            //start = {}
           }
         }}
         onPressIn={(e) => {
@@ -61,9 +63,11 @@ export default Button = ({
           }*/
         }}
         android_ripple={ripple}>
+        {iconLeft ?? <></>}
         { children ? children :
-          <Text style={mergeStyle(textStyle, textColor, textSize, disabled)}>{text}</Text>
+          <TextView style={mergeStyle(textStyle, textColor, textSize, disabled)}>{text}</TextView>
         }
+        {iconRight ?? <></>}
       </Pressable>
     </View>
   );
@@ -203,9 +207,10 @@ const styles = StyleSheet.create({
     backgroundColor: colorAccent//colorPrimary
   },
   buttonText: {
+    flex: 1,
     color: textButton,
     fontSize: 16,
     fontWeight: 'bold',
-    textAlign: 'center',
+    textAlign: 'center'
   }
 });

+ 4 - 3
Strides-APP/app/components/CheckBoxText.js

@@ -1,6 +1,7 @@
 import React from 'react';
 import { View, Text, StyleSheet } from 'react-native';
 import CheckBox from '../components/CheckBox';
+import TextView from './TextView';
 
 const CheckBoxText = (
   {
@@ -18,11 +19,11 @@ const CheckBoxText = (
       value={value}
       disabled={disabled}
       onValueChange={onValueChange}/>
-    <Text
+    <TextView
       style={[textStyle, flexText ? {flex: 1} : {}]}
       onPress={() => {
         if (onValueChange) onValueChange(!value)
-      }}>{text}</Text>
+      }}>{text}</TextView>
   </View>
 )
 
@@ -40,6 +41,6 @@ const styles = StyleSheet.create({
   checkboxText: {
     color: '#222',
     fontSize: 16,
-    padding: 4
+    paddingLeft: 4
   }
 })

+ 104 - 39
Strides-APP/app/components/CountryIcon.js

@@ -4,9 +4,11 @@
  */
 import React from 'react';
 import { Image, StyleSheet, Text } from 'react-native';
-import countries from './countrys.json';
+import countries from './countrysLocale.json';
 import Button from './Button';
 import apiUser from '../api/apiUser';
+import { i18nUtil } from '../i18n';
+import TextView from './TextView';
 
 const RNCountryList = {
   AD: require('../images/country/AD.png'),
@@ -289,50 +291,59 @@ export const GetCountryList = (back) => {
     //console.log('原始国家', countries.length);
     apiUser.getCountryList().then(res => {
       if (res.data && res.data.length) {
-        res.data.forEach(item => {
-          const country = {
-            countryNum: item.callingCode,
-            countryCode: item.countryCode,
-            countryName: item.countryName,
-            currency: item.currency,
-            currencySign: item.currencySymbol
-          };
-          list.push(country);
+        countries.forEach(items => {
+          res.data.forEach(item => {
+            if (item.countryCode == items.countryCode) {
+              const country = {
+                countryNum: item.callingCode,
+                countryCode: item.countryCode,
+                countryName: i18nUtil.analyzeLocaleData(items.countryNames, items.countryName),
+                currency: item.currency,
+                currencySign: item.currencySymbol
+              };
+              list.push(country);
+            }
+          })
         })
         if (list.length > 0) {
           global.ComCountryList = list;
           if (back) back(global.ComCountryList);
         }
       }
-    }).catch(err => {
-      countries.forEach(item => {
-        if (RNCountryList[item.countryCode] !== undefined && item.region !== 'Antarctic' && item.subregion !== 'Polynesia' && !item.exclude) {
-          //item.subregion === 'Eastern Asia' || item.subregion === 'South-Eastern Asia') {
-          const country = {
-            countryNum: item.callingCode[0],
-            countryCode: item.countryCode,
-            countryName: item.countryName,
-            currencySign: item.currencySign,
-            currency: item.currency[0]
-          };
-          list.push(country);
-          /*if (RNCurrencyList.indexOf(item.countryCode) >= 0) {
-            curl.push(country);
-          }*/
-        }
-      })
-      //console.log('最终国家', list.length);
-      if (list.length > 0) {
-        global.ComCountryList = list;
-        //global.CurCountryList = curl;
-        if (back) back(global.ComCountryList);
-      }
+    }).catch(errs => {
+      getLocaleCountryList(back)
     });
   } else {
     if (back) back(global.ComCountryList);
   }
 }
 
+const getLocaleCountryList = (back) => {
+  const list = []
+  countries.forEach(item => {
+    if (RNCountryList[item.countryCode] !== undefined && item.region !== 'Antarctic' && item.subregion !== 'Polynesia' && !item.exclude) {
+      //item.subregion === 'Eastern Asia' || item.subregion === 'South-Eastern Asia') {
+      const country = {
+        countryNum: item.callingCode[0],
+        countryCode: item.countryCode,
+        countryName: i18nUtil.analyzeLocaleData(item.countryNames, item.countryName),
+        currencySign: item.currencySign,
+        currency: item.currency[0]
+      };
+      list.push(country);
+      /*if (RNCurrencyList.indexOf(item.countryCode) >= 0) {
+        curl.push(country);
+      }*/
+    }
+  })
+  //console.log('最终国家', list.length);
+  if (list.length > 0) {
+    global.ComCountryList = list;
+    //global.CurCountryList = curl;
+    if (back) back(global.ComCountryList);
+  }
+}
+
 export const GetCurrencyCountryList = (back) => {
   if (global.CurCountryList === undefined) {
     GetCountryList(list => {
@@ -398,7 +409,7 @@ export const CountryDropNum = ({country, value, onClick}) => {
         borderRadius={0}
         style={styles.iconBorder}
         countryCode={country.countryCode}/>
-      <Text style={styles.countryDropItemText}>{country.countryName} ({'+'+country.countryNum})</Text>
+      <TextView style={styles.countryDropItemText}>{country.countryName} ({'+'+country.countryNum})</TextView>
       { value == country.countryNum &&
         <MaterialIcons
           name='check-circle'
@@ -424,7 +435,7 @@ export const CountryDropCode = ({country, value, onClick}) => {
         style={styles.iconBorder}
         borderRadius={0}
         countryCode={country.countryCode}/>
-      <Text style={styles.countryDropItemText}>{country.countryName}</Text>
+      <TextView style={styles.countryDropItemText}>{country.countryName}</TextView>
       { value == country.countryCode &&
         <MaterialIcons
           name='check-circle'
@@ -450,22 +461,76 @@ export const CountryDropCurrency = ({country, value, onClick}) => {
         style={styles.iconBorder}
         borderRadius={0}
         countryCode={country.countryCode}/>
-      <Text style={styles.countryDropItemText}>{country.countryName} ({country.currencySign})</Text>
+      <TextView style={styles.countryDropItemText}>{country.countryName} ({country.currencySign})</TextView>
       { value == country.countryCode
       ? <MaterialIcons
           name='check-circle'
           color={colorAccent}
           size={20}/>
-      : <Text style={styles.countryExchangeRate}>{country.exchangeRate}</Text>
+      : <TextView style={styles.countryExchangeRate}>{country.exchangeRate}</TextView>
       }
     </Button>
   )
 }
 
+export const TestCountryFilter = () => {
+  console.log("国家列表");
+  console.log(countries.length, countryNew.length, countryAll.length);
+  return
+  const list = [], lang = {}
+  countries.forEach(item => {
+    for (let i of countryNew) {
+      if (i.countryCode == item.countryCode) {
+        const country = Object.assign({}, i);
+        const names = i.countryNames;
+        /*delete country.countryNames;
+        country.countryNames = {
+          cs: names.ces,//捷克
+          en: names.common,//英语
+          et: names.est,//爱沙尼亚
+          cy: names.cym,//威尔士
+          de: names.deu,//德国
+          fi: names.fin,//芬兰
+          fr: names.fra,//法国
+          hr: names.hrv,//克罗地亚
+          it: names.ita,//意大利
+          ja: names.jpn,//日本
+          ko: names.kor,//韩国
+          km: names.common,//柬埔寨
+          my: names.common,//缅甸
+          nl: names.nld,//荷兰
+          pl: names.pol,//波兰
+          pt: names.por,//葡萄牙
+          ru: names.rus,//俄罗斯
+          sk: names.slk,//斯洛伐克
+          es: names.spa,//西班牙
+          th: langs[country.countryCode],//泰国
+          ur: names.urd,//巴基斯坦-乌尔都语
+          vi: names.common,//越南
+          zh: names.zho,//中文
+        }*/
+        country.countryNames={
+          ...names,
+          hr: names.hr ?? names.en,
+          it: names.it ?? names.en,
+          ja: names.ja ?? names.en
+        }
+        lang[country.countryCode] = names.zho
+        list.push(country);
+        break;
+      }
+    }
+  })
+  console.log('====================================');
+  console.log(list);
+  console.log('====================================');
+  //console.log(lang);
+}
+
 const styles = StyleSheet.create({
   countryDropItem: {
     borderRadius: 0,
-    backgroundColor: 'white'
+    backgroundColor: colorLight
   },
   countryDropItemView: {
     flex: 1,
@@ -477,7 +542,7 @@ const styles = StyleSheet.create({
   },
   countryDropItemText: {
     flex: 1,
-    color: '#333',
+    color: textPrimary,
     fontSize: 14,
     paddingLeft: 16
   },

+ 36 - 27
Strides-APP/app/components/Dialog.js

@@ -9,6 +9,7 @@ import Toast from 'react-native-root-toast';
 import utils from '../utils/utils';
 import ModalPortal from './ModalPortal';
 import Button from './Button';
+import TextView from './TextView';
 
 const maxDef = isIOS ? 480 : 540;
 var _maxWidth = isIOS ? $vw(75) : $vw(87);
@@ -30,8 +31,8 @@ const showDialog = (props) => {
     align: props.align || 'left',
     title: props.title || '',
     message: props.message || '',
-    ok: props.ok || 'OK',
-    cancel: props.cancel || 'CANCEL',
+    ok: props.ok || $t('nav.ok'),
+    cancel: props.cancel || $t('nav.cancel'),
     showCancel: props.showCancel != undefined ? props.showCancel : true,
     callback: props.callback
   }
@@ -52,16 +53,16 @@ const showResultDialog = (message, ok, back) => {
     title: isIOS ? message : '',
     message: !isIOS ? message : '',
     showCancel: false,
-    ok: ok || 'I Known',
+    ok: ok || $t('nav.ok'),
     callback: back
   }
   showDialog(param);
 }
 
-const showProgressDialog = (message='Loading...') => {
+const showProgressDialog = (message=$t('nav.loading')) => {
   //message = message ?? 'Waiting...';
   ModalPortal.showLoading((
-    isIOS ? message//<IOSProgress message={message}/>
+    isIOS ? <IOSProgress message={message}/>
           : <AndroidProgress message={message}/>
   ));
 }
@@ -82,17 +83,17 @@ const IOSDialog = (props) => {
   return (
     <View style={iosStyle.modalDialog}>
       { props.title != '' &&
-        <Text style={iosStyle.modalTitle}>{props.title}</Text>
+        <TextView style={iosStyle.modalTitle}>{props.title}</TextView>
       }
       { props.message != '' &&
-        <Text style={[
+        <TextView style={[
           iosStyle.message,
           {
             textAlign: props.align
           }
         ]}>
           {props.message}
-        </Text>
+        </TextView>
       }
       <View style={iosStyle.modalFooter}>
         <Button
@@ -128,19 +129,19 @@ const AndroidDialog = (props) => {
   return (
     <View style={andStyles.modalDialog}>
       { props.title != '' &&
-        <Text style={andStyles.title}>
+        <TextView style={andStyles.title}>
           {props.title}
-        </Text>
+        </TextView>
       }
       { props.message != '' &&
-        <Text style={[
+        <TextView style={[
           andStyles.message,
           {
             textAlign: props.align
           }
         ]}>
           {props.message}
-        </Text>
+        </TextView>
       }
       <View style={andStyles.modalFooter}>
         { props.showCancel &&
@@ -175,7 +176,7 @@ const AndroidButton = ({title, onPress}) => {
         style={andStyles.modalPress}
         android_ripple={ripple}
         onPress={onPress}>
-        <Text style={andStyles.modalBtnText}>{title}</Text>
+        <TextView style={andStyles.modalBtnText}>{title}</TextView>
       </Pressable>
     </View>
   );
@@ -187,11 +188,11 @@ const IOSProgress = (props) => {
       <Image
         style={iosStyle.loadingIcon}
         source={require('../images/icon/loading.gif')}/>
-      <Text
+      <TextView
         style={iosStyle.proMessage}
         onPress={() => {
           dismissLoading();
-        }}>{props.message}</Text>
+        }}>{props.message}</TextView>
     </View>
   );
 }
@@ -212,11 +213,11 @@ const AndroidProgress = (props) => {
             direction={'clockwise'}
             spinDuration={2000}/>
         </View>
-        <Text
+        <TextView
           style={andStyles.proMessage}
           onPress={() => {
             dismissLoading();
-          }}>{props.message}</Text>
+          }}>{props.message}</TextView>
       </View>
     </View>
   );
@@ -229,7 +230,7 @@ const iosStyle = StyleSheet.create({
     marginRight: 'auto',
     borderRadius: 12,
     overflow: 'hidden',
-    backgroundColor: 'white'
+    backgroundColor: colorLight
   },
   modalProgress: {
     width: 180,
@@ -238,7 +239,7 @@ const iosStyle = StyleSheet.create({
     marginLeft: 'auto',
     marginRight: 'auto',
     borderRadius: 12,
-    backgroundColor: 'white'
+    backgroundColor: colorLight
   },
   loadingIcon: {
     width: 80,
@@ -261,7 +262,7 @@ const iosStyle = StyleSheet.create({
     textAlign: 'center',
   },
   message: {
-    color: '#333',
+    color: textPrimary,
     fontSize: 14,
     paddingTop: 4,
     paddingLeft: 20,
@@ -283,7 +284,7 @@ const iosStyle = StyleSheet.create({
   btnGroup: {
     flex: 1,
     borderRadius: 0,
-    backgroundColor: 'white'
+    backgroundColor: colorLight
   },
   btnRight: {
     borderLeftWidth: 1,
@@ -318,7 +319,7 @@ const andStyles = StyleSheet.create({
     borderRadius: 3,
     marginLeft: 'auto',
     marginRight: 'auto',
-    backgroundColor: 'white'
+    backgroundColor: colorLight
   },
   progressDialog: {
     width: maxWidth,
@@ -330,7 +331,7 @@ const andStyles = StyleSheet.create({
     borderRadius: 3,
     marginLeft: 'auto',
     marginRight: 'auto',
-    backgroundColor: 'white'
+    backgroundColor: colorLight
   },
   title: {
     color: '#000',
@@ -338,13 +339,13 @@ const andStyles = StyleSheet.create({
     fontSize: 18
   },
   message: {
-    color: '#333',
+    color: textPrimary,
     fontSize: 14,
     paddingBottom: 8,
   },
   proMessage: {
     flex: 1,
-    color: '#333',
+    color: textPrimary,
     fontSize: 15,
     paddingLeft: 24
   },
@@ -372,6 +373,9 @@ const andStyles = StyleSheet.create({
   },
   endView: {
     paddingTop: 16
+  },
+  halfView: {
+    paddingTop: 8
   }
 });
 
@@ -418,7 +422,11 @@ const getStringMessage = (msg) => {
 
 export const InitSomething = () => {
   global.dialogId = undefined;
-  global.EndView = () => <View style={andStyles.endView}/>
+  global.EndView = ({half=false}) => (
+    half
+    ? <View style={andStyles.halfView}/>
+    : <View style={andStyles.endView}/>
+  )
 
   global.toastShort = (msg) => {
     if (typeof msg !== 'string')
@@ -430,7 +438,8 @@ export const InitSomething = () => {
         shadow: false,
         opacity: 0.85,
         textStyle: {
-          fontSize: 14
+          fontSize: 14,
+          lineHeight: 18
         },
         backgroundColor: '#222222',
         containerStyle: {

+ 17 - 13
Strides-APP/app/components/Dropdown.js

@@ -3,6 +3,7 @@ import { FlatList, Keyboard, Pressable, StyleSheet, Text, View } from 'react-nat
 import Modal from 'react-native-modal';
 import Button from './Button';
 import Dialog, { getDialogWidth } from './Dialog';
+import TextView from './TextView';
 
 //const DialogMaxWidth = $vw(85) > 500 ? 500 : $vw(85);
 //const DialogIOSWidth = $vw(75) > 450 ? 450 : $vw(75);
@@ -147,17 +148,23 @@ export default Dropdown = ({
         onPress={() => showList()}>
         { showText && 
           ( selected 
-            ? <Text style={[ui.flex1, textStyle]} numberOfLines={1}>{selected}</Text>
-            : <Text style={[textStyle, placeholderStyle]} numberOfLines={1}>{placeholder}</Text>
+            ? <TextView style={[textStyle, ui.flex1]} numberOfLines={1}>{selected}</TextView>
+            : <TextView style={[placeholderStyle, ui.flex1]} numberOfLines={1}>{placeholder}</TextView>
           )
         }
-        { showIcon && 
-          <Entypo 
-            name={'chevron-thin-down'}
-            size={14}
+        { showIcon && (isIOS
+        ? <MaterialIcons 
+            name={'keyboard-arrow-down'}
+            size={24}
             color={iconColor}
             style={iconStyle}
           />
+        : <MaterialIcons
+            name={'arrow-drop-down'}
+            size={24}
+            color={iconColor}
+            style={iconStyle}
+          />)
         }
       </Pressable>
       <Modal
@@ -168,7 +175,7 @@ export default Dropdown = ({
         {...Dialog.modalProps}
       >
         <View style={styles.dialog}>
-          { title !== '' && <Text style={styles.title}>{title}</Text> }
+          { title !== '' && <TextView style={styles.title}>{title}</TextView> }
           <FlatList
             data={list}
             ref={refFlat}
@@ -206,12 +213,12 @@ const styles = StyleSheet.create({
   },
   valueView: {
     paddingLeft: 16,
-    paddingRight: 32,
+    paddingRight: 8,
     alignItems: 'center',
     flexDirection: 'row'
   },
   valueText: {
-    color: textPrimary,
+    color: '#000',
     fontSize: 16
   },
   itemView: {
@@ -230,9 +237,6 @@ const styles = StyleSheet.create({
     color: textPlacehoder
   },
   iconStyle: {
-    top: '50%',
-    right: 16,
-    marginTop: -7,
-    position: 'absolute',
+    marginLeft: 8
   }
 });

+ 6 - 4
Strides-APP/app/components/HeaderTitle.js

@@ -1,13 +1,14 @@
 import React from 'react';
 import { Platform, StyleSheet, Text } from 'react-native';
 
-const HeaderTitle = ({scope, style=styles.titleColor}) => (
+const HeaderTitle = ({scope, title="", style=styles.titleColor}) => (
   <Text
     ariaLevel="1"
     numberOfLines={1}
+    allowFontScaling={false}
     accessibilityRole="header"
     style={[styles.title, style]}>
-    {$t(scope)}
+    {scope ? $t(scope) : title}
   </Text>
 );
 
@@ -21,8 +22,9 @@ const styles = StyleSheet.create({
     },
     android: {
       fontSize: 20,
-      fontFamily: 'sans-serif-medium',
-      fontWeight: 'normal'
+      //fontFamily: 'sans-serif-medium',
+      fontWeight: 'normal',
+      lineHeight: 26
     },
     default: {
       fontSize: 18,

+ 5 - 6
Strides-APP/app/components/ModalPortal.js

@@ -5,7 +5,6 @@
 import React, { Component } from 'react';
 import { StyleSheet, View } from 'react-native';
 import Modal from 'react-native-modal';
-import Dialog from './Dialog';
 
 let modal
 export default class ModalPortal extends Component {
@@ -17,7 +16,7 @@ export default class ModalPortal extends Component {
       showIOSLoading: false,
       children: <></>,
       loadChildren: <></>,
-      loadMessage: "Loading...",
+      loadMessage: "...",
     };
     modal = this;
     this.isHide = true;
@@ -73,7 +72,7 @@ export default class ModalPortal extends Component {
     if (isIOS) {
       //console.log("showIOSLoading", children)
       this.setState({
-        loadMessage: children,
+        loadChildren: children,
         showIOSLoading: true
       })
     } else {
@@ -124,12 +123,12 @@ export default class ModalPortal extends Component {
   }
 
   onModalShow() {
-    console.log('onModalShow', this.isHide);
+    //console.log('onModalShow', this.isHide);
     this.isHide = false;
   }
 
   onModalHide() {
-    console.log('onModalHide', this.isHide);
+    //console.log('onModalHide', this.isHide);
     this.isHide = true;
   }
 
@@ -163,7 +162,7 @@ export default class ModalPortal extends Component {
         </Modal>
         { this.state.showIOSLoading &&
           <View style={styles.iosLoadingView}>
-            <Dialog.IOSProgress message={this.state.loadMessage}/>
+            {this.state.loadChildren}
           </View>
         }
       </>

+ 4 - 0
Strides-APP/app/components/Switch.js

@@ -1,3 +1,7 @@
+/**
+ * 自定义Switch组件
+ * @邠心vbe on 2023/08/30
+ */
 import React from 'react';
 import { Switch, SwitchProps, Text, View } from 'react-native';
 import Animated from 'react-native-reanimated';

+ 28 - 18
Strides-APP/app/components/TextRadius.js → Strides-APP/app/components/TextView.js

@@ -1,3 +1,7 @@
+/**
+ * 自定义Text组件
+ * @邠心vbe on 2023/08/30
+ */
 import React from 'react';
 import { Text, View } from 'react-native';
 
@@ -19,7 +23,7 @@ const getRadius = (style) => {
       continue;
     }
     if (name.indexOf('padding') >= 0) {
-      text[name] = s[name];
+      view[name] = s[name];
       continue;
     }
     if (name.indexOf('color') == 0) {
@@ -33,9 +37,6 @@ const getRadius = (style) => {
     if (name.indexOf('border') >= 0) {
       view[name] = s[name];
       continue;
-    } else if (name.indexOf('Radius') >= 0) {
-      view[name] = s[name];
-      continue;
     }
     if (name.indexOf('background') >= 0) {
       view[name] = s[name];
@@ -48,27 +49,36 @@ const getRadius = (style) => {
     if (name.indexOf('position') >= 0) {
       view[name] = s[name];
       continue;
-    } else {
-      view[name] = s[name];
     }
+    view[name] = s[name];
   }
+  //修复RN0.7x+渲染中文无法对齐
+  if (text.fontSize) {
+    text.lineHeight = text.fontSize * 1.3;
+  } else {
+    text.fontSize = 14;
+    text.lineHeight = 18.2;
+  }
+  text.includeFontPadding = false;
   return {
     view: view,
     text: text
   }
 }
 
-const TextRadius = ({style, onPress, children}) => {
-  if (isIOS) {
-    const styles = getRadius(style);
-    return (
-      <View style={styles.view}>
-        <Text style={styles.text} onPress={onPress}>{children}</Text>
-      </View>
-    );
-  } else {
-    return <Text style={style} onPress={onPress}>{children}</Text>;
-  }
+const TextView = ({style, ellipsizeMode, numberOfLines, allowFontScaling=false, onPress, onLongPress, children}) => {
+  const styles = getRadius(style);
+  return (
+    <View style={styles.view}>
+      <Text
+        style={styles.text}
+        allowFontScaling={allowFontScaling}
+        ellipsizeMode={ellipsizeMode}
+        numberOfLines={numberOfLines}
+        onPress={onPress}
+        onLongPress={onLongPress}>{children}</Text>
+    </View>
+  );
 };
 
-export default TextRadius;
+export default TextView;

BIN
Strides-APP/app/images/drawer-logo.png


BIN
Strides-APP/app/images/icon/search-loading.gif


+ 26 - 10
Strides-APP/app/pages/Launch.js

@@ -3,28 +3,44 @@
  * @邠心vbe on 2021/03/22
  */
 import React from 'react'
-import { Image, StyleSheet, Text, View } from 'react-native'
+import { Animated, Easing, Image, StyleSheet, View } from 'react-native'
 import {InitSomething} from '../components/Dialog';
+import MyStatusBar from '../components/MyStatusBar';
 import { PageList } from './Router';
+import app from '../../app.json';
 
 class Launch extends React.Component {
   
   constructor(props) {
     super(props);
     this.state = {
-      isShow: true
+      isShow: true,
+      opacity: new Animated.Value(0)
     };
   }
 
   componentDidMount() {
+    this.init();
+    MyStatusBar.setStatusBarThemes(MyStatusBar.DARK_STYLE, colorLight);
     InitSomething();
     if (isIOS) {
       this.goHome(100);
     } else {
-      this.goHome(3000);
+      this.goHome(2500);
     }
   }
 
+  init() {
+    Animated.timing(this.state.opacity, {
+      toValue: 1,
+      duration: 1500,
+      easing: Easing.linear,
+      useNativeDriver: true
+    }).start(() => {
+      
+    });
+  }
+
   goHome(time) {
     setTimeout(() => {
       this.setState({
@@ -40,13 +56,13 @@ class Launch extends React.Component {
       ? isIOS
         ? <View style={styles.iosContent}></View>
         : <View style={styles.container}>
-            <View style={styles.content}>
+            <Animated.View style={[styles.content, {opacity: this.state.opacity}]}>
               <Image
                 style={styles.logo}
                 resizeMode='contain'
                 source={require('../images/app-logo.png')}/>
               {/* <Text style={styles.slogan}>Feel The Green Energy</Text> */}
-            </View>
+            </Animated.View>
           </View>
       : <View style={styles.loaded}></View>
     );
@@ -58,12 +74,12 @@ export default Launch
 const styles = StyleSheet.create({
   container: {
     alignItems: 'center',
-    backgroundColor: colorThemes,
+    backgroundColor: colorLight,
     ...StyleSheet.absoluteFillObject
   },
   iosContent: {
     flex: 1,
-    backgroundColor: colorThemes
+    backgroundColor: colorLight
   },
   loaded: {
     flex: 1,
@@ -74,15 +90,15 @@ const styles = StyleSheet.create({
     paddingTop: $vh(30)
   },
   logo: {
-    width: 190.67,
-    height: 60,
+    width: 240,
+    height: 120,
     marginLeft: 0,
     //top: $vh(25),
     //bottom: $vh(25),
     //position: 'absolute'
   },
   slogan: {
-    color: '#333',
+    color: textPrimary,
     fontSize: 14,
     fontStyle: 'italic',
     fontWeight: 'bold'

+ 1 - 0
Strides-APP/app/pages/Router.js

@@ -364,6 +364,7 @@ const Title = (title, opt = {}, titleScope) => {
     },
     headerTintColor: pageTitleTint, //配置标题栏文字和图标颜色
     headerBackTitle: ' ', //配置iOS返回按钮文字
+    headerBackTitleVisible: false,
     ...opt
   }
   if (titleScope && $t) {

+ 10 - 9
Strides-APP/app/pages/Settings.js

@@ -12,6 +12,7 @@ import { i18nUtil } from '../i18n';
 import routeUtil from '../utils/routeUtil';
 import storage, { getStorage, setStorage } from '../utils/storage';
 import app from '../../app.json';
+import TextView from '../components/TextView';
 
 export const SETTINGS_KEY = 'CHARGE_SETTINGS_V2'
 export const SettingUtil = {
@@ -164,7 +165,7 @@ export default class Settings extends Component {
       <View style={ui.flex1}>
         { app.modules.i18n &&
           <View style={styles.menuView}>
-            <Text style={styles.buttonText}>{$t('settings.language')}</Text>
+            <TextView style={styles.buttonText}>{$t('settings.language')}</TextView>
             <Dropdown
               list={this.state.localeList}
               //title={$t('settings.language')}
@@ -178,12 +179,12 @@ export default class Settings extends Component {
             />
           </View>
         }
-        <Text style={styles.title}>{$t('settings.notification')}</Text>
+        <TextView style={styles.title}>{$t('settings.notification')}</TextView>
         <Button
           style={styles.itemButton}
           viewStyle={styles.itemView}
           onClick={() => this.changeNotifySwitch("notifyChargingComplete", !userInfo.notifyChargingComplete)}>
-          <Text style={styles.buttonText}>{$t('settings.notifyChargingComplete')}</Text>
+          <TextView style={styles.buttonText}>{$t('settings.notifyChargingComplete')}</TextView>
           <Switch
             value={this.state.userInfo.notifyChargingComplete}
             onValueChange={v => this.changeNotifySwitch("notifyChargingComplete", v)}/>
@@ -192,7 +193,7 @@ export default class Settings extends Component {
           style={styles.itemButton}
           viewStyle={styles.itemView}
           onClick={() => this.changeNotifySwitch("notifyLowBalance", !userInfo.notifyLowBalance)}>
-          <Text style={styles.buttonText}>{$t('settings.notifyLowBalance')}</Text>
+          <TextView style={styles.buttonText}>{$t('settings.notifyLowBalance')}</TextView>
           <Switch 
             value={this.state.userInfo.notifyLowBalance}
             onValueChange={v => this.changeNotifySwitch("notifyLowBalance", v)}/>
@@ -201,23 +202,23 @@ export default class Settings extends Component {
           style={styles.itemButton}
           viewStyle={styles.itemView}
           onClick={() => this.changeNotifySwitch("notifyPromotionsOffers", !userInfo.notifyPromotionsOffers)}>
-          <Text style={styles.buttonText}>{$t('settings.notifyPromotionsOffers')}</Text>
+          <TextView style={styles.buttonText}>{$t('settings.notifyPromotionsOffers')}</TextView>
           <Switch
             value={this.state.userInfo.notifyPromotionsOffers}
             onValueChange={v => this.changeNotifySwitch("notifyPromotionsOffers", v)}/>
         </Button>
-        <Text style={styles.title}>{$t('settings.maps')}</Text>
+        <TextView style={styles.title}>{$t('settings.maps')}</TextView>
         <Button
           style={styles.itemButton}
           viewStyle={styles.itemView}
           onClick={() => this.changeSwitch('alwaysLocation', !this.state.settings.alwaysLocation)}>
-          <Text style={styles.buttonText}>{$t('settings.showMyLocations')}</Text>
+          <TextView style={styles.buttonText}>{$t('settings.showMyLocations')}</TextView>
           <Switch
             value={this.state.settings.alwaysLocation}
             onValueChange={v => this.changeSwitch('alwaysLocation', v)}/>
         </Button>
         <View style={styles.menuView}>
-          <Text style={styles.buttonText}>{$t('settings.refreshInterval')}</Text>
+          <TextView style={styles.buttonText}>{$t('settings.refreshInterval')}</TextView>
           <Dropdown
             style={styles.localeSelect}
             list={this.state.intervalList}
@@ -235,7 +236,7 @@ export default class Settings extends Component {
           style={styles.itemButton}
           viewStyle={styles.itemView}
           onClick={() => this.changeSwitch('moveMyLocation', !this.state.settings.moveMyLocation)}>
-          <Text style={styles.buttonText}>{$t('settings.moveMyLocations')}</Text>
+          <TextView style={styles.buttonText}>{$t('settings.moveMyLocations')}</TextView>
           <Switch
             value={this.state.settings.moveMyLocation}
             onValueChange={v => this.changeSwitch('moveMyLocation', v)}/>

+ 4 - 3
Strides-APP/app/pages/about/About.js

@@ -5,6 +5,7 @@
 import React from 'react';
 import { View, Text, Image, StyleSheet } from 'react-native';
 import app from '../../../app.json';
+import TextView from '../../components/TextView';
 import { PageList } from '../Router';
 
 const author = 2023;
@@ -20,10 +21,10 @@ export default About = () => {
       <Text style={styles.versionName}>{app.versionName}</Text>
       <Text style={ui.flex1}></Text>
       <View style={ui.flexcc}>
-        <Text style={styles.linkText} onPress={() => startPage(PageList.condition)}>{$t("drawer.termsOfUse")}</Text>
-        <Text style={styles.linkText} onPress={() => startPage(PageList.privacy)}>{$t("drawer.privacyPolicy")}</Text>
+        <TextView style={styles.linkText} onPress={() => startPage(PageList.condition)}>{$t("drawer.termsOfUse")}</TextView>
+        <TextView style={styles.linkText} onPress={() => startPage(PageList.privacy)}>{$t("drawer.privacyPolicy")}</TextView>
       </View>
-      <Text style={styles.copyright}>{'Copyright ' + /*app.versionName + ' Build ' + app.versionCode + */getYearRange()+' Strides YTL Pte. Ltd.'}</Text>
+      <Text style={styles.copyright}>{'Copyright ' + /*app.versionName + ' Build ' + app.versionCode + */getYearRange()+' LUMI CHARGING PTE. LTD.'}</Text>
     </View>
   );
 }

+ 3 - 2
Strides-APP/app/pages/about/Contact.js

@@ -2,6 +2,7 @@ import React, { Component } from 'react';
 import { View, Text, StyleSheet, Image, Linking } from 'react-native';
 import Button from '../../components/Button';
 import app from '../../../app.json';
+import TextView from '../../components/TextView';
 
 export default class Contact extends Component {
   constructor(props) {
@@ -27,8 +28,8 @@ export default class Contact extends Component {
             resizeMode='contain'
             source={require('../../images/about-logo.png')}/>
         </View>
-        <Text style={styles.labelText}>{$t("support.labelOpenTime")}</Text>
-        <Text style={styles.contentText}>{$t("support.timeAllDay")}</Text>
+        <TextView style={styles.labelText}>{$t("support.labelOpenTime")}</TextView>
+        <TextView style={styles.contentText}>{$t("support.timeAllDay")}</TextView>
         <View style={ui.flex1}></View>
         <Button
           text={$t("support.btnCallSupport")}

+ 1 - 1
Strides-APP/app/pages/alert/Alerts.js

@@ -105,7 +105,7 @@ export default class Alerts extends Component {
             onRefresh={() => this.getMessageList()}
           />
         }
-        ListEmptyComponent={<Text style={styles.noData}>{$t('notification.empty')}</Text>}
+        ListEmptyComponent={<Text style={styles.noData} allowFontScaling={false}>{$t('notification.empty')}</Text>}
       />
     );
   }

+ 7 - 6
Strides-APP/app/pages/alert/Campaign.js

@@ -7,6 +7,7 @@ import { View, Text, StyleSheet, ScrollView, Image } from 'react-native';
 import apiNotification from '../../api/apiNotification';
 import Button from '../../components/Button';
 import Dialog from '../../components/Dialog';
+import TextView from '../../components/TextView';
 import { PageList } from '../Router';
 
 export default class Campaign extends Component {
@@ -64,20 +65,20 @@ export default class Campaign extends Component {
             resizeMode="cover"
           />
           <View style={styles.header}>
-            <Text
+            <TextView
               style={styles.textTitle}>
               {this.state.messageInfo.notificationTitle}
-            </Text>
-            <Text
+            </TextView>
+            <TextView
               style={styles.textDate}
               numberOfLines={1}>
               {this.state.messageInfo.createTime}
-            </Text>
+            </TextView>
           </View>
-          <Text
+          <TextView
             style={styles.textMessage}>
             {this.state.messageInfo.notificationText}
-          </Text>
+          </TextView>
         </ScrollView>
       </View>
     );

+ 7 - 6
Strides-APP/app/pages/alert/ItemView.js

@@ -4,6 +4,7 @@
  */
 import React from 'react';
 import { Pressable, StyleSheet, Text, View } from 'react-native';
+import TextView from '../../components/TextView';
 
 const IconType = ({type, style, size=32, color=colorAccent}) => {
   switch (type) {
@@ -107,23 +108,23 @@ export default ItemView = ({item, index, separators, onPress, onLongPress}) => {
       />
       <View style={styles.itemContent}>
         <View style={ui.flexc}>
-          <Text
+          <TextView
             style={[styles.textTitle, (!item.readStatus && styles.unread)]}
             numberOfLines={1}
             ellipsizeMode="tail">
             {item.notificationTitle}
-          </Text>
-          <Text
+          </TextView>
+          <TextView
             style={[styles.textDate, (!item.readStatus && styles.unread)]}
             numberOfLines={1}>
             {item.createTime}
-          </Text>
+          </TextView>
         </View>
-        <Text
+        <TextView
           style={styles.textMessage}
           numberOfLines={2}>
           {item.notificationText}
-        </Text>
+        </TextView>
       </View>
     </Pressable>
   )

+ 7 - 6
Strides-APP/app/pages/alert/Message.js

@@ -7,6 +7,7 @@ import { View, Text, StyleSheet, ScrollView } from 'react-native';
 import apiNotification from '../../api/apiNotification';
 import Button from '../../components/Button';
 import Dialog from '../../components/Dialog';
+import TextView from '../../components/TextView';
 import { PageList } from '../Router';
 
 export default class Message extends Component {
@@ -56,22 +57,22 @@ export default class Message extends Component {
     return (
       <View style={styles.container}>
         <View style={styles.header}>
-          <Text
+          <TextView
             style={styles.textTitle}>
             {this.state.messageInfo.notificationTitle}
-          </Text>
-          <Text
+          </TextView>
+          <TextView
             style={styles.textDate}
             numberOfLines={1}>
             {this.state.messageInfo.createTime}
-          </Text>
+          </TextView>
         </View>
         <ScrollView
           style={ui.flex1}>
-          <Text
+          <TextView
             style={styles.textMessage}>
             {this.state.messageInfo.notificationText}
-          </Text>
+          </TextView>
         </ScrollView>
         { this.state.messageInfo.notificationType == "Review" &&
           <Button

+ 13 - 10
Strides-APP/app/pages/alert/Notification.js

@@ -26,12 +26,12 @@ export default class Notification extends Component {
       component: Promotions
     }]
     this.tabBarStyle = {
-      style: styles.tabStyle,
-      pressColor: rippleColor,
-      scrollEnabled: false,
-      indicatorStyle: styles.indicator,
-      activeTintColor: app.isWhitelabel ? textPrimary : colorLight,
-      inactiveTintColor: app.isWhitelabel ? textSecondary : "#E0E0E0"
+      tabBarStyle: styles.tabStyle,
+      tabBarPressColor: rippleColor,
+      tabBarScrollEnabled: false,
+      tabBarIndicatorStyle: styles.indicator,
+      tabBarActiveTintColor: app.isWhitelabel ? textPrimary : colorLight,
+      tabBarInactiveTintColor: app.isWhitelabel ? textSecondary : "#E0E0E0"
     }
     this.isHide = false;
   }
@@ -67,16 +67,19 @@ export default class Notification extends Component {
     return (
       <Tab.Navigator
         style={styles.container}
-        tabBarOptions={this.tabBarStyle}
-        lazy={false}
-        lazyPreloadDistance={1}>
+        screenOptions={{
+          lazy: false,
+          lazyPreloadDistance: 1,
+          ...this.tabBarStyle
+        }}>
         { this.pageAdapter.map((item, index) => 
           <Tab.Screen
             key={index}
             name={item.name}
             component={item.component}
             options={{
-              title: item.title
+              title: item.title,
+              tabBarAllowFontScaling: false
             }}
           />
         )}

+ 2 - 2
Strides-APP/app/pages/alert/Promotions.js

@@ -109,13 +109,13 @@ export default class Promotions extends Component {
               onRefresh={() => this.getMessageList()}
             />
           }
-          ListEmptyComponent={<Text style={styles.noData}>{$t('notification.empty')}</Text>}
+          ListEmptyComponent={<Text style={styles.noData} allowFontScaling={false}>{$t('notification.empty')}</Text>}
         />
       );
     } else {
       return (
         <View>
-          <Text style={ui.noData}> Coming soon </Text>
+          <Text style={ui.noData} allowFontScaling={false}> Coming soon </Text>
         </View>
       );
     }

+ 1 - 1
Strides-APP/app/pages/bookmark/Bookmarks.js

@@ -148,7 +148,7 @@ export default class Bookmarks extends Component {
           renderItem={this.listItem}
           keyExtractor={item => item.id}
           keyboardShouldPersistTaps="always"
-          ListEmptyComponent={<Text style={styles.noResult}>{$t('home.noSearch')}</Text>}
+          ListEmptyComponent={<Text style={styles.noResult} allowFontScaling={false}>{$t('home.noSearch')}</Text>}
           refreshControl={
             <RefreshControl
               {...MyRefreshProps()}

+ 44 - 43
Strides-APP/app/pages/charge/Charge.js

@@ -9,11 +9,12 @@ import Button from '../../components/Button';
 import Dialog from '../../components/Dialog';
 import { PageList } from '../Router';
 import { BatteryView, ChargeStyle, circleSize, EnterStationDialog, TypeImage } from './Charging';
-import Payment, { PaymentDefault, PAYTYPE } from '../wallet/Payment';
+import Payment from '../wallet/Payment';
 import { QRResult } from './QRScan';
 import { ErrorDialog } from './InfoDialog';
 import utils from '../../utils/utils';
-import TextRadius from '../../components/TextRadius';
+import TextView from '../../components/TextView';
+import { PaymentDefault, PAYTYPE } from '../payment/PaymentConfig';
 
 export default class Charge extends Component {
 
@@ -149,35 +150,35 @@ export default class Charge extends Component {
               height: 42
             }}
             source={require('../../images/charge/icon-station-no.png')}>
-            <Text style={{
+            <TextView style={{
               left: 0,
               right: 0,
               bottom: 1,
               fontSize: 8,
               textAlign: 'center',
               position: 'absolute'
-            }}>{this.state.connectorInfo.connectorId}</Text>
+            }}>{this.state.connectorInfo.connectorId}</TextView>
           </ImageBackground>
           <View style={ChargeStyle.infoGroup}>
-            <Text style={ChargeStyle.infoTitle}>Type</Text>
-            <Text style={ChargeStyle.infoText}>{this.state.connectorInfo.chargeType}{this.state.connectorInfo.wattage}</Text>
+            <TextView style={ChargeStyle.infoTitle}>Type</TextView>
+            <TextView style={ChargeStyle.infoText}>{this.state.connectorInfo.chargeType}{this.state.connectorInfo.wattage}</TextView>
           </View>
           <View style={ChargeStyle.infoGroup}>
-            <Text style={ChargeStyle.infoTitle}>Power</Text>
-            <Text style={ChargeStyle.infoText}>{this.state.connectorInfo.wattage}kW{/*this.state.connectorInfo.rateType*/}</Text>
+            <TextView style={ChargeStyle.infoTitle}>Power</TextView>
+            <TextView style={ChargeStyle.infoText}>{this.state.connectorInfo.wattage}kW{/*this.state.connectorInfo.rateType*/}</TextView>
           </View>
           <View style={ChargeStyle.infoGroup}>
-            <Text style={ChargeStyle.infoTitle}>Rate</Text>
-            <Text style={ChargeStyle.infoText}>{currency}{this.state.connectorInfo.rate}/{this.state.connectorInfo.rateType}</Text>
+            <TextView style={ChargeStyle.infoTitle}>Rate</TextView>
+            <TextView style={ChargeStyle.infoText}>{currency}{this.state.connectorInfo.rate}/{this.state.connectorInfo.rateType}</TextView>
           </View>
           { this.state.connectorInfo.isCheckThrough
           ? <View style={ChargeStyle.infoGroup}>
               <MaterialIcons name='check-circle' size={18} color='#90DB0A'/>
-              <Text style={ChargeStyle.authText}>Authenticated</Text>
+              <TextView style={ChargeStyle.authText}>Authenticated</TextView>
             </View>
           : <View style={ChargeStyle.infoGroup}>
               <MaterialCommunityIcons name='close-circle' size={18} color='#FF6666'/>
-              <Text style={ChargeStyle.authText}>Not Connected</Text>
+              <TextView style={ChargeStyle.authText}>Not Connected</TextView>
             </View>
           }
         </View>
@@ -197,44 +198,44 @@ export default class Charge extends Component {
               height: 42
             }}
             source={require('../../images/charge/icon-station-no.png')}>
-            <Text style={{
+            <TextView style={{
               left: 0,
               right: 0,
               bottom: 1,
               fontSize: 8,
-              color: '#333',
+              color: textPrimary,
               textAlign: 'center',
               position: 'absolute'
-            }}>{this.state.connectorInfo.connectorId}</Text>
+            }}>{this.state.connectorInfo.connectorId}</TextView>
           </ImageBackground>
           <View style={ChargeStyle.infoGroup}>
-            <Text style={ChargeStyle.infoTitle}>Type</Text>
-            <Text style={ChargeStyle.infoText}>{this.state.connectorInfo.chargeType}{this.state.connectorInfo.wattage}</Text>
+            <TextView style={ChargeStyle.infoTitle}>Type</TextView>
+            <TextView style={ChargeStyle.infoText}>{this.state.connectorInfo.chargeType}{this.state.connectorInfo.wattage}</TextView>
           </View>
           <View style={ChargeStyle.infoGroup}>
-            <Text style={ChargeStyle.infoTitle}>Power</Text>
-            <Text style={ChargeStyle.infoText}>{this.state.connectorInfo.wattage}kW</Text>
+            <TextView style={ChargeStyle.infoTitle}>Power</TextView>
+            <TextView style={ChargeStyle.infoText}>{this.state.connectorInfo.wattage}kW</TextView>
           </View>
           <View style={ChargeStyle.infoGroup}>
-            <Text style={ChargeStyle.infoTitle}>Rate</Text>
-            <Text style={ChargeStyle.infoText}>{currency}{this.state.connectorInfo.rate}/{this.state.connectorInfo.rateType}</Text>
+            <TextView style={ChargeStyle.infoTitle}>Rate</TextView>
+            <TextView style={ChargeStyle.infoText}>{currency}{this.state.connectorInfo.rate}/{this.state.connectorInfo.rateType}</TextView>
           </View>
           <View style={ChargeStyle.infoGroup}>
-            <Text style={styles.inUse}>In Use</Text>
+            <TextView style={styles.inUse}>In Use</TextView>
           </View>
         </View>
         <View style={[ChargeStyle.stationInfoView, ChargeStyle.itemDivide]}>
           <View style={ChargeStyle.infoGroup}>
-            <Text style={ChargeStyle.infoTitle}>Time Elapsed</Text>
-            <Text style={ChargeStyle.infoText}>{this.state.connectorInfo.timeElapsed?.toFixed(0) ?? 0} Minutes</Text>
+            <TextView style={ChargeStyle.infoTitle}>Time Elapsed</TextView>
+            <TextView style={ChargeStyle.infoText}>{this.state.connectorInfo.timeElapsed?.toFixed(0) ?? 0} Minutes</TextView>
           </View>
           <View style={ChargeStyle.infoGroup}>
-            <Text style={ChargeStyle.infoTitle}>Total kWh Delivered</Text>
-            <Text style={ChargeStyle.infoText}>{this.state.connectorInfo.totalKWhDelivered ?? 0} kWh</Text>
+            <TextView style={ChargeStyle.infoTitle}>Total kWh Delivered</TextView>
+            <TextView style={ChargeStyle.infoText}>{this.state.connectorInfo.totalKWhDelivered ?? 0} kWh</TextView>
           </View>
           <View style={ChargeStyle.infoGroup}>
-            <Text style={ChargeStyle.infoTitle}>Total Charges</Text>
-            <Text style={ChargeStyle.infoText}>{currency} {this.state.connectorInfo.totalCharges?.toFixed(2) ?? '0.0'}</Text>
+            <TextView style={ChargeStyle.infoTitle}>Total Charges</TextView>
+            <TextView style={ChargeStyle.infoText}>{currency} {this.state.connectorInfo.totalCharges?.toFixed(2) ?? '0.0'}</TextView>
           </View>
         </View>
       </View>
@@ -246,7 +247,7 @@ export default class Charge extends Component {
   StepRateView() {
     return (
       <>
-        <Text style={styles.title}>Rates</Text>
+        <TextView style={styles.title}>Rates</TextView>
         <View style={styles.listView}>
           { this.state.rateList.length > 0
             ? this.state.rateList.map((item, index) => {
@@ -256,20 +257,20 @@ export default class Charge extends Component {
                       style={ChargeStyle.infoIcon}
                       source={item.type?.indexOf('AC') >= 0 ? TypeImage.AC : TypeImage.DC}/>
                     <View style={ChargeStyle.infoGroup}>
-                      <Text style={ChargeStyle.infoTitle}>Type</Text>
-                      <Text style={ChargeStyle.infoText}>{item.type}</Text>
+                      <TextView style={ChargeStyle.infoTitle}>Type</TextView>
+                      <TextView style={ChargeStyle.infoText}>{item.type}</TextView>
                     </View>
                     <View style={ChargeStyle.infoGroup}>
-                      <Text style={ChargeStyle.infoTitle}>Power</Text>
-                      <Text style={ChargeStyle.infoText}>{item.power}</Text>
+                      <TextView style={ChargeStyle.infoTitle}>Power</TextView>
+                      <TextView style={ChargeStyle.infoText}>{item.power}</TextView>
                     </View>
                     <View style={ChargeStyle.infoGroup}>
-                      <Text style={ChargeStyle.infoTitle}>Rate</Text>
-                      <Text style={ChargeStyle.infoText}>{item.rates}</Text>
+                      <TextView style={ChargeStyle.infoTitle}>Rate</TextView>
+                      <TextView style={ChargeStyle.infoText}>{item.rates}</TextView>
                     </View>
                     { item?.connectorCount?.available > 0
-                      ? <TextRadius style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable]}>Available</TextRadius>
-                      : <TextRadius style={[ChargeStyle.infoStatus, ChargeStyle.statusUnavailable]}>Unavailable</TextRadius>
+                      ? <TextView style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable]}>Available</TextView>
+                      : <TextView style={[ChargeStyle.infoStatus, ChargeStyle.statusUnavailable]}>Unavailable</TextView>
                     }
                   </View>
                 );
@@ -310,14 +311,14 @@ export default class Charge extends Component {
   StepStartView() {
     return (
       <>
-        <Text style={styles.title}>Your Selection</Text>
+        <TextView style={styles.title}>Your Selection</TextView>
         {this.StationInfo()}
         <View style={ui.center}>
           <ImageBackground
             style={styles.batteryBorder}
             source={require('../../images/charge/ic-charge-circle.png')}>
             <Text style={{
-              color: '#333',
+              color: textPrimary,
               fontSize: 16,
               lineHeight: 22,
               textAlign: 'center'
@@ -428,7 +429,7 @@ export default class Charge extends Component {
       }
       if (!this.props.visible && this.props.onCharge)
         this.props.onCharge()
-    }).catch(err => {
+    }).catch((err) => {
       //TODO 模拟测试
       this.setState({
         isStart: false,
@@ -545,7 +546,7 @@ export default class Charge extends Component {
             break;
         }
       }
-    }).catch(err => {
+    }).catch((err) => {
       toastShort(err)
       this.setState({
         errorCode: 'A9',
@@ -575,7 +576,7 @@ export default class Charge extends Component {
           this.autoCheckIsCharge();
         }, 30000);*/
         Dialog.dismissLoading();
-      }).catch(err => {
+      }).catch((err) => {
         //toastShort(err);
         Dialog.dismissLoading();
         this.setState({
@@ -617,7 +618,7 @@ export default class Charge extends Component {
         Dialog.dismissLoading();
         toastShort('An error detected, please retry.')
       }
-    }).catch(err => {
+    }).catch((err) => {
       Dialog.dismissLoading();
       toastShort(err);
       this.setState({

+ 16 - 13
Strides-APP/app/pages/charge/Charging.js

@@ -24,15 +24,18 @@ export const TypeImage = {
 
 export const TypeImageList = [
   {
-    name: 'AC',
+    name: "AC",
+    nameScope: 'charging.AC',
     key: 'AC',
     icon: require('../../images/charge/ic-type-ac.png')
   }, {
-    name: 'DC',
+    name: "DC",
+    nameScope:'charging.DC',
     key: 'DC',
     icon: require('../../images/charge/ic-type-dc.png')
   }, /*{
     name: 'Chademo',
+    nameScope: 'charging.Chademo',
     key: 'CHADEMO',
     icon: require('../../images/charge/ic-type-chademo.png')
   }*/
@@ -211,7 +214,7 @@ export const EnterStationDialog = ({visible, stationId, onConfirm, onClose}) =>
   var [inputStationId, setInput] = useState('')
 
   const enterStatioinId= () => {
-    console.log(inputStationId);
+    //console.log(inputStationId);
     if (inputStationId) {
       QRResult.applyInputStation(inputStationId, stationId, (success, err) => {
         setInput('')
@@ -299,14 +302,14 @@ const styles = StyleSheet.create({
     justifyContent: 'flex-end',
   },
   batteryPercent: {
-    color: '#333',
+    color: textPrimary,
     fontSize: 22,
     textAlign: 'center',
     paddingTop: 10,
     paddingBottom: 8
   },
   batterySoc: {
-    color: '#333',
+    color: textPrimary,
     fontSize: 18,
     textAlign: 'center',
     paddingTop: 10,
@@ -361,7 +364,7 @@ const styles = StyleSheet.create({
   },
   completeTip: {
     width: circleSize,
-    color: '#333',
+    color: textPrimary,
     fontSize: 16,
     paddingLeft: 16,
     paddingRight: 16,
@@ -373,7 +376,7 @@ const styles = StyleSheet.create({
     marginRight: 'auto',
     borderRadius: isIOS ? 20 : 3,
     width: DialogMaxWidth,
-    backgroundColor: 'white'
+    backgroundColor: colorLight
   },
   stationInput: {
     ...$padding(4, 10),
@@ -430,12 +433,12 @@ export const ChargeStyle = StyleSheet.create({
     fontSize: 12
   },
   infoText: {
-    color: '#333',
+    color: textPrimary,
     fontSize: 12,
     paddingTop: 4
   },
   infoBoldNumber: {
-    color: '#333',
+    color: textPrimary,
     fontSize: 14,
     paddingTop: 3,
     fontWeight: 'bold'
@@ -454,12 +457,12 @@ export const ChargeStyle = StyleSheet.create({
     borderTopColor: '#eee'
   },
   statusSelected: {
-    color: '#333',
+    color: textPrimary,
     ...$padding(4, 11),
     backgroundColor: colorAccent
   },
   statusAvailable: {
-    color: 'white',
+    color: textLight,
     backgroundColor: '#90DB0A'
   },
   statusUnavailable: {
@@ -469,11 +472,11 @@ export const ChargeStyle = StyleSheet.create({
     backgroundColor: '#CCC'
   },
   statusWarning: {
-    color: 'white',
+    color: textLight,
     backgroundColor: colorAccent
   },
   rateText: {
-    color: '#333',
+    color: textPrimary,
     fontSize: 14,
   },
   ratePrice: {

+ 2 - 2
Strides-APP/app/pages/charge/Details.js

@@ -29,9 +29,9 @@ export default class Details extends Component {
         title: 'Site Information'
       }, {
         title: 'Charge'
-      }, /*{
+      }, {
         title: 'Reserve'
-      }*/],
+      }],
       refreshId: 0,
       refreshing: false,
       showContent: false,

+ 31 - 27
Strides-APP/app/pages/charge/InfoDialog.js

@@ -16,7 +16,7 @@ export const DialogMaxWidth = $vw(85) > 500 ? 500 : $vw(85);
 export const LoginDialog = ({onHide, onClose}) => {
   return (
     <View style={styles.dialog}>
-      <Text style={styles.title}>Oops!</Text>
+      <Text style={styles.title}>{$t('charging.titleOops')}</Text>
       <Text style={styles.message}>You need to be registered user of {app.displayName}</Text>
       <View style={styles.loginTipView}>
         <Text style={styles.tipText}>Login to use this feature</Text>
@@ -28,7 +28,7 @@ export const LoginDialog = ({onHide, onClose}) => {
         textSize={17}
         style={styles.loginButton}
         viewStyle={styles.loginButtonView}
-        text='Login'
+        text={$t('sign.btnLogin')}
         onClick={() => {
           onHide();
           startPage(PageList.login)
@@ -40,7 +40,7 @@ export const LoginDialog = ({onHide, onClose}) => {
           startPage(PageList.register, {actionLogin: true});
         }}>
         <Text style={styles.signUpText}>Don't have an account?</Text>
-        <Text style={styles.signUpLink}>Sign Up</Text>
+        <Text style={styles.signUpLink}>{$t('sign.btnSignUp')}</Text>
       </Pressable>
       <Ionicons
         name='close-outline'
@@ -60,14 +60,14 @@ export const ErrorDialog = ({visible, code, message, onClose}) => {
       onBackdropPress={onClose}
       {...ModalProps}>
       <View style={styles.dialog}>
-        <Text style={styles.title}>Oops!</Text>
+        <Text style={styles.title}>{$t('charging.titleOops')}</Text>
         <Text style={styles.message}>{message}</Text>
         <ErrorImage code={code}/>
         <Button
           textSize={17}
           style={styles.loginButton}
           viewStyle={styles.loginButtonView}
-          text='Okay'
+          text={$t('charging.btnOkay')}
           onClick={onClose}/>
         <Text style={{fontSize: 12}}></Text>
         <Ionicons
@@ -89,12 +89,12 @@ export const LowCreditDialog = ({visible, onClose}) => {
       onBackdropPress={onClose}
       {...ModalProps}>
       <View style={styles.dialog}>
-        <Text style={styles.title}>Low Credits</Text>
-        <Text style={styles.message}>Your credit is below S$5.</Text>
-        <Text style={styles.message}>{"This charging session will end if there is insufficient credits.\n"}</Text>
+        <Text style={styles.title}>{$t('wallet.titleLowCredits')}</Text>
+        <Text style={styles.message}>{$t('wallet.creditIsBelow5')}</Text>
+        <Text style={styles.message}>{$t('wallet.tipsLowCredits')}</Text>
         <View style={$padding(12,0)}>
           <Button
-            text="Top-Up"
+            text={$t('wallet.btnTop_Up')}
             onClick={() => onClose(true)}/>
         </View>
         <Ionicons
@@ -116,8 +116,8 @@ export const CancelReserveDialog = ({visible, onClose}) => {
       onBackdropPress={onClose}
       {...ModalProps}>
       <View style={styles.dialog}>
-        <Text style={styles.title}>Cancel</Text>
-        <Text style={styles.message}>Are you sure you want to cancel the reservation?</Text>
+        <Text style={styles.title}>{$t('charging.cancleReservation')}</Text>
+        <Text style={styles.message}>{$t('charging.comfirmCancleReservation')}</Text>
         <View style={styles.loginTipView}>
           <Text style={styles.tipText}></Text>
           <Image
@@ -128,12 +128,12 @@ export const CancelReserveDialog = ({visible, onClose}) => {
           <Button
             textSize={17}
             style={ui.flex1}
-            text='Confirm'
+            text={$t('common.confirm')}
             onClick={() => onClose(true)}/>
           <Button
             textSize={17}
             style={styles.cancelCloseBtn}
-            text='Close'
+            text={$t('common.close')}
             onClick={() => onClose(false)}/>
         </View>
         <Pressable
@@ -154,25 +154,29 @@ export const RegisterDialog = ({address, onClose}) => {
   return (
     <View style={styles.dialog}>
       <View style={ui.center}>
-        <Text style={styles.regTitleText}>Thank you for registering with</Text>
+        <Text style={styles.regTitleText}>{$t('sign.thanksRegisterWith')}</Text>
         <Image
           source={require('../../images/app-logo.png')}
           style={Styles.logo}
+          resizeMode='contain'
         />
-        {/* <Text style={styles.regMessageText}>a confirmation email has been sent to</Text>
-        <Text style={styles.regMessageLink}>{address}</Text> */}
+        <Text style={styles.regMessageText}>{$t('sign.aConfirmationEmailTo')}</Text>
+        <Text style={styles.regMessageLink}>{address}</Text>
       </View>
       <View style={$padding(12,0)}>
         <Button
-          text="Back to Login"
+          text={$t('sign.back2Login')}
           onClick={onClose}/>
       </View>
-      <Ionicons
-        name='close-outline'
-        size={30}
-        color={'#999'}
+      <Pressable
         style={styles.closeIcon}
-        onPress={onClose} />
+        android_ripple={rippleLess}
+        onPress={onClose}>
+        <Ionicons
+          name='close-outline'
+          size={30}
+          color={'#999'}/>
+      </Pressable>
     </View>
   );
 }
@@ -222,7 +226,7 @@ const styles = StyleSheet.create({
     marginLeft: 'auto',
     marginRight: 'auto',
     borderRadius: isIOS ? 20 : 3,
-    backgroundColor: 'white'
+    backgroundColor: colorLight
   },
   title: {
     color: '#000',
@@ -231,7 +235,7 @@ const styles = StyleSheet.create({
   },
   message: {
     zIndex: 2,
-    color: '#333',
+    color: textPrimary,
     fontSize: 14,
     lineHeight: 22,
     paddingTop: 16,
@@ -269,14 +273,14 @@ const styles = StyleSheet.create({
     justifyContent: 'center'
   },
   signUpView: {
-    color: '#333',
+    color: textPrimary,
     paddingTop: 16,
     flexDirection: "row",
     alignItems: "center",
     justifyContent: "center"
   },
   signUpText: {
-    color: '#333',
+    color: textPrimary,
     fontSize: 12
   },
   signUpLink: {
@@ -298,7 +302,7 @@ const styles = StyleSheet.create({
   },
   regTitleText: {
     color: '#000',
-    fontSize: 16,
+    fontSize: 20,
     ...$padding(32, 0, 16),
     textAlign: 'center'
   },

+ 5 - 5
Strides-APP/app/pages/charge/Rating.js

@@ -31,7 +31,7 @@ export default class Rating extends React.Component {
   }
 
   submit() {
-    console.log(this.state);
+    //console.log(this.state);
     if (this.state.locationOfStation == 0) {
       toastShort('Please rate \'Location of Station\'');
       return;
@@ -52,7 +52,7 @@ export default class Rating extends React.Component {
     apiUser.rateCharge(this.state).then(res => {
       Dialog.dismissLoading();
       startPage(PageList.home)
-    }).catch(err => {
+    }).catch((err) => {
       Dialog.dismissLoading();
       toastShort(err);
     });
@@ -161,7 +161,7 @@ const styles = StyleSheet.create({
     padding: 16,
     alignItems: 'center',
     flexDirection: 'row',
-    backgroundColor: '#fff'
+    backgroundColor: colorLight
   },
   stationIcon: {
     width: 40,
@@ -182,7 +182,7 @@ const styles = StyleSheet.create({
   ratingView: {
     padding: 16,
     marginTop: 16,
-    backgroundColor: 'white'
+    backgroundColor: colorLight
   },
   ratingTitle: {
     color: '#000',
@@ -198,7 +198,7 @@ const styles = StyleSheet.create({
     marginRight: 10
   },
   otherText: {
-    color: '#333',
+    color: textPrimary,
     minHeight: 120,
     marginTop: 16,
     borderWidth: 1,

+ 9 - 9
Strides-APP/app/pages/charge/Reserve.js

@@ -11,7 +11,7 @@ import apiCharge from '../../api/apiCharge';
 import Dialog from '../../components/Dialog';
 import { PageList } from '../Router';
 import { CancelReserveDialog } from './InfoDialog';
-import TextRadius from '../../components/TextRadius';
+import TextView from '../../components/TextView';
 
 export default class Reserve extends Component {
 
@@ -120,7 +120,7 @@ export default class Reserve extends Component {
       } else {
         this.stopCountdown();
       }
-    }).catch(err => {
+    }).catch((err) => {
       this.stopCountdown();
     });
   }
@@ -135,7 +135,7 @@ export default class Reserve extends Component {
       toastShort('Reserved successfully!');
       this.props.onRefresh();
       this.getReserve();
-    }).catch(err => {
+    }).catch((err) => {
       Dialog.dismissLoading();
       toastShort(err)
     });
@@ -147,9 +147,9 @@ export default class Reserve extends Component {
       apiCharge.cancelReserve(this.state.userReserve.reservePk).then(res => {
         Dialog.dismissLoading();
         this.props.onRefresh();
-        toastShort('Cancel successfully!');
+        toastShort($t('common.cancelSuccess'));
         this.getReserve();
-      }).catch(err => {
+      }).catch((err) => {
         Dialog.dismissLoading();
         toastShort(err)
       });
@@ -262,7 +262,7 @@ export default class Reserve extends Component {
               : this.reserveView()
             )
           : <View style={[{height: $vh(50)}, ui.flexvc]}>
-              <Text style={{color: '#333', fontSize: 14}}>Reservation is not available for this site.</Text>
+              <Text style={{color: textPrimary, fontSize: 14}}>Reservation is not available for this site.</Text>
             </View>
         }
         <CancelReserveDialog
@@ -328,8 +328,8 @@ export default class Reserve extends Component {
                 { index == this.state.checkIndex
                   ? <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusSelected]}>Selected</Text>
                   : (item.available
-                    ? <TextRadius style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable]}>Available</TextRadius>
-                    : <TextRadius style={[ChargeStyle.infoStatus, ChargeStyle.statusUnavailable]}>Unavailable</TextRadius>
+                    ? <TextView style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable]}>Available</TextView>
+                    : <TextView style={[ChargeStyle.infoStatus, ChargeStyle.statusUnavailable]}>Unavailable</TextView>
                   )
                 }
               </Pressable>
@@ -413,7 +413,7 @@ export default class Reserve extends Component {
         <View style={styles.cancelView}>
           <Button
             text='Cancel Reservation'
-            textColor='#fff'
+            textColor={textButton}
             style={styles.cancelButton}
             viewStyle={styles.cancelButtonView}
             onClick={() => this.cancelReserve()}

+ 15 - 12
Strides-APP/app/pages/chargeV2/ChargeAdapter.js

@@ -16,6 +16,7 @@ import utils from '../../utils/utils';
 import { Styles } from '../../components/Toolbar';
 import { getFocusedRouteNameFromRoute } from '@react-navigation/core';
 import app from '../../../app.json';
+import HeaderTitle from '../../components/HeaderTitle';
 
 export const PagerList = {
   "tabInfo": "Info",
@@ -52,12 +53,12 @@ export default class ChargeAdapter extends Component {
       component: Explore
     }*/]
     this.tabBarStyle = {
-      style: styles.tabStyle,
-      pressColor: rippleColor,
-      scrollEnabled: this.pageAdapter.length > 3,
-      indicatorStyle: styles.indicator,
-      activeTintColor: app.isWhitelabel ? textPrimary : colorLight,
-      inactiveTintColor: app.isWhitelabel ? textSecondary : "#E0E0E0"
+      tabBarStyle: styles.tabStyle,
+      tabBarPressColor: rippleColor,
+      tabBarScrollEnabled: this.pageAdapter.length > 3,
+      tabBarIndicatorStyle: styles.indicator,
+      tabBarActiveTintColor: app.isWhitelabel ? textPrimary : colorLight,
+      tabBarInactiveTintColor: app.isWhitelabel ? textSecondary : "#E0E0E0"
     }
     this.action = "";
     this.isHide = false;
@@ -193,7 +194,7 @@ export default class ChargeAdapter extends Component {
     if (!this.titleName) {
       this.titleName = this.state.stationInfo.name;
       this.props.navigation.setOptions({
-        headerTitle: this.titleName,
+        headerTitle: () => (<HeaderTitle title={this.titleName}/>),
         headerRight: () => (
           <Pressable
             style={Styles.backIcon}
@@ -259,17 +260,19 @@ export default class ChargeAdapter extends Component {
         }>
         <Tab.Navigator
           style={{minHeight: $vht(100)}}
-          tabBarOptions={this.tabBarStyle}
-          lazy={false}
-          initialRouteName={PagerList.tabCharge}
-          lazyPreloadDistance={1}>
+          screenOptions={{
+            lazy: false,
+            lazyPreloadDistance: 1,
+            ...this.tabBarStyle
+          }}>
           { this.pageAdapter.map((item, index) => 
             <Tab.Screen
               key={index}
               name={item.name}
               component={item.component}
               options={{
-                title: item.title
+                title: item.title,
+                tabBarAllowFontScaling: false
               }}
             />
           )}

+ 32 - 29
Strides-APP/app/pages/chargeV2/Charging.js

@@ -13,6 +13,7 @@ import { DialogMaxWidth } from './InfoDialog';
 import { QRResult } from '../charge/QRScan';
 import Svg, { Defs, LinearGradient, Rect, Stop } from 'react-native-svg';
 import utils from '../../utils/utils';
+import TextView from '../../components/TextView';
 
 export const circleSize = $vw(50.66) < 300 ? $vw(50.66) : 300;
 
@@ -186,10 +187,10 @@ export const BatteryView = ({soc, isPending, isCharging}) => {
                 </View>
               </View>
               { isPending
-                ? <Text style={styles.batterySoc}>Initiating...</Text>
+                ? <TextView style={styles.batterySoc}>{$t('charging.statusInitiating')}</TextView>
                 : powerPercent != -1
-                  ? <Text style={styles.batteryPercent}>{powerPercent}%</Text>
-                  : <Text style={styles.batterySoc}>{'In Charging'}</Text>
+                  ? <TextView style={styles.batteryPercent}>{powerPercent}%</TextView>
+                  : <TextView style={styles.batterySoc}>{$t('charging.statusInCharging')}</TextView>
               }
             </View>
             <View style={styles.plusRightView}>
@@ -205,7 +206,7 @@ export const BatteryView = ({soc, isPending, isCharging}) => {
         <Image
           style={styles.disconnectIcon}
           source={require('../../images/charge/charge-complete.png')}/>
-        <Text style={styles.completeTip}>Please disconnect and return connector to charging station</Text>
+        <TextView style={styles.completeTip}>{$t('charging.tipsDisconnectConnector')}</TextView>
       </View>
   );
 }
@@ -225,28 +226,29 @@ export const DashboardView = ({isCharging=false, connectorInfo={}}) => {
             </Defs>
           </Svg>
         </View>
-        <Text style={styles.dashboardTitleWhite}>Charging In Progress...</Text>
+        <TextView style={styles.dashboardTitleWhite}>{$t('charging.chargingInProgress')}</TextView>
         <View style={styles.dashboardItemGroup}>
           <View style={styles.dashboardItemView}>
-            <Text style={styles.dashboardItemValue}>{utils.minutes2HHmm(connectorInfo?.timeElapsed ?? 0)}</Text>
-            <Text
+            <TextView style={styles.dashboardItemValue}>{utils.minutes2HHmm(connectorInfo?.timeElapsed ?? 0)}</TextView>
+            <TextView
               numberOfLines={1}
               ellipsizeMode="tail"
-              style={styles.dashboardItemTitle}>Time Elapsed</Text>
+              style={styles.dashboardItemTitle}>{$t('charging.labelTimeElapsed')}</TextView>
           </View>
           <View style={styles.dashboardItemView}>
-            <Text style={styles.dashboardItemValue}>{utils.toFixed(connectorInfo?.totalKWhDelivered, 2) ?? "0"} kWh</Text>
-            <Text
+            <TextView style={styles.dashboardItemValue}>{utils.toFixed(connectorInfo?.totalKWhDelivered, 2) ?? "0"} kWh</TextView>
+            <TextView
               numberOfLines={1}
               ellipsizeMode="tail"
-              style={styles.dashboardItemTitle}>Total kWh Delivered</Text>
+              style={styles.dashboardItemTitle}>{$t('charging.labelTotalkWh')}</TextView>
           </View>
           <View style={styles.dashboardItemView}>
-          <Text style={styles.dashboardItemValueWeight}>{currency} {utils.toFixed(connectorInfo?.totalCharges, 2) ?? "0.0"}</Text>
-            <Text
+            {/* <Text style={styles.dashboardItemValueWeight}>{currency} {utils.toFixed(connectorInfo?.totalCharges, 2) ?? "0.0"}</Text> */}
+            <TextView style={styles.dashboardItemValueWeight}>{connectorInfo?.totalCharges ?? "$0.0"}</TextView>
+            <TextView
               numberOfLines={1}
               ellipsizeMode="tail"
-              style={styles.dashboardItemTitle}>Total Charges</Text>
+              style={styles.dashboardItemTitle}>{$t('charging.labelTotalCharges')}</TextView>
           </View>
         </View>
       </View>
@@ -254,28 +256,28 @@ export const DashboardView = ({isCharging=false, connectorInfo={}}) => {
   } else {
     return (
       <View style={styles.dashboardGreyView}>
-        <Text style={styles.dashboardTitle}>Press Start To Begin</Text>
+        <TextView style={styles.dashboardTitle}>{$t('charging.pressStartToBegin')}</TextView>
         <View style={styles.dashboardItemGroup}>
           <View style={styles.dashboardItemView}>
-            <Text style={styles.dashboardItemValue}>-</Text>
-            <Text
+            <TextView style={styles.dashboardItemValue}>-</TextView>
+            <TextView
               numberOfLines={1}
               ellipsizeMode="tail"
-              style={styles.dashboardItemTitle}>Time Elapsed</Text>
+              style={styles.dashboardItemTitle}>{$t('charging.labelTimeElapsed')}</TextView>
           </View>
           <View style={styles.dashboardItemView}>
-            <Text style={styles.dashboardItemValue}>-</Text>
-            <Text
+            <TextView style={styles.dashboardItemValue}>-</TextView>
+            <TextView
               numberOfLines={1}
               ellipsizeMode="tail"
-              style={styles.dashboardItemTitle}>Total kWh Delivered</Text>
+              style={styles.dashboardItemTitle}>{$t('charging.labelTotalkWh')}</TextView>
           </View>
           <View style={styles.dashboardItemView}>
-          <Text style={styles.dashboardItemValueWeight}>-</Text>
-            <Text
+            <TextView style={styles.dashboardItemValueWeight}>-</TextView>
+            <TextView
               numberOfLines={1}
               ellipsizeMode="tail"
-              style={styles.dashboardItemTitle}>Total Charges</Text>
+              style={styles.dashboardItemTitle}>{$t('charging.labelTotalCharges')}</TextView>
           </View>
         </View>
       </View>
@@ -305,7 +307,7 @@ export const EnterStationDialog = ({visible, stationId, onConfirm, onClose}) =>
         });
       }
     } else {
-      toastShort('Please input Station ID')
+      toastShort($t('charging.plsInputStationId'));
     }
   }
 
@@ -326,11 +328,12 @@ export const EnterStationDialog = ({visible, stationId, onConfirm, onClose}) =>
         onBackdropPress={() => onClose}
         onBackButtonPress={() => onClose}>
         <View style={styles.stationDialog}>
-          <Text style={styles.stationInputTitle}>Enter Station ID</Text>
+          <TextView style={styles.stationInputTitle}>{$t('charging.enterStationId')}</TextView>
           <TextInput
             style={styles.stationInput}
+            allowFontScaling={false}
             defaultValue={inputStationId}
-            placeholder='e.g: LEMOC0002-1'
+            placeholder={$t('charging.hintExampleStationId')}
             placeholderTextColor={textPlacehoder}
             onChangeText={text => setInput(text)}
           />
@@ -339,14 +342,14 @@ export const EnterStationDialog = ({visible, stationId, onConfirm, onClose}) =>
               textSize={17}
               style={styles.buttonCancel}
               viewStyle={ViewHeight(42)}
-              text='Close'
+              text={$t('common.close')}
               textColor={textCancel}
               onClick={onClose}/>
             <Button
               textSize={17}
               style={styles.buttonOK}
               viewStyle={ViewHeight(42)}
-              text='Confirm'
+              text={$t('common.confirm')}
               onClick={() => enterStatioinId()}/>
           </View>
         </View>

+ 27 - 23
Strides-APP/app/pages/chargeV2/InfoDialog.js

@@ -10,16 +10,17 @@ import Button from "../../components/Button";
 import { Styles } from "../../components/Toolbar";
 import { PageList } from "../Router";
 import app from "../../../app.json"
+import TextView from "../../components/TextView";
 
 export const DialogMaxWidth = $vw(85) > 500 ? 500 : $vw(85);
 
 export const LoginDialog = ({onHide, onClose}) => {
   return (
     <View style={styles.dialog}>
-      <Text style={styles.title}>Oops!</Text>
-      <Text style={styles.message}>You need to be registered user of {app.displayName}</Text>
+      <TextView style={styles.title}>{$t('charging.titleOops')}</TextView>
+      <TextView style={styles.message}>You need to be registered user of {app.displayName}</TextView>
       <View style={styles.loginTipView}>
-        <Text style={styles.tipText}>Login to use this feature</Text>
+        <TextView style={styles.tipText}>Login to use this feature</TextView>
         <Image
           style={styles.tipImage}
           source={require('../../images/login-head.png')}/>
@@ -28,7 +29,7 @@ export const LoginDialog = ({onHide, onClose}) => {
         textSize={17}
         style={styles.loginButton}
         viewStyle={styles.loginButtonView}
-        text='Login'
+        text={$t('sign.btnLogin')}
         onClick={() => {
           onHide();
           startPage(PageList.login)
@@ -39,8 +40,8 @@ export const LoginDialog = ({onHide, onClose}) => {
           onHide();
           startPage(PageList.register, {actionLogin: true});
         }}>
-        <Text style={styles.signUpText}>Don't have an account?</Text>
-        <Text style={styles.signUpLink}>Sign Up</Text>
+        <TextView style={styles.signUpText}>Don't have an account?</TextView>
+        <TextView style={styles.signUpLink}>{$t('sign.btnSignUp')}</TextView>
       </Pressable>
       <Ionicons
         name='close-outline'
@@ -60,16 +61,16 @@ export const ErrorDialog = ({visible, code, message, onClose}) => {
       onBackdropPress={onClose}
       {...ModalProps}>
       <View style={styles.dialog}>
-        <Text style={styles.title}>Oops!</Text>
-        <Text style={styles.message}>{message}</Text>
+        <TextView style={styles.title}>{$t('charging.titleOops')}</TextView>
+        <TextView style={styles.message}>{message}</TextView>
         <ErrorImage code={code}/>
         <Button
           textSize={17}
           style={styles.loginButton}
           viewStyle={styles.loginButtonView}
-          text='Okay'
+          text={$t('charging.btnOkay')}
           onClick={onClose}/>
-        <Text style={{fontSize: 12}}></Text>
+        <TextView style={{fontSize: 12}}></TextView>
         <Ionicons
           name='close-outline'
           size={30}
@@ -89,12 +90,12 @@ export const LowCreditDialog = ({visible, onClose}) => {
       onBackdropPress={onClose}
       {...ModalProps}>
       <View style={styles.dialog}>
-        <Text style={styles.title}>Low Credits</Text>
-        <Text style={styles.message}>Your credit is below S$5.</Text>
-        <Text style={styles.message}>{"This charging session will end if there is insufficient credits.\n"}</Text>
+        <TextView style={styles.title}>{$t('wallet.titleLowCredits')}</TextView>
+        <TextView style={styles.message}>{$t('wallet.creditIsBelow5')}</TextView>
+        <TextView style={styles.message}>{$t('wallet.tipsLowCredits')}</TextView>
         <View style={$padding(12,0)}>
           <Button
-            text="Top-Up"
+            text={$t('wallet.btnTop_Up')}
             onClick={() => onClose(true)}/>
         </View>
         <Ionicons
@@ -116,10 +117,10 @@ export const CancelReserveDialog = ({visible, onClose}) => {
       onBackdropPress={onClose}
       {...ModalProps}>
       <View style={styles.dialog}>
-        <Text style={styles.title}>Cancel</Text>
-        <Text style={styles.message}>Are you sure you want to cancel the reservation?</Text>
+        <TextView style={styles.title}>{$t('charging.cancleReservation')}</TextView>
+        <TextView style={styles.message}>{$t('charging.comfirmCancleReservation')}</TextView>
         <View style={styles.loginTipView}>
-          <Text style={styles.tipText}></Text>
+          <TextView style={styles.tipText}></TextView>
           <Image
             style={styles.tipImage}
             source={require('../../images/login-head.png')}/>
@@ -128,12 +129,12 @@ export const CancelReserveDialog = ({visible, onClose}) => {
           <Button
             textSize={17}
             style={ui.flex1}
-            text='Confirm'
+            text={$t('common.confirm')}
             onClick={() => onClose(true)}/>
           <Button
             textSize={17}
             style={styles.cancelCloseBtn}
-            text='Close'
+            text={$t('common.close')}
             onClick={() => onClose(false)}/>
         </View>
         <Pressable
@@ -154,18 +155,18 @@ export const RegisterDialog = ({address, onClose}) => {
   return (
     <View style={styles.dialog}>
       <View style={ui.center}>
-        <Text style={styles.regTitleText}>Thank you for registering with</Text>
+        <TextView style={styles.regTitleText}>{$t('sign.thanksRegisterWith')}</TextView>
         <Image
           source={require('../../images/app-logo.png')}
           style={Styles.logo}
           resizeMode='contain'
         />
-        <Text style={styles.regMessageText}>a confirmation email has been sent to</Text>
-        <Text style={styles.regMessageLink}>{address}</Text>
+        <TextView style={styles.regMessageText}>{$t('sign.aConfirmationEmailTo')}</TextView>
+        <TextView style={styles.regMessageLink}>{address}</TextView>
       </View>
       <View style={$padding(12,0)}>
         <Button
-          text="Back to Login"
+          text={$t('sign.back2Login')}
           onClick={onClose}/>
       </View>
       <Pressable
@@ -209,6 +210,8 @@ const ErrorImage = ({code}) => {
           resizeMode="contain"
           source={require('../../images/site/error-A9.jpg')}/>
       );
+    case 'none':
+      return <EndView></EndView>;
     default:
       return (
         <Image
@@ -273,6 +276,7 @@ const styles = StyleSheet.create({
     flex: 1,
     height: 54,
     alignItems: 'center',
+    flexDirection: 'row',
     justifyContent: 'center'
   },
   signUpView: {

+ 44 - 46
Strides-APP/app/pages/chargeV2/Payment.js

@@ -1,56 +1,52 @@
-import React, { Component } from 'react';
+import React, { Component, useEffect, useState } from 'react';
 import { View, Text, StyleSheet } from 'react-native';
 import BadgeSelectItem from '../../components/BadgeSelectItem';
-import { PAYTYPE } from '../wallet/Payment';
-import { PaymentIcon } from '../wallet/TopupPaythod';
+import TextView from '../../components/TextView';
+import { getPaymenOptions, PaymentIcon, PAYTYPE } from '../payment/PaymentConfig';
 import { ChargeStyle } from './Charging';
 
-export const CHARGE_PAYTYPE = [/*{
-  // 按次支付
-  name: "Pay Per Use",
-  type: PAYTYPE.PAY_PER_USE,
-  title: "SGQR",
-  icon: "PAYNOW"
-}, */{
-  // 钱包余额支付
-  name: "Credits",
-  type: PAYTYPE.CREDIT_WALLET,
-  icon: "WALLET",
-  balance: true
-}]
-
-export const PaymentList = ({isSelect=true, payType, payPerUse, onMethodChange}) => (
-  CHARGE_PAYTYPE.map((item, index) => {
-    if (isSelect || payType==item.type) {
-      return (
-        <BadgeSelectItem
-          key={index}
-          style={ChargeStyle.stationInfoView}
-          checked={payType==item.type}
-          onPress={() => {
-            if (onMethodChange) {
-              onMethodChange(item.type);
-            }
-          }}>
-          <PaymentIcon 
-            method={item.icon}
-            checked={payType==item.type}/>
-          <View style={styles.paymentView}>
-            <Text style={styles.valueText}>{item.balance ? (currency + userInfo.credit) : item.title}</Text>
-            <Text style={styles.paymentText}>{item.name}</Text>
-          </View>
-        </BadgeSelectItem>
-      )
-    } else {
-      return null
-    }
-  })
-)
+export const PaymentList = ({isSelect=true, payType, payPerUse, onMethodChange}) => {
+  const [options, setOptions] = useState([])
+  useEffect(() => {
+    getPaymenOptions(options => {
+      setOptions(options)
+    })
+  }, [])
+  return (
+    options.map((item, index) => {
+      if (isSelect || payType==item.value) {
+        return (
+          <BadgeSelectItem
+            key={index}
+            style={ChargeStyle.stationInfoView}
+            checked={payType==item.value}
+            onPress={() => {
+              if (onMethodChange) {
+                onMethodChange(item.value);
+              }
+            }}>
+            <PaymentIcon
+              method={item.iconFont}
+              checked={payType==item.value}/>
+            <View style={styles.paymentView}>
+              <TextView style={styles.valueText}>{item.value == PAYTYPE.CREDIT_WALLET ? ("" + userInfo?.creditStr) : item.desc}</TextView>
+              <TextView style={styles.paymentText}>{item.title}</TextView>
+            </View>
+          </BadgeSelectItem>
+        )
+      } else {
+        return null
+      }
+    })
+  )
+}
 
 const styles = StyleSheet.create({
   paymentView: {
     flex: 1,
-    alignItems: 'center'
+    alignItems: 'center',
+    flexDirection: 'row-reverse',
+    justifyContent: 'flex-end'
   },
   valueText: {
     color: textPrimary,
@@ -59,6 +55,8 @@ const styles = StyleSheet.create({
   },
   paymentText: {
     color: textSecondary,
-    fontSize: 12
+    fontSize: 12,
+    paddingLeft: 8,
+    paddingRight: 16
   }
 })

+ 47 - 44
Strides-APP/app/pages/chargeV2/Summary.js

@@ -7,7 +7,9 @@ import { View, Text, StyleSheet, Image, ScrollView, RefreshControl } from 'react
 import apiCharge from '../../api/apiCharge';
 import Button from '../../components/Button';
 import Dialog from '../../components/Dialog';
+import TextView from '../../components/TextView';
 import { MyRefreshProps } from '../../components/ThemesConfig';
+import routeUtil from '../../utils/routeUtil';
 import utils from '../../utils/utils';
 import { PageList } from '../Router';
 import { ChargeStyle, TypeImage } from './Charging';
@@ -91,7 +93,8 @@ export default class Summary extends Component {
   toRating() {
     this.canBack = true;
     //goBack();
-    startPage(PageList.home)
+    //routeUtil.resetToHome(this.props);
+    startPage(PageList.home);
     //startPage(PageList.rating, this.state.stationInfo);
   }
 
@@ -117,106 +120,106 @@ export default class Summary extends Component {
         <View style={{height:16}}></View>
         <View style={styles.sections}>
           <View style={styles.formRow}>
-            <Text style={styles.label}>{$t('wallet.labelTransactionId')}</Text>
-            <Text style={styles.text}>{this.state.summaryInfo.transactionPk}</Text>
+            <TextView style={styles.label}>{$t('wallet.labelTransactionId')}</TextView>
+            <TextView style={styles.text}>{this.state.summaryInfo.transactionPk}</TextView>
           </View>
           <View style={styles.formRow}>
-            <Text style={styles.label}>{$t('wallet.labelReferenceId')}</Text>
-            <Text style={styles.text}>{this.state.summaryInfo.chargingPk}</Text>
+            <TextView style={styles.label}>{$t('wallet.labelReferenceId')}</TextView>
+            <TextView style={styles.text}>{this.state.summaryInfo.chargingPk}</TextView>
           </View>
           <View style={styles.formRow}>
-            <Text style={styles.label}>{$t('wallet.labelDateTime')}</Text>
-            <Text style={styles.text}>{this.state.summaryInfo.dateTime}</Text>
+            <TextView style={styles.label}>{$t('wallet.labelDateTime')}</TextView>
+            <TextView style={styles.text}>{this.state.summaryInfo.dateTime}</TextView>
           </View>
         </View>
         <View style={styles.sections}>
-          <Text style={styles.formTitle}>{$t('wallet.labelYourStation')}</Text>
+          <TextView style={styles.formTitle}>{$t('wallet.labelYourStation')}</TextView>
           <View style={styles.formRow}>
-            <Text style={styles.label}>{$t('wallet.labelStationId') + this.state.summaryInfo.chargeBoxPk}</Text>
+            <TextView style={styles.label}>{$t('wallet.labelStationId') + this.state.summaryInfo.chargeBoxPk}</TextView>
           </View>
-          <Text style={styles.stationInfoText}>{this.state.summaryInfo?.boxAddress}</Text>
+          <TextView style={styles.stationInfoText}>{this.state.summaryInfo?.boxAddress}</TextView>
         </View> 
 
         <View style={styles.sections2}>
-          <Text style={styles.formTitle}>{$t('wallet.labelYourConnector')}</Text>
+          <TextView style={styles.formTitle}>{$t('wallet.labelYourConnector')}</TextView>
           <View style={styles.stationInfoView}>
             <Image
               style={ChargeStyle.infoIcon}
               source={this.state.summaryInfo.connectorType?.indexOf('AC') >= 0 ? TypeImage.AC : TypeImage.DC}/>
             <View style={ChargeStyle.infoGroup}>
-              <Text style={ChargeStyle.infoText}>{this.state.summaryInfo.connectorType}</Text>
-              <Text style={ChargeStyle.infoTitle}>{$t('charging.labelType')}</Text>
+              <TextView style={ChargeStyle.infoText}>{this.state.summaryInfo.connectorType}</TextView>
+              <TextView style={ChargeStyle.infoTitle}>{$t('charging.labelType')}</TextView>
             </View>
             <View style={ChargeStyle.infoGroup}>
-              <Text style={ChargeStyle.infoText}>{this.state.summaryInfo.connectorWattage}</Text>
-              <Text style={ChargeStyle.infoTitle}>{$t('charging.labelPower')}</Text>
+              <TextView style={ChargeStyle.infoText}>{this.state.summaryInfo.connectorWattage}</TextView>
+              <TeTextViewxt style={ChargeStyle.infoTitle}>{$t('charging.labelPower')}</TeTextViewxt>
             </View>
             <View style={ChargeStyle.infoGroup}>
-              <Text style={ChargeStyle.infoText}>{this.state.summaryInfo.connectorRate}</Text>
-              <Text style={ChargeStyle.infoTitle}>{$t('charging.labelRate')}</Text>
+              <TextView style={ChargeStyle.infoText}>{this.state.summaryInfo.connectorRate}</TextView>
+              <TextView style={ChargeStyle.infoTitle}>{$t('charging.labelRate')}</TextView>
             </View>
           </View>
         </View> 
 
         <View style={styles.sections}>
-          <Text style={styles.formTitle}>{$t('wallet.labelBreakdown')}</Text>
+          <TextView style={styles.formTitle}>{$t('wallet.labelBreakdown')}</TextView>
           <View style={styles.formRow}>
-            <Text style={styles.label}>{$t('wallet.labelReservationFee')}</Text>
-            <Text style={styles.text}>{currency}{this.state.summaryInfo.reservationFee ?? 0}</Text>
+            <TextView style={styles.label}>{$t('wallet.labelReservationFee')}</TextView>
+            <TextView style={styles.text}>{currency}{this.state.summaryInfo.reservationFee ?? 0}</Text>
           </View>
           { utils.isNotEmpty(this.state.summaryInfo.idleFee) &&
             <View style={styles.formRow}>
-              <Text style={styles.label}>{$t('wallet.labelIdleFee')}</Text>
-              <Text style={styles.text}>{currency}{this.state.summaryInfo.idleFee}</Text>
+              <TextView style={styles.label}>{$t('wallet.labelIdleFee')}</TextView>
+              <TextView style={styles.text}>{currency}{this.state.summaryInfo.idleFee}</Text>
             </View>
           }
           <View style={styles.formRow}>
-            <Text style={styles.label}>{$t('wallet.labelChargeTime')}</Text>
-            <Text style={styles.text}>{utils.hour2HHmm(this.state.summaryInfo.chargeTime)}</Text>
+            <TextView style={styles.label}>{$t('wallet.labelChargeTime')}</TextView>
+            <TextView style={styles.text}>{utils.hour2HHmm(this.state.summaryInfo.chargeTime)}</TextView>
           </View>
           <View style={styles.formRow}>
-            <Text style={styles.label}>{$t('wallet.labelChargeDelivered')}</Text>
-            <Text style={styles.text}>{this.state.summaryInfo.chargeDelivered ?? 0}kWh</Text>
+            <TextView style={styles.label}>{$t('wallet.labelChargeDelivered')}</TextView>
+            <TextView style={styles.text}>{this.state.summaryInfo.chargeDelivered ?? 0}kWh</TextView>
           </View>
           <View style={styles.formRow}>
-            <Text style={styles.label}>{$t('wallet.labelChargeRates')}</Text>
-            <Text style={styles.text}>{currency}{this.state.summaryInfo.chargeRates ?? '0.0'}</Text>
+            <TextView style={styles.label}>{$t('wallet.labelChargeRates')}</TextView>
+            <TextView style={styles.text}>{currency}{this.state.summaryInfo.chargeRates ?? '0.0'}</Text>
           </View>
         </View>
 
         <View style={styles.sections}>
-          <Text style={styles.formTitle}>{$t('wallet.labelSubtotal')}</Text>
+          <TextView style={styles.formTitle}>{$t('wallet.labelSubtotal')}</TextView>
           <View style={styles.formRow}>
-            <Text style={styles.label}>{$t('wallet.labelPaymentMadeBy')}</Text>
-            <Text style={styles.text}>{this.state.summaryInfo.paymentType}</Text>
+            <TextView style={styles.label}>{$t('wallet.labelPaymentMadeBy')}</TextView>
+            <TextView style={styles.text}>{this.state.summaryInfo.paymentType}</TextView>
           </View>
           <View style={styles.formRow}>
-            <Text style={styles.label}>{$t('wallet.labelPreviousBalance')}</Text>
-            <Text style={styles.text}>{this.getSummaryText(this.state.summaryInfo.previousBalance)}</Text>
+            <TextView style={styles.label}>{$t('wallet.labelPreviousBalance')}</TextView>
+            <TextView style={styles.text}>{this.getSummaryText(this.state.summaryInfo.previousBalance)}</TextView>
           </View>
           <View style={styles.formRow}>
-            <Text style={styles.label}>{$t('wallet.labelPayment')}</Text>
-            <Text style={styles.text}>{currency}{this.state.summaryInfo.payment ?? '0.0'}</Text>
+            <TextView style={styles.label}>{$t('wallet.labelPayment')}</TextView>
+            <TextView style={styles.text}>{currency}{this.state.summaryInfo.payment ?? '0.0'}</Text>
           </View>
           <View style={styles.formRow}>
-            <Text style={styles.label}>{$t('wallet.labelExchangeRate')}</Text>
-            <Text style={styles.text}>{this.getSummaryText(this.state.summaryInfo.exchangeRate)}</Text>
+            <TextView style={styles.label}>{$t('wallet.labelExchangeRate')}</TextView>
+            <TextView style={styles.text}>{this.getSummaryText(this.state.summaryInfo.exchangeRate)}</TextView>
           </View>
           <View style={styles.formRow}>
-            <Text style={styles.label}>{$t('wallet.labelFinalPayment')}</Text>
-            <Text style={styles.text}>{this.getSummaryText(this.state.summaryInfo.finalPayment)}</Text>
+            <TextView style={styles.label}>{$t('wallet.labelFinalPayment')}</TextView>
+            <TextView style={styles.text}>{this.getSummaryText(this.state.summaryInfo.finalPayment)}</TextView>
           </View>
           <View style={styles.formRow}>
-            <Text style={styles.label}>{$t('wallet.labelResultingBalance')}</Text>
-            <Text style={styles.text}>{this.getSummaryText(this.state.summaryInfo.resultingBalance)}</Text>
+            <TextView style={styles.label}>{$t('wallet.labelResultingBalance')}</TextView>
+            <TextView style={styles.text}>{this.getSummaryText(this.state.summaryInfo.resultingBalance)}</TextView>
           </View>
         </View>
         { this.state.isActully &&
           <View style={styles.bottomButton}>
-            <Text
+            <TextView
               style={styles.feedback}
-              onPress={() => startPage(PageList.feedback)}>{$t('wallet.linkSubmitFeedback')}</Text>
-            <Text style={styles.tipText}>{$t('wallet.tipsReceipt')}</Text>
+              onPress={() => startPage(PageList.feedback)}>{$t('wallet.linkSubmitFeedback')}</TextView>
+            <TextView style={styles.tipText}>{$t('wallet.tipsReceipt')}</TextView>
             <Button
               text={$t('home.done')}
               elevation={1.5}

+ 64 - 63
Strides-APP/app/pages/chargeV2/SummaryV2.js

@@ -11,6 +11,7 @@ import { MyRefreshProps } from '../../components/ThemesConfig';
 import utils from '../../utils/utils';
 import { PageList } from '../Router';
 import app from '../../../app.json';
+import TextView from '../../components/TextView';
 
 export default class Summary extends Component {
   constructor(props) {
@@ -132,32 +133,32 @@ export default class Summary extends Component {
                 name="check-circle-fill"
                 color={colorAccent}
                 size={56}/>
-              <Text style={styles.topTitle}>{$t('receipt.successful')}</Text>
-              <Text style={styles.topDesc}>{$t('receipt.chargingSessionComplete')}</Text>
+              <TextView style={styles.topTitle}>{$t('receipt.successful')}</TextView>
+              <TextView style={styles.topDesc}>{$t('receipt.chargingSessionComplete')}</TextView>
             </>}
             { utils.isNotEmpty(this.state.summaryInfo.top.company) &&
               <View style={styles.formRow}>
-                <Text style={styles.label}>{$t('sign.labelCompany')}:</Text>
-                <Text style={styles.text}>{this.state.summaryInfo.top.company}</Text>
+                <TextView style={styles.label}>{$t('sign.labelCompany')}:</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.top.company}</TextView>
               </View>
             }
             { utils.isNotEmpty(this.state.summaryInfo.top.registrationNo) &&
               <View style={styles.formRow}>
-                <Text style={styles.label}>{$t('receipt.labelRegistrationNo')}</Text>
-                <Text style={styles.text}>{this.state.summaryInfo.top.registrationNo}</Text>
+                <TextView style={styles.label}>{$t('receipt.labelRegistrationNo')}</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.top.registrationNo}</TextView>
               </View>
             }
             <View style={styles.formRow}>
-              <Text style={styles.label}>{$t('receipt.labelTransactionID')}</Text>
-              <Text style={styles.text}>{this.state.summaryInfo.top.transactionId}</Text>
+              <TextView style={styles.label}>{$t('receipt.labelTransactionID')}</TextView>
+              <TextView style={styles.text}>{this.state.summaryInfo.top.transactionId}</TextView>
             </View>
             <View style={styles.formRow}>
-              <Text style={styles.label}>{$t('receipt.labelReferenceID')}</Text>
-              <Text style={styles.text}>{this.state.summaryInfo.top.referenceId}</Text>
+              <TextView style={styles.label}>{$t('receipt.labelReferenceID')}</TextView>
+              <TextView style={styles.text}>{this.state.summaryInfo.top.referenceId}</TextView>
             </View>
             <View style={styles.formRow}>
-              <Text style={styles.label}>{$t('receipt.labelDateTime')}</Text>
-              <Text style={styles.text}>{this.state.summaryInfo.top.dateTime}</Text>
+              <TextView style={styles.label}>{$t('receipt.labelDateTime')}</TextView>
+              <TextView style={styles.text}>{this.state.summaryInfo.top.dateTime}</TextView>
             </View>
           </View>
         }
@@ -177,119 +178,119 @@ export default class Summary extends Component {
         </View> */}
         { utils.isNotEmpty(this.state.summaryInfo.station) &&
           <View style={styles.sections}>
-            <Text style={styles.formTitle}>{$t('wallet.labelYourStation')}</Text>
+            <TextView style={styles.formTitle}>{$t('wallet.labelYourStation')}</TextView>
             <View style={styles.formRow}>
-              <Text style={styles.label}>{$t('wallet.labelStationId')}</Text>
-              <Text style={styles.text}>{this.state.summaryInfo.station.stationId}</Text>
+              <TextView style={styles.label}>{$t('wallet.labelStationId')}</TextView>
+              <TextView style={styles.text}>{this.state.summaryInfo.station.stationId}</TextView>
             </View>
             <View style={styles.formRow}>
-              <Text style={styles.label}>{$t('receipt.labelSiteName')}</Text>
-              <Text style={styles.text}>{this.state.summaryInfo.station.siteName ?? "-"}</Text>
+              <TextView style={styles.label}>{$t('receipt.labelSiteName')}</TextView>
+              <TextView style={styles.text}>{this.state.summaryInfo.station.siteName ?? "-"}</TextView>
             </View>
           </View>
         }
         { utils.isNotEmpty(this.state.summaryInfo.connector) &&
           <View style={styles.sections}>
-            <Text style={styles.formTitle}>{$t('wallet.labelYourConnector')}</Text>
+            <TextView style={styles.formTitle}>{$t('wallet.labelYourConnector')}</TextView>
             <View style={styles.formRow}>
-              <Text style={styles.label}>{$t('charging.labelType')}:</Text>
-              <Text style={styles.text}>{this.state.summaryInfo.connector.type}</Text>
+              <TextView style={styles.label}>{$t('charging.labelType')}:</TextView>
+              <TextView style={styles.text}>{this.state.summaryInfo.connector.type}</TextView>
             </View>
             <View style={styles.formRow}>
-              <Text style={styles.label}>{$t('charging.labelPower')}:</Text>
-              <Text style={styles.text}>{this.state.summaryInfo.connector.power}</Text>
+              <TextView style={styles.label}>{$t('charging.labelPower')}:</TextView>
+              <TextView style={styles.text}>{this.state.summaryInfo.connector.power}</TextView>
             </View>
             <View style={styles.formRow}>
-              <Text style={styles.label}>{$t('charging.labelRates')}:</Text>
-              <Text style={styles.text}>{this.state.summaryInfo.connector.rates}</Text>
+              <TextView style={styles.label}>{$t('charging.labelRates')}:</TextView>
+              <TextView style={styles.text}>{this.state.summaryInfo.connector.rates}</TextView>
             </View>
           </View>
         }
         { utils.isNotEmpty(this.state.summaryInfo.chargingFee) &&
           <View style={styles.sections}>
-            <Text style={styles.formTitle}>{$t('receipt.breakdownChargingFees')}</Text>
+            <TextView style={styles.formTitle}>{$t('receipt.breakdownChargingFees')}</TextView>
             <View style={styles.formRow}>
-              <Text style={styles.label}>{$t('wallet.labelChargeTime')}</Text>
-              <Text style={styles.text}>{this.state.summaryInfo.chargingFee.chargeTime ?? "-"}</Text>
+              <TextView style={styles.label}>{$t('wallet.labelChargeTime')}</TextView>
+              <TextView style={styles.text}>{this.state.summaryInfo.chargingFee.chargeTime ?? "-"}</TextView>
             </View>
             <View style={styles.formRow}>
-              <Text style={styles.label}>{$t('wallet.labelChargeDelivered')}</Text>
-              <Text style={styles.text}>{this.state.summaryInfo.chargingFee.chargeDelivered ?? 0}</Text>
+              <TextView style={styles.label}>{$t('wallet.labelChargeDelivered')}</TextView>
+              <TextView style={styles.text}>{this.state.summaryInfo.chargingFee.chargeDelivered ?? 0}</TextView>
             </View>
             <View style={styles.formRow}>
-              <Text style={styles.label}>{this.getTaxTitle($t('receipt.labelChargTransSubtotal2'))}</Text>
-              <Text style={styles.text}>{this.state.summaryInfo.chargingFee.transactionSubtotal}</Text>
+              <TextView style={styles.label}>{this.getTaxTitle($t('receipt.labelChargTransSubtotal2'))}</TextView>
+              <TextView style={styles.text}>{this.state.summaryInfo.chargingFee.transactionSubtotal}</TextView>
             </View>
           </View>
         }
         { utils.isNotEmpty(this.state.summaryInfo.idleFee) &&
           <View style={styles.sections}>
-            <Text style={styles.formTitle}>{$t('receipt.breakdownIdlesFees')}</Text>
+            <TextView style={styles.formTitle}>{$t('receipt.breakdownIdlesFees')}</TextView>
             <View style={styles.formRow}>
-              <Text style={styles.label}>{$t('receipt.labelIdleStartTime')}</Text>
-              <Text style={styles.text}>{this.state.summaryInfo.idleFee.startTime}</Text>
+              <TextView style={styles.label}>{$t('receipt.labelIdleStartTime')}</TextView>
+              <TextView style={styles.text}>{this.state.summaryInfo.idleFee.startTime}</TextView>
             </View>
             <View style={styles.formRow}>
-              <Text style={styles.label}>{$t('receipt.labelIdleDuration')}</Text>
-              <Text style={styles.text}>{this.state.summaryInfo.idleFee.duration}</Text>
+              <TextView style={styles.label}>{$t('receipt.labelIdleDuration')}</TextView>
+              <TextView style={styles.text}>{this.state.summaryInfo.idleFee.duration}</TextView>
             </View>
             <View style={styles.formRow}>
-              <Text style={styles.label}>{this.getTaxTitle($t('receipt.labelIdleFeeSubtotal2'))}</Text>
-              <Text style={styles.text}>{this.state.summaryInfo.idleFee.subtotal}</Text>
+              <TextView style={styles.label}>{this.getTaxTitle($t('receipt.labelIdleFeeSubtotal2'))}</TextView>
+              <TextView style={styles.text}>{this.state.summaryInfo.idleFee.subtotal}</TextView>
             </View>
           </View>
         }
         { utils.isNotEmpty(this.state.summaryInfo.reservationFee) &&
           <View style={styles.sections}>
-            <Text style={styles.formTitle}>{$t('receipt.breakdownReservationFees')}</Text>
+            <TextView style={styles.formTitle}>{$t('receipt.breakdownReservationFees')}</TextView>
             <View style={styles.formRow}>
-              <Text style={styles.label}>{$t('receipt.labelTimeReservation')}</Text>
-              <Text style={styles.text}>{this.state.summaryInfo.reservationFee.reservationTime}</Text>
+              <TextView style={styles.label}>{$t('receipt.labelTimeReservation')}</TextView>
+              <TextView style={styles.text}>{this.state.summaryInfo.reservationFee.reservationTime}</TextView>
             </View>
             <View style={styles.formRow}>
-              <Text style={styles.label}>{$t('receipt.labelDurationReservation')}</Text>
-              <Text style={styles.text}>{this.state.summaryInfo.reservationFee.reservationDuration}</Text>
+              <TextView style={styles.label}>{$t('receipt.labelDurationReservation')}</TextView>
+              <TextView style={styles.text}>{this.state.summaryInfo.reservationFee.reservationDuration}</TextView>
             </View>
             <View style={styles.formRow}>
-              <Text style={styles.label}>{this.getTaxTitle($t('receipt.labelReservationFeeSubtotal2'))}</Text>
-              <Text style={styles.text}>{this.state.summaryInfo.reservationFee.reservationFeeSubtotal}</Text>
+              <TextView style={styles.label}>{this.getTaxTitle($t('receipt.labelReservationFeeSubtotal2'))}</TextView>
+              <TextView style={styles.text}>{this.state.summaryInfo.reservationFee.reservationFeeSubtotal}</TextView>
             </View>
           </View>
         }
         { utils.isNotEmpty(this.state.summaryInfo.payment) &&
           <View style={styles.sections}>
-            <Text style={styles.formTitle}>{$t('receipt.breakdownPayment')}</Text>
+            <TextView style={styles.formTitle}>{$t('receipt.breakdownPayment')}</TextView>
             <View style={styles.formRow}>
-              <Text style={styles.label}>{$t('wallet.labelPaymentMadeBy')}</Text>
-              <Text style={styles.text}>{this.state.summaryInfo.payment.paymentMadeBy ?? "-"}</Text>
+              <TextView style={styles.label}>{$t('wallet.labelPaymentMadeBy')}</TextView>
+              <TextView style={styles.text}>{this.state.summaryInfo.payment.paymentMadeBy ?? "-"}</TextView>
             </View>
             { utils.isNotEmpty(this.state.summaryInfo.payment.transactionSubtotal) &&
               <View style={styles.formRow}>
-                <Text style={styles.label}>{this.getTaxTitle($t('receipt.labelChargTransSubtotal2'))}</Text>
-                <Text style={styles.text}>{this.state.summaryInfo.payment.transactionSubtotal}</Text>
+                <TextView style={styles.label}>{this.getTaxTitle($t('receipt.labelChargTransSubtotal2'))}</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.payment.transactionSubtotal}</TextView>
               </View>
             }
             { utils.isNotEmpty(this.state.summaryInfo.payment.idleFeeSubtotal) &&
               <View style={styles.formRow}>
-                <Text style={styles.label}>{this.getTaxTitle($t('receipt.labelIdleFeeSubtotal2'))}</Text>
-                <Text style={styles.text}>{this.state.summaryInfo.payment.idleFeeSubtotal}</Text>
+                <TextView style={styles.label}>{this.getTaxTitle($t('receipt.labelIdleFeeSubtotal2'))}</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.payment.idleFeeSubtotal}</TextView>
               </View>
             }
             { utils.isNotEmpty(this.state.summaryInfo.payment.reservationFeeSubtotal) &&
               <View style={styles.formRow}>
-                <Text style={styles.label}>{this.getTaxTitle($t('receipt.labelReservationFeeSubtotal2'))}</Text>
-                <Text style={styles.text}>{this.state.summaryInfo.payment.reservationFeeSubtotal}</Text>
+                <TextView style={styles.label}>{this.getTaxTitle($t('receipt.labelReservationFeeSubtotal2'))}</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.payment.reservationFeeSubtotal}</TextView>
               </View>
             }
             { utils.isNotEmpty(this.state.summaryInfo.payment.finalPayment) &&
               <View style={styles.formRow}>
-                <Text style={styles.label}>
+                <TextView style={styles.label}>
                   { utils.isNotEmpty(this.state.summaryInfo.payment.discountCredit)
                   ? $t('receipt.labelAfterDiscount')
                   : $t('receipt.labelFinalPaymentAmount')
                   }
-                </Text>
-                <Text style={styles.text}>{this.state.summaryInfo.payment.finalPayment ?? "-"}</Text>
+                </TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.payment.finalPayment ?? "-"}</TextView>
               </View>
             }
             {/* <View style={styles.formRow}>
@@ -297,24 +298,24 @@ export default class Summary extends Component {
               <Text style={styles.text}>{currency}{this.state.summaryInfo.chargeRates ?? '0.0'}</Text>
             </View> */}
             <View style={styles.formRow}>
-              <Text style={styles.label}>{$t('wallet.labelExchangeRate')}</Text>
-              <Text style={styles.text}>{this.state.summaryInfo.payment.exchangeRate ?? "-"}</Text>
+              <TextView style={styles.label}>{$t('wallet.labelExchangeRate')}</TextView>
+              <TextView style={styles.text}>{this.state.summaryInfo.payment.exchangeRate ?? "-"}</TextView>
             </View>
             {/* <View style={styles.formRow}>
               <Text style={styles.label}>{$t('wallet.labelPreviousBalance')}</Text>
               <Text style={styles.text}>{this.getSummaryText(this.state.summaryInfo.previousBalance)}</Text>
             </View> */}
             <View style={styles.formRow}>
-              <Text style={styles.label}>{$t('wallet.labelResultingBalance')}</Text>
-              <Text style={styles.text}>{this.state.summaryInfo.payment.resultingBalance ?? "-"}</Text>
+              <TextView style={styles.label}>{$t('wallet.labelResultingBalance')}</TextView>
+              <TextView style={styles.text}>{this.state.summaryInfo.payment.resultingBalance ?? "-"}</TextView>
             </View>
           </View>
         }
         { this.state.isActully &&
           <View style={styles.bottomButton}>
-            <Text
+            <TextView
               style={styles.feedback}
-              onPress={() => startPage(PageList.feedback)}>{$t('wallet.linkSubmitFeedback')}</Text>
+              onPress={() => startPage(PageList.feedback)}>{$t('wallet.linkSubmitFeedback')}</TextView>
             {/* <Text style={styles.tipText}>{$t('wallet.tipsReceipt')}</Text> */}
             <Button
               text={$t('home.done')}

+ 11 - 10
Strides-APP/app/pages/chargeV2/TabInfos.js

@@ -4,6 +4,7 @@
  */
 import React, { Component } from 'react';
 import { View, Text, StyleSheet } from 'react-native';
+import TextView from '../../components/TextView';
 import PagerUtil from './PagerUtil';
 
 export default class TabInfos extends Component {
@@ -49,25 +50,25 @@ export default class TabInfos extends Component {
   render() {
     return (
       <View style={$padding(0, 16)}>
-        <Text style={styles.title}>{$t('charging.siteName')}</Text>
+        <TextView style={styles.title}>{$t('charging.siteName')}</TextView>
         <View style={styles.infoView}>
-          <Text style={styles.infoText}>{this.state.stationInfo?.name}</Text>
+          <TextView style={styles.infoText}>{this.state.stationInfo?.name}</TextView>
         </View>
-        <Text style={styles.title}>{$t('charging.siteAddress')}</Text>
+        <TextView style={styles.title}>{$t('charging.siteAddress')}</TextView>
         <View style={styles.infoView}>
-          <Text style={styles.infoText}>{this.state.stationInfo?.address}</Text>
+          <TextView style={styles.infoText}>{this.state.stationInfo?.address}</TextView>
         </View>
-        <Text style={styles.title}>{$t('charging.parkingFees')}</Text>
+        <TextView style={styles.title}>{$t('charging.parkingFees')}</TextView>
         <View style={styles.infoView}>
-          <Text style={styles.infoText}>{this.getParkingFee()}</Text>
+          <TextView style={styles.infoText}>{this.getParkingFee()}</TextView>
         </View>
-        <Text style={styles.title}>{$t('charging.operatingHours')}</Text>
+        <TextView style={styles.title}>{$t('charging.operatingHours')}</TextView>
         <View style={styles.infoView}>
-          <Text style={styles.infoText}>{this.getOperatingHours()}</Text>
+          <TextView style={styles.infoText}>{this.getOperatingHours()}</TextView>
         </View>
-        <Text style={styles.title}>{$t('charging.additionalInformation')}</Text>
+        <TextView style={styles.title}>{$t('charging.additionalInformation')}</TextView>
         <View style={styles.infoView}>
-          <Text style={styles.infoText}>{this.state.stationInfo?.additionalNotes}</Text>
+          <TextView style={styles.infoText}>{this.state.stationInfo?.additionalNotes}</TextView>
         </View>
       </View>
     );

+ 28 - 27
Strides-APP/app/pages/chargeV2/TabReserve.js

@@ -12,6 +12,7 @@
  import { CancelReserveDialog } from './InfoDialog';
 import PagerUtil from './PagerUtil';
 import BadgeSelectItem from '../../components/BadgeSelectItem';
+import TextView from '../../components/TextView';
 
 export default class TabReserve extends Component {
   constructor(props) {
@@ -260,7 +261,7 @@ export default class TabReserve extends Component {
               : this.reserveView()
             )
           : <View style={[{height: $vh(50)}, ui.flexvc]}>
-              <Text style={{color: textPrimary, fontSize: 14}}>{$t('charging.unallowReservation')}</Text>
+              <TextView style={{color: textPrimary, fontSize: 14}}>{$t('charging.unallowReservation')}</TextView>
             </View>
         }
         <CancelReserveDialog
@@ -292,7 +293,7 @@ export default class TabReserve extends Component {
     return (
       <>
       <View style={{minHeight: $vht(75)}}>
-        <Text style={styles.title}>{$t('charging.chooseConnector')}</Text>
+        <TextView style={styles.title}>{$t('charging.chooseConnector')}</TextView>
         { this.state.total > 0
           ? this.state.stationInfo.rateList.map((item, index) => {
             const _type = item.type?.indexOf('AC') >= 0 ? 'AC' : 'DC';
@@ -314,29 +315,29 @@ export default class TabReserve extends Component {
                   style={ChargeStyle.infoIcon}
                   source={_type == "AC" ? TypeImage.AC : TypeImage.DC}/>
                 <View style={ChargeStyle.infoGroup}>
-                  <Text style={ChargeStyle.infoText}>{item.type}</Text>
-                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelType')}</Text>
+                  <TextView style={ChargeStyle.infoText}>{item.type}</TextView>
+                  <TextView style={ChargeStyle.infoTitle}>{$t('charging.labelType')}</TextView>
                 </View>
                 <View style={ChargeStyle.infoGroup}>
-                  <Text style={ChargeStyle.infoText}>{item.power}</Text>
-                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelPower')}</Text>
+                  <TextView style={ChargeStyle.infoText}>{item.power}</TextView>
+                  <TextView style={ChargeStyle.infoTitle}>{$t('charging.labelPower')}</TextView>
                 </View>
                 <View style={ChargeStyle.infoGroup}>
-                  <Text style={ChargeStyle.infoText}>{this.getAvailable(_type)}</Text>
-                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelAvailableTotal')}</Text>
+                  <TextView style={ChargeStyle.infoText}>{this.getAvailable(_type)}</TextView>
+                  <TextView style={ChargeStyle.infoTitle}>{$t('charging.labelAvailableTotal')}</TextView>
                 </View>
                 <View style={ChargeStyle.infoGroup}>
                   { item?.connectorCount?.available > 0
-                    ? <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable]}>{$t('charging.statusAvailable')}</Text>
-                    : <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusUnavailable]}>{$t('charging.statusUnavailable')}</Text>
+                    ? <TextView style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable]}>{$t('charging.statusAvailable')}</TextView>
+                    : <TextView style={[ChargeStyle.infoStatus, ChargeStyle.statusUnavailable]}>{$t('charging.statusUnavailable')}</TextView>
                   }
-                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelStatus')}</Text>
+                  <TextView style={ChargeStyle.infoTitle}>{$t('charging.labelStatus')}</TextView>
                 </View>
                 {/* index == this.state.checkIndex
                   ? <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusSelected]}>Selected</Text>
                   : (item.available
-                    ? <TextRadius style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable]}>Available</TextRadius>
-                    : <TextRadius style={[ChargeStyle.infoStatus, ChargeStyle.statusUnavailable]}>Unavailable</TextRadius>
+                    ? <TextView style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable]}>Available</TextView>
+                    : <TextView style={[ChargeStyle.infoStatus, ChargeStyle.statusUnavailable]}>Unavailable</TextView>
                   )
                 */}
               </BadgeSelectItem>
@@ -345,15 +346,15 @@ export default class TabReserve extends Component {
         }
         { this.state.checkConnector.available
           ? <>
-              <Text style={styles.title}>{$t('charging.chooseRate')}</Text>
+              <TextView style={styles.title}>{$t('charging.chooseRate')}</TextView>
               <BadgeSelectItem
                 style={ChargeStyle.stationInfoView}
                 checked={true}>
                 <Image 
                   style={ChargeStyle.infoIcon}
                   source={require('../../images/charge/ic-type-rate.png')}/>
-                <Text style={ChargeStyle.rateText}>{$t('charging.labelRate')}</Text>
-                <Text style={[ChargeStyle.ratePrice]}>{this.state.checkConnector.rates}</Text>
+                <TextView style={ChargeStyle.rateText}>{$t('charging.labelRate')}</TextView>
+                <TextView style={[ChargeStyle.ratePrice]}>{this.state.checkConnector.rates}</TextView>
                 <Text></Text>
                 {/* <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusSelected]}>Selected</Text> */}
               </BadgeSelectItem>
@@ -381,7 +382,7 @@ export default class TabReserve extends Component {
     return (
       <>
         <View style={{minHeight: $vht(80)}}>
-          <Text style={styles.title}>Your Selection</Text>
+          <TextView style={styles.title}>Your Selection</TextView>
           { info &&
             <View>
               <BadgeSelectItem
@@ -391,16 +392,16 @@ export default class TabReserve extends Component {
                   style={ChargeStyle.infoIcon}
                   source={info.type?.indexOf('AC') >= 0 ? TypeImage.AC : TypeImage.DC}/>
                 <View style={ChargeStyle.infoGroup}>
-                  <Text style={ChargeStyle.infoText}>{info.type}</Text>
-                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelType')}</Text>
+                  <TextView style={ChargeStyle.infoText}>{info.type}</TextView>
+                  <TextView style={ChargeStyle.infoTitle}>{$t('charging.labelType')}</TextView>
                 </View>
                 <View style={ChargeStyle.infoGroup}>
-                  <Text style={ChargeStyle.infoText}>{info.power}</Text>
-                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelPower')}</Text>
+                  <TextView style={ChargeStyle.infoText}>{info.power}</TextView>
+                  <TextView style={ChargeStyle.infoTitle}>{$t('charging.labelPower')}</TextView>
                 </View>
                 <View style={ChargeStyle.infoGroup}>
-                  <Text style={ChargeStyle.infoBoldNumber}>{this.getAvailable(info.connectorCount)}</Text>
-                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelAvailableTotal')}</Text>
+                  <TextView style={ChargeStyle.infoBoldNumber}>{this.getAvailable(info.connectorCount)}</TextView>
+                  <TextView style={ChargeStyle.infoTitle}>{$t('charging.labelAvailableTotal')}</TextView>
                 </View>
                 <Text></Text>
                 {/* <SelectableIcon selected={true}/> */}
@@ -411,16 +412,16 @@ export default class TabReserve extends Component {
                 <Image 
                   style={ChargeStyle.infoIcon}
                   source={require('../../images/charge/ic-type-rate.png')}/>
-                <Text style={ChargeStyle.rateText}>{$t('charging.labelRate')}</Text>
-                <Text style={[ChargeStyle.ratePrice]}>{info.rates}</Text>
+                <TextView style={ChargeStyle.rateText}>{$t('charging.labelRate')}</TextView>
+                <TextView style={[ChargeStyle.ratePrice]}>{info.rates}</TextView>
                 <Text></Text>
                 {/* <SelectableIcon selected={true}/> */}
               </BadgeSelectItem>
             </View>
           }
-          <Text style={styles.timeleftText}>{$t('charging.reserveTimeLeft')}</Text>
+          <TextView style={styles.timeleftText}>{$t('charging.reserveTimeLeft')}</TextView>
           <View style={styles.timeleftView}>
-            <Text style={styles.timeleft}>{this.state.timeLeft}</Text>
+            <TextView style={styles.timeleft}>{this.state.timeLeft}</TextView>
           </View>
           <View style={styles.cancelView}>
             <Button

+ 19 - 18
Strides-APP/app/pages/charging/StationInfoView.js

@@ -5,6 +5,7 @@
 import React from 'react';
 import { Image, Text, View } from 'react-native';
 import BadgeSelectItem from '../../components/BadgeSelectItem';
+import TextView from '../../components/TextView';
 import { ChargeStyle, TypeImage } from '../chargeV2/Charging';
 
 export default StationInfoView = ({connectorInfo={}, isCharging, isPending}) => (
@@ -31,66 +32,66 @@ export default StationInfoView = ({connectorInfo={}, isCharging, isPending}) =>
       style={ChargeStyle.infoIcon}
       source={connectorInfo?.chargeType?.indexOf('AC') >= 0 ? TypeImage.AC : TypeImage.DC}/>
     <View style={ChargeStyle.infoGroup}>
-      <Text
+      <TextView
         numberOfLines={1}
         ellipsizeMode="tail"
-        style={ChargeStyle.infoText}>{connectorInfo.chargeType}{connectorInfo.wattage}</Text>
-      <Text style={ChargeStyle.infoTitle}>{$t('charging.labelType')}</Text>
+        style={ChargeStyle.infoText}>{connectorInfo.chargeType}{connectorInfo.wattage}</TextView>
+      <TextView style={ChargeStyle.infoTitle}>{$t('charging.labelType')}</TextView>
     </View>
     <View style={ChargeStyle.infoGroup}>
-      <Text
+      <TextView
         numberOfLines={1}
         ellipsizeMode="tail"
-        style={ChargeStyle.infoText}>{connectorInfo.rate}/{connectorInfo.rateType}</Text>
-      <Text style={ChargeStyle.infoTitle}>{$t('charging.labelRate')}</Text>
+        style={ChargeStyle.infoText}>{connectorInfo.rate}/{connectorInfo.rateType}</TextView>
+      <TextView style={ChargeStyle.infoTitle}>{$t('charging.labelRate')}</TextView>
     </View>
     <View style={ChargeStyle.infoGroup}>
-      <Text
+      <TextView
         numberOfLines={1}
         ellipsizeMode="tail"
-        style={ChargeStyle.infoText}>{connectorInfo.wattage}kW{/*connectorInfo.rateType*/}</Text>
-      <Text style={ChargeStyle.infoTitle}>{$t('charging.labelPower')}</Text>
+        style={ChargeStyle.infoText}>{connectorInfo.wattage}kW{/*connectorInfo.rateType*/}</TextView>
+      <TextView style={ChargeStyle.infoTitle}>{$t('charging.labelPower')}</TextView>
     </View>
     <View style={ChargeStyle.infoGroup}>
       { isCharging
       ? (isPending
         ? (
-            <Text
+            <TextView
               numberOfLines={1}
               ellipsizeMode="tail"
               style={[ChargeStyle.infoStatus, ChargeStyle.statusAuthenticated]}>
               {$t('charging.statusPreparing')}
-            </Text>
+            </TextView>
           )
         : (
-            <Text
+            <TextView
               numberOfLines={1}
               ellipsizeMode="tail"
               style={[ChargeStyle.infoStatus, ChargeStyle.statusAuthenticated]}>
               {$t('charging.statusCharging')}
-            </Text>
+            </TextView>
           )
         )
       : (connectorInfo.isCheckThrough
         ? (
-            <Text
+            <TextView
               numberOfLines={1}
               ellipsizeMode="tail"
               style={[ChargeStyle.infoStatus, ChargeStyle.statusAuthenticated]}>
               {$t('charging.statusAuthenticated')}
-            </Text>
+            </TextView>
           )
         : (
-            <Text
+            <TextView
               numberOfLines={1}
               ellipsizeMode="tail"
               style={[ChargeStyle.infoStatus, ChargeStyle.statusError]}>
               {$t('charging.statusNotConnected')}
-            </Text>
+            </TextView>
           )
         )
       }
-      <Text style={ChargeStyle.infoTitle}>{$t('charging.labelStatus')}</Text>
+      <TextView style={ChargeStyle.infoTitle}>{$t('charging.labelStatus')}</TextView>
     </View>
   </BadgeSelectItem>
 );

+ 4 - 3
Strides-APP/app/pages/charging/StepChargeView.js

@@ -5,6 +5,7 @@
 import React from 'react';
 import { StyleSheet, Text, View } from 'react-native';
 import Button from '../../components/Button';
+import TextView from '../../components/TextView';
 import { circleSize, DashboardView } from '../chargeV2/Charging';
 import { PaymentList } from '../chargeV2/Payment';
 import StationInfoView from './StationInfoView';
@@ -26,7 +27,7 @@ export default StepStartView = ({
       <DashboardView
         isCharging={isCharging}
         connectorInfo={connectorInfo}/>
-      <Text style={styles.title}>{$t('charging.selectedCharger')}</Text>
+      <TextView style={styles.title}>{$t('charging.selectedCharger')}</TextView>
       <StationInfoView
         isCharging={isCharging}
         isPending={isPending}
@@ -50,7 +51,7 @@ export default StepStartView = ({
           </Text>
         </ImageBackground>
       </View> */}
-      <Text style={styles.title}>{$t('charging.selectPaymentMethod')}</Text>
+      <TextView style={styles.title}>{$t('charging.selectPaymentMethod')}</TextView>
       <PaymentList
         payType={currentPayment}
         payPerUse={curerntPerUser}
@@ -64,7 +65,7 @@ export default StepStartView = ({
         onMethodChange={() => this.onMethodChange()}
       /> */}
       { lastUpdated
-        ? <Text style={styles.updateTip}>{$t('charging.lastUpdatedAt') + lastUpdated + '\n' + $t('charging.pullDownRefresh')}</Text>
+        ? <TextView style={styles.updateTip}>{$t('charging.lastUpdatedAt') + lastUpdated + '\n' + $t('charging.pullDownRefresh')}</TextView>
         : <></>
       }
     </View>

+ 25 - 24
Strides-APP/app/pages/charging/StepStartView.js

@@ -8,6 +8,7 @@ import Button from '../../components/Button';
 import { ChargeStyle, TypeImage } from '../chargeV2/Charging';
 import { PageList } from '../Router';
 import app from '../../../app.json';
+import TextView from '../../components/TextView';
 
 export default StepStartView = ({
   isPrivate,
@@ -18,8 +19,8 @@ export default StepStartView = ({
 }) => (
   <View>
     <View style={{minHeight: $vht(80)}}>
-      <Text style={styles.gstText}>{app.modules.nationally ? $t('charging.tipsRatesTax2') : $t('charging.tipsRatesTax')}</Text>
-      <Text style={styles.title}>{$t('charging.acChargers')} ({(stationInfo?.acConnector?.available ?? "0") + $t('charging.numberAvailable')})</Text>
+      <TextView style={styles.gstText}>{app.modules.nationally ? $t('charging.tipsRatesTax2') : $t('charging.tipsRatesTax')}</TextView>
+      <TextView style={styles.title}>{$t('charging.acChargers')} ({(stationInfo?.acConnector?.available ?? "0") + $t('charging.numberAvailable')})</TextView>
       { stationInfo.acRates?.length > 0
         ? stationInfo.acRates.map((item, index) => {
             return (
@@ -28,30 +29,30 @@ export default StepStartView = ({
                   style={ChargeStyle.infoIcon}
                   source={TypeImage.AC}/>
                 <View style={ChargeStyle.infoGroup}>
-                  <Text style={ChargeStyle.infoText}>{item.type}</Text>
-                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelType')}</Text>
+                  <TextView style={ChargeStyle.infoText}>{item.type}</TextView>
+                  <TextView style={ChargeStyle.infoTitle}>{$t('charging.labelType')}</TextView>
                 </View>
                 <View style={ChargeStyle.infoGroup}>
-                  <Text style={ChargeStyle.infoText}>{item.rates}</Text>
-                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelRate')}</Text>
+                  <TextView style={ChargeStyle.infoText}>{item.rates}</TextView>
+                  <TextView style={ChargeStyle.infoTitle}>{$t('charging.labelRate')}</TextView>
                 </View>
                 <View style={ChargeStyle.infoGroup}>
-                  <Text style={ChargeStyle.infoText}>{item.power}</Text>
-                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelPower')}</Text>
+                  <TextView style={ChargeStyle.infoText}>{item.power}</TextView>
+                  <TextView style={ChargeStyle.infoTitle}>{$t('charging.labelPower')}</TextView>
                 </View>
                 <View style={ChargeStyle.infoGroup}>
                   { item?.connectorCount?.available > 0
-                    ? <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable]}>{$t('charging.statusAvailable')}</Text>
-                    : <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusUnavailable]}>{$t('charging.statusUnavailable')}</Text>
+                    ? <TextView style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable]}>{$t('charging.statusAvailable')}</TextView>
+                    : <TextView style={[ChargeStyle.infoStatus, ChargeStyle.statusUnavailable]}>{$t('charging.statusUnavailable')}</TextView>
                   }
-                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelStatus')}</Text>
+                  <TextView style={ChargeStyle.infoTitle}>{$t('charging.labelStatus')}</TextView>
                 </View>
               </View>
             );
           })
-        : <Text style={ui.noData}>{$t('charging.noRates')}</Text>
+        : <TextView style={ui.noData}>{$t('charging.noRates')}</TextView>
       }
-      <Text style={styles.title}>{$t('charging.dcChargers')} ({(stationInfo?.dcConnector?.available ?? "0") + $t('charging.numberAvailable')})</Text>
+      <TextView style={styles.title}>{$t('charging.dcChargers')} ({(stationInfo?.dcConnector?.available ?? "0") + $t('charging.numberAvailable')})</TextView>
       { stationInfo.dcRates?.length > 0
         ? stationInfo.dcRates.map((item, index) => {
             return (
@@ -60,32 +61,32 @@ export default StepStartView = ({
                   style={ChargeStyle.infoIcon}
                   source={TypeImage.DC}/>
                 <View style={ChargeStyle.infoGroup}>
-                  <Text style={ChargeStyle.infoText}>{item.type}</Text>
-                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelType')}</Text>
+                  <TextView style={ChargeStyle.infoText}>{item.type}</TextView>
+                  <TextView style={ChargeStyle.infoTitle}>{$t('charging.labelType')}</TextView>
                 </View>
                 <View style={ChargeStyle.infoGroup}>
-                  <Text style={ChargeStyle.infoText}>{item.rates}</Text>
-                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelRate')}</Text>
+                  <TextView style={ChargeStyle.infoText}>{item.rates}</TextView>
+                  <TextView style={ChargeStyle.infoTitle}>{$t('charging.labelRate')}</TextView>
                 </View>
                 <View style={ChargeStyle.infoGroup}>
-                  <Text style={ChargeStyle.infoText}>{item.power}</Text>
-                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelPower')}</Text>
+                  <TextView style={ChargeStyle.infoText}>{item.power}</TextView>
+                  <TextView style={ChargeStyle.infoTitle}>{$t('charging.labelPower')}</TextView>
                 </View>
                 <View style={ChargeStyle.infoGroup}>
                   { item?.connectorCount?.available > 0
-                    ? <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable]}>{$t('charging.statusAvailable')}</Text>
-                    : <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusUnavailable]}>{$t('charging.statusUnavailable')}</Text>
+                    ? <TextView style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable]}>{$t('charging.statusAvailable')}</TextView>
+                    : <TextView style={[ChargeStyle.infoStatus, ChargeStyle.statusUnavailable]}>{$t('charging.statusUnavailable')}</TextView>
                   }
-                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelStatus')}</Text>
+                  <TextView style={ChargeStyle.infoTitle}>{$t('charging.labelStatus')}</TextView>
                 </View>
               </View>
             );
           })
-        : <Text style={ui.noData}>{$t('charging.noRates')}</Text>
+        : <TextView style={ui.noData}>{$t('charging.noRates')}</TextView>
       }
       { isPrivate &&
         <View style={styles.privateView}> 
-          <Text style={styles.privateText}>{$t('charging.ratesPrivateNote')}</Text>
+          <TextView style={styles.privateText}>{$t('charging.ratesPrivateNote')}</TextView>
         </View>
       }
     </View>

+ 6 - 4
Strides-APP/app/pages/chargingV2/ChargingPage.js

@@ -45,7 +45,7 @@ export default class ChargingPage extends Component {
         //测试进入
         //this.testInit();
         //正常进入
-        this.refreshChargeData(0);
+        this.getConnectorInfo();
       })
     }
   }
@@ -57,7 +57,9 @@ export default class ChargingPage extends Component {
         status: "Initiating"
       }
     }, () => {
-      this.refreshChargeData(2000);
+      setTimeout(() => {
+        this.getConnectorInfo();
+      }, 2000);
     })
   }
 
@@ -158,12 +160,12 @@ export default class ChargingPage extends Component {
             });
             break;
         }
-        this.setState(state);
+        this.setState(state)
       }
     }).catch(err => {
       Dialog.showResultDialog("An error occurred:\n" + err, "Retry", () => {
         this.getConnectorInfo();
-      });
+      })
       //toastShort(err)
     })
   }

+ 6 - 5
Strides-APP/app/pages/chargingV2/StepAuth.js

@@ -5,6 +5,7 @@
 import React, { useEffect, useState } from 'react';
 import { View, Text, Image, StyleSheet } from 'react-native';
 import Button from '../../components/Button';
+import TextView from '../../components/TextView';
 import { PaymentList } from '../chargeV2/Payment';
 
 export default StepAuth = ({
@@ -55,16 +56,16 @@ export default StepAuth = ({
             : require('../../images/site/charging-status-ready.png')
           }/>
         { isAuthentic
-        ? <Text style={styles.stepTitle}>{$t('charging.stepAuthenticated')}</Text>
+        ? <TextView style={styles.stepTitle}>{$t('charging.stepAuthenticated')}</TextView>
         : <View style={ui.flexcc}>
-            <Text style={styles.stepTitle}>{$t('charging.stepAuthenticating')}</Text>
-            <Text style={[styles.stepTitle, {width: 30, marginRight: -10}]}>{loadingEmps}</Text>
+            <TextView style={styles.stepTitle}>{$t('charging.stepAuthenticating')}</TextView>
+            <TextView style={[styles.stepTitle, {width: 30, marginRight: -10}]}>{loadingEmps}</TextView>
           </View>
         }
-        <Text style={styles.stepDesc}>{$t(isAuthentic ? 'charging.stepAuthenticatedDesc' : 'charging.stepAuthenticatingDesc')}</Text>
+        <TextView style={styles.stepDesc}>{$t(isAuthentic ? 'charging.stepAuthenticatedDesc' : 'charging.stepAuthenticatingDesc')}</TextView>
       </View>
       <View style={styles.bottomView}>
-        <Text style={styles.label}>{$t('charging.paymentMethod')}</Text>
+        <TextView style={styles.label}>{$t('charging.paymentMethod')}</TextView>
         <PaymentList
           payType={currentPayment}
           payPerUse={curerntPerUse}

+ 28 - 27
Strides-APP/app/pages/chargingV2/StepCharging.js

@@ -5,6 +5,7 @@
 import React, { useEffect, useRef, useState } from 'react';
 import { Animated, Easing, Image, ScrollView, StyleSheet, Text, View } from 'react-native';
 import Button, { ElevationObject } from '../../components/Button';
+import TextView from '../../components/TextView';
 import utils from '../../utils/utils';
 import { PaymentList } from '../chargeV2/Payment';
 
@@ -98,59 +99,59 @@ const StepCharging = ({
             style={styles.stepImage}
             resizeMode="contain"
             source={require('../../images/site/charging-status-charge.png')}/>
-          <Text style={styles.stepTitle}>{$t('charging.statusCharging')}</Text>
-          <Text style={styles.stepDesc}>{$t('charging.stepChargingDesc')}</Text>
+          <TextView style={styles.stepTitle}>{$t('charging.statusCharging')}</TextView>
+          <TextView style={styles.stepDesc}>{$t('charging.stepChargingDesc')}</TextView>
           { connectorInfo.batteryPercent >= 0 &&
-            <Text
+            <TextView
               style={styles.socText}
-              numberOfLines={1}>{connectorInfo.batteryPercent || "0"}%</Text>
+              numberOfLines={1}>{connectorInfo.batteryPercent || "0"}%</TextView>
           }
         </View>
         <View style={styles.infoRow}>
           <View style={skipAnim ? styles.infoCarded : styles.infoCard}>
-            <Text style={styles.infoTitle}>{$t('charging.labelTimeElapsed')}</Text>
-            <Text
+            <TextView style={styles.infoTitle}>{$t('charging.labelTimeElapsed')}</TextView>
+            <TextView
               numberOfLines={1}
               ellipsizeMode="tail"
-              style={styles.infoText}>{utils.minutes2HHMM(connectorInfo?.timeElapsed ?? 0)}</Text>
-            <Text style={styles.infoDesc}></Text>
+              style={styles.infoText}>{utils.minutes2HHMM(connectorInfo?.timeElapsed ?? 0)}</TextView>
+            <TextView style={styles.infoDesc}></TextView>
           </View>
           <View style={skipAnim ? styles.infoCarded : styles.infoCard}>
-            <Text style={styles.infoTitle}>{$t('charging.labelTotalkWh')}</Text>
-            <Text
+            <TextView style={styles.infoTitle}>{$t('charging.labelTotalkWh')}</TextView>
+            <TextView
               numberOfLines={1}
               ellipsizeMode="tail"
-              style={styles.infoText}>{connectorInfo.totalKWhDelivered || "0"} kWh</Text>
-            <Text style={styles.infoDesc}></Text>
+              style={styles.infoText}>{connectorInfo.totalKWhDelivered || "0"} kWh</TextView>
+            <TextView style={styles.infoDesc}></TextView>
           </View>
         </View>
         <View style={styles.infoRow}>
           <View style={skipAnim ? styles.infoCarded : styles.infoCard}>
-            <Text style={styles.infoTitle}>{$t('charging.labelRate')}</Text>
-            <Text
+            <TextView style={styles.infoTitle}>{$t('charging.labelRate')}</TextView>
+            <TextView
               numberOfLines={2}
               ellipsizeMode="tail"
-              style={styles.infoText}>{connectorInfo.rates || "S$0.00/kWh"}</Text>
-            <Text
+              style={styles.infoText}>{connectorInfo.rates || "S$0.00/kWh"}</TextView>
+            <TextView
               numberOfLines={1}
               ellipsizeMode="tail"
-              style={styles.infoDesc}>({connectorInfo.userRates || "S$0.00/kWh"})</Text>
+              style={styles.infoDesc}>({connectorInfo.userRates || "S$0.00/kWh"})</TextView>
           </View>
           <View style={skipAnim ? styles.infoCarded : styles.infoCard}>
-            <Text style={styles.infoTitle}>{$t('charging.labelTotalCharges')}</Text>
-            <Text
+            <TextView style={styles.infoTitle}>{$t('charging.labelTotalCharges')}</TextView>
+            <TextView
               numberOfLines={2}
               ellipsizeMode="tail"
-              style={styles.infoText}>{connectorInfo.totalCharges || "S$ 0.0"}</Text>
-            <Text
+              style={styles.infoText}>{connectorInfo.totalCharges || "S$ 0.0"}</TextView>
+            <TextView
               numberOfLines={1}
               ellipsizeMode="tail"
-              style={styles.infoDesc}>({connectorInfo.userTotalCharges || "S$ 0.0"})</Text>
+              style={styles.infoDesc}>({connectorInfo.userTotalCharges || "S$ 0.0"})</TextView>
           </View>
         </View>
       </Animated.View>
       <EndView/>
-      <Text style={styles.label}>{$t('charging.paymentMethod')}</Text>
+      <TextView style={styles.label}>{$t('charging.paymentMethod')}</TextView>
       <PaymentList
         payType={currentPayment}
         payPerUse={curerntPerUse}
@@ -173,13 +174,13 @@ const StepCharging = ({
           resizeMode="contain"
           source={require('../../images/site/charging-status-ready.png')}/>
         <View style={ui.flexcc}>
-          <Text style={styles.stepTitle}>{$t('charging.stepInitializing')}</Text>
-          <Text style={[styles.stepTitle, {width: 30, marginRight: -10}]}>{loadingEmps}</Text>
+          <TextView style={styles.stepTitle}>{$t('charging.stepInitializing')}</TextView>
+          <TextView style={[styles.stepTitle, {width: 30, marginRight: -10}]}>{loadingEmps}</TextView>
         </View>
-        <Text style={styles.stepDesc}>{$t('charging.stepInitializingDesc')}</Text>
+        <TextView style={styles.stepDesc}>{$t('charging.stepInitializingDesc')}</TextView>
       </Animated.View>
       <View style={styles.bottomView}>
-        <Text style={styles.label}>{$t('charging.paymentMethod')}</Text>
+        <TextView style={styles.label}>{$t('charging.paymentMethod')}</TextView>
         <PaymentList
           payType={currentPayment}
           payPerUse={curerntPerUse}

+ 20 - 19
Strides-APP/app/pages/chargingV2/StepStart.js

@@ -5,6 +5,7 @@
 import React from 'react';
 import { View, Text, Image, StyleSheet, ScrollView } from 'react-native';
 import Button, { ElevationObject } from '../../components/Button';
+import TextView from '../../components/TextView';
 import { ChargeStyle } from '../chargeV2/Charging';
 import { PaymentList } from '../chargeV2/Payment';
 
@@ -24,54 +25,54 @@ export default StepStart = ({
           style={styles.stepImage}
           resizeMode="contain"
           source={require('../../images/site/charging-status-unknow.png')}/>
-        <Text style={styles.stepTitle}>{$t('charging.stepInsertConnector')}</Text>
-        <Text style={styles.stepDesc}>{$t('charging.stepInsertConnectorDesc')}</Text>
+        <TextView style={styles.stepTitle}>{$t('charging.stepInsertConnector')}</TextView>
+        <TextView style={styles.stepDesc}>{$t('charging.stepInsertConnectorDesc')}</TextView>
       </View>
       <View style={styles.infoRow}>
         <View style={styles.infoCard}>
-          <Text style={styles.infoTitle}>{$t('charging.labelType')}</Text>
-          <Text
+          <TextView style={styles.infoTitle}>{$t('charging.labelType')}</TextView>
+          <TextView
             numberOfLines={1}
             ellipsizeMode="tail"
-            style={styles.infoText}>{connectorInfo.chargeType}{connectorInfo.wattage || "AC22"}</Text>
-          <Text style={styles.infoDesc}></Text>
+            style={styles.infoText}>{connectorInfo.chargeType}{connectorInfo.wattage || "AC22"}</TextView>
+          <TextView style={styles.infoDesc}></TextView>
         </View>
         <View style={styles.infoCard}>
-          <Text style={styles.infoTitle}>{$t('charging.labelPower')}</Text>
-          <Text
+          <TextView style={styles.infoTitle}>{$t('charging.labelPower')}</TextView>
+          <TextView
             numberOfLines={1}
             ellipsizeMode="tail"
-            style={styles.infoText}>{connectorInfo.wattage || "0"} kW{/*connectorInfo.rateType*/}</Text>
-          <Text style={styles.infoDesc}></Text>
+            style={styles.infoText}>{connectorInfo.wattage || "0"} kW{/*connectorInfo.rateType*/}</TextView>
+          <TextView style={styles.infoDesc}></TextView>
         </View>
       </View>
       <View style={styles.infoRow}>
         <View style={styles.infoCard}>
-          <Text style={styles.infoTitle}>{$t('charging.labelRate')}</Text>
+          <TextView style={styles.infoTitle}>{$t('charging.labelRate')}</TextView>
           {/* <Text
             numberOfLines={1}
             ellipsizeMode="tail"
             style={styles.infoText}>{connectorInfo.rate || "0.00"}/{connectorInfo.rateType || "kWh"}</Text> */}
-          <Text
+          <TextView
             numberOfLines={2}
             ellipsizeMode="tail"
-            style={styles.infoText}>{connectorInfo.rates || "S$0.00/kWh"}</Text>
-          <Text
+            style={styles.infoText}>{connectorInfo.rates || "S$0.00/kWh"}</TextView>
+          <TextView
             numberOfLines={1}
             ellipsizeMode="tail"
-            style={styles.infoDesc}>({connectorInfo.userRates || "S$0.00/kWh"})</Text>
+            style={styles.infoDesc}>({connectorInfo.userRates || "S$0.00/kWh"})</TextView>
         </View>
         <View style={styles.infoCard}>
-          <Text style={styles.infoTitle}>{$t('charging.labelStatus')}</Text>
-          <Text
+          <TextView style={styles.infoTitle}>{$t('charging.labelStatus')}</TextView>
+          <TextView
             style={[ChargeStyle.statusAuthenticated, styles.infoStatus]}>
             {$t('charging.statusAvailable')}
-          </Text>
+          </TextView>
         </View>
       </View>
       <EndView/>
       <EndView half/>
-      <Text style={styles.label}>{$t('charging.paymentMethod')}</Text>
+      <TextView style={styles.label}>{$t('charging.paymentMethod')}</TextView>
       <PaymentList
         payType={currentPayment}
         payPerUse={curerntPerUse}

+ 5 - 4
Strides-APP/app/pages/chargingV2/StepStop.js

@@ -4,6 +4,7 @@
  */
 import React, { useEffect, useState } from 'react';
 import { View, Text, Image, StyleSheet } from 'react-native';
+import TextView from '../../components/TextView';
 import { PaymentList } from '../chargeV2/Payment';
 
 export default StepStop = ({
@@ -41,14 +42,14 @@ export default StepStop = ({
           source={require('../../images/site/charging-status-ready.png')}
         />
         <View style={ui.flexcc}>
-          <Text style={styles.stepTitle}>{$t('charging.stepStoppingCharge')}</Text>
-          <Text style={[styles.stepTitle, {width: 30, marginRight: -10}]}>{loadingEmps}</Text>
+          <TextView style={styles.stepTitle}>{$t('charging.stepStoppingCharge')}</TextView>
+          <TextView style={[styles.stepTitle, {width: 30, marginRight: -10}]}>{loadingEmps}</TextView>
         </View>
-        <Text style={styles.stepDesc}>{$t('charging.stepStoppingChargeDesc')}</Text>
+        <TextView style={styles.stepDesc}>{$t('charging.stepStoppingChargeDesc')}</TextView>
       </View>
 
       <View style={styles.bottomView}>
-      <Text style={styles.label}>{$t('charging.paymentMethod')}</Text>
+      <TextView style={styles.label}>{$t('charging.paymentMethod')}</TextView>
         <PaymentList
           payType={currentPayment}
           payPerUse={curerntPerUse}

+ 30 - 27
Strides-APP/app/pages/home/Drawer.js

@@ -19,7 +19,7 @@ import { TouchableWithoutFeedback } from 'react-native-gesture-handler';
 import { toTopupPage } from '../payment/PaymentConfig';
 import utils from '../../utils/utils';
 import apiNotification from '../../api/apiNotification';
-import TextRadius from '../../components/TextRadius';
+import TextView from '../../components/TextView';
 
 const Drawer = createDrawerNavigator();
 
@@ -138,6 +138,9 @@ export default class Home extends Component {
         drawerStyle={{
           width: $vw(75) > 320 ? 320 : $vw(75),
           backgroundColor: colorLight
+        }}
+        screenOptions={{
+          headerShown: false
         }}>
         <Drawer.Screen name="maps" component={Maps} />
       </Drawer.Navigator>
@@ -210,7 +213,7 @@ const DrawerContent = ({isLogin, userInfo, onLogout, notificationCount=0, naviga
           style={styles.nickView}
           android_ripple={ripple}
           onPress={() => startPage(isLogin ? PageList.profile : PageList.login)}>
-          <Text
+          <TextView
             style={styles.nickname}
             ellipsizeMode='tail'
             numberOfLines={1}>
@@ -220,7 +223,7 @@ const DrawerContent = ({isLogin, userInfo, onLogout, notificationCount=0, naviga
                 : $t('drawer.logging')
               : $t('drawer.sign')
             }
-          </Text>
+          </TextView>
           <FontAwesome
             size={24}
             color='#999'
@@ -279,7 +282,7 @@ const DrawerContent = ({isLogin, userInfo, onLogout, notificationCount=0, naviga
             {/* <Image
               style={styles.icon}
               source={require('../../images/icon/draw-charge.png')}/> */}
-            <Text style={styles.label}>{$t('drawer.charging')}</Text>
+            <TextView style={styles.label}>{$t('drawer.charging')}</TextView>
           </Button>
         : <View
             style={styles.disableItem}>
@@ -292,7 +295,7 @@ const DrawerContent = ({isLogin, userInfo, onLogout, notificationCount=0, naviga
             {/* <Image
               style={styles.icon}
               source={require('../../images/icon/draw-charge-no.png')}/> */}
-            <Text style={styles.disable}>{$t('drawer.charging')}</Text>
+            <TextView style={styles.disable}>{$t('drawer.charging')}</TextView>
           </View>
       }
       {/* isLogin
@@ -326,7 +329,7 @@ const DrawerContent = ({isLogin, userInfo, onLogout, notificationCount=0, naviga
           <Image
             style={styles.icon}
             source={require('../../images/icon/draw-transaction.png')}/>
-          <Text style={styles.label}>{$t('drawer.wallet')}</Text>
+          <TextView style={styles.label}>{$t('drawer.wallet')}</TextView>
         </Button>
         <Button
           style={styles.itemButton}
@@ -337,8 +340,8 @@ const DrawerContent = ({isLogin, userInfo, onLogout, notificationCount=0, naviga
             size={26}
             name={"wallet-outline"}
             color={colorDark}/>
-          <Text style={styles.label}>{$t('drawer.topup')}</Text>
-          <Text style={styles.balanceText2}>{currency}{userInfo.credit}</Text>
+          <TextView style={styles.label}>{$t('drawer.topup')}</TextView>
+          <TextView style={styles.balanceText2}>{userInfo.creditStr}</TextView>
         </Button></>
       }
 
@@ -355,11 +358,11 @@ const DrawerContent = ({isLogin, userInfo, onLogout, notificationCount=0, naviga
             name="notifications"
             color="#222"
             size={26}/>
-          <Text style={styles.label}>{$t('route.notifications')}</Text>
-          { notificationCount > 0 &&
+          <TextView style={styles.label}>{$t('route.notifications')}</TextView>
+          { notificationCount > 0 && (
             notificationCount < 100
-            ? <TextRadius style={styles.bridgeText}>{notificationCount}</TextRadius>
-            : <TextRadius style={styles.bridgeText2}>99+</TextRadius>
+            ? <Text style={styles.bridgeText} allowFontScaling={false}>{notificationCount}</Text>
+            : <Text style={styles.bridgeText2} allowFontScaling={false}>99+</Text>)
           }
         </Button>
       }
@@ -375,7 +378,7 @@ const DrawerContent = ({isLogin, userInfo, onLogout, notificationCount=0, naviga
             name="stars"
             color="#222"
             size={26}/>
-          <Text style={styles.label}>{$t('route.bookmarks')}</Text>
+          <TextView style={styles.label}>{$t('route.bookmarks')}</TextView>
         </Button>
       }
       {/*附加功能-结束*/}
@@ -391,7 +394,7 @@ const DrawerContent = ({isLogin, userInfo, onLogout, notificationCount=0, naviga
             name="card-membership"
             color="#222"
             size={26}/>
-          <Text style={styles.label}>{$t('drawer.members')}</Text>
+          <TextView style={styles.label}>{$t('drawer.members')}</TextView>
         </Button>
       }
       <Button
@@ -405,7 +408,7 @@ const DrawerContent = ({isLogin, userInfo, onLogout, notificationCount=0, naviga
           name="message-alert-outline"
           color="#222"
           size={24}/>
-        <Text style={styles.label}>{$t('drawer.feedback')}</Text>
+        <TextView style={styles.label}>{$t('drawer.feedback')}</TextView>
       </Button>
 
       {/* <Button
@@ -428,13 +431,13 @@ const DrawerContent = ({isLogin, userInfo, onLogout, notificationCount=0, naviga
         onClick={() => {
           startPage(PageList.settings);
         }}>
-        <Ionicons
+        <MaterialIcons
           style={styles.icon}
-          name="md-settings-outline"
+          name="settings"
           color="#222"
           size={25}
         />
-        <Text style={styles.label}>{$t('drawer.settings')}</Text>
+        <TextView style={styles.label}>{$t('drawer.settings')}</TextView>
       </Button>
       { app.modules.support &&
         <Button
@@ -449,7 +452,7 @@ const DrawerContent = ({isLogin, userInfo, onLogout, notificationCount=0, naviga
             color="#333"
             size={26}
           />
-          <Text style={styles.label}>{$t('drawer.contactSupport')}</Text>
+          <TextView style={styles.label}>{$t('drawer.contactSupport')}</TextView>
         </Button>
       }
       <Button
@@ -464,14 +467,14 @@ const DrawerContent = ({isLogin, userInfo, onLogout, notificationCount=0, naviga
           color="#333"
           size={26}
         />
-        <Text style={styles.label}>{$t('drawer.about')}</Text>
+        <TextView style={styles.label}>{$t('drawer.about')}</TextView>
       </Button>
 
       {/* <View style={styles.divided}></View> */}
       { DEBUG &&
       <>
         <View style={styles.divideLogin}></View>
-        <Text style={{color: "#ccc", paddingLeft: 16, paddingBottom: 8, fontSize: 12}}>{$t('drawer.debugOnly')}</Text>
+        <TextView style={{color: "#ccc", paddingLeft: 16, paddingBottom: 8, fontSize: 12}}>{$t('drawer.debugOnly')}</TextView>
         <Button
           style={styles.itemButton}
           viewStyle={styles.itemView}
@@ -483,7 +486,7 @@ const DrawerContent = ({isLogin, userInfo, onLogout, notificationCount=0, naviga
             name="file-eye-outline"
             color="#222"
             size={26}/>
-          <Text style={styles.label}>{$t('drawer.privacyPolicy')}</Text>
+          <TextView style={styles.label}>{$t('drawer.privacyPolicy')}</TextView>
         </Button>
         <Button
           style={styles.itemButton}
@@ -496,7 +499,7 @@ const DrawerContent = ({isLogin, userInfo, onLogout, notificationCount=0, naviga
             name="file-eye-outline"
             color="#222"
             size={26}/>
-          <Text style={styles.label}>{$t('drawer.termsOfUse')}</Text>
+          <TextView style={styles.label}>{$t('drawer.termsOfUse')}</TextView>
         </Button>
         <Button
           style={styles.itemButton}
@@ -512,7 +515,7 @@ const DrawerContent = ({isLogin, userInfo, onLogout, notificationCount=0, naviga
             name="notifications-none"
             color="#222"
             size={26}/>
-          <Text style={styles.label}>{$t('route.notificationTest')}</Text>
+          <TextView style={styles.label}>{$t('route.notificationTest')}</TextView>
         </Button>
         <Button
           style={styles.itemButton}
@@ -525,7 +528,7 @@ const DrawerContent = ({isLogin, userInfo, onLogout, notificationCount=0, naviga
             name="map-legend"
             color="#222"
             size={26}/>
-          <Text style={styles.label}>{$t('drawer.mapsTest')}</Text>
+          <TextView style={styles.label}>{$t('drawer.mapsTest')}</TextView>
         </Button>
       </>}
 
@@ -559,7 +562,7 @@ const DrawerContent = ({isLogin, userInfo, onLogout, notificationCount=0, naviga
         <Image
           style={Styles.logo}
           resizeMode='contain'
-          source={require('../../images/app-logo.png')}
+          source={require('../../images/drawer-logo.png')}
         />
         <Text style={styles.versionText}>{app.displayName + '  ' + app.versionName}</Text>
       </View>
@@ -657,7 +660,7 @@ const styles = StyleSheet.create({
   label: {
     flex: 1,
     color: textPrimary,
-    fontSize: 16.5
+    fontSize: 16,
   },
   disable: {
     color: '#999',

+ 34 - 103
Strides-APP/app/pages/home/Home.js

@@ -1,23 +1,19 @@
 /**
- * 首页地图
+ * 首页通用
  * @邠心vbe on 2021/08/13
  */
 import React, { Component } from 'react';
-import { View, Text, Pressable, Image, StyleSheet, BackHandler, Linking } from 'react-native';
+import { View, StyleSheet, BackHandler, Linking } from 'react-native';
+import app from '../../../app.json'
+import { i18nUtil } from '../../i18n';
 import apiBase from '../../api/apiBase';
 import apiStation from '../../api/apiStation';
 import Dialog from '../../components/Dialog';
-import { Styles } from '../../components/Toolbar';
-import MyStatusBar from '../../components/MyStatusBar';
 import utils from '../../utils/utils';
-import { PageList } from '../Router';
 import { SettingUtil } from '../Settings';
-import BottomSiteInfo from './maps/BottomSiteInfo';
+import MyStatusBar from '../../components/MyStatusBar';
 import LocationPermission from './maps/LocationPermission';
-import Maps from './maps/Maps';
-import SearchTool from './maps/SearchTool';
-import app from '../../../app.json'
-import { i18nUtil } from '../../i18n';
+import MapUI from './MapUI';
 
 export default class HomePage extends Component {
   constructor(props) {
@@ -61,13 +57,17 @@ export default class HomePage extends Component {
         this.checkPermission2Geo();
       })
       this.isHide = false;
-      //MyStatusBar.setStatusBarThemes(MyStatusBar.DARK_STYLE, colorLight);
+      if (!app.isWhitelabel) {
+        MyStatusBar.setStatusBarThemes(MyStatusBar.DARK_STYLE, colorLight);
+      }
     });
 
     navigation.addListener('blur', () => {
       //toastShort('onStop')
       this.isHide = true;
-      //MyStatusBar.setStatusBarThemes(MyStatusBar.LIGHT_STYLE, colorPrimaryDark);
+      if (!app.isWhitelabel) {
+        MyStatusBar.setStatusBarThemes(MyStatusBar.LIGHT_STYLE, colorPrimaryDark);
+      }
       setTimeout(() => {
         navigation.closeDrawer();
       }, 200);
@@ -89,7 +89,6 @@ export default class HomePage extends Component {
     });*/
     BackHandler.addEventListener('hardwareBackPress', this.toExit)
     this.isHide = false;
-    //MyStatusBar.setStatusBarThemes(MyStatusBar.DARK_STYLE, colorLight);
     this.checkUpdateVersion();
   }
 
@@ -104,6 +103,12 @@ export default class HomePage extends Component {
 
   toExit = () => {
     //console.log("beforeRemove", this.isHide);
+    if (this.state.stationInfo?.id) {
+      this.setState({
+        stationInfo: {}
+      });
+      return true;
+    }
     if (!this.isHide) {
       if (isIOS) {
         //e.preventDefault();
@@ -122,6 +127,14 @@ export default class HomePage extends Component {
     }
   }
 
+  onMapReady() {
+    this.setState({
+      mapReady: true
+    }, () => {
+      this.checkPermission2Geo();
+    });
+  }
+
   checkUpdateVersion() {
     apiBase.checkUpdate().then(res => {
       if (res.data.versionCode > app.versionCode) {
@@ -408,62 +421,17 @@ export default class HomePage extends Component {
   render() {
     return (
       <View style={ui.flex1}>
-        <View style={Styles.toolbar}>
-          <Pressable
-            style={Styles.backIcon}
-            android_ripple = {rippleLess}
-            onPress={() => {
-              this.props.navigation.toggleDrawer();
-            }}>
-            <EvilIcons name={'navicon'} size={28} color={colorPrimary} />
-          </Pressable>
-          <View style={styles.logoView}>
-            <Image
-              source={require('../../images/tool-logo.png')}
-              style={Styles.logo}
-              resizeMode="contain"
-            />
-          </View>
-          {/* <Text style={ui.flex1}></Text> */}
-          <Pressable
-            style={Styles.backIcon}
-            android_ripple = {rippleLess}
-            onPress={() => startPage(PageList.scanqr, {actionDetail: true})}>
-            <MaterialCommunityIcons
-              name='line-scan'
-              size={20}
-              color={colorPrimary}
-            />
-          </Pressable>
-        </View>
-        <SearchTool
-          count={this.state.stopList.length}
-          mapReady={this.state.mapReady}
+        <MapUI
+          state={this.state}
+          navigation={this.props.navigation}
           onFilter={data => this.findFilter(data)}
+          onMapReady={() => this.onMapReady()}
+          onFavorite={() => this.favoriteSite()}
           onLocation={() => this.checkPermission2Geo(true)}
-          navigation={this.props.navigation}
+          showUserLocation={this.state.hasPermission && this.settingInfo.alwaysLocation}
+          viewChargeStation={id => this.viewChargeStation(id)}
         />
-        <View style={styles.mapContent}>
-          {/* this.state.hasPermission &&*/}
-          <Maps.Maps3
-            region={this.state.region}
-            stopList={this.state.stopList}
-            onMapReady={() => {
-              this.setState({
-                mapReady: true
-              }, () => {
-                this.checkPermission2Geo();
-              });
-            }}
-            onMarkerPress={id => this.viewChargeStation(id)}
-            showUserLocation={this.state.hasPermission && this.settingInfo.alwaysLocation}
-          />
-          <BottomSiteInfo
-            stationInfo={this.state.stationInfo}
-            onFavorite={() => this.favoriteSite()}
-          />
-          <LocationPermission.VIEW visible={this.state.permissionDenied}/>
-        </View>
+        <LocationPermission.VIEW visible={this.state.permissionDenied}/>
         <View style={styles.drawerLeftTouchView}></View>
       </View>
     );
@@ -471,38 +439,6 @@ export default class HomePage extends Component {
 }
 
 const styles = StyleSheet.create({
-  logoView: {
-    /*left: 0,
-    right: 0,
-    bottom: 0,
-    zIndex: 1,
-    alignItems: 'center',
-    position: 'absolute',*/
-    flex: 1,
-    alignItems: 'center',
-    justifyContent: 'center'
-  },
-  searchView: {
-    ...$padding(8, 16, 16),
-    backgroundColor: colorThemes
-  },
-  searchInput: {
-    alignItems: 'center',
-    borderWidth: 1,
-    borderStyle: 'solid',
-    borderRadius: 60,
-    borderColor: colorAccent,
-    flexDirection: 'row',
-    paddingLeft: 16,
-    paddingRight: 16,
-    backgroundColor: 'rgba(255, 255, 255, 0.5)'
-  },
-  searchText: {
-    flex: 1,
-    color: '#444',
-    padding: 8,
-    fontSize: 15
-  },
   drawerLeftTouchView: {
     top: 0,
     left: 0,
@@ -511,10 +447,5 @@ const styles = StyleSheet.create({
     zIndex: 5,
     position: 'absolute',
     backgroundColor: 'rgba(0,0,0,0)'
-  },
-  mapContent: {
-    flex: 1,
-    zIndex: 4,
-    position: 'relative',
   }
 })

+ 128 - 0
Strides-APP/app/pages/home/MapUI.js

@@ -0,0 +1,128 @@
+/**
+ * 首页地图定制布局
+ * @邠心vbe on 2023/09/01
+ */
+import React from 'react';
+import { View, Pressable, Image, StyleSheet } from 'react-native';
+import { Styles } from '../../components/Toolbar';
+import { PageList } from '../Router';
+import BottomSiteInfo from './maps/BottomSiteInfo';
+import Maps from './maps/Maps';
+import SearchTool from './maps/SearchTool';
+
+export default MapUI = ({
+  state,
+  navigation,
+  onFilter,
+  onMapReady,
+  onFavorite,
+  onLocation,
+  showUserLocation,
+  viewChargeStation
+}) => {
+  return (
+    <View style={ui.flex1}>
+      <View style={Styles.toolbar}>
+        <Pressable
+          style={styles.navIcon}
+          android_ripple = {rippleLess}
+          onPress={() => {
+            navigation?.toggleDrawer();
+          }}>
+          <EvilIcons
+            name={'navicon'}
+            size={28}
+            color={colorPrimary}
+            style={{width: 28, height: 28, lineHeight: 28}} />
+        </Pressable>
+        <View style={styles.logoView}>
+          <Image
+            source={require('../../images/tool-logo.png')}
+            style={Styles.logo}
+            resizeMode="contain"
+          />
+        </View>
+        {/* <Text style={ui.flex1}></Text> */}
+        <Pressable
+          style={styles.navIcon}
+          android_ripple = {rippleLess}
+          onPress={() => startPage(PageList.scanqr, {actionDetail: true})}>
+          <MaterialCommunityIcons
+            name='line-scan'
+            size={20}
+            color={colorPrimary}
+          />
+        </Pressable>
+      </View>
+      <SearchTool
+        count={state.stopList?.length}
+        mapReady={state.mapReady}
+        onFilter={onFilter}
+        onLocation={onLocation}
+        navigation={navigation}
+      />
+      <View style={styles.mapContent}>
+        <Maps.Maps3
+          region={state.region}
+          stopList={state.stopList}
+          onMapReady={onMapReady}
+          onMarkerPress={viewChargeStation}
+          showUserLocation={showUserLocation}
+        />
+        <BottomSiteInfo
+          stationInfo={state.stationInfo}
+          onFavorite={onFavorite}
+        />
+      </View>
+    </View>
+  );
+}
+
+const styles = StyleSheet.create({
+  logoView: {
+    /*left: 0,
+    right: 0,
+    bottom: 0,
+    zIndex: 1,
+    alignItems: 'center',
+    position: 'absolute',*/
+    flex: 1,
+    alignItems: 'center',
+    justifyContent: 'center'
+  },
+  navIcon: {
+    width: 42,
+    height: 42,
+    marginLeft: 4,
+    marginRight: 2,
+    alignItems: 'center',
+    flexDirection: 'row',
+    justifyContent: 'center'
+  },
+  searchView: {
+    ...$padding(8, 16, 16),
+    backgroundColor: colorThemes
+  },
+  searchInput: {
+    alignItems: 'center',
+    borderWidth: 1,
+    borderStyle: 'solid',
+    borderRadius: 60,
+    borderColor: colorAccent,
+    flexDirection: 'row',
+    paddingLeft: 16,
+    paddingRight: 16,
+    backgroundColor: 'rgba(255, 255, 255, 0.5)'
+  },
+  searchText: {
+    flex: 1,
+    color: '#444',
+    padding: 8,
+    fontSize: 15
+  },
+  mapContent: {
+    flex: 1,
+    zIndex: 4,
+    position: 'relative',
+  }
+})

+ 0 - 311
Strides-APP/app/pages/home/Search.js

@@ -1,311 +0,0 @@
-/**
- * 搜索页
- * @邠心vbe on 2021/04/12
- */
-import React, { Component } from 'react';
-import { View, Text, StyleSheet, TextInput, FlatList, Pressable, Linking, Image } from 'react-native';
-import apiStation from '../../api/apiStation';
-import TextRadius from '../../components/TextRadius';
-import utils from '../../utils/utils';
-import Provider from '../charge/Provider';
-import { PageList } from '../Router';
-
-export default class Search extends Component {
-  constructor(props) {
-    super(props);
-    this.state = {
-      isSearch: false,
-      searchResult: [{id:0}]
-    };
-  }
-
-  componentDidMount() {
-    this.getGeoLocation();
-  }
-
-  getGeoLocation() {
-    navigator.geolocation.getCurrentPosition(location => {
-      let latlng = {
-        lat: location.coords.latitude,
-        lng: location.coords.longitude
-      }
-      this.searchStation(latlng);
-    }, error => {
-      console.warn("getGeoLocation", error);
-    });
-  }
-
-  searchStation(latlng) {
-    this.setState({
-      isSearch: true
-    });
-    latlng.siteName = this.searchWorld
-    apiStation.searchStation(latlng).then(res => {
-      if (res.data.sites) {
-        const list = [];
-        res.data.sites.forEach(item => {
-          list.push({
-            id: item.sitePk,
-            name: item.siteName,
-            address: item.siteAddress,
-            latitude: item.locationLatitude,
-            longitude: item.locationLongitude,
-            acConnector: item.acConnector,
-            allConnector: item.allConnector,
-            dcConnector: item.dcConnector,
-            siteType: item.siteType,
-            distance: utils.getDistance(item.distance),
-            serviceProvider: item.serviceProvider
-          });
-        });
-        this.setState({
-          isSearch: false,
-          searchResult: list
-        });
-      }
-    }).catch(err => {
-      console.log('err', err);
-      this.setState({
-        isSearch: false,
-        searchResult: []
-      });
-    });
-  }
-
-  intoStation(info) {
-    startPage(PageList.chargeDetail, {stationInfo: info, action: 'search'});
-  }
-
-  listItem = ({item, index, separators}) => {
-    if (item.id) {
-      return (
-        <View 
-          style={styles.itemView}
-          key={index}>
-          <Pressable
-            style={styles.stationInfo}
-            onPress={() => this.intoStation(item)}>
-            <View style={styles.nameView}>
-              <Text style={styles.stationName}>{item.name}</Text>
-              { item.allConnector && item.allConnector.available > 0 &&
-                <TextRadius style={[styles.infoStatus, styles.available]}>Available</TextRadius>
-              }
-            </View>
-            <Provider providers={item.serviceProvider}/>
-            <Text style={styles.stationAddress}>{item.address}</Text>
-            <View style={styles.connectView}>
-              <ConnectType {...item.acConnector}/>
-              <ConnectType {...item.dcConnector}/>
-            </View>
-          </Pressable>
-          <Pressable 
-            style={styles.directView}
-            onPress={() => {
-              utils.directMaps(item.latitude, item.longitude, item.address);
-            }}>
-            <MaterialIcons
-              name='directions'
-              size={32}
-              color={colorAccent}/>
-            <Text style={styles.distanceText}>{item.distance}</Text>
-          </Pressable>
-        </View>
-      );
-    } else {
-      return null;
-    }
-  }
-
-  render() {
-    return (
-      <View style={styles.container}>
-        <View style={styles.searchView}>
-          <Feather
-            name={'search'}
-            size={20}
-            color={'#999'}/>
-          <TextInput
-            style={styles.searchInput}
-            autoFocus={true}
-            maxLength={50}
-            numberOfLines={1}
-            returnKeyType={'search'}
-            clearButtonMode={'while-editing'}
-            placeholder='Search using location name or service provider'
-            placeholderTextColor={textPlacehoder}
-            onChangeText={text => {
-              this.searchWorld = text;
-            }}
-            onSubmitEditing={() => {
-              this.getGeoLocation();
-            }}/>
-        </View>
-        { this.state.isSearch
-        ? <View style={styles.searchingView}>
-            <Image
-              style={styles.seachingIcon}
-              source={require('../../images/icon/loading.gif')}/>
-          </View>
-        : <FlatList
-            style={styles.listView}
-            data={this.state.searchResult}
-            renderItem={this.listItem}
-            keyExtractor={item => item.id}
-            ListEmptyComponent={<Text style={styles.noResult}>No search result</Text>}
-          />
-        }
-      </View>
-    );
-  }
-}
-
-export const ConnectType = ({type, available, all}) => {
-  if (type) {
-    return (
-      <View style={styles.connectType}>
-        <Text style={styles.typeLabel}>{type}</Text>
-        <Text style={styles.typeContent}><Text style={styles.typeBold}>{available}</Text>/{all}</Text>
-      </View>
-    );
-  } else {
-    return null;
-  }
-}
-
-const styles = StyleSheet.create({
-  container: {
-    flex: 1,
-    backgroundColor: 'white'
-  },
-  searchView: {
-    marginTop: 16,
-    marginLeft: 16,
-    marginRight: 16,
-    marginBottom: 8,
-    paddingLeft: 16,
-    paddingRight: 16,
-    borderRadius: 60,
-    alignItems: 'center',
-    flexDirection: 'row',
-    backgroundColor: '#F5F5F5'
-  },
-  searchInput: {
-    flex: 1,
-    color: '#333',
-    ...$padding(6, 8),
-    fontSize: 15,
-    marginLeft: 4,
-  },
-  searchingView: {
-    padding: 16,
-    alignItems: 'center'
-  },
-  seachingIcon: {
-    width: 60,
-    height: 60
-  },
-  noResult: {
-    color: '#999',
-    fontSize: 14,
-    padding: 20,
-    textAlign: 'center',
-  },
-  listView: {
-    flex: 1
-  },
-  itemView: {
-    alignItems: 'center',
-    flexDirection: 'row',
-    borderBottomWidth: 1,
-    borderBottomColor: '#eee'
-  },
-  stationInfo: {
-    flex: 1,
-    padding: 16
-  },
-  nameView: {
-    paddingTop: 3,
-    alignItems: 'center',
-    flexDirection: 'row'
-  },
-  stationName: {
-    color: '#333',
-    fontSize: 18,
-    fontWeight: 'bold'
-  },
-  stationAddress: {
-    color: '#666',
-    fontSize: 14,
-    paddingBottom: 8
-  },
-  infoStatus: {
-    fontSize: 10,
-    paddingTop: 3,
-    paddingLeft: 8,
-    paddingRight: 8,
-    paddingBottom: 3,
-    borderRadius: 5,
-    marginLeft: 12,
-  },
-  selected: {
-    color: '#333',
-    backgroundColor: colorAccent
-  },
-  available: {
-    color: 'white',
-    backgroundColor: '#90DB0A'
-  },
-  unavailable: {
-    color: '#999',
-    fontSize: 10.5,
-    paddingTop: 7,
-    paddingLeft: 9,
-    paddingRight: 9,
-    paddingBottom: 7,
-    backgroundColor: '#CCC'
-  },
-  connectView: {
-    paddingTop: 4,
-    paddingBottom: 4,
-    alignItems: 'center',
-    flexDirection: 'row'
-  },
-  connectType: {
-    borderWidth: 1,
-    borderColor: '#333',
-    borderRadius: 3,
-    marginRight: 16,
-    alignItems: 'center',
-    flexDirection: 'row',
-  },
-  typeLabel: {
-    color: '#fff',
-    fontSize: 12,
-    paddingTop: 2,
-    paddingLeft: 10,
-    paddingRight: 10,
-    paddingBottom: 2,
-    backgroundColor: '#333'
-  },
-  typeContent: {
-    color: '#333',
-    fontSize: 12,
-    paddingLeft: 10,
-    paddingRight: 6,
-  },
-  typeBold: {
-    fontSize: 14,
-    fontWeight: 'bold'
-  },
-  directView: {
-    zIndex: 1,
-    paddingTop: 4,
-    paddingRight: 16,
-    alignItems: 'center'
-  },
-  distanceText: {
-    color: '#333',
-    fontSize: 12,
-    paddingTop: 2
-  },
-});

+ 14 - 13
Strides-APP/app/pages/home/maps/BottomSiteInfo.js

@@ -10,6 +10,7 @@ import { ChargeStyle } from '../../charge/Charging';
 import { PageList } from '../../Router';
 import ConnectType from '../../search/ConnectType';
 import app from '../../../../app.json';
+import TextView from '../../../components/TextView';
 
 export default BottomSiteInfo = ({stationInfo = {}, onFavorite}) => {
   const getAvailable = (type) => {
@@ -57,14 +58,14 @@ export default BottomSiteInfo = ({stationInfo = {}, onFavorite}) => {
             style={styles.stationInfo}
             //onPress={() => startPage(PageList.chargeDetail, {stationInfo: stationInfo, action: 'view'})}}
             onPress={toChargePage}>
-            <Text
+            <TextView
               ellipsizeMode='tail'
               numberOfLines={1}
-              style={styles.stationTitle}>{stationInfo.name}</Text>
-            <Text
+              style={styles.stationTitle}>{stationInfo.name}</TextView>
+            <TextView
               style={styles.stationAddress}
               ellipsizeMode='tail'
-              numberOfLines={2}>{stationInfo.address}</Text>
+              numberOfLines={2}>{stationInfo.address}</TextView>
           </Pressable>
           {/* <View style={styles.stationAvailable}>
             <Image
@@ -113,9 +114,9 @@ export default BottomSiteInfo = ({stationInfo = {}, onFavorite}) => {
           </View>
           <View style={styles.stationStatusView}>
             { (stationInfo.allConnector && stationInfo.allConnector.available > 0) &&
-              <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable, styles.stationStatus]}>{$t("charging.statusAvailable")}</Text>
+              <TextView style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable, styles.stationStatus]}>{$t("charging.statusAvailable")}</TextView>
             }
-            <Text style={[ChargeStyle.infoStatus, stationInfo.siteType == 'Public' ? ChargeStyle.statusAvailable : ChargeStyle.statusWarning, styles.siteTypes]}>{stationInfo.siteType}</Text>
+            <TextView style={[ChargeStyle.infoStatus, stationInfo.siteType == 'Public' ? ChargeStyle.statusAvailable : ChargeStyle.statusWarning, styles.siteTypes]}>{stationInfo.siteType}</TextView>
           </View>
         </View>
         <View style={styles.divideLine}></View>
@@ -123,23 +124,23 @@ export default BottomSiteInfo = ({stationInfo = {}, onFavorite}) => {
           style={styles.infoDetailsView}
           onPress={toChargePage}>
           <View style={ui.flex1}>
-            <Text style={styles.infoTitle}>{$t("charging.operatingHours")}</Text>
+            <TextView style={styles.infoTitle}>{$t("charging.operatingHours")}</TextView>
             <View style={styles.infoView}>
-              <Text style={styles.infoText}>{getOperatingHours()}</Text>
+              <TextView style={styles.infoText}>{getOperatingHours()}</TextView>
             </View>
           </View>
           <View style={{width: 4}}></View>
           <View style={ui.flex1}>
-            <Text style={styles.infoTitle}>{$t("charging.parkingFees")}</Text>
+            <TextView style={styles.infoTitle}>{$t("charging.parkingFees")}</TextView>
             <View style={styles.infoView}>
-              <Text style={styles.infoText}>{getParkingFee()}</Text>
+              <TextView style={styles.infoText}>{getParkingFee()}</TextView>
             </View>
           </View>
           <View style={{width: 4}}></View>
           <View style={ui.flex1}>
-            <Text style={styles.infoTitle}>{$t("charging.additionalInfo")}</Text>
+            <TextView style={styles.infoTitle}>{$t("charging.additionalInfo")}</TextView>
             <View style={styles.infoView}>
-              <Text style={styles.infoText}>{stationInfo?.additionalNotes}</Text>
+              <TextView style={styles.infoText}>{stationInfo?.additionalNotes}</TextView>
             </View>
           </View>
         </Pressable>
@@ -172,7 +173,7 @@ const styles = StyleSheet.create({
   stationTitle: {
     color: '#000',
     fontSize: 16,
-    paddingBottom: 4
+    paddingBottom: 8
   },
   stationAddress: {
     color: '#999',

+ 23 - 15
Strides-APP/app/pages/home/maps/Filter.js

@@ -6,6 +6,7 @@ import React from 'react'
 import { Image, Pressable, StyleSheet, Text, TouchableOpacity, TouchableOpacityBase, View } from 'react-native'
 import BadgeSelectItem from '../../../components/BadgeSelectItem';
 import Button, { ElevationObject } from '../../../components/Button';
+import TextView from '../../../components/TextView';
 import { TypeImageList } from '../../charge/Charging';
 
 class Filter extends React.Component {
@@ -15,13 +16,13 @@ class Filter extends React.Component {
       count: 0,
       connectorType: TypeImageList,
       parkingFee: [{
-        name: 'All',
+        name: $t('home.all'),
         value: 'ALL'
       },{
-        name: 'Free',
+        name: $t('home.free'),
         value: 'FREE'
       },{
-        name: 'Chargeable',
+        name: $t('home.chargeable'),
         value: 'CHARGEABLE'
       }],
       activationType: [/*'SCAN QR', 'RFID Card needed', 'Reservation Required'*/],
@@ -110,7 +111,7 @@ class Filter extends React.Component {
     return (
       <View style={styles.filterView}>
         <View style={ui.flexc}>
-          <Text style={styles.titleText}>Filters</Text>
+          <TextView style={styles.titleText}>{$t('home.filters')}</TextView>
           {/* <Pressable
             android_ripple = {rippleLess}
             onPress={() => {
@@ -122,7 +123,7 @@ class Filter extends React.Component {
           </Pressable> */}
         </View>
         {/* Connector Type */}
-        <Text style={styles.labelText}>Choose Connector Type</Text>
+        <TextView style={styles.labelText}>{$t('home.chooseConnecterType')}</TextView>
         <View style={styles.connectorView}>
           { this.state.connectorType.map((item, index) => {
               return (
@@ -135,7 +136,7 @@ class Filter extends React.Component {
                   <Image 
                     style={styles.typeIcon}
                     source={item.icon}/>
-                  <Text style={styles.typeName}>{item.name}</Text>
+                  <TextView style={styles.typeName}>{item.nameScope ? $t(item.nameScope) : item.name}</TextView>
                 </BadgeSelectItem>
               )
             })
@@ -144,7 +145,7 @@ class Filter extends React.Component {
         { /* Choose Parking Fee */
           this.state.parkingFee.length > 0 &&
           <>
-            <Text style={styles.labelText}>Choose Parking Fee</Text>
+            <TextView style={styles.labelText}>{$t('home.chooseParkingFee')}</TextView>
             <View style={styles.activationView}>
               { this.state.parkingFee.map((item, index) => {
                 return (
@@ -159,7 +160,7 @@ class Filter extends React.Component {
                     onPressOut={() => {
                       this.selectParkingFee(index);
                     }}>
-                      <Text style={styles.typeText}>{item.name}</Text>
+                      <TextView style={styles.typeText}>{item.name}</TextView>
                     </TouchableOpacity>
                 )
               })}
@@ -169,7 +170,7 @@ class Filter extends React.Component {
         { /* Activation Type */
           this.state.activationType.length > 0 &&
           <>
-            <Text style={styles.labelText}>Choose Activation Type</Text>
+            <TextView style={styles.labelText}>{$t('home.chooseActivationType')}</TextView>
             <View style={styles.activationView}>
               { this.state.activationType.map((item, index) => {
                 return (
@@ -184,7 +185,7 @@ class Filter extends React.Component {
                     onPress={() => {
                       this.selectActivation(index);
                     }}>
-                    <Text style={styles.typeText}>{item}</Text>
+                    <TextView style={styles.typeText}>{item}</TextView>
                   </TouchableOpacity>
                 )
               })}
@@ -194,7 +195,7 @@ class Filter extends React.Component {
         { /* Provider Type */
           this.state.providerType.length > 0 &&
           <>
-            <Text style={styles.labelText}>Choose Provider</Text>
+            <TextView style={styles.labelText}>{$t('home.chooseProvider')}</TextView>
             <View style={styles.activationView}>
             { this.state.providerType.map((item, index) => {
                 return (
@@ -209,7 +210,7 @@ class Filter extends React.Component {
                     onPress={() => {
                       this.selectProvidor(index);
                     }}>
-                    <Text style={styles.typeText}>{item}</Text>
+                    <TextView style={styles.typeText}>{item}</TextView>
                   </TouchableOpacity>
                 )
               })}
@@ -218,16 +219,17 @@ class Filter extends React.Component {
         }
         <View style={styles.bottomView}>
           { this.state.count > 0 &&
-            <Text style={{
+            <TextView style={{
               color: '#000',
               fontSize: 15,
               fontWeight: 'bold',
               marginRight: 16
-            }}>{this.state.count} Results</Text>
+            }}>{this.state.count} {$t('home.filterResults')}</TextView>
           }
           <Button
             viewStyle={styles.done}
-            text='Done'
+            textStyle={styles.doneText}
+            text={$t('home.done')}
             borderRadius={3}
             onClick={() => this.done()}/>
         </View>
@@ -329,5 +331,11 @@ const styles = StyleSheet.create({
     paddingRight: 14,
     alignItems: 'center',
     justifyContent: 'center'
+  },
+  doneText: {
+    color: textButton,
+    fontSize: 15,
+    fontWeight: 'bold',
+    textAlign: 'center'
   }
 })

+ 5 - 3
Strides-APP/app/pages/home/maps/LocationPermission.js

@@ -6,6 +6,7 @@ import React from 'react';
 import { StyleSheet, Text, View } from 'react-native';
 import { check, openSettings, PERMISSIONS, request, RESULTS } from 'react-native-permissions';
 import Button from '../../../components/Button';
+import TextView from '../../../components/TextView';
 
 //global.hasPermission = false;
 
@@ -84,14 +85,14 @@ const getPermission = (back) => {
 const LocationPermission = ({visible=false}) => (
   visible
   ? <View style={styles.permissionView}>
-      <Text
+      <TextView
         style={styles.permissionText}
-        numberOfLines={1}>{$t("home.locationPermissionTips")}</Text>
+        numberOfLines={1}>{$t("home.locationPermissionTips")}</TextView>
       <Button
         style={styles.viewButton}
         viewStyle={styles.viewButtonStyle}
         onClick={() => openSettings().catch(() => console.warn('cannot open settings'))}>
-        <Text style={styles.buttonText}>{$t("nav.view")}</Text>
+        <TextView style={styles.buttonText}>{$t("nav.view")}</TextView>
         <FontAwesome
           size={16}
           color={colorPrimary}
@@ -108,6 +109,7 @@ const styles = StyleSheet.create({
     right: 16,
     bottom: 32,
     opacity: .8,
+    zIndex: 5,
     overflow: 'hidden',
     borderRadius: 30,
     position: 'absolute',

+ 5 - 3
Strides-APP/app/pages/member/ApplyMember.js

@@ -13,6 +13,7 @@ import { UploadView } from '../sign/RegisterDriver';
 import ImagePicker from 'react-native-image-crop-picker';
 import CheckBoxText from '../../components/CheckBoxText';
 import Button from '../../components/Button';
+import TextView from '../../components/TextView';
 
 const options = {
   width: 300,
@@ -129,7 +130,7 @@ export default class ApplyMember extends Component {
       <View style={styles.container}>
         <View style={styles.applyForm}>
           <View style={styles.formItem}>
-            <Text style={styles.inputLabel}>{$t('members.membership')}</Text>
+            <TextView style={styles.inputLabel}>{$t('members.membership')}</TextView>
             <Dropdown
               style={styles.selectView}
               textStyle={styles.selectText}
@@ -143,9 +144,10 @@ export default class ApplyMember extends Component {
               }}/>
           </View>
           <View style={styles.formItem}>
-            <Text style={styles.inputLabel}>{$t('members.membershipNo')}</Text>
+            <TextView style={styles.inputLabel}>{$t('members.membershipNo')}</TextView>
             <TextInput
               style={styles.inputView}
+              allowFontScaling={false}
               placeholder={$t('members.membershipNo')}
               placeholderTextColor={textPlacehoder}
               maxLength={50}
@@ -154,7 +156,7 @@ export default class ApplyMember extends Component {
             />
           </View>
           <View style={styles.formItem}>
-            <Text style={styles.inputLabel}>{$t('members.labelUpload')}</Text>
+            <TextView style={styles.inputLabel}>{$t('members.labelUpload')}</TextView>
             <View style={styles.uploadGroup}>
               <UploadView
                 style={styles.uploadView}

+ 9 - 9
Strides-APP/app/pages/member/MembersList.js

@@ -6,7 +6,7 @@ import React, { Component } from 'react';
 import { View, Text, StyleSheet, Pressable } from 'react-native';
 import apiMember from '../../api/apiMember';
 import { ElevationObject } from '../../components/Button';
-import TextRadius from '../../components/TextRadius';
+import TextView from '../../components/TextView';
 import utils from '../../utils/utils';
 
 export default class MembersList extends Component {
@@ -73,20 +73,20 @@ export default class MembersList extends Component {
           </View>
           <View style={styles.memberItem}>
             { utils.isEmpty(item.groupType)
-            ? <Text style={styles.textLabel2}>{$t('members.membership')}:</Text>
-            : <TextRadius style={styles.textType}>{item.groupType}</TextRadius>
+            ? <TextView style={styles.textLabel2}>{$t('members.membership')}:</TextView>
+            : <TextView style={styles.textType}>{item.groupType}</TextView>
             }
-            <Text
+            <TextView
               style={styles.textValue2}
-              numberOfLines={1}>{item.membership}</Text>
+              numberOfLines={1}>{item.membership}</TextView>
           </View>
           <View style={styles.memberItem}>
-            <Text style={styles.textLabel}>{$t('members.membershipNo')}:</Text>
-            <Text style={styles.textValue}>{item.membershipNo}</Text>
+            <TextView style={styles.textLabel}>{$t('members.membershipNo')}:</TextView>
+            <TextView style={styles.textValue}>{item.membershipNo}</TextView>
           </View>
           <View style={styles.memberItem}>
-            <Text style={styles.textLabel}>{$t('members.status')}:</Text>
-            <Text style={styles.textValue}>{this.getMembershipStatus(item.membershipStatus)}</Text>
+            <TextView style={styles.textLabel}>{$t('members.status')}:</TextView>
+            <TextView style={styles.textValue}>{this.getMembershipStatus(item.membershipStatus)}</TextView>
           </View>
         </Pressable>
       ))

+ 19 - 17
Strides-APP/app/pages/my/AddVehicle.js

@@ -27,15 +27,15 @@ export default class AddVehicle extends Component {
 
   validate() {
     if (!this.form.brand) {
-      toastShort('Please type brand');
+      toastShort($t('profile.msgInputBrand'));
       return;
     }
     if (!this.form.model) {
-      toastShort('Please type model');
+      toastShort($t('profile.msgInputModel'));
       return;
     }
     if (!this.form.licensePlate) {
-      toastShort('Please type license plate');
+      toastShort($t('profile.msgInputPlate'));
       return;
     }
     const params = this.form;
@@ -47,9 +47,9 @@ export default class AddVehicle extends Component {
     Dialog.showProgressDialog();
     apiUser.addVehicle(params).then(res => {
       Dialog.dismissLoading();
-      toastShort('Add vehicle successfully');
+      toastShort($t('common.addSuccess'));
       goBack();
-    }).catch(err => {
+    }).catch((err) => {
       Dialog.dismissLoading();
       toastShort(err);
     });
@@ -59,37 +59,39 @@ export default class AddVehicle extends Component {
     return (
       <View style={styles.container}>
         <View style={styles.formView}>
-          <Text style={styles.label}>Brand</Text>
+          <Text style={styles.label}>{$t('profile.vehicleBrand')}</Text>
           <TextInput
             style={styles.formInput}
-            placeholder='Add text'
-            placeholderTextColor={textPlacehoder}
+            allowFontScaling={false}
+            placeholder={$t('common.inputAddText')}
             maxLength={25}
+            placeholderTextColor={textPlacehoder}
             onChangeText={text => this.form.brand = text}
           />
         </View>
         <View style={styles.formView}>
-          <Text style={styles.label}>Model</Text>
+          <Text style={styles.label}>{$t('profile.vehicleModel')}</Text>
           <TextInput
             style={styles.formInput}
-            placeholder='Add text'
-            placeholderTextColor={textPlacehoder}
+            allowFontScaling={false}
+            placeholder={$t('common.inputAddText')}
             maxLength={50}
+            placeholderTextColor={textPlacehoder}
             onChangeText={text => this.form.model = text}
           />
         </View>
         <View style={styles.formView}>
-          <Text style={styles.label}>License Plate</Text>
+          <Text style={styles.label}>{$t('profile.licensePlate')}</Text>
           <TextInput
             style={styles.formInput}
-            placeholder='Add text'
-            placeholderTextColor={textPlacehoder}
+            placeholder={$t('common.inputAddText')}
             maxLength={20}
+            placeholderTextColor={textPlacehoder}
             onChangeText={text => this.form.licensePlate = text}
           />
         </View>
         <View style={styles.formView}>
-          <Text style={styles.label}>Choose Connecter Type</Text>
+          <Text style={styles.label}>{$t('profile.chooseConnecterType')}</Text>
           <View style={styles.connectorView}>
             { this.state.connectorType.map((item, index) => {
                 return (
@@ -102,7 +104,7 @@ export default class AddVehicle extends Component {
                     <Image
                       style={styles.typeIcon}
                       source={item.icon}/>
-                    <Text style={styles.typeName}>{item.name}</Text>
+                    <Text style={styles.typeName}>{item.nameScope ? $t(item.nameScope) : item.name}</Text>
                     {/* index==this.state.connectorIndex &&
                       <View style={styles.checkedIcon}>
                         <ChargeItemSelect size={12} selected={true}/>
@@ -117,7 +119,7 @@ export default class AddVehicle extends Component {
         <Text style={ui.flex1}></Text>
         <View style={styles.buttonView}>
           <Button
-            text='Add Vehicle'
+            text={$t('profile.addVehicle')}
             elevation={1.5}
             onClick={() => {
               this.validate();

+ 109 - 53
Strides-APP/app/pages/my/EditProfile.js

@@ -11,8 +11,9 @@ import apiUser from '../../api/apiUser';
 import { host } from '../../api/http';
 import { ModalProps } from '../../components/BottomModal';
 import Button, { ViewHeight } from '../../components/Button';
-import { CountryDropNum, GetCountryList } from '../../components/CountryIcon';
+import { CountryDropCode, CountryDropNum, GetCountryList } from '../../components/CountryIcon';
 import Dropdown from '../../components/Dropdown';
+import TextView from '../../components/TextView';
 import { UploadThemes } from '../../components/ThemesConfig';
 import utils from '../../utils/utils';
 import { DialogMaxWidth } from '../charge/InfoDialog';
@@ -70,11 +71,10 @@ const EditDialog = ({visible, title, value, keyType, countryList, areaNo, showCa
       onBackdropPress={() => onChangedText('')}
       onBackButtonPress={() => onChangedText('')}>
       <View style={styles.dialog}>
-        <Text style={styles.editNickname}>{title}</Text>
+        <TextView style={styles.editNickname}>{title}</TextView>
         <View style={ui.flexc}>
           { showCalling &&
             <Dropdown
-              title='Country'
               prefixText="+"
               list={countryList}
               value={callingCode}   
@@ -108,14 +108,14 @@ const EditDialog = ({visible, title, value, keyType, countryList, areaNo, showCa
             style={styles.btnCancel}
             viewStyle={ViewHeight(42)}
             textColor={textCancel}
-            text='Cancel'
+            text={$t('common.cancel')}
             onClick={() => {
               onChangedText('');
             }}/>
           <Button
             style={styles.btnOk}
             viewStyle={ViewHeight(42)}
-            text='Save'
+            text={$t('common.save')}
             onClick={() => {
               onChangedText(changedText, callingCode);
             }}/>
@@ -145,7 +145,7 @@ export default class EditProfile extends Component {
         visible: false
       },
       userInfo: {},
-      countryNums: []
+      countryList: []
     };
     this.editAddress = false;
   }
@@ -168,14 +168,14 @@ export default class EditProfile extends Component {
     })
     GetCountryList(list => {
       this.setState({
-        countryNums: list
+        countryList: list
       })
     })
   }
 
   updateProfile(_userInfo) {
     apiUser.setProfile(_userInfo).then(res => {
-      toastShort('Update profile successfully');
+      toastShort($t('profile.updateSuccess'));
       userInfo = _userInfo;
       this.setState({
         userInfo: _userInfo
@@ -195,39 +195,55 @@ export default class EditProfile extends Component {
     });
   }
 
+  pickAvatar() {
+    console.log("pickAvatar", $t('common.cropperTitle'));
+    ImagePicker.openPicker({
+      width: 200,
+      height: 200,
+      ...options,
+      cropperToolbarTitle: $t('common.cropperTitle')
+    }).then(image => {
+      if (image.path) {
+        this.uploadAvatar(image)
+      }
+    }).catch(errs => {
+      console.info("pickAvatarError", errs);
+    });
+  }
+
+  uploadAvatar(image) {
+    apiUpload.uploadImage(image.path, image.mime, 'USER_PROFILE').then(res => {
+      if (res.success && res.data.picturePath) {
+        //this.canUpdate = true;
+        this.state.userInfo.photoUrl = res.data.picturePath
+        /*this.setState({
+          userInfo: userInfo
+        });*/
+        this.updateProfile(this.state.userInfo);
+      } else {
+        toastShort($t('common.uploadFailed'));
+      }
+    }).catch(err => {
+      toastShort(err);
+    });
+  }
+
+  changeCountry(value, index) {
+    if (this.state.userInfo.countryCode !== value) {
+      var info = Object.assign({}, userInfo);
+      info.countryCode = value;
+      this.updateProfile(info)
+    }
+  }
+
   render() {
     return (
       <View style={styles.container}>
         <Pressable
           style={styles.profileView}
           android_ripple={ripple}
-          onPress={() => {
-            ImagePicker.openPicker({
-              width: 200,
-              height: 200,
-              ...options
-            }).then(image => {
-              if (image.path) {
-                apiUpload.uploadImage(image.path, image.mime, 'USER_PROFILE').then(res => {
-                  if (res.success && res.data.picturePath) {
-                    //this.canUpdate = true;
-                    this.state.userInfo.photoUrl = res.data.picturePath
-                    /*this.setState({
-                      userInfo: userInfo
-                    });*/
-                    this.updateProfile(this.state.userInfo);
-                  } else {
-                    toastShort('Upload failed, please retry');
-                  }
-                }).catch(err => {
-                  toastShort(err);
-                });
-              }
-            }).catch(err => {
-
-            });
-          }}>
-          <Text style={styles.label}>Avatar</Text>
+          onPress={() => this.pickAvatar()}>
+          <TextView style={styles.label}>{$t('profile.avatar')}</TextView>
           <Text style={ui.flex1}></Text>
           { userInfo.photoUrl
           ? <Image
@@ -247,17 +263,41 @@ export default class EditProfile extends Component {
             this.setState({
               editDialog: {
                 key: 'nickName',
-                title: 'Update Nickname',
+                title: $t('profile.updateNickname'),
                 value: this.state.userInfo.nickName,
                 visible: true
               }
             });
           }}>
-          <Text style={styles.label}>Nickname</Text>
-          <Text style={styles.infoText}>{this.state.userInfo.nickName}</Text>
+          <TextView style={styles.label}>{$t('profile.nickname')}</TextView>
+          <TextView style={styles.infoText}>{this.state.userInfo.nickName}</TextView>
           <ArrowRight/>
         </Pressable>
         <Divide/>
+        <View style={styles.profileView}>
+          <TextView style={styles.label}>{$t('sign.labelCountry')}</TextView>
+          <TextView style={styles.infoText}></TextView>
+          <ArrowRight/>
+          <Dropdown
+            style={styles.selectView}
+            title={$t('sign.labelCountry')}
+            list={this.state.countryList}
+            value={this.state.userInfo.countryCode}
+            nameKey='countryName'
+            valueKey='countryCode'
+            showIcon={false}
+            textStyle={styles.rightText}
+            onChange={(value, index)=> this.changeCountry(value, index)}
+            customerItemView={
+              (item, index, onClick) => 
+              <CountryDropCode
+                key={index} 
+                country={item}
+                value={this.state.userInfo.countryCode}
+                onClick={onClick}/>
+            }/>
+        </View>
+        <Divide/>
         <Pressable
           style={styles.profileView}
           android_ripple={ripple}
@@ -267,14 +307,14 @@ export default class EditProfile extends Component {
                 key: 'phone',
                 visible: true,
                 showCalling: true,
-                title: 'Update Phone Number',
+                title: $t('profile.updatePhoneNumber'),
                 value: this.state.userInfo.phone,
                 areaNo: this.state.userInfo.callingCode
               }
             });
           }}>
-          <Text style={styles.label}>Phone Number</Text>
-          <Text style={styles.infoText}>{(utils.isNotEmpty(this.state.userInfo.callingCode) && "+" + this.state.userInfo.callingCode)} {this.state.userInfo.phone}</Text>
+          <TextView style={styles.label}>{$t('profile.phoneNumber')}</TextView>
+          <TextView style={styles.infoText}>{(utils.isNotEmpty(this.state.userInfo.callingCode) && "+" + this.state.userInfo.callingCode)} {this.state.userInfo.phone}</TextView>
           <ArrowRight/>
         </Pressable>
         <Divide/>
@@ -282,8 +322,8 @@ export default class EditProfile extends Component {
           style={[styles.profileView, {
             paddingRight: 48
           }]}>
-          <Text style={styles.label}>Email</Text>
-          <Text style={styles.infoText}>{userInfo.email}</Text>
+          <TextView style={styles.label}>{$t('profile.email')}</TextView>
+          <TextView style={styles.infoText}>{userInfo.email}</TextView>
           {/*<ArrowRight/>*/}
         </Pressable>
         <Divide/>
@@ -302,8 +342,8 @@ export default class EditProfile extends Component {
             this.editAddress = true;
             startPage(PageList.editAddress);
           }}>
-          <Text style={styles.label}>Address</Text>
-          <Text style={styles.infoText}>{userInfo.addressLine}</Text>
+          <TextView style={styles.label}>{$t('profile.address')}</TextView>
+          <TextView style={styles.infoText}>{userInfo.addressLine}</TextView>
           <ArrowRight/>
         </Pressable>
         <Divide/>
@@ -311,8 +351,8 @@ export default class EditProfile extends Component {
           style={[styles.profileView, {
             paddingRight: 48
           }]}>
-          <Text style={styles.label}>User Type</Text>
-          <Text style={styles.infoText}>{userInfo.userType}</Text>
+          <TextView style={styles.label}>{$t('profile.userType')}</TextView>
+          <TextView style={styles.infoText}>{userInfo.userType}</TextView>
           {/*<ArrowRight/>*/}
         </Pressable>
         <Divide/>
@@ -320,8 +360,8 @@ export default class EditProfile extends Component {
           style={[styles.profileView, {
             paddingRight: 48
           }]}>
-          <Text style={styles.label}>Registration Date</Text>
-          <Text style={styles.infoText}>{"-"}</Text>
+          <TextView style={styles.label}>{$t('profile.registerDate')}</TextView>
+          <TextView style={styles.infoText}>{"-"}</TextView>
           {/*<ArrowRight/>*/}
         </Pressable>
         <EditDialog
@@ -329,7 +369,7 @@ export default class EditProfile extends Component {
           visible={this.state.editDialog.visible}
           value={this.state.editDialog.value}
           areaNo={this.state.editDialog.areaNo}
-          countryList={this.state.countryNums}
+          countryList={this.state.countryList}
           showCalling={this.state.editDialog.showCalling}
           keyType={this.state.editDialog.key == 'phone' ? 'phone-pad' : 'default'}
           onChangedText={(text, areaNo) => {
@@ -337,7 +377,7 @@ export default class EditProfile extends Component {
               var info = Object.assign({}, userInfo);
               if (this.state.editDialog.key == 'phone') {
                 if (!/^[0-9\+]\d{6,15}$/.test(text)) {
-                  toastShort('Phone Number is incorrect format');
+                  toastShort($t('profile.errPhoneNumberFormat'));
                   return;
                 }
                 info.callingCode = areaNo;
@@ -439,12 +479,28 @@ const styles = StyleSheet.create({
     marginRight: 8,
     minHeight: 43,
     borderRadius: 4,
+    alignItems: 'center',
     flexDirection: 'row',
+    ...$padding(0, 5, 0, 12),
     backgroundColor: '#F6F6F6'
   },
+  selectView: {
+    top: 0,
+    left: 0,
+    bottom: 0,
+    width: $vw(100),
+    alignItems: 'center',
+    flexDirection: 'row',
+    position: 'absolute',
+  },
   selectText: {
     color: textPrimary,
-    fontSize: 15,
-    ...$padding(11, 10),
+    fontSize: 15
+  },
+  rightText: {
+    right: 48,
+    color: textPrimary,
+    fontSize: 14,
+    position: 'absolute'
   },
 });

+ 13 - 13
Strides-APP/app/pages/my/ProfileV2.js

@@ -8,6 +8,7 @@ import apiUser from '../../api/apiUser';
 import { host, setAccessToken } from '../../api/http';
 import Button, { ElevationObject } from '../../components/Button';
 import Dialog from '../../components/Dialog';
+import TextView from '../../components/TextView';
 import { getStorageJsonSync, setStorage, setStorageJson } from '../../utils/storage';
 import utils from '../../utils/utils';
 import { PageList } from '../Router';
@@ -116,12 +117,12 @@ export default class ProfileV2 extends Component {
               source={require('../../images/user/ic-avatar-default.png')}/>
           }
           <View style={styles.infoContent}>
-            <Text
+            <TextView
               style={styles.nickname}
               ellipsizeMode='tail'
-              numberOfLines={1}>{this.state.userInfo.nickName}</Text>
-            <Text style={styles.userText}>{this.state.userInfo.email}</Text>
-            <Text style={styles.userText}>{(utils.isNotEmpty(this.state.userInfo.callingCode) && "+" + this.state.userInfo.callingCode + " ") + this.state.userInfo.phone}</Text>
+              numberOfLines={1}>{this.state.userInfo.nickName}</TextView>
+            <TextView style={styles.userText}>{this.state.userInfo.email}</TextView>
+            <TextView style={styles.userText}>{(utils.isNotEmpty(this.state.userInfo.callingCode) && "+" + this.state.userInfo.callingCode + " ") + this.state.userInfo.phone}</TextView>
           </View>
           <FontAwesome
             size={34}
@@ -137,8 +138,8 @@ export default class ProfileV2 extends Component {
             style={styles.cardIcon}
             source={require('../../images/user/card-wallet.png')}/>
           <View style={styles.cardInfo}>
-            <Text style={styles.cardLabel}>{$t('wallet.creditWalletLabel')}</Text>
-            <Text style={styles.cardPrimary}>{currency}{this.state.userInfo.credit}</Text>
+            <TextView style={styles.cardLabel}>{$t('wallet.creditWalletLabel')}</TextView>
+            <TextView style={styles.cardPrimary}>{this.state.userInfo.creditStr}</TextView>
           </View>
           <FontAwesome
             size={28}
@@ -154,8 +155,8 @@ export default class ProfileV2 extends Component {
             style={styles.cardIcon}
             source={require('../../images/user/card-vehicle.png')}/>
           <View style={styles.cardInfo}>
-            <Text style={styles.cardLabel}>{$t('profile.myVehicles')}</Text>
-            <Text style={styles.cardPrimary}>{this.state.userInfo.countVehicle}</Text>
+            <TextView style={styles.cardLabel}>{$t('profile.myVehicles')}</TextView>
+            <TextView style={styles.cardPrimary}>{this.state.userInfo.countVehicle}</TextView>
           </View>
           <FontAwesome
             size={28}
@@ -211,7 +212,7 @@ export default class ProfileV2 extends Component {
             style={styles.cardIcon}
             source={require('../../images/user/card-account.png')}/>
           <View style={styles.cardInfo}>
-            <Text style={styles.cardLabel}>{$t('route.changePassword')}</Text>
+            <TextView style={styles.cardLabel}>{$t('route.changePassword')}</TextView>
           </View>
           <FontAwesome
             size={28}
@@ -227,7 +228,7 @@ export default class ProfileV2 extends Component {
             style={styles.cardIcon}
             source={require('../../images/user/card-notification.png')}/>
           <View style={styles.cardInfo}>
-            <Text style={styles.cardLabel}>{$t('profile.notificationSettings')}</Text>
+            <TextView style={styles.cardLabel}>{$t('profile.notificationSettings')}</TextView>
           </View>
           <FontAwesome
             size={28}
@@ -263,7 +264,7 @@ export default class ProfileV2 extends Component {
             size={32}
             color="#00638C"/>
           <View style={styles.cardInfo}>
-            <Text style={styles.cardLabel}>{$t('profile.deleteAccount')}</Text>
+            <TextView style={styles.cardLabel}>{$t('profile.deleteAccount')}</TextView>
           </View>
           <FontAwesome
             size={28}
@@ -372,8 +373,7 @@ const styles = StyleSheet.create({
   },
   cardPrimary: {
     color: textPrimary,
-    fontSize: 18,
-    paddingBottom: 2
+    fontSize: 18
   },
   cardLabel: {
     color: textPrimary,

+ 4 - 3
Strides-APP/app/pages/my/VehicleList.js

@@ -7,6 +7,7 @@ import { View, Text, Image, StyleSheet, Pressable } from 'react-native';
 import apiUser from '../../api/apiUser';
 import { ElevationObject } from '../../components/Button';
 import Dialog from '../../components/Dialog';
+import TextView from '../../components/TextView';
 import VehicleType from '../../icons/VehicleType';
 import { PageList } from '../Router';
 
@@ -109,15 +110,15 @@ export default class VehicleList extends Component {
               <Image
                 style={styles.vehicleIcon}
                 source={require('../../images/user/ic-vehicle-model.png')}/>
-              <Text style={styles.vehicleName}>{item.brand} {item.model}</Text>
+              <TextView style={styles.vehicleName}>{item.brand} {item.model}</TextView>
             </View>
-            <Text style={styles.vehicleModle}>{item.licensePlate}</Text>
+            <TextView style={styles.vehicleModle}>{item.licensePlate}</TextView>
             <View style={styles.vehicleRow}>
               <View style={styles.vehicleTypeRow}>
                 <View style={styles.vehicleTypeIcon}>
                   <VehicleType size={10}/>
                 </View>
-                <Text style={styles.vehicleType}>TYPE {item.connectorType}</Text>
+                <TextView style={styles.vehicleType}>TYPE {item.connectorType}</TextView>
               </View>
               {/* <Image
                 style={styles.vehicleIcon}

+ 27 - 27
Strides-APP/app/pages/payment/FormCard.js

@@ -3,7 +3,7 @@
  * @邠心vbe on 2021/06/09
  */
 import React, { Component } from 'react';
-import { View, Text, StyleSheet, TextInput, Linking, ScrollView, KeyboardAvoidingView } from 'react-native';
+import { View, Text, StyleSheet, TextInput, KeyboardAvoidingView } from 'react-native';
 import Button from '../../components/Button';
 import apiWallet from '../../api/apiWallet';
 import Dialog from '../../components/Dialog';
@@ -33,31 +33,31 @@ export default class FormCard extends Component {
 
   validate() {
     if (!this.form.nameOnCard) {
-      toastShort('Please type name on card');
+      toastShort($t('payment.plsInputCardName'));
       return;
     }
     if (!this.form.cardNumber) {
-      toastShort('Please type card number');
+      toastShort($t('payment.plsInputCardNo'));
       return;
     }
     if (!/^\d{9,}$/.test(this.form.cardNumber)) {
-      toastShort('Please type a correct card number');
+      toastShort($t('payment.errInputCardNo'));
       return;
     }
     if (!this.state.validTill) {
-      toastShort('Please type valid till');
+      toastShort($t('payment.plsInputTill'));
       return;
     }
     if (!/^(0[1-9]|1[0-2])\/(2[1-9]|[3-5][0-9])/.test(this.state.validTill)) {
-      toastShort('Please type a correct valid till');
+      toastShort($t('payment.errInputTill'));
       return;
     }
     if (!this.form.cardCvv) {
-      toastShort('Please type CVV');
+      toastShort($t('payment.plsInputCvv'));
       return;
     }
     if (!/^\d{3}$/.test(this.form.cardCvv)) {
-      toastShort('Please type a correct CVV');
+      toastShort($t('payment.errInputCvv'));
       return;
     }
     const params = this.form;
@@ -92,18 +92,18 @@ export default class FormCard extends Component {
               <Text style={styles.currency}>{currency+' '}</Text>
               {this.state.amount}
             </Text>
-            <Text style={styles.amountTitle}>Top up</Text>
+            <Text style={styles.amountTitle}>{$t('wallet.topUp')}</Text>
             <View style={styles.buttonGroup}>
               <Button
                 style={styles.button2}
-                text='Open in Browser'
+                text={$t('payment.openInBrowser')}
                 elevation={1.5}
                 onClick={() => {
                   this.openBrowser();
                 }}/>
               <View style={styles.button}>
                 <Button
-                  text='Payment Completed'
+                  text={$t('payment.paymentCompleted')}
                   elevation={1.5}
                   onClick={() => {
                     goBack();
@@ -115,16 +115,16 @@ export default class FormCard extends Component {
         </View>
       : <KeyboardAvoidingView style={styles.container}>
           <View style={styles.formView}>
-            <Text style={styles.label}>Name on Card</Text>
+            <Text style={styles.label}>{$t('payment.labelCardName')}</Text>
             <TextInput
               style={styles.cardNameInput}
-              placeholder={'Your name on card'}
+              placeholder={$t('payment.hintCardName')}
               placeholderTextColor={textPlacehoder}
               maxLength={50}
               onChangeText={text => this.form.nameOnCard = text}/>
           </View>
           <View style={styles.formView}>
-            <Text style={styles.label}>Card Number</Text>
+            <Text style={styles.label}>{$t('payment.labelCardNo')}</Text>
             <View style={styles.cardNumberRow}>
               <FontAwesome
                 name='credit-card'
@@ -133,7 +133,7 @@ export default class FormCard extends Component {
               <Text style={styles.leftLine}/>
               <TextInput
                 style={styles.cardNumber}
-                placeholder='Card Number'
+                placeholder={$t('payment.labelCardNo')}
                 placeholderTextColor={textPlacehoder}
                 maxLength={25}
                 keyboardType={'numeric'}
@@ -144,9 +144,9 @@ export default class FormCard extends Component {
           </View>
           <View style={styles.formRow}>
             <View style={styles.formColumn}>
-              <Text style={styles.label}>Valid Till</Text>
+              <Text style={styles.label}>{$t('payment.labelValidTill')}</Text>
               <TipInput
-                placeholder='MM/YY'
+                placeholder={$t('payment.hintValidTill')}
                 maxLength={5}
                 keyboardType={'numeric'}
                 value={this.state.validTill}
@@ -186,7 +186,7 @@ export default class FormCard extends Component {
           <Text style={ui.flex1}></Text>
           <View style={styles.buttonView}>
             <Button
-              text='Confirm'
+              text={$t('common.confirm')}
               elevation={1.5}
               onClick={() => {
                 this.validate();
@@ -221,7 +221,7 @@ const styles = StyleSheet.create({
   container: {
     width: $vw(100),
     flex: 1,
-    backgroundColor: 'white'
+    backgroundColor: pageBackground
   },
   formView: {
     paddingTop: 16,
@@ -243,7 +243,7 @@ const styles = StyleSheet.create({
     paddingRight: 8
   },
   label: {
-    color: '#333',
+    color: textPrimary,
     fontSize: 14
   },
   leftLine: {
@@ -259,12 +259,12 @@ const styles = StyleSheet.create({
   },
   cardNumber: {
     flex: 1,
-    color: '#333',
+    color: textPrimary,
     fontSize: 16,
     paddingLeft: 8,
   },
   cardNameInput: {
-    color: '#333',
+    color: textPrimary,
     fontSize: 14,
     lineHeight: 16,
     paddingTop: 4,
@@ -286,7 +286,7 @@ const styles = StyleSheet.create({
   cardInput: {
     flex: 1,
     ...$padding(4, 0),
-    color: '#333',
+    color: textPrimary,
     fontSize: 14,
   },
   countryRow: {
@@ -299,7 +299,7 @@ const styles = StyleSheet.create({
   },
   countryPicker: {
     flex: 1,
-    color: '#333',
+    color: textPrimary,
     fontSize: 14,
     paddingTop: 4,
     paddingLeft: 16
@@ -311,7 +311,7 @@ const styles = StyleSheet.create({
     color: '#12A5F9',
     fontSize: 12,
     position: 'absolute',
-    backgroundColor: 'white',
+    backgroundColor: colorLight
   },
   switchRow: {
     padding: 16,
@@ -319,7 +319,7 @@ const styles = StyleSheet.create({
     flexDirection: 'row',
   },
   tipsText: {
-    color: '#333',
+    color: textPrimary,
     fontSize: 12,
     padding: 16,
   },
@@ -352,6 +352,6 @@ const styles = StyleSheet.create({
     marginRight: 16,
     borderColor: '#DDD',
     borderWidth: 1,
-    backgroundColor: '#fff'
+    backgroundColor: colorLight
   }
 })

+ 20 - 20
Strides-APP/app/pages/payment/PayNow.js

@@ -4,7 +4,7 @@
  */
 import React, { Component } from 'react';
 import { View, Text, StyleSheet, Image, ScrollView } from 'react-native';
-import CameraRoll from "@react-native-community/cameraroll";
+import {CameraRoll} from "@react-native-camera-roll/camera-roll";
 import ViewShot from "react-native-view-shot";
 import Button from '../../components/Button';
 import Dialog from '../../components/Dialog';
@@ -25,7 +25,7 @@ export default class PayNow extends Component {
   componentDidMount() {
     if (this.props.route.params.amount) {
       this.setState({
-        amount: this.props.route.params.amount,
+        amount: this.props.route.params?.amount,
         base64: 'data:image/png;base64,' + this.props.route.params.base64
       });
     }
@@ -37,9 +37,9 @@ export default class PayNow extends Component {
       Dialog.dismissLoading();
       CameraRoll.save(uri, { type: 'photo' });
       console.log("Save Photo with ", uri);
-      toastShort('Save to gallery successfully');
-    }).catch(err => {
-      toastShort(err)
+      toastShort($t('payment.successSave2gallery'));
+    }).catch(errs => {
+      toastShort(errs)
       Dialog.dismissLoading();
     });
   }
@@ -52,8 +52,8 @@ export default class PayNow extends Component {
     .then(res => {
       console.log('getPermission', res)
       this.checkPermission();
-    }).catch(err => {
-      console.info('getPermission', err)
+    }).catch(errs => {
+      console.info('getPermission', errs)
     });
   }
 
@@ -69,7 +69,7 @@ export default class PayNow extends Component {
           if (isIOS) {
             this.savePhoto();
           } else {
-            toastShort('Save to gallery failed');
+            toastShort($t('payment.errSave2gallery'));
           }
           break;
         case RESULTS.DENIED:
@@ -79,7 +79,7 @@ export default class PayNow extends Component {
             this.getPermission();
           } else {
             this.denied = true;
-            toastShort('Save to gallery failed');
+            toastShort($t('payment.errSave2gallery'));
           }
           break;
         case RESULTS.LIMITED:
@@ -93,9 +93,9 @@ export default class PayNow extends Component {
         case RESULTS.BLOCKED:
           console.log('权限被拒绝,不再可请求');
           Dialog.showDialog({
-            title: 'Error',
-            message: 'Can not save images, Please grant storage or gallery permissions.',
-            ok: 'SETTING',
+            title: $t('common.error'),
+            message: $t('payment.errSave2galleryPermission'),
+            ok: $t('payment.btnSetting'),
             callback: btn => {
               if (btn == Dialog.BUTTON_OK) {
                 console.log('ok');
@@ -106,7 +106,7 @@ export default class PayNow extends Component {
           //toastShort('Save to gallery failed');
           break;
       }
-    }).catch(err => {
+    }).catch(err1 => {
 
     })
   }
@@ -132,12 +132,12 @@ export default class PayNow extends Component {
           : <View style={styles.qrCode}></View>
           }
         </ViewShot>
-        <Text style={styles.title}>Top Up Amount: {currency + ' ' + this.state.amount}</Text>
-        <Text style={styles.tips}>Please save the QR Code, and use your bank app to scan the QR Code.</Text>
-        <Text style={styles.tips}>Once the payment is completed, the top up amount will be credited into your credits automatically.</Text>
+        <Text style={styles.title}>{$t('payment.labelTopUpAmount')} {currency + ' ' + this.state.amount}</Text>
+        <Text style={styles.tips}>{$t('payment.tipsQrPaymentSave')}</Text>
+        <Text style={styles.tips}>{$t('payment.tipsQrPayment')}</Text>
         <View style={styles.button2}>
           <Button
-            text='Save QR Code to Gallery'
+            text={$t('payment.btnSave2gallery')}
             elevation={1.5}
             onClick={() => {
               this.checkPermission();
@@ -145,7 +145,7 @@ export default class PayNow extends Component {
         </View>
         <View style={styles.button}>
           <Button
-            text='Payment Completed'
+            text={$t('payment.paymentCompleted')}
             elevation={1.5}
             onClick={() => {
               goBack();
@@ -177,13 +177,13 @@ const styles = StyleSheet.create({
     backgroundColor: '#efefef'
   },
   title: {
-    color: '#333',
+    color: textPrimary,
     fontSize: 18,
     paddingBottom: 16,
     textAlign: 'center'
   },
   tips: {
-    color: '#333',
+    color: textPrimary,
     padding: 16,
   },
   button: {

+ 20 - 21
Strides-APP/app/pages/payment/PayPerUse.js

@@ -4,11 +4,10 @@
  */
 import React, { Component } from 'react';
 import { View, Text, StyleSheet, Image, ScrollView } from 'react-native';
-import CameraRoll from "@react-native-community/cameraroll";
+import {CameraRoll} from "@react-native-camera-roll/camera-roll";
 import ViewShot from "react-native-view-shot";
 import Button from '../../components/Button';
 import Dialog from '../../components/Dialog';
-import { Balance } from '../wallet/Payment';
 import {check, request, openSettings, PERMISSIONS, RESULTS} from 'react-native-permissions';
 
 export default class PayPerUse extends Component {
@@ -37,9 +36,9 @@ export default class PayPerUse extends Component {
       Dialog.dismissLoading();
       CameraRoll.save(uri, { type: 'photo' });
       console.log("Save Photo with ", uri);
-      toastShort('Save to gallery successfully');
-    }).catch(err => {
-      toastShort(err)
+      toastShort($t('payment.successSave2gallery'));
+    }).catch(errs => {
+      toastShort(errs)
       Dialog.dismissLoading();
     });
   }
@@ -52,8 +51,8 @@ export default class PayPerUse extends Component {
     .then(res => {
       console.log('getPermission', res)
       this.checkPermission();
-    }).catch(err => {
-      console.info('getPermission', err)
+    }).catch(errs => {
+      console.info('getPermission', errs)
     });
   }
 
@@ -69,7 +68,7 @@ export default class PayPerUse extends Component {
           if (isIOS) {
             this.savePhoto();
           } else {
-            toastShort('Save to gallery failed');
+            toastShort($t('payment.errSave2gallery'));
           }
           break;
         case RESULTS.DENIED:
@@ -79,7 +78,7 @@ export default class PayPerUse extends Component {
             this.getPermission();
           } else {
             this.denied = true;
-            toastShort('Save to gallery failed');
+            toastShort($t('payment.errSave2gallery'));
           }
           break;
         case RESULTS.LIMITED:
@@ -93,9 +92,9 @@ export default class PayPerUse extends Component {
         case RESULTS.BLOCKED:
           console.log('权限被拒绝,不再可请求');
           Dialog.showDialog({
-            title: 'Error',
-            message: 'Can not save images, Please grant storage or gallery permissions.',
-            ok: 'SETTING',
+            title: $t('common.error'),
+            message: $t('payment.errSave2galleryPermission'),
+            ok: $t('payment.btnSetting'),
             callback: btn => {
               if (btn == Dialog.BUTTON_OK) {
                 console.log('ok');
@@ -106,7 +105,7 @@ export default class PayPerUse extends Component {
           //toastShort('Save to gallery failed');
           break;
       }
-    }).catch(err => {
+    }).catch(err1 => {
 
     })
   }
@@ -132,13 +131,13 @@ export default class PayPerUse extends Component {
           : <View style={styles.qrCode}></View>
           }
         </ViewShot>
-        <Text style={styles.title}>Amount: {currency + ' ' + this.state.amount}</Text>
-        <Text style={styles.tips}>Please save the QR Code, and use your bank/eWallet app to scan the QR Code. </Text>
-        <Text style={styles.tips}>Once the payment is received, the charging will start automatically.</Text>
-        <Text style={styles.tips}>Unutilized amount will be refunded.</Text>
+        <Text style={styles.title}>{$t('payment.labelAmount')} {currency + ' ' + this.state.amount}</Text>
+        <Text style={styles.tips}>{$t('payment.tipsPayPerUseSave')}</Text>
+        <Text style={styles.tips}>{$t('payment.tipsPayPerUseCharge')}</Text>
+        <Text style={styles.tips}>{$t('payment.tipsPayPerUse')}</Text>
         <View style={styles.button}>
           <Button
-            text='Save QR Code to Gallery'
+            text={$t('payment.btnSave2gallery')}
             elevation={1.5}
             onClick={() => {
               this.checkPermission();
@@ -146,7 +145,7 @@ export default class PayPerUse extends Component {
         </View>
         <View style={styles.button2}>
           <Button
-            text='Payment Completed'
+            text={$t('payment.paymentCompleted')}
             elevation={1.5}
             onClick={() => {
               goBack();goBack();
@@ -177,13 +176,13 @@ const styles = StyleSheet.create({
     backgroundColor: '#F0F0F0'
   },
   title: {
-    color: '#333',
+    color: textPrimary,
     fontSize: 18,
     paddingBottom: 16,
     textAlign: 'center'
   },
   tips: {
-    color: '#333',
+    color: textPrimary,
     ...$padding(0, 16, 16),
   },
   button: {

+ 27 - 51
Strides-APP/app/pages/payment/PaymentMethod.js

@@ -7,10 +7,11 @@ import { View, Text, StyleSheet, Pressable, Image } from 'react-native';
 import apiWallet from '../../api/apiWallet';
 import Button from '../../components/Button';
 import Dialog from '../../components/Dialog';
+import TextView from '../../components/TextView';
 import ChargeItemSelect from '../../icons/ChargeItemSelect';
 import { PageList } from '../Router';
-import { PaymentDefault, PAYTYPE } from '../wallet/Payment';
 import { WalletTitle } from '../wallet/Topup';
+import { getPaymenOptions, PaymentDefault, PAYTYPE } from './PaymentConfig';
 
 export default class PaymentMethod extends Component {
   constructor(props) {
@@ -18,34 +19,7 @@ export default class PaymentMethod extends Component {
     this.state = {
       cIndex: 0,
       selectIndex: 0,
-      paymentOptions: [{
-        title: "Credits",
-        desc: '',
-        value: PAYTYPE.CREDIT_WALLET,
-        icon: require('../../images/icon/draw-wallet.png')
-      }],
-      paymentOptionsWallet: [{
-        title: "Credits",
-        desc: '',
-        value: PAYTYPE.CREDIT_WALLET,
-        icon: require('../../images/icon/draw-wallet.png')
-      }, {
-        title: "Pay Per Use",
-        desc: '(SGQR)',
-        value: PAYTYPE.PAY_PER_USE,
-        icon: require('../../images/wallet/ic_payperuse.png')
-      }],
-      paymentOptionsUse: [{
-        title: "Pay Per Use",
-        desc: '(SGQR)',
-        value: PAYTYPE.PAY_PER_USE,
-        icon: require('../../images/wallet/ic_payperuse.png')
-      }, {
-        title: "Credits",
-        desc: '',
-        value: PAYTYPE.CREDIT_WALLET,
-        icon: require('../../images/icon/draw-wallet.png')
-      }],
+      paymentOptions: [],
       amountList: [{
         amount: 10,
         power: 0
@@ -59,14 +33,16 @@ export default class PaymentMethod extends Component {
       connectorInfo: {}
     };
     this.rate = 0
+    this.isPayPerUseFirst = PaymentDefault.DEFAULT.payType == PAYTYPE.PAY_PER_USE;
     this.ENABLE_2C2P = PaymentDefault.is2c2p;
-    this.FirstPayPerUse = PaymentDefault.DEFAULT.payType == PAYTYPE.PAY_PER_USE;
   }
 
   componentDidMount() {
-    /*this.setState({
-      paymentOptions: this.FirstPayPerUse ? this.state.paymentOptionsUse : this.state.paymentOptionsWallet
-    });*/
+    getPaymenOptions(options => {
+      this.setState({
+        paymentOptions: options
+      });
+    })
     const params = this.props.route.params;
     const info = params.info ?? {};
     console.log('----params----', params);
@@ -100,7 +76,7 @@ export default class PaymentMethod extends Component {
       title: info.title,
       value: info.value
     };
-    if (info.value == PAYTYPE.CREDIT_WALLET) {
+    if (info.value != PAYTYPE.PAY_PER_USE) {
       goBack();
     } else {
       const amount = this.state.amountList[this.state.selectIndex].amount;
@@ -139,7 +115,7 @@ export default class PaymentMethod extends Component {
       title: info.title,
       value: info.value
     };
-    if (info.value == PAYTYPE.CREDIT_WALLET) {
+    if (this.state.cIndex == 1) {
       goBack();
     } else {
       const amount = this.state.amountList[this.state.selectIndex].amount;
@@ -192,8 +168,8 @@ export default class PaymentMethod extends Component {
         </View>
         <View style={styles.contentView}>
           <View style={styles.optionView}>
-            <WalletTitle>Payment Option</WalletTitle>
-            <Text style={styles.subTitle}>Pay with: </Text>
+            <WalletTitle>{$t('payment.paymentOption')}</WalletTitle>
+            <TextView style={styles.subTitle}>{$t('payment.labelPayWith')}</TextView>
             { this.state.paymentOptions.map((item, index) => {
               return (
                 <Pressable
@@ -209,10 +185,10 @@ export default class PaymentMethod extends Component {
                   <Image
                     style={styles.accountLogo}
                     source={item.icon}/>
-                  <Text style={styles.paytypeText}>
+                  <TextView style={styles.paytypeText}>
                     {item.title} {' '}
-                    {item.desc ? item.desc : '(Balance '+currency+userInfo.credit+')'}
-                  </Text>
+                    {item.desc ? item.desc : '(' + $t('wallet.balance') + ' ' +currency+userInfo.creditStr+')'}
+                  </TextView>
                   <View style={styles.selectIcon}>
                     <ChargeItemSelect size={18} selected={index == this.state.cIndex} tint={colorPrimary}/>
                   </View>
@@ -221,10 +197,10 @@ export default class PaymentMethod extends Component {
             })
           }
           </View>
-          { (this.state.cIndex == (this.FirstPayPerUse ? 0 : 1)) &&
+          { (this.state.cIndex == (this.isPayPerUseFirst ? 0 : 1)) &&
             <View style={[styles.optionView, ui.flex1]}>
-              <WalletTitle>Pay Per Use</WalletTitle>
-              <Text style={styles.subTitle}>Choose Amount:</Text>
+              <WalletTitle>{$t('payment.payPerUse')}</WalletTitle>
+              <TextView style={styles.subTitle}>{$t('payment.labelChooseAmount')}</TextView>
               <View style={styles.amountItems}>
                 { this.state.amountList.map((item, index) => {
                     return (
@@ -243,19 +219,19 @@ export default class PaymentMethod extends Component {
                           style={index == this.state.selectIndex ? styles.amountActive : styles.amountText}>
                           {currency}{item.amount}
                         </Text>
-                        <Text style={index == this.state.selectIndex ? styles.powerTextActive : styles.powerText}>~{item.power}kWh</Text>
+                        <TextView style={index == this.state.selectIndex ? styles.powerTextActive : styles.powerText}>~{item.power}kWh</TextView>
                       </Pressable>
                     );
                   })
                 }
               </View>
               <Text style={ui.flex1}></Text>
-              <Text style={styles.warningText}>Un-utilized amount will be refunded.</Text>
+              <TextView style={styles.warningText}>{$t('payment.tipsPayPerUse')}</TextView>
             </View>
           }
         </View>
         <Button
-          text="Confirm"
+          text={$t('common.confirm')}
           style={$margin(16)}
           onClick={() => this.ENABLE_2C2P ? this.onConfirmV2() : this.onConfirm()}
         />
@@ -283,10 +259,10 @@ const styles = StyleSheet.create({
     padding: 16,
     borderRadius: 10,
     marginBottom: 16,
-    backgroundColor: 'white'
+    backgroundColor: colorLight
   },
   subTitle: {
-    color: '#333',
+    color: textPrimary,
     fontSize: 14,
     marginTop: 16,
     marginBottom: 16
@@ -308,7 +284,7 @@ const styles = StyleSheet.create({
   },
   paytypeText: {
     flex: 1,
-    color: '#333',
+    color: textPrimary,
     fontSize: 14,
   },
   selectIcon: {
@@ -341,14 +317,14 @@ const styles = StyleSheet.create({
     textAlign: 'center'
   },
   amountActive: {
-    color: '#fff',
+    color: textLight,
     fontSize: 24,
     height: 50,
     lineHeight: 60,
     textAlign: 'center'
   },
   powerText: {
-    color: '#333',
+    color: textPrimary,
     padding: 6,
     fontSize: 12,
     textAlign: 'right',

+ 3 - 2
Strides-APP/app/pages/search/ConnectType.js

@@ -1,12 +1,13 @@
 import React from 'react';
 import { StyleSheet, Text, View } from 'react-native';
+import TextView from '../../components/TextView';
 
 export default ConnectType = ({type, available, all, color=textPrimary}) => {
   if (type) {
     return (
       <View style={[styles.connectType, {borderColor: color}]}>
-        <Text style={[styles.typeLabel, {backgroundColor: color}]}>{type}</Text>
-        <Text style={[styles.typeContent, {color: color}]}><Text style={styles.typeBold}>{available}</Text>/{all}</Text>
+        <TextView style={[styles.typeLabel, {backgroundColor: color}]}>{type}</TextView>
+        <TextView style={[styles.typeContent, {color: color}]}><Text style={styles.typeBold}>{available}</Text>/{all}</TextView>
       </View>
     );
   } else {

+ 2 - 2
Strides-APP/app/pages/search/ListView.js

@@ -4,7 +4,7 @@
  */
 import React from 'react';
 import { Pressable, StyleSheet, Text, View } from 'react-native';
-import TextRadius from '../../components/TextRadius';
+import TextView from '../../components/TextView';
 import utils from '../../utils/utils';
 import Provider from '../charge/Provider';
 import ConnectType from './ConnectType';
@@ -21,7 +21,7 @@ export default ListView = ({item, index, separators, onPress}) => {
           <View style={styles.nameView}>
             <Text style={styles.stationName}>{item.name}</Text>
             { item.allConnector && item.allConnector.available > 0 &&
-              <TextRadius style={[styles.infoStatus, styles.available]}>Available</TextRadius>
+              <TextView style={[styles.infoStatus, styles.available]}>Available</TextView>
             }
           </View>
           <Provider providers={item.serviceProvider}/>

+ 6 - 6
Strides-APP/app/pages/search/ListViewV2.js

@@ -4,7 +4,7 @@
  */
 import React from 'react';
 import { Pressable, StyleSheet, Text, View } from 'react-native';
-import TextRadius from '../../components/TextRadius';
+import TextView from '../../components/TextView';
 import utils from '../../utils/utils';
 import Provider from '../charge/Provider';
 import ConnectType from './ConnectType';
@@ -24,22 +24,22 @@ export default ListViewV2 = ({item, index, separators, onPress, onFavorite}) =>
           color="#E5E5E5"
         /> */}
         <View style={styles.stationInfo} >
-          <Text style={styles.stationName}>{item.name}</Text>
+          <TextView style={styles.stationName}>{item.name}</TextView>
           <View style={ui.flex}>
             <View style={ui.flex1}>
-              <Text style={styles.stationAddress}>{item.address}</Text>
+              <TextView style={styles.stationAddress}>{item.address}</TextView>
               {/* <Provider providers={item.serviceProvider}/> */}
               <View style={styles.connectView}>
                 <ConnectType color={textCancel} {...item.acConnector}/>
                 <ConnectType color={textCancel} {...item.dcConnector}/>
               </View>
               <View style={ui.flexc}>
-                <TextRadius style={[styles.infoStatus, styles.available]}>{item.distance}</TextRadius>
+                <TextView style={[styles.infoStatus, styles.available]}>{item.distance}</TextView>
                 {item.allConnector && item.allConnector.available > 0 &&
-                  <TextRadius style={[styles.infoStatus, styles.available]}>{$t('charging.statusAvailable')}</TextRadius>
+                  <TextView style={[styles.infoStatus, styles.available]}>{$t('charging.statusAvailable')}</TextView>
                 }
                 {item.siteType == "Private" &&
-                  <TextRadius style={[styles.infoStatus, styles.private]}>{$t('home.statusPrivate')}</TextRadius>
+                  <TextView style={[styles.infoStatus, styles.private]}>{$t('home.statusPrivate')}</TextView>
                 }
               </View>
             </View>

+ 4 - 4
Strides-APP/app/pages/search/Search.js

@@ -30,7 +30,7 @@ export default class Search extends Component {
       }
       this.searchStation(latlng);
     }, error => {
-      console.warn("getGeoLocation", error);
+      console.info("[Search] getGeoLocation", error);
     });
   }
 
@@ -63,7 +63,7 @@ export default class Search extends Component {
         });
       }
     }).catch(err => {
-      console.log('err', err);
+      console.log('searchStation-err', err);
       this.setState({
         isSearch: false,
         searchResult: []
@@ -94,7 +94,7 @@ export default class Search extends Component {
             numberOfLines={1}
             returnKeyType={'search'}
             clearButtonMode={'while-editing'}
-            placeholder='Search using site name or service provider'
+            placeholder={$t('home.searchHint')}
             placeholderTextColor={textPlacehoder}
             onChangeText={text => {
               this.searchWorld = text;
@@ -114,7 +114,7 @@ export default class Search extends Component {
             data={this.state.searchResult}
             renderItem={this.listItem}
             keyExtractor={item => item.id}
-            ListEmptyComponent={<Text style={styles.noResult}>No search result</Text>}
+            ListEmptyComponent={<Text style={styles.noResult}>{$t('home.noSearch')}</Text>}
           />
         }
       </View>

+ 4 - 4
Strides-APP/app/pages/search/SearchV2.js

@@ -163,7 +163,7 @@ export default class Search extends Component {
 const styles = StyleSheet.create({
   container: {
     flex: 1,
-    backgroundColor: colorLight //pageBackground
+    backgroundColor: pageBackground
   },
   searchView: {
     marginTop: 16,
@@ -188,12 +188,12 @@ const styles = StyleSheet.create({
     lineHeight: 20
   },
   searchingView: {
-    padding: 0,
+    padding: 16,
     alignItems: 'center'
   },
   seachingIcon: {
-    width: 120,
-    height: 120
+    width: 60,
+    height: 60
   },
   noResult: {
     color: '#999',

+ 31 - 20
Strides-APP/app/pages/sign/Login.js

@@ -4,16 +4,15 @@
  */
 import React from 'react';
 import {View, Text, StyleSheet, Image, TextInput, ScrollView, Platform, Pressable} from 'react-native';
-import CheckBox from '@react-native-community/checkbox';
 import { BackButton } from '../../components/Toolbar';
 import apiUser from '../../api/apiUser';
 import { setAccessToken } from '../../api/http';
-import { getStorageSync, setStorage, getStorageJsonSync, setStorageJson, getStorage } from '../../utils/storage';
+import { getStorageJsonSync, setStorageJson } from '../../utils/storage';
 import Button from '../../components/Button';
 import Dialog from '../../components/Dialog';
 import { PageList } from '../Router';
-import utils from '../../utils/utils';
 import CheckBoxText from '../../components/CheckBoxText';
+import TextView from '../../components/TextView';
 
 export const AutoLogin = async (back) => {
   const data = await getStorageJsonSync('loginData')
@@ -23,7 +22,7 @@ export const AutoLogin = async (back) => {
         setAccessToken(res.data.accessToken);
         if (back) back();
       }
-    }).catch((err) => {
+    }).catch(err => {
       console.warn('AutoLogin', err);
       toastShort('Sign in failed')
     });
@@ -37,7 +36,8 @@ export default class Login extends React.Component {
     this.state = {
       email: '',
       password: '',
-      rememberMe: true
+      rememberMe: true,
+      showPassword: false
     }
   }
 
@@ -87,11 +87,11 @@ export default class Login extends React.Component {
   onLogin() {
     //console.log(this.state);
     if (this.state.email == '') {
-      toastShort('Please enter email address');
+      toastShort($t('sign.plsInputEmail'));
       return;
     }
     if (this.state.password == '') {
-      toastShort('Please enter password');
+      toastShort($t('sign.plsInputPassword'));
       return;
     }
     Dialog.showProgressDialog();
@@ -111,7 +111,7 @@ export default class Login extends React.Component {
         toastShort(res.msg);
         Dialog.dismissLoading();
       }
-    }).catch((err) => {
+    }).catch(err => {
       toastShort(err);
       Dialog.dismissLoading();
     });
@@ -121,6 +121,12 @@ export default class Login extends React.Component {
     return isIOS ? statusHeight : 4;
   }
 
+  togglePassword() {
+    this.setState({
+      showPassword: !this.state.showPassword
+    })
+  }
+
   render() {
     return (
       <View style={ui.flex1}>
@@ -142,7 +148,7 @@ export default class Login extends React.Component {
               style={styles.logoImg}
               resizeMode='contain'
               source={require('../../images/app-logo.png')} /> */}
-            {/* <Text style={styles.loginTitle}>Please Login</Text> */}
+            <TextView style={styles.loginTitle}>{$t('sign.plsLoginTitle')}</TextView>
           </View>
           <View style={styles.loginForm}>
             <View style={styles.inputView}>
@@ -153,7 +159,7 @@ export default class Login extends React.Component {
               />
               <TextInput
                 style={styles.inputText}
-                placeholder={"E-mail"}
+                placeholder={$t('sign.labelE_mail')}
                 placeholderTextColor={textPlacehoder}
                 keyboardType="email-address"
                 textContentType='emailAddress'
@@ -177,10 +183,10 @@ export default class Login extends React.Component {
               />
               <TextInput 
                 style={styles.inputText}
-                placeholder="Password"
+                placeholder={$t('sign.labelPassword')}
                 placeholderTextColor={textPlacehoder}
                 textContentType='password'
-                secureTextEntry={true}
+                secureTextEntry={!this.state.showPassword}
                 maxLength={20}
                 onChangeText={(v) => {
                   this.setState({
@@ -190,6 +196,11 @@ export default class Login extends React.Component {
                 onSubmitEditing={() => {
                   this.onLogin();
                 }}/>
+              <MaterialCommunityIcons
+                name={this.state.showPassword ? 'eye' : 'eye-off' }
+                size={20}
+                color={textCancel}
+                onPress={() => this.togglePassword()}/>
             </View>
             <View style={ui.flexcw}>
               <View style={$padding(12, 8)}>
@@ -198,16 +209,16 @@ export default class Login extends React.Component {
                   onValueChange={(newValue) => {
                     this.setState({ rememberMe: newValue });
                   }}
-                  text={'Remember me'}
+                  text={$t('sign.rememberMe')}
                 />
               </View>
-              <Text
+              <TextView
                 style={styles.linksText}
-                onPress={() => startPage(PageList.forgotPassword)}>Forgot Password</Text>
+                onPress={() => startPage(PageList.forgotPassword)}>{$t('sign.forgotPassword')}</TextView>
             </View>
             <Button
               style={styles.loginButton}
-              text='LOGIN'
+              text={$t('sign.btnLogin')}
               elevation={1.5}
               onClick={() => {
                 this.onLogin()
@@ -216,19 +227,19 @@ export default class Login extends React.Component {
           </View>
         </View>
         <View style={styles.signView}>
-          <Text style={{color: textPrimary}}>New User?</Text>
+          <TextView style={{color: textPrimary}}>{$t('sign.tipNewUser')}</TextView>
           {/* <Text
             style={styles.linksText}
             onPress={() => {
               startPage(PageList.register);
             }}
           >Click here to sign up</Text> */}
-          <Text 
+          <TextView 
             style={styles.linksText}
-            onPress={() => startPage(PageList.register)}>Register as Public User</Text>
+            onPress={() => startPage(PageList.register)}>{$t('sign.registerPublicUser')}</TextView>
           {/* <Text 
             style={styles.linksText}
-            onPress={() => startPage(PageList.register, {isFleetUser: true})}>Register as Fleet / PHV Driver</Text> */}
+            onPress={() => startPage(PageList.register, {isFleetUser: true})}>{$t('sign.registerDriverUser')}</Text> */}
         </View>
       </ScrollView>
       </View>

+ 19 - 20
Strides-APP/app/pages/sign/LoginV2.js

@@ -11,8 +11,8 @@ import { getStorageJsonSync, setStorageJson } from '../../utils/storage';
 import Button from '../../components/Button';
 import Dialog from '../../components/Dialog';
 import { PageList } from '../Router';
-import utils from '../../utils/utils';
 import CheckBoxText from '../../components/CheckBoxText';
+import TextView from '../../components/TextView';
 
 export const AutoLogin = async (back) => {
   const data = await getStorageJsonSync('loginData')
@@ -109,7 +109,7 @@ export default class Login extends React.Component {
         toastShort(res.msg);
         Dialog.dismissLoading();
       }
-    }).catch((err) => {
+    }).catch(err => {
       toastShort(err);
       Dialog.dismissLoading();
     });
@@ -121,7 +121,7 @@ export default class Login extends React.Component {
 
   render() {
     return (
-      <View style={ui.container}>
+      <View style={styles.container}>
       <View style={[styles.backBtn, {top: this.getBackTopPosition()}]}>
         <BackButton/>
       </View>
@@ -142,13 +142,13 @@ export default class Login extends React.Component {
                 style={styles.inputText}
                 placeholder={"E-mail"}
                 placeholderTextColor={textPlacehoder}
-                textContentType="emailAddress"
+                textContentType='emailAddress'
                 defaultValue={this.state.email}
-                maxLength={50}
+                clearButtonMode='while-editing'
                 autoCapitalize="none"
                 autoComplete="off"
                 autoCorrect={false}
-                clearButtonMode='while-editing'
+                maxLength={50}
                 onChangeText={(v) => {
                   this.setState({
                     email: v
@@ -183,9 +183,9 @@ export default class Login extends React.Component {
                   text={'Remember me'}
                 />
               </View>
-              <Text
+              <TextView
                 style={styles.linksText}
-                onPress={() => startPage(PageList.forgotPassword)}>Forgot Password</Text>
+                onPress={() => startPage(PageList.forgotPassword)}>Forgot Password</TextView>
             </View>
             <Button
               style={styles.loginButton}
@@ -198,13 +198,13 @@ export default class Login extends React.Component {
           </View>
         </View>
         <View style={styles.signView}>
-          <Text style={{color: '#333'}}>New User?</Text>
-          <Text
+          <TextView style={{color: textPrimary}}>New User?</TextView>
+          <TextView
             style={styles.linksText}
             onPress={() => {
               startPage(PageList.register);
             }}
-          >Click here to sign up</Text>
+          >Click here to sign up</TextView>
         </View>
       </ScrollView>
       </View>
@@ -215,7 +215,7 @@ export default class Login extends React.Component {
 const styles = StyleSheet.create({
   container: {
     flex: 1,
-    backgroundColor: colorLight
+    backgroundColor: colorThemes
   },
   backBtn:{
     top: 4,
@@ -233,18 +233,18 @@ const styles = StyleSheet.create({
   },
   headerImg: {
     width: $vw(63),
-    height: 100
+    height: 60,
+    marginTop: 60
   },
   loginView: {
     padding: 16,
     marginTop: -24,
     borderTopLeftRadius: 24,
-    borderTopRightRadius: 24,
-    backgroundColor: 'white'
+    borderTopRightRadius: 24
   },
   logoImg: {
     width:136.19,
-    height: 42.85,
+    height: 40,
     marginTop: 18
   },
   loginForm: {
@@ -264,15 +264,14 @@ const styles = StyleSheet.create({
   },
   inputText: {
     flex: 1,
-    color: '#333',
+    color: textPrimary,
     fontSize: 15,
     paddingTop: 6,
     paddingLeft: 16,
     paddingBottom: 6
   },
   loginButton: {
-    marginTop: 32,
-    borderRadius: 4
+    marginTop: 32
   },
   signView: {
     paddingTop: 32,
@@ -280,7 +279,7 @@ const styles = StyleSheet.create({
     alignItems: 'center',
   },
   checkText: {
-    color: '#333',
+    color: textPrimary,
     fontSize: 14,
     paddingLeft: 8
   },

+ 76 - 60
Strides-APP/app/pages/sign/RegisterV2.js

@@ -1,6 +1,6 @@
 /**
- * 注册页面V2
- * @邠心vbe on 2022/12/23
+ * 带Public和PHV切换的注册页面V2
+ * @邠心vbe on 2023/02/01
  */
 import React from 'react';
 import { View, Text, ScrollView, StyleSheet, TextInput, Pressable, Image } from 'react-native';
@@ -17,9 +17,10 @@ import apiUpload from '../../api/apiUpload';
 import { host } from '../../api/http';
 import { ModalProps } from '../../components/BottomModal';
 import { CountryDropNum, GetCountryList } from '../../components/CountryIcon';
-import CheckBox from '../../components/CheckBox';
 import StrengthView from './StrengthView';
+import CheckBox from '../../components/CheckBox';
 import { UploadThemes } from '../../components/ThemesConfig';
+import TextView from '../../components/TextView';
 
 const options = {
   width: 300,
@@ -34,7 +35,7 @@ const options = {
   ...UploadThemes
 }
 
-export default class Register extends React.Component {
+export default class RegisterV2 extends React.Component {
   constructor(props) {
     super(props);
     this.state = {
@@ -42,7 +43,9 @@ export default class Register extends React.Component {
       strength: 0,
       countryNum: '65',
       countryShow: false,
-      userInfo: {},
+      userInfo: {
+        email: ""
+      },
       countryNums: [],
       wrongCount: 0,
       params: {...this.props.route.params},
@@ -115,39 +118,39 @@ export default class Register extends React.Component {
     //console.log('sign up', this.state);
     var info = this.state.userInfo;
     if (!info.nickName) {
-      toastShort('Please enter display name');
+      toastShort($t('sign.plsInputDiaplayName'));
       return;
     }
     if (!info.email) {
-      toastShort('Please enter email address');
+      toastShort($t('sign.plsInputEmail'));
       return;
     }
     if (!/^[a-zA-Z0-9]+[\S]+@[a-zA-Z0-9_-]+[\.][\Sa-zA-Z]+$/.test(info.email)) {
-      toastShort('Email is incorrect format');
+      toastShort($t('sign.errEmailFormat'));
       return;
     }
     if (!info.phone) {
-      toastShort('Please enter contact number');
+      toastShort($t('sign.plsInputContactNo'));
       return;
     }
     if (!/^\d{6,15}$/.test(info.phone)) {
-      toastShort('Phone Number is incorrect format');
+      toastShort($t('sign.errContactNoFormat'));
       return;
     }
     if (!this.state.password) {
-      toastShort('Please enter password');
+      toastShort($t('sign.plsInputPassword'));
       return;
     }
     if (this.state.strength < StrengthView.V4.maxStrength) {
-      toastShort('Password is not strong');
+      toastShort($t('sign.errPasswordStrong'));
       return;
     }
     if (!info.password) {
-      toastShort('Please enter confirm password');
+      toastShort($t('sign.plsInputPassword2'));
       return;
     }
     if (info.password != this.state.password) {
-      toastShort('The twice passwords are inconsistent');
+      toastShort($t('sign.errPasswordConfirm'));
       if (this.state.wrongCount < 3) {
         this.setState({
           wrongCount: this.state.wrongCount + 1
@@ -157,11 +160,11 @@ export default class Register extends React.Component {
     }
     if (this.state.isFleetDriver) {
       if (!info.pdvLicence) {
-        toastShort('Please enter PDV Licence');
+        toastShort($t('sign.plsInputPDVLicence'));
         return;
       }
       if (this.state.pdvImages[0] == '' || this.state.pdvImages[1] == '') {
-        toastShort('Please upload PDV Licence Photos');
+        toastShort($t('sign.plsUploadLicencePhotos'));
         return;
       }
     }
@@ -180,10 +183,19 @@ export default class Register extends React.Component {
     apiUser.register(param).then(res => {
       Dialog.dismissLoading();
       //toastShort('Sign up successfully!');
-      this.setState({
-        email: param.email,
-        visible: true
-      });
+      if (isIOS) {
+        setTimeout(() => {
+          this.setState({
+            email: param.email,
+            visible: true
+          });
+        }, 500);
+      } else {
+        this.setState({
+          email: param.email,
+          visible: true
+        });
+      }
       //this.backToLogin();
     }).catch(err => {
       toastShort(err);
@@ -205,7 +217,10 @@ export default class Register extends React.Component {
   }
 
   uploadImage(index) {
-    ImagePicker.openPicker(options).then(image => {
+    ImagePicker.openPicker({
+      ...options,
+      cropperToolbarTitle: $t('common.cropperTitle')
+    }).then(image => {
       if (image.path) {
         apiUpload.uploadImage(image.path, image.mime, 'PDVL').then(res => {
           if (res.success && res.data.picturePath) {
@@ -214,15 +229,16 @@ export default class Register extends React.Component {
             this.setState({
               pdvImages: imageUrl
             });
+            toastShort($t('common.uploadSuccess'));
           } else {
-            toastShort('Upload failed, please retry');
+            toastShort($t('common.updateFailed'));
           }
         }).catch(err => {
           toastShort(err);
         });
       }
-    }).catch(err => {
-      //console.log(err);
+    }).catch(err1 => {
+      //console.log(err1);
     });
   }
 
@@ -257,52 +273,52 @@ export default class Register extends React.Component {
           keyboardShouldPersistTaps={'handled'}>
           <View style={styles.signView}>
             <View style={styles.tabView}>
-              <Text 
+              <TextView 
                 style={[
                   styles.tabText, 
                   this.state.isFleetDriver ? {} : styles.tabActive
                 ]}
                 onPress={() => this.changeTab(false)}
-              >Public Users</Text>
-              <Text
+              >Public Users</TextView>
+              <TextView
                 style={[
                   styles.tabText,
                   this.state.isFleetDriver ? styles.tabActive: {}
                 ]}
                 onPress={() => this.changeTab(true)}
-              >Fleet/PH Drivers</Text>
+              >Fleet/PH Drivers</TextView>
             </View>
             {/* <Text style={styles.title}>Registration</Text> */}
             <View style={styles.signInput}>
-              <Text style={styles.inputLabel}>Display Name</Text>
+              <TextView style={styles.inputLabel}>{$t('sign.labelDisplayName')}</TextView>
               <TextInput
                 style={styles.inputView} 
-                placeholder='Display Name'
+                placeholder={$t('sign.labelDisplayName')}
                 placeholderTextColor={textPlacehoder}
                 maxLength={50}
                 onChangeText={v => this.changeInfo('nickName', v)}
               />
             </View>
             <View style={styles.signInput}>
-              <Text style={styles.inputLabel}>Email Address</Text>
+              <TextView style={styles.inputLabel}>{$t('sign.labelEmail')}</TextView>
               <TextInput
                 style={styles.inputView}
-                placeholder='Email Address'
+                placeholder={$t('sign.labelEmail')}
                 placeholderTextColor={textPlacehoder}
                 maxLength={50}
                 onChangeText={v => this.changeInfo('email', v)}
               />
             </View>
             <View style={styles.signInput}>
-              <Text style={styles.inputLabel}>Phone Number</Text>
+              <TextView style={styles.inputLabel}>{$t('sign.labelPhoneNumber')}</TextView>
               <View style={styles.mobileView}>
                 <View style={styles.dropView}>
                   <TextInput style={styles.dropInput} editable={false}/>
-                  <Text style={styles.countryText}>{"+" + this.state.countryNum}</Text>
+                  <TextView style={styles.countryText}>{"+" + this.state.countryNum}</TextView>
                   <MaterialIcons name={'arrow-drop-down'} size={24} color={colorDark}/>
                   <Dropdown
                     style={styles.dropLayer}
-                    title='Country'
+                    title={$t('sign.labelCountry')}
                     prefixText="+"
                     list={this.state.countryNums}
                     value={this.state.countryNum}
@@ -324,7 +340,7 @@ export default class Register extends React.Component {
                 </View>
                 <TextInput
                   style={styles.contactView}
-                  placeholder='Mobile Number'
+                  placeholder={$t('sign.labelMobileNumber')}
                   placeholderTextColor={textPlacehoder}
                   keyboardType='phone-pad'
                   maxLength={15}
@@ -333,11 +349,11 @@ export default class Register extends React.Component {
               </View>
             </View>
             <View style={styles.signInput}>
-              <Text style={styles.inputLabel}>Create Password</Text>
+              <TextView style={styles.inputLabel}>{$t('sign.labelCreatePassword')}</TextView>
               <TextInput
                 secureTextEntry={this.state.wrongCount < 3}
                 style={styles.inputView}
-                placeholder='Password'
+                placeholder={$t('sign.labelPassword')}
                 placeholderTextColor={textPlacehoder}
                 maxLength={20}
                 onChangeText={(value) => {
@@ -346,17 +362,17 @@ export default class Register extends React.Component {
               />
             </View>
             <View style={styles.signInput}>
-              <Text style={styles.inputLabel}></Text>
+              <TextView style={styles.inputLabel}></TextView>
               <View style={styles.passwordView}>
                 <StrengthView.V4.VIEW {...this.state}/>
               </View>
             </View>
             <View style={styles.signInput}>
-              <Text style={styles.inputLabel}>Confirm Password</Text>
+              <TextView style={styles.inputLabel}>{$t('sign.labelConfirmPassword')}</TextView>
               <TextInput
                 secureTextEntry={this.state.wrongCount < 3}
                 style={styles.inputView}
-                placeholder='Password'
+                placeholder={$t('sign.labelPassword')}
                 placeholderTextColor={textPlacehoder}
                 maxLength={20}
                 onChangeText={v => this.changeInfo('password', v)}
@@ -365,10 +381,10 @@ export default class Register extends React.Component {
             { this.state.isFleetDriver &&
               <>
               <View style={styles.signInput}>
-                <Text style={styles.inputLabel}>Your Company</Text>
+                <TextView style={styles.inputLabel}>{$t('sign.labelYourCompany')}</TextView>
                 <Dropdown
                   style={[styles.inputView, ui.flexc]}
-                  title='Company'
+                  title={$t('sign.labelCompany')}
                   list={this.state.companyList}
                   value={this.state.fleetCompanyId}
                   valueKey='fleetCompanyId'
@@ -380,17 +396,17 @@ export default class Register extends React.Component {
                   }}/>
               </View>
               <View style={styles.signInput}>
-                <Text style={styles.inputLabel}>{'PDV Licence'}</Text>
+                <TextView style={styles.inputLabel}>{$t('sign.labelPDVLicence')}</TextView>
                 <TextInput
                   style={styles.inputView}
-                  placeholder='PH Driver Vocational Licence'
+                  placeholder={$t('sign.hintPDVLicence')}
                   placeholderTextColor={textPlacehoder}
                   maxLength={20}
                   onChangeText={v => this.changeInfo('pdvLicence', v)}
                 />
               </View>
               <View style={styles.signInput}>
-                <Text style={styles.inputLabel}>{'  PDV Photos\n(Front & Back)'}</Text>
+                <TextView style={styles.inputLabel}>{$t('sign.labelPDVPhotos')}</TextView>
                 <View style={styles.uploadGroup}>
                   { this.state.pdvImages.map((item, index) => (
                     <UploadView
@@ -404,14 +420,14 @@ export default class Register extends React.Component {
               </>
             }
             <View style={styles.referView}>
-              <Text style={styles.referTitle}>Have a referral code?</Text>
+              <TextView style={styles.referTitle}>Have a referral code?</TextView>
               <View style={styles.referText}>
-                <Text>You'll get</Text>
-                <Text style={styles.weight}>$5</Text>
-                <Text>Credit as registration bonus!</Text>
+                <TextView>You'll get</TextView>
+                <TeTextViewxt style={styles.weight}>$5</TeTextViewxt>
+                <TextView>Credit as registration bonus!</TextView>
               </View>
               <View style={styles.codeView}>
-                <Text style={{ color: textPrimary, fontSize: 16 }}>Referral Code</Text>
+                <TextView style={{ color: textPrimary, fontSize: 16 }}>Referral Code</TextView>
                 <TextInput
                   style={styles.codeText}
                   maxLength={6}
@@ -427,21 +443,21 @@ export default class Register extends React.Component {
                 onValueChange={v => this.changeAgree(v)}
               />
               <View style={styles.agreeTextRow}>
-                <Text style={styles.agreeText} onPress={() => this.changeAgree(!this.state.agree)}>
-                  {'I have read and I agree with the '}
-                </Text>
-                <Text style={styles.agreeLink} onPress={() => startPage(PageList.condition)}>Terms of Use</Text>
-                <Text style={styles.agreeText}>{' '}</Text>
-                <Text style={styles.agreeText}>{'and '}</Text>
-                <Text style={styles.agreeLink} onPress={() => startPage(PageList.privacy)}>Privacy Policy</Text>
-                <Text style={styles.agreeText}>.</Text>
+                <TextView style={styles.agreeText} onPress={() => this.changeAgree(!this.state.agree)}>
+                  {$t('sign.iHaveReadAndAgree')}
+                </TextView>
+                <TextView style={styles.agreeLink} onPress={() => startPage(PageList.condition)}>{$t('drawer.termsOfUse')}</TextView>
+                <TextView style={styles.agreeText}>{' '}</TextView>
+                <TextView style={styles.agreeText}>{$t('sign.linkAndLink')}</TextView>
+                <TextView style={styles.agreeLink} onPress={() => startPage(PageList.privacy)}>{$t('drawer.privacyPolicy')}</TextView>
+                <TextView style={styles.agreeText}>{$t('sign.linkAndLinkEnd')}</TextView>
               </View>
             </View>
             <Button
               style={styles.signButton}
               elevation={1.5}
               disabled={!this.state.agree}
-              text='SIGN UP'
+              text={$t('sign.btnSignUp')}
               fontSize={14}
               onClick={() => {
                 this.onRegister();

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.