Get URL to my media player from Firestore by using redux - firebase

I try to add my URL from firebase to my mediaplayer by using redux. If I print my URL in a text component in my flatlist, the URL display. But when I try to set the url to my media player it doesn't work. If I just copy the url from firebase it works. So what schould I do if I want my URL adress from Firebase by using redux?
async function playSound() {
console.log('Loading Sound');
const { sound } = await Audio.Sound.createAsync(
{uri: 'Url copied from firebase storage works fine here.'}
//But if I try to get URL from my redux I doesn't work.
{uri: userSongs.downloadURL}
);
When I set the url in my flatlist like this it works fine.
<FlatList
numColumns={3}
horizontal={false}
data={userSongs}
renderItem={({ item }) => (
<View>
<Text>{item.downloadURL}</Text>
</View>
)}
/>
this is my action.js in my redux
export function fetchUserSongs() {
return ((dispatch) => {
firebase.firestore()
.collection("users")
.doc(firebase.auth().currentUser.uid)
.collection("usersSong")
.orderBy("creation", "asc")
.get()
.then((snapshot) => {
let posts = snapshot.docs.map(doc => {
const data = doc.data();
const id = doc.id;
return { id, ...data }
})
dispatch({ type: USERS_SONGS_STATE_CHANGE, posts })
console.log(posts);
})
})
}

Related

“user Does not exists” Firebase

I started this tutorial (https://www.freecodecamp.org/news/react-native-firebase-tutorial/) on Firebase and React Native. Everything is working well overall.
But I have this error: “User does not exist anymore.” for the Login.
However, users are well rooted in Firebase.
const onLoginPress = () => {
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then((response) => {
const uid = response.user.uid
const usersRef = firebase.firestore().collection('users')
usersRef
.doc(uid)
.get()
.then(firestoreDocument => {
if (!firestoreDocument.exists) {
alert("User does not exist anymore.")
return;
}
const user = firestoreDocument.data()
navigation.navigate('Home', {user})
})
.catch(error => {
alert(error)
});
})
.catch(error => {
alert(error)
})
}
With
const usersRef = firebase.firestore().collection('users')
usersRef
.doc(uid)
.get()
.then(firestoreDocument => {
if (!firestoreDocument.exists) {
alert("User does not exist anymore.")
return;
}
const user = firestoreDocument.data()
navigation.navigate('Home', {user})
})
you actually query the user document with the id corresponding to the user's uid in the users collection.
This document is normally created by the onRegisterPress() function in the tutorial. If you get the "User does not exist anymore." message, it means that the user document is not present in the collection.
So you need to check why this is the case: the onRegisterPress() function was not called? The doc was deleted? There are security rules that prevent creating the document? etc...

Get value from firebase and use it in useState React native

First the user chooses his allergies by clicking on a toggle button. If the state is true or false the state is being added successfully to firebase.
Allergy Selctor Page
Then in my homepage I want to retrieve that allergy state and apply it in the allergy list state located on home page.
Home Page Image
I am able to get the state value from firebase using this
useEffect(() => {
firebase
.firestore()
.collection("userDb")
.where("userId", "==", user.email)
.onSnapshot(
(querySnapshot) => {
const newTypes = [];
querySnapshot.forEach((doc) => {
const type = doc.data();
newTypes.push(type);
});
setTypes(newTypes[0].Dairy);
setStatus(type);
},
(error) => {
console.log(error);
}
);
}, []);
useEffect(() => {
const fetchFav = async () => {
try {
const list = [];
await firebase
.firestore()
.collection("userDb")
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
const { Dairy, Peanut } = doc.data();
list.push({
userid: doc.id,
dairy: Dairy,
peanut: Peanut,
});
});
});
setPosts(list);
if (loading) {
setLoading(false);
}
} catch (e) {
console.log(e);
}
};
fetchFav();
searchApi(term);
}, []);
But I don't know how to apply this to my state. I wanted to use type in status(type) which gets the actual true/false value when I console log it gets the right value from firebase but for some reason its always true in status(type);
const [status, setStatus] = useState(type);
<Button
onPress={() => setStatus(!status)}
title={`Prevent Dairy: ${
status ? "on (search again for new results)" : "off"
}`}
></Button>
I want it to be dynamic as the user can change his allergies so I didn't use route.params for the allergies.
Any ideas appreciated. Thanks.

