├── .gitignore
├── .npmignore
├── LICENCE
├── README.md
├── assets
├── icons
│ ├── player-pause@1x.png
│ ├── player-pause@2x.png
│ ├── player-pause@3x.png
│ ├── player-play.png
│ ├── player-play@2x.png
│ ├── player-play@3x.png
│ ├── player-restart.png
│ ├── player-restart@2x.png
│ └── player-restart@3x.png
└── screen-ios.png
├── doc
├── custom-controls-bar.md
├── full-screen-player.md
└── install.md
├── package.json
├── src
├── images
│ └── index.ts
├── index.ts
└── modules
│ └── video-player
│ ├── components
│ ├── DefaultBottomControlsBar.tsx
│ ├── DefaultMainControl.tsx
│ ├── PlayerIcon.tsx
│ ├── PlayerLoader.tsx
│ └── VideoPlayer.tsx
│ ├── hooks
│ └── useVideoState.ts
│ ├── index.ts
│ ├── logic
│ └── utils.ts
│ └── types.ts
├── tsconfig.json
├── tslint.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | build/
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .vscode/
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Andréas Hanss
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | React Native True Sight
3 |
4 |
5 |
6 |
7 | A cross-platform video player with customizable controls.
8 |
9 |
10 |
11 | This library provide a fully customisable video player that work both on Android and iOS. It also come with common use case documentation of things that you would like to implements.
12 |
13 | By default there are two controls slots that are displayed respectively on different part of the parent container and you can use default components provided by this library:
14 |
15 | - **Middle**. Contain by default a grid display two buttons:
16 | - One with play / pause alternating.
17 | - Another that will restart the video.
18 | - **Bottom**. Contain the video current time, a progress bar and the total duration.
19 | - **Loader**. There is also a loader that will trigger while video is charging (network issues, bootstraping, ...).
20 |
21 | ## Documentation
22 |
23 | - [Installation chapter](./doc/install.md)
24 | - [Render a FullScreen Video player](./doc/full-screen-player.md)
25 | - [Implement your own controls bar](./doc/custom-controls-bar.md)
26 |
27 | # Quick documentation
28 |
29 | This is simple as that.
30 |
31 | VideoPlayer ship around any video component, but fits well with react-video. In v2 you've total control on the video component.
32 |
33 | - **autoStart** - Whether or not the video should start when rendered (Default to true).
34 | - **mainControl** - The component used to render the main control bar, you can use the default one provided by this lib or your own.
35 | - **bottomControl** - The component used to render the bottom control bar, you can use the default one provided by this lib or your own.
36 |
37 | For advanced configuration, such as infinite loop, check the rest of the documentation and custom controls bar.
38 |
39 | ```jsx
40 | import React, { Component } from "react";
41 | import { View } from "react-native";
42 | import Video from "react-native-video";
43 | import { VideoPlayer, DefaultMainControl, DefaultBottomControlsBar } from "react-native-true-sight";
44 |
45 | export default class HomeScreen extends Component {
46 | render() {
47 | return (
48 | }
51 | bottomControl={args => }
52 | >
53 | {args => (
54 |
62 | )}
63 |
64 | );
65 | }
66 | }
67 | ```
68 |
--------------------------------------------------------------------------------
/assets/icons/player-pause@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScreamZ/react-native-true-sight/cd520331d0e590a7377f7b360bec64f4a56018ea/assets/icons/player-pause@1x.png
--------------------------------------------------------------------------------
/assets/icons/player-pause@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScreamZ/react-native-true-sight/cd520331d0e590a7377f7b360bec64f4a56018ea/assets/icons/player-pause@2x.png
--------------------------------------------------------------------------------
/assets/icons/player-pause@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScreamZ/react-native-true-sight/cd520331d0e590a7377f7b360bec64f4a56018ea/assets/icons/player-pause@3x.png
--------------------------------------------------------------------------------
/assets/icons/player-play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScreamZ/react-native-true-sight/cd520331d0e590a7377f7b360bec64f4a56018ea/assets/icons/player-play.png
--------------------------------------------------------------------------------
/assets/icons/player-play@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScreamZ/react-native-true-sight/cd520331d0e590a7377f7b360bec64f4a56018ea/assets/icons/player-play@2x.png
--------------------------------------------------------------------------------
/assets/icons/player-play@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScreamZ/react-native-true-sight/cd520331d0e590a7377f7b360bec64f4a56018ea/assets/icons/player-play@3x.png
--------------------------------------------------------------------------------
/assets/icons/player-restart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScreamZ/react-native-true-sight/cd520331d0e590a7377f7b360bec64f4a56018ea/assets/icons/player-restart.png
--------------------------------------------------------------------------------
/assets/icons/player-restart@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScreamZ/react-native-true-sight/cd520331d0e590a7377f7b360bec64f4a56018ea/assets/icons/player-restart@2x.png
--------------------------------------------------------------------------------
/assets/icons/player-restart@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScreamZ/react-native-true-sight/cd520331d0e590a7377f7b360bec64f4a56018ea/assets/icons/player-restart@3x.png
--------------------------------------------------------------------------------
/assets/screen-ios.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScreamZ/react-native-true-sight/cd520331d0e590a7377f7b360bec64f4a56018ea/assets/screen-ios.png
--------------------------------------------------------------------------------
/doc/custom-controls-bar.md:
--------------------------------------------------------------------------------
1 | # Extending the controllers
2 |
3 | You can extends the default bar by providing your own bar component. I suggest to copy the default component in order to have an example.
4 |
5 | For that please clone this repository, and run `npm run build`, this will generate the `build` folder with the typescript compiled code.
6 |
7 | - `DefaultBottomControlsBar` is the bar of the bottom of the screen, with timers and timeline.
8 | - `DefaultMainControl` is the bar of the middle of the screen, with video controls buttons.
9 |
10 | ## Custom components injected props
11 | Both components receive the following props, you can also provide your own if you extends them.
12 |
13 | ```ts
14 | // Common interface
15 | interface InjectedControlProps {
16 | playCursorTime: number;
17 | videoTotalTime: number;
18 | videoPaused: boolean;
19 | videoLoading: boolean;
20 | setPlaying(): void;
21 | setPaused(): void;
22 | setPosition(to: number): void;
23 | }
24 |
25 | interface MainControlProps extends InjectedControlProps {
26 | restartButton?: boolean;
27 | }
28 |
29 | interface BottomControlProps extends InjectedControlProps {
30 | barColor?: string;
31 | joyStickColor?: string;
32 | navigationDisabled?: boolean;
33 | }
34 | ```
--------------------------------------------------------------------------------
/doc/full-screen-player.md:
--------------------------------------------------------------------------------
1 |
2 | # Handling fullscreen
3 |
4 | The best way to deal with that is to display a full screen modal and render a `VideoPlayer` component.
5 |
6 | Don't forget to add a background around your video. Here we use a black one.
7 |
8 | ```jsx
9 | import PropTypes from 'prop-types'
10 | import React, { PureComponent } from 'react'
11 | import { StatusBar, View } from 'react-native'
12 | import VideoPlayer from 'react-native-true-sight'
13 | import Immersive from 'react-native-immersive'
14 |
15 | import CloseButton from '../../Components/Common/CloseButton'
16 |
17 | export default class FullScreenVideoModal extends PureComponent {
18 | render() {
19 | return (
20 |
21 |
22 |
23 |
24 |
25 | )
26 | }
27 | }
28 | ```
29 |
30 | # Android specific
31 |
32 | For cross compatibility this library doesn't ship with a tool that allow fullscreen on android.
33 |
34 | But you can easily implement this behavior using a full screen library like [React Native Immersive](https://github.com/mockingbot/react-native-immersive).
35 |
36 | Here is an example :
37 |
38 | ```jsx
39 | import PropTypes from 'prop-types'
40 | import React, { Component } from 'react'
41 | import { Platform, StatusBar, View } from 'react-native'
42 | import { VideoPlayer, DefaultMainControl, DefaultBottomControlsBar } from "react-native-true-sight";
43 | import Immersive from 'react-native-immersive'
44 | import CloseButton from '../../Components/Common/CloseButton'
45 |
46 | export default class FullScreenVideoModal extends Component {
47 | constructor(props) {
48 | super(props)
49 |
50 | this.onClose = this.onClose.bind(this)
51 | }
52 |
53 | componentDidMount() {
54 | if (Platform.OS === 'android') Immersive.on()
55 | }
56 |
57 | componentWillUnmount() {
58 | if (Platform.OS === 'android') Immersive.off()
59 | }
60 |
61 | onClose() {
62 | // Dismiss modal
63 | }
64 |
65 | render() {
66 | // You just need to flex: 1 and display and overlay a close button with position: absolute;
67 | return (
68 |
69 |
70 |
71 | }
74 | bottomControl={args => }
75 | >
76 | {args => (
77 |
85 | )}
86 |
87 |
88 | )
89 | }
90 | }
91 | ```
--------------------------------------------------------------------------------
/doc/install.md:
--------------------------------------------------------------------------------
1 | # Step 1 - Install library
2 |
3 | Run either `npm i react-native-true-sight --save` or `yarn add react-native-true-sight`
4 |
5 | # Step 2 - Install dependencies
6 |
7 | This library best fit with [React Native Video](https://github.com/react-native-community/react-native-video) and has a required dependency with [React Native Slider](https://github.com/react-native-community/react-native-slider).
8 |
9 | As in V2 everything is thinking modular, you can use other library but you'll need it for the default components.
10 |
11 | Please be sure to follow the doc in order to set-up those correctly.
12 |
13 | **Now you're ready to go!**
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-true-sight",
3 | "description": "A cross-platform video player with customizable controls for React Native.",
4 | "version": "2.0.0",
5 | "license": "MIT",
6 | "author": {
7 | "name": "Andréas HANSS",
8 | "url": "https://www.linkedin.com/in/andreas-hanss/"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/ScreamZ/react-native-true-sight.git"
13 | },
14 | "bugs": {
15 | "url": "https://github.com/ScreamZ/react-native-true-sight/issues"
16 | },
17 | "main": "build/index.js",
18 | "types": "build/index.d.ts",
19 | "scripts": {
20 | "test": "jest",
21 | "build": "tsc",
22 | "build:watch": "tsc --watch",
23 | "prepublishOnly": "tsc"
24 | },
25 | "peerDependencies": {
26 | "@react-native-community/slider": "^3.0.3",
27 | "react": "*",
28 | "react-native": ">=0.60.0",
29 | "react-native-video": "^5.0.0"
30 | },
31 | "devDependencies": {
32 | "@react-native-community/slider": "^3.0.3",
33 | "@types/react": "^16.9.49",
34 | "@types/react-native": "^0.63.17",
35 | "@types/react-native-video": "^5.0.1",
36 | "jest": "^22.2.2",
37 | "tslint": "^5.9.1",
38 | "typescript": "^4.0.2"
39 | },
40 | "jest": {
41 | "preset": "react-native"
42 | },
43 | "keywords": [
44 | "react",
45 | "react-native",
46 | "video",
47 | "video-player",
48 | "customizable",
49 | "cross-platform"
50 | ]
51 | }
52 |
--------------------------------------------------------------------------------
/src/images/index.ts:
--------------------------------------------------------------------------------
1 | export const playerPause = require('../../assets/icons/player-pause.png')
2 | export const playerPlay = require('../../assets/icons/player-play.png')
3 | export const playerRestart = require('../../assets/icons/player-restart.png')
4 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./modules/video-player";
2 |
--------------------------------------------------------------------------------
/src/modules/video-player/components/DefaultBottomControlsBar.tsx:
--------------------------------------------------------------------------------
1 | import React, { useRef } from "react";
2 | import { View, Text, StyleSheet } from "react-native";
3 | import Slider from "@react-native-community/slider";
4 | import { secondsToMS } from "../logic/utils";
5 | import { InjectedControlProps } from "../types";
6 |
7 | interface BottomControlProps extends InjectedControlProps {
8 | barColor?: string;
9 | joyStickColor?: string;
10 | navigationDisabled?: boolean;
11 | }
12 |
13 | export const DefaultBottomControlsBar: React.FC = (
14 | props
15 | ) => {
16 | const wasPausedBeforeSliding = useRef(props.videoPaused);
17 |
18 | return (
19 |
20 |
21 | {secondsToMS(props.playCursorTime)}
22 |
23 | {
31 | wasPausedBeforeSliding.current = props.videoPaused; // To know if we need to play after sliding.
32 | props.setPaused();
33 | }}
34 | onSlidingComplete={(val) => {
35 | props.setPosition(Math.round(val));
36 |
37 | // Mark playing again if not paused before sliding
38 | if (!wasPausedBeforeSliding.current) {
39 | props.setPlaying();
40 | }
41 | }}
42 | />
43 | {secondsToMS(props.videoTotalTime)}
44 |
45 | );
46 | };
47 |
48 | const styles = StyleSheet.create({
49 | barWrapper: {
50 | flexDirection: "row",
51 | backgroundColor: "rgba(0, 0, 0, 0.5)",
52 | height: 60,
53 | paddingHorizontal: 20,
54 | justifyContent: "space-between",
55 | alignItems: "center",
56 | },
57 | currentTime: {
58 | color: "white",
59 | width: 40,
60 | },
61 | loadingBar: {
62 | flex: 1,
63 | marginHorizontal: 10,
64 | },
65 | totalTime: {
66 | color: "white",
67 | width: 40,
68 | },
69 | });
70 |
--------------------------------------------------------------------------------
/src/modules/video-player/components/DefaultMainControl.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { View, StyleSheet } from "react-native";
3 | import { PlayerIcon } from "./PlayerIcon";
4 | import { playerPlay, playerPause, playerRestart } from "../../../images";
5 | import { InjectedControlProps } from "../types";
6 |
7 | interface MainControlProps extends InjectedControlProps {
8 | restartButton?: boolean;
9 | }
10 |
11 | export const DefaultMainControl: React.FC = (props) => (
12 |
13 | {props.videoPaused ? (
14 |
15 | ) : (
16 |
17 | )}
18 | {props.restartButton && (
19 | props.setPosition(0)}
22 | />
23 | )}
24 |
25 | );
26 |
27 | const styles = StyleSheet.create({
28 | barWrapper: {
29 | flexDirection: "row",
30 | flexWrap: "wrap",
31 | justifyContent: "center",
32 | maxWidth: 160,
33 | minWidth: 80,
34 | backgroundColor: "rgba(0, 0, 0, 0.25)",
35 | borderRadius: 5,
36 | },
37 | barItem: {
38 | margin: 5,
39 | },
40 | });
41 |
--------------------------------------------------------------------------------
/src/modules/video-player/components/PlayerIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { View, TouchableOpacity, Image, StyleSheet } from "react-native";
3 |
4 | interface Props {
5 | iconSource: number;
6 | onPress: () => void;
7 | iconColor?: string;
8 | }
9 |
10 | export const PlayerIcon: React.FC = (props) => (
11 |
12 |
13 |
18 |
19 |
20 | );
21 |
22 | const styles = StyleSheet.create({
23 | iconWrapper: {
24 | padding: 5,
25 | },
26 | icon: {
27 | margin: 15,
28 | width: 30,
29 | height: 30,
30 | },
31 | });
32 |
--------------------------------------------------------------------------------
/src/modules/video-player/components/PlayerLoader.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { View, ActivityIndicator, Platform, StyleSheet } from "react-native";
3 |
4 | interface Props {
5 | color?: string;
6 | }
7 |
8 | export const PlayerLoader: React.FC = ({ color }) => (
9 |
10 |
14 |
15 | );
16 |
17 | const styles = StyleSheet.create({
18 | progressBar: {
19 | flex: 1,
20 | justifyContent: "center",
21 | },
22 | });
23 |
--------------------------------------------------------------------------------
/src/modules/video-player/components/VideoPlayer.tsx:
--------------------------------------------------------------------------------
1 | import React, { useRef, useState, useEffect } from "react";
2 | import {
3 | TouchableWithoutFeedback,
4 | View,
5 | Animated,
6 | StyleSheet,
7 | } from "react-native";
8 | import Video from "react-native-video";
9 | import { useVideoState } from "../hooks/useVideoState";
10 | import { InjectedControlProps, InjectedPlayerProps } from "../types";
11 | import { PlayerLoader } from "./PlayerLoader";
12 |
13 | interface PlayerProps {
14 | autoStart: boolean;
15 | children(props: InjectedPlayerProps): React.ReactNode;
16 | mainControl(data: InjectedControlProps): React.ReactNode;
17 | bottomControl(data: InjectedControlProps): React.ReactNode;
18 | }
19 |
20 | interface ProgressStatus {
21 | currentTime: number;
22 | playableDuration: number;
23 | }
24 |
25 | export const VideoPlayer: React.FC = (props) => {
26 | const playerRef = useRef