Forráskód Böngészése

UI Enhancement
https://dev.wormwood.com.sg/zentao/task-view-339.html

vbea 1 éve
szülő
commit
5b6a8ecee7

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

@@ -22,7 +22,7 @@
         <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:statusBarColor">@color/white</item>
         <item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
     </style>
 

+ 2 - 2
Strides-APP/app/components/Dialog.js

@@ -307,7 +307,7 @@ const andStyles = StyleSheet.create({
     paddingLeft: 20,
     paddingRight: 20,
     paddingBottom: 8,
-    borderRadius: 3,
+    borderRadius: 4,
     marginLeft: 'auto',
     marginRight: 'auto',
     backgroundColor: colorLight
@@ -319,7 +319,7 @@ const andStyles = StyleSheet.create({
     paddingLeft: 24,
     paddingRight: 24,
     paddingBottom: 16,
-    borderRadius: 3,
+    borderRadius: 4,
     marginLeft: 'auto',
     marginRight: 'auto',
     backgroundColor: colorLight

+ 19 - 25
Strides-APP/app/components/Dropdown.js

@@ -4,6 +4,7 @@ import Button from './Button';
 import Dialog from './Dialog';
 import TextView from './TextView';
 import app from '../../app.json';
+import MyModal from './MyModal';
 
 //const DialogMaxWidth = $vw(85) > 500 ? 500 : $vw(85);
 //const DialogIOSWidth = $vw(75) > 450 ? 450 : $vw(75);
@@ -167,32 +168,25 @@ export default Dropdown = ({
           />)
         }
       </Pressable>
-      <Modal
+      <MyModal
         visible={visible}
-        transparent={true}
-        animationType="fade"
-        statusBarTranslucent={true}>
-        <View style={styles.dialog}>
-          <Text
-            style={StyleSheet.absoluteFillObject}
-            onPress={() => showDialog(false)}>
-          </Text>
-          <View style={styles.dialogContent}>
-            { title !== '' && <TextView style={styles.title}>{title}</TextView> }
-            <FlatList
-              data={list}
-              ref={refFlat}
-              renderItem={renderItem}
-              initialScrollIndex={currentIndex}
-              keyExtractor={(item, index) => index}
-              style={{maxHeight: $vh(55)}}
-              getItemLayout={(data, index) => (
-                {length: itemHeight, offset: itemHeight * index, index}
-              )}
-            />
-          </View>
+        style={styles.dialogContent}
+        onLayerPress={() => showDialog(false)}>
+        <View style={$padding(4, 0, 0)}>
+          { title !== '' && <TextView style={styles.title}>{title}</TextView> }
+          <FlatList
+            data={list}
+            ref={refFlat}
+            renderItem={renderItem}
+            initialScrollIndex={currentIndex}
+            keyExtractor={(item, index) => index}
+            style={{maxHeight: $vh(55)}}
+            getItemLayout={(data, index) => (
+              {length: itemHeight, offset: itemHeight * index, index}
+            )}
+          />
         </View>
-      </Modal>
+      </MyModal>
     </>
   );
 }
@@ -211,7 +205,7 @@ const styles = StyleSheet.create({
     paddingTop: isIOS ? 12 : 8,
     paddingBottom: isIOS ? 12 : 8,
     backgroundColor: colorLight,
-    borderRadius: isIOS ? 10 : 3
+    borderRadius: isIOS ? 10 : 4
   },
   title: {
     color: '#000',

+ 45 - 0
Strides-APP/app/components/MyModal.js

@@ -0,0 +1,45 @@
+import React from 'react';
+import { StyleSheet, Modal, View, Text } from 'react-native';
+import Dialog from './Dialog';
+
+const MyModal = ({
+  visible=false,
+  style=styles.content,
+  layerStyle=styles.dialog,
+  animationType="fade",
+  onLayerPress,
+  children
+}) => (
+  <Modal
+    visible={visible}
+    transparent={true}
+    animationType={animationType}
+    statusBarTranslucent={true}>
+    <View style={layerStyle}>
+      <Text
+        style={StyleSheet.absoluteFillObject}
+        onPress={onLayerPress}>
+      </Text>
+      <View style={style}>
+        {children}
+      </View>
+    </View>
+  </Modal>
+);
+
+export default MyModal;
+
+const styles = StyleSheet.create({
+  dialog: {
+    flex: 1,
+    alignItems: 'center',
+    justifyContent: 'center',
+    backgroundColor: 'rgba(0,0,0,.5)'
+  },
+  content: {
+    width: Dialog.dialogWidth,
+    borderRadius: isIOS ? 16 : 8,
+    overflow: 'hidden',
+    backgroundColor: colorLight
+  }
+})

+ 3 - 3
Strides-APP/app/components/MyStatusBar.js

@@ -3,8 +3,7 @@
  * @邠心vbe on 2023/02/15
  */
 import React, { Component } from 'react';
-import { Animated, StatusBar } from 'react-native';
-import TextView from './TextView';
+import { StatusBar } from 'react-native';
 
 let statusBar;
 export default class MyStatusBar extends Component {
@@ -20,6 +19,7 @@ export default class MyStatusBar extends Component {
 
   static DARK_STYLE = "dark-content";
   static LIGHT_STYLE = "light-content";
+  static DEFAULT_STYLE = themeStatusBar;
 
   /**
    * 设置状态栏颜色
@@ -42,7 +42,7 @@ export default class MyStatusBar extends Component {
    * @param {*} theme 主题:MyStatusBar.DARK_STYLE, MyStatusBar.LIGHT_STYLE
    * @param {*} color 颜色
    */
-  static setStatusBarThemes(theme, color) {
+  static setStatusBarThemes(theme, color="transparent") {
     statusBar.setStatusBarThemes(theme, color);
   }
 

+ 1 - 1
Strides-APP/app/components/Toolbar.js

@@ -94,7 +94,7 @@ export const Styles = StyleSheet.create({
     alignItems: 'center',
     flexDirection: 'row',
     justifyContent: 'center',
-    backgroundColor: colorPrimary
+    backgroundColor: colorPrimaryDark
   },
   backIcon: {
     width: 48,

+ 1 - 1
Strides-APP/app/components/VbeSkeleton.js

@@ -38,7 +38,7 @@ const VbeSkeleton = ({
   layout=[],
   numberOfLines,
   animationType=ANIMATION_TYPE.SHIVER,
-  animationDirection=ANIMATION_DIRECTION.HORIZONTAL_LEFT,
+  animationDirection=ANIMATION_DIRECTION.HORIZONTAL_RIGHT,
   easing=Easing.bezier(0.5, 0, 0.25, 1),
   isLoading=true,
   boneColor='#eeeeee',

+ 4 - 4
Strides-APP/app/pages/Router.js

@@ -320,13 +320,13 @@ export var PageList = {
     component: ViewAlerts
   },
   'viewArticle': {
-    title: 'View Article',
-    titleScope: 'notification.viewMessage',
+    //title: 'View Article',
+    //titleScope: 'notification.viewMessage',
     component: ViewArticle
   },
   'viewCampaign': {
-    title: 'View Campaign',
-    titleScope: 'notification.viewMessage',
+    //title: 'View Campaign',
+    //titleScope: 'notification.viewMessage',
     component: ViewCampaign
   },
   'notify': {

+ 76 - 21
Strides-APP/app/pages/alert/ViewArticle.js

@@ -3,14 +3,15 @@
  * @邠心vbe on 2023/10/24
  */
 import React, { Component } from 'react';
-import { View,  StyleSheet, Image, ScrollView, Linking } from 'react-native';
+import { View,  StyleSheet, Image, ScrollView, Linking, Animated } from 'react-native';
 import Swiper from 'react-native-swiper';
 import apiArticle from '../../api/apiArticle';
-import HeaderTitle from '../../components/HeaderTitle';
 import TextView from '../../components/TextView';
 import VbeSkeleton from '../../components/VbeSkeleton';
 import utils from '../../utils/utils';
 import { PagerView } from './ViewUtil';
+import MyStatusBar from '../../components/MyStatusBar';
+import Toolbar, { BackButton } from '../../components/Toolbar';
 
 export default class ViewArticle extends Component {
   constructor(props) {
@@ -18,11 +19,13 @@ export default class ViewArticle extends Component {
     this.state = {
       id: "",
       loading: true,
+      showTitleBar: false,
       messageInfo: {
         articleTypeName: "",
         articleTitle: "",
         articleContent: ""
-      }
+      },
+      opacity: 1
     };
   }
 
@@ -34,6 +37,10 @@ export default class ViewArticle extends Component {
         this.readMessage();
       })
     }
+    MyStatusBar.setStatusBarTheme(MyStatusBar.LIGHT_STYLE);
+    this.props.navigation.addListener('beforeRemove', (e) => {
+      MyStatusBar.setStatusBarTheme(MyStatusBar.DEFAULT_STYLE);
+    });
   }
 
   readMessage() {
@@ -51,9 +58,9 @@ export default class ViewArticle extends Component {
 
   setPageTitle() {
     if (this.state.messageInfo.articleTitle) {
-      this.props.navigation.setOptions({
+      /*this.props.navigation.setOptions({
         headerTitle: () => (<HeaderTitle title={this.state.messageInfo.articleTitle}/>)
-      });
+      })*/
       setTimeout(() => {
         this.setState({
           loading: false
@@ -66,6 +73,32 @@ export default class ViewArticle extends Component {
     Linking.openURL(utils.getImageUrl(url))
   }
 
+  onScrollView(e) {
+    if (e.nativeEvent.contentOffset) {
+      const isR = e.nativeEvent.contentOffset.y >= $vw(91);
+      if (isR != this.state.showTitleBar) {
+        this.setState({
+          opacity: new Animated.Value(0),
+          showTitleBar: isR
+        }, () => {
+          this.startTitleAnimate();
+        });
+        MyStatusBar.setStatusBarTheme(isR ? MyStatusBar.DEFAULT_STYLE : MyStatusBar.LIGHT_STYLE);
+      }
+    }
+  }
+
+  startTitleAnimate() {
+    Animated.timing(this.state.opacity, {
+      toValue: 1,
+      duration: 200,
+      easing: Easing.linear,
+      useNativeDriver: true
+    }).start(() => {
+      
+    });
+  }
+
   render() {
     if (this.state.loading) {
       return (
@@ -92,6 +125,7 @@ export default class ViewArticle extends Component {
       )
     } else {
       return (
+        <>
         <ScrollView
           style={styles.container}
           contentContainerStyle={$padding(0,0,32)}
@@ -128,17 +162,17 @@ export default class ViewArticle extends Component {
                 numberOfLines={1}>
                 {this.state.messageInfo.createTime}
               </TextView>
-            </View>
-            <View style={ui.flexc}>
-              <MaterialCommunityIcons
-                name="eye-check-outline"
-                size={12}
-                color={textPrimary}/>
-              <TextView
-                style={styles.textView}
-                numberOfLines={1}>
-                {this.state.messageInfo.articleViews}
-              </TextView>
+              <View style={ui.flexc}>
+                <MaterialCommunityIcons
+                  name="eye-check-outline"
+                  size={12}
+                  color={textPrimary}/>
+                <TextView
+                  style={styles.textView}
+                  numberOfLines={1}>
+                  {this.state.messageInfo.articleViews}
+                </TextView>
+              </View>
             </View>
             <View style={ui.flex}>
               <TextView 
@@ -159,7 +193,7 @@ export default class ViewArticle extends Component {
               { this.state.messageInfo.articleLinks.map((item, index) =>
                 <View style={styles.itemLink} key={index}>
                   <TextView style={styles.linkIndex}>{index + 1}.</TextView>
-                  <TextView 
+                  <TextView
                     style={styles.linkHyper}
                     onPress={() => this.accessLink(item.articleLink)}>{item.articleLinkName}</TextView>
                 </View>
@@ -168,6 +202,13 @@ export default class ViewArticle extends Component {
           }
           <EndView/>
         </ScrollView>
+        <Animated.View style={[styles.toolbar, {opacity: this.state.opacity}]}>
+          { this.state.showTitleBar
+          ? <Toolbar title={this.state.messageInfo.articleTitle}/>
+          : <BackButton style={styles.backIcon} color={"#F0F0F0"}/>
+          }
+        </Animated.View>
+        </>
       );
     }
   }
@@ -178,6 +219,21 @@ const styles = StyleSheet.create({
     flex: 1,
     backgroundColor: pageBackground
   },
+  toolbar: {
+    top: 0,
+    left: 0,
+    right: 0,
+    position: 'absolute'
+  },
+  backIcon: {
+    width: 48,
+    height: 48,
+    zIndex: 2,
+    marginTop: statusHeight + 4,
+    marginLeft: 4,
+    alignItems: 'center',
+    justifyContent: 'center'
+  },
   loadingView: {
     flex: 1,
     padding: 16,
@@ -186,24 +242,23 @@ const styles = StyleSheet.create({
   },
   textTitle: {
     color: textPrimary,
-    fontSize: 14,
+    fontSize: 18,
     fontWeight: 'bold',
     paddingBottom: 2
   },
   textDate: {
     flex: 1,
     color: textSecondary,
-    fontSize: 12,
+    fontSize: 10,
     paddingLeft: 2
   },
   textView: {
     color: textSecondary,
-    fontSize: 14,
+    fontSize: 10,
     padding: 4
   },
   header: {
     padding: 16,
-    //...ElevationObject(1),
     backgroundColor: pageBackground
   },
   textMessage: {

+ 68 - 13
Strides-APP/app/pages/alert/ViewCampaign.js

@@ -2,16 +2,16 @@
  * 活动详情
  * @邠心vbe on 2023/08/17
  */
- import React, { Component } from 'react';
- import { View,  StyleSheet, Image, ScrollView, Linking } from 'react-native';
- import Swiper from 'react-native-swiper';
- import apiArticle from '../../api/apiArticle';
-import { ElevationObject } from '../../components/Button';
- import HeaderTitle from '../../components/HeaderTitle';
- import TextView from '../../components/TextView';
- import utils from '../../utils/utils';
-import { PagerView } from './ViewUtil';
+import React, { Component } from 'react';
+import { View,  StyleSheet, Image, ScrollView, Linking, Animated, Easing } from 'react-native';
+import Swiper from 'react-native-swiper';
+import apiArticle from '../../api/apiArticle';
+import TextView from '../../components/TextView';
 import VbeSkeleton from '../../components/VbeSkeleton';
+import utils from '../../utils/utils';
+import { PagerView } from './ViewUtil';
+import MyStatusBar from '../../components/MyStatusBar';
+import Toolbar, { BackButton } from '../../components/Toolbar';
 
 export default class ViewCampaign extends Component {
   constructor(props) {
@@ -19,11 +19,13 @@ export default class ViewCampaign extends Component {
     this.state = {
       id: "",
       loading: true,
+      showTitleBar: false,
       messageInfo: {
         articleTypeName: "",
         articleTitle: "",
         articleContent: ""
-      }
+      },
+      opacity: 1
     };
   }
 
@@ -35,6 +37,10 @@ export default class ViewCampaign extends Component {
         this.readMessage();
       })
     }
+    MyStatusBar.setStatusBarTheme(MyStatusBar.LIGHT_STYLE);
+    this.props.navigation.addListener('beforeRemove', (e) => {
+      MyStatusBar.setStatusBarTheme(MyStatusBar.DEFAULT_STYLE);
+    });
   }
 
   readMessage() {
@@ -52,9 +58,9 @@ export default class ViewCampaign extends Component {
 
   setPageTitle() {
     if (this.state.messageInfo.articleTitle) {
-      this.props.navigation.setOptions({
+      /*this.props.navigation.setOptions({
         headerTitle: () => (<HeaderTitle title={this.state.messageInfo.articleTitle}/>)
-      })
+      })*/
       setTimeout(() => {
         this.setState({
           loading: false
@@ -80,6 +86,32 @@ export default class ViewCampaign extends Component {
     }
   }
 
+  onScrollView(e) {
+    if (e.nativeEvent.contentOffset) {
+      const isR = e.nativeEvent.contentOffset.y >= $vw(91);
+      if (isR != this.state.showTitleBar) {
+        this.setState({
+          opacity: new Animated.Value(0),
+          showTitleBar: isR
+        }, () => {
+          this.startTitleAnimate();
+        });
+        MyStatusBar.setStatusBarTheme(isR ? MyStatusBar.DARK_STYLE : MyStatusBar.LIGHT_STYLE);
+      }
+    }
+  }
+
+  startTitleAnimate() {
+    Animated.timing(this.state.opacity, {
+      toValue: 1,
+      duration: 200,
+      easing: Easing.linear,
+      useNativeDriver: true
+    }).start(() => {
+      
+    });
+  }
+
   render() {
     if (this.state.loading) {
       return (
@@ -106,8 +138,10 @@ export default class ViewCampaign extends Component {
       )
     }
     return (
+      <>
       <ScrollView
         style={styles.container}
+        onScroll={e => this.onScrollView(e)}
         contentContainerStyle={$padding(0,0,32)}
         stickyHeaderIndices={[utils.isNotEmpty(this.state.messageInfo.articleImages) ? 1 : 0]}>
         { utils.isNotEmpty(this.state.messageInfo.articleImages) &&
@@ -199,6 +233,13 @@ export default class ViewCampaign extends Component {
         }
         <EndView/>
       </ScrollView>
+      <Animated.View style={[styles.toolbar, {opacity: this.state.opacity}]}>
+        { this.state.showTitleBar
+        ? <Toolbar title={this.state.messageInfo.articleTitle}/>
+        : <BackButton style={styles.backIcon} color={"#F0F0F0"}/>
+        }
+      </Animated.View>
+      </>
     );
   }
 }
@@ -208,6 +249,21 @@ const styles = StyleSheet.create({
     flex: 1,
     backgroundColor: pageBackground
   },
+  toolbar: {
+    top: 0,
+    left: 0,
+    right: 0,
+    position: 'absolute'
+  },
+  backIcon: {
+    width: 48,
+    height: 48,
+    zIndex: 2,
+    marginTop: statusHeight + 4,
+    marginLeft: 4,
+    alignItems: 'center',
+    justifyContent: 'center'
+  },
   loadingView: {
     flex: 1,
     padding: 16,
@@ -233,7 +289,6 @@ const styles = StyleSheet.create({
   },
   header: {
     padding: 16,
-    ...ElevationObject(1),
     backgroundColor: pageBackground
   },
   textSummary: {

+ 1 - 1
Strides-APP/app/pages/chargingV2/DialogPayPerUse.js

@@ -65,7 +65,7 @@ const styles = StyleSheet.create({
   },
   content: {
     width: Dialog.dialogWidth,
-    borderRadius: 16,
+    borderRadius: isIOS ? 16 : 12,
     overflow: 'hidden',
     backgroundColor: colorLight
   },

+ 1 - 1
Strides-APP/app/pages/home/Home.js

@@ -89,7 +89,7 @@ export default class HomePage extends Component {
         MyStatusBar.setStatusBarThemes(themeStatusBar, colorPrimaryDark);
       }*/
       setTimeout(() => {
-        MyStatusBar.setStatusBarTheme(app.isWhitelabel ? MyStatusBar.DARK_STYLE : MyStatusBar.LIGHT_STYLE);
+        MyStatusBar.setStatusBarTheme(MyStatusBar.DEFAULT_STYLE);
       }, 50);
       setTimeout(() => {
         navigation.closeDrawer();

+ 32 - 25
Strides-APP/app/pages/my/Feedback.js

@@ -14,6 +14,8 @@ import Modal from 'react-native-modal';
 import { UploadThemes } from '../../components/ThemesConfig';
 import apiBase from '../../api/apiBase.js';
 import utils from '../../utils/utils';
+import MyModal from '../../components/MyModal';
+import VbeSkeleton from '../../components/VbeSkeleton';
 
 const options = {
   cropping: false,
@@ -287,17 +289,17 @@ export default class Feedback extends React.Component {
 
           {this.state.typeOfFeedback == "csf" && <>
             <Text style={styles.typeTitle}>{$t('feedback.labelStation')}</Text>
-            <Pressable
-              style={[styles.pickerView, styles.stationView]}
-              android_ripple={ripple}
-              onPress={() => this.setState({showDialog: true})}>
+            <Button
+              style={styles.pickerView}
+              viewStyle={[styles.stationView, ui.flex1]}
+              onClick={() => this.setState({showDialog: true})}>
               <MaterialIcons
                 name="search"
                 size={24}
                 color="#00638C"
               />
               <Text style={styles.textStation}>{this.state.chargeBoxId}</Text>
-            </Pressable>
+            </Button>
             <Text style={styles.typeTitle}>{$t('feedback.labelConnector')}</Text>
             <View style={styles.pickerView}>
               <Dropdown
@@ -317,18 +319,11 @@ 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}
-          statusBarTranslucent={true}
-          onBackdropPress={() => this.changeChargeBox()}
-          onBackButtonPress={() => this.changeChargeBox()}>
-          <View style={Dialog.styles.modalDialog}>
-            <View style={[styles.pickerView, styles.stationView]}>
+        <MyModal
+          visible={this.state.showDialog}
+          onLayerPress={() => this.changeChargeBox()}>
+          <View style={$padding(8, 0)}>
+            <View style={styles.searchView}>
               <MaterialIcons
                 name="search"
                 size={24}
@@ -342,11 +337,13 @@ export default class Feedback extends React.Component {
               />
             </View>
             { this.state.searching
-            ? <View style={[styles.stationList, ui.center]}>
-                <Image
-                  style={styles.seachingIcon}
-                  source={require('../../images/icon/search-loading.gif')}/>
-              </View>
+            ? <VbeSkeleton
+                style={styles.stationList}
+                layout={[
+                  {width: "80%", height: 20, ...$margin(14, 0, 14, 16)},
+                  {width: "80%", height: 20, ...$margin(14, 0, 14, 16)},
+                  {width: "80%", height: 20, ...$margin(14, 0, 14, 16)}
+                ]}/>
             : <FlatList
                 style={styles.stationList}
                 data={this.state.chargeBoxList}
@@ -356,7 +353,7 @@ export default class Feedback extends React.Component {
               />
             }
           </View>
-        </Modal>
+        </MyModal>
       </ScrollView>
     );
   }
@@ -432,8 +429,6 @@ const styles = StyleSheet.create({
     borderWidth: 1,
     borderColor: '#999',
     borderRadius: 6,
-    marginTop: 8,
-    marginBottom: 8,
     overflow: 'hidden',
     justifyContent: 'center',
     backgroundColor: '#F5F5F5'
@@ -450,6 +445,18 @@ const styles = StyleSheet.create({
     alignItems: 'center',
     flexDirection: 'row'
   },
+  searchView: {
+    height: 44,
+    paddingLeft: 12,
+    borderWidth: 1,
+    borderColor: '#999',
+    borderRadius: 6,
+    ...$margin(8, 16, 16),
+    overflow: 'hidden',
+    alignItems: 'center',
+    flexDirection: 'row',
+    backgroundColor: '#F5F5F5'
+  },
   feedbackInput: {
     color: textPrimary,
     minHeight: 100,

+ 76 - 58
Strides-APP/app/pages/wallet/History.js

@@ -8,6 +8,7 @@ import apiWallet from '../../api/apiWallet';
 import TextView from '../../components/TextView';
 import { PageList } from '../Router';
 import app from '../../../app.json';
+import VbeSkeleton from '../../components/VbeSkeleton';
 
 const IconCharge = require('../../images/wallet/ic-type-charge.png');
 const IconPayment = require('../../images/wallet/ic-type-payment.png');
@@ -16,17 +17,19 @@ export default class History extends Component {
   constructor(props) {
     super(props);
     this.state = {
-      historyList: []
+      loading: true,
+      historyList: [],
+      loadingList: ["", "", "", "", ""]
     };
     this.refreshing = false;
   }
 
   componentDidMount() {
-    this.getHistory();
+    //this.getHistory();
   }
 
   componentDidUpdate() {
-    if (this.props.refresh && !this.refreshing && this.props.shown) {
+    if ((this.state.loading || this.props.refresh) && !this.refreshing && this.props.shown) {
       this.refreshing = true;
       this.getHistory();
     }
@@ -45,7 +48,6 @@ export default class History extends Component {
       return;
     }
     apiWallet.getTransactionList({latestPk: ''}).then(res => {
-      this.stopRefresh();
       if (res.data) {
         this.setState({
           historyList: res.data
@@ -55,13 +57,16 @@ export default class History extends Component {
       this.setState({
         historyList: []
       });
+    }).finally(() => {
+      this.setState({
+        loading: false,
+      });
       this.stopRefresh();
     });
   }
 
   getHistoryV2() {
     apiWallet.getTransactionListV2({latestPk: ''}).then(res => {
-      this.stopRefresh();
       if (res.data && res.data.length) {
         this.setState({
           historyList: res.data.slice(0, 10)
@@ -75,6 +80,10 @@ export default class History extends Component {
       this.setState({
         historyList: []
       });
+    }).finally(() => {
+      this.setState({
+        loading: false,
+      });
       this.stopRefresh();
     });
   }
@@ -112,58 +121,67 @@ export default class History extends Component {
   render() {
     return (
       <View style={this.props.shown ? ui.flex1 : styles.hide}>
-        {/* <View style={ui.flexcc}>
-          <Text style={styles.rangeText}>9th Aug to 12th Aug</Text>
-          <MaterialIcons
-            name='arrow-drop-down'
-            color={colorDark}
-            size={28}/>
-        </View> */}
-        <View style={styles.listView}>
-        { this.state.historyList.length > 0
-          ? <> 
-            {
-              this.state.historyList.map((item, index) => {
-                return (
-                  <Pressable
-                    key={index}
-                    android_ripple={ripple}
-                    style={styles.itemView}
-                    onPress={() => {
-                      this.toSummary(index)
-                    }}>
-                    <Image
-                      style={styles.iconType}
-                      resizeMode="contain"
-                      source={(item.amountSymbol == 'P' || item.remarks) ? IconPayment : IconCharge}/>
-                    <View style={[styles.itemContent, index > 0 && styles.divide]}>
-                      <View style={ui.flex1}>
-                        <TextView style={styles.issueName}>{this.getTransTitle(item)}</TextView>
-                        <TextView style={styles.issueDesc}>{$t('wallet.labelTransactionId') + item.creditRecordPk}</TextView>
-                      </View>
-                      { item.amountSymbol == 'M'
-                        ? <TextView style={styles.amountDuct}>- {item.amount}</TextView>
-                        : <TextView style={styles.amountText}>+ {item.amount}</TextView>
-                      }
-                    </View>
-                  </Pressable>
-                );
-              })
-            }
-            </>
-          : <Text style={styles.noResult}>{$t('wallet.noHistoryData')}</Text>
-        }
-        </View>
-        { (app.v3.overview && this.state.historyList.length > 0) && 
-          <TouchableOpacity
-            style={styles.moreButton}
-            activeOpacity={0.4}
-            onPress={() => this.toHistoryList()}>
-            <Text
-              style={ui.link}>
-              {$t("wallet.viewMore")}
-            </Text>
-          </TouchableOpacity>
+        { this.state.loading
+        ? <View style={styles.listView}>
+          { this.state.loadingList.map((item, index) =>
+            <View style={styles.itemView} key={index}>
+              <VbeSkeleton
+                style={styles.iconType}
+                layout={[
+                  {width: 32, height: 32, borderRadius: 30, marginRight: 16}
+                ]}/>
+              <View style={styles.itemContent}>
+                <VbeSkeleton
+                  style={ui.flex1}
+                  layout={[
+                    {width: '100%', height: 15, marginTop: 4},
+                    {width: '60%', height: 15, marginTop: 4}
+                  ]}/>
+              </View>
+            </View>
+          )}
+          </View>
+        : <><View style={styles.listView}>
+          { this.state.historyList.length > 0
+          ? this.state.historyList.map((item, index) => (
+              <Pressable
+                key={index}
+                android_ripple={ripple}
+                style={styles.itemView}
+                onPress={() => {
+                  this.toSummary(index)
+                }}>
+                <Image
+                  style={styles.iconType}
+                  resizeMode="contain"
+                  source={(item.amountSymbol == 'P' || item.remarks) ? IconPayment : IconCharge}/>
+                <View style={[styles.itemContent, index > 0 && styles.divide]}>
+                  <View style={ui.flex1}>
+                    <TextView style={styles.issueName}>{this.getTransTitle(item)}</TextView>
+                    <TextView style={styles.issueDesc}>{$t('wallet.labelTransactionId') + item.creditRecordPk}</TextView>
+                  </View>
+                  { item.amountSymbol == 'M'
+                    ? <TextView style={styles.amountDuct}>- {item.amount}</TextView>
+                    : <TextView style={styles.amountText}>+ {item.amount}</TextView>
+                  }
+                </View>
+              </Pressable>
+            ))
+            : <Text style={styles.noResult}>{$t('wallet.noHistoryData')}</Text>
+          }
+          </View>
+          { (app.v3.overview && this.state.historyList.length > 0) && 
+            <TouchableOpacity
+              style={styles.moreButton}
+              activeOpacity={0.4}
+              onPress={() => this.toHistoryList()}>
+              <Text
+                style={ui.link}>
+                {$t("wallet.viewMore")}
+              </Text>
+            </TouchableOpacity>
+          }
+        </>
         }
       </View>
     );
@@ -179,7 +197,7 @@ const styles = StyleSheet.create({
     fontSize: 14
   },
   listView: {
-    marginTop: 16,
+    marginTop: 0,
     minHeight: $vh(30),
     backgroundColor: colorLight
   },