video keeps playing when I navigate to another screen - react-native

when I upload a video from my gallery and then try to navigate to the next screen,the video keeps playing in the background.
below is my code:
import React, {PureComponent} from 'react';
import {
StyleSheet,
Text,
TouchableOpacity,
View,
Animated,
ProgressBarAndroid,
} from 'react-native';
import {RNCamera} from 'react-native-camera';
import Icon from 'react-native-vector-icons/Entypo';
import ImagePicker from 'react-native-image-crop-picker';
import Video from 'react-native-video';
import { withNavigationFocus } from 'react-navigation';
class Shoot extends PureComponent {
constructor(props) {
super(props);
this.state = {
recording: false,
processing: true,
upload: false,
galleryVideo: '',
progress30: '',
progress60: '',
progress15: 0,
video: '',
progressStatus: 0,
progressStatus60: 0,
progressStatus15: 0,
videoPaused: false
};
}
render() {
return (
<View style={styles.container}>
{this.state.upload == true && (
<TouchableOpacity
style={{
backgroundColor: '#e75480',
position: 'absolute',
width: 80,
height: 30,
zIndex: 2,
padding: 5,
borderRadius: 5,
right: 0,
justifyContent: 'center',
alignContent: 'center',
}}
onPress={() => this.props.navigation.navigate('Post', {key: 1})}>
<Text style={{color: 'white', textAlign: 'center'}}>Next</Text>
</TouchableOpacity>
)}
{this.state.upload == false && (
<TouchableOpacity
style={{
position: 'absolute',
bottom: 0,
right: '15%',
justifyContent: 'center',
alignItems: 'center',
}}
onPress={this.video.bind(this)}>
<Icon name="image" size={30} color="white" />
<Text style={{color: 'white', fontWeight: 'bold'}}>Upload</Text>
</TouchableOpacity>
)}
<TouchableOpacity
onPress={this.take60sVideo.bind(this)}
style={{
width: 60,
height: 60,
justifyContent: 'center',
alignContent: 'center',
position: 'absolute',
bottom: 0,
left: '25%',
}}>
<Text style={{textAlign: 'center', color: 'red', fontSize: 15}}>
60s
</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={this.take15sVideo.bind(this)}
style={{
width: 60,
height: 60,
justifyContent: 'center',
alignContent: 'center',
position: 'absolute',
bottom: 0,
left: '5%',
}}>
<Text style={{textAlign: 'center', color: 'red', fontSize: 15}}>
15s
</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={this.take30sVideo.bind(this)}
style={styles.capture}></TouchableOpacity>
{this.state.progress30 === true && (
<View
style={{
width: '100%',
height: 15,
top: 0,
position: 'absolute',
bottom: 0,
zIndex: 2,
}}>
{/* <Animated.View
style={
([StyleSheet.absoluteFill],
{backgroundColor: '#8BED4F', width: '50%', height: 10})
}
/> */}
<ProgressBarAndroid
styleAttr="Horizontal"
progress={this.state.progressStatus}
indeterminate={false}
color="#e75480"
/>
</View>
)}
{this.state.progress60 === true && (
<View
style={{
width: '100%',
height: 15,
top: 0,
position: 'absolute',
bottom: 0,
zIndex: 2,
}}>
{/* <Animated.View
style={
([StyleSheet.absoluteFill],
{backgroundColor: '#8BED4F', width: '50%', height: 10})
}
/> */}
<ProgressBarAndroid
styleAttr="Horizontal"
progress={this.state.progressStatus60}
indeterminate={false}
color="#e75480"
/>
</View>
)}
{this.state.progress15 === true && (
<View
style={{
width: '100%',
height: 15,
top: 0,
position: 'absolute',
bottom: 0,
zIndex: 2,
}}>
{/* <Animated.View
style={
([StyleSheet.absoluteFill],
{backgroundColor: '#8BED4F', width: '50%', height: 10})
}
/> */}
<ProgressBarAndroid
styleAttr="Horizontal"
progress={this.state.progressStatus15}
indeterminate={false}
color="#e75480"
/>
</View>
)}
{this.state.video == '' ? (
<RNCamera
ref={(ref) => {
this.camera = ref;
}}
style={styles.preview}
type={RNCamera.Constants.Type.back}
flashMode={RNCamera.Constants.FlashMode.on}
androidCameraPermissionOptions={{
title: 'Permission to use camera',
message: 'We need your permission to use your camera',
buttonPositive: 'Ok',
buttonNegative: 'Cancel',
}}
androidRecordAudioPermissionOptions={{
title: 'Permission to use audio recording',
message: 'We need your permission to use your audio',
buttonPositive: 'Ok',
buttonNegative: 'Cancel',
}}
captureAudio={true}
/>
) : (
<Video
source={{uri: this.state.video}}
style={{
position: 'absolute',
top: 0,
left: 0,
alignItems: 'stretch',
bottom: 0,
right: 0,
height: '90%',
}}
resizeMode="cover"
repeat={true}
paused={this.state.videoPaused}
/>
)}
</View>
);
}
static getDerivedStateFromProps(nextProps, prevState) {
return {
...prevState,
videoPaused: !nextProps.navigation.isFocused()
}
}
video = () => {
ImagePicker.openPicker({
mediaType: 'video',
}).then((video) => {
this.setState({
galleryVideo: 1,
video: video.path,
upload: true,
});
});
};
take30sVideo = async () => {
if (this.camera) {
try {
const options = {
quality: 2,
videoBitrate: 8000000,
maxDuration: 30,
};
const promise = this.camera.recordAsync(options);
this.setState({progress30: true});
this.value = setInterval(() => {
if (this.state.progressStatus <= 1) {
this.setState({progressStatus: this.state.progressStatus + 0.01});
}
}, 100);
if (promise) {
this.setState({recording: true});
const data = await promise;
this.setState({recording: false, upload: true, progress30: false});
console.log(data);
console.log('upload', this.state.upload);
}
} catch (error) {
console.log(error);
}
}
};
take60sVideo = async () => {
if (this.camera) {
try {
const options = {
quality: 2,
videoBitrate: 8000000,
maxDuration: 60,
};
const promise = this.camera.recordAsync(options);
this.setState({progress60: true});
this.value = setInterval(() => {
if (this.state.progressStatus60 <= 1) {
this.setState({
progressStatus60: this.state.progressStatus60 + 0.01,
});
}
}, 100);
if (promise) {
this.setState({recording: true});
const data = await promise;
this.setState({recording: false, upload: true, progress60: false});
console.log(data);
console.log('upload', this.state.upload);
}
} catch (error) {
console.log(error);
}
}
};
take15sVideo = async () => {
if (this.camera) {
try {
const options = {
quality: 2,
videoBitrate: 8000000,
maxDuration: 15,
};
const promise = this.camera.recordAsync(options);
this.setState({progress15: true});
this.value = setInterval(() => {
if (this.state.progressStatus15 <= 1) {
this.setState({
progressStatus15: this.state.progressStatus15 + 0.01,
});
}
}, 100);
if (promise) {
this.setState({recording: true});
const data = await promise;
this.setState({recording: false, upload: true, progress15: false});
console.log(data);
console.log('upload', this.state.upload);
}
} catch (error) {
console.log(error);
}
}
};
}
export default withNavigationFocus(Shoot);
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
backgroundColor: 'black',
},
preview: {
height: '90%',
justifyContent: 'flex-end',
alignItems: 'center',
},
capture: {
backgroundColor: '#e75480',
borderRadius: 40,
borderWidth: 3,
borderColor: 'red',
width: 60,
height: 60,
position: 'absolute',
bottom: 0,
justifyContent: 'center',
left: '45%',
alignContent: 'center',
},
});
I have already tried withNavigationFocus but it is not working,let me know if any other way is there,also let me know if anything else is required for clarification.
Any suggestion would be great.

