React Native: How to play video in FlatList - reactjs

I have been in this code for a while in trying to list my videos from local directory to my react native app. I have asked couple of questions as a newbie in this regards earlier as I started the project but did not get any answer. Somehow I managed to get to this point.
I have been able to get my videos from my local directory but the issue am having now is that my videos are only showing grey thumbnails, and when I tap on any video it gives a ReferenceError: Can't find variable: videos
Below is my screenshot and my code. Please I need to correct the wrong things am doing on this code. Thanks in advance for your help.
constructor(Props) {
super(Props);
this.state = {
videos: [],
playing: false,
screenType: 'content',
resizeMode: 'contain',
currentTime: 0,
videoPlayer: null,
duration: 0,
isFullScreen: false,
isLoading: true,
paused: false,
playerState: PLAYER_STATES.PLAYING,
};
This is my constructor
componentDidMount() {
var filePath = RNFetchBlob.fs.dirs.MovieDir + '/Voc Vedos/';
RNFetchBlob.fs.ls(filePath)
.then(files => {
this.setState({videos:files});
console.warn(files);
}).catch(err=>alert(err.toString()))
}
This is where I got my videos from my local directory on my device
render() {
const { duration, currentTime, paused, overlay } = this.state
return(
<View style={styles.container}>
<FlatList
data={this.state.videos}
keyExtractor={item=>item}
ItemSeparatorComponent={() => { return (<View style={styles.separator} />) }}
// viewabilityConfig={this.viewabilityConfig}
// onViewableItemsChanged={this.onViewableItemsChanged}
// viewabilityConfig={{
// itemVisiblePercentThreshold: 95
// }}
numColumns={3}
renderItem={({ item, index, separators }) => (
<TouchableOpacity
onPress={() => this._onPress(item)}
style={{width:100,height:100}}>
<View
style={{width:100,height:100, margin:8}}
>
<Video
source ={{uri: '/storage/emulated/0/Movies/Voc Vedos/'+{item}}}
ref={(ref: Video) => { this.video = ref }}
style={{width:100,height:100}}
rate={this.state.rate}
paused={this.state.paused}
volume={this.state.volume}
muted={this.state.muted}
resizeMode={this.state.resizeMode}
onLoad={this.onLoad}
onProgress={this.onProgress}
onEnd={this.onEnd}
onAudioBecomingNoisy={this.onAudioBecomingNoisy}
onAudioFocusChanged={this.onAudioFocusChanged}
/>
<MediaControls
isFullScreen={this.state.isFullScreen}
duration={duration}
isLoading={this.state.isLoading}
mainColor="purple"
onFullScreen={noop}
onPaused={this.state.onPaused}
onReplay={this.state.onReplay}
onSeek={this.state.onSeek}
onSeeking={this.state.onSeeking}
playerState={this.state.playerState}
progress={currentTime}
/>
</View>
</TouchableOpacity>
)}
/>
</View>
This is my render code.
please I need help on how to display my videos correctly and play video when tapped

according to react-native-video docs:
for file source in device storage, must write 'file://' begin of path
Example:
source={{ uri: 'file:///sdcard/Movies/sintel.mp4' }}
read document https://github.com/react-native-community/react-native-video#source

Related

Fetching document as JSON using React-Native

I have found some similar solutions but not one that does exactly what I want.
Here is what I wanna do: I have a few documents saved as JSON in a server, I want to fetch those documents using React-Native and display them on my phone.
However, think about a solution when I don't have to change my code every time I upload a new document to the server. React-native should be able to fetch everything from the server, even the new documents, without having to add new lines of code in the return{}. Those documents might differ from each other, some includes only text, some include text and input fields, some include pictures, text and input fields.
If something is unclear please let me know in the comment section.
Any suggestion would be highly appreciated!
Example of JSON how it would look like:
{
"results":[
{
"contract":{
"title":"Contract test",
"content":"You can always follow the progress of your application by logging on the the application portal. Please note that all communication from DTU will take place via this portal. When we have sent you a message on the ..."
},
"fillable_fields": {
"FIELD_NAME_1": "FIELD_VALUE_1",
"FIELD_NAME_2": "FIELD_VALUE_2",
"FIELD_NAME_N": "FIELD_VALUE_N"
},
"picture":{
"medium":"https://www.healthcaredenmark.dk/media/11272/bridgeit_logo.png"
}
}
]
}
My code in React-Native:
class HomeScreen extends React.Component {
constructor() {
super();
this.state = {};
this.getRemoteData();
}
static navigationOptions = {
title: 'List of documents',
};
getRemoteData = () => {
const url = "https://demo8106568.mockable.io/results";
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: res.results
});
})
.catch(error => {
console.log("get data error from:" + url + " error:" + error);
});
};
capFirstLetter = (string) => {
return string.charAt(0).toUpperCase() + string.slice(1);
}
renderNativeItem = (item) => {
const contract =
this.capFirstLetter(item.contract.title);
//this.capFirstLetter(item.name.content);
return <ListItem
roundAvatar
title={contract}
subtitle={item.content}
avatar={{ uri: item.picture.thumbnail }}
onPress={() => this.onPressItem(item)}
/>;
}
onPressItem = (item) => {
this.props.navigation.navigate('Detail', {item: item})
}
render() {
return (
<View>
<FlatList
data={this.state.data}
renderItem={({item}) => this.renderNativeItem(item)}
/>
{/* <Button
title="Go Detail"
onPress={() => this.props.navigation.navigate('Detail', {source: "homescreen"})}
/> */}
</View>
);
}
}
class DetailScreen extends React.Component {
static navigationOptions = {
title: 'Content of selected'
};
render() {
const source = this.props.navigation.state.params.source;
const item = this.props.navigation.state.params.item;
let contract = "";
let img = "";
let inp = "";
let content ="";
if (item != null) {
contract = item.contract.title;
img = item.picture.medium;
content = item.contract.content;
inp = item.fillable_fields.FIELD_NAME_1;
}
return (
<View style={styles.container}>
<Text style={styles.text}>{contract} </Text>
<Image
style={{width: 300, height: 128}}
source={{uri: img}}
/>
<Text style={styles.text} > {content} </Text>
<TextInput style={{textAlign: 'center', borderWidth:1, marginBottom: 7, height: 50}} source={{uri: inp}}/>
<Button title="Go back to the list" onPress={this._goHome}/>
</View>
);
}
_goHome = async () => {
this.props.navigation.navigate('Home');
};
}
I understand what you are trying to accomplish. But I really don't think you can make it work like you want. You can compare it to calling a normal API endpoint. You will most likely have a method like:
getContracts() {
fetch('CONTRACTS_ENDPOINT').then(res => doSomethingWithContracts(res))
}
You already know that this data returns contracts and you already know what data to expect there. Therefore you can easy access fields like contract.name, or contract.date.
And when you want to call some other endpoint you will do something similar
getSomethingElse() {
fetch('OTHER_ENPOINT').then(res => ...)
}
You will know the data that comes with the OTHER_ENPOINT, so you can directly access its fields.
So my suggestion is, think of each of you document as a separate API endpoint. Of course, if you change your document, you will also need to change client side implementation, so for example if you rename contract.title to contract.otherWordForTitle then you will obviously need to change that on the client as well.
From what I know, what you want, to have the client always know the document structure without updating it in order to know that a document has changed, is not possible. But of course, I might be wrong and there can be a workaround :-)

