From 7b3bcf6121d5f2383302d31d2e1fe0ff3798432a Mon Sep 17 00:00:00 2001
From: oman21 <oman21.dev@gmail.com>
Date: Mon, 18 Oct 2021 14:14:13 +0700
Subject: [PATCH] Add feature double tap for skip duration

---
 README.md |   2 +
 index.js  | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 122 insertions(+)

diff --git a/README.md b/README.md
index 83c5cca..7ca4c03 100644
--- a/README.md
+++ b/README.md
@@ -132,6 +132,8 @@ All other props are passed to the react-native-video component.
 | stop                    |                 | Stop the playback and reset back to 0:00.                                 |
 | pause                   |                 | Pause the playback.                                                       |
 | resume                  |                 | Resume the playback.                                                      |
+| enableSkip              | boolean (default: false)  | Double tap for skip duration.                                     |
+| timeSkip                | seconds: install (default: 5 sec)  | Duration time for skip.                                     |
 
 ## Future features
 
diff --git a/index.js b/index.js
index 61c3f94..a365cb7 100644
--- a/index.js
+++ b/index.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
 import { Image, ImageBackground, Platform, StyleSheet, Text, TextInput, TouchableOpacity, View, ViewPropTypes } from 'react-native';
 import Icon from 'react-native-vector-icons/MaterialIcons';
 import Video from 'react-native-video'; // eslint-disable-line
+import Entypo from 'react-native-vector-icons/Entypo';
 
 const BackgroundImage = ImageBackground || Image; // fall back to Image if RN < 0.46
 
@@ -117,6 +118,27 @@ const styles = StyleSheet.create({
   },
   durationText: {
     color: 'white'
+  },
+  skipWrapper: {
+    position:'absolute',
+    zIndex: 2
+  },
+  skipButtonWrapper: {
+    flex:1,
+    justifyContent:'center',
+    alignItems:'center'
+  },
+  skipButtonDisplay: {
+    width:100, 
+    height:100,
+    backgroundColor: '#0000007a',
+    borderRadius: 50,
+    justifyContent: 'center',
+    alignItems: 'center'
+  },
+  skipButtonDisplayText: {
+    color: '#fff',
+    fontSize: 12
   }
 });
 
@@ -134,12 +156,17 @@ export default class VideoPlayer extends Component {
       isControlsVisible: !props.hideControlsOnStart,
       duration: 0,
       isSeeking: false,
+      skipIconForward: false,
+      skipIconBackward: false,
+      enableSkip: props.enableSkip
     };
 
     this.seekBarWidth = 200;
     this.wasPlayingBeforeSeek = props.autoplay;
     this.seekTouchStart = 0;
     this.seekProgressStart = 0;
+    this.skipTapDelay = 300;
+    this.timeSkip = props.timeSkip?props.timeSkip:5;
 
     this.onLayout = this.onLayout.bind(this);
     this.onStartPress = this.onStartPress.bind(this);
@@ -332,6 +359,16 @@ export default class VideoPlayer extends Component {
     };
   }
 
+  getSizeTapWrapper() {
+    const { videoWidth, videoHeight } = this.props;
+    const { width } = this.state;
+    const ratio = videoHeight / videoWidth;
+    return {
+      height: (width * ratio)-45,
+      width,
+    };
+  }
+
   hideControls() {
     if (this.props.onHideControls) {
       this.props.onHideControls();
@@ -388,6 +425,50 @@ export default class VideoPlayer extends Component {
     this.showControls();
   }
 
+  setSkipTime(type) {
+    const w = this.seekBarWidth;
+    const t = this.state.duration;
+    const ws = (this.timeSkip*w)/t;
+    const diff = ws - this.seekTouchStart;
+    const ratio = 100 / this.seekBarWidth;
+    var progress = 0;
+    if(type==='forward'){
+      progress = this.state.progress + ((ratio * diff) / 100);
+    }else{
+      progress = this.state.progress - ((ratio * diff) / 100);
+    }
+
+    this.setState({
+      progress,
+    });
+
+    this.player.seek(progress * this.state.duration);
+  }
+
+  handleSkip(type) {
+    this.showControls();
+    const now = Date.now();
+    if (this.lastTap && (now - this.lastTap) < this.skipTapDelay) {
+      if(type==='forward'){
+        this.setState({skipIconForward: true, skipIconBackward: false}, ()=>{
+          this.setSkipTime('forward');
+          setTimeout(() => {
+            this.setState({skipIconForward: false});
+          }, 1000);
+        })
+      }else{
+        this.setState({skipIconBackward: true, skipIconForward: false}, ()=>{
+          this.setSkipTime('backward');
+          setTimeout(() => {
+            this.setState({skipIconBackward: false});
+          }, 1000);
+        })
+      }
+    } else {
+      this.lastTap = now;
+    }
+  }
+
   renderStartButton() {
     const { customStyles } = this.props;
     return (
@@ -519,6 +600,45 @@ export default class VideoPlayer extends Component {
     } = this.props;
     return (
       <View style={customStyles.videoWrapper}>
+        {
+          this.state.enableSkip?(
+            <View style={styles.skipWrapper}>
+              <View 
+                style={[
+                  this.getSizeTapWrapper(), 
+                  {flexDirection:"row"}
+                ]}
+              >
+                <TouchableOpacity 
+                  style={styles.skipButtonWrapper}
+                  onPress={() => this.handleSkip('backward')}
+                >
+                {
+                  this.state.skipIconBackward?(
+                    <View style={styles.skipButtonDisplay}>
+                      <Entypo name={"controller-fast-backward"} color={'#fff'} size={27}/>
+                      <Text style={styles.skipButtonDisplayText}>{this.timeSkip} sec</Text>
+                    </View>
+                  ):null
+                } 
+                </TouchableOpacity>
+                <TouchableOpacity 
+                  style={styles.skipButtonWrapper}
+                  onPress={() => this.handleSkip('forward')}
+                >
+                  {
+                  this.state.skipIconForward?(
+                    <View style={styles.skipButtonDisplay}>
+                      <Entypo name={"controller-fast-forward"} color={'#fff'} size={27}/>
+                      <Text style={styles.skipButtonDisplayText}>{this.timeSkip} sec</Text>
+                    </View>
+                  ):null
+                }
+                </TouchableOpacity>
+              </View>
+            </View>
+          ):null
+        }
         <Video
           {...props}
           style={[