I have finally resolved this issue by creating a function for next button and setting the state of paused to true whenever the screen navigates to the other screen.
Hope,that helps.

Setting the video screen as "fullScreenModal" worked for me:
("react-native": "0.71.2", "#react-navigation/native": "^6.1.3", "react-native-video": "^5.2.1")
react navigation stack definition:
function Stack() {
return (
<Stack.Navigator>
<Stack.Screen name="SomeScreen" component={SomeScreen} />
<Stack.Screen
name="FullScreenVideo"
component={FullScreenVideo}
options={{ headerShown: false, presentation: "fullScreenModal" }}
/>
</Stack.Navigator>;
)
}
FullScreenVideo.jsx
import React from "react";
import Video from "react-native-video";
import { StyleSheet } from "react-native";
export default function FullScreenVideo({ navigation, route }) {
const videoUri = route.params.uri;
return (
<Video
source={{ uri: videoUri }}
controls
onEnd={() => navigation.pop()}
style={styles.backgroundVideo}
/>
);
}
var styles = StyleSheet.create({
backgroundVideo: {
position: "absolute",
top: 0,
left: 0,
bottom: 0,
right: 0,
},
});

I think you also could killed or paused the video when the component will be unmounted:
// Class component
componentWillUnmount() {
videoRef.seek(0)
this.setState({isPaused: true})
}
OR
// functional component
useEffect(() => {
return () => {
videoRef.current.seek(0)
setIsPaused(true)
}
}, [])
You also can use methods from react native navigation:
Call a function when focused screen changes
I hope it’ll help someone

Related

"Can't perform a React state update on an unmounted component" Follow by continuous "TYPE ERORR: null is not an object"

