ActivityIndicator while takePictureAsync with Expo Camera - react-native

I'm developing a React Native app and I want to show an ActivityIndicator component while the Expo Camera is processing the image.
<TouchableOpacity style={{alignSelf: 'center'}} onPress={takePicture}>
<FontAwesome name="camera" style={{ color: "#FEA428", fontSize: 50, marginBottom: 20}}/>
</TouchableOpacity>
So the function takePicture do this:
const takePicture = async () => {
setLoading(prevState => !prevState)
if(cameraRef){
let photo = await cameraRef.takePictureAsync({quality: 0.5, skipProcessing: true, fixOrientation: false});
setLoading(prevState => !prevState)
setPhotoAbove(photo)
}
}
setLoading change the "isLoading" prop which say if the ActivityIndicator is going to be visible or not. However, after the first setLoading(prevState => !prevState) then the following code is never executed. Am I missing something here?
EDIT: I'm posting an Expo Snack with the code for testing purposes. In Web the problem isn't visible but for example if you run it on Android it will.
https://snack.expo.io/#avradev/0a8a01
Tks.

You should move your second setLoading just after your await method call like this:
const takePicture = async () => {
setLoading(prevState => !prevState)
if(cameraRef){
let photo = await cameraRef.takePictureAsync({quality: 0.5, skipProcessing: true, fixOrientation: false});
setLoading(prevState => !prevState);
setPhotoAbove(photo);
}
}

Related

screenOptions:{{tabBarHideonKeyboard: true}} not Working