React Native : How to open a specific URL with onPress function from an array of urls?

I have an array of urls like that:
[ "https://hey.com",
"https://yes.com",
"https://wow.com",
/..
]
I have the same icon but multiple times. I want each of them to redirect to its specific url when on pressed.
I tried this code but it's not working:
onPress=(arrayOfURL)=>{
for (i in arrayOfURL)
{
this.setState({ browserOpen: true });
WebBrowser.openBrowserAsync(JSON.stringify(arrayOfURL[i]))
.then(() => {
WebBrowser.dismissBrowser();
this.setState({ browserOpen: false });
});
}
}
The code for the icon:
<View >
<Icon
name='sc-telegram'
type='evilicon'
color='black'
onPress={this.onPress} />
</View>
A quick help ! Have not tested the code, but this is what i do.
const urls = ["https://hey.com", "https://yes.com", "https://wow.com"];
urls.map(value => {
return <View>
<Icon
name='sc-telegram'
type='evilicon'
color='black'
onPress={() => this.openSpecificUrl(value)} />
</View>
})
onSpecificUrl = (specificUrl) => {
//you will get specific url from an array. You can perform your opening logic here
}
So it seems you're doing a logic error. You don't need a for loop, just call onPress() method by each icon passing it a parameter that specifies the link you want to be opened:
<Icon
name='sc-telegram'
type='evilicon'
color='black'
onPress={() => this.onPress(arrayOfURL[0])} />
And then simply:
onPress = (url) => {
[...]
WebBrowser.openBrowserAsync(url).then(...)
[...]
}
If your problem is how to assign your link to icons dynamically...well that's just an other question. I hope I haven't misunderstood everything this time and that this can helps you solve your doubts.

