0

I have a comments subcollection that needs to be connected to my posts ("thoughts") however I'm unable to dynamically add the document id to my doc(). I'm currently leaving it blank because of no other solution. Therefore, it is generating a new document ID instead of joining on to its parent document.

doc(post.id) doesn't work because post isn't defined in my addComments or getComments and just doc(id) doesn't work because of the same reason.

Here's what it's currently doing Here's what it's currently doing

and here's what I want it to do. I manually entered the document id of this post for demonstration purposes: and here's what I want it to do. I manually entered the document id of this post for demonstration purposes

Here's my fire.js

  import FirebaseKeys from "./config/FirebaseKeys";
  import firebase from "firebase/app";
  import '@firebase/auth';
  import 'firebase/database';
  import '@firebase/firestore';
  import "firebase/storage";
  require("firebase/firestore");

 class Fire {
    constructor() {
    firebase.initializeApp(FirebaseKeys);
}

getPosts = async (displayLatestPost) => {
    const post = await this.firestore.collection('thoughts').orderBy('timestamp', 'desc').limit(10).get()
    
    let postArray =[]
    post.forEach((post) => {
        
        postArray.push({id: post.id, ...post.data()})
    })

  displayLatestPost(postArray)  
}

getComments = async (latestComment) => {
    const fetchedComment = await this.firestore.collection('thoughts').doc().collection('comments').orderBy('timestamp', 'desc').limit(10).get()

    let commentArray = []
    fetchedComment.forEach((comment) => {
       
        commentArray.push({id: comment.id, ...comment.data()})
    })
   console.log(" post id: " + post.id + "comment id: " + fetchedComment.id)
    latestComment(commentArray)
}


addPost = async ({ thoughtTitle, thoughtText, localUri, photoURL, displayName}) => {
    const remoteUri = await this.uploadPhotoAsync(localUri, `photos/${this.uid}/${Date.now()}`);
   

    return new Promise((res, rej) => {
        
         this.firestore
            .collection("thoughts")
            .add({
                uid: this.uid,
                displayName,
                photoURL,
                thoughtTitle,
                thoughtText,
                image: remoteUri,
                timestamp: this.timestamp,
                
            })
            .then(ref => {
                res(ref);
            })
            .catch(error => {
                rej(error);
            });
    });
};

 //comments
 addComments = async ({ displayName, photoURL, commentText}) => {


console.log("add a comment - post id:" )


return new Promise((res, rej) => {
    
     this.firestore
        .collection("thoughts")
        .doc()
        .collection('comments')
        .add({
            uid: this.uid,
            displayName,
            photoURL,
            commentText,
            timestamp: this.timestamp,
            
        })
        .then(ref => {
            res(ref);
        })
        .catch(error => {
            rej(error);
        });
   });
};
 //comments

uploadPhotoAsync = (uri, filename) => {
    return new Promise(async (res, rej) => {
        const response = await fetch(uri);
        const file = await response.blob();

        let upload = firebase
            .storage()
            .ref(filename)
            .put(file);

        upload.on(
            "state_changed",
            snapshot => {},
            err => {
                rej(err);
            },
            async () => {
                const url = await upload.snapshot.ref.getDownloadURL();
                res(url);
            }
        );
    });
};

createUser = async user => {
    let remoteUri = null;
    
    try {
        await firebase.auth().createUserWithEmailAndPassword(user.email, user.password);
        
        let db = this.firestore.collection("users").doc(this.uid);
    
        db.set({
            displayName: user.displayName,
            email: user.email,
            photoURL: null
        });
    
        if (user.photoURL) {
            remoteUri = await this.uploadPhotoAsync(user.photoURL, `avatars/${this.uid}`);
    
            db.set({ photoURL: remoteUri }, { merge: true });
        }
    } catch (error) {
        alert("Error: ", error);
    }
    };


    updateProfile = async user => {
        let remoteUri = null;
        
        try {
            let db = this.firestore.collection("users").doc(this.uid);
           
        
            db.update({
              displayName: user.displayName,
              photoURL: user.photoURL
            });
        
            if (user.photoURL) {
                remoteUri = await this.uploadPhotoAsync(user.photoURL, `avatars/${this.uid}`);
        
                db.set({ photoURL: remoteUri }, { merge: true });
            }
        } catch (error) {
            alert("Error: ", error);
        }  
        
        }


signOut = () => {
    firebase.auth().signOut();
};

get firestore() {
    return firebase.firestore();
}

get uid() {
    return (firebase.auth().currentUser || {}).uid;
}

get timestamp() {
     return Date.now();  
  }

 }