How to combine two Redux Firebase Cloud Firestore queries?

I have two redux queries that pull posts from my Firebase Firestore. The first successfully displays all of the posts of the people I'm following:
export function fetchUsersFollowingPosts(uid) {
return ((dispatch, getState) => {
firebase.firestore()
.collection("posts")
.doc(uid)
.collection("userPosts")
.orderBy("creation", "asc")
.get()
.then((snapshot) => {
const uid = snapshot.query.EP.path.segments[1];
const user = getState().usersState.users.find(el => el.uid === uid);
let posts = snapshot.docs.map(doc => {
const data = doc.data();
const id = doc.id;
return { id, ...data, user }
})
for (let i = 0; i < posts.length; i++) {
dispatch(fetchUsersFollowingLikes(uid, posts[i].id))
}
dispatch({ type: USERS_POSTS_STATE_CHANGE, posts, uid })
})
})
}
The second shows all of my own posts.
export function fetchUserPosts() {
return ((dispatch) => {
firebase.firestore()
.collection("posts")
.doc(firebase.auth().currentUser.uid)
.collection("userPosts")
.orderBy("creation", "desc")
.get()
.then((snapshot) => {
let posts = snapshot.docs.map(doc => {
const data = doc.data();
const id = doc.id;
return { id, ...data }
})
dispatch({ type: USER_POSTS_STATE_CHANGE, posts })
})
})
}
Here's where I currently list the users from the people I follow. But how do I combine them so I can show both my posts and those of the people that I'm following in a single FlatList?
function Feed(props) {
useStatusBar('dark-content');
const [posts, setPosts] = useState([]);
const [refreshing, setRefreshing] = useState(false)
useEffect(() => {
if (props.usersFollowingLoaded == props.following.length && props.following.length !== 0) {
props.feed.sort(function (x, y) {
return y.creation.toDate() - x.creation.toDate();
})
setPosts(props.feed);
setRefreshing(false)
}
}, [props.usersFollowingLoaded, props.feed])
return (
<View style={styles.background}>
{posts.length > 0 ?
<View style={styles.containerGallery}>
<FlatList
refreshControl={
<RefreshControl
refreshing={refreshing}
tintColor="white"
onRefresh={() => {
setRefreshing(true);
props.reload()
}}
/>
}
showsVerticalScrollIndicator={false}
numColumns={1}
horizontal={false}
data={posts}
renderItem={({ item }) => (
<View style={styles.containerImage}>
<Card title={item.title} onPress={() => props.navigation.navigate(routes.GOOD_STUFF_DETAIL, { item: item, postId: item.id, uid: item.user.uid, user: item.user,})} showLike={true} author={"Recommended by " + item.user.name} likeItem={item} likeCount={item.likesCount} icon={categories.categories[item.categoryID].icon} timeStamp={timeDifference(new Date(), item.creation.toDate())}/>
</View>
)}
/>
</View>
: <NothingHere title="Follow friends" text="To see their Good Stuff here" icon="search" color="white"/> }
</View>
)
}
const mapStateToProps = (store) => ({
currentUser: store.userState.currentUser,
following: store.userState.following,
feed: store.usersState.feed,
usersFollowingLoaded: store.usersState.usersFollowingLoaded,
})
const mapDispatchProps = (dispatch) => bindActionCreators({ reload }, dispatch);
export default connect(mapStateToProps, mapDispatchProps)(Feed);
Below is my data structure:
Thanks for reading!
Maybe I misunderstood the database structure but AFAIK it doesn't seem possible to combine both queries. From what I see of the database structure you want to retrieve the posts of userB where you (userA) gave a "like". In order to get that information you scan across the path posts/{userUID}/userPosts/{docId}/likes. Given the queries scan different collection ranges I don't see a way to mix them.
Separately, let's assume for the sake of the argument that you already have a list containing the user UIDs of the people you follow. Then, the Firestore query feature that gets closer to the desired behavior are Collection Group queries. Having in mind the structure of the database:
posts
uid1
userPosts
doc1
doc2
uid2
userPosts
docX
docY
Essentially, collection group queries are a way to simultaneously query across all userPosts collections at once. If each document were to have the author ID as a field you would be able to do something like this:
db.collectionGroup("userPosts").where("uid", "in", ListOfFollowedUsers)
This won't totally solve the problem because the in operator clause is limited to 10 values, so you may apply it at most for 10 followed users.
Overall I would suggest to keep the queries separated and merge them in the application code.
Based on your data, and knowing for a fact how limited queries on Firestore are, you need to do that merge on the client.
What I would do is to keep the list on redux and handle the merge on the reducer. You just need to listen to both actions and then merge them as an array on your app.
If you want to avoid the user seeing partial data (eg, you show your own posts and then another refresh just adds your followers post thus the UI will change) you might want to keep two flags (booleans) while you are loading data and show a spinner if both lists haven't been loaded.
Unless you want to refactor your code, then a lot of merging happens on the client.
Also, side node, I would NOT dispatch something on a for loop like you are doing on the first one, because if that triggers a firestore request, then it might get expensive real fast.

