vbea 3 lat temu
rodzic
commit
180785c23a

+ 1 - 1
Strides-APP/android/app/build.gradle

@@ -3,7 +3,7 @@ apply plugin: 'com.google.gms.google-services'
 
 import com.android.build.OutputFile
 
-def myVersionName = "2.2.0" //★★★★★版本号★★★★★
+def myVersionName = "2.2.1" //★★★★★版本号★★★★★
 /**
  * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
  * and bundleReleaseJsAndAssets).

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

@@ -1,2 +1,2 @@
-#Tue Feb 14 20:45:35 CST 2023
-VERSION_CODE=181
+#Wed Mar 15 19:12:06 CST 2023
+VERSION_CODE=184

+ 2 - 2
Strides-APP/app.json

@@ -1,8 +1,8 @@
 {
   "name": "JuicePlus",
   "displayName": "ChargEco",
-  "versionCode": 120,
-  "versionName": "V2.2.0",
+  "versionCode": 130,
+  "versionName": "V2.2.1",
   "product": false,
   "debug": true
 }

+ 15 - 0
Strides-APP/app/api/apiBase.js

@@ -0,0 +1,15 @@
+import { get, post } from "./http";
+
+const prefix = 'devicesApi/base/';
+
+export default {
+  listChargeBox(search) {
+    return get(prefix + "getChargeBoxes", {chargeBoxId: search})
+  },
+  listConnector(id) {
+    return get(prefix + "getConnectors", {chargeBoxId: id})
+  },
+  checkUpdate() {
+    return get(prefix + "checkAppVersion")
+  }
+}

+ 1 - 0
Strides-APP/app/components/Dialog.js

@@ -373,6 +373,7 @@ const andStyles = StyleSheet.create({
 });
 
 export default Dialog = {
+  styles: andStyles,
   BUTTON_OK: BUTTON_OK,
   BUTTON_CANCEL: BUTTON_CANCEL,
   showDialog: showDialog,

+ 32 - 15
Strides-APP/app/components/Dropdown.js

@@ -52,16 +52,14 @@ export default Dropdown = ({
         } else {
           changeValue(item);
         }*/
-        if (onChange) {
-          onChange(valueKey ? item[valueKey] : item, 0)
-        }
+        setChange(valueKey ? item[valueKey] : item, 0);
       } else {
-        changeItem();
+        changeItem(true);
       }
     }
   }, [list]);
 