Assigning an id to this.state to play the correct animation? React Native

I have been working ways to figure this problem out. The way I have my code set up is that I have animations in a flatlist and it has created a single animation for how ever many data I have. For example, my JSON data contains 8 categories, my flatlist automatically contains 8 animations. Here is my code:
<FlatList
data={ this.state.dataSource}
ItemSeparatorComponent = {this.FlatListItemSeparator}
renderItem={({item}) => <View>
<Card>
<View>
<TouchableOpacity
onPress={this.anim_star.bind(this, item.id)}
style={{position:'absolute', height: '100%', width: '100%',}}>
<Animation
progress={this.state.progress[item.id]}
source={require('../Animations/favourite_app_icon.json')}
style={{ height: '100%', width: '100%', position: 'absolute'}}
resizeMode={`contain`}
/>
</TouchableOpacity>
</View>
</Card>
</View>
}
keyExtractor={(item, index) => index.toString()}
removeClippedSubviews
/>
Right here, I have been trying to give those animations a unique id (item.id). Here is the function I used for binding:
anim_star = (id) => {
let progress = this.state.progress;
progress[id] = new Animated.Value(0);
this.setState({ progress });
console.log(this.state.progress);
Animated.timing(this.state.progress, {
toValue: 1,
duration: 2000,
easing: Easing.linear,
}).start();
}
I did a console.log to see my results and I can see the correct id being displayed, but I don't think it's corresponding correctly. The animation does not play at all. When I take off the [item.id] in the progress=this.state.progress, the animation works, but every animation in that flatlist plays. I want to play that one animation with the correct id.
Here is my console results:
https://imgur.com/a/7Ja3Tlk
And lastly here are what the constructors look like:
constructor(props)
{
super(props);
this.state = {
isLoading: true,
id: '',
dataSource: '',
progress: {},
};
Also to note, when all animations did play I console logged those results and noticed that the _children array have [AnimatedProps] and I think that is what triggers the animation.
https://imgur.com/a/9a3VPZa
The array(8) represents all the data that have animations triggered. The reason why the 10 is still there is because I didn't remove:
let progress = this.state.progress;
progress[id] = new Animated.Value(0);
I tried searching for some answers similar to my problem, but I could not find a single solution. Is there a way to play animations uniquely?
Also you may notice that is not part of the react-native library, that is because I am using Lottie to play json animations.

React Native - setNativeProps() on parentElement.props.children = undefined

I'm developing a school management app for myself.
All students in my class are listed in a Flatlist with their parents' phone numbers beside to enable me to send them text messages when a student is absent.
I have a FlatList with Listitems, each of which contains a Touchopacity component with Text child inside.
On successful sending an sms to a student's parent (smsParent method) I want to setNativeProps on both TouchOpacity and its Text child (manipulate their style props). I use ref=(()=>...) to have reference to Touchopacity and then this.props.children (only 1 child) to get to its Text child.
Then however I cannot use setNativeProps (=undefined).
However, when I use ref=(()=>...) on Text as well and then refer to it, setNativeProps works /like in case of its parent/.
Why can't I use setNativeProps() on a child when refering to it by parentEl.props.children? (only 1 child, checked in debugger, it's properly identified)
Please read comments in smsParent method
/*sorry for inserting a snippet - code insertion was crazily formatted/
/**code simplified/
class SingleClassPage extends Component {
buttons = new Array();
constructor(props) {
super(props);
this.state = { students: [] };
this.smsParent = this.smsParent.bind(this);
}
componentDidMount() {
//fetch students from api and setState..
this._getStudentsList();
}
_getStudentsList() {
// ...
}
//flatlist item
ListEl(props) {
return (
<View>
<TouchableOpacity ref={el => { let a = props.item.attId + 'att'; props.buttons[a] = el; }}
style={[styles.buttonDef, (props.item.phone_parent ? styles.buttonBlue : styles.buttonGray)]}
onPress={() => { props.smsSendHandler(props.item, 'attendance', a) }}>
<Text style={props.item.phone_parent ? styles.buttonTextLight : styles.buttonTextDark}>
{props.item.smsAttSent ? 'sms sent' : 'sms send'}
</Text>
</TouchableOpacity>
</View>
)
}
render() {
return (
<View style={{ flex: 1, }}>
<FlatList
data={this.state.students}
extraData={this.state}
keyExtractor={item => item.attId}
renderItem={({ item }) => <this.ListEl buttons={this.buttons} item={item} smsSendHandler={this.smsParent} />}
/>
<BusyIndicator />
</View>
);
}
smsParent(student, msgCategory, smsButton) {
//call myjava module and upon successful callback call below:
let parEl = this.buttons[smsButton];
//childEl is an object with props.children set to text 'sms sent/send' when I watch its value in debugger
//so it's correctly identified
let childEl = parEl.props.children;
// WORKS
parEl.setNativeProps({ style: { backgroundColor: 'green' } });
// OOPS
childEl.setNativeProps({ style: { color: 'black' } });
}
}
edit1
Posting a screenshot of the error (also as response to Dyo's suggestion below - the same error Dyo...)
I think you have to iterate throught children to pass them nativeProps (even if there's only one child) :
smsParent(student, msgCategory, smsButton) {
//call myjava module and upon successful callback call below:
let parEl = this.buttons[smsButton];
React.Children.forEach(parEl.props.children, child => { child.setNativeProps({ style: { color: 'black' } }) });
parEl.setNativeProps({ style: { backgroundColor: 'green' } });
}

ScrollView Breaking When Adding Adjacent Component in React/js

I am working with React Native Web and Reactjs and I have the following code on one of my pages.
What I'm trying to do is make the background image and footer scrollable, while keeping the header (HorizontalNavBar) fixed at the top,. The ScrollView works fine when I don't try to render the VerticalNavBar over it, but breaks when I try to do so. It doesn't even work if the VerticalNavBar is present but rendering null.
Any help would be greatly appreciated. Attaching a screenshot. Thx!
var Home = React.createClass({
getInitialState: function() {
return {
source: homeImageSmall,
windowWidth: window.innerWidth,
tutorialDirection: 'row',
verticalNavOpen: false};
},
closeVerticalNav: function() {
this.setState({verticalNavOpen: false})
},
openVerticalNav: function() {
this.setState({verticalNavOpen: true})
},
render() {
return (
<View>
<View style={{flex:0}}>
<HorizontalNavBar verticalNavOpen = {this.state.verticalNavOpen} openVerticalNav = {this.openVerticalNav}/>
<ScrollView style={{flex:0}}>
<View>
<Image
style={contentStyle.backGroundImage}
source={this.state.source}>
<View style = {[styles.doubleIphoneWrapper, {flexDirection: this.state.tutorialDirection}]}>
<Tutorial content={Baseball} name={"Baseball"}/>
<Tutorial content={Football} name={"Football"}/>
</View>
</Image>
<Footer/>
</View>
</ScrollView>
<VerticalNav verticalNavOpen = {this.state.verticalNavOpen} closeVerticalNav = {this.closeVerticalNav}/>
</View>
</View>
)
}
})
Turns out I had overflow: hidden in my HTML file. The above code works if I remove that and set the position of both the horizontal and vertical menus to 'fixed'. #facepalm

Resources