Fire.shared = new Fire();
export default Fire;

Here's the screen where the comment is added.

import React from "react";
import { View, StyleSheet, Text, Image, ScrollView, TextInput, FlatList, 
TouchableOpacity} from "react-native";
import Fire from '../../Fire';

import CreatedAtText from '../../components/Text/CreatedAtText';

import Colors from  '../../constants/Colors';
import moment from 'moment';
import ThoughtCommentItem from '../../components/Thought/ThoughtCommentItem';
import FloatingButton from '../../components/UI/FloatingButton';




 export default class ThoughtDetailsScreen extends React.Component {
      state = {
     commentText: "",  
     latestComments: [],
     user: {
        photoURL: "",
        displayName: ""
    }
  
 };

 handleComment = () => {
    Fire.shared
        .addComments({  commentText: this.state.commentText.trim(), photoURL: this.state.user.photoURL, displayName: this.state.user.displayName })
        .then(ref => {
            this.setState({ commentText: ""});
        })
        .catch(error => {
            
            alert(error);
        });
};

displayLatestComment = (latestComments) => {
    this.setState({latestComments: latestComments});
   console.log("Latest Comments Details" + JSON.stringify(latestComments));
}

componentDidMount(){
   Fire.shared.getComments(this.displayLatestComment);
  
}

unsubscribe = null;

   componentDidMount() {
      const user = this.props.uid || Fire.shared.uid;


       this.unsubscribe = Fire.shared.firestore
          .collection("users")
          .doc(user)
          .onSnapshot(doc => {
           this.setState({ user: doc.data() });
       });
     }

   componentWillUnmount() {
      this.unsubscribe();
  } 


comment = () => {
    console.log("comment button clicked ")
  
 }

like = () => {
    console.log("like button clicked ")
  
}

fave = () => {
    console.log("fave button clicked ")
  
}



render(){
    
      const thought = this.props.navigation.getParam('thought');

 
   return(
      <View style={styles.container}>
   <ScrollView  style={{flex:1}}> 

  
       
    <Text>{thought.thoughtTitle}</Text>
    <Image style={styles.userAvatar} source={{uri: thought.photoURL}}/>
    <CreatedAtText style={styles.timestamp}> {moment(thought.timestamp).fromNow()}</CreatedAtText>
    <Text> User: {thought.displayName} </Text>
       <Image style={styles.headerPhoto} source={{uri: thought.image}}/>
    <Text>Main text: {thought.thoughtText}</Text>




 <View style={styles.commentsDisplay}>
   <FlatList
            showsVerticalScrollIndicator={false}
            keyExtractor={item => item.id}
            style={styles.feed}
            data={this.state.latestComments}
            renderItem={({item,index}) => {
                return ( 
                <ThoughtCommentItem
                photoURL={item.photoURL}
                displayName={item.displayName}
                timestamp={item.timestamp}
                commentText={item.commentText}
                onSelect={() => this.props.navigation.navigate('CommentDetails', {comment : item})}
                />
                );
            }

            } 
        />
     </View>


    <View style={styles.commentContainer}>
         <TextInput
           style={styles.commentBox}
           placeholder="Add your thoughts about this!"
           onChangeText={commentText =>this.setState({commentText})}
           defaultValue={this.state.commentText}
      />
     <Text onPress={this.handleComment} style={styles.commentButton}> Comment 
   </Text> 

   </View>
  

   </ScrollView>  
   <FloatingButton style={styles.floatingbutton}
   onSelectComment={this.comment}
   onSelectLike={this.like}
   onSelectFavorite={this.fave}
   />
   </View>

 );
 }
 };

 const styles = StyleSheet.create({
  container: {
    flex:1
},
commentContainer:{
    flexDirection: "row"
},
 headerPhoto : {
    height: 250,
    width: 200
},
userAvatar: {
    backgroundColor: Colors.subheadings,
    borderColor: Colors.accent,
    borderWidth:3.5, 

backgroundColor: Colors.maintext,
marginEnd: 15,
width: 35,
height: 35,
borderRadius: 20,
},
 floatingbutton:{
  bottom: 70,
  right: 60
 },
commentButton: {
   color: "#788eec",
    fontWeight: "bold",
    fontSize: 16
 },
commentsDisplay: {
   flex: 1,
   backgroundColor: Colors.background
 },
 feed : {
    marginHorizontal: 16

  }
});
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • You can directly pull the document id using docId = document.getId() after retrieving a document as per documentation here: https://firebase.google.com/docs/firestore/query-data/get-data#java_3 You can also specify an exact document path like so: db.collection("foo").document(docId).collection("bar") Also note that you have to alternate between collection and document when specifying the path. Does this help? – Sanzio Angeli Aug 20 '20 at 14:21
  • I'm trying to refer to the parent document of the subcollection.... – Blessing Platinum-Williams Aug 20 '20 at 15:05
  • Ah I think I understand now... Ran into the same issue once. I think this was the thread that helped me but at the very least it will be useful to you: https://stackoverflow.com/questions/56219469/firestore-get-the-parent-document-of-a-subcollection – Sanzio Angeli Aug 20 '20 at 15:13