-  const changeItem = () => {
+  const changeItem = (init) => {
     if (nameKey && valueKey) {
       for (var i = 0; i < list.length; i++) {
         let item = list[i];
@@ -70,11 +68,24 @@ export default Dropdown = ({
           if (list.length > 20) {
             setCurrent(i > 5 ? i - 4 : 0);
           }
-          break;
+          return;
         }
       }
+      if (init) {
+        const item = list[0];
+        setChange(item[valueKey], 0);
+      }
     } else {
-      changeValue(prefixText+value+suffixText);
+      if (init) {
+        let _i = list.indexOf(value);
+        if (_i >= 0) {
+          setChange(list[_i], _i);
+        } else {
+          setChange(list[0], 0);
+        }
+      } else {
+        changeValue(prefixText+value+suffixText);
+      }
     }
   }
   const showList = () => {
@@ -94,12 +105,11 @@ export default Dropdown = ({
     }*/
   }
   const renderItem = ({ item, index, separators }) => {
+    const _value = (valueKey ? item[valueKey] : item);
     if (customerItemView) {
       return customerItemView(item, index, () => {
         showDialog(false);
-        if (onChange) {
-          onChange(valueKey ? item[valueKey] : item, index)
-        }
+        setChange(_value, index);
       })
     } else {
       return (
@@ -109,14 +119,21 @@ export default Dropdown = ({
           textStyle={styles.itemText}
           onClick={() => {
             showDialog(false);
-            if (onChange) {
-              onChange(valueKey ? item[valueKey] : item, index)
-            }
-          }
-        }/>
+            setChange(_value, index);
+          }}
+          iconRight={(_value == value) && <MaterialIcons name="radio-button-checked" color={colorAccent} size={22}/>}
+        />
       )
     }
   }
+  const setChange = (v, i) => {
+    setTimeout(() => {
+      if (onChange) {
+        onChange(v, i)
+      }
+    }, 300);
+    
+  }
   return (
     <>
       <Pressable

+ 33 - 2
Strides-APP/app/pages/home/Home.js

@@ -3,8 +3,9 @@
  * @邠心vbe on 2021/08/13
  */
 import React, { Component } from 'react';
-import { View, Text, Pressable, Image, StyleSheet, BackHandler } from 'react-native';
+import { View, Text, Pressable, Image, StyleSheet, BackHandler, Linking } from 'react-native';
 import {check, request, openSettings, PERMISSIONS, RESULTS} from 'react-native-permissions';
+import apiBase from '../../api/apiBase';
 import apiStation from '../../api/apiStation';
 import Dialog from '../../components/Dialog';
 import { Styles } from '../../components/Toolbar';
@@ -14,6 +15,7 @@ import { SettingUtil } from '../Settings';
 import BottomSiteInfo from './maps/BottomSiteInfo';
 import Maps from './maps/Maps';
 import SearchTool from './maps/SearchTool';
+import app from '../../../app.json'
 
 export default class HomePage extends Component {
   constructor(props) {
@@ -72,6 +74,7 @@ export default class HomePage extends Component {
     });*/
     BackHandler.addEventListener('hardwareBackPress', this.toExit)
     this.isHide = false;
+    this.checkUpdateVersion();
   }
 
   /*componentDidUpdate() {
@@ -93,7 +96,7 @@ export default class HomePage extends Component {
   }
 
   toExit = () => {
-    console.log("beforeRemove", this.isHide);
+    //console.log("beforeRemove", this.isHide);
     if (!this.isHide) {
       if (isIOS) {
         //e.preventDefault();
@@ -112,6 +115,34 @@ export default class HomePage extends Component {
     }
   }
 
+  checkUpdateVersion() {
+    apiBase.checkUpdate().then(res => {
+      if (res.data.versionCode > app.versionCode) {
+        var desc =  JSON.parse(res.data?.versionDesc ?? "{}");
+        if (desc && typeof desc.en == "string") {
+          Dialog.showDialog({
+            title: "Version Update",
+            message: "New Version: " + res.data.versionName + "\n\n" + desc.en,
+            showCancel: !(res.data.force),
+            ok: "UPDATE NOW",
+            cancel: "LATER",
+            callback: btn => {
+              if (btn == Dialog.BUTTON_OK) {
+                //TODO 跳转到应用商店
+                const uri = isIOS
+                ? 'itms-appss://apps.apple.com/app/id1664718768'
+                : 'market://details?id=com.strides.chargeco'
+                Linking.openURL(uri);
+              }
+            }
+          })
+        }
+      }
+    }).catch(err => {
+
+    })
+  }
+
   getPermission() {
     request(
       isIOS 

+ 215 - 10
Strides-APP/app/pages/my/Feedback.js

@@ -3,7 +3,7 @@
  * @邠心vbe on 2021/04/28
  */
 import React from 'react';
-import { View, Text, StyleSheet, ScrollView, TextInput, Image, Pressable } from 'react-native';
+import { View, Text, StyleSheet, ScrollView, TextInput, Image, Pressable, FlatList } from 'react-native';
 import Button from '../../components/Button';
 import apiUpload from '../../api/apiUpload';
 import { host } from '../../api/http';
@@ -11,7 +11,9 @@ import apiUser from '../../api/apiUser';
 import Dialog from '../../components/Dialog';
 import ImagePicker from 'react-native-image-crop-picker';
 import Dropdown from '../../components/Dropdown';
+import Modal from 'react-native-modal';
 import { UploadThemes } from '../../components/ThemesConfig';
+import apiBase from '../../api/apiBase.js';
 
 const options = {
   cropping: false,
@@ -33,8 +35,15 @@ export default class Feedback extends React.Component {
     this.state= {
       typeList: [],
       feedback: '',
+      searchId: '',
+      connectorId: '',
+      chargeBoxId: '',
       typeOfFeedback: '',
-      imageUrl: ['', '', '']
+      imageUrl: ['', '', ''],
+      chargeBoxList: [],
+      connectorList: [],
+      showDialog: false,
+      searching: false
     }
   }
 
@@ -59,6 +68,7 @@ export default class Feedback extends React.Component {
         this.noTypeDialog();
       }
     });
+    this.getChargeBox("");
   }
 
   noTypeDialog() {
@@ -71,6 +81,68 @@ export default class Feedback extends React.Component {
     }, 500);
   }
 
+  changeType(type, index) {
+    this.setState({
+      typeOfFeedback: type
+    })
+  }
+
+  listChargeBox(searchId) {
+    this.setState({
+      searching: true,
+      searchId: searchId
+    }, () => {
+      setTimeout(() => {
+        this.getChargeBox(searchId)
+      }, 400);
+    });
+  }
+
+  getChargeBox(searchId) {
+    if (searchId != this.state.searchId) {
+      return;
+    }
+    apiBase.listChargeBox(this.state.searchId).then(res => {
+      if (res.data) {
+        this.setState({
+          chargeBoxList: res.data,
+          searching: false
+        })
+      } else {
+        this.setState({
+          chargeBoxList: [],
+          searching: false,
+          connectorId: ""
+        })
+      }
+    }).catch(err => {
+      this.setState({
+        chargeBoxList: [],
+        searching: false,
+        connectorId: ""
+      })
+    })
+  }
+
+  listConnector() {
+    //Dialog.showProgressDialog()
+    apiBase.listConnector(this.state.chargeBoxId).then(res => {
+      if (res.data) {
+        this.setState({
+          connectorList: res.data
+        })
+      } else {
+        this.setState({
+          connectorList: []
+        })
+      }
+    }).catch(err => {
+      this.setState({
+        connectorList: []
+      })
+    })
+  }
+
   uploadImage(index) {
     ImagePicker.openPicker(options).then(image => {
       if (image.path) {
@@ -107,7 +179,9 @@ export default class Feedback extends React.Component {
       "feedback": this.state.feedback,
       "feedbackImgOne": this.state.imageUrl[0],
       "feedbackImgTwo": this.state.imageUrl[1],
-      "feedbackImgThree": this.state.imageUrl[2]
+      "feedbackImgThree": this.state.imageUrl[2],
+      "chargeBoxId": this.state.chargeBoxId,
+      "connectorId": this.state.connectorId
     }
     Dialog.showProgressDialog();
     apiUser.feedback(params).then(res => {
@@ -121,6 +195,25 @@ export default class Feedback extends React.Component {
     });
   }
 
+  changeChargeBox(id) {
+    if (id) {
+      this.setState({
+        showDialog: false,
+        chargeBoxId: id
+      }, () => this.listConnector())
+    } else {
+      this.setState({
+        showDialog: false
+      })
+    }
+  }
+
+  changeConnector(id) {
+    this.setState({
+      connectorId: id
+    })
+  }
+
   render() {
     return (
       <ScrollView
@@ -149,17 +242,14 @@ export default class Feedback extends React.Component {
               nameKey={'value'}
               valueKey={'key'}
               placeholder='Select'
-              onChange={(value, index)=> {
-                this.setState({
-                  typeOfFeedback: value
-                })
-              }}/>
+              onChange={(value, index) => this.changeType(value, index)}/>
           </View>
           
           <Text style={styles.typeTitle}>Please fill in here (500 words)</Text>
           <TextInput
             style={styles.feedbackInput}
             multiline={true}
+            maxLength={1000}
             numberOfLines={8}
             textAlignVertical='top'
             onChangeText={text => {
@@ -167,7 +257,7 @@ export default class Feedback extends React.Component {
                 feedback: text
               });
             }}/>
-
+          <Text style={styles.typeTitle}>Please upload relevant images</Text>
           <View
             style={styles.uploadGroup}>
             { this.state.imageUrl.map((item, index) => {
@@ -192,6 +282,30 @@ export default class Feedback extends React.Component {
               })
             }
           </View>
+          {this.state.typeOfFeedback == "csf" && <>
+            <Text style={styles.typeTitle}>Which charging station?</Text>
+            <Pressable
+              style={[styles.pickerView, styles.stationView]}
+              android_ripple={ripple}
+              onPress={() => this.setState({showDialog: true})}>
+              <MaterialIcons
+                name="search"
+                size={24}
+                color="#00638C"
+              />
+              <Text style={styles.textStation}>{this.state.chargeBoxId}</Text>
+            </Pressable>
+            <Text style={styles.typeTitle}>Which connecter?</Text>
+            <View style={styles.pickerView}>
+              <Dropdown
+                style={styles.pickerViewInfo}
+                title="Select a Connecter"
+                list={this.state.connectorList}
+                value={this.state.connectorId}
+                placeholder="Select"
+                onChange={value => this.changeConnector(value)}/>
+            </View>
+          </>}
           <Button
             style={styles.button}
             text='Submit Feedback'
@@ -200,9 +314,60 @@ export default class Feedback extends React.Component {
               this.submitFeedback();
             }}/>
         </View>
+        <Modal
+          style={{zIndex: 900}}
+          isVisible={this.state.showDialog}
+          avoidKeyboard={true}
+          animationIn={"fadeIn"}
+          animationOut={"fadeOut"}
+          useNativeDriver={true}
+          onBackdropPress={() => this.changeChargeBox()}
+          onBackButtonPress={() => this.changeChargeBox()}>
+          <View style={Dialog.styles.modalDialog}>
+            <View style={[styles.pickerView, styles.stationView]}>
+              <MaterialIcons
+                name="search"
+                size={24}
+                color="#00638C"
+              />
+              <TextInput
+                style={styles.inputView}
+                maxLength={50}
+                placeholder="Searching for ChargeBoxId"
+                onChangeText={text => this.listChargeBox(text)}
+              />
+            </View>
+            { this.state.searching
+            ? <View style={[styles.stationList, ui.center]}>
+                <Image
+                  style={styles.seachingIcon}
+                  source={require('../../images/icon/search-loading.gif')}/>
+              </View>
+            : <FlatList
+                style={styles.stationList}
+                data={this.state.chargeBoxList}
+                keyExtractor={(item,index) => index}
+                renderItem={this.listItem}
+                ListEmptyComponent={<Text style={styles.noResult}>No Station</Text>}
+              />
+            }
+          </View>
+        </Modal>
       </ScrollView>
     );
   }
+
+  listItem = ({item, index}) => {
+    return (
+      <Button
+        key={index}
+        text={item}
+        style={styles.stationItemView}
+        textStyle={styles.stationItemText}
+        onClick={() => this.changeChargeBox(item)}
+      />
+    )
+  }
 }
 
 const styles = StyleSheet.create({
@@ -246,6 +411,17 @@ const styles = StyleSheet.create({
     fontWeight: 'bold',
     paddingBottom: 10
   },
+  stationView: {
+    paddingLeft: 12,
+    alignItems: 'center',
+    flexDirection: 'row'
+  },
+  textStation: {
+    flex: 1,
+    color: textPrimary,
+    fontSize: 15,
+    paddingLeft: 8
+  },
   pickerView: {
     //width: $vw(60),
     height: 44,
@@ -258,6 +434,11 @@ const styles = StyleSheet.create({
     justifyContent: 'center',
     backgroundColor: '#F5F5F5'
   },
+  inputView: {
+    flex: 1,
+    color: textPrimary,
+    fontSize: 15
+  },
   pickerViewInfo: {
     height: 44,
     paddingLeft: 16,
@@ -276,7 +457,7 @@ const styles = StyleSheet.create({
     backgroundColor: '#F5F5F5'
   },
   uploadGroup: {
-    paddingTop: 16,
+    //paddingTop: 16,
     alignItems: 'center',
     flexDirection: 'row',
     justifyContent: 'center'
@@ -294,5 +475,29 @@ const styles = StyleSheet.create({
     marginTop: 32,
     marginBottom: 16,
     borderRadius: 4
+  },
+  stationList: {
+    height: $vh(35)
+  },
+  noResult: {
+    color: textCancel,
+    fontSize: 12,
+    textAlign: 'center',
+    paddingTop: 32
+  },
+  stationItemView: {
+    borderRadius: 0,
+    backgroundColor: colorLight,
+    borderTopWidth: 1,
+    borderTopColor: '#F0F0F0'
+  },
+  stationItemText: {
+    flex: 1,
+    color: textPrimary,
+    fontSize: 15
+  },
+  seachingIcon: {
+    width: 100,
+    height: 100
   }
 })

+ 1 - 1
Strides-APP/app/utils/constant.js

@@ -13,7 +13,7 @@ import DeviceInfo from 'react-native-device-info';
 import apiUser from '../api/apiUser';
 
 navigator.geolocation = require('@react-native-community/geolocation');
-
+global.$t = (scop) => (scop);
 global.$width = Dimensions.get('window').width;
 global.$height = Dimensions.get('window').height;
 global.statusHeight = 0;