The app I'm working on is a multi-game app (a bird game, mole game, and minesweeper). The start screen is the menu. It gives me a cycle warning on this screen. When I tap to start the mole game, it plays naturally, but the moment I press the back button to go back to the menu, I receive errors: "Can't perform a React state update on an unmounted component" error follow by continuous "TYPE ERORR: null is not an object".
Screenshots:
Cycle warning
Null is not an object
Node.js errors
Mole Game's App.js
import {
View,
StyleSheet,
Image,
SafeAreaView,
Text,
TouchableWithoutFeedback
} from 'react-native';
import Images from './assets/Images';
import Constants from './Constants';
import Mole from './Mole';
import GameOver from './GameOver';
import Clear from './Clear';
import Pause from './Pause';
const DEFAULT_TIME = 20;
const DEFAULT_STATE = {
level: 1,
score: 0,
time: DEFAULT_TIME,
cleared: false,
paused: false,
gameover: false,
health: 100
}
export default class MoleGame extends Component {
constructor(props) {
super(props);
this.moles = [];
this.state = DEFAULT_STATE;
this.molesPopping = 0;
this.interval = null;
this.timeInterval = null;
}
componentDidMount = () => {
this.setupTicks(DEFAULT_STATE, this.pause);
}
setupTicks = () => {
let speed = 750 - (this.state.level * 50);
if (speed < 350) {
speed = 350;
}
this.interval = setInterval(this.popRandomMole, speed);
this.timeInterval = setInterval(this.timerTick, 1000);
}
reset = () => {
this.molesPopping = 0;
this.setState(DEFAULT_STATE, this.setupTicks)
}
pause = () => {
if (this.interval) clearInterval(this.interval);
if (this.timeInterval) clearInterval(this.timeInterval);
this.setState({
paused: true
});
}
resume = () => {
this.molesPopping = 0;
this.setState({
paused: false
}, this.setupTicks);
}
nextLevel = () => {
this.molesPopping = 0;
this.setState({
level: this.state.level + 1,
cleared: false,
gameover: false,
time: DEFAULT_TIME
}, this.setupTicks)
}
timerTick = () => {
if (this.state.time === 0) {
clearInterval(this.interval);
clearInterval(this.timeInterval);
this.setState({
cleared: true
})
} else {
this.setState({
time: this.state.time - 1
})
}
}
gameOver = () => {
clearInterval(this.interval);
clearInterval(this.timerInterval);
this.setState({
gameover: true
})
}
onDamage = () => {
if (this.state.cleared || this.state.gameOver || this.state.paused) {
return;
}
let targetHealth = this.state.health - 10 < 0 ? 0 : this.state.health - 20;
this.setState({
health: targetHealth
});
if (targetHealth <= 0) {
this.gameOver();
}
}
onHeal = () => {
let targetHealth = this.state.health + 10 > 100 ? 100 : this.state.health + 10
this.setState({
health: targetHealth
})
}
onScore = () => {
this.setState({
score: this.state.score + 1
})
}
randomBetween = (min, max) => {
return Math.floor(Math.random() * (max - min + 1) + min);
}
onFinishPopping = (index) => {
this.molesPopping -= 1;
}
popRandomMole = () => {
if (this.moles.length != 12) {
return;
}
let randomIndex = this.randomBetween(0, 11);
if (!this.moles[randomIndex].isPopping && this.molesPopping < 3) {
this.molesPopping += 1;
this.moles[randomIndex].pop();
}
}
render() {
let healthBarWidth = (Constants.MAX_WIDTH - Constants.XR * 100 - Constants.XR * 60 - Constants.XR * 6) * this.state.health / 100;
return (
<View style={styles.container}>
<Image style={styles.backgroundImage} resizeMode="stretch" source={Images.background} />
<View style={styles.topPanel}>
<SafeAreaView>
<View style={styles.statsContainer}>
<View style={styles.stats}>
<View style={styles.levelContainer}>
<Text style={styles.levelTitle}>Level</Text>
<Text style={styles.levelNumber}>{this.state.level}</Text>
</View>
</View>
<View style={styles.stats}>
<View style={styles.timeBar}>
<Text style={styles.timeNumber}>{this.state.time}</Text>
</View>
<Image style={styles.timeIcon} resizeMode="stretch" source={Images.timeIcon} />
</View>
<View style={styles.stats}>
<View style={styles.scoreBar}>
<Text style={styles.scoreNumber}>{this.state.score}</Text>
</View>
<Image style={styles.scoreIcon} resizeMode="stretch" source={Images.scoreIcon} />
</View>
<View style={styles.stats}>
<TouchableWithoutFeedback onPress={this.pause}>
<View style={styles.pauseButton}>
<Image style={styles.pauseButtonIcon} resizeMode="stretch" source={Images.pauseIcon} />
</View>
</TouchableWithoutFeedback>
</View>
</View>
<View style={styles.healthBarContainer}>
<View style={styles.healthBar}>
<View style={[styles.healthBarInner, { width: healthBarWidth }]} />
</View>
<Image style={styles.healthIcon} resizeMode="stretch" source={Images.healthIcon} />
</View>
</SafeAreaView>
</View>
<View style={styles.playArea}>
{Array.apply(null, Array(4)).map((el, rowIdx) => {
return (
<View style={styles.playRow} key={rowIdx}>
{Array.apply(null, Array(3)).map((el, colIdx) => {
let moleIdx = (rowIdx * 3) + colIdx;
return (
<View style={styles.playCell} key={colIdx}>
<Mole
index={moleIdx}
onDamage={this.onDamage}
onHeal={this.onHeal}
onFinishPopping={this.onFinishPopping}
onScore={this.onScore}
ref={(ref) => { this.moles[moleIdx] = ref }}
/>
</View>
)
})}
</View>
)
})}
</View>
{this.state.cleared && <Clear onReset={this.reset} onNextLevel={this.nextLevel} level={this.state.level} score={this.state.score} />}
{this.state.gameover && <GameOver onReset={this.reset} level={this.state.level} score={this.state.score} />}
{this.state.paused && <Pause onReset={this.reset} onResume={this.resume} />}
</View>
)
}
};
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column'
},
backgroundImage: {
width: Constants.MAX_WIDTH,
height: Constants.MAX_HEIGHT,
position: 'absolute'
},
topPanel: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: Constants.YR * 250,
flexDirection: 'column'
},
statsContainer: {
width: Constants.MAX_WIDTH,
height: Constants.YR * 120,
flexDirection: 'row'
},
stats: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
pauseButton: {
width: Constants.YR * 50,
height: Constants.YR * 50,
backgroundColor: 'black',
borderColor: 'white',
borderWidth: 3,
borderRadius: 10,
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center'
},
pauseButtonIcon: {
width: Constants.YR * 25,
height: Constants.YR * 25,
},
levelContainer: {
width: Constants.YR * 80,
height: Constants.YR * 80,
backgroundColor: '#ff1a1a',
borderColor: 'white',
borderWidth: 3,
borderRadius: 10,
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center'
},
levelTitle: {
fontSize: 21,
color: 'white',
fontFamily: 'LilitaOne'
},
levelNumber: {
fontSize: 17,
color: 'white',
fontFamily: 'LilitaOne'
},
scoreIcon: {
position: 'absolute',
left: 0,
width: Constants.YR * 40,
height: Constants.YR * 40,
},
scoreBar: {
height: Constants.YR * 25,
position: 'absolute',
left: 20,
right: 5,
backgroundColor: 'white',
borderRadius: 13,
justifyContent: 'center',
alignItems: 'center'
},
scoreNumber: {
fontSize: 17,
color: 'black',
fontFamily: 'LilitaOne',
},
timeIcon: {
position: 'absolute',
left: 0,
width: Constants.YR * 40,
height: Constants.YR * 40,
},
timeBar: {
height: Constants.YR * 25,
position: 'absolute',
left: 20,
right: 5,
backgroundColor: 'white',
borderRadius: 13,
justifyContent: 'center',
alignItems: 'center'
},
timeNumber: {
fontSize: 17,
color: 'black',
fontFamily: 'LilitaOne',
},
healthBarContainer: {
height: Constants.YR * 40,
width: Constants.MAX_WIDTH - Constants.XR * 120,
marginLeft: Constants.XR * 60
},
healthIcon: {
position: 'absolute',
top: 0,
left: 0,
width: Constants.YR * 46,
height: Constants.YR * 40,
},
healthBar: {
height: Constants.YR * 20,
width: Constants.MAX_WIDTH - Constants.XR * 100 - Constants.XR * 60,
marginLeft: Constants.XR * 40,
marginTop: Constants.YR * 10,
backgroundColor: 'white',
borderRadius: Constants.YR * 10
},
healthBarInner: {
position: 'absolute',
backgroundColor: '#ff1a1a',
left: Constants.XR * 3,
top: Constants.YR * 3,
bottom: Constants.YR * 3,
borderRadius: Constants.YR * 8
},
playArea: {
width: Constants.MAX_WIDTH,
marginTop: Constants.YR * 250,
height: Constants.MAX_HEIGHT - Constants.YR * 250 - Constants.YR * 112,
flexDirection: 'column',
},
playRow: {
height: (Constants.MAX_HEIGHT - Constants.YR * 250 - Constants.YR * 112) / 4,
width: Constants.MAX_WIDTH,
flexDirection: 'row',
},
playCell: {
width: Constants.MAX_WIDTH / 3,
height: (Constants.MAX_HEIGHT - Constants.YR * 250 - Constants.YR * 112) / 4,
alignItems: 'center'
}
});
Mole.js
import { View, StyleSheet, Button, Image, TouchableWithoutFeedback } from 'react-native';
import Images from './assets/Images';
import SpriteSheet from 'rn-sprite-sheet';
//import Constants from './Constants';
export default class Mole extends Component {
constructor (props) {
super(props);
this.mole = null;
this.actionTimeout = null;
this.isPopping = false;
this.isWacked = false;
this.isHealing = false;
this.isAttacking = false;
this.isFeisty = false;
}
pop =()=>{
this.isWacked = false;
this.isPopping = true;
this.isAttacking = false;
this.isFeisty = Math.random() < 0.4;
if(!this.isFeisty){
this.isHealing = Math.random() < 0.12;
}
if(this.isHealing){
this.mole.play({
type: "heal",
onFinish : ()=>{
this.actionTimeout = setTimeout(()=>{
this.mole.play({
type : "hide",
fps: 24,
onFinish: ()=>{
this.isPopping = false;
this.props.onFinishPopping(this.props.index);
}
})
}, 1000);
}
})
}
else{
this.mole.play({
type : "appear",
fps: 24,
onFinish: ()=>{
if (this.isFeisty){
this.actionTimeout = setTimeout(() => {
this.isAttacking = true;
this.props.onDamage();
this.mole.play({
type: "attack",
fps: 12,
onFinish: () => {
this.mole.play({
type: "hide",
fps: 24,
onFinish: () => {
this.isPopping = false;
this.props.onFinishPopping(this.props.index);
}
})
}
})
}, 1000)
}
else{
this.actionTimeout = setTimeout(()=>{
this.mole.play({
type : "hide",
fps: 24,
onFinish: ()=>{
this.isPopping = false;
this.props.onFinishPopping(this.props.index);
}
})
}, 1000);
}
}
})
}
}
whack = ()=>{
if(!this.isPopping || this.isWacked || this.isAttacking){
return;
}
if (this.actionTimeout){
clearTimeout(this.actionTimeout);
}
this.isWacked = true;
this.props.onScore ();
if( this.isHealing){
this.props.onHeal();
}
this.mole.play({
type: "dizzy",
fps: 24,
onFinish: () => {
this.mole.play({
type: "faint",
fps: 24,
onFinish: () => {
this.isPopping = false;
this.props.onFinishPopping(this.props.index);
}
})
}
})
}
render() {
return (
<View style= {styles.container}>
<SpriteSheet
ref= {ref=> {this.mole = ref}}
source = {Images.sprites}
columns = {6}
rows = {8}
width = {100}
animations = {{
idle: [0],
appear: [1,2,3,4],
hide: [4,3,2,1,0],
dizzy : [36,37,38],
faint: [42,43,44,0],
attack: [11,12,13,14,15,16],
heal: [24,25,26,27,28,29,30,31,32,33]
}} />
<TouchableWithoutFeedback onPress= {this.whack} style= {{position: 'absolute', top: 0 , bottom:0, left:0, right:0 }}>
<View style= {{position: 'absolute', top: 0 , bottom:0, left:0, right:0 }} />
</TouchableWithoutFeedback>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1
}
})
index.js (Navigation)
import React from "react";
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import MoleGame from './Game1/App.js'
import BirdGame from './Game2/App.js'
import HomeScreen from './Game3/screens/HomeScreen.js'
import GameScreen from './Game3/screens/GameScreen.js'
import Home from './Home.js'
const Stack = createStackNavigator();
export default App = () => {
return (
<NavigationContainer>
<Stack.Navigator screenOptions={{ headerShown: false, gestureEnabled: false, }} >
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="MoleGame" component={MoleGame} />
<Stack.Screen name="BirdGame" component={BirdGame} />
<Stack.Screen name="MineSweeperHome" component={HomeScreen} />
<Stack.Screen name="MineSweeperGame" component={GameScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
Home.js
import { View, Text, TouchableOpacity, Dimensions, StyleSheet } from 'react-native';
const GameButton = ({ label, onPress, style }) => {
return (
<TouchableOpacity
style={style}
onPress={onPress}
>
<Text style={styles.text}>{label}</Text>
</TouchableOpacity>
);
};
export default Home = ({ navigation }) => {
return (
<View style={styles.container}>
<View style={styles.welcome}>
<Text style={styles.text}> Welcome! </Text>
</View>
<View style={styles.buttonContainer}>
<GameButton
label={'Play Bird Game'}
onPress={() => navigation.navigate('BirdGame')}
style={styles.gameButton}
/>
<GameButton
label={'Play Mole Game'}
onPress={() => navigation.navigate('MoleGame')}
style={{ ...styles.gameButton, backgroundColor: 'green' }}
/>
<GameButton
label={'Play MineSweeper Game'}
onPress={() => navigation.navigate('MineSweeperHome')}
style={{ ...styles.gameButton, backgroundColor: 'grey' }}
/>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#FFCB43'
},
welcome: {
alignItems: 'center',
justifyContent: 'center',
padding: 10,
marginTop: 80,
},
buttonContainer: {
flex: 1,
alignItems: 'center',
justifyContent: 'space-evenly'
},
gameButton: {
height: 70,
width: Dimensions.get("screen").width / 1.4,
backgroundColor: 'red',
borderColor: 'red',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 20,
borderWidth: 2,
borderColor: 'black',
},
text: {
fontSize: 22,
}
})
Some context and notices
The project was started on my friend's mac machine. According to him, he doesn't receive those errors. And he also was able to build the app into an .apk for Android
We are both using the same node.js version (15.11.0)
There are times when my metro fails to run, and when that happens, the app runs without any errors.
I have tried to build it into an .apk, but the build failed.
Before the app was combined into 3 games. They were individual games. The mole game did run and was built successfully into an .apk.
The mole game was forked from here The code is pretty much the same.
UPDATE :
After adding
componentWillUnmount(){
clearInterval(this.interval);
clearInterval(this.timerInterval);
}
to the App.js. I no longer get continuous "TypeError: null is not an object (evaluating '_this.moles[randomIndex].isPopping')". Unfortunately, I still get TypeError: null is not an object (evaluating '_this.moles.play) error. This happens when I go back to the main menu. I've tried adding clearTimeout(this.actionTimeout) on mole.js, but that didn't give any effect.
Screenshot :
TypeError: null is not an object (evaluating '_this.moles.play)
The setInterval() function is commonly used to set a delay for functions that are executed again and again, such as animations. You can cancel the interval using clearInterval().
When you navigate from MoleGame to Home, the MoleGame route is popped off the navigation stack and its component is unmounted. But the intervals from the setupTicks method are still executing, and trying to set state on the MoleGame component and to access this (neither of which are possible).
Try clearInterval on componentWillUnmount to stop the intervals that are set in the setupTicks method.
// App.js
export default class MoleGame extends Component {
constructor(props) {
super(props);
this.interval = null;
this.timeInterval = null;
...
}
componentDidMount = () => {
this.setupTicks(DEFAULT_STATE, this.pause);
}
componentWillUnmount() {
clearInterval(this.interval);
clearInterval(this.timeInterval);
}
}
Similarly in Mole.js you would have to handle the same scenario for any setTimeout()s you have by using clearTimeout()
Further reading: Plugging memory leaks in your app
By adding:
componentWillUnmount(){
clearInterval(this.interval);
clearInterval(this.timerInterval);
}
it fixed the continuous TypeError: null is not an object (evaluating '_this.moles[randomIndex].isPopping')
To remove the "TypeError: null is not an object (evaluating '_this.moles.play)" error, I had to put this.mole.play into a variable within Mole.js because this no longer refer to this.mole.play when I go back to the menu. There is still memory leak warnings since it is finishing the animation as I go to the menu, but there are no more critical errors.

How to make background black of react native camera?

Problem:
I have created react native camera but it is looked like this.
But the problem is the background color of camera action icons are transparent.
This is my code.
import React, {useState, useEffect} from 'react';
import {
AppRegistry,
StyleSheet,
TouchableOpacity,
View,
Image,
CameraRoll,
Platform,
} from 'react-native';
import IoniconsIcon from 'react-native-vector-icons/Ionicons';
import EntypoIcon from 'react-native-vector-icons/Entypo';
import AppText from '_components/appText';
import {RNCamera} from 'react-native-camera';
import normalize from '_utils/fontsize';
let countRecordTime;
const PendingView = () => (
<View
style={{
flex: 1,
backgroundColor: 'lightgreen',
justifyContent: 'center',
alignItems: 'center',
}}>
<AppText>Waiting</AppText>
</View>
);
const takePicture = async (camera) => {
const options = {quality: 1};
const data = await camera.takePictureAsync(options);
console.log(data.uri);
};
const recordVideo = async (
camera,
recording,
seconds,
setRecording,
setSeconds,
) => {
if (camera) {
if (!recording) {
startRecording(recording, setRecording, seconds, setSeconds, camera);
} else {
stopRecording(camera, setSeconds);
}
}
};
const startRecording = async (
recording,
setRecording,
seconds,
setSeconds,
camera,
) => {
setRecording(true);
console.log('>>>>>> start recording seconds', seconds);
const cameraConfig = {maxDuration: 20};
const data = await camera.recordAsync(cameraConfig);
console.log('>>>>>> data', data);
setRecording(false);
};
const stopRecording = (camera, setSeconds) => {
camera.stopRecording();
setSeconds(0);
};
const secondsToMMSS = (seconds) => {
console.log('>>>>>> seconds', seconds);
let m = Math.floor(seconds / 60);
let s = Math.floor(seconds % 60);
let mDisplay = m < 10 ? `0${m}` : `${m}`;
let sDisplay = s < 10 ? `0${s}` : `${s}`;
return `${mDisplay}:${sDisplay}`;
};
const VideoRecording = (props) => {
const [type, setType] = useState(RNCamera.Constants.Type.back);
const [flashMode, setFlashMode] = useState(RNCamera.Constants.FlashMode.off);
const [recording, setRecording] = useState(false);
const [seconds, setSeconds] = useState(0);
useEffect(() => {
if (recording) {
countRecordTime = setInterval(() => setSeconds(seconds + 1), 1000);
}
if (!recording && countRecordTime) {
clearInterval(countRecordTime);
}
if (seconds === 20 && countRecordTime) {
clearInterval(countRecordTime);
setRecording(false);
}
return () => {
if (countRecordTime) {
clearInterval(countRecordTime);
}
};
}, [recording, seconds]);
return (
<View style={styles.container}>
<RNCamera
style={styles.preview}
autoFocus={RNCamera.Constants.AutoFocus.on}
type={type}
flashMode={flashMode}
faceDetectionMode={RNCamera.Constants.FaceDetection.Mode.fast}
ratio="4:3"
whiteBalance={RNCamera.Constants.WhiteBalance.auto}
captureAudio={true}
androidCameraPermissionOptions={{
title: 'Permission to use camera',
message: 'We need your permission to use your camera',
buttonPositive: 'Ok',
buttonNegative: 'Cancel',
}}
androidRecordAudioPermissionOptions={{
title: 'Permission to use audio recording',
message: 'We need your permission to use your audio',
buttonPositive: 'Ok',
buttonNegative: 'Cancel',
}}>
{({camera, status, recordAudioPermissionStatus}) => {
if (status !== 'READY') {
return <PendingView />;
}
return (
<>
<View
style={{
flex: 0,
flexDirection: 'row',
justifyContent: 'center',
}}>
<TouchableOpacity
style={styles.capture}
onPress={(_) => {
switch (flashMode) {
case RNCamera.Constants.FlashMode.off:
setFlashMode(RNCamera.Constants.FlashMode.auto);
break;
case RNCamera.Constants.FlashMode.auto:
setFlashMode(RNCamera.Constants.FlashMode.on);
break;
case RNCamera.Constants.FlashMode.on:
setFlashMode(RNCamera.Constants.FlashMode.off);
break;
}
}}>
<Image
source={
flashMode === RNCamera.Constants.FlashMode.auto
? require('../../assets/img/flashAuto.png')
: flashMode === RNCamera.Constants.FlashMode.on
? require('../../assets/img/flashOn.png')
: require('../../assets/img/flashOff.png')
}
style={{width: 30, height: 30}}
resizeMode={'contain'}
/>
</TouchableOpacity>
{/* <TouchableOpacity
onPress={() => takePicture(camera)}
style={styles.capture}>
<Image
source={require('../../assets/img/cameraButton.png')}
style={{width: 50, height: 50}}
resizeMode={'contain'}
/>
</TouchableOpacity> */}
<TouchableOpacity
style={styles.iconContainer}
onPress={() =>
recordVideo(
camera,
recording,
seconds,
setRecording,
setSeconds,
)
}>
<EntypoIcon
style={styles.icon}
size={40}
color={recording ? 'red' : 'white'}
name="video-camera"
/>
{recording ? (
<AppText styles={styles.recordingTime}>
{secondsToMMSS(seconds)}
</AppText>
) : null}
</TouchableOpacity>
<TouchableOpacity
style={styles.capture}
onPress={(_) => {
if (type === RNCamera.Constants.Type.back) {
setType(RNCamera.Constants.Type.front);
} else {
setType(RNCamera.Constants.Type.back);
}
}}>
<Image
source={require('../../assets/img/cameraFlipIcon.png')}
style={{width: 40, height: 40}}
resizeMode={'contain'}
/>
</TouchableOpacity>
</View>
<TouchableOpacity style={styles.closeButton} onPress={() => {}}>
<IoniconsIcon
name={'ios-close'}
style={styles.closeButtonIcon}
/>
</TouchableOpacity>
</>
);
}}
</RNCamera>
</View>
);
};
export default VideoRecording;
const styles = StyleSheet.create({
container: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
},
preview: {
flex: 1,
justifyContent: 'flex-end',
alignItems: 'center',
backgroundColor: 'black',
},
capture: {
flex: 0,
borderRadius: 5,
padding: 15,
paddingHorizontal: 20,
alignSelf: 'center',
margin: 20,
},
cptureView: {
backgroundColor: '#ffffff',
width: 100,
height: 100,
borderRadius: 100,
alignItems: 'center',
justifyContent: 'center',
marginBottom: '20%',
marginTop: '5%',
},
camIcon: {
color: '#858181',
},
iconContainer: {
justifyContent: 'center',
alignItems: 'center',
},
icon: {
marginHorizontal: 15,
// paddingVertical: 10,
},
recordingTime: {
color: 'red',
},
closeButton: {
position: 'absolute',
backgroundColor: '#aaaaaab0',
width: 50,
height: 50,
borderRadius: 25,
justifyContent: 'center',
top: Platform.OS === 'ios' ? 45 : 10,
left: 10,
},
closeButtonIcon: {
fontSize: Platform.OS === 'ios' ? 40 : 40,
fontWeight: 'bold',
alignSelf: 'center',
lineHeight: Platform.OS === 'ios' ? 58 : 40,
},
});
Can someone help me to make it black? I tried a lot to find out way to do so that. But I could not so if someone can help me it would be grateful. Thank you very much.
Have you try this?
...
<View
style={{
flex: 0,
flexDirection: 'row',
justifyContent: 'center',
backgroundColor: "#000000", //-----------------Here?
}}>
<TouchableOpacity
style={styles.capture}
onPress={(_) => {
switch (flashMode) {
case RNCamera.Constants.FlashMode.off:
setFlashMode(RNCamera.Constants.FlashMode.auto);
...

How do I move a buttom from behind an image using React Native

I have attached a screen shot of the problem. I can't seem to be able to move my button from behind an image object. I tried zIndex and moving my Views around but I am still getting the same issue. The code seems to only work on IOS but not on Android.
Your assistance will be greatly appreciated. my code is below.
Thank you.
Project Screenshot
import * as React from 'react';
import { StyleSheet, Image, View } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import Constants from 'expo-constants';
import * as Permissions from 'expo-permissions';
import { EvilIcons } from '#expo/vector-icons'
export default class ImagePickerExample extends React.Component {
state = {
image: null,
};
render() {
let { image } = this.state;
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<View style={styles.emptyProfile}>
{image && <Image source={{ uri: image }} style={styles.profile} />}
</View>
<View style={{ marginLeft: 70, justifyContent: 'center', bottom: 25 }}>
<View style={styles.camera}>
<EvilIcons name='camera' size={35} color='#fff' onPress={this._pickImage} />
</View>
</View>
</View>
);
}
componentDidMount() {
this.getPermissionAsync();
}
getPermissionAsync = async () => {
if (Constants.platform.ios) {
const { status } = await Permissions.askAsync(Permissions.CAMERA_ROLL);
if (status !== 'granted') {
alert('Sorry, we need camera roll permissions to make this work!');
}
}
};
_pickImage = async () => {
try {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 4],
quality: 1,
});
if (!result.cancelled) {
this.setState({ image: result.uri });
}
console.log(result);
} catch (E) {
console.log(E);
}
};
}
const styles = StyleSheet.create({
camera: {
backgroundColor: '#fd4336',
height: 50,
width: 50,
borderRadius: 25,
justifyContent: 'center',
alignItems: 'center',
position: 'absolute',
},
profile: {
width: 190,
height: 190,
borderRadius: 100,
justifyContent: 'center',
alignItems: 'center',
},
emptyProfile: {
width: 200,
height: 200,
borderRadius: 100,
backgroundColor: '#c1b78d',
borderWidth: 5,
borderColor: '#fff',
shadowColor: 'black',
shadowOffset: { width: 0, height: 3 },
shadowRadius: 3,
shadowOpacity: 0.3,
elevation: 5
}
})
SCREENSHOT
Okay I managed to figure it out! by wrapping another View
<View style={styles.emptyProfile}>
{image && <Image source={{ uri: image }} style={styles.profile} />}
<View style={{ left: 140, position: 'absolute', bottom: 50 }} >
<View style={styles.camera}>
<EvilIcons name='camera' size={35} color='#fff' onPress={this._pickImage} />
</View>
</View>
</View>
);

Upload an image to Firebase with React Native

i am trying to make a photo uploader to firebase in react native. I followed a tutorial and adopted the code 1-on-1. However, after I want to execute the code everything appears to work until the code has to be uploaded then I get the following error:
Possible Unhandled Promise Rejection (id: 0): Error: An unknown error
has occurred.
fn#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:2132:45
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:127402:44
putFile#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:137147:104
uploadImage#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:210966:91
touchableHandlePress#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:54240:47
touchableHandlePress#[native code]
_performSideEffectsForTransition#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:52872:36
_performSideEffectsForTransition#[native code]
_receiveSignal#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:52798:46
_receiveSignal#[native code] touchableHandleResponderRelease#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:52677:26
touchableHandleResponderRelease#[native code]
invokeGuardedCallbackImpl#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:8997:21
invokeGuardedCallback#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:9093:42
invokeGuardedCallbackAndCatchFirstError#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:9097:36
executeDispatch#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:9204:48
executeDispatchesInOrder#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:9224:26
executeDispatchesAndRelease#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:9329:35
forEach#[native code]
forEachAccumulated#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:9319:22
runEventsInBatch#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:9353:27
runExtractedPluginEventsInBatch#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:9441:25
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:10467:42
batchedUpdates$1#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:21921:20
batchedUpdates#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:10415:36
_receiveRootNodeIDEvent#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:10466:23
receiveTouches#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:10496:34
__callFunction#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:2650:49
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:2363:31
__guard#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:2604:15
callFunctionReturnFlushedQueue#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:2362:21
callFunctionReturnFlushedQueue#[native code]
Is anyone familiar with uploading images to firebase? in the tutorial they use uuid but for some reason the app breaks when I try to use this, so I left it out. this is the corresponding code:
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, Image, Button, ScrollView, ImageBackground, Dimensions,TouchableOpacity, FlatList,
AsyncStorage} from 'react-native';
import { Avatar, ListItem } from 'react-native-elements';
import Icon from 'react-native-vector-icons/FontAwesome';
import ImagePicker from 'react-native-image-picker';
import firebase from 'react-native-firebase';
const SCREEN_WIDTH = Dimensions.get("window").width;
const list = [
{
title: '',
icon: 'cake',
url: 'ProfileSettings',
},
]
const options = {
title: 'Select Image',
storageOptions: {
skipBackup: true,
path: 'images'
}
};
const ImageRow = ({ image, windowWidth, popImage }) => (
<View>
<Image
source={{ uri: image }}
style={[styles.img, { width: windowWidth / 2 - 15 }]}
onError={popImage}
/>
</View>
);
class profileScreen extends Component {
constructor(props){
super(props);
this.state = {
first: '',
place: '',
province: '',
uid: '',
profileImg: '',
email: '',
imgSource: '',
uploading: false,
progress: 0,
images: []
}
}
getUserData = (user) => {
console.log(user);
let ref = firebase.database().ref('Users/' + user);
ref.on('value' , snapshot =>{
var state = snapshot.val();
this.setState({
first: state.firstname,
place: state.place,
province: state.province,
uid: user,
profileImg: state.profileImg,
birthday: state.birthday,
email: state.email,
year: state.registered.split("-",1)
})
})
}
componentDidMount(){
let user = firebase.auth().currentUser;
console.log(user);
console.log('test');
this.getUserData(user.uid);
let images;
AsyncStorage.getItem('images')
.then(data => {
images = JSON.parse(data) || [];
this.setState({
images: images
});
})
.catch(error => {
console.log(error);
});
}
/**
* Select image method
*/
pickImage = () => {
ImagePicker.showImagePicker(options, response => {
if (response.didCancel) {
console.log('You cancelled image picker 😟');
} else if (response.error) {
alert('And error occured: ', response.error);
} else {
const source = { uri: response.uri };
this.setState({
imgSource: source,
imageUri: response.uri
});
console.log(source);
}
});
};
/**
* Upload image method
*/
uploadImage = () => {
const ext = this.state.imageUri.split('.').pop(); // Extract image extension
const filename = `unique image' + ${ext}`; // Generate unique name
const imageRef = firebase.storage().ref('tutorials/images').child(filename +ext);
let mime = 'image/jpg';
imageRef.put(this.state.imageUri, { contentType: mime }).then((snapshot)=>{
console.log('Image uploaded successfully.')
}).catch((error)=>{
console.log('Image uploading failed:' + error);
});
};
/**
* Remove image from the state and persistance storage
*/
removeImage = imageIndex => {
let images = this.state.images;
images.pop(imageIndex);
this.setState({ images });
AsyncStorage.setItem('images', JSON.stringify(images));
};
render() {
const { uploading, imgSource, progress, images } = this.state;
const windowWidth = Dimensions.get('window').width;
const disabledStyle = uploading ? styles.disabledBtn : {};
const actionBtnStyles = [styles.btn, disabledStyle];
return (
<ScrollView style={styles.scorllVert}>
<View style={{ alignItems: 'flex-start', justifyContent: 'center', marginBottom: 40 }}>
<View style={styles.intro}>
<Text style={styles.introText}>Hoi, ik ben {this.state.first}{"\n"}<Text style={styles.introTextSpan}>Lid geworden in {this.state.year}</Text></Text>
{ this.state.profileImg ?
<Avatar size="large" rounded source={{uri: this.state.profileImg,}} onPress={() => console.log("Works!")} activeOpacity={0.7} />
:
<Avatar size="large" rounded title="GLR" onPress={() => console.log("Works!")} activeOpacity={0.7} />
}
</View>
<View style={styles.divider}>
</View>
<View style={styles.about}>
<Text style={styles.profileName}>Over</Text>
<View style={styles.aboutLiving}>
<Icon name='home' style={styles.icon}/>
<Text style={styles.aboutText}>Woont in {this.state.place}, {this.state.province}</Text>
</View>
</View>
<View style={styles.divider}>
</View>
<View style={styles.about}>
<Text style={styles.profileName}>Door {this.state.first} versterkt</Text>
<View style={styles.aboutLiving}>
<Icon name='check-circle' style={[styles.icon, styles.iconGreen]}/>
<Text style={styles.aboutText}>E-mail adres</Text>
</View>
</View>
<View style={styles.divider}>
</View>
<View style={styles.about}>
<Text style={styles.profileName}>Recente activiteiten</Text>
<Text >N.v.t.</Text>
<TouchableOpacity
style={actionBtnStyles}
onPress={this.pickImage}
disabled={uploading}
>
<View>
<Text style={styles.btnTxt}>Pick image</Text>
</View>
</TouchableOpacity>
{imgSource !== '' && (
<View>
<Image source={imgSource} style={styles.image} />
{uploading && (
<View
style={[styles.progressBar, { width: `${progress}%` }]}
/>
)}
<TouchableOpacity
style={actionBtnStyles}
onPress={this.uploadImage}
disabled={uploading}
>
<View>
{uploading ? (
<Text style={styles.btnTxt}>Uploading ...</Text>
) : (
<Text style={styles.btnTxt}>Upload image</Text>
)}
</View>
</TouchableOpacity>
</View>
)}
<View>
<Text
style={{
fontWeight: '600',
paddingTop: 20,
alignSelf: 'center'
}}
>
{images.length > 0
? 'Your uploaded images'
: 'There is no image you uploaded'}
</Text>
</View>
<FlatList
numColumns={2}
style={{ marginTop: 20 }}
data={images}
renderItem={({ item: image, index }) => (
<ImageRow
windowWidth={windowWidth}
image={image}
popImage={() => this.removeImage(index)}
/>
)}
keyExtractor={index => index}
/>
</View>
</View>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
profileName:{
fontWeight: 'bold',
fontSize: 22,
marginTop: 20,
},
list:{
marginTop: 40,
width: '100%'
},intro:{
flex: 1,
flexDirection: 'row',
flexWrap: 'wrap',
alignItems: 'flex-start',
padding: 25,
marginBottom: 30,
paddingTop: 80,
},
introText:{
marginLeft: 0,
marginRight: 50,
fontSize: 22,
fontWeight: "700",
marginTop:15,
},
introTextSpan:{
marginLeft: 0,
marginRight: 40,
fontSize: 15,
fontWeight: "200",
marginTop:50,
},
divider:{
width: SCREEN_WIDTH-50,
padding: 10,
borderBottomColor: 'grey',
borderBottomWidth: 0.5,
marginTop: 10,
marginLeft: 25
},
about:{
paddingLeft: 25,
},
aboutLiving:{
flexDirection: 'row',
flexWrap: 'wrap',
alignItems: 'flex-start',
paddingTop: 10
},
icon:{
fontSize: 18
},
aboutText:{
marginLeft: 30
},
iconGreen:{
color: 'green'
}
,
button: {
marginTop: 30,
marginBottom: 20,
paddingVertical: 10,
alignItems: 'center',
backgroundColor: '#019BB4',
width: 300
},
buttonText: {
fontSize: 20,
fontWeight: 'bold',
color: '#fff'
},
scorllVert:{
marginBottom: 40
},
btn: {
paddingLeft: 20,
paddingRight: 20,
paddingTop: 10,
paddingBottom: 10,
borderRadius: 20,
backgroundColor: 'rgb(3, 154, 229)',
marginTop: 20,
alignItems: 'center'
},
disabledBtn: {
backgroundColor: 'rgba(3,155,229,0.5)'
},
btnTxt: {
color: '#fff'
},
image: {
marginTop: 20,
minWidth: 200,
height: 200,
resizeMode: 'contain',
backgroundColor: '#ccc',
},
img: {
flex: 1,
height: 100,
margin: 5,
resizeMode: 'contain',
borderWidth: 1,
borderColor: '#eee',
backgroundColor: '#ccc'
},
progressBar: {
backgroundColor: 'rgb(3, 154, 229)',
height: 3,
shadowColor: '#000',
}
})
export default profileScreen;
I am fairly new to react native and would like to upload images, is there anyone who can help me get this done? Even if there are other methods, I am open to that!
Try wrapping your uploadImage function in a try..catch and see if you can get a clearer error message in catch
Try this code for uploading the image,
const ext = this.state.imageUri.split('.').pop(); // Extract image extension
const filename = `unique image' + ${ext}`; // Generate unique name
const imageRef = firebase.storage().ref('tutorials/images).child(filename +ext);
let mime = 'image/jpg';
imageRef.put(this.state.imageUri, { contentType: mime }).then((snapshot)=>{
console.log('Image uploaded successfully.')
}).catch((error)=>{
console.log('Image uploading failed');
});

React-Native-Camera Amazon Kindle 7 (5.1.1) Rear Camera captures Solid Green

For some reason 2 Amazon Kindles that I have for testing running the latest updates (Android 5.1.1) is producing just a solid green colour when capturing with React-Native-Camera.
I've also tested on my Xiaomi Mi6, a Mi5 and also an Asus Zen 8" Tablet, all working fine, but the Kindle produces this weired outcome... What's really strange is the viewfinder is fine, it looks as if it'll take a picture but doesn't. The Front Facing camera is fine also.
Using react-native-camera: ^1.1.4
Capture.js
import React, { Component } from 'react';
import { StyleSheet, Text, View, ActivityIndicator } from 'react-native';
import { Avatar } from 'react-native-elements';
import { RNCamera } from 'react-native-camera';
import { inject, observer } from 'mobx-react/native';
import ImagePicker from 'react-native-image-crop-picker';
let Type = null;
const typeArr = [
{ Name: 'Front', Type: RNCamera.Constants.Type.front },
{ Name: 'Back', Type: RNCamera.Constants.Type.back },
{ Name: null, Type: RNCamera.Constants.Type.back },
];
const styles = StyleSheet.create({
entryTitle: {
fontSize: 22,
fontWeight: '700',
},
container: {
flex: 1,
flexDirection: 'column',
},
preview: {
flex: 1,
justifyContent: 'flex-end',
alignItems: 'center',
},
loading: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'rgba(255, 255, 255, 0.8)',
},
});
#inject('store')
#observer
export default class Capture extends Component {
constructor(props) {
super(props);
this.state = { Type: RNCamera.Constants.Type.back, CaptureInProgress: false };
Type =
this.props.navigation.state.params.Type == null
? null
: this.props.navigation.state.params.Type;
}
state = {
Type: typeArr.find(element => element.Name === Type).Type,
};
barcodeScanned(response) {
this.props.store.CaptureStore.captureData = response.data;
this.props.navigation.state.params.AfterCapture();
this.props.navigation.goBack();
}
takePicture = async function () {
if (this.camera) {
this.setState({ CaptureInProgress: true });
const options = { quality: 0.5, base64: true, fixOrientation: true };
const data = await this.camera.takePictureAsync(options);
this.props.store.CaptureStore.captureData = data.base64;
this.props.navigation.state.params.AfterCapture();
this.setState({ CaptureInProgress: false });
this.props.navigation.goBack();
}
};
openGallery() {
ImagePicker.openPicker({
width: 300,
height: 400,
cropping: true,
includeBase64: true,
}).then((image) => {
this.props.store.CaptureStore.captureData = image.data;
this.props.navigation.state.params.AfterCapture();
this.props.navigation.goBack();
});
}
switchCamera() {
if (this.state.Type === RNCamera.Constants.Type.back) {
this.setState({ Type: RNCamera.Constants.Type.front });
} else {
this.setState({ Type: RNCamera.Constants.Type.back });
}
}
renderTakePhotoButton() {
if (this.props.navigation.state.params.Mode === 'photo') {
return (
<View
style={{
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Avatar
medium
rounded
icon={{ name: 'refresh', color: 'grey', type: 'font-awesome' }}
onPress={() => this.switchCamera()}
activeOpacity={1}
/>
<Avatar
large
rounded
icon={{ name: 'camera', color: 'grey' }}
onPress={() => this.takePicture()}
activeOpacity={1}
/>
<Avatar
medium
rounded
icon={{ name: 'folder-open-o', color: 'grey', type: 'font-awesome' }}
onPress={() => this.openGallery()}
activeOpacity={1}
/>
</View>
);
}
return null;
}
render() {
return (
<View style={styles.container}>
<View
style={{
height: '10%',
padding: 10,
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Text style={styles.entryTitle}>{this.props.navigation.state.params.Title}</Text>
</View>
<View
style={{
height: this.props.navigation.state.params.Mode === 'photo' ? '75%' : '90%',
flexDirection: 'column',
}}
>
<RNCamera
ref={(ref) => {
this.camera = ref;
}}
style={styles.preview}
barCodeTypes={
this.props.navigation.state.params.Mode === 'qr'
? [RNCamera.Constants.BarCodeType.qr]
: []
}
type={this.state.Type}
// flashMode={RNCamera.Constants.FlashMode.on}
permissionDialogTitle="Permission to use camera"
permissionDialogMessage="We need your permission to use your camera phone"
onBarCodeRead={response => this.barcodeScanned(response)}
/>
</View>
{this.renderTakePhotoButton()}
{this.state.CaptureInProgress && (
<View style={styles.loading}>
<ActivityIndicator size="large" color="#0000ff" />
</View>
)}
</View>
);
}
}
Specifically, it all happens in 'takePicture', the afterCapture just handles the consumption of the base64 that is temporarily in the CaptureStore...
Fixed in React-Native-Camera 1.1.5