2 Answers2

1

As you have already stated, the issue is with this piece of code:

this.firestore
.collection("thoughts")
.doc()
.collection('comments')
.add({
    uid: this.uid,
    displayName,
    photoURL,
    commentText,
    timestamp: this.timestamp,
        

In order to add subcollections or documents, you first need a valid reference and in this case .doc() is not specifying the document that will contain the subcollection. Ths is why you have the output of above(screenshots) and this is why your question is “ how to get my firestore document id”.

To get a firestore document id dynamically, you need to use a query that will produce a document object through which you will need to iterate for the id property and finally you will use this id property inside .doc().

Needless to say, the filters used in the query must produce one result; from the code you have shared I see that you can use uid as a filter. Here is the code about how to achieve this:

const firebase = require("firebase/app");
require("firebase/firestore");

firebase.initializeApp({
  projectId: 'project123'
});
var db = firebase.firestore();

function InsertSubcollection(collection,uid){

    db.collection(collection).where("uid", "==", uid).get()
        .then(function (querySnapshot) {
            let id =0;
            querySnapshot.forEach((doc) => id = doc.id);
            const docRef = db.collection('thoughts').doc(id).collection('comments');
            docRef.doc().add({
                name: "Sample123",
                test: true
              });
        })
        .catch(function (error) {
            console.log("Error getting documents: ", error);
        });
}

I hope this sheds some light on.

If you want to know more about firestore queries, please visit this.

Antonio Ramirez
  • 943
  • 6
  • 15
1

Sorry that I didn't post the solution that found.

Needed to add this line to the screen where the comment was added -

handleComment = () => {
        Fire.shared
            .addComments({  commentText: this.state.commentText.trim(), photoURL: this.state.user.photoURL, displayName: this.state.user.displayName, postID: this.props.navigation.state.params.thought.id })
            .then(ref => {
                this.setState({ commentText: ""});
            })
            .catch(error => {
                
                alert(error);
            });
    }; 

And then I had to add postID to my Fire.js addComments.

 addComments = async ({ displayName, photoURL, commentText, postID}) => {

    return new Promise((res, rej) => {
        
         this.firestore
            .collection("thoughts")
            .doc(postID)
            .collection('comments')
            .add({
                uid: this.uid,
                displayName,
                photoURL,
                commentText,
                timestamp: this.timestamp,
                
            })
            .then(ref => {
                res(ref);
            })
            .catch(error => {
                rej(error);
            });
    });
   };

So basically the lines of code added were :

postID: this.props.navigation.state.params.thought.id

and -

 .doc(postID)

I really hope this helps someone having similar issues.