Retrieving/Handling Image URL From Firestore - React Native

I am new to React Native and Firebase (Firestore) and I'm developing an app where I have to retrieve posts to my feed.
I can retrieve all data I need, but I don't know how to display the image in the frontend. The post document has a field image which is saved as an URL and stored in the Firebase storage.
Does anyone know how can I get the image displayed? I am using a to sort the data.
This is my retrieveData() and it prints the correct URL:
retrieveData() {
var that = this;
let postRef = firebase
.firestore()
.collection("posts")
.orderBy("timestamp", "desc")
.limit(10);
let allPosts = postRef
.get()
.then((snapshot) => {
snapshot.forEach((doc) => {
var post = that.state.posts;
const data = (doc.id, "=>", doc.data());
that.setState({ posts: post });
console.log(data.image);
post.push({
id: data.id,
title: data.title,
description: data.description,
expireDate: data.expireDate,
timestamp: data.timestamp,
image: data.image,
});
});
})
.catch((err) => {
console.log("Error getting documents", err);
});
}
And this is how I am calling the image in the flatlist:
<Image
source={post.image}
style={styles.postImage}
/>
Can anyone help me with that?
Thanks in advance.
Can you share the image url ? And preferred way is to store image on the firebase storage and get the downloadUrl then store that url in the firestore document.
fileref.put(file)
.then(snapshot => {
return snapshot.ref.getDownloadURL(); // Will return a promise with the download
link
})
.then(downloadURL => {
console.log(`Successfully uploaded file and got download link - ${downloadURL}`);
return downloadURL;
})
.catch(error => {
// Use to signal error if something goes wrong.
console.log(`Failed to upload file and get link - ${error}`);
});

React Native | Firebase Firestore | Storing user data only after pressing twice

What I expect: When I press the text "sign up" once it will both create a user in firebase auth and then create a user in firestore with the single property.
What is actually happening: When I press the text "Sign Up" it immediately creates a user in firebase auth, but then it only creates a user in firestore with the property once I press either the username field, the password field, or the Sign Up button for a second time.
I suspect: That this has to do something with promises and my nested .then's but can't figure out why it is operating this way; seems really strange.
Sample code:
import React from "react";
import * as firebase from "firebase";
import "firebase/firestore";
const Screen = () => {
const firestore = firebase.firestore();
const writeUserData = uid => {
const docRef = firestore.doc(`users/${uid}`);
docRef
.set({
example_property
})
.then(item =>
console.log("successfully added user to the collection" + item)
)
.catch(err => {
console.log(err);
});
};
return (
<View style={styles.container}>
<TouchableOpacity
style={styles.submitButton}
onPress={() =>
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then(userObj => writeUserData(userObj.user.uid))
.catch(error => {
var errorCode = error.code;
var errorMessage = error.message;
console.log("errorCode: " + errorCode);
console.log("errorMessage: " + errorMessage);
})
}
>
<Text>Sign Up</Text>
</TouchableOpacity>
</View>
);
};
It now works that I've removed the anonymous function from the .then in writeUserData. See here:
const writeUserData = uid => {
const docRef = firestore.doc(`users/${uid}`);
docRef
.set({
example_property
})
.then(console.log("successfully added user"))
.catch(err => {
console.log(err);
});
};

Resources