When I am using custom tab bar through tabBar function tabBarHideOnKeyboard does not work but without tabBar function it works fine, any ideas on how I can make it work using tabBar function as well.
Add "softwareKeyboardLayoutMode": "pan" in app.json file under "android" key and then restart your expo server with expo start -c
<Tab.Navigator
tabBarOptions={{
showLabel: false,
keyboardHidesTabBar: true, // use this props to hide bottom tabs when keyboard shown
}}
the docs says to use tabBarHideOnKeyboard, but not working at all.
then i found keyboardHidesTabBar and works like a charm
I was using my customTab as well. And after huge amount of search, solved the problem with the help of Keyboard event listeners.
This is the best solution, I've found so far.
Here's my solution:
import { useEffect, useState } from "react";
import { Keyboard, Text, TouchableOpacity, View } from "react-native"
export default function TabBar({ state, descriptors, navigation }) {
// As default it should be visible
const [visible, setVisible] = useState(true);
useEffect(() => {
const showSubscription = Keyboard.addListener("keyboardDidShow", () => {
//Whenever keyboard did show make it don't visible
setVisible(false);
});
const hideSubscription = Keyboard.addListener("keyboardDidHide", () => {
setVisible(true);
});
return () => {
showSubscription.remove();
hideSubscription.remove();
};
}, []);
//Return your whole container like so
return visible && (
<View>
...
</View>
)
tabBarHideOnKeyboard or keyboardHidesTabBar options didn't work for me.
You'll get the tabBarHideOnKeyboard from the props for the custom tabBar.
tabBar={(props) => {
return (
<View>
{props.state.routes.map((route, index) => {
// You can replace Pressable and View with anything you like
return (
props.descriptors[route.key].options.tabBarHideOnKeyboard && (
<Pressable>
<View
style={{
width: 200,
height: 200,
backgroundColor: "green",
}}
/>
</Pressable>
)
);
})}
</View>
);
You can read more here

React Native Expo Video av-expo -- Directly play as fullscreen

I'm trying to do this by react-native using the av-expo video.
What I'm trying to do is to launch the video directly in full screen without going through the "Video" stack (without the double loading of the MediaPlayerScreen stack + the native fullScreen stack).
If the user mutes the full screen by the native fullScreen output button, then we go back directly to a stack
The idea is to use only the native fullscreen stack to display the video. Is this possible?
I don't know if I'm clear, if it can help to understand, here is the code of my MediaPlayerScreen component
export class MediaPlayerScreen extends Component {
static navigationOptions = {
//header: null,
headerTitle: '',
headerTransparent: false,
headerTintColor: 'white',
}
constructor(props) {
super(props)
this.AG = AG.instance
this.filePath =
this.AG.getFilePath() + props.navigation.state.params.file
this.windowWidth = Dimensions.get('window').width
this.windowHeight = Dimensions.get('window').height
}
//
onPlaybackStatusUpdate = (playbackStatus) => {
if (playbackStatus.didJustFinish) {
this.props.navigation.goBack()
}
}
//
_handleVideoRef = async (component) => {
const playbackObject = component
if (playbackObject) {
await playbackObject.loadAsync({
uri: this.filePath,
shouldPlay: false,
posterSource: this.poster,
})
// todo: Trigger fullScreen without videoStack loading
//playbackObject.presentFullscreenPlayer();
playbackObject.playAsync()
//playbackObject.setOnPlaybackStatusUpdate(this.onPlaybackStatusUpdate);
}
}
componentDidMount() {
ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.LANDSCAPE)
}
componentWillUnmount() {
//playbackObject.dismissFullscreenPlayer();
//this.props.navigation.goBack();
ScreenOrientation.lockAsync(
ScreenOrientation.OrientationLock.PORTRAIT_UP
)
}
onFullscreenUpdate = ({ fullscreenUpdate, status }) => {
console.log(fullscreenUpdate, status)
switch (fullscreenUpdate) {
case Video.FULLSCREEN_UPDATE_PLAYER_WILL_PRESENT:
console.log(' the fullscreen player is about to present')
break
case Video.FULLSCREEN_UPDATE_PLAYER_DID_PRESENT:
console.log('the fullscreen player just finished presenting')
break
case Video.FULLSCREEN_UPDATE_PLAYER_WILL_DISMISS:
console.log('the fullscreen player is about to dismiss')
break
case Video.FULLSCREEN_UPDATE_PLAYER_DID_DISMISS:
console.log('the fullscreen player just finished dismissing')
}
}
render() {
return (
<SafeAreaView style={styles.container}>
<Video
ref={this._handleVideoRef}
useNativeControls
rate={1.0}
resizeMode="contain"
onPlaybackStatusUpdate={(playbackStatus) =>
this.onPlaybackStatusUpdate(playbackStatus)
}
onFullscreenUpdate={this.onFullscreenUpdate}
style={{
width: this.windowHeight,
height: this.windowWidth,
}}
/>
</SafeAreaView>
)
}
}
Thanks for your help,
Meums/
hey if I am not getting you wrong you want to load the player fullscreen by default.you can follow this approach:
const videoRef = useRef(null);
<Video
ref={videoRef}
useNativeControls={false}
style={styles.container}
isLooping
source={{
uri: videoUri,
}}
onLoad={()=>{
videoRef?.current?.presentFullscreenPlayer();
}
resizeMode="contain"
onPlaybackStatusUpdate={(status) => setStatus(() => status)}
/>

callback function in useEffect caches state, even when dependency changes?

I've seen a couple of close questions, but none that really answered my question. I have the following code in React Native.
# activities add/edit screen
# ...
const [activities, setActivities] = useState([]);
useEffect(() => {
const _setup = async () => {
const temp = await fetch(...); // fetching data from server with await
setActivities(temp);
// building save button
navigation.setOptions({
headerRight: () => (
<TouchableOpacity onPress={() => _submit()}>
<Text style={{ color: '#007AFF', fontSize: 18, }}>
Save
</Text>
</TouchableOpacity>
)
});
setReady(true);
};
_setup();
}, []);
So I build the headerRight button in useEffect to pass it an local _submit function, which looks like this.
const _submit = async () => {
console.log(activities);
try {
// fetch to send data to server
} catch (e) {
showError(e);
}
};
There is a FlatList on this screen which gets dynamically extended based on user interaction. All is well until the user presses the save button in the header. It always loads the activities array from the last hot refresh/render. But the FlatList re-renders ok, the array gets extended just as I want it to be. I tried using "useCallback" on the _submit function and set the dependency to "activities", but still, the header button seems to call the "initial" _submit function. The only thing that helped was to split the useEffect into two separates and one handling the re-render of the button.
// first one to fetch data on initial mound
// ...
useEffect(() => {
// I am using react-navigation and react-native-screens 2.7.0
navigation.setOptions({
headerRight: () => (
<TouchableOpacity onPress={() => _submit()}>
<Text style={{ color: '#007AFF', fontSize: 18, }}>
Save
</Text>
</TouchableOpacity>
)
});
}, [activities]);
It works fine, but somehow feels kind of hacky... I was under the impression that normal functions (like the _submit) would get re-constructed with each re-render, which would be triggered with e.g. a new array-element being pushed to the activities, no? For completion, I add elements to activities like this.
const _addActivity = () => {
const temp = [...activities];
const initActivity = {
title: '', // will be set later via TextInput
startTime: new Date(),
endTime: new Date(),
}
temp.push(initActivity);
setActivities(temp);
}

React Native Youtube Video is not playing if not fullscreen

I am facing this weird issue, my youtube videos are not playing if the fullscreen is set to false. But if I set it to true, then it works fine. I dont need the full screen. Following is my code
{this.state.showYoutube &&
<YouTube
apiKey="MY-YOUTUBE-API-KEY"
videoId={this.state.videoId} // The YouTube video ID
play={false} // control playback of video with true/false
fullscreen={false} // control whether the video should play in fullscreen or inline
loop={false} // control whether the video should loop when ended
//onReady={e => this.setState({ isReady: true })}
//onChangeState={e => this.setState({ status: e.state })}
//onChangeQuality={e => this.setState({ quality: e.quality })}
//onError={e => this.setState({error: e.error})}
onError={e => console.log(e.error)}
style={{alignSelf: 'stretch', height: 300}}
/>
}
if this.state.showYoutube is set to true then Youtube will be shown and the this.state.videoId is set after an api call,
I have used height and removed native base drawer after facing overlaying issue. Then it was working.
But now it is not working again, please tell me what I am doing wrong.
Found the solution here
export default class extends Component {
state = {
height: 215
}
handleReady = () => {
setTimeout(() => this.setState({ height: 216 }), 500);
}
render() {
return (
<YouTube
apiKey = {config.YOUTUBE_API_KEY}
ref = {item => this.player = item}
videoId = {getVideoId(uri)}
controls = {1}
onReady = {this.handleReady}
style = {{ alignSelf: 'stretch', height: height }}
/>
);
}
}
It worked for me, but still there is a known issue which is that if resumed from a full screen, the screen is not auto oriented.

Error: Error: No views in hierarchy found matching: (with tag value: is "email" and view has effective visibility=VISIBLE)

Hi I am doing detox test for react native android version 0.57. I have two screens launch screen and login screen. I am able to go to login screen but while testing elements in login screen I am getting this error. Code is
`
describe.only('test2', () => {
before(async () => {
await device.reloadReactNative();
});
it('should have welcome screen', async () => {
await expect(element(by.text('Welcome'))).toBeVisible();
});
it('should Click button', async () => {
await expect(element(by.id('login'))).toBeVisible();
await element(by.id('login')).tap();
});
it('should select email', async () => {
await expect(element(by.id('email'))).toBeVisible();
});
})
`
while executing last it i am getting this error. help me out this issue.
render function is
render() {
return (
<View style={styles.mainContainer} testID='email'>
{this.renderTopLogoContainer()}
{this.renderBottomContainer()}
<View style={{ height: 30, justifyContent: 'center' }}>
{this.state.useMobile ? <Text style={{ color: colors.SECONDARY_FONT_COLOR, alignSelf: 'center', fontSize: 13, }} onPress={() => { this.setState({ useMobile: false, wrongEntryMesaage: '', userName: "" }) }}>{I18n.t("Use email")}</Text> : <Text style={{ color: colors.SECONDARY_FONT_COLOR, alignSelf: 'center', fontSize: 13 }} onPress={() => { this.setState({ useMobile: true, wrongEntryMesaage: '', userName: "" }) }}>{I18n.t("Use mobile")}</Text>}
</View>
<Button
rounded title={I18n.t("GET OTP")}
buttonStyle={styles.button}
disabled={
this.state.useMobile ? (this.state.userName.length === 10) && (this.props.processingRequest) || !(this.state.userName.length === 10) && !(this.props.processingRequest)
: (this.state.userName.length > 0) && (this.props.processingRequest) || !(this.state.userName.length > 0) && !(this.props.processingRequest)}
onPress={this.onGetOTPForUserNamePressedDebounced}
/>
{this.props.getOtpFailed ? Snackbar.show({
title: this.props.error.display_message,
duration: Snackbar.LENGTH_LONG
}) : null}
</View>
);
}
Check the view hierarchy for the testID
So when trying to debug testIDs not showing you should check the view hierarchy it is easiest done in iOS. Instructions for doing it can be found here https://github.com/wix/Detox/blob/master/docs/Troubleshooting.RunningTests.md#debug-view-hierarchy
Try a timeout
If it is in the view hierarchy then Detox could be expecting the testID before it has actually is displayed. You could edit your last test so that it uses a waitFor with a timeout.
https://github.com/wix/Detox/blob/master/docs/Troubleshooting.RunningTests.md#test-tries-to-find-my-component-before-its-created
Something like this may work. Though you may need to adjust the time of the timeout
await waitFor(element(by.text('email'))).toBeVisible().withTimeout(2000);
Watch your test
You should also watch your test and make sure that it is performing as is expected. Is it navigating from screen to screen? Can you see the buttons being tapped?
Perhaps even though it is clicking the button the navigation isn